@lionrapid/core 0.6.1 → 0.6.3
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 +8 -3
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +34 -26
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +267 -24
- package/dist/browser/lionrapid.auto.dev.js.map +2 -2
- package/dist/browser/lionrapid.auto.js +267 -24
- 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 +241 -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 +241 -2
- package/dist/browser/lionrapid.iife.dev.js.map +2 -2
- package/dist/browser/lionrapid.iife.js +241 -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 +241 -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/repositories/network-repository.d.ts +42 -1
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +161 -1
- package/dist/repositories/network-repository.js.map +1 -1
- package/package.json +1 -1
|
@@ -3829,6 +3829,8 @@ var __lionrapidAutoExports = (() => {
|
|
|
3829
3829
|
};
|
|
3830
3830
|
|
|
3831
3831
|
// src/repositories/network-repository.ts
|
|
3832
|
+
var SURFACE_HEADER = "X-LR-Surface";
|
|
3833
|
+
var SURFACE_TAG = "snippet";
|
|
3832
3834
|
var _NetworkRepository = class _NetworkRepository {
|
|
3833
3835
|
constructor(config = {}) {
|
|
3834
3836
|
__publicField(this, "name", "NetworkRepository");
|
|
@@ -3844,6 +3846,31 @@ var __lionrapidAutoExports = (() => {
|
|
|
3844
3846
|
* and payload can never desync because they live and die together.
|
|
3845
3847
|
*/
|
|
3846
3848
|
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3849
|
+
/**
|
|
3850
|
+
* Miss-path flood protection (READINESS before-scale #1: one cold locale
|
|
3851
|
+
* switch measured ~380 requests in 10 s — every t() miss refired a key GET
|
|
3852
|
+
* and a namespace GET on every re-render, forever on animated pages).
|
|
3853
|
+
*/
|
|
3854
|
+
/** GETs currently on the wire, keyed by `url|if-none-match` — identical
|
|
3855
|
+
* concurrent requests share one fetch; each awaiter gets a clone. The
|
|
3856
|
+
* If-None-Match value is part of the key so the 304-without-snapshot
|
|
3857
|
+
* unconditional refetch never coalesces onto a conditional flight. */
|
|
3858
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
3859
|
+
/** URL → 404/423 seen recently: repeats are answered locally (same typed
|
|
3860
|
+
* error, no network) until `until`. 423 honors the server's Retry-After.
|
|
3861
|
+
* Cleared entirely by any successful write — discovery sync keeps its
|
|
3862
|
+
* miss → batch → served-back round-trip. */
|
|
3863
|
+
__publicField(this, "negativeCache", /* @__PURE__ */ new Map());
|
|
3864
|
+
/** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
|
|
3865
|
+
* wrong or unauthorized for this project, and retrying cannot heal it —
|
|
3866
|
+
* after the first one nothing touches the network again for this
|
|
3867
|
+
* instance (a page reload starts fresh). Holds the status that tripped
|
|
3868
|
+
* it so short-circuits answer honestly. */
|
|
3869
|
+
__publicField(this, "authFailedStatus", null);
|
|
3870
|
+
/** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
|
|
3871
|
+
* a WRITE degrades this instance to read-only — writes stop, silently
|
|
3872
|
+
* and without retries, while reads keep serving the page. */
|
|
3873
|
+
__publicField(this, "writesFailedStatus", null);
|
|
3847
3874
|
/**
|
|
3848
3875
|
* Default endpoint templates with placeholder support
|
|
3849
3876
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3851,6 +3878,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
3851
3878
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3852
3879
|
getTranslation: "/api/integrations/translations/:key",
|
|
3853
3880
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
3881
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3854
3882
|
setTranslation: "/api/integrations/sync/:key",
|
|
3855
3883
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
3856
3884
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -3962,6 +3990,40 @@ var __lionrapidAutoExports = (() => {
|
|
|
3962
3990
|
this.handleCaughtError(error);
|
|
3963
3991
|
}
|
|
3964
3992
|
}
|
|
3993
|
+
/**
|
|
3994
|
+
* Project-level config for this key — the dashboard's language list,
|
|
3995
|
+
* served under the same trust model as translation reads (docs/20,
|
|
3996
|
+
* companion track). Returns null when the server doesn't expose the
|
|
3997
|
+
* endpoint (404 — older server, feature not deployed) or the payload is
|
|
3998
|
+
* unusable: absence means "keep your local fallback", never an error.
|
|
3999
|
+
*/
|
|
4000
|
+
async getProjectConfig() {
|
|
4001
|
+
if (!this.config?.options?.baseUrl) {
|
|
4002
|
+
throw new NetworkError("API base URL is not set");
|
|
4003
|
+
}
|
|
4004
|
+
const url = this.buildUrl("getProjectConfig", {});
|
|
4005
|
+
logger.debug(`API GET ProjectConfig: ${url}`);
|
|
4006
|
+
try {
|
|
4007
|
+
const response = await this.makeRequest(url, "GET");
|
|
4008
|
+
if (response.status === 404) {
|
|
4009
|
+
return null;
|
|
4010
|
+
}
|
|
4011
|
+
if (!response.ok) {
|
|
4012
|
+
this.handleHttpError(response, "project-config");
|
|
4013
|
+
}
|
|
4014
|
+
const data = await response.json();
|
|
4015
|
+
if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
|
|
4016
|
+
logger.warn("project-config payload unusable \u2014 keeping local fallback");
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
4019
|
+
return {
|
|
4020
|
+
defaultLocale: data.defaultLocale,
|
|
4021
|
+
languages: data.languages.map(String)
|
|
4022
|
+
};
|
|
4023
|
+
} catch (error) {
|
|
4024
|
+
this.handleCaughtError(error);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
3965
4027
|
/**
|
|
3966
4028
|
* Shared namespace fetch with the canonical 304 contract:
|
|
3967
4029
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4286,7 +4348,87 @@ var __lionrapidAutoExports = (() => {
|
|
|
4286
4348
|
/**
|
|
4287
4349
|
* Make HTTP request with timeout and headers
|
|
4288
4350
|
*/
|
|
4289
|
-
|
|
4351
|
+
makeRequest(url, method, body, extraHeaders) {
|
|
4352
|
+
if (this.authFailedStatus !== null) {
|
|
4353
|
+
return Promise.resolve(
|
|
4354
|
+
new Response(null, {
|
|
4355
|
+
status: this.authFailedStatus,
|
|
4356
|
+
statusText: `auth latched off after ${this.authFailedStatus}`
|
|
4357
|
+
})
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
if (method !== "GET") {
|
|
4361
|
+
if (this.writesFailedStatus !== null) {
|
|
4362
|
+
return Promise.resolve(
|
|
4363
|
+
new Response(null, {
|
|
4364
|
+
status: this.writesFailedStatus,
|
|
4365
|
+
statusText: `writes latched off after ${this.writesFailedStatus}`
|
|
4366
|
+
})
|
|
4367
|
+
);
|
|
4368
|
+
}
|
|
4369
|
+
return this.doFetch(url, method, body, extraHeaders).then(
|
|
4370
|
+
(resp) => this.recordOutcome(url, method, resp)
|
|
4371
|
+
);
|
|
4372
|
+
}
|
|
4373
|
+
const negative = this.negativeCache.get(url);
|
|
4374
|
+
if (negative) {
|
|
4375
|
+
if (Date.now() < negative.until) {
|
|
4376
|
+
logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
|
|
4377
|
+
const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
|
|
4378
|
+
return Promise.resolve(
|
|
4379
|
+
new Response(null, { status: negative.status, statusText: "negative cache", headers })
|
|
4380
|
+
);
|
|
4381
|
+
}
|
|
4382
|
+
this.negativeCache.delete(url);
|
|
4383
|
+
}
|
|
4384
|
+
const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
|
|
4385
|
+
const existing = this.inflightGets.get(flightKey);
|
|
4386
|
+
if (existing) {
|
|
4387
|
+
return existing.then((resp) => this.cloneResponse(resp));
|
|
4388
|
+
}
|
|
4389
|
+
const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
|
|
4390
|
+
this.inflightGets.set(flightKey, flight);
|
|
4391
|
+
return flight.then((resp) => this.cloneResponse(resp));
|
|
4392
|
+
}
|
|
4393
|
+
/** Bookkeeping on every real (non-synthesized) response — runs once per
|
|
4394
|
+
* physical fetch, never once per coalesced awaiter. */
|
|
4395
|
+
recordOutcome(url, method, response) {
|
|
4396
|
+
const status = response.status;
|
|
4397
|
+
if (status === 401 || status === 403 && method === "GET") {
|
|
4398
|
+
if (this.authFailedStatus === null) {
|
|
4399
|
+
this.authFailedStatus = status;
|
|
4400
|
+
console.warn(
|
|
4401
|
+
`[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
return response;
|
|
4405
|
+
}
|
|
4406
|
+
if ((status === 403 || status === 429) && method !== "GET") {
|
|
4407
|
+
if (this.writesFailedStatus === null) {
|
|
4408
|
+
this.writesFailedStatus = status;
|
|
4409
|
+
logger.warn(
|
|
4410
|
+
`Write path disabled after ${status} (scope/quota) \u2014 running read-only`
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
return response;
|
|
4414
|
+
}
|
|
4415
|
+
if (method === "GET" && (status === 404 || status === 423)) {
|
|
4416
|
+
const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
|
|
4417
|
+
const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
|
|
4418
|
+
this.negativeCache.set(url, { status, until: Date.now() + ttl });
|
|
4419
|
+
} else if (method !== "GET" && response.ok) {
|
|
4420
|
+
this.negativeCache.clear();
|
|
4421
|
+
} else if (method === "GET" && response.ok) {
|
|
4422
|
+
this.negativeCache.delete(url);
|
|
4423
|
+
}
|
|
4424
|
+
return response;
|
|
4425
|
+
}
|
|
4426
|
+
/** Test doubles (and exotic runtimes) hand us plain objects — only real
|
|
4427
|
+
* Responses need cloning so each coalesced awaiter can read the body. */
|
|
4428
|
+
cloneResponse(response) {
|
|
4429
|
+
return typeof response.clone === "function" ? response.clone() : response;
|
|
4430
|
+
}
|
|
4431
|
+
async doFetch(url, method, body, extraHeaders) {
|
|
4290
4432
|
const controller = new AbortController();
|
|
4291
4433
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4292
4434
|
const timeoutId = setTimeout(
|
|
@@ -4296,6 +4438,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
4296
4438
|
try {
|
|
4297
4439
|
const headers = {
|
|
4298
4440
|
"Content-Type": "application/json",
|
|
4441
|
+
[SURFACE_HEADER]: SURFACE_TAG,
|
|
4299
4442
|
...this.config.options?.headers,
|
|
4300
4443
|
...extraHeaders
|
|
4301
4444
|
};
|
|
@@ -4319,6 +4462,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
4319
4462
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4320
4463
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4321
4464
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4465
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4322
4466
|
var NetworkRepository = _NetworkRepository;
|
|
4323
4467
|
|
|
4324
4468
|
// src/browser/exclude-filter.ts
|
|
@@ -5561,9 +5705,11 @@ var __lionrapidAutoExports = (() => {
|
|
|
5561
5705
|
this.config = {
|
|
5562
5706
|
enabled: config.enabled ?? true,
|
|
5563
5707
|
priority: config.priority ?? 0,
|
|
5708
|
+
// Use build sequence by default
|
|
5564
5709
|
options: {
|
|
5565
5710
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
5566
5711
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5712
|
+
// 24 hours default
|
|
5567
5713
|
maxSize: config.options?.maxSize ?? 500,
|
|
5568
5714
|
...config.options
|
|
5569
5715
|
}
|
|
@@ -5576,6 +5722,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5576
5722
|
this.migrateIfNeeded();
|
|
5577
5723
|
}
|
|
5578
5724
|
}
|
|
5725
|
+
/**
|
|
5726
|
+
* Check if localStorage is available
|
|
5727
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5728
|
+
*/
|
|
5579
5729
|
checkAvailability() {
|
|
5580
5730
|
if (typeof window === "undefined") {
|
|
5581
5731
|
return false;
|
|
@@ -5589,6 +5739,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5589
5739
|
return false;
|
|
5590
5740
|
}
|
|
5591
5741
|
}
|
|
5742
|
+
/**
|
|
5743
|
+
* Handle version migrations - clear cache when version changes
|
|
5744
|
+
*/
|
|
5592
5745
|
migrateIfNeeded() {
|
|
5593
5746
|
try {
|
|
5594
5747
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5599,6 +5752,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5599
5752
|
} catch {
|
|
5600
5753
|
}
|
|
5601
5754
|
}
|
|
5755
|
+
/**
|
|
5756
|
+
* Get a translation value by key
|
|
5757
|
+
* Returns null if not found or expired
|
|
5758
|
+
*/
|
|
5602
5759
|
get(key) {
|
|
5603
5760
|
if (!this.available) return null;
|
|
5604
5761
|
try {
|
|
@@ -5615,6 +5772,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5615
5772
|
return null;
|
|
5616
5773
|
}
|
|
5617
5774
|
}
|
|
5775
|
+
/**
|
|
5776
|
+
* Set a translation value
|
|
5777
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5778
|
+
*/
|
|
5618
5779
|
set(key, value) {
|
|
5619
5780
|
if (!this.available) return;
|
|
5620
5781
|
const fullKey = this.prefix + key;
|
|
@@ -5639,6 +5800,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5639
5800
|
}
|
|
5640
5801
|
}
|
|
5641
5802
|
}
|
|
5803
|
+
/**
|
|
5804
|
+
* Remove a translation by key
|
|
5805
|
+
*/
|
|
5642
5806
|
remove(key) {
|
|
5643
5807
|
if (!this.available) return;
|
|
5644
5808
|
try {
|
|
@@ -5646,6 +5810,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5646
5810
|
} catch {
|
|
5647
5811
|
}
|
|
5648
5812
|
}
|
|
5813
|
+
/**
|
|
5814
|
+
* Clear all LionRapid entries from localStorage
|
|
5815
|
+
* Preserves entries from other applications
|
|
5816
|
+
*/
|
|
5649
5817
|
clear() {
|
|
5650
5818
|
if (!this.available) return;
|
|
5651
5819
|
try {
|
|
@@ -5660,6 +5828,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5660
5828
|
} catch {
|
|
5661
5829
|
}
|
|
5662
5830
|
}
|
|
5831
|
+
/**
|
|
5832
|
+
* Clear all entries for a specific namespace
|
|
5833
|
+
*/
|
|
5663
5834
|
clearNamespace(locale, namespace) {
|
|
5664
5835
|
if (!this.available) return;
|
|
5665
5836
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5678,6 +5849,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5678
5849
|
} catch {
|
|
5679
5850
|
}
|
|
5680
5851
|
}
|
|
5852
|
+
/**
|
|
5853
|
+
* Get namespace translations
|
|
5854
|
+
*/
|
|
5681
5855
|
getNamespace(locale, namespace) {
|
|
5682
5856
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5683
5857
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5689,10 +5863,16 @@ var __lionrapidAutoExports = (() => {
|
|
|
5689
5863
|
return null;
|
|
5690
5864
|
}
|
|
5691
5865
|
}
|
|
5866
|
+
/**
|
|
5867
|
+
* Set namespace translations
|
|
5868
|
+
*/
|
|
5692
5869
|
setNamespace(locale, namespace, translations) {
|
|
5693
5870
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5694
5871
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5695
5872
|
}
|
|
5873
|
+
/**
|
|
5874
|
+
* Get all cache entries for debugging
|
|
5875
|
+
*/
|
|
5696
5876
|
getEntries() {
|
|
5697
5877
|
const entries = {};
|
|
5698
5878
|
if (!this.available) return entries;
|
|
@@ -5712,12 +5892,21 @@ var __lionrapidAutoExports = (() => {
|
|
|
5712
5892
|
}
|
|
5713
5893
|
return entries;
|
|
5714
5894
|
}
|
|
5895
|
+
/**
|
|
5896
|
+
* Get repository configuration
|
|
5897
|
+
*/
|
|
5715
5898
|
getConfig() {
|
|
5716
5899
|
return this.config;
|
|
5717
5900
|
}
|
|
5901
|
+
/**
|
|
5902
|
+
* Check if localStorage is available
|
|
5903
|
+
*/
|
|
5718
5904
|
isAvailable() {
|
|
5719
5905
|
return this.available;
|
|
5720
5906
|
}
|
|
5907
|
+
/**
|
|
5908
|
+
* Get the number of entries in the cache
|
|
5909
|
+
*/
|
|
5721
5910
|
getEntryCount() {
|
|
5722
5911
|
if (!this.available) return 0;
|
|
5723
5912
|
let count = 0;
|
|
@@ -5732,12 +5921,18 @@ var __lionrapidAutoExports = (() => {
|
|
|
5732
5921
|
}
|
|
5733
5922
|
return count;
|
|
5734
5923
|
}
|
|
5924
|
+
/**
|
|
5925
|
+
* Check if error is a quota exceeded error
|
|
5926
|
+
*/
|
|
5735
5927
|
isQuotaError(error) {
|
|
5736
5928
|
if (error instanceof DOMException) {
|
|
5737
5929
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5738
5930
|
}
|
|
5739
5931
|
return false;
|
|
5740
5932
|
}
|
|
5933
|
+
/**
|
|
5934
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5935
|
+
*/
|
|
5741
5936
|
evictOldest() {
|
|
5742
5937
|
if (!this.available) return;
|
|
5743
5938
|
try {
|
|
@@ -5765,6 +5960,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5765
5960
|
} catch {
|
|
5766
5961
|
}
|
|
5767
5962
|
}
|
|
5963
|
+
/**
|
|
5964
|
+
* Get cache statistics
|
|
5965
|
+
*/
|
|
5768
5966
|
getCacheStats() {
|
|
5769
5967
|
const size = this.getEntryCount();
|
|
5770
5968
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5778,7 +5976,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
5778
5976
|
|
|
5779
5977
|
// src/browser/storage/cache-storage-repository.ts
|
|
5780
5978
|
var CACHE_NAME = "lionrapid-v1";
|
|
5781
|
-
var CACHE_PREFIX = "lionrapid
|
|
5979
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5782
5980
|
var CacheStorageRepository = class {
|
|
5783
5981
|
constructor(config = {}) {
|
|
5784
5982
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5793,6 +5991,7 @@ var __lionrapidAutoExports = (() => {
|
|
|
5793
5991
|
options: {
|
|
5794
5992
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5795
5993
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5994
|
+
// 7 days default
|
|
5796
5995
|
...config.options
|
|
5797
5996
|
}
|
|
5798
5997
|
};
|
|
@@ -5800,9 +5999,17 @@ var __lionrapidAutoExports = (() => {
|
|
|
5800
5999
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5801
6000
|
this.available = this.checkAvailability();
|
|
5802
6001
|
}
|
|
6002
|
+
/**
|
|
6003
|
+
* Check if Cache API is available
|
|
6004
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
6005
|
+
*/
|
|
5803
6006
|
checkAvailability() {
|
|
5804
6007
|
return typeof caches !== "undefined";
|
|
5805
6008
|
}
|
|
6009
|
+
/**
|
|
6010
|
+
* Get a translation value by key
|
|
6011
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
6012
|
+
*/
|
|
5806
6013
|
async get(key) {
|
|
5807
6014
|
if (!this.available) {
|
|
5808
6015
|
throw new TranslationNotFoundError(key);
|
|
@@ -5826,6 +6033,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5826
6033
|
throw new TranslationNotFoundError(key);
|
|
5827
6034
|
}
|
|
5828
6035
|
}
|
|
6036
|
+
/**
|
|
6037
|
+
* Set a translation value
|
|
6038
|
+
*/
|
|
5829
6039
|
async set(key, value) {
|
|
5830
6040
|
if (!this.available) return;
|
|
5831
6041
|
try {
|
|
@@ -5844,6 +6054,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5844
6054
|
} catch {
|
|
5845
6055
|
}
|
|
5846
6056
|
}
|
|
6057
|
+
/**
|
|
6058
|
+
* Remove a translation by key
|
|
6059
|
+
*/
|
|
5847
6060
|
async remove(key) {
|
|
5848
6061
|
if (!this.available) return;
|
|
5849
6062
|
try {
|
|
@@ -5852,6 +6065,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5852
6065
|
} catch {
|
|
5853
6066
|
}
|
|
5854
6067
|
}
|
|
6068
|
+
/**
|
|
6069
|
+
* Clear all LionRapid entries from the cache
|
|
6070
|
+
*/
|
|
5855
6071
|
async clear() {
|
|
5856
6072
|
if (!this.available) return;
|
|
5857
6073
|
try {
|
|
@@ -5863,6 +6079,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5863
6079
|
} catch {
|
|
5864
6080
|
}
|
|
5865
6081
|
}
|
|
6082
|
+
/**
|
|
6083
|
+
* Clear all entries for a specific namespace
|
|
6084
|
+
*/
|
|
5866
6085
|
async clearNamespace(locale, namespace) {
|
|
5867
6086
|
if (!this.available) return;
|
|
5868
6087
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5880,6 +6099,10 @@ var __lionrapidAutoExports = (() => {
|
|
|
5880
6099
|
} catch {
|
|
5881
6100
|
}
|
|
5882
6101
|
}
|
|
6102
|
+
/**
|
|
6103
|
+
* Get namespace translations
|
|
6104
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
6105
|
+
*/
|
|
5883
6106
|
async getNamespace(locale, namespace) {
|
|
5884
6107
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5885
6108
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5890,16 +6113,29 @@ var __lionrapidAutoExports = (() => {
|
|
|
5890
6113
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5891
6114
|
}
|
|
5892
6115
|
}
|
|
6116
|
+
/**
|
|
6117
|
+
* Set namespace translations
|
|
6118
|
+
*/
|
|
5893
6119
|
async setNamespace(locale, namespace, translations) {
|
|
5894
6120
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5895
6121
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5896
6122
|
}
|
|
6123
|
+
/**
|
|
6124
|
+
* Get repository configuration
|
|
6125
|
+
*/
|
|
5897
6126
|
getConfig() {
|
|
5898
6127
|
return this.config;
|
|
5899
6128
|
}
|
|
6129
|
+
/**
|
|
6130
|
+
* Check if Cache API is available
|
|
6131
|
+
*/
|
|
5900
6132
|
isAvailable() {
|
|
5901
6133
|
return this.available;
|
|
5902
6134
|
}
|
|
6135
|
+
/**
|
|
6136
|
+
* Get all cached entries (for debugging)
|
|
6137
|
+
* Returns an object mapping keys to values
|
|
6138
|
+
*/
|
|
5903
6139
|
async getEntries() {
|
|
5904
6140
|
const entries = {};
|
|
5905
6141
|
if (!this.available) return entries;
|
|
@@ -5922,6 +6158,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5922
6158
|
}
|
|
5923
6159
|
return entries;
|
|
5924
6160
|
}
|
|
6161
|
+
/**
|
|
6162
|
+
* Get cache statistics
|
|
6163
|
+
*/
|
|
5925
6164
|
async getCacheStats() {
|
|
5926
6165
|
if (!this.available) {
|
|
5927
6166
|
return { size: 0, cacheName: this.cacheName };
|
|
@@ -5941,8 +6180,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
5941
6180
|
function resolveWritePolicy(apiKey) {
|
|
5942
6181
|
const isPublishable = apiKey.startsWith("pk_");
|
|
5943
6182
|
return {
|
|
6183
|
+
bootNetwork: isPublishable,
|
|
5944
6184
|
syncMissing: { enabled: isPublishable },
|
|
5945
|
-
|
|
6185
|
+
warnRefused: !isPublishable
|
|
5946
6186
|
};
|
|
5947
6187
|
}
|
|
5948
6188
|
function configForWindow(config) {
|
|
@@ -6091,9 +6331,9 @@ var __lionrapidAutoExports = (() => {
|
|
|
6091
6331
|
return;
|
|
6092
6332
|
}
|
|
6093
6333
|
const writePolicy = resolveWritePolicy(config.apiKey);
|
|
6094
|
-
if (writePolicy.
|
|
6334
|
+
if (writePolicy.warnRefused) {
|
|
6095
6335
|
console.error(
|
|
6096
|
-
"[LionRapid] data-api-key must be a publishable pk_ key
|
|
6336
|
+
"[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."
|
|
6097
6337
|
);
|
|
6098
6338
|
}
|
|
6099
6339
|
const initialLocale = resolveInitialLocale(config);
|
|
@@ -6147,25 +6387,28 @@ var __lionrapidAutoExports = (() => {
|
|
|
6147
6387
|
})
|
|
6148
6388
|
);
|
|
6149
6389
|
}
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6390
|
+
if (writePolicy.bootNetwork) {
|
|
6391
|
+
builder.use(
|
|
6392
|
+
new NetworkRepository({
|
|
6393
|
+
enabled: true,
|
|
6394
|
+
options: {
|
|
6395
|
+
baseUrl: config.baseUrl,
|
|
6396
|
+
apiKey: config.apiKey,
|
|
6397
|
+
// pk_ keys sync missing source strings (discovery) — see
|
|
6398
|
+
// resolveWritePolicy.
|
|
6399
|
+
syncMissing: writePolicy.syncMissing,
|
|
6400
|
+
preloadNamespaces: [
|
|
6401
|
+
{
|
|
6402
|
+
locale: initialLocale,
|
|
6403
|
+
namespace: config.namespace,
|
|
6404
|
+
eager: true
|
|
6405
|
+
}
|
|
6406
|
+
]
|
|
6407
|
+
}
|
|
6408
|
+
})
|
|
6409
|
+
);
|
|
6410
|
+
}
|
|
6411
|
+
const core = await builder.build();
|
|
6169
6412
|
const autoTranslator = new AutoTranslator({
|
|
6170
6413
|
excludeSelectors: config.excludeSelectors
|
|
6171
6414
|
});
|