@shuo-li/i18n 1.0.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.
@@ -0,0 +1,664 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await 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/cacheEvents.ts
2
+ var I18N_RESOURCES_UPDATED_EVENT = "golucky:i18n-resources-updated";
3
+ function emitI18nResourcesUpdated() {
4
+ if (typeof window === "undefined") return;
5
+ window.dispatchEvent(new CustomEvent(I18N_RESOURCES_UPDATED_EVENT));
6
+ }
7
+
8
+ // src/core/manager.ts
9
+ var _i18next = require('i18next'); var _i18next2 = _interopRequireDefault(_i18next);
10
+
11
+ // src/core/sse.ts
12
+ var RECONNECT_DELAYS = [1e3, 2e3, 5e3, 1e4, 3e4];
13
+ var SSEClient = class {
14
+ constructor() {
15
+ this.options = null;
16
+ this.onMessage = null;
17
+ this.abortController = null;
18
+ this.reconnectAttempt = 0;
19
+ this.shouldReconnect = true;
20
+ this.reconnectTimer = null;
21
+ this.reconnectDelayOverride = null;
22
+ }
23
+ start(onMessage, options) {
24
+ this.options = options;
25
+ this.onMessage = onMessage;
26
+ this.shouldReconnect = true;
27
+ this.reconnectAttempt = 0;
28
+ void this.connect();
29
+ }
30
+ close() {
31
+ this.shouldReconnect = false;
32
+ if (this.reconnectTimer !== null) {
33
+ clearTimeout(this.reconnectTimer);
34
+ this.reconnectTimer = null;
35
+ }
36
+ _optionalChain([this, 'access', _2 => _2.abortController, 'optionalAccess', _3 => _3.abort, 'call', _4 => _4()]);
37
+ this.abortController = null;
38
+ }
39
+ async connect() {
40
+ if (!this.options || !this.onMessage) return;
41
+ this.abortController = new AbortController();
42
+ await this.listen(this.abortController.signal);
43
+ if (this.shouldReconnect) {
44
+ this.scheduleReconnect();
45
+ }
46
+ }
47
+ async listen(signal) {
48
+ if (!this.options || !this.onMessage) return;
49
+ const { baseURL, eventsPath, getHeaders } = this.options;
50
+ const url = new URL(eventsPath, baseURL).toString();
51
+ const headers = { Accept: "text/event-stream", ...getHeaders() };
52
+ let res;
53
+ try {
54
+ res = await fetch(url, { headers, signal });
55
+ } catch (e) {
56
+ return;
57
+ }
58
+ if (res.status === 401 || res.status === 403) {
59
+ this.shouldReconnect = false;
60
+ return;
61
+ }
62
+ if (res.status === 429) {
63
+ const retryAfter = res.headers.get("Retry-After");
64
+ if (retryAfter) {
65
+ this.reconnectDelayOverride = Number(retryAfter) * 1e3;
66
+ }
67
+ return;
68
+ }
69
+ if (!res.ok || !res.body) return;
70
+ this.reconnectAttempt = 0;
71
+ this.reconnectDelayOverride = null;
72
+ const reader = res.body.getReader();
73
+ const decoder = new TextDecoder();
74
+ let buffer = "";
75
+ let heartbeatTimer = setTimeout(() => reader.cancel(), 3e4);
76
+ const resetHeartbeat = () => {
77
+ clearTimeout(heartbeatTimer);
78
+ heartbeatTimer = setTimeout(() => reader.cancel(), 3e4);
79
+ };
80
+ try {
81
+ while (true) {
82
+ const { done, value } = await reader.read();
83
+ if (done) break;
84
+ resetHeartbeat();
85
+ buffer += decoder.decode(value, { stream: true });
86
+ const events = buffer.split("\n\n");
87
+ buffer = _nullishCoalesce(events.pop(), () => ( ""));
88
+ for (const event of events) {
89
+ this.parseEvent(event);
90
+ }
91
+ }
92
+ } finally {
93
+ clearTimeout(heartbeatTimer);
94
+ }
95
+ }
96
+ parseEvent(raw) {
97
+ let data = "";
98
+ for (const line of raw.split("\n")) {
99
+ if (line.startsWith("data:")) {
100
+ data += line.slice(5).trim();
101
+ }
102
+ }
103
+ if (!data) return;
104
+ try {
105
+ const parsed = JSON.parse(data);
106
+ _optionalChain([this, 'access', _5 => _5.onMessage, 'optionalCall', _6 => _6(parsed)]);
107
+ } catch (e2) {
108
+ }
109
+ }
110
+ scheduleReconnect() {
111
+ if (!this.shouldReconnect) return;
112
+ const delay = _nullishCoalesce(this.reconnectDelayOverride, () => ( (_nullishCoalesce(RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)], () => ( 3e4)))));
113
+ this.reconnectAttempt++;
114
+ this.reconnectDelayOverride = null;
115
+ this.reconnectTimer = setTimeout(() => {
116
+ this.reconnectTimer = null;
117
+ void this.connect();
118
+ }, delay);
119
+ }
120
+ };
121
+
122
+ // src/core/utils.ts
123
+ function buildKey(moduleCode, langCode, store) {
124
+ return store.cacheGroupKey ? `${moduleCode}_${langCode}_${store.cacheGroupKey}` : `${moduleCode}_${langCode}`;
125
+ }
126
+ function buildUnloginKey(langCode) {
127
+ return `UNLOGIN_${langCode}`;
128
+ }
129
+ function flatToNested(flat) {
130
+ const result = {};
131
+ for (const [key, value] of Object.entries(flat)) {
132
+ const parts = key.split(".");
133
+ let cur = result;
134
+ for (let i = 0; i < parts.length - 1; i++) {
135
+ const part = parts[i];
136
+ if (typeof cur[part] !== "object" || cur[part] === null) {
137
+ cur[part] = {};
138
+ }
139
+ cur = cur[part];
140
+ }
141
+ cur[parts[parts.length - 1]] = value;
142
+ }
143
+ return result;
144
+ }
145
+ function deepMerge(target, source) {
146
+ const result = { ...target };
147
+ for (const [key, sv] of Object.entries(source)) {
148
+ const tv = result[key];
149
+ if (sv !== null && typeof sv === "object" && !Array.isArray(sv) && tv !== null && typeof tv === "object" && !Array.isArray(tv)) {
150
+ result[key] = deepMerge(tv, sv);
151
+ } else {
152
+ result[key] = sv;
153
+ }
154
+ }
155
+ return result;
156
+ }
157
+ function parseI18nValues(values) {
158
+ const result = {};
159
+ for (const { termCode, langValue } of values) {
160
+ if (termCode && langValue !== void 0) {
161
+ result[termCode] = _nullishCoalesce(langValue, () => ( ""));
162
+ }
163
+ }
164
+ return result;
165
+ }
166
+ function toDefaultParams(p) {
167
+ const result = {};
168
+ if (_optionalChain([p, 'optionalAccess', _7 => _7.storeName])) result["storeName"] = p.storeName;
169
+ if (_optionalChain([p, 'optionalAccess', _8 => _8.langCode])) result["langCode"] = p.langCode;
170
+ if (_optionalChain([p, 'optionalAccess', _9 => _9.moduleCode])) result["moduleCode"] = p.moduleCode;
171
+ if (_optionalChain([p, 'optionalAccess', _10 => _10.version]) != null) result["version"] = String(p.version);
172
+ return result;
173
+ }
174
+ function toQueryString(params) {
175
+ return new URLSearchParams(
176
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
177
+ ).toString();
178
+ }
179
+
180
+ // src/core/worker-manager.ts
181
+ var WorkerManager = class {
182
+ constructor() {
183
+ this.worker = null;
184
+ this.pendingCallbacks = /* @__PURE__ */ new Set();
185
+ this.finalized = false;
186
+ }
187
+ start(createWorker, payload, callbacks, storage) {
188
+ this.terminate();
189
+ this.finalized = false;
190
+ this.pendingCallbacks = /* @__PURE__ */ new Set();
191
+ if (payload.tasks.length === 0) {
192
+ callbacks.onDone();
193
+ return;
194
+ }
195
+ const worker = createWorker();
196
+ this.worker = worker;
197
+ worker.onMessage((msg) => {
198
+ if (msg.type === "moduleLoaded") {
199
+ const m = msg;
200
+ const p = callbacks.onModuleLoaded({
201
+ storeName: m.storeName,
202
+ langCode: m.langCode,
203
+ moduleCode: m.moduleCode,
204
+ version: m.version
205
+ });
206
+ this.pendingCallbacks.add(p);
207
+ p.finally(() => this.pendingCallbacks.delete(p));
208
+ } else if (msg.type === "dataReady") {
209
+ if (!storage) return;
210
+ const p = (async () => {
211
+ const existing = await storage.getRecord(msg.storeName, msg.record.key);
212
+ if (existing && msg.record.version <= existing.version) return;
213
+ let resources;
214
+ if (msg.storeIndex === 0) {
215
+ resources = deepMerge(
216
+ _nullishCoalesce(_optionalChain([existing, 'optionalAccess', _11 => _11.resources]), () => ( {})),
217
+ msg.record.resources
218
+ );
219
+ } else {
220
+ resources = { ..._nullishCoalesce(_optionalChain([existing, 'optionalAccess', _12 => _12.resources]), () => ( {})), ...msg.record.resources };
221
+ }
222
+ const mergedRecord = { ...msg.record, resources };
223
+ await storage.putRecord(msg.storeName, mergedRecord);
224
+ await callbacks.onModuleLoaded({
225
+ storeName: msg.task.storeName,
226
+ langCode: mergedRecord.langCode,
227
+ moduleCode: mergedRecord.moduleCode,
228
+ version: mergedRecord.version
229
+ });
230
+ })().catch(() => {
231
+ });
232
+ this.pendingCallbacks.add(p);
233
+ p.finally(() => this.pendingCallbacks.delete(p));
234
+ } else if (msg.type === "moduleError") {
235
+ _optionalChain([callbacks, 'access', _13 => _13.onModuleError, 'optionalCall', _14 => _14(msg)]);
236
+ } else if (msg.type === "done" || msg.type === "error") {
237
+ if (msg.type === "error") _optionalChain([callbacks, 'access', _15 => _15.onError, 'optionalCall', _16 => _16()]);
238
+ this.finalize(callbacks);
239
+ }
240
+ });
241
+ worker.postMessage({ type: "start", payload });
242
+ }
243
+ terminate() {
244
+ _optionalChain([this, 'access', _17 => _17.worker, 'optionalAccess', _18 => _18.terminate, 'call', _19 => _19()]);
245
+ this.worker = null;
246
+ }
247
+ isRunning() {
248
+ return this.worker !== null;
249
+ }
250
+ finalize(callbacks) {
251
+ if (this.finalized) return;
252
+ this.finalized = true;
253
+ const pending = [...this.pendingCallbacks];
254
+ Promise.allSettled(pending).then(() => {
255
+ this.terminate();
256
+ callbacks.onDone();
257
+ });
258
+ }
259
+ };
260
+
261
+ // src/core/manager.ts
262
+ var DB_NAME = "i18n_cache";
263
+ var PRIORITY_MODULES = ["ENUMS", "CUSTOMER"];
264
+ var currentLang = "zh_CN";
265
+ var currentSig = null;
266
+ var currentPromise = null;
267
+ var resolvedStores = [];
268
+ var resolvedModules = [];
269
+ var resolvedLanguages = [];
270
+ var unloginPullStarted = false;
271
+ var managerOptions = null;
272
+ var initOptions = null;
273
+ var loadedModules = /* @__PURE__ */ new Set();
274
+ var loadingModules = /* @__PURE__ */ new Set();
275
+ var sseClient = new SSEClient();
276
+ var workerManager = new WorkerManager();
277
+ var pendingSSESyncs = /* @__PURE__ */ new Map();
278
+ var isFlushing = false;
279
+ function createI18nManager(options) {
280
+ managerOptions = options;
281
+ return {
282
+ initI18n,
283
+ closeSSE,
284
+ ensureModules,
285
+ getResource,
286
+ getAllRecordsByModule
287
+ };
288
+ }
289
+ function closeSSE() {
290
+ sseClient.close();
291
+ workerManager.terminate();
292
+ pendingSSESyncs.clear();
293
+ isFlushing = false;
294
+ loadedModules.clear();
295
+ loadingModules.clear();
296
+ unloginPullStarted = false;
297
+ currentSig = null;
298
+ currentPromise = null;
299
+ }
300
+ async function initI18n(options) {
301
+ const sig = buildSig(options);
302
+ if (sig === currentSig && currentPromise) return currentPromise;
303
+ if (currentPromise) closeSSE();
304
+ currentSig = sig;
305
+ initOptions = options;
306
+ currentPromise = _initI18n(options).catch((err) => {
307
+ currentSig = null;
308
+ currentPromise = null;
309
+ throw err;
310
+ });
311
+ return currentPromise;
312
+ }
313
+ async function ensureModules(moduleCodes) {
314
+ const toLoad = moduleCodes.filter((m) => {
315
+ const key = `${m}_${currentLang}`;
316
+ if (loadedModules.has(key) || loadingModules.has(key)) return false;
317
+ loadingModules.add(key);
318
+ return true;
319
+ });
320
+ if (toLoad.length === 0) return;
321
+ await Promise.allSettled(
322
+ toLoad.map(async (m) => {
323
+ try {
324
+ _i18next2.default.addResourceBundle(currentLang, m, getStaticLocale(m, currentLang), true, false);
325
+ await injectFromDB(m, currentLang);
326
+ } catch (e3) {
327
+ } finally {
328
+ loadingModules.delete(`${m}_${currentLang}`);
329
+ }
330
+ })
331
+ );
332
+ await _i18next2.default.changeLanguage(currentLang);
333
+ }
334
+ async function getResource(moduleCode, langCode) {
335
+ const storage = managerOptions.storage;
336
+ let merged = { ...getStaticLocale(moduleCode, langCode) };
337
+ const results = await Promise.allSettled(
338
+ resolvedStores.map(
339
+ (store) => storage.getRecord(store.name, buildKey(moduleCode, langCode, store))
340
+ )
341
+ );
342
+ for (let i = 0; i < resolvedStores.length; i++) {
343
+ const result = results[i];
344
+ if (result.status === "rejected") continue;
345
+ const record = result.value;
346
+ if (!_optionalChain([record, 'optionalAccess', _20 => _20.resources])) continue;
347
+ merged = i === 0 ? deepMerge(merged, record.resources) : deepMerge(merged, flatToNested(record.resources));
348
+ }
349
+ return merged;
350
+ }
351
+ async function getAllRecordsByModule(moduleCode) {
352
+ const storage = managerOptions.storage;
353
+ const results = [];
354
+ for (const store of resolvedStores) {
355
+ const records = await storage.getAllByModule(store.name, moduleCode);
356
+ results.push(...records);
357
+ }
358
+ return results;
359
+ }
360
+ async function _initI18n(options) {
361
+ const { storage } = managerOptions;
362
+ const { apiContext } = options;
363
+ currentLang = options.language;
364
+ resolvedStores = options.stores;
365
+ resolvedLanguages = _nullishCoalesce(options.languages, () => ( [options.language]));
366
+ resolvedModules = _nullishCoalesce(options.modules, () => ( []));
367
+ await checkCacheVersion(storage, options);
368
+ await storage.ensureSchemaVersion(resolvedStores);
369
+ for (const lang of resolvedLanguages) {
370
+ for (const m of resolvedModules) {
371
+ _i18next2.default.addResourceBundle(lang, m, getStaticLocale(m, lang), true, false);
372
+ }
373
+ }
374
+ await injectCurrentLanguageModules(currentLang);
375
+ await _i18next2.default.changeLanguage(currentLang);
376
+ if (!apiContext.isLoggedIn()) {
377
+ if (!unloginPullStarted && apiContext.unloginPull) {
378
+ unloginPullStarted = true;
379
+ await doUnloginPull(storage, options);
380
+ }
381
+ return;
382
+ }
383
+ if (!apiContext.pull || !managerOptions.createWorker) return;
384
+ const hasCache = await storage.hasLoginResources(resolvedStores);
385
+ if (!hasCache) {
386
+ await startWorkerFull(options);
387
+ await injectCurrentLanguageModules(currentLang);
388
+ await _i18next2.default.changeLanguage(currentLang);
389
+ emitI18nResourcesUpdated();
390
+ }
391
+ if (apiContext.sse) {
392
+ startSSE(options);
393
+ }
394
+ }
395
+ async function checkCacheVersion(storage, options) {
396
+ if (options.version == null) return;
397
+ const stored = Number(await _asyncNullishCoalesce(await storage.getMeta("cache_version"), async () => ( 0)));
398
+ if (options.version <= stored) return;
399
+ for (const store of options.stores) {
400
+ await storage.clearStore(store.name);
401
+ }
402
+ await storage.setMeta("cache_version", String(options.version));
403
+ }
404
+ async function doUnloginPull(storage, options) {
405
+ const { apiContext } = options;
406
+ if (!apiContext.unloginPull) return;
407
+ const unloginStoreName = resolvedStores[0].name;
408
+ try {
409
+ const blocks = await doFetch(apiContext.unloginPull, options);
410
+ for (const block of blocks) {
411
+ for (const mod of _nullishCoalesce(block.modules, () => ( []))) {
412
+ if (mod.moduleCode !== "UNLOGIN") continue;
413
+ await storage.putRecord(unloginStoreName, {
414
+ key: buildUnloginKey(block.langCode),
415
+ moduleCode: "UNLOGIN",
416
+ langCode: block.langCode,
417
+ version: _nullishCoalesce(mod.version, () => ( 0)),
418
+ resources: flatToNested(parseI18nValues(_nullishCoalesce(mod.i18nValues, () => ( []))))
419
+ });
420
+ }
421
+ }
422
+ } catch (e4) {
423
+ }
424
+ _i18next2.default.addResourceBundle(currentLang, "UNLOGIN", getStaticLocale("UNLOGIN", currentLang), true, false);
425
+ const dbUnlogin = await storage.getRecord(unloginStoreName, buildUnloginKey(currentLang));
426
+ if (_optionalChain([dbUnlogin, 'optionalAccess', _21 => _21.resources])) {
427
+ _i18next2.default.addResourceBundle(currentLang, "UNLOGIN", dbUnlogin.resources, true, true);
428
+ }
429
+ await _i18next2.default.changeLanguage(currentLang);
430
+ emitI18nResourcesUpdated();
431
+ }
432
+ async function startWorkerFull(options) {
433
+ const { apiContext } = options;
434
+ const pull = apiContext.pull;
435
+ const tasks = [];
436
+ for (const store of resolvedStores) {
437
+ for (const lang of resolvedLanguages) {
438
+ for (const module of sortedModules()) {
439
+ tasks.push({
440
+ storeName: store.name,
441
+ langCode: lang,
442
+ moduleCode: module,
443
+ version: 0,
444
+ trigger: "init-full"
445
+ });
446
+ }
447
+ }
448
+ }
449
+ await runWorker(options, tasks, pull);
450
+ }
451
+ function runWorker(options, tasks, pull) {
452
+ return new Promise((resolve, reject) => {
453
+ const { apiContext } = options;
454
+ const workerWritesDB = _nullishCoalesce(managerOptions.workerWritesDB, () => ( false));
455
+ workerManager.start(
456
+ managerOptions.createWorker,
457
+ {
458
+ baseURL: apiContext.baseURL,
459
+ headers: apiContext.getHeaders(),
460
+ pullPath: pull.path,
461
+ pullMethod: _nullishCoalesce(pull.method, () => ( "GET")),
462
+ stores: resolvedStores,
463
+ tasks,
464
+ dbName: DB_NAME
465
+ },
466
+ {
467
+ onModuleLoaded: async (data) => {
468
+ await onModuleLoaded(data);
469
+ },
470
+ onModuleError: (_data) => {
471
+ },
472
+ onDone: resolve,
473
+ onError: () => reject(new Error("[i18n] Worker fatal error"))
474
+ },
475
+ workerWritesDB ? void 0 : managerOptions.storage
476
+ );
477
+ });
478
+ }
479
+ async function onModuleLoaded(data) {
480
+ if (data.langCode === currentLang && loadedModules.has(`${data.moduleCode}_${currentLang}`)) {
481
+ await injectFromDB(data.moduleCode, data.langCode);
482
+ await _i18next2.default.changeLanguage(currentLang);
483
+ emitI18nResourcesUpdated();
484
+ }
485
+ }
486
+ async function injectFromDB(moduleCode, langCode) {
487
+ const storage = managerOptions.storage;
488
+ for (let i = 0; i < resolvedStores.length; i++) {
489
+ const store = resolvedStores[i];
490
+ const key = buildKey(moduleCode, langCode, store);
491
+ const record = await storage.getRecord(store.name, key);
492
+ if (!_optionalChain([record, 'optionalAccess', _22 => _22.resources])) continue;
493
+ if (i === 0) {
494
+ _i18next2.default.addResourceBundle(langCode, moduleCode, record.resources, true, true);
495
+ } else {
496
+ _i18next2.default.addResources(langCode, moduleCode, record.resources);
497
+ }
498
+ }
499
+ loadedModules.add(`${moduleCode}_${langCode}`);
500
+ }
501
+ async function injectCurrentLanguageModules(langCode) {
502
+ const modules = sortedModules();
503
+ const results = await Promise.allSettled(modules.map((m) => injectFromDB(m, langCode)));
504
+ const retries = modules.filter((_, i) => results[i].status === "rejected");
505
+ if (retries.length > 0) {
506
+ await Promise.allSettled(retries.map((m) => injectFromDB(m, langCode)));
507
+ }
508
+ }
509
+ function startSSE(options) {
510
+ const { apiContext } = options;
511
+ if (!apiContext.sse) return;
512
+ sseClient.start(
513
+ (raw) => {
514
+ const msg = _optionalChain([apiContext, 'access', _23 => _23.sse, 'optionalAccess', _24 => _24.transformMessage]) ? apiContext.sse.transformMessage(raw) : raw;
515
+ void handleSSEMessage(msg, options);
516
+ },
517
+ {
518
+ baseURL: apiContext.baseURL,
519
+ eventsPath: apiContext.sse.path,
520
+ getHeaders: apiContext.getHeaders
521
+ }
522
+ );
523
+ }
524
+ async function handleSSEMessage(msg, options) {
525
+ if (msg.type !== "i18n_update") return;
526
+ if (!options.apiContext.sseSync) return;
527
+ const store = resolvedStores.find((s) => s.name === msg.storeName);
528
+ if (!store) return;
529
+ const cg = store.cacheGroupKey;
530
+ const key = `${msg.storeName}_${msg.moduleCode}_${msg.langCode}_${_nullishCoalesce(cg, () => ( ""))}`;
531
+ const localVersion = await getStoredVersion(store, msg.moduleCode, msg.langCode);
532
+ const pending = pendingSSESyncs.get(key);
533
+ const knownTarget = pending ? Math.max(localVersion, pending.targetVersion) : localVersion;
534
+ if (msg.version <= knownTarget) return;
535
+ if (!pending && pendingSSESyncs.size >= 50) return;
536
+ pendingSSESyncs.set(key, {
537
+ key,
538
+ storeName: msg.storeName,
539
+ moduleCode: msg.moduleCode,
540
+ langCode: msg.langCode,
541
+ cacheGroupKey: cg,
542
+ targetVersion: pending ? Math.max(pending.targetVersion, msg.version) : msg.version
543
+ });
544
+ void flushPendingSSESyncs(options);
545
+ }
546
+ async function flushPendingSSESyncs(options) {
547
+ if (isFlushing) return;
548
+ isFlushing = true;
549
+ const tasks = [...pendingSSESyncs.values()];
550
+ pendingSSESyncs.clear();
551
+ const sorted = [
552
+ ...tasks.filter((t) => t.langCode === currentLang),
553
+ ...tasks.filter((t) => t.langCode !== currentLang)
554
+ ];
555
+ try {
556
+ for (const task of sorted) {
557
+ let localVersion;
558
+ try {
559
+ localVersion = await getStoredVersion(
560
+ { name: task.storeName, cacheGroupKey: task.cacheGroupKey },
561
+ task.moduleCode,
562
+ task.langCode
563
+ );
564
+ } catch (e5) {
565
+ localVersion = 0;
566
+ }
567
+ if (task.targetVersion <= localVersion) continue;
568
+ try {
569
+ await pullAndStore(task, localVersion, options);
570
+ if (task.langCode === currentLang && loadedModules.has(`${task.moduleCode}_${task.langCode}`)) {
571
+ await injectFromDB(task.moduleCode, task.langCode);
572
+ await _i18next2.default.changeLanguage(currentLang);
573
+ emitI18nResourcesUpdated();
574
+ }
575
+ } catch (e6) {
576
+ }
577
+ }
578
+ } finally {
579
+ isFlushing = false;
580
+ if (pendingSSESyncs.size > 0) void flushPendingSSESyncs(options);
581
+ }
582
+ }
583
+ async function pullAndStore(task, fromVersion, options) {
584
+ const storage = managerOptions.storage;
585
+ const sseSync = options.apiContext.sseSync;
586
+ const blocks = await doFetch(sseSync, options, {
587
+ storeName: task.storeName,
588
+ langCode: task.langCode,
589
+ moduleCode: task.moduleCode,
590
+ version: fromVersion
591
+ });
592
+ for (const block of blocks) {
593
+ for (const mod of _nullishCoalesce(block.modules, () => ( []))) {
594
+ const store = resolvedStores.find((s) => s.name === task.storeName);
595
+ if (!store) continue;
596
+ const storeIndex = resolvedStores.indexOf(store);
597
+ const key = buildKey(mod.moduleCode, block.langCode, store);
598
+ const existing = await storage.getRecord(store.name, key);
599
+ if (mod.version <= (_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _25 => _25.version]), () => ( 0)))) continue;
600
+ let resources;
601
+ const parsed = parseI18nValues(_nullishCoalesce(mod.i18nValues, () => ( [])));
602
+ if (storeIndex === 0) {
603
+ resources = deepMerge(
604
+ _nullishCoalesce(_optionalChain([existing, 'optionalAccess', _26 => _26.resources]), () => ( {})),
605
+ flatToNested(parsed)
606
+ );
607
+ } else {
608
+ resources = { ..._nullishCoalesce(_optionalChain([existing, 'optionalAccess', _27 => _27.resources]), () => ( {})), ...parsed };
609
+ }
610
+ const version = Math.max(mod.version, task.targetVersion);
611
+ await storage.putRecord(store.name, {
612
+ key,
613
+ moduleCode: mod.moduleCode,
614
+ langCode: block.langCode,
615
+ version,
616
+ resources
617
+ });
618
+ }
619
+ }
620
+ }
621
+ async function doFetch(config, options, internalParams) {
622
+ const fetchFn = _nullishCoalesce(managerOptions.fetchFn, () => ( globalThis.fetch.bind(globalThis)));
623
+ const headers = options.apiContext.getHeaders();
624
+ const url = options.apiContext.baseURL + config.path;
625
+ const params = "buildParams" in config && config.buildParams ? config.buildParams(internalParams) : toDefaultParams(internalParams);
626
+ const method = "method" in config ? config.method : void 0;
627
+ let res;
628
+ if (method === "POST") {
629
+ res = await fetchFn(url, {
630
+ method: "POST",
631
+ headers: { "Content-Type": "application/json", ...headers },
632
+ body: JSON.stringify(params)
633
+ });
634
+ } else {
635
+ res = await fetchFn(`${url}?${toQueryString(params)}`, { headers });
636
+ }
637
+ if (!res.ok) throw new Error(`[i18n] fetch failed: ${res.status} ${url}`);
638
+ const raw = await res.json();
639
+ return "transform" in config && config.transform ? config.transform(raw) : raw;
640
+ }
641
+ function buildSig(options) {
642
+ const cgKeys = options.stores.map((s) => _nullishCoalesce(s.cacheGroupKey, () => ( ""))).join(",");
643
+ return [options.apiContext.baseURL, options.language, cgKeys].join("|");
644
+ }
645
+ function sortedModules() {
646
+ const priority = PRIORITY_MODULES.filter((m) => resolvedModules.includes(m));
647
+ const rest = resolvedModules.filter((m) => !PRIORITY_MODULES.includes(m));
648
+ return [...priority, ...rest];
649
+ }
650
+ function getStaticLocale(moduleCode, langCode) {
651
+ return _nullishCoalesce(_optionalChain([initOptions, 'optionalAccess', _28 => _28.staticLocales, 'optionalAccess', _29 => _29[langCode], 'optionalAccess', _30 => _30[moduleCode]]), () => ( {}));
652
+ }
653
+ async function getStoredVersion(store, moduleCode, langCode) {
654
+ const storage = managerOptions.storage;
655
+ const key = buildKey(moduleCode, langCode, store);
656
+ const record = await storage.getRecord(store.name, key);
657
+ return _nullishCoalesce(_optionalChain([record, 'optionalAccess', _31 => _31.version]), () => ( 0));
658
+ }
659
+
660
+
661
+
662
+
663
+
664
+ exports.I18N_RESOURCES_UPDATED_EVENT = I18N_RESOURCES_UPDATED_EVENT; exports.emitI18nResourcesUpdated = emitI18nResourcesUpdated; exports.createI18nManager = createI18nManager;