@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
@@ -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
- async makeRequest(url, method, body, extraHeaders) {
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 };