@shuo-li/i18n 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,20 +39,22 @@ interface I18nInitOptions {
39
39
  sse?: SSEConfig;
40
40
  };
41
41
  }
42
+ interface I18nValue {
43
+ termCode?: string;
44
+ langValue?: string;
45
+ }
42
46
  interface PullLangBlock {
43
47
  langCode: string;
44
48
  modules: Array<{
45
49
  moduleCode: string;
46
50
  version: number;
47
- /** transform 函数返回的最终资源对象,直接写入 DB 和 i18next */
48
- resources: Record<string, unknown>;
51
+ i18nValues: I18nValue[];
49
52
  }>;
50
53
  }
51
54
  interface SSEMessage {
52
55
  type: string;
53
56
  langCode: string;
54
57
  moduleCode: string;
55
- /** SSE 消息携带的数据版本号,与本地 version 对比决定是否拉取 */
56
58
  version: number;
57
59
  storeName: string;
58
60
  }
@@ -71,7 +73,6 @@ interface ResourceRecord {
71
73
  key: string;
72
74
  moduleCode: string;
73
75
  langCode: string;
74
- /** 当前已存储数据的版本号(对应旧的 toSynTime) */
75
76
  version: number;
76
77
  resources: Record<string, unknown>;
77
78
  }
@@ -81,6 +82,8 @@ interface WorkerTask {
81
82
  moduleCode?: string;
82
83
  /** 全量拉取时为 0;SSE 增量拉取时为本地已有版本号 */
83
84
  version?: number;
85
+ /** 唯一标识,用于主线程回传 parsedData 时关联任务(Web 模式下必须) */
86
+ taskId?: number;
84
87
  trigger?: 'init-full' | 'sse';
85
88
  /** 经 buildParams 映射后的实际请求参数;未提供时 Worker 使用内部字段名 */
86
89
  params?: Record<string, unknown>;
@@ -90,6 +93,14 @@ interface WorkerRawDataMessage {
90
93
  task: WorkerTask;
91
94
  raw: unknown;
92
95
  }
96
+ /** Worker 写入一个模块后通知主线程(Web 模式,Worker 写 IDB) */
97
+ interface WorkerModuleLoadedMessage {
98
+ type: 'moduleLoaded';
99
+ storeName: string;
100
+ moduleCode: string;
101
+ langCode: string;
102
+ version: number;
103
+ }
93
104
  interface WorkerModuleErrorMessage {
94
105
  type: 'moduleError';
95
106
  storeName: string;
@@ -103,7 +114,7 @@ interface WorkerErrorMessage {
103
114
  type: 'error';
104
115
  message?: string;
105
116
  }
106
- type WorkerOutMessage = WorkerRawDataMessage | WorkerModuleErrorMessage | WorkerDoneMessage | WorkerErrorMessage;
117
+ type WorkerOutMessage = WorkerRawDataMessage | WorkerModuleLoadedMessage | WorkerModuleErrorMessage | WorkerDoneMessage | WorkerErrorMessage;
107
118
  interface WorkerLike {
108
119
  postMessage(data: unknown): void;
109
120
  onMessage(handler: (data: WorkerOutMessage) => void): void;
@@ -119,4 +130,4 @@ declare function getAllRecordsByModule(moduleCode: string): Promise<ResourceReco
119
130
  declare const I18N_RESOURCES_UPDATED_EVENT = "golucky:i18n-resources-updated";
120
131
  declare function emitI18nResourcesUpdated(): void;
121
132
 
122
- export { I18N_RESOURCES_UPDATED_EVENT as I, type PullLangBlock as P, type ResourceRecord as R, type SSEMessage as S, type WorkerLike as W, getResource as a, type I18nInitOptions as b, closeSSE as c, type StandardPullParams as d, ensureModules as e, type StoreConfig as f, getAllRecordsByModule as g, emitI18nResourcesUpdated as h, initI18n as i };
133
+ export { I18N_RESOURCES_UPDATED_EVENT as I, type PullLangBlock as P, type ResourceRecord as R, type SSEMessage as S, type WorkerLike as W, getResource as a, type I18nInitOptions as b, closeSSE as c, type StandardPullParams as d, ensureModules as e, type StoreConfig as f, getAllRecordsByModule as g, emitI18nResourcesUpdated as h, initI18n as i, type I18nValue as j };
@@ -39,20 +39,22 @@ interface I18nInitOptions {
39
39
  sse?: SSEConfig;
40
40
  };
41
41
  }
42
+ interface I18nValue {
43
+ termCode?: string;
44
+ langValue?: string;
45
+ }
42
46
  interface PullLangBlock {
43
47
  langCode: string;
44
48
  modules: Array<{
45
49
  moduleCode: string;
46
50
  version: number;
47
- /** transform 函数返回的最终资源对象,直接写入 DB 和 i18next */
48
- resources: Record<string, unknown>;
51
+ i18nValues: I18nValue[];
49
52
  }>;
50
53
  }
51
54
  interface SSEMessage {
52
55
  type: string;
53
56
  langCode: string;
54
57
  moduleCode: string;
55
- /** SSE 消息携带的数据版本号,与本地 version 对比决定是否拉取 */
56
58
  version: number;
57
59
  storeName: string;
58
60
  }
@@ -71,7 +73,6 @@ interface ResourceRecord {
71
73
  key: string;
72
74
  moduleCode: string;
73
75
  langCode: string;
74
- /** 当前已存储数据的版本号(对应旧的 toSynTime) */
75
76
  version: number;
76
77
  resources: Record<string, unknown>;
77
78
  }
@@ -81,6 +82,8 @@ interface WorkerTask {
81
82
  moduleCode?: string;
82
83
  /** 全量拉取时为 0;SSE 增量拉取时为本地已有版本号 */
83
84
  version?: number;
85
+ /** 唯一标识,用于主线程回传 parsedData 时关联任务(Web 模式下必须) */
86
+ taskId?: number;
84
87
  trigger?: 'init-full' | 'sse';
85
88
  /** 经 buildParams 映射后的实际请求参数;未提供时 Worker 使用内部字段名 */
86
89
  params?: Record<string, unknown>;
@@ -90,6 +93,14 @@ interface WorkerRawDataMessage {
90
93
  task: WorkerTask;
91
94
  raw: unknown;
92
95
  }
96
+ /** Worker 写入一个模块后通知主线程(Web 模式,Worker 写 IDB) */
97
+ interface WorkerModuleLoadedMessage {
98
+ type: 'moduleLoaded';
99
+ storeName: string;
100
+ moduleCode: string;
101
+ langCode: string;
102
+ version: number;
103
+ }
93
104
  interface WorkerModuleErrorMessage {
94
105
  type: 'moduleError';
95
106
  storeName: string;
@@ -103,7 +114,7 @@ interface WorkerErrorMessage {
103
114
  type: 'error';
104
115
  message?: string;
105
116
  }
106
- type WorkerOutMessage = WorkerRawDataMessage | WorkerModuleErrorMessage | WorkerDoneMessage | WorkerErrorMessage;
117
+ type WorkerOutMessage = WorkerRawDataMessage | WorkerModuleLoadedMessage | WorkerModuleErrorMessage | WorkerDoneMessage | WorkerErrorMessage;
107
118
  interface WorkerLike {
108
119
  postMessage(data: unknown): void;
109
120
  onMessage(handler: (data: WorkerOutMessage) => void): void;
@@ -119,4 +130,4 @@ declare function getAllRecordsByModule(moduleCode: string): Promise<ResourceReco
119
130
  declare const I18N_RESOURCES_UPDATED_EVENT = "golucky:i18n-resources-updated";
120
131
  declare function emitI18nResourcesUpdated(): void;
121
132
 
122
- export { I18N_RESOURCES_UPDATED_EVENT as I, type PullLangBlock as P, type ResourceRecord as R, type SSEMessage as S, type WorkerLike as W, getResource as a, type I18nInitOptions as b, closeSSE as c, type StandardPullParams as d, ensureModules as e, type StoreConfig as f, getAllRecordsByModule as g, emitI18nResourcesUpdated as h, initI18n as i };
133
+ export { I18N_RESOURCES_UPDATED_EVENT as I, type PullLangBlock as P, type ResourceRecord as R, type SSEMessage as S, type WorkerLike as W, getResource as a, type I18nInitOptions as b, closeSSE as c, type StandardPullParams as d, ensureModules as e, type StoreConfig as f, getAllRecordsByModule as g, emitI18nResourcesUpdated as h, initI18n as i, type I18nValue as j };
@@ -1,3 +1,12 @@
1
+ import {
2
+ buildKey,
3
+ deepMerge,
4
+ flatToNested,
5
+ parseI18nValues,
6
+ toDefaultParams,
7
+ toQueryString
8
+ } from "./chunk-NEXKR7GY.js";
9
+
1
10
  // src/core/cacheEvents.ts
2
11
  var I18N_RESOURCES_UPDATED_EVENT = "golucky:i18n-resources-updated";
3
12
  function emitI18nResourcesUpdated() {
@@ -5,52 +14,6 @@ function emitI18nResourcesUpdated() {
5
14
  window.dispatchEvent(new CustomEvent(I18N_RESOURCES_UPDATED_EVENT));
6
15
  }
7
16
 
8
- // src/core/utils.ts
9
- function buildKey(moduleCode, langCode, store) {
10
- return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
11
- }
12
- function flatToNested(flat) {
13
- const result = {};
14
- for (const [key, value] of Object.entries(flat)) {
15
- const parts = key.split(".");
16
- let cur = result;
17
- for (let i = 0; i < parts.length - 1; i++) {
18
- const part = parts[i];
19
- if (typeof cur[part] !== "object" || cur[part] === null) {
20
- cur[part] = {};
21
- }
22
- cur = cur[part];
23
- }
24
- cur[parts[parts.length - 1]] = value;
25
- }
26
- return result;
27
- }
28
- function deepMerge(target, source) {
29
- const result = { ...target };
30
- for (const [key, sv] of Object.entries(source)) {
31
- const tv = result[key];
32
- if (sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv)) {
33
- result[key] = deepMerge(tv, sv);
34
- } else {
35
- result[key] = sv;
36
- }
37
- }
38
- return result;
39
- }
40
- function toDefaultParams(p) {
41
- const result = {};
42
- if (p?.storeName) result["storeName"] = p.storeName;
43
- if (p?.langCode) result["langCode"] = p.langCode;
44
- if (p?.moduleCode) result["moduleCode"] = p.moduleCode;
45
- if (p?.version != null) result["version"] = String(p.version);
46
- return result;
47
- }
48
- function toQueryString(params) {
49
- return new URLSearchParams(
50
- Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
51
- ).toString();
52
- }
53
-
54
17
  // src/core/manager.ts
55
18
  import i18n from "i18next";
56
19
 
@@ -182,12 +145,31 @@ var WorkerManager = class {
182
145
  }
183
146
  const worker = createWorker();
184
147
  this.worker = worker;
148
+ const workerWritesDB = typeof callbacks.transformRaw === "function";
185
149
  worker.onMessage((msg) => {
186
150
  if (msg.type === "rawData") {
187
- const p = callbacks.onRawData(msg.task, msg.raw).catch(() => {
188
- });
189
- this.pendingCallbacks.add(p);
190
- p.finally(() => this.pendingCallbacks.delete(p));
151
+ if (workerWritesDB && callbacks.transformRaw) {
152
+ let blocks;
153
+ try {
154
+ blocks = callbacks.transformRaw(msg.task, msg.raw);
155
+ } catch (err) {
156
+ console.error("[i18n] transform failed", msg.task, err);
157
+ blocks = [];
158
+ }
159
+ worker.postMessage({ type: "parsedData", taskId: msg.task.taskId, blocks });
160
+ } else if (callbacks.onRawData) {
161
+ const p = callbacks.onRawData(msg.task, msg.raw).catch(() => {
162
+ });
163
+ this.pendingCallbacks.add(p);
164
+ p.finally(() => this.pendingCallbacks.delete(p));
165
+ }
166
+ } else if (msg.type === "moduleLoaded") {
167
+ if (callbacks.onModuleLoaded) {
168
+ const p = callbacks.onModuleLoaded(msg).catch(() => {
169
+ });
170
+ this.pendingCallbacks.add(p);
171
+ p.finally(() => this.pendingCallbacks.delete(p));
172
+ }
191
173
  } else if (msg.type === "moduleError") {
192
174
  callbacks.onModuleError?.(msg);
193
175
  } else if (msg.type === "done" || msg.type === "error") {
@@ -227,6 +209,7 @@ var resolvedLanguages = [];
227
209
  var unloginPullStarted = false;
228
210
  var managerOptions = null;
229
211
  var initOptions = null;
212
+ var taskIdCounter = 0;
230
213
  var loadedModules = /* @__PURE__ */ new Set();
231
214
  var loadingModules = /* @__PURE__ */ new Set();
232
215
  var sseClient = new SSEClient();
@@ -363,24 +346,15 @@ async function _initI18n(options) {
363
346
  startSSE(options);
364
347
  }
365
348
  }
366
- var CACHE_VERSION_KEY = "__meta__:cache_version";
367
349
  async function checkCacheVersion(storage, options) {
368
350
  if (options.version == null) return;
369
- const baseStoreName = resolvedStores[0].name;
370
351
  const current = String(options.version);
371
- const record = await storage.getRecord(baseStoreName, CACHE_VERSION_KEY).catch(() => void 0);
372
- const stored = record?.resources?.["value"];
352
+ const stored = await storage.getMeta("cache_version").catch(() => null);
373
353
  if (stored === current) return;
374
354
  for (const store of options.stores) {
375
355
  await storage.clearStore(store.name);
376
356
  }
377
- await storage.putRecord(baseStoreName, {
378
- key: CACHE_VERSION_KEY,
379
- moduleCode: "__meta__",
380
- langCode: "__meta__",
381
- version: 0,
382
- resources: { value: current }
383
- });
357
+ await storage.setMeta("cache_version", current);
384
358
  }
385
359
  async function doUnloginPull(storage, options) {
386
360
  const { apiContext } = options;
@@ -398,9 +372,10 @@ async function doUnloginPull(storage, options) {
398
372
  for (const mod of block.modules ?? []) {
399
373
  const key = buildKey(mod.moduleCode, block.langCode, baseStore);
400
374
  const existing = await storage.getRecord(baseStore.name, key);
375
+ const flat = parseI18nValues(mod.i18nValues);
401
376
  const resources = deepMerge(
402
377
  existing?.resources ?? {},
403
- mod.resources
378
+ flatToNested(flat)
404
379
  );
405
380
  await storage.putRecord(baseStore.name, {
406
381
  key,
@@ -420,16 +395,13 @@ async function startWorkerFull(options) {
420
395
  const pull = apiContext.pull;
421
396
  const tasks = [];
422
397
  for (const store of resolvedStores) {
423
- for (const lang of resolvedLanguages) {
424
- for (const module of sortedModules()) {
425
- const internal = { storeName: store.name, langCode: lang, moduleCode: module, version: 0 };
426
- tasks.push({
427
- ...internal,
428
- trigger: "init-full",
429
- params: pull.buildParams ? pull.buildParams(internal) : void 0
430
- });
431
- }
432
- }
398
+ const internal = { storeName: store.name };
399
+ tasks.push({
400
+ storeName: store.name,
401
+ taskId: ++taskIdCounter,
402
+ trigger: "init-full",
403
+ params: pull.buildParams ? pull.buildParams(internal) : void 0
404
+ });
433
405
  }
434
406
  await runWorker(options, tasks, pull);
435
407
  }
@@ -437,16 +409,35 @@ function runWorker(options, tasks, pull) {
437
409
  return new Promise((resolve, reject) => {
438
410
  const { apiContext } = options;
439
411
  const storage = managerOptions.storage;
440
- workerManager.start(
441
- managerOptions.createWorker,
442
- {
443
- baseURL: apiContext.baseURL,
444
- headers: apiContext.getHeaders(),
445
- pullPath: pull.path,
446
- pullMethod: pull.method ?? "GET",
447
- tasks
448
- },
449
- {
412
+ const workerWritesDB = managerOptions.workerWritesDB ?? false;
413
+ const payload = {
414
+ baseURL: apiContext.baseURL,
415
+ headers: apiContext.getHeaders(),
416
+ pullPath: pull.path,
417
+ pullMethod: pull.method ?? "GET",
418
+ tasks,
419
+ ...workerWritesDB ? { storeConfigs: resolvedStores } : {}
420
+ };
421
+ if (workerWritesDB) {
422
+ workerManager.start(managerOptions.createWorker, payload, {
423
+ transformRaw: (task, raw) => {
424
+ try {
425
+ return pull.transform ? pull.transform(raw) : raw;
426
+ } catch (err) {
427
+ console.error("[i18n] pull transform \u5931\u8D25", task, err);
428
+ return [];
429
+ }
430
+ },
431
+ onModuleLoaded: async (info) => {
432
+ await onModuleLoaded(info);
433
+ },
434
+ onModuleError: () => {
435
+ },
436
+ onDone: resolve,
437
+ onError: () => reject(new Error("[i18n] Worker fatal error"))
438
+ });
439
+ } else {
440
+ workerManager.start(managerOptions.createWorker, payload, {
450
441
  onRawData: async (task, raw) => {
451
442
  let blocks;
452
443
  try {
@@ -463,7 +454,8 @@ function runWorker(options, tasks, pull) {
463
454
  const key = buildKey(mod.moduleCode, block.langCode, store);
464
455
  const existing = await storage.getRecord(store.name, key);
465
456
  if (existing && mod.version <= existing.version) continue;
466
- const resources = storeIndex === 0 ? deepMerge(existing?.resources ?? {}, mod.resources) : { ...existing?.resources ?? {}, ...mod.resources };
457
+ const flat = parseI18nValues(mod.i18nValues);
458
+ const resources = storeIndex === 0 ? deepMerge(existing?.resources ?? {}, flatToNested(flat)) : { ...existing?.resources ?? {}, ...flat };
467
459
  await storage.putRecord(store.name, {
468
460
  key,
469
461
  moduleCode: mod.moduleCode,
@@ -473,19 +465,19 @@ function runWorker(options, tasks, pull) {
473
465
  });
474
466
  await onModuleLoaded({
475
467
  storeName: task.storeName,
476
- langCode: block.langCode,
477
468
  moduleCode: mod.moduleCode,
469
+ langCode: block.langCode,
478
470
  version: mod.version
479
471
  });
480
472
  }
481
473
  }
482
474
  },
483
- onModuleError: (_data) => {
475
+ onModuleError: () => {
484
476
  },
485
477
  onDone: resolve,
486
478
  onError: () => reject(new Error("[i18n] Worker fatal error"))
487
- }
488
- );
479
+ });
480
+ }
489
481
  });
490
482
  }
491
483
  async function onModuleLoaded(data) {
@@ -609,7 +601,8 @@ async function pullAndStore(task, fromVersion, options) {
609
601
  const key = buildKey(mod.moduleCode, block.langCode, store);
610
602
  const existing = await storage.getRecord(store.name, key);
611
603
  if (mod.version <= (existing?.version ?? 0)) continue;
612
- const resources = storeIndex === 0 ? deepMerge(existing?.resources ?? {}, mod.resources) : { ...existing?.resources ?? {}, ...mod.resources };
604
+ const flat = parseI18nValues(mod.i18nValues);
605
+ const resources = storeIndex === 0 ? deepMerge(existing?.resources ?? {}, flatToNested(flat)) : { ...existing?.resources ?? {}, ...flat };
613
606
  const version = Math.max(mod.version, task.targetVersion);
614
607
  await storage.putRecord(store.name, {
615
608
  key,
@@ -665,7 +658,5 @@ async function getStoredVersion(store, moduleCode, langCode) {
665
658
  export {
666
659
  I18N_RESOURCES_UPDATED_EVENT,
667
660
  emitI18nResourcesUpdated,
668
- flatToNested,
669
- deepMerge,
670
661
  createI18nManager
671
662
  };
@@ -0,0 +1,61 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/utils.ts
2
+ function buildKey(moduleCode, langCode, store) {
3
+ return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
4
+ }
5
+ function parseI18nValues(values) {
6
+ const result = {};
7
+ for (const v of values) {
8
+ if (v.termCode) result[v.termCode] = _nullishCoalesce(v.langValue, () => ( ""));
9
+ }
10
+ return result;
11
+ }
12
+ function flatToNested(flat) {
13
+ const result = {};
14
+ for (const [key, value] of Object.entries(flat)) {
15
+ const parts = key.split(".");
16
+ let cur = result;
17
+ for (let i = 0; i < parts.length - 1; i++) {
18
+ const part = parts[i];
19
+ if (typeof cur[part] !== "object" || cur[part] === null) {
20
+ cur[part] = {};
21
+ }
22
+ cur = cur[part];
23
+ }
24
+ cur[parts[parts.length - 1]] = value;
25
+ }
26
+ return result;
27
+ }
28
+ function deepMerge(target, source) {
29
+ const result = { ...target };
30
+ for (const [key, sv] of Object.entries(source)) {
31
+ const tv = result[key];
32
+ if (sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv)) {
33
+ result[key] = deepMerge(tv, sv);
34
+ } else {
35
+ result[key] = sv;
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ function toDefaultParams(p) {
41
+ const result = {};
42
+ if (_optionalChain([p, 'optionalAccess', _ => _.storeName])) result["storeName"] = p.storeName;
43
+ if (_optionalChain([p, 'optionalAccess', _2 => _2.langCode])) result["langCode"] = p.langCode;
44
+ if (_optionalChain([p, 'optionalAccess', _3 => _3.moduleCode])) result["moduleCode"] = p.moduleCode;
45
+ if (_optionalChain([p, 'optionalAccess', _4 => _4.version]) != null) result["version"] = String(p.version);
46
+ return result;
47
+ }
48
+ function toQueryString(params) {
49
+ return new URLSearchParams(
50
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
51
+ ).toString();
52
+ }
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+ exports.buildKey = buildKey; exports.parseI18nValues = parseI18nValues; exports.flatToNested = flatToNested; exports.deepMerge = deepMerge; exports.toDefaultParams = toDefaultParams; exports.toQueryString = toQueryString;
@@ -0,0 +1,61 @@
1
+ // src/core/utils.ts
2
+ function buildKey(moduleCode, langCode, store) {
3
+ return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
4
+ }
5
+ function parseI18nValues(values) {
6
+ const result = {};
7
+ for (const v of values) {
8
+ if (v.termCode) result[v.termCode] = v.langValue ?? "";
9
+ }
10
+ return result;
11
+ }
12
+ function flatToNested(flat) {
13
+ const result = {};
14
+ for (const [key, value] of Object.entries(flat)) {
15
+ const parts = key.split(".");
16
+ let cur = result;
17
+ for (let i = 0; i < parts.length - 1; i++) {
18
+ const part = parts[i];
19
+ if (typeof cur[part] !== "object" || cur[part] === null) {
20
+ cur[part] = {};
21
+ }
22
+ cur = cur[part];
23
+ }
24
+ cur[parts[parts.length - 1]] = value;
25
+ }
26
+ return result;
27
+ }
28
+ function deepMerge(target, source) {
29
+ const result = { ...target };
30
+ for (const [key, sv] of Object.entries(source)) {
31
+ const tv = result[key];
32
+ if (sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv)) {
33
+ result[key] = deepMerge(tv, sv);
34
+ } else {
35
+ result[key] = sv;
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ function toDefaultParams(p) {
41
+ const result = {};
42
+ if (p?.storeName) result["storeName"] = p.storeName;
43
+ if (p?.langCode) result["langCode"] = p.langCode;
44
+ if (p?.moduleCode) result["moduleCode"] = p.moduleCode;
45
+ if (p?.version != null) result["version"] = String(p.version);
46
+ return result;
47
+ }
48
+ function toQueryString(params) {
49
+ return new URLSearchParams(
50
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
51
+ ).toString();
52
+ }
53
+
54
+ export {
55
+ buildKey,
56
+ parseI18nValues,
57
+ flatToNested,
58
+ deepMerge,
59
+ toDefaultParams,
60
+ toQueryString
61
+ };