@plumbus/core 0.6.3 → 0.6.4

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.
@@ -1,13 +1,49 @@
1
1
  import type { MessageCatalog, TranslationDefinition } from '../types/translation.js';
2
- interface DefineTranslationInput {
2
+ /** Union of message keys across every locale catalog in M */
3
+ type UnionKeys<M extends Record<string, MessageCatalog>> = {
4
+ [L in keyof M]: keyof M[L] & string;
5
+ }[keyof M];
6
+ /**
7
+ * Keys present in the global union but absent from locale L.
8
+ * `never` when L already has every key.
9
+ */
10
+ type MissingKeysForLocale<M extends Record<string, MessageCatalog>, L extends keyof M> = Exclude<UnionKeys<M>, keyof M[L] & string>;
11
+ /**
12
+ * Force TS to expand mapped types in error messages (avoids dumping
13
+ * `Record<UnionKeys<{...}>, string>` into the diagnostic).
14
+ */
15
+ type Prettify<T> = {
16
+ [K in keyof T]: T[K];
17
+ } & {};
18
+ /**
19
+ * Per-locale catalog: pass-through when complete; otherwise expect a
20
+ * concrete `{ key: string; ... }` for the full key union so TS reports
21
+ * native diagnostics on that locale attribute, e.g.:
22
+ * Property 'farewell' is missing ...
23
+ * Type '…' is missing the following properties from type '…': greeting1, greeting2
24
+ */
25
+ type SameKeyMessages<M extends Record<string, MessageCatalog>> = {
26
+ [L in keyof M]: [MissingKeysForLocale<M, L>] extends [never] ? M[L] : Prettify<{
27
+ [K in UnionKeys<M>]: string;
28
+ }>;
29
+ };
30
+ interface DefineTranslationInput<M extends Record<string, MessageCatalog>> {
3
31
  name: string;
4
32
  defaultLocale: string;
5
33
  locales: string[];
6
- messages: Record<string, MessageCatalog>;
34
+ /**
35
+ * Infer M from the literal catalogs, then require every locale to cover
36
+ * UnionKeys. Incomplete locales get a prettified concrete object type so
37
+ * TS emits native "missing the following properties: …" diagnostics.
38
+ */
39
+ messages: SameKeyMessages<M>;
7
40
  }
8
41
  /**
9
42
  * Define a translation catalog — a named set of i18n message strings.
10
43
  *
44
+ * Validates at typecheck (for literal / `as const` catalogs):
45
+ * - all locales must declare the exact same message keys
46
+ *
11
47
  * Validates at import time:
12
48
  * - name is required
13
49
  * - defaultLocale must exist in locales
@@ -16,6 +52,6 @@ interface DefineTranslationInput {
16
52
  *
17
53
  * Returns a deeply frozen TranslationDefinition.
18
54
  */
19
- export declare function defineTranslation(config: DefineTranslationInput): TranslationDefinition;
55
+ export declare function defineTranslation<const M extends Record<string, MessageCatalog>>(config: DefineTranslationInput<M>): TranslationDefinition;
20
56
  export {};
21
57
  //# sourceMappingURL=defineTranslation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defineTranslation.d.ts","sourceRoot":"","sources":["../../src/define/defineTranslation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAIrF,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,sBAAsB,GAAG,qBAAqB,CAsEvF"}
1
+ {"version":3,"file":"defineTranslation.d.ts","sourceRoot":"","sources":["../../src/define/defineTranslation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAIrF,6DAA6D;AAC7D,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI;KACxD,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM;CACpC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;GAGG;AACH,KAAK,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,OAAO,CAC9F,SAAS,CAAC,CAAC,CAAC,EACZ,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CACpB,CAAC;AAEF;;;GAGG;AACH,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AAEjD;;;;;;GAMG;AACH,KAAK,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI;KAC9D,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACxD,CAAC,CAAC,CAAC,CAAC,GACJ,QAAQ,CAAC;SAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM;KAAE,CAAC;CAC9C,CAAC;AAEF,UAAU,sBAAsB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC9E,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAChC,qBAAqB,CAsEvB"}
@@ -3,6 +3,9 @@ import { throwDefineValidationError } from './validation-error.js';
3
3
  /**
4
4
  * Define a translation catalog — a named set of i18n message strings.
5
5
  *
6
+ * Validates at typecheck (for literal / `as const` catalogs):
7
+ * - all locales must declare the exact same message keys
8
+ *
6
9
  * Validates at import time:
7
10
  * - name is required
8
11
  * - defaultLocale must exist in locales
@@ -1 +1 @@
1
- {"version":3,"file":"defineTranslation.js","sourceRoot":"","sources":["../../src/define/defineTranslation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"defineTranslation.js","sourceRoot":"","sources":["../../src/define/defineTranslation.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,9 @@ export const commonTranslation = defineTranslation({
30
30
 
31
31
  - Place translation files in `app/translations/<name>.translation.ts` (classic layout), **or** use the locale-folder layout below
32
32
  - Each assembler exports one `defineTranslation()` result
33
- - All locales **must** have the same key set — mismatched keys throw at import time
33
+ - All locales **must** have the same key set — mismatched keys fail at typecheck for literal catalogs and throw at import time otherwise
34
+ - Prefer literal / `as const` catalogs so `tsc` catches drift. A locale typed as `Record<string, string>` skips compile-time key checks and still relies on import-time validation
35
+ - When a non-default locale has an extra key, typecheck may error on the **default** locale (“missing key X”) while runtime errors on the **other** locale (“extra key X”) — both catch the same drift
34
36
  - Messages use ICU MessageFormat: `{name}` for interpolation, `{count, plural, ...}` for plurals, `{gender, select, ...}` for select
35
37
  - The `name` field is the namespace: keys resolve as `<namespace>.<key>` (e.g., `common.nav.overview`)
36
38
 
@@ -105,4 +107,21 @@ function Nav() {
105
107
 
106
108
  ## Translation Sync
107
109
 
108
- `defineTranslation()` validates key consistency at import time. If locale "he" is missing keys that "en" has, the app throws immediately. This ensures tests and CI catch translation drift.
110
+ `defineTranslation()` enforces key consistency at typecheck for literal / `as const` catalogs, and again at import time. If locale "he" is missing keys that "en" has, `tsc` fails for typed catalogs and the app throws immediately otherwise. This ensures tests and CI catch translation drift.
111
+
112
+ ### Dynamic locales
113
+
114
+ ```ts
115
+ const he: Record<string, string> = loadFromCms("he"); // no compile-time key parity
116
+
117
+ defineTranslation({
118
+ name: "common",
119
+ defaultLocale: "en",
120
+ locales: ["en", "he"],
121
+ messages: {
122
+ en: { greeting: "Hello", farewell: "Bye" },
123
+ he,
124
+ },
125
+ });
126
+ // Runtime still throws if `he` is missing/extra keys relative to `en`.
127
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumbus/core",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Plumbus framework core — types, SDK, runtime, CLI, test utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -109,6 +109,6 @@
109
109
  "lint": "biome lint ./src",
110
110
  "format": "biome format --write ./src",
111
111
  "format:check": "biome format ./src",
112
- "typecheck": "tsc --noEmit"
112
+ "typecheck": "tsc --noEmit && node ./scripts/check-define-translation-types.mjs"
113
113
  }
114
114
  }