@lionrapid/core 0.6.0 → 0.6.2
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/browser/auto-init.d.ts +17 -3
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +57 -30
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +285 -27
- package/dist/browser/lionrapid.auto.dev.js.map +2 -2
- package/dist/browser/lionrapid.auto.js +285 -27
- package/dist/browser/lionrapid.auto.js.map +2 -2
- package/dist/browser/lionrapid.auto.min.js +4 -4
- package/dist/browser/lionrapid.auto.min.js.map +3 -3
- package/dist/browser/lionrapid.esm.js +244 -2
- package/dist/browser/lionrapid.esm.js.map +2 -2
- package/dist/browser/lionrapid.esm.min.js +3 -3
- package/dist/browser/lionrapid.esm.min.js.map +3 -3
- package/dist/browser/lionrapid.iife.dev.js +244 -2
- package/dist/browser/lionrapid.iife.dev.js.map +2 -2
- package/dist/browser/lionrapid.iife.js +244 -2
- package/dist/browser/lionrapid.iife.js.map +2 -2
- package/dist/browser/lionrapid.iife.min.js +3 -3
- package/dist/browser/lionrapid.iife.min.js.map +3 -3
- package/dist/browser/lionrapid.umd.js +244 -2
- package/dist/browser/lionrapid.umd.js.map +2 -2
- package/dist/browser/lionrapid.umd.min.js +3 -3
- package/dist/browser/lionrapid.umd.min.js.map +3 -3
- package/dist/browser/storage/cache-storage-repository.d.ts +65 -0
- package/dist/browser/storage/cache-storage-repository.d.ts.map +1 -1
- package/dist/browser/storage/cache-storage-repository.js +77 -7
- package/dist/browser/storage/cache-storage-repository.js.map +1 -1
- package/dist/browser/storage/localstorage-repository.d.ts +62 -0
- package/dist/browser/storage/localstorage-repository.d.ts.map +1 -1
- package/dist/browser/storage/localstorage-repository.js +71 -4
- package/dist/browser/storage/localstorage-repository.js.map +1 -1
- package/dist/config/repository.config.d.ts +17 -0
- package/dist/config/repository.config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lionRapid.d.ts +1 -0
- package/dist/lionRapid.d.ts.map +1 -1
- package/dist/lionRapid.js +14 -0
- package/dist/lionRapid.js.map +1 -1
- package/dist/repositories/network-repository.d.ts +42 -1
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +149 -1
- package/dist/repositories/network-repository.js.map +1 -1
- package/package.json +1 -1
|
@@ -2766,6 +2766,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
2766
2766
|
// 🔥 EMBEDDED: Core integration maps
|
|
2767
2767
|
__publicField(this, "formatters", /* @__PURE__ */ new Map());
|
|
2768
2768
|
__publicField(this, "processors", /* @__PURE__ */ new Map());
|
|
2769
|
+
__publicField(this, "warnedNoInterpolate", false);
|
|
2769
2770
|
// 📡 EVENT-DRIVEN: Just track for cleanup
|
|
2770
2771
|
__publicField(this, "eventPlugins", /* @__PURE__ */ new Set());
|
|
2771
2772
|
// 📦 CONTENT: Parser and serializer for ContentUnit processing
|
|
@@ -3058,6 +3059,11 @@ var __lionrapidAutoExports = (() => {
|
|
|
3058
3059
|
resolvedLocale,
|
|
3059
3060
|
resolvedKey
|
|
3060
3061
|
);
|
|
3062
|
+
} else if (!this.warnedNoInterpolate && params && Object.keys(params).length > 0 && processedValue.includes("{")) {
|
|
3063
|
+
this.warnedNoInterpolate = true;
|
|
3064
|
+
console.warn(
|
|
3065
|
+
"[LionRapid] Translation params were passed but no interpolate formatter is registered \u2014 {placeholders} render literally. Install @lionrapid/formatters and add .use(coreFormattersPlugin) to the builder."
|
|
3066
|
+
);
|
|
3061
3067
|
}
|
|
3062
3068
|
if (this.processors.size > 0) {
|
|
3063
3069
|
for (const [name, processor] of this.processors.entries()) {
|
|
@@ -3838,6 +3844,31 @@ var __lionrapidAutoExports = (() => {
|
|
|
3838
3844
|
* and payload can never desync because they live and die together.
|
|
3839
3845
|
*/
|
|
3840
3846
|
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3847
|
+
/**
|
|
3848
|
+
* Miss-path flood protection (READINESS before-scale #1: one cold locale
|
|
3849
|
+
* switch measured ~380 requests in 10 s — every t() miss refired a key GET
|
|
3850
|
+
* and a namespace GET on every re-render, forever on animated pages).
|
|
3851
|
+
*/
|
|
3852
|
+
/** GETs currently on the wire, keyed by `url|if-none-match` — identical
|
|
3853
|
+
* concurrent requests share one fetch; each awaiter gets a clone. The
|
|
3854
|
+
* If-None-Match value is part of the key so the 304-without-snapshot
|
|
3855
|
+
* unconditional refetch never coalesces onto a conditional flight. */
|
|
3856
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
3857
|
+
/** URL → 404/423 seen recently: repeats are answered locally (same typed
|
|
3858
|
+
* error, no network) until `until`. 423 honors the server's Retry-After.
|
|
3859
|
+
* Cleared entirely by any successful write — discovery sync keeps its
|
|
3860
|
+
* miss → batch → served-back round-trip. */
|
|
3861
|
+
__publicField(this, "negativeCache", /* @__PURE__ */ new Map());
|
|
3862
|
+
/** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
|
|
3863
|
+
* wrong or unauthorized for this project, and retrying cannot heal it —
|
|
3864
|
+
* after the first one nothing touches the network again for this
|
|
3865
|
+
* instance (a page reload starts fresh). Holds the status that tripped
|
|
3866
|
+
* it so short-circuits answer honestly. */
|
|
3867
|
+
__publicField(this, "authFailedStatus", null);
|
|
3868
|
+
/** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
|
|
3869
|
+
* a WRITE degrades this instance to read-only — writes stop, silently
|
|
3870
|
+
* and without retries, while reads keep serving the page. */
|
|
3871
|
+
__publicField(this, "writesFailedStatus", null);
|
|
3841
3872
|
/**
|
|
3842
3873
|
* Default endpoint templates with placeholder support
|
|
3843
3874
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3845,6 +3876,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
3845
3876
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3846
3877
|
getTranslation: "/api/integrations/translations/:key",
|
|
3847
3878
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
3879
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3848
3880
|
setTranslation: "/api/integrations/sync/:key",
|
|
3849
3881
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
3850
3882
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -3956,6 +3988,40 @@ var __lionrapidAutoExports = (() => {
|
|
|
3956
3988
|
this.handleCaughtError(error);
|
|
3957
3989
|
}
|
|
3958
3990
|
}
|
|
3991
|
+
/**
|
|
3992
|
+
* Project-level config for this key — the dashboard's language list,
|
|
3993
|
+
* served under the same trust model as translation reads (docs/20,
|
|
3994
|
+
* companion track). Returns null when the server doesn't expose the
|
|
3995
|
+
* endpoint (404 — older server, feature not deployed) or the payload is
|
|
3996
|
+
* unusable: absence means "keep your local fallback", never an error.
|
|
3997
|
+
*/
|
|
3998
|
+
async getProjectConfig() {
|
|
3999
|
+
if (!this.config?.options?.baseUrl) {
|
|
4000
|
+
throw new NetworkError("API base URL is not set");
|
|
4001
|
+
}
|
|
4002
|
+
const url = this.buildUrl("getProjectConfig", {});
|
|
4003
|
+
logger.debug(`API GET ProjectConfig: ${url}`);
|
|
4004
|
+
try {
|
|
4005
|
+
const response = await this.makeRequest(url, "GET");
|
|
4006
|
+
if (response.status === 404) {
|
|
4007
|
+
return null;
|
|
4008
|
+
}
|
|
4009
|
+
if (!response.ok) {
|
|
4010
|
+
this.handleHttpError(response, "project-config");
|
|
4011
|
+
}
|
|
4012
|
+
const data = await response.json();
|
|
4013
|
+
if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
|
|
4014
|
+
logger.warn("project-config payload unusable \u2014 keeping local fallback");
|
|
4015
|
+
return null;
|
|
4016
|
+
}
|
|
4017
|
+
return {
|
|
4018
|
+
defaultLocale: data.defaultLocale,
|
|
4019
|
+
languages: data.languages.map(String)
|
|
4020
|
+
};
|
|
4021
|
+
} catch (error) {
|
|
4022
|
+
this.handleCaughtError(error);
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3959
4025
|
/**
|
|
3960
4026
|
* Shared namespace fetch with the canonical 304 contract:
|
|
3961
4027
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4280,7 +4346,87 @@ var __lionrapidAutoExports = (() => {
|
|
|
4280
4346
|
/**
|
|
4281
4347
|
* Make HTTP request with timeout and headers
|
|
4282
4348
|
*/
|
|
4283
|
-
|
|
4349
|
+
makeRequest(url, method, body, extraHeaders) {
|
|
4350
|
+
if (this.authFailedStatus !== null) {
|
|
4351
|
+
return Promise.resolve(
|
|
4352
|
+
new Response(null, {
|
|
4353
|
+
status: this.authFailedStatus,
|
|
4354
|
+
statusText: `auth latched off after ${this.authFailedStatus}`
|
|
4355
|
+
})
|
|
4356
|
+
);
|
|
4357
|
+
}
|
|
4358
|
+
if (method !== "GET") {
|
|
4359
|
+
if (this.writesFailedStatus !== null) {
|
|
4360
|
+
return Promise.resolve(
|
|
4361
|
+
new Response(null, {
|
|
4362
|
+
status: this.writesFailedStatus,
|
|
4363
|
+
statusText: `writes latched off after ${this.writesFailedStatus}`
|
|
4364
|
+
})
|
|
4365
|
+
);
|
|
4366
|
+
}
|
|
4367
|
+
return this.doFetch(url, method, body, extraHeaders).then(
|
|
4368
|
+
(resp) => this.recordOutcome(url, method, resp)
|
|
4369
|
+
);
|
|
4370
|
+
}
|
|
4371
|
+
const negative = this.negativeCache.get(url);
|
|
4372
|
+
if (negative) {
|
|
4373
|
+
if (Date.now() < negative.until) {
|
|
4374
|
+
logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
|
|
4375
|
+
const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
|
|
4376
|
+
return Promise.resolve(
|
|
4377
|
+
new Response(null, { status: negative.status, statusText: "negative cache", headers })
|
|
4378
|
+
);
|
|
4379
|
+
}
|
|
4380
|
+
this.negativeCache.delete(url);
|
|
4381
|
+
}
|
|
4382
|
+
const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
|
|
4383
|
+
const existing = this.inflightGets.get(flightKey);
|
|
4384
|
+
if (existing) {
|
|
4385
|
+
return existing.then((resp) => this.cloneResponse(resp));
|
|
4386
|
+
}
|
|
4387
|
+
const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
|
|
4388
|
+
this.inflightGets.set(flightKey, flight);
|
|
4389
|
+
return flight.then((resp) => this.cloneResponse(resp));
|
|
4390
|
+
}
|
|
4391
|
+
/** Bookkeeping on every real (non-synthesized) response — runs once per
|
|
4392
|
+
* physical fetch, never once per coalesced awaiter. */
|
|
4393
|
+
recordOutcome(url, method, response) {
|
|
4394
|
+
const status = response.status;
|
|
4395
|
+
if (status === 401 || status === 403 && method === "GET") {
|
|
4396
|
+
if (this.authFailedStatus === null) {
|
|
4397
|
+
this.authFailedStatus = status;
|
|
4398
|
+
console.warn(
|
|
4399
|
+
`[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
|
|
4400
|
+
);
|
|
4401
|
+
}
|
|
4402
|
+
return response;
|
|
4403
|
+
}
|
|
4404
|
+
if ((status === 403 || status === 429) && method !== "GET") {
|
|
4405
|
+
if (this.writesFailedStatus === null) {
|
|
4406
|
+
this.writesFailedStatus = status;
|
|
4407
|
+
logger.warn(
|
|
4408
|
+
`Write path disabled after ${status} (scope/quota) \u2014 running read-only`
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
return response;
|
|
4412
|
+
}
|
|
4413
|
+
if (method === "GET" && (status === 404 || status === 423)) {
|
|
4414
|
+
const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
|
|
4415
|
+
const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
|
|
4416
|
+
this.negativeCache.set(url, { status, until: Date.now() + ttl });
|
|
4417
|
+
} else if (method !== "GET" && response.ok) {
|
|
4418
|
+
this.negativeCache.clear();
|
|
4419
|
+
} else if (method === "GET" && response.ok) {
|
|
4420
|
+
this.negativeCache.delete(url);
|
|
4421
|
+
}
|
|
4422
|
+
return response;
|
|
4423
|
+
}
|
|
4424
|
+
/** Test doubles (and exotic runtimes) hand us plain objects — only real
|
|
4425
|
+
* Responses need cloning so each coalesced awaiter can read the body. */
|
|
4426
|
+
cloneResponse(response) {
|
|
4427
|
+
return typeof response.clone === "function" ? response.clone() : response;
|
|
4428
|
+
}
|
|
4429
|
+
async doFetch(url, method, body, extraHeaders) {
|
|
4284
4430
|
const controller = new AbortController();
|
|
4285
4431
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4286
4432
|
const timeoutId = setTimeout(
|
|
@@ -4313,6 +4459,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
4313
4459
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4314
4460
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4315
4461
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4462
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4316
4463
|
var NetworkRepository = _NetworkRepository;
|
|
4317
4464
|
|
|
4318
4465
|
// src/browser/exclude-filter.ts
|
|
@@ -5555,9 +5702,11 @@ var __lionrapidAutoExports = (() => {
|
|
|
5555
5702
|
this.config = {
|
|
5556
5703
|
enabled: config.enabled ?? true,
|
|
5557
5704
|
priority: config.priority ?? 0,
|
|
5705
|
+
// Use build sequence by default
|
|
5558
5706
|
options: {
|
|
5559
5707
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
5560
5708
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5709
|
+
// 24 hours default
|
|
5561
5710
|
maxSize: config.options?.maxSize ?? 500,
|
|
5562
5711
|
...config.options
|
|
5563
5712
|
}
|
|
@@ -5570,6 +5719,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5570
5719
|
this.migrateIfNeeded();
|
|
5571
5720
|
}
|
|
5572
5721
|
}
|
|
5722
|
+
/**
|
|
5723
|
+
* Check if localStorage is available
|
|
5724
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5725
|
+
*/
|
|
5573
5726
|
checkAvailability() {
|
|
5574
5727
|
if (typeof window === "undefined") {
|
|
5575
5728
|
return false;
|
|
@@ -5583,6 +5736,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5583
5736
|
return false;
|
|
5584
5737
|
}
|
|
5585
5738
|
}
|
|
5739
|
+
/**
|
|
5740
|
+
* Handle version migrations - clear cache when version changes
|
|
5741
|
+
*/
|
|
5586
5742
|
migrateIfNeeded() {
|
|
5587
5743
|
try {
|
|
5588
5744
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5593,6 +5749,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5593
5749
|
} catch {
|
|
5594
5750
|
}
|
|
5595
5751
|
}
|
|
5752
|
+
/**
|
|
5753
|
+
* Get a translation value by key
|
|
5754
|
+
* Returns null if not found or expired
|
|
5755
|
+
*/
|
|
5596
5756
|
get(key) {
|
|
5597
5757
|
if (!this.available) return null;
|
|
5598
5758
|
try {
|
|
@@ -5609,6 +5769,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5609
5769
|
return null;
|
|
5610
5770
|
}
|
|
5611
5771
|
}
|
|
5772
|
+
/**
|
|
5773
|
+
* Set a translation value
|
|
5774
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5775
|
+
*/
|
|
5612
5776
|
set(key, value) {
|
|
5613
5777
|
if (!this.available) return;
|
|
5614
5778
|
const fullKey = this.prefix + key;
|
|
@@ -5633,6 +5797,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5633
5797
|
}
|
|
5634
5798
|
}
|
|
5635
5799
|
}
|
|
5800
|
+
/**
|
|
5801
|
+
* Remove a translation by key
|
|
5802
|
+
*/
|
|
5636
5803
|
remove(key) {
|
|
5637
5804
|
if (!this.available) return;
|
|
5638
5805
|
try {
|
|
@@ -5640,6 +5807,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5640
5807
|
} catch {
|
|
5641
5808
|
}
|
|
5642
5809
|
}
|
|
5810
|
+
/**
|
|
5811
|
+
* Clear all LionRapid entries from localStorage
|
|
5812
|
+
* Preserves entries from other applications
|
|
5813
|
+
*/
|
|
5643
5814
|
clear() {
|
|
5644
5815
|
if (!this.available) return;
|
|
5645
5816
|
try {
|
|
@@ -5654,6 +5825,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5654
5825
|
} catch {
|
|
5655
5826
|
}
|
|
5656
5827
|
}
|
|
5828
|
+
/**
|
|
5829
|
+
* Clear all entries for a specific namespace
|
|
5830
|
+
*/
|
|
5657
5831
|
clearNamespace(locale, namespace) {
|
|
5658
5832
|
if (!this.available) return;
|
|
5659
5833
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5672,6 +5846,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5672
5846
|
} catch {
|
|
5673
5847
|
}
|
|
5674
5848
|
}
|
|
5849
|
+
/**
|
|
5850
|
+
* Get namespace translations
|
|
5851
|
+
*/
|
|
5675
5852
|
getNamespace(locale, namespace) {
|
|
5676
5853
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5677
5854
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5683,10 +5860,16 @@ var __lionrapidAutoExports = (() => {
|
|
|
5683
5860
|
return null;
|
|
5684
5861
|
}
|
|
5685
5862
|
}
|
|
5863
|
+
/**
|
|
5864
|
+
* Set namespace translations
|
|
5865
|
+
*/
|
|
5686
5866
|
setNamespace(locale, namespace, translations) {
|
|
5687
5867
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5688
5868
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5689
5869
|
}
|
|
5870
|
+
/**
|
|
5871
|
+
* Get all cache entries for debugging
|
|
5872
|
+
*/
|
|
5690
5873
|
getEntries() {
|
|
5691
5874
|
const entries = {};
|
|
5692
5875
|
if (!this.available) return entries;
|
|
@@ -5706,12 +5889,21 @@ var __lionrapidAutoExports = (() => {
|
|
|
5706
5889
|
}
|
|
5707
5890
|
return entries;
|
|
5708
5891
|
}
|
|
5892
|
+
/**
|
|
5893
|
+
* Get repository configuration
|
|
5894
|
+
*/
|
|
5709
5895
|
getConfig() {
|
|
5710
5896
|
return this.config;
|
|
5711
5897
|
}
|
|
5898
|
+
/**
|
|
5899
|
+
* Check if localStorage is available
|
|
5900
|
+
*/
|
|
5712
5901
|
isAvailable() {
|
|
5713
5902
|
return this.available;
|
|
5714
5903
|
}
|
|
5904
|
+
/**
|
|
5905
|
+
* Get the number of entries in the cache
|
|
5906
|
+
*/
|
|
5715
5907
|
getEntryCount() {
|
|
5716
5908
|
if (!this.available) return 0;
|
|
5717
5909
|
let count = 0;
|
|
@@ -5726,12 +5918,18 @@ var __lionrapidAutoExports = (() => {
|
|
|
5726
5918
|
}
|
|
5727
5919
|
return count;
|
|
5728
5920
|
}
|
|
5921
|
+
/**
|
|
5922
|
+
* Check if error is a quota exceeded error
|
|
5923
|
+
*/
|
|
5729
5924
|
isQuotaError(error) {
|
|
5730
5925
|
if (error instanceof DOMException) {
|
|
5731
5926
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5732
5927
|
}
|
|
5733
5928
|
return false;
|
|
5734
5929
|
}
|
|
5930
|
+
/**
|
|
5931
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5932
|
+
*/
|
|
5735
5933
|
evictOldest() {
|
|
5736
5934
|
if (!this.available) return;
|
|
5737
5935
|
try {
|
|
@@ -5759,6 +5957,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5759
5957
|
} catch {
|
|
5760
5958
|
}
|
|
5761
5959
|
}
|
|
5960
|
+
/**
|
|
5961
|
+
* Get cache statistics
|
|
5962
|
+
*/
|
|
5762
5963
|
getCacheStats() {
|
|
5763
5964
|
const size = this.getEntryCount();
|
|
5764
5965
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5772,7 +5973,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
5772
5973
|
|
|
5773
5974
|
// src/browser/storage/cache-storage-repository.ts
|
|
5774
5975
|
var CACHE_NAME = "lionrapid-v1";
|
|
5775
|
-
var CACHE_PREFIX = "lionrapid
|
|
5976
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5776
5977
|
var CacheStorageRepository = class {
|
|
5777
5978
|
constructor(config = {}) {
|
|
5778
5979
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5787,6 +5988,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
5787
5988
|
options: {
|
|
5788
5989
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5789
5990
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5991
|
+
// 7 days default
|
|
5790
5992
|
...config.options
|
|
5791
5993
|
}
|
|
5792
5994
|
};
|
|
@@ -5794,9 +5996,17 @@ var __lionrapidAutoExports = (() => {
|
|
|
5794
5996
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5795
5997
|
this.available = this.checkAvailability();
|
|
5796
5998
|
}
|
|
5999
|
+
/**
|
|
6000
|
+
* Check if Cache API is available
|
|
6001
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
6002
|
+
*/
|
|
5797
6003
|
checkAvailability() {
|
|
5798
6004
|
return typeof caches !== "undefined";
|
|
5799
6005
|
}
|
|
6006
|
+
/**
|
|
6007
|
+
* Get a translation value by key
|
|
6008
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
6009
|
+
*/
|
|
5800
6010
|
async get(key) {
|
|
5801
6011
|
if (!this.available) {
|
|
5802
6012
|
throw new TranslationNotFoundError(key);
|
|
@@ -5820,6 +6030,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5820
6030
|
throw new TranslationNotFoundError(key);
|
|
5821
6031
|
}
|
|
5822
6032
|
}
|
|
6033
|
+
/**
|
|
6034
|
+
* Set a translation value
|
|
6035
|
+
*/
|
|
5823
6036
|
async set(key, value) {
|
|
5824
6037
|
if (!this.available) return;
|
|
5825
6038
|
try {
|
|
@@ -5838,6 +6051,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5838
6051
|
} catch {
|
|
5839
6052
|
}
|
|
5840
6053
|
}
|
|
6054
|
+
/**
|
|
6055
|
+
* Remove a translation by key
|
|
6056
|
+
*/
|
|
5841
6057
|
async remove(key) {
|
|
5842
6058
|
if (!this.available) return;
|
|
5843
6059
|
try {
|
|
@@ -5846,6 +6062,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5846
6062
|
} catch {
|
|
5847
6063
|
}
|
|
5848
6064
|
}
|
|
6065
|
+
/**
|
|
6066
|
+
* Clear all LionRapid entries from the cache
|
|
6067
|
+
*/
|
|
5849
6068
|
async clear() {
|
|
5850
6069
|
if (!this.available) return;
|
|
5851
6070
|
try {
|
|
@@ -5857,6 +6076,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5857
6076
|
} catch {
|
|
5858
6077
|
}
|
|
5859
6078
|
}
|
|
6079
|
+
/**
|
|
6080
|
+
* Clear all entries for a specific namespace
|
|
6081
|
+
*/
|
|
5860
6082
|
async clearNamespace(locale, namespace) {
|
|
5861
6083
|
if (!this.available) return;
|
|
5862
6084
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5874,6 +6096,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5874
6096
|
} catch {
|
|
5875
6097
|
}
|
|
5876
6098
|
}
|
|
6099
|
+
/**
|
|
6100
|
+
* Get namespace translations
|
|
6101
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
6102
|
+
*/
|
|
5877
6103
|
async getNamespace(locale, namespace) {
|
|
5878
6104
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5879
6105
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5884,16 +6110,29 @@ var __lionrapidAutoExports = (() => {
|
|
|
5884
6110
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5885
6111
|
}
|
|
5886
6112
|
}
|
|
6113
|
+
/**
|
|
6114
|
+
* Set namespace translations
|
|
6115
|
+
*/
|
|
5887
6116
|
async setNamespace(locale, namespace, translations) {
|
|
5888
6117
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5889
6118
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5890
6119
|
}
|
|
6120
|
+
/**
|
|
6121
|
+
* Get repository configuration
|
|
6122
|
+
*/
|
|
5891
6123
|
getConfig() {
|
|
5892
6124
|
return this.config;
|
|
5893
6125
|
}
|
|
6126
|
+
/**
|
|
6127
|
+
* Check if Cache API is available
|
|
6128
|
+
*/
|
|
5894
6129
|
isAvailable() {
|
|
5895
6130
|
return this.available;
|
|
5896
6131
|
}
|
|
6132
|
+
/**
|
|
6133
|
+
* Get all cached entries (for debugging)
|
|
6134
|
+
* Returns an object mapping keys to values
|
|
6135
|
+
*/
|
|
5897
6136
|
async getEntries() {
|
|
5898
6137
|
const entries = {};
|
|
5899
6138
|
if (!this.available) return entries;
|
|
@@ -5916,6 +6155,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5916
6155
|
}
|
|
5917
6156
|
return entries;
|
|
5918
6157
|
}
|
|
6158
|
+
/**
|
|
6159
|
+
* Get cache statistics
|
|
6160
|
+
*/
|
|
5919
6161
|
async getCacheStats() {
|
|
5920
6162
|
if (!this.available) {
|
|
5921
6163
|
return { size: 0, cacheName: this.cacheName };
|
|
@@ -5935,8 +6177,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5935
6177
|
function resolveWritePolicy(apiKey) {
|
|
5936
6178
|
const isPublishable = apiKey.startsWith("pk_");
|
|
5937
6179
|
return {
|
|
6180
|
+
bootNetwork: isPublishable,
|
|
5938
6181
|
syncMissing: { enabled: isPublishable },
|
|
5939
|
-
|
|
6182
|
+
warnRefused: !isPublishable
|
|
5940
6183
|
};
|
|
5941
6184
|
}
|
|
5942
6185
|
function configForWindow(config) {
|
|
@@ -6013,7 +6256,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
6013
6256
|
function readLocaleCookie(availableLanguages) {
|
|
6014
6257
|
if (typeof document === "undefined") return null;
|
|
6015
6258
|
const match = document.cookie.match(
|
|
6016
|
-
/lionrapid_locale=([a-z]{2,3}(?:-[a-z0-9]{2,4})*)/i
|
|
6259
|
+
/(?:^|;\s*)lionrapid_locale=([a-z]{2,3}(?:-[a-z0-9]{2,4})*)/i
|
|
6017
6260
|
);
|
|
6018
6261
|
if (!match) return null;
|
|
6019
6262
|
return matchLocale([match[1]], availableLanguages, "") || null;
|
|
@@ -6059,9 +6302,21 @@ var __lionrapidAutoExports = (() => {
|
|
|
6059
6302
|
document.documentElement.dir = localeDir(locale);
|
|
6060
6303
|
}
|
|
6061
6304
|
}
|
|
6062
|
-
|
|
6305
|
+
function findConfigScript() {
|
|
6306
|
+
const current = document.currentScript;
|
|
6307
|
+
if (current instanceof HTMLScriptElement && current.dataset.apiKey) {
|
|
6308
|
+
return current;
|
|
6309
|
+
}
|
|
6063
6310
|
const scripts = document.querySelectorAll("script[data-api-key]");
|
|
6064
|
-
|
|
6311
|
+
if (scripts.length > 1) {
|
|
6312
|
+
console.warn(
|
|
6313
|
+
`[LionRapid] ${scripts.length} script tags with data-api-key found; using the last one`
|
|
6314
|
+
);
|
|
6315
|
+
}
|
|
6316
|
+
return scripts[scripts.length - 1];
|
|
6317
|
+
}
|
|
6318
|
+
async function autoInit() {
|
|
6319
|
+
const script = findConfigScript();
|
|
6065
6320
|
if (!script) {
|
|
6066
6321
|
console.warn("[LionRapid] No script tag with data-api-key found");
|
|
6067
6322
|
return;
|
|
@@ -6073,9 +6328,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
6073
6328
|
return;
|
|
6074
6329
|
}
|
|
6075
6330
|
const writePolicy = resolveWritePolicy(config.apiKey);
|
|
6076
|
-
if (writePolicy.
|
|
6331
|
+
if (writePolicy.warnRefused) {
|
|
6077
6332
|
console.error(
|
|
6078
|
-
"[LionRapid] data-api-key must be a publishable pk_ key
|
|
6333
|
+
"[LionRapid] data-api-key must be a publishable pk_ key \u2014 network translations are disabled. Copy the snippet from the integration hub to get the pk_ key for this project."
|
|
6079
6334
|
);
|
|
6080
6335
|
}
|
|
6081
6336
|
const initialLocale = resolveInitialLocale(config);
|
|
@@ -6129,25 +6384,28 @@ var __lionrapidAutoExports = (() => {
|
|
|
6129
6384
|
})
|
|
6130
6385
|
);
|
|
6131
6386
|
}
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6387
|
+
if (writePolicy.bootNetwork) {
|
|
6388
|
+
builder.use(
|
|
6389
|
+
new NetworkRepository({
|
|
6390
|
+
enabled: true,
|
|
6391
|
+
options: {
|
|
6392
|
+
baseUrl: config.baseUrl,
|
|
6393
|
+
apiKey: config.apiKey,
|
|
6394
|
+
// pk_ keys sync missing source strings (discovery) — see
|
|
6395
|
+
// resolveWritePolicy.
|
|
6396
|
+
syncMissing: writePolicy.syncMissing,
|
|
6397
|
+
preloadNamespaces: [
|
|
6398
|
+
{
|
|
6399
|
+
locale: initialLocale,
|
|
6400
|
+
namespace: config.namespace,
|
|
6401
|
+
eager: true
|
|
6402
|
+
}
|
|
6403
|
+
]
|
|
6404
|
+
}
|
|
6405
|
+
})
|
|
6406
|
+
);
|
|
6407
|
+
}
|
|
6408
|
+
const core = await builder.build();
|
|
6151
6409
|
const autoTranslator = new AutoTranslator({
|
|
6152
6410
|
excludeSelectors: config.excludeSelectors
|
|
6153
6411
|
});
|