@lionrapid/core 0.6.6 → 0.7.0

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/dist/browser/auto-init.d.ts +6 -0
  3. package/dist/browser/auto-init.d.ts.map +1 -1
  4. package/dist/browser/auto-init.js +36 -6
  5. package/dist/browser/auto-init.js.map +1 -1
  6. package/dist/browser/language-switcher.d.ts +12 -0
  7. package/dist/browser/language-switcher.d.ts.map +1 -1
  8. package/dist/browser/language-switcher.js +58 -0
  9. package/dist/browser/language-switcher.js.map +1 -1
  10. package/dist/browser/lionrapid.auto.dev.js +163 -14
  11. package/dist/browser/lionrapid.auto.dev.js.map +3 -3
  12. package/dist/browser/lionrapid.auto.js +163 -14
  13. package/dist/browser/lionrapid.auto.js.map +3 -3
  14. package/dist/browser/lionrapid.auto.min.js +5 -5
  15. package/dist/browser/lionrapid.auto.min.js.map +4 -4
  16. package/dist/browser/lionrapid.esm.js +103 -10
  17. package/dist/browser/lionrapid.esm.js.map +2 -2
  18. package/dist/browser/lionrapid.esm.min.js +4 -4
  19. package/dist/browser/lionrapid.esm.min.js.map +3 -3
  20. package/dist/browser/lionrapid.iife.dev.js +103 -10
  21. package/dist/browser/lionrapid.iife.dev.js.map +2 -2
  22. package/dist/browser/lionrapid.iife.js +103 -10
  23. package/dist/browser/lionrapid.iife.js.map +2 -2
  24. package/dist/browser/lionrapid.iife.min.js +4 -4
  25. package/dist/browser/lionrapid.iife.min.js.map +3 -3
  26. package/dist/browser/lionrapid.umd.js +103 -10
  27. package/dist/browser/lionrapid.umd.js.map +2 -2
  28. package/dist/browser/lionrapid.umd.min.js +4 -4
  29. package/dist/browser/lionrapid.umd.min.js.map +3 -3
  30. package/dist/browser/storage/cache-storage-repository.d.ts +9 -0
  31. package/dist/browser/storage/cache-storage-repository.d.ts.map +1 -1
  32. package/dist/browser/storage/cache-storage-repository.js +14 -10
  33. package/dist/browser/storage/cache-storage-repository.js.map +1 -1
  34. package/dist/browser/storage/scope.d.ts +12 -0
  35. package/dist/browser/storage/scope.d.ts.map +1 -0
  36. package/dist/browser/storage/scope.js +15 -0
  37. package/dist/browser/storage/scope.js.map +1 -0
  38. package/dist/builder.d.ts.map +1 -1
  39. package/dist/builder.js +9 -0
  40. package/dist/builder.js.map +1 -1
  41. package/dist/handlers/async-storage-handler.d.ts +9 -0
  42. package/dist/handlers/async-storage-handler.d.ts.map +1 -1
  43. package/dist/handlers/async-storage-handler.js +42 -0
  44. package/dist/handlers/async-storage-handler.js.map +1 -1
  45. package/dist/handlers/handler-chain.d.ts +2 -0
  46. package/dist/handlers/handler-chain.d.ts.map +1 -1
  47. package/dist/handlers/handler-chain.js +29 -0
  48. package/dist/handlers/handler-chain.js.map +1 -1
  49. package/dist/handlers/sync-storage-handler.d.ts +4 -0
  50. package/dist/handlers/sync-storage-handler.d.ts.map +1 -1
  51. package/dist/handlers/sync-storage-handler.js +35 -0
  52. package/dist/handlers/sync-storage-handler.js.map +1 -1
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +1 -1
  55. package/dist/interfaces/handler.interface.d.ts +4 -0
  56. package/dist/interfaces/handler.interface.d.ts.map +1 -1
  57. package/package.json +3 -3
@@ -1234,6 +1234,24 @@ var LionRapid = (() => {
1234
1234
  });
1235
1235
  await Promise.allSettled(savePromises);
1236
1236
  }
