gt-react 10.19.17 → 10.19.18

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/macros.mjs CHANGED
@@ -4342,7 +4342,7 @@ function sanitizeJsxChildren(childrenAsObjects) {
4342
4342
  return Array.isArray(childrenAsObjects) ? childrenAsObjects.map(sanitizeChild) : sanitizeChild(childrenAsObjects);
4343
4343
  }
4344
4344
  //#endregion
4345
- //#region ../i18n/dist/versionId-BkJZGHXr.mjs
4345
+ //#region ../i18n/dist/versionId-BTjLA0FZ.mjs
4346
4346
  /**
4347
4347
  * Throw errors if there are any errors and log warnings if there are any warnings
4348
4348
  * @param {ValidationResult[]} results - The results to print
@@ -4608,83 +4608,269 @@ function routeCreateTranslationLoader({ type, remoteTranslationLoaderParams, loa
4608
4608
  case "disabled": return createFallbackTranslationLoader();
4609
4609
  }
4610
4610
  }
4611
- function isPlainObject(value) {
4612
- if (value == null || typeof value !== "object") return false;
4613
- const prototype = Object.getPrototypeOf(value);
4614
- return prototype === Object.prototype || prototype === null;
4611
+ function getDictionaryPath(id) {
4612
+ const path = id ? id.split(".") : [];
4613
+ for (const segment of path) assertSafeDictionaryPathSegment(segment, id);
4614
+ return path;
4615
4615
  }
4616
- function copyCacheValue(value) {
4617
- if (Array.isArray(value)) return [...value];
4618
- if (isPlainObject(value)) return { ...value };
4619
- return value;
4616
+ function assertSafeDictionaryPathSegment(segment, path) {
4617
+ if (segment === "__proto__" || segment === "constructor" || segment === "prototype") throw new Error(`Dictionary path "${path}" contains an unsafe segment`);
4618
+ }
4619
+ function isDictionaryObject(value) {
4620
+ return typeof value === "object" && value != null && !Array.isArray(value);
4621
+ }
4622
+ function cloneDictionaryValue(value) {
4623
+ if (value === void 0 || typeof value === "string") return value;
4624
+ return structuredClone(value);
4625
+ }
4626
+ function getDictionaryValueAtPath(dictionary, path) {
4627
+ let current = dictionary;
4628
+ for (const segment of getDictionaryPath(path)) {
4629
+ if (!isDictionaryObject(current)) return;
4630
+ current = current[segment];
4631
+ }
4632
+ return current;
4633
+ }
4634
+ function setDictionaryValueAtPath(dictionary, path, value) {
4635
+ const segments = getDictionaryPath(path);
4636
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4637
+ if (segments.length === 0) {
4638
+ if (isDictionaryObject(value)) replaceDictionary(dictionary, value);
4639
+ return;
4640
+ }
4641
+ let current = dictionary;
4642
+ for (const segment of segments.slice(0, -1)) {
4643
+ const next = current[segment];
4644
+ if (!isDictionaryObject(next)) current[segment] = {};
4645
+ current = current[segment];
4646
+ }
4647
+ const leafSegment = segments[segments.length - 1];
4648
+ current[leafSegment] = value;
4649
+ }
4650
+ function getDictionaryEntry(value) {
4651
+ if (!isDictionaryLeafNode(value)) return;
4652
+ return {
4653
+ entry: Array.isArray(value) ? value[0] : value,
4654
+ options: Array.isArray(value) ? value[1] ?? {} : {}
4655
+ };
4656
+ }
4657
+ function getDictionaryValue(value) {
4658
+ if (Object.keys(value.options).length === 0) return value.entry;
4659
+ return [value.entry, value.options];
4620
4660
  }
4661
+ function resolveDictionaryLookupOptions(options) {
4662
+ const { $format, context, ...rest } = options;
4663
+ return {
4664
+ ...rest,
4665
+ $format: isStringFormat($format) ? $format : "ICU",
4666
+ ...rest.$context === void 0 && typeof context === "string" && { $context: context }
4667
+ };
4668
+ }
4669
+ function isDictionaryLeafNode(value) {
4670
+ if (typeof value === "string") return true;
4671
+ if (!Array.isArray(value) || typeof value[0] !== "string") return false;
4672
+ if (value.length === 1) return true;
4673
+ return value.length === 2 && isDictionaryOptions(value[1]);
4674
+ }
4675
+ function isDictionaryOptions(value) {
4676
+ if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
4677
+ const options = value;
4678
+ return (options.$context === void 0 || typeof options.$context === "string") && (options.$format === void 0 || isStringFormat(options.$format)) && (options.$maxChars === void 0 || typeof options.$maxChars === "number") && (options.context === void 0 || typeof options.context === "string");
4679
+ }
4680
+ function isStringFormat(value) {
4681
+ return value === "ICU" || value === "I18NEXT" || value === "STRING";
4682
+ }
4683
+ function replaceDictionary(target, source) {
4684
+ for (const key of Object.keys(target)) delete target[key];
4685
+ for (const key of Object.keys(source)) target[key] = source[key];
4686
+ }
4687
+ function assertSafeDictionaryObject(dictionary, parentPath = "") {
4688
+ for (const [key, value] of Object.entries(dictionary)) {
4689
+ const path = parentPath ? `${parentPath}.${key}` : key;
4690
+ assertSafeDictionaryPathSegment(key, path);
4691
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4692
+ }
4693
+ }
4694
+ var DictionarySourceNotFoundError = class extends Error {
4695
+ constructor(id) {
4696
+ super(`I18nManager: source dictionary entry ${id} is not defined`);
4697
+ this.name = "DictionarySourceNotFoundError";
4698
+ }
4699
+ };
4621
4700
  /**
4622
- * Cache class
4623
- * This is designed in such a way that it is the responsibility of the client
4624
- * to invoke the cache miss method when a cache miss occurs.
4625
- *
4626
- * TODO: maybe add "OutputValue" as a reflection of "InputKey"
4701
+ * Builds the dictionary value for a requested path by combining existing target
4702
+ * translations with runtime translations of any source leaves that are missing.
4627
4703
  */
4628
- var Cache = class {
4629
- /**
4630
- * Constructor
4631
- * @param {Object} params - The parameters for the cache
4632
- * @param {Record<CacheKey, CacheValue>} params.init - The initial cache
4633
- * @param {CacheLifecycle} [lifecycle] - Optional lifecycle callbacks
4634
- */
4635
- constructor(init, lifecycle) {
4636
- this.cache = {};
4637
- this.fallbackPromises = {};
4704
+ async function materializeDictionaryValue({ key, sourceValue, targetValue, translateEntry }) {
4705
+ if (getDictionaryEntry(targetValue) !== void 0) return cloneDictionaryValue(targetValue);
4706
+ if (isDictionaryObject(targetValue) && !isDictionaryObject(sourceValue)) return cloneDictionaryValue(targetValue);
4707
+ const sourceEntry = getDictionaryEntry(sourceValue);
4708
+ if (sourceEntry !== void 0) return await translateEntry(key, sourceEntry);
4709
+ if (!isDictionaryObject(sourceValue)) throw new DictionarySourceNotFoundError(key);
4710
+ const targetDictionary = isDictionaryObject(targetValue) ? targetValue : {};
4711
+ const keys = new Set([...Object.keys(sourceValue), ...Object.keys(targetDictionary)]);
4712
+ const entries = await Promise.all(Array.from(keys).map(async (childKey) => {
4713
+ const childPath = key ? `${key}.${childKey}` : childKey;
4714
+ assertSafeDictionaryPathSegment(childKey, childPath);
4715
+ const childSource = sourceValue[childKey];
4716
+ if (childSource === void 0) return [childKey, cloneDictionaryValue(targetDictionary[childKey])];
4717
+ return [childKey, await materializeDictionaryValue({
4718
+ key: childPath,
4719
+ sourceValue: childSource,
4720
+ targetValue: targetDictionary[childKey],
4721
+ translateEntry
4722
+ })];
4723
+ }));
4724
+ return Object.fromEntries(entries);
4725
+ }
4726
+ function cloneDictionaryEntry(entry) {
4727
+ return {
4728
+ entry: entry.entry,
4729
+ options: structuredClone(entry.options)
4730
+ };
4731
+ }
4732
+ var DictionaryCache = class {
4733
+ constructor({ init, lifecycle = {}, runtimeTranslate }) {
4734
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4735
+ this.pendingMaterializations = /* @__PURE__ */ new Map();
4638
4736
  this.cache = structuredClone(init);
4639
- this.onHit = lifecycle?.onHit;
4640
- this.onMiss = lifecycle?.onMiss;
4737
+ this.runtimeTranslate = runtimeTranslate;
4738
+ this.lifecycle = lifecycle;
4641
4739
  }
4642
- /**
4643
- * Set the value for a key
4644
- */
4645
- setCache(cacheKey, value) {
4646
- this.cache[cacheKey] = value;
4740
+ getEntry(key) {
4741
+ const value = getDictionaryValueAtPath(this.cache, key);
4742
+ const entry = getDictionaryEntry(value);
4743
+ if (entry === void 0) return;
4744
+ const outputEntry = cloneDictionaryEntry(entry);
4745
+ this.lifecycle.onHit?.({
4746
+ inputKey: key,
4747
+ cacheKey: key,
4748
+ cacheValue: value,
4749
+ outputValue: outputEntry
4750
+ });
4751
+ return outputEntry;
4647
4752
  }
4648
- /**
4649
- * Look up the key
4650
- */
4651
- getCache(key) {
4652
- const cacheKey = this.genKey(key);
4653
- return this.cache[cacheKey];
4753
+ getValue(key) {
4754
+ const value = getDictionaryValueAtPath(this.cache, key);
4755
+ if (value === void 0) return;
4756
+ const outputValue = cloneDictionaryValue(value);
4757
+ this.lifecycle.onDictionaryObjectCacheHit?.({
4758
+ inputKey: key,
4759
+ cacheKey: key,
4760
+ cacheValue: value,
4761
+ outputValue
4762
+ });
4763
+ return outputValue;
4764
+ }
4765
+ setValue(key, value) {
4766
+ setDictionaryValueAtPath(this.cache, key, cloneDictionaryValue(value));
4654
4767
  }
4655
- /**
4656
- * Get the internal cache
4657
- * @returns The internal cache
4658
- *
4659
- * @internal - used by gt-tanstack-start
4660
- */
4661
4768
  getInternalCache() {
4662
- return Object.fromEntries(Object.entries(this.cache).map(([key, value]) => [key, copyCacheValue(value)]));
4769
+ return cloneDictionaryValue(this.cache);
4770
+ }
4771
+ async materializeValue(key, sourceValue, targetValue = getDictionaryValueAtPath(this.cache, key)) {
4772
+ let materializationPromise = this.pendingMaterializations.get(key);
4773
+ if (!materializationPromise) {
4774
+ materializationPromise = materializeDictionaryValue({
4775
+ key,
4776
+ sourceValue,
4777
+ targetValue,
4778
+ translateEntry: async (entryKey, sourceEntry) => getDictionaryValue(await this.materializeEntry(entryKey, sourceEntry))
4779
+ }).then((value) => {
4780
+ this.setValue(key, value);
4781
+ return value;
4782
+ });
4783
+ this.pendingMaterializations.set(key, materializationPromise);
4784
+ }
4785
+ try {
4786
+ return await materializationPromise;
4787
+ } finally {
4788
+ this.pendingMaterializations.delete(key);
4789
+ }
4663
4790
  }
4664
- /**
4665
- * Get the mutable cache for subclasses that need custom read/write behavior.
4666
- */
4667
- getMutableCache() {
4668
- return this.cache;
4791
+ async materializeEntry(key, sourceEntry) {
4792
+ let translationPromise = this.pendingTranslations.get(key);
4793
+ if (!translationPromise) {
4794
+ translationPromise = this.runtimeTranslate(key, sourceEntry).then((value) => {
4795
+ setDictionaryValueAtPath(this.cache, key, value);
4796
+ const entry = getDictionaryEntry(value);
4797
+ if (entry === void 0) throw new Error("DictionaryCache materializeEntry did not return a DictionaryEntry");
4798
+ this.lifecycle.onMiss?.({
4799
+ inputKey: key,
4800
+ cacheKey: key,
4801
+ cacheValue: value,
4802
+ outputValue: cloneDictionaryEntry(entry)
4803
+ });
4804
+ return cloneDictionaryEntry(entry);
4805
+ });
4806
+ this.pendingTranslations.set(key, translationPromise);
4807
+ }
4808
+ try {
4809
+ return cloneDictionaryEntry(await translationPromise);
4810
+ } finally {
4811
+ this.pendingTranslations.delete(key);
4812
+ }
4669
4813
  }
4670
- /**
4671
- * Fallback to the value from the fallback function on a cache miss
4672
- * @important assumes that the fallback error handling done upstream
4673
- */
4674
- async missCache(...args) {
4675
- const key = args[0];
4676
- const cacheKey = this.genKey(key);
4677
- if (this.fallbackPromises[cacheKey] !== void 0) return await this.fallbackPromises[cacheKey];
4678
- const fallbackPromise = this.fallback(...args);
4679
- this.fallbackPromises[cacheKey] = fallbackPromise;
4814
+ };
4815
+ var ResourceCache = class {
4816
+ constructor({ load, lifecycle = {}, ttl }) {
4817
+ this.cache = /* @__PURE__ */ new Map();
4818
+ this.pendingLoads = /* @__PURE__ */ new Map();
4819
+ this.loadResource = load;
4820
+ this.lifecycle = lifecycle;
4821
+ this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4822
+ }
4823
+ get(key) {
4824
+ const entry = this.cache.get(key);
4825
+ if (!entry || this.isExpired(entry)) return;
4826
+ this.lifecycle.onHit?.({
4827
+ inputKey: key,
4828
+ cacheKey: key,
4829
+ cacheValue: entry,
4830
+ outputValue: entry.value
4831
+ });
4832
+ return entry.value;
4833
+ }
4834
+ set(key, value, { expiresAt = this.getExpiresAt() } = {}) {
4835
+ this.cache.set(key, {
4836
+ expiresAt,
4837
+ value
4838
+ });
4839
+ }
4840
+ async getOrLoad(key) {
4841
+ return this.get(key) ?? await this.load(key);
4842
+ }
4843
+ async load(key) {
4844
+ let loadPromise = this.pendingLoads.get(key);
4845
+ if (!loadPromise) {
4846
+ loadPromise = this.loadResource(key).then((value) => {
4847
+ const entry = {
4848
+ expiresAt: this.getExpiresAt(),
4849
+ value
4850
+ };
4851
+ this.cache.set(key, entry);
4852
+ this.lifecycle.onMiss?.({
4853
+ inputKey: key,
4854
+ cacheKey: key,
4855
+ cacheValue: entry,
4856
+ outputValue: entry.value
4857
+ });
4858
+ return entry;
4859
+ });
4860
+ this.pendingLoads.set(key, loadPromise);
4861
+ }
4680
4862
  try {
4681
- const value = await fallbackPromise;
4682
- this.setCache(cacheKey, value);
4683
- return value;
4863
+ return (await loadPromise).value;
4684
4864
  } finally {
4685
- delete this.fallbackPromises[cacheKey];
4865
+ this.pendingLoads.delete(key);
4686
4866
  }
4687
4867
  }
4868
+ getExpiresAt() {
4869
+ return this.ttl < 0 ? this.ttl : Date.now() + this.ttl;
4870
+ }
4871
+ isExpired(entry) {
4872
+ return entry.expiresAt > 0 && entry.expiresAt < Date.now();
4873
+ }
4688
4874
  };
4689
4875
  /**
4690
4876
  * Hash a message string
@@ -4729,20 +4915,22 @@ function normalizeBatchConfig(batchConfig) {
4729
4915
  * Locale logic is handled at the LocalesCache level. Use a callback function that has the
4730
4916
  * locale parameter embedded if you wish to use the locale code.
4731
4917
  */
4732
- var TranslationsCache = class extends Cache {
4918
+ var TranslationsCache = class {
4733
4919
  /**
4734
4920
  * Constructor
4735
4921
  * @param {Object} params - The parameters for the cache
4736
4922
  * @param {Record<Hash, TranslationValue>} params.init - The initial cache
4737
4923
  * @param {Function} params.fallback - Get the fallback value for a cache miss
4738
4924
  */
4739
- constructor({ init, translateMany, lifecycle, batchConfig }) {
4740
- super(init, lifecycle);
4741
- this._queue = [];
4742
- this._batchTimer = null;
4743
- this._activeRequests = 0;
4744
- this._translateMany = translateMany;
4745
- this._batchConfig = normalizeBatchConfig(batchConfig);
4925
+ constructor({ init, translateMany, lifecycle = {}, batchConfig }) {
4926
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4927
+ this.queue = [];
4928
+ this.batchTimer = null;
4929
+ this.activeRequests = 0;
4930
+ this.cache = structuredClone(init);
4931
+ this.translateMany = translateMany;
4932
+ this.batchConfig = normalizeBatchConfig(batchConfig);
4933
+ this.lifecycle = lifecycle;
4746
4934
  }
4747
4935
  /**
4748
4936
  * Get the translation value for a given key
@@ -4750,10 +4938,11 @@ var TranslationsCache = class extends Cache {
4750
4938
  * @returns The translation value
4751
4939
  */
4752
4940
  get(key) {
4753
- const value = this.getCache(key);
4754
- if (value != null && this.onHit) this.onHit({
4941
+ const cacheKey = this.getCacheKey(key);
4942
+ const value = this.cache[cacheKey];
4943
+ if (value != null) this.lifecycle.onHit?.({
4755
4944
  inputKey: key,
4756
- cacheKey: this.genKey(key),
4945
+ cacheKey,
4757
4946
  cacheValue: value,
4758
4947
  outputValue: value
4759
4948
  });
@@ -4765,75 +4954,64 @@ var TranslationsCache = class extends Cache {
4765
4954
  * @returns The translation value
4766
4955
  */
4767
4956
  async miss(key) {
4768
- const value = await this.missCache(key);
4769
- if (value != null && this.onMiss) this.onMiss({
4770
- inputKey: key,
4771
- cacheKey: this.genKey(key),
4772
- cacheValue: value,
4773
- outputValue: value
4774
- });
4775
- return value;
4957
+ const cacheKey = this.getCacheKey(key);
4958
+ let translationPromise = this.pendingTranslations.get(cacheKey);
4959
+ if (!translationPromise) {
4960
+ translationPromise = this.translate(key);
4961
+ this.pendingTranslations.set(cacheKey, translationPromise);
4962
+ }
4963
+ try {
4964
+ const value = await translationPromise;
4965
+ if (value != null) this.lifecycle.onMiss?.({
4966
+ inputKey: key,
4967
+ cacheKey,
4968
+ cacheValue: value,
4969
+ outputValue: value
4970
+ });
4971
+ return value;
4972
+ } finally {
4973
+ this.pendingTranslations.delete(cacheKey);
4974
+ }
4776
4975
  }
4777
- /**
4778
- * Generate a key for the cache
4779
- * @param key - The translation key
4780
- * @returns The key
4781
- */
4782
- genKey(key) {
4976
+ getInternalCache() {
4977
+ return structuredClone(this.cache);
4978
+ }
4979
+ getCacheKey(key) {
4783
4980
  return hashMessage(key.message, key.options);
4784
4981
  }
4785
- /**
4786
- * Get the fallback value for a cache miss
4787
- * @param key - The translation key
4788
- * @returns The fallback value
4789
- */
4790
- fallback(key) {
4791
- const translationPromise = this._enqueueTranslation(key);
4792
- if (this._queue.length >= this._batchConfig.maxBatchSize) this._flushNow();
4793
- else this._scheduleBatch();
4982
+ translate(key) {
4983
+ const translationPromise = this.enqueueTranslation(key);
4984
+ if (this.queue.length >= this.batchConfig.maxBatchSize) this.flushNow();
4985
+ else this.scheduleBatch();
4794
4986
  return translationPromise;
4795
4987
  }
4796
- /**
4797
- * Flush the queue now
4798
- */
4799
- _flushNow() {
4800
- if (this._batchTimer) {
4801
- clearTimeout(this._batchTimer);
4802
- this._batchTimer = null;
4988
+ flushNow() {
4989
+ if (this.batchTimer) {
4990
+ clearTimeout(this.batchTimer);
4991
+ this.batchTimer = null;
4803
4992
  }
4804
- this._drainQueue();
4805
- }
4806
- /**
4807
- * Schedule a batch of translations
4808
- */
4809
- _scheduleBatch() {
4810
- if (this._batchTimer) return;
4811
- this._batchTimer = setTimeout(() => {
4812
- this._batchTimer = null;
4813
- this._drainQueue();
4814
- }, this._batchConfig.batchInterval);
4815
- }
4816
- /**
4817
- * Drain the queue
4818
- */
4819
- _drainQueue() {
4820
- while (this._queue.length > 0 && this._activeRequests < this._batchConfig.maxConcurrentRequests) {
4821
- const batch = this._queue.splice(0, this._batchConfig.maxBatchSize);
4822
- this._sendBatchRequest(batch);
4993
+ this.drainQueue();
4994
+ }
4995
+ scheduleBatch() {
4996
+ if (this.batchTimer) return;
4997
+ this.batchTimer = setTimeout(() => {
4998
+ this.batchTimer = null;
4999
+ this.drainQueue();
5000
+ }, this.batchConfig.batchInterval);
5001
+ }
5002
+ drainQueue() {
5003
+ while (this.queue.length > 0 && this.activeRequests < this.batchConfig.maxConcurrentRequests) {
5004
+ const batch = this.queue.splice(0, this.batchConfig.maxBatchSize);
5005
+ this.sendBatchRequest(batch);
4823
5006
  }
4824
- if (this._queue.length > 0) this._scheduleBatch();
5007
+ if (this.queue.length > 0) this.scheduleBatch();
4825
5008
  }
4826
- /**
4827
- * Enqueue translation request and return a promise that resolves when the translation is ready
4828
- * @param {TranslationKey<TranslationValue>} key - The translation key
4829
- * @returns {Promise<TranslationValue>} The translation promise
4830
- */
4831
- _enqueueTranslation(key) {
4832
- const hash = this.genKey(key);
5009
+ enqueueTranslation(key) {
5010
+ const hash = this.getCacheKey(key);
4833
5011
  const options = key.options;
4834
5012
  const metadataOptions = options;
4835
5013
  return new Promise((resolve, reject) => {
4836
- this._queue.push({
5014
+ this.queue.push({
4837
5015
  key: hash,
4838
5016
  source: key.message,
4839
5017
  metadata: {
@@ -4848,38 +5026,28 @@ var TranslationsCache = class extends Cache {
4848
5026
  });
4849
5027
  });
4850
5028
  }
4851
- /**
4852
- * Send a batch request for translations
4853
- * @param {QueueEntry<TranslationValue>[]} batch - The batch of requests to send
4854
- */
4855
- async _sendBatchRequest(batch) {
4856
- this._activeRequests++;
5029
+ async sendBatchRequest(batch) {
5030
+ this.activeRequests++;
4857
5031
  const requests = convertBatchToTranslateManyParams(batch);
4858
- const response = await this._sendBatchRequestWithErrorHandling(batch, requests);
4859
- if (response) this._handleTranslationResponse(batch, response);
4860
- this._activeRequests--;
5032
+ const response = await this.sendBatchRequestWithErrorHandling(batch, requests);
5033
+ if (response) this.handleTranslationResponse(batch, response);
5034
+ this.activeRequests--;
4861
5035
  }
4862
- /**
4863
- * Send a translation request with error handling
4864
- */
4865
- async _sendBatchRequestWithErrorHandling(batch, requests) {
5036
+ async sendBatchRequestWithErrorHandling(batch, requests) {
4866
5037
  try {
4867
- return await this._translateMany(requests);
5038
+ return await this.translateMany(requests);
4868
5039
  } catch (error) {
4869
5040
  for (const entry of batch) entry.reject(error);
4870
5041
  return;
4871
5042
  }
4872
5043
  }
4873
- /**
4874
- * Handle a translation response
4875
- */
4876
- _handleTranslationResponse(batch, response) {
5044
+ handleTranslationResponse(batch, response) {
4877
5045
  for (const entry of batch) {
4878
5046
  const { key } = entry;
4879
5047
  const result = response[key];
4880
5048
  if (result && result.success) {
4881
5049
  const translation = result.translation;
4882
- this.setCache(key, translation);
5050
+ this.cache[key] = translation;
4883
5051
  entry.resolve(translation);
4884
5052
  } else entry.reject(result?.error);
4885
5053
  }
@@ -4897,415 +5065,87 @@ function convertBatchToTranslateManyParams(batch) {
4897
5065
  return acc;
4898
5066
  }, {});
4899
5067
  }
4900
- /**
4901
- * Default cache expiry time in milliseconds
4902
- */
4903
- const DEFAULT_CACHE_EXPIRY_TIME = 6e4;
4904
- /**
4905
- * Cache for looking up translations by locale
4906
- */
4907
- var LocalesCache = class extends Cache {
4908
- /**
4909
- * Constructor
4910
- * @param {Object} params - The parameters for the cache
4911
- * @param {Record<string, CacheEntry<TranslationValue>>} params.init - The initial cache
4912
- * @param {number | null} params.ttl - The time to live for cache entries
4913
- * @param {SafeTranslationsLoader<TranslationValue>} params.loadTranslations - The translation loader function
4914
- * @param {CreateTranslateMany} params.createTranslateMany - Factory function for creating a translate many function
4915
- */
4916
- constructor({ init = {}, ttl, batchConfig, loadTranslations, createTranslateMany, lifecycle: { onLocalesCacheHit: onHit, onLocalesCacheMiss: onMiss, onTranslationsCacheHit, onTranslationsCacheMiss } }) {
4917
- super(init, {
4918
- onHit,
4919
- onMiss
5068
+ var LocalesCache = class {
5069
+ constructor({ ttl, batchConfig, defaultLocale, dictionary = {}, loadTranslations, loadDictionary, createTranslateMany, translateDictionaryEntry, lifecycle }) {
5070
+ this.translations = new ResourceCache({
5071
+ ttl,
5072
+ load: async (locale) => new TranslationsCache({
5073
+ init: await loadTranslations(locale),
5074
+ lifecycle: createTranslationsCacheLifecycle(locale, lifecycle),
5075
+ translateMany: createTranslateMany(locale),
5076
+ batchConfig
5077
+ }),
5078
+ lifecycle: {
5079
+ onHit: lifecycle.onLocalesCacheHit,
5080
+ onMiss: lifecycle.onLocalesCacheMiss
5081
+ }
4920
5082
  });
4921
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
4922
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4923
- this._translationLoader = loadTranslations;
4924
- this._createTranslateMany = createTranslateMany;
4925
- this._batchConfig = batchConfig;
4926
- this._onTranslationsCacheHit = onTranslationsCacheHit;
4927
- this._onTranslationsCacheMiss = onTranslationsCacheMiss;
4928
- }
4929
- /**
4930
- * Get the translations for a given locale
4931
- * @param key - The locale
4932
- * @returns The translations
4933
- */
4934
- get(key) {
4935
- const entry = this.getCache(key);
4936
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
4937
- const value = entry.translationsCache;
4938
- if (value != null && this.onHit) this.onHit({
4939
- inputKey: key,
4940
- cacheKey: this.genKey(key),
4941
- cacheValue: entry,
4942
- outputValue: value
5083
+ this.dictionaries = new ResourceCache({
5084
+ ttl,
5085
+ load: async (locale) => createDictionaryCache({
5086
+ locale,
5087
+ dictionary: await loadDictionary(locale),
5088
+ translate: translateDictionaryEntry,
5089
+ lifecycle
5090
+ }),
5091
+ lifecycle: {
5092
+ onHit: lifecycle.onLocalesDictionaryCacheHit,
5093
+ onMiss: lifecycle.onLocalesDictionaryCacheMiss
5094
+ }
4943
5095
  });
4944
- return value;
5096
+ this.dictionaries.set(defaultLocale, createDictionaryCache({
5097
+ locale: defaultLocale,
5098
+ dictionary,
5099
+ translate: translateDictionaryEntry,
5100
+ lifecycle
5101
+ }), { expiresAt: -1 });
4945
5102
  }
4946
- /**
4947
- * Miss the cache
4948
- * @param key - The locale
4949
- * @returns The translations cache
4950
- */
4951
- async miss(key) {
4952
- const cacheValue = await this.missCache(key);
4953
- const value = cacheValue.translationsCache;
4954
- if (value != null && this.onMiss) this.onMiss({
4955
- inputKey: key,
4956
- cacheKey: this.genKey(key),
4957
- cacheValue,
4958
- outputValue: value
4959
- });
4960
- return value;
5103
+ getTranslations(locale) {
5104
+ return this.translations.get(locale);
4961
5105
  }
4962
- /**
4963
- * Generate the cache key for a given locale
4964
- * @param key - The locale
4965
- * @returns The cache key
4966
- *
4967
- * This is just an identity function, no transformation needed
4968
- */
4969
- genKey(key) {
4970
- return key;
5106
+ getOrLoadTranslations(locale) {
5107
+ return this.translations.getOrLoad(locale);
4971
5108
  }
4972
- /**
4973
- * Fallback for a cache miss
4974
- * @param locale - The locale
4975
- * @returns The cache entry
4976
- */
4977
- async fallback(locale) {
4978
- return {
4979
- translationsCache: new TranslationsCache({
4980
- init: await this._translationLoader(locale),
4981
- lifecycle: this._createTranslationsCacheLifecycle(locale),
4982
- translateMany: this._createTranslateMany(locale),
4983
- batchConfig: this._batchConfig
4984
- }),
4985
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
4986
- };
5109
+ getDictionary(locale) {
5110
+ return this.dictionaries.get(locale);
4987
5111
  }
4988
- /**
4989
- * Create the translations cache lifecycle
4990
- * @param locale - The locale
4991
- * @returns The translations cache lifecycle
4992
- */
4993
- _createTranslationsCacheLifecycle(locale) {
4994
- return {
4995
- onHit: this._onTranslationsCacheHit ? (params) => this._onTranslationsCacheHit({
4996
- locale,
4997
- ...params
4998
- }) : void 0,
4999
- onMiss: this._onTranslationsCacheMiss ? (params) => this._onTranslationsCacheMiss({
5000
- locale,
5001
- ...params
5002
- }) : void 0
5003
- };
5112
+ getOrLoadDictionary(locale) {
5113
+ return this.dictionaries.getOrLoad(locale);
5004
5114
  }
5005
5115
  };
5006
- function getDictionaryPath(id) {
5007
- if (!id) return [];
5008
- return id.split(".");
5009
- }
5010
- function isDictionaryValue(value) {
5011
- return typeof value === "object" && value != null && !Array.isArray(value);
5012
- }
5013
- function getDictionaryEntry(value) {
5014
- if (!isDictionaryLeafNode(value)) return;
5116
+ function createTranslationsCacheLifecycle(locale, lifecycle) {
5015
5117
  return {
5016
- entry: Array.isArray(value) ? value[0] : value,
5017
- options: Array.isArray(value) ? value[1] ?? {} : {}
5118
+ onHit: withLocale(locale, lifecycle.onTranslationsCacheHit),
5119
+ onMiss: withLocale(locale, lifecycle.onTranslationsCacheMiss)
5018
5120
  };
5019
5121
  }
5020
- function getDictionaryValue(value) {
5021
- if (Object.keys(value.options).length === 0) return value.entry;
5022
- return [value.entry, value.options];
5023
- }
5024
- function resolveDictionaryLookupOptions(options) {
5025
- const { $format, ...rest } = options;
5026
- return {
5027
- ...rest,
5028
- $format: isStringFormat($format) ? $format : "ICU",
5029
- ...rest.$context === void 0 && typeof rest.context === "string" && { $context: rest.context }
5030
- };
5031
- }
5032
- function isDictionaryLeafNode(value) {
5033
- if (typeof value === "string") return true;
5034
- if (!Array.isArray(value) || typeof value[0] !== "string") return false;
5035
- if (value.length === 1) return true;
5036
- return value.length === 2 && isDictionaryOptions(value[1]);
5037
- }
5038
- function isDictionaryOptions(value) {
5039
- if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
5040
- const options = value;
5041
- return (options.$context === void 0 || typeof options.$context === "string") && (options.$format === void 0 || isStringFormat(options.$format)) && (options.$maxChars === void 0 || typeof options.$maxChars === "number") && (options.context === void 0 || typeof options.context === "string");
5042
- }
5043
- function isStringFormat(value) {
5044
- return value === "ICU" || value === "I18NEXT" || value === "STRING";
5122
+ function createDictionaryCache({ locale, dictionary, translate, lifecycle }) {
5123
+ const { onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } = lifecycle;
5124
+ return new DictionaryCache({
5125
+ init: dictionary,
5126
+ runtimeTranslate: (key, sourceEntry) => translate(locale, key, sourceEntry),
5127
+ lifecycle: {
5128
+ onHit: withLocale(locale, onDictionaryCacheHit),
5129
+ onMiss: withLocale(locale, onDictionaryCacheMiss),
5130
+ onDictionaryObjectCacheHit: withLocale(locale, onDictionaryObjectCacheHit)
5131
+ }
5132
+ });
5045
5133
  }
5046
- function replaceDictionary(target, source) {
5047
- for (const key of Object.keys(target)) delete target[key];
5048
- Object.assign(target, source);
5134
+ function withLocale(locale, callback) {
5135
+ return callback ? (params) => callback({
5136
+ locale,
5137
+ ...params
5138
+ }) : void 0;
5049
5139
  }
5050
- var DictionarySourceNotFoundError = class extends Error {
5051
- constructor(id) {
5052
- super(`I18nManager: source dictionary entry ${id} is not defined`);
5053
- this.name = "DictionarySourceNotFoundError";
5054
- }
5055
- };
5056
- /**
5057
- * A cache for a single locale's dictionary
5058
- *
5059
- * Principles:
5060
- * - This class is language agnostic, and should never store the locale code as a parameter.
5061
- * Locale logic is handled at the LocalesDictionaryCache level. Use a callback function
5062
- * that has the locale parameter embedded if you wish to use the locale code.
5063
- */
5064
- var DictionaryCache = class extends Cache {
5065
- /**
5066
- * Constructor
5067
- * @param {Object} params - The parameters for the cache
5068
- * @param {Dictionary} params.init - The initial cache
5069
- */
5070
- constructor({ init, lifecycle, runtimeTranslate }) {
5071
- super(init, lifecycle);
5072
- this._runtimeTranslate = runtimeTranslate;
5073
- this.onHitObj = lifecycle?.onHitObj;
5074
- this.onMissObj = lifecycle?.onMissObj;
5075
- }
5076
- /**
5077
- * Get the dictionary value for a given key
5078
- * @param key - The dictionary key
5079
- * @returns The dictionary value
5080
- */
5081
- get(key) {
5082
- const value = this.getCache(key);
5083
- const entry = getDictionaryEntry(value);
5084
- if (entry === void 0) return;
5085
- if (this.onHit) this.onHit({
5086
- inputKey: key,
5087
- cacheKey: this.genKey(key),
5088
- cacheValue: value,
5089
- outputValue: entry
5090
- });
5091
- return entry;
5092
- }
5093
- set(key, value) {
5094
- const dictionaryValue = getDictionaryValue(value);
5095
- this.setCache(this.genKey(key), dictionaryValue);
5096
- }
5097
- getObj(key) {
5098
- const value = this.getCache(key);
5099
- if (value === void 0) return;
5100
- const outputValue = structuredClone(value);
5101
- if (this.onHitObj) this.onHitObj({
5102
- inputKey: key,
5103
- cacheKey: this.genKey(key),
5104
- cacheValue: value,
5105
- outputValue
5106
- });
5107
- return outputValue;
5108
- }
5109
- setObj(key, value) {
5110
- this.setCache(this.genKey(key), structuredClone(value));
5111
- }
5112
- async missObj(key, sourceObject) {
5113
- const sourceEntry = getDictionaryEntry(sourceObject);
5114
- if (sourceEntry !== void 0) return getDictionaryValue(await this.miss(key, sourceEntry));
5115
- if (!isDictionaryValue(sourceObject)) throw new DictionarySourceNotFoundError(key);
5116
- const translatedEntries = await Promise.all(Object.entries(sourceObject).map(async ([childKey, childSource]) => {
5117
- const childPath = key ? `${key}.${childKey}` : childKey;
5118
- return [childKey, await this.missObj(childPath, childSource)];
5119
- }));
5120
- const translatedObject = Object.fromEntries(translatedEntries);
5121
- this.setObj(key, translatedObject);
5122
- return translatedObject;
5123
- }
5124
- /**
5125
- * Miss the cache
5126
- * @param key - The dictionary key
5127
- * @returns The dictionary value
5128
- */
5129
- async miss(key, sourceEntry) {
5130
- const value = await this.missCache(key, sourceEntry);
5131
- const entry = getDictionaryEntry(value);
5132
- if (entry === void 0) throw new Error("DictionaryCache missCache did not return a DictionaryEntry");
5133
- if (this.onMiss) this.onMiss({
5134
- inputKey: key,
5135
- cacheKey: this.genKey(key),
5136
- cacheValue: value,
5137
- outputValue: entry
5138
- });
5139
- return entry;
5140
- }
5141
- /**
5142
- * Set the value for a key
5143
- */
5144
- setCache(cacheKey, value) {
5145
- const cache = this.getMutableCache();
5146
- const dictionaryPath = getDictionaryPath(cacheKey);
5147
- if (dictionaryPath.length === 0) {
5148
- if (isDictionaryValue(value)) replaceDictionary(cache, value);
5149
- return;
5150
- }
5151
- let current = cache;
5152
- for (const key of dictionaryPath.slice(0, -1)) {
5153
- const next = current[key];
5154
- if (!isDictionaryValue(next)) current[key] = {};
5155
- current = current[key];
5156
- }
5157
- current[dictionaryPath[dictionaryPath.length - 1]] = value;
5158
- }
5159
- /**
5160
- * Look up the key
5161
- */
5162
- getCache(key) {
5163
- const dictionaryPath = getDictionaryPath(this.genKey(key));
5164
- let current = this.getMutableCache();
5165
- if (dictionaryPath.length === 0) return current;
5166
- for (const pathSegment of dictionaryPath) {
5167
- if (!isDictionaryValue(current)) return;
5168
- current = current[pathSegment];
5169
- }
5170
- return current;
5171
- }
5172
- /**
5173
- * Generate a key for the cache
5174
- * @param key - The dictionary key
5175
- * @returns The key
5176
- */
5177
- genKey(key) {
5178
- return key;
5179
- }
5180
- /**
5181
- * Get the fallback value for a cache miss
5182
- * @param key - The dictionary key
5183
- * @returns The fallback value
5184
- *
5185
- * @throws {Error} - If the fallback is not implemented
5186
- */
5187
- fallback(key, sourceEntry) {
5188
- return this._runtimeTranslate(key, sourceEntry);
5189
- }
5190
- };
5191
- /**
5192
- * Cache for looking up dictionaries by locale
5193
- */
5194
- var LocalesDictionaryCache = class extends Cache {
5195
- /**
5196
- * Constructor
5197
- * @param {Object} params - The parameters for the cache
5198
- * @param {number | null} params.ttl - The time to live for cache entries
5199
- * @param {DictionaryLoader} params.loadDictionary - The dictionary loader function
5200
- */
5201
- constructor({ ttl, defaultLocale, dictionary = {}, loadDictionary, runtimeTranslate, lifecycle: { onLocalesDictionaryCacheHit: onHit, onLocalesDictionaryCacheMiss: onMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } }) {
5202
- super({}, {
5203
- onHit,
5204
- onMiss
5205
- });
5206
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
5207
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
5208
- this._dictionaryLoader = loadDictionary;
5209
- this._runtimeTranslate = runtimeTranslate;
5210
- this._onDictionaryCacheHit = onDictionaryCacheHit;
5211
- this._onDictionaryCacheMiss = onDictionaryCacheMiss;
5212
- this._onDictionaryObjectCacheHit = onDictionaryObjectCacheHit;
5213
- this.setCache(defaultLocale, {
5214
- dictionaryCache: new DictionaryCache({
5215
- init: dictionary,
5216
- runtimeTranslate: this._createDictionaryRuntimeTranslate(defaultLocale),
5217
- lifecycle: this._createDictionaryCacheLifecycle(defaultLocale)
5218
- }),
5219
- expiresAt: -1
5220
- });
5221
- }
5222
- /**
5223
- * Get the dictionary for a given locale
5224
- * @param key - The locale
5225
- * @returns The dictionary
5226
- */
5227
- get(key) {
5228
- const entry = this.getCache(key);
5229
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
5230
- const value = entry.dictionaryCache;
5231
- if (value != null && this.onHit) this.onHit({
5232
- inputKey: key,
5233
- cacheKey: this.genKey(key),
5234
- cacheValue: entry,
5235
- outputValue: value
5236
- });
5237
- return value;
5238
- }
5239
- /**
5240
- * Miss the cache
5241
- * @param key - The locale
5242
- * @returns The dictionary cache
5243
- */
5244
- async miss(key) {
5245
- const cacheValue = await this.missCache(key);
5246
- const value = cacheValue.dictionaryCache;
5247
- if (value != null && this.onMiss) this.onMiss({
5248
- inputKey: key,
5249
- cacheKey: this.genKey(key),
5250
- cacheValue,
5251
- outputValue: value
5252
- });
5253
- return value;
5254
- }
5255
- /**
5256
- * Generate the cache key for a given locale
5257
- * @param key - The locale
5258
- * @returns The cache key
5259
- *
5260
- * This is just an identity function, no transformation needed
5261
- */
5262
- genKey(key) {
5263
- return key;
5264
- }
5265
- /**
5266
- * Fallback for a cache miss
5267
- * @param locale - The locale
5268
- * @returns The cache entry
5269
- */
5270
- async fallback(locale) {
5271
- return {
5272
- dictionaryCache: new DictionaryCache({
5273
- init: await this._dictionaryLoader(locale),
5274
- runtimeTranslate: this._createDictionaryRuntimeTranslate(locale),
5275
- lifecycle: this._createDictionaryCacheLifecycle(locale)
5276
- }),
5277
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
5278
- };
5279
- }
5280
- /**
5281
- * Create the dictionary cache lifecycle
5282
- * @param locale - The locale
5283
- * @returns The dictionary cache lifecycle
5284
- */
5285
- _createDictionaryCacheLifecycle(locale) {
5286
- return {
5287
- onHit: this._onDictionaryCacheHit ? (params) => this._onDictionaryCacheHit({
5288
- locale,
5289
- ...params
5290
- }) : void 0,
5291
- onMiss: this._onDictionaryCacheMiss ? (params) => this._onDictionaryCacheMiss({
5292
- locale,
5293
- ...params
5294
- }) : void 0,
5295
- onHitObj: this._onDictionaryObjectCacheHit ? (params) => this._onDictionaryObjectCacheHit({
5296
- locale,
5297
- ...params
5298
- }) : void 0
5299
- };
5300
- }
5301
- _createDictionaryRuntimeTranslate(locale) {
5302
- return (key, sourceEntry) => this._runtimeTranslate(locale, key, sourceEntry);
5303
- }
5304
- };
5140
+ const LOCALES_CACHE_HIT_EVENT_NAME = "locales-cache-hit";
5305
5141
  const LOCALES_CACHE_MISS_EVENT_NAME = "locales-cache-miss";
5142
+ const TRANSLATIONS_CACHE_HIT_EVENT_NAME = "translations-cache-hit";
5306
5143
  const TRANSLATIONS_CACHE_MISS_EVENT_NAME = "translations-cache-miss";
5144
+ const LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME = "locales-dictionary-cache-hit";
5307
5145
  const LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME = "locales-dictionary-cache-miss";
5146
+ const DICTIONARY_CACHE_HIT_EVENT_NAME = "dictionary-cache-hit";
5308
5147
  const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5148
+ const DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME = "dictionary-object-cache-hit";
5309
5149
  /**
5310
5150
  * Maps consumer-facing lifecycle callbacks to internal locales cache lifecycle callbacks.
5311
5151
  * The consumer API exposes simplified params (locale, hash, value) while the internal
@@ -5313,22 +5153,24 @@ const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5313
5153
  *
5314
5154
  * @deprecated - move to subscription api instead
5315
5155
  */
5316
- function createLifecycleCallbacks(emit) {
5156
+ function createLifecycleCallbacks(emit, hasListeners = () => true) {
5317
5157
  return {
5318
5158
  onLocalesCacheHit: (params) => {
5319
- emit("locales-cache-hit", {
5159
+ if (!hasListeners("locales-cache-hit")) return;
5160
+ emit(LOCALES_CACHE_HIT_EVENT_NAME, {
5320
5161
  locale: params.inputKey,
5321
5162
  translations: params.outputValue.getInternalCache()
5322
5163
  });
5323
5164
  },
5324
5165
  onLocalesCacheMiss: (params) => {
5166
+ if (!hasListeners("locales-cache-miss")) return;
5325
5167
  emit(LOCALES_CACHE_MISS_EVENT_NAME, {
5326
5168
  locale: params.inputKey,
5327
5169
  translations: params.outputValue.getInternalCache()
5328
5170
  });
5329
5171
  },
5330
5172
  onTranslationsCacheHit: (params) => {
5331
- emit("translations-cache-hit", {
5173
+ emit(TRANSLATIONS_CACHE_HIT_EVENT_NAME, {
5332
5174
  locale: params.locale,
5333
5175
  hash: params.cacheKey,
5334
5176
  translation: params.outputValue
@@ -5342,19 +5184,21 @@ function createLifecycleCallbacks(emit) {
5342
5184
  });
5343
5185
  },
5344
5186
  onLocalesDictionaryCacheHit: (params) => {
5345
- emit("locales-dictionary-cache-hit", {
5187
+ if (!hasListeners("locales-dictionary-cache-hit")) return;
5188
+ emit(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, {
5346
5189
  locale: params.inputKey,
5347
5190
  dictionary: params.outputValue.getInternalCache()
5348
5191
  });
5349
5192
  },
5350
5193
  onLocalesDictionaryCacheMiss: (params) => {
5194
+ if (!hasListeners("locales-dictionary-cache-miss")) return;
5351
5195
  emit(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, {
5352
5196
  locale: params.inputKey,
5353
5197
  dictionary: params.outputValue.getInternalCache()
5354
5198
  });
5355
5199
  },
5356
5200
  onDictionaryCacheHit: (params) => {
5357
- emit("dictionary-cache-hit", {
5201
+ emit(DICTIONARY_CACHE_HIT_EVENT_NAME, {
5358
5202
  locale: params.locale,
5359
5203
  id: params.cacheKey,
5360
5204
  dictionaryEntry: params.outputValue
@@ -5368,7 +5212,7 @@ function createLifecycleCallbacks(emit) {
5368
5212
  });
5369
5213
  },
5370
5214
  onDictionaryObjectCacheHit: (params) => {
5371
- emit("dictionary-object-cache-hit", {
5215
+ emit(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, {
5372
5216
  locale: params.locale,
5373
5217
  id: params.cacheKey,
5374
5218
  dictionaryValue: params.outputValue
@@ -5403,6 +5247,9 @@ var EventEmitter = class {
5403
5247
  emit(eventName, event) {
5404
5248
  this.listeners[eventName]?.forEach((subscriber) => subscriber(event));
5405
5249
  }
5250
+ hasListeners(eventName) {
5251
+ return (this.listeners[eventName]?.size ?? 0) > 0;
5252
+ }
5406
5253
  };
5407
5254
  /**
5408
5255
  * Subscribes to the lifecycle callbacks and emits the events to the event emitter
@@ -5412,7 +5259,7 @@ var EventEmitter = class {
5412
5259
  * and is only used internally
5413
5260
  */
5414
5261
  function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, onTranslationsCacheHit, onTranslationsCacheMiss, onLocalesDictionaryCacheHit, onLocalesDictionaryCacheMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit }, subscribe) {
5415
- if (onLocalesCacheHit) subscribe("locales-cache-hit", (event) => {
5262
+ if (onLocalesCacheHit) subscribe(LOCALES_CACHE_HIT_EVENT_NAME, (event) => {
5416
5263
  onLocalesCacheHit({
5417
5264
  ...event,
5418
5265
  value: event.translations
@@ -5424,7 +5271,7 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5424
5271
  value: event.translations
5425
5272
  });
5426
5273
  });
5427
- if (onTranslationsCacheHit) subscribe("translations-cache-hit", (event) => {
5274
+ if (onTranslationsCacheHit) subscribe(TRANSLATIONS_CACHE_HIT_EVENT_NAME, (event) => {
5428
5275
  onTranslationsCacheHit({
5429
5276
  ...event,
5430
5277
  value: event.translation
@@ -5436,19 +5283,19 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5436
5283
  value: event.translation
5437
5284
  });
5438
5285
  });
5439
- if (onLocalesDictionaryCacheHit) subscribe("locales-dictionary-cache-hit", (event) => {
5286
+ if (onLocalesDictionaryCacheHit) subscribe(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5440
5287
  onLocalesDictionaryCacheHit(event);
5441
5288
  });
5442
5289
  if (onLocalesDictionaryCacheMiss) subscribe(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5443
5290
  onLocalesDictionaryCacheMiss(event);
5444
5291
  });
5445
- if (onDictionaryCacheHit) subscribe("dictionary-cache-hit", (event) => {
5292
+ if (onDictionaryCacheHit) subscribe(DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5446
5293
  onDictionaryCacheHit(event);
5447
5294
  });
5448
5295
  if (onDictionaryCacheMiss) subscribe(DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5449
5296
  onDictionaryCacheMiss(event);
5450
5297
  });
5451
- if (onDictionaryObjectCacheHit) subscribe("dictionary-object-cache-hit", (event) => {
5298
+ if (onDictionaryObjectCacheHit) subscribe(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, (event) => {
5452
5299
  onDictionaryObjectCacheHit(event);
5453
5300
  });
5454
5301
  }
@@ -5479,26 +5326,32 @@ var I18nManager = class extends EventEmitter {
5479
5326
  locales: this.config.locales,
5480
5327
  customMapping: this.config.customMapping
5481
5328
  });
5482
- const loadTranslations = createTranslationLoader(params);
5483
- const loadDictionary = createDictionaryLoader(params);
5329
+ const loadTranslations = routeCreateTranslationLoader({
5330
+ loadTranslations: params.loadTranslations,
5331
+ type: getLoadTranslationsType(params),
5332
+ remoteTranslationLoaderParams: {
5333
+ cacheUrl: params.cacheUrl,
5334
+ projectId: params.projectId,
5335
+ _versionId: params._versionId,
5336
+ _branchId: params._branchId,
5337
+ customMapping: params.customMapping
5338
+ }
5339
+ });
5340
+ const loadDictionary = params.loadDictionary ?? (() => Promise.resolve({}));
5484
5341
  const runtimeTranslationTimeout = this.config.runtimeTranslation?.timeout ?? DEFAULT_TRANSLATION_TIMEOUT;
5485
5342
  const runtimeTranslationMetadata = this.config.runtimeTranslation?.metadata ?? {};
5486
5343
  const createTranslateMany = createTranslateManyFactory(this.getGTClassClean(), runtimeTranslationTimeout, runtimeTranslationMetadata);
5487
5344
  subscribeLifecycleCallbacks(params.lifecycle ?? {}, (...args) => this.subscribe(...args));
5488
- const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args));
5345
+ const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args), (eventName) => this.hasListeners(eventName));
5489
5346
  this.localesCache = new LocalesCache({
5490
- loadTranslations,
5491
- createTranslateMany,
5492
- lifecycle,
5493
- ttl: this.config.cacheExpiryTime,
5494
- batchConfig: this.config.batchConfig
5495
- });
5496
- this.localesDictionaryCache = new LocalesDictionaryCache({
5497
5347
  defaultLocale: this.config.defaultLocale,
5498
5348
  dictionary: params.dictionary,
5349
+ loadTranslations,
5499
5350
  loadDictionary,
5500
- runtimeTranslate: (locale, id, sourceEntry) => this.dictionaryRuntimeTranslate(locale, id, sourceEntry),
5351
+ createTranslateMany,
5352
+ translateDictionaryEntry: (locale, id, sourceEntry) => this.translateDictionaryEntry(locale, id, sourceEntry),
5501
5353
  ttl: this.config.cacheExpiryTime,
5354
+ batchConfig: this.config.batchConfig,
5502
5355
  lifecycle
5503
5356
  });
5504
5357
  }
@@ -5570,9 +5423,7 @@ var I18nManager = class extends EventEmitter {
5570
5423
  try {
5571
5424
  const translationLocale = this.resolveCacheLocale(locale);
5572
5425
  if (!translationLocale) return {};
5573
- let txCache = this.localesCache.get(translationLocale);
5574
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5575
- return txCache.getInternalCache();
5426
+ return (await this.localesCache.getOrLoadTranslations(translationLocale)).getInternalCache();
5576
5427
  } catch (error) {
5577
5428
  this.handleError(error);
5578
5429
  return {};
@@ -5585,10 +5436,8 @@ var I18nManager = class extends EventEmitter {
5585
5436
  async loadDictionary(locale) {
5586
5437
  try {
5587
5438
  const dictionaryLocale = this.resolveCacheLocale(locale);
5588
- if (!dictionaryLocale) return this.localesDictionaryCache.get(this.config.defaultLocale)?.getInternalCache() ?? {};
5589
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5590
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5591
- return dictionaryCache.getInternalCache();
5439
+ if (!dictionaryLocale) return this.getDefaultDictionaryCache()?.getInternalCache() ?? {};
5440
+ return (await this.localesCache.getOrLoadDictionary(dictionaryLocale)).getInternalCache();
5592
5441
  } catch (error) {
5593
5442
  this.handleError(error);
5594
5443
  return {};
@@ -5599,8 +5448,8 @@ var I18nManager = class extends EventEmitter {
5599
5448
  */
5600
5449
  lookupDictionary(locale, id) {
5601
5450
  try {
5602
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5603
- return this.localesDictionaryCache.get(dictionaryLocale)?.get(id);
5451
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5452
+ return this.localesCache.getDictionary(dictionaryLocale)?.getEntry(id);
5604
5453
  } catch (error) {
5605
5454
  this.handleError(error);
5606
5455
  return;
@@ -5611,8 +5460,8 @@ var I18nManager = class extends EventEmitter {
5611
5460
  */
5612
5461
  lookupDictionaryObj(locale, id) {
5613
5462
  try {
5614
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5615
- return this.localesDictionaryCache.get(dictionaryLocale)?.getObj(id);
5463
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5464
+ return this.localesCache.getDictionary(dictionaryLocale)?.getValue(id);
5616
5465
  } catch (error) {
5617
5466
  this.handleError(error);
5618
5467
  return;
@@ -5625,19 +5474,10 @@ var I18nManager = class extends EventEmitter {
5625
5474
  async lookupDictionaryWithFallback(locale, id) {
5626
5475
  try {
5627
5476
  const dictionaryLocale = this.resolveCacheLocale(locale);
5628
- if (!dictionaryLocale) {
5629
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5630
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5631
- return sourceEntry;
5632
- }
5633
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5634
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5635
- let dictionaryEntry = dictionaryCache.get(id);
5636
- if (dictionaryEntry === void 0) {
5637
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5638
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5639
- dictionaryEntry = await dictionaryCache.miss(id, sourceEntry);
5640
- }
5477
+ if (!dictionaryLocale) return this.getSourceDictionaryEntry(id);
5478
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5479
+ let dictionaryEntry = dictionaryCache.getEntry(id);
5480
+ if (dictionaryEntry === void 0) dictionaryEntry = await dictionaryCache.materializeEntry(id, this.getSourceDictionaryEntry(id));
5641
5481
  return dictionaryEntry;
5642
5482
  } catch (error) {
5643
5483
  this.handleError(error);
@@ -5651,25 +5491,41 @@ var I18nManager = class extends EventEmitter {
5651
5491
  async lookupDictionaryObjWithFallback(locale, id) {
5652
5492
  try {
5653
5493
  const dictionaryLocale = this.resolveCacheLocale(locale);
5654
- if (!dictionaryLocale) {
5655
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5656
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5657
- return sourceObject;
5494
+ if (!dictionaryLocale) return this.getSourceDictionaryObject(id);
5495
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5496
+ const targetObject = dictionaryCache.getValue(id);
5497
+ const sourceObject = this.getSourceDictionaryObject(id, { throwOnMissing: false });
5498
+ if (sourceObject === void 0) {
5499
+ if (targetObject !== void 0) return targetObject;
5500
+ throw new DictionarySourceNotFoundError(id);
5658
5501
  }
5659
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5660
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5661
- let dictionaryObject = dictionaryCache.getObj(id);
5662
- if (dictionaryObject === void 0) {
5663
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5664
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5665
- dictionaryObject = await dictionaryCache.missObj(id, sourceObject);
5666
- }
5667
- return dictionaryObject;
5502
+ return await dictionaryCache.materializeValue(id, sourceObject, targetObject);
5668
5503
  } catch (error) {
5669
5504
  this.handleError(error);
5670
5505
  return;
5671
5506
  }
5672
5507
  }
5508
+ async translateDictionaryEntry(locale, id, sourceEntry) {
5509
+ const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5510
+ if (typeof translation !== "string") throw new Error(`Dictionary entry "${id}" could not be translated into a string. Check the source entry and translation loader output.`);
5511
+ return translation;
5512
+ }
5513
+ getSourceDictionaryEntry(id) {
5514
+ const sourceEntry = this.getDefaultDictionaryCache()?.getEntry(id);
5515
+ if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5516
+ return sourceEntry;
5517
+ }
5518
+ getSourceDictionaryObject(id, { throwOnMissing = true } = {}) {
5519
+ const sourceObject = this.getDefaultDictionaryCache()?.getValue(id);
5520
+ if (sourceObject === void 0 && throwOnMissing) throw new DictionarySourceNotFoundError(id);
5521
+ return sourceObject;
5522
+ }
5523
+ getDefaultDictionaryCache() {
5524
+ return this.localesCache.getDictionary(this.config.defaultLocale);
5525
+ }
5526
+ resolveDictionaryCacheLocale(locale) {
5527
+ return this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5528
+ }
5673
5529
  /**
5674
5530
  * Just lookup a translation
5675
5531
  */
@@ -5677,7 +5533,7 @@ var I18nManager = class extends EventEmitter {
5677
5533
  try {
5678
5534
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5679
5535
  if (!translationLocale) return message;
5680
- const txCache = this.localesCache.get(translationLocale);
5536
+ const txCache = this.localesCache.getTranslations(translationLocale);
5681
5537
  if (!txCache) return void 0;
5682
5538
  return txCache.get({
5683
5539
  message,
@@ -5715,9 +5571,7 @@ var I18nManager = class extends EventEmitter {
5715
5571
  if (!translationLocale) return (message) => message;
5716
5572
  const resolvedPrefetchEntries = resolvePrefetchEntriesByLocale(prefetchEntries, translationLocale, (entryLocale) => this.resolveCacheLocale(entryLocale) ?? this.resolveLocale(entryLocale));
5717
5573
  if (resolvedPrefetchEntries.length !== prefetchEntries.length) logger_default.warn(`I18nManager: getLookupTranslation(): prefetchEntries must all be the same locale, ignoring all entries that are not for ${translationLocale}`);
5718
- let txCache = this.localesCache.get(translationLocale);
5719
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5720
- if (!txCache) return () => void 0;
5574
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5721
5575
  await Promise.all(resolvedPrefetchEntries.filter((entry) => txCache.get(entry) == null).map((entry) => txCache.miss(entry)));
5722
5576
  return (message, options = {}) => {
5723
5577
  return txCache.get({
@@ -5819,8 +5673,7 @@ var I18nManager = class extends EventEmitter {
5819
5673
  async lookupTranslationWithFallbackResolved(locale, message, options) {
5820
5674
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5821
5675
  if (!translationLocale) return message;
5822
- let txCache = this.localesCache.get(translationLocale);
5823
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5676
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5824
5677
  let translation = txCache.get({
5825
5678
  message,
5826
5679
  options: lookupOptions
@@ -5832,14 +5685,6 @@ var I18nManager = class extends EventEmitter {
5832
5685
  return translation;
5833
5686
  }
5834
5687
  /**
5835
- * Runtime lookup function for dictionaries
5836
- */
5837
- async dictionaryRuntimeTranslate(locale, id, sourceEntry) {
5838
- const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5839
- if (typeof translation !== "string") throw new Error(`Dictionary entry "${id}" could not be translated into a string. Check the source entry and translation loader output.`);
5840
- return translation;
5841
- }
5842
- /**
5843
5688
  * A helper function to create a gt class that is locale agnostic
5844
5689
  * This is helpful for when our getLocale function is bound to a
5845
5690
  * specific context
@@ -5936,28 +5781,6 @@ function resolvePrefetchEntriesByLocale(prefetchEntries, locale, resolveLocale)
5936
5781
  }
5937
5782
  });
5938
5783
  }
5939
- /**
5940
- * Helper function for creating a translation loader
5941
- */
5942
- function createTranslationLoader(params) {
5943
- return routeCreateTranslationLoader({
5944
- loadTranslations: params.loadTranslations,
5945
- type: getLoadTranslationsType(params),
5946
- remoteTranslationLoaderParams: {
5947
- cacheUrl: params.cacheUrl,
5948
- projectId: params.projectId,
5949
- _versionId: params._versionId,
5950
- _branchId: params._branchId,
5951
- customMapping: params.customMapping
5952
- }
5953
- });
5954
- }
5955
- /**
5956
- * Helper function for creating a dictionary loader
5957
- */
5958
- function createDictionaryLoader(params) {
5959
- return params.loadDictionary ?? (() => Promise.resolve({}));
5960
- }
5961
5784
  let i18nManager = void 0;
5962
5785
  let fallbackDefaultLocale = "en";
5963
5786
  let conditionStore = { getLocale: () => fallbackDefaultLocale };