@craft-ts/mcp 0.7.0-beta.17 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-ts/mcp",
3
- "version": "0.7.0-beta.17",
3
+ "version": "0.7.0-beta.19",
4
4
  "description": "MCP server, Agent Skills, and LLM files for coding agents using @craft-ts/core",
5
5
  "author": "Romain Geffrault",
6
6
  "license": "MIT",
@@ -60,9 +60,18 @@ 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. `@craft-ts/i18n` has no framework and no Effect import;
64
- use `@craft-ts/i18n-effect` only inside an Effect program. Load
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.
68
+ - Forms start with `state` + `insertForm`. Choose `insertSelectFormTree` for a
69
+ nested field, `insertFormAttributes` for validators/visibility,
70
+ `insertFormSchema` for whole-form validation and `insertFormSubmit` for a
71
+ mutation. Bind the selected field with `CraftFieldDirective`; expose
72
+ `field.exceptions` through `fieldErrorNode`, and read submission state with
73
+ `form().submitting()` / `form().hasSubmitExceptions()`. Do not reach for
74
+ native `FormData` as the primary form model.
66
75
  - Run existing architecture tests. Do not add an architecture rule for the feature.
67
76
  - Keep `npm run typecheck` in the project CI; for generated projects this is
68
77
  already wired into `.github/workflows/ci.yml` alongside the architecture
@@ -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 Effect v4 and agent integrations and
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` has **no CraftTS, Angular or Effect import**. The catalogue is
9
- a plain TypeScript value and the runtime works in a browser, a server, a worker
10
- or a test without a framework. Do not reach for Effect to translate a string.
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: