@sveltekit-i18n/base 3.0.0-next.4 → 3.0.1
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 +1 -1
- package/dist/I18n.svelte.d.ts +8 -3
- package/dist/I18n.svelte.js +14 -5
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -328,7 +328,7 @@ Load-triggering methods return the promise of the matching load — concurrent d
|
|
|
328
328
|
- `setRoute(route)` – update the current route
|
|
329
329
|
- `loadConfig(config)` – (re)configure the instance
|
|
330
330
|
- `addTranslations(translations)` – add synchronous translations
|
|
331
|
-
- `snapshot()` – serialize the active locale (and the fallback) for the current route, shaped like `config.translations` so the receiving instance hydrates
|
|
331
|
+
- `snapshot()` – serialize the active locale (and the fallback) for the current route, shaped like `config.translations` so the receiving instance hydrates by passing it to `addTranslations()`
|
|
332
332
|
- `invalidate(locale?)` – mark loaded translations stale (one locale, or all); loaders run again on the next load trigger, and a load still in flight for an invalidated locale settles with its data discarded
|
|
333
333
|
- `destroy()` – detach a per-request or per-component instance: in-flight loads settle discarded, further load and mutation calls are ignored, reads keep working
|
|
334
334
|
|
package/dist/I18n.svelte.d.ts
CHANGED
|
@@ -46,9 +46,14 @@ declare class I18nCore<ParserParams extends Parser.Params = any, ParserOutput =
|
|
|
46
46
|
* Serializes what this instance holds for the active locale and the fallback
|
|
47
47
|
* locale, narrowed to the current route: a key owned only by loaders that do
|
|
48
48
|
* not match the route is left out. The result is shaped like
|
|
49
|
-
* `config.translations`, so a client hydrates by
|
|
50
|
-
*
|
|
51
|
-
* loaders from fetching the same data again.
|
|
49
|
+
* `config.translations`, so a client hydrates by passing it to
|
|
50
|
+
* `addTranslations()` — the bookkeeping derived from it then keeps the
|
|
51
|
+
* matching loaders from fetching the same data again. Apply it to the
|
|
52
|
+
* instance rather than assigning it to `config.translations`: the payload is
|
|
53
|
+
* a subset — two locales, and nothing of a key its loaders claim for another
|
|
54
|
+
* route — so assigning it would drop the rest of the config's own data.
|
|
55
|
+
* A literal `__proto__` key is left out: the serializer SvelteKit hands load
|
|
56
|
+
* data to refuses an object that carries one.
|
|
52
57
|
*/
|
|
53
58
|
snapshot: () => Translations.SerializedTranslations;
|
|
54
59
|
/**
|
package/dist/I18n.svelte.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fetchTranslations, hasOwn, mergeTranslations, read, resolveLoaders, sanitizerFactory, sanitizeTranslationLocales, testRoute, toDotNotation, translate } from './utils.js';
|
|
1
|
+
import { fetchTranslations, hasOwn, mergeTranslations, omitProtoKeys, read, resolveLoaders, sanitizerFactory, sanitizeTranslationLocales, testRoute, toDotNotation, translate } from './utils.js';
|
|
2
2
|
import { logError, logger, loggerFactory, setLogger } from './logger.js';
|
|
3
3
|
const defaultCache = Number.POSITIVE_INFINITY;
|
|
4
4
|
class I18nCore {
|
|
@@ -211,9 +211,14 @@ class I18nCore {
|
|
|
211
211
|
* Serializes what this instance holds for the active locale and the fallback
|
|
212
212
|
* locale, narrowed to the current route: a key owned only by loaders that do
|
|
213
213
|
* not match the route is left out. The result is shaped like
|
|
214
|
-
* `config.translations`, so a client hydrates by
|
|
215
|
-
*
|
|
216
|
-
* loaders from fetching the same data again.
|
|
214
|
+
* `config.translations`, so a client hydrates by passing it to
|
|
215
|
+
* `addTranslations()` — the bookkeeping derived from it then keeps the
|
|
216
|
+
* matching loaders from fetching the same data again. Apply it to the
|
|
217
|
+
* instance rather than assigning it to `config.translations`: the payload is
|
|
218
|
+
* a subset — two locales, and nothing of a key its loaders claim for another
|
|
219
|
+
* route — so assigning it would drop the rest of the config's own data.
|
|
220
|
+
* A literal `__proto__` key is left out: the serializer SvelteKit hands load
|
|
221
|
+
* data to refuses an object that carries one.
|
|
217
222
|
*/
|
|
218
223
|
snapshot = () => {
|
|
219
224
|
const { fallbackLocale } = this.#config ?? {};
|
|
@@ -228,9 +233,13 @@ class I18nCore {
|
|
|
228
233
|
if (!data)
|
|
229
234
|
return acc;
|
|
230
235
|
const offRoute = this.#offRouteKeys(locale, route);
|
|
231
|
-
const
|
|
236
|
+
const onRoute = Object.keys(data)
|
|
232
237
|
.filter((key) => !offRoute.has(key))
|
|
233
238
|
.reduce((keep, key) => ({ ...keep, [key]: read(data, key) }), {});
|
|
239
|
+
const relevant = omitProtoKeys(onRoute);
|
|
240
|
+
if (relevant !== onRoute) {
|
|
241
|
+
logger.warn(`Leaving a '__proto__' key of locale '${locale}' out of the snapshot: load data cannot carry it.`);
|
|
242
|
+
}
|
|
234
243
|
// An empty entry would still stamp the locale's freshness on the client,
|
|
235
244
|
// starting its `cache` window on data it never received.
|
|
236
245
|
if (!Object.keys(relevant).length)
|
package/dist/utils.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare const sanitizeTranslationLocales: (input: Translations.Serialized
|
|
|
17
17
|
export declare const toDotNotation: DotNotation.T;
|
|
18
18
|
export declare const resolveLoaders: (input?: readonly Loader.LoaderModule[]) => Loader.LoaderModule[];
|
|
19
19
|
export declare const mergeTranslations: (target: any, source: any, path: string, onConflict?: (path: string) => void) => any;
|
|
20
|
+
export declare const omitProtoKeys: (value: any) => any;
|
|
20
21
|
export declare const serialize: (input: Array<Loader.LoaderModule & {
|
|
21
22
|
data: any;
|
|
22
23
|
}>) => Translations.SerializedTranslations;
|
package/dist/utils.js
CHANGED
|
@@ -185,6 +185,27 @@ export const mergeTranslations = (target, source, path, onConflict) => {
|
|
|
185
185
|
[key]: hasOwn(acc, key) ? mergeTranslations(read(acc, key), read(source, key), `${path}.${key}`, onConflict) : read(source, key),
|
|
186
186
|
}), target);
|
|
187
187
|
};
|
|
188
|
+
const isPlainObject = (value) => {
|
|
189
|
+
if (!value || typeof value !== 'object')
|
|
190
|
+
return false;
|
|
191
|
+
const proto = Object.getPrototypeOf(value);
|
|
192
|
+
return proto === Object.prototype || proto === null;
|
|
193
|
+
};
|
|
194
|
+
// devalue, which SvelteKit serializes load data with, refuses an object with
|
|
195
|
+
// an own '__proto__' key. Branches without one are returned as they are.
|
|
196
|
+
export const omitProtoKeys = (value) => {
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
const items = value.map(omitProtoKeys);
|
|
199
|
+
return items.some((item, i) => item !== value[i]) ? items : value;
|
|
200
|
+
}
|
|
201
|
+
if (!isPlainObject(value))
|
|
202
|
+
return value;
|
|
203
|
+
const keys = Object.keys(value);
|
|
204
|
+
const entries = keys.filter((key) => key !== '__proto__').map((key) => [key, omitProtoKeys(value[key])]);
|
|
205
|
+
if (entries.length === keys.length && entries.every(([key, item]) => item === value[key]))
|
|
206
|
+
return value;
|
|
207
|
+
return entries.reduce((acc, [key, item]) => ({ ...acc, [key]: item }), {});
|
|
208
|
+
};
|
|
188
209
|
const reportLoaderConflict = (path) => {
|
|
189
210
|
logger.warn(`Conflicting translations for '${path}'. Keeping the value of the last loader.`);
|
|
190
211
|
};
|
package/package.json
CHANGED