@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.
Files changed (42) hide show
  1. package/dist/browser/auto-init.d.ts +8 -3
  2. package/dist/browser/auto-init.d.ts.map +1 -1
  3. package/dist/browser/auto-init.js +34 -26
  4. package/dist/browser/auto-init.js.map +1 -1
  5. package/dist/browser/lionrapid.auto.dev.js +267 -24
  6. package/dist/browser/lionrapid.auto.dev.js.map +2 -2
  7. package/dist/browser/lionrapid.auto.js +267 -24
  8. package/dist/browser/lionrapid.auto.js.map +2 -2
  9. package/dist/browser/lionrapid.auto.min.js +4 -4
  10. package/dist/browser/lionrapid.auto.min.js.map +3 -3
  11. package/dist/browser/lionrapid.esm.js +241 -2
  12. package/dist/browser/lionrapid.esm.js.map +2 -2
  13. package/dist/browser/lionrapid.esm.min.js +3 -3
  14. package/dist/browser/lionrapid.esm.min.js.map +3 -3
  15. package/dist/browser/lionrapid.iife.dev.js +241 -2
  16. package/dist/browser/lionrapid.iife.dev.js.map +2 -2
  17. package/dist/browser/lionrapid.iife.js +241 -2
  18. package/dist/browser/lionrapid.iife.js.map +2 -2
  19. package/dist/browser/lionrapid.iife.min.js +3 -3
  20. package/dist/browser/lionrapid.iife.min.js.map +3 -3
  21. package/dist/browser/lionrapid.umd.js +241 -2
  22. package/dist/browser/lionrapid.umd.js.map +2 -2
  23. package/dist/browser/lionrapid.umd.min.js +3 -3
  24. package/dist/browser/lionrapid.umd.min.js.map +3 -3
  25. package/dist/browser/storage/cache-storage-repository.d.ts +65 -0
  26. package/dist/browser/storage/cache-storage-repository.d.ts.map +1 -1
  27. package/dist/browser/storage/cache-storage-repository.js +77 -7
  28. package/dist/browser/storage/cache-storage-repository.js.map +1 -1
  29. package/dist/browser/storage/localstorage-repository.d.ts +62 -0
  30. package/dist/browser/storage/localstorage-repository.d.ts.map +1 -1
  31. package/dist/browser/storage/localstorage-repository.js +71 -4
  32. package/dist/browser/storage/localstorage-repository.js.map +1 -1
  33. package/dist/config/repository.config.d.ts +17 -0
  34. package/dist/config/repository.config.d.ts.map +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/repositories/network-repository.d.ts +42 -1
  39. package/dist/repositories/network-repository.d.ts.map +1 -1
  40. package/dist/repositories/network-repository.js +161 -1
  41. package/dist/repositories/network-repository.js.map +1 -1
  42. 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
- async makeRequest(url, method, body, extraHeaders) {
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 };