@better-i18n/core 0.1.8 → 0.1.9
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 +67 -4
- package/dist/__tests__/cdn-fallback.test.d.ts +2 -0
- package/dist/__tests__/cdn-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/cdn-fallback.test.js +380 -0
- package/dist/__tests__/cdn-fallback.test.js.map +1 -0
- package/dist/cdn.d.ts +18 -1
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +182 -17
- package/dist/cdn.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +25 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/localStorage.d.ts +8 -0
- package/dist/storage/localStorage.d.ts.map +1 -0
- package/dist/storage/localStorage.js +36 -0
- package/dist/storage/localStorage.js.map +1 -0
- package/dist/storage/memory.d.ts +7 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +17 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/types.d.ts +46 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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 @@
|
|
|
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
|
|
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,
|
|
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;AAoSjB;;;;;;;;;;;;;;;;;;;;;;;;;;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
|
-
|
|
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
|
|
37
|
+
* Fetch with AbortController-based timeout
|
|
9
38
|
*/
|
|
10
|
-
const
|
|
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
|
|
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
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
148
|
+
* Fetch messages from CDN (raw, no cache)
|
|
45
149
|
*/
|
|
46
|
-
const
|
|
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
|
|
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,50 @@ 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 logger = createLogger(config, "messages");
|
|
174
|
+
const cacheKey = `${buildCacheKey(config.cdnBaseUrl, config.project)}|${locale}`;
|
|
175
|
+
const storageKey = buildMessagesStorageKey(config.project, locale);
|
|
176
|
+
// 1. Memory cache
|
|
177
|
+
const memoryCached = messagesCache.get(cacheKey);
|
|
178
|
+
if (memoryCached)
|
|
179
|
+
return memoryCached;
|
|
180
|
+
// 2. CDN fetch
|
|
181
|
+
try {
|
|
182
|
+
const messages = await fetchMessagesFromCdn(config, locale, fetchFn);
|
|
183
|
+
messagesCache.set(cacheKey, messages, config.manifestCacheTtlMs);
|
|
184
|
+
// Write-through to storage (fire-and-forget)
|
|
185
|
+
writeToStorage(config.storage, storageKey, messages);
|
|
186
|
+
return messages;
|
|
187
|
+
}
|
|
188
|
+
catch (cdnError) {
|
|
189
|
+
logger.warn(`CDN fetch failed for locale "${locale}", trying fallback sources`, cdnError);
|
|
190
|
+
// 3. Persistent storage
|
|
191
|
+
const stored = await readFromStorage(config.storage, storageKey);
|
|
192
|
+
if (stored) {
|
|
193
|
+
logger.info(`serving messages for "${locale}" from persistent storage (stale)`);
|
|
194
|
+
messagesCache.set(cacheKey, stored, config.manifestCacheTtlMs);
|
|
195
|
+
return stored;
|
|
196
|
+
}
|
|
197
|
+
// 4. staticData
|
|
198
|
+
const resolved = await resolveStaticData(config.staticData);
|
|
199
|
+
if (resolved && resolved[locale]) {
|
|
200
|
+
logger.info(`serving messages for "${locale}" from staticData`);
|
|
201
|
+
return resolved[locale];
|
|
202
|
+
}
|
|
203
|
+
// 5. No fallback available
|
|
204
|
+
throw cdnError;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
// ─── Public API ─────────────────────────────────────────────────────
|
|
62
208
|
/**
|
|
63
209
|
* Create an i18n core instance
|
|
64
210
|
*
|
|
@@ -72,6 +218,19 @@ const fetchMessages = async (config, locale, fetchFn) => {
|
|
|
72
218
|
* const messages = await i18n.getMessages('en')
|
|
73
219
|
* const locales = await i18n.getLocales()
|
|
74
220
|
* ```
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* // With fallback support
|
|
225
|
+
* const i18n = createI18nCore({
|
|
226
|
+
* project: 'acme/dashboard',
|
|
227
|
+
* defaultLocale: 'en',
|
|
228
|
+
* storage: createAutoStorage(),
|
|
229
|
+
* staticData: { en: { common: { hello: "Hello" } } },
|
|
230
|
+
* fetchTimeout: 5000,
|
|
231
|
+
* retryCount: 2,
|
|
232
|
+
* })
|
|
233
|
+
* ```
|
|
75
234
|
*/
|
|
76
235
|
export const createI18nCore = (config) => {
|
|
77
236
|
const normalized = normalizeConfig(config);
|
|
@@ -79,7 +238,7 @@ export const createI18nCore = (config) => {
|
|
|
79
238
|
return {
|
|
80
239
|
config: normalized,
|
|
81
240
|
getManifest: (options) => getManifestWithCache(normalized, fetchFn, options?.forceRefresh),
|
|
82
|
-
getMessages: (locale) =>
|
|
241
|
+
getMessages: (locale) => getMessagesWithFallback(normalized, locale, fetchFn),
|
|
83
242
|
getLocales: async () => {
|
|
84
243
|
const manifest = await getManifestWithCache(normalized, fetchFn);
|
|
85
244
|
const languages = extractLanguages(manifest);
|
|
@@ -99,9 +258,15 @@ export const createI18nCore = (config) => {
|
|
|
99
258
|
};
|
|
100
259
|
};
|
|
101
260
|
/**
|
|
102
|
-
* Clear
|
|
261
|
+
* Clear all caches (useful for testing)
|
|
103
262
|
*/
|
|
104
263
|
export const clearManifestCache = () => {
|
|
105
264
|
manifestCache.clear();
|
|
106
265
|
};
|
|
266
|
+
/**
|
|
267
|
+
* Clear the messages cache (useful for testing)
|
|
268
|
+
*/
|
|
269
|
+
export const clearMessagesCache = () => {
|
|
270
|
+
messagesCache.clear();
|
|
271
|
+
};
|
|
107
272
|
//# 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":"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;
|
|
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,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,MAAM,EAAE,CAAC;IACjF,MAAM,UAAU,GAAG,uBAAuB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEnE,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,MAAM,EAAE,OAAO,CAAC,CAAC;QACrE,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,MAAM,4BAA4B,EAAE,QAAQ,CAAC,CAAC;QAE1F,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,MAAM,mCAAmC,CAAC,CAAC;YAChF,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,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,yBAAyB,MAAM,mBAAmB,CAAC,CAAC;YAChE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1B,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"}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
/**
|
package/dist/config.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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
|
|
147
|
+
* Translation messages keyed by namespace.
|
|
148
|
+
* Matches the CDN response format: `{ "common": { "key": "value" }, "auth": { ... } }`
|
|
105
149
|
*/
|
|
106
|
-
export type Messages = Record<string,
|
|
150
|
+
export type Messages = Record<string, Record<string, any>>;
|
|
107
151
|
/**
|
|
108
152
|
* Logger interface
|
|
109
153
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;;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;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|