@xndrjs/i18n 0.8.0 → 0.8.1
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/dist/index.d.ts +20 -1
- package/dist/index.js +56 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/IcuTranslationProviderMulti.ts +12 -0
- package/src/builder-load-registry.test.ts +44 -0
- package/src/builder-load-registry.ts +47 -8
- package/src/builder-types.test.ts +9 -1
- package/src/builder.test.ts +43 -0
- package/src/builder.ts +5 -0
- package/src/i18n-handle.ts +17 -3
- package/src/index.ts +5 -0
- package/src/load-state.ts +15 -0
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,20 @@ type DeliveryArtifactsMap<RequestLocales extends string, DeliveryArea extends st
|
|
|
7
7
|
/** When a delivery-area map is configured, resolve partition locales; otherwise keep the full pool. */
|
|
8
8
|
type LocalesForDeliveryAreaOrAll<RequestLocales extends string, DeliveryArea extends string, DeliveryArtifacts extends DeliveryArtifactsMap<RequestLocales, DeliveryArea>, Area extends string> = [DeliveryArea] extends [never] ? RequestLocales : Area extends DeliveryArea ? DeliveryArtifacts[Area][number] : never;
|
|
9
9
|
|
|
10
|
+
/** Status of a lazy builder resource (`namespace` × partition). */
|
|
11
|
+
type I18nResourceLoadStatus = "pending" | "loaded" | "error";
|
|
12
|
+
/** One tracked resource entry in {@link I18nLoadState}. */
|
|
13
|
+
interface I18nResourceLoadRecord {
|
|
14
|
+
namespace: string;
|
|
15
|
+
partition: string;
|
|
16
|
+
status: I18nResourceLoadStatus;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** Global snapshot of resource loads attempted on an i18n handle. */
|
|
20
|
+
interface I18nLoadState {
|
|
21
|
+
resources: readonly I18nResourceLoadRecord[];
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
/** Serialized builder resource: namespace + partition (locale / delivery area). */
|
|
11
25
|
type BuilderResourceEntry = readonly [namespace: string, partition: string];
|
|
12
26
|
|
|
@@ -118,7 +132,10 @@ declare class IcuTranslationProviderMulti<Schema extends MultiDictionary, Params
|
|
|
118
132
|
/** Live dictionary reference (loaded namespaces only). Do not mutate. */
|
|
119
133
|
getAll(): PartialMultiDictionary<Schema, RequestLocales>;
|
|
120
134
|
hasBuilderResourceLoaded(namespace: string, partition: string): boolean;
|
|
135
|
+
markBuilderResourcePending(namespace: string, partition: string): void;
|
|
121
136
|
markBuilderResourceLoaded(namespace: string, partition: string): void;
|
|
137
|
+
markBuilderResourceError(namespace: string, partition: string, error: unknown): void;
|
|
138
|
+
getBuilderLoadState(): I18nLoadState;
|
|
122
139
|
seedBuilderResources(resources: readonly BuilderResourceEntry[]): void;
|
|
123
140
|
serialize(): I18nSerializedState<PartialMultiDictionary<Schema, RequestLocales>>;
|
|
124
141
|
applyLoadMergeNamespace<NS extends keyof Schema>(namespace: NS, values: PartialKeyDictionary<Schema[NS], RequestLocales>): void;
|
|
@@ -189,6 +206,8 @@ interface I18nHandle<Schema extends MultiDictionary, Params extends MultiParams<
|
|
|
189
206
|
dictionary: PartialMultiDictionary<Schema, RequestLocales>;
|
|
190
207
|
resources: readonly (readonly [string, string])[];
|
|
191
208
|
};
|
|
209
|
+
/** Snapshot of resource loads attempted on this handle (pending / loaded / error). */
|
|
210
|
+
getLoadState(): I18nLoadState;
|
|
192
211
|
}
|
|
193
212
|
declare function createI18nHandle<Schema extends MultiDictionary, Params extends MultiParams<Schema>, RequestLocales extends string = LocaleOfMulti<Schema>>(engine: HandleEngine<Schema, Params, RequestLocales>, options?: I18nHandleOptions<Schema, RequestLocales>): I18nHandle<Schema, Params, RequestLocales>;
|
|
194
213
|
|
|
@@ -229,4 +248,4 @@ type DeliveryResourceId = {
|
|
|
229
248
|
*/
|
|
230
249
|
type FetchArtifact = (id: DeliveryResourceId) => Promise<unknown>;
|
|
231
250
|
|
|
232
|
-
export { type BuilderResourceEntry, type DeliveryArtifactsMap, type DeliveryResourceId, type FetchArtifact, type I18nCreateInput, type I18nEngineMulti, type I18nEngineMultiImpl, type I18nHandle, type I18nHandleOptions, type I18nScopeMulti, type I18nScopeMultiForLocale, type I18nSerializedState, IcuTranslationProviderMulti, type IcuTranslationProviderOptions, type KeyDictionary, type LoadNamespacesInput, type LocaleDictionary, type LocaleFallbackMap$1 as LocaleFallbackMap, type LocaleOfKeys, type LocaleOfMulti, type LocalesForDeliveryArea, type LocalesForDeliveryAreaOrAll, type MissingTranslationContext, type MultiDictionary, type MultiParams, type NamespaceBoundTranslateFn, type NamespaceLoader, type OnMissingTranslation, type ParamsForNamespaces, type PartialKeyDictionary, type PartialMultiDictionary, type PartitionForLocale, type PartitionedLoader, type ResolvedLocaleTemplate, type SchemaForNamespaces, type ScopeForLocale, createI18nHandle, formatLocaleFallbackChain, invokeNamespaceLoader, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, normalizeI18nCreateInput, resolveLocaleTemplate, validateLocaleFallback };
|
|
251
|
+
export { type BuilderResourceEntry, type DeliveryArtifactsMap, type DeliveryResourceId, type FetchArtifact, type I18nCreateInput, type I18nEngineMulti, type I18nEngineMultiImpl, type I18nHandle, type I18nHandleOptions, type I18nLoadState, type I18nResourceLoadRecord, type I18nResourceLoadStatus, type I18nScopeMulti, type I18nScopeMultiForLocale, type I18nSerializedState, IcuTranslationProviderMulti, type IcuTranslationProviderOptions, type KeyDictionary, type LoadNamespacesInput, type LocaleDictionary, type LocaleFallbackMap$1 as LocaleFallbackMap, type LocaleOfKeys, type LocaleOfMulti, type LocalesForDeliveryArea, type LocalesForDeliveryAreaOrAll, type MissingTranslationContext, type MultiDictionary, type MultiParams, type NamespaceBoundTranslateFn, type NamespaceLoader, type OnMissingTranslation, type ParamsForNamespaces, type PartialKeyDictionary, type PartialMultiDictionary, type PartitionForLocale, type PartitionedLoader, type ResolvedLocaleTemplate, type SchemaForNamespaces, type ScopeForLocale, createI18nHandle, formatLocaleFallbackChain, invokeNamespaceLoader, mergeDictionaryLocalesCore, mergeNamespaceLocalesCore, normalizeI18nCreateInput, resolveLocaleTemplate, validateLocaleFallback };
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,9 @@ var I18nHandleImpl = class {
|
|
|
28
28
|
serialize() {
|
|
29
29
|
return this.engine.serialize();
|
|
30
30
|
}
|
|
31
|
+
getLoadState() {
|
|
32
|
+
return this.engine.getBuilderLoadState();
|
|
33
|
+
}
|
|
31
34
|
};
|
|
32
35
|
function createI18nHandle(engine, options) {
|
|
33
36
|
return new I18nHandleImpl(engine, options);
|
|
@@ -59,9 +62,15 @@ async function applyHandleLoad(engine, options, input, partitionForLocale) {
|
|
|
59
62
|
if (engine.hasBuilderResourceLoaded(namespace, partition)) {
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
engine.markBuilderResourcePending(namespace, partition);
|
|
66
|
+
try {
|
|
67
|
+
const data = await invokeNamespaceLoader(loader, partition, { locale: input.locale });
|
|
68
|
+
engine.applyLoadMergeNamespace(namespace, data);
|
|
69
|
+
engine.markBuilderResourceLoaded(namespace, partition);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
engine.markBuilderResourceError(namespace, partition, error);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
65
74
|
})
|
|
66
75
|
);
|
|
67
76
|
}
|
|
@@ -79,26 +88,54 @@ function formatBuilderResourceKey(namespace, partition) {
|
|
|
79
88
|
return `${namespace}\0${partition}`;
|
|
80
89
|
}
|
|
81
90
|
var BuilderLoadRegistry = class {
|
|
82
|
-
|
|
91
|
+
states = /* @__PURE__ */ new Map();
|
|
83
92
|
has(namespace, partition) {
|
|
84
|
-
return this.
|
|
93
|
+
return this.states.get(formatBuilderResourceKey(namespace, partition))?.status === "loaded";
|
|
94
|
+
}
|
|
95
|
+
markPending(namespace, partition) {
|
|
96
|
+
this.setState(namespace, partition, { status: "pending" });
|
|
85
97
|
}
|
|
86
98
|
mark(namespace, partition) {
|
|
87
|
-
this.
|
|
99
|
+
this.markLoaded(namespace, partition);
|
|
100
|
+
}
|
|
101
|
+
markLoaded(namespace, partition) {
|
|
102
|
+
this.setState(namespace, partition, { status: "loaded" });
|
|
103
|
+
}
|
|
104
|
+
markError(namespace, partition, error) {
|
|
105
|
+
this.setState(namespace, partition, { status: "error", error });
|
|
88
106
|
}
|
|
89
107
|
/** Snapshot of loaded resources as `[namespace, partition]` tuples. */
|
|
90
108
|
entries() {
|
|
91
109
|
const out = [];
|
|
92
|
-
for (const
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
for (const record of this.states.values()) {
|
|
111
|
+
if (record.status === "loaded") {
|
|
112
|
+
out.push([record.namespace, record.partition]);
|
|
113
|
+
}
|
|
95
114
|
}
|
|
96
115
|
return out;
|
|
97
116
|
}
|
|
98
117
|
seed(resources) {
|
|
99
118
|
for (const [namespace, partition] of resources) {
|
|
100
|
-
this.
|
|
119
|
+
this.markLoaded(namespace, partition);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
getLoadState() {
|
|
123
|
+
const resources = [...this.states.values()].sort((a, b) => {
|
|
124
|
+
const byNamespace = a.namespace.localeCompare(b.namespace);
|
|
125
|
+
return byNamespace !== 0 ? byNamespace : a.partition.localeCompare(b.partition);
|
|
126
|
+
});
|
|
127
|
+
return { resources };
|
|
128
|
+
}
|
|
129
|
+
setState(namespace, partition, patch) {
|
|
130
|
+
const record = {
|
|
131
|
+
namespace,
|
|
132
|
+
partition,
|
|
133
|
+
status: patch.status
|
|
134
|
+
};
|
|
135
|
+
if (patch.error !== void 0) {
|
|
136
|
+
record.error = patch.error;
|
|
101
137
|
}
|
|
138
|
+
this.states.set(formatBuilderResourceKey(namespace, partition), record);
|
|
102
139
|
}
|
|
103
140
|
};
|
|
104
141
|
|
|
@@ -340,9 +377,18 @@ var IcuTranslationProviderMulti = class {
|
|
|
340
377
|
hasBuilderResourceLoaded(namespace, partition) {
|
|
341
378
|
return this.builderLoadRegistry.has(namespace, partition);
|
|
342
379
|
}
|
|
380
|
+
markBuilderResourcePending(namespace, partition) {
|
|
381
|
+
this.builderLoadRegistry.markPending(namespace, partition);
|
|
382
|
+
}
|
|
343
383
|
markBuilderResourceLoaded(namespace, partition) {
|
|
344
384
|
this.builderLoadRegistry.mark(namespace, partition);
|
|
345
385
|
}
|
|
386
|
+
markBuilderResourceError(namespace, partition, error) {
|
|
387
|
+
this.builderLoadRegistry.markError(namespace, partition, error);
|
|
388
|
+
}
|
|
389
|
+
getBuilderLoadState() {
|
|
390
|
+
return this.builderLoadRegistry.getLoadState();
|
|
391
|
+
}
|
|
346
392
|
seedBuilderResources(resources) {
|
|
347
393
|
this.builderLoadRegistry.seed(resources);
|
|
348
394
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/builder-loaders.ts","../src/i18n-handle.ts","../src/serialized-state.ts","../src/builder-load-registry.ts","../src/resolve-locale.ts","../src/format-core.ts","../src/project-locales.ts","../src/scope-multi.ts","../src/IcuTranslationProviderMulti.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** Split-by-locale or custom-area loader — partition key is locale or area name. */\nexport type PartitionedLoader<T> = (\n partition: string,\n context: { locale: string }\n) => Promise<T> | T;\n\nexport type NamespaceLoader<T> = PartitionedLoader<T>;\n\nexport async function invokeNamespaceLoader<T>(\n loader: NamespaceLoader<T>,\n partition: string,\n context: { locale: string }\n): Promise<T> {\n return await loader(partition, context);\n}\n","import { invokeNamespaceLoader, type NamespaceLoader } from \"./builder-loaders.js\";\nimport type { I18nEngineMultiImpl } from \"./engine.js\";\nimport type {\n LocaleFallbackMap,\n LocaleOfMulti,\n MultiDictionary,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\nimport type { I18nScopeMultiForLocale } from \"./scope-multi.js\";\nimport type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from \"./scope-types.js\";\n\n/** Engine handle accepted by {@link createI18nHandle} (with or without localeFallback). */\ntype HandleEngine<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n> = I18nEngineMultiImpl<Schema, Params, RequestLocales, LocaleFallbackMap | undefined>;\n\n/** Non-empty namespace list — required by {@link I18nHandle.load} / {@link I18nHandle.peek}. */\ntype NonEmptyNamespaces<NS extends readonly string[]> = NS extends readonly [] ? never : NS;\n\n/** Maps a request locale to the delivery partition key (locale itself, or a custom area). */\nexport type PartitionForLocale<RequestLocales extends string> = (locale: RequestLocales) => string;\n\nexport type I18nHandleOptions<\n Schema extends MultiDictionary,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> = {\n namespaceLoaders?: {\n [NS in keyof Schema & string]?: NamespaceLoader<\n PartialKeyDictionary<Schema[NS], RequestLocales> | Schema[NS]\n >;\n };\n /**\n * Resolves the loader partition for a locale.\n * Default: identity (`locale → locale`) for split-by-locale.\n * Custom delivery: codegen injects `locale → LOCALE_DELIVERY_AREA[locale]`.\n */\n partitionForLocale?: PartitionForLocale<RequestLocales>;\n};\n\nexport type LoadNamespacesInput<\n Schema extends MultiDictionary,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends string,\n> = {\n namespaces: NonEmptyNamespaces<NsList>;\n locale: Locale;\n};\n\n/** Locale-bound scope returned by {@link I18nHandle.load} / {@link I18nHandle.peek}. */\nexport type ScopeForLocale<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n> = I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n>;\n\n/**\n * Single runtime handle: pass `namespaces` + `locale`; partition is derived via\n * {@link I18nHandleOptions.partitionForLocale}.\n */\nexport interface I18nHandle<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> {\n load<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): Promise<ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale>>;\n\n /**\n * Sync scope when every requested resource is already marked loaded (e.g. hydrate seed).\n * Returns `null` when a normal async {@link load} is still required.\n */\n peek<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale> | null;\n\n /** Dictionary (loaded namespaces) + resources for SSR→CSR hydrate. */\n serialize(): {\n dictionary: PartialMultiDictionary<Schema, RequestLocales>;\n resources: readonly (readonly [string, string])[];\n };\n}\n\nexport class I18nHandleImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> implements I18nHandle<Schema, Params, RequestLocales> {\n private readonly partitionForLocale: PartitionForLocale<RequestLocales>;\n\n constructor(\n private readonly engine: HandleEngine<Schema, Params, RequestLocales>,\n private readonly options?: I18nHandleOptions<Schema, RequestLocales>\n ) {\n this.partitionForLocale = options?.partitionForLocale ?? ((locale) => locale);\n }\n\n async load<\n const NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): Promise<ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale>> {\n await applyHandleLoad(this.engine, this.options, input, this.partitionForLocale);\n return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);\n }\n\n peek<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale> | null {\n if (!areHandleResourcesReady(this.engine, this.options, input, this.partitionForLocale)) {\n return null;\n }\n return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);\n }\n\n serialize() {\n return this.engine.serialize();\n }\n}\n\nexport function createI18nHandle<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options?: I18nHandleOptions<Schema, RequestLocales>\n): I18nHandle<Schema, Params, RequestLocales> {\n return new I18nHandleImpl(engine, options);\n}\n\nfunction areHandleResourcesReady<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options: I18nHandleOptions<Schema, RequestLocales> | undefined,\n input: LoadNamespacesInput<Schema, NsList, Locale>,\n partitionForLocale: PartitionForLocale<RequestLocales>\n): boolean {\n if (input.namespaces.length === 0) {\n return false;\n }\n const partition = partitionForLocale(input.locale);\n return input.namespaces.every((namespace) => {\n const loader = options?.namespaceLoaders?.[namespace];\n if (loader === undefined) {\n return true;\n }\n return engine.hasBuilderResourceLoaded(namespace, partition);\n });\n}\n\nasync function applyHandleLoad<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options: I18nHandleOptions<Schema, RequestLocales> | undefined,\n input: LoadNamespacesInput<Schema, NsList, Locale>,\n partitionForLocale: PartitionForLocale<RequestLocales>\n): Promise<void> {\n if (input.namespaces.length === 0) {\n throw new Error(\"[i18n] load() requires a non-empty namespaces array.\");\n }\n\n const partition = partitionForLocale(input.locale);\n\n await Promise.all(\n input.namespaces.map(async (namespace) => {\n const loader = options?.namespaceLoaders?.[namespace];\n if (loader === undefined) {\n return;\n }\n\n if (engine.hasBuilderResourceLoaded(namespace, partition)) {\n return;\n }\n\n const data = await invokeNamespaceLoader(loader, partition, { locale: input.locale });\n engine.applyLoadMergeNamespace(namespace, data);\n engine.markBuilderResourceLoaded(namespace, partition);\n })\n );\n}\n","import type { BuilderResourceEntry } from \"./builder-load-registry.js\";\n\n/**\n * Portable i18n instance state after `serialize()`: dictionary strings + which\n * builder resources are already loaded (namespace × partition).\n */\nexport interface I18nSerializedState<Dictionary = Record<string, unknown>> {\n dictionary: Dictionary;\n resources: readonly BuilderResourceEntry[];\n}\n\n/**\n * Input nested under generated `createI18n({ state? })`. Omitted `state` defaults to an empty\n * dictionary (lazy cold start). `resources` may be omitted or `[]`.\n */\nexport type I18nCreateInput<Dictionary = Record<string, unknown>> = {\n dictionary: Dictionary;\n resources?: readonly BuilderResourceEntry[];\n};\n\nexport function normalizeI18nCreateInput<Dictionary>(\n value?: I18nCreateInput<Dictionary>\n): I18nSerializedState<Dictionary> {\n return {\n dictionary: value?.dictionary ?? ({} as Dictionary),\n resources: value?.resources ?? [],\n };\n}\n","/** Stable key for a lazy-loaded builder resource (namespace + locale or delivery area). */\nexport function formatBuilderResourceKey(namespace: string, partition: string): string {\n return `${namespace}\\0${partition}`;\n}\n\n/** Serialized builder resource: namespace + partition (locale / delivery area). */\nexport type BuilderResourceEntry = readonly [namespace: string, partition: string];\n\n/**\n * Dedup cache for lazy-loaded builder resources (`namespace` × partition).\n * A repeated `load()` for the same resource skips the loader (avoids re-fetch / re-merge).\n */\nexport class BuilderLoadRegistry {\n private readonly loaded = new Set<string>();\n\n has(namespace: string, partition: string): boolean {\n return this.loaded.has(formatBuilderResourceKey(namespace, partition));\n }\n\n mark(namespace: string, partition: string): void {\n this.loaded.add(formatBuilderResourceKey(namespace, partition));\n }\n\n /** Snapshot of loaded resources as `[namespace, partition]` tuples. */\n entries(): BuilderResourceEntry[] {\n const out: BuilderResourceEntry[] = [];\n for (const key of this.loaded) {\n const sep = key.indexOf(\"\\0\");\n out.push([key.slice(0, sep), key.slice(sep + 1)]);\n }\n return out;\n }\n\n seed(resources: readonly BuilderResourceEntry[]): void {\n for (const [namespace, partition] of resources) {\n this.mark(namespace, partition);\n }\n }\n}\n","export type LocaleFallbackMap = Record<string, string | null>;\n\nexport interface ResolvedLocaleTemplate {\n template: string;\n resolvedLocale: string;\n fallbackChain: readonly string[];\n}\n\nexport function validateLocaleFallback(map: LocaleFallbackMap): void {\n for (const startLocale of Object.keys(map)) {\n const visited = new Set<string>();\n let current: string | undefined = startLocale;\n\n while (current !== undefined) {\n if (visited.has(current)) {\n throw new Error(`[i18n] Circular locale fallback detected involving \"${current}\"`);\n }\n visited.add(current);\n\n const next: string | null | undefined = map[current];\n if (next === undefined || next === null) {\n break;\n }\n current = next;\n }\n }\n}\n\nexport function resolveLocaleTemplate(\n localeByKey: Record<string, string | undefined> | undefined,\n requestedLocale: string,\n fallbackMap?: LocaleFallbackMap\n): ResolvedLocaleTemplate | undefined {\n const chain: string[] = [requestedLocale];\n let currentLocale = requestedLocale;\n\n while (true) {\n const template = localeByKey?.[currentLocale];\n if (template !== undefined) {\n return { template, resolvedLocale: currentLocale, fallbackChain: chain };\n }\n\n if (!fallbackMap) {\n return undefined;\n }\n\n const fallback = fallbackMap[currentLocale];\n if (fallback === undefined || fallback === null) {\n return undefined;\n }\n\n if (chain.includes(fallback)) {\n throw new Error(\n `[i18n] Circular locale fallback detected: ${[...chain, fallback].join(\" → \")}`\n );\n }\n\n chain.push(fallback);\n currentLocale = fallback;\n }\n}\n\nexport function formatLocaleFallbackChain(\n requestedLocale: string,\n fallbackMap?: LocaleFallbackMap\n): string {\n if (!fallbackMap) {\n return requestedLocale;\n }\n\n const chain: string[] = [requestedLocale];\n let current = requestedLocale;\n\n while (true) {\n const fallback = fallbackMap[current];\n if (fallback === undefined || fallback === null) {\n break;\n }\n if (chain.includes(fallback)) {\n chain.push(fallback);\n break;\n }\n chain.push(fallback);\n current = fallback;\n }\n\n return chain.join(\" → \");\n}\n","import { IntlMessageFormat } from \"intl-messageformat\";\nimport { formatLocaleFallbackChain, resolveLocaleTemplate } from \"./resolve-locale.js\";\nimport type { LocaleCache, LocaleFallbackMap, OnMissingTranslation } from \"./types.js\";\n\nexport type FormatCoreContext = {\n key: string;\n namespace?: string | undefined;\n locale: string;\n localeFallback?: LocaleFallbackMap | undefined;\n onMissing: OnMissingTranslation;\n};\n\nexport type ResolveAndFormatOptions = {\n localeByKey: Record<string, string | undefined> | undefined;\n locale: string;\n params?: Record<string, unknown> | undefined;\n getCache: (resolvedLocale: string) => LocaleCache;\n context: FormatCoreContext;\n};\n\nfunction formatErrorLabel(context: Pick<FormatCoreContext, \"key\" | \"namespace\">): string {\n if (context.namespace !== undefined) {\n return `namespace \"${context.namespace}\", key \"${context.key}\"`;\n }\n return `key \"${context.key}\"`;\n}\n\nfunction formatMissingError(context: FormatCoreContext): string {\n const chain = formatLocaleFallbackChain(context.locale, context.localeFallback);\n if (context.namespace !== undefined) {\n return `[i18n] Missing key or locale: namespace \"${context.namespace}\", key \"${context.key}\" [${context.locale}] (fallback chain: ${chain})`;\n }\n return `[i18n] Missing key or locale: \"${context.key}\" [${context.locale}] (fallback chain: ${chain})`;\n}\n\nfunction resolveMissing(context: FormatCoreContext): string {\n const chain = formatLocaleFallbackChain(context.locale, context.localeFallback);\n if (context.onMissing === \"key\") {\n if (context.namespace !== undefined) {\n return `${context.namespace}.${context.key}`;\n }\n return context.key;\n }\n if (typeof context.onMissing === \"function\") {\n return context.onMissing({\n ...(context.namespace !== undefined ? { namespace: context.namespace } : {}),\n key: context.key,\n locale: context.locale,\n fallbackChain: chain,\n });\n }\n throw new Error(formatMissingError(context));\n}\n\n/**\n * Shared runtime path for both ICU providers: resolve locale (with fallback) →\n * compile template (cached) → format with params; handles missing keys via `onMissing`.\n */\nexport function resolveAndFormat(options: ResolveAndFormatOptions): string {\n const { localeByKey, locale, params, getCache, context } = options;\n const resolved = resolveLocaleTemplate(localeByKey, locale, context.localeFallback);\n\n if (!resolved) {\n return resolveMissing(context);\n }\n\n const { template, resolvedLocale } = resolved;\n const cache = getCache(resolvedLocale);\n const cacheKey = context.key;\n const label = formatErrorLabel(context);\n\n if (!cache[cacheKey]) {\n try {\n cache[cacheKey] = new IntlMessageFormat(template, resolvedLocale);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[i18n ICU Syntax Error] Dictionary error for ${label} [${resolvedLocale}]: ${message}`\n );\n }\n }\n\n try {\n return cache[cacheKey].format(params) as string;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[i18n Formatting Error] Invalid or missing parameters for ${label} [${resolvedLocale}]: ${message}`\n );\n }\n}\n","import { resolveLocaleTemplate } from \"./resolve-locale.js\";\nimport type {\n KeyDictionary,\n LocaleFallbackMap,\n LocaleOfMulti,\n LocaleOfKeys,\n MultiDictionary,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\n\nfunction classifyAreaLocales(\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): { full: string[]; preserve: string[] } {\n const areaSet = new Set(areaLocales);\n const full: string[] = [];\n const preserve: string[] = [];\n\n for (const locale of areaLocales) {\n const fallback = localeFallback?.[locale];\n if (fallback !== null && fallback !== undefined && areaSet.has(fallback)) {\n preserve.push(locale);\n } else {\n full.push(locale);\n }\n }\n\n return { full, preserve };\n}\n\nfunction projectKeyDictionary<T extends KeyDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const targetLocales = [...new Set(locales)];\n const result = {} as T;\n\n for (const [key, localesByKey] of Object.entries(dictionary)) {\n const projected: Record<string, string> = {};\n\n for (const locale of targetLocales) {\n const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);\n if (resolved !== undefined) {\n projected[locale] = resolved.template;\n }\n }\n\n if (Object.keys(projected).length > 0) {\n result[key as keyof T] = projected as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Builds a namespace dictionary that contains only the requested locales.\n * For each key and target locale, copies the direct template when present;\n * otherwise resolves through `localeFallback` (same rules as `.get()` at runtime).\n */\nexport function projectNamespaceLocalesCore<T extends KeyDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n return projectKeyDictionary(dictionary, locales, localeFallback);\n}\n\n/**\n * Projects a single namespace for a custom delivery area.\n * Full locales resolve through `localeFallback` (e.g. external chains like `it → en-US`);\n * preserve locales copy only their direct canonical entry when present.\n */\nexport function projectNamespaceForDeliveryAreaCore<T extends KeyDictionary>(\n dictionary: T,\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const { full, preserve } = classifyAreaLocales(areaLocales, localeFallback);\n const fullProjection =\n full.length > 0 ? projectKeyDictionary(dictionary, full, localeFallback) : undefined;\n const result = {} as T;\n\n for (const [key, localesByKey] of Object.entries(dictionary)) {\n const projected: Record<string, string> = {};\n\n if (fullProjection !== undefined) {\n const fullEntry = fullProjection[key as keyof T];\n if (fullEntry !== undefined) {\n Object.assign(projected, fullEntry);\n }\n }\n\n for (const locale of preserve) {\n const template = localesByKey[locale];\n if (template !== undefined) {\n projected[locale] = template;\n }\n }\n\n if (Object.keys(projected).length > 0) {\n result[key as keyof T] = projected as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Projects a multi-namespace dictionary for a custom delivery area.\n * Each namespace uses the same delivery-area rules as `projectNamespaceForDeliveryAreaCore`.\n */\nexport function projectDictionaryForDeliveryAreaCore<T extends MultiDictionary>(\n dictionary: T,\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const result = {} as T;\n\n for (const namespace of Object.keys(dictionary) as (keyof T & string)[]) {\n const namespaceDictionary = dictionary[namespace];\n if (namespaceDictionary === undefined) {\n continue;\n }\n\n result[namespace] = projectNamespaceForDeliveryAreaCore(\n namespaceDictionary,\n areaLocales,\n localeFallback\n ) as T[typeof namespace];\n }\n\n return result;\n}\n\n/**\n * Projects every namespace in a multi-namespace dictionary.\n */\nexport function projectDictionaryLocalesCore<T extends MultiDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const result = {} as T;\n\n for (const namespace of Object.keys(dictionary) as (keyof T & string)[]) {\n const namespaceDictionary = dictionary[namespace];\n if (namespaceDictionary === undefined) {\n continue;\n }\n\n result[namespace] = projectKeyDictionary(\n namespaceDictionary,\n locales,\n localeFallback\n ) as T[typeof namespace];\n }\n\n return result;\n}\n\n/**\n * Merges locale entries per translation key without dropping keys or locales\n * already present in `existing`. Incoming locales overwrite the same locale on conflict.\n */\nexport function mergeNamespaceLocalesCore<\n T extends KeyDictionary,\n Locales extends string = LocaleOfKeys<T>,\n>(existing: T, incoming: PartialKeyDictionary<T, Locales>): T {\n const merged = structuredClone(existing);\n\n for (const [key, incomingLocales] of Object.entries(incoming)) {\n if (incomingLocales === undefined || typeof incomingLocales !== \"object\") {\n continue;\n }\n\n merged[key as keyof T] = {\n ...(merged[key as keyof T] ?? {}),\n ...incomingLocales,\n } as T[keyof T];\n }\n\n return merged;\n}\n\n/**\n * Merges locale entries per key in every namespace present in `incoming`.\n * Namespaces and keys not in `incoming` are preserved from `existing`.\n */\nexport function mergeDictionaryLocalesCore<\n T extends MultiDictionary,\n Locales extends string = LocaleOfMulti<T>,\n>(existing: T, incoming: PartialMultiDictionary<T, Locales>): T {\n const merged = structuredClone(existing) as T;\n\n for (const namespace of Object.keys(incoming) as (keyof T & string)[]) {\n const incomingNamespace = incoming[namespace];\n if (incomingNamespace === undefined) {\n continue;\n }\n\n merged[namespace] = mergeNamespaceLocalesCore(\n (merged[namespace] ?? {}) as T[typeof namespace],\n incomingNamespace as T[typeof namespace]\n ) as T[typeof namespace];\n }\n\n return merged;\n}\n","import type { IcuTranslationProviderMulti } from \"./IcuTranslationProviderMulti.js\";\nimport type { LocaleFallbackMap, LocaleOfMulti, MultiDictionary } from \"./types.js\";\nimport type { MultiParams, ParamArgs } from \"./scope-types.js\";\n\n/** Type-safe translation scope for a multi-namespace schema subset. */\nexport interface I18nScopeMulti<\n Schema extends MultiDictionary,\n Params,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> {\n t<\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n locale: RequestLocales,\n ...params: ParamArgs<Params[NS][K]>\n ): string;\n forLocale<Locale extends RequestLocales>(\n locale: Locale\n ): I18nScopeMultiForLocale<Schema, Params, RequestLocales, Locale>;\n}\n\n/** Multi-namespace scope with a bound locale — `t(namespace, key)` without a locale argument. */\nexport interface I18nScopeMultiForLocale<\n Schema extends MultiDictionary,\n Params,\n RequestLocales extends string,\n Locale extends RequestLocales,\n> {\n readonly locale: Locale;\n t<\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n ...params: ParamArgs<Params[NS][K]>\n ): string;\n forLocale<Next extends RequestLocales>(\n locale: Next\n ): I18nScopeMultiForLocale<Schema, Params, RequestLocales, Next>;\n}\n\nexport class I18nScopeMultiImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n Fallback extends LocaleFallbackMap | undefined,\n> implements I18nScopeMulti<Schema, Params, RequestLocales> {\n constructor(\n private readonly engine: IcuTranslationProviderMulti<Schema, Params, RequestLocales, Fallback>\n ) {}\n\n t = <\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n locale: RequestLocales,\n ...args: ParamArgs<Params[NS][K]>\n ): string => {\n const params = args[0] as Record<string, unknown> | undefined;\n return this.engine.getWithLocale(String(namespace), String(key), locale, params);\n };\n\n forLocale = <Locale extends RequestLocales>(\n locale: Locale\n ): I18nScopeMultiForLocaleImpl<Schema, Params, RequestLocales, Locale, Fallback> => {\n return new I18nScopeMultiForLocaleImpl(this.engine, locale);\n };\n}\n\nexport class I18nScopeMultiForLocaleImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n Locale extends RequestLocales,\n Fallback extends LocaleFallbackMap | undefined,\n> implements I18nScopeMultiForLocale<Schema, Params, RequestLocales, Locale> {\n constructor(\n private readonly engine: IcuTranslationProviderMulti<Schema, Params, RequestLocales, Fallback>,\n readonly locale: Locale\n ) {}\n\n t = <\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n ...args: ParamArgs<Params[NS][K]>\n ): string => {\n const params = args[0] as Record<string, unknown> | undefined;\n return this.engine.getWithLocale(String(namespace), String(key), this.locale, params);\n };\n\n forLocale = <Next extends RequestLocales>(\n locale: Next\n ): I18nScopeMultiForLocaleImpl<Schema, Params, RequestLocales, Next, Fallback> => {\n return new I18nScopeMultiForLocaleImpl(this.engine, locale);\n };\n}\n","import { BuilderLoadRegistry, type BuilderResourceEntry } from \"./builder-load-registry.js\";\nimport type { I18nEngineMulti } from \"./engine.js\";\nimport type { I18nSerializedState } from \"./serialized-state.js\";\nimport { resolveAndFormat } from \"./format-core.js\";\nimport { mergeNamespaceLocalesCore } from \"./project-locales.js\";\nimport { validateLocaleFallback } from \"./resolve-locale.js\";\nimport type {\n IcuTranslationProviderOptions,\n LocaleFallbackMap,\n LocaleOfMulti,\n MultiCompiledCache,\n MultiDictionary,\n OnMissingTranslation,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\nimport { I18nScopeMultiForLocaleImpl, I18nScopeMultiImpl } from \"./scope-multi.js\";\nimport type { I18nScopeMulti, I18nScopeMultiForLocale } from \"./scope-multi.js\";\nimport type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from \"./scope-types.js\";\n\nexport class IcuTranslationProviderMulti<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n Fallback extends LocaleFallbackMap | undefined = undefined,\n> implements I18nEngineMulti<Schema, Params, RequestLocales> {\n readonly __i18nEngineMode = \"multi\" as const;\n\n /** Loaded namespaces only — cold start is `{}`; never claim full Schema until loaded. */\n private dictionary: PartialMultiDictionary<Schema, RequestLocales>;\n private compiledCache: MultiCompiledCache = {};\n private readonly builderLoadRegistry = new BuilderLoadRegistry();\n private readonly localeFallback?: Fallback;\n private readonly onMissing: OnMissingTranslation;\n\n constructor(\n dictionary: PartialMultiDictionary<Schema, RequestLocales> = {},\n options?: IcuTranslationProviderOptions<Fallback>\n ) {\n this.dictionary = structuredClone(dictionary);\n if (options?.localeFallback) {\n validateLocaleFallback(options.localeFallback);\n this.localeFallback = options.localeFallback;\n }\n this.onMissing = options?.onMissing ?? \"throw\";\n }\n\n getWithLocale(\n namespace: string,\n key: string,\n locale: string,\n params?: Record<string, unknown>\n ): string {\n const localeByKey = (\n this.dictionary[namespace as keyof Schema] as\n | Record<string, Record<string, string>>\n | undefined\n )?.[key];\n\n return resolveAndFormat({\n localeByKey,\n locale,\n params,\n getCache: (resolvedLocale) => {\n if (!this.compiledCache[resolvedLocale]) {\n this.compiledCache[resolvedLocale] = {};\n }\n if (!this.compiledCache[resolvedLocale]![namespace]) {\n this.compiledCache[resolvedLocale]![namespace] = {};\n }\n return this.compiledCache[resolvedLocale]![namespace]!;\n },\n context: {\n namespace,\n key,\n locale,\n localeFallback: this.localeFallback,\n onMissing: this.onMissing,\n },\n });\n }\n\n toScope<\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(options: {\n namespaces: NsList;\n locale: Locale;\n }): I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n >;\n toScope<NsList extends readonly (keyof Schema & string)[]>(options: {\n namespaces: NsList;\n }): I18nScopeMulti<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales\n >;\n toScope<\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(options: { namespaces: NsList; locale?: Locale }) {\n if (options.locale !== undefined) {\n return new I18nScopeMultiForLocaleImpl(this, options.locale) as I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n >;\n }\n return new I18nScopeMultiImpl(this) as I18nScopeMulti<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales\n >;\n }\n\n /** Live dictionary reference (loaded namespaces only). Do not mutate. */\n getAll(): PartialMultiDictionary<Schema, RequestLocales> {\n return this.dictionary;\n }\n\n hasBuilderResourceLoaded(namespace: string, partition: string): boolean {\n return this.builderLoadRegistry.has(namespace, partition);\n }\n\n markBuilderResourceLoaded(namespace: string, partition: string): void {\n this.builderLoadRegistry.mark(namespace, partition);\n }\n\n seedBuilderResources(resources: readonly BuilderResourceEntry[]): void {\n this.builderLoadRegistry.seed(resources);\n }\n\n serialize(): I18nSerializedState<PartialMultiDictionary<Schema, RequestLocales>> {\n return {\n dictionary: this.dictionary,\n resources: this.builderLoadRegistry.entries(),\n };\n }\n\n applyLoadMergeNamespace<NS extends keyof Schema>(\n namespace: NS,\n values: PartialKeyDictionary<Schema[NS], RequestLocales>\n ): void {\n const existing = (this.dictionary[namespace] ?? {}) as Schema[NS];\n const merged = mergeNamespaceLocalesCore(existing, values);\n\n this.dictionary = {\n ...this.dictionary,\n [namespace]: merged,\n };\n\n for (const [key, incomingLocales] of Object.entries(values)) {\n if (incomingLocales === undefined || typeof incomingLocales !== \"object\") {\n continue;\n }\n\n for (const locale of Object.keys(incomingLocales)) {\n delete this.compiledCache[locale]?.[namespace as string]?.[key];\n }\n }\n }\n\n applyLoadMergeAll(values: PartialMultiDictionary<Schema, RequestLocales>): void {\n for (const namespace of Object.keys(values) as (keyof Schema & string)[]) {\n const incoming = values[namespace];\n if (incoming !== undefined) {\n this.applyLoadMergeNamespace(namespace, incoming);\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/builder-loaders.ts","../src/i18n-handle.ts","../src/serialized-state.ts","../src/builder-load-registry.ts","../src/resolve-locale.ts","../src/format-core.ts","../src/project-locales.ts","../src/scope-multi.ts","../src/IcuTranslationProviderMulti.ts"],"names":[],"mappings":";;;AAQA,eAAsB,qBAAA,CACpB,MAAA,EACA,SAAA,EACA,OAAA,EACY;AACZ,EAAA,OAAO,MAAM,MAAA,CAAO,SAAA,EAAW,OAAO,CAAA;AACxC;;;ACmFO,IAAM,iBAAN,MAIiD;AAAA,EAGtD,WAAA,CACmB,QACA,OAAA,EACjB;AAFiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAEjB,IAAA,IAAA,CAAK,kBAAA,GAAqB,OAAA,EAAS,kBAAA,KAAuB,CAAC,MAAA,KAAW,MAAA,CAAA;AAAA,EACxE;AAAA,EAJmB,MAAA;AAAA,EACA,OAAA;AAAA,EAJF,kBAAA;AAAA,EASjB,MAAM,KAIJ,KAAA,EACyE;AACzE,IAAA,MAAM,gBAAgB,IAAA,CAAK,MAAA,EAAQ,KAAK,OAAA,EAAS,KAAA,EAAO,KAAK,kBAAkB,CAAA;AAC/E,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,EAAE,UAAA,EAAY,KAAA,CAAM,UAAA,EAAY,CAAA,CAAE,SAAA,CAAU,KAAA,CAAM,MAAM,CAAA;AAAA,EACrF;AAAA,EAEA,KACE,KAAA,EACuE;AACvE,IAAA,IAAI,CAAC,wBAAwB,IAAA,CAAK,MAAA,EAAQ,KAAK,OAAA,EAAS,KAAA,EAAO,IAAA,CAAK,kBAAkB,CAAA,EAAG;AACvF,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,EAAE,UAAA,EAAY,KAAA,CAAM,UAAA,EAAY,CAAA,CAAE,SAAA,CAAU,KAAA,CAAM,MAAM,CAAA;AAAA,EACrF;AAAA,EAEA,SAAA,GAAY;AACV,IAAA,OAAO,IAAA,CAAK,OAAO,SAAA,EAAU;AAAA,EAC/B;AAAA,EAEA,YAAA,GAA8B;AAC5B,IAAA,OAAO,IAAA,CAAK,OAAO,mBAAA,EAAoB;AAAA,EACzC;AACF,CAAA;AAEO,SAAS,gBAAA,CAKd,QACA,OAAA,EAC4C;AAC5C,EAAA,OAAO,IAAI,cAAA,CAAe,MAAA,EAAQ,OAAO,CAAA;AAC3C;AAEA,SAAS,uBAAA,CAOP,MAAA,EACA,OAAA,EACA,KAAA,EACA,kBAAA,EACS;AACT,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG;AACjC,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,KAAA,CAAM,MAAM,CAAA;AACjD,EAAA,OAAO,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM,CAAC,SAAA,KAAc;AAC3C,IAAA,MAAM,MAAA,GAAS,OAAA,EAAS,gBAAA,GAAmB,SAAS,CAAA;AACpD,IAAA,IAAI,WAAW,MAAA,EAAW;AACxB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,SAAS,CAAA;AAAA,EAC7D,CAAC,CAAA;AACH;AAEA,eAAe,eAAA,CAOb,MAAA,EACA,OAAA,EACA,KAAA,EACA,kBAAA,EACe;AACf,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,EACxE;AAEA,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,KAAA,CAAM,MAAM,CAAA;AAEjD,EAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,IACZ,KAAA,CAAM,UAAA,CAAW,GAAA,CAAI,OAAO,SAAA,KAAc;AACxC,MAAA,MAAM,MAAA,GAAS,OAAA,EAAS,gBAAA,GAAmB,SAAS,CAAA;AACpD,MAAA,IAAI,WAAW,MAAA,EAAW;AACxB,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,SAAS,CAAA,EAAG;AACzD,QAAA;AAAA,MACF;AAEA,MAAA,MAAA,CAAO,0BAAA,CAA2B,WAAW,SAAS,CAAA;AACtD,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,qBAAA,CAAsB,MAAA,EAAQ,WAAW,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAQ,CAAA;AACpF,QAAA,MAAA,CAAO,uBAAA,CAAwB,WAAW,IAAI,CAAA;AAC9C,QAAA,MAAA,CAAO,yBAAA,CAA0B,WAAW,SAAS,CAAA;AAAA,MACvD,SAAS,KAAA,EAAO;AACd,QAAA,MAAA,CAAO,wBAAA,CAAyB,SAAA,EAAW,SAAA,EAAW,KAAK,CAAA;AAC3D,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAC;AAAA,GACH;AACF;;;ACnMO,SAAS,yBACd,KAAA,EACiC;AACjC,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,KAAA,EAAO,UAAA,IAAe,EAAC;AAAA,IACnC,SAAA,EAAW,KAAA,EAAO,SAAA,IAAa;AAAC,GAClC;AACF;;;ACxBO,SAAS,wBAAA,CAAyB,WAAmB,SAAA,EAA2B;AACrF,EAAA,OAAO,CAAA,EAAG,SAAS,CAAA,EAAA,EAAK,SAAS,CAAA,CAAA;AACnC;AASO,IAAM,sBAAN,MAA0B;AAAA,EACd,MAAA,uBAAa,GAAA,EAAoC;AAAA,EAElE,GAAA,CAAI,WAAmB,SAAA,EAA4B;AACjD,IAAA,OAAO,IAAA,CAAK,OAAO,GAAA,CAAI,wBAAA,CAAyB,WAAW,SAAS,CAAC,GAAG,MAAA,KAAW,QAAA;AAAA,EACrF;AAAA,EAEA,WAAA,CAAY,WAAmB,SAAA,EAAyB;AACtD,IAAA,IAAA,CAAK,SAAS,SAAA,EAAW,SAAA,EAAW,EAAE,MAAA,EAAQ,WAAW,CAAA;AAAA,EAC3D;AAAA,EAEA,IAAA,CAAK,WAAmB,SAAA,EAAyB;AAC/C,IAAA,IAAA,CAAK,UAAA,CAAW,WAAW,SAAS,CAAA;AAAA,EACtC;AAAA,EAEA,UAAA,CAAW,WAAmB,SAAA,EAAyB;AACrD,IAAA,IAAA,CAAK,SAAS,SAAA,EAAW,SAAA,EAAW,EAAE,MAAA,EAAQ,UAAU,CAAA;AAAA,EAC1D;AAAA,EAEA,SAAA,CAAU,SAAA,EAAmB,SAAA,EAAmB,KAAA,EAAsB;AACpE,IAAA,IAAA,CAAK,SAAS,SAAA,EAAW,SAAA,EAAW,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAO,CAAA;AAAA,EAChE;AAAA;AAAA,EAGA,OAAA,GAAkC;AAChC,IAAA,MAAM,MAA8B,EAAC;AACrC,IAAA,KAAA,MAAW,MAAA,IAAU,IAAA,CAAK,MAAA,CAAO,MAAA,EAAO,EAAG;AACzC,MAAA,IAAI,MAAA,CAAO,WAAW,QAAA,EAAU;AAC9B,QAAA,GAAA,CAAI,KAAK,CAAC,MAAA,CAAO,SAAA,EAAW,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,MAC/C;AAAA,IACF;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEA,KAAK,SAAA,EAAkD;AACrD,IAAA,KAAA,MAAW,CAAC,SAAA,EAAW,SAAS,CAAA,IAAK,SAAA,EAAW;AAC9C,MAAA,IAAA,CAAK,UAAA,CAAW,WAAW,SAAS,CAAA;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,YAAA,GAA8B;AAC5B,IAAA,MAAM,SAAA,GAAY,CAAC,GAAG,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACzD,MAAA,MAAM,WAAA,GAAc,CAAA,CAAE,SAAA,CAAU,aAAA,CAAc,EAAE,SAAS,CAAA;AACzD,MAAA,OAAO,gBAAgB,CAAA,GAAI,WAAA,GAAc,EAAE,SAAA,CAAU,aAAA,CAAc,EAAE,SAAS,CAAA;AAAA,IAChF,CAAC,CAAA;AACD,IAAA,OAAO,EAAE,SAAA,EAAU;AAAA,EACrB;AAAA,EAEQ,QAAA,CACN,SAAA,EACA,SAAA,EACA,KAAA,EACM;AACN,IAAA,MAAM,MAAA,GAAiC;AAAA,MACrC,SAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAQ,KAAA,CAAM;AAAA,KAChB;AACA,IAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAW;AAC7B,MAAA,MAAA,CAAO,QAAQ,KAAA,CAAM,KAAA;AAAA,IACvB;AACA,IAAA,IAAA,CAAK,OAAO,GAAA,CAAI,wBAAA,CAAyB,SAAA,EAAW,SAAS,GAAG,MAAM,CAAA;AAAA,EACxE;AACF,CAAA;;;ACrEO,SAAS,uBAAuB,GAAA,EAA8B;AACnE,EAAA,KAAA,MAAW,WAAA,IAAe,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,EAAG;AAC1C,IAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,IAAA,IAAI,OAAA,GAA8B,WAAA;AAElC,IAAA,OAAO,YAAY,MAAA,EAAW;AAC5B,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AACxB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oDAAA,EAAuD,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,MACnF;AACA,MAAA,OAAA,CAAQ,IAAI,OAAO,CAAA;AAEnB,MAAA,MAAM,IAAA,GAAkC,IAAI,OAAO,CAAA;AACnD,MAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,IAAA,EAAM;AACvC,QAAA;AAAA,MACF;AACA,MAAA,OAAA,GAAU,IAAA;AAAA,IACZ;AAAA,EACF;AACF;AAEO,SAAS,qBAAA,CACd,WAAA,EACA,eAAA,EACA,WAAA,EACoC;AACpC,EAAA,MAAM,KAAA,GAAkB,CAAC,eAAe,CAAA;AACxC,EAAA,IAAI,aAAA,GAAgB,eAAA;AAEpB,EAAA,OAAO,IAAA,EAAM;AACX,IAAA,MAAM,QAAA,GAAW,cAAc,aAAa,CAAA;AAC5C,IAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,MAAA,OAAO,EAAE,QAAA,EAAU,cAAA,EAAgB,aAAA,EAAe,eAAe,KAAA,EAAM;AAAA,IACzE;AAEA,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,QAAA,GAAW,YAAY,aAAa,CAAA;AAC1C,IAAA,IAAI,QAAA,KAAa,MAAA,IAAa,QAAA,KAAa,IAAA,EAAM;AAC/C,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,KAAA,CAAM,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC5B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,0CAAA,EAA6C,CAAC,GAAG,KAAA,EAAO,QAAQ,CAAA,CAAE,IAAA,CAAK,UAAK,CAAC,CAAA;AAAA,OAC/E;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AACnB,IAAA,aAAA,GAAgB,QAAA;AAAA,EAClB;AACF;AAEO,SAAS,yBAAA,CACd,iBACA,WAAA,EACQ;AACR,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAkB,CAAC,eAAe,CAAA;AACxC,EAAA,IAAI,OAAA,GAAU,eAAA;AAEd,EAAA,OAAO,IAAA,EAAM;AACX,IAAA,MAAM,QAAA,GAAW,YAAY,OAAO,CAAA;AACpC,IAAA,IAAI,QAAA,KAAa,MAAA,IAAa,QAAA,KAAa,IAAA,EAAM;AAC/C,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,QAAA,CAAS,QAAQ,CAAA,EAAG;AAC5B,MAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AACnB,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AACnB,IAAA,OAAA,GAAU,QAAA;AAAA,EACZ;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,UAAK,CAAA;AACzB;;;ACnEA,SAAS,iBAAiB,OAAA,EAA+D;AACvF,EAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,IAAA,OAAO,CAAA,WAAA,EAAc,OAAA,CAAQ,SAAS,CAAA,QAAA,EAAW,QAAQ,GAAG,CAAA,CAAA,CAAA;AAAA,EAC9D;AACA,EAAA,OAAO,CAAA,KAAA,EAAQ,QAAQ,GAAG,CAAA,CAAA,CAAA;AAC5B;AAEA,SAAS,mBAAmB,OAAA,EAAoC;AAC9D,EAAA,MAAM,KAAA,GAAQ,yBAAA,CAA0B,OAAA,CAAQ,MAAA,EAAQ,QAAQ,cAAc,CAAA;AAC9E,EAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,IAAA,OAAO,CAAA,yCAAA,EAA4C,OAAA,CAAQ,SAAS,CAAA,QAAA,EAAW,OAAA,CAAQ,GAAG,CAAA,GAAA,EAAM,OAAA,CAAQ,MAAM,CAAA,mBAAA,EAAsB,KAAK,CAAA,CAAA,CAAA;AAAA,EAC3I;AACA,EAAA,OAAO,kCAAkC,OAAA,CAAQ,GAAG,MAAM,OAAA,CAAQ,MAAM,sBAAsB,KAAK,CAAA,CAAA,CAAA;AACrG;AAEA,SAAS,eAAe,OAAA,EAAoC;AAC1D,EAAA,MAAM,KAAA,GAAQ,yBAAA,CAA0B,OAAA,CAAQ,MAAA,EAAQ,QAAQ,cAAc,CAAA;AAC9E,EAAA,IAAI,OAAA,CAAQ,cAAc,KAAA,EAAO;AAC/B,IAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACnC,MAAA,OAAO,CAAA,EAAG,OAAA,CAAQ,SAAS,CAAA,CAAA,EAAI,QAAQ,GAAG,CAAA,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO,OAAA,CAAQ,GAAA;AAAA,EACjB;AACA,EAAA,IAAI,OAAO,OAAA,CAAQ,SAAA,KAAc,UAAA,EAAY;AAC3C,IAAA,OAAO,QAAQ,SAAA,CAAU;AAAA,MACvB,GAAI,QAAQ,SAAA,KAAc,MAAA,GAAY,EAAE,SAAA,EAAW,OAAA,CAAQ,SAAA,EAAU,GAAI,EAAC;AAAA,MAC1E,KAAK,OAAA,CAAQ,GAAA;AAAA,MACb,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,aAAA,EAAe;AAAA,KAChB,CAAA;AAAA,EACH;AACA,EAAA,MAAM,IAAI,KAAA,CAAM,kBAAA,CAAmB,OAAO,CAAC,CAAA;AAC7C;AAMO,SAAS,iBAAiB,OAAA,EAA0C;AACzE,EAAA,MAAM,EAAE,WAAA,EAAa,MAAA,EAAQ,MAAA,EAAQ,QAAA,EAAU,SAAQ,GAAI,OAAA;AAC3D,EAAA,MAAM,QAAA,GAAW,qBAAA,CAAsB,WAAA,EAAa,MAAA,EAAQ,QAAQ,cAAc,CAAA;AAElF,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,eAAe,OAAO,CAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,EAAE,QAAA,EAAU,cAAA,EAAe,GAAI,QAAA;AACrC,EAAA,MAAM,KAAA,GAAQ,SAAS,cAAc,CAAA;AACrC,EAAA,MAAM,WAAW,OAAA,CAAQ,GAAA;AACzB,EAAA,MAAM,KAAA,GAAQ,iBAAiB,OAAO,CAAA;AAEtC,EAAA,IAAI,CAAC,KAAA,CAAM,QAAQ,CAAA,EAAG;AACpB,IAAA,IAAI;AACF,MAAA,KAAA,CAAM,QAAQ,CAAA,GAAI,IAAI,iBAAA,CAAkB,UAAU,cAAc,CAAA;AAAA,IAClE,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,6CAAA,EAAgD,KAAK,CAAA,EAAA,EAAK,cAAc,MAAM,OAAO,CAAA;AAAA,OACvF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,OAAO,KAAA,CAAM,QAAQ,CAAA,CAAE,MAAA,CAAO,MAAM,CAAA;AAAA,EACtC,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,0DAAA,EAA6D,KAAK,CAAA,EAAA,EAAK,cAAc,MAAM,OAAO,CAAA;AAAA,KACpG;AAAA,EACF;AACF;;;AC6EO,SAAS,yBAAA,CAGd,UAAa,QAAA,EAA+C;AAC5D,EAAA,MAAM,MAAA,GAAS,gBAAgB,QAAQ,CAAA;AAEvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,eAAe,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC7D,IAAA,IAAI,eAAA,KAAoB,MAAA,IAAa,OAAO,eAAA,KAAoB,QAAA,EAAU;AACxE,MAAA;AAAA,IACF;AAEA,IAAA,MAAA,CAAO,GAAc,CAAA,GAAI;AAAA,MACvB,GAAI,MAAA,CAAO,GAAc,CAAA,IAAK,EAAC;AAAA,MAC/B,GAAG;AAAA,KACL;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAMO,SAAS,0BAAA,CAGd,UAAa,QAAA,EAAiD;AAC9D,EAAA,MAAM,MAAA,GAAS,gBAAgB,QAAQ,CAAA;AAEvC,EAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAA2B;AACrE,IAAA,MAAM,iBAAA,GAAoB,SAAS,SAAS,CAAA;AAC5C,IAAA,IAAI,sBAAsB,MAAA,EAAW;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,MAAA,CAAO,SAAS,CAAA,GAAI,yBAAA;AAAA,MACjB,MAAA,CAAO,SAAS,CAAA,IAAK,EAAC;AAAA,MACvB;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;ACrKO,IAAM,qBAAN,MAKqD;AAAA,EAC1D,YACmB,MAAA,EACjB;AADiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAChB;AAAA,EADgB,MAAA;AAAA,EAGnB,CAAA,GAAI,CAIF,SAAA,EACA,GAAA,EACA,WACG,IAAA,KACQ;AACX,IAAA,MAAM,MAAA,GAAS,KAAK,CAAC,CAAA;AACrB,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,aAAA,CAAc,MAAA,CAAO,SAAS,GAAG,MAAA,CAAO,GAAG,CAAA,EAAG,MAAA,EAAQ,MAAM,CAAA;AAAA,EACjF,CAAA;AAAA,EAEA,SAAA,GAAY,CACV,MAAA,KACkF;AAClF,IAAA,OAAO,IAAI,2BAAA,CAA4B,IAAA,CAAK,MAAA,EAAQ,MAAM,CAAA;AAAA,EAC5D,CAAA;AACF,CAAA;AAEO,IAAM,2BAAA,GAAN,MAAM,4BAAA,CAMgE;AAAA,EAC3E,WAAA,CACmB,QACR,MAAA,EACT;AAFiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACR,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EACR;AAAA,EAFgB,MAAA;AAAA,EACR,MAAA;AAAA,EAGX,CAAA,GAAI,CAIF,SAAA,EACA,GAAA,EAAA,GACG,IAAA,KACQ;AACX,IAAA,MAAM,MAAA,GAAS,KAAK,CAAC,CAAA;AACrB,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,aAAA,CAAc,MAAA,CAAO,SAAS,CAAA,EAAG,MAAA,CAAO,GAAG,CAAA,EAAG,IAAA,CAAK,MAAA,EAAQ,MAAM,CAAA;AAAA,EACtF,CAAA;AAAA,EAEA,SAAA,GAAY,CACV,MAAA,KACgF;AAChF,IAAA,OAAO,IAAI,4BAAA,CAA4B,IAAA,CAAK,MAAA,EAAQ,MAAM,CAAA;AAAA,EAC5D,CAAA;AACF,CAAA;;;ACpFO,IAAM,8BAAN,MAKsD;AAAA,EAClD,gBAAA,GAAmB,OAAA;AAAA;AAAA,EAGpB,UAAA;AAAA,EACA,gBAAoC,EAAC;AAAA,EAC5B,mBAAA,GAAsB,IAAI,mBAAA,EAAoB;AAAA,EAC9C,cAAA;AAAA,EACA,SAAA;AAAA,EAEjB,WAAA,CACE,UAAA,GAA6D,EAAC,EAC9D,OAAA,EACA;AACA,IAAA,IAAA,CAAK,UAAA,GAAa,gBAAgB,UAAU,CAAA;AAC5C,IAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,MAAA,sBAAA,CAAuB,QAAQ,cAAc,CAAA;AAC7C,MAAA,IAAA,CAAK,iBAAiB,OAAA,CAAQ,cAAA;AAAA,IAChC;AACA,IAAA,IAAA,CAAK,SAAA,GAAY,SAAS,SAAA,IAAa,OAAA;AAAA,EACzC;AAAA,EAEA,aAAA,CACE,SAAA,EACA,GAAA,EACA,MAAA,EACA,MAAA,EACQ;AACR,IAAA,MAAM,WAAA,GACJ,IAAA,CAAK,UAAA,CAAW,SAAyB,IAGvC,GAAG,CAAA;AAEP,IAAA,OAAO,gBAAA,CAAiB;AAAA,MACtB,WAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA,EAAU,CAAC,cAAA,KAAmB;AAC5B,QAAA,IAAI,CAAC,IAAA,CAAK,aAAA,CAAc,cAAc,CAAA,EAAG;AACvC,UAAA,IAAA,CAAK,aAAA,CAAc,cAAc,CAAA,GAAI,EAAC;AAAA,QACxC;AACA,QAAA,IAAI,CAAC,IAAA,CAAK,aAAA,CAAc,cAAc,CAAA,CAAG,SAAS,CAAA,EAAG;AACnD,UAAA,IAAA,CAAK,aAAA,CAAc,cAAc,CAAA,CAAG,SAAS,IAAI,EAAC;AAAA,QACpD;AACA,QAAA,OAAO,IAAA,CAAK,aAAA,CAAc,cAAc,CAAA,CAAG,SAAS,CAAA;AAAA,MACtD,CAAA;AAAA,MACA,OAAA,EAAS;AAAA,QACP,SAAA;AAAA,QACA,GAAA;AAAA,QACA,MAAA;AAAA,QACA,gBAAgB,IAAA,CAAK,cAAA;AAAA,QACrB,WAAW,IAAA,CAAK;AAAA;AAClB,KACD,CAAA;AAAA,EACH;AAAA,EAqBA,QAGE,OAAA,EAAkD;AAClD,IAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAW;AAChC,MAAA,OAAO,IAAI,2BAAA,CAA4B,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IAM7D;AACA,IAAA,OAAO,IAAI,mBAAmB,IAAI,CAAA;AAAA,EAKpC;AAAA;AAAA,EAGA,MAAA,GAAyD;AACvD,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA,EAEA,wBAAA,CAAyB,WAAmB,SAAA,EAA4B;AACtE,IAAA,OAAO,IAAA,CAAK,mBAAA,CAAoB,GAAA,CAAI,SAAA,EAAW,SAAS,CAAA;AAAA,EAC1D;AAAA,EAEA,0BAAA,CAA2B,WAAmB,SAAA,EAAyB;AACrE,IAAA,IAAA,CAAK,mBAAA,CAAoB,WAAA,CAAY,SAAA,EAAW,SAAS,CAAA;AAAA,EAC3D;AAAA,EAEA,yBAAA,CAA0B,WAAmB,SAAA,EAAyB;AACpE,IAAA,IAAA,CAAK,mBAAA,CAAoB,IAAA,CAAK,SAAA,EAAW,SAAS,CAAA;AAAA,EACpD;AAAA,EAEA,wBAAA,CAAyB,SAAA,EAAmB,SAAA,EAAmB,KAAA,EAAsB;AACnF,IAAA,IAAA,CAAK,mBAAA,CAAoB,SAAA,CAAU,SAAA,EAAW,SAAA,EAAW,KAAK,CAAA;AAAA,EAChE;AAAA,EAEA,mBAAA,GAAsB;AACpB,IAAA,OAAO,IAAA,CAAK,oBAAoB,YAAA,EAAa;AAAA,EAC/C;AAAA,EAEA,qBAAqB,SAAA,EAAkD;AACrE,IAAA,IAAA,CAAK,mBAAA,CAAoB,KAAK,SAAS,CAAA;AAAA,EACzC;AAAA,EAEA,SAAA,GAAiF;AAC/E,IAAA,OAAO;AAAA,MACL,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,SAAA,EAAW,IAAA,CAAK,mBAAA,CAAoB,OAAA;AAAQ,KAC9C;AAAA,EACF;AAAA,EAEA,uBAAA,CACE,WACA,MAAA,EACM;AACN,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,UAAA,CAAW,SAAS,KAAK,EAAC;AACjD,IAAA,MAAM,MAAA,GAAS,yBAAA,CAA0B,QAAA,EAAU,MAAM,CAAA;AAEzD,IAAA,IAAA,CAAK,UAAA,GAAa;AAAA,MAChB,GAAG,IAAA,CAAK,UAAA;AAAA,MACR,CAAC,SAAS,GAAG;AAAA,KACf;AAEA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,eAAe,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC3D,MAAA,IAAI,eAAA,KAAoB,MAAA,IAAa,OAAO,eAAA,KAAoB,QAAA,EAAU;AACxE,QAAA;AAAA,MACF;AAEA,MAAA,KAAA,MAAW,MAAA,IAAU,MAAA,CAAO,IAAA,CAAK,eAAe,CAAA,EAAG;AACjD,QAAA,OAAO,KAAK,aAAA,CAAc,MAAM,CAAA,GAAI,SAAmB,IAAI,GAAG,CAAA;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBAAkB,MAAA,EAA8D;AAC9E,IAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,EAAgC;AACxE,MAAA,MAAM,QAAA,GAAW,OAAO,SAAS,CAAA;AACjC,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,IAAA,CAAK,uBAAA,CAAwB,WAAW,QAAQ,CAAA;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF","file":"index.js","sourcesContent":["/** Split-by-locale or custom-area loader — partition key is locale or area name. */\nexport type PartitionedLoader<T> = (\n partition: string,\n context: { locale: string }\n) => Promise<T> | T;\n\nexport type NamespaceLoader<T> = PartitionedLoader<T>;\n\nexport async function invokeNamespaceLoader<T>(\n loader: NamespaceLoader<T>,\n partition: string,\n context: { locale: string }\n): Promise<T> {\n return await loader(partition, context);\n}\n","import { invokeNamespaceLoader, type NamespaceLoader } from \"./builder-loaders.js\";\nimport type { I18nEngineMultiImpl } from \"./engine.js\";\nimport type {\n LocaleFallbackMap,\n LocaleOfMulti,\n MultiDictionary,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\nimport type { I18nScopeMultiForLocale } from \"./scope-multi.js\";\nimport type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from \"./scope-types.js\";\nimport type { I18nLoadState } from \"./load-state.js\";\n\n/** Engine handle accepted by {@link createI18nHandle} (with or without localeFallback). */\ntype HandleEngine<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n> = I18nEngineMultiImpl<Schema, Params, RequestLocales, LocaleFallbackMap | undefined>;\n\n/** Non-empty namespace list — required by {@link I18nHandle.load} / {@link I18nHandle.peek}. */\ntype NonEmptyNamespaces<NS extends readonly string[]> = NS extends readonly [] ? never : NS;\n\n/** Maps a request locale to the delivery partition key (locale itself, or a custom area). */\nexport type PartitionForLocale<RequestLocales extends string> = (locale: RequestLocales) => string;\n\nexport type I18nHandleOptions<\n Schema extends MultiDictionary,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> = {\n namespaceLoaders?: {\n [NS in keyof Schema & string]?: NamespaceLoader<\n PartialKeyDictionary<Schema[NS], RequestLocales> | Schema[NS]\n >;\n };\n /**\n * Resolves the loader partition for a locale.\n * Default: identity (`locale → locale`) for split-by-locale.\n * Custom delivery: codegen injects `locale → LOCALE_DELIVERY_AREA[locale]`.\n */\n partitionForLocale?: PartitionForLocale<RequestLocales>;\n};\n\nexport type LoadNamespacesInput<\n Schema extends MultiDictionary,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends string,\n> = {\n namespaces: NonEmptyNamespaces<NsList>;\n locale: Locale;\n};\n\n/** Locale-bound scope returned by {@link I18nHandle.load} / {@link I18nHandle.peek}. */\nexport type ScopeForLocale<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n> = I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n>;\n\n/**\n * Single runtime handle: pass `namespaces` + `locale`; partition is derived via\n * {@link I18nHandleOptions.partitionForLocale}.\n */\nexport interface I18nHandle<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> {\n load<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): Promise<ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale>>;\n\n /**\n * Sync scope when every requested resource is already marked loaded (e.g. hydrate seed).\n * Returns `null` when a normal async {@link load} is still required.\n */\n peek<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale> | null;\n\n /** Dictionary (loaded namespaces) + resources for SSR→CSR hydrate. */\n serialize(): {\n dictionary: PartialMultiDictionary<Schema, RequestLocales>;\n resources: readonly (readonly [string, string])[];\n };\n\n /** Snapshot of resource loads attempted on this handle (pending / loaded / error). */\n getLoadState(): I18nLoadState;\n}\n\nexport class I18nHandleImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> implements I18nHandle<Schema, Params, RequestLocales> {\n private readonly partitionForLocale: PartitionForLocale<RequestLocales>;\n\n constructor(\n private readonly engine: HandleEngine<Schema, Params, RequestLocales>,\n private readonly options?: I18nHandleOptions<Schema, RequestLocales>\n ) {\n this.partitionForLocale = options?.partitionForLocale ?? ((locale) => locale);\n }\n\n async load<\n const NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): Promise<ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale>> {\n await applyHandleLoad(this.engine, this.options, input, this.partitionForLocale);\n return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);\n }\n\n peek<const NsList extends readonly (keyof Schema & string)[], Locale extends RequestLocales>(\n input: LoadNamespacesInput<Schema, NsList, Locale>\n ): ScopeForLocale<Schema, Params, RequestLocales, NsList, Locale> | null {\n if (!areHandleResourcesReady(this.engine, this.options, input, this.partitionForLocale)) {\n return null;\n }\n return this.engine.toScope({ namespaces: input.namespaces }).forLocale(input.locale);\n }\n\n serialize() {\n return this.engine.serialize();\n }\n\n getLoadState(): I18nLoadState {\n return this.engine.getBuilderLoadState();\n }\n}\n\nexport function createI18nHandle<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options?: I18nHandleOptions<Schema, RequestLocales>\n): I18nHandle<Schema, Params, RequestLocales> {\n return new I18nHandleImpl(engine, options);\n}\n\nfunction areHandleResourcesReady<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options: I18nHandleOptions<Schema, RequestLocales> | undefined,\n input: LoadNamespacesInput<Schema, NsList, Locale>,\n partitionForLocale: PartitionForLocale<RequestLocales>\n): boolean {\n if (input.namespaces.length === 0) {\n return false;\n }\n const partition = partitionForLocale(input.locale);\n return input.namespaces.every((namespace) => {\n const loader = options?.namespaceLoaders?.[namespace];\n if (loader === undefined) {\n return true;\n }\n return engine.hasBuilderResourceLoaded(namespace, partition);\n });\n}\n\nasync function applyHandleLoad<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n>(\n engine: HandleEngine<Schema, Params, RequestLocales>,\n options: I18nHandleOptions<Schema, RequestLocales> | undefined,\n input: LoadNamespacesInput<Schema, NsList, Locale>,\n partitionForLocale: PartitionForLocale<RequestLocales>\n): Promise<void> {\n if (input.namespaces.length === 0) {\n throw new Error(\"[i18n] load() requires a non-empty namespaces array.\");\n }\n\n const partition = partitionForLocale(input.locale);\n\n await Promise.all(\n input.namespaces.map(async (namespace) => {\n const loader = options?.namespaceLoaders?.[namespace];\n if (loader === undefined) {\n return;\n }\n\n if (engine.hasBuilderResourceLoaded(namespace, partition)) {\n return;\n }\n\n engine.markBuilderResourcePending(namespace, partition);\n try {\n const data = await invokeNamespaceLoader(loader, partition, { locale: input.locale });\n engine.applyLoadMergeNamespace(namespace, data);\n engine.markBuilderResourceLoaded(namespace, partition);\n } catch (error) {\n engine.markBuilderResourceError(namespace, partition, error);\n throw error;\n }\n })\n );\n}\n","import type { BuilderResourceEntry } from \"./builder-load-registry.js\";\n\n/**\n * Portable i18n instance state after `serialize()`: dictionary strings + which\n * builder resources are already loaded (namespace × partition).\n */\nexport interface I18nSerializedState<Dictionary = Record<string, unknown>> {\n dictionary: Dictionary;\n resources: readonly BuilderResourceEntry[];\n}\n\n/**\n * Input nested under generated `createI18n({ state? })`. Omitted `state` defaults to an empty\n * dictionary (lazy cold start). `resources` may be omitted or `[]`.\n */\nexport type I18nCreateInput<Dictionary = Record<string, unknown>> = {\n dictionary: Dictionary;\n resources?: readonly BuilderResourceEntry[];\n};\n\nexport function normalizeI18nCreateInput<Dictionary>(\n value?: I18nCreateInput<Dictionary>\n): I18nSerializedState<Dictionary> {\n return {\n dictionary: value?.dictionary ?? ({} as Dictionary),\n resources: value?.resources ?? [],\n };\n}\n","import type { I18nLoadState, I18nResourceLoadRecord } from \"./load-state.js\";\n\n/** Stable key for a lazy-loaded builder resource (namespace + locale or delivery area). */\nexport function formatBuilderResourceKey(namespace: string, partition: string): string {\n return `${namespace}\\0${partition}`;\n}\n\n/** Serialized builder resource: namespace + partition (locale / delivery area). */\nexport type BuilderResourceEntry = readonly [namespace: string, partition: string];\n\n/**\n * Dedup cache for lazy-loaded builder resources (`namespace` × partition).\n * Tracks pending / loaded / error for {@link I18nLoadState}.\n */\nexport class BuilderLoadRegistry {\n private readonly states = new Map<string, I18nResourceLoadRecord>();\n\n has(namespace: string, partition: string): boolean {\n return this.states.get(formatBuilderResourceKey(namespace, partition))?.status === \"loaded\";\n }\n\n markPending(namespace: string, partition: string): void {\n this.setState(namespace, partition, { status: \"pending\" });\n }\n\n mark(namespace: string, partition: string): void {\n this.markLoaded(namespace, partition);\n }\n\n markLoaded(namespace: string, partition: string): void {\n this.setState(namespace, partition, { status: \"loaded\" });\n }\n\n markError(namespace: string, partition: string, error: unknown): void {\n this.setState(namespace, partition, { status: \"error\", error });\n }\n\n /** Snapshot of loaded resources as `[namespace, partition]` tuples. */\n entries(): BuilderResourceEntry[] {\n const out: BuilderResourceEntry[] = [];\n for (const record of this.states.values()) {\n if (record.status === \"loaded\") {\n out.push([record.namespace, record.partition]);\n }\n }\n return out;\n }\n\n seed(resources: readonly BuilderResourceEntry[]): void {\n for (const [namespace, partition] of resources) {\n this.markLoaded(namespace, partition);\n }\n }\n\n getLoadState(): I18nLoadState {\n const resources = [...this.states.values()].sort((a, b) => {\n const byNamespace = a.namespace.localeCompare(b.namespace);\n return byNamespace !== 0 ? byNamespace : a.partition.localeCompare(b.partition);\n });\n return { resources };\n }\n\n private setState(\n namespace: string,\n partition: string,\n patch: Pick<I18nResourceLoadRecord, \"status\"> & Partial<Pick<I18nResourceLoadRecord, \"error\">>\n ): void {\n const record: I18nResourceLoadRecord = {\n namespace,\n partition,\n status: patch.status,\n };\n if (patch.error !== undefined) {\n record.error = patch.error;\n }\n this.states.set(formatBuilderResourceKey(namespace, partition), record);\n }\n}\n","export type LocaleFallbackMap = Record<string, string | null>;\n\nexport interface ResolvedLocaleTemplate {\n template: string;\n resolvedLocale: string;\n fallbackChain: readonly string[];\n}\n\nexport function validateLocaleFallback(map: LocaleFallbackMap): void {\n for (const startLocale of Object.keys(map)) {\n const visited = new Set<string>();\n let current: string | undefined = startLocale;\n\n while (current !== undefined) {\n if (visited.has(current)) {\n throw new Error(`[i18n] Circular locale fallback detected involving \"${current}\"`);\n }\n visited.add(current);\n\n const next: string | null | undefined = map[current];\n if (next === undefined || next === null) {\n break;\n }\n current = next;\n }\n }\n}\n\nexport function resolveLocaleTemplate(\n localeByKey: Record<string, string | undefined> | undefined,\n requestedLocale: string,\n fallbackMap?: LocaleFallbackMap\n): ResolvedLocaleTemplate | undefined {\n const chain: string[] = [requestedLocale];\n let currentLocale = requestedLocale;\n\n while (true) {\n const template = localeByKey?.[currentLocale];\n if (template !== undefined) {\n return { template, resolvedLocale: currentLocale, fallbackChain: chain };\n }\n\n if (!fallbackMap) {\n return undefined;\n }\n\n const fallback = fallbackMap[currentLocale];\n if (fallback === undefined || fallback === null) {\n return undefined;\n }\n\n if (chain.includes(fallback)) {\n throw new Error(\n `[i18n] Circular locale fallback detected: ${[...chain, fallback].join(\" → \")}`\n );\n }\n\n chain.push(fallback);\n currentLocale = fallback;\n }\n}\n\nexport function formatLocaleFallbackChain(\n requestedLocale: string,\n fallbackMap?: LocaleFallbackMap\n): string {\n if (!fallbackMap) {\n return requestedLocale;\n }\n\n const chain: string[] = [requestedLocale];\n let current = requestedLocale;\n\n while (true) {\n const fallback = fallbackMap[current];\n if (fallback === undefined || fallback === null) {\n break;\n }\n if (chain.includes(fallback)) {\n chain.push(fallback);\n break;\n }\n chain.push(fallback);\n current = fallback;\n }\n\n return chain.join(\" → \");\n}\n","import { IntlMessageFormat } from \"intl-messageformat\";\nimport { formatLocaleFallbackChain, resolveLocaleTemplate } from \"./resolve-locale.js\";\nimport type { LocaleCache, LocaleFallbackMap, OnMissingTranslation } from \"./types.js\";\n\nexport type FormatCoreContext = {\n key: string;\n namespace?: string | undefined;\n locale: string;\n localeFallback?: LocaleFallbackMap | undefined;\n onMissing: OnMissingTranslation;\n};\n\nexport type ResolveAndFormatOptions = {\n localeByKey: Record<string, string | undefined> | undefined;\n locale: string;\n params?: Record<string, unknown> | undefined;\n getCache: (resolvedLocale: string) => LocaleCache;\n context: FormatCoreContext;\n};\n\nfunction formatErrorLabel(context: Pick<FormatCoreContext, \"key\" | \"namespace\">): string {\n if (context.namespace !== undefined) {\n return `namespace \"${context.namespace}\", key \"${context.key}\"`;\n }\n return `key \"${context.key}\"`;\n}\n\nfunction formatMissingError(context: FormatCoreContext): string {\n const chain = formatLocaleFallbackChain(context.locale, context.localeFallback);\n if (context.namespace !== undefined) {\n return `[i18n] Missing key or locale: namespace \"${context.namespace}\", key \"${context.key}\" [${context.locale}] (fallback chain: ${chain})`;\n }\n return `[i18n] Missing key or locale: \"${context.key}\" [${context.locale}] (fallback chain: ${chain})`;\n}\n\nfunction resolveMissing(context: FormatCoreContext): string {\n const chain = formatLocaleFallbackChain(context.locale, context.localeFallback);\n if (context.onMissing === \"key\") {\n if (context.namespace !== undefined) {\n return `${context.namespace}.${context.key}`;\n }\n return context.key;\n }\n if (typeof context.onMissing === \"function\") {\n return context.onMissing({\n ...(context.namespace !== undefined ? { namespace: context.namespace } : {}),\n key: context.key,\n locale: context.locale,\n fallbackChain: chain,\n });\n }\n throw new Error(formatMissingError(context));\n}\n\n/**\n * Shared runtime path for both ICU providers: resolve locale (with fallback) →\n * compile template (cached) → format with params; handles missing keys via `onMissing`.\n */\nexport function resolveAndFormat(options: ResolveAndFormatOptions): string {\n const { localeByKey, locale, params, getCache, context } = options;\n const resolved = resolveLocaleTemplate(localeByKey, locale, context.localeFallback);\n\n if (!resolved) {\n return resolveMissing(context);\n }\n\n const { template, resolvedLocale } = resolved;\n const cache = getCache(resolvedLocale);\n const cacheKey = context.key;\n const label = formatErrorLabel(context);\n\n if (!cache[cacheKey]) {\n try {\n cache[cacheKey] = new IntlMessageFormat(template, resolvedLocale);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[i18n ICU Syntax Error] Dictionary error for ${label} [${resolvedLocale}]: ${message}`\n );\n }\n }\n\n try {\n return cache[cacheKey].format(params) as string;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[i18n Formatting Error] Invalid or missing parameters for ${label} [${resolvedLocale}]: ${message}`\n );\n }\n}\n","import { resolveLocaleTemplate } from \"./resolve-locale.js\";\nimport type {\n KeyDictionary,\n LocaleFallbackMap,\n LocaleOfMulti,\n LocaleOfKeys,\n MultiDictionary,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\n\nfunction classifyAreaLocales(\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): { full: string[]; preserve: string[] } {\n const areaSet = new Set(areaLocales);\n const full: string[] = [];\n const preserve: string[] = [];\n\n for (const locale of areaLocales) {\n const fallback = localeFallback?.[locale];\n if (fallback !== null && fallback !== undefined && areaSet.has(fallback)) {\n preserve.push(locale);\n } else {\n full.push(locale);\n }\n }\n\n return { full, preserve };\n}\n\nfunction projectKeyDictionary<T extends KeyDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const targetLocales = [...new Set(locales)];\n const result = {} as T;\n\n for (const [key, localesByKey] of Object.entries(dictionary)) {\n const projected: Record<string, string> = {};\n\n for (const locale of targetLocales) {\n const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);\n if (resolved !== undefined) {\n projected[locale] = resolved.template;\n }\n }\n\n if (Object.keys(projected).length > 0) {\n result[key as keyof T] = projected as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Builds a namespace dictionary that contains only the requested locales.\n * For each key and target locale, copies the direct template when present;\n * otherwise resolves through `localeFallback` (same rules as `.get()` at runtime).\n */\nexport function projectNamespaceLocalesCore<T extends KeyDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n return projectKeyDictionary(dictionary, locales, localeFallback);\n}\n\n/**\n * Projects a single namespace for a custom delivery area.\n * Full locales resolve through `localeFallback` (e.g. external chains like `it → en-US`);\n * preserve locales copy only their direct canonical entry when present.\n */\nexport function projectNamespaceForDeliveryAreaCore<T extends KeyDictionary>(\n dictionary: T,\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const { full, preserve } = classifyAreaLocales(areaLocales, localeFallback);\n const fullProjection =\n full.length > 0 ? projectKeyDictionary(dictionary, full, localeFallback) : undefined;\n const result = {} as T;\n\n for (const [key, localesByKey] of Object.entries(dictionary)) {\n const projected: Record<string, string> = {};\n\n if (fullProjection !== undefined) {\n const fullEntry = fullProjection[key as keyof T];\n if (fullEntry !== undefined) {\n Object.assign(projected, fullEntry);\n }\n }\n\n for (const locale of preserve) {\n const template = localesByKey[locale];\n if (template !== undefined) {\n projected[locale] = template;\n }\n }\n\n if (Object.keys(projected).length > 0) {\n result[key as keyof T] = projected as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Projects a multi-namespace dictionary for a custom delivery area.\n * Each namespace uses the same delivery-area rules as `projectNamespaceForDeliveryAreaCore`.\n */\nexport function projectDictionaryForDeliveryAreaCore<T extends MultiDictionary>(\n dictionary: T,\n areaLocales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const result = {} as T;\n\n for (const namespace of Object.keys(dictionary) as (keyof T & string)[]) {\n const namespaceDictionary = dictionary[namespace];\n if (namespaceDictionary === undefined) {\n continue;\n }\n\n result[namespace] = projectNamespaceForDeliveryAreaCore(\n namespaceDictionary,\n areaLocales,\n localeFallback\n ) as T[typeof namespace];\n }\n\n return result;\n}\n\n/**\n * Projects every namespace in a multi-namespace dictionary.\n */\nexport function projectDictionaryLocalesCore<T extends MultiDictionary>(\n dictionary: T,\n locales: readonly string[],\n localeFallback?: LocaleFallbackMap\n): T {\n const result = {} as T;\n\n for (const namespace of Object.keys(dictionary) as (keyof T & string)[]) {\n const namespaceDictionary = dictionary[namespace];\n if (namespaceDictionary === undefined) {\n continue;\n }\n\n result[namespace] = projectKeyDictionary(\n namespaceDictionary,\n locales,\n localeFallback\n ) as T[typeof namespace];\n }\n\n return result;\n}\n\n/**\n * Merges locale entries per translation key without dropping keys or locales\n * already present in `existing`. Incoming locales overwrite the same locale on conflict.\n */\nexport function mergeNamespaceLocalesCore<\n T extends KeyDictionary,\n Locales extends string = LocaleOfKeys<T>,\n>(existing: T, incoming: PartialKeyDictionary<T, Locales>): T {\n const merged = structuredClone(existing);\n\n for (const [key, incomingLocales] of Object.entries(incoming)) {\n if (incomingLocales === undefined || typeof incomingLocales !== \"object\") {\n continue;\n }\n\n merged[key as keyof T] = {\n ...(merged[key as keyof T] ?? {}),\n ...incomingLocales,\n } as T[keyof T];\n }\n\n return merged;\n}\n\n/**\n * Merges locale entries per key in every namespace present in `incoming`.\n * Namespaces and keys not in `incoming` are preserved from `existing`.\n */\nexport function mergeDictionaryLocalesCore<\n T extends MultiDictionary,\n Locales extends string = LocaleOfMulti<T>,\n>(existing: T, incoming: PartialMultiDictionary<T, Locales>): T {\n const merged = structuredClone(existing) as T;\n\n for (const namespace of Object.keys(incoming) as (keyof T & string)[]) {\n const incomingNamespace = incoming[namespace];\n if (incomingNamespace === undefined) {\n continue;\n }\n\n merged[namespace] = mergeNamespaceLocalesCore(\n (merged[namespace] ?? {}) as T[typeof namespace],\n incomingNamespace as T[typeof namespace]\n ) as T[typeof namespace];\n }\n\n return merged;\n}\n","import type { IcuTranslationProviderMulti } from \"./IcuTranslationProviderMulti.js\";\nimport type { LocaleFallbackMap, LocaleOfMulti, MultiDictionary } from \"./types.js\";\nimport type { MultiParams, ParamArgs } from \"./scope-types.js\";\n\n/** Type-safe translation scope for a multi-namespace schema subset. */\nexport interface I18nScopeMulti<\n Schema extends MultiDictionary,\n Params,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n> {\n t<\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n locale: RequestLocales,\n ...params: ParamArgs<Params[NS][K]>\n ): string;\n forLocale<Locale extends RequestLocales>(\n locale: Locale\n ): I18nScopeMultiForLocale<Schema, Params, RequestLocales, Locale>;\n}\n\n/** Multi-namespace scope with a bound locale — `t(namespace, key)` without a locale argument. */\nexport interface I18nScopeMultiForLocale<\n Schema extends MultiDictionary,\n Params,\n RequestLocales extends string,\n Locale extends RequestLocales,\n> {\n readonly locale: Locale;\n t<\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n ...params: ParamArgs<Params[NS][K]>\n ): string;\n forLocale<Next extends RequestLocales>(\n locale: Next\n ): I18nScopeMultiForLocale<Schema, Params, RequestLocales, Next>;\n}\n\nexport class I18nScopeMultiImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n Fallback extends LocaleFallbackMap | undefined,\n> implements I18nScopeMulti<Schema, Params, RequestLocales> {\n constructor(\n private readonly engine: IcuTranslationProviderMulti<Schema, Params, RequestLocales, Fallback>\n ) {}\n\n t = <\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n locale: RequestLocales,\n ...args: ParamArgs<Params[NS][K]>\n ): string => {\n const params = args[0] as Record<string, unknown> | undefined;\n return this.engine.getWithLocale(String(namespace), String(key), locale, params);\n };\n\n forLocale = <Locale extends RequestLocales>(\n locale: Locale\n ): I18nScopeMultiForLocaleImpl<Schema, Params, RequestLocales, Locale, Fallback> => {\n return new I18nScopeMultiForLocaleImpl(this.engine, locale);\n };\n}\n\nexport class I18nScopeMultiForLocaleImpl<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string,\n Locale extends RequestLocales,\n Fallback extends LocaleFallbackMap | undefined,\n> implements I18nScopeMultiForLocale<Schema, Params, RequestLocales, Locale> {\n constructor(\n private readonly engine: IcuTranslationProviderMulti<Schema, Params, RequestLocales, Fallback>,\n readonly locale: Locale\n ) {}\n\n t = <\n const NS extends keyof Schema & keyof Params & string,\n const K extends keyof Schema[NS] & keyof Params[NS] & string,\n >(\n namespace: NS,\n key: K,\n ...args: ParamArgs<Params[NS][K]>\n ): string => {\n const params = args[0] as Record<string, unknown> | undefined;\n return this.engine.getWithLocale(String(namespace), String(key), this.locale, params);\n };\n\n forLocale = <Next extends RequestLocales>(\n locale: Next\n ): I18nScopeMultiForLocaleImpl<Schema, Params, RequestLocales, Next, Fallback> => {\n return new I18nScopeMultiForLocaleImpl(this.engine, locale);\n };\n}\n","import { BuilderLoadRegistry, type BuilderResourceEntry } from \"./builder-load-registry.js\";\nimport type { I18nEngineMulti } from \"./engine.js\";\nimport type { I18nSerializedState } from \"./serialized-state.js\";\nimport { resolveAndFormat } from \"./format-core.js\";\nimport { mergeNamespaceLocalesCore } from \"./project-locales.js\";\nimport { validateLocaleFallback } from \"./resolve-locale.js\";\nimport type {\n IcuTranslationProviderOptions,\n LocaleFallbackMap,\n LocaleOfMulti,\n MultiCompiledCache,\n MultiDictionary,\n OnMissingTranslation,\n PartialKeyDictionary,\n PartialMultiDictionary,\n} from \"./types.js\";\nimport { I18nScopeMultiForLocaleImpl, I18nScopeMultiImpl } from \"./scope-multi.js\";\nimport type { I18nScopeMulti, I18nScopeMultiForLocale } from \"./scope-multi.js\";\nimport type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from \"./scope-types.js\";\n\nexport class IcuTranslationProviderMulti<\n Schema extends MultiDictionary,\n Params extends MultiParams<Schema>,\n RequestLocales extends string = LocaleOfMulti<Schema>,\n Fallback extends LocaleFallbackMap | undefined = undefined,\n> implements I18nEngineMulti<Schema, Params, RequestLocales> {\n readonly __i18nEngineMode = \"multi\" as const;\n\n /** Loaded namespaces only — cold start is `{}`; never claim full Schema until loaded. */\n private dictionary: PartialMultiDictionary<Schema, RequestLocales>;\n private compiledCache: MultiCompiledCache = {};\n private readonly builderLoadRegistry = new BuilderLoadRegistry();\n private readonly localeFallback?: Fallback;\n private readonly onMissing: OnMissingTranslation;\n\n constructor(\n dictionary: PartialMultiDictionary<Schema, RequestLocales> = {},\n options?: IcuTranslationProviderOptions<Fallback>\n ) {\n this.dictionary = structuredClone(dictionary);\n if (options?.localeFallback) {\n validateLocaleFallback(options.localeFallback);\n this.localeFallback = options.localeFallback;\n }\n this.onMissing = options?.onMissing ?? \"throw\";\n }\n\n getWithLocale(\n namespace: string,\n key: string,\n locale: string,\n params?: Record<string, unknown>\n ): string {\n const localeByKey = (\n this.dictionary[namespace as keyof Schema] as\n | Record<string, Record<string, string>>\n | undefined\n )?.[key];\n\n return resolveAndFormat({\n localeByKey,\n locale,\n params,\n getCache: (resolvedLocale) => {\n if (!this.compiledCache[resolvedLocale]) {\n this.compiledCache[resolvedLocale] = {};\n }\n if (!this.compiledCache[resolvedLocale]![namespace]) {\n this.compiledCache[resolvedLocale]![namespace] = {};\n }\n return this.compiledCache[resolvedLocale]![namespace]!;\n },\n context: {\n namespace,\n key,\n locale,\n localeFallback: this.localeFallback,\n onMissing: this.onMissing,\n },\n });\n }\n\n toScope<\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(options: {\n namespaces: NsList;\n locale: Locale;\n }): I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n >;\n toScope<NsList extends readonly (keyof Schema & string)[]>(options: {\n namespaces: NsList;\n }): I18nScopeMulti<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales\n >;\n toScope<\n NsList extends readonly (keyof Schema & string)[],\n Locale extends RequestLocales,\n >(options: { namespaces: NsList; locale?: Locale }) {\n if (options.locale !== undefined) {\n return new I18nScopeMultiForLocaleImpl(this, options.locale) as I18nScopeMultiForLocale<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales,\n Locale\n >;\n }\n return new I18nScopeMultiImpl(this) as I18nScopeMulti<\n SchemaForNamespaces<Schema, NsList>,\n ParamsForNamespaces<Schema, Params, NsList>,\n RequestLocales\n >;\n }\n\n /** Live dictionary reference (loaded namespaces only). Do not mutate. */\n getAll(): PartialMultiDictionary<Schema, RequestLocales> {\n return this.dictionary;\n }\n\n hasBuilderResourceLoaded(namespace: string, partition: string): boolean {\n return this.builderLoadRegistry.has(namespace, partition);\n }\n\n markBuilderResourcePending(namespace: string, partition: string): void {\n this.builderLoadRegistry.markPending(namespace, partition);\n }\n\n markBuilderResourceLoaded(namespace: string, partition: string): void {\n this.builderLoadRegistry.mark(namespace, partition);\n }\n\n markBuilderResourceError(namespace: string, partition: string, error: unknown): void {\n this.builderLoadRegistry.markError(namespace, partition, error);\n }\n\n getBuilderLoadState() {\n return this.builderLoadRegistry.getLoadState();\n }\n\n seedBuilderResources(resources: readonly BuilderResourceEntry[]): void {\n this.builderLoadRegistry.seed(resources);\n }\n\n serialize(): I18nSerializedState<PartialMultiDictionary<Schema, RequestLocales>> {\n return {\n dictionary: this.dictionary,\n resources: this.builderLoadRegistry.entries(),\n };\n }\n\n applyLoadMergeNamespace<NS extends keyof Schema>(\n namespace: NS,\n values: PartialKeyDictionary<Schema[NS], RequestLocales>\n ): void {\n const existing = (this.dictionary[namespace] ?? {}) as Schema[NS];\n const merged = mergeNamespaceLocalesCore(existing, values);\n\n this.dictionary = {\n ...this.dictionary,\n [namespace]: merged,\n };\n\n for (const [key, incomingLocales] of Object.entries(values)) {\n if (incomingLocales === undefined || typeof incomingLocales !== \"object\") {\n continue;\n }\n\n for (const locale of Object.keys(incomingLocales)) {\n delete this.compiledCache[locale]?.[namespace as string]?.[key];\n }\n }\n }\n\n applyLoadMergeAll(values: PartialMultiDictionary<Schema, RequestLocales>): void {\n for (const namespace of Object.keys(values) as (keyof Schema & string)[]) {\n const incoming = values[namespace];\n if (incoming !== undefined) {\n this.applyLoadMergeNamespace(namespace, incoming);\n }\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -127,10 +127,22 @@ export class IcuTranslationProviderMulti<
|
|
|
127
127
|
return this.builderLoadRegistry.has(namespace, partition);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
markBuilderResourcePending(namespace: string, partition: string): void {
|
|
131
|
+
this.builderLoadRegistry.markPending(namespace, partition);
|
|
132
|
+
}
|
|
133
|
+
|
|
130
134
|
markBuilderResourceLoaded(namespace: string, partition: string): void {
|
|
131
135
|
this.builderLoadRegistry.mark(namespace, partition);
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
markBuilderResourceError(namespace: string, partition: string, error: unknown): void {
|
|
139
|
+
this.builderLoadRegistry.markError(namespace, partition, error);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getBuilderLoadState() {
|
|
143
|
+
return this.builderLoadRegistry.getLoadState();
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
seedBuilderResources(resources: readonly BuilderResourceEntry[]): void {
|
|
135
147
|
this.builderLoadRegistry.seed(resources);
|
|
136
148
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { BuilderLoadRegistry } from "./builder-load-registry.js";
|
|
3
|
+
|
|
4
|
+
describe("BuilderLoadRegistry", () => {
|
|
5
|
+
it("tracks pending, loaded, and error states", () => {
|
|
6
|
+
const registry = new BuilderLoadRegistry();
|
|
7
|
+
const failure = new Error("boom");
|
|
8
|
+
|
|
9
|
+
registry.markPending("billing", "it");
|
|
10
|
+
expect(registry.getLoadState()).toEqual({
|
|
11
|
+
resources: [{ namespace: "billing", partition: "it", status: "pending" }],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
registry.markLoaded("billing", "it");
|
|
15
|
+
expect(registry.has("billing", "it")).toBe(true);
|
|
16
|
+
expect(registry.getLoadState()).toEqual({
|
|
17
|
+
resources: [{ namespace: "billing", partition: "it", status: "loaded" }],
|
|
18
|
+
});
|
|
19
|
+
expect(registry.entries()).toEqual([["billing", "it"]]);
|
|
20
|
+
|
|
21
|
+
registry.markError("default", "en", failure);
|
|
22
|
+
expect(registry.getLoadState()).toEqual({
|
|
23
|
+
resources: [
|
|
24
|
+
{ namespace: "billing", partition: "it", status: "loaded" },
|
|
25
|
+
{ namespace: "default", partition: "en", status: "error", error: failure },
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("seeds hydrated resources as loaded", () => {
|
|
31
|
+
const registry = new BuilderLoadRegistry();
|
|
32
|
+
registry.seed([
|
|
33
|
+
["default", "it"],
|
|
34
|
+
["billing", "it"],
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
expect(registry.getLoadState()).toEqual({
|
|
38
|
+
resources: [
|
|
39
|
+
{ namespace: "billing", partition: "it", status: "loaded" },
|
|
40
|
+
{ namespace: "default", partition: "it", status: "loaded" },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { I18nLoadState, I18nResourceLoadRecord } from "./load-state.js";
|
|
2
|
+
|
|
1
3
|
/** Stable key for a lazy-loaded builder resource (namespace + locale or delivery area). */
|
|
2
4
|
export function formatBuilderResourceKey(namespace: string, partition: string): string {
|
|
3
5
|
return `${namespace}\0${partition}`;
|
|
@@ -8,32 +10,69 @@ export type BuilderResourceEntry = readonly [namespace: string, partition: strin
|
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Dedup cache for lazy-loaded builder resources (`namespace` × partition).
|
|
11
|
-
*
|
|
13
|
+
* Tracks pending / loaded / error for {@link I18nLoadState}.
|
|
12
14
|
*/
|
|
13
15
|
export class BuilderLoadRegistry {
|
|
14
|
-
private readonly
|
|
16
|
+
private readonly states = new Map<string, I18nResourceLoadRecord>();
|
|
15
17
|
|
|
16
18
|
has(namespace: string, partition: string): boolean {
|
|
17
|
-
return this.
|
|
19
|
+
return this.states.get(formatBuilderResourceKey(namespace, partition))?.status === "loaded";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
markPending(namespace: string, partition: string): void {
|
|
23
|
+
this.setState(namespace, partition, { status: "pending" });
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
mark(namespace: string, partition: string): void {
|
|
21
|
-
this.
|
|
27
|
+
this.markLoaded(namespace, partition);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
markLoaded(namespace: string, partition: string): void {
|
|
31
|
+
this.setState(namespace, partition, { status: "loaded" });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
markError(namespace: string, partition: string, error: unknown): void {
|
|
35
|
+
this.setState(namespace, partition, { status: "error", error });
|
|
22
36
|
}
|
|
23
37
|
|
|
24
38
|
/** Snapshot of loaded resources as `[namespace, partition]` tuples. */
|
|
25
39
|
entries(): BuilderResourceEntry[] {
|
|
26
40
|
const out: BuilderResourceEntry[] = [];
|
|
27
|
-
for (const
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
for (const record of this.states.values()) {
|
|
42
|
+
if (record.status === "loaded") {
|
|
43
|
+
out.push([record.namespace, record.partition]);
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
return out;
|
|
32
47
|
}
|
|
33
48
|
|
|
34
49
|
seed(resources: readonly BuilderResourceEntry[]): void {
|
|
35
50
|
for (const [namespace, partition] of resources) {
|
|
36
|
-
this.
|
|
51
|
+
this.markLoaded(namespace, partition);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getLoadState(): I18nLoadState {
|
|
56
|
+
const resources = [...this.states.values()].sort((a, b) => {
|
|
57
|
+
const byNamespace = a.namespace.localeCompare(b.namespace);
|
|
58
|
+
return byNamespace !== 0 ? byNamespace : a.partition.localeCompare(b.partition);
|
|
59
|
+
});
|
|
60
|
+
return { resources };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private setState(
|
|
64
|
+
namespace: string,
|
|
65
|
+
partition: string,
|
|
66
|
+
patch: Pick<I18nResourceLoadRecord, "status"> & Partial<Pick<I18nResourceLoadRecord, "error">>
|
|
67
|
+
): void {
|
|
68
|
+
const record: I18nResourceLoadRecord = {
|
|
69
|
+
namespace,
|
|
70
|
+
partition,
|
|
71
|
+
status: patch.status,
|
|
72
|
+
};
|
|
73
|
+
if (patch.error !== undefined) {
|
|
74
|
+
record.error = patch.error;
|
|
37
75
|
}
|
|
76
|
+
this.states.set(formatBuilderResourceKey(namespace, partition), record);
|
|
38
77
|
}
|
|
39
78
|
}
|
|
@@ -37,7 +37,7 @@ describe("builder-types", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
describe("I18nHandle typestate", () => {
|
|
40
|
-
it("exposes load / peek / serialize", () => {
|
|
40
|
+
it("exposes load / peek / serialize / getLoadState", () => {
|
|
41
41
|
const engine = new IcuTranslationProviderMulti<TypestateSchema, TypestateParams>({});
|
|
42
42
|
const handle = createI18nHandle(engine);
|
|
43
43
|
expectTypeOf(handle).toMatchTypeOf<I18nHandle<TypestateSchema, TypestateParams>>();
|
|
@@ -45,5 +45,13 @@ describe("I18nHandle typestate", () => {
|
|
|
45
45
|
dictionary: PartialMultiDictionary<TypestateSchema>;
|
|
46
46
|
resources: readonly (readonly [string, string])[];
|
|
47
47
|
}>();
|
|
48
|
+
expectTypeOf(handle.getLoadState).returns.toEqualTypeOf<{
|
|
49
|
+
resources: readonly {
|
|
50
|
+
namespace: string;
|
|
51
|
+
partition: string;
|
|
52
|
+
status: "pending" | "loaded" | "error";
|
|
53
|
+
error?: unknown;
|
|
54
|
+
}[];
|
|
55
|
+
}>();
|
|
48
56
|
});
|
|
49
57
|
});
|
package/src/builder.test.ts
CHANGED
|
@@ -257,4 +257,47 @@ describe("I18nHandle", () => {
|
|
|
257
257
|
"You have 1 invoice for Ada"
|
|
258
258
|
);
|
|
259
259
|
});
|
|
260
|
+
|
|
261
|
+
it("getLoadState() tracks pending, loaded, and error resources", async () => {
|
|
262
|
+
let resolveBilling!: () => void;
|
|
263
|
+
const billingPending = new Promise<typeof billingEn>((resolve) => {
|
|
264
|
+
resolveBilling = () => resolve(billingEn);
|
|
265
|
+
});
|
|
266
|
+
const billingLoader = vi.fn(() => billingPending);
|
|
267
|
+
const failingLoader = vi.fn(async () => {
|
|
268
|
+
throw new Error("load failed");
|
|
269
|
+
});
|
|
270
|
+
const engine = new IcuTranslationProviderMulti<MultiSchema, TestMultiParams>({});
|
|
271
|
+
const handle = createI18nHandle(engine, {
|
|
272
|
+
namespaceLoaders: {
|
|
273
|
+
billing: billingLoader,
|
|
274
|
+
default: failingLoader,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
expect(handle.getLoadState()).toEqual({ resources: [] });
|
|
279
|
+
|
|
280
|
+
const pending = handle.load({ namespaces: ["billing"], locale: "en" });
|
|
281
|
+
expect(handle.getLoadState()).toEqual({
|
|
282
|
+
resources: [{ namespace: "billing", partition: "en", status: "pending" }],
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
resolveBilling();
|
|
286
|
+
await pending;
|
|
287
|
+
|
|
288
|
+
expect(handle.getLoadState()).toEqual({
|
|
289
|
+
resources: [{ namespace: "billing", partition: "en", status: "loaded" }],
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await expect(handle.load({ namespaces: ["default"], locale: "it" })).rejects.toThrow(
|
|
293
|
+
"load failed"
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect(handle.getLoadState()).toEqual({
|
|
297
|
+
resources: [
|
|
298
|
+
{ namespace: "billing", partition: "en", status: "loaded" },
|
|
299
|
+
{ namespace: "default", partition: "it", status: "error", error: expect.any(Error) },
|
|
300
|
+
],
|
|
301
|
+
});
|
|
302
|
+
});
|
|
260
303
|
});
|
package/src/builder.ts
CHANGED
package/src/i18n-handle.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from "./types.js";
|
|
10
10
|
import type { I18nScopeMultiForLocale } from "./scope-multi.js";
|
|
11
11
|
import type { MultiParams, ParamsForNamespaces, SchemaForNamespaces } from "./scope-types.js";
|
|
12
|
+
import type { I18nLoadState } from "./load-state.js";
|
|
12
13
|
|
|
13
14
|
/** Engine handle accepted by {@link createI18nHandle} (with or without localeFallback). */
|
|
14
15
|
type HandleEngine<
|
|
@@ -89,6 +90,9 @@ export interface I18nHandle<
|
|
|
89
90
|
dictionary: PartialMultiDictionary<Schema, RequestLocales>;
|
|
90
91
|
resources: readonly (readonly [string, string])[];
|
|
91
92
|
};
|
|
93
|
+
|
|
94
|
+
/** Snapshot of resource loads attempted on this handle (pending / loaded / error). */
|
|
95
|
+
getLoadState(): I18nLoadState;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
export class I18nHandleImpl<
|
|
@@ -127,6 +131,10 @@ export class I18nHandleImpl<
|
|
|
127
131
|
serialize() {
|
|
128
132
|
return this.engine.serialize();
|
|
129
133
|
}
|
|
134
|
+
|
|
135
|
+
getLoadState(): I18nLoadState {
|
|
136
|
+
return this.engine.getBuilderLoadState();
|
|
137
|
+
}
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
export function createI18nHandle<
|
|
@@ -194,9 +202,15 @@ async function applyHandleLoad<
|
|
|
194
202
|
return;
|
|
195
203
|
}
|
|
196
204
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
engine.markBuilderResourcePending(namespace, partition);
|
|
206
|
+
try {
|
|
207
|
+
const data = await invokeNamespaceLoader(loader, partition, { locale: input.locale });
|
|
208
|
+
engine.applyLoadMergeNamespace(namespace, data);
|
|
209
|
+
engine.markBuilderResourceLoaded(namespace, partition);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
engine.markBuilderResourceError(namespace, partition, error);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
200
214
|
})
|
|
201
215
|
);
|
|
202
216
|
}
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,11 @@ export { createI18nHandle, invokeNamespaceLoader } from "./builder.js";
|
|
|
20
20
|
export type { BuilderResourceEntry } from "./builder-load-registry.js";
|
|
21
21
|
export type { I18nCreateInput, I18nSerializedState } from "./serialized-state.js";
|
|
22
22
|
export { normalizeI18nCreateInput } from "./serialized-state.js";
|
|
23
|
+
export type {
|
|
24
|
+
I18nLoadState,
|
|
25
|
+
I18nResourceLoadRecord,
|
|
26
|
+
I18nResourceLoadStatus,
|
|
27
|
+
} from "./load-state.js";
|
|
23
28
|
|
|
24
29
|
export { IcuTranslationProviderMulti } from "./IcuTranslationProviderMulti.js";
|
|
25
30
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Status of a lazy builder resource (`namespace` × partition). */
|
|
2
|
+
export type I18nResourceLoadStatus = "pending" | "loaded" | "error";
|
|
3
|
+
|
|
4
|
+
/** One tracked resource entry in {@link I18nLoadState}. */
|
|
5
|
+
export interface I18nResourceLoadRecord {
|
|
6
|
+
namespace: string;
|
|
7
|
+
partition: string;
|
|
8
|
+
status: I18nResourceLoadStatus;
|
|
9
|
+
error?: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Global snapshot of resource loads attempted on an i18n handle. */
|
|
13
|
+
export interface I18nLoadState {
|
|
14
|
+
resources: readonly I18nResourceLoadRecord[];
|
|
15
|
+
}
|