@logixjs/i18n 1.0.2-beta.0 → 1.0.2

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/I18n.cjs CHANGED
@@ -28,118 +28,6 @@ module.exports = __toCommonJS(I18n_exports);
28
28
 
29
29
  // src/internal/driver/i18n.ts
30
30
  var import_effect = require("effect");
31
-
32
- // src/internal/token/token.ts
33
- var InvalidI18nMessageTokenError = class extends Error {
34
- constructor(reason, details, fix) {
35
- super(`[InvalidI18nMessageTokenError] reason=${reason}`);
36
- this.reason = reason;
37
- this.details = details;
38
- this.fix = fix;
39
- this.name = "InvalidI18nMessageTokenError";
40
- }
41
- };
42
- var TOKEN_BUDGET = {
43
- keyMaxLen: 96,
44
- optionKeyMaxCount: 8,
45
- optionValueStringMaxLen: 96
46
- };
47
- var LANGUAGE_FROZEN_KEYS = /* @__PURE__ */ new Set(["lng", "lngs"]);
48
- var DANGEROUS_OPTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
49
- var isJsonPrimitive = (value) => value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string";
50
- var invalidToken = (reason, details, fix) => {
51
- throw new InvalidI18nMessageTokenError(reason, details, fix);
52
- };
53
- var canonicalizeTokenOptions = (options) => {
54
- if (!options) return void 0;
55
- if (typeof options !== "object" || options === null || Array.isArray(options)) {
56
- invalidToken("optionValueInvalid", { field: "options", actual: typeof options }, [
57
- "\u8BF7\u4F20\u5165 plain object \u4F5C\u4E3A options\uFF08Record<string, JsonPrimitive>\uFF09\u3002",
58
- "\u4E0D\u8981\u4F20\u5165\u6570\u7EC4/\u51FD\u6570/\u7C7B\u5B9E\u4F8B\u7B49\u4E0D\u53EF\u5E8F\u5217\u5316\u503C\u3002"
59
- ]);
60
- }
61
- const entries = Object.entries(options).filter((p) => p[1] !== void 0);
62
- if (entries.length === 0) return void 0;
63
- if (entries.length > TOKEN_BUDGET.optionKeyMaxCount) {
64
- invalidToken(
65
- "tooManyOptions",
66
- {
67
- field: "options",
68
- max: TOKEN_BUDGET.optionKeyMaxCount,
69
- actual: entries.length
70
- },
71
- [
72
- `\u51CF\u5C11 options \u952E\u6570\u91CF\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.optionKeyMaxCount}\uFF09\u3002`,
73
- "\u628A\u8F83\u957F\u7684\u4FE1\u606F\u632A\u5230 key \u6216 defaultValue\uFF1B\u907F\u514D\u628A\u5927\u5BF9\u8C61\u585E\u8FDB token\u3002"
74
- ]
75
- );
76
- }
77
- for (const [k, v] of entries) {
78
- if (DANGEROUS_OPTION_KEYS.has(k) || k.length === 0) {
79
- invalidToken("optionKeyInvalid", { field: `options.${k}`, key: k }, [
80
- "\u8BF7\u4F7F\u7528\u666E\u901A\u5B57\u6BB5\u540D\u4F5C\u4E3A options key\uFF08\u907F\u514D __proto__/constructor/prototype \u7B49\u5371\u9669\u952E\uFF09\u3002",
81
- "\u5982\u9700\u4F20\u9012\u590D\u6742\u7ED3\u6784\uFF0C\u8BF7\u5148\u5728\u5C55\u793A\u8FB9\u754C\u8F6C\u6362\u4E3A\u5B57\u7B26\u4E32\u3002"
82
- ]);
83
- }
84
- if (LANGUAGE_FROZEN_KEYS.has(k)) {
85
- invalidToken("languageFrozen", { field: `options.${k}`, key: k }, [
86
- "\u4E0D\u8981\u5728 token options \u4E2D\u4F20\u5165 lng/lngs \u7B49\u8BED\u8A00\u51BB\u7ED3\u5B57\u6BB5\u3002",
87
- "\u8BED\u8A00\u7531\u5916\u90E8 i18n \u5B9E\u4F8B\u51B3\u5B9A\uFF1Btoken \u53EA\u8868\u8FBE\u201C\u8981\u7FFB\u8BD1\u4EC0\u4E48\u201D\u3002"
88
- ]);
89
- }
90
- if (!isJsonPrimitive(v)) {
91
- invalidToken("optionValueInvalid", { field: `options.${k}`, actual: typeof v }, [
92
- "options value \u53EA\u5141\u8BB8 JsonPrimitive\uFF08null/boolean/number/string\uFF09\u3002",
93
- "\u4E0D\u8981\u4F20\u5165\u5BF9\u8C61/\u6570\u7EC4/\u51FD\u6570\uFF1B\u9700\u8981\u65F6\u8BF7\u5728\u5C55\u793A\u8FB9\u754C\u5148\u683C\u5F0F\u5316\u6210\u5B57\u7B26\u4E32\u3002"
94
- ]);
95
- }
96
- if (typeof v === "number" && !Number.isFinite(v)) {
97
- invalidToken("numberNotJsonSafe", { field: `options.${k}`, value: String(v) }, [
98
- "\u4E0D\u8981\u5728 token options \u4E2D\u4F20\u5165 NaN/Infinity\u3002",
99
- "\u8BF7\u5148\u628A\u8BE5\u6570\u503C\u8F6C\u6362\u4E3A\u53EF JSON \u5316\u7684 number \u6216 string\u3002"
100
- ]);
101
- }
102
- if (typeof v === "string" && v.length > TOKEN_BUDGET.optionValueStringMaxLen) {
103
- invalidToken(
104
- "optionValueTooLong",
105
- {
106
- field: `options.${k}`,
107
- maxLen: TOKEN_BUDGET.optionValueStringMaxLen,
108
- actualLen: v.length
109
- },
110
- [
111
- `\u7F29\u77ED\u5B57\u7B26\u4E32\u503C\u957F\u5EA6\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.optionValueStringMaxLen}\uFF09\u3002`,
112
- "\u628A\u957F\u6587\u672C\u79FB\u5230 defaultValue \u6216\u76F4\u63A5\u5728\u5C55\u793A\u8FB9\u754C\u751F\u6210\u6700\u7EC8\u5B57\u7B26\u4E32\u3002"
113
- ]
114
- );
115
- }
116
- }
117
- entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
118
- const out = {};
119
- for (const [k, v] of entries) {
120
- out[k] = v;
121
- }
122
- return out;
123
- };
124
- var token = (key, options) => {
125
- if (key.length > TOKEN_BUDGET.keyMaxLen) {
126
- invalidToken("keyTooLong", { field: "key", maxLen: TOKEN_BUDGET.keyMaxLen, actualLen: key.length }, [
127
- `\u7F29\u77ED key\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.keyMaxLen}\uFF09\u3002`,
128
- "\u5982\u679C key \u8FC7\u957F\uFF0C\u5EFA\u8BAE\u6539\u4E3A\u201C\u7A33\u5B9A key + \u53D8\u91CF options/defaultValue\u201D\u3002"
129
- ]);
130
- }
131
- const canon = canonicalizeTokenOptions(options);
132
- return canon ? {
133
- _tag: "i18n",
134
- key,
135
- options: canon
136
- } : {
137
- _tag: "i18n",
138
- key
139
- };
140
- };
141
-
142
- // src/internal/driver/i18n.ts
143
31
  var I18nSnapshotSchema = import_effect.Schema.Struct({
144
32
  language: import_effect.Schema.String,
145
33
  init: import_effect.Schema.Literals(["pending", "ready", "failed"]),
@@ -209,22 +97,22 @@ var makeI18nService = (driver) => import_effect.Effect.gen(function* () {
209
97
  yield* update({ init: "ready", language });
210
98
  }
211
99
  });
212
- const fallback = (key, options) => typeof options?.defaultValue === "string" ? options.defaultValue : key;
213
- const t = (key, options) => {
100
+ const fallback = (message, hints) => hints?.fallback ?? message.key;
101
+ const render = (message, hints) => {
214
102
  if (currentSnapshot.init !== "ready") {
215
- return fallback(key, options);
103
+ return fallback(message, hints);
216
104
  }
217
105
  try {
218
- return driver.t(key, options);
106
+ return driver.t(message.key, message.params);
219
107
  } catch {
220
- return fallback(key, options);
108
+ return fallback(message, hints);
221
109
  }
222
110
  };
223
- const tReady = (key, options, timeoutMs) => import_effect.Effect.gen(function* () {
111
+ const renderReady = (message, hints, timeoutMs) => import_effect.Effect.gen(function* () {
224
112
  const cap = timeoutMs ?? 5e3;
225
113
  const snap0 = yield* import_effect.SubscriptionRef.get(snapshotRef);
226
- if (snap0.init === "ready") return t(key, options);
227
- if (snap0.init === "failed") return fallback(key, options);
114
+ if (snap0.init === "ready") return render(message, hints);
115
+ if (snap0.init === "failed") return fallback(message, hints);
228
116
  const wait = import_effect.Stream.filter(import_effect.SubscriptionRef.changes(snapshotRef), (s) => s.init !== "pending").pipe(
229
117
  import_effect.Stream.runHead,
230
118
  import_effect.Effect.timeoutOption(import_effect.Duration.millis(cap)),
@@ -234,21 +122,19 @@ var makeI18nService = (driver) => import_effect.Effect.gen(function* () {
234
122
  const snap1 = yield* import_effect.SubscriptionRef.get(snapshotRef);
235
123
  if (snap1.init !== "pending") {
236
124
  yield* import_effect.Fiber.interrupt(fiber);
237
- return snap1.init === "ready" ? t(key, options) : fallback(key, options);
125
+ return snap1.init === "ready" ? render(message, hints) : fallback(message, hints);
238
126
  }
239
127
  const outcome = yield* import_effect.Fiber.join(fiber);
240
128
  return import_effect.Option.match(outcome, {
241
- onNone: () => fallback(key, options),
242
- onSome: (snap) => snap.init === "ready" ? t(key, options) : fallback(key, options)
129
+ onNone: () => fallback(message, hints),
130
+ onSome: (snap) => snap.init === "ready" ? render(message, hints) : fallback(message, hints)
243
131
  });
244
132
  });
245
133
  return {
246
- instance: driver,
247
134
  snapshot: snapshotRef,
248
- token,
249
135
  changeLanguage,
250
- t,
251
- tReady
136
+ render,
137
+ renderReady
252
138
  };
253
139
  });
254
140
  var I18n = {
package/dist/I18n.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/I18n.ts","../src/internal/driver/i18n.ts","../src/internal/token/token.ts"],"sourcesContent":["export type {\n I18nDriver,\n I18nInitState,\n I18nService,\n I18nSnapshot,\n I18nTokenOptionsInput,\n} from './internal/driver/i18n.js'\nexport { I18n, I18nSnapshotSchema, I18nTag } from './internal/driver/i18n.js'\n","import {\n Duration,\n Effect,\n Fiber,\n Layer,\n ManagedRuntime,\n Option,\n ServiceMap,\n Scope,\n Schema,\n Stream,\n SubscriptionRef,\n} from 'effect'\n\nimport type { I18nMessageToken, I18nTokenOptionsInput } from '../token/token.js'\nimport { token } from '../token/token.js'\n\nexport type { I18nTokenOptionsInput } from '../token/token.js'\n\nexport type I18nDriver = {\n readonly language: string\n readonly isInitialized?: boolean\n readonly t: (key: string, options?: unknown) => string\n readonly changeLanguage: (language: string) => Promise<unknown> | unknown\n readonly on: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown\n readonly off: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown\n}\n\nexport type I18nInitState = 'pending' | 'ready' | 'failed'\n\nexport type I18nSnapshot = {\n readonly language: string\n readonly init: I18nInitState\n readonly seq: number\n}\n\nexport const I18nSnapshotSchema = Schema.Struct({\n language: Schema.String,\n init: Schema.Literals(['pending', 'ready', 'failed']),\n seq: Schema.Number,\n})\n\nexport type I18nService = {\n readonly instance: I18nDriver\n readonly snapshot: SubscriptionRef.SubscriptionRef<I18nSnapshot>\n readonly token: (key: string, options?: I18nTokenOptionsInput) => I18nMessageToken\n readonly changeLanguage: (language: string) => Effect.Effect<void, never, never>\n readonly t: (key: string, options?: I18nTokenOptionsInput) => string\n readonly tReady: (\n key: string,\n options?: I18nTokenOptionsInput,\n timeoutMs?: number,\n ) => Effect.Effect<string, never, never>\n}\n\nexport class I18nTag extends ServiceMap.Service<I18nTag, I18nService>()('@logixjs/i18n/I18n') {}\n\nconst asNonEmptyString = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined\n\nconst makeI18nService = (driver: I18nDriver): Effect.Effect<I18nService, never, Scope.Scope> =>\n Effect.gen(function* () {\n const init: I18nInitState = driver.isInitialized ? 'ready' : 'pending'\n let currentSnapshot: I18nSnapshot = {\n language: driver.language,\n init,\n seq: 0,\n }\n const snapshotRef = yield* SubscriptionRef.make<I18nSnapshot>(currentSnapshot)\n\n const update = (patch: Partial<Pick<I18nSnapshot, 'language' | 'init'>>): Effect.Effect<void> =>\n SubscriptionRef.update(snapshotRef, (prev) => {\n const next: I18nSnapshot = {\n language: patch.language ?? prev.language,\n init: patch.init ?? prev.init,\n seq: prev.seq + 1,\n }\n currentSnapshot = next\n return next\n })\n\n const pushSnapshot = (patch: Partial<Pick<I18nSnapshot, 'language' | 'init'>>): void => {\n const next: I18nSnapshot = {\n language: patch.language ?? currentSnapshot.language,\n init: patch.init ?? currentSnapshot.init,\n seq: currentSnapshot.seq + 1,\n }\n currentSnapshot = next\n void Effect.runPromise(SubscriptionRef.set(snapshotRef, next).pipe(Effect.catchCause(() => Effect.void)))\n }\n\n const onInitialized = (): void => {\n pushSnapshot({ init: 'ready', language: driver.language })\n }\n\n const onLanguageChanged = (lang: unknown): void => {\n pushSnapshot({\n init: 'ready',\n language: asNonEmptyString(lang) ?? driver.language,\n })\n }\n\n yield* Effect.sync(() => {\n driver.on('initialized', onInitialized)\n driver.on('languageChanged', onLanguageChanged)\n })\n\n const scope = yield* Effect.scope\n yield* Scope.addFinalizer(\n scope,\n Effect.sync(() => {\n driver.off('initialized', onInitialized)\n driver.off('languageChanged', onLanguageChanged)\n }),\n )\n\n const changeLanguage = (language: string): Effect.Effect<void, never, never> =>\n Effect.gen(function* () {\n yield* update({ language, init: 'pending' })\n const exit = yield* Effect.exit(\n Effect.tryPromise({\n try: () => Promise.resolve(driver.changeLanguage(language)),\n catch: () => undefined,\n }),\n )\n if (exit._tag === 'Failure') {\n yield* update({ init: 'failed' })\n } else {\n yield* update({ init: 'ready', language })\n }\n })\n\n const fallback = (key: string, options?: I18nTokenOptionsInput): string =>\n typeof options?.defaultValue === 'string' ? options.defaultValue : key\n\n const t = (key: string, options?: I18nTokenOptionsInput): string => {\n if (currentSnapshot.init !== 'ready') {\n return fallback(key, options)\n }\n try {\n return driver.t(key, options)\n } catch {\n return fallback(key, options)\n }\n }\n\n const tReady = (\n key: string,\n options?: I18nTokenOptionsInput,\n timeoutMs?: number,\n ): Effect.Effect<string, never, never> =>\n Effect.gen(function* () {\n const cap = timeoutMs ?? 5000\n const snap0 = yield* SubscriptionRef.get(snapshotRef)\n if (snap0.init === 'ready') return t(key, options)\n if (snap0.init === 'failed') return fallback(key, options)\n\n const wait = Stream.filter(SubscriptionRef.changes(snapshotRef), (s) => s.init !== 'pending').pipe(\n Stream.runHead,\n Effect.timeoutOption(Duration.millis(cap)),\n Effect.map((maybe) => (Option.isSome(maybe) ? maybe.value : Option.none())),\n )\n\n const fiber = yield* wait.pipe(Effect.forkChild)\n\n const snap1 = yield* SubscriptionRef.get(snapshotRef)\n if (snap1.init !== 'pending') {\n yield* Fiber.interrupt(fiber)\n return snap1.init === 'ready' ? t(key, options) : fallback(key, options)\n }\n\n const outcome = yield* Fiber.join(fiber)\n return Option.match(outcome, {\n onNone: () => fallback(key, options),\n onSome: (snap) => (snap.init === 'ready' ? t(key, options) : fallback(key, options)),\n })\n })\n\n return {\n instance: driver,\n snapshot: snapshotRef,\n token,\n changeLanguage,\n t,\n tReady,\n } as const\n })\n\nexport const I18n = {\n layer: (driver: I18nDriver): Layer.Layer<I18nTag, never, never> => Layer.effect(I18nTag, makeI18nService(driver)),\n} as const\n","export type JsonPrimitive = null | boolean | number | string\n\nexport type I18nTokenOptions = Readonly<Record<string, JsonPrimitive>>\nexport type I18nTokenOptionsInput = Readonly<Record<string, JsonPrimitive | undefined>>\n\nexport type I18nMessageToken = {\n readonly _tag: 'i18n'\n readonly key: string\n readonly options?: I18nTokenOptions\n}\n\nexport type InvalidI18nMessageTokenReason =\n | 'keyTooLong'\n | 'tooManyOptions'\n | 'optionKeyInvalid'\n | 'optionValueInvalid'\n | 'optionValueTooLong'\n | 'numberNotJsonSafe'\n | 'languageFrozen'\n\nexport class InvalidI18nMessageTokenError extends Error {\n readonly name = 'InvalidI18nMessageTokenError'\n\n constructor(\n readonly reason: InvalidI18nMessageTokenReason,\n readonly details: unknown,\n readonly fix: ReadonlyArray<string>,\n ) {\n super(`[InvalidI18nMessageTokenError] reason=${reason}`)\n }\n}\n\nconst TOKEN_BUDGET = {\n keyMaxLen: 96,\n optionKeyMaxCount: 8,\n optionValueStringMaxLen: 96,\n} as const\n\nconst LANGUAGE_FROZEN_KEYS = new Set(['lng', 'lngs'])\nconst DANGEROUS_OPTION_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\nconst isJsonPrimitive = (value: unknown): value is JsonPrimitive =>\n value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string'\n\nconst invalidToken = (reason: InvalidI18nMessageTokenReason, details: unknown, fix: ReadonlyArray<string>): never => {\n throw new InvalidI18nMessageTokenError(reason, details, fix)\n}\n\nexport const canonicalizeTokenOptions = (options: I18nTokenOptionsInput | undefined): I18nTokenOptions | undefined => {\n if (!options) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options)) {\n invalidToken('optionValueInvalid', { field: 'options', actual: typeof options }, [\n '请传入 plain object 作为 options(Record<string, JsonPrimitive>)。',\n '不要传入数组/函数/类实例等不可序列化值。',\n ])\n }\n\n const entries = Object.entries(options).filter((p): p is [string, JsonPrimitive] => p[1] !== undefined)\n\n if (entries.length === 0) return undefined\n\n if (entries.length > TOKEN_BUDGET.optionKeyMaxCount) {\n invalidToken(\n 'tooManyOptions',\n {\n field: 'options',\n max: TOKEN_BUDGET.optionKeyMaxCount,\n actual: entries.length,\n },\n [\n `减少 options 键数量(建议 ≤ ${TOKEN_BUDGET.optionKeyMaxCount})。`,\n '把较长的信息挪到 key 或 defaultValue;避免把大对象塞进 token。',\n ],\n )\n }\n\n for (const [k, v] of entries) {\n if (DANGEROUS_OPTION_KEYS.has(k) || k.length === 0) {\n invalidToken('optionKeyInvalid', { field: `options.${k}`, key: k }, [\n '请使用普通字段名作为 options key(避免 __proto__/constructor/prototype 等危险键)。',\n '如需传递复杂结构,请先在展示边界转换为字符串。',\n ])\n }\n\n if (LANGUAGE_FROZEN_KEYS.has(k)) {\n invalidToken('languageFrozen', { field: `options.${k}`, key: k }, [\n '不要在 token options 中传入 lng/lngs 等语言冻结字段。',\n '语言由外部 i18n 实例决定;token 只表达“要翻译什么”。',\n ])\n }\n\n if (!isJsonPrimitive(v)) {\n invalidToken('optionValueInvalid', { field: `options.${k}`, actual: typeof v }, [\n 'options value 只允许 JsonPrimitive(null/boolean/number/string)。',\n '不要传入对象/数组/函数;需要时请在展示边界先格式化成字符串。',\n ])\n }\n\n if (typeof v === 'number' && !Number.isFinite(v)) {\n invalidToken('numberNotJsonSafe', { field: `options.${k}`, value: String(v) }, [\n '不要在 token options 中传入 NaN/Infinity。',\n '请先把该数值转换为可 JSON 化的 number 或 string。',\n ])\n }\n\n if (typeof v === 'string' && v.length > TOKEN_BUDGET.optionValueStringMaxLen) {\n invalidToken(\n 'optionValueTooLong',\n {\n field: `options.${k}`,\n maxLen: TOKEN_BUDGET.optionValueStringMaxLen,\n actualLen: v.length,\n },\n [\n `缩短字符串值长度(建议 ≤ ${TOKEN_BUDGET.optionValueStringMaxLen})。`,\n '把长文本移到 defaultValue 或直接在展示边界生成最终字符串。',\n ],\n )\n }\n }\n\n entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\n const out: Record<string, JsonPrimitive> = {}\n for (const [k, v] of entries) {\n out[k] = v\n }\n return out\n}\n\nexport const token = (key: string, options?: I18nTokenOptionsInput): I18nMessageToken => {\n if (key.length > TOKEN_BUDGET.keyMaxLen) {\n invalidToken('keyTooLong', { field: 'key', maxLen: TOKEN_BUDGET.keyMaxLen, actualLen: key.length }, [\n `缩短 key(建议 ≤ ${TOKEN_BUDGET.keyMaxLen})。`,\n '如果 key 过长,建议改为“稳定 key + 变量 options/defaultValue”。',\n ])\n }\n\n const canon = canonicalizeTokenOptions(options)\n return canon\n ? {\n _tag: 'i18n',\n key,\n options: canon,\n }\n : {\n _tag: 'i18n',\n key,\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/I18n.ts","../src/internal/driver/i18n.ts"],"sourcesContent":["export type {\n I18nDriver,\n I18nInitState,\n I18nRenderHints,\n I18nService,\n I18nSnapshot,\n I18nTokenParamsInput,\n} from './internal/driver/i18n.js'\n\nexport { I18n, I18nSnapshotSchema, I18nTag } from './internal/driver/i18n.js'\n","import {\n Duration,\n Effect,\n Fiber,\n Layer,\n ManagedRuntime,\n Option,\n ServiceMap,\n Scope,\n Schema,\n Stream,\n SubscriptionRef,\n} from 'effect'\n\nimport type { I18nMessageToken, I18nTokenParamsInput } from '../token/token.js'\n\nexport type { I18nTokenParamsInput } from '../token/token.js'\n\n/**\n * Low-level driver contract.\n *\n * Package-level canonical consumption remains service-first:\n * callers should read `services.i18n` or `I18nTag`, rather than coupling to driver lifecycle details directly.\n */\nexport type I18nDriver = {\n readonly language: string\n readonly isInitialized?: boolean\n readonly t: (key: string, input?: unknown) => string\n readonly changeLanguage: (language: string) => Promise<unknown> | unknown\n readonly on: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown\n readonly off: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown\n}\n\nexport type I18nInitState = 'pending' | 'ready' | 'failed'\n\nexport type I18nSnapshot = {\n readonly language: string\n readonly init: I18nInitState\n readonly seq: number\n}\n\nexport const I18nSnapshotSchema = Schema.Struct({\n language: Schema.String,\n init: Schema.Literals(['pending', 'ready', 'failed']),\n seq: Schema.Number,\n})\n\nexport type I18nRenderHints = {\n readonly fallback?: string\n}\n\nexport type I18nService = {\n readonly snapshot: SubscriptionRef.SubscriptionRef<I18nSnapshot>\n readonly changeLanguage: (language: string) => Effect.Effect<void, never, never>\n readonly render: (token: I18nMessageToken, hints?: I18nRenderHints) => string\n readonly renderReady: (\n token: I18nMessageToken,\n hints?: I18nRenderHints,\n timeoutMs?: number,\n ) => Effect.Effect<string, never, never>\n}\n\nexport class I18nTag extends ServiceMap.Service<I18nTag, I18nService>()('@logixjs/i18n/I18n') {}\n\nconst asNonEmptyString = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined\n\nconst makeI18nService = (driver: I18nDriver): Effect.Effect<I18nService, never, Scope.Scope> =>\n Effect.gen(function* () {\n const init: I18nInitState = driver.isInitialized ? 'ready' : 'pending'\n let currentSnapshot: I18nSnapshot = {\n language: driver.language,\n init,\n seq: 0,\n }\n const snapshotRef = yield* SubscriptionRef.make<I18nSnapshot>(currentSnapshot)\n\n const update = (patch: Partial<Pick<I18nSnapshot, 'language' | 'init'>>): Effect.Effect<void> =>\n SubscriptionRef.update(snapshotRef, (prev) => {\n const next: I18nSnapshot = {\n language: patch.language ?? prev.language,\n init: patch.init ?? prev.init,\n seq: prev.seq + 1,\n }\n currentSnapshot = next\n return next\n })\n\n const pushSnapshot = (patch: Partial<Pick<I18nSnapshot, 'language' | 'init'>>): void => {\n const next: I18nSnapshot = {\n language: patch.language ?? currentSnapshot.language,\n init: patch.init ?? currentSnapshot.init,\n seq: currentSnapshot.seq + 1,\n }\n currentSnapshot = next\n void Effect.runPromise(SubscriptionRef.set(snapshotRef, next).pipe(Effect.catchCause(() => Effect.void)))\n }\n\n const onInitialized = (): void => {\n pushSnapshot({ init: 'ready', language: driver.language })\n }\n\n const onLanguageChanged = (lang: unknown): void => {\n pushSnapshot({\n init: 'ready',\n language: asNonEmptyString(lang) ?? driver.language,\n })\n }\n\n yield* Effect.sync(() => {\n driver.on('initialized', onInitialized)\n driver.on('languageChanged', onLanguageChanged)\n })\n\n const scope = yield* Effect.scope\n yield* Scope.addFinalizer(\n scope,\n Effect.sync(() => {\n driver.off('initialized', onInitialized)\n driver.off('languageChanged', onLanguageChanged)\n }),\n )\n\n /**\n * Canonical runtime-facing lifecycle wiring:\n * - driver events update the shared snapshot\n * - consumers stay on the service contract (`render`, `renderReady`, `snapshot`)\n */\n const changeLanguage = (language: string): Effect.Effect<void, never, never> =>\n Effect.gen(function* () {\n yield* update({ language, init: 'pending' })\n const exit = yield* Effect.exit(\n Effect.tryPromise({\n try: () => Promise.resolve(driver.changeLanguage(language)),\n catch: () => undefined,\n }),\n )\n if (exit._tag === 'Failure') {\n yield* update({ init: 'failed' })\n } else {\n yield* update({ init: 'ready', language })\n }\n })\n\n const fallback = (message: I18nMessageToken, hints?: I18nRenderHints): string => hints?.fallback ?? message.key\n\n const render = (message: I18nMessageToken, hints?: I18nRenderHints): string => {\n if (currentSnapshot.init !== 'ready') {\n return fallback(message, hints)\n }\n try {\n return driver.t(message.key, message.params as I18nTokenParamsInput | undefined)\n } catch {\n return fallback(message, hints)\n }\n }\n\n const renderReady = (\n message: I18nMessageToken,\n hints?: I18nRenderHints,\n timeoutMs?: number,\n ): Effect.Effect<string, never, never> =>\n Effect.gen(function* () {\n const cap = timeoutMs ?? 5000\n const snap0 = yield* SubscriptionRef.get(snapshotRef)\n if (snap0.init === 'ready') return render(message, hints)\n if (snap0.init === 'failed') return fallback(message, hints)\n\n const wait = Stream.filter(SubscriptionRef.changes(snapshotRef), (s) => s.init !== 'pending').pipe(\n Stream.runHead,\n Effect.timeoutOption(Duration.millis(cap)),\n Effect.map((maybe) => (Option.isSome(maybe) ? maybe.value : Option.none())),\n )\n\n const fiber = yield* wait.pipe(Effect.forkChild)\n\n const snap1 = yield* SubscriptionRef.get(snapshotRef)\n if (snap1.init !== 'pending') {\n yield* Fiber.interrupt(fiber)\n return snap1.init === 'ready' ? render(message, hints) : fallback(message, hints)\n }\n\n const outcome = yield* Fiber.join(fiber)\n return Option.match(outcome, {\n onNone: () => fallback(message, hints),\n onSome: (snap) => (snap.init === 'ready' ? render(message, hints) : fallback(message, hints)),\n })\n })\n\n return {\n snapshot: snapshotRef,\n changeLanguage,\n render,\n renderReady,\n } as const\n })\n\nexport const I18n = {\n layer: (driver: I18nDriver): Layer.Layer<I18nTag, never, never> => Layer.effect(I18nTag, makeI18nService(driver)),\n} as const\n"],"mappings":"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","names":[]}
package/dist/I18n.d.cts CHANGED
@@ -1,10 +1,17 @@
1
+ import { I18nMessageToken } from './Token.cjs';
2
+ export { I18nTokenParamsInput } from './Token.cjs';
1
3
  import { SubscriptionRef, Effect, Layer, ServiceMap, Schema } from 'effect';
2
- import { I18nTokenOptionsInput, I18nMessageToken } from './Token.cjs';
3
4
 
5
+ /**
6
+ * Low-level driver contract.
7
+ *
8
+ * Package-level canonical consumption remains service-first:
9
+ * callers should read `services.i18n` or `I18nTag`, rather than coupling to driver lifecycle details directly.
10
+ */
4
11
  type I18nDriver = {
5
12
  readonly language: string;
6
13
  readonly isInitialized?: boolean;
7
- readonly t: (key: string, options?: unknown) => string;
14
+ readonly t: (key: string, input?: unknown) => string;
8
15
  readonly changeLanguage: (language: string) => Promise<unknown> | unknown;
9
16
  readonly on: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown;
10
17
  readonly off: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown;
@@ -20,13 +27,14 @@ declare const I18nSnapshotSchema: Schema.Struct<{
20
27
  readonly init: Schema.Literals<readonly ["pending", "ready", "failed"]>;
21
28
  readonly seq: Schema.Number;
22
29
  }>;
30
+ type I18nRenderHints = {
31
+ readonly fallback?: string;
32
+ };
23
33
  type I18nService = {
24
- readonly instance: I18nDriver;
25
34
  readonly snapshot: SubscriptionRef.SubscriptionRef<I18nSnapshot>;
26
- readonly token: (key: string, options?: I18nTokenOptionsInput) => I18nMessageToken;
27
35
  readonly changeLanguage: (language: string) => Effect.Effect<void, never, never>;
28
- readonly t: (key: string, options?: I18nTokenOptionsInput) => string;
29
- readonly tReady: (key: string, options?: I18nTokenOptionsInput, timeoutMs?: number) => Effect.Effect<string, never, never>;
36
+ readonly render: (token: I18nMessageToken, hints?: I18nRenderHints) => string;
37
+ readonly renderReady: (token: I18nMessageToken, hints?: I18nRenderHints, timeoutMs?: number) => Effect.Effect<string, never, never>;
30
38
  };
31
39
  declare const I18nTag_base: ServiceMap.ServiceClass<I18nTag, "@logixjs/i18n/I18n", I18nService>;
32
40
  declare class I18nTag extends I18nTag_base {
@@ -35,4 +43,4 @@ declare const I18n: {
35
43
  readonly layer: (driver: I18nDriver) => Layer.Layer<I18nTag, never, never>;
36
44
  };
37
45
 
38
- export { I18n, type I18nDriver, type I18nInitState, type I18nService, type I18nSnapshot, I18nSnapshotSchema, I18nTag, I18nTokenOptionsInput };
46
+ export { I18n, type I18nDriver, type I18nInitState, type I18nRenderHints, type I18nService, type I18nSnapshot, I18nSnapshotSchema, I18nTag };
package/dist/I18n.d.ts CHANGED
@@ -1,10 +1,17 @@
1
+ import { I18nMessageToken } from './Token.js';
2
+ export { I18nTokenParamsInput } from './Token.js';
1
3
  import { SubscriptionRef, Effect, Layer, ServiceMap, Schema } from 'effect';
2
- import { I18nTokenOptionsInput, I18nMessageToken } from './Token.js';
3
4
 
5
+ /**
6
+ * Low-level driver contract.
7
+ *
8
+ * Package-level canonical consumption remains service-first:
9
+ * callers should read `services.i18n` or `I18nTag`, rather than coupling to driver lifecycle details directly.
10
+ */
4
11
  type I18nDriver = {
5
12
  readonly language: string;
6
13
  readonly isInitialized?: boolean;
7
- readonly t: (key: string, options?: unknown) => string;
14
+ readonly t: (key: string, input?: unknown) => string;
8
15
  readonly changeLanguage: (language: string) => Promise<unknown> | unknown;
9
16
  readonly on: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown;
10
17
  readonly off: (event: 'initialized' | 'languageChanged', handler: (...args: any[]) => void) => unknown;
@@ -20,13 +27,14 @@ declare const I18nSnapshotSchema: Schema.Struct<{
20
27
  readonly init: Schema.Literals<readonly ["pending", "ready", "failed"]>;
21
28
  readonly seq: Schema.Number;
22
29
  }>;
30
+ type I18nRenderHints = {
31
+ readonly fallback?: string;
32
+ };
23
33
  type I18nService = {
24
- readonly instance: I18nDriver;
25
34
  readonly snapshot: SubscriptionRef.SubscriptionRef<I18nSnapshot>;
26
- readonly token: (key: string, options?: I18nTokenOptionsInput) => I18nMessageToken;
27
35
  readonly changeLanguage: (language: string) => Effect.Effect<void, never, never>;
28
- readonly t: (key: string, options?: I18nTokenOptionsInput) => string;
29
- readonly tReady: (key: string, options?: I18nTokenOptionsInput, timeoutMs?: number) => Effect.Effect<string, never, never>;
36
+ readonly render: (token: I18nMessageToken, hints?: I18nRenderHints) => string;
37
+ readonly renderReady: (token: I18nMessageToken, hints?: I18nRenderHints, timeoutMs?: number) => Effect.Effect<string, never, never>;
30
38
  };
31
39
  declare const I18nTag_base: ServiceMap.ServiceClass<I18nTag, "@logixjs/i18n/I18n", I18nService>;
32
40
  declare class I18nTag extends I18nTag_base {
@@ -35,4 +43,4 @@ declare const I18n: {
35
43
  readonly layer: (driver: I18nDriver) => Layer.Layer<I18nTag, never, never>;
36
44
  };
37
45
 
38
- export { I18n, type I18nDriver, type I18nInitState, type I18nService, type I18nSnapshot, I18nSnapshotSchema, I18nTag, I18nTokenOptionsInput };
46
+ export { I18n, type I18nDriver, type I18nInitState, type I18nRenderHints, type I18nService, type I18nSnapshot, I18nSnapshotSchema, I18nTag };
package/dist/I18n.js CHANGED
@@ -1,10 +1,8 @@
1
- import "./chunk-FDPDEDU7.js";
2
1
  import {
3
2
  I18n,
4
3
  I18nSnapshotSchema,
5
4
  I18nTag
6
- } from "./chunk-4NKGIEWG.js";
7
- import "./chunk-NWWL4MNH.js";
5
+ } from "./chunk-DQVWWU7T.js";
8
6
  export {
9
7
  I18n,
10
8
  I18nSnapshotSchema,
package/dist/Token.cjs CHANGED
@@ -21,7 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var Token_exports = {};
22
22
  __export(Token_exports, {
23
23
  InvalidI18nMessageTokenError: () => InvalidI18nMessageTokenError,
24
- canonicalizeTokenOptions: () => canonicalizeTokenOptions,
25
24
  token: () => token
26
25
  });
27
26
  module.exports = __toCommonJS(Token_exports);
@@ -38,75 +37,83 @@ var InvalidI18nMessageTokenError = class extends Error {
38
37
  };
39
38
  var TOKEN_BUDGET = {
40
39
  keyMaxLen: 96,
41
- optionKeyMaxCount: 8,
42
- optionValueStringMaxLen: 96
40
+ paramKeyMaxCount: 8,
41
+ paramValueStringMaxLen: 96
43
42
  };
44
43
  var LANGUAGE_FROZEN_KEYS = /* @__PURE__ */ new Set(["lng", "lngs"]);
45
- var DANGEROUS_OPTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
44
+ var DANGEROUS_PARAM_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
45
+ var LEGACY_RENDER_FALLBACK_KEY = `default${"Value"}`;
46
+ var RESERVED_RENDER_FALLBACK_KEYS = /* @__PURE__ */ new Set([LEGACY_RENDER_FALLBACK_KEY]);
46
47
  var isJsonPrimitive = (value) => value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string";
47
48
  var invalidToken = (reason, details, fix) => {
48
49
  throw new InvalidI18nMessageTokenError(reason, details, fix);
49
50
  };
50
- var canonicalizeTokenOptions = (options) => {
51
- if (!options) return void 0;
52
- if (typeof options !== "object" || options === null || Array.isArray(options)) {
53
- invalidToken("optionValueInvalid", { field: "options", actual: typeof options }, [
54
- "\u8BF7\u4F20\u5165 plain object \u4F5C\u4E3A options\uFF08Record<string, JsonPrimitive>\uFF09\u3002",
51
+ var canonicalizeTokenParams = (params) => {
52
+ if (!params) return void 0;
53
+ if (typeof params !== "object" || params === null || Array.isArray(params)) {
54
+ invalidToken("paramValueInvalid", { field: "params", actual: typeof params }, [
55
+ "\u8BF7\u4F20\u5165 plain object \u4F5C\u4E3A params\uFF08Record<string, JsonPrimitive>\uFF09\u3002",
55
56
  "\u4E0D\u8981\u4F20\u5165\u6570\u7EC4/\u51FD\u6570/\u7C7B\u5B9E\u4F8B\u7B49\u4E0D\u53EF\u5E8F\u5217\u5316\u503C\u3002"
56
57
  ]);
57
58
  }
58
- const entries = Object.entries(options).filter((p) => p[1] !== void 0);
59
+ const entries = Object.entries(params).filter((p) => p[1] !== void 0);
59
60
  if (entries.length === 0) return void 0;
60
- if (entries.length > TOKEN_BUDGET.optionKeyMaxCount) {
61
+ if (entries.length > TOKEN_BUDGET.paramKeyMaxCount) {
61
62
  invalidToken(
62
- "tooManyOptions",
63
+ "tooManyParams",
63
64
  {
64
- field: "options",
65
- max: TOKEN_BUDGET.optionKeyMaxCount,
65
+ field: "params",
66
+ max: TOKEN_BUDGET.paramKeyMaxCount,
66
67
  actual: entries.length
67
68
  },
68
69
  [
69
- `\u51CF\u5C11 options \u952E\u6570\u91CF\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.optionKeyMaxCount}\uFF09\u3002`,
70
- "\u628A\u8F83\u957F\u7684\u4FE1\u606F\u632A\u5230 key \u6216 defaultValue\uFF1B\u907F\u514D\u628A\u5927\u5BF9\u8C61\u585E\u8FDB token\u3002"
70
+ `\u51CF\u5C11 params \u952E\u6570\u91CF\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.paramKeyMaxCount}\uFF09\u3002`,
71
+ "\u907F\u514D\u628A\u5927\u5BF9\u8C61\u6216\u5C55\u793A\u515C\u5E95\u6587\u6848\u585E\u8FDB semantic token\u3002"
71
72
  ]
72
73
  );
73
74
  }
74
75
  for (const [k, v] of entries) {
75
- if (DANGEROUS_OPTION_KEYS.has(k) || k.length === 0) {
76
- invalidToken("optionKeyInvalid", { field: `options.${k}`, key: k }, [
77
- "\u8BF7\u4F7F\u7528\u666E\u901A\u5B57\u6BB5\u540D\u4F5C\u4E3A options key\uFF08\u907F\u514D __proto__/constructor/prototype \u7B49\u5371\u9669\u952E\uFF09\u3002",
76
+ if (RESERVED_RENDER_FALLBACK_KEYS.has(k)) {
77
+ invalidToken("renderFallbackReserved", { field: `params.${k}`, key: k }, [
78
+ "\u4E0D\u8981\u628A\u65E7\u7684\u5C55\u793A\u515C\u5E95\u5B57\u6BB5\u653E\u8FDB semantic token params\u3002",
79
+ "\u8BF7\u628A\u515C\u5E95\u6587\u6848\u6539\u653E\u5230 render/renderReady \u7684 hints.fallback\u3002"
80
+ ]);
81
+ }
82
+ if (DANGEROUS_PARAM_KEYS.has(k) || k.length === 0) {
83
+ invalidToken("paramKeyInvalid", { field: `params.${k}`, key: k }, [
84
+ "\u8BF7\u4F7F\u7528\u666E\u901A\u5B57\u6BB5\u540D\u4F5C\u4E3A params key\uFF0C\u907F\u514D __proto__/constructor/prototype \u7B49\u5371\u9669\u952E\u3002",
78
85
  "\u5982\u9700\u4F20\u9012\u590D\u6742\u7ED3\u6784\uFF0C\u8BF7\u5148\u5728\u5C55\u793A\u8FB9\u754C\u8F6C\u6362\u4E3A\u5B57\u7B26\u4E32\u3002"
79
86
  ]);
80
87
  }
81
88
  if (LANGUAGE_FROZEN_KEYS.has(k)) {
82
- invalidToken("languageFrozen", { field: `options.${k}`, key: k }, [
83
- "\u4E0D\u8981\u5728 token options \u4E2D\u4F20\u5165 lng/lngs \u7B49\u8BED\u8A00\u51BB\u7ED3\u5B57\u6BB5\u3002",
89
+ invalidToken("languageFrozen", { field: `params.${k}`, key: k }, [
90
+ "\u4E0D\u8981\u5728 token params \u4E2D\u4F20\u5165 lng/lngs \u7B49\u8BED\u8A00\u51BB\u7ED3\u5B57\u6BB5\u3002",
84
91
  "\u8BED\u8A00\u7531\u5916\u90E8 i18n \u5B9E\u4F8B\u51B3\u5B9A\uFF1Btoken \u53EA\u8868\u8FBE\u201C\u8981\u7FFB\u8BD1\u4EC0\u4E48\u201D\u3002"
85
92
  ]);
86
93
  }
87
94
  if (!isJsonPrimitive(v)) {
88
- invalidToken("optionValueInvalid", { field: `options.${k}`, actual: typeof v }, [
89
- "options value \u53EA\u5141\u8BB8 JsonPrimitive\uFF08null/boolean/number/string\uFF09\u3002",
95
+ invalidToken("paramValueInvalid", { field: `params.${k}`, actual: typeof v }, [
96
+ "params value \u53EA\u5141\u8BB8 JsonPrimitive\uFF08null/boolean/number/string\uFF09\u3002",
90
97
  "\u4E0D\u8981\u4F20\u5165\u5BF9\u8C61/\u6570\u7EC4/\u51FD\u6570\uFF1B\u9700\u8981\u65F6\u8BF7\u5728\u5C55\u793A\u8FB9\u754C\u5148\u683C\u5F0F\u5316\u6210\u5B57\u7B26\u4E32\u3002"
91
98
  ]);
92
99
  }
93
100
  if (typeof v === "number" && !Number.isFinite(v)) {
94
- invalidToken("numberNotJsonSafe", { field: `options.${k}`, value: String(v) }, [
95
- "\u4E0D\u8981\u5728 token options \u4E2D\u4F20\u5165 NaN/Infinity\u3002",
101
+ invalidToken("numberNotJsonSafe", { field: `params.${k}`, value: String(v) }, [
102
+ "\u4E0D\u8981\u5728 token params \u4E2D\u4F20\u5165 NaN/Infinity\u3002",
96
103
  "\u8BF7\u5148\u628A\u8BE5\u6570\u503C\u8F6C\u6362\u4E3A\u53EF JSON \u5316\u7684 number \u6216 string\u3002"
97
104
  ]);
98
105
  }
99
- if (typeof v === "string" && v.length > TOKEN_BUDGET.optionValueStringMaxLen) {
106
+ if (typeof v === "string" && v.length > TOKEN_BUDGET.paramValueStringMaxLen) {
100
107
  invalidToken(
101
- "optionValueTooLong",
108
+ "paramValueTooLong",
102
109
  {
103
- field: `options.${k}`,
104
- maxLen: TOKEN_BUDGET.optionValueStringMaxLen,
110
+ field: `params.${k}`,
111
+ maxLen: TOKEN_BUDGET.paramValueStringMaxLen,
105
112
  actualLen: v.length
106
113
  },
107
114
  [
108
- `\u7F29\u77ED\u5B57\u7B26\u4E32\u503C\u957F\u5EA6\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.optionValueStringMaxLen}\uFF09\u3002`,
109
- "\u628A\u957F\u6587\u672C\u79FB\u5230 defaultValue \u6216\u76F4\u63A5\u5728\u5C55\u793A\u8FB9\u754C\u751F\u6210\u6700\u7EC8\u5B57\u7B26\u4E32\u3002"
115
+ `\u7F29\u77ED\u5B57\u7B26\u4E32\u503C\u957F\u5EA6\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.paramValueStringMaxLen}\uFF09\u3002`,
116
+ "\u628A\u957F\u6587\u672C\u7559\u5728\u5C55\u793A\u8FB9\u754C\uFF0C\u4E0D\u8981\u653E\u8FDB semantic token\u3002"
110
117
  ]
111
118
  );
112
119
  }
@@ -118,18 +125,18 @@ var canonicalizeTokenOptions = (options) => {
118
125
  }
119
126
  return out;
120
127
  };
121
- var token = (key, options) => {
128
+ var token = (key, params) => {
122
129
  if (key.length > TOKEN_BUDGET.keyMaxLen) {
123
130
  invalidToken("keyTooLong", { field: "key", maxLen: TOKEN_BUDGET.keyMaxLen, actualLen: key.length }, [
124
131
  `\u7F29\u77ED key\uFF08\u5EFA\u8BAE \u2264 ${TOKEN_BUDGET.keyMaxLen}\uFF09\u3002`,
125
- "\u5982\u679C key \u8FC7\u957F\uFF0C\u5EFA\u8BAE\u6539\u4E3A\u201C\u7A33\u5B9A key + \u53D8\u91CF options/defaultValue\u201D\u3002"
132
+ "\u5982\u679C key \u8FC7\u957F\uFF0C\u5EFA\u8BAE\u6539\u4E3A\u201C\u7A33\u5B9A key + \u5C11\u91CF semantic params\u201D\u3002"
126
133
  ]);
127
134
  }
128
- const canon = canonicalizeTokenOptions(options);
135
+ const canon = canonicalizeTokenParams(params);
129
136
  return canon ? {
130
137
  _tag: "i18n",
131
138
  key,
132
- options: canon
139
+ params: canon
133
140
  } : {
134
141
  _tag: "i18n",
135
142
  key
@@ -138,7 +145,6 @@ var token = (key, options) => {
138
145
  // Annotate the CommonJS export names for ESM import in node:
139
146
  0 && (module.exports = {
140
147
  InvalidI18nMessageTokenError,
141
- canonicalizeTokenOptions,
142
148
  token
143
149
  });
144
150
  //# sourceMappingURL=Token.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Token.ts","../src/internal/token/token.ts"],"sourcesContent":["export type {\n I18nMessageToken,\n I18nTokenOptions,\n I18nTokenOptionsInput,\n InvalidI18nMessageTokenReason,\n JsonPrimitive,\n} from './internal/token/token.js'\nexport { InvalidI18nMessageTokenError, canonicalizeTokenOptions, token } from './internal/token/token.js'\n","export type JsonPrimitive = null | boolean | number | string\n\nexport type I18nTokenOptions = Readonly<Record<string, JsonPrimitive>>\nexport type I18nTokenOptionsInput = Readonly<Record<string, JsonPrimitive | undefined>>\n\nexport type I18nMessageToken = {\n readonly _tag: 'i18n'\n readonly key: string\n readonly options?: I18nTokenOptions\n}\n\nexport type InvalidI18nMessageTokenReason =\n | 'keyTooLong'\n | 'tooManyOptions'\n | 'optionKeyInvalid'\n | 'optionValueInvalid'\n | 'optionValueTooLong'\n | 'numberNotJsonSafe'\n | 'languageFrozen'\n\nexport class InvalidI18nMessageTokenError extends Error {\n readonly name = 'InvalidI18nMessageTokenError'\n\n constructor(\n readonly reason: InvalidI18nMessageTokenReason,\n readonly details: unknown,\n readonly fix: ReadonlyArray<string>,\n ) {\n super(`[InvalidI18nMessageTokenError] reason=${reason}`)\n }\n}\n\nconst TOKEN_BUDGET = {\n keyMaxLen: 96,\n optionKeyMaxCount: 8,\n optionValueStringMaxLen: 96,\n} as const\n\nconst LANGUAGE_FROZEN_KEYS = new Set(['lng', 'lngs'])\nconst DANGEROUS_OPTION_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\nconst isJsonPrimitive = (value: unknown): value is JsonPrimitive =>\n value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string'\n\nconst invalidToken = (reason: InvalidI18nMessageTokenReason, details: unknown, fix: ReadonlyArray<string>): never => {\n throw new InvalidI18nMessageTokenError(reason, details, fix)\n}\n\nexport const canonicalizeTokenOptions = (options: I18nTokenOptionsInput | undefined): I18nTokenOptions | undefined => {\n if (!options) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options)) {\n invalidToken('optionValueInvalid', { field: 'options', actual: typeof options }, [\n '请传入 plain object 作为 options(Record<string, JsonPrimitive>)。',\n '不要传入数组/函数/类实例等不可序列化值。',\n ])\n }\n\n const entries = Object.entries(options).filter((p): p is [string, JsonPrimitive] => p[1] !== undefined)\n\n if (entries.length === 0) return undefined\n\n if (entries.length > TOKEN_BUDGET.optionKeyMaxCount) {\n invalidToken(\n 'tooManyOptions',\n {\n field: 'options',\n max: TOKEN_BUDGET.optionKeyMaxCount,\n actual: entries.length,\n },\n [\n `减少 options 键数量(建议 ≤ ${TOKEN_BUDGET.optionKeyMaxCount})。`,\n '把较长的信息挪到 key 或 defaultValue;避免把大对象塞进 token。',\n ],\n )\n }\n\n for (const [k, v] of entries) {\n if (DANGEROUS_OPTION_KEYS.has(k) || k.length === 0) {\n invalidToken('optionKeyInvalid', { field: `options.${k}`, key: k }, [\n '请使用普通字段名作为 options key(避免 __proto__/constructor/prototype 等危险键)。',\n '如需传递复杂结构,请先在展示边界转换为字符串。',\n ])\n }\n\n if (LANGUAGE_FROZEN_KEYS.has(k)) {\n invalidToken('languageFrozen', { field: `options.${k}`, key: k }, [\n '不要在 token options 中传入 lng/lngs 等语言冻结字段。',\n '语言由外部 i18n 实例决定;token 只表达“要翻译什么”。',\n ])\n }\n\n if (!isJsonPrimitive(v)) {\n invalidToken('optionValueInvalid', { field: `options.${k}`, actual: typeof v }, [\n 'options value 只允许 JsonPrimitive(null/boolean/number/string)。',\n '不要传入对象/数组/函数;需要时请在展示边界先格式化成字符串。',\n ])\n }\n\n if (typeof v === 'number' && !Number.isFinite(v)) {\n invalidToken('numberNotJsonSafe', { field: `options.${k}`, value: String(v) }, [\n '不要在 token options 中传入 NaN/Infinity。',\n '请先把该数值转换为可 JSON 化的 number 或 string。',\n ])\n }\n\n if (typeof v === 'string' && v.length > TOKEN_BUDGET.optionValueStringMaxLen) {\n invalidToken(\n 'optionValueTooLong',\n {\n field: `options.${k}`,\n maxLen: TOKEN_BUDGET.optionValueStringMaxLen,\n actualLen: v.length,\n },\n [\n `缩短字符串值长度(建议 ≤ ${TOKEN_BUDGET.optionValueStringMaxLen})。`,\n '把长文本移到 defaultValue 或直接在展示边界生成最终字符串。',\n ],\n )\n }\n }\n\n entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\n const out: Record<string, JsonPrimitive> = {}\n for (const [k, v] of entries) {\n out[k] = v\n }\n return out\n}\n\nexport const token = (key: string, options?: I18nTokenOptionsInput): I18nMessageToken => {\n if (key.length > TOKEN_BUDGET.keyMaxLen) {\n invalidToken('keyTooLong', { field: 'key', maxLen: TOKEN_BUDGET.keyMaxLen, actualLen: key.length }, [\n `缩短 key(建议 ≤ ${TOKEN_BUDGET.keyMaxLen})。`,\n '如果 key 过长,建议改为“稳定 key + 变量 options/defaultValue”。',\n ])\n }\n\n const canon = canonicalizeTokenOptions(options)\n return canon\n ? {\n _tag: 'i18n',\n key,\n options: canon,\n }\n : {\n _tag: 'i18n',\n key,\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/Token.ts","../src/internal/token/token.ts"],"sourcesContent":["export type {\n I18nMessageToken,\n I18nTokenParams,\n I18nTokenParamsInput,\n InvalidI18nMessageTokenReason,\n JsonPrimitive,\n} from './internal/token/token.js'\nexport { InvalidI18nMessageTokenError, token } from './internal/token/token.js'\n","export type JsonPrimitive = null | boolean | number | string\n\nexport type I18nTokenParams = Readonly<Record<string, JsonPrimitive>>\nexport type I18nTokenParamsInput = Readonly<Record<string, JsonPrimitive | undefined>>\n\nexport type I18nMessageToken = {\n readonly _tag: 'i18n'\n readonly key: string\n readonly params?: I18nTokenParams\n}\n\nexport type InvalidI18nMessageTokenReason =\n | 'keyTooLong'\n | 'tooManyParams'\n | 'paramKeyInvalid'\n | 'paramValueInvalid'\n | 'paramValueTooLong'\n | 'numberNotJsonSafe'\n | 'languageFrozen'\n | 'renderFallbackReserved'\n\nexport class InvalidI18nMessageTokenError extends Error {\n readonly name = 'InvalidI18nMessageTokenError'\n\n constructor(\n readonly reason: InvalidI18nMessageTokenReason,\n readonly details: unknown,\n readonly fix: ReadonlyArray<string>,\n ) {\n super(`[InvalidI18nMessageTokenError] reason=${reason}`)\n }\n}\n\nconst TOKEN_BUDGET = {\n keyMaxLen: 96,\n paramKeyMaxCount: 8,\n paramValueStringMaxLen: 96,\n} as const\n\nconst LANGUAGE_FROZEN_KEYS = new Set(['lng', 'lngs'])\nconst DANGEROUS_PARAM_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\nconst LEGACY_RENDER_FALLBACK_KEY = `default${'Value'}`\nconst RESERVED_RENDER_FALLBACK_KEYS = new Set([LEGACY_RENDER_FALLBACK_KEY])\n\nconst isJsonPrimitive = (value: unknown): value is JsonPrimitive =>\n value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string'\n\nconst invalidToken = (reason: InvalidI18nMessageTokenReason, details: unknown, fix: ReadonlyArray<string>): never => {\n throw new InvalidI18nMessageTokenError(reason, details, fix)\n}\n\nexport const canonicalizeTokenParams = (params: I18nTokenParamsInput | undefined): I18nTokenParams | undefined => {\n if (!params) return undefined\n if (typeof params !== 'object' || params === null || Array.isArray(params)) {\n invalidToken('paramValueInvalid', { field: 'params', actual: typeof params }, [\n '请传入 plain object 作为 params(Record<string, JsonPrimitive>)。',\n '不要传入数组/函数/类实例等不可序列化值。',\n ])\n }\n\n const entries = Object.entries(params).filter((p): p is [string, JsonPrimitive] => p[1] !== undefined)\n\n if (entries.length === 0) return undefined\n\n if (entries.length > TOKEN_BUDGET.paramKeyMaxCount) {\n invalidToken(\n 'tooManyParams',\n {\n field: 'params',\n max: TOKEN_BUDGET.paramKeyMaxCount,\n actual: entries.length,\n },\n [\n `减少 params 键数量(建议 ≤ ${TOKEN_BUDGET.paramKeyMaxCount})。`,\n '避免把大对象或展示兜底文案塞进 semantic token。',\n ],\n )\n }\n\n for (const [k, v] of entries) {\n if (RESERVED_RENDER_FALLBACK_KEYS.has(k)) {\n invalidToken('renderFallbackReserved', { field: `params.${k}`, key: k }, [\n '不要把旧的展示兜底字段放进 semantic token params。',\n '请把兜底文案改放到 render/renderReady 的 hints.fallback。',\n ])\n }\n\n if (DANGEROUS_PARAM_KEYS.has(k) || k.length === 0) {\n invalidToken('paramKeyInvalid', { field: `params.${k}`, key: k }, [\n '请使用普通字段名作为 params key,避免 __proto__/constructor/prototype 等危险键。',\n '如需传递复杂结构,请先在展示边界转换为字符串。',\n ])\n }\n\n if (LANGUAGE_FROZEN_KEYS.has(k)) {\n invalidToken('languageFrozen', { field: `params.${k}`, key: k }, [\n '不要在 token params 中传入 lng/lngs 等语言冻结字段。',\n '语言由外部 i18n 实例决定;token 只表达“要翻译什么”。',\n ])\n }\n\n if (!isJsonPrimitive(v)) {\n invalidToken('paramValueInvalid', { field: `params.${k}`, actual: typeof v }, [\n 'params value 只允许 JsonPrimitive(null/boolean/number/string)。',\n '不要传入对象/数组/函数;需要时请在展示边界先格式化成字符串。',\n ])\n }\n\n if (typeof v === 'number' && !Number.isFinite(v)) {\n invalidToken('numberNotJsonSafe', { field: `params.${k}`, value: String(v) }, [\n '不要在 token params 中传入 NaN/Infinity。',\n '请先把该数值转换为可 JSON 化的 number 或 string。',\n ])\n }\n\n if (typeof v === 'string' && v.length > TOKEN_BUDGET.paramValueStringMaxLen) {\n invalidToken(\n 'paramValueTooLong',\n {\n field: `params.${k}`,\n maxLen: TOKEN_BUDGET.paramValueStringMaxLen,\n actualLen: v.length,\n },\n [\n `缩短字符串值长度(建议 ≤ ${TOKEN_BUDGET.paramValueStringMaxLen})。`,\n '把长文本留在展示边界,不要放进 semantic token。',\n ],\n )\n }\n }\n\n entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n\n const out: Record<string, JsonPrimitive> = {}\n for (const [k, v] of entries) {\n out[k] = v\n }\n return out\n}\n\nexport const token = (key: string, params?: I18nTokenParamsInput): I18nMessageToken => {\n if (key.length > TOKEN_BUDGET.keyMaxLen) {\n invalidToken('keyTooLong', { field: 'key', maxLen: TOKEN_BUDGET.keyMaxLen, actualLen: key.length }, [\n `缩短 key(建议 ≤ ${TOKEN_BUDGET.keyMaxLen})。`,\n '如果 key 过长,建议改为“稳定 key + 少量 semantic params”。',\n ])\n }\n\n const canon = canonicalizeTokenParams(params)\n return canon\n ? {\n _tag: 'i18n',\n key,\n params: canon,\n }\n : {\n _tag: 'i18n',\n key,\n }\n}\n"],"mappings":"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","names":[]}
package/dist/Token.d.cts CHANGED
@@ -1,12 +1,12 @@
1
1
  type JsonPrimitive = null | boolean | number | string;
2
- type I18nTokenOptions = Readonly<Record<string, JsonPrimitive>>;
3
- type I18nTokenOptionsInput = Readonly<Record<string, JsonPrimitive | undefined>>;
2
+ type I18nTokenParams = Readonly<Record<string, JsonPrimitive>>;
3
+ type I18nTokenParamsInput = Readonly<Record<string, JsonPrimitive | undefined>>;
4
4
  type I18nMessageToken = {
5
5
  readonly _tag: 'i18n';
6
6
  readonly key: string;
7
- readonly options?: I18nTokenOptions;
7
+ readonly params?: I18nTokenParams;
8
8
  };
9
- type InvalidI18nMessageTokenReason = 'keyTooLong' | 'tooManyOptions' | 'optionKeyInvalid' | 'optionValueInvalid' | 'optionValueTooLong' | 'numberNotJsonSafe' | 'languageFrozen';
9
+ type InvalidI18nMessageTokenReason = 'keyTooLong' | 'tooManyParams' | 'paramKeyInvalid' | 'paramValueInvalid' | 'paramValueTooLong' | 'numberNotJsonSafe' | 'languageFrozen' | 'renderFallbackReserved';
10
10
  declare class InvalidI18nMessageTokenError extends Error {
11
11
  readonly reason: InvalidI18nMessageTokenReason;
12
12
  readonly details: unknown;
@@ -14,7 +14,6 @@ declare class InvalidI18nMessageTokenError extends Error {
14
14
  readonly name = "InvalidI18nMessageTokenError";
15
15
  constructor(reason: InvalidI18nMessageTokenReason, details: unknown, fix: ReadonlyArray<string>);
16
16
  }
17
- declare const canonicalizeTokenOptions: (options: I18nTokenOptionsInput | undefined) => I18nTokenOptions | undefined;
18
- declare const token: (key: string, options?: I18nTokenOptionsInput) => I18nMessageToken;
17
+ declare const token: (key: string, params?: I18nTokenParamsInput) => I18nMessageToken;
19
18
 
20
- export { type I18nMessageToken, type I18nTokenOptions, type I18nTokenOptionsInput, InvalidI18nMessageTokenError, type InvalidI18nMessageTokenReason, type JsonPrimitive, canonicalizeTokenOptions, token };
19
+ export { type I18nMessageToken, type I18nTokenParams, type I18nTokenParamsInput, InvalidI18nMessageTokenError, type InvalidI18nMessageTokenReason, type JsonPrimitive, token };
package/dist/Token.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  type JsonPrimitive = null | boolean | number | string;
2
- type I18nTokenOptions = Readonly<Record<string, JsonPrimitive>>;
3
- type I18nTokenOptionsInput = Readonly<Record<string, JsonPrimitive | undefined>>;
2
+ type I18nTokenParams = Readonly<Record<string, JsonPrimitive>>;
3
+ type I18nTokenParamsInput = Readonly<Record<string, JsonPrimitive | undefined>>;
4
4
  type I18nMessageToken = {
5
5
  readonly _tag: 'i18n';
6
6
  readonly key: string;
7
- readonly options?: I18nTokenOptions;
7
+ readonly params?: I18nTokenParams;
8
8
  };
9
- type InvalidI18nMessageTokenReason = 'keyTooLong' | 'tooManyOptions' | 'optionKeyInvalid' | 'optionValueInvalid' | 'optionValueTooLong' | 'numberNotJsonSafe' | 'languageFrozen';
9
+ type InvalidI18nMessageTokenReason = 'keyTooLong' | 'tooManyParams' | 'paramKeyInvalid' | 'paramValueInvalid' | 'paramValueTooLong' | 'numberNotJsonSafe' | 'languageFrozen' | 'renderFallbackReserved';
10
10
  declare class InvalidI18nMessageTokenError extends Error {
11
11
  readonly reason: InvalidI18nMessageTokenReason;
12
12
  readonly details: unknown;
@@ -14,7 +14,6 @@ declare class InvalidI18nMessageTokenError extends Error {
14
14
  readonly name = "InvalidI18nMessageTokenError";
15
15
  constructor(reason: InvalidI18nMessageTokenReason, details: unknown, fix: ReadonlyArray<string>);
16
16
  }
17
- declare const canonicalizeTokenOptions: (options: I18nTokenOptionsInput | undefined) => I18nTokenOptions | undefined;
18
- declare const token: (key: string, options?: I18nTokenOptionsInput) => I18nMessageToken;
17
+ declare const token: (key: string, params?: I18nTokenParamsInput) => I18nMessageToken;
19
18
 
20
- export { type I18nMessageToken, type I18nTokenOptions, type I18nTokenOptionsInput, InvalidI18nMessageTokenError, type InvalidI18nMessageTokenReason, type JsonPrimitive, canonicalizeTokenOptions, token };
19
+ export { type I18nMessageToken, type I18nTokenParams, type I18nTokenParamsInput, InvalidI18nMessageTokenError, type InvalidI18nMessageTokenReason, type JsonPrimitive, token };
package/dist/Token.js CHANGED
@@ -1,12 +1,9 @@
1
- import "./chunk-LW6LHDDL.js";
2
1
  import {
3
2
  InvalidI18nMessageTokenError,
4
- canonicalizeTokenOptions,
5
3
  token
6
- } from "./chunk-NWWL4MNH.js";
4
+ } from "./chunk-LGNB43KG.js";
7
5
  export {
8
6
  InvalidI18nMessageTokenError,
9
- canonicalizeTokenOptions,
10
7
  token
11
8
  };
12
9
  //# sourceMappingURL=Token.js.map