1237
+ /** Forget `locale:namespace` in every memory and storage handler (a 404). */
1238
+ async forgetNamespaceInHandlers(context) {
1239
+ for (const handler of this.syncHandlers) {
1240
+ try {
1241
+ handler.forgetNamespace?.(context);
1242
+ } catch (error) {
1243
+ logger.warn(`Failed to clear namespace in ${handler.name}`, error);
1244
+ }
1245
+ }
1246
+ for (const handler of this.asyncHandlers) {
1247
+ if (handler.type !== "storage" && handler.type !== "memory") continue;
1248
+ try {
1249
+ await handler.forgetNamespace?.(context);
1250
+ } catch (error) {
1251
+ logger.warn(`Failed to clear namespace in ${handler.name}`, error);
1252
+ }
1253
+ }
1254
+ }
1237
1255
  async saveNamespaceToHandlers(translations, context) {
1238
1256
  this.saveNamespaceToMemoryHandlers(translations, context);
1239
1257
  await this.saveNamespaceToStorageHandlers(translations, context);
@@ -1335,6 +1353,10 @@ var LionRapid = (() => {
1335
1353
  });
1336
1354
  return result.data;
1337
1355
  }
1356
+ if (result && result.status === 404) {
1357
+ await this.forgetNamespaceInHandlers({ namespace, locale });
1358
+ return null;
1359
+ }
1338
1360
  } catch (error) {
1339
1361
  logger.warn(
1340
1362
  `Failed to load namespace: ${locale}:${namespace} from ${handler.name}`,
@@ -1718,6 +1740,7 @@ var LionRapid = (() => {
1718
1740
  context.locale + ":" + context.namespace,
1719
1741
  this.name
1720
1742
  );
1743
+ const previousKeys = this.storedNamespaceKeys(context.locale, context.namespace);
1721
1744
  const namespaceCacheKey = createNamespaceCacheKey(
1722
1745
  context.locale,
1723
1746
  context.namespace
@@ -1729,11 +1752,38 @@ var LionRapid = (() => {
1729
1752
  context.locale,
1730
1753
  context.namespace
1731
1754
  );
1755
+ for (const key of previousKeys) {
1756
+ if (!(key in flattened)) this.removeQuietly(key);
1757
+ }
1732
1758
  for (const [flatKey, flatValue] of Object.entries(flattened)) {
1733
1759
  this.setWithTTL(flatKey, flatValue);
1734
1760
  }
1735
1761
  repoBus.setSuccess(JSON.stringify(translations));
1736
1762
  }
1763
+ /** Forget a namespace the server says does not exist (404). See AsyncStorageHandler. */
1764
+ forgetNamespace(context) {
1765
+ if (!context.namespace) return;
1766
+ for (const key of this.storedNamespaceKeys(context.locale, context.namespace)) {
1767
+ this.removeQuietly(key);
1768
+ }
1769
+ this.clearNamespace(context.locale, context.namespace);
1770
+ }
1771
+ storedNamespaceKeys(locale, namespace) {
1772
+ try {
1773
+ const blob = this.repository.get(createNamespaceCacheKey(locale, namespace));
1774
+ if (!blob) return [];
1775
+ return Object.keys(flattenTranslations(JSON.parse(blob), "", locale, namespace));
1776
+ } catch {
1777
+ return [];
1778
+ }
1779
+ }
1780
+ removeQuietly(cacheKey) {
1781
+ this.ttlMap.delete(cacheKey);
1782
+ try {
1783
+ this.repository.remove(cacheKey);
1784
+ } catch {
1785
+ }
1786
+ }
1737
1787
  /**
1738
1788
  * ✅ Strategy 1: Direct cache lookup using unified pattern
1739
1789
  * Returns parsed ContentUnit (text + styles) or null
@@ -1987,6 +2037,7 @@ var LionRapid = (() => {
1987
2037
  logger.debug(
1988
2038
  `Saving namespace: ${context.locale}:${context.namespace} (${keyCount} keys)`
1989
2039
  );
2040
+ const previousKeys = await this.storedNamespaceKeys(context.locale, context.namespace);
1990
2041
  const namespaceCacheKey = createNamespaceCacheKey(
1991
2042
  context.locale,
1992
2043
  context.namespace
@@ -1998,6 +2049,9 @@ var LionRapid = (() => {
1998
2049
  context.locale,
1999
2050
  context.namespace
2000
2051
  );
2052
+ for (const key of previousKeys) {
2053
+ if (!(key in flattened)) await this.removeQuietly(key);
2054
+ }
2001
2055
  let savedCount = 0;
2002
2056
  for (const [flatKey, flatValue] of Object.entries(flattened)) {
2003
2057
  await this.setWithTTL(flatKey, flatValue);
@@ -2006,6 +2060,35 @@ var LionRapid = (() => {
2006
2060
  logger.debug(`Saved: ${savedCount} keys + namespace blob`);
2007
2061
  repoBus.setSuccess(JSON.stringify(translations));
2008
2062
  }
2063
+ /**
2064
+ * Forget a namespace the server says does not exist (404): its blob and
2065
+ * every per-key entry the blob listed. A network failure never gets here,
2066
+ * so the offline floor still serves when the server is unreachable.
2067
+ */
2068
+ async forgetNamespace(context) {
2069
+ if (!context.namespace) return;
2070
+ for (const key of await this.storedNamespaceKeys(context.locale, context.namespace)) {
2071
+ await this.removeQuietly(key);
2072
+ }
2073
+ await this.removeQuietly(createNamespaceCacheKey(context.locale, context.namespace));
2074
+ }
2075
+ /** The per-key cache keys (`locale:namespace:key`) the stored blob lists. */
2076
+ async storedNamespaceKeys(locale, namespace) {
2077
+ try {
2078
+ const blob = await this.repository.get(createNamespaceCacheKey(locale, namespace));
2079
+ if (!blob) return [];
2080
+ return Object.keys(flattenTranslations(JSON.parse(blob), "", locale, namespace));
2081
+ } catch {
2082
+ return [];
2083
+ }
2084
+ }
2085
+ async removeQuietly(cacheKey) {
2086
+ this.ttlMap.delete(cacheKey);
2087
+ try {
2088
+ await this.repository.remove(cacheKey);
2089
+ } catch {
2090
+ }
2091
+ }
2009
2092
  /**
2010
2093
  * ✅ Strategy 1: Direct cache lookup using unified pattern
2011
2094
  * Returns parsed ContentUnit (text + styles) or null
@@ -4233,6 +4316,12 @@ var LionRapid = (() => {
4233
4316
  logger.debug(`Preloaded: ${namespaceKey}`);
4234
4317
  return;
4235
4318
  }
4319
+ if (result?.status === 404) {
4320
+ await handlerChain.forgetNamespaceInHandlers({
4321
+ locale: preloadConfig.locale,
4322
+ namespace: preloadConfig.namespace
4323
+ });
4324
+ }
4236
4325
  if (result?.error) {
4237
4326
  warnLoudlyOnce(namespaceKey, result.error);
4238
4327
  logger.warn(`Preload failed: ${namespaceKey}`, result.error);
@@ -5609,6 +5698,8 @@ var LionRapid = (() => {
5609
5698
  __publicField(this, "type", "storage");
5610
5699
  __publicField(this, "config");
5611
5700
  __publicField(this, "cacheName");
5701
+ /** CACHE_PREFIX, plus `<scope>/` when scoped. */
5702
+ __publicField(this, "entryPrefix");
5612
5703
  __publicField(this, "defaultTTL");
5613
5704
  __publicField(this, "available");
5614
5705
  this.config = {
@@ -5622,6 +5713,8 @@ var LionRapid = (() => {
5622
5713
  }
5623
5714
  };
5624
5715
  this.cacheName = this.config.options?.cacheName ?? CACHE_NAME;
5716
+ const scope = this.config.options?.scope;
5717
+ this.entryPrefix = scope ? `${CACHE_PREFIX}${encodeURIComponent(scope)}/` : CACHE_PREFIX;
5625
5718
  this.defaultTTL = this.config.options?.ttl ?? 7 * 24 * 60 * 60 * 1e3;
5626
5719
  this.available = this.checkAvailability();
5627
5720
  }
@@ -5642,13 +5735,13 @@ var LionRapid = (() => {
5642
5735
  }
5643
5736
  try {
5644
5737
  const cache = await caches.open(this.cacheName);
5645
- const response = await cache.match(CACHE_PREFIX + key);
5738
+ const response = await cache.match(this.entryPrefix + key);
5646
5739
  if (!response) {
5647
5740
  throw new TranslationNotFoundError(key);
5648
5741
  }
5649
5742
  const data = await response.json();
5650
5743
  if (data.expiry !== null && Date.now() > data.expiry) {
5651
- await cache.delete(CACHE_PREFIX + key);
5744
+ await cache.delete(this.entryPrefix + key);
5652
5745
  throw new TranslationNotFoundError(key);
5653
5746
  }
5654
5747
  return data.value;
@@ -5672,7 +5765,7 @@ var LionRapid = (() => {
5672
5765
  timestamp: Date.now()
5673
5766
  };
5674
5767
  await cache.put(
5675
- CACHE_PREFIX + key,
5768
+ this.entryPrefix + key,
5676
5769
  new Response(JSON.stringify(data), {
5677
5770
  headers: { "Content-Type": "application/json" }
5678
5771
  })
@@ -5687,7 +5780,7 @@ var LionRapid = (() => {
5687
5780
  if (!this.available) return;
5688
5781
  try {
5689
5782
  const cache = await caches.open(this.cacheName);
5690
- await cache.delete(CACHE_PREFIX + key);
5783
+ await cache.delete(this.entryPrefix + key);
5691
5784
  } catch {
5692
5785
  }
5693
5786
  }
@@ -5700,7 +5793,7 @@ var LionRapid = (() => {
5700
5793
  const cache = await caches.open(this.cacheName);
5701
5794
  const keys = await cache.keys();
5702
5795
  await Promise.all(
5703
- keys.filter((request) => request.url.includes(CACHE_PREFIX)).map((request) => cache.delete(request))
5796
+ keys.filter((request) => request.url.includes(this.entryPrefix)).map((request) => cache.delete(request))
5704
5797
  );
5705
5798
  } catch {
5706
5799
  }
@@ -5718,7 +5811,7 @@ var LionRapid = (() => {
5718
5811
  await Promise.all(
5719
5812
  keys.filter((request) => {
5720
5813
  const url = request.url;
5721
- const key = url.includes(CACHE_PREFIX) ? url.substring(url.indexOf(CACHE_PREFIX) + CACHE_PREFIX.length) : "";
5814
+ const key = url.includes(this.entryPrefix) ? url.substring(url.indexOf(this.entryPrefix) + this.entryPrefix.length) : "";
5722
5815
  return key === namespaceCacheKey || key.startsWith(keyPrefix);
5723
5816
  }).map((request) => cache.delete(request))
5724
5817
  );
@@ -5770,9 +5863,9 @@ var LionRapid = (() => {
5770
5863
  const keys = await cache.keys();
5771
5864
  for (const request of keys) {
5772
5865
  const url = request.url;
5773
- if (!url.includes(CACHE_PREFIX)) continue;
5866
+ if (!url.includes(this.entryPrefix)) continue;
5774
5867
  const key = url.substring(
5775
- url.indexOf(CACHE_PREFIX) + CACHE_PREFIX.length
5868
+ url.indexOf(this.entryPrefix) + this.entryPrefix.length
5776
5869
  );
5777
5870
  try {
5778
5871
  const value = await this.get(key);
@@ -5794,7 +5887,7 @@ var LionRapid = (() => {
5794
5887
  try {
5795
5888
  const cache = await caches.open(this.cacheName);
5796
5889
  const keys = await cache.keys();
5797
- const size = keys.filter((r) => r.url.includes(CACHE_PREFIX)).length;
5890
+ const size = keys.filter((r) => r.url.includes(this.entryPrefix)).length;
5798
5891
  return { size, cacheName: this.cacheName };
5799
5892
  } catch {
5800
5893
  return { size: 0, cacheName: this.cacheName };
@@ -5806,7 +5899,7 @@ var LionRapid = (() => {
5806
5899
  var DEFAULT_API_BASE_URL = "https://app.lionrapid.com";
5807
5900
 
5808
5901
  // src/index.ts
5809
- var VERSION = "0.6.6";
5902
+ var VERSION = "0.7.0";
5810
5903
  return __toCommonJS(index_exports);
5811
5904
  })();
5812
5905
  //# sourceMappingURL=lionrapid.iife.js.map