Overall Flow
Data Source (non-developers write error messages)
|
+-- Google Sheets (tabs = per-language separation)
+-- Airtable
+-- Notion
+-- CSV (local file, per-language file separation)
+-- XLSX (local file, sheets = per-language separation)
|
v
@sanghyuk-2i/huh-cli --- pull command
|
+-- Fetch per data source via Adapter pattern
+-- Parse with @sanghyuk-2i/huh-core's parseSheetData
+-- Validate with @sanghyuk-2i/huh-core's validateConfig
+-- (i18n) Cross-locale validate with @sanghyuk-2i/huh-core's validateLocales
|
v
huh.json or huh/ directory (build-time artifact)
| +-- ko.json, en.json (per-locale)
| +-- index.ts (auto-generated barrel)
v
@sanghyuk-2i/huh-react, @sanghyuk-2i/huh-vue, @sanghyuk-2i/huh-svelte --- HuhProvider
|
+-- Inject JSON data via source prop (single language)
+-- Inject multi-language data via locales prop (i18n)
+-- Trigger errors with huh(code, variables) (direct trackId or via errorMap)
+-- Look up + substitute variables via @sanghyuk-2i/huh-core's resolveError
|
v
User-provided custom renderers --- TOAST / MODAL / PAGE / custom typesPackage Dependency Graph
@sanghyuk-2i/huh-react --depends--> @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-vue --depends--> @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-svelte --depends--> @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-cli --depends--> @sanghyuk-2i/huh-core
@sanghyuk-2i/huh-plugin-sentry --depends--> @sanghyuk-2i/huh-core (+ @sentry/browser peer dep)
@sanghyuk-2i/huh-plugin-datadog --depends--> @sanghyuk-2i/huh-core (+ @datadog/browser-logs peer dep)
@sanghyuk-2i/huh-core -- (zero dependencies)- core has no external dependencies and can be used anywhere.
- react depends on core; react/react-dom are peer dependencies.
- vue depends on core; vue >= 3.3 is a peer dependency.
- svelte depends on core; svelte >= 5.0 is a peer dependency.
- cli depends on core and uses commander/googleapis/picocolors/xlsx.
- plugin-sentry depends on core; @sentry/browser is a peer dependency.
- plugin-datadog depends on core; @datadog/browser-logs is a peer dependency.
Package Responsibilities
@sanghyuk-2i/huh-core
| Module | Responsibility |
|---|---|
schema.ts | All type definitions (ErrorConfig, ErrorEntry, ERROR_TYPES, ACTION_TYPES, HuhPlugin, etc.) |
plugin.ts | Plugin runner utility (runPluginHook) |
parser.ts | Converts sheet raw data (2D string array) to ErrorConfig |
template.ts | placeholder substitution utility |
resolver.ts | Looks up error by trackId + applies variable substitution |
validator.ts | ErrorConfig validation (errors + warnings) |
locale-validator.ts | Multi-language cross-locale validation (trackId consistency, type/actionType mismatch) |
@sanghyuk-2i/huh-react
| Module | Responsibility |
|---|---|
ErrorContentProvider.tsx | Context Provider, error state management, renderer invocation |
useErrorContent.ts | huh/clearError hook |
types.ts | ErrorRenderProps, RendererMap type definitions |
@sanghyuk-2i/huh-vue
| Module | Responsibility |
|---|---|
ErrorContentProvider.ts | provide/inject-based Provider, error state management, render function renderer invocation |
useErrorContent.ts | inject-based useHuh composable |
types.ts | ErrorRenderProps, RendererMap (Vue Component) type definitions |
@sanghyuk-2i/huh-svelte
| Module | Responsibility |
|---|---|
HuhProvider.svelte | setContext-based Provider, $state error state management, dynamic component rendering |
useErrorContent.ts | getContext-based useHuh |
context.ts | Separated Symbol injection key |
types.ts | ErrorRenderProps, RendererMap (Svelte Component) type definitions |
@sanghyuk-2i/huh-cli
| Module | Responsibility |
|---|---|
commands/init.ts | Config file template generation, source type definitions |
commands/pull.ts | Fetch data via Adapter, parse, validate, generate JSON file |
commands/validate.ts | JSON file validation |
adapters/types.ts | SourceAdapter interface definition |
adapters/registry.ts | Adapter register/get/clear registry |
adapters/google-sheets.ts | Google Sheets API v4 adapter |
adapters/airtable.ts | Airtable REST API adapter |
adapters/notion.ts | Notion API adapter |
adapters/csv.ts | Local CSV file adapter (RFC 4180 compatible parser) |
adapters/xlsx.ts | Local XLSX file adapter (SheetJS) |
adapters/index.ts | Barrel -- auto-registers all adapters on import |
fetch-sheet.ts | Google Sheets API v4 data fetch (used by adapter) |
fetch-airtable.ts | Re-export shim -> adapters/airtable |
fetch-notion.ts | Re-export shim -> adapters/notion |
generate.ts | ErrorConfig -> JSON file write; in i18n mode, generates per-locale files + index.ts barrel |
JSON DSL Schema
ErrorConfig (Record<trackId, ErrorEntry>)
|
+-- trackId: string (unique identifier)
|
+-- ErrorEntry
+-- type: ErrorType (uppercase. Built-in: 'TOAST' | 'MODAL' | 'PAGE' + custom types)
+-- message: string (supports {{variable}} templates)
+-- title?: string
+-- image?: string
+-- severity?: Severity (uppercase. Built-in: 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL' + custom severity)
+-- action?: ErrorAction
+-- label: string
+-- type: ActionType (uppercase. Built-in: 'REDIRECT' | 'RETRY' | 'BACK' | 'DISMISS' + custom actions)
+-- target?: string (required for REDIRECT)Type Extensibility
ErrorType and ActionType are open-ended string types:
- Built-in types:
ERROR_TYPES(TOAST,MODAL,PAGE),ACTION_TYPES(REDIRECT,RETRY,BACK,DISMISS),SEVERITY_LEVELS(INFO,WARNING,ERROR,CRITICAL) - Custom types (e.g.,
BANNER,SNACKBAR,OPEN_CHAT) can be freely added from data sources - The parser automatically converts to uppercase (lowercase input is accepted)
- Custom types work automatically when you register the corresponding renderer in the framework's
RendererMap - Custom action types are handled via the
HuhProvider'sonCustomActioncallback
Rendering Strategy
The Provider does not supply default renderers. This is intentional:
- Since every project has a different design system, providing default UI would still require customization.
- Users implement renderers for each error type they use via
RendererMap. In addition to the built-in types (TOAST,MODAL,PAGE), renderers for custom types can also be added. - Renderers receive
ErrorRenderProps, withonActionandonDismisscallbacks automatically generated.
Action Handling Flow
User clicks action button -> onAction() called
|
+-- REDIRECT -> router.push(target) if router provided, else window.location.href = target
+-- BACK -> router.back() if router provided, else window.history.back()
+-- RETRY -> clearError() + onRetry callback
+-- DISMISS -> clearError()
+-- Custom -> clearError() + onCustomAction callbackPlugin System
Plugins provide extension points for error monitoring, analytics, and custom integrations. Each framework Provider accepts a plugins prop.
Plugin Lifecycle
huh(code, variables)
|
+-- resolveError -> set active error
+-- runPluginHook(plugins, 'onError', resolved, context) <-- Plugin hook
|
v
User clicks action button -> onAction()
|
+-- runPluginHook(plugins, 'onAction', error, action) <-- Plugin hook
+-- Execute action (REDIRECT / RETRY / DISMISS / etc.)Error Isolation
Plugin errors are caught and logged as warnings. A failing plugin never breaks the error rendering flow.
Official Plugins
| Package | Description |
|---|---|
@sanghyuk-2i/huh-plugin-sentry | Sentry integration — captureMessage on error, addBreadcrumb on action |
@sanghyuk-2i/huh-plugin-datadog | Datadog integration — datadogLogs.logger on error, info-level action logging |
Build Configuration
The monorepo is managed with the following tools:
| Tool | Role |
|---|---|
| pnpm workspace | Package manager + workspaces |
| turborepo | Build/test pipeline (caching, dependency ordering) |
| tsup | TypeScript bundling (CJS + ESM + IIFE + d.ts) |
| vitest | Test runner |
Build Outputs
| File | Format | Purpose |
|---|---|---|
dist/index.js | CJS | require() |
dist/index.mjs | ESM | import |
dist/index.global.js | IIFE (minified) | CDN, <script> |
dist/index.d.ts | TypeScript declarations | Type support |
Build Order
Turborepo analyzes dependency relationships and determines the order automatically:
1. @sanghyuk-2i/huh-core (no dependencies, built first)
2. @sanghyuk-2i/huh-react + @sanghyuk-2i/huh-vue + @sanghyuk-2i/huh-svelte + @sanghyuk-2i/huh-cli (built in parallel after core)Directory Structure
/
+-- package.json # Root (monorepo scripts)
+-- pnpm-workspace.yaml
+-- turbo.json
+-- tsconfig.base.json
+-- vitest.workspace.ts
+-- docs/ # Documentation
| +-- getting-started.md
| +-- google-sheet-guide.md
| +-- airtable-guide.md
| +-- notion-guide.md
| +-- api-core.md
| +-- api-react.md
| +-- api-cli.md
| +-- architecture.md
+-- packages/
+-- core/
| +-- package.json
| +-- tsconfig.json
| +-- tsup.config.ts
| +-- src/
| +-- index.ts
| +-- schema.ts
| +-- parser.ts
| +-- resolver.ts
| +-- template.ts
| +-- validator.ts
| +-- locale-validator.ts
| +-- __tests__/
+-- react/
| +-- package.json
| +-- tsup.config.ts
| +-- src/
| +-- index.ts
| +-- ErrorContentProvider.tsx
| +-- useErrorContent.ts
| +-- types.ts
| +-- __tests__/
+-- vue/
| +-- package.json
| +-- tsup.config.ts
| +-- src/
| +-- index.ts
| +-- ErrorContentProvider.ts
| +-- useErrorContent.ts
| +-- types.ts
| +-- __tests__/
+-- svelte/
| +-- package.json
| +-- svelte.config.js
| +-- src/
| +-- index.ts
| +-- HuhProvider.svelte
| +-- useErrorContent.ts
| +-- context.ts
| +-- types.ts
| +-- __tests__/
+-- cli/
+-- package.json
+-- tsconfig.json
+-- tsup.config.ts
+-- src/
+-- index.ts
+-- fetch-sheet.ts
+-- fetch-airtable.ts # re-export -> adapters/airtable
+-- fetch-notion.ts # re-export -> adapters/notion
+-- generate.ts
+-- adapters/
| +-- index.ts # barrel (auto-registers all adapters)
| +-- types.ts # SourceAdapter interface
| +-- registry.ts # register/get/clear
| +-- google-sheets.ts
| +-- airtable.ts
| +-- notion.ts
| +-- csv.ts
| +-- xlsx.ts
+-- commands/
| +-- init.ts
| +-- pull.ts
| +-- validate.ts
+-- __tests__/