@lionrapid/core 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/auto-init.d.ts +17 -3
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +57 -30
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +285 -27
- package/dist/browser/lionrapid.auto.dev.js.map +2 -2
- package/dist/browser/lionrapid.auto.js +285 -27
- package/dist/browser/lionrapid.auto.js.map +2 -2
- package/dist/browser/lionrapid.auto.min.js +4 -4
- package/dist/browser/lionrapid.auto.min.js.map +3 -3
- package/dist/browser/lionrapid.esm.js +244 -2
- package/dist/browser/lionrapid.esm.js.map +2 -2
- package/dist/browser/lionrapid.esm.min.js +3 -3
- package/dist/browser/lionrapid.esm.min.js.map +3 -3
- package/dist/browser/lionrapid.iife.dev.js +244 -2
- package/dist/browser/lionrapid.iife.dev.js.map +2 -2
- package/dist/browser/lionrapid.iife.js +244 -2
- package/dist/browser/lionrapid.iife.js.map +2 -2
- package/dist/browser/lionrapid.iife.min.js +3 -3
- package/dist/browser/lionrapid.iife.min.js.map +3 -3
- package/dist/browser/lionrapid.umd.js +244 -2
- package/dist/browser/lionrapid.umd.js.map +2 -2
- package/dist/browser/lionrapid.umd.min.js +3 -3
- package/dist/browser/lionrapid.umd.min.js.map +3 -3
- package/dist/browser/storage/cache-storage-repository.d.ts +65 -0
- package/dist/browser/storage/cache-storage-repository.d.ts.map +1 -1
- package/dist/browser/storage/cache-storage-repository.js +77 -7
- package/dist/browser/storage/cache-storage-repository.js.map +1 -1
- package/dist/browser/storage/localstorage-repository.d.ts +62 -0
- package/dist/browser/storage/localstorage-repository.d.ts.map +1 -1
- package/dist/browser/storage/localstorage-repository.js +71 -4
- package/dist/browser/storage/localstorage-repository.js.map +1 -1
- package/dist/config/repository.config.d.ts +17 -0
- package/dist/config/repository.config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lionRapid.d.ts +1 -0
- package/dist/lionRapid.d.ts.map +1 -1
- package/dist/lionRapid.js +14 -0
- package/dist/lionRapid.js.map +1 -1
- package/dist/repositories/network-repository.d.ts +42 -1
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +149 -1
- package/dist/repositories/network-repository.js.map +1 -1
- package/package.json +1 -1
|
@@ -2911,6 +2911,7 @@ var LionRapidCore = class {
|
|
|
2911
2911
|
// 🔥 EMBEDDED: Core integration maps
|
|
2912
2912
|
__publicField(this, "formatters", /* @__PURE__ */ new Map());
|
|
2913
2913
|
__publicField(this, "processors", /* @__PURE__ */ new Map());
|
|
2914
|
+
__publicField(this, "warnedNoInterpolate", false);
|
|
2914
2915
|
// 📡 EVENT-DRIVEN: Just track for cleanup
|
|
2915
2916
|
__publicField(this, "eventPlugins", /* @__PURE__ */ new Set());
|
|
2916
2917
|
// 📦 CONTENT: Parser and serializer for ContentUnit processing
|
|
@@ -3203,6 +3204,11 @@ var LionRapidCore = class {
|
|
|
3203
3204
|
resolvedLocale,
|
|
3204
3205
|
resolvedKey
|
|
3205
3206
|
);
|
|
3207
|
+
} else if (!this.warnedNoInterpolate && params && Object.keys(params).length > 0 && processedValue.includes("{")) {
|
|
3208
|
+
this.warnedNoInterpolate = true;
|
|
3209
|
+
console.warn(
|
|
3210
|
+
"[LionRapid] Translation params were passed but no interpolate formatter is registered \u2014 {placeholders} render literally. Install @lionrapid/formatters and add .use(coreFormattersPlugin) to the builder."
|
|
3211
|
+
);
|
|
3206
3212
|
}
|
|
3207
3213
|
if (this.processors.size > 0) {
|
|
3208
3214
|
for (const [name, processor] of this.processors.entries()) {
|
|
@@ -3983,6 +3989,31 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3983
3989
|
* and payload can never desync because they live and die together.
|
|
3984
3990
|
*/
|
|
3985
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);
|
|
3986
4017
|
/**
|
|
3987
4018
|
* Default endpoint templates with placeholder support
|
|
3988
4019
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3990,6 +4021,7 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
3990
4021
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3991
4022
|
getTranslation: "/api/integrations/translations/:key",
|
|
3992
4023
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
4024
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3993
4025
|
setTranslation: "/api/integrations/sync/:key",
|
|
3994
4026
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
3995
4027
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -4101,6 +4133,40 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4101
4133
|
this.handleCaughtError(error);
|
|
4102
4134
|
}
|
|
4103
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
|
+
}
|
|
4104
4170
|
/**
|
|
4105
4171
|
* Shared namespace fetch with the canonical 304 contract:
|
|
4106
4172
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4425,7 +4491,87 @@ var _NetworkRepository = class _NetworkRepository {
|
|
|
4425
4491
|
/**
|
|
4426
4492
|
* Make HTTP request with timeout and headers
|
|
4427
4493
|
*/
|
|
4428
|
-
|
|
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) {
|
|
4429
4575
|
const controller = new AbortController();
|
|
4430
4576
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4431
4577
|
const timeoutId = setTimeout(
|
|
@@ -4458,6 +4604,7 @@ __publicField(_NetworkRepository, "SNAPSHOT_PREFIX", "lionrapid:ns:");
|
|
|
4458
4604
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4459
4605
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4460
4606
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4607
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4461
4608
|
var NetworkRepository = _NetworkRepository;
|
|
4462
4609
|
|
|
4463
4610
|
// src/content/validator/ContentValidator.ts
|
|
@@ -5056,9 +5203,11 @@ var LocalStorageRepository = class {
|
|
|
5056
5203
|
this.config = {
|
|
5057
5204
|
enabled: config.enabled ?? true,
|
|
5058
5205
|
priority: config.priority ?? 0,
|
|
5206
|
+
// Use build sequence by default
|
|
5059
5207
|
options: {
|
|
5060
5208
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
5061
5209
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5210
|
+
// 24 hours default
|
|
5062
5211
|
maxSize: config.options?.maxSize ?? 500,
|
|
5063
5212
|
...config.options
|
|
5064
5213
|
}
|
|
@@ -5071,6 +5220,10 @@ var LocalStorageRepository = class {
|
|
|
5071
5220
|
this.migrateIfNeeded();
|
|
5072
5221
|
}
|
|
5073
5222
|
}
|
|
5223
|
+
/**
|
|
5224
|
+
* Check if localStorage is available
|
|
5225
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5226
|
+
*/
|
|
5074
5227
|
checkAvailability() {
|
|
5075
5228
|
if (typeof window === "undefined") {
|
|
5076
5229
|
return false;
|
|
@@ -5084,6 +5237,9 @@ var LocalStorageRepository = class {
|
|
|
5084
5237
|
return false;
|
|
5085
5238
|
}
|
|
5086
5239
|
}
|
|
5240
|
+
/**
|
|
5241
|
+
* Handle version migrations - clear cache when version changes
|
|
5242
|
+
*/
|
|
5087
5243
|
migrateIfNeeded() {
|
|
5088
5244
|
try {
|
|
5089
5245
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5094,6 +5250,10 @@ var LocalStorageRepository = class {
|
|
|
5094
5250
|
} catch {
|
|
5095
5251
|
}
|
|
5096
5252
|
}
|
|
5253
|
+
/**
|
|
5254
|
+
* Get a translation value by key
|
|
5255
|
+
* Returns null if not found or expired
|
|
5256
|
+
*/
|
|
5097
5257
|
get(key) {
|
|
5098
5258
|
if (!this.available) return null;
|
|
5099
5259
|
try {
|
|
@@ -5110,6 +5270,10 @@ var LocalStorageRepository = class {
|
|
|
5110
5270
|
return null;
|
|
5111
5271
|
}
|
|
5112
5272
|
}
|
|
5273
|
+
/**
|
|
5274
|
+
* Set a translation value
|
|
5275
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5276
|
+
*/
|
|
5113
5277
|
set(key, value) {
|
|
5114
5278
|
if (!this.available) return;
|
|
5115
5279
|
const fullKey = this.prefix + key;
|
|
@@ -5134,6 +5298,9 @@ var LocalStorageRepository = class {
|
|
|
5134
5298
|
}
|
|
5135
5299
|
}
|
|
5136
5300
|
}
|
|
5301
|
+
/**
|
|
5302
|
+
* Remove a translation by key
|
|
5303
|
+
*/
|
|
5137
5304
|
remove(key) {
|
|
5138
5305
|
if (!this.available) return;
|
|
5139
5306
|
try {
|
|
@@ -5141,6 +5308,10 @@ var LocalStorageRepository = class {
|
|
|
5141
5308
|
} catch {
|
|
5142
5309
|
}
|
|
5143
5310
|
}
|
|
5311
|
+
/**
|
|
5312
|
+
* Clear all LionRapid entries from localStorage
|
|
5313
|
+
* Preserves entries from other applications
|
|
5314
|
+
*/
|
|
5144
5315
|
clear() {
|
|
5145
5316
|
if (!this.available) return;
|
|
5146
5317
|
try {
|
|
@@ -5155,6 +5326,9 @@ var LocalStorageRepository = class {
|
|
|
5155
5326
|
} catch {
|
|
5156
5327
|
}
|
|
5157
5328
|
}
|
|
5329
|
+
/**
|
|
5330
|
+
* Clear all entries for a specific namespace
|
|
5331
|
+
*/
|
|
5158
5332
|
clearNamespace(locale, namespace) {
|
|
5159
5333
|
if (!this.available) return;
|
|
5160
5334
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5173,6 +5347,9 @@ var LocalStorageRepository = class {
|
|
|
5173
5347
|
} catch {
|
|
5174
5348
|
}
|
|
5175
5349
|
}
|
|
5350
|
+
/**
|
|
5351
|
+
* Get namespace translations
|
|
5352
|
+
*/
|
|
5176
5353
|
getNamespace(locale, namespace) {
|
|
5177
5354
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5178
5355
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5184,10 +5361,16 @@ var LocalStorageRepository = class {
|
|
|
5184
5361
|
return null;
|
|
5185
5362
|
}
|
|
5186
5363
|
}
|
|
5364
|
+
/**
|
|
5365
|
+
* Set namespace translations
|
|
5366
|
+
*/
|
|
5187
5367
|
setNamespace(locale, namespace, translations) {
|
|
5188
5368
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5189
5369
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5190
5370
|
}
|
|
5371
|
+
/**
|
|
5372
|
+
* Get all cache entries for debugging
|
|
5373
|
+
*/
|
|
5191
5374
|
getEntries() {
|
|
5192
5375
|
const entries = {};
|
|
5193
5376
|
if (!this.available) return entries;
|
|
@@ -5207,12 +5390,21 @@ var LocalStorageRepository = class {
|
|
|
5207
5390
|
}
|
|
5208
5391
|
return entries;
|
|
5209
5392
|
}
|
|
5393
|
+
/**
|
|
5394
|
+
* Get repository configuration
|
|
5395
|
+
*/
|
|
5210
5396
|
getConfig() {
|
|
5211
5397
|
return this.config;
|
|
5212
5398
|
}
|
|
5399
|
+
/**
|
|
5400
|
+
* Check if localStorage is available
|
|
5401
|
+
*/
|
|
5213
5402
|
isAvailable() {
|
|
5214
5403
|
return this.available;
|
|
5215
5404
|
}
|
|
5405
|
+
/**
|
|
5406
|
+
* Get the number of entries in the cache
|
|
5407
|
+
*/
|
|
5216
5408
|
getEntryCount() {
|
|
5217
5409
|
if (!this.available) return 0;
|
|
5218
5410
|
let count = 0;
|
|
@@ -5227,12 +5419,18 @@ var LocalStorageRepository = class {
|
|
|
5227
5419
|
}
|
|
5228
5420
|
return count;
|
|
5229
5421
|
}
|
|
5422
|
+
/**
|
|
5423
|
+
* Check if error is a quota exceeded error
|
|
5424
|
+
*/
|
|
5230
5425
|
isQuotaError(error) {
|
|
5231
5426
|
if (error instanceof DOMException) {
|
|
5232
5427
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5233
5428
|
}
|
|
5234
5429
|
return false;
|
|
5235
5430
|
}
|
|
5431
|
+
/**
|
|
5432
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5433
|
+
*/
|
|
5236
5434
|
evictOldest() {
|
|
5237
5435
|
if (!this.available) return;
|
|
5238
5436
|
try {
|
|
@@ -5260,6 +5458,9 @@ var LocalStorageRepository = class {
|
|
|
5260
5458
|
} catch {
|
|
5261
5459
|
}
|
|
5262
5460
|
}
|
|
5461
|
+
/**
|
|
5462
|
+
* Get cache statistics
|
|
5463
|
+
*/
|
|
5263
5464
|
getCacheStats() {
|
|
5264
5465
|
const size = this.getEntryCount();
|
|
5265
5466
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5273,7 +5474,7 @@ var LocalStorageRepository = class {
|
|
|
5273
5474
|
|
|
5274
5475
|
// src/browser/storage/cache-storage-repository.ts
|
|
5275
5476
|
var CACHE_NAME = "lionrapid-v1";
|
|
5276
|
-
var CACHE_PREFIX = "lionrapid
|
|
5477
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5277
5478
|
var CacheStorageRepository = class {
|
|
5278
5479
|
constructor(config = {}) {
|
|
5279
5480
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5288,6 +5489,7 @@ var CacheStorageRepository = class {
|
|
|
5288
5489
|
options: {
|
|
5289
5490
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5290
5491
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5492
|
+
// 7 days default
|
|
5291
5493
|
...config.options
|
|
5292
5494
|
}
|
|
5293
5495
|
};
|
|
@@ -5295,9 +5497,17 @@ var CacheStorageRepository = class {
|
|
|
5295
5497
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5296
5498
|
this.available = this.checkAvailability();
|
|
5297
5499
|
}
|
|
5500
|
+
/**
|
|
5501
|
+
* Check if Cache API is available
|
|
5502
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
5503
|
+
*/
|
|
5298
5504
|
checkAvailability() {
|
|
5299
5505
|
return typeof caches !== "undefined";
|
|
5300
5506
|
}
|
|
5507
|
+
/**
|
|
5508
|
+
* Get a translation value by key
|
|
5509
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
5510
|
+
*/
|
|
5301
5511
|
async get(key) {
|
|
5302
5512
|
if (!this.available) {
|
|
5303
5513
|
throw new TranslationNotFoundError(key);
|
|
@@ -5321,6 +5531,9 @@ var CacheStorageRepository = class {
|
|
|
5321
5531
|
throw new TranslationNotFoundError(key);
|
|
5322
5532
|
}
|
|
5323
5533
|
}
|
|
5534
|
+
/**
|
|
5535
|
+
* Set a translation value
|
|
5536
|
+
*/
|
|
5324
5537
|
async set(key, value) {
|
|
5325
5538
|
if (!this.available) return;
|
|
5326
5539
|
try {
|
|
@@ -5339,6 +5552,9 @@ var CacheStorageRepository = class {
|
|
|
5339
5552
|
} catch {
|
|
5340
5553
|
}
|
|
5341
5554
|
}
|
|
5555
|
+
/**
|
|
5556
|
+
* Remove a translation by key
|
|
5557
|
+
*/
|
|
5342
5558
|
async remove(key) {
|
|
5343
5559
|
if (!this.available) return;
|
|
5344
5560
|
try {
|
|
@@ -5347,6 +5563,9 @@ var CacheStorageRepository = class {
|
|
|
5347
5563
|
} catch {
|
|
5348
5564
|
}
|
|
5349
5565
|
}
|
|
5566
|
+
/**
|
|
5567
|
+
* Clear all LionRapid entries from the cache
|
|
5568
|
+
*/
|
|
5350
5569
|
async clear() {
|
|
5351
5570
|
if (!this.available) return;
|
|
5352
5571
|
try {
|
|
@@ -5358,6 +5577,9 @@ var CacheStorageRepository = class {
|
|
|
5358
5577
|
} catch {
|
|
5359
5578
|
}
|
|
5360
5579
|
}
|
|
5580
|
+
/**
|
|
5581
|
+
* Clear all entries for a specific namespace
|
|
5582
|
+
*/
|
|
5361
5583
|
async clearNamespace(locale, namespace) {
|
|
5362
5584
|
if (!this.available) return;
|
|
5363
5585
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5375,6 +5597,10 @@ var CacheStorageRepository = class {
|
|
|
5375
5597
|
} catch {
|
|
5376
5598
|
}
|
|
5377
5599
|
}
|
|
5600
|
+
/**
|
|
5601
|
+
* Get namespace translations
|
|
5602
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
5603
|
+
*/
|
|
5378
5604
|
async getNamespace(locale, namespace) {
|
|
5379
5605
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5380
5606
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5385,16 +5611,29 @@ var CacheStorageRepository = class {
|
|
|
5385
5611
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5386
5612
|
}
|
|
5387
5613
|
}
|
|
5614
|
+
/**
|
|
5615
|
+
* Set namespace translations
|
|
5616
|
+
*/
|
|
5388
5617
|
async setNamespace(locale, namespace, translations) {
|
|
5389
5618
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5390
5619
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5391
5620
|
}
|
|
5621
|
+
/**
|
|
5622
|
+
* Get repository configuration
|
|
5623
|
+
*/
|
|
5392
5624
|
getConfig() {
|
|
5393
5625
|
return this.config;
|
|
5394
5626
|
}
|
|
5627
|
+
/**
|
|
5628
|
+
* Check if Cache API is available
|
|
5629
|
+
*/
|
|
5395
5630
|
isAvailable() {
|
|
5396
5631
|
return this.available;
|
|
5397
5632
|
}
|
|
5633
|
+
/**
|
|
5634
|
+
* Get all cached entries (for debugging)
|
|
5635
|
+
* Returns an object mapping keys to values
|
|
5636
|
+
*/
|
|
5398
5637
|
async getEntries() {
|
|
5399
5638
|
const entries = {};
|
|
5400
5639
|
if (!this.available) return entries;
|
|
@@ -5417,6 +5656,9 @@ var CacheStorageRepository = class {
|
|
|
5417
5656
|
}
|
|
5418
5657
|
return entries;
|
|
5419
5658
|
}
|
|
5659
|
+
/**
|
|
5660
|
+
* Get cache statistics
|
|
5661
|
+
*/
|
|
5420
5662
|
async getCacheStats() {
|
|
5421
5663
|
if (!this.available) {
|
|
5422
5664
|
return { size: 0, cacheName: this.cacheName };
|