@lionrapid/core 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/event-emitter.d.ts.map +1 -1
- package/dist/base/event-emitter.js +2 -1
- package/dist/base/event-emitter.js.map +1 -1
- package/dist/browser/attribute-extractor.d.ts.map +1 -1
- package/dist/browser/attribute-extractor.js +2 -1
- package/dist/browser/attribute-extractor.js.map +1 -1
- package/dist/browser/auto-init.d.ts +69 -0
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +193 -61
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/block-extractor.d.ts.map +1 -1
- package/dist/browser/block-extractor.js +3 -1
- package/dist/browser/block-extractor.js.map +1 -1
- package/dist/browser/editor/editor-api.d.ts +50 -6
- package/dist/browser/editor/editor-api.d.ts.map +1 -1
- package/dist/browser/editor/editor-api.js +121 -19
- package/dist/browser/editor/editor-api.js.map +1 -1
- package/dist/browser/editor/editor-init.js +4 -1
- package/dist/browser/editor/editor-init.js.map +1 -1
- package/dist/browser/editor/sidebar.d.ts +4 -3
- package/dist/browser/editor/sidebar.d.ts.map +1 -1
- package/dist/browser/editor/sidebar.js +93 -27
- package/dist/browser/editor/sidebar.js.map +1 -1
- package/dist/browser/editor/snapshot-viewer.d.ts.map +1 -1
- package/dist/browser/editor/snapshot-viewer.js +32 -3
- package/dist/browser/editor/snapshot-viewer.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +599 -245
- package/dist/browser/lionrapid.auto.dev.js.map +4 -4
- package/dist/browser/lionrapid.auto.js +599 -245
- package/dist/browser/lionrapid.auto.js.map +4 -4
- package/dist/browser/lionrapid.auto.min.js +4 -3
- package/dist/browser/lionrapid.auto.min.js.map +4 -4
- package/dist/browser/lionrapid.editor.js +1467 -1115
- package/dist/browser/lionrapid.editor.js.map +4 -4
- package/dist/browser/lionrapid.editor.min.js +4 -3
- package/dist/browser/lionrapid.editor.min.js.map +4 -4
- package/dist/browser/lionrapid.esm.js +951 -196
- package/dist/browser/lionrapid.esm.js.map +4 -4
- package/dist/browser/lionrapid.esm.min.js +3 -2
- package/dist/browser/lionrapid.esm.min.js.map +4 -4
- package/dist/browser/lionrapid.iife.dev.js +951 -196
- package/dist/browser/lionrapid.iife.dev.js.map +4 -4
- package/dist/browser/lionrapid.iife.js +951 -196
- package/dist/browser/lionrapid.iife.js.map +4 -4
- package/dist/browser/lionrapid.iife.min.js +3 -2
- package/dist/browser/lionrapid.iife.min.js.map +4 -4
- package/dist/browser/lionrapid.umd.js +951 -196
- package/dist/browser/lionrapid.umd.js.map +4 -4
- package/dist/browser/lionrapid.umd.min.js +3 -2
- package/dist/browser/lionrapid.umd.min.js.map +4 -4
- package/dist/browser/sanitizer.d.ts +37 -0
- package/dist/browser/sanitizer.d.ts.map +1 -0
- package/dist/browser/sanitizer.js +93 -0
- package/dist/browser/sanitizer.js.map +1 -0
- package/dist/builder.d.ts +16 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +43 -5
- package/dist/builder.js.map +1 -1
- package/dist/config/lionrapid.config.d.ts +0 -1
- package/dist/config/lionrapid.config.d.ts.map +1 -1
- package/dist/config/repository.config.d.ts +2 -2
- package/dist/config/repository.config.d.ts.map +1 -1
- package/dist/content/index.d.ts +3 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +5 -0
- package/dist/content/index.js.map +1 -1
- package/dist/content/sanitizer/HTMLSanitizer.d.ts +105 -0
- package/dist/content/sanitizer/HTMLSanitizer.d.ts.map +1 -0
- package/dist/content/sanitizer/HTMLSanitizer.js +257 -0
- package/dist/content/sanitizer/HTMLSanitizer.js.map +1 -0
- package/dist/content/serializer/HTMLSerializer.d.ts +6 -2
- package/dist/content/serializer/HTMLSerializer.d.ts.map +1 -1
- package/dist/content/serializer/HTMLSerializer.js +16 -8
- package/dist/content/serializer/HTMLSerializer.js.map +1 -1
- package/dist/content/splitter/BlockSplitter.d.ts +66 -0
- package/dist/content/splitter/BlockSplitter.d.ts.map +1 -0
- package/dist/content/splitter/BlockSplitter.js +219 -0
- package/dist/content/splitter/BlockSplitter.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/lionRapid.d.ts +1 -2
- package/dist/lionRapid.d.ts.map +1 -1
- package/dist/lionRapid.js +5 -7
- package/dist/lionRapid.js.map +1 -1
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.d.ts.map +1 -1
- package/dist/repositories/index.js +1 -0
- package/dist/repositories/index.js.map +1 -1
- package/dist/repositories/network-repository.d.ts +30 -8
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +88 -74
- package/dist/repositories/network-repository.js.map +1 -1
- package/dist/repositories/static-repository.d.ts +40 -0
- package/dist/repositories/static-repository.d.ts.map +1 -0
- package/dist/repositories/static-repository.js +60 -0
- package/dist/repositories/static-repository.js.map +1 -0
- package/dist/types/lionRapid.types.d.ts +2 -0
- package/dist/types/lionRapid.types.d.ts.map +1 -1
- package/dist/types/translation.types.d.ts +0 -3
- package/dist/types/translation.types.d.ts.map +1 -1
- package/dist/utils/context-utils.d.ts +1 -2
- package/dist/utils/context-utils.d.ts.map +1 -1
- package/dist/utils/context-utils.js +3 -6
- package/dist/utils/context-utils.js.map +1 -1
- package/dist/utils/locale.d.ts +57 -0
- package/dist/utils/locale.d.ts.map +1 -0
- package/dist/utils/locale.js +116 -0
- package/dist/utils/locale.js.map +1 -0
- package/dist/utils/translation-flow.d.ts +1 -1
- package/dist/utils/translation-flow.d.ts.map +1 -1
- package/dist/utils/translation-flow.js +2 -3
- package/dist/utils/translation-flow.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
2
|
+
var __lionrapidAutoExports = (() => {
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -336,6 +336,49 @@ var LionRapid = (() => {
|
|
|
336
336
|
packageEmoji: "\u{1F981}"
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
+
// src/utils/locale.ts
|
|
340
|
+
var LOCALE_TAG_REGEX = /^[a-z]{2,3}(?:-[a-z0-9]{2,4})*$/i;
|
|
341
|
+
var RTL_LANGUAGES = /* @__PURE__ */ new Set(["ar", "fa", "he", "ur", "yi"]);
|
|
342
|
+
function primarySubtag(tag) {
|
|
343
|
+
return tag.split("-")[0].toLowerCase();
|
|
344
|
+
}
|
|
345
|
+
function normalizeLocale(tag) {
|
|
346
|
+
const trimmed = tag.trim();
|
|
347
|
+
if (!trimmed) return trimmed;
|
|
348
|
+
const [lang, ...rest] = trimmed.split("-");
|
|
349
|
+
const out = [lang.toLowerCase()];
|
|
350
|
+
for (const sub of rest) {
|
|
351
|
+
if (sub.length === 4) {
|
|
352
|
+
out.push(sub[0].toUpperCase() + sub.slice(1).toLowerCase());
|
|
353
|
+
} else if (sub.length === 2) {
|
|
354
|
+
out.push(sub.toUpperCase());
|
|
355
|
+
} else {
|
|
356
|
+
out.push(sub.toLowerCase());
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return out.join("-");
|
|
360
|
+
}
|
|
361
|
+
function matchLocale(requested, available, fallback) {
|
|
362
|
+
if (available.length === 0) return fallback;
|
|
363
|
+
const avail = available.map((raw) => ({
|
|
364
|
+
raw,
|
|
365
|
+
lower: normalizeLocale(raw).toLowerCase()
|
|
366
|
+
}));
|
|
367
|
+
for (const req of requested) {
|
|
368
|
+
if (!req) continue;
|
|
369
|
+
const reqLower = normalizeLocale(req).toLowerCase();
|
|
370
|
+
const exact = avail.find((a) => a.lower === reqLower);
|
|
371
|
+
if (exact) return exact.raw;
|
|
372
|
+
const primary = primarySubtag(req);
|
|
373
|
+
const byPrimary = avail.find((a) => a.lower === primary);
|
|
374
|
+
if (byPrimary) return byPrimary.raw;
|
|
375
|
+
}
|
|
376
|
+
return fallback;
|
|
377
|
+
}
|
|
378
|
+
function localeDir(tag) {
|
|
379
|
+
return RTL_LANGUAGES.has(primarySubtag(tag)) ? "rtl" : "ltr";
|
|
380
|
+
}
|
|
381
|
+
|
|
339
382
|
// src/base/event-emitter.ts
|
|
340
383
|
var EventEmitter = class {
|
|
341
384
|
constructor() {
|
|
@@ -624,7 +667,7 @@ var LionRapid = (() => {
|
|
|
624
667
|
complete: () => {
|
|
625
668
|
this.emit("language:change:success", {
|
|
626
669
|
context: this.createContext(requestId, [...baseTrace, "complete"]),
|
|
627
|
-
payload: { oldLocale, newLocale }
|
|
670
|
+
payload: { oldLocale, newLocale, dir: localeDir(newLocale) }
|
|
628
671
|
});
|
|
629
672
|
},
|
|
630
673
|
error: (error) => {
|
|
@@ -1034,12 +1077,11 @@ var LionRapid = (() => {
|
|
|
1034
1077
|
}
|
|
1035
1078
|
|
|
1036
1079
|
// src/utils/context-utils.ts
|
|
1037
|
-
function createTranslationContext(parsedKey, currentLocale, defaultNamespace
|
|
1080
|
+
function createTranslationContext(parsedKey, currentLocale, defaultNamespace) {
|
|
1038
1081
|
const effectiveNamespace = parsedKey.hasExplicitNamespace ? parsedKey.namespace : defaultNamespace;
|
|
1039
1082
|
return {
|
|
1040
1083
|
locale: currentLocale,
|
|
1041
|
-
namespace: effectiveNamespace
|
|
1042
|
-
fallbackLocale: fallbackLocale || currentLocale
|
|
1084
|
+
namespace: effectiveNamespace
|
|
1043
1085
|
};
|
|
1044
1086
|
}
|
|
1045
1087
|
|
|
@@ -1156,7 +1198,7 @@ var LionRapid = (() => {
|
|
|
1156
1198
|
}
|
|
1157
1199
|
|
|
1158
1200
|
// src/utils/translation-flow.ts
|
|
1159
|
-
function prepareTranslationRequest(key, currentLocale, defaultValueOrParams, paramsOrDefaultValue, defaultNamespace
|
|
1201
|
+
function prepareTranslationRequest(key, currentLocale, defaultValueOrParams, paramsOrDefaultValue, defaultNamespace) {
|
|
1160
1202
|
const startTime = performance.now();
|
|
1161
1203
|
const { defaultValue, params } = parseTranslationParams(
|
|
1162
1204
|
defaultValueOrParams,
|
|
@@ -1166,8 +1208,7 @@ var LionRapid = (() => {
|
|
|
1166
1208
|
const context = createTranslationContext(
|
|
1167
1209
|
parsedKey,
|
|
1168
1210
|
currentLocale,
|
|
1169
|
-
defaultNamespace
|
|
1170
|
-
fallbackLocale
|
|
1211
|
+
defaultNamespace
|
|
1171
1212
|
);
|
|
1172
1213
|
const individualCacheKey = createIndividualCacheKey(
|
|
1173
1214
|
context.locale,
|
|
@@ -1189,7 +1230,6 @@ var LionRapid = (() => {
|
|
|
1189
1230
|
locale: currentLocale,
|
|
1190
1231
|
namespace: context.namespace,
|
|
1191
1232
|
// ✅ Use effective namespace (explicit or default)
|
|
1192
|
-
fallbackLocale,
|
|
1193
1233
|
individualCacheKey,
|
|
1194
1234
|
// ✅ Ready-to-use cache key
|
|
1195
1235
|
namespaceCacheKey,
|
|
@@ -2299,6 +2339,157 @@ var LionRapid = (() => {
|
|
|
2299
2339
|
}
|
|
2300
2340
|
};
|
|
2301
2341
|
|
|
2342
|
+
// src/content/sanitizer/HTMLSanitizer.ts
|
|
2343
|
+
var SAFE_TAGS = /* @__PURE__ */ new Set([
|
|
2344
|
+
"a",
|
|
2345
|
+
"abbr",
|
|
2346
|
+
"b",
|
|
2347
|
+
"bdi",
|
|
2348
|
+
"bdo",
|
|
2349
|
+
"br",
|
|
2350
|
+
"cite",
|
|
2351
|
+
"code",
|
|
2352
|
+
"del",
|
|
2353
|
+
"dfn",
|
|
2354
|
+
"em",
|
|
2355
|
+
"i",
|
|
2356
|
+
"img",
|
|
2357
|
+
"ins",
|
|
2358
|
+
"kbd",
|
|
2359
|
+
"mark",
|
|
2360
|
+
"q",
|
|
2361
|
+
"s",
|
|
2362
|
+
"samp",
|
|
2363
|
+
"small",
|
|
2364
|
+
"span",
|
|
2365
|
+
"strong",
|
|
2366
|
+
"sub",
|
|
2367
|
+
"sup",
|
|
2368
|
+
"time",
|
|
2369
|
+
"u",
|
|
2370
|
+
"var",
|
|
2371
|
+
"wbr"
|
|
2372
|
+
]);
|
|
2373
|
+
var SAFE_ATTRS = /* @__PURE__ */ new Set([
|
|
2374
|
+
"alt",
|
|
2375
|
+
"cite",
|
|
2376
|
+
"class",
|
|
2377
|
+
"datetime",
|
|
2378
|
+
"dir",
|
|
2379
|
+
"height",
|
|
2380
|
+
"href",
|
|
2381
|
+
"id",
|
|
2382
|
+
"lang",
|
|
2383
|
+
"loading",
|
|
2384
|
+
"rel",
|
|
2385
|
+
"role",
|
|
2386
|
+
"src",
|
|
2387
|
+
"target",
|
|
2388
|
+
"title",
|
|
2389
|
+
"width"
|
|
2390
|
+
]);
|
|
2391
|
+
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "cite"]);
|
|
2392
|
+
var SAFE_STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
2393
|
+
"color",
|
|
2394
|
+
"background-color",
|
|
2395
|
+
"font-family",
|
|
2396
|
+
"font-size",
|
|
2397
|
+
"font-style",
|
|
2398
|
+
"font-weight",
|
|
2399
|
+
"letter-spacing",
|
|
2400
|
+
"line-height",
|
|
2401
|
+
"text-decoration",
|
|
2402
|
+
"text-decoration-color",
|
|
2403
|
+
"text-decoration-line",
|
|
2404
|
+
"text-decoration-style",
|
|
2405
|
+
"text-transform",
|
|
2406
|
+
"vertical-align",
|
|
2407
|
+
"white-space",
|
|
2408
|
+
"word-spacing"
|
|
2409
|
+
]);
|
|
2410
|
+
var DANGEROUS_CSS_VALUE = /url\s*\(|expression\s*\(|image-set\s*\(|element\s*\(|paint\s*\(|javascript:|\\|[<>{}@]/i;
|
|
2411
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
2412
|
+
var NAMED_ENTITIES = {
|
|
2413
|
+
amp: "&",
|
|
2414
|
+
lt: "<",
|
|
2415
|
+
gt: ">",
|
|
2416
|
+
quot: '"',
|
|
2417
|
+
apos: "'",
|
|
2418
|
+
nbsp: "\xA0",
|
|
2419
|
+
colon: ":",
|
|
2420
|
+
semi: ";",
|
|
2421
|
+
sol: "/",
|
|
2422
|
+
bsol: "\\",
|
|
2423
|
+
Tab: " ",
|
|
2424
|
+
NewLine: "\n"
|
|
2425
|
+
};
|
|
2426
|
+
function decodeEntities2(value) {
|
|
2427
|
+
return value.replace(
|
|
2428
|
+
/&(?:#[xX]([0-9a-fA-F]+);?|#(\d+);?|([a-zA-Z]+);)/g,
|
|
2429
|
+
(full, hex, dec, name) => {
|
|
2430
|
+
if (hex) return String.fromCodePoint(parseInt(hex, 16));
|
|
2431
|
+
if (dec) return String.fromCodePoint(parseInt(dec, 10));
|
|
2432
|
+
return NAMED_ENTITIES[name] ?? full;
|
|
2433
|
+
}
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
function escapeHtml(text) {
|
|
2437
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2438
|
+
}
|
|
2439
|
+
function sanitizeUrl(value) {
|
|
2440
|
+
const decoded = decodeEntities2(value);
|
|
2441
|
+
const probe = decoded.replace(/[\u0000-\u0020\u007f]/g, "").toLowerCase();
|
|
2442
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/.exec(probe);
|
|
2443
|
+
if (!scheme) return value;
|
|
2444
|
+
return SAFE_SCHEMES.has(scheme[1]) ? value : null;
|
|
2445
|
+
}
|
|
2446
|
+
function sanitizeStyle(value) {
|
|
2447
|
+
return filterCssDeclarations(value, true);
|
|
2448
|
+
}
|
|
2449
|
+
function filterCssDeclarations(value, requirePropAllowlist) {
|
|
2450
|
+
const decoded = decodeEntities2(value);
|
|
2451
|
+
const kept = [];
|
|
2452
|
+
for (const declaration of decoded.split(";")) {
|
|
2453
|
+
const colon = declaration.indexOf(":");
|
|
2454
|
+
if (colon === -1) continue;
|
|
2455
|
+
const prop = declaration.slice(0, colon).trim().toLowerCase();
|
|
2456
|
+
const val = declaration.slice(colon + 1).trim();
|
|
2457
|
+
if (!/^[a-z-]+$/.test(prop)) continue;
|
|
2458
|
+
if (requirePropAllowlist && !SAFE_STYLE_PROPS.has(prop)) continue;
|
|
2459
|
+
if (val === "" || DANGEROUS_CSS_VALUE.test(val)) continue;
|
|
2460
|
+
kept.push(`${prop}: ${val}`);
|
|
2461
|
+
}
|
|
2462
|
+
return kept.length > 0 ? kept.join("; ") : null;
|
|
2463
|
+
}
|
|
2464
|
+
var ATTR_PATTERN = /([^\s=/>"']+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]*)))?/g;
|
|
2465
|
+
function sanitizeOpeningTag(rawOpen) {
|
|
2466
|
+
if (!rawOpen.startsWith("<")) return null;
|
|
2467
|
+
const tagName = extractTagName(rawOpen);
|
|
2468
|
+
if (!/^[a-z][a-z0-9-]*$/.test(tagName) || !SAFE_TAGS.has(tagName)) {
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
const end = rawOpen.endsWith("/>") ? rawOpen.length - 2 : rawOpen.endsWith(">") ? rawOpen.length - 1 : rawOpen.length;
|
|
2472
|
+
const attrSource = rawOpen.slice(1 + tagName.length, end);
|
|
2473
|
+
const attrs = {};
|
|
2474
|
+
for (const match of attrSource.matchAll(ATTR_PATTERN)) {
|
|
2475
|
+
const name = match[1].toLowerCase();
|
|
2476
|
+
const value = match[2] ?? match[3] ?? match[4] ?? "";
|
|
2477
|
+
if (name in attrs) continue;
|
|
2478
|
+
if (name === "style") {
|
|
2479
|
+
const cleanStyle = sanitizeStyle(value);
|
|
2480
|
+
if (cleanStyle !== null) attrs[name] = cleanStyle;
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
const allowed = SAFE_ATTRS.has(name) || /^data-[a-z0-9-]*$/.test(name) || /^aria-[a-z0-9-]*$/.test(name);
|
|
2484
|
+
if (!allowed) continue;
|
|
2485
|
+
if (URL_ATTRS.has(name)) {
|
|
2486
|
+
if (sanitizeUrl(value) === null) continue;
|
|
2487
|
+
}
|
|
2488
|
+
attrs[name] = value;
|
|
2489
|
+
}
|
|
2490
|
+
return serializeOpeningTag(tagName, attrs);
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2302
2493
|
// src/content/serializer/HTMLSerializer.ts
|
|
2303
2494
|
var HTMLSerializer = class {
|
|
2304
2495
|
serialize(unit) {
|
|
@@ -2308,24 +2499,25 @@ var LionRapid = (() => {
|
|
|
2308
2499
|
let lastIndex = 0;
|
|
2309
2500
|
let match;
|
|
2310
2501
|
while ((match = tagPattern.exec(text)) !== null) {
|
|
2311
|
-
result += text.slice(lastIndex, match.index);
|
|
2502
|
+
result += escapeHtml(text.slice(lastIndex, match.index));
|
|
2312
2503
|
const [fullMatch, closingSlash, indexStr] = match;
|
|
2313
2504
|
const styleList = styles[indexStr] ?? [];
|
|
2314
2505
|
const rawOpen = readRawStyle(styleList);
|
|
2315
|
-
|
|
2506
|
+
const safeOpen = rawOpen === null ? null : sanitizeOpeningTag(rawOpen);
|
|
2507
|
+
if (rawOpen && safeOpen !== null) {
|
|
2316
2508
|
const tagName = extractTagName(rawOpen);
|
|
2317
2509
|
const isVoid = VOID_TAGS.has(tagName);
|
|
2318
2510
|
if (closingSlash) {
|
|
2319
2511
|
if (!isVoid) result += `</${tagName}>`;
|
|
2320
2512
|
} else {
|
|
2321
|
-
result +=
|
|
2513
|
+
result += safeOpen;
|
|
2322
2514
|
}
|
|
2323
2515
|
} else {
|
|
2324
2516
|
result += fullMatch;
|
|
2325
2517
|
}
|
|
2326
2518
|
lastIndex = match.index + fullMatch.length;
|
|
2327
2519
|
}
|
|
2328
|
-
result += text.slice(lastIndex);
|
|
2520
|
+
result += escapeHtml(text.slice(lastIndex));
|
|
2329
2521
|
return result;
|
|
2330
2522
|
}
|
|
2331
2523
|
};
|
|
@@ -2564,10 +2756,9 @@ var LionRapid = (() => {
|
|
|
2564
2756
|
var INDEXED_TAG_REGEX = /<\/?[0-9]+\/?>/;
|
|
2565
2757
|
var HTML_TAG_REGEX = /<[a-z][a-z0-9]*(?:\s+[^>]*)?\/?>/i;
|
|
2566
2758
|
var LionRapidCore = class {
|
|
2567
|
-
constructor(eventEmitter, handlerChain, defaultLocale = "en",
|
|
2759
|
+
constructor(eventEmitter, handlerChain, defaultLocale = "en", defaultNamespace) {
|
|
2568
2760
|
__publicField(this, "handlerChain");
|
|
2569
2761
|
__publicField(this, "currentLocale");
|
|
2570
|
-
__publicField(this, "fallbackLocale");
|
|
2571
2762
|
__publicField(this, "defaultNamespace");
|
|
2572
2763
|
__publicField(this, "pendingTranslations", /* @__PURE__ */ new Map());
|
|
2573
2764
|
__publicField(this, "bus");
|
|
@@ -2585,7 +2776,6 @@ var LionRapid = (() => {
|
|
|
2585
2776
|
this.bus = eventEmitter;
|
|
2586
2777
|
this.handlerChain = handlerChain;
|
|
2587
2778
|
this.currentLocale = defaultLocale;
|
|
2588
|
-
this.fallbackLocale = fallbackLocale;
|
|
2589
2779
|
this.defaultNamespace = defaultNamespace;
|
|
2590
2780
|
this.cache = new CacheManager(
|
|
2591
2781
|
this.handlerChain,
|
|
@@ -2650,8 +2840,7 @@ var LionRapid = (() => {
|
|
|
2650
2840
|
this.currentLocale,
|
|
2651
2841
|
firstArg,
|
|
2652
2842
|
secondArg,
|
|
2653
|
-
this.defaultNamespace
|
|
2654
|
-
this.fallbackLocale
|
|
2843
|
+
this.defaultNamespace
|
|
2655
2844
|
);
|
|
2656
2845
|
logger.debug(`Translation requested: ${request.cleanKey}`);
|
|
2657
2846
|
const translateBus = this.bus.translation(request);
|
|
@@ -2701,8 +2890,7 @@ var LionRapid = (() => {
|
|
|
2701
2890
|
this.currentLocale,
|
|
2702
2891
|
firstArg,
|
|
2703
2892
|
secondArg,
|
|
2704
|
-
this.defaultNamespace
|
|
2705
|
-
this.fallbackLocale
|
|
2893
|
+
this.defaultNamespace
|
|
2706
2894
|
);
|
|
2707
2895
|
logger.debug(`HTML translation requested: ${request.cleanKey}`);
|
|
2708
2896
|
const translateBus = this.bus.translation(request);
|
|
@@ -2910,8 +3098,7 @@ var LionRapid = (() => {
|
|
|
2910
3098
|
this.currentLocale,
|
|
2911
3099
|
params,
|
|
2912
3100
|
defaultValue,
|
|
2913
|
-
this.defaultNamespace
|
|
2914
|
-
this.fallbackLocale
|
|
3101
|
+
this.defaultNamespace
|
|
2915
3102
|
);
|
|
2916
3103
|
const syncResult = this.handlerChain.handleSync(request);
|
|
2917
3104
|
const rawValue = syncResult && syncResult.status === 200 ? syncResult.value : request.parsedDefaultValue || request.cleanKey;
|
|
@@ -3032,8 +3219,7 @@ var LionRapid = (() => {
|
|
|
3032
3219
|
this.currentLocale,
|
|
3033
3220
|
void 0,
|
|
3034
3221
|
void 0,
|
|
3035
|
-
this.defaultNamespace
|
|
3036
|
-
this.fallbackLocale
|
|
3222
|
+
this.defaultNamespace
|
|
3037
3223
|
);
|
|
3038
3224
|
return this.handlerChain.isCached(request);
|
|
3039
3225
|
}
|
|
@@ -3143,6 +3329,155 @@ var LionRapid = (() => {
|
|
|
3143
3329
|
}
|
|
3144
3330
|
};
|
|
3145
3331
|
|
|
3332
|
+
// src/repositories/memory-repository.ts
|
|
3333
|
+
var MemoryRepository = class {
|
|
3334
|
+
constructor(config) {
|
|
3335
|
+
__publicField(this, "name", "MemoryRepository");
|
|
3336
|
+
__publicField(this, "type", "memory");
|
|
3337
|
+
__publicField(this, "config", {});
|
|
3338
|
+
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
3339
|
+
__publicField(this, "accessOrder", /* @__PURE__ */ new Map());
|
|
3340
|
+
__publicField(this, "maxSize", 1e3);
|
|
3341
|
+
this.config = config;
|
|
3342
|
+
this.maxSize = this.config.options?.maxSize || 1e3;
|
|
3343
|
+
}
|
|
3344
|
+
get(key) {
|
|
3345
|
+
const value = this.cache.get(key);
|
|
3346
|
+
if (value !== void 0) {
|
|
3347
|
+
this.accessOrder.set(key, Date.now());
|
|
3348
|
+
return value;
|
|
3349
|
+
}
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
set(key, value) {
|
|
3353
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
3354
|
+
this.evictOldest();
|
|
3355
|
+
}
|
|
3356
|
+
this.cache.set(key, value);
|
|
3357
|
+
this.accessOrder.set(key, Date.now());
|
|
3358
|
+
}
|
|
3359
|
+
remove(key) {
|
|
3360
|
+
this.cache.delete(key);
|
|
3361
|
+
this.accessOrder.delete(key);
|
|
3362
|
+
}
|
|
3363
|
+
clear() {
|
|
3364
|
+
this.cache.clear();
|
|
3365
|
+
this.accessOrder.clear();
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Clear all entries for a specific namespace
|
|
3369
|
+
* Removes both the namespace cache entry and all individual translation keys
|
|
3370
|
+
*/
|
|
3371
|
+
clearNamespace(locale, namespace) {
|
|
3372
|
+
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
3373
|
+
const keyPrefix = `${locale}:${namespace}:`;
|
|
3374
|
+
this.cache.delete(namespaceCacheKey);
|
|
3375
|
+
this.accessOrder.delete(namespaceCacheKey);
|
|
3376
|
+
for (const key of this.cache.keys()) {
|
|
3377
|
+
if (key.startsWith(keyPrefix)) {
|
|
3378
|
+
this.cache.delete(key);
|
|
3379
|
+
this.accessOrder.delete(key);
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
getNamespace(locale, namespace) {
|
|
3384
|
+
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
3385
|
+
const namespaceData = this.cache.get(namespaceCacheKey);
|
|
3386
|
+
if (!namespaceData) return null;
|
|
3387
|
+
try {
|
|
3388
|
+
return JSON.parse(namespaceData);
|
|
3389
|
+
} catch (error) {
|
|
3390
|
+
this.cache.delete(namespaceCacheKey);
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
setNamespace(locale, namespace, translations) {
|
|
3395
|
+
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
3396
|
+
this.cache.set(namespaceCacheKey, JSON.stringify(translations));
|
|
3397
|
+
}
|
|
3398
|
+
evictOldest() {
|
|
3399
|
+
let oldestKey = "";
|
|
3400
|
+
let oldestTime = Date.now();
|
|
3401
|
+
for (const [key, time] of this.accessOrder.entries()) {
|
|
3402
|
+
if (time < oldestTime) {
|
|
3403
|
+
oldestTime = time;
|
|
3404
|
+
oldestKey = key;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
if (oldestKey) {
|
|
3408
|
+
this.cache.delete(oldestKey);
|
|
3409
|
+
this.accessOrder.delete(oldestKey);
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
/**
|
|
3413
|
+
* Get all cache entries for debugging
|
|
3414
|
+
*/
|
|
3415
|
+
getEntries() {
|
|
3416
|
+
const entries = {};
|
|
3417
|
+
for (const [key, value] of this.cache.entries()) {
|
|
3418
|
+
entries[key] = value;
|
|
3419
|
+
}
|
|
3420
|
+
return entries;
|
|
3421
|
+
}
|
|
3422
|
+
getConfig() {
|
|
3423
|
+
return this.config;
|
|
3424
|
+
}
|
|
3425
|
+
};
|
|
3426
|
+
|
|
3427
|
+
// src/repositories/static-repository.ts
|
|
3428
|
+
var StaticRepository = class {
|
|
3429
|
+
constructor(bundle, config = {}) {
|
|
3430
|
+
__publicField(this, "name", "StaticRepository");
|
|
3431
|
+
__publicField(this, "type", "memory");
|
|
3432
|
+
__publicField(this, "config");
|
|
3433
|
+
__publicField(this, "mem");
|
|
3434
|
+
this.config = {
|
|
3435
|
+
enabled: config.enabled ?? true,
|
|
3436
|
+
priority: config.priority ?? 0.5,
|
|
3437
|
+
// after the live memory (0), before storage
|
|
3438
|
+
options: {
|
|
3439
|
+
ttl: Number.MAX_SAFE_INTEGER,
|
|
3440
|
+
maxSize: Number.MAX_SAFE_INTEGER,
|
|
3441
|
+
...config.options
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
this.mem = new MemoryRepository({ options: { maxSize: Number.MAX_SAFE_INTEGER } });
|
|
3445
|
+
for (const [locale, namespaces] of Object.entries(bundle)) {
|
|
3446
|
+
for (const [namespace, translations] of Object.entries(namespaces)) {
|
|
3447
|
+
this.mem.setNamespace(locale, namespace, translations);
|
|
3448
|
+
const flat = flattenTranslations(
|
|
3449
|
+
translations,
|
|
3450
|
+
"",
|
|
3451
|
+
locale,
|
|
3452
|
+
namespace
|
|
3453
|
+
);
|
|
3454
|
+
for (const [key, value] of Object.entries(flat)) this.mem.set(key, value);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
get(key) {
|
|
3459
|
+
return this.mem.get(key);
|
|
3460
|
+
}
|
|
3461
|
+
getNamespace(locale, namespace) {
|
|
3462
|
+
return this.mem.getNamespace(locale, namespace);
|
|
3463
|
+
}
|
|
3464
|
+
getEntries() {
|
|
3465
|
+
return this.mem.getEntries();
|
|
3466
|
+
}
|
|
3467
|
+
getConfig() {
|
|
3468
|
+
return this.config;
|
|
3469
|
+
}
|
|
3470
|
+
// Read-only: the live memory layer owns fresh network data; this stays pristine.
|
|
3471
|
+
set() {
|
|
3472
|
+
}
|
|
3473
|
+
remove() {
|
|
3474
|
+
}
|
|
3475
|
+
clear() {
|
|
3476
|
+
}
|
|
3477
|
+
setNamespace() {
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
|
|
3146
3481
|
// src/sync/missing-translation-queue.ts
|
|
3147
3482
|
var COOLDOWN_MS = 6e4;
|
|
3148
3483
|
var MissingTranslationQueue = class {
|
|
@@ -3305,6 +3640,9 @@ var LionRapid = (() => {
|
|
|
3305
3640
|
};
|
|
3306
3641
|
|
|
3307
3642
|
// src/builder.ts
|
|
3643
|
+
function isLoudPreloadFailure(error) {
|
|
3644
|
+
return !(error instanceof TranslationNotFoundError || error instanceof TranslationLockedError);
|
|
3645
|
+
}
|
|
3308
3646
|
var LionRapidBuilder = class _LionRapidBuilder {
|
|
3309
3647
|
constructor(config) {
|
|
3310
3648
|
__publicField(this, "config");
|
|
@@ -3315,7 +3653,6 @@ var LionRapid = (() => {
|
|
|
3315
3653
|
__publicField(this, "syncQueue", null);
|
|
3316
3654
|
this.config = {
|
|
3317
3655
|
defaultLocale: config.defaultLocale || "en",
|
|
3318
|
-
fallbackLocale: config.fallbackLocale || "en",
|
|
3319
3656
|
namespace: config.namespace || "app"
|
|
3320
3657
|
};
|
|
3321
3658
|
}
|
|
@@ -3351,6 +3688,15 @@ var LionRapid = (() => {
|
|
|
3351
3688
|
}
|
|
3352
3689
|
return this;
|
|
3353
3690
|
}
|
|
3691
|
+
/**
|
|
3692
|
+
* Offline resilience: seed a translation bundle (or the CLI's snapshot files)
|
|
3693
|
+
* as a read-only sync layer that resolves synchronously even with the server
|
|
3694
|
+
* down and nothing cached. Sits after the live memory layer (priority 0) at
|
|
3695
|
+
* `priority` (default 0.5), so fresh network data always shadows it.
|
|
3696
|
+
*/
|
|
3697
|
+
useStaticBundle(bundle, priority = 0.5) {
|
|
3698
|
+
return this.use(new StaticRepository(bundle, { priority }));
|
|
3699
|
+
}
|
|
3354
3700
|
async build() {
|
|
3355
3701
|
const eventEmitter = new EventEmitter();
|
|
3356
3702
|
const buildBus = eventEmitter.builder();
|
|
@@ -3407,7 +3753,6 @@ var LionRapid = (() => {
|
|
|
3407
3753
|
eventEmitter,
|
|
3408
3754
|
handlerChain,
|
|
3409
3755
|
this.config.defaultLocale,
|
|
3410
|
-
this.config.fallbackLocale,
|
|
3411
3756
|
this.config.namespace
|
|
3412
3757
|
);
|
|
3413
3758
|
for (const plugin of this.plugins) {
|
|
@@ -3438,7 +3783,17 @@ var LionRapid = (() => {
|
|
|
3438
3783
|
logger.debug("Preload targets", {
|
|
3439
3784
|
namespaces: eagerPreloads.map((c) => `${c.locale}:${c.namespace}`)
|
|
3440
3785
|
});
|
|
3786
|
+
let warnedLoudly = false;
|
|
3787
|
+
const warnLoudlyOnce = (namespaceKey, error) => {
|
|
3788
|
+
if (!isLoudPreloadFailure(error) || warnedLoudly) return;
|
|
3789
|
+
warnedLoudly = true;
|
|
3790
|
+
console.warn(
|
|
3791
|
+
`[LionRapid] Could not load translations for ${namespaceKey} \u2014 the page stays untranslated until the server is reachable.`,
|
|
3792
|
+
error
|
|
3793
|
+
);
|
|
3794
|
+
};
|
|
3441
3795
|
const promises = eagerPreloads.map(async (preloadConfig) => {
|
|
3796
|
+
const namespaceKey = `${preloadConfig.locale}:${preloadConfig.namespace}`;
|
|
3442
3797
|
try {
|
|
3443
3798
|
const result = await networkHandler.loadNamespace(
|
|
3444
3799
|
preloadConfig.locale,
|
|
@@ -3447,19 +3802,19 @@ var LionRapid = (() => {
|
|
|
3447
3802
|
if (result && result.data && result.status === 200) {
|
|
3448
3803
|
const context = {
|
|
3449
3804
|
locale: preloadConfig.locale,
|
|
3450
|
-
namespace: preloadConfig.namespace
|
|
3451
|
-
fallbackLocale: this.config.fallbackLocale
|
|
3805
|
+
namespace: preloadConfig.namespace
|
|
3452
3806
|
};
|
|
3453
3807
|
await handlerChain.saveNamespaceToHandlers(result.data, context);
|
|
3454
|
-
logger.debug(
|
|
3455
|
-
|
|
3456
|
-
|
|
3808
|
+
logger.debug(`Preloaded: ${namespaceKey}`);
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
if (result?.error) {
|
|
3812
|
+
warnLoudlyOnce(namespaceKey, result.error);
|
|
3813
|
+
logger.warn(`Preload failed: ${namespaceKey}`, result.error);
|
|
3457
3814
|
}
|
|
3458
3815
|
} catch (error) {
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
error
|
|
3462
|
-
);
|
|
3816
|
+
warnLoudlyOnce(namespaceKey, error);
|
|
3817
|
+
logger.warn(`Preload failed: ${namespaceKey}`, error);
|
|
3463
3818
|
}
|
|
3464
3819
|
});
|
|
3465
3820
|
await Promise.allSettled(promises);
|
|
@@ -3473,8 +3828,16 @@ var LionRapid = (() => {
|
|
|
3473
3828
|
__publicField(this, "name", "NetworkRepository");
|
|
3474
3829
|
__publicField(this, "type", "network");
|
|
3475
3830
|
__publicField(this, "config", {});
|
|
3476
|
-
/**
|
|
3477
|
-
|
|
3831
|
+
/**
|
|
3832
|
+
* Namespace snapshot per key "locale:namespace": the server version TOGETHER
|
|
3833
|
+
* with the payload it describes, persisted as ONE localStorage entry.
|
|
3834
|
+
*
|
|
3835
|
+
* Canonical 304 contract (same as edge KV bundle + WP transient cache):
|
|
3836
|
+
* never claim freshness you can't back. If-None-Match is only sent when the
|
|
3837
|
+
* snapshot is in hand; a 304 answers with that snapshot's payload; version
|
|
3838
|
+
* and payload can never desync because they live and die together.
|
|
3839
|
+
*/
|
|
3840
|
+
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3478
3841
|
/**
|
|
3479
3842
|
* Default endpoint templates with placeholder support
|
|
3480
3843
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3587,36 +3950,50 @@ var LionRapid = (() => {
|
|
|
3587
3950
|
logger.debug("API base URL is not set");
|
|
3588
3951
|
throw new NetworkError("API base URL is not set");
|
|
3589
3952
|
}
|
|
3953
|
+
try {
|
|
3954
|
+
return await this.fetchNamespaceRaw(locale, namespace);
|
|
3955
|
+
} catch (error) {
|
|
3956
|
+
this.handleCaughtError(error);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Shared namespace fetch with the canonical 304 contract:
|
|
3961
|
+
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
3962
|
+
* - 304 answers with that snapshot's payload — never with "nothing".
|
|
3963
|
+
* - A 304 with no snapshot (server/proxy surprise) refetches unconditionally.
|
|
3964
|
+
* - A 200 stores version + payload atomically before either is trusted.
|
|
3965
|
+
*/
|
|
3966
|
+
async fetchNamespaceRaw(locale, namespace, allowConditional = true) {
|
|
3590
3967
|
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3591
3968
|
logger.debug(`API GET Namespace: ${url}`);
|
|
3592
3969
|
const namespaceKey = `${locale}:${namespace}`;
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
if (
|
|
3601
|
-
logger.debug(`Namespace ${namespaceKey}: 304 Not Modified`);
|
|
3602
|
-
return
|
|
3603
|
-
}
|
|
3604
|
-
if (!response.ok) {
|
|
3605
|
-
this.handleHttpError(response, namespaceKey);
|
|
3606
|
-
}
|
|
3607
|
-
const data = await response.json();
|
|
3608
|
-
logger.debug("API response data:", data);
|
|
3609
|
-
if (!data.translations) {
|
|
3610
|
-
throw new TranslationNotFoundError(namespaceKey);
|
|
3611
|
-
}
|
|
3612
|
-
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3613
|
-
if (version) {
|
|
3614
|
-
this.setVersion(namespaceKey, version);
|
|
3970
|
+
const snapshot = allowConditional ? this.getSnapshot(namespaceKey) : void 0;
|
|
3971
|
+
const extraHeaders = {};
|
|
3972
|
+
if (snapshot) {
|
|
3973
|
+
extraHeaders["If-None-Match"] = `W/"${snapshot.version}"`;
|
|
3974
|
+
}
|
|
3975
|
+
const response = await this.makeRequest(url, "GET", void 0, extraHeaders);
|
|
3976
|
+
if (response.status === 304) {
|
|
3977
|
+
if (snapshot) {
|
|
3978
|
+
logger.debug(`Namespace ${namespaceKey}: 304 Not Modified (snapshot served)`);
|
|
3979
|
+
return snapshot.translations;
|
|
3615
3980
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3981
|
+
logger.debug(`Namespace ${namespaceKey}: unexpected 304 with no snapshot \u2014 refetching`);
|
|
3982
|
+
return this.fetchNamespaceRaw(locale, namespace, false);
|
|
3983
|
+
}
|
|
3984
|
+
if (!response.ok) {
|
|
3985
|
+
this.handleHttpError(response, namespaceKey);
|
|
3619
3986
|
}
|
|
3987
|
+
const data = await response.json();
|
|
3988
|
+
logger.debug("API response data:", data);
|
|
3989
|
+
if (!data.translations) {
|
|
3990
|
+
throw new TranslationNotFoundError(namespaceKey);
|
|
3991
|
+
}
|
|
3992
|
+
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3993
|
+
if (version) {
|
|
3994
|
+
this.setSnapshot(namespaceKey, { version, translations: data.translations });
|
|
3995
|
+
}
|
|
3996
|
+
return data.translations;
|
|
3620
3997
|
}
|
|
3621
3998
|
/**
|
|
3622
3999
|
* Save translation to API (if supported)
|
|
@@ -3701,33 +4078,8 @@ var LionRapid = (() => {
|
|
|
3701
4078
|
logger.debug("API base URL is not set");
|
|
3702
4079
|
throw new NetworkError("API base URL is not set");
|
|
3703
4080
|
}
|
|
3704
|
-
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3705
|
-
logger.debug(`API GET Namespace ContentUnits: ${url}`);
|
|
3706
|
-
const namespaceKey = `${locale}:${namespace}`;
|
|
3707
4081
|
try {
|
|
3708
|
-
|
|
3709
|
-
const cachedVersion = this.getVersion(namespaceKey);
|
|
3710
|
-
if (cachedVersion) {
|
|
3711
|
-
extraHeaders["If-None-Match"] = `W/"${cachedVersion}"`;
|
|
3712
|
-
}
|
|
3713
|
-
const response = await this.makeRequest(url, "GET", void 0, extraHeaders);
|
|
3714
|
-
if (response.status === 304) {
|
|
3715
|
-
logger.debug(`Namespace ContentUnits ${namespaceKey}: 304 Not Modified`);
|
|
3716
|
-
return null;
|
|
3717
|
-
}
|
|
3718
|
-
if (!response.ok) {
|
|
3719
|
-
this.handleHttpError(response, namespaceKey);
|
|
3720
|
-
}
|
|
3721
|
-
const data = await response.json();
|
|
3722
|
-
logger.debug("API response data:", data);
|
|
3723
|
-
if (!data.translations) {
|
|
3724
|
-
throw new TranslationNotFoundError(namespaceKey);
|
|
3725
|
-
}
|
|
3726
|
-
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3727
|
-
if (version) {
|
|
3728
|
-
this.setVersion(namespaceKey, version);
|
|
3729
|
-
}
|
|
3730
|
-
return data.translations;
|
|
4082
|
+
return await this.fetchNamespaceRaw(locale, namespace);
|
|
3731
4083
|
} catch (error) {
|
|
3732
4084
|
this.handleCaughtError(error);
|
|
3733
4085
|
}
|
|
@@ -3837,25 +4189,37 @@ var LionRapid = (() => {
|
|
|
3837
4189
|
getConfig() {
|
|
3838
4190
|
return this.config;
|
|
3839
4191
|
}
|
|
3840
|
-
/** Read persisted version
|
|
3841
|
-
|
|
3842
|
-
|
|
4192
|
+
/** Read a persisted snapshot (version + payload), falling back to memory.
|
|
4193
|
+
* Legacy bare-version entries and corrupt snapshots are deleted on sight —
|
|
4194
|
+
* a version without its payload must never drive a conditional request. */
|
|
4195
|
+
getSnapshot(namespaceKey) {
|
|
4196
|
+
const mem = this.namespaceSnapshots.get(namespaceKey);
|
|
3843
4197
|
if (mem) return mem;
|
|
3844
4198
|
try {
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
4199
|
+
localStorage.removeItem(_NetworkRepository.LEGACY_VERSION_PREFIX + namespaceKey);
|
|
4200
|
+
const raw = localStorage.getItem(_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey);
|
|
4201
|
+
if (raw) {
|
|
4202
|
+
const parsed = JSON.parse(raw);
|
|
4203
|
+
if (parsed && typeof parsed.version === "string" && parsed.version.length > 0 && parsed.translations && typeof parsed.translations === "object") {
|
|
4204
|
+
this.namespaceSnapshots.set(namespaceKey, parsed);
|
|
4205
|
+
return parsed;
|
|
4206
|
+
}
|
|
4207
|
+
localStorage.removeItem(_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey);
|
|
3849
4208
|
}
|
|
3850
4209
|
} catch {
|
|
3851
4210
|
}
|
|
3852
4211
|
return void 0;
|
|
3853
4212
|
}
|
|
3854
|
-
/** Persist version
|
|
3855
|
-
|
|
3856
|
-
|
|
4213
|
+
/** Persist version + payload as ONE entry (memory + localStorage). On quota
|
|
4214
|
+
* failure the previous stored snapshot stays — still a consistent pair. */
|
|
4215
|
+
setSnapshot(namespaceKey, snapshot) {
|
|
4216
|
+
this.namespaceSnapshots.set(namespaceKey, snapshot);
|
|
3857
4217
|
try {
|
|
3858
|
-
localStorage.setItem(
|
|
4218
|
+
localStorage.setItem(
|
|
4219
|
+
_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey,
|
|
4220
|
+
JSON.stringify(snapshot)
|
|
4221
|
+
);
|
|
4222
|
+
localStorage.removeItem(_NetworkRepository.LEGACY_VERSION_PREFIX + namespaceKey);
|
|
3859
4223
|
} catch {
|
|
3860
4224
|
}
|
|
3861
4225
|
}
|
|
@@ -3944,105 +4308,13 @@ var LionRapid = (() => {
|
|
|
3944
4308
|
}
|
|
3945
4309
|
}
|
|
3946
4310
|
};
|
|
3947
|
-
/** localStorage key prefix for persisted
|
|
3948
|
-
__publicField(_NetworkRepository, "
|
|
4311
|
+
/** localStorage key prefix for persisted snapshots (version + payload) */
|
|
4312
|
+
__publicField(_NetworkRepository, "SNAPSHOT_PREFIX", "lionrapid:ns:");
|
|
4313
|
+
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4314
|
+
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4315
|
+
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
3949
4316
|
var NetworkRepository = _NetworkRepository;
|
|
3950
4317
|
|
|
3951
|
-
// src/repositories/memory-repository.ts
|
|
3952
|
-
var MemoryRepository = class {
|
|
3953
|
-
constructor(config) {
|
|
3954
|
-
__publicField(this, "name", "MemoryRepository");
|
|
3955
|
-
__publicField(this, "type", "memory");
|
|
3956
|
-
__publicField(this, "config", {});
|
|
3957
|
-
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
3958
|
-
__publicField(this, "accessOrder", /* @__PURE__ */ new Map());
|
|
3959
|
-
__publicField(this, "maxSize", 1e3);
|
|
3960
|
-
this.config = config;
|
|
3961
|
-
this.maxSize = this.config.options?.maxSize || 1e3;
|
|
3962
|
-
}
|
|
3963
|
-
get(key) {
|
|
3964
|
-
const value = this.cache.get(key);
|
|
3965
|
-
if (value !== void 0) {
|
|
3966
|
-
this.accessOrder.set(key, Date.now());
|
|
3967
|
-
return value;
|
|
3968
|
-
}
|
|
3969
|
-
return null;
|
|
3970
|
-
}
|
|
3971
|
-
set(key, value) {
|
|
3972
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
3973
|
-
this.evictOldest();
|
|
3974
|
-
}
|
|
3975
|
-
this.cache.set(key, value);
|
|
3976
|
-
this.accessOrder.set(key, Date.now());
|
|
3977
|
-
}
|
|
3978
|
-
remove(key) {
|
|
3979
|
-
this.cache.delete(key);
|
|
3980
|
-
this.accessOrder.delete(key);
|
|
3981
|
-
}
|
|
3982
|
-
clear() {
|
|
3983
|
-
this.cache.clear();
|
|
3984
|
-
this.accessOrder.clear();
|
|
3985
|
-
}
|
|
3986
|
-
/**
|
|
3987
|
-
* Clear all entries for a specific namespace
|
|
3988
|
-
* Removes both the namespace cache entry and all individual translation keys
|
|
3989
|
-
*/
|
|
3990
|
-
clearNamespace(locale, namespace) {
|
|
3991
|
-
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
3992
|
-
const keyPrefix = `${locale}:${namespace}:`;
|
|
3993
|
-
this.cache.delete(namespaceCacheKey);
|
|
3994
|
-
this.accessOrder.delete(namespaceCacheKey);
|
|
3995
|
-
for (const key of this.cache.keys()) {
|
|
3996
|
-
if (key.startsWith(keyPrefix)) {
|
|
3997
|
-
this.cache.delete(key);
|
|
3998
|
-
this.accessOrder.delete(key);
|
|
3999
|
-
}
|
|
4000
|
-
}
|
|
4001
|
-
}
|
|
4002
|
-
getNamespace(locale, namespace) {
|
|
4003
|
-
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
4004
|
-
const namespaceData = this.cache.get(namespaceCacheKey);
|
|
4005
|
-
if (!namespaceData) return null;
|
|
4006
|
-
try {
|
|
4007
|
-
return JSON.parse(namespaceData);
|
|
4008
|
-
} catch (error) {
|
|
4009
|
-
this.cache.delete(namespaceCacheKey);
|
|
4010
|
-
return null;
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
setNamespace(locale, namespace, translations) {
|
|
4014
|
-
const namespaceCacheKey = createNamespaceCacheKey(locale, namespace);
|
|
4015
|
-
this.cache.set(namespaceCacheKey, JSON.stringify(translations));
|
|
4016
|
-
}
|
|
4017
|
-
evictOldest() {
|
|
4018
|
-
let oldestKey = "";
|
|
4019
|
-
let oldestTime = Date.now();
|
|
4020
|
-
for (const [key, time] of this.accessOrder.entries()) {
|
|
4021
|
-
if (time < oldestTime) {
|
|
4022
|
-
oldestTime = time;
|
|
4023
|
-
oldestKey = key;
|
|
4024
|
-
}
|
|
4025
|
-
}
|
|
4026
|
-
if (oldestKey) {
|
|
4027
|
-
this.cache.delete(oldestKey);
|
|
4028
|
-
this.accessOrder.delete(oldestKey);
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
|
-
/**
|
|
4032
|
-
* Get all cache entries for debugging
|
|
4033
|
-
*/
|
|
4034
|
-
getEntries() {
|
|
4035
|
-
const entries = {};
|
|
4036
|
-
for (const [key, value] of this.cache.entries()) {
|
|
4037
|
-
entries[key] = value;
|
|
4038
|
-
}
|
|
4039
|
-
return entries;
|
|
4040
|
-
}
|
|
4041
|
-
getConfig() {
|
|
4042
|
-
return this.config;
|
|
4043
|
-
}
|
|
4044
|
-
};
|
|
4045
|
-
|
|
4046
4318
|
// src/browser/exclude-filter.ts
|
|
4047
4319
|
var DEFAULT_PATTERNS = {
|
|
4048
4320
|
emails: true,
|
|
@@ -4433,7 +4705,7 @@ var LionRapid = (() => {
|
|
|
4433
4705
|
isInsideSkipTag(element) {
|
|
4434
4706
|
let current = element.parentElement;
|
|
4435
4707
|
while (current) {
|
|
4436
|
-
if (SKIP_TAGS.has(current.tagName)) {
|
|
4708
|
+
if (SKIP_TAGS.has(current.tagName.toUpperCase())) {
|
|
4437
4709
|
return true;
|
|
4438
4710
|
}
|
|
4439
4711
|
current = current.parentElement;
|
|
@@ -4547,7 +4819,7 @@ var LionRapid = (() => {
|
|
|
4547
4819
|
isInsideSkipTag(element) {
|
|
4548
4820
|
let current = element.parentElement;
|
|
4549
4821
|
while (current) {
|
|
4550
|
-
if (SKIP_TAGS2.has(current.tagName)) return true;
|
|
4822
|
+
if (SKIP_TAGS2.has(current.tagName.toUpperCase())) return true;
|
|
4551
4823
|
current = current.parentElement;
|
|
4552
4824
|
}
|
|
4553
4825
|
return false;
|
|
@@ -5660,6 +5932,50 @@ var LionRapid = (() => {
|
|
|
5660
5932
|
};
|
|
5661
5933
|
|
|
5662
5934
|
// src/browser/auto-init.ts
|
|
5935
|
+
function resolveWritePolicy(apiKey) {
|
|
5936
|
+
const isPublishable = apiKey.startsWith("pk_");
|
|
5937
|
+
return {
|
|
5938
|
+
syncMissing: { enabled: isPublishable },
|
|
5939
|
+
warnReadOnly: !isPublishable
|
|
5940
|
+
};
|
|
5941
|
+
}
|
|
5942
|
+
function configForWindow(config) {
|
|
5943
|
+
return { ...config, apiKey: "" };
|
|
5944
|
+
}
|
|
5945
|
+
function createDeferredLocaleHandler(effects) {
|
|
5946
|
+
let applyLocale = null;
|
|
5947
|
+
let pendingLocale = null;
|
|
5948
|
+
return {
|
|
5949
|
+
onChange: async (locale) => {
|
|
5950
|
+
effects.setCookie(locale);
|
|
5951
|
+
if (effects.navigate) {
|
|
5952
|
+
effects.navigate(locale);
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
if (applyLocale) {
|
|
5956
|
+
await applyLocale(locale);
|
|
5957
|
+
return;
|
|
5958
|
+
}
|
|
5959
|
+
pendingLocale = locale;
|
|
5960
|
+
},
|
|
5961
|
+
attach: (apply) => {
|
|
5962
|
+
applyLocale = apply;
|
|
5963
|
+
if (pendingLocale !== null) {
|
|
5964
|
+
const replay = pendingLocale;
|
|
5965
|
+
pendingLocale = null;
|
|
5966
|
+
void apply(replay).catch(() => {
|
|
5967
|
+
});
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
};
|
|
5971
|
+
}
|
|
5972
|
+
function onDomReady(fn) {
|
|
5973
|
+
if (document.readyState === "loading") {
|
|
5974
|
+
document.addEventListener("DOMContentLoaded", fn, { once: true });
|
|
5975
|
+
} else {
|
|
5976
|
+
fn();
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5663
5979
|
function parseConfig(script) {
|
|
5664
5980
|
const apiKey = script.dataset.apiKey || "";
|
|
5665
5981
|
const baseUrl = script.dataset.baseUrl || "https://api.lionrapid.com";
|
|
@@ -5675,6 +5991,7 @@ var LionRapid = (() => {
|
|
|
5675
5991
|
const switcherShowNames = script.dataset.switcherShowNames === "true";
|
|
5676
5992
|
const switcherCustomCss = script.dataset.switcherCustomCss || void 0;
|
|
5677
5993
|
const editorCdnUrl = script.dataset.editorCdn || void 0;
|
|
5994
|
+
const offline = script.dataset.offline !== "false";
|
|
5678
5995
|
return {
|
|
5679
5996
|
apiKey,
|
|
5680
5997
|
baseUrl,
|
|
@@ -5689,28 +6006,58 @@ var LionRapid = (() => {
|
|
|
5689
6006
|
switcherShowFlags,
|
|
5690
6007
|
switcherShowNames,
|
|
5691
6008
|
switcherCustomCss,
|
|
5692
|
-
editorCdnUrl
|
|
6009
|
+
editorCdnUrl,
|
|
6010
|
+
offline
|
|
5693
6011
|
};
|
|
5694
6012
|
}
|
|
5695
6013
|
function readLocaleCookie(availableLanguages) {
|
|
5696
6014
|
if (typeof document === "undefined") return null;
|
|
5697
|
-
const match = document.cookie.match(
|
|
6015
|
+
const match = document.cookie.match(
|
|
6016
|
+
/lionrapid_locale=([a-z]{2,3}(?:-[a-z0-9]{2,4})*)/i
|
|
6017
|
+
);
|
|
5698
6018
|
if (!match) return null;
|
|
5699
|
-
return
|
|
6019
|
+
return matchLocale([match[1]], availableLanguages, "") || null;
|
|
5700
6020
|
}
|
|
5701
6021
|
function detectBrowserLanguage(availableLanguages, defaultLocale) {
|
|
5702
6022
|
if (typeof navigator === "undefined") return defaultLocale;
|
|
5703
|
-
const
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
}
|
|
5708
|
-
return defaultLocale;
|
|
6023
|
+
const requested = navigator.languages && navigator.languages.length > 0 ? [...navigator.languages] : [
|
|
6024
|
+
navigator.language || navigator.userLanguage || ""
|
|
6025
|
+
];
|
|
6026
|
+
return matchLocale(requested, availableLanguages, defaultLocale);
|
|
5709
6027
|
}
|
|
5710
|
-
function
|
|
5711
|
-
const
|
|
6028
|
+
function buildLocaleUrl(pathname, search, locale, defaultLocale, availableLanguages) {
|
|
6029
|
+
const segments = pathname.split("/");
|
|
6030
|
+
const first = segments[1] ?? "";
|
|
6031
|
+
const hasLocalePrefix = LOCALE_TAG_REGEX.test(first) && matchLocale([first], availableLanguages, "") !== "";
|
|
6032
|
+
const path = hasLocalePrefix ? "/" + segments.slice(2).join("/") : pathname;
|
|
5712
6033
|
const prefix = locale === defaultLocale ? "" : "/" + locale;
|
|
5713
|
-
|
|
6034
|
+
return prefix + (path || "/") + search;
|
|
6035
|
+
}
|
|
6036
|
+
function navigateToLocale(locale, defaultLocale, availableLanguages) {
|
|
6037
|
+
window.location.href = buildLocaleUrl(
|
|
6038
|
+
window.location.pathname,
|
|
6039
|
+
window.location.search,
|
|
6040
|
+
locale,
|
|
6041
|
+
defaultLocale,
|
|
6042
|
+
availableLanguages
|
|
6043
|
+
);
|
|
6044
|
+
}
|
|
6045
|
+
function resolveInitialLocale(config) {
|
|
6046
|
+
if (config.mode === "dynamic-only") {
|
|
6047
|
+
const htmlLang = typeof document !== "undefined" ? document.documentElement.lang : "";
|
|
6048
|
+
return htmlLang ? normalizeLocale(htmlLang) : config.defaultLocale;
|
|
6049
|
+
}
|
|
6050
|
+
const cookieLocale = readLocaleCookie(config.languages);
|
|
6051
|
+
if (cookieLocale) return cookieLocale;
|
|
6052
|
+
if (config.autoDetect) {
|
|
6053
|
+
return detectBrowserLanguage(config.languages, config.defaultLocale);
|
|
6054
|
+
}
|
|
6055
|
+
return config.defaultLocale;
|
|
6056
|
+
}
|
|
6057
|
+
function applyLocaleDir(locale) {
|
|
6058
|
+
if (typeof document !== "undefined") {
|
|
6059
|
+
document.documentElement.dir = localeDir(locale);
|
|
6060
|
+
}
|
|
5714
6061
|
}
|
|
5715
6062
|
async function autoInit() {
|
|
5716
6063
|
const scripts = document.querySelectorAll("script[data-api-key]");
|
|
@@ -5720,24 +6067,19 @@ var LionRapid = (() => {
|
|
|
5720
6067
|
return;
|
|
5721
6068
|
}
|
|
5722
6069
|
const config = parseConfig(script);
|
|
5723
|
-
window.__lionrapidConfig = config;
|
|
6070
|
+
window.__lionrapidConfig = configForWindow(config);
|
|
5724
6071
|
if (!config.apiKey) {
|
|
5725
6072
|
console.warn("[LionRapid] Missing data-api-key attribute");
|
|
5726
6073
|
return;
|
|
5727
6074
|
}
|
|
5728
|
-
|
|
5729
|
-
if (
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
const cookieLocale = readLocaleCookie(config.languages);
|
|
5734
|
-
if (cookieLocale) {
|
|
5735
|
-
initialLocale = cookieLocale;
|
|
5736
|
-
} else if (config.autoDetect) {
|
|
5737
|
-
initialLocale = detectBrowserLanguage(config.languages, config.defaultLocale);
|
|
5738
|
-
}
|
|
6075
|
+
const writePolicy = resolveWritePolicy(config.apiKey);
|
|
6076
|
+
if (writePolicy.warnReadOnly) {
|
|
6077
|
+
console.error(
|
|
6078
|
+
"[LionRapid] data-api-key must be a publishable pk_ key; running read-only"
|
|
6079
|
+
);
|
|
5739
6080
|
}
|
|
5740
|
-
const
|
|
6081
|
+
const initialLocale = resolveInitialLocale(config);
|
|
6082
|
+
applyLocaleDir(initialLocale);
|
|
5741
6083
|
const languageSwitcher = new LanguageSwitcher();
|
|
5742
6084
|
languageSwitcher.setLanguages(config.languages);
|
|
5743
6085
|
languageSwitcher.setInitialLocale(initialLocale);
|
|
@@ -5763,26 +6105,39 @@ var LionRapid = (() => {
|
|
|
5763
6105
|
}
|
|
5764
6106
|
languageSwitcher.initAll();
|
|
5765
6107
|
};
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
6108
|
+
onDomReady(renderSwitcher);
|
|
6109
|
+
const localeHandler = createDeferredLocaleHandler({
|
|
6110
|
+
setCookie: (locale) => {
|
|
6111
|
+
document.cookie = `lionrapid_locale=${locale}; path=/; max-age=31536000; SameSite=Lax`;
|
|
6112
|
+
},
|
|
6113
|
+
navigate: config.mode === "dynamic-only" ? (locale) => navigateToLocale(locale, config.defaultLocale, config.languages) : null
|
|
6114
|
+
});
|
|
6115
|
+
languageSwitcher.setOnLanguageChange(localeHandler.onChange);
|
|
5771
6116
|
try {
|
|
5772
|
-
const
|
|
6117
|
+
const builder = LionRapidBuilder.init({
|
|
5773
6118
|
defaultLocale: initialLocale,
|
|
5774
6119
|
namespace: config.namespace
|
|
5775
|
-
}).use(new MemoryRepository({ enabled: true })).use(new LocalStorageRepository({ enabled: true })).use(new CacheStorageRepository({ enabled: true }))
|
|
6120
|
+
}).use(new MemoryRepository({ enabled: true })).use(new LocalStorageRepository({ enabled: true })).use(new CacheStorageRepository({ enabled: true }));
|
|
6121
|
+
if (config.offline !== false) {
|
|
6122
|
+
builder.use(
|
|
6123
|
+
new CacheStorageRepository({
|
|
6124
|
+
enabled: true,
|
|
6125
|
+
options: {
|
|
6126
|
+
ttl: Number.MAX_SAFE_INTEGER,
|
|
6127
|
+
cacheName: "lionrapid-offline-v1"
|
|
6128
|
+
}
|
|
6129
|
+
})
|
|
6130
|
+
);
|
|
6131
|
+
}
|
|
6132
|
+
const core = await builder.use(
|
|
5776
6133
|
new NetworkRepository({
|
|
5777
6134
|
enabled: true,
|
|
5778
6135
|
options: {
|
|
5779
6136
|
baseUrl: config.baseUrl,
|
|
5780
6137
|
apiKey: config.apiKey,
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
batchSize: 50
|
|
5785
|
-
},
|
|
6138
|
+
// pk_ keys sync missing source strings (discovery); anything else
|
|
6139
|
+
// runs read-only — see resolveWritePolicy.
|
|
6140
|
+
syncMissing: writePolicy.syncMissing,
|
|
5786
6141
|
preloadNamespaces: [
|
|
5787
6142
|
{
|
|
5788
6143
|
locale: initialLocale,
|
|
@@ -5803,15 +6158,11 @@ var LionRapid = (() => {
|
|
|
5803
6158
|
sourceLocale: config.defaultLocale
|
|
5804
6159
|
});
|
|
5805
6160
|
languageSwitcher.setCore(core);
|
|
5806
|
-
|
|
5807
|
-
document.cookie = `lionrapid_locale=${locale}; path=/; max-age=31536000; SameSite=Lax`;
|
|
5808
|
-
if (config.mode === "dynamic-only") {
|
|
5809
|
-
navigateToLocale(locale, config.defaultLocale);
|
|
5810
|
-
return;
|
|
5811
|
-
}
|
|
6161
|
+
localeHandler.attach(async (locale) => {
|
|
5812
6162
|
await core.changeLanguage(locale, {
|
|
5813
6163
|
preloadNamespaces: [config.namespace]
|
|
5814
6164
|
});
|
|
6165
|
+
applyLocaleDir(locale);
|
|
5815
6166
|
autoTranslator.updateLocale(locale);
|
|
5816
6167
|
autoTranslator.retranslate();
|
|
5817
6168
|
});
|
|
@@ -5858,6 +6209,7 @@ var LionRapid = (() => {
|
|
|
5858
6209
|
await core.changeLanguage(locale, {
|
|
5859
6210
|
preloadNamespaces: [config.namespace]
|
|
5860
6211
|
});
|
|
6212
|
+
applyLocaleDir(locale);
|
|
5861
6213
|
autoTranslator.updateLocale(locale);
|
|
5862
6214
|
autoTranslator.retranslate();
|
|
5863
6215
|
languageSwitcher.initAll();
|
|
@@ -5880,10 +6232,12 @@ var LionRapid = (() => {
|
|
|
5880
6232
|
editorScript.async = true;
|
|
5881
6233
|
document.body.appendChild(editorScript);
|
|
5882
6234
|
}
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
6235
|
+
onDomReady(() => {
|
|
6236
|
+
if (config.mode !== "dynamic-only") {
|
|
6237
|
+
autoTranslator.translatePage();
|
|
6238
|
+
}
|
|
6239
|
+
autoTranslator.observe();
|
|
6240
|
+
});
|
|
5887
6241
|
} catch (error) {
|
|
5888
6242
|
console.error("[LionRapid] Initialization failed:", error);
|
|
5889
6243
|
}
|