@contractspec/lib.contracts-spec 2.3.0 → 2.5.0

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.
Files changed (40) hide show
  1. package/dist/integrations/index.js +379 -53
  2. package/dist/integrations/providers/deepgram.d.ts +3 -0
  3. package/dist/integrations/providers/deepgram.js +129 -0
  4. package/dist/integrations/providers/elevenlabs.js +6 -3
  5. package/dist/integrations/providers/fal-image.d.ts +3 -0
  6. package/dist/integrations/providers/fal-image.js +122 -0
  7. package/dist/integrations/providers/fal.js +2 -2
  8. package/dist/integrations/providers/gradium.js +2 -2
  9. package/dist/integrations/providers/image.d.ts +85 -0
  10. package/dist/integrations/providers/image.js +46 -0
  11. package/dist/integrations/providers/index.d.ts +6 -0
  12. package/dist/integrations/providers/index.js +380 -54
  13. package/dist/integrations/providers/openai-image.d.ts +3 -0
  14. package/dist/integrations/providers/openai-image.js +126 -0
  15. package/dist/integrations/providers/openai-realtime.d.ts +3 -0
  16. package/dist/integrations/providers/openai-realtime.js +127 -0
  17. package/dist/integrations/providers/registry.js +192 -33
  18. package/dist/integrations/providers/voice-video-sync.d.ts +29 -0
  19. package/dist/integrations/providers/voice-video-sync.js +1 -0
  20. package/dist/integrations/providers/voice.d.ts +149 -12
  21. package/dist/integrations/spec.d.ts +1 -1
  22. package/dist/node/integrations/index.js +379 -53
  23. package/dist/node/integrations/providers/deepgram.js +129 -0
  24. package/dist/node/integrations/providers/elevenlabs.js +6 -3
  25. package/dist/node/integrations/providers/fal-image.js +122 -0
  26. package/dist/node/integrations/providers/fal.js +2 -2
  27. package/dist/node/integrations/providers/gradium.js +2 -2
  28. package/dist/node/integrations/providers/image.js +46 -0
  29. package/dist/node/integrations/providers/index.js +380 -54
  30. package/dist/node/integrations/providers/openai-image.js +126 -0
  31. package/dist/node/integrations/providers/openai-realtime.js +127 -0
  32. package/dist/node/integrations/providers/registry.js +192 -33
  33. package/dist/node/integrations/providers/voice-video-sync.js +0 -0
  34. package/dist/node/translations/i18n-factory.js +229 -0
  35. package/dist/node/translations/index.js +64 -4
  36. package/dist/translations/i18n-factory.d.ts +90 -0
  37. package/dist/translations/i18n-factory.js +229 -0
  38. package/dist/translations/index.d.ts +1 -0
  39. package/dist/translations/index.js +64 -4
  40. package/package.json +88 -4
