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