@craft-ts/mcp 0.7.0-beta.18 → 0.7.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -8,12 +8,46 @@ Public docs: https://craft-ts.github.io/craft
|
|
|
8
8
|
LLM index: https://craft-ts.github.io/craft/llms.txt
|
|
9
9
|
Full dump: https://craft-ts.github.io/craft/llms-full.txt
|
|
10
10
|
|
|
11
|
-
For a new framework-independent app, start
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
For a new framework-independent app, start by asking the user only:
|
|
12
|
+
|
|
13
|
+
> What kind of application are you building, and what are its main features?
|
|
14
|
+
> Keep the answer high-level for now; we will refine the details afterwards.
|
|
15
|
+
|
|
16
|
+
Do not begin by asking whether to use EffectTS. From the application type and
|
|
17
|
+
the main features, infer whether a backend is needed. If it is, propose the
|
|
18
|
+
stack explicitly and ask for confirmation:
|
|
19
|
+
|
|
20
|
+
> I suggest CraftTS for the frontend and EffectTS v4 for the backend. EffectTS
|
|
21
|
+
> fits this project particularly well because its typed services, Layers and
|
|
22
|
+
> errors align with CraftTS's dependency graph and typed server boundary. Is
|
|
23
|
+
> that stack OK?
|
|
24
|
+
|
|
25
|
+
When the user confirms the proposal, use EffectTS for the backend by default.
|
|
26
|
+
If the user rejects it or names another backend, do not silently scaffold an
|
|
27
|
+
EffectTS backend; honour the requested choice, or leave the backend out when
|
|
28
|
+
no alternative is specified.
|
|
29
|
+
|
|
30
|
+
Once the stack is confirmed, create an empty, domain-ready starter with the
|
|
31
|
+
quality surfaces enabled from the beginning:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx --yes --package @craft-ts/dev-tools@beta craft create <directory> \
|
|
35
|
+
--yes --no-demos --domain app \
|
|
36
|
+
--frontend-runtime=plain --backend-runtime=effect \
|
|
37
|
+
--i18n=strict --design-system=basic --typed-css \
|
|
38
|
+
--references=all --agents=codex
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Replace `--backend-runtime=effect` with the user's explicit backend choice,
|
|
42
|
+
or `--backend-runtime=none` when they decline a backend. When no Effect
|
|
43
|
+
runtime is selected, use `--references=craft-ts` instead of `--references=all`.
|
|
44
|
+
Keep
|
|
45
|
+
`--no-demos`, `--i18n=strict`, `--design-system=basic` and `--typed-css` by
|
|
46
|
+
default for agent-created projects. The starter contains the CraftTS
|
|
47
|
+
architecture and tooling contract, but no prefilled product pages or demo
|
|
48
|
+
content. CraftTS source references are always enabled, and EffectTS references
|
|
49
|
+
are included whenever either runtime uses EffectTS; there is no final reference
|
|
50
|
+
confirmation question.
|
|
17
51
|
|
|
18
52
|
When MCP tools are available, call `get_best_practices` once, then `search_documentation` / `get_skill` instead of inventing APIs.
|
|
19
53
|
|
package/content/docs-index.json
CHANGED
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
{
|
|
183
183
|
"path": "/guide/create-project",
|
|
184
184
|
"title": "Create a CraftTS project",
|
|
185
|
-
"body": "# Create a CraftTS project\n\nUse `craft create` to generate a framework-independent CraftTS application\nwith routing, a typed API example, linting, tests, and the architecture\ncontract already wired up.\n\n## Prerequisites\n\nThe beta toolchain requires Node.js 20.19 or newer. The `craft` executable is\npublished by `@craft-ts/dev-tools`; it is not provided by the unrelated npm\npackage named `craft`.\n\nFor a new project, invoke the executable explicitly through `npx`:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app\n```\n\nThe first `--yes` belongs to `npx`: it accepts the temporary package\ninstallation. The command remains interactive because `craft create` itself\nwas not given `--yes`.\n\nThe command uses the published `beta` package. A checkout of CraftTS can\ncontain a newer creation flow than the version currently published on npm;\ncheck the resolved version with `npm view @craft-ts/dev-tools@beta version` if\nthe prompts shown by your terminal do not match this page.\n\n## Interactive creation\n\nRun the command in a real terminal without `craft create --yes`:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app\n```\n\nThe generator presents menus for:\n\n- the frontend runtime: `plain` or `effect`;\n- the backend runtime: `none`, `promise`, or `effect`;\n- type-safe i18n, its locales, and its default locale;\n- the design system;\n- typed CSS;\n- a standalone or Nx workspace;\n- CraftTS and, when Effect is selected, EffectTS source references for agent\n context;\n- integrations for Codex, Cursor, Claude Code, or Gemini CLI.\n\nThe frontend and backend choices are independent. To create a plain browser\napplication whose server functions use Effect v4, choose `plain` for the\nfrontend and `effect` for the backend.\n\nUse `↑`/`↓` to move and `Enter` to confirm a single choice. For locales and\nagent integrations, use `Space` to select or deselect several items, then\n`Enter` to confirm. The project directory remains a text field because it is\na free-form path. If the directory is omitted, the generator asks for it too:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create\n```\n\nThe agent question is a multi-selection list. Use `↑`/`↓` to move, `Space` to\nselect or deselect an integration, and `Enter` to confirm. Codex starts\nselected, preserving the default used by scripted creation. Every starter\nreceives an `AGENTS.md` project guide describing its selected runtimes and\nfeatures; selected integrations additionally receive their editor-specific\nproject instructions and skills.\n\n### Creating inside an existing Git repository\n\nAn existing `.git` directory makes the destination non-empty. Generate into\nthe current repository with `--force`:\n\n```bash\ncd pet-foster-family\nnpx --yes --package @craft-ts/dev-tools@beta craft create . --force\n```\n\n`--force` only permits writing into a non-empty destination; it does not turn\noff the configuration prompts. Review generated file changes before\ncommitting when the repository already contains application code.\n\nDuring the interactive flow, reference sources are cloned by default:\n\n- CraftTS sources go into `.references/craft-ts`;\n- EffectTS sources are also cloned when an Effect frontend or backend is\n selected;\n- the sources are available to agents without replacing the installed npm\n packages.\n\nAnswer `n` to opt out. In non-interactive mode, references remain opt-in so\nthat `--yes` does not silently perform network clones; use\n`--references=craft-ts` or `--references=all` explicitly.\n\nThe cloned repositories are reference material for coding agents only. The\ngenerated application always imports the published CraftTS and EffectTS npm\npackages from `package.json`; it does not use `file:` dependencies or\nTypeScript/Vite aliases to the clones. Use `npm run update:references` to fetch\nthe requested refs and refresh the recorded SHAs.\n\n## Non-interactive creation\n\nPass `--yes` after `create` to use defaults and disable all prompts. Combine it\nwith explicit options when the generated configuration must be reproducible:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --effect=none --agents=codex\n```\n\nFor a minimal plain starter:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --effect=none --i18n=none --design-system=none --no-typed-css \\\n --agents=none\n```\n\nTo create a backend-only Effect project and clone both reference sources:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --frontend-runtime=plain --backend-runtime=effect \\\n --references=all\n```\n\nThe main configuration options are:\n\n| Option | Values | Purpose |\n| -------------------- | ------------------------------------- | ----------------------------------------------------------------------- |\n| `--effect` | `v4`, `none` | Select the Effect v4 or plain starter |\n| `--frontend-runtime` | `plain`, `effect` | Choose the frontend runtime |\n| `--backend-runtime` | `none`, `promise`, `effect` | Choose server functions |\n| `--effect-scope` | `none`, `frontend`, `backend`, `both` | Set Effect placement |\n| `--agents` | comma-separated names or `none` | Add editor-specific agent integrations; `AGENTS.md` is always generated |\n| `--i18n` | `strict`, `loose`, `none` | Configure type-safe i18n |\n| `--design-system` | `basic`, `none` | Include the design-system starter |\n| `--typed-css` | flag / `--no-typed-css` | Enable or disable typed CSS |\n| `--workspace` | `standalone`, `nx` | Choose the workspace layout |\n| `--references` | `none`, `craft-ts`, `all` | Include source references |\n| `--no-demos` | flag | Generate a domain feature without explanatory demo pages |\n| `--domain` | slug | Name the first domain feature when using `--no-demos` |\n| `--force` | flag | Allow an existing non-empty destination |\n| `--json` | flag | Print the effective configuration as JSON |\n\nUse `craft create --help` to see the complete list:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create --help\n```\n\nFor a domain-first starting point, omit the explanatory home/services/about\npages and name the feature explicitly:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create pet-foster \\\n --yes --no-demos --domain animal --frontend-runtime=effect \\\n --backend-runtime=effect\n```\n\nThe generated feature lives under `src/app/features/animal/`. Add a form to\nthat feature with the existing primitives and its unit/submission test:\n\n```bash\ncraft add form animal\n# advanced nested/schema variant:\ncraft add form animal --advanced\n```\n\n## After generation\n\nThe generator creates a Git repository when the destination is not already\ninside another repository. It does not create a commit. The generated\n`.gitignore` excludes `node_modules/`, build outputs, test reports, and local\nreference clones.\n\nInstall dependencies and start the generated application:\n\n```bash\ncd my-app\nnpm install\nnpm run dev\n```\n\nThe generated project also includes the following checks:\n\n```bash\nnpm run lint\nnpm run typecheck\nnpm test\nnpm run architecture\nnpm run build\n```\n\nWith a backend, `src/server/application.ts` owns the registry and runtime\nLayer, while `src/server/node-http.ts` is only the Node stream adapter.\n`server.ts` re-exports both for compatibility. In the backend-only Effect\nprofile, the browser remains plain CraftTS; Effect services, middleware and\nerror projections stay under the server boundary.\n\n## Troubleshooting\n\n### `could not determine executable to run`\n\nIf the error mentions `craft@0.1.0`, `npx` resolved the unrelated public npm\npackage named `craft`. Use the explicit `--package @craft-ts/dev-tools@beta`\nform shown above.\n\nIf `@craft-ts/dev-tools` is already installed in the project, its local binary\ncan also be called with:\n\n```bash\nnpx craft create my-app\n```\n\nThe explicit form is still the safest command when bootstrapping a project\nthat has no `package.json` yet.\n"
|
|
185
|
+
"body": "# Create a CraftTS project\n\nUse `craft create` to generate a framework-independent CraftTS application\nwith routing, a typed API example, linting, tests, and the architecture\ncontract already wired up.\n\n## Prerequisites\n\nThe beta toolchain requires Node.js 20.19 or newer. The `craft` executable is\npublished by `@craft-ts/dev-tools`; it is not provided by the unrelated npm\npackage named `craft`.\n\nFor a new project, invoke the executable explicitly through `npx`:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app\n```\n\nThe first `--yes` belongs to `npx`: it accepts the temporary package\ninstallation. The command remains interactive because `craft create` itself\nwas not given `--yes`.\n\nThe command uses the published `beta` package. A checkout of CraftTS can\ncontain a newer creation flow than the version currently published on npm;\ncheck the resolved version with `npm view @craft-ts/dev-tools@beta version` if\nthe prompts shown by your terminal do not match this page.\n\n## Interactive creation\n\nRun the command in a real terminal without `craft create --yes`:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app\n```\n\nThe generator presents menus in this order:\n\n- the application type: frontend-only or full-stack;\n- for a full-stack app, the backend runtime: `promise` or `effect` (EffectTS\n v4 is recommended);\n- the frontend runtime: `plain` or `effect`;\n- type-safe i18n, its locales, and its default locale;\n- the design system;\n- typed CSS;\n- a standalone or Nx workspace;\n- integrations for Codex, Cursor, or Cloud Code.\n\nThe frontend and backend choices are independent. To create a plain browser\napplication whose server functions use Effect v4, choose `plain` for the\nfrontend and `effect` for the backend.\n\nUse `↑`/`↓` to move and `Enter` to confirm a single choice. For locales and\nagent integrations, use `Space` to select or deselect several items, then\n`Enter` to confirm. The project directory remains a text field because it is\na free-form path. If the directory is omitted, the generator asks for it too:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create\n```\n\nThe agent question is a multi-selection list. Use `↑`/`↓` to move, `Space` to\nselect or deselect an integration, and `Enter` to confirm. Codex starts\nselected, preserving the default used by scripted creation. Every starter\nreceives an `AGENTS.md` project guide describing its selected runtimes and\nfeatures; selected integrations additionally receive their editor-specific\nproject instructions and skills.\n\n## Agent-assisted creation\n\nWhen an agent starts a new project, it should first ask what kind of\napplication is being built and what its main features are, without collecting\ndetailed requirements yet. If those features imply a backend, it should\npropose EffectTS v4 for the backend and explain that its typed services, Layers\nand errors fit CraftTS's typed server boundary. The user can confirm that\nstack, reject it, or name another backend; the agent must not add an EffectTS\nbackend after an explicit rejection.\n\nThe agent should create a domain-ready but empty starter with the design\nsystem, typed CSS and strict i18n enabled, and without the explanatory demo\npages:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --no-demos --domain app \\\n --frontend-runtime=plain --backend-runtime=effect \\\n --i18n=strict --design-system=basic --typed-css \\\n --references=all --agents=codex\n```\n\nUse `--backend-runtime=none` when the user declines a backend, or the explicit\nrequested backend when it is supported. When no Effect runtime is selected,\nuse `--references=craft-ts` instead of `--references=all`. The `--no-demos`\nstarter still\ncontains the architecture/tooling baseline and a domain boundary, but no\nprefilled product pages or demo content.\n\n### Creating inside an existing Git repository\n\nAn existing `.git` directory makes the destination non-empty. Generate into\nthe current repository with `--force`:\n\n```bash\ncd pet-foster-family\nnpx --yes --package @craft-ts/dev-tools@beta craft create . --force\n```\n\n`--force` only permits writing into a non-empty destination; it does not turn\noff the configuration prompts. Review generated file changes before\ncommitting when the repository already contains application code.\n\nDuring the interactive flow, reference sources are cloned automatically:\n\n- CraftTS sources go into `.references/craft-ts`;\n- EffectTS sources are also cloned when an Effect frontend or backend is\n selected;\n- the sources are available to agents without replacing the installed npm\n packages.\n\nThere is no reference confirmation prompt. The same defaults apply in\nnon-interactive mode: CraftTS is cloned, and EffectTS is cloned whenever an\nEffect frontend or backend is selected. Use `--references=none` to opt out, or\n`--references=craft-ts` / `--references=all` to choose explicitly.\n\nThe cloned repositories are reference material for coding agents only. The\ngenerated application always imports the published CraftTS and EffectTS npm\npackages from `package.json`; it does not use `file:` dependencies or\nTypeScript/Vite aliases to the clones. Use `npm run update:references` to fetch\nthe requested refs and refresh the recorded SHAs.\n\n## Non-interactive creation\n\nPass `--yes` after `create` to use defaults and disable all prompts. Combine it\nwith explicit options when the generated configuration must be reproducible:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --effect=none --agents=codex\n```\n\nFor a minimal plain starter:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --effect=none --i18n=none --design-system=none --no-typed-css \\\n --agents=none\n```\n\nTo create a backend-only Effect project and clone both reference sources:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create my-app \\\n --yes --frontend-runtime=plain --backend-runtime=effect \\\n --references=all\n```\n\nThe main configuration options are:\n\n| Option | Values | Purpose |\n| -------------------- | ------------------------------------- | ------------------------------------------------------------------------- |\n| `--effect` | `v4`, `none` | Select the Effect v4 or plain starter |\n| `--frontend-runtime` | `plain`, `effect` | Choose the frontend runtime |\n| `--backend-runtime` | `none`, `promise`, `effect` | Choose server functions |\n| `--effect-scope` | `none`, `frontend`, `backend`, `both` | Set Effect placement |\n| `--agents` | comma-separated names or `none` | Add editor-specific agent integrations; `AGENTS.md` is always generated |\n| `--i18n` | `strict`, `loose`, `none` | Configure type-safe i18n |\n| `--design-system` | `basic`, `none` | Include the design-system starter |\n| `--typed-css` | flag / `--no-typed-css` | Enable or disable typed CSS |\n| `--workspace` | `standalone`, `nx` | Choose the workspace layout |\n| `--references` | `none`, `craft-ts`, `all` | Include source references (default: CraftTS, plus EffectTS when selected) |\n| `--no-demos` | flag | Generate a domain feature without explanatory demo pages |\n| `--domain` | slug | Name the first domain feature when using `--no-demos` |\n| `--force` | flag | Allow an existing non-empty destination |\n| `--json` | flag | Print the effective configuration as JSON |\n\nUse `craft create --help` to see the complete list:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create --help\n```\n\nFor a domain-first starting point, omit the explanatory home/services/about\npages and name the feature explicitly:\n\n```bash\nnpx --yes --package @craft-ts/dev-tools@beta craft create pet-foster \\\n --yes --no-demos --domain animal --frontend-runtime=effect \\\n --backend-runtime=effect\n```\n\nThe generated feature lives under `src/app/features/animal/`. Add a form to\nthat feature with the existing primitives and its unit/submission test:\n\n```bash\ncraft add form animal\n# advanced nested/schema variant:\ncraft add form animal --advanced\n```\n\n## After generation\n\nThe generator creates a Git repository when the destination is not already\ninside another repository. It does not create a commit. The generated\n`.gitignore` excludes `node_modules/`, build outputs, test reports, and local\nreference clones.\n\nInstall dependencies and start the generated application:\n\n```bash\ncd my-app\nnpm install\nnpm run dev\n```\n\nThe generated project also includes the following checks:\n\n```bash\nnpm run lint\nnpm run typecheck\nnpm test\nnpm run architecture\nnpm run build\n```\n\nWith a backend, `src/server/application.ts` owns the registry and runtime\nLayer, while `src/server/node-http.ts` is only the Node stream adapter.\n`server.ts` re-exports both for compatibility. In the backend-only Effect\nprofile, the browser remains plain CraftTS; Effect services, middleware and\nerror projections stay under the server boundary.\n\n## Troubleshooting\n\n### `could not determine executable to run`\n\nIf the error mentions `craft@0.1.0`, `npx` resolved the unrelated public npm\npackage named `craft`. Use the explicit `--package @craft-ts/dev-tools@beta`\nform shown above.\n\nIf `@craft-ts/dev-tools` is already installed in the project, its local binary\ncan also be called with:\n\n```bash\nnpx craft create my-app\n```\n\nThe explicit form is still the safest command when bootstrapping a project\nthat has no `package.json` yet.\n"
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
"path": "/guide/deployment",
|
|
@@ -242,27 +242,27 @@
|
|
|
242
242
|
{
|
|
243
243
|
"path": "/guide/i18n",
|
|
244
244
|
"title": "Type-safe i18n",
|
|
245
|
-
"body": "# Type-safe i18n\n\n`@craft-ts/i18n`
|
|
245
|
+
"body": "# Type-safe i18n\n\n`@craft-ts/i18n` is the CraftTS i18n integration. The catalogue remains a plain\ndeclarative TypeScript value, while DI-aware tokens use the existing CraftTS\nservice contracts. A catalogue that does not use DI can still be formatted by\n`runtime.t`; a catalogue with DI is rendered through the reactive CraftTS\ntranslator so its dependencies are checked like component dependencies.\n\n## The contract\n\nSix things are guaranteed, and all six are checked before the app runs.\n\n| guarantee | what it costs you to break |\n| ----------------------------------------------------------- | ------------------------------------------------------------------- |\n| the key set is a **closed union** | an unknown key does not compile — no silent `order.totl` |\n| every locale has the **same keys with the same parameters** | a translation you forgot is a compile error, not a fallback |\n| parameters are **typed by their token** | a date cannot be passed where a currency amount belongs |\n| a plural carries **every category the locale requires** | Polish needs `one`/`few`/`many`/`other`; French needs `one`/`other` |\n| a DI-aware token declares its **CraftTS services** | a missing provider is a compile error at the component/route boundary |\n| a token declared with a **schema** types its own input | the call site passes what the schema parses, not what the formatter wants |\n\nThe usual failure mode of a translation layer is that all of these are\nruntime concerns: a missing key renders its own name, a wrong parameter renders\n`[object Object]`, and a missing plural category renders the wrong branch to the\nusers of one locale only. None of that is observable from the code that calls\n`t`.\n\n## The shape of it\n\n```\nsrc/i18n/\n catalog.ts the reference locale — defineCatalog + msg + plural\n locales/fr-FR.ts every other locale — defineLocaleLike\n project-tokens.ts business tokens: defineToken / defineTokenFactory\n runtime.ts createI18nRuntime, and the reactive binding\n```\n\nA key is its dotted path: `order.total` reaches\n`{ order: { total: msg`…` } }`.\n\n## Where to go next\n\n- [The catalogue](./catalog.md) — `defineCatalog`, `msg`, `plural`,\n `defineLocale`, `defineLocaleLike`.\n- [Tokens](./tokens.md) — the shipped semantic tokens, and how to add your own.\n- [The runtime](./runtime.md) — `createI18nRuntime`, `t`, `bind`, lazy locales.\n- [With Effect](./effect.md) — `@craft-ts/i18n-effect`.\n\nTwo checks belong in CI, and `craft create` wires both:\n\n```bash\nnpm run i18n:check\nnpm run i18n:test\n```\n\nA working example lives in the demo, at `apps/demo/src/app/examples/i18n/`.\n\n## Guard visible text in Craft templates\n\n`craft create` enables this preset for every project generated **with** i18n —\nits own pages already take their copy from the catalogue. A project generated\nwithout i18n never sees the rule. To add it by hand to an existing application:\n\n```js\nimport craftRules from '@craft-ts/dev-tools/eslint-rules';\n\nexport default [\n {\n plugins: { 'craft-ts': craftRules },\n rules: { ...craftRules.configs.i18n.rules },\n },\n];\n```\n\n`craft-ts/require-i18n-text` reports static text in visible headings,\nparagraphs, labels, buttons, links and options, plus visible `placeholder`,\n`aria-label` and `title` attributes — and it looks *inside* the visible\nposition, so `p('Total: ' + t('cart.total'))`, `` span(`Total: ${amount}`) ``,\n`label(isNew ? 'New' : 'Returning')`, `p(name || 'Anonymous')` and a literal in\na children array are reported too. Only what carries letters counts: `first + ' ' + last`\nis glue between values, not copy. Dynamic business values, `i18n.t(...)`, its\nkey and parameters, a generator child and catalogue files are accepted. Server files and tests are excluded so\ntechnical messages and assertions can remain literal. The rule stays separate\nfrom the recommended preset, because it only makes sense once the catalogue is\nthe application's source of truth — which is exactly the condition `craft\ncreate` checks when it decides to enable it.\n"
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"path": "/guide/i18n/catalog",
|
|
249
249
|
"title": "The catalogue",
|
|
250
|
-
"body": "# The catalogue\n\nA catalogue is a plain nested object. Nothing is parsed, nothing is loaded from\nJSON at build time, and every guarantee on this page comes from the type of the\nvalue itself.\n\n\n\n## Tokens name the parameters\n\n\n\nA token carries a **name** and a **formatter**. The name becomes the parameter\nkey; the formatter decides how the value is rendered in the active locale. That\nis why `msg` can derive the params type of a message from the tokens it\ninterpolates — see [Tokens](./tokens.md) for the full list.\n\n## `defineCatalog`, `msg`, `plural`\n\n\n\n`msg` is a **tagged template**: the literal parts are text, the interpolations\nare tokens. `` msg`Order total ${amount}.` `` has params `{ amount: number }`,\nand nothing else.\n\n`plural(count, branches)` takes the counting token and one message per category.\nWhich categories are _required_ is decided by the locale id, not by you:\n`defineLocale('pl-PL', …)` will not accept a plural missing `few` or `many`.\nThat check is a type error, before any Polish speaker sees the wrong branch.\n\nKeys nest as deeply as you like; the key used at the call site is the dotted\npath.\n\n## Every other locale is `defineLocaleLike`\n\n\n\n`defineLocale` is for the **reference** locale — the one that decides what the\nkey set is. Every other locale goes through `defineLocaleLike(reference, id,\ncatalog)`, which checks three things against the reference at compile time:\n\n- the same keys, no more and no fewer;\n- the same parameters on every message;\n- the plural categories that _this_ locale requires, which may differ from the\n reference's.\n\nA renamed key in the reference therefore breaks every translation file that\nstill has the old name, which is the entire point. It also runs\n`assertLocaleParity` at construction, so a mismatch that slips past the types —\na catalogue built dynamically, say — still fails loudly rather than rendering a\nkey name.\n\n## Checking outside the typechecker\n\n```bash\nnpm run i18n:check\n```\n\nRuns catalogue validation and locale parity as an ordinary command, so CI and a\npre-commit hook can see what `tsc` sees. Under the hood it is\n`validateCatalog` / `assertValidCatalog` (also exported from\n`@craft-ts/i18n/testing`) and `validateLocaleParity` / `assertLocaleParity`.\n\nWhen it fails, it names the key and the locale. Add the key; do not loosen the\ncatalogue's type to make the message go away.\n\n## Next\n\n- [Tokens](./tokens.md) — what `amount` and `count` above actually are.\n- [The runtime](./runtime.md) — turning these locales into a `t`.\n"
|
|
250
|
+
"body": "# The catalogue\n\nA catalogue is a plain nested object. Nothing is parsed, nothing is loaded from\nJSON at build time, and every guarantee on this page comes from the type of the\nvalue itself.\n\n\n\n## Tokens name the parameters\n\n\n\nA token carries a **name** and a **formatter**. The name becomes the parameter\nkey; the formatter decides how the value is rendered in the active locale. That\nis why `msg` can derive the params type of a message from the tokens it\ninterpolates — see [Tokens](./tokens.md) for the full list.\n\n## `defineCatalog`, `msg`, `plural`\n\n\n\n`msg` is a **tagged template**: the literal parts are text, the interpolations\nare tokens. `` msg`Order total ${amount}.` `` has params `{ amount: number }`,\nand nothing else.\n\n`plural(count, branches)` takes the counting token and one message per category.\nWhich categories are _required_ is decided by the locale id, not by you:\n`defineLocale('pl-PL', …)` will not accept a plural missing `few` or `many`.\nThat check is a type error, before any Polish speaker sees the wrong branch.\n\nKeys nest as deeply as you like; the key used at the call site is the dotted\npath.\n\n## Every other locale is `defineLocaleLike`\n\n\n\n`defineLocale` is for the **reference** locale — the one that decides what the\nkey set is. Every other locale goes through `defineLocaleLike(reference, id,\ncatalog)`, which checks three things against the reference at compile time:\n\n- the same keys, no more and no fewer;\n- the same parameters on every message;\n- the plural categories that _this_ locale requires, which may differ from the\n reference's.\n\n`assertLocaleParity` adds one check the types cannot express: two locales must\nalso agree on **how** each token is resolved. A locale that swapped a\nservice-resolved money token for a static one, or dropped a parameter's schema,\nrenders through a different path — it is reported as a `LOCALE_MISMATCH` rather\nthan silently formatting in the wrong currency.\n\nA renamed key in the reference therefore breaks every translation file that\nstill has the old name, which is the entire point. It also runs\n`assertLocaleParity` at construction, so a mismatch that slips past the types —\na catalogue built dynamically, say — still fails loudly rather than rendering a\nkey name.\n\n## Checking outside the typechecker\n\n```bash\nnpm run i18n:check\n```\n\nRuns catalogue validation and locale parity as an ordinary command, so CI and a\npre-commit hook can see what `tsc` sees. Under the hood it is\n`validateCatalog` / `assertValidCatalog` (also exported from\n`@craft-ts/i18n/testing`) and `validateLocaleParity` / `assertLocaleParity`.\n\nWhen it fails, it names the key and the locale. Add the key; do not loosen the\ncatalogue's type to make the message go away.\n\n## Delivering a catalogue as data\n\n`serializeCatalog(catalog)` produces a JSON-safe shape where each token is\nreduced to its stable `tokenId` and parameter name — the receiving application\nregisters the executable formatters. A token that parses its input is marked, and\na token whose formatter is resolved from the injector is **refused**: its\nformatter only exists at render time, so a serialised copy would silently format\nwith the default options. Deliver such a message from the application instead.\n\n## Next\n\n- [Tokens](./tokens.md) — what `amount` and `count` above actually are.\n- [The runtime](./runtime.md) — turning these locales into a `t`.\n"
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
253
|
"path": "/guide/i18n/effect",
|
|
254
254
|
"title": "i18n with Effect",
|
|
255
|
-
"body": "# i18n with Effect\n\n`@craft-ts/i18n-effect` is an **adapter, and only an adapter**. It exposes three\nthings — a service tag, a `Layer`, and one function — over a runtime you built\nthe ordinary way. `@craft-ts/i18n` itself never imports Effect, and plain\ncomponent code should keep calling `t` directly.\n\n## The Layer\n\n\n\n`provideI18nRuntime(runtime)` returns `Layer.Layer<I18nEffectService>`. It wraps\nthe runtime you already have, so there is exactly one active locale in the\nprocess — the Effect side does not get its own.\n\n## Bind the locales once\n\n`translateEffect` has no value parameter carrying the locales, so TypeScript has\nnothing to infer them from. Called bare, its key parameter resolves to `never`\nand **even a valid key is rejected**. Bind them once, in the same file as the\nLayer:\n\n\n\nFrom there, `t` has the closed key union and the typed params back. Passing the\ntype arguments at every call site —\n`translateEffect<typeof locales, 'order.total'>(…)` — works too, and is what\nthis wrapper spares you.\n\n## `translateEffect`\n\n\n\nThe signature is `translateEffect(key, params) =>\nEffect.Effect<string, never, I18nEffectService>`. Same closed key union, same\ntyped params, same string as `runtime.t` — the snippet above is checked against\n`runtime.t` in the docs test suite rather than trusted.\n\nThe error channel is `never` on purpose: a translation that reaches this point\ncannot fail on a bad key or a bad parameter, because neither compiles. What\n_can_ fail is the locale not being loaded, and that is a defect in the app's\nstartup, which is why it throws `I18nRuntimeError` rather than becoming a typed\nfailure every call site would have to handle.\n\n## When to reach for it\n\nUse `translateEffect` **inside an Effect program** — a domain service building a\nmessage, a server handler rendering an email. In a component,
|
|
255
|
+
"body": "# i18n with Effect\n\n`@craft-ts/i18n-effect` is an **adapter, and only an adapter**. It exposes three\nthings — a service tag, a `Layer`, and one function — over a runtime you built\nthe ordinary way. `@craft-ts/i18n` itself never imports Effect, and plain\ncomponent code should keep calling `t` directly.\n\n## The Layer\n\n\n\n`provideI18nRuntime(runtime)` returns `Layer.Layer<I18nEffectService>`. It wraps\nthe runtime you already have, so there is exactly one active locale in the\nprocess — the Effect side does not get its own.\n\n## Bind the locales once\n\n`translateEffect` has no value parameter carrying the locales, so TypeScript has\nnothing to infer them from. Called bare, its key parameter resolves to `never`\nand **even a valid key is rejected**. Bind them once, in the same file as the\nLayer:\n\n\n\nFrom there, `t` has the closed key union and the typed params back. Passing the\ntype arguments at every call site —\n`translateEffect<typeof locales, 'order.total'>(…)` — works too, and is what\nthis wrapper spares you.\n\n## `translateEffect`\n\n\n\nThe signature is `translateEffect(key, params) =>\nEffect.Effect<string, never, I18nEffectService>`. Same closed key union, same\ntyped params, same string as `runtime.t` — the snippet above is checked against\n`runtime.t` in the docs test suite rather than trusted.\n\nThe error channel is `never` on purpose: a translation that reaches this point\ncannot fail on a bad key or a bad parameter, because neither compiles. What\n_can_ fail is the locale not being loaded, and that is a defect in the app's\nstartup, which is why it throws `I18nRuntimeError` rather than becoming a typed\nfailure every call site would have to handle.\n\n## When to reach for it\n\nUse `translateEffect` **inside an Effect program** — a domain service building a\nmessage, a server handler rendering an email. In a component, the bound\ntranslator is the shorter path, and reaching for Effect just to format a string\nadds a requirement to the program for nothing.\n\nAn Effect program is not a Craft injection context, so `translateEffect` accepts\n`StaticTranslationKey` — the keys whose formatting resolves no service. A\nmessage whose token yields a Craft service is rendered by the component-side\ntranslator; that is what keeps the `never` error channel above honest.\n\nSee also the [Effect adapters](../advanced/effect.md) page for the rest of the\n`@craft-ts/*-effect` family.\n"
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
258
|
"path": "/guide/i18n/runtime",
|
|
259
259
|
"title": "The runtime",
|
|
260
|
-
"body": "# The runtime\n\n`createI18nRuntime` turns a set of locales into the object the application\ntranslates through. It holds one active locale, and it is deliberately small:\n`locale`, `setLocale`, `translate` (aliased `t`), `bind`, `loadLocale`.\n\n\n\n`strict` defaults to **on**. At construction, every catalogue is validated and\nevery locale is checked for parity against the first one — so a catalogue built\nin a way the types could not see still fails at startup rather than at the\nmoment a user opens the page that needs it. Pass `strict: false` only when you\nhave a reason you can write down.\n\n`timeZone` belongs on the runtime, once. Putting it on each call site is how two\ndates in the same view end up in two zones.\n\n## Translating\n\n\n\n`t` **is** `translate` — the same function under two names, so a call site can\nread as `t('order.total', …)` without a local alias. The params argument is\noptional exactly when the message has no parameters, and required, with its\nexact shape, when it does.\n\n`setLocale(id)` throws `I18nRuntimeError` with the code `LOCALE_NOT_LOADED` for\na locale the runtime does not hold. So does `t`, if the active locale was\nsomehow never loaded. The error is not a formatting failure to be swallowed: it\nmeans the app is about to render the wrong language.\n\n## Reactive translation\n\nA string that does not change when the locale changes is not a translation.\n`runtime.bind(dependency)` returns a translator whose result re-reads whenever\nthe dependency does — the dependency being an ordinary Craft reader, typically\nthe `state` that holds the active locale:\n\n\n\n`translate('order.items', { count })` then returns a generator the template\nyields like any other Craft reader. One service owns the locale for the whole\napp; components consume it rather than each building a local binding, which is\nwhat keeps two components from disagreeing about which language is on screen.\n\n## Loading catalogues\n\n\n\n`createI18nLoader` caches by id and — the part that matters — **evicts a failed\nload**, so a catalogue whose chunk died on a flaky network can be retried\ninstead of staying permanently poisoned. `loadLocale(id)` resolves once the\ncatalogue is in; only then does `setLocale` accept it.\n\n::: warning A locale must be listed to be named\n`setLocale` and `loadLocale` are keyed on the ids in `locales`, so today a\nlocale that is **not** in that array cannot be named without a cast — while a\nlocale that _is_ in it counts as already loaded and never reaches the loader.\nIn practice that means the fully lazy catalogue is not expressible in the types\nyet. List every locale, and treat `loader` as the retry-safe cache in front of\nwhatever your own loading code does.\n:::\n\nA lazily obtained locale is not present at construction, so it is **not**\ncovered by the startup parity check. Keep it covered by `npm run i18n:check`,\nwhich reads the files rather than the runtime.\n\n## Next\n\n- [With Effect](./effect.md) — the same keys, as an `Effect`.\n"
|
|
260
|
+
"body": "# The runtime\n\n`createI18nRuntime` turns a set of locales into the object the application\ntranslates through. It holds one active locale, and it is deliberately small:\n`locale`, `setLocale`, `translate` (aliased `t`), `bind`, `loadLocale`.\n\n\n\n`strict` defaults to **on**. At construction, every catalogue is validated and\nevery locale is checked for parity against the first one — so a catalogue built\nin a way the types could not see still fails at startup rather than at the\nmoment a user opens the page that needs it. Pass `strict: false` only when you\nhave a reason you can write down.\n\n`timeZone` belongs on the runtime, once. Putting it on each call site is how two\ndates in the same view end up in two zones.\n\n## Translating\n\n\n\n`t` **is** `translate` — the same function under two names, so a call site can\nread as `t('order.total', …)` without a local alias. The params argument is\noptional exactly when the message has no parameters, and required, with its\nexact shape, when it does.\n\n`setLocale(id)` throws `I18nRuntimeError` with the code `LOCALE_NOT_LOADED` for\na locale the runtime does not hold. So does `t`, if the active locale was\nsomehow never loaded. The error is not a formatting failure to be swallowed: it\nmeans the app is about to render the wrong language.\n\nThe other codes it raises, all for the same reason — rendering something wrong\nis worse than not rendering:\n\n| code | when |\n| -------------------------- | ------------------------------------------------------------------ |\n| `MISSING_PARAM` | a token's parameter is absent from the params object |\n| `INVALID_PARAM` | a guard rejected the value, or a schema's issues, quoted verbatim |\n| `ASYNC_SCHEMA` | a parameter's schema returned a promise; a message renders in sync |\n| `CRAFT_INJECTION_REQUIRED` | `t` met a token that resolves a service (see below) |\n| `INVALID_PLURAL_COUNT` | a plural selector that is not a finite number |\n| `UNKNOWN_KEY` | a key that no longer exists in the loaded catalogue |\n\n## Reactive translation\n\nA string that does not change when the locale changes is not a translation.\n`runtime.bind(dependency)` returns a translator whose result re-reads whenever\nthe dependency does — the dependency being an ordinary Craft reader, typically\nthe `state` that holds the active locale:\n\n\n\n`translate('order.items', { count })` then returns a generator the template\nyields like any other Craft reader. One service owns the locale for the whole\napp; components consume it rather than each building a local binding, which is\nwhat keeps two components from disagreeing about which language is on screen.\n\n### DI inside a translation\n\nDependencies belong to the token that needs them, not to the whole i18n\nruntime:\n\n```ts\nconst orderAmount = money('amount', function* () {\n const currency = yield* ClientCurrency();\n return { currency: currency.code, minimumFractionDigits: 2 };\n});\n\nconst catalog = defineCatalog({\n order: msg`Order total ${orderAmount}.`,\n});\n```\n\nIn a template, the translator's result is used exactly like any other child or\nattribute value — pass it, do not drive it:\n\n```ts\np(translate('order', { amount: 1234.5 }));\np({ title: translate('order', { amount: 1234.5 }) }, 'Order');\n```\n\nBoth forms carry `ClientCurrency` into the component dependency contract, so\nthe route check reports a missing provider at compile time, just as it does for\na service yielded by the component factory.\n\nThe reader is a function, so `yield* translate(...)` does not type-check; and\ndriving it yourself inside a template generator (`yield* translate(...)()`)\nhides the dependency from that check, exactly as a service yielded straight\nfrom a template does. Pass the reader.\n\n`t` refuses such a message at compile time: its key type is\n`StaticTranslationKey`, the keys whose formatting resolves nothing. A resolver\nthat yields no request is still a `t` key — the type and the runtime draw the\nsame line.\n\n## Loading catalogues\n\n\n\n`createI18nLoader` caches by id and — the part that matters — **evicts a failed\nload**, so a catalogue whose chunk died on a flaky network can be retried\ninstead of staying permanently poisoned. `loadLocale(id)` resolves once the\ncatalogue is in; only then does `setLocale` accept it.\n\n::: warning A locale must be listed to be named\n`setLocale` and `loadLocale` are keyed on the ids in `locales`, so today a\nlocale that is **not** in that array cannot be named without a cast — while a\nlocale that _is_ in it counts as already loaded and never reaches the loader.\nIn practice that means the fully lazy catalogue is not expressible in the types\nyet. List every locale, and treat `loader` as the retry-safe cache in front of\nwhatever your own loading code does.\n:::\n\nA lazily obtained locale is not present at construction, so it is **not**\ncovered by the startup parity check. Keep it covered by `npm run i18n:check`,\nwhich reads the files rather than the runtime.\n\n## Next\n\n- [With Effect](./effect.md) — the same keys, as an `Effect`.\n"
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"path": "/guide/i18n/tokens",
|
|
264
264
|
"title": "Tokens",
|
|
265
|
-
"body": "# Tokens\n\nA token is the unit that makes a message parameter typed. It carries a **name**\n(the parameter key), a **kind**,
|
|
265
|
+
"body": "# Tokens\n\nA token is the unit that makes a message parameter typed. It carries a **name**\n(the parameter key), a **kind**, a way to check the value — a guard or a\n**schema** — and a way to render it: a **formatter**, or a **resolver** that\nbuilds one from the injector. Both see the active locale.\n\n## The shipped tokens\n\nThey are semantic, not stylistic, and every one of them formats through `Intl`,\nso the output follows the locale rather than a hand-written rule:\n\n\n\n| factory | parameter type | formats as |\n| ------------------------------------ | ---------------- | ----------------------------- |\n| `number`, `integer`, `compactNumber` | `number` | decimal, no fraction, compact |\n| `percent` | `number` | `0.125` → `12.5 %` |\n| `money` | `number` | currency, `EUR` by default |\n| `dateShort`, `dateLong`, `dateTime` | `Date \\| number` | date and date-time styles |\n| `relativeTime` | `number` | `-2` → `2 days ago` |\n\nEach is a factory: `factory(name, adapter?, options?)`. The **name** is what the\nparams object will be keyed by, so the same factory serves any number of\nparameters — `money('amount')` and `money('refund')` are two different tokens.\nThe **adapter** position takes any of three things: a type guard, a\n[Standard Schema](#validating-and-parsing-with-a-schema), or a\n[generator](#options-that-come-from-a-service) that resolves the options from a\nservice.\n\n### Options that come from a service\n\nEvery factory — not only `money` — accepts a CraftTS generator in place of the\nadapter when its options depend on a service:\n\n```ts\nconst orderAmount = money('amount', function* () {\n const currency = yield* ClientCurrency();\n return { currency: currency.code, minimumFractionDigits: 2 };\n});\n```\n\nThe yielded service is part of the token's type. It is therefore propagated to\nthe translation reader and then to the component/route DI check. A provider\nmissing from the reachable `craftComponent`/route scope fails compilation.\nThe generator runs when the message is rendered, not when the catalogue module\nis imported.\n\nIt must be a **generator function**. An arrow that returns a generator satisfies\nthe signature but is not one, so it is rejected rather than silently installed\nas the value guard.\n\n## Validating and parsing with a schema\n\nThe second argument also accepts a **Standard Schema**: the same contract\n`state`, `query` and forms already take, so a Zod, Valibot or ArkType schema\nwritten for the rest of the application drops in unchanged.\n\n```ts\nconst placedAt = dateLong('placedAt', z.coerce.date());\n\nmsg`Placed on ${placedAt}.`;\n// the call site passes a string, the formatter receives a Date\ntranslate('order', { placedAt: '2026-08-25T14:30:00Z' });\n```\n\nA schema is not only a guard: the parameter type is the schema's **input** and\nthe formatter receives its **output**. Parsing therefore happens once, in the\ncatalogue, instead of at every call site. An invalid value raises\n`I18nRuntimeError` with the schema's own issue messages, and an asynchronous\nschema is refused — a translation renders synchronously.\n\nA project token declared with `defineToken` takes the same `schema` field, and\nmay combine it with `resolveFormatter`: the parameter is parsed, the formatter\nis resolved from the injector.\n\n## Your own token\n\nBusiness vocabulary does not belong in a shared library. `defineToken` builds\none, and it looks exactly like a shipped token at the call site:\n\n\n\nThe `validate` guard is what keeps an arbitrary string out of the params type: a\nmessage that interpolates this token accepts `'paid' | 'pending' | 'refunded'`\nand nothing else. Without it, the parameter widens and the token stops earning\nits place. A `schema` field does the same job and can parse on the way in;\n`defineToken` accepts either, and may combine a schema with a `resolveFormatter`\nso the parameter is parsed and the formatter comes from the injector.\n\nA token can also resolve its formatter from the injector rather than carry one.\nHere the unit system is a Craft service, so the same catalogue renders\nkilogrammes for one user and pounds for another:\n\n\n\nThe yielded service travels with the message: pass the reader to the template\nand a missing provider is a compile error, exactly as for a service yielded by\nthe component factory. See\n[DI inside a translation](./runtime.md#di-inside-a-translation).\n\n`format` receives the value and a context carrying `locale` and, when the\nruntime was given one, `timeZone`. Keep the branching on `context.locale`\ncoarse — a language prefix, not a full locale match — unless you genuinely have\nper-region wording.\n\n## `format` or `resolveFormatter`, never both\n\nA token formats through exactly one of the two, and both renderers take\n`resolveFormatter` first whenever it is there:\n\n| declared | when the formatter is known |\n| ------------------ | -------------------------------------------------------------- |\n| `format` | when the catalogue is written — `formatters.money('EUR')` |\n| `resolveFormatter` | at render time, from the injector — the client's currency |\n\nSo a token with a resolver declares no `format`. `t` still renders it if the\nresolver yields nothing; the moment it yields a service request, the message\nbelongs to a bound translator and its key leaves `StaticTranslationKey`.\n\n`percent` takes a ratio, not a percentage: `0.125`, not `12.5`. That is `Intl`'s\nconvention and the token does not second-guess it.\n\n## A family of tokens\n\nWhen the same formatting rule serves several parameter names and options,\n`defineTokenFactory` builds the factory instead of the token:\n\n\n\nThat is exactly how `number`, `money` and the rest are built; there is no\nprivileged path for the shipped ones.\n\nConventionally these live in `src/i18n/project-tokens.ts`, which is where\n`craft create` puts them and what the generated agent skill points at.\n\n## Next\n\n- [The runtime](./runtime.md) — spending a catalogue built from these.\n"
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
"path": "/guide/migration/wave-1-tag-and-provided-in",
|
|
@@ -712,7 +712,7 @@
|
|
|
712
712
|
{
|
|
713
713
|
"path": "/reference",
|
|
714
714
|
"title": "API index",
|
|
715
|
-
"body": "# API index\n\nEvery documented export, with the page that covers it. Use <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>F</kbd>.\n\nFor an explanation rather than a lookup, start from the [Guide](/guide/).\nCoding agents: [llms.txt](https://craft-ts.github.io/craft/llms.txt) and\n[coding agents](/resources/ai-agents).\n\n## Primitives\n\n| Symbol | What it does | Page |\n| ------------------- | -------------------------------------------------------- | --------------------------------------------- |\n| `state` | Signal-based state you own | [Local state](/guide/state/local-state) |\n| `craftStateMachine` | Declarative finite-state workflow | [State machines](/guide/state/state-machines) |\n| `query` | Server data, re-fetched from reactive `params` | [query](/guide/state/server-state) |\n| `mutation` | Server write, triggered explicitly | [Mutations](/guide/state/mutations) |\n| `queryParams` | State that lives in the URL query string | [queryParams](/guide/state/url-state) |\n| `asyncProcess` | One-off async operation with lifecycle state | [asyncProcess](/guide/state/async-process) |\n| `craftUse` | Drives a primitive outside a generator (component field) | [Learn 1](/learn/01-first-state) |\n\nNot sure which one: [Which primitive should I use?](/guide/concepts/choose-primitive)\n\n## Runtime context\n\nTyped helpers that recover `get` / `set` / `update` / `patch` from DI, for\nwrappers, WebMCP tools, and other advanced patterns. Everyday insertions\nalready receive those methods as arguments — see\n[Anatomy of a primitive](/guide/concepts/primitive-anatomy#injectable-runtime-context).\n\n| Symbol | What it does | Page |\n| ----------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| `injectStateMethodRuntimeContext` | `state` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectQueryMethodRuntimeContext` | `query` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectMutationMethodRuntimeContext` | `mutation` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectQueryParamsMethodRuntimeContext` | `queryParams` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectAsyncProcessMethodRuntimeContext` | `asyncProcess` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectPrimitiveMethodRuntimeContext` | Same context, untyped `kind` | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `providePrimitiveResourceRuntimeObserver` | Observes `query` / `mutation` / `asyncProcess` / `queryParams` values | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n\n## Composition\n\n| Symbol | What it does | Page |\n| ------------------------ | ----------------------------------------------- | -------------------------------------------------------- |\n| `craftPipe` | Composes several insertions into one | [Insertions](/guide/concepts/insertions) |\n| `craftYieldRecord` | Resolves a record of primitive generators | [craftService](/guide/app/craft-service) |\n| `insertStatePipe` | Composes several `state` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertQueryPipe` | Composes several `query` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertMutationPipe` | Composes several `mutation` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertQueryParamsPipe` | Composes several `queryParams` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertAsyncProcessPipe` | Composes several `asyncProcess` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertStateMachinePipe` | Composes several `craftStateMachine` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `craftGen` | A standalone tracked generator | [Generators](/guide/concepts/generators) |\n| `craftMatch` | Exhaustive pattern matching | [Pattern matching](/guide/advanced/pattern-matching) |\n| `.pipe(...)` | Program operators on a craft generator | [Program operators](/guide/advanced/program-operators) |\n| `catchTag`, `retry` | Operators for `.pipe(...)` | [Program operators](/guide/advanced/program-operators) |\n\n## Insertions\n\n| Symbol | What it does | Page |\n| --------------------------------- | ----------------------------------------------- | ------------------------------------------------------------- |\n| `insertSelect` | Derives a slice of a primitive | [Selecting](/guide/state/select) |\n| `insertEntities` | Entity collection storage and updates | [Collections](/guide/state/collections) |\n| `insertStoragePersister` | Persists through the configured storage backend | [Persistence](/guide/state/persistence) |\n| `insertReactOnMutation` | Reloads / optimistically patches on a mutation | [React on mutation](/guide/state/react-on-mutation) |\n| `insertPaginationPlaceholderData` | Placeholder rows while a page loads | [Pagination placeholder](/guide/state/pagination-placeholder) |\n\n## Forms\n\n| Symbol | What it does | Page |\n| --------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------- |\n| `insertForm` | Derives a form from a `state` | [Forms](/guide/forms/) |\n| `insertFormAttributes` | Validators, `disable`, `hidden` | [Forms](/guide/forms/) |\n| `insertSelectFormTree` | Targets a field sub-tree | [Nested forms](/guide/forms/nested) |\n| `insertSubFormField` | A nested sub-form | [Nested forms](/guide/forms/nested) |\n| `insertFormSubmit` | Wires submission to a mutation | [Submitting](/guide/forms/submit) |\n| `insertNoopTypingAnchor` | Type anchor required per field tree | [Forms](/guide/forms/) |\n| `CraftFieldDirective` | Binds a typed field to a Craft DOM node | [Forms](/guide/forms/) |\n| `fieldErrorNode.exhaustive` / `.partial` | Exhaustive or partial validation rendering | [Forms](/guide/forms/) |\n| `cRequired`, `cEmail`, `cMin`/`cMax`, `cMinLength`/`cMaxLength`, `cPattern` | Built-in validators | [Validators](/guide/forms/validation) |\n| `cValidate`, `cAsyncValidate` | Custom and async validators | [Validators](/guide/forms/validation) |\n\n## Services and DI\n\n| Symbol | What it does | Page |\n| --------------------------- | ------------------------------------------ | ------------------------------------------------- |\n| `craftService` | Declares a named, scoped service | [craftService](/guide/app/craft-service) |\n| `abstract` | Declares a contract with no implementation | [Abstract services](/guide/app/abstract-services) |\n| `X.OmitInputs` | Opts out of a service's input bindings | [Public API](/guide/app/expose-api) |\n| `onAppStart` | Startup callback owned by a service | [App start](/guide/app/app-start) |\n| `craftLazy` | Defers a service's instantiation | [Lazy services](/guide/app/lazy-services) |\n| `craftRegisterFor` | Registry-driven service resolution | [Register](/guide/app/register) |\n| `provideCraftTargetWrapper` | Wraps craft targets at a provider boundary | [Target wrapper](/guide/app/target-wrapper) |\n| `provideTemplateTrace` | Wraps effective template renders | [Observability](/guide/advanced/observability) |\n| `provideCraftRouterTrace` | Wraps Router events and Craft route stages | [Observability](/guide/advanced/observability) |\n| `provideCraftHttpTrace` | Wraps CraftHttpClient requests | [Observability](/guide/advanced/observability) |\n| `craftAppConfig` | Application config with the routing graph | [Routing setup](/guide/routing/setup) |\n\n## Routing\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- |\n| `craftRoute`, `craftRoutes` | Declares typed routes and collections | [Setup](/guide/routing/setup) |\n| `ValidateCascadeRoutesFile`, `CanRun` | Compile-time DI check for a routes file | [Setup](/guide/routing/setup) |\n| `RouteCheckedDI` | Per-route `O(1)` variant of the check | [Scaling routes](/guide/routing/scaling) |\n| `.withParent`, `ParentRoutes`, `assertChildRouteMounts` | Pins a child collection to its mount | [Scaling routes](/guide/routing/scaling) |\n| `withRetry` | Retryable lazy `loadComponent` / `loadChildren` | [Setup](/guide/routing/setup) |\n| `provideCraftRouter`, `provideCraftLoading` | Router with craft loading features | [Pending UI](/guide/routing/pending-ui) |\n| `withA11yNavigationFocus`, `CraftTitleStrategy` | Focus after nav; route `title` → document | [Accessibility](/guide/components/accessibility) |\n| `heading`, `headingSection`, `headingRoot`, `skipLink`, `liveRegion`, `fieldControl`, `disclosureControl`, `buttonControl`, `clickFocus` | Relative outline, skip link, live regions, accessible control props, focus | [Accessibility](/guide/components/accessibility) |\n| `withErrorComponent`, `withRouteLoadError`, `withTransitionTimings` | Router features | [Route load errors](/guide/routing/route-load-errors) |\n| `CraftRouterOutlet` | Non-blocking outlet | [Pending UI](/guide/routing/pending-ui) |\n| `craftRouterLink` | Type-safe navigation target | [Setup](/guide/routing/setup) |\n| `assertExhaustiveRouteExceptions` | Exhaustiveness proof for route exceptions | [Exceptions](/guide/concepts/exceptions) |\n\n## Server rendering\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------- |\n| `renderCraft`, `renderToString` | Renders an isolated request to HTML, CSS, and a transfer snapshot | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `startCraft` | Hydrates an SSR host or mounts a fresh client application automatically | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `hydrateCraft` | Restores transferred state and claims the existing browser DOM | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `pendingNode({ ssr })` | Declares `block`, `fallback`, or `client` behavior for suspended data | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `CRAFT_SSR_POLICY` | Route-level default SSR policy | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `CraftUnhandledSsrResolutionError`, `CraftSsrTimeoutError` | Reports missing policies and timed-out blocking sources | [SSR and hydration](/guide/advanced/ssr-hydration) |\n\n## Exceptions\n\n| Symbol | What it does | Page |\n| ---------------------------------- | ---------------------------------------- | --------------------------------------------------------------- |\n| `craftException` | Creates a declared, typed exception | [Exceptions](/guide/concepts/exceptions) |\n| `craftExceptionHandler` | Handles route exceptions | [Exceptions](/guide/concepts/exceptions) |\n| `.exceptions()`, `.hasException()` | Reads a primitive's exceptions by origin | [query](/guide/state/server-state) |\n| `globalError()` | Delegates to the global error component | [Global error component](/guide/routing/global-error-component) |\n\n## Reactivity\n\n| Symbol | What it does | Page |\n| -------------------- | ---------------------------------- | ------------------------------------------------------------ |\n| `craftComputed` | Tracked `computed` | [craftComputed](/guide/reactivity/craft-computed) |\n| `craftEffect` | Tracked `effect` | [craftEffect](/guide/reactivity/craft-effect) |\n| `craftMethod` | A tracked method on a primitive | [craftMethod](/guide/reactivity/craft-method) |\n| `source$` | An imperative event source | [source$](/guide/reactivity/source) |\n| `on$` | Binds a method to a source | [on$](/guide/reactivity/on) |\n| `fromEventToSource$` | DOM event → source | [fromEventToSource$](/guide/reactivity/from-event-to-source) |\n| `sourceFromEvent` | Event-driven source helper | [sourceFromEvent](/guide/reactivity/source-from-event) |\n| `afterRecomputation` | Runs after a recomputation settles | [afterRecomputation](/guide/reactivity/after-recomputation) |\n\n## HTTP and boundaries\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------- |\n| `CraftHttpClient` | Tracked HTTP client with typed exceptions | [query](/guide/state/server-state) |\n| `browserBoundary` | Marks a service as a browser boundary | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `BrowserDocument`, `BrowserDocument.setLang`, `BrowserDocument.setDir` | Reads and updates document title, language, and direction | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `Console` | Yieldable console, overridable for tracing | [Observability](/guide/advanced/observability) |\n\n## Testing\n\n| Symbol | What it does | Page |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------- |\n| `setupCraftServiceTestingByRegister` | Sets up a service from a full register | [Testing services](/guide/testing/services) |\n| `boundaryOnly` | Keeps the graph real, mocks boundaries | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `mockHttpRequestForRoute` | Mocks endpoints for a route | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `ComponentTemplateOf`, `ComponentLogicOutputOf`, `SetupTestComponentTemplate` | Resolves component logic and validates a template at compile time | [Type-level tests](/guide/testing/type-level) |\n| `TemplateHasElement`, `TemplateRendersNamedElementWhen`, `TemplateNamedElementRendersStateWhen`, `TemplateNamedElementDelegatesToContext`, `TemplateRenderAvailableActionWhen` | Proves what a template renders and uses | [Type-level tests](/guide/testing/type-level) |\n| `Expect`, `Equal` | Turns a type-level result into a compile-time assertion | [Type-level tests](/guide/testing/type-level) |\n| `createArchitectureGraph`, `noExclusiveLink`, `assertCraftUnique`, `assertHttpEndpointUnique`, `assertCraftComputedPure`, `assertNoDependencyCycles`, `assertDeclarativeArchitecture`, `assertRouteDiProofs`, `assertPathBoundaries`, `assertMutationHasReactOn`, `assertPrimitiveLoaderRequirements`, `assertQueryMutationHasServerState`, `assertPersistedPrimitiveHasUnique`, `assertInsertSelectUnique`, `assertCraftEffectNoNetwork`, `assertCraftEffectNoImperativeSync`, `assertInteractiveElementNamed` | Typed lookups and declarative architecture helpers | [Architecture rules](/guide/testing/architecture) |\n\n## Effect integration\n\n`@craft-ts/effect`, in full. The guide is [Effect\nintegration](/guide/advanced/effect).\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `installCraftEffectBridge` | Installs both bridges once, at bootstrap | [Install the bridge](/guide/advanced/effect#install-the-bridge-once) |\n| `queryEffect`, `mutationEffect`, `asyncProcessEffect`, `computedEffect`, `methodEffect` | The Effect-backed adapters of the Craft primitives | [Choose the right adapter](/guide/advanced/effect#choose-the-right-adapter) |\n| `runEffect`, `CraftEffectInterrupted` | Yields one Effect and maps its exit onto Craft's channels | [runEffect](/guide/advanced/effect#runeffect-the-low-level-form) |\n| `syncEffect`, `SyncOp`, `CraftEffectNotSynchronous`, `NotDeclaredSynchronous` | Declares and runs an Effect that never suspends | [Synchronous members](/guide/advanced/effect#run-a-synchronous-member-from-a-computed) |\n| `provideLayer` | Attaches a built Effect context to a Craft injector | [Provide services with Layer](/guide/advanced/effect#provide-services-with-layer) |\n| `effectService`, `SelectedMembers` | Selects a service from a Craft factory, recording the dependency | [Select a service](/guide/advanced/effect#select-an-effect-service-from-craft) |\n| `mockEffectService`, `UnstubbedEffectMember` | A focused Layer for tests; an unstubbed member fails loudly | [Testing](/guide/advanced/effect#testing) |\n| `EffectRequirementsCheckedDI`, `ProvidedEffectServicesOf`, `ProvidedEffectServicesOfRoute` | The route-level proof that every requirement is provided | [Provide services with Layer](/guide/advanced/effect#provide-services-with-layer) |\n| `effectServerMiddleware`, `executeEffect`, `EffectServerMiddleware`, `EffectServerMiddlewareContext` | Effect middleware and execution for server functions | [Server functions POC](/guide/advanced/effect#server-functions-current-poc) |\n\n### Lower-level exports\n\nPublic, but rarely needed directly. They exist for wrappers, generated code and\ntooling rather than for application code.\n\n| Symbol | What it is |\n| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `composeEffect` | Composes yieldable Effect middleware in declaration order, without continuations. `effectServerMiddleware` is the everyday door. |\n| `runYieldedEffect` | The single-Effect runner the bridge itself calls. Use `runEffect`, which keeps the call site blamable. |\n| `assertNoRequirements`, `AssertNoRequirements`, `MissingRequirements`, `RealRequirements`, `CraftPhantomRequirement` | Moves the `R = never` check to the **yield site**, so an unmet requirement points at the offending line instead of surfacing at runtime. `CraftPhantomRequirement` is what excludes `SyncOp` from that check. |\n| `CRAFT_EFFECT_LEVEL`, `resolveEffectLevel`, `CraftEffectLevel` | The per-injector Effect level: the built context, a `MemoMap` forked from the parent's, and a scope closed with the injector. Read it when writing your own provider; `provideLayer` is the normal way in. |\n| `AsEffect`, `CraftProgramSuccess`, `CraftProgramExceptions` | A **type-only projection** of a Craft program onto `Effect<A, E>`. It changes no runtime behaviour; it exists so a hover tooltip reads `Effect<User, UserNotFound>` instead of a raw generator type. |\n| `installCraftSyncEffectBridge` | Already installed by `installCraftEffectBridge`. Call it directly only in a host that installs the synchronous bridge alone. |\n\n## Typed styles\n\n`@craft-ts/style` is a **build step**: none of these symbols emit anything\nwithout `craftStyle` from `@craft-ts/style/vite` in the Vite config. See\n[Activating the style system](/guide/style/setup).\n\n| Symbol | What it does | Page |\n| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `craftStyle`, `emitStyles`, `renderCss`, `styleDump`, `findStyleModules` | The build-time emitter and its artefacts (`@craft-ts/style/vite`) | [Activating the style system](/guide/style/setup) |\n| `definePalette`, `darkOf`, `palette` | Colour tokens carrying both of their values, plus the default set | [Defining a design system](/guide/style/define) |\n| `defineBreakpoints`, `at`, `above`, `below` | The viewport axis, as an ordered one | [Defining a design system](/guide/style/define) |\n| `defineStateAxis`, `defineAxis`, `onlyVarsOfKind`, `axisPoint` | Attribute-driven axes, with an optional write constraint | [Defining a design system](/guide/style/define) |\n| `defineContainer` | A container axis, closed at the element that declares the container | [Defining a design system](/guide/style/define) |\n| `scheme`, `motion`, `forcedColors`, `contrast`, `scrollState`, `descendant` | The standard axes, driven by the user agent or by element state | [Axes and the matrix](/guide/style/variants) |\n| `cssVars`, `kind`, `assign`, `set` | Typed custom properties, registered through `@property` | [Tokens and variables](/guide/style/tokens) |\n| `space`, `unit`, `radii`, `radius`, `lineWidth`, `num`, `text`, `font` | The closed value scales — no value is a string | [Tokens and variables](/guide/style/tokens) |\n| `unsafeLength`, `unsafeAssume` | The marked escape hatches; both propagate `unproven` | [Tokens and variables](/guide/style/tokens) |\n| `craftStyles`, `when` | A sheet, and conjunction by nesting | [Axes and the matrix](/guide/style/variants) |\n| `requires`, `provides`, `declares`, `seal`, `scrollPort`, `noClipping`, `containerType`, `clipOverflow` | Context obligations, and where they become an error | [Context obligations](/guide/style/obligations) |\n| `visualMatrix`, `applyScenario`, `branch`, `contentCases`, `assertExhaustiveVisualMatrix`, `baselinesIn` | The scenario matrix (`@craft-ts/style-testing`) | [Testing visual states](/guide/style/testing) |\n| `matrixSizeByComponent`, `impactedClasses`, `varsWrittenBy`, `danglingVars`, `unproven`, `extractionGaps`, `undischargedObligations` | Graph queries over the style dump (`@craft-ts/dev-tools`) | [Testing visual states](/guide/style/testing) |\n| `style_impact`, `style_matrix`, `style_debt` | The same questions as MCP tools | [Testing visual states](/guide/style/testing#the-same-questions-from-an-agent) |\n\n## Internationalisation\n\n`@craft-ts/i18n` has no CraftTS, Angular or Effect import; the catalogue is a\nplain TypeScript value.\n\n| Symbol | What it does | Page |\n| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| `defineCatalog`, `msg`, `plural` | The catalogue, its messages, and per-locale plural categories | [The catalogue](/guide/i18n/catalog) |\n| `defineLocale`, `defineLocaleLike` | The reference locale, and every other one checked against it | [The catalogue](/guide/i18n/catalog) |\n| `number`, `integer`, `percent`, `compactNumber`, `money`, `dateShort`, `dateLong`, `dateTime`, `relativeTime` | The shipped semantic tokens, formatted through `Intl` | [Tokens](/guide/i18n/tokens) |\n| `defineToken`, `defineTokenFactory`, `formatters` | Project tokens, and the factory the shipped ones are built from | [Tokens](/guide/i18n/tokens) |\n| `createI18nRuntime`, `translate` / `t`, `setLocale`, `locale` | The runtime and its one active locale | [The runtime](/guide/i18n/runtime) |\n| `bind`, `createReactiveTranslator` | A translator that re-reads when the locale state changes | [The runtime](/guide/i18n/runtime#reactive-translation) |\n| `createI18nLoader`, `loadLocale` | Lazy locales, cached by id, evicted on failure | [The runtime](/guide/i18n/runtime#lazy-locales) |\n| `validateCatalog`, `assertValidCatalog`, `validateLocaleParity`, `assertLocaleParity` | The checks behind `npm run i18n:check` (also `@craft-ts/i18n/testing`) | [The catalogue](/guide/i18n/catalog#checking-outside-the-typechecker) |\n| `I18nRuntimeError` | `NO_LOCALES`, `LOCALE_NOT_LOADED`, `INVALID_NUMBER`, `INVALID_DATE` | [The runtime](/guide/i18n/runtime) |\n| `provideI18nRuntime`, `translateEffect`, `I18nEffectService` | The Effect adapter (`@craft-ts/i18n-effect`) | [With Effect](/guide/i18n/effect) |\n\n## Tooling\n\n| Command / rule | What it does | Page |\n| ---------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `npx craft route add` | Scaffolds a typed route | [Automation](/guide/routing/automation) |\n| `npx craft route split` | Splits a flat collection | [Scaling routes](/guide/routing/scaling) |\n| `npx craft route verify` | Optional compiler-fixture suite for the type machinery | [Automation](/guide/routing/automation#compiler-fixture-suite-optional) |\n| `craft-brand --root src` | Generates and refreshes `GenDeps_*` | [Brand config](/guide/routing/setup#generated-dependencies) |\n| `@craft-ts/dev-tools/eslint-rules` | The ESLint rule set | [ESLint rules](/guide/routing/eslint-rules) · [Accessibility](/guide/components/accessibility) |\n| `npx craft-graph` | Writes the static Craft graph | [Architecture rules](/guide/testing/architecture) · [Craft graph vs Nx](/guide/testing/craft-graph-vs-nx) |\n| `npx nx architecture <app>` | Runs the app's architecture Vitest suite | [Architecture rules](/guide/testing/architecture) · [Craft graph vs Nx](/guide/testing/craft-graph-vs-nx) |\n| Live page MCP `page` | Drive the open `ng serve` tab (dev only) | [Live page MCP](/guide/ai/dev-page) |\n| Template migrator | Migrates templates to craft components | [Template migrator](/guide/components/template-migrator) |\n\n## Deployment\n\n::: warning Experimental\nThe deployment tooling is not settled: these symbols and commands can still\nchange between minor versions. See the\n[deployment guide](/guide/deployment/) for what exists today.\n:::\n\n| Symbol / command | What it does | Page |\n| ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------ |\n| `defineCraftDeployment` | Declares the deployment of an application in `craft.deploy.ts` | [Manifest reference](/guide/deployment/manifest) |\n| `checkCraftDeployment`, `checkCraftDeploymentArtifact` | Runs the manifest, module graph and artefact checks | [Diagnostics](/guide/deployment/diagnostics) |\n| `resolveCraftDeploymentManifest`, `serializeCraftDeploymentManifest`, `parseCraftDeploymentManifest` | Resolves, writes and reads the provider-neutral artefact form | [Manifest reference](/guide/deployment/manifest) |\n| `CraftDeploymentProvider`, `CRAFT_DEPLOYMENT_PROVIDERS` | The provider contract and the capability matrix | [Providers](/guide/deployment/providers) |\n| `npx craft-ts check` | Validates a deployment before building | [Deployment overview](/guide/deployment/) |\n| `npx craft-ts manifest` | Writes `dist/<app>/craft-deployment-manifest.json` | [Deployment overview](/guide/deployment/) |\n| `npx craft-ts deploy preview` | Shows what a provider would change, without changing it | [Alchemy provider](/guide/deployment/alchemy) |\n| `npx craft-ts deploy` | Applies that plan once `--yes` approves it | [Alchemy provider](/guide/deployment/alchemy) |\n| `createCraftDeploymentProvider` | The single factory a provider package exports | [Providers](/guide/deployment/providers) |\n| `createAlchemyDeploymentProvider`, `planAlchemyDeployment` | The Alchemy provider and its Cloudflare/AWS planning | [Alchemy provider](/guide/deployment/alchemy) |\n| `npx craft-ts providers` | Prints the provider capability matrix | [Providers](/guide/deployment/providers) |\n"
|
|
715
|
+
"body": "# API index\n\nEvery documented export, with the page that covers it. Use <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>F</kbd>.\n\nFor an explanation rather than a lookup, start from the [Guide](/guide/).\nCoding agents: [llms.txt](https://craft-ts.github.io/craft/llms.txt) and\n[coding agents](/resources/ai-agents).\n\n## Primitives\n\n| Symbol | What it does | Page |\n| ------------------- | -------------------------------------------------------- | --------------------------------------------- |\n| `state` | Signal-based state you own | [Local state](/guide/state/local-state) |\n| `craftStateMachine` | Declarative finite-state workflow | [State machines](/guide/state/state-machines) |\n| `query` | Server data, re-fetched from reactive `params` | [query](/guide/state/server-state) |\n| `mutation` | Server write, triggered explicitly | [Mutations](/guide/state/mutations) |\n| `queryParams` | State that lives in the URL query string | [queryParams](/guide/state/url-state) |\n| `asyncProcess` | One-off async operation with lifecycle state | [asyncProcess](/guide/state/async-process) |\n| `craftUse` | Drives a primitive outside a generator (component field) | [Learn 1](/learn/01-first-state) |\n\nNot sure which one: [Which primitive should I use?](/guide/concepts/choose-primitive)\n\n## Runtime context\n\nTyped helpers that recover `get` / `set` / `update` / `patch` from DI, for\nwrappers, WebMCP tools, and other advanced patterns. Everyday insertions\nalready receive those methods as arguments — see\n[Anatomy of a primitive](/guide/concepts/primitive-anatomy#injectable-runtime-context).\n\n| Symbol | What it does | Page |\n| ----------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| `injectStateMethodRuntimeContext` | `state` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectQueryMethodRuntimeContext` | `query` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectMutationMethodRuntimeContext` | `mutation` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectQueryParamsMethodRuntimeContext` | `queryParams` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectAsyncProcessMethodRuntimeContext` | `asyncProcess` writes inside an insertion method | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `injectPrimitiveMethodRuntimeContext` | Same context, untyped `kind` | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n| `providePrimitiveResourceRuntimeObserver` | Observes `query` / `mutation` / `asyncProcess` / `queryParams` values | [Anatomy](/guide/concepts/primitive-anatomy#injectable-runtime-context) |\n\n## Composition\n\n| Symbol | What it does | Page |\n| ------------------------ | ----------------------------------------------- | -------------------------------------------------------- |\n| `craftPipe` | Composes several insertions into one | [Insertions](/guide/concepts/insertions) |\n| `craftYieldRecord` | Resolves a record of primitive generators | [craftService](/guide/app/craft-service) |\n| `insertStatePipe` | Composes several `state` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertQueryPipe` | Composes several `query` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertMutationPipe` | Composes several `mutation` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertQueryParamsPipe` | Composes several `queryParams` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertAsyncProcessPipe` | Composes several `asyncProcess` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `insertStateMachinePipe` | Composes several `craftStateMachine` insertions | [Typed insertion pipes](/guide/concepts/insertion-pipes) |\n| `craftGen` | A standalone tracked generator | [Generators](/guide/concepts/generators) |\n| `craftMatch` | Exhaustive pattern matching | [Pattern matching](/guide/advanced/pattern-matching) |\n| `.pipe(...)` | Program operators on a craft generator | [Program operators](/guide/advanced/program-operators) |\n| `catchTag`, `retry` | Operators for `.pipe(...)` | [Program operators](/guide/advanced/program-operators) |\n\n## Insertions\n\n| Symbol | What it does | Page |\n| --------------------------------- | ----------------------------------------------- | ------------------------------------------------------------- |\n| `insertSelect` | Derives a slice of a primitive | [Selecting](/guide/state/select) |\n| `insertEntities` | Entity collection storage and updates | [Collections](/guide/state/collections) |\n| `insertStoragePersister` | Persists through the configured storage backend | [Persistence](/guide/state/persistence) |\n| `insertReactOnMutation` | Reloads / optimistically patches on a mutation | [React on mutation](/guide/state/react-on-mutation) |\n| `insertPaginationPlaceholderData` | Placeholder rows while a page loads | [Pagination placeholder](/guide/state/pagination-placeholder) |\n\n## Forms\n\n| Symbol | What it does | Page |\n| --------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------- |\n| `insertForm` | Derives a form from a `state` | [Forms](/guide/forms/) |\n| `insertFormAttributes` | Validators, `disable`, `hidden` | [Forms](/guide/forms/) |\n| `insertSelectFormTree` | Targets a field sub-tree | [Nested forms](/guide/forms/nested) |\n| `insertSubFormField` | A nested sub-form | [Nested forms](/guide/forms/nested) |\n| `insertFormSubmit` | Wires submission to a mutation | [Submitting](/guide/forms/submit) |\n| `insertNoopTypingAnchor` | Type anchor required per field tree | [Forms](/guide/forms/) |\n| `CraftFieldDirective` | Binds a typed field to a Craft DOM node | [Forms](/guide/forms/) |\n| `fieldErrorNode.exhaustive` / `.partial` | Exhaustive or partial validation rendering | [Forms](/guide/forms/) |\n| `cRequired`, `cEmail`, `cMin`/`cMax`, `cMinLength`/`cMaxLength`, `cPattern` | Built-in validators | [Validators](/guide/forms/validation) |\n| `cValidate`, `cAsyncValidate` | Custom and async validators | [Validators](/guide/forms/validation) |\n\n## Services and DI\n\n| Symbol | What it does | Page |\n| --------------------------- | ------------------------------------------ | ------------------------------------------------- |\n| `craftService` | Declares a named, scoped service | [craftService](/guide/app/craft-service) |\n| `abstract` | Declares a contract with no implementation | [Abstract services](/guide/app/abstract-services) |\n| `X.OmitInputs` | Opts out of a service's input bindings | [Public API](/guide/app/expose-api) |\n| `onAppStart` | Startup callback owned by a service | [App start](/guide/app/app-start) |\n| `craftLazy` | Defers a service's instantiation | [Lazy services](/guide/app/lazy-services) |\n| `craftRegisterFor` | Registry-driven service resolution | [Register](/guide/app/register) |\n| `provideCraftTargetWrapper` | Wraps craft targets at a provider boundary | [Target wrapper](/guide/app/target-wrapper) |\n| `provideTemplateTrace` | Wraps effective template renders | [Observability](/guide/advanced/observability) |\n| `provideCraftRouterTrace` | Wraps Router events and Craft route stages | [Observability](/guide/advanced/observability) |\n| `provideCraftHttpTrace` | Wraps CraftHttpClient requests | [Observability](/guide/advanced/observability) |\n| `craftAppConfig` | Application config with the routing graph | [Routing setup](/guide/routing/setup) |\n\n## Routing\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- |\n| `craftRoute`, `craftRoutes` | Declares typed routes and collections | [Setup](/guide/routing/setup) |\n| `ValidateCascadeRoutesFile`, `CanRun` | Compile-time DI check for a routes file | [Setup](/guide/routing/setup) |\n| `RouteCheckedDI` | Per-route `O(1)` variant of the check | [Scaling routes](/guide/routing/scaling) |\n| `.withParent`, `ParentRoutes`, `assertChildRouteMounts` | Pins a child collection to its mount | [Scaling routes](/guide/routing/scaling) |\n| `withRetry` | Retryable lazy `loadComponent` / `loadChildren` | [Setup](/guide/routing/setup) |\n| `provideCraftRouter`, `provideCraftLoading` | Router with craft loading features | [Pending UI](/guide/routing/pending-ui) |\n| `withA11yNavigationFocus`, `CraftTitleStrategy` | Focus after nav; route `title` → document | [Accessibility](/guide/components/accessibility) |\n| `heading`, `headingSection`, `headingRoot`, `skipLink`, `liveRegion`, `fieldControl`, `disclosureControl`, `buttonControl`, `clickFocus` | Relative outline, skip link, live regions, accessible control props, focus | [Accessibility](/guide/components/accessibility) |\n| `withErrorComponent`, `withRouteLoadError`, `withTransitionTimings` | Router features | [Route load errors](/guide/routing/route-load-errors) |\n| `CraftRouterOutlet` | Non-blocking outlet | [Pending UI](/guide/routing/pending-ui) |\n| `craftRouterLink` | Type-safe navigation target | [Setup](/guide/routing/setup) |\n| `assertExhaustiveRouteExceptions` | Exhaustiveness proof for route exceptions | [Exceptions](/guide/concepts/exceptions) |\n\n## Server rendering\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------- |\n| `renderCraft`, `renderToString` | Renders an isolated request to HTML, CSS, and a transfer snapshot | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `startCraft` | Hydrates an SSR host or mounts a fresh client application automatically | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `hydrateCraft` | Restores transferred state and claims the existing browser DOM | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `pendingNode({ ssr })` | Declares `block`, `fallback`, or `client` behavior for suspended data | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `CRAFT_SSR_POLICY` | Route-level default SSR policy | [SSR and hydration](/guide/advanced/ssr-hydration) |\n| `CraftUnhandledSsrResolutionError`, `CraftSsrTimeoutError` | Reports missing policies and timed-out blocking sources | [SSR and hydration](/guide/advanced/ssr-hydration) |\n\n## Exceptions\n\n| Symbol | What it does | Page |\n| ---------------------------------- | ---------------------------------------- | --------------------------------------------------------------- |\n| `craftException` | Creates a declared, typed exception | [Exceptions](/guide/concepts/exceptions) |\n| `craftExceptionHandler` | Handles route exceptions | [Exceptions](/guide/concepts/exceptions) |\n| `.exceptions()`, `.hasException()` | Reads a primitive's exceptions by origin | [query](/guide/state/server-state) |\n| `globalError()` | Delegates to the global error component | [Global error component](/guide/routing/global-error-component) |\n\n## Reactivity\n\n| Symbol | What it does | Page |\n| -------------------- | ---------------------------------- | ------------------------------------------------------------ |\n| `craftComputed` | Tracked `computed` | [craftComputed](/guide/reactivity/craft-computed) |\n| `craftEffect` | Tracked `effect` | [craftEffect](/guide/reactivity/craft-effect) |\n| `craftMethod` | A tracked method on a primitive | [craftMethod](/guide/reactivity/craft-method) |\n| `source$` | An imperative event source | [source$](/guide/reactivity/source) |\n| `on$` | Binds a method to a source | [on$](/guide/reactivity/on) |\n| `fromEventToSource$` | DOM event → source | [fromEventToSource$](/guide/reactivity/from-event-to-source) |\n| `sourceFromEvent` | Event-driven source helper | [sourceFromEvent](/guide/reactivity/source-from-event) |\n| `afterRecomputation` | Runs after a recomputation settles | [afterRecomputation](/guide/reactivity/after-recomputation) |\n\n## HTTP and boundaries\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------- |\n| `CraftHttpClient` | Tracked HTTP client with typed exceptions | [query](/guide/state/server-state) |\n| `browserBoundary` | Marks a service as a browser boundary | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `BrowserDocument`, `BrowserDocument.setLang`, `BrowserDocument.setDir` | Reads and updates document title, language, and direction | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `Console` | Yieldable console, overridable for tracing | [Observability](/guide/advanced/observability) |\n\n## Testing\n\n| Symbol | What it does | Page |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------- |\n| `setupCraftServiceTestingByRegister` | Sets up a service from a full register | [Testing services](/guide/testing/services) |\n| `boundaryOnly` | Keeps the graph real, mocks boundaries | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `mockHttpRequestForRoute` | Mocks endpoints for a route | [Browser boundaries](/guide/testing/browser-boundaries) |\n| `ComponentTemplateOf`, `ComponentLogicOutputOf`, `SetupTestComponentTemplate` | Resolves component logic and validates a template at compile time | [Type-level tests](/guide/testing/type-level) |\n| `TemplateHasElement`, `TemplateRendersNamedElementWhen`, `TemplateNamedElementRendersStateWhen`, `TemplateNamedElementDelegatesToContext`, `TemplateRenderAvailableActionWhen` | Proves what a template renders and uses | [Type-level tests](/guide/testing/type-level) |\n| `Expect`, `Equal` | Turns a type-level result into a compile-time assertion | [Type-level tests](/guide/testing/type-level) |\n| `createArchitectureGraph`, `noExclusiveLink`, `assertCraftUnique`, `assertHttpEndpointUnique`, `assertCraftComputedPure`, `assertNoDependencyCycles`, `assertDeclarativeArchitecture`, `assertRouteDiProofs`, `assertPathBoundaries`, `assertMutationHasReactOn`, `assertPrimitiveLoaderRequirements`, `assertQueryMutationHasServerState`, `assertPersistedPrimitiveHasUnique`, `assertInsertSelectUnique`, `assertCraftEffectNoNetwork`, `assertCraftEffectNoImperativeSync`, `assertInteractiveElementNamed` | Typed lookups and declarative architecture helpers | [Architecture rules](/guide/testing/architecture) |\n\n## Effect integration\n\n`@craft-ts/effect`, in full. The guide is [Effect\nintegration](/guide/advanced/effect).\n\n| Symbol | What it does | Page |\n| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `installCraftEffectBridge` | Installs both bridges once, at bootstrap | [Install the bridge](/guide/advanced/effect#install-the-bridge-once) |\n| `queryEffect`, `mutationEffect`, `asyncProcessEffect`, `computedEffect`, `methodEffect` | The Effect-backed adapters of the Craft primitives | [Choose the right adapter](/guide/advanced/effect#choose-the-right-adapter) |\n| `runEffect`, `CraftEffectInterrupted` | Yields one Effect and maps its exit onto Craft's channels | [runEffect](/guide/advanced/effect#runeffect-the-low-level-form) |\n| `syncEffect`, `SyncOp`, `CraftEffectNotSynchronous`, `NotDeclaredSynchronous` | Declares and runs an Effect that never suspends | [Synchronous members](/guide/advanced/effect#run-a-synchronous-member-from-a-computed) |\n| `provideLayer` | Attaches a built Effect context to a Craft injector | [Provide services with Layer](/guide/advanced/effect#provide-services-with-layer) |\n| `effectService`, `SelectedMembers` | Selects a service from a Craft factory, recording the dependency | [Select a service](/guide/advanced/effect#select-an-effect-service-from-craft) |\n| `mockEffectService`, `UnstubbedEffectMember` | A focused Layer for tests; an unstubbed member fails loudly | [Testing](/guide/advanced/effect#testing) |\n| `EffectRequirementsCheckedDI`, `ProvidedEffectServicesOf`, `ProvidedEffectServicesOfRoute` | The route-level proof that every requirement is provided | [Provide services with Layer](/guide/advanced/effect#provide-services-with-layer) |\n| `effectServerMiddleware`, `executeEffect`, `EffectServerMiddleware`, `EffectServerMiddlewareContext` | Effect middleware and execution for server functions | [Server functions POC](/guide/advanced/effect#server-functions-current-poc) |\n\n### Lower-level exports\n\nPublic, but rarely needed directly. They exist for wrappers, generated code and\ntooling rather than for application code.\n\n| Symbol | What it is |\n| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `composeEffect` | Composes yieldable Effect middleware in declaration order, without continuations. `effectServerMiddleware` is the everyday door. |\n| `runYieldedEffect` | The single-Effect runner the bridge itself calls. Use `runEffect`, which keeps the call site blamable. |\n| `assertNoRequirements`, `AssertNoRequirements`, `MissingRequirements`, `RealRequirements`, `CraftPhantomRequirement` | Moves the `R = never` check to the **yield site**, so an unmet requirement points at the offending line instead of surfacing at runtime. `CraftPhantomRequirement` is what excludes `SyncOp` from that check. |\n| `CRAFT_EFFECT_LEVEL`, `resolveEffectLevel`, `CraftEffectLevel` | The per-injector Effect level: the built context, a `MemoMap` forked from the parent's, and a scope closed with the injector. Read it when writing your own provider; `provideLayer` is the normal way in. |\n| `AsEffect`, `CraftProgramSuccess`, `CraftProgramExceptions` | A **type-only projection** of a Craft program onto `Effect<A, E>`. It changes no runtime behaviour; it exists so a hover tooltip reads `Effect<User, UserNotFound>` instead of a raw generator type. |\n| `installCraftSyncEffectBridge` | Already installed by `installCraftEffectBridge`. Call it directly only in a host that installs the synchronous bridge alone. |\n\n## Typed styles\n\n`@craft-ts/style` is a **build step**: none of these symbols emit anything\nwithout `craftStyle` from `@craft-ts/style/vite` in the Vite config. See\n[Activating the style system](/guide/style/setup).\n\n| Symbol | What it does | Page |\n| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `craftStyle`, `emitStyles`, `renderCss`, `styleDump`, `findStyleModules` | The build-time emitter and its artefacts (`@craft-ts/style/vite`) | [Activating the style system](/guide/style/setup) |\n| `definePalette`, `darkOf`, `palette` | Colour tokens carrying both of their values, plus the default set | [Defining a design system](/guide/style/define) |\n| `defineBreakpoints`, `at`, `above`, `below` | The viewport axis, as an ordered one | [Defining a design system](/guide/style/define) |\n| `defineStateAxis`, `defineAxis`, `onlyVarsOfKind`, `axisPoint` | Attribute-driven axes, with an optional write constraint | [Defining a design system](/guide/style/define) |\n| `defineContainer` | A container axis, closed at the element that declares the container | [Defining a design system](/guide/style/define) |\n| `scheme`, `motion`, `forcedColors`, `contrast`, `scrollState`, `descendant` | The standard axes, driven by the user agent or by element state | [Axes and the matrix](/guide/style/variants) |\n| `cssVars`, `kind`, `assign`, `set` | Typed custom properties, registered through `@property` | [Tokens and variables](/guide/style/tokens) |\n| `space`, `unit`, `radii`, `radius`, `lineWidth`, `num`, `text`, `font` | The closed value scales — no value is a string | [Tokens and variables](/guide/style/tokens) |\n| `unsafeLength`, `unsafeAssume` | The marked escape hatches; both propagate `unproven` | [Tokens and variables](/guide/style/tokens) |\n| `craftStyles`, `when` | A sheet, and conjunction by nesting | [Axes and the matrix](/guide/style/variants) |\n| `requires`, `provides`, `declares`, `seal`, `scrollPort`, `noClipping`, `containerType`, `clipOverflow` | Context obligations, and where they become an error | [Context obligations](/guide/style/obligations) |\n| `visualMatrix`, `applyScenario`, `branch`, `contentCases`, `assertExhaustiveVisualMatrix`, `baselinesIn` | The scenario matrix (`@craft-ts/style-testing`) | [Testing visual states](/guide/style/testing) |\n| `matrixSizeByComponent`, `impactedClasses`, `varsWrittenBy`, `danglingVars`, `unproven`, `extractionGaps`, `undischargedObligations` | Graph queries over the style dump (`@craft-ts/dev-tools`) | [Testing visual states](/guide/style/testing) |\n| `style_impact`, `style_matrix`, `style_debt` | The same questions as MCP tools | [Testing visual states](/guide/style/testing#the-same-questions-from-an-agent) |\n\n## Internationalisation\n\n`@craft-ts/i18n` is the CraftTS i18n integration: the catalogue stays a plain\nTypeScript value, and a token may resolve a Craft service or parse its\nparameter with a Standard Schema. The package imports core for types only.\n\n| Symbol | What it does | Page |\n| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| `defineCatalog`, `msg`, `plural` | The catalogue, its messages, and per-locale plural categories | [The catalogue](/guide/i18n/catalog) |\n| `defineLocale`, `defineLocaleLike` | The reference locale, and every other one checked against it | [The catalogue](/guide/i18n/catalog) |\n| `number`, `integer`, `percent`, `compactNumber`, `money`, `dateShort`, `dateLong`, `dateTime`, `relativeTime` | The shipped semantic tokens, formatted through `Intl` | [Tokens](/guide/i18n/tokens) |\n| `defineToken`, `defineTokenFactory`, `formatters`, `TokenFormatter`, `FormatterContext` | Project tokens, and the factory the shipped ones are built from | [Tokens](/guide/i18n/tokens) |\n| `createI18nRuntime`, `translate` / `t`, `setLocale`, `locale` | The runtime and its one active locale | [The runtime](/guide/i18n/runtime) |\n| `TranslationDependencies`, `StaticTranslationKey` | The services a message resolves, and the keys `t` can render alone | [The runtime](/guide/i18n/runtime#di-inside-a-translation) |\n| `TokenSchema`, `TokenSchemaInput`, `TokenSchemaOutput`, `TokenFactory` | Declaring a parameter with a Standard Schema | [Tokens](/guide/i18n/tokens) |\n| `bind`, `createReactiveTranslator` | A translator that re-reads when the locale state changes | [The runtime](/guide/i18n/runtime#reactive-translation) |\n| `createI18nLoader`, `loadLocale` | Lazy locales, cached by id, evicted on failure | [The runtime](/guide/i18n/runtime#lazy-locales) |\n| `validateCatalog`, `assertValidCatalog`, `validateLocaleParity`, `assertLocaleParity` | The checks behind `npm run i18n:check` (also `@craft-ts/i18n/testing`) | [The catalogue](/guide/i18n/catalog#checking-outside-the-typechecker) |\n| `serializeCatalog`, `serializeToken` | JSON-safe delivery shape; refuses a token that resolves a service | [The catalogue](/guide/i18n/catalog) |\n| `I18nRuntimeError` | `LOCALE_NOT_LOADED`, `MISSING_PARAM`, `INVALID_PARAM`, `CRAFT_INJECTION_REQUIRED`, … | [The runtime](/guide/i18n/runtime) |\n| `provideI18nRuntime`, `translateEffect`, `I18nEffectService` | The Effect adapter (`@craft-ts/i18n-effect`) | [With Effect](/guide/i18n/effect) |\n\n## Tooling\n\n| Command / rule | What it does | Page |\n| ---------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `npx craft route add` | Scaffolds a typed route | [Automation](/guide/routing/automation) |\n| `npx craft route split` | Splits a flat collection | [Scaling routes](/guide/routing/scaling) |\n| `npx craft route verify` | Optional compiler-fixture suite for the type machinery | [Automation](/guide/routing/automation#compiler-fixture-suite-optional) |\n| `craft-brand --root src` | Generates and refreshes `GenDeps_*` | [Brand config](/guide/routing/setup#generated-dependencies) |\n| `@craft-ts/dev-tools/eslint-rules` | The ESLint rule set | [ESLint rules](/guide/routing/eslint-rules) · [Accessibility](/guide/components/accessibility) |\n| `npx craft-graph` | Writes the static Craft graph | [Architecture rules](/guide/testing/architecture) · [Craft graph vs Nx](/guide/testing/craft-graph-vs-nx) |\n| `npx nx architecture <app>` | Runs the app's architecture Vitest suite | [Architecture rules](/guide/testing/architecture) · [Craft graph vs Nx](/guide/testing/craft-graph-vs-nx) |\n| Live page MCP `page` | Drive the open `ng serve` tab (dev only) | [Live page MCP](/guide/ai/dev-page) |\n| Template migrator | Migrates templates to craft components | [Template migrator](/guide/components/template-migrator) |\n\n## Deployment\n\n::: warning Experimental\nThe deployment tooling is not settled: these symbols and commands can still\nchange between minor versions. See the\n[deployment guide](/guide/deployment/) for what exists today.\n:::\n\n| Symbol / command | What it does | Page |\n| ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------ |\n| `defineCraftDeployment` | Declares the deployment of an application in `craft.deploy.ts` | [Manifest reference](/guide/deployment/manifest) |\n| `checkCraftDeployment`, `checkCraftDeploymentArtifact` | Runs the manifest, module graph and artefact checks | [Diagnostics](/guide/deployment/diagnostics) |\n| `resolveCraftDeploymentManifest`, `serializeCraftDeploymentManifest`, `parseCraftDeploymentManifest` | Resolves, writes and reads the provider-neutral artefact form | [Manifest reference](/guide/deployment/manifest) |\n| `CraftDeploymentProvider`, `CRAFT_DEPLOYMENT_PROVIDERS` | The provider contract and the capability matrix | [Providers](/guide/deployment/providers) |\n| `npx craft-ts check` | Validates a deployment before building | [Deployment overview](/guide/deployment/) |\n| `npx craft-ts manifest` | Writes `dist/<app>/craft-deployment-manifest.json` | [Deployment overview](/guide/deployment/) |\n| `npx craft-ts deploy preview` | Shows what a provider would change, without changing it | [Alchemy provider](/guide/deployment/alchemy) |\n| `npx craft-ts deploy` | Applies that plan once `--yes` approves it | [Alchemy provider](/guide/deployment/alchemy) |\n| `createCraftDeploymentProvider` | The single factory a provider package exports | [Providers](/guide/deployment/providers) |\n| `createAlchemyDeploymentProvider`, `planAlchemyDeployment` | The Alchemy provider and its Cloudflare/AWS planning | [Alchemy provider](/guide/deployment/alchemy) |\n| `npx craft-ts providers` | Prints the provider capability matrix | [Providers](/guide/deployment/providers) |\n"
|
|
716
716
|
},
|
|
717
717
|
{
|
|
718
718
|
"path": "/resources/ai-agents",
|
|
@@ -738,7 +738,7 @@
|
|
|
738
738
|
{
|
|
739
739
|
"path": "/resources/examples",
|
|
740
740
|
"title": "Examples",
|
|
741
|
-
"body": "# Examples\n\nEvery example below is a real route of one of the demo applications. Each\nopens in StackBlitz on the relevant file, already navigated to the page.\n\nThe demo groups them the way you would meet them: **components** first, then the\n**primitives** on their own, then the same features **behind services**, then\n**routing** and the rest.\n\n::: tip Just want to poke at something?\nThe [Playground](https://stackblitz.com/fork/github/craft-ts/craft-ts-demo/tree/main?file=src%2Fapp%2Fexamples%2Fplayground%2Fplayground.ts&initialpath=%2Fplayground)\nis a shareable sandbox with a small todo flow — the fastest way to try an idea.\n:::\n\n## Components\n\nFunctional, selectorless components rendered from typed hyperscript.\n\n| Example | What it shows |\n| --- | --- |\n| [Functional Components](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/component-demo.ts&initialpath=/) | `craftComponent`, inputs and outputs as factory parameters, hyperscript templates |\n| [Reactive Composition](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/component-composition-demo.ts&initialpath=/component-composition) | Composing components and directives with `.pipe(...)` |\n| [Content Projection](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/content-projection-demo.ts&initialpath=/content-projection) | Free DOM content, typed DOM contracts, and logical projection by contract |\n| [Pending Block](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/pending-node-demo.ts&initialpath=/pending-node) | Type-safe async suspension with `settledValue`, `settled(...)` and `pendingNode` |\n| [Pending Block — Exception](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/pending-node-exception-demo.ts&initialpath=/pending-node/exception) | Coordinating pending, reloading and business-exception fallbacks with `pendingNode` and `catchNode` |\n\n## Primitives\n\nUsing `state`, `query`, `mutation`, `queryParams` and `asyncProcess` directly,\nwith no service layer.\n\n| Example | What it shows |\n| --- | --- |\n| [Query](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/query/query.ts&initialpath=/query/1) | `query()` with reactive params, status and caching |\n| [Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/mutation/mutation.ts&initialpath=/mutation/1) | `mutation()` with manual control of modification operations |\n| [List with Pagination](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/list-with-pagination/list-with-pagination.ts&initialpath=/list-with-pagination) | Pagination with hand-managed query params and page state |\n| [Granular Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/granular-mutation/granular-mutation.ts&initialpath=/granular-mutation) | Optimistic updates and cache invalidation, done by hand |\n| [Full Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/full-demo/full-demo.ts&initialpath=/full-demo) | Everything at once, without store or service abstractions |\n| [Login Form](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/forms/login-form.ts&initialpath=/login-form) | `insertForm`, validators, and a typed submit wired to a mutation |\n| [Pixel Art](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/pixel-art/pixel-art.ts&initialpath=/pixel-art) | `state` + `insertSelect` over a flat array |\n| [Pixel Art Matrix](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/pixel-art-matrix/pixel-art-matrix.ts&initialpath=/pixel-art-matrix) | Nested `insertSelect` and internal `source$` between rows and cells |\n| [Exceptions](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/exceptions/exceptions.ts&initialpath=/exceptions) | Business exceptions on `query()`, rendered per code with `matchNode.exhaustive` |\n| [Exception QueryParams](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/exceptions/exception-query-params.ts&initialpath=/exception-query-params) | `queryParams` decode failures through `hasException()` and `exceptions().parse` |\n\n## State machines\n\nState machines for explicit transitions, history and collection-oriented UI.\n\n| Example | What it shows |\n| --- | --- |\n| [Profile editor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine/profile-editor.ts&initialpath=/state-machine) | `craftStateMachine`, typed transitions and persisted history |\n| [Text editor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine/text-editor.ts&initialpath=/state-machine-text) | A compact state machine for editing, validation and transitions |\n| [Task board](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine-list/task-board.ts&initialpath=/state-machine-list) | A state machine per list item with history and reactive collection updates |\n\n## Services\n\nThe same features, packaged behind `craftService`.\n\n| Example | What it shows |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| [Craft Query](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/query/query.ts&initialpath=/craft/query/1) | A reusable query service with configured storage persistence (localStorage by default) |\n| [Craft Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/mutation/mutation.ts&initialpath=/craft/mutation/1) | Create / update / delete with reactive cache synchronisation |\n| [Craft List Pagination](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/list-with-pagination/list-with-pagination.ts&initialpath=/craft/list-with-pagination) | `queryParams` + `insertPaginationPlaceholderData` in a service |\n| [Craft Granular Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/granular-mutation/granular-mutation.ts&initialpath=/craft/granular-mutation) | `insertReactOnMutation` updating cached data without a reload |\n| [Craft Full Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/full-demo/full-demo.ts&initialpath=/craft/full-demo) | Queries, mutations, async work, URL state and persistence together |\n| [craftService Counter](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/craft-service-counter.ts&initialpath=/craft-service/counter) | The smallest possible service — scopes and composition |\n| [craftService User Detail](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/craft-service-user-detail.ts&initialpath=/craft-service/user-detail) | Service inputs, and exposing only part of a dependency |\n| [craftRegisterFor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/register-for.ts&initialpath=/craft-service/register-for) | A parent driving live children through a typed registry |\n\n## Effect\n\nConcrete EffectTS integration examples, using the dedicated Effect demo.\n\n| Example | What it shows |\n| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Profile Lookup](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-profile-lookup.ts&initialpath=/) | `queryEffect`, typed business errors, and pending / exception rendering |\n| [Access Check](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-access-check-shared-service.ts&initialpath=/access) | An Effect service provided by the application Layer |\n| [Team Overview](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-team-overview-layer-scope.ts&initialpath=/team) | Combining application-wide and route-scoped Effect Layers |\n| [Effect Playground](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-playground.ts&initialpath=/playground) | A shareable todo sandbox with `queryEffect`, `mutationEffect`, and a route-provided Effect service |\n| [Translate in an Effect](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/shared/i18n-domain.ts&initialpath=/i18n) | `provideI18nRuntime` as a route Layer, `translateEffect` inside a plain Effect program, and the locale as Craft state driving the query params |\n\n## Design system\n\nThe typed style system, at all three of its levels. Both routes read from the\nsame sheets under `src/app/examples/design-system/`, which has a README walking\nthrough the same progression in code.\n\n| Example | What it shows |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Mini Design System](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/design-system/foundation.style.ts&initialpath=/design-system) | `definePalette`, `defineStateAxis`, `cssVars` and the theme: one dark-mode rule for the whole system, and variants as `data-*` attributes rather than class strings |\n| [Scroll context](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/design-system/scroll.style.ts&initialpath=/design-system/scroll) | Level 3: `requires(scrollPort.block)` travelling up the tree, `provides(...)` on the layout that owns the area, and the `scrollState` axis |\n\nStart from [Activating the style system](/guide/style/setup) — the sheets emit\nnothing without the Vite plugin.\n\n## Internationalisation\n\n| Example | What it shows |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Type-safe i18n](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/i18n/i18n.service.ts&initialpath=/i18n) | `defineCatalog` + `msg` + `plural`, a second locale through `defineLocaleLike`, every shipped semantic token, a custom `defineToken`, and `runtime.bind` switching the whole page reactively |\n\nThe guide is [Type-safe i18n](/guide/i18n/).\n\n## Routing\n\n| Example | What it shows |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| [Query Params in the route](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/list-with-pagination/qp-list-with-pagination.ts&initialpath=/query-params) | `queryParams` declared on the route rather than in a component |\n| [Guard Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/guard-demo/GuardDemo.ts&initialpath=/guard-demo) | Guards as bare generators, and `handleExceptions` per code |\n| [Slow Page](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/slow-page/slow-page.routes.ts&initialpath=/slow-page) | Non-blocking navigation: the stay → blank → loader phases, and a `craftGen` resolver recovered locally with `catchTag` |\n| [View Transitions](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/view-transitions/view-transitions.routes.ts&initialpath=/view-transitions) | Outlet-driven view transitions surviving the guard/resolve chain, with a per-route skeleton |\n| [Lazy Layout](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/lazy-layout/lazy-layout.routes.ts&initialpath=/craft/lazy-layout/1) | A lazy child collection with its own DI check and a route-provided service |\n\n## Tooling\n\n| Example | What it shows |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |\n| [Playground](https://stackblitz.com/fork/github/craft-ts/craft-ts-demo/tree/main?file=src%2Fapp%2Fexamples%2Fplayground%2Fplayground.ts&initialpath=%2Fplayground) | A shareable sandbox: a small todo flow with `craftService`, `query()` and `mutation()` |\n| [Send Context to AI](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/ia/demo-send-context/demo-send-context.ts&initialpath=/demo-send-context) | Exporting the live dependency graph and app context to an assistant |\n\n## Notes\n\nEach example ships its own `api.service.ts` simulating the network, so every\nroute works standalone.\n\nSource repository:\n[craft-ts-demo](https://github.com/craft-ts/craft-ts-demo).\n\nEffect demo source repository:\n[craft-demo-effect](https://github.com/craft-ts/craft-demo-effect).\n"
|
|
741
|
+
"body": "# Examples\n\nEvery example below is a real route of one of the demo applications. Each\nopens in StackBlitz on the relevant file, already navigated to the page.\n\nThe demo groups them the way you would meet them: **components** first, then the\n**primitives** on their own, then the same features **behind services**, then\n**routing** and the rest.\n\n::: tip Just want to poke at something?\nThe [Playground](https://stackblitz.com/fork/github/craft-ts/craft-ts-demo/tree/main?file=src%2Fapp%2Fexamples%2Fplayground%2Fplayground.ts&initialpath=%2Fplayground)\nis a shareable sandbox with a small todo flow — the fastest way to try an idea.\n:::\n\n## Components\n\nFunctional, selectorless components rendered from typed hyperscript.\n\n| Example | What it shows |\n| --- | --- |\n| [Functional Components](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/component-demo.ts&initialpath=/) | `craftComponent`, inputs and outputs as factory parameters, hyperscript templates |\n| [Reactive Composition](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/component-composition-demo.ts&initialpath=/component-composition) | Composing components and directives with `.pipe(...)` |\n| [Content Projection](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/content-projection-demo.ts&initialpath=/content-projection) | Free DOM content, typed DOM contracts, and logical projection by contract |\n| [Pending Block](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/pending-node-demo.ts&initialpath=/pending-node) | Type-safe async suspension with `settledValue`, `settled(...)` and `pendingNode` |\n| [Pending Block — Exception](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/component/pending-node-exception-demo.ts&initialpath=/pending-node/exception) | Coordinating pending, reloading and business-exception fallbacks with `pendingNode` and `catchNode` |\n\n## Primitives\n\nUsing `state`, `query`, `mutation`, `queryParams` and `asyncProcess` directly,\nwith no service layer.\n\n| Example | What it shows |\n| --- | --- |\n| [Query](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/query/query.ts&initialpath=/query/1) | `query()` with reactive params, status and caching |\n| [Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/mutation/mutation.ts&initialpath=/mutation/1) | `mutation()` with manual control of modification operations |\n| [List with Pagination](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/list-with-pagination/list-with-pagination.ts&initialpath=/list-with-pagination) | Pagination with hand-managed query params and page state |\n| [Granular Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/granular-mutation/granular-mutation.ts&initialpath=/granular-mutation) | Optimistic updates and cache invalidation, done by hand |\n| [Full Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/full-demo/full-demo.ts&initialpath=/full-demo) | Everything at once, without store or service abstractions |\n| [Login Form](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/forms/login-form.ts&initialpath=/login-form) | `insertForm`, validators, and a typed submit wired to a mutation |\n| [Pixel Art](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/pixel-art/pixel-art.ts&initialpath=/pixel-art) | `state` + `insertSelect` over a flat array |\n| [Pixel Art Matrix](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/pixel-art-matrix/pixel-art-matrix.ts&initialpath=/pixel-art-matrix) | Nested `insertSelect` and internal `source$` between rows and cells |\n| [Exceptions](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/exceptions/exceptions.ts&initialpath=/exceptions) | Business exceptions on `query()`, rendered per code with `matchNode.exhaustive` |\n| [Exception QueryParams](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/exceptions/exception-query-params.ts&initialpath=/exception-query-params) | `queryParams` decode failures through `hasException()` and `exceptions().parse` |\n\n## State machines\n\nState machines for explicit transitions, history and collection-oriented UI.\n\n| Example | What it shows |\n| --- | --- |\n| [Profile editor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine/profile-editor.ts&initialpath=/state-machine) | `craftStateMachine`, typed transitions and persisted history |\n| [Text editor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine/text-editor.ts&initialpath=/state-machine-text) | A compact state machine for editing, validation and transitions |\n| [Task board](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/primitives/state-machine-list/task-board.ts&initialpath=/state-machine-list) | A state machine per list item with history and reactive collection updates |\n\n## Services\n\nThe same features, packaged behind `craftService`.\n\n| Example | What it shows |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| [Craft Query](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/query/query.ts&initialpath=/craft/query/1) | A reusable query service with configured storage persistence (localStorage by default) |\n| [Craft Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/mutation/mutation.ts&initialpath=/craft/mutation/1) | Create / update / delete with reactive cache synchronisation |\n| [Craft List Pagination](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/list-with-pagination/list-with-pagination.ts&initialpath=/craft/list-with-pagination) | `queryParams` + `insertPaginationPlaceholderData` in a service |\n| [Craft Granular Mutation](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/granular-mutation/granular-mutation.ts&initialpath=/craft/granular-mutation) | `insertReactOnMutation` updating cached data without a reload |\n| [Craft Full Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/full-demo/full-demo.ts&initialpath=/craft/full-demo) | Queries, mutations, async work, URL state and persistence together |\n| [craftService Counter](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/craft-service-counter.ts&initialpath=/craft-service/counter) | The smallest possible service — scopes and composition |\n| [craftService User Detail](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/craft-service-user-detail.ts&initialpath=/craft-service/user-detail) | Service inputs, and exposing only part of a dependency |\n| [craftRegisterFor](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft-service/register-for.ts&initialpath=/craft-service/register-for) | A parent driving live children through a typed registry |\n\n## Effect\n\nConcrete EffectTS integration examples, using the dedicated Effect demo.\n\n| Example | What it shows |\n| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Profile Lookup](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-profile-lookup.ts&initialpath=/) | `queryEffect`, typed business errors, and pending / exception rendering |\n| [Access Check](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-access-check-shared-service.ts&initialpath=/access) | An Effect service provided by the application Layer |\n| [Team Overview](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-team-overview-layer-scope.ts&initialpath=/team) | Combining application-wide and route-scoped Effect Layers |\n| [Effect Playground](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/examples/effect/effect-playground.ts&initialpath=/playground) | A shareable todo sandbox with `queryEffect`, `mutationEffect`, and a route-provided Effect service |\n| [Translate in an Effect](https://stackblitz.com/github/craft-ts/craft-demo-effect/tree/main/?file=src/app/shared/i18n-domain.ts&initialpath=/i18n) | `provideI18nRuntime` as a route Layer, `translateEffect` inside a plain Effect program, and the locale as Craft state driving the query params |\n\n## Design system\n\nThe typed style system, at all three of its levels. Both routes read from the\nsame sheets under `src/app/examples/design-system/`, which has a README walking\nthrough the same progression in code.\n\n| Example | What it shows |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Mini Design System](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/design-system/foundation.style.ts&initialpath=/design-system) | `definePalette`, `defineStateAxis`, `cssVars` and the theme: one dark-mode rule for the whole system, and variants as `data-*` attributes rather than class strings |\n| [Scroll context](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/design-system/scroll.style.ts&initialpath=/design-system/scroll) | Level 3: `requires(scrollPort.block)` travelling up the tree, `provides(...)` on the layout that owns the area, and the `scrollState` axis |\n\nStart from [Activating the style system](/guide/style/setup) — the sheets emit\nnothing without the Vite plugin.\n\n## Internationalisation\n\n| Example | What it shows |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Type-safe i18n](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/i18n/i18n.service.ts&initialpath=/i18n) | `defineCatalog` + `msg` + `plural`, a second locale through `defineLocaleLike`, every shipped semantic token, a custom `defineToken` that resolves a service and parses its parameter with a schema, and `runtime.bind` switching the whole page reactively |\n\nThe guide is [Type-safe i18n](/guide/i18n/).\n\n## Routing\n\n| Example | What it shows |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| [Query Params in the route](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/list-with-pagination/qp-list-with-pagination.ts&initialpath=/query-params) | `queryParams` declared on the route rather than in a component |\n| [Guard Demo](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/guard-demo/GuardDemo.ts&initialpath=/guard-demo) | Guards as bare generators, and `handleExceptions` per code |\n| [Slow Page](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/slow-page/slow-page.routes.ts&initialpath=/slow-page) | Non-blocking navigation: the stay → blank → loader phases, and a `craftGen` resolver recovered locally with `catchTag` |\n| [View Transitions](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/routes/view-transitions/view-transitions.routes.ts&initialpath=/view-transitions) | Outlet-driven view transitions surviving the guard/resolve chain, with a per-route skeleton |\n| [Lazy Layout](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/craft/lazy-layout/lazy-layout.routes.ts&initialpath=/craft/lazy-layout/1) | A lazy child collection with its own DI check and a route-provided service |\n\n## Tooling\n\n| Example | What it shows |\n| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |\n| [Playground](https://stackblitz.com/fork/github/craft-ts/craft-ts-demo/tree/main?file=src%2Fapp%2Fexamples%2Fplayground%2Fplayground.ts&initialpath=%2Fplayground) | A shareable sandbox: a small todo flow with `craftService`, `query()` and `mutation()` |\n| [Send Context to AI](https://stackblitz.com/github/craft-ts/craft-ts-demo/tree/main/?file=src/app/examples/ia/demo-send-context/demo-send-context.ts&initialpath=/demo-send-context) | Exporting the live dependency graph and app context to an assistant |\n\n## Notes\n\nEach example ships its own `api.service.ts` simulating the network, so every\nroute works standalone.\n\nSource repository:\n[craft-ts-demo](https://github.com/craft-ts/craft-ts-demo).\n\nEffect demo source repository:\n[craft-demo-effect](https://github.com/craft-ts/craft-demo-effect).\n"
|
|
742
742
|
},
|
|
743
743
|
{
|
|
744
744
|
"path": "/resources/migration",
|
package/package.json
CHANGED
package/skills/craft-ts/SKILL.md
CHANGED
|
@@ -60,8 +60,10 @@ If MCP is not configured, read https://ng-angular-stack.github.io/craft/llms.txt
|
|
|
60
60
|
typecheck and emit nothing. Load `craft-ts-style` before touching one.
|
|
61
61
|
- Translations live in a `@craft-ts/i18n` catalogue: `defineCatalog` + `msg` for
|
|
62
62
|
the reference locale, `defineLocaleLike` for every other one, so a missing key
|
|
63
|
-
is a compile error.
|
|
64
|
-
|
|
63
|
+
is a compile error. A token may validate or parse its parameter with a
|
|
64
|
+
Standard Schema, and resolve its formatter from a Craft service; a project
|
|
65
|
+
generated with i18n also forbids visible literals in templates. No Effect
|
|
66
|
+
import — use `@craft-ts/i18n-effect` only inside an Effect program. Load
|
|
65
67
|
`craft-ts-i18n` before adding a key, a locale or a token.
|
|
66
68
|
- Forms start with `state` + `insertForm`. Choose `insertSelectFormTree` for a
|
|
67
69
|
nested field, `insertFormAttributes` for validators/visibility,
|
|
@@ -22,7 +22,8 @@ suite that already exists.
|
|
|
22
22
|
|
|
23
23
|
## 1. Bootstrap a new project
|
|
24
24
|
|
|
25
|
-
Prefer `craft create`, which asks for
|
|
25
|
+
Prefer `craft create`, which asks for the application type first, recommends
|
|
26
|
+
EffectTS v4 for a full-stack backend, and then asks for agent integrations. It
|
|
26
27
|
generates the application, API/page example, routes, ESLint, unit tests,
|
|
27
28
|
architecture suite and Playwright commands together:
|
|
28
29
|
|
|
@@ -5,9 +5,11 @@ description: Build and review type-safe internationalisation in a CraftTS projec
|
|
|
5
5
|
|
|
6
6
|
# CraftTS type-safe i18n
|
|
7
7
|
|
|
8
|
-
`@craft-ts/i18n`
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
`@craft-ts/i18n` integrates with CraftTS for DI-aware translation tokens. The
|
|
9
|
+
catalogue remains declarative, and non-DI messages still work with `runtime.t`.
|
|
10
|
+
Use the CraftTS-bound translator whenever a token yields a service. Do not reach
|
|
11
|
+
for Effect to translate a string; use `@craft-ts/i18n-effect` only inside an
|
|
12
|
+
Effect program.
|
|
11
13
|
|
|
12
14
|
The contract it enforces, all at typecheck time:
|
|
13
15
|
|
|
@@ -89,6 +91,93 @@ return { language, setLocale: language.setLocale, translate: runtime.bind(langua
|
|
|
89
91
|
`bind(...)('key', params)` returns a generator the template yields, like any
|
|
90
92
|
other Craft reader.
|
|
91
93
|
|
|
94
|
+
### DI-aware tokens
|
|
95
|
+
|
|
96
|
+
Any token factory accepts a **generator function** in place of the adapter. The
|
|
97
|
+
yielded services are carried into the translation reader's component dependency
|
|
98
|
+
contract:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const amount = money('amount', function* () {
|
|
102
|
+
const currency = yield* ClientCurrency();
|
|
103
|
+
return { currency: currency.code, minimumFractionDigits: 2 };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const catalog = defineCatalog({
|
|
107
|
+
order: msg`Order total ${amount}.`,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Render it with the translator returned by `runtime.bind(...)`, and **pass the
|
|
112
|
+
reader** — as a child or as an attribute value:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
p(translate('order', { amount: 1234.5 }));
|
|
116
|
+
p({ title: translate('order', { amount: 1234.5 }) }, 'Order');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Both carry the dependency, so the component and route DI checks fail
|
|
120
|
+
compilation if `ClientCurrency` is not provided. Do not drive the reader
|
|
121
|
+
yourself (`yield* translate(...)()`): it renders the same string but the
|
|
122
|
+
dependency disappears from the check.
|
|
123
|
+
|
|
124
|
+
`runtime.t` accepts `StaticTranslationKey` only — the keys that resolve no
|
|
125
|
+
service — so a DI message on that path is a compile error, not a runtime one.
|
|
126
|
+
An arrow function that returns a generator is refused: the options factory must
|
|
127
|
+
be a `function*`.
|
|
128
|
+
|
|
129
|
+
### No visible literal in a template
|
|
130
|
+
|
|
131
|
+
A project generated with i18n ships `craft-ts/require-i18n-text` in its ESLint
|
|
132
|
+
configuration: a static string in `heading`/`p`/`label`/`button`/`a`/`option`/…
|
|
133
|
+
or in a `placeholder`, `aria-label` or `title` attribute is an error. Put the
|
|
134
|
+
copy in `src/i18n/catalog.ts` (and in every locale) and read it with
|
|
135
|
+
`i18n.t(...)`.
|
|
136
|
+
|
|
137
|
+
Wrapping the literal does not hide it — concatenation, template text, ternary
|
|
138
|
+
branches, `||` fallbacks and children arrays are all inspected:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
p('Total: ' + i18n.t('cart.total', { amount })); // reported: 'Total: '
|
|
142
|
+
p(i18n.t('cart.totalLine', { amount })); // the whole sentence is a key
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Only text carrying letters counts, so `first + ' ' + last` is fine. The key and
|
|
146
|
+
parameters of `i18n.t(...)`, generator children, catalogue files, server files
|
|
147
|
+
and tests are all exempt. A project generated without i18n does not get the
|
|
148
|
+
rule.
|
|
149
|
+
|
|
150
|
+
A project token does the same through `defineToken`, and declares no `format`
|
|
151
|
+
when the formatter only exists at render time:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const weight = defineToken({
|
|
155
|
+
name: 'weight',
|
|
156
|
+
kind: 'weight',
|
|
157
|
+
resolveFormatter: function* () {
|
|
158
|
+
const units = yield* Units();
|
|
159
|
+
const unit = (yield* units.system()) === 'imperial' ? 'pound' : 'kilogram';
|
|
160
|
+
return (value: number, context) =>
|
|
161
|
+
new Intl.NumberFormat(context.locale, { style: 'unit', unit }).format(value);
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Schema-declared parameters
|
|
167
|
+
|
|
168
|
+
The adapter position also takes a Standard Schema (Zod, Valibot, ArkType — the
|
|
169
|
+
same contract as `state`, `query` and forms). The parameter type becomes the
|
|
170
|
+
schema's input and the formatter receives its output, so the schema parses
|
|
171
|
+
once, in the catalogue:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const placedAt = dateLong('placedAt', z.coerce.date());
|
|
175
|
+
translate('order', { placedAt: '2026-08-25T14:30:00Z' });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`defineToken` takes the same `schema` field and may combine it with
|
|
179
|
+
`resolveFormatter`.
|
|
180
|
+
|
|
92
181
|
## With Effect
|
|
93
182
|
|
|
94
183
|
`@craft-ts/i18n-effect` is the adapter, and only the adapter:
|