@@ -0,0 +1,229 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ var __toCommonJS = (from) => {
8
+ var entry = __moduleCache.get(from), desc;
9
+ if (entry)
10
+ return entry;
11
+ entry = __defProp({}, "__esModule", { value: true });
12
+ if (from && typeof from === "object" || typeof from === "function")
13
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ }));
17
+ __moduleCache.set(from, entry);
18
+ return entry;
19
+ };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
+
32
+ // src/translations/registry.ts
33
+ class TranslationRegistry {
34
+ specs = new Map;
35
+ latestByLocale = new Map;
36
+ locales = new Set;
37
+ domains = new Set;
38
+ constructor(items) {
39
+ if (items) {
40
+ for (const spec of items) {
41
+ this.register(spec);
42
+ }
43
+ }
44
+ }
45
+ register(spec) {
46
+ const fullKey = this.makeKey(spec.meta.key, spec.meta.version, spec.locale);
47
+ this.specs.set(fullKey, spec);
48
+ const localeKey = `${spec.meta.key}:${spec.locale}`;
49
+ const existing = this.latestByLocale.get(localeKey);
50
+ if (!existing || this.isNewerVersion(spec.meta.version, existing.meta.version)) {
51
+ this.latestByLocale.set(localeKey, spec);
52
+ }
53
+ this.locales.add(spec.locale);
54
+ this.domains.add(spec.meta.domain);
55
+ }
56
+ get(key, version, locale) {
57
+ return this.specs.get(this.makeKey(key, version, locale));
58
+ }
59
+ getLatest(key, locale) {
60
+ return this.latestByLocale.get(`${key}:${locale}`);
61
+ }
62
+ getMessage(specKey, messageKey, locale, version) {
63
+ const spec = version ? this.get(specKey, version, locale) : this.getLatest(specKey, locale);
64
+ if (!spec)
65
+ return;
66
+ return spec.messages[messageKey];
67
+ }
68
+ getWithFallback(specKey, messageKey, locale, fallbackLocale, version) {
69
+ const spec = version ? this.get(specKey, version, locale) : this.getLatest(specKey, locale);
70
+ if (spec) {
71
+ const message = spec.messages[messageKey];
72
+ if (message) {
73
+ return { spec, message, locale, fromFallback: false };
74
+ }
75
+ fallbackLocale = fallbackLocale ?? spec.fallback;
76
+ }
77
+ if (fallbackLocale && fallbackLocale !== locale) {
78
+ const fallbackSpec = version ? this.get(specKey, version, fallbackLocale) : this.getLatest(specKey, fallbackLocale);
79
+ if (fallbackSpec) {
80
+ const message = fallbackSpec.messages[messageKey];
81
+ if (message) {
82
+ return {
83
+ spec: fallbackSpec,
84
+ message,
85
+ locale: fallbackLocale,
86
+ fromFallback: true
87
+ };
88
+ }
89
+ }
90
+ }
91
+ return;
92
+ }
93
+ getValue(specKey, messageKey, locale, fallback) {
94
+ const result = this.getWithFallback(specKey, messageKey, locale);
95
+ return result?.message.value ?? fallback ?? messageKey;
96
+ }
97
+ listLocales(specKey) {
98
+ const locales = [];
99
+ for (const [key, spec] of this.specs.entries()) {
100
+ if (key.startsWith(`${specKey}:`)) {
101
+ if (!locales.includes(spec.locale)) {
102
+ locales.push(spec.locale);
103
+ }
104
+ }
105
+ }
106
+ return locales;
107
+ }
108
+ list() {
109
+ return [...this.specs.values()];
110
+ }
111
+ listByLocale(locale) {
112
+ return [...this.specs.values()].filter((s) => s.locale === locale);
113
+ }
114
+ listByDomain(domain) {
115
+ return [...this.specs.values()].filter((s) => s.meta.domain === domain);
116
+ }
117
+ has(key, locale, version) {
118
+ if (version) {
119
+ return this.specs.has(this.makeKey(key, version, locale));
120
+ }
121
+ return this.latestByLocale.has(`${key}:${locale}`);
122
+ }
123
+ getLocales() {
124
+ return this.locales;
125
+ }
126
+ getDomains() {
127
+ return this.domains;
128
+ }
129
+ getStats() {
130
+ let totalMessages = 0;
131
+ for (const spec of this.specs.values()) {
132
+ totalMessages += Object.keys(spec.messages).length;
133
+ }
134
+ return {
135
+ totalSpecs: this.specs.size,
136
+ locales: [...this.locales],
137
+ domains: [...this.domains],
138
+ totalMessages
139
+ };
140
+ }
141
+ clear() {
142
+ this.specs.clear();
143
+ this.latestByLocale.clear();
144
+ this.locales.clear();
145
+ this.domains.clear();
146
+ }
147
+ makeKey(key, version, locale) {
148
+ return `${key}:${version}:${locale}`;
149
+ }
150
+ isNewerVersion(a, b) {
151
+ const parseVersion = (v) => {
152
+ const parts = v.split(".").map(Number);
153
+ return {
154
+ major: parts[0] ?? 0,
155
+ minor: parts[1] ?? 0,
156
+ patch: parts[2] ?? 0
157
+ };
158
+ };
159
+ const va = parseVersion(a);
160
+ const vb = parseVersion(b);
161
+ if (va.major !== vb.major)
162
+ return va.major > vb.major;
163
+ if (va.minor !== vb.minor)
164
+ return va.minor > vb.minor;
165
+ return va.patch > vb.patch;
166
+ }
167
+ }
168
+
169
+ // src/translations/i18n-factory.ts
170
+ var DEFAULT_LOCALE = "en";
171
+ var SUPPORTED_LOCALES = ["en", "fr", "es"];
172
+ function resolveLocale(optionsLocale, runtimeLocale) {
173
+ const raw = runtimeLocale ?? optionsLocale ?? DEFAULT_LOCALE;
174
+ if (isSupportedLocale(raw))
175
+ return raw;
176
+ const base = raw.split("-")[0];
177
+ if (base && isSupportedLocale(base))
178
+ return base;
179
+ return DEFAULT_LOCALE;
180
+ }
181
+ function isSupportedLocale(locale) {
182
+ return SUPPORTED_LOCALES.includes(locale);
183
+ }
184
+ function interpolate(template, params) {
185
+ if (!params)
186
+ return template;
187
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
188
+ if (key in params)
189
+ return String(params[key]);
190
+ return match;
191
+ });
192
+ }
193
+ function createI18nFactory(config) {
194
+ let sharedRegistry = null;
195
+ function getRegistry() {
196
+ if (!sharedRegistry) {
197
+ sharedRegistry = new TranslationRegistry(config.catalogs);
198
+ }
199
+ return sharedRegistry;
200
+ }
201
+ const factory = {
202
+ create(optionsLocale, runtimeLocale) {
203
+ const locale = resolveLocale(optionsLocale, runtimeLocale);
204
+ const registry = getRegistry();
205
+ return {
206
+ locale,
207
+ t(key, params) {
208
+ const raw = registry.getValue(config.specKey, key, locale, key);
209
+ return interpolate(raw, params);
210
+ }
211
+ };
212
+ },
213
+ getDefault() {
214
+ return factory.create(DEFAULT_LOCALE);
215
+ },
216
+ resetRegistry() {
217
+ sharedRegistry = null;
218
+ }
219
+ };
220
+ return factory;
221
+ }
222
+ export {
223
+ resolveLocale,
224
+ isSupportedLocale,
225
+ interpolate,
226
+ createI18nFactory,
227
+ SUPPORTED_LOCALES,
228
+ DEFAULT_LOCALE
229
+ };
@@ -28,9 +28,6 @@ var __export = (target, all) => {
28
28
  };
