@lionrapid/core 0.6.1 → 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 +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 +264 -24
- package/dist/browser/lionrapid.auto.dev.js.map +2 -2
- package/dist/browser/lionrapid.auto.js +264 -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 +238 -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 +238 -2
- package/dist/browser/lionrapid.iife.dev.js.map +2 -2
- package/dist/browser/lionrapid.iife.js +238 -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 +238 -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 +149 -1
- package/dist/repositories/network-repository.js.map +1 -1
- package/package.json +1 -1
|
@@ -3989,6 +3989,31 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3989
3989
|
* and payload can never desync because they live and die together.
|
|
3990
3990
|
*/
|
|
3991
3991
|
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3992
|
+
/**
|
|
3993
|
+
* Miss-path flood protection (READINESS before-scale #1: one cold locale
|
|
3994
|
+
* switch measured ~380 requests in 10 s — every t() miss refired a key GET
|
|
3995
|
+
* and a namespace GET on every re-render, forever on animated pages).
|
|
3996
|
+
*/
|
|
3997
|
+
/** GETs currently on the wire, keyed by `url|if-none-match` — identical
|
|
3998
|
+
* concurrent requests share one fetch; each awaiter gets a clone. The
|
|
3999
|
+
* If-None-Match value is part of the key so the 304-without-snapshot
|
|
4000
|
+
* unconditional refetch never coalesces onto a conditional flight. */
|
|
4001
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
4002
|
+
/** URL → 404/423 seen recently: repeats are answered locally (same typed
|
|
4003
|
+
* error, no network) until `until`. 423 honors the server's Retry-After.
|
|
4004
|
+
* Cleared entirely by any successful write — discovery sync keeps its
|
|
4005
|
+
* miss → batch → served-back round-trip. */
|
|
4006
|
+
__publicField(this, "negativeCache", /* @__PURE__ */ new Map());
|
|
4007
|
+
/** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
|
|
4008
|
+
* wrong or unauthorized for this project, and retrying cannot heal it —
|
|
4009
|
+
* after the first one nothing touches the network again for this
|
|
4010
|
+
* instance (a page reload starts fresh). Holds the status that tripped
|
|
4011
|
+
* it so short-circuits answer honestly. */
|
|
4012
|
+
__publicField(this, "authFailedStatus", null);
|
|
4013
|
+
/** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
|
|
4014
|
+
* a WRITE degrades this instance to read-only — writes stop, silently
|
|
4015
|
+
* and without retries, while reads keep serving the page. */
|
|
4016
|
+
__publicField(this, "writesFailedStatus", null);
|
|
3992
4017
|
/**
|
|
3993
4018
|
* Default endpoint templates with placeholder support
|
|
3994
4019
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3996,6 +4021,7 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3996
4021
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3997
4022
|
getTranslation: "/api/integrations/translations/:key",
|
|
3998
4023
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
4024
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3999
4025
|
setTranslation: "/api/integrations/sync/:key",
|
|
4000
4026
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
4001
4027
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -4107,6 +4133,40 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4107
4133
|
this.handleCaughtError(error);
|
|
4108
4134
|
}
|
|
4109
4135
|
}
|
|
4136
|
+
/**
|
|
4137
|
+
* Project-level config for this key — the dashboard's language list,
|
|
4138
|
+
* served under the same trust model as translation reads (docs/20,
|
|
4139
|
+
* companion track). Returns null when the server doesn't expose the
|
|
4140
|
+
* endpoint (404 — older server, feature not deployed) or the payload is
|
|
4141
|
+
* unusable: absence means "keep your local fallback", never an error.
|
|
4142
|
+
*/
|
|
4143
|
+
async getProjectConfig() {
|
|
4144
|
+
if (!this.config?.options?.baseUrl) {
|
|
4145
|
+
throw new NetworkError("API base URL is not set");
|
|
4146
|
+
}
|
|
4147
|
+
const url = this.buildUrl("getProjectConfig", {});
|
|
4148
|
+
logger.debug(`API GET ProjectConfig: ${url}`);
|
|
4149
|
+
try {
|
|
4150
|
+
const response = await this.makeRequest(url, "GET");
|
|
4151
|
+
if (response.status === 404) {
|
|
4152
|
+
return null;
|
|
4153
|
+
}
|
|
4154
|
+
if (!response.ok) {
|
|
4155
|
+
this.handleHttpError(response, "project-config");
|
|
4156
|
+
}
|
|
4157
|
+
const data = await response.json();
|
|
4158
|
+
if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
|
|
4159
|
+
logger.warn("project-config payload unusable \u2014 keeping local fallback");
|
|
4160
|
+
return null;
|
|
4161
|
+
}
|
|
4162
|
+
return {
|
|
4163
|
+
defaultLocale: data.defaultLocale,
|
|
4164
|
+
languages: data.languages.map(String)
|
|
4165
|
+
};
|
|
4166
|
+
} catch (error) {
|
|
4167
|
+
this.handleCaughtError(error);
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4110
4170
|
/**
|
|
4111
4171
|
* Shared namespace fetch with the canonical 304 contract:
|
|
4112
4172
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4431,7 +4491,87 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4431
4491
|
/**
|
|
4432
4492
|
* Make HTTP request with timeout and headers
|
|
4433
4493
|
*/
|
|
4434
|
-
|
|
4494
|
+
makeRequest(url, method, body, extraHeaders) {
|
|
4495
|
+
if (this.authFailedStatus !== null) {
|
|
4496
|
+
return Promise.resolve(
|
|
4497
|
+
new Response(null, {
|
|
4498
|
+
status: this.authFailedStatus,
|
|
4499
|
+
statusText: `auth latched off after ${this.authFailedStatus}`
|
|
4500
|
+
})
|
|
4501
|
+
);
|
|
4502
|
+
}
|
|
4503
|
+
if (method !== "GET") {
|
|
4504
|
+
if (this.writesFailedStatus !== null) {
|
|
4505
|
+
return Promise.resolve(
|
|
4506
|
+
new Response(null, {
|
|
4507
|
+
status: this.writesFailedStatus,
|
|
4508
|
+
statusText: `writes latched off after ${this.writesFailedStatus}`
|
|
4509
|
+
})
|
|
4510
|
+
);
|
|
4511
|
+
}
|
|
4512
|
+
return this.doFetch(url, method, body, extraHeaders).then(
|
|
4513
|
+
(resp) => this.recordOutcome(url, method, resp)
|
|
4514
|
+
);
|
|
4515
|
+
}
|
|
4516
|
+
const negative = this.negativeCache.get(url);
|
|
4517
|
+
if (negative) {
|
|
4518
|
+
if (Date.now() < negative.until) {
|
|
4519
|
+
logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
|
|
4520
|
+
const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
|
|
4521
|
+
return Promise.resolve(
|
|
4522
|
+
new Response(null, { status: negative.status, statusText: "negative cache", headers })
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4525
|
+
this.negativeCache.delete(url);
|
|
4526
|
+
}
|
|
4527
|
+
const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
|
|
4528
|
+
const existing = this.inflightGets.get(flightKey);
|
|
4529
|
+
if (existing) {
|
|
4530
|
+
return existing.then((resp) => this.cloneResponse(resp));
|
|
4531
|
+
}
|
|
4532
|
+
const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
|
|
4533
|
+
this.inflightGets.set(flightKey, flight);
|
|
4534
|
+
return flight.then((resp) => this.cloneResponse(resp));
|
|
4535
|
+
}
|
|
4536
|
+
/** Bookkeeping on every real (non-synthesized) response — runs once per
|
|
4537
|
+
* physical fetch, never once per coalesced awaiter. */
|
|
4538
|
+
recordOutcome(url, method, response) {
|
|
4539
|
+
const status = response.status;
|
|
4540
|
+
if (status === 401 || status === 403 && method === "GET") {
|
|
4541
|
+
if (this.authFailedStatus === null) {
|
|
4542
|
+
this.authFailedStatus = status;
|
|
4543
|
+
console.warn(
|
|
4544
|
+
`[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
|
|
4545
|
+
);
|
|
4546
|
+
}
|
|
4547
|
+
return response;
|
|
4548
|
+
}
|
|
4549
|
+
if ((status === 403 || status === 429) && method !== "GET") {
|
|
4550
|
+
if (this.writesFailedStatus === null) {
|
|
4551
|
+
this.writesFailedStatus = status;
|
|
4552
|
+
logger.warn(
|
|
4553
|
+
`Write path disabled after ${status} (scope/quota) \u2014 running read-only`
|
|
4554
|
+
);
|
|
4555
|
+
}
|
|
4556
|
+
return response;
|
|
4557
|
+
}
|
|
4558
|
+
if (method === "GET" && (status === 404 || status === 423)) {
|
|
4559
|
+
const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
|
|
4560
|
+
const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
|
|
4561
|
+
this.negativeCache.set(url, { status, until: Date.now() + ttl });
|
|
4562
|
+
} else if (method !== "GET" && response.ok) {
|
|
4563
|
+
this.negativeCache.clear();
|
|
4564
|
+
} else if (method === "GET" && response.ok) {
|
|
4565
|
+
this.negativeCache.delete(url);
|
|
4566
|
+
}
|
|
4567
|
+
return response;
|
|
4568
|
+
}
|
|
4569
|
+
/** Test doubles (and exotic runtimes) hand us plain objects — only real
|
|
4570
|
+
* Responses need cloning so each coalesced awaiter can read the body. */
|
|
4571
|
+
cloneResponse(response) {
|
|
4572
|
+
return typeof response.clone === "function" ? response.clone() : response;
|
|
4573
|
+
}
|
|
4574
|
+
async doFetch(url, method, body, extraHeaders) {
|
|
4435
4575
|
const controller = new AbortController();
|
|
4436
4576
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4437
4577
|
const timeoutId = setTimeout(
|
|
@@ -4464,6 +4604,7 @@ __publicField(_NetworkRepository, "SNAPSHOT_PREFIX", "lionrapid:ns:");
|
|
|
4464
4604
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4465
4605
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4466
4606
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4607
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4467
4608
|
var NetworkRepository = _NetworkRepository;
|
|
4468
4609
|
|
|
4469
4610
|
// src/content/validator/ContentValidator.ts
|
|
@@ -5062,9 +5203,11 @@ var LocalStorageRepository = class {
|
|
|
5062
5203
|
this.config = {
|
|
5063
5204
|
enabled: config.enabled ?? true,
|
|
5064
5205
|
priority: config.priority ?? 0,
|
|
5206
|
+
// Use build sequence by default
|
|
5065
5207
|
options: {
|
|
5066
5208
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
5067
5209
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5210
|
+
// 24 hours default
|
|
5068
5211
|
maxSize: config.options?.maxSize ?? 500,
|
|
5069
5212
|
...config.options
|
|
5070
5213
|
}
|
|
@@ -5077,6 +5220,10 @@ var LocalStorageRepository = class {
|
|
|
5077
5220
|
this.migrateIfNeeded();
|
|
5078
5221
|
}
|
|
5079
5222
|
}
|
|
5223
|
+
/**
|
|
5224
|
+
* Check if localStorage is available
|
|
5225
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5226
|
+
*/
|
|
5080
5227
|
checkAvailability() {
|
|
5081
5228
|
if (typeof window === "undefined") {
|
|
5082
5229
|
return false;
|
|
@@ -5090,6 +5237,9 @@ var LocalStorageRepository = class {
|
|
|
5090
5237
|
return false;
|
|
5091
5238
|
}
|
|
5092
5239
|
}
|
|
5240
|
+
/**
|
|
5241
|
+
* Handle version migrations - clear cache when version changes
|
|
5242
|
+
*/
|
|
5093
5243
|
migrateIfNeeded() {
|
|
5094
5244
|
try {
|
|
5095
5245
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5100,6 +5250,10 @@ var LocalStorageRepository = class {
|
|
|
5100
5250
|
} catch {
|
|
5101
5251
|
}
|
|
5102
5252
|
}
|
|
5253
|
+
/**
|
|
5254
|
+
* Get a translation value by key
|
|
5255
|
+
* Returns null if not found or expired
|
|
5256
|
+
*/
|
|
5103
5257
|
get(key) {
|
|
5104
5258
|
if (!this.available) return null;
|
|
5105
5259
|
try {
|
|
@@ -5116,6 +5270,10 @@ var LocalStorageRepository = class {
|
|
|
5116
5270
|
return null;
|
|
5117
5271
|
}
|
|
5118
5272
|
}
|
|
5273
|
+
/**
|
|
5274
|
+
* Set a translation value
|
|
5275
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5276
|
+
*/
|
|
5119
5277
|
set(key, value) {
|
|
5120
5278
|
if (!this.available) return;
|
|
5121
5279
|
const fullKey = this.prefix + key;
|
|
@@ -5140,6 +5298,9 @@ var LocalStorageRepository = class {
|
|
|
5140
5298
|
}
|
|
5141
5299
|
}
|
|
5142
5300
|
}
|
|
5301
|
+
/**
|
|
5302
|
+
* Remove a translation by key
|
|
5303
|
+
*/
|
|
5143
5304
|
remove(key) {
|
|
5144
5305
|
if (!this.available) return;
|
|
5145
5306
|
try {
|
|
@@ -5147,6 +5308,10 @@ var LocalStorageRepository = class {
|
|
|
5147
5308
|
} catch {
|
|
5148
5309
|
}
|
|
5149
5310
|
}
|
|
5311
|
+
/**
|
|
5312
|
+
* Clear all LionRapid entries from localStorage
|
|
5313
|
+
* Preserves entries from other applications
|
|
5314
|
+
*/
|
|
5150
5315
|
clear() {
|
|
5151
5316
|
if (!this.available) return;
|
|
5152
5317
|
try {
|
|
@@ -5161,6 +5326,9 @@ var LocalStorageRepository = class {
|
|
|
5161
5326
|
} catch {
|
|
5162
5327
|
}
|
|
5163
5328
|
}
|
|
5329
|
+
/**
|
|
5330
|
+
* Clear all entries for a specific namespace
|
|
5331
|
+
*/
|
|
5164
5332
|
clearNamespace(locale, namespace) {
|
|
5165
5333
|
if (!this.available) return;
|
|
5166
5334
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5179,6 +5347,9 @@ var LocalStorageRepository = class {
|
|
|
5179
5347
|
} catch {
|
|
5180
5348
|
}
|
|
5181
5349
|
}
|
|
5350
|
+
/**
|
|
5351
|
+
* Get namespace translations
|
|
5352
|
+
*/
|
|
5182
5353
|
getNamespace(locale, namespace) {
|
|
5183
5354
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5184
5355
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5190,10 +5361,16 @@ var LocalStorageRepository = class {
|
|
|
5190
5361
|
return null;
|
|
5191
5362
|
}
|
|
5192
5363
|
}
|
|
5364
|
+
/**
|
|
5365
|
+
* Set namespace translations
|
|
5366
|
+
*/
|
|
5193
5367
|
setNamespace(locale, namespace, translations) {
|
|
5194
5368
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5195
5369
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5196
5370
|
}
|
|
5371
|
+
/**
|
|
5372
|
+
* Get all cache entries for debugging
|
|
5373
|
+
*/
|
|
5197
5374
|
getEntries() {
|
|
5198
5375
|
const entries = {};
|
|
5199
5376
|
if (!this.available) return entries;
|
|
@@ -5213,12 +5390,21 @@ var LocalStorageRepository = class {
|
|
|
5213
5390
|
}
|
|
5214
5391
|
return entries;
|
|
5215
5392
|
}
|
|
5393
|
+
/**
|
|
5394
|
+
* Get repository configuration
|
|
5395
|
+
*/
|
|
5216
5396
|
getConfig() {
|
|
5217
5397
|
return this.config;
|
|
5218
5398
|
}
|
|
5399
|
+
/**
|
|
5400
|
+
* Check if localStorage is available
|
|
5401
|
+
*/
|
|
5219
5402
|
isAvailable() {
|
|
5220
5403
|
return this.available;
|
|
5221
5404
|
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Get the number of entries in the cache
|
|
5407
|
+
*/
|
|
5222
5408
|
getEntryCount() {
|
|
5223
5409
|
if (!this.available) return 0;
|
|
5224
5410
|
let count = 0;
|
|
@@ -5233,12 +5419,18 @@ var LocalStorageRepository = class {
|
|
|
5233
5419
|
}
|
|
5234
5420
|
return count;
|
|
5235
5421
|
}
|
|
5422
|
+
/**
|
|
5423
|
+
* Check if error is a quota exceeded error
|
|
5424
|
+
*/
|
|
5236
5425
|
isQuotaError(error) {
|
|
5237
5426
|
if (error instanceof DOMException) {
|
|
5238
5427
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5239
5428
|
}
|
|
5240
5429
|
return false;
|
|
5241
5430
|
}
|
|
5431
|
+
/**
|
|
5432
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5433
|
+
*/
|
|
5242
5434
|
evictOldest() {
|
|
5243
5435
|
if (!this.available) return;
|
|
5244
5436
|
try {
|
|
@@ -5266,6 +5458,9 @@ var LocalStorageRepository = class {
|
|
|
5266
5458
|
} catch {
|
|
5267
5459
|
}
|
|
5268
5460
|
}
|
|
5461
|
+
/**
|
|
5462
|
+
* Get cache statistics
|
|
5463
|
+
*/
|
|
5269
5464
|
getCacheStats() {
|
|
5270
5465
|
const size = this.getEntryCount();
|
|
5271
5466
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5279,7 +5474,7 @@ var LocalStorageRepository = class {
|
|
|
5279
5474
|
|
|
5280
5475
|
// src/browser/storage/cache-storage-repository.ts
|
|
5281
5476
|
var CACHE_NAME = "lionrapid-v1";
|
|
5282
|
-
var CACHE_PREFIX = "lionrapid
|
|
5477
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5283
5478
|
var CacheStorageRepository = class {
|
|
5284
5479
|
constructor(config = {}) {
|
|
5285
5480
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5294,6 +5489,7 @@ var CacheStorageRepository = class {
|
|
|
5294
5489
|
options: {
|
|
5295
5490
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5296
5491
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5492
|
+
// 7 days default
|
|
5297
5493
|
...config.options
|
|
5298
5494
|
}
|
|
5299
5495
|
};
|
|
@@ -5301,9 +5497,17 @@ var CacheStorageRepository = class {
|
|
|
5301
5497
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5302
5498
|
this.available = this.checkAvailability();
|
|
5303
5499
|
}
|
|
5500
|
+
/**
|
|
5501
|
+
* Check if Cache API is available
|
|
5502
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
5503
|
+
*/
|
|
5304
5504
|
checkAvailability() {
|
|
5305
5505
|
return typeof caches !== "undefined";
|
|
5306
5506
|
}
|
|
5507
|
+
/**
|
|
5508
|
+
* Get a translation value by key
|
|
5509
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
5510
|
+
*/
|
|
5307
5511
|
async get(key) {
|
|
5308
5512
|
if (!this.available) {
|
|
5309
5513
|
throw new TranslationNotFoundError(key);
|
|
@@ -5327,6 +5531,9 @@ var CacheStorageRepository = class {
|
|
|
5327
5531
|
throw new TranslationNotFoundError(key);
|
|
5328
5532
|
}
|
|
5329
5533
|
}
|
|
5534
|
+
/**
|
|
5535
|
+
* Set a translation value
|
|
5536
|
+
*/
|
|
5330
5537
|
async set(key, value) {
|
|
5331
5538
|
if (!this.available) return;
|
|
5332
5539
|
try {
|
|
@@ -5345,6 +5552,9 @@ var CacheStorageRepository = class {
|
|
|
5345
5552
|
} catch {
|
|
5346
5553
|
}
|
|
5347
5554
|
}
|
|
5555
|
+
/**
|
|
5556
|
+
* Remove a translation by key
|
|
5557
|
+
*/
|
|
5348
5558
|
async remove(key) {
|
|
5349
5559
|
if (!this.available) return;
|
|
5350
5560
|
try {
|
|
@@ -5353,6 +5563,9 @@ var CacheStorageRepository = class {
|
|
|
5353
5563
|
} catch {
|
|
5354
5564
|
}
|
|
5355
5565
|
}
|
|
5566
|
+
/**
|
|
5567
|
+
* Clear all LionRapid entries from the cache
|
|
5568
|
+
*/
|
|
5356
5569
|
async clear() {
|
|
5357
5570
|
if (!this.available) return;
|
|
5358
5571
|
try {
|
|
@@ -5364,6 +5577,9 @@ var CacheStorageRepository = class {
|
|
|
5364
5577
|
} catch {
|
|
5365
5578
|
}
|
|
5366
5579
|
}
|
|
5580
|
+
/**
|
|
5581
|
+
* Clear all entries for a specific namespace
|
|
5582
|
+
*/
|
|
5367
5583
|
async clearNamespace(locale, namespace) {
|
|
5368
5584
|
if (!this.available) return;
|
|
5369
5585
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5381,6 +5597,10 @@ var CacheStorageRepository = class {
|
|
|
5381
5597
|
} catch {
|
|
5382
5598
|
}
|
|
5383
5599
|
}
|
|
5600
|
+
/**
|
|
5601
|
+
* Get namespace translations
|
|
5602
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
5603
|
+
*/
|
|
5384
5604
|
async getNamespace(locale, namespace) {
|
|
5385
5605
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5386
5606
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5391,16 +5611,29 @@ var CacheStorageRepository = class {
|
|
|
5391
5611
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5392
5612
|
}
|
|
5393
5613
|
}
|
|
5614
|
+
/**
|
|
5615
|
+
* Set namespace translations
|
|
5616
|
+
*/
|
|
5394
5617
|
async setNamespace(locale, namespace, translations) {
|
|
5395
5618
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5396
5619
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5397
5620
|
}
|
|
5621
|
+
/**
|
|
5622
|
+
* Get repository configuration
|
|
5623
|
+
*/
|
|
5398
5624
|
getConfig() {
|
|
5399
5625
|
return this.config;
|
|
5400
5626
|
}
|
|
5627
|
+
/**
|
|
5628
|
+
* Check if Cache API is available
|
|
5629
|
+
*/
|
|
5401
5630
|
isAvailable() {
|
|
5402
5631
|
return this.available;
|
|
5403
5632
|
}
|
|
5633
|
+
/**
|
|
5634
|
+
* Get all cached entries (for debugging)
|
|
5635
|
+
* Returns an object mapping keys to values
|
|
5636
|
+
*/
|
|
5404
5637
|
async getEntries() {
|
|
5405
5638
|
const entries = {};
|
|
5406
5639
|
if (!this.available) return entries;
|
|
@@ -5423,6 +5656,9 @@ var CacheStorageRepository = class {
|
|
|
5423
5656
|
}
|
|
5424
5657
|
return entries;
|
|
5425
5658
|
}
|
|
5659
|
+
/**
|
|
5660
|
+
* Get cache statistics
|
|
5661
|
+
*/
|
|
5426
5662
|
async getCacheStats() {
|
|
5427
5663
|
if (!this.available) {
|
|
5428
5664
|
return { size: 0, cacheName: this.cacheName };
|