@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
|
@@ -3897,6 +3897,31 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3897
3897
|
* and payload can never desync because they live and die together.
|
|
3898
3898
|
*/
|
|
3899
3899
|
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3900
|
+
/**
|
|
3901
|
+
* Miss-path flood protection (READINESS before-scale #1: one cold locale
|
|
3902
|
+
* switch measured ~380 requests in 10 s — every t() miss refired a key GET
|
|
3903
|
+
* and a namespace GET on every re-render, forever on animated pages).
|
|
3904
|
+
*/
|
|
3905
|
+
/** GETs currently on the wire, keyed by `url|if-none-match` — identical
|
|
3906
|
+
* concurrent requests share one fetch; each awaiter gets a clone. The
|
|
3907
|
+
* If-None-Match value is part of the key so the 304-without-snapshot
|
|
3908
|
+
* unconditional refetch never coalesces onto a conditional flight. */
|
|
3909
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
3910
|
+
/** URL → 404/423 seen recently: repeats are answered locally (same typed
|
|
3911
|
+
* error, no network) until `until`. 423 honors the server's Retry-After.
|
|
3912
|
+
* Cleared entirely by any successful write — discovery sync keeps its
|
|
3913
|
+
* miss → batch → served-back round-trip. */
|
|
3914
|
+
__publicField(this, "negativeCache", /* @__PURE__ */ new Map());
|
|
3915
|
+
/** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
|
|
3916
|
+
* wrong or unauthorized for this project, and retrying cannot heal it —
|
|
3917
|
+
* after the first one nothing touches the network again for this
|
|
3918
|
+
* instance (a page reload starts fresh). Holds the status that tripped
|
|
3919
|
+
* it so short-circuits answer honestly. */
|
|
3920
|
+
__publicField(this, "authFailedStatus", null);
|
|
3921
|
+
/** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
|
|
3922
|
+
* a WRITE degrades this instance to read-only — writes stop, silently
|
|
3923
|
+
* and without retries, while reads keep serving the page. */
|
|
3924
|
+
__publicField(this, "writesFailedStatus", null);
|
|
3900
3925
|
/**
|
|
3901
3926
|
* Default endpoint templates with placeholder support
|
|
3902
3927
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3904,6 +3929,7 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3904
3929
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3905
3930
|
getTranslation: "/api/integrations/translations/:key",
|
|
3906
3931
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
3932
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3907
3933
|
setTranslation: "/api/integrations/sync/:key",
|
|
3908
3934
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
3909
3935
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -4015,6 +4041,40 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4015
4041
|
this.handleCaughtError(error);
|
|
4016
4042
|
}
|
|
4017
4043
|
}
|
|
4044
|
+
/**
|
|
4045
|
+
* Project-level config for this key — the dashboard's language list,
|
|
4046
|
+
* served under the same trust model as translation reads (docs/20,
|
|
4047
|
+
* companion track). Returns null when the server doesn't expose the
|
|
4048
|
+
* endpoint (404 — older server, feature not deployed) or the payload is
|
|
4049
|
+
* unusable: absence means "keep your local fallback", never an error.
|
|
4050
|
+
*/
|
|
4051
|
+
async getProjectConfig() {
|
|
4052
|
+
if (!this.config?.options?.baseUrl) {
|
|
4053
|
+
throw new NetworkError("API base URL is not set");
|
|
4054
|
+
}
|
|
4055
|
+
const url = this.buildUrl("getProjectConfig", {});
|
|
4056
|
+
logger.debug(`API GET ProjectConfig: ${url}`);
|
|
4057
|
+
try {
|
|
4058
|
+
const response = await this.makeRequest(url, "GET");
|
|
4059
|
+
if (response.status === 404) {
|
|
4060
|
+
return null;
|
|
4061
|
+
}
|
|
4062
|
+
if (!response.ok) {
|
|
4063
|
+
this.handleHttpError(response, "project-config");
|
|
4064
|
+
}
|
|
4065
|
+
const data = await response.json();
|
|
4066
|
+
if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
|
|
4067
|
+
logger.warn("project-config payload unusable \u2014 keeping local fallback");
|
|
4068
|
+
return null;
|
|
4069
|
+
}
|
|
4070
|
+
return {
|
|
4071
|
+
defaultLocale: data.defaultLocale,
|
|
4072
|
+
languages: data.languages.map(String)
|
|
4073
|
+
};
|
|
4074
|
+
} catch (error) {
|
|
4075
|
+
this.handleCaughtError(error);
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4018
4078
|
/**
|
|
4019
4079
|
* Shared namespace fetch with the canonical 304 contract:
|
|
4020
4080
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4339,7 +4399,87 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4339
4399
|
/**
|
|
4340
4400
|
* Make HTTP request with timeout and headers
|
|
4341
4401
|
*/
|
|
4342
|
-
|
|
4402
|
+
makeRequest(url, method, body, extraHeaders) {
|
|
4403
|
+
if (this.authFailedStatus !== null) {
|
|
4404
|
+
return Promise.resolve(
|
|
4405
|
+
new Response(null, {
|
|
4406
|
+
status: this.authFailedStatus,
|
|
4407
|
+
statusText: `auth latched off after ${this.authFailedStatus}`
|
|
4408
|
+
})
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
if (method !== "GET") {
|
|
4412
|
+
if (this.writesFailedStatus !== null) {
|
|
4413
|
+
return Promise.resolve(
|
|
4414
|
+
new Response(null, {
|
|
4415
|
+
status: this.writesFailedStatus,
|
|
4416
|
+
statusText: `writes latched off after ${this.writesFailedStatus}`
|
|
4417
|
+
})
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
4420
|
+
return this.doFetch(url, method, body, extraHeaders).then(
|
|
4421
|
+
(resp) => this.recordOutcome(url, method, resp)
|
|
4422
|
+
);
|
|
4423
|
+
}
|
|
4424
|
+
const negative = this.negativeCache.get(url);
|
|
4425
|
+
if (negative) {
|
|
4426
|
+
if (Date.now() < negative.until) {
|
|
4427
|
+
logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
|
|
4428
|
+
const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
|
|
4429
|
+
return Promise.resolve(
|
|
4430
|
+
new Response(null, { status: negative.status, statusText: "negative cache", headers })
|
|
4431
|
+
);
|
|
4432
|
+
}
|
|
4433
|
+
this.negativeCache.delete(url);
|
|
4434
|
+
}
|
|
4435
|
+
const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
|
|
4436
|
+
const existing = this.inflightGets.get(flightKey);
|
|
4437
|
+
if (existing) {
|
|
4438
|
+
return existing.then((resp) => this.cloneResponse(resp));
|
|
4439
|
+
}
|
|
4440
|
+
const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
|
|
4441
|
+
this.inflightGets.set(flightKey, flight);
|
|
4442
|
+
return flight.then((resp) => this.cloneResponse(resp));
|
|
4443
|
+
}
|
|
4444
|
+
/** Bookkeeping on every real (non-synthesized) response — runs once per
|
|
4445
|
+
* physical fetch, never once per coalesced awaiter. */
|
|
4446
|
+
recordOutcome(url, method, response) {
|
|
4447
|
+
const status = response.status;
|
|
4448
|
+
if (status === 401 || status === 403 && method === "GET") {
|
|
4449
|
+
if (this.authFailedStatus === null) {
|
|
4450
|
+
this.authFailedStatus = status;
|
|
4451
|
+
console.warn(
|
|
4452
|
+
`[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
|
|
4453
|
+
);
|
|
4454
|
+
}
|
|
4455
|
+
return response;
|
|
4456
|
+
}
|
|
4457
|
+
if ((status === 403 || status === 429) && method !== "GET") {
|
|
4458
|
+
if (this.writesFailedStatus === null) {
|
|
4459
|
+
this.writesFailedStatus = status;
|
|
4460
|
+
logger.warn(
|
|
4461
|
+
`Write path disabled after ${status} (scope/quota) \u2014 running read-only`
|
|
4462
|
+
);
|
|
4463
|
+
}
|
|
4464
|
+
return response;
|
|
4465
|
+
}
|
|
4466
|
+
if (method === "GET" && (status === 404 || status === 423)) {
|
|
4467
|
+
const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
|
|
4468
|
+
const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
|
|
4469
|
+
this.negativeCache.set(url, { status, until: Date.now() + ttl });
|
|
4470
|
+
} else if (method !== "GET" && response.ok) {
|
|
4471
|
+
this.negativeCache.clear();
|
|
4472
|
+
} else if (method === "GET" && response.ok) {
|
|
4473
|
+
this.negativeCache.delete(url);
|
|
4474
|
+
}
|
|
4475
|
+
return response;
|
|
4476
|
+
}
|
|
4477
|
+
/** Test doubles (and exotic runtimes) hand us plain objects — only real
|
|
4478
|
+
* Responses need cloning so each coalesced awaiter can read the body. */
|
|
4479
|
+
cloneResponse(response) {
|
|
4480
|
+
return typeof response.clone === "function" ? response.clone() : response;
|
|
4481
|
+
}
|
|
4482
|
+
async doFetch(url, method, body, extraHeaders) {
|
|
4343
4483
|
const controller = new AbortController();
|
|
4344
4484
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4345
4485
|
const timeoutId = setTimeout(
|
|
@@ -4372,6 +4512,7 @@ __publicField(_NetworkRepository, "SNAPSHOT_PREFIX", "lionrapid:ns:");
|
|
|
4372
4512
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4373
4513
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4374
4514
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4515
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4375
4516
|
var NetworkRepository = _NetworkRepository;
|
|
4376
4517
|
|
|
4377
4518
|
// src/content/validator/ContentValidator.ts
|
|
@@ -4970,9 +5111,11 @@ var LocalStorageRepository = class {
|
|
|
4970
5111
|
this.config = {
|
|
4971
5112
|
enabled: config.enabled ?? true,
|
|
4972
5113
|
priority: config.priority ?? 0,
|
|
5114
|
+
// Use build sequence by default
|
|
4973
5115
|
options: {
|
|
4974
5116
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
4975
5117
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5118
|
+
// 24 hours default
|
|
4976
5119
|
maxSize: config.options?.maxSize ?? 500,
|
|
4977
5120
|
...config.options
|
|
4978
5121
|
}
|
|
@@ -4985,6 +5128,10 @@ var LocalStorageRepository = class {
|
|
|
4985
5128
|
this.migrateIfNeeded();
|
|
4986
5129
|
}
|
|
4987
5130
|
}
|
|
5131
|
+
/**
|
|
5132
|
+
* Check if localStorage is available
|
|
5133
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5134
|
+
*/
|
|
4988
5135
|
checkAvailability() {
|
|
4989
5136
|
if (typeof window === "undefined") {
|
|
4990
5137
|
return false;
|
|
@@ -4998,6 +5145,9 @@ var LocalStorageRepository = class {
|
|
|
4998
5145
|
return false;
|
|
4999
5146
|
}
|
|
5000
5147
|
}
|
|
5148
|
+
/**
|
|
5149
|
+
* Handle version migrations - clear cache when version changes
|
|
5150
|
+
*/
|
|
5001
5151
|
migrateIfNeeded() {
|
|
5002
5152
|
try {
|
|
5003
5153
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5008,6 +5158,10 @@ var LocalStorageRepository = class {
|
|
|
5008
5158
|
} catch {
|
|
5009
5159
|
}
|
|
5010
5160
|
}
|
|
5161
|
+
/**
|
|
5162
|
+
* Get a translation value by key
|
|
5163
|
+
* Returns null if not found or expired
|
|
5164
|
+
*/
|
|
5011
5165
|
get(key) {
|
|
5012
5166
|
if (!this.available) return null;
|
|
5013
5167
|
try {
|
|
@@ -5024,6 +5178,10 @@ var LocalStorageRepository = class {
|
|
|
5024
5178
|
return null;
|
|
5025
5179
|
}
|
|
5026
5180
|
}
|
|
5181
|
+
/**
|
|
5182
|
+
* Set a translation value
|
|
5183
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5184
|
+
*/
|
|
5027
5185
|
set(key, value) {
|
|
5028
5186
|
if (!this.available) return;
|
|
5029
5187
|
const fullKey = this.prefix + key;
|
|
@@ -5048,6 +5206,9 @@ var LocalStorageRepository = class {
|
|
|
5048
5206
|
}
|
|
5049
5207
|
}
|
|
5050
5208
|
}
|
|
5209
|
+
/**
|
|
5210
|
+
* Remove a translation by key
|
|
5211
|
+
*/
|
|
5051
5212
|
remove(key) {
|
|
5052
5213
|
if (!this.available) return;
|
|
5053
5214
|
try {
|
|
@@ -5055,6 +5216,10 @@ var LocalStorageRepository = class {
|
|
|
5055
5216
|
} catch {
|
|
5056
5217
|
}
|
|
5057
5218
|
}
|
|
5219
|
+
/**
|
|
5220
|
+
* Clear all LionRapid entries from localStorage
|
|
5221
|
+
* Preserves entries from other applications
|
|
5222
|
+
*/
|
|
5058
5223
|
clear() {
|
|
5059
5224
|
if (!this.available) return;
|
|
5060
5225
|
try {
|
|
@@ -5069,6 +5234,9 @@ var LocalStorageRepository = class {
|
|
|
5069
5234
|
} catch {
|
|
5070
5235
|
}
|
|
5071
5236
|
}
|
|
5237
|
+
/**
|
|
5238
|
+
* Clear all entries for a specific namespace
|
|
5239
|
+
*/
|
|
5072
5240
|
clearNamespace(locale, namespace) {
|
|
5073
5241
|
if (!this.available) return;
|
|
5074
5242
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5087,6 +5255,9 @@ var LocalStorageRepository = class {
|
|
|
5087
5255
|
} catch {
|
|
5088
5256
|
}
|
|
5089
5257
|
}
|
|
5258
|
+
/**
|
|
5259
|
+
* Get namespace translations
|
|
5260
|
+
*/
|
|
5090
5261
|
getNamespace(locale, namespace) {
|
|
5091
5262
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5092
5263
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5098,10 +5269,16 @@ var LocalStorageRepository = class {
|
|
|
5098
5269
|
return null;
|
|
5099
5270
|
}
|
|
5100
5271
|
}
|
|
5272
|
+
/**
|
|
5273
|
+
* Set namespace translations
|
|
5274
|
+
*/
|
|
5101
5275
|
setNamespace(locale, namespace, translations) {
|
|
5102
5276
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5103
5277
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5104
5278
|
}
|
|
5279
|
+
/**
|
|
5280
|
+
* Get all cache entries for debugging
|
|
5281
|
+
*/
|
|
5105
5282
|
getEntries() {
|
|
5106
5283
|
const entries = {};
|
|
5107
5284
|
if (!this.available) return entries;
|
|
@@ -5121,12 +5298,21 @@ var LocalStorageRepository = class {
|
|
|
5121
5298
|
}
|
|
5122
5299
|
return entries;
|
|
5123
5300
|
}
|
|
5301
|
+
/**
|
|
5302
|
+
* Get repository configuration
|
|
5303
|
+
*/
|
|
5124
5304
|
getConfig() {
|
|
5125
5305
|
return this.config;
|
|
5126
5306
|
}
|
|
5307
|
+
/**
|
|
5308
|
+
* Check if localStorage is available
|
|
5309
|
+
*/
|
|
5127
5310
|
isAvailable() {
|
|
5128
5311
|
return this.available;
|
|
5129
5312
|
}
|
|
5313
|
+
/**
|
|
5314
|
+
* Get the number of entries in the cache
|
|
5315
|
+
*/
|
|
5130
5316
|
getEntryCount() {
|
|
5131
5317
|
if (!this.available) return 0;
|
|
5132
5318
|
let count = 0;
|
|
@@ -5141,12 +5327,18 @@ var LocalStorageRepository = class {
|
|
|
5141
5327
|
}
|
|
5142
5328
|
return count;
|
|
5143
5329
|
}
|
|
5330
|
+
/**
|
|
5331
|
+
* Check if error is a quota exceeded error
|
|
5332
|
+
*/
|
|
5144
5333
|
isQuotaError(error) {
|
|
5145
5334
|
if (error instanceof DOMException) {
|
|
5146
5335
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5147
5336
|
}
|
|
5148
5337
|
return false;
|
|
5149
5338
|
}
|
|
5339
|
+
/**
|
|
5340
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5341
|
+
*/
|
|
5150
5342
|
evictOldest() {
|
|
5151
5343
|
if (!this.available) return;
|
|
5152
5344
|
try {
|
|
@@ -5174,6 +5366,9 @@ var LocalStorageRepository = class {
|
|
|
5174
5366
|
} catch {
|
|
5175
5367
|
}
|
|
5176
5368
|
}
|
|
5369
|
+
/**
|
|
5370
|
+
* Get cache statistics
|
|
5371
|
+
*/
|
|
5177
5372
|
getCacheStats() {
|
|
5178
5373
|
const size = this.getEntryCount();
|
|
5179
5374
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5187,7 +5382,7 @@ var LocalStorageRepository = class {
|
|
|
5187
5382
|
|
|
5188
5383
|
// src/browser/storage/cache-storage-repository.ts
|
|
5189
5384
|
var CACHE_NAME = "lionrapid-v1";
|
|
5190
|
-
var CACHE_PREFIX = "lionrapid
|
|
5385
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5191
5386
|
var CacheStorageRepository = class {
|
|
5192
5387
|
constructor(config = {}) {
|
|
5193
5388
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5202,6 +5397,7 @@ var CacheStorageRepository = class {
|
|
|
5202
5397
|
options: {
|
|
5203
5398
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5204
5399
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5400
|
+
// 7 days default
|
|
5205
5401
|
...config.options
|
|
5206
5402
|
}
|
|
5207
5403
|
};
|
|
@@ -5209,9 +5405,17 @@ var CacheStorageRepository = class {
|
|
|
5209
5405
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5210
5406
|
this.available = this.checkAvailability();
|
|
5211
5407
|
}
|
|
5408
|
+
/**
|
|
5409
|
+
* Check if Cache API is available
|
|
5410
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
5411
|
+
*/
|
|
5212
5412
|
checkAvailability() {
|
|
5213
5413
|
return typeof caches !== "undefined";
|
|
5214
5414
|
}
|
|
5415
|
+
/**
|
|
5416
|
+
* Get a translation value by key
|
|
5417
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
5418
|
+
*/
|
|
5215
5419
|
async get(key) {
|
|
5216
5420
|
if (!this.available) {
|
|
5217
5421
|
throw new TranslationNotFoundError(key);
|
|
@@ -5235,6 +5439,9 @@ var CacheStorageRepository = class {
|
|
|
5235
5439
|
throw new TranslationNotFoundError(key);
|
|
5236
5440
|
}
|
|
5237
5441
|
}
|
|
5442
|
+
/**
|
|
5443
|
+
* Set a translation value
|
|
5444
|
+
*/
|
|
5238
5445
|
async set(key, value) {
|
|
5239
5446
|
if (!this.available) return;
|
|
5240
5447
|
try {
|
|
@@ -5253,6 +5460,9 @@ var CacheStorageRepository = class {
|
|
|
5253
5460
|
} catch {
|
|
5254
5461
|
}
|
|
5255
5462
|
}
|
|
5463
|
+
/**
|
|
5464
|
+
* Remove a translation by key
|
|
5465
|
+
*/
|
|
5256
5466
|
async remove(key) {
|
|
5257
5467
|
if (!this.available) return;
|
|
5258
5468
|
try {
|
|
@@ -5261,6 +5471,9 @@ var CacheStorageRepository = class {
|
|
|
5261
5471
|
} catch {
|
|
5262
5472
|
}
|
|
5263
5473
|
}
|
|
5474
|
+
/**
|
|
5475
|
+
* Clear all LionRapid entries from the cache
|
|
5476
|
+
*/
|
|
5264
5477
|
async clear() {
|
|
5265
5478
|
if (!this.available) return;
|
|
5266
5479
|
try {
|
|
@@ -5272,6 +5485,9 @@ var CacheStorageRepository = class {
|
|
|
5272
5485
|
} catch {
|
|
5273
5486
|
}
|
|
5274
5487
|
}
|
|
5488
|
+
/**
|
|
5489
|
+
* Clear all entries for a specific namespace
|
|
5490
|
+
*/
|
|
5275
5491
|
async clearNamespace(locale, namespace) {
|
|
5276
5492
|
if (!this.available) return;
|
|
5277
5493
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5289,6 +5505,10 @@ var CacheStorageRepository = class {
|
|
|
5289
5505
|
} catch {
|
|
5290
5506
|
}
|
|
5291
5507
|
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Get namespace translations
|
|
5510
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
5511
|
+
*/
|
|
5292
5512
|
async getNamespace(locale, namespace) {
|
|
5293
5513
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5294
5514
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5299,16 +5519,29 @@ var CacheStorageRepository = class {
|
|
|
5299
5519
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5300
5520
|
}
|
|
5301
5521
|
}
|
|
5522
|
+
/**
|
|
5523
|
+
* Set namespace translations
|
|
5524
|
+
*/
|
|
5302
5525
|
async setNamespace(locale, namespace, translations) {
|
|
5303
5526
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5304
5527
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5305
5528
|
}
|
|
5529
|
+
/**
|
|
5530
|
+
* Get repository configuration
|
|
5531
|
+
*/
|
|
5306
5532
|
getConfig() {
|
|
5307
5533
|
return this.config;
|
|
5308
5534
|
}
|
|
5535
|
+
/**
|
|
5536
|
+
* Check if Cache API is available
|
|
5537
|
+
*/
|
|
5309
5538
|
isAvailable() {
|
|
5310
5539
|
return this.available;
|
|
5311
5540
|
}
|
|
5541
|
+
/**
|
|
5542
|
+
* Get all cached entries (for debugging)
|
|
5543
|
+
* Returns an object mapping keys to values
|
|
5544
|
+
*/
|
|
5312
5545
|
async getEntries() {
|
|
5313
5546
|
const entries = {};
|
|
5314
5547
|
if (!this.available) return entries;
|
|
@@ -5331,6 +5564,9 @@ var CacheStorageRepository = class {
|
|
|
5331
5564
|
}
|
|
5332
5565
|
return entries;
|
|
5333
5566
|
}
|
|
5567
|
+
/**
|
|
5568
|
+
* Get cache statistics
|
|
5569
|
+
*/
|
|
5334
5570
|
async getCacheStats() {
|
|
5335
5571
|
if (!this.available) {
|
|
5336
5572
|
return { size: 0, cacheName: this.cacheName };
|