29
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
30
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
- // src/translations/spec.ts
32
- var defineTranslation = (spec) => spec;
33
-
34
31
  // src/translations/registry.ts
35
32
  class TranslationRegistry {
36
33
  specs = new Map;
@@ -168,6 +165,63 @@ class TranslationRegistry {
168
165
  }
169
166
  }
170
167
 
168
+ // src/translations/i18n-factory.ts
169
+ var DEFAULT_LOCALE = "en";
170
+ var SUPPORTED_LOCALES = ["en", "fr", "es"];
171
+ function resolveLocale(optionsLocale, runtimeLocale) {
172
+ const raw = runtimeLocale ?? optionsLocale ?? DEFAULT_LOCALE;
173
+ if (isSupportedLocale(raw))
174
+ return raw;
175
+ const base = raw.split("-")[0];
176
+ if (base && isSupportedLocale(base))
177
+ return base;
178
+ return DEFAULT_LOCALE;
179
+ }
180
+ function isSupportedLocale(locale) {
181
+ return SUPPORTED_LOCALES.includes(locale);
182
+ }
183
+ function interpolate(template, params) {
184
+ if (!params)
185
+ return template;
186
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
187
+ if (key in params)
188
+ return String(params[key]);
189
+ return match;
190
+ });
191
+ }
192
+ function createI18nFactory(config) {
193
+ let sharedRegistry = null;
194
+ function getRegistry() {
195
+ if (!sharedRegistry) {
196
+ sharedRegistry = new TranslationRegistry(config.catalogs);
197
+ }
198
+ return sharedRegistry;
199
+ }
200
+ const factory = {
201
+ create(optionsLocale, runtimeLocale) {
202
+ const locale = resolveLocale(optionsLocale, runtimeLocale);
203
+ const registry = getRegistry();
204
+ return {
205
+ locale,
206
+ t(key, params) {
207
+ const raw = registry.getValue(config.specKey, key, locale, key);
208
+ return interpolate(raw, params);
209
+ }
210
+ };
211
+ },
212
+ getDefault() {
213
+ return factory.create(DEFAULT_LOCALE);
214
+ },
215
+ resetRegistry() {
216
+ sharedRegistry = null;
217
+ }
218
+ };
219
+ return factory;
220
+ }
221
+
222
+ // src/translations/spec.ts
223
+ var defineTranslation = (spec) => spec;
224
+
171
225
  // src/translations/validation.ts
