@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
@@ -3829,6 +3829,8 @@ var __lionrapidAutoExports = (() => {
3829
3829
  };
3830
3830
 
3831
3831
  // src/repositories/network-repository.ts
3832
+ var SURFACE_HEADER = "X-LR-Surface";
3833
+ var SURFACE_TAG = "snippet";
3832
3834
  var _NetworkRepository = class _NetworkRepository {
3833
3835
  constructor(config = {}) {
3834
3836
  __publicField(this, "name", "NetworkRepository");
@@ -3844,6 +3846,31 @@ var __lionrapidAutoExports = (() => {
3844
3846
  * and payload can never desync because they live and die together.
3845
3847
  */
3846
3848
  __publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
3849
+ /**
3850
+ * Miss-path flood protection (READINESS before-scale #1: one cold locale
3851
+ * switch measured ~380 requests in 10 s — every t() miss refired a key GET
3852
+ * and a namespace GET on every re-render, forever on animated pages).
3853
+ */
3854
+ /** GETs currently on the wire, keyed by `url|if-none-match` — identical
3855
+ * concurrent requests share one fetch; each awaiter gets a clone. The
3856
+ * If-None-Match value is part of the key so the 304-without-snapshot
3857
+ * unconditional refetch never coalesces onto a conditional flight. */
3858
+ __publicField(this, "inflightGets", /* @__PURE__ */ new Map());
3859
+ /** URL → 404/423 seen recently: repeats are answered locally (same typed
3860
+ * error, no network) until `until`. 423 honors the server's Retry-After.
3861
+ * Cleared entirely by any successful write — discovery sync keeps its
3862
+ * miss → batch → served-back round-trip. */
3863
+ __publicField(this, "negativeCache", /* @__PURE__ */ new Map());
3864
+ /** One-way latch: a 401 anywhere (or a 403 on a READ) means the key is
3865
+ * wrong or unauthorized for this project, and retrying cannot heal it —
3866
+ * after the first one nothing touches the network again for this
3867
+ * instance (a page reload starts fresh). Holds the status that tripped
3868
+ * it so short-circuits answer honestly. */
3869
+ __publicField(this, "authFailedStatus", null);
3870
+ /** Write-only latch (auth-byok Phase 18): a 403 (scope) or 429 (quota) on
3871
+ * a WRITE degrades this instance to read-only — writes stop, silently
3872
+ * and without retries, while reads keep serving the page. */
3873
+ __publicField(this, "writesFailedStatus", null);
3847
3874
  /**
3848
3875
  * Default endpoint templates with placeholder support
3849
3876
  * Placeholders: :key, :locale, :namespace
@@ -3851,6 +3878,7 @@ var __lionrapidAutoExports = (() => {
3851
3878
  __publicField(this, "DEFAULT_ENDPOINTS", {
3852
3879
  getTranslation: "/api/integrations/translations/:key",
3853
3880
  getNamespace: "/api/integrations/translations/:locale/:namespace",
3881
+ getProjectConfig: "/api/integrations/project-config",
3854
3882
  setTranslation: "/api/integrations/sync/:key",
3855
3883
  setNamespace: "/api/integrations/sync/:locale/:namespace",
3856
3884
  setBatch: "/api/integrations/sync/batch",
@@ -3962,6 +3990,40 @@ var __lionrapidAutoExports = (() => {
3962
3990
  this.handleCaughtError(error);
3963
3991
  }
3964
3992
  }
3993
+ /**
3994
+ * Project-level config for this key — the dashboard's language list,
3995
+ * served under the same trust model as translation reads (docs/20,
3996
+ * companion track). Returns null when the server doesn't expose the
3997
+ * endpoint (404 — older server, feature not deployed) or the payload is
3998
+ * unusable: absence means "keep your local fallback", never an error.
3999
+ */
4000
+ async getProjectConfig() {
4001
+ if (!this.config?.options?.baseUrl) {
4002
+ throw new NetworkError("API base URL is not set");
4003
+ }
4004
+ const url = this.buildUrl("getProjectConfig", {});
4005
+ logger.debug(`API GET ProjectConfig: ${url}`);
4006
+ try {
4007
+ const response = await this.makeRequest(url, "GET");
4008
+ if (response.status === 404) {
4009
+ return null;
4010
+ }
4011
+ if (!response.ok) {
4012
+ this.handleHttpError(response, "project-config");
4013
+ }
4014
+ const data = await response.json();
4015
+ if (!data || typeof data.defaultLocale !== "string" || !Array.isArray(data.languages)) {
4016
+ logger.warn("project-config payload unusable \u2014 keeping local fallback");
4017
+ return null;
4018
+ }
4019
+ return {
4020
+ defaultLocale: data.defaultLocale,
4021
+ languages: data.languages.map(String)
4022
+ };
4023
+ } catch (error) {
4024
+ this.handleCaughtError(error);
4025
+ }
4026
+ }
3965
4027
  /**
3966
4028
  * Shared namespace fetch with the canonical 304 contract:
3967
4029
  * - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
@@ -4286,7 +4348,87 @@ var __lionrapidAutoExports = (() => {
4286
4348
  /**
4287
4349
  * Make HTTP request with timeout and headers
4288
4350
  */
4289
- async makeRequest(url, method, body, extraHeaders) {
4351
+ makeRequest(url, method, body, extraHeaders) {
4352
+ if (this.authFailedStatus !== null) {
4353
+ return Promise.resolve(
4354
+ new Response(null, {
4355
+ status: this.authFailedStatus,
4356
+ statusText: `auth latched off after ${this.authFailedStatus}`
4357
+ })
4358
+ );
4359
+ }
4360
+ if (method !== "GET") {
4361
+ if (this.writesFailedStatus !== null) {
4362
+ return Promise.resolve(
4363
+ new Response(null, {
4364
+ status: this.writesFailedStatus,
4365
+ statusText: `writes latched off after ${this.writesFailedStatus}`
4366
+ })
4367
+ );
4368
+ }
4369
+ return this.doFetch(url, method, body, extraHeaders).then(
4370
+ (resp) => this.recordOutcome(url, method, resp)
4371
+ );
4372
+ }
4373
+ const negative = this.negativeCache.get(url);
4374
+ if (negative) {
4375
+ if (Date.now() < negative.until) {
4376
+ logger.debug(`Negative-cache hit (${negative.status}): ${url}`);
4377
+ const headers = negative.status === 423 ? { "Retry-After": String(Math.ceil((negative.until - Date.now()) / 1e3)) } : void 0;
4378
+ return Promise.resolve(
4379
+ new Response(null, { status: negative.status, statusText: "negative cache", headers })
4380
+ );
4381
+ }
4382
+ this.negativeCache.delete(url);
4383
+ }
4384
+ const flightKey = `${url}|${extraHeaders?.["If-None-Match"] ?? ""}`;
4385
+ const existing = this.inflightGets.get(flightKey);
4386
+ if (existing) {
4387
+ return existing.then((resp) => this.cloneResponse(resp));
4388
+ }
4389
+ const flight = this.doFetch(url, method, body, extraHeaders).then((resp) => this.recordOutcome(url, method, resp)).finally(() => this.inflightGets.delete(flightKey));
4390
+ this.inflightGets.set(flightKey, flight);
4391
+ return flight.then((resp) => this.cloneResponse(resp));
4392
+ }
4393
+ /** Bookkeeping on every real (non-synthesized) response — runs once per
4394
+ * physical fetch, never once per coalesced awaiter. */
4395
+ recordOutcome(url, method, response) {
4396
+ const status = response.status;
4397
+ if (status === 401 || status === 403 && method === "GET") {
4398
+ if (this.authFailedStatus === null) {
4399
+ this.authFailedStatus = status;
4400
+ console.warn(
4401
+ `[LionRapid] API key rejected (${status}) \u2014 network translations are disabled for this session. Fix the key and reload; retrying cannot heal an auth failure.`
4402
+ );
4403
+ }
4404
+ return response;
4405
+ }
4406
+ if ((status === 403 || status === 429) && method !== "GET") {
4407
+ if (this.writesFailedStatus === null) {
4408
+ this.writesFailedStatus = status;
4409
+ logger.warn(
4410
+ `Write path disabled after ${status} (scope/quota) \u2014 running read-only`
4411
+ );
4412
+ }
4413
+ return response;
4414
+ }
4415
+ if (method === "GET" && (status === 404 || status === 423)) {
4416
+ const retryAfterSec = status === 423 ? Number(response.headers?.get?.("Retry-After")) : NaN;
4417
+ const ttl = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? retryAfterSec * 1e3 : this.config.options?.negativeCacheTtlMs ?? _NetworkRepository.DEFAULT_NEGATIVE_TTL_MS;
4418
+ this.negativeCache.set(url, { status, until: Date.now() + ttl });
4419
+ } else if (method !== "GET" && response.ok) {
4420
+ this.negativeCache.clear();
4421
+ } else if (method === "GET" && response.ok) {
4422
+ this.negativeCache.delete(url);
4423
+ }
4424
+ return response;
4425
+ }
4426
+ /** Test doubles (and exotic runtimes) hand us plain objects — only real
4427
+ * Responses need cloning so each coalesced awaiter can read the body. */
4428
+ cloneResponse(response) {
4429
+ return typeof response.clone === "function" ? response.clone() : response;
4430
+ }
4431
+ async doFetch(url, method, body, extraHeaders) {
4290
4432
  const controller = new AbortController();
4291
4433
  const timeout = this.config.options?.timeout ?? 1e4;
4292
4434
  const timeoutId = setTimeout(
@@ -4296,6 +4438,7 @@ var __lionrapidAutoExports = (() => {
4296
4438
  try {
4297
4439
  const headers = {
4298
4440
  "Content-Type": "application/json",
4441
+ [SURFACE_HEADER]: SURFACE_TAG,
4299
4442
  ...this.config.options?.headers,
4300
4443
  ...extraHeaders
4301
4444
  };
@@ -4319,6 +4462,7 @@ var __lionrapidAutoExports = (() => {
4319
4462
  /** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
4320
4463
  * desync the snapshot store exists to prevent; entries are deleted on sight. */
4321
4464
  __publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
4465
+ __publicField(_NetworkRepository, "DEFAULT_NEGATIVE_TTL_MS", 6e4);
4322
4466
  var NetworkRepository = _NetworkRepository;
4323
4467
 
4324
4468
  // src/browser/exclude-filter.ts
@@ -5561,9 +5705,11 @@ var __lionrapidAutoExports = (() => {
5561
5705
  this.config = {
5562
5706
  enabled: config.enabled ?? true,
5563
5707
  priority: config.priority ?? 0,
5708
+ // Use build sequence by default
5564
5709
  options: {
5565
5710
  prefix: config.options?.prefix ?? STORAGE_PREFIX,
5566
5711
  ttl: config.options?.ttl ?? 24 * 60 * 60 * 1e3,
5712
+ // 24 hours default
5567
5713
  maxSize: config.options?.maxSize ?? 500,
5568
5714
  ...config.options
5569
5715
  }
@@ -5576,6 +5722,10 @@ var __lionrapidAutoExports = (() => {
5576
5722
  this.migrateIfNeeded();
5577
5723
  }
5578
5724
  }
5725
+ /**
5726
+ * Check if localStorage is available
5727
+ * Returns false in SSR, private browsing, or when quota is 0
5728
+ */
5579
5729
  checkAvailability() {
5580
5730
  if (typeof window === "undefined") {
5581
5731
  return false;
@@ -5589,6 +5739,9 @@ var __lionrapidAutoExports = (() => {
5589
5739
  return false;
5590
5740
  }
5591
5741
  }
5742
+ /**
5743
+ * Handle version migrations - clear cache when version changes
5744
+ */
5592
5745
  migrateIfNeeded() {
5593
5746
  try {
5594
5747
  const storedVersion = window.localStorage.getItem(VERSION_KEY);
@@ -5599,6 +5752,10 @@ var __lionrapidAutoExports = (() => {
5599
5752
  } catch {
5600
5753
  }
5601
5754
  }
5755
+ /**
5756
+ * Get a translation value by key
5757
+ * Returns null if not found or expired
5758
+ */
5602
5759
  get(key) {
5603
5760
  if (!this.available) return null;
5604
5761
  try {
@@ -5615,6 +5772,10 @@ var __lionrapidAutoExports = (() => {
5615
5772
  return null;
5616
5773
  }
5617
5774
  }
5775
+ /**
5776
+ * Set a translation value
5777
+ * Automatically handles quota exceeded by evicting oldest entries
5778
+ */
5618
5779
  set(key, value) {
5619
5780
  if (!this.available) return;
5620
5781
  const fullKey = this.prefix + key;
@@ -5639,6 +5800,9 @@ var __lionrapidAutoExports = (() => {
5639
5800
  }
5640
5801
  }
5641
5802
  }
5803
+ /**
5804
+ * Remove a translation by key
5805
+ */
5642
5806
  remove(key) {
5643
5807
  if (!this.available) return;
5644
5808
  try {
@@ -5646,6 +5810,10 @@ var __lionrapidAutoExports = (() => {
5646
5810
  } catch {
5647
5811
  }
5648
5812
  }
5813
+ /**
5814
+ * Clear all LionRapid entries from localStorage
5815
+ * Preserves entries from other applications
5816
+ */
5649
5817
  clear() {
5650
5818
  if (!this.available) return;
5651
5819
  try {
@@ -5660,6 +5828,9 @@ var __lionrapidAutoExports = (() => {
5660
5828
  } catch {
5661
5829
  }
5662
5830
  }
5831
+ /**
5832
+ * Clear all entries for a specific namespace
5833
+ */
5663
5834
  clearNamespace(locale, namespace) {
5664
5835
  if (!this.available) return;
5665
5836
  const namespaceCacheKey = `@${locale}:${namespace}`;
@@ -5678,6 +5849,9 @@ var __lionrapidAutoExports = (() => {
5678
5849
  } catch {
5679
5850
  }
5680
5851
  }
5852
+ /**
5853
+ * Get namespace translations
5854
+ */
5681
5855
  getNamespace(locale, namespace) {
5682
5856
  const namespaceCacheKey = `@${locale}:${namespace}`;
5683
5857
  const value = this.get(namespaceCacheKey);
@@ -5689,10 +5863,16 @@ var __lionrapidAutoExports = (() => {
5689
5863
  return null;
5690
5864
  }
5691
5865
  }
5866
+ /**
5867
+ * Set namespace translations
5868
+ */
5692
5869
  setNamespace(locale, namespace, translations) {
5693
5870
  const namespaceCacheKey = `@${locale}:${namespace}`;
5694
5871
  this.set(namespaceCacheKey, JSON.stringify(translations));
5695
5872
  }
5873
+ /**
5874
+ * Get all cache entries for debugging
5875
+ */
5696
5876
  getEntries() {
5697
5877
  const entries = {};
5698
5878
  if (!this.available) return entries;
@@ -5712,12 +5892,21 @@ var __lionrapidAutoExports = (() => {
5712
5892
  }
5713
5893
  return entries;
5714
5894
  }
5895
+ /**
5896
+ * Get repository configuration
5897
+ */
5715
5898
  getConfig() {
5716
5899
  return this.config;
5717
5900
  }
5901
+ /**
5902
+ * Check if localStorage is available
5903
+ */
5718
5904
  isAvailable() {
5719
5905
  return this.available;
5720
5906
  }
5907
+ /**
5908
+ * Get the number of entries in the cache
5909
+ */
5721
5910
  getEntryCount() {
5722
5911
  if (!this.available) return 0;
5723
5912
  let count = 0;
@@ -5732,12 +5921,18 @@ var __lionrapidAutoExports = (() => {
5732
5921
  }
5733
5922
  return count;
5734
5923
  }
5924
+ /**
5925
+ * Check if error is a quota exceeded error
5926
+ */
5735
5927
  isQuotaError(error) {
5736
5928
  if (error instanceof DOMException) {
5737
5929
  return error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED";
5738
5930
  }
5739
5931
  return false;
5740
5932
  }
5933
+ /**
5934
+ * Evict oldest 25% of entries when quota is exceeded
5935
+ */
5741
5936
  evictOldest() {
5742
5937
  if (!this.available) return;
5743
5938
  try {
@@ -5765,6 +5960,9 @@ var __lionrapidAutoExports = (() => {
5765
5960
  } catch {
5766
5961
  }
5767
5962
  }
5963
+ /**
5964
+ * Get cache statistics
5965
+ */
5768
5966
  getCacheStats() {
5769
5967
  const size = this.getEntryCount();
5770
5968
  const usage = this.maxSize > 0 ? `${Math.round(size / this.maxSize * 100)}%` : "0%";
@@ -5778,7 +5976,7 @@ var __lionrapidAutoExports = (() => {
5778
5976
 
5779
5977
  // src/browser/storage/cache-storage-repository.ts
5780
5978
  var CACHE_NAME = "lionrapid-v1";
5781
- var CACHE_PREFIX = "lionrapid:";
5979
+ var CACHE_PREFIX = "https://cache.lionrapid.internal/";
5782
5980
  var CacheStorageRepository = class {
5783
5981
  constructor(config = {}) {
5784
5982
  __publicField(this, "name", "CacheStorageRepository");
@@ -5793,6 +5991,7 @@ var __lionrapidAutoExports = (() => {
5793
5991
  options: {
5794
5992
  cacheName: config.options?.cacheName ?? CACHE_NAME,
5795
5993
  ttl: config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3,
5994
+ // 7 days default
5796
5995
  ...config.options
5797
5996
  }
5798
5997
  };
@@ -5800,9 +5999,17 @@ var __lionrapidAutoExports = (() => {
5800
5999
  this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
5801
6000
  this.available = this.checkAvailability();
5802
6001
  }
6002
+ /**
6003
+ * Check if Cache API is available
6004
+ * Returns false in SSR, old browsers, or insecure contexts
6005
+ */
5803
6006
  checkAvailability() {
5804
6007
  return typeof caches !== "undefined";
5805
6008
  }
6009
+ /**
6010
+ * Get a translation value by key
6011
+ * @throws {TranslationNotFoundError} When translation doesn't exist or is expired
6012
+ */
5806
6013
  async get(key) {
5807
6014
  if (!this.available) {
5808
6015
  throw new TranslationNotFoundError(key);
@@ -5826,6 +6033,9 @@ var __lionrapidAutoExports = (() => {
5826
6033
  throw new TranslationNotFoundError(key);
5827
6034
  }
5828
6035
  }
6036
+ /**
6037
+ * Set a translation value
6038
+ */
5829
6039
  async set(key, value) {
5830
6040
  if (!this.available) return;
5831
6041
  try {
@@ -5844,6 +6054,9 @@ var __lionrapidAutoExports = (() => {
5844
6054
  } catch {
5845
6055
  }
5846
6056
  }
6057
+ /**
6058
+ * Remove a translation by key
6059
+ */
5847
6060
  async remove(key) {
5848
6061
  if (!this.available) return;
5849
6062
  try {
@@ -5852,6 +6065,9 @@ var __lionrapidAutoExports = (() => {
5852
6065
  } catch {
5853
6066
  }
5854
6067
  }
6068
+ /**
6069
+ * Clear all LionRapid entries from the cache
6070
+ */
5855
6071
  async clear() {
5856
6072
  if (!this.available) return;
5857
6073
  try {
@@ -5863,6 +6079,9 @@ var __lionrapidAutoExports = (() => {
5863
6079
  } catch {
5864
6080
  }
5865
6081
  }
6082
+ /**
6083
+ * Clear all entries for a specific namespace
6084
+ */
5866
6085
  async clearNamespace(locale, namespace) {
5867
6086
  if (!this.available) return;
5868
6087
  const namespaceCacheKey = `@${locale}:${namespace}`;
@@ -5880,6 +6099,10 @@ var __lionrapidAutoExports = (() => {
5880
6099
  } catch {
5881
6100
  }
5882
6101
  }
6102
+ /**
6103
+ * Get namespace translations
6104
+ * @throws {TranslationNotFoundError} When namespace doesn't exist
6105
+ */
5883
6106
  async getNamespace(locale, namespace) {
5884
6107
  const namespaceCacheKey = `@${locale}:${namespace}`;
5885
6108
  const value = await this.get(namespaceCacheKey);
@@ -5890,16 +6113,29 @@ var __lionrapidAutoExports = (() => {
5890
6113
  throw new TranslationNotFoundError(namespaceCacheKey);
5891
6114
  }
5892
6115
  }
6116
+ /**
6117
+ * Set namespace translations
6118
+ */
5893
6119
  async setNamespace(locale, namespace, translations) {
5894
6120
  const namespaceCacheKey = `@${locale}:${namespace}`;
5895
6121
  await this.set(namespaceCacheKey, JSON.stringify(translations));
5896
6122
  }
6123
+ /**
6124
+ * Get repository configuration
6125
+ */
5897
6126
  getConfig() {
5898
6127
  return this.config;
5899
6128
  }
6129
+ /**
6130
+ * Check if Cache API is available
6131
+ */
5900
6132
  isAvailable() {
5901
6133
  return this.available;
5902
6134
  }
6135
+ /**
6136
+ * Get all cached entries (for debugging)
6137
+ * Returns an object mapping keys to values
6138
+ */
5903
6139
  async getEntries() {
5904
6140
  const entries = {};
5905
6141
  if (!this.available) return entries;
@@ -5922,6 +6158,9 @@ var __lionrapidAutoExports = (() => {
5922
6158
  }
5923
6159
  return entries;
5924
6160
  }
6161
+ /**
6162
+ * Get cache statistics
6163
+ */
5925
6164
  async getCacheStats() {
5926
6165
  if (!this.available) {
5927
6166
  return { size: 0, cacheName: this.cacheName };
@@ -5941,8 +6180,9 @@ var __lionrapidAutoExports = (() => {
5941
6180
  function resolveWritePolicy(apiKey) {
5942
6181
  const isPublishable = apiKey.startsWith("pk_");
5943
6182
  return {
6183
+ bootNetwork: isPublishable,
5944
6184
  syncMissing: { enabled: isPublishable },
5945
- warnReadOnly: !isPublishable
6185
+ warnRefused: !isPublishable
5946
6186
  };
5947
6187
  }
5948
6188
  function configForWindow(config) {
@@ -6091,9 +6331,9 @@ var __lionrapidAutoExports = (() => {
6091
6331
  return;
6092
6332
  }
6093
6333
  const writePolicy = resolveWritePolicy(config.apiKey);
6094
- if (writePolicy.warnReadOnly) {
6334
+ if (writePolicy.warnRefused) {
6095
6335
  console.error(
6096
- "[LionRapid] data-api-key must be a publishable pk_ key; running read-only"
6336
+ "[LionRapid] data-api-key must be a publishable pk_ key \u2014 network translations are disabled. Copy the snippet from the integration hub to get the pk_ key for this project."
6097
6337
  );
6098
6338
  }
6099
6339
  const initialLocale = resolveInitialLocale(config);
@@ -6147,25 +6387,28 @@ var __lionrapidAutoExports = (() => {
6147
6387
  })
6148
6388
  );
6149
6389
  }
6150
- const core = await builder.use(
6151
- new NetworkRepository({
6152
- enabled: true,
6153
- options: {
6154
- baseUrl: config.baseUrl,
6155
- apiKey: config.apiKey,
6156
- // pk_ keys sync missing source strings (discovery); anything else
6157
- // runs read-only — see resolveWritePolicy.
6158
- syncMissing: writePolicy.syncMissing,
6159
- preloadNamespaces: [
6160
- {
6161
- locale: initialLocale,
6162
- namespace: config.namespace,
6163
- eager: true
6164
- }
6165
- ]
6166
- }
6167
- })
6168
- ).build();
6390
+ if (writePolicy.bootNetwork) {
6391
+ builder.use(
6392
+ new NetworkRepository({
6393
+ enabled: true,
6394
+ options: {
6395
+ baseUrl: config.baseUrl,
6396
+ apiKey: config.apiKey,
6397
+ // pk_ keys sync missing source strings (discovery) — see
6398
+ // resolveWritePolicy.
6399
+ syncMissing: writePolicy.syncMissing,
6400
+ preloadNamespaces: [
6401
+ {
6402
+ locale: initialLocale,
6403
+ namespace: config.namespace,
6404
+ eager: true
6405
+ }
6406
+ ]
6407
+ }
6408
+ })
6409
+ );
6410
+ }
6411
+ const core = await builder.build();
6169
6412
  const autoTranslator = new AutoTranslator({
6170
6413
  excludeSelectors: config.excludeSelectors
6171
6414
  });