@better-i18n/core 0.1.8 → 0.1.10

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/README.md CHANGED
@@ -8,6 +8,8 @@ Framework-agnostic core utilities for fetching translations from Better i18n CDN
8
8
  - **Edge-Ready** - Optimized for edge environments (Cloudflare, Vercel, etc.)
9
9
  - **Type-Safe** - Full TypeScript support
10
10
  - **Cached** - Built-in manifest caching with configurable TTL
11
+ - **Offline Fallback** - 3-tier fallback chain: CDN → persistent storage → static data
12
+ - **Resilient** - Configurable fetch timeout and retry with exponential backoff
11
13
 
12
14
  ## Installation
13
15
 
@@ -56,19 +58,80 @@ interface I18nCoreConfig {
56
58
  debug?: boolean; // default: false
57
59
  logLevel?: LogLevel; // default: "warn"
58
60
  fetch?: typeof fetch; // custom fetch function
61
+
62
+ // Fallback & Resilience
63
+ storage?: TranslationStorage; // persistent cache (localStorage, AsyncStorage, etc.)
64
+ staticData?: Record<string, Messages> | (() => Promise<Record<string, Messages>>);
65
+ fetchTimeout?: number; // default: 10000 (10s) — abort if CDN doesn't respond
66
+ retryCount?: number; // default: 1 — retry attempts on CDN failure
59
67
  }
