@bleedingdev/modern-js-renderer-octane 0.0.0-stage → 3.9.3-ultramodern.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/LICENSE +21 -0
- package/bin/octane-tsc.mjs +4 -0
- package/dist/cjs/bootstrap.js +68 -0
- package/dist/cjs/client.js +421 -0
- package/dist/cjs/entry-application.js +88 -0
- package/dist/cjs/entry-client.js +95 -0
- package/dist/cjs/entry-server.js +135 -0
- package/dist/cjs/federation-context.js +72 -0
- package/dist/cjs/federation.js +205 -0
- package/dist/cjs/i18n/I18nProvider.js +59 -0
- package/dist/cjs/i18n/LocalizedLink.js +80 -0
- package/dist/cjs/i18n/context.js +41 -0
- package/dist/cjs/i18n/index.js +83 -0
- package/dist/cjs/i18n/languageSync.js +99 -0
- package/dist/cjs/i18n/types.js +18 -0
- package/dist/cjs/i18n/useI18n.js +110 -0
- package/dist/cjs/i18n-external-snapshot.js +645 -0
- package/dist/cjs/index.js +46 -0
- package/dist/cjs/manifest.js +167 -0
- package/dist/cjs/plugin/compiler.js +122 -0
- package/dist/cjs/plugin/create.js +266 -0
- package/dist/cjs/plugin/index.js +151 -0
- package/dist/cjs/plugin/profile.js +91 -0
- package/dist/cjs/plugin/source.js +56 -0
- package/dist/cjs/router-client.js +139 -0
- package/dist/cjs/router-external-snapshot.js +291 -0
- package/dist/cjs/router-handler.js +171 -0
- package/dist/cjs/router-server-cleanup.js +46 -0
- package/dist/cjs/router-server-snapshot.js +405 -0
- package/dist/cjs/router-server.js +100 -0
- package/dist/cjs/router-snapshot.js +270 -0
- package/dist/cjs/router.js +218 -0
- package/dist/cjs/routes.js +190 -0
- package/dist/cjs/server.js +245 -0
- package/dist/cjs/typecheck.js +615 -0
- package/dist/cjs/worker-federation-context.js +63 -0
- package/dist/cjs/worker-federation-contexts.js +97 -0
- package/dist/cjs/worker-federation.js +97 -0
- package/dist/esm/bootstrap.mjs +24 -0
- package/dist/esm/client.mjs +377 -0
- package/dist/esm/entry-application.mjs +44 -0
- package/dist/esm/entry-client.mjs +57 -0
- package/dist/esm/entry-server.mjs +91 -0
- package/dist/esm/federation-context.mjs +25 -0
- package/dist/esm/federation.mjs +167 -0
- package/dist/esm/i18n/I18nProvider.mjs +21 -0
- package/dist/esm/i18n/LocalizedLink.mjs +42 -0
- package/dist/esm/i18n/context.mjs +3 -0
- package/dist/esm/i18n/index.mjs +8 -0
- package/dist/esm/i18n/languageSync.mjs +61 -0
- package/dist/esm/i18n/types.mjs +0 -0
- package/dist/esm/i18n/useI18n.mjs +72 -0
- package/dist/esm/i18n-external-snapshot.mjs +576 -0
- package/dist/esm/index.mjs +8 -0
- package/dist/esm/manifest.mjs +117 -0
- package/dist/esm/router-client.mjs +98 -0
- package/dist/esm/router-external-snapshot.mjs +244 -0
- package/dist/esm/router-handler.mjs +133 -0
- package/dist/esm/router-server-cleanup.mjs +8 -0
- package/dist/esm/router-server-snapshot.mjs +367 -0
- package/dist/esm/router-server.mjs +11 -0
- package/dist/esm/router-snapshot.mjs +199 -0
- package/dist/esm/router.mjs +94 -0
- package/dist/esm/routes.mjs +134 -0
- package/dist/esm/server.mjs +204 -0
- package/dist/esm/worker-federation-context.mjs +19 -0
- package/dist/esm/worker-federation-contexts.mjs +47 -0
- package/dist/esm/worker-federation.mjs +59 -0
- package/dist/esm-node/bootstrap.mjs +24 -0
- package/dist/esm-node/manifest.mjs +117 -0
- package/dist/esm-node/plugin/compiler.mjs +67 -0
- package/dist/esm-node/plugin/create.mjs +228 -0
- package/dist/esm-node/plugin/index.mjs +103 -0
- package/dist/esm-node/plugin/profile.mjs +53 -0
- package/dist/esm-node/plugin/source.mjs +7 -0
- package/dist/esm-node/typecheck.mjs +560 -0
- package/dist/types/bootstrap.d.ts +10 -0
- package/dist/types/client.d.ts +40 -0
- package/dist/types/entry-application.d.ts +19 -0
- package/dist/types/entry-client.d.ts +10 -0
- package/dist/types/entry-server.d.ts +14 -0
- package/dist/types/federation-context.d.ts +28 -0
- package/dist/types/federation.d.ts +20 -0
- package/dist/types/i18n/I18nProvider.d.ts +8 -0
- package/dist/types/i18n/LocalizedLink.d.ts +14 -0
- package/dist/types/i18n/context.d.ts +9 -0
- package/dist/types/i18n/index.d.ts +9 -0
- package/dist/types/i18n/languageSync.d.ts +26 -0
- package/dist/types/i18n/types.d.ts +34 -0
- package/dist/types/i18n/useI18n.d.ts +21 -0
- package/dist/types/i18n-external-snapshot.d.ts +43 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/manifest.d.ts +34 -0
- package/dist/types/plugin/compiler-manifest.d.cts +10 -0
- package/dist/types/plugin/compiler.d.ts +5 -0
- package/dist/types/plugin/create.d.ts +23 -0
- package/dist/types/plugin/index.d.ts +3 -0
- package/dist/types/plugin/profile.d.ts +2 -0
- package/dist/types/plugin/source.d.ts +1 -0
- package/dist/types/plugin/svg-component-template.d.cts +6 -0
- package/dist/types/router-client.d.ts +12 -0
- package/dist/types/router-external-snapshot.d.ts +10 -0
- package/dist/types/router-handler.d.ts +13 -0
- package/dist/types/router-server-cleanup.d.ts +3 -0
- package/dist/types/router-server-snapshot.d.ts +15 -0
- package/dist/types/router-server.d.ts +11 -0
- package/dist/types/router-snapshot.d.ts +14 -0
- package/dist/types/router.d.ts +30 -0
- package/dist/types/routes.d.ts +20 -0
- package/dist/types/server.d.ts +47 -0
- package/dist/types/typecheck.d.ts +31 -0
- package/dist/types/worker-federation-context.d.ts +34 -0
- package/dist/types/worker-federation-contexts.d.ts +25 -0
- package/dist/types/worker-federation.d.ts +20 -0
- package/package.json +278 -4
- package/src/bootstrap.ts +60 -0
- package/src/client.ts +568 -0
- package/src/entry-application.ts +76 -0
- package/src/entry-client.ts +90 -0
- package/src/entry-server.ts +153 -0
- package/src/federation-context.ts +53 -0
- package/src/federation.ts +307 -0
- package/src/i18n/I18nProvider.ts +47 -0
- package/src/i18n/LocalizedLink.ts +86 -0
- package/src/i18n/context.ts +12 -0
- package/src/i18n/index.ts +31 -0
- package/src/i18n/languageSync.ts +132 -0
- package/src/i18n/types.ts +36 -0
- package/src/i18n/useI18n.ts +119 -0
- package/src/i18n-external-snapshot.ts +803 -0
- package/src/index.ts +10 -0
- package/src/manifest.ts +253 -0
- package/src/plugin/compiler-manifest.cjs +204 -0
- package/src/plugin/compiler.ts +104 -0
- package/src/plugin/create.ts +273 -0
- package/src/plugin/index.ts +107 -0
- package/src/plugin/profile.ts +50 -0
- package/src/plugin/source.ts +16 -0
- package/src/plugin/svg-component-template.cjs +17 -0
- package/src/router-client.ts +144 -0
- package/src/router-external-snapshot.ts +363 -0
- package/src/router-handler.ts +185 -0
- package/src/router-server-cleanup.ts +11 -0
- package/src/router-server-snapshot.ts +564 -0
- package/src/router-server.ts +21 -0
- package/src/router-snapshot.ts +251 -0
- package/src/router.ts +192 -0
- package/src/routes.ts +274 -0
- package/src/server.ts +340 -0
- package/src/typecheck.ts +723 -0
- package/src/worker-federation-context.ts +51 -0
- package/src/worker-federation-contexts.ts +91 -0
- package/src/worker-federation.ts +120 -0
- package/README.md +0 -3
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
import { createNativeI18n, createNativeI18nInstance, getNativeI18nRewriteSnapshot, getNativeI18nSnapshotSeed, invalidateNativeI18nSnapshot } from "@modern-js/i18n-runtime-extensions/native";
|
|
2
|
+
import i18next from "i18next";
|
|
3
|
+
import { I18nContext } from "./i18n/context.mjs";
|
|
4
|
+
import { OCTANE_WORKER_CONTEXT_KEYS, registerOctaneWorkerContext, registerOctaneWorkerRouterRewrite } from "./worker-federation-contexts.mjs";
|
|
5
|
+
const defaultProfile = {
|
|
6
|
+
key: 'ultramodern.i18next',
|
|
7
|
+
version: '1'
|
|
8
|
+
};
|
|
9
|
+
const profiles = new Map();
|
|
10
|
+
const associations = new WeakMap();
|
|
11
|
+
const restored = new WeakSet();
|
|
12
|
+
const disposals = new WeakMap();
|
|
13
|
+
const disposedContexts = new WeakSet();
|
|
14
|
+
const retiredInstances = new WeakSet();
|
|
15
|
+
const MAX_BYTES = 4194304;
|
|
16
|
+
const MAX_NODES = 100000;
|
|
17
|
+
const MAX_DEPTH = 32;
|
|
18
|
+
const MAX_OPTIONS_BYTES = 65536;
|
|
19
|
+
function record(value) {
|
|
20
|
+
return 'object' == typeof value && null !== value && (Object.getPrototypeOf(value) === Object.prototype || null === Object.getPrototypeOf(value));
|
|
21
|
+
}
|
|
22
|
+
function copyJson(value, maxBytes = MAX_BYTES) {
|
|
23
|
+
let bytes = 0;
|
|
24
|
+
let nodes = 0;
|
|
25
|
+
const ancestors = new Set();
|
|
26
|
+
const charge = (value, quoted = true)=>{
|
|
27
|
+
if (value.length > maxBytes) throw new Error('i18n snapshot exceeds its budget');
|
|
28
|
+
bytes += new TextEncoder().encode(quoted ? JSON.stringify(value) : value).byteLength;
|
|
29
|
+
if (bytes > maxBytes) throw new Error('i18n snapshot exceeds its budget');
|
|
30
|
+
};
|
|
31
|
+
const visit = (input, depth)=>{
|
|
32
|
+
if (++nodes > MAX_NODES || depth > MAX_DEPTH) throw new Error('i18n snapshot exceeds its structural budget');
|
|
33
|
+
if (null === input || 'boolean' == typeof input) {
|
|
34
|
+
charge(String(input), false);
|
|
35
|
+
return input;
|
|
36
|
+
}
|
|
37
|
+
if ('number' == typeof input) {
|
|
38
|
+
if (!Number.isFinite(input)) throw new Error('i18n snapshot numbers must be finite');
|
|
39
|
+
charge(String(input), false);
|
|
40
|
+
return input;
|
|
41
|
+
}
|
|
42
|
+
if ('string' == typeof input) {
|
|
43
|
+
charge(input);
|
|
44
|
+
return input;
|
|
45
|
+
}
|
|
46
|
+
if ('object' != typeof input || null === input || (Array.isArray(input) ? Object.getPrototypeOf(input) !== Array.prototype : !record(input))) throw new Error('i18n snapshots contain only plain JSON data');
|
|
47
|
+
if (ancestors.has(input)) throw new Error('i18n snapshots cannot contain cycles');
|
|
48
|
+
ancestors.add(input);
|
|
49
|
+
const result = Array.isArray(input) ? [] : {};
|
|
50
|
+
const keys = Reflect.ownKeys(input);
|
|
51
|
+
if (Array.isArray(input) && (input.length > MAX_NODES || keys.length !== input.length + 1 || keys.slice(0, -1).some((key, index)=>key !== String(index)))) throw new Error('i18n snapshot arrays must be dense');
|
|
52
|
+
charge(Array.isArray(input) ? '[]' : '{}', false);
|
|
53
|
+
let entries = 0;
|
|
54
|
+
for (const key of keys){
|
|
55
|
+
if (Array.isArray(input) && 'length' === key) continue;
|
|
56
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
57
|
+
if ('string' != typeof key || '__proto__' === key || 'prototype' === key || 'constructor' === key || !descriptor || !descriptor.enumerable || !('value' in descriptor)) throw new Error('i18n snapshots cannot contain special properties or accessors');
|
|
58
|
+
if (entries++) charge(',', false);
|
|
59
|
+
if (!Array.isArray(input)) {
|
|
60
|
+
charge(key);
|
|
61
|
+
charge(':', false);
|
|
62
|
+
}
|
|
63
|
+
Object.defineProperty(result, key, {
|
|
64
|
+
value: visit(descriptor.value, depth + 1),
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
ancestors.delete(input);
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
return visit(value, 0);
|
|
74
|
+
}
|
|
75
|
+
function jsonRecord(value, maxBytes = MAX_BYTES) {
|
|
76
|
+
const copied = copyJson(value, maxBytes);
|
|
77
|
+
if (!record(copied)) throw new Error('i18n snapshot must contain an object');
|
|
78
|
+
return copied;
|
|
79
|
+
}
|
|
80
|
+
function canonicalJson(value) {
|
|
81
|
+
if (Array.isArray(value)) return value.map(canonicalJson);
|
|
82
|
+
if (null !== value && 'object' == typeof value) {
|
|
83
|
+
const result = {};
|
|
84
|
+
for (const key of Object.keys(value).sort())result[key] = canonicalJson(value[key]);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
function sameResourcesKey(value) {
|
|
90
|
+
return JSON.stringify(canonicalJson(copyJson(value)));
|
|
91
|
+
}
|
|
92
|
+
function exactKeys(value, allowed) {
|
|
93
|
+
if (Object.keys(value).some((key)=>!allowed.includes(key))) throw new Error('Unknown i18n snapshot field');
|
|
94
|
+
}
|
|
95
|
+
function language(value) {
|
|
96
|
+
if ('string' != typeof value || !value || value.length > 128) throw new Error('Invalid i18n snapshot language');
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
function profileId(value) {
|
|
100
|
+
if (!record(value)) throw new Error('Invalid i18n snapshot profile');
|
|
101
|
+
exactKeys(value, [
|
|
102
|
+
'key',
|
|
103
|
+
'version'
|
|
104
|
+
]);
|
|
105
|
+
const key = language(value.key);
|
|
106
|
+
const version = language(value.version);
|
|
107
|
+
return {
|
|
108
|
+
key,
|
|
109
|
+
version
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function profileKey(profile) {
|
|
113
|
+
return JSON.stringify([
|
|
114
|
+
profile.key,
|
|
115
|
+
profile.version
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
118
|
+
function genuineInstance(value) {
|
|
119
|
+
if ('object' != typeof value || null === value) return false;
|
|
120
|
+
for (const key of [
|
|
121
|
+
'options',
|
|
122
|
+
'services',
|
|
123
|
+
'store',
|
|
124
|
+
'language'
|
|
125
|
+
]){
|
|
126
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
127
|
+
if (!descriptor || !('value' in descriptor)) return false;
|
|
128
|
+
}
|
|
129
|
+
const services = Object.getOwnPropertyDescriptor(value, 'services')?.value;
|
|
130
|
+
const connector = services && 'object' == typeof services ? Object.getOwnPropertyDescriptor(services, 'backendConnector')?.value : void 0;
|
|
131
|
+
const state = connector && 'object' == typeof connector ? Object.getOwnPropertyDescriptor(connector, 'state')?.value : void 0;
|
|
132
|
+
const pending = record(state) && Object.values(Object.getOwnPropertyDescriptors(state)).some((descriptor)=>!('value' in descriptor) || 1 === descriptor.value);
|
|
133
|
+
return Object.getPrototypeOf(value) === Object.getPrototypeOf(i18next) && Object.getOwnPropertyDescriptor(value, 'isInitialized')?.value === true && Object.getOwnPropertyDescriptor(value, 'isInitializing')?.value !== true && !Object.getOwnPropertyDescriptor(value, 'isLanguageChangingTo')?.value && 'string' == typeof Object.getOwnPropertyDescriptor(value, 'language')?.value && !pending;
|
|
134
|
+
}
|
|
135
|
+
function stringList(value, allowFalse = false) {
|
|
136
|
+
return allowFalse && false === value || 'string' == typeof value || Array.isArray(value) && value.every((item)=>'string' == typeof item);
|
|
137
|
+
}
|
|
138
|
+
const booleanOptions = new Set([
|
|
139
|
+
'debug',
|
|
140
|
+
'nonExplicitSupportedLngs',
|
|
141
|
+
'lowerCaseLng',
|
|
142
|
+
'cleanCode',
|
|
143
|
+
'ignoreJSONStructure',
|
|
144
|
+
'returnNull',
|
|
145
|
+
'returnEmptyString',
|
|
146
|
+
'returnObjects',
|
|
147
|
+
'returnDetails',
|
|
148
|
+
'appendNamespaceToMissingKey',
|
|
149
|
+
'appendNamespaceToCIMode',
|
|
150
|
+
'cacheInBuiltFormats'
|
|
151
|
+
]);
|
|
152
|
+
const stringOptions = new Set([
|
|
153
|
+
'pluralSeparator',
|
|
154
|
+
'contextSeparator'
|
|
155
|
+
]);
|
|
156
|
+
const interpolationBooleans = new Set([
|
|
157
|
+
'alwaysFormat',
|
|
158
|
+
'escapeValue',
|
|
159
|
+
'useRawValueToEscape',
|
|
160
|
+
'skipOnVariables'
|
|
161
|
+
]);
|
|
162
|
+
const interpolationStrings = new Set([
|
|
163
|
+
'formatSeparator',
|
|
164
|
+
'prefix',
|
|
165
|
+
'suffix',
|
|
166
|
+
'prefixEscaped',
|
|
167
|
+
'suffixEscaped',
|
|
168
|
+
'unescapeSuffix',
|
|
169
|
+
'unescapePrefix',
|
|
170
|
+
'nestingPrefix',
|
|
171
|
+
'nestingSuffix',
|
|
172
|
+
'nestingPrefixEscaped',
|
|
173
|
+
'nestingSuffixEscaped',
|
|
174
|
+
'nestingOptionsSeparator'
|
|
175
|
+
]);
|
|
176
|
+
function validatePortableOptions(options) {
|
|
177
|
+
for (const [key, value] of Object.entries(options)){
|
|
178
|
+
let valid = false;
|
|
179
|
+
if (booleanOptions.has(key)) valid = 'boolean' == typeof value;
|
|
180
|
+
else if (stringOptions.has(key)) valid = 'string' == typeof value;
|
|
181
|
+
else if ([
|
|
182
|
+
'keySeparator',
|
|
183
|
+
'nsSeparator',
|
|
184
|
+
'joinArrays'
|
|
185
|
+
].includes(key)) valid = false === value || 'string' == typeof value;
|
|
186
|
+
else if ([
|
|
187
|
+
'ns',
|
|
188
|
+
'defaultNS',
|
|
189
|
+
'fallbackNS'
|
|
190
|
+
].includes(key)) valid = stringList(value, 'ns' !== key);
|
|
191
|
+
else if ('preload' === key) valid = false === value || Array.isArray(value) && stringList(value);
|
|
192
|
+
else if ('supportedLngs' === key) valid = false === value || Array.isArray(value) && stringList(value);
|
|
193
|
+
else if ('fallbackLng' === key) valid = stringList(value, true) || record(value) && Object.values(value).every((item)=>Array.isArray(item) && stringList(item));
|
|
194
|
+
else if ('load' === key) valid = 'string' == typeof value && [
|
|
195
|
+
'all',
|
|
196
|
+
'currentOnly',
|
|
197
|
+
'languageOnly'
|
|
198
|
+
].includes(value);
|
|
199
|
+
else if ('enableSelector' === key) valid = 'boolean' == typeof value || 'optimize' === value || 'strict' === value;
|
|
200
|
+
else if ('compatibilityJSON' === key) valid = 'v4' === value;
|
|
201
|
+
else if ('postProcess' === key) valid = false === value || Array.isArray(value) && 0 === value.length;
|
|
202
|
+
else if ([
|
|
203
|
+
'saveMissing',
|
|
204
|
+
'updateMissing'
|
|
205
|
+
].includes(key)) valid = false === value;
|
|
206
|
+
else if ('interpolation' === key && record(value)) valid = Object.entries(value).every(([name, field])=>{
|
|
207
|
+
if (interpolationBooleans.has(name)) return 'boolean' == typeof field;
|
|
208
|
+
if (interpolationStrings.has(name)) return 'string' == typeof field;
|
|
209
|
+
if ('defaultVariables' === name) return record(field);
|
|
210
|
+
return 'maxReplaces' === name && 'number' == typeof field && Number.isSafeInteger(field) && field >= 0 && field <= 100000;
|
|
211
|
+
});
|
|
212
|
+
if (!valid) throw new Error(`i18n option ${key} requires an explicit Worker profile`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function i18n_external_snapshot_resources(value) {
|
|
216
|
+
const copied = jsonRecord(value);
|
|
217
|
+
if (Object.keys(copied).length > 64) throw new Error('Too many i18n snapshot languages');
|
|
218
|
+
const result = {};
|
|
219
|
+
for (const [lng, namespaces] of Object.entries(copied)){
|
|
220
|
+
language(lng);
|
|
221
|
+
if (!record(namespaces) || Object.keys(namespaces).length > 256) throw new Error('Invalid i18n snapshot namespaces');
|
|
222
|
+
const bundles = {};
|
|
223
|
+
for (const [namespace, bundle] of Object.entries(namespaces)){
|
|
224
|
+
language(namespace);
|
|
225
|
+
if ('string' != typeof bundle && !record(bundle)) throw new Error('Invalid i18n snapshot resource bundle');
|
|
226
|
+
bundles[namespace] = bundle;
|
|
227
|
+
}
|
|
228
|
+
result[lng] = bundles;
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
function loadedResources(instance) {
|
|
233
|
+
const result = {};
|
|
234
|
+
const descriptor = Object.getOwnPropertyDescriptor(instance.store, 'data');
|
|
235
|
+
if (!descriptor || !('value' in descriptor)) throw new Error('Loaded i18n resources cannot use accessors');
|
|
236
|
+
const loaded = jsonRecord(descriptor.value);
|
|
237
|
+
for (const lng of Object.keys(loaded)){
|
|
238
|
+
const data = jsonRecord(instance.getDataByLanguage(lng));
|
|
239
|
+
const bundles = {};
|
|
240
|
+
for (const namespace of Object.keys(data)){
|
|
241
|
+
const bundle = instance.getResourceBundle(lng, namespace);
|
|
242
|
+
if ('string' != typeof bundle && !record(bundle)) throw new Error('Invalid loaded i18n resource bundle');
|
|
243
|
+
bundles[namespace] = bundle;
|
|
244
|
+
}
|
|
245
|
+
result[lng] = bundles;
|
|
246
|
+
}
|
|
247
|
+
return i18n_external_snapshot_resources(result);
|
|
248
|
+
}
|
|
249
|
+
function localizedUrls(value) {
|
|
250
|
+
if (void 0 === value || 'boolean' == typeof value) return value;
|
|
251
|
+
if (!record(value)) throw new Error('Invalid i18n snapshot localized URLs');
|
|
252
|
+
const result = {};
|
|
253
|
+
for (const [path, map] of Object.entries(value)){
|
|
254
|
+
if (!record(map)) throw new Error('Invalid i18n snapshot localized URL map');
|
|
255
|
+
const paths = {};
|
|
256
|
+
for (const [lng, target] of Object.entries(map)){
|
|
257
|
+
language(lng);
|
|
258
|
+
if ('string' != typeof target) throw new Error('Invalid localized URL target');
|
|
259
|
+
paths[lng] = target;
|
|
260
|
+
}
|
|
261
|
+
result[path] = paths;
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
function rewriteSnapshot(value) {
|
|
266
|
+
const input = jsonRecord(value, MAX_OPTIONS_BYTES);
|
|
267
|
+
exactKeys(input, [
|
|
268
|
+
'language',
|
|
269
|
+
'languages',
|
|
270
|
+
'localisedUrls'
|
|
271
|
+
]);
|
|
272
|
+
const lng = language(input.language);
|
|
273
|
+
if (!Array.isArray(input.languages) || !input.languages.length || input.languages.length > 64) throw new Error('Invalid i18n rewrite languages');
|
|
274
|
+
const languages = input.languages.map(language);
|
|
275
|
+
if (new Set(languages).size !== languages.length || !languages.includes(lng)) throw new Error('Invalid i18n rewrite language selection');
|
|
276
|
+
const urls = localizedUrls(input.localisedUrls);
|
|
277
|
+
return {
|
|
278
|
+
language: lng,
|
|
279
|
+
languages,
|
|
280
|
+
...void 0 === urls ? {} : {
|
|
281
|
+
localisedUrls: urls
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function captureOctaneI18nRewriteSnapshot(rewrite) {
|
|
286
|
+
const snapshot = getNativeI18nRewriteSnapshot(rewrite);
|
|
287
|
+
if (!snapshot) throw new Error('Unknown i18n router rewrite requires a named profile');
|
|
288
|
+
return rewriteSnapshot(snapshot);
|
|
289
|
+
}
|
|
290
|
+
function restoreOctaneI18nRewriteSnapshot(value) {
|
|
291
|
+
const snapshot = rewriteSnapshot(value);
|
|
292
|
+
return createNativeI18n({
|
|
293
|
+
languages: snapshot.languages,
|
|
294
|
+
fallbackLanguage: snapshot.language,
|
|
295
|
+
localisedUrls: snapshot.localisedUrls
|
|
296
|
+
}, {}).rewrite(()=>snapshot.language);
|
|
297
|
+
}
|
|
298
|
+
function registerOctaneI18nSnapshotProfile(profile) {
|
|
299
|
+
const id = profileId({
|
|
300
|
+
key: profile.key,
|
|
301
|
+
version: profile.version
|
|
302
|
+
});
|
|
303
|
+
if ('function' != typeof profile.captureOptions || 'function' != typeof profile.validateOptions || 'function' != typeof profile.create || void 0 !== profile.dispose && 'function' != typeof profile.dispose) throw new Error('Invalid i18n Worker profile implementation');
|
|
304
|
+
const key = profileKey(id);
|
|
305
|
+
if (key === profileKey(defaultProfile) || profiles.has(key)) throw new Error('Duplicate i18n Worker profile');
|
|
306
|
+
const captured = Object.freeze({
|
|
307
|
+
...id,
|
|
308
|
+
captureOptions: profile.captureOptions,
|
|
309
|
+
validateOptions: profile.validateOptions,
|
|
310
|
+
create: profile.create,
|
|
311
|
+
...profile.dispose ? {
|
|
312
|
+
dispose: profile.dispose
|
|
313
|
+
} : {}
|
|
314
|
+
});
|
|
315
|
+
profiles.set(key, captured);
|
|
316
|
+
return ()=>{
|
|
317
|
+
if (profiles.get(key) === captured) profiles.delete(key);
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function associateOctaneI18nSnapshotProfile(instance, id) {
|
|
321
|
+
const copied = profileId(jsonRecord(id));
|
|
322
|
+
const profile = profiles.get(profileKey(copied));
|
|
323
|
+
if (!genuineInstance(instance) || retiredInstances.has(instance)) throw new Error('Profiles require a live genuine i18n instance');
|
|
324
|
+
if (!profile) throw new Error('The exact i18n Worker profile must be registered locally');
|
|
325
|
+
if (restored.has(instance)) {
|
|
326
|
+
const association = associations.get(instance);
|
|
327
|
+
if (association?.profile !== profile || profileKey(association.id) !== profileKey(copied)) throw new Error('A restored i18n context retains its owning profile');
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
associations.set(instance, {
|
|
331
|
+
id: copied,
|
|
332
|
+
profile
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
function captureOctaneI18nSnapshot(value) {
|
|
336
|
+
const instance = value.instance;
|
|
337
|
+
if (disposedContexts.has(value) || retiredInstances.has(instance)) throw new Error('Cannot capture a disposed i18n Worker context');
|
|
338
|
+
if (!genuineInstance(instance)) throw new Error('Worker i18n snapshots require a genuine settled i18next instance');
|
|
339
|
+
if (instance.language !== value.language) throw new Error('Worker i18n context language must be settled');
|
|
340
|
+
const association = associations.get(instance);
|
|
341
|
+
const selected = association?.id ?? defaultProfile;
|
|
342
|
+
let options;
|
|
343
|
+
if (profileKey(selected) === profileKey(defaultProfile)) {
|
|
344
|
+
const seed = getNativeI18nSnapshotSeed(instance);
|
|
345
|
+
const { lng, resources, initAsync, partialBundledLanguages, ...portable } = seed;
|
|
346
|
+
options = jsonRecord(portable, MAX_OPTIONS_BYTES);
|
|
347
|
+
validatePortableOptions(options);
|
|
348
|
+
} else {
|
|
349
|
+
const profile = profiles.get(profileKey(selected));
|
|
350
|
+
if (!profile || association?.profile !== profile) throw new Error('Missing or changed exact i18n Worker profile');
|
|
351
|
+
options = jsonRecord(profile.captureOptions(instance), MAX_OPTIONS_BYTES);
|
|
352
|
+
profile.validateOptions(options);
|
|
353
|
+
}
|
|
354
|
+
return parseSnapshot({
|
|
355
|
+
version: 1,
|
|
356
|
+
profile: selected,
|
|
357
|
+
language: value.language,
|
|
358
|
+
languages: [
|
|
359
|
+
...value.languages
|
|
360
|
+
],
|
|
361
|
+
...void 0 === value.localisedUrls ? {} : {
|
|
362
|
+
localisedUrls: value.localisedUrls
|
|
363
|
+
},
|
|
364
|
+
options,
|
|
365
|
+
resources: loadedResources(instance)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function parseSnapshot(value) {
|
|
369
|
+
const input = jsonRecord(value);
|
|
370
|
+
exactKeys(input, [
|
|
371
|
+
'version',
|
|
372
|
+
'profile',
|
|
373
|
+
'language',
|
|
374
|
+
'languages',
|
|
375
|
+
'localisedUrls',
|
|
376
|
+
'options',
|
|
377
|
+
'resources'
|
|
378
|
+
]);
|
|
379
|
+
if (1 !== input.version) throw new Error('Unsupported i18n snapshot version');
|
|
380
|
+
const lng = language(input.language);
|
|
381
|
+
if (!Array.isArray(input.languages) || !input.languages.length || input.languages.length > 64) throw new Error('Invalid i18n snapshot supported languages');
|
|
382
|
+
const languages = input.languages.map(language);
|
|
383
|
+
if (new Set(languages).size !== languages.length || !languages.includes(lng)) throw new Error('Invalid i18n snapshot language selection');
|
|
384
|
+
const options = jsonRecord(input.options, MAX_OPTIONS_BYTES);
|
|
385
|
+
const urls = localizedUrls(input.localisedUrls);
|
|
386
|
+
return {
|
|
387
|
+
version: 1,
|
|
388
|
+
profile: profileId(input.profile),
|
|
389
|
+
language: lng,
|
|
390
|
+
languages,
|
|
391
|
+
...void 0 === urls ? {} : {
|
|
392
|
+
localisedUrls: urls
|
|
393
|
+
},
|
|
394
|
+
options,
|
|
395
|
+
resources: i18n_external_snapshot_resources(input.resources)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function throwIfAborted(signal) {
|
|
399
|
+
signal?.throwIfAborted();
|
|
400
|
+
}
|
|
401
|
+
function disposeInstance(instance) {
|
|
402
|
+
for (const event of [
|
|
403
|
+
'initialized',
|
|
404
|
+
'loaded',
|
|
405
|
+
'failedLoading',
|
|
406
|
+
'languageChanging',
|
|
407
|
+
'languageChanged',
|
|
408
|
+
'missingKey',
|
|
409
|
+
'*'
|
|
410
|
+
])instance.off(event);
|
|
411
|
+
instance.store.off('added');
|
|
412
|
+
instance.store.off('removed');
|
|
413
|
+
}
|
|
414
|
+
function retireInstance(instance) {
|
|
415
|
+
if ('object' != typeof instance || null === instance || Object.getPrototypeOf(instance) !== Object.getPrototypeOf(i18next)) return;
|
|
416
|
+
retiredInstances.add(instance);
|
|
417
|
+
associations.delete(instance);
|
|
418
|
+
invalidateNativeI18nSnapshot(instance);
|
|
419
|
+
}
|
|
420
|
+
function cleanupFreshInstance(instance, dispose) {
|
|
421
|
+
if (restored.has(instance)) return;
|
|
422
|
+
retireInstance(instance);
|
|
423
|
+
if (genuineInstance(instance)) disposeInstance(instance);
|
|
424
|
+
dispose?.();
|
|
425
|
+
}
|
|
426
|
+
function createProfileInstance(profile, snapshot, signal) {
|
|
427
|
+
return new Promise((resolve, reject)=>{
|
|
428
|
+
let finished = false;
|
|
429
|
+
const abort = ()=>{
|
|
430
|
+
if (finished) return;
|
|
431
|
+
finished = true;
|
|
432
|
+
signal?.removeEventListener('abort', abort);
|
|
433
|
+
reject(signal?.reason ?? new Error('i18n restoration aborted'));
|
|
434
|
+
};
|
|
435
|
+
signal?.addEventListener('abort', abort, {
|
|
436
|
+
once: true
|
|
437
|
+
});
|
|
438
|
+
if (signal?.aborted) abort();
|
|
439
|
+
Promise.resolve().then(()=>{
|
|
440
|
+
if (finished) throw signal?.reason ?? new Error('i18n restoration aborted');
|
|
441
|
+
return profile.create({
|
|
442
|
+
language: snapshot.language,
|
|
443
|
+
options: snapshot.options,
|
|
444
|
+
resources: i18n_external_snapshot_resources(snapshot.resources),
|
|
445
|
+
...signal ? {
|
|
446
|
+
signal
|
|
447
|
+
} : {}
|
|
448
|
+
});
|
|
449
|
+
}).then((instance)=>{
|
|
450
|
+
if (finished) {
|
|
451
|
+
try {
|
|
452
|
+
cleanupFreshInstance(instance, ()=>profile.dispose?.(instance));
|
|
453
|
+
} catch {}
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
finished = true;
|
|
457
|
+
signal?.removeEventListener('abort', abort);
|
|
458
|
+
resolve(instance);
|
|
459
|
+
}, (error)=>{
|
|
460
|
+
if (finished) return;
|
|
461
|
+
finished = true;
|
|
462
|
+
signal?.removeEventListener('abort', abort);
|
|
463
|
+
reject(error);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
async function restoreOctaneI18nSnapshot(value, signal) {
|
|
468
|
+
throwIfAborted(signal);
|
|
469
|
+
const snapshot = parseSnapshot(value);
|
|
470
|
+
const expectedResources = sameResourcesKey(snapshot.resources);
|
|
471
|
+
let instance;
|
|
472
|
+
let dispose;
|
|
473
|
+
let restoredProfile;
|
|
474
|
+
if (profileKey(snapshot.profile) === profileKey(defaultProfile)) {
|
|
475
|
+
validatePortableOptions(snapshot.options);
|
|
476
|
+
instance = createNativeI18nInstance({
|
|
477
|
+
...snapshot.options,
|
|
478
|
+
lng: snapshot.language,
|
|
479
|
+
resources: i18n_external_snapshot_resources(snapshot.resources),
|
|
480
|
+
initAsync: false,
|
|
481
|
+
partialBundledLanguages: false
|
|
482
|
+
});
|
|
483
|
+
} else {
|
|
484
|
+
const profile = profiles.get(profileKey(snapshot.profile));
|
|
485
|
+
if (!profile) throw new Error('Missing exact i18n Worker profile');
|
|
486
|
+
restoredProfile = profile;
|
|
487
|
+
profile.validateOptions(snapshot.options);
|
|
488
|
+
instance = await createProfileInstance(profile, snapshot, signal);
|
|
489
|
+
if (profile.dispose) dispose = ()=>profile.dispose?.(instance);
|
|
490
|
+
}
|
|
491
|
+
try {
|
|
492
|
+
throwIfAborted(signal);
|
|
493
|
+
if (restoredProfile && profiles.get(profileKey(snapshot.profile)) !== restoredProfile) throw new Error('The i18n Worker profile changed during restoration');
|
|
494
|
+
if (!genuineInstance(instance) || instance.language !== snapshot.language || restored.has(instance)) throw new Error('An i18n Worker profile must create a fresh settled instance');
|
|
495
|
+
const current = loadedResources(instance);
|
|
496
|
+
if (sameResourcesKey(current) !== expectedResources) throw new Error('An i18n Worker profile must restore every loaded resource');
|
|
497
|
+
restored.add(instance);
|
|
498
|
+
if (restoredProfile) associations.set(instance, {
|
|
499
|
+
id: {
|
|
500
|
+
...snapshot.profile
|
|
501
|
+
},
|
|
502
|
+
profile: restoredProfile
|
|
503
|
+
});
|
|
504
|
+
const context = {
|
|
505
|
+
instance,
|
|
506
|
+
language: snapshot.language,
|
|
507
|
+
languages: snapshot.languages,
|
|
508
|
+
...void 0 === snapshot.localisedUrls ? {} : {
|
|
509
|
+
localisedUrls: snapshot.localisedUrls
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
let disposed = false;
|
|
513
|
+
disposals.set(context, ()=>{
|
|
514
|
+
if (disposed) return;
|
|
515
|
+
disposed = true;
|
|
516
|
+
disposedContexts.add(context);
|
|
517
|
+
retireInstance(instance);
|
|
518
|
+
disposeInstance(instance);
|
|
519
|
+
dispose?.();
|
|
520
|
+
});
|
|
521
|
+
return context;
|
|
522
|
+
} catch (error) {
|
|
523
|
+
cleanupFreshInstance(instance, dispose);
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function disposeOctaneI18nSnapshot(value) {
|
|
528
|
+
const dispose = disposals.get(value);
|
|
529
|
+
disposals.delete(value);
|
|
530
|
+
dispose?.();
|
|
531
|
+
}
|
|
532
|
+
let registrations = 0;
|
|
533
|
+
let releaseContext;
|
|
534
|
+
function registerOctaneI18nWorkerContexts() {
|
|
535
|
+
if (!releaseContext) {
|
|
536
|
+
const releaseRewrite = registerOctaneWorkerRouterRewrite({
|
|
537
|
+
capture: captureOctaneI18nRewriteSnapshot,
|
|
538
|
+
restore: restoreOctaneI18nRewriteSnapshot
|
|
539
|
+
});
|
|
540
|
+
try {
|
|
541
|
+
const unregisterContext = registerOctaneWorkerContext(I18nContext, {
|
|
542
|
+
key: OCTANE_WORKER_CONTEXT_KEYS.i18n,
|
|
543
|
+
encode: (value)=>{
|
|
544
|
+
if (null === value) throw new Error('Missing i18n context provider');
|
|
545
|
+
return captureOctaneI18nSnapshot(value);
|
|
546
|
+
},
|
|
547
|
+
decode: restoreOctaneI18nSnapshot,
|
|
548
|
+
dispose: (value)=>{
|
|
549
|
+
if (null !== value) disposeOctaneI18nSnapshot(value);
|
|
550
|
+
},
|
|
551
|
+
isProvided: (value)=>null !== value
|
|
552
|
+
});
|
|
553
|
+
releaseContext = ()=>{
|
|
554
|
+
try {
|
|
555
|
+
unregisterContext();
|
|
556
|
+
} finally{
|
|
557
|
+
releaseRewrite();
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
} catch (error) {
|
|
561
|
+
releaseRewrite();
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
registrations++;
|
|
566
|
+
let released = false;
|
|
567
|
+
return ()=>{
|
|
568
|
+
if (released) return;
|
|
569
|
+
released = true;
|
|
570
|
+
if (0 === --registrations) {
|
|
571
|
+
releaseContext?.();
|
|
572
|
+
releaseContext = void 0;
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
export { associateOctaneI18nSnapshotProfile, captureOctaneI18nRewriteSnapshot, captureOctaneI18nSnapshot, disposeOctaneI18nSnapshot, registerOctaneI18nSnapshotProfile, registerOctaneI18nWorkerContexts, restoreOctaneI18nRewriteSnapshot, restoreOctaneI18nSnapshot };
|