@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
|
@@ -2899,6 +2899,7 @@ var LionRapid = (() => {
|
|
|
2899
2899
|
// 🔥 EMBEDDED: Core integration maps
|
|
2900
2900
|
__publicField(this, "formatters", /* @__PURE__ */ new Map());
|
|
2901
2901
|
__publicField(this, "processors", /* @__PURE__ */ new Map());
|
|
2902
|
+
__publicField(this, "warnedNoInterpolate", false);
|
|
2902
2903
|
// 📡 EVENT-DRIVEN: Just track for cleanup
|
|
2903
2904
|
__publicField(this, "eventPlugins", /* @__PURE__ */ new Set());
|
|
2904
2905
|
// 📦 CONTENT: Parser and serializer for ContentUnit processing
|
|
@@ -3191,6 +3192,11 @@ var LionRapid = (() => {
|
|
|
3191
3192
|
resolvedLocale,
|
|
3192
3193
|
resolvedKey
|
|
3193
3194
|
);
|
|
3195
|
+
} else if (!this.warnedNoInterpolate && params && Object.keys(params).length > 0 && processedValue.includes("{")) {
|
|
3196
|
+
this.warnedNoInterpolate = true;
|
|
3197
|
+
console.warn(
|
|
3198
|
+
"[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."
|
|
3199
|
+
);
|
|
3194
3200
|
}
|
|
3195
3201
|
if (this.processors.size > 0) {
|
|
3196
3202
|
for (const [name, processor] of this.processors.entries()) {
|
|
@@ -3971,6 +3977,31 @@ var LionRapid = (() => {
|
|
|
3971
3977
|
* and payload can never desync because they live and die together.
|
|
3972
3978
|
*/
|
|
3973
3979
|
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3980
|
+
/**
|
|
3981
|
+
* Miss-path flood protection (READINESS before-scale #1: one cold locale
|
|
3982
|
+
* switch measured ~380 requests in 10 s — every t() miss refired a key GET
|
|
3983
|
+
* and a namespace GET on every re-render, forever on animated pages).
|
|
3984
|
+
*/
|
|
3985
|
+
/** GETs currently on the wire, keyed by `url|if-none-match` — identical
|
|
3986
|
+
* concurrent requests share one fetch; each awaiter gets a clone. The
|
|
3987
|
+
* If-None-Match value is part of the key so the 304-without-snapshot
|
|
3988
|
+
* unconditional refetch never coalesces onto a conditional flight. */
|
|
3989
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
3990
|
+
/** URL → 404/423 seen recently: repeats are answered locally (same typed
|
|
3991
|
+
* error, no network) until `until`. 423 honors the server's Retry-After.
|
|
3992
|
+
* Cleared entirely by any successful write — discovery sync keeps its
|
|
3993
|
+
* miss → batch → served-back round-trip. */
|
|
3994
|
+
__publicField(this, "negativeCache", /* @__PURE__ */ new Map());
|
|
3995
|
+
/** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
|
|
3996
|
+
* wrong or unauthorized for this project, and retrying cannot heal it —
|
|
3997
|
+
* after the first one nothing touches the network again for this
|
|
3998
|
+
* instance (a page reload starts fresh). Holds the status that tripped
|
|
3999
|
+
* it so short-circuits answer honestly. */
|
|
4000
|
+
__publicField(this, "authFailedStatus", null);
|
|
4001
|
+
/** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
|
|
4002
|
+
* a WRITE degrades this instance to read-only — writes stop, silently
|
|
4003
|
+
* and without retries, while reads keep serving the page. */
|
|
4004
|
+
__publicField(this, "writesFailedStatus", null);
|
|
3974
4005
|
/**
|
|
3975
4006
|
* Default endpoint templates with placeholder support
|
|
3976
4007
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3978,6 +4009,7 @@ var LionRapid = (() => {
|
|
|
3978
4009
|
__publicField(this, "DEFAULT_ENDPOINTS", {
|
|
3979
4010
|
getTranslation: "/api/integrations/translations/:key",
|
|
3980
4011
|
getNamespace: "/api/integrations/translations/:locale/:namespace",
|
|
4012
|
+
getProjectConfig: "/api/integrations/project-config",
|
|
3981
4013
|
setTranslation: "/api/integrations/sync/:key",
|
|
3982
4014
|
setNamespace: "/api/integrations/sync/:locale/:namespace",
|
|
3983
4015
|
setBatch: "/api/integrations/sync/batch",
|
|
@@ -4089,6 +4121,40 @@ var LionRapid = (() => {
|
|
|
4089
4121
|
this.handleCaughtError(error);
|
|
4090
4122
|
}
|
|
4091
4123
|
}
|
|
4124
|
+
/**
|
|
4125
|
+
* Project-level config for this key — the dashboard's language list,
|
|
4126
|
+
* served under the same trust model as translation reads (docs/20,
|
|
4127
|
+
* companion track). Returns null when the server doesn't expose the
|
|
4128
|
+
* endpoint (404 — older server, feature not deployed) or the payload is
|
|
4129
|
+
* unusable: absence means "keep your local fallback", never an error.
|
|
4130
|
+
*/
|
|
4131
|
+
async getProjectConfig() {
|
|
4132
|
+
if (!this.config?.options?.baseUrl) {
|
|
4133
|
+
throw new NetworkError("API base URL is not set");
|
|
4134
|
+
}
|
|
4135
|
+
const url = this.buildUrl("getProjectConfig", {});
|
|
4136
|
+
logger.debug(`API GET ProjectConfig: ${url}`);
|
|
4137
|
+
try {
|
|
4138
|
+
const response = await this.makeRequest(url, "GET");
|
|
4139
|
+
if (response.status === 404) {
|
|
4140
|
+
return null;
|
|
4141
|
+
}
|
|
4142
|
+
if (!response.ok) {
|
|
4143
|
+
this.handleHttpError(response, "project-config");
|
|
4144
|
+
}
|
|
4145
|
+
const data = await response.json();
|
|
4146
|
+
if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
|
|
4147
|
+
logger.warn("project-config payload unusable \u2014 keeping local fallback");
|
|
4148
|
+
return null;
|
|
4149
|
+
}
|
|
4150
|
+
return {
|
|
4151
|
+
defaultLocale: data.defaultLocale,
|
|
4152
|
+
languages: data.languages.map(String)
|
|
4153
|
+
};
|
|
4154
|
+
} catch (error) {
|
|
4155
|
+
this.handleCaughtError(error);
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4092
4158
|
/**
|
|
4093
4159
|
* Shared namespace fetch with the canonical 304 contract:
|
|
4094
4160
|
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
@@ -4413,7 +4479,87 @@ var LionRapid = (() => {
|
|
|
4413
4479
|
/**
|
|
4414
4480
|
* Make HTTP request with timeout and headers
|
|
4415
4481
|
*/
|
|
4416
|
-
|
|
4482
|
+
makeRequest(url, method, body, extraHeaders) {
|
|
4483
|
+
if (this.authFailedStatus !== null) {
|
|
4484
|
+
return Promise.resolve(
|
|
4485
|
+
new Response(null, {
|
|
4486
|
+
status: this.authFailedStatus,
|
|
4487
|
+
statusText: `auth latched off after ${this.authFailedStatus}`
|
|
4488
|
+
})
|
|
4489
|
+
);
|
|
4490
|
+
}
|
|
4491
|
+
if (method !== "GET") {
|
|
4492
|
+
if (this.writesFailedStatus !== null) {
|
|
4493
|
+
return Promise.resolve(
|
|
4494
|
+
new Response(null, {
|
|
4495
|
+
status: this.writesFailedStatus,
|
|
4496
|
+
statusText: `writes latched off after ${this.writesFailedStatus}`
|
|
4497
|
+
})
|
|
4498
|
+
);
|
|
4499
|
+
}
|
|
4500
|
+
return this.doFetch(url, method, body, extraHeaders).then(
|
|
4501
|
+
(resp) => this.recordOutcome(url, method, resp)
|
|
4502
|
+
);
|
|
4503
|
+
}
|
|
4504
|
+
const negative = this.negativeCache.get(url);
|
|
4505
|
+
if (negative) {
|
|
4506
|
+
if (Date.now() < negative.until) {
|
|
4507
|
+
logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
|
|
4508
|
+
const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
|
|
4509
|
+
return Promise.resolve(
|
|
4510
|
+
new Response(null, { status: negative.status, statusText: "negative cache", headers })
|
|
4511
|
+
);
|
|
4512
|
+
}
|
|
4513
|
+
this.negativeCache.delete(url);
|
|
4514
|
+
}
|
|
4515
|
+
const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
|
|
4516
|
+
const existing = this.inflightGets.get(flightKey);
|
|
4517
|
+
if (existing) {
|
|
4518
|
+
return existing.then((resp) => this.cloneResponse(resp));
|
|
4519
|
+
}
|
|
4520
|
+
const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
|
|
4521
|
+
this.inflightGets.set(flightKey, flight);
|
|
4522
|
+
return flight.then((resp) => this.cloneResponse(resp));
|
|
4523
|
+
}
|
|
4524
|
+
/** Bookkeeping on every real (non-synthesized) response — runs once per
|
|
4525
|
+
* physical fetch, never once per coalesced awaiter. */
|
|
4526
|
+
recordOutcome(url, method, response) {
|
|
4527
|
+
const status = response.status;
|
|
4528
|
+
if (status === 401 || status === 403 && method === "GET") {
|
|
4529
|
+
if (this.authFailedStatus === null) {
|
|
4530
|
+
this.authFailedStatus = status;
|
|
4531
|
+
console.warn(
|
|
4532
|
+
`[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
|
|
4533
|
+
);
|
|
4534
|
+
}
|
|
4535
|
+
return response;
|
|
4536
|
+
}
|
|
4537
|
+
if ((status === 403 || status === 429) && method !== "GET") {
|
|
4538
|
+
if (this.writesFailedStatus === null) {
|
|
4539
|
+
this.writesFailedStatus = status;
|
|
4540
|
+
logger.warn(
|
|
4541
|
+
`Write path disabled after ${status} (scope/quota) \u2014 running read-only`
|
|
4542
|
+
);
|
|
4543
|
+
}
|
|
4544
|
+
return response;
|
|
4545
|
+
}
|
|
4546
|
+
if (method === "GET" && (status === 404 || status === 423)) {
|
|
4547
|
+
const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
|
|
4548
|
+
const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
|
|
4549
|
+
this.negativeCache.set(url, { status, until: Date.now() + ttl });
|
|
4550
|
+
} else if (method !== "GET" && response.ok) {
|
|
4551
|
+
this.negativeCache.clear();
|
|
4552
|
+
} else if (method === "GET" && response.ok) {
|
|
4553
|
+
this.negativeCache.delete(url);
|
|
4554
|
+
}
|
|
4555
|
+
return response;
|
|
4556
|
+
}
|
|
4557
|
+
/** Test doubles (and exotic runtimes) hand us plain objects — only real
|
|
4558
|
+
* Responses need cloning so each coalesced awaiter can read the body. */
|
|
4559
|
+
cloneResponse(response) {
|
|
4560
|
+
return typeof response.clone === "function" ? response.clone() : response;
|
|
4561
|
+
}
|
|
4562
|
+
async doFetch(url, method, body, extraHeaders) {
|
|
4417
4563
|
const controller = new AbortController();
|
|
4418
4564
|
const timeout = this.config.options?.timeout ?? 1e4;
|
|
4419
4565
|
const timeoutId = setTimeout(
|
|
@@ -4446,6 +4592,7 @@ var LionRapid = (() => {
|
|
|
4446
4592
|
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4447
4593
|
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4448
4594
|
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4595
|
+
__publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
|
|
4449
4596
|
var NetworkRepository = _NetworkRepository;
|
|
4450
4597
|
|
|
4451
4598
|
// src/content/validator/ContentValidator.ts
|
|
@@ -5044,9 +5191,11 @@ var LionRapid = (() => {
|
|
|
5044
5191
|
this.config = {
|
|
5045
5192
|
enabled: config.enabled ?? true,
|
|
5046
5193
|
priority: config.priority ?? 0,
|
|
5194
|
+
// Use build sequence by default
|
|
5047
5195
|
options: {
|
|
5048
5196
|
prefix: config.options?.prefix ?? STORAGE_PREFIX,
|
|
5049
5197
|
ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
|
|
5198
|
+
// 24 hours default
|
|
5050
5199
|
maxSize: config.options?.maxSize ?? 500,
|
|
5051
5200
|
...config.options
|
|
5052
5201
|
}
|
|
@@ -5059,6 +5208,10 @@ var LionRapid = (() => {
|
|
|
5059
5208
|
this.migrateIfNeeded();
|
|
5060
5209
|
}
|
|
5061
5210
|
}
|
|
5211
|
+
/**
|
|
5212
|
+
* Check if localStorage is available
|
|
5213
|
+
* Returns false in SSR, private browsing, or when quota is 0
|
|
5214
|
+
*/
|
|
5062
5215
|
checkAvailability() {
|
|
5063
5216
|
if (typeof window === "undefined") {
|
|
5064
5217
|
return false;
|
|
@@ -5072,6 +5225,9 @@ var LionRapid = (() => {
|
|
|
5072
5225
|
return false;
|
|
5073
5226
|
}
|
|
5074
5227
|
}
|
|
5228
|
+
/**
|
|
5229
|
+
* Handle version migrations - clear cache when version changes
|
|
5230
|
+
*/
|
|
5075
5231
|
migrateIfNeeded() {
|
|
5076
5232
|
try {
|
|
5077
5233
|
const storedVersion = window.localStorage.getItem(VERSION_KEY);
|
|
@@ -5082,6 +5238,10 @@ var LionRapid = (() => {
|
|
|
5082
5238
|
} catch {
|
|
5083
5239
|
}
|
|
5084
5240
|
}
|
|
5241
|
+
/**
|
|
5242
|
+
* Get a translation value by key
|
|
5243
|
+
* Returns null if not found or expired
|
|
5244
|
+
*/
|
|
5085
5245
|
get(key) {
|
|
5086
5246
|
if (!this.available) return null;
|
|
5087
5247
|
try {
|
|
@@ -5098,6 +5258,10 @@ var LionRapid = (() => {
|
|
|
5098
5258
|
return null;
|
|
5099
5259
|
}
|
|
5100
5260
|
}
|
|
5261
|
+
/**
|
|
5262
|
+
* Set a translation value
|
|
5263
|
+
* Automatically handles quota exceeded by evicting oldest entries
|
|
5264
|
+
*/
|
|
5101
5265
|
set(key, value) {
|
|
5102
5266
|
if (!this.available) return;
|
|
5103
5267
|
const fullKey = this.prefix + key;
|
|
@@ -5122,6 +5286,9 @@ var LionRapid = (() => {
|
|
|
5122
5286
|
}
|
|
5123
5287
|
}
|
|
5124
5288
|
}
|
|
5289
|
+
/**
|
|
5290
|
+
* Remove a translation by key
|
|
5291
|
+
*/
|
|
5125
5292
|
remove(key) {
|
|
5126
5293
|
if (!this.available) return;
|
|
5127
5294
|
try {
|
|
@@ -5129,6 +5296,10 @@ var LionRapid = (() => {
|
|
|
5129
5296
|
} catch {
|
|
5130
5297
|
}
|
|
5131
5298
|
}
|
|
5299
|
+
/**
|
|
5300
|
+
* Clear all LionRapid entries from localStorage
|
|
5301
|
+
* Preserves entries from other applications
|
|
5302
|
+
*/
|
|
5132
5303
|
clear() {
|
|
5133
5304
|
if (!this.available) return;
|
|
5134
5305
|
try {
|
|
@@ -5143,6 +5314,9 @@ var LionRapid = (() => {
|
|
|
5143
5314
|
} catch {
|
|
5144
5315
|
}
|
|
5145
5316
|
}
|
|
5317
|
+
/**
|
|
5318
|
+
* Clear all entries for a specific namespace
|
|
5319
|
+
*/
|
|
5146
5320
|
clearNamespace(locale, namespace) {
|
|
5147
5321
|
if (!this.available) return;
|
|
5148
5322
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5161,6 +5335,9 @@ var LionRapid = (() => {
|
|
|
5161
5335
|
} catch {
|
|
5162
5336
|
}
|
|
5163
5337
|
}
|
|
5338
|
+
/**
|
|
5339
|
+
* Get namespace translations
|
|
5340
|
+
*/
|
|
5164
5341
|
getNamespace(locale, namespace) {
|
|
5165
5342
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5166
5343
|
const value = this.get(namespaceCacheKey);
|
|
@@ -5172,10 +5349,16 @@ var LionRapid = (() => {
|
|
|
5172
5349
|
return null;
|
|
5173
5350
|
}
|
|
5174
5351
|
}
|
|
5352
|
+
/**
|
|
5353
|
+
* Set namespace translations
|
|
5354
|
+
*/
|
|
5175
5355
|
setNamespace(locale, namespace, translations) {
|
|
5176
5356
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5177
5357
|
this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5178
5358
|
}
|
|
5359
|
+
/**
|
|
5360
|
+
* Get all cache entries for debugging
|
|
5361
|
+
*/
|
|
5179
5362
|
getEntries() {
|
|
5180
5363
|
const entries = {};
|
|
5181
5364
|
if (!this.available) return entries;
|
|
@@ -5195,12 +5378,21 @@ var LionRapid = (() => {
|
|
|
5195
5378
|
}
|
|
5196
5379
|
return entries;
|
|
5197
5380
|
}
|
|
5381
|
+
/**
|
|
5382
|
+
* Get repository configuration
|
|
5383
|
+
*/
|
|
5198
5384
|
getConfig() {
|
|
5199
5385
|
return this.config;
|
|
5200
5386
|
}
|
|
5387
|
+
/**
|
|
5388
|
+
* Check if localStorage is available
|
|
5389
|
+
*/
|
|
5201
5390
|
isAvailable() {
|
|
5202
5391
|
return this.available;
|
|
5203
5392
|
}
|
|
5393
|
+
/**
|
|
5394
|
+
* Get the number of entries in the cache
|
|
5395
|
+
*/
|
|
5204
5396
|
getEntryCount() {
|
|
5205
5397
|
if (!this.available) return 0;
|
|
5206
5398
|
let count = 0;
|
|
@@ -5215,12 +5407,18 @@ var LionRapid = (() => {
|
|
|
5215
5407
|
}
|
|
5216
5408
|
return count;
|
|
5217
5409
|
}
|
|
5410
|
+
/**
|
|
5411
|
+
* Check if error is a quota exceeded error
|
|
5412
|
+
*/
|
|
5218
5413
|
isQuotaError(error) {
|
|
5219
5414
|
if (error instanceof DOMException) {
|
|
5220
5415
|
return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
|
|
5221
5416
|
}
|
|
5222
5417
|
return false;
|
|
5223
5418
|
}
|
|
5419
|
+
/**
|
|
5420
|
+
* Evict oldest 25% of entries when quota is exceeded
|
|
5421
|
+
*/
|
|
5224
5422
|
evictOldest() {
|
|
5225
5423
|
if (!this.available) return;
|
|
5226
5424
|
try {
|
|
@@ -5248,6 +5446,9 @@ var LionRapid = (() => {
|
|
|
5248
5446
|
} catch {
|
|
5249
5447
|
}
|
|
5250
5448
|
}
|
|
5449
|
+
/**
|
|
5450
|
+
* Get cache statistics
|
|
5451
|
+
*/
|
|
5251
5452
|
getCacheStats() {
|
|
5252
5453
|
const size = this.getEntryCount();
|
|
5253
5454
|
const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
|
|
@@ -5261,7 +5462,7 @@ var LionRapid = (() => {
|
|
|
5261
5462
|
|
|
5262
5463
|
// src/browser/storage/cache-storage-repository.ts
|
|
5263
5464
|
var CACHE_NAME = "lionrapid-v1";
|
|
5264
|
-
var CACHE_PREFIX = "lionrapid
|
|
5465
|
+
var CACHE_PREFIX = "https://cache.lionrapid.internal/";
|
|
5265
5466
|
var CacheStorageRepository = class {
|
|
5266
5467
|
constructor(config = {}) {
|
|
5267
5468
|
__publicField(this, "name", "CacheStorageRepository");
|
|
@@ -5276,6 +5477,7 @@ var LionRapid = (() => {
|
|
|
5276
5477
|
options: {
|
|
5277
5478
|
cacheName: config.options?.cacheName ?? CACHE_NAME,
|
|
5278
5479
|
ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
|
|
5480
|
+
// 7 days default
|
|
5279
5481
|
...config.options
|
|
5280
5482
|
}
|
|
5281
5483
|
};
|
|
@@ -5283,9 +5485,17 @@ var LionRapid = (() => {
|
|
|
5283
5485
|
this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
|
|
5284
5486
|
this.available = this.checkAvailability();
|
|
5285
5487
|
}
|
|
5488
|
+
/**
|
|
5489
|
+
* Check if Cache API is available
|
|
5490
|
+
* Returns false in SSR, old browsers, or insecure contexts
|
|
5491
|
+
*/
|
|
5286
5492
|
checkAvailability() {
|
|
5287
5493
|
return typeof caches !== "undefined";
|
|
5288
5494
|
}
|
|
5495
|
+
/**
|
|
5496
|
+
* Get a translation value by key
|
|
5497
|
+
* @throws {TranslationNotFoundError} When translation doesn't exist or is expired
|
|
5498
|
+
*/
|
|
5289
5499
|
async get(key) {
|
|
5290
5500
|
if (!this.available) {
|
|
5291
5501
|
throw new TranslationNotFoundError(key);
|
|
@@ -5309,6 +5519,9 @@ var LionRapid = (() => {
|
|
|
5309
5519
|
throw new TranslationNotFoundError(key);
|
|
5310
5520
|
}
|
|
5311
5521
|
}
|
|
5522
|
+
/**
|
|
5523
|
+
* Set a translation value
|
|
5524
|
+
*/
|
|
5312
5525
|
async set(key, value) {
|
|
5313
5526
|
if (!this.available) return;
|
|
5314
5527
|
try {
|
|
@@ -5327,6 +5540,9 @@ var LionRapid = (() => {
|
|
|
5327
5540
|
} catch {
|
|
5328
5541
|
}
|
|
5329
5542
|
}
|
|
5543
|
+
/**
|
|
5544
|
+
* Remove a translation by key
|
|
5545
|
+
*/
|
|
5330
5546
|
async remove(key) {
|
|
5331
5547
|
if (!this.available) return;
|
|
5332
5548
|
try {
|
|
@@ -5335,6 +5551,9 @@ var LionRapid = (() => {
|
|
|
5335
5551
|
} catch {
|
|
5336
5552
|
}
|
|
5337
5553
|
}
|
|
5554
|
+
/**
|
|
5555
|
+
* Clear all LionRapid entries from the cache
|
|
5556
|
+
*/
|
|
5338
5557
|
async clear() {
|
|
5339
5558
|
if (!this.available) return;
|
|
5340
5559
|
try {
|
|
@@ -5346,6 +5565,9 @@ var LionRapid = (() => {
|
|
|
5346
5565
|
} catch {
|
|
5347
5566
|
}
|
|
5348
5567
|
}
|
|
5568
|
+
/**
|
|
5569
|
+
* Clear all entries for a specific namespace
|
|
5570
|
+
*/
|
|
5349
5571
|
async clearNamespace(locale, namespace) {
|
|
5350
5572
|
if (!this.available) return;
|
|
5351
5573
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
@@ -5363,6 +5585,10 @@ var LionRapid = (() => {
|
|
|
5363
5585
|
} catch {
|
|
5364
5586
|
}
|
|
5365
5587
|
}
|
|
5588
|
+
/**
|
|
5589
|
+
* Get namespace translations
|
|
5590
|
+
* @throws {TranslationNotFoundError} When namespace doesn't exist
|
|
5591
|
+
*/
|
|
5366
5592
|
async getNamespace(locale, namespace) {
|
|
5367
5593
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5368
5594
|
const value = await this.get(namespaceCacheKey);
|
|
@@ -5373,16 +5599,29 @@ var LionRapid = (() => {
|
|
|
5373
5599
|
throw new TranslationNotFoundError(namespaceCacheKey);
|
|
5374
5600
|
}
|
|
5375
5601
|
}
|
|
5602
|
+
/**
|
|
5603
|
+
* Set namespace translations
|
|
5604
|
+
*/
|
|
5376
5605
|
async setNamespace(locale, namespace, translations) {
|
|
5377
5606
|
const namespaceCacheKey = `@${locale}:${namespace}`;
|
|
5378
5607
|
await this.set(namespaceCacheKey, JSON.stringify(translations));
|
|
5379
5608
|
}
|
|
5609
|
+
/**
|
|
5610
|
+
* Get repository configuration
|
|
5611
|
+
*/
|
|
5380
5612
|
getConfig() {
|
|
5381
5613
|
return this.config;
|
|
5382
5614
|
}
|
|
5615
|
+
/**
|
|
5616
|
+
* Check if Cache API is available
|
|
5617
|
+
*/
|
|
5383
5618
|
isAvailable() {
|
|
5384
5619
|
return this.available;
|
|
5385
5620
|
}
|
|
5621
|
+
/**
|
|
5622
|
+
* Get all cached entries (for debugging)
|
|
5623
|
+
* Returns an object mapping keys to values
|
|
5624
|
+
*/
|
|
5386
5625
|
async getEntries() {
|
|
5387
5626
|
const entries = {};
|
|
5388
5627
|
if (!this.available) return entries;
|
|
@@ -5405,6 +5644,9 @@ var LionRapid = (() => {
|
|
|
5405
5644
|
}
|
|
5406
5645
|
return entries;
|
|
5407
5646
|
}
|
|
5647
|
+
/**
|
|
5648
|
+
* Get cache statistics
|
|
5649
|
+
*/
|
|
5408
5650
|
async getCacheStats() {
|
|
5409
5651
|
if (!this.available) {
|
|
5410
5652
|
return { size: 0, cacheName: this.cacheName };
|