60
68
  ```
61
69
 
70
+ ## Offline Fallback
71
+
72
+ When CDN is unavailable, translations are resolved through a 3-tier fallback chain:
73
+
74
+ ```
75
+ CDN (primary) → Storage (persistent cache) → Static Data (bundled) → throw
76
+ ```
77
+
78
+ ### Storage Adapter
79
+
80
+ Persist translations locally so they survive CDN outages and app restarts:
81
+
82
+ ```typescript
83
+ import { createI18nCore, createLocalStorage } from "@better-i18n/core";
84
+
85
+ const i18n = createI18nCore({
86
+ project: "your-org/your-project",
87
+ defaultLocale: "en",
88
+ storage: createLocalStorage(), // browser localStorage
89
+ });
90
+ ```
91
+
92
+ Built-in adapters:
93
+
94
+ | Adapter | Environment | Import |
95
+ | --- | --- | --- |
96
+ | `createLocalStorage()` | Browser | `@better-i18n/core` |
97
+ | `createMemoryStorage()` | Any (non-persistent) | `@better-i18n/core` |
98
+ | `createAutoStorage()` | Auto-detect (localStorage or memory) | `@better-i18n/core` |
99
+
100
+ You can also provide any object implementing `{ get, set, remove? }` for custom stores (e.g., AsyncStorage, MMKV).
101
+
102
+ ### Static Data
103
+
104
+ Bundle translations as a last-resort fallback:
105
+
106
+ ```typescript
107
+ const i18n = createI18nCore({
108
+ project: "your-org/your-project",
109
+ defaultLocale: "en",
110
+ // Static import
111
+ staticData: {
112
+ en: { common: { hello: "Hello" } },
113
+ tr: { common: { hello: "Merhaba" } },
114
+ },
115
+ // Or lazy import (code-split)
116
+ // staticData: () => import('./fallback-translations.json'),
117
+ });
118
+ ```
119
+
62
120
  ## Framework Integrations
63
121
 
64
- This package is the foundation for:
122
+ This package is the foundation for all Better i18n SDKs:
123
+
124
+ | Package | Framework | Built On |
125
+ | --- | --- | --- |
126
+ | [`@better-i18n/next`](https://www.npmjs.com/package/@better-i18n/next) | Next.js | `next-intl` |
127
+ | [`@better-i18n/use-intl`](https://www.npmjs.com/package/@better-i18n/use-intl) | React, Vite, TanStack Start | `use-intl` |
128
+ | [`@better-i18n/expo`](https://www.npmjs.com/package/@better-i18n/expo) | Expo / React Native | `i18next` |
65
129
 
66
- - `@better-i18n/next` - Next.js integration with ISR support
67
- - `@better-i18n/react` - React/TanStack integration (coming soon)
130
+ If you're using one of these frameworks, install the framework SDK instead — `@better-i18n/core` is included automatically as a dependency.
68
131
 
69
132
  ## Documentation
70
133
 
71
- Full documentation available at [docs.better-i18n.com](https://docs.better-i18n.com)
134
+ Full documentation available at [docs.better-i18n.com/core](https://docs.better-i18n.com/core)
72
135
 
73
136
  ## License
74
137
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cdn-fallback.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn-fallback.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cdn-fallback.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,380 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { createI18nCore, clearManifestCache, clearMessagesCache, } from "../cdn";
3
+ import { createMemoryStorage } from "../storage/memory";
4
+ // ─── Test fixtures ──────────────────────────────────────────────────
5
+ const MANIFEST = {
6
+ projectSlug: "dashboard",
7
+ sourceLanguage: "en",
8
+ languages: [
9
+ { code: "en", name: "English", isSource: true },
10
+ { code: "tr", name: "Turkish" },
11
+ ],
12
+ updatedAt: "2026-01-01T00:00:00Z",
13
+ };
14
+ const MESSAGES_EN = {
15
+ common: { hello: "Hello", goodbye: "Goodbye" },
16
+ };
17
+ const MESSAGES_TR = {
18
+ common: { hello: "Merhaba", goodbye: "Hoşçakal" },
19
+ };
20
+ const BASE_CONFIG = {
21
+ project: "acme/dashboard",
22
+ defaultLocale: "en",
23
+ cdnBaseUrl: "https://cdn.test.com",
24
+ };
25
+ // ─── Helpers ────────────────────────────────────────────────────────
26
+ /**
27
+ * Create a mock fetch that responds with given data, or throws.
28
+ */
29
+ const createMockFetch = (responses) => {
30
+ return vi.fn(async (input, init) => {
31
+ // Check if aborted
32
+ if (init?.signal?.aborted) {
33
+ throw new DOMException("The operation was aborted.", "AbortError");
34
+ }
35
+ const url = typeof input === "string" ? input : input.toString();
36
+ for (const [pattern, response] of Object.entries(responses)) {
37
+ if (url.includes(pattern)) {
38
+ if (response.throws) {
39
+ throw new Error(`Network error for ${pattern}`);
40
+ }
41
+ return {
42
+ ok: response.ok,
43
+ status: response.ok ? 200 : 500,
44
+ json: async () => response.data,
45
+ };
46
+ }
47
+ }
48
+ throw new Error(`Unmocked URL: ${url}`);
49
+ });
50
+ };
51
+ /**
52
+ * Create a fetch that hangs forever (for timeout tests)
53
+ */
54
+ const createHangingFetch = () => {
55
+ return vi.fn((_input, init) => new Promise((_resolve, reject) => {
56
+ // Listen for abort signal
57
+ if (init?.signal) {
58
+ init.signal.addEventListener("abort", () => {
59
+ reject(new DOMException("The operation was aborted.", "AbortError"));
60
+ });
61
+ }
62
+ // Never resolves otherwise
63
+ }));
64
+ };
65
+ // ─── Tests ──────────────────────────────────────────────────────────
66
+ describe("cdn fallback", () => {
67
+ beforeEach(() => {
68
+ clearManifestCache();
69
+ clearMessagesCache();
70
+ });
71
+ // ─── CDN success path ───────────────────────────────────────────
72
+ describe("CDN success path", () => {
73
+ it("fetches messages from CDN successfully", async () => {
74
+ const mockFetch = createMockFetch({
75
+ "manifest.json": { ok: true, data: MANIFEST },
76
+ "en/translations.json": { ok: true, data: MESSAGES_EN },
77
+ });
78
+ const i18n = createI18nCore({ ...BASE_CONFIG, fetch: mockFetch });
79
+ const messages = await i18n.getMessages("en");
80
+ expect(messages).toEqual(MESSAGES_EN);
81
+ });
82
+ it("fetches manifest from CDN successfully", async () => {
83
+ const mockFetch = createMockFetch({
84
+ "manifest.json": { ok: true, data: MANIFEST },
85
+ });
86
+ const i18n = createI18nCore({ ...BASE_CONFIG, fetch: mockFetch });
87
+ const manifest = await i18n.getManifest();
88
+ expect(manifest.languages).toHaveLength(2);
89
+ expect(manifest.languages[0].code).toBe("en");
90
+ });
91
+ it("caches messages in memory on subsequent calls", async () => {
92
+ const mockFetch = createMockFetch({
93
+ "en/translations.json": { ok: true, data: MESSAGES_EN },
94
+ });
95
+ const i18n = createI18nCore({ ...BASE_CONFIG, fetch: mockFetch });
96
+ await i18n.getMessages("en");
97
+ await i18n.getMessages("en");
98
+ // Should only fetch once — second call served from memory
99
+ expect(mockFetch).toHaveBeenCalledTimes(1);
100
+ });
101
+ it("writes through to storage on CDN success", async () => {
102
+ const storage = createMemoryStorage();
103
+ const setSpy = vi.spyOn(storage, "set");
104
+ const mockFetch = createMockFetch({
105
+ "en/translations.json": { ok: true, data: MESSAGES_EN },
106
+ });
107
+ const i18n = createI18nCore({
108
+ ...BASE_CONFIG,
109
+ fetch: mockFetch,
110
+ storage,
111
+ });
112
+ await i18n.getMessages("en");
113
+ // Wait for fire-and-forget storage write
114
+ await new Promise((r) => setTimeout(r, 10));
115
+ expect(setSpy).toHaveBeenCalledWith(expect.stringContaining("messages"), expect.any(String));
116
+ });
117
+ });
118
+ // ─── CDN failure → storage fallback ─────────────────────────────
119
+ describe("CDN failure → storage fallback", () => {
120
+ it("falls back to storage when CDN fails for messages", async () => {
121
+ const storage = createMemoryStorage();
122
+ // Pre-populate storage with cached messages
123
+ await storage.set("@better-i18n:messages:acme/dashboard:en", JSON.stringify(MESSAGES_EN));
124
+ const mockFetch = createMockFetch({
125
+ "en/translations.json": { ok: false },
126
+ });
127
+ const i18n = createI18nCore({
128
+ ...BASE_CONFIG,
129
+ fetch: mockFetch,
130
+ storage,
131
+ retryCount: 0,
132
+ });
133
+ const messages = await i18n.getMessages("en");
134
+ expect(messages).toEqual(MESSAGES_EN);
135
+ });
136
+ it("falls back to storage when CDN fails for manifest", async () => {
137
+ const storage = createMemoryStorage();
138
+ // Pre-populate storage with cached manifest
139
+ await storage.set("@better-i18n:manifest:acme/dashboard", JSON.stringify(MANIFEST));
140
+ const mockFetch = createMockFetch({
141
+ "manifest.json": { ok: false },
142
+ });
143
+ const i18n = createI18nCore({
144
+ ...BASE_CONFIG,
145
+ fetch: mockFetch,
146
+ storage,
147
+ retryCount: 0,
148
+ });
149
+ const manifest = await i18n.getManifest();
150
+ expect(manifest.languages).toHaveLength(2);
151
+ });
152
+ it("falls back to storage on network error", async () => {
153
+ const storage = createMemoryStorage();
154
+ await storage.set("@better-i18n:messages:acme/dashboard:tr", JSON.stringify(MESSAGES_TR));
155
+ const mockFetch = createMockFetch({
156
+ "tr/translations.json": { ok: false, throws: true },
157
+ });
158
+ const i18n = createI18nCore({
159
+ ...BASE_CONFIG,
160
+ fetch: mockFetch,
161
+ storage,
162
+ retryCount: 0,
163
+ });
164
+ const messages = await i18n.getMessages("tr");
165
+ expect(messages).toEqual(MESSAGES_TR);
166
+ });
167
+ });
168
+ // ─── CDN failure + storage miss → staticData ────────────────────
169
+ describe("CDN failure + storage miss → staticData fallback", () => {
170
+ it("falls back to staticData when CDN and storage fail", async () => {
171
+ const mockFetch = createMockFetch({
172
+ "en/translations.json": { ok: false, throws: true },
173
+ });
174
+ const i18n = createI18nCore({
175
+ ...BASE_CONFIG,
176
+ fetch: mockFetch,
177
+ staticData: { en: MESSAGES_EN, tr: MESSAGES_TR },
178
+ retryCount: 0,
179
+ });
180
+ const messages = await i18n.getMessages("en");
181
+ expect(messages).toEqual(MESSAGES_EN);
182
+ });
183
+ it("resolves lazy staticData function", async () => {
184
+ const mockFetch = createMockFetch({
185
+ "tr/translations.json": { ok: false, throws: true },
186
+ });
187
+ const i18n = createI18nCore({
188
+ ...BASE_CONFIG,
189
+ fetch: mockFetch,
190
+ staticData: async () => ({ en: MESSAGES_EN, tr: MESSAGES_TR }),
191
+ retryCount: 0,
192
+ });
193
+ const messages = await i18n.getMessages("tr");
194
+ expect(messages).toEqual(MESSAGES_TR);
195
+ });
196
+ it("throws when locale not in staticData", async () => {
197
+ const mockFetch = createMockFetch({
198
+ "fr/translations.json": { ok: false, throws: true },
199
+ });
200
+ const i18n = createI18nCore({
201
+ ...BASE_CONFIG,
202
+ fetch: mockFetch,
203
+ staticData: { en: MESSAGES_EN },
204
+ retryCount: 0,
205
+ });
206
+ await expect(i18n.getMessages("fr")).rejects.toThrow();
207
+ });
208
+ });
209
+ // ─── Complete failure ───────────────────────────────────────────
210
+ describe("complete failure (no CDN, no storage, no staticData)", () => {
211
+ it("throws when all sources fail for messages", async () => {
212
+ const mockFetch = createMockFetch({
213
+ "en/translations.json": { ok: false, throws: true },
214
+ });
215
+ const i18n = createI18nCore({
216
+ ...BASE_CONFIG,
217
+ fetch: mockFetch,
218
+ retryCount: 0,
219
+ });
220
+ await expect(i18n.getMessages("en")).rejects.toThrow();
221
+ });
222
+ it("throws when all sources fail for manifest", async () => {
223
+ const mockFetch = createMockFetch({
224
+ "manifest.json": { ok: false, throws: true },
225
+ });
226
+ const i18n = createI18nCore({
227
+ ...BASE_CONFIG,
228
+ fetch: mockFetch,
229
+ retryCount: 0,
230
+ });
231
+ await expect(i18n.getManifest()).rejects.toThrow();
232
+ });
233
+ });
234
+ // ─── Timeout ────────────────────────────────────────────────────
235
+ describe("timeout", () => {
236
+ it("aborts fetch after timeout and falls back to storage", async () => {
237
+ const storage = createMemoryStorage();
238
+ await storage.set("@better-i18n:messages:acme/dashboard:en", JSON.stringify(MESSAGES_EN));
239
+ const hangingFetch = createHangingFetch();
240
+ const i18n = createI18nCore({
241
+ ...BASE_CONFIG,
242
+ fetch: hangingFetch,
243
+ storage,
244
+ fetchTimeout: 50, // 50ms timeout for fast test
245
+ retryCount: 0,
246
+ });
247
+ const messages = await i18n.getMessages("en");
248
+ expect(messages).toEqual(MESSAGES_EN);
249
+ });
250
+ it("aborts manifest fetch after timeout and falls back to storage", async () => {
251
+ const storage = createMemoryStorage();
252
+ await storage.set("@better-i18n:manifest:acme/dashboard", JSON.stringify(MANIFEST));
253
+ const hangingFetch = createHangingFetch();
254
+ const i18n = createI18nCore({
255
+ ...BASE_CONFIG,
256
+ fetch: hangingFetch,
257
+ storage,
258
+ fetchTimeout: 50,
259
+ retryCount: 0,
260
+ });
261
+ const manifest = await i18n.getManifest();
262
+ expect(manifest.languages).toHaveLength(2);
263
+ });
264
+ });
265
+ // ─── Retry logic ────────────────────────────────────────────────
266
+ describe("retry logic", () => {
267
+ it("retries on failure before falling back", async () => {
268
+ let callCount = 0;
269
+ const flakyFetch = vi.fn(async (input) => {
270
+ const url = typeof input === "string" ? input : input.toString();
271
+ if (url.includes("translations.json")) {
272
+ callCount++;
273
+ if (callCount <= 2) {
274
+ throw new Error("Transient error");
275
+ }
276
+ return {
277
+ ok: true,
278
+ status: 200,
279
+ json: async () => MESSAGES_EN,
280
+ };
281
+ }
282
+ throw new Error(`Unmocked URL: ${url}`);
283
+ });
284
+ const i18n = createI18nCore({
285
+ ...BASE_CONFIG,
286
+ fetch: flakyFetch,
287
+ retryCount: 2,
288
+ fetchTimeout: 5000,
289
+ });
290
+ const messages = await i18n.getMessages("en");
291
+ expect(messages).toEqual(MESSAGES_EN);
292
+ // 1st attempt + 2 retries = 3 calls total
293
+ expect(callCount).toBe(3);
294
+ });
295
+ it("falls back to storage after all retries exhausted", async () => {
296
+ const storage = createMemoryStorage();
297
+ await storage.set("@better-i18n:messages:acme/dashboard:en", JSON.stringify(MESSAGES_EN));
298
+ let callCount = 0;
299
+ const alwaysFailFetch = vi.fn(async () => {
300
+ callCount++;
301
+ throw new Error("Network error");
302
+ });
303
+ const i18n = createI18nCore({
304
+ ...BASE_CONFIG,
305
+ fetch: alwaysFailFetch,
306
+ storage,
307
+ retryCount: 2,
308
+ fetchTimeout: 5000,
309
+ });
310
+ const messages = await i18n.getMessages("en");
311
+ expect(messages).toEqual(MESSAGES_EN);
312
+ // 1st attempt + 2 retries = 3 calls
313
+ expect(callCount).toBe(3);
314
+ });
315
+ });
316
+ // ─── Storage adapter ────────────────────────────────────────────
317
+ describe("storage adapter", () => {
318
+ it("createMemoryStorage works correctly", async () => {
319
+ const storage = createMemoryStorage();
320
+ expect(await storage.get("key")).toBeNull();
321
+ await storage.set("key", "value");
322
+ expect(await storage.get("key")).toBe("value");
323
+ await storage.remove("key");
324
+ expect(await storage.get("key")).toBeNull();
325
+ });
326
+ it("handles storage.get errors gracefully", async () => {
327
+ const brokenStorage = {
328
+ get: async () => {
329
+ throw new Error("Storage read failed");
330
+ },
331
+ set: async () => {
332
+ throw new Error("Storage write failed");
333
+ },
334
+ };
335
+ const mockFetch = createMockFetch({
336
+ "en/translations.json": { ok: false, throws: true },
337
+ });
338
+ const i18n = createI18nCore({
339
+ ...BASE_CONFIG,
340
+ fetch: mockFetch,
341
+ storage: brokenStorage,
342
+ retryCount: 0,
343
+ });
344
+ // Should still throw CDN error even though storage also fails
345
+ await expect(i18n.getMessages("en")).rejects.toThrow();
346
+ });
347
+ });
348
+ // ─── Config defaults ───────────────────────────────────────────
349
+ describe("config defaults", () => {
350
+ it("uses default fetchTimeout of 10000ms", () => {
351
+ const mockFetch = createMockFetch({});
352
+ const i18n = createI18nCore({ ...BASE_CONFIG, fetch: mockFetch });
353
+ expect(i18n.config.fetchTimeout).toBe(10000);
354
+ });
355
+ it("uses default retryCount of 1", () => {
356
+ const mockFetch = createMockFetch({});
357
+ const i18n = createI18nCore({ ...BASE_CONFIG, fetch: mockFetch });
358
+ expect(i18n.config.retryCount).toBe(1);
359
+ });
360
+ it("allows overriding fetchTimeout", () => {
361
+ const mockFetch = createMockFetch({});
362
+ const i18n = createI18nCore({
363
+ ...BASE_CONFIG,
364
+ fetch: mockFetch,
365
+ fetchTimeout: 5000,
366
+ });
367
+ expect(i18n.config.fetchTimeout).toBe(5000);
368
+ });
369
+ it("allows overriding retryCount", () => {
370
+ const mockFetch = createMockFetch({});
371
+ const i18n = createI18nCore({
372
+ ...BASE_CONFIG,
373
+ fetch: mockFetch,
374
+ retryCount: 3,
375
+ });
376
+ expect(i18n.config.retryCount).toBe(3);
377
+ });
378
+ });
379
+ });
380
+ //# sourceMappingURL=cdn-fallback.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn-fallback.test.js","sourceRoot":"","sources":["../../src/__tests__/cdn-fallback.test.ts"],"names":[],"mappings":"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"}
package/dist/cdn.d.ts CHANGED
@@ -12,10 +12,27 @@ import type { I18nCore, I18nCoreConfig } from "./types";
12
12
  * const messages = await i18n.getMessages('en')
13
13
  * const locales = await i18n.getLocales()
14
14
  * ```
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // With fallback support
19
+ * const i18n = createI18nCore({
20
+ * project: 'acme/dashboard',
21
+ * defaultLocale: 'en',
22
+ * storage: createAutoStorage(),
23
+ * staticData: { en: { common: { hello: "Hello" } } },
24
+ * fetchTimeout: 5000,
25
+ * retryCount: 2,
26
+ * })
27
+ * ```
15
28
  */
16
29
  export declare const createI18nCore: (config: I18nCoreConfig) => I18nCore;
17
30
  /**
18
- * Clear the manifest cache (useful for testing)
31
+ * Clear all caches (useful for testing)
19
32
  */
20
33
  export declare const clearManifestCache: () => void;
34
+ /**
35
+ * Clear the messages cache (useful for testing)
36
+ */
37
+ export declare const clearMessagesCache: () => void;
21
38
  //# sourceMappingURL=cdn.d.ts.map
package/dist/cdn.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAKf,MAAM,SAAS,CAAC;AAuFjB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,cAAc,KAAG,QAkCvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC"}
1
+ {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAMf,MAAM,SAAS,CAAC;AAqSjB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,cAAc,KAAG,QAmCvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC"}
package/dist/cdn.js CHANGED
@@ -2,18 +2,97 @@ import { TtlCache, buildCacheKey } from "./cache";
2
2
  import { getProjectBaseUrl, normalizeConfig } from "./config";
3
3
  import { createLogger } from "./logger";
4
4
  import { extractLanguages } from "./manifest";
5
- // Global manifest cache (shared across instances)
5
+ const STORAGE_PREFIX = "@better-i18n";
6
+ // Global caches (shared across instances)
6
7
  const manifestCache = new TtlCache();
8
+ const messagesCache = new TtlCache();
9
+ // ─── Storage helpers ────────────────────────────────────────────────
10
+ const buildManifestStorageKey = (project) => `${STORAGE_PREFIX}:manifest:${project}`;
11
+ const buildMessagesStorageKey = (project, locale) => `${STORAGE_PREFIX}:messages:${project}:${locale}`;
12
+ const readFromStorage = async (storage, key) => {
13
+ if (!storage)
14
+ return null;
15
+ try {
16
+ const raw = await storage.get(key);
17
+ if (!raw)
18
+ return null;
19
+ return JSON.parse(raw);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ };
25
+ const writeToStorage = async (storage, key, data) => {
26
+ if (!storage)
27
+ return;
28
+ try {
29
+ await storage.set(key, JSON.stringify(data));
30
+ }
31
+ catch {
32
+ // Storage full or unavailable — silently fail
33
+ }
34
+ };
35
+ // ─── Fetch helpers ──────────────────────────────────────────────────
7
36
  /**
8
- * Fetch manifest from CDN
37
+ * Fetch with AbortController-based timeout
9
38
  */
10
- const fetchManifest = async (config, fetchFn) => {
39
+ const fetchWithTimeout = async (fetchFn, url, init, timeoutMs) => {
40
+ const controller = new AbortController();
41
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
42
+ try {
43
+ const response = await fetchFn(url, {
44
+ ...init,
45
+ signal: controller.signal,
46
+ });
47
+ return response;
48
+ }
49
+ finally {
50
+ clearTimeout(timer);
51
+ }
52
+ };
53
+ /**
54
+ * Fetch with retry logic
55
+ */
56
+ const fetchWithRetry = async (fetchFn, url, init, timeoutMs, retryCount) => {
57
+ let lastError;
58
+ for (let attempt = 0; attempt <= retryCount; attempt++) {
59
+ try {
60
+ return await fetchWithTimeout(fetchFn, url, init, timeoutMs);
61
+ }
62
+ catch (err) {
63
+ lastError = err;
64
+ // Don't retry on the last attempt
65
+ if (attempt < retryCount) {
66
+ // Simple exponential backoff: 200ms, 400ms, ...
67
+ await new Promise((resolve) => setTimeout(resolve, 200 * (attempt + 1)));
68
+ }
69
+ }
70
+ }
71
+ throw lastError;
72
+ };
73
+ // ─── Resolve staticData helper ──────────────────────────────────────
74
+ const resolveStaticData = async (staticData) => {
75
+ if (!staticData)
76
+ return null;
77
+ if (typeof staticData === "function") {
78
+ try {
79
+ return await staticData();
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ return staticData;
86
+ };
87
+ // ─── Manifest fetching with fallback ────────────────────────────────
88
+ /**
89
+ * Fetch manifest from CDN (raw, no cache)
90
+ */
91
+ const fetchManifestFromCdn = async (config, fetchFn) => {
11
92
  const logger = createLogger(config, "manifest");
12
93
  const url = `${getProjectBaseUrl(config)}/manifest.json`;
13
94
  logger.debug("fetching", url);
14
- const response = await fetchFn(url, {
15
- headers: { "Cache-Control": "no-cache" },
16
- });
95
+ const response = await fetchWithRetry(fetchFn, url, { headers: { "Cache-Control": "no-cache" } }, config.fetchTimeout, config.retryCount);
17
96
  if (!response.ok) {
18
97
  const message = `Manifest fetch failed (${response.status})`;
19
98
  logger.error(message);
@@ -27,29 +106,52 @@ const fetchManifest = async (config, fetchFn) => {
27
106
  return data;
28
107
  };
29
108
  /**
30
- * Get manifest with caching
109
+ * Get manifest with full fallback chain:
110
+ * 1. Memory cache (TtlCache)
111
+ * 2. CDN fetch (with timeout + retry)
112
+ * 3. Persistent storage
113
+ * 4. Throw (last resort)
31
114
  */
32
115
  const getManifestWithCache = async (config, fetchFn, forceRefresh = false) => {
116
+ const logger = createLogger(config, "manifest");
33
117
  const cacheKey = buildCacheKey(config.cdnBaseUrl, config.project);
118
+ const storageKey = buildManifestStorageKey(config.project);
119
+ // 1. Memory cache
34
120
  if (!forceRefresh) {
35
121
  const cached = manifestCache.get(cacheKey);
36
122
  if (cached)
37
123
  return cached;
38
124
  }
39
- const manifest = await fetchManifest(config, fetchFn);
40
- manifestCache.set(cacheKey, manifest, config.manifestCacheTtlMs);
41
- return manifest;
125
+ // 2. CDN fetch
126
+ try {
127
+ const manifest = await fetchManifestFromCdn(config, fetchFn);
128
+ manifestCache.set(cacheKey, manifest, config.manifestCacheTtlMs);
129
+ // Write-through to storage (fire-and-forget)
130
+ writeToStorage(config.storage, storageKey, manifest);
131
+ return manifest;
132
+ }
133
+ catch (cdnError) {
134
+ logger.warn("CDN fetch failed, trying fallback sources", cdnError);
135
+ // 3. Persistent storage
136
+ const stored = await readFromStorage(config.storage, storageKey);
137
+ if (stored && Array.isArray(stored.languages)) {
138
+ logger.info("serving manifest from persistent storage (stale)");
139
+ manifestCache.set(cacheKey, stored, config.manifestCacheTtlMs);
140
+ return stored;
141
+ }
142
+ // 4. No fallback available
143
+ throw cdnError;
144
+ }
42
145
  };
146
+ // ─── Messages fetching with fallback ────────────────────────────────
43
147
  /**
44
- * Fetch messages for a locale
148
+ * Fetch messages from CDN (raw, no cache)
45
149
  */
46
- const fetchMessages = async (config, locale, fetchFn) => {
150
+ const fetchMessagesFromCdn = async (config, locale, fetchFn) => {
47
151
  const logger = createLogger(config, "messages");
48
152
  const url = `${getProjectBaseUrl(config)}/${locale}/translations.json`;
49
153
  logger.debug("fetching", url);
50
- const response = await fetchFn(url, {
51
- headers: { "Cache-Control": "no-cache" },
52
- });
154
+ const response = await fetchWithRetry(fetchFn, url, { headers: { "Cache-Control": "no-cache" } }, config.fetchTimeout, config.retryCount);
53
155
  if (!response.ok) {
54
156
  const message = `Messages fetch failed for locale "${locale}" (${response.status})`;
55
157
  logger.error(message);
@@ -59,6 +161,51 @@ const fetchMessages = async (config, locale, fetchFn) => {
59
161
  logger.debug("fetched", { locale, keys: Object.keys(data).length });
60
162
  return data;
61
163
  };
164
+ /**
165
+ * Get messages with full fallback chain:
166
+ * 1. Memory cache (TtlCache)
167
+ * 2. CDN fetch (with timeout + retry)
168
+ * 3. Persistent storage
169
+ * 4. staticData
170
+ * 5. Throw (last resort)
171
+ */
172
+ const getMessagesWithFallback = async (config, locale, fetchFn) => {
173
+ const safeLng = locale.toLowerCase(); // CDN convention: always lowercase
174
+ const logger = createLogger(config, "messages");
175
+ const cacheKey = `${buildCacheKey(config.cdnBaseUrl, config.project)}|${safeLng}`;
176
+ const storageKey = buildMessagesStorageKey(config.project, safeLng);
177
+ // 1. Memory cache
178
+ const memoryCached = messagesCache.get(cacheKey);
179
+ if (memoryCached)
180
+ return memoryCached;
181
+ // 2. CDN fetch
182
+ try {
183
+ const messages = await fetchMessagesFromCdn(config, safeLng, fetchFn);
184
+ messagesCache.set(cacheKey, messages, config.manifestCacheTtlMs);
185
+ // Write-through to storage (fire-and-forget)
186
+ writeToStorage(config.storage, storageKey, messages);
187
+ return messages;
188
+ }
189
+ catch (cdnError) {
190
+ logger.warn(`CDN fetch failed for locale "${safeLng}", trying fallback sources`, cdnError);
191
+ // 3. Persistent storage
192
+ const stored = await readFromStorage(config.storage, storageKey);
193
+ if (stored) {
194
+ logger.info(`serving messages for "${safeLng}" from persistent storage (stale)`);
195
+ messagesCache.set(cacheKey, stored, config.manifestCacheTtlMs);
196
+ return stored;
197
+ }
198
+ // 4. staticData
199
+ const resolved = await resolveStaticData(config.staticData);
200
+ if (resolved && resolved[safeLng]) {
201
+ logger.info(`serving messages for "${safeLng}" from staticData`);
202
+ return resolved[safeLng];
203
+ }
204
+ // 5. No fallback available
205
+ throw cdnError;
206
+ }
207
+ };
208
+ // ─── Public API ─────────────────────────────────────────────────────
62
209
  /**
63
210
  * Create an i18n core instance
64
211
  *
@@ -72,6 +219,19 @@ const fetchMessages = async (config, locale, fetchFn) => {
72
219
  * const messages = await i18n.getMessages('en')
73
220
  * const locales = await i18n.getLocales()
74
221
  * ```
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * // With fallback support
226
+ * const i18n = createI18nCore({
227
+ * project: 'acme/dashboard',
228
+ * defaultLocale: 'en',
229
+ * storage: createAutoStorage(),
230
+ * staticData: { en: { common: { hello: "Hello" } } },
231
+ * fetchTimeout: 5000,
232
+ * retryCount: 2,
233
+ * })
234
+ * ```
75
235
  */
76
236
  export const createI18nCore = (config) => {
77
237
  const normalized = normalizeConfig(config);
@@ -79,7 +239,7 @@ export const createI18nCore = (config) => {
79
239
  return {
80
240
  config: normalized,
81
241
  getManifest: (options) => getManifestWithCache(normalized, fetchFn, options?.forceRefresh),
82
- getMessages: (locale) => fetchMessages(normalized, locale, fetchFn),
242
+ getMessages: (locale) => getMessagesWithFallback(normalized, locale, fetchFn),
83
243
  getLocales: async () => {
84
244
  const manifest = await getManifestWithCache(normalized, fetchFn);
85
245
  const languages = extractLanguages(manifest);
@@ -99,9 +259,15 @@ export const createI18nCore = (config) => {
99
259
  };
100
260
  };
101
261
  /**
102
- * Clear the manifest cache (useful for testing)
262
+ * Clear all caches (useful for testing)
103
263
  */
104
264
  export const clearManifestCache = () => {
105
265
  manifestCache.clear();
106
266
  };
267
+ /**
268
+ * Clear the messages cache (useful for testing)
269
+ */
270
+ export const clearMessagesCache = () => {
271
+ messagesCache.clear();
272
+ };
107
273
  //# sourceMappingURL=cdn.js.map
package/dist/cdn.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cdn.js","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cdn.js","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAW9C,MAAM,cAAc,GAAG,cAAc,CAAC;AAEtC,0CAA0C;AAC1C,MAAM,aAAa,GAAG,IAAI,QAAQ,EAAoB,CAAC;AACvD,MAAM,aAAa,GAAG,IAAI,QAAQ,EAAY,CAAC;AAE/C,uEAAuE;AAEvE,MAAM,uBAAuB,GAAG,CAAC,OAAe,EAAU,EAAE,CAC1D,GAAG,cAAc,aAAa,OAAO,EAAE,CAAC;AAE1C,MAAM,uBAAuB,GAAG,CAAC,OAAe,EAAE,MAAc,EAAU,EAAE,CAC1E,GAAG,cAAc,aAAa,OAAO,IAAI,MAAM,EAAE,CAAC;AAEpD,MAAM,eAAe,GAAG,KAAK,EAC3B,OAAuC,EACvC,GAAW,EACQ,EAAE;IACrB,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,KAAK,EAC1B,OAAuC,EACvC,GAAW,EACX,IAAa,EACE,EAAE;IACjB,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;AACH,CAAC,CAAC;AAEF,uEAAuE;AAEvE;;GAEG;AACH,MAAM,gBAAgB,GAAG,KAAK,EAC5B,OAAqB,EACrB,GAAW,EACX,IAA6B,EAC7B,SAAiB,EACE,EAAE;IACrB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;YAClC,GAAG,IAAI;YACP,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAG,KAAK,EAC1B,OAAqB,EACrB,GAAW,EACX,IAA6B,EAC7B,SAAiB,EACjB,UAAkB,EACC,EAAE;IACrB,IAAI,SAAkB,CAAC;IAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,CAAC;YAChB,kCAAkC;YAClC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,gDAAgD;gBAChD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5B,UAAU,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CACzC,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC;AAClB,CAAC,CAAC;AAEF,uEAAuE;AAEvE,MAAM,iBAAiB,GAAG,KAAK,EAC7B,UAAwC,EACE,EAAE;IAC5C,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,OAAO,MAAM,UAAU,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,uEAAuE;AAEvE;;GAEG;AACH,MAAM,oBAAoB,GAAG,KAAK,EAChC,MAAwB,EACxB,OAAqB,EACM,EAAE;IAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAEzD,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAE9B,MAAM,QAAQ,GAAG,MAAM,cAAc,CACnC,OAAO,EACP,GAAG,EACH,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,EAC5C,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,UAAU,CAClB,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,0BAA0B,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAqB,CAAC;IAEzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG,KAAK,EAChC,MAAwB,EACxB,OAAqB,EACrB,YAAY,GAAG,KAAK,EACO,EAAE;IAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,uBAAuB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE3D,kBAAkB;IAClB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IAED,eAAe;IACf,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7D,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAEjE,6CAA6C;QAC7C,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAErD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,QAAQ,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,QAAQ,CAAC,CAAC;QAEnE,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,eAAe,CAClC,MAAM,CAAC,OAAO,EACd,UAAU,CACX,CAAC;QACF,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;YAChE,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC/D,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,2BAA2B;QAC3B,MAAM,QAAQ,CAAC;IACjB,CAAC;AACH,CAAC,CAAC;AAEF,uEAAuE;AAEvE;;GAEG;AACH,MAAM,oBAAoB,GAAG,KAAK,EAChC,MAAwB,EACxB,MAAc,EACd,OAAqB,EACF,EAAE;IACrB,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,MAAM,oBAAoB,CAAC;IAEvE,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAE9B,MAAM,QAAQ,GAAG,MAAM,cAAc,CACnC,OAAO,EACP,GAAG,EACH,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,EAC5C,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,UAAU,CAClB,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,qCAAqC,MAAM,MAAM,QAAQ,CAAC,MAAM,GAAG,CAAC;QACpF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAa,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpE,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,uBAAuB,GAAG,KAAK,EACnC,MAAwB,EACxB,MAAc,EACd,OAAqB,EACF,EAAE;IACrB,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,mCAAmC;IACzE,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;IAClF,MAAM,UAAU,GAAG,uBAAuB,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEpE,kBAAkB;IAClB,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,eAAe;IACf,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAEjE,6CAA6C;QAC7C,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAErD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,QAAQ,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,gCAAgC,OAAO,4BAA4B,EAAE,QAAQ,CAAC,CAAC;QAE3F,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAW,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC3E,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,mCAAmC,CAAC,CAAC;YACjF,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC/D,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,gBAAgB;QAChB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,mBAAmB,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,2BAA2B;QAC3B,MAAM,QAAQ,CAAC;IACjB,CAAC;AACH,CAAC,CAAC;AAEF,uEAAuE;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAsB,EAAY,EAAE;IACjE,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,IAAI,KAAK,CAAC;IAE1C,OAAO;QACL,MAAM,EAAE,UAAU;QAElB,WAAW,EAAE,CAAC,OAAoC,EAAE,EAAE,CACpD,oBAAoB,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC;QAElE,WAAW,EAAE,CAAC,MAAc,EAAE,EAAE,CAC9B,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;QAEtD,UAAU,EAAE,KAAK,IAAuB,EAAE;YACxC,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjE,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAE7C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAED,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QAED,YAAY,EAAE,KAAK,IAA+B,EAAE;YAClD,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjE,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAE7C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAS,EAAE;IAC3C,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAS,EAAE;IAC3C,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK/E;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,aAW9C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,cAAc,KAAG,gBAiBxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,MACO,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAO/E;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,aAW9C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,cAAc,KAAG,gBAmBxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,MACO,CAAC"}
package/dist/config.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const DEFAULT_CDN_BASE_URL = "https://cdn.better-i18n.com";
2
2
  const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
3
+ const DEFAULT_FETCH_TIMEOUT_MS = 10_000; // 10 seconds
4
+ const DEFAULT_RETRY_COUNT = 1;
3
5
  /**
4
6
  * Parse project string "org/slug" into workspaceId and projectSlug
5
7
  */
@@ -30,6 +32,8 @@ export const normalizeConfig = (config) => {
30
32
  projectSlug,
31
33
  cdnBaseUrl: config.cdnBaseUrl?.replace(/\/$/, "") || DEFAULT_CDN_BASE_URL,
32
34
  manifestCacheTtlMs: config.manifestCacheTtlMs ?? DEFAULT_CACHE_TTL_MS,
35
+ fetchTimeout: config.fetchTimeout ?? DEFAULT_FETCH_TIMEOUT_MS,
36
+ retryCount: config.retryCount ?? DEFAULT_RETRY_COUNT,
33
37
  };
34
38
  };
35
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAC3D,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAExD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAiB,EAAE;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,yCAAyC,OAAO,oDAAoD,CACrG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;QACrB,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;KACtB,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAsB,EAAoB,EAAE;IAC1E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElE,OAAO;QACL,GAAG,MAAM;QACT,WAAW;QACX,WAAW;QACX,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,oBAAoB;QACzE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,oBAAoB;KACtE,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAU,EAAE,CACpE,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAC3D,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AACxD,MAAM,wBAAwB,GAAG,MAAM,CAAC,CAAC,aAAa;AACtD,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAiB,EAAE;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,yCAAyC,OAAO,oDAAoD,CACrG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;QACrB,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;KACtB,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAsB,EAAoB,EAAE;IAC1E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElE,OAAO;QACL,GAAG,MAAM;QACT,WAAW;QACX,WAAW;QACX,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,oBAAoB;QACzE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,oBAAoB;QACrE,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,wBAAwB;QAC7D,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,mBAAmB;KACrD,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAU,EAAE,CACpE,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../src/i18n/detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAE7E;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CA+BvB"}
1
+ {"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../src/i18n/detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAM7E;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAmCvB"}
@@ -1,3 +1,5 @@
1
+ /** Case-insensitive locale lookup — returns the canonical (CDN) form if matched */
2
+ const findLocale = (code, available) => code ? available.find((a) => a.toLowerCase() === code.toLowerCase()) : undefined;
1
3
  /**
2
4
  * Framework-agnostic locale detection logic
3
5
  */
@@ -6,16 +8,19 @@ export function detectLocale(options) {
6
8
  let locale;
7
9
  let detectedFrom;
8
10
  // Priority: path > cookie > header > default
9
- if (pathLocale && availableLocales.includes(pathLocale)) {
10
- locale = pathLocale;
11
+ const pathMatch = findLocale(pathLocale, availableLocales);
12
+ const cookieMatch = findLocale(cookieLocale, availableLocales);
13
+ const headerMatch = findLocale(headerLocale, availableLocales);
14
+ if (pathMatch) {
15
+ locale = pathMatch;
11
16
  detectedFrom = "path";
12
17
  }
13
- else if (cookieLocale && availableLocales.includes(cookieLocale)) {
14
- locale = cookieLocale;
18
+ else if (cookieMatch) {
19
+ locale = cookieMatch;
15
20
  detectedFrom = "cookie";
16
21
  }
17
- else if (headerLocale && availableLocales.includes(headerLocale)) {
18
- locale = headerLocale;
22
+ else if (headerMatch) {
23
+ locale = headerMatch;
19
24
  detectedFrom = "header";
20
25
  }
21
26
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"detection.js","sourceRoot":"","sources":["../../src/i18n/detection.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,OAA+B;IAE/B,MAAM,EACJ,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,gBAAgB,GACjB,GAAG,OAAO,CAAC;IAEZ,IAAI,MAAc,CAAC;IACnB,IAAI,YAAmD,CAAC;IAExD,6CAA6C;IAC7C,IAAI,UAAU,IAAI,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACxD,MAAM,GAAG,UAAU,CAAC;QACpB,YAAY,GAAG,MAAM,CAAC;IACxB,CAAC;SAAM,IAAI,YAAY,IAAI,gBAAgB,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACnE,MAAM,GAAG,YAAY,CAAC;QACtB,YAAY,GAAG,QAAQ,CAAC;IAC1B,CAAC;SAAM,IAAI,YAAY,IAAI,gBAAgB,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACnE,MAAM,GAAG,YAAY,CAAC;QACtB,YAAY,GAAG,QAAQ,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,aAAa,CAAC;QACvB,YAAY,GAAG,SAAS,CAAC;IAC3B,CAAC;IAED,0DAA0D;IAC1D,MAAM,eAAe,GAAG,YAAY,KAAK,MAAM,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"detection.js","sourceRoot":"","sources":["../../src/i18n/detection.ts"],"names":[],"mappings":"AAEA,mFAAmF;AACnF,MAAM,UAAU,GAAG,CAAC,IAA+B,EAAE,SAAmB,EAAE,EAAE,CAC1E,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAEnF;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,OAA+B;IAE/B,MAAM,EACJ,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,gBAAgB,GACjB,GAAG,OAAO,CAAC;IAEZ,IAAI,MAAc,CAAC;IACnB,IAAI,YAAmD,CAAC;IAExD,6CAA6C;IAC7C,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,UAAU,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,UAAU,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;IAE/D,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,GAAG,SAAS,CAAC;QACnB,YAAY,GAAG,MAAM,CAAC;IACxB,CAAC;SAAM,IAAI,WAAW,EAAE,CAAC;QACvB,MAAM,GAAG,WAAW,CAAC;QACrB,YAAY,GAAG,QAAQ,CAAC;IAC1B,CAAC;SAAM,IAAI,WAAW,EAAE,CAAC;QACvB,MAAM,GAAG,WAAW,CAAC;QACrB,YAAY,GAAG,QAAQ,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,aAAa,CAAC;QACvB,YAAY,GAAG,SAAS,CAAC;IAC3B,CAAC;IAED,0DAA0D;IAC1D,MAAM,eAAe,GAAG,YAAY,KAAK,MAAM,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AACnD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export { createI18nCore, clearManifestCache } from "./cdn";
1
+ export { createI18nCore, clearManifestCache, clearMessagesCache } from "./cdn";
2
+ export { createAutoStorage, createLocalStorage, createMemoryStorage } from "./storage";
2
3
  export { normalizeConfig, parseProject, getProjectBaseUrl } from "./config";
3
4
  export { extractLanguages } from "./manifest";
4
5
  export { createLogger } from "./logger";
5
6
  export { detectLocale } from "./i18n/detection";
6
7
  export { TtlCache, buildCacheKey } from "./cache";
7
8
  export { extractLocale, getLocaleFromPath, hasLocalePrefix, removeLocalePrefix, addLocalePrefix, replaceLocaleInPath, createLocalePath, type LocaleConfig, } from "./utils/locale";
8
- export type { I18nCoreConfig, NormalizedConfig, ParsedProject, ManifestResponse, ManifestLanguage, ManifestFile, LanguageOption, Messages, Locale, I18nCore, Logger, LogLevel, CacheEntry, } from "./types";
9
+ export type { I18nCoreConfig, NormalizedConfig, ParsedProject, ManifestResponse, ManifestLanguage, ManifestFile, LanguageOption, Messages, Locale, I18nCore, TranslationStorage, Logger, LogLevel, CacheEntry, } from "./types";
9
10
  export type { I18nMiddlewareConfig, LocaleDetectionOptions, LocaleDetectionResult, LocalePrefix, } from "./i18n/types";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAG3D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAEV,cAAc,EACd,gBAAgB,EAChB,aAAa,EAGb,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EAGd,QAAQ,EACR,MAAM,EAGN,QAAQ,EAGR,MAAM,EACN,QAAQ,EACR,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,GACb,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAG/E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGvF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAEV,cAAc,EACd,gBAAgB,EAChB,aAAa,EAGb,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EAGd,QAAQ,EACR,MAAM,EAGN,QAAQ,EAGR,kBAAkB,EAGlB,MAAM,EACN,QAAQ,EACR,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,GACb,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // Main API
2
- export { createI18nCore, clearManifestCache } from "./cdn";
2
+ export { createI18nCore, clearManifestCache, clearMessagesCache } from "./cdn";
3
+ // Storage adapters
4
+ export { createAutoStorage, createLocalStorage, createMemoryStorage } from "./storage";
3
5
  // Configuration utilities
4
6
  export { normalizeConfig, parseProject, getProjectBaseUrl } from "./config";
5
7
  // Manifest utilities
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE3D,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE5E,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAElD,oEAAoE;AACpE,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE/E,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEvF,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE5E,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAElD,oEAAoE;AACpE,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { TranslationStorage } from "../types";
2
+ export { createLocalStorage } from "./localStorage";
3
+ export { createMemoryStorage } from "./memory";
4
+ /**
5
+ * Auto-detect the best available storage.
6
+ * Uses localStorage if available (browser), otherwise falls back to in-memory.
7
+ */
8
+ export declare const createAutoStorage: () => TranslationStorage;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAInD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAU/C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAAO,kBAcpC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { createLocalStorage } from "./localStorage";
2
+ import { createMemoryStorage } from "./memory";
3
+ export { createLocalStorage } from "./localStorage";
4
+ export { createMemoryStorage } from "./memory";
5
+ /**
6
+ * Auto-detect the best available storage.
7
+ * Uses localStorage if available (browser), otherwise falls back to in-memory.
8
+ */
9
+ export const createAutoStorage = () => {
10
+ try {
11
+ const storage = globalThis.localStorage;
12
+ if (storage) {
13
+ // Test that localStorage actually works (may throw in private browsing, iframes, etc.)
14
+ const testKey = "__better_i18n_test__";
15
+ storage.setItem(testKey, "1");
16
+ storage.removeItem(testKey);
17
+ return createLocalStorage();
18
+ }
19
+ }
20
+ catch {
21
+ // localStorage not available or not writable
22
+ }
23
+ return createMemoryStorage();
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAU/C;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAuB,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAI,UAA4C,CAAC,YAAY,CAAC;QAC3E,IAAI,OAAO,EAAE,CAAC;YACZ,uFAAuF;YACvF,MAAM,OAAO,GAAG,sBAAsB,CAAC;YACvC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;IAC/C,CAAC;IACD,OAAO,mBAAmB,EAAE,CAAC;AAC/B,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { TranslationStorage } from "../types";
2
+ /**
3
+ * Create a storage adapter backed by browser localStorage.
4
+ * All operations are wrapped in try/catch to handle environments
5
+ * where localStorage is unavailable or full (quota exceeded).
6
+ */
7
+ export declare const createLocalStorage: () => TranslationStorage;
8
+ //# sourceMappingURL=localStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localStorage.d.ts","sourceRoot":"","sources":["../../src/storage/localStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAkBnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,kBAsBpC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Access localStorage via globalThis to work in any environment.
3
+ */
4
+ const getStorage = () => globalThis.localStorage;
5
+ /**
6
+ * Create a storage adapter backed by browser localStorage.
7
+ * All operations are wrapped in try/catch to handle environments
8
+ * where localStorage is unavailable or full (quota exceeded).
9
+ */
10
+ export const createLocalStorage = () => ({
11
+ get: async (key) => {
12
+ try {
13
+ return getStorage()?.getItem(key) ?? null;
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ },
19
+ set: async (key, value) => {
20
+ try {
21
+ getStorage()?.setItem(key, value);
22
+ }
23
+ catch {
24
+ // Storage full or unavailable — silently fail
25
+ }
26
+ },
27
+ remove: async (key) => {
28
+ try {
29
+ getStorage()?.removeItem(key);
30
+ }
31
+ catch {
32
+ // Silently fail
33
+ }
34
+ },
35
+ });
36
+ //# sourceMappingURL=localStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localStorage.js","sourceRoot":"","sources":["../../src/storage/localStorage.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,UAAU,GAAG,GAA2B,EAAE,CAC7C,UAA4C,CAAC,YAAY,CAAC;AAE7D;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAuB,EAAE,CAAC,CAAC;IAC3D,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,OAAO,UAAU,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,UAAU,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;IACH,CAAC;IACD,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACpB,IAAI,CAAC;YACH,UAAU,EAAE,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB;QAClB,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { TranslationStorage } from "../types";
2
+ /**
3
+ * Create an in-memory storage adapter backed by a Map.
4
+ * Useful as a fallback when no persistent storage is available (e.g., SSR).
5
+ */
6
+ export declare const createMemoryStorage: () => TranslationStorage;
7
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO,kBAWtC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Create an in-memory storage adapter backed by a Map.
3
+ * Useful as a fallback when no persistent storage is available (e.g., SSR).
4
+ */
5
+ export const createMemoryStorage = () => {
6
+ const store = new Map();
7
+ return {
8
+ get: async (key) => store.get(key) ?? null,
9
+ set: async (key, value) => {
10
+ store.set(key, value);
11
+ },
12
+ remove: async (key) => {
13
+ store.delete(key);
14
+ },
15
+ };
16
+ };
17
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAuB,EAAE;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,OAAO;QACL,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI;QAC1C,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;YACxB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
package/dist/types.d.ts CHANGED
@@ -6,6 +6,15 @@ export type Locale = string;
6
6
  * Log level for debugging
7
7
  */
8
8
  export type LogLevel = "debug" | "info" | "warn" | "error" | "silent";
9
+ /**
10
+ * Pluggable storage interface for persistent translation caching.
11
+ * Compatible with browser localStorage, AsyncStorage, MMKV, or any key-value store.
12
+ */
13
+ export interface TranslationStorage {
14
+ get(key: string): Promise<string | null>;
15
+ set(key: string, value: string): Promise<void>;
16
+ remove?(key: string): Promise<void>;
17
+ }
9
18
  /**
10
19
  * Core i18n configuration
11
20
  */
@@ -42,6 +51,38 @@ export interface I18nCoreConfig {
42
51
  * Custom fetch function (useful for testing or custom environments)
43
52
  */
44
53
  fetch?: typeof fetch;
54
+ /**
55
+ * Persistent storage adapter for offline fallback.
56
+ * When CDN is unavailable, translations are served from storage.
57
+ */
58
+ storage?: TranslationStorage;
59
+ /**
60
+ * Bundled/static translations as last-resort fallback.
61
+ * Used when both CDN and storage are unavailable.
62
+ * Can be a static object or a lazy-loading function.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // Static import
67
+ * staticData: { en: { common: { hello: "Hello" } } }
68
+ *
69
+ * // Lazy import
70
+ * staticData: () => import('./fallback-translations.json')
71
+ * ```
72
+ */
73
+ staticData?: Record<string, Messages> | (() => Promise<Record<string, Messages>>);
74
+ /**
75
+ * CDN fetch timeout in milliseconds.
76
+ * If a fetch does not complete within this time, the request is aborted
77
+ * and fallback sources are tried.
78
+ * @default 10000
79
+ */
80
+ fetchTimeout?: number;
81
+ /**
82
+ * Number of retry attempts on CDN fetch failure.
83
+ * @default 1
84
+ */
85
+ retryCount?: number;
45
86
  }
