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