172
226
  function validateTranslationSpec(spec) {
173
227
  const issues = [];
@@ -524,10 +578,16 @@ export {
524
578
  validateTranslationSpec,
525
579
  validateTranslationRegistry,
526
580
  validateICUFormat,
581
+ resolveLocale,
582
+ isSupportedLocale,
583
+ interpolate,
527
584
  findMissingTranslations,
528
585
  findAllMissingTranslations,
529
586
  defineTranslation,
587
+ createI18nFactory,
530
588
  assertTranslationSpecValid,
531
589
  TranslationValidationError,
532
- TranslationRegistry
590
+ TranslationRegistry,
591
+ SUPPORTED_LOCALES,
592
+ DEFAULT_LOCALE
533
593
  };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Generic i18n factory for ContractSpec packages.
3
+ *
4
+ * Eliminates the boilerplate duplicated across package-level
5
+ * i18n/messages.ts files by extracting the common interpolation,
6
+ * registry management, locale resolution, and t() wiring.
7
+ *
8
+ * @module translations/i18n-factory
9
+ */
10
+ import type { TranslationSpec } from './spec';
11
+ /** Default locale when none is specified. */
12
+ export declare const DEFAULT_LOCALE = "en";
13
+ /** Supported locales shipped with ContractSpec packages. */
14
+ export declare const SUPPORTED_LOCALES: readonly ["en", "fr", "es"];
15
+ /** A locale included in the default set. */
16
+ export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
17
+ /**
18
+ * Resolve the effective locale from two optional overrides.
19
+ *
20
+ * Priority: `runtimeLocale > optionsLocale > DEFAULT_LOCALE`.
21
+ * Falls back to base language (e.g., `"fr-CA"` -> `"fr"`) when needed.
22
+ */
23
+ export declare function resolveLocale(optionsLocale?: string, runtimeLocale?: string): string;
24
+ /** Check whether a string is a supported locale. */
25
+ export declare function isSupportedLocale(locale: string): locale is SupportedLocale;
26
+ /**
27
+ * Simple placeholder interpolation.
28
+ *
29
+ * Replaces `{key}` tokens with values from the params map.
30
+ * Does NOT handle full ICU (plurals, selects) — only simple
31
+ * string/number substitution.
32
+ */
33
+ export declare function interpolate(template: string, params?: Record<string, string | number>): string;
34
+ /**
35
+ * A package-scoped i18n instance.
36
+ *
37
+ * @typeParam K - Union type of valid message keys for the package
38
+ */
39
+ export interface I18nInstance<K extends string = string> {
40
+ /** Translate a message key with optional placeholder interpolation. */
41
+ t(key: K | string, params?: Record<string, string | number>): string;
42
+ /** The effective locale being used. */
43
+ readonly locale: string;
44
+ }
45
+ /** Configuration for `createI18nFactory`. */
46
+ export interface I18nFactoryConfig {
47
+ /** The spec key used to register translations (e.g., `"ai-agent.messages"`). */
48
+ specKey: string;
49
+ /** Translation catalogs to register (typically `[en, fr, es]`). */
50
+ catalogs: TranslationSpec[];
51
+ }
52
+ /** The object returned by `createI18nFactory`. */
53
+ export interface I18nFactory<K extends string = string> {
54
+ /** Create an i18n instance for a given locale. */
55
+ create(optionsLocale?: string, runtimeLocale?: string): I18nInstance<K>;
56
+ /** Create a default (English) i18n instance. */
57
+ getDefault(): I18nInstance<K>;
58
+ /** Reset the shared registry (useful for testing). */
59
+ resetRegistry(): void;
60
+ }
61
+ /**
62
+ * Create a package-scoped i18n factory.
63
+ *
64
+ * Replaces the ~100 lines of boilerplate previously duplicated
65
+ * in every package's `i18n/messages.ts`.
66
+ *
67
+ * @typeParam K - The package's message key union type
68
+ * @param config - Spec key and translation catalogs
69
+ * @returns An I18nFactory with `create()`, `getDefault()`, and `resetRegistry()`
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * import { createI18nFactory } from '@contractspec/lib.contracts-spec/translations';
74
+ * import type { AgentMessageKey } from './keys';
75
+ * import { enMessages } from './catalogs/en';
76
+ * import { frMessages } from './catalogs/fr';
77
+ * import { esMessages } from './catalogs/es';
78
+ *
79
+ * const factory = createI18nFactory<AgentMessageKey>({
80
+ * specKey: 'ai-agent.messages',
81
+ * catalogs: [enMessages, frMessages, esMessages],
82
+ * });
83
+ *
84
+ * export type AgentI18n = I18nInstance<AgentMessageKey>;
85
+ * export const createAgentI18n = factory.create;
86
+ * export const getDefaultI18n = factory.getDefault;
87
+ * export const resetI18nRegistry = factory.resetRegistry;
88
+ * ```
89
+ */
90
+ export declare function createI18nFactory<K extends string = string>(config: I18nFactoryConfig): I18nFactory<K>;
@@ -0,0 +1,229 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ var __toCommonJS = (from) => {
8
+ var entry = __moduleCache.get(from), desc;
9
+ if (entry)
10
+ return entry;
11
+ entry = __defProp({}, "__esModule", { value: true });
12
+ if (from && typeof from === "object" || typeof from === "function")
13
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ }));
17
+ __moduleCache.set(from, entry);
18
+ return entry;
19
+ };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
+ var __require = import.meta.require;
31
+
32
+ // src/translations/registry.ts
33
+ class TranslationRegistry {
34
+ specs = new Map;
35
+ latestByLocale = new Map;
36
+ locales = new Set;
37
+ domains = new Set;
38
+ constructor(items) {
39
+ if (items) {
40
+ for (const spec of items) {
41
+ this.register(spec);
42
+ }
43
+ }
44
+ }
45
+ register(spec) {
46
+ const fullKey = this.makeKey(spec.meta.key, spec.meta.version, spec.locale);
47
+ this.specs.set(fullKey, spec);
48
+ const localeKey = `${spec.meta.key}:${spec.locale}`;
49
+ const existing = this.latestByLocale.get(localeKey);
50
+ if (!existing || this.isNewerVersion(spec.meta.version, existing.meta.version)) {
51
+ this.latestByLocale.set(localeKey, spec);
52
+ }
53
+ this.locales.add(spec.locale);
54
+ this.domains.add(spec.meta.domain);
55
+ }
56
+ get(key, version, locale) {
57
+ return this.specs.get(this.makeKey(key, version, locale));
58
+ }
59
+ getLatest(key, locale) {
60
+ return this.latestByLocale.get(`${key}:${locale}`);
61
+ }
62
+ getMessage(specKey, messageKey, locale, version) {
63
+ const spec = version ? this.get(specKey, version, locale) : this.getLatest(specKey, locale);
64
+ if (!spec)
65
+ return;
66
+ return spec.messages[messageKey];
67
+ }
68
+ getWithFallback(specKey, messageKey, locale, fallbackLocale, version) {
69
+ const spec = version ? this.get(specKey, version, locale) : this.getLatest(specKey, locale);
70
+ if (spec) {
71
+ const message = spec.messages[messageKey];
72
+ if (message) {
73
+ return { spec, message, locale, fromFallback: false };
74
+ }
75
+ fallbackLocale = fallbackLocale ?? spec.fallback;
76
+ }
77
+ if (fallbackLocale && fallbackLocale !== locale) {
78
+ const fallbackSpec = version ? this.get(specKey, version, fallbackLocale) : this.getLatest(specKey, fallbackLocale);
79
+ if (fallbackSpec) {
80
+ const message = fallbackSpec.messages[messageKey];
81
+ if (message) {
82
+ return {
83
+ spec: fallbackSpec,
84
+ message,
85
+ locale: fallbackLocale,
86
+ fromFallback: true
87
+ };
88
+ }
89
+ }
90
+ }
91
+ return;
92
+ }
93
+ getValue(specKey, messageKey, locale, fallback) {
94
+ const result = this.getWithFallback(specKey, messageKey, locale);
95
+ return result?.message.value ?? fallback ?? messageKey;
96
+ }
97
+ listLocales(specKey) {
98
+ const locales = [];
99
+ for (const [key, spec] of this.specs.entries()) {
100
+ if (key.startsWith(`${specKey}:`)) {
101
+ if (!locales.includes(spec.locale)) {
102
+ locales.push(spec.locale);
103
+ }
104
+ }
105
+ }
106
+ return locales;
107
+ }
108
+ list() {
109
+ return [...this.specs.values()];
110
+ }
111
+ listByLocale(locale) {
112
+ return [...this.specs.values()].filter((s) => s.locale === locale);
113
+ }
114
+ listByDomain(domain) {
115
+ return [...this.specs.values()].filter((s) => s.meta.domain === domain);
116
+ }
117
+ has(key, locale, version) {
118
+ if (version) {
119
+ return this.specs.has(this.makeKey(key, version, locale));
120
+ }
121
+ return this.latestByLocale.has(`${key}:${locale}`);
122
+ }
123
+ getLocales() {
124
+ return this.locales;
125
+ }
126
+ getDomains() {
127
+ return this.domains;
128
+ }
129
+ getStats() {
130
+ let totalMessages = 0;
131
+ for (const spec of this.specs.values()) {
132
+ totalMessages += Object.keys(spec.messages).length;
133
+ }
134
+ return {
135
+ totalSpecs: this.specs.size,
136
+ locales: [...this.locales],
137
+ domains: [...this.domains],
138
+ totalMessages
139
+ };
140
+ }
141
+ clear() {
142
+ this.specs.clear();
143
+ this.latestByLocale.clear();
144
+ this.locales.clear();
145
+ this.domains.clear();
146
+ }
147
+ makeKey(key, version, locale) {
148
+ return `${key}:${version}:${locale}`;
149
+ }
150
+ isNewerVersion(a, b) {
151
+ const parseVersion = (v) => {
152
+ const parts = v.split(".").map(Number);
153
+ return {
154
+ major: parts[0] ?? 0,
155
+ minor: parts[1] ?? 0,
156
+ patch: parts[2] ?? 0
157
+ };
158
+ };
159
+ const va = parseVersion(a);
160
+ const vb = parseVersion(b);
161
+ if (va.major !== vb.major)
162
+ return va.major > vb.major;
163
+ if (va.minor !== vb.minor)
164
+ return va.minor > vb.minor;
165
+ return va.patch > vb.patch;
166
+ }
167
+ }
168
+
169
+ // src/translations/i18n-factory.ts
170
+ var DEFAULT_LOCALE = "en";
171
+ var SUPPORTED_LOCALES = ["en", "fr", "es"];
172
+ function resolveLocale(optionsLocale, runtimeLocale) {
173
+ const raw = runtimeLocale ?? optionsLocale ?? DEFAULT_LOCALE;
174
+ if (isSupportedLocale(raw))
175
+ return raw;
176
+ const base = raw.split("-")[0];
177
+ if (base && isSupportedLocale(base))
178
+ return base;
179
+ return DEFAULT_LOCALE;
180
+ }
181
+ function isSupportedLocale(locale) {
182
+ return SUPPORTED_LOCALES.includes(locale);
183
+ }
184
+ function interpolate(template, params) {
185
+ if (!params)
186
+ return template;
187
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
188
+ if (key in params)
189
+ return String(params[key]);
190
+ return match;
191
+ });
192
+ }
193
+ function createI18nFactory(config) {
194
+ let sharedRegistry = null;
195
+ function getRegistry() {
196
+ if (!sharedRegistry) {
197
+ sharedRegistry = new TranslationRegistry(config.catalogs);
198
+ }
199
+ return sharedRegistry;
200
+ }
201
+ const factory = {
202
+ create(optionsLocale, runtimeLocale) {
203
+ const locale = resolveLocale(optionsLocale, runtimeLocale);
204
+ const registry = getRegistry();
205
+ return {
206
+ locale,
207
+ t(key, params) {
208
+ const raw = registry.getValue(config.specKey, key, locale, key);
209
+ return interpolate(raw, params);
210
+ }
211
+ };
212
+ },
213
+ getDefault() {
214
+ return factory.create(DEFAULT_LOCALE);
215
+ },
216
+ resetRegistry() {
217
+ sharedRegistry = null;
218
+ }
219
+ };
220
+ return factory;
221
+ }
222
+ export {
223
+ resolveLocale,
224
+ isSupportedLocale,
225
+ interpolate,
226
+ createI18nFactory,
227
+ SUPPORTED_LOCALES,
228
+ DEFAULT_LOCALE
229
+ };
@@ -42,3 +42,4 @@ export * from './registry';
42
42
  export * from './validation';
43
43
  export type { TranslationEntry, TranslationCatalogMeta, PlatformTranslationCatalog, BlueprintTranslationCatalog, } from './catalog';
44
44
  export * from './tenant';
45
+ export * from './i18n-factory';