46
87
  /**
47
88
  * Parsed project identifier
@@ -56,6 +97,8 @@ export interface ParsedProject {
56
97
  export interface NormalizedConfig extends I18nCoreConfig, ParsedProject {
57
98
  cdnBaseUrl: string;
58
99
  manifestCacheTtlMs: number;
100
+ fetchTimeout: number;
101
+ retryCount: number;
59
102
  }
60
103
  /**
61
104
  * Language information from manifest
@@ -101,9 +144,10 @@ export interface LanguageOption {
101
144
  isDefault?: boolean;
102
145
  }
103
146
  /**
104
- * Translation messages (flat or nested key-value pairs)
147
+ * Translation messages keyed by namespace.
148
+ * Matches the CDN response format: `{ "common": { "key": "value" }, "auth": { ... } }`
105
149
  */
106
- export type Messages = Record<string, unknown>;
150
+ export type Messages = Record<string, Record<string, any>>;
107
151
  /**
108
152
  * Logger interface
109
153
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAErB;;;OAGG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAE7B;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElF;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,cAAc,EAAE,aAAa;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACpC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,MAAM,EAAE,gBAAgB,CAAC;IAEzB;;OAEG;IACH,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEjF;;OAEG;IACH,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEnD;;OAEG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpC;;OAEG;IACH,YAAY,EAAE,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CAC/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-i18n/core",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Framework-agnostic core utilities for Better i18n",
5
5
  "license": "MIT",
6
6
  "repository": {