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/browser.cjs CHANGED
@@ -4402,7 +4402,7 @@ function sanitizeJsxChildren(childrenAsObjects) {
4402
4402
  return Array.isArray(childrenAsObjects) ? childrenAsObjects.map(sanitizeChild) : sanitizeChild(childrenAsObjects);
4403
4403
  }
4404
4404
  //#endregion
4405
- //#region ../i18n/dist/versionId-BkJZGHXr.mjs
4405
+ //#region ../i18n/dist/versionId-BTjLA0FZ.mjs
4406
4406
  /**
4407
4407
  * Throw errors if there are any errors and log warnings if there are any warnings
4408
4408
  * @param {ValidationResult[]} results - The results to print
@@ -4668,83 +4668,269 @@ function routeCreateTranslationLoader({ type, remoteTranslationLoaderParams, loa
4668
4668
  case "disabled": return createFallbackTranslationLoader();
4669
4669
  }
4670
4670
  }
4671
- function isPlainObject(value) {
4672
- if (value == null || typeof value !== "object") return false;
4673
- const prototype = Object.getPrototypeOf(value);
4674
- return prototype === Object.prototype || prototype === null;
4671
+ function getDictionaryPath(id) {
4672
+ const path = id ? id.split(".") : [];
4673
+ for (const segment of path) assertSafeDictionaryPathSegment(segment, id);
4674
+ return path;
4675
4675
  }
4676
- function copyCacheValue(value) {
4677
- if (Array.isArray(value)) return [...value];
4678
- if (isPlainObject(value)) return { ...value };
4679
- return value;
4676
+ function assertSafeDictionaryPathSegment(segment, path) {
4677
+ if (segment === "__proto__" || segment === "constructor" || segment === "prototype") throw new Error(`Dictionary path "${path}" contains an unsafe segment`);
4678
+ }
4679
+ function isDictionaryObject(value) {
4680
+ return typeof value === "object" && value != null && !Array.isArray(value);
4681
+ }
4682
+ function cloneDictionaryValue(value) {
4683
+ if (value === void 0 || typeof value === "string") return value;
4684
+ return structuredClone(value);
4685
+ }
4686
+ function getDictionaryValueAtPath(dictionary, path) {
4687
+ let current = dictionary;
4688
+ for (const segment of getDictionaryPath(path)) {
4689
+ if (!isDictionaryObject(current)) return;
4690
+ current = current[segment];
4691
+ }
4692
+ return current;
4693
+ }
4694
+ function setDictionaryValueAtPath(dictionary, path, value) {
4695
+ const segments = getDictionaryPath(path);
4696
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4697
+ if (segments.length === 0) {
4698
+ if (isDictionaryObject(value)) replaceDictionary(dictionary, value);
4699
+ return;
4700
+ }
4701
+ let current = dictionary;
4702
+ for (const segment of segments.slice(0, -1)) {
4703
+ const next = current[segment];
4704
+ if (!isDictionaryObject(next)) current[segment] = {};
4705
+ current = current[segment];
4706
+ }
4707
+ const leafSegment = segments[segments.length - 1];
4708
+ current[leafSegment] = value;
4709
+ }
4710
+ function getDictionaryEntry(value) {
4711
+ if (!isDictionaryLeafNode(value)) return;
4712
+ return {
4713
+ entry: Array.isArray(value) ? value[0] : value,
4714
+ options: Array.isArray(value) ? value[1] ?? {} : {}
4715
+ };
4716
+ }
4717
+ function getDictionaryValue(value) {
4718
+ if (Object.keys(value.options).length === 0) return value.entry;
4719
+ return [value.entry, value.options];
4720
+ }
4721
+ function resolveDictionaryLookupOptions(options) {
4722
+ const { $format, context, ...rest } = options;
4723
+ return {
4724
+ ...rest,
4725
+ $format: isStringFormat($format) ? $format : "ICU",
4726
+ ...rest.$context === void 0 && typeof context === "string" && { $context: context }
4727
+ };
4728
+ }
4729
+ function isDictionaryLeafNode(value) {
4730
+ if (typeof value === "string") return true;
4731
+ if (!Array.isArray(value) || typeof value[0] !== "string") return false;
4732
+ if (value.length === 1) return true;
4733
+ return value.length === 2 && isDictionaryOptions(value[1]);
4734
+ }
4735
+ function isDictionaryOptions(value) {
4736
+ if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
4737
+ const options = value;
4738
+ 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");
4739
+ }
4740
+ function isStringFormat(value) {
4741
+ return value === "ICU" || value === "I18NEXT" || value === "STRING";
4742
+ }
4743
+ function replaceDictionary(target, source) {
4744
+ for (const key of Object.keys(target)) delete target[key];
4745
+ for (const key of Object.keys(source)) target[key] = source[key];
4746
+ }
4747
+ function assertSafeDictionaryObject(dictionary, parentPath = "") {
4748
+ for (const [key, value] of Object.entries(dictionary)) {
4749
+ const path = parentPath ? `${parentPath}.${key}` : key;
4750
+ assertSafeDictionaryPathSegment(key, path);
4751
+ if (isDictionaryObject(value)) assertSafeDictionaryObject(value, path);
4752
+ }
4680
4753
  }
4754
+ var DictionarySourceNotFoundError = class extends Error {
4755
+ constructor(id) {
4756
+ super(`I18nManager: source dictionary entry ${id} is not defined`);
4757
+ this.name = "DictionarySourceNotFoundError";
4758
+ }
4759
+ };
4681
4760
  /**
4682
- * Cache class
4683
- * This is designed in such a way that it is the responsibility of the client
4684
- * to invoke the cache miss method when a cache miss occurs.
4685
- *
4686
- * TODO: maybe add "OutputValue" as a reflection of "InputKey"
4687
- */
4688
- var Cache = class {
4689
- /**
4690
- * Constructor
4691
- * @param {Object} params - The parameters for the cache
4692
- * @param {Record<CacheKey, CacheValue>} params.init - The initial cache
4693
- * @param {CacheLifecycle} [lifecycle] - Optional lifecycle callbacks
4694
- */
4695
- constructor(init, lifecycle) {
4696
- this.cache = {};
4697
- this.fallbackPromises = {};
4761
+ * Builds the dictionary value for a requested path by combining existing target
4762
+ * translations with runtime translations of any source leaves that are missing.
4763
+ */
4764
+ async function materializeDictionaryValue({ key, sourceValue, targetValue, translateEntry }) {
4765
+ if (getDictionaryEntry(targetValue) !== void 0) return cloneDictionaryValue(targetValue);
4766
+ if (isDictionaryObject(targetValue) && !isDictionaryObject(sourceValue)) return cloneDictionaryValue(targetValue);
4767
+ const sourceEntry = getDictionaryEntry(sourceValue);
4768
+ if (sourceEntry !== void 0) return await translateEntry(key, sourceEntry);
4769
+ if (!isDictionaryObject(sourceValue)) throw new DictionarySourceNotFoundError(key);
4770
+ const targetDictionary = isDictionaryObject(targetValue) ? targetValue : {};
4771
+ const keys = new Set([...Object.keys(sourceValue), ...Object.keys(targetDictionary)]);
4772
+ const entries = await Promise.all(Array.from(keys).map(async (childKey) => {
4773
+ const childPath = key ? `${key}.${childKey}` : childKey;
4774
+ assertSafeDictionaryPathSegment(childKey, childPath);
4775
+ const childSource = sourceValue[childKey];
4776
+ if (childSource === void 0) return [childKey, cloneDictionaryValue(targetDictionary[childKey])];
4777
+ return [childKey, await materializeDictionaryValue({
4778
+ key: childPath,
4779
+ sourceValue: childSource,
4780
+ targetValue: targetDictionary[childKey],
4781
+ translateEntry
4782
+ })];
4783
+ }));
4784
+ return Object.fromEntries(entries);
4785
+ }
4786
+ function cloneDictionaryEntry(entry) {
4787
+ return {
4788
+ entry: entry.entry,
4789
+ options: structuredClone(entry.options)
4790
+ };
4791
+ }
4792
+ var DictionaryCache = class {
4793
+ constructor({ init, lifecycle = {}, runtimeTranslate }) {
4794
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4795
+ this.pendingMaterializations = /* @__PURE__ */ new Map();
4698
4796
  this.cache = structuredClone(init);
4699
- this.onHit = lifecycle?.onHit;
4700
- this.onMiss = lifecycle?.onMiss;
4797
+ this.runtimeTranslate = runtimeTranslate;
4798
+ this.lifecycle = lifecycle;
4701
4799
  }
4702
- /**
4703
- * Set the value for a key
4704
- */
4705
- setCache(cacheKey, value) {
4706
- this.cache[cacheKey] = value;
4800
+ getEntry(key) {
4801
+ const value = getDictionaryValueAtPath(this.cache, key);
4802
+ const entry = getDictionaryEntry(value);
4803
+ if (entry === void 0) return;
4804
+ const outputEntry = cloneDictionaryEntry(entry);
4805
+ this.lifecycle.onHit?.({
4806
+ inputKey: key,
4807
+ cacheKey: key,
4808
+ cacheValue: value,
4809
+ outputValue: outputEntry
4810
+ });
4811
+ return outputEntry;
4707
4812
  }
4708
- /**
4709
- * Look up the key
4710
- */
4711
- getCache(key) {
4712
- const cacheKey = this.genKey(key);
4713
- return this.cache[cacheKey];
4813
+ getValue(key) {
4814
+ const value = getDictionaryValueAtPath(this.cache, key);
4815
+ if (value === void 0) return;
4816
+ const outputValue = cloneDictionaryValue(value);
4817
+ this.lifecycle.onDictionaryObjectCacheHit?.({
4818
+ inputKey: key,
4819
+ cacheKey: key,
4820
+ cacheValue: value,
4821
+ outputValue
4822
+ });
4823
+ return outputValue;
4824
+ }
4825
+ setValue(key, value) {
4826
+ setDictionaryValueAtPath(this.cache, key, cloneDictionaryValue(value));
4714
4827
  }
4715
- /**
4716
- * Get the internal cache
4717
- * @returns The internal cache
4718
- *
4719
- * @internal - used by gt-tanstack-start
4720
- */
4721
4828
  getInternalCache() {
4722
- return Object.fromEntries(Object.entries(this.cache).map(([key, value]) => [key, copyCacheValue(value)]));
4829
+ return cloneDictionaryValue(this.cache);
4830
+ }
4831
+ async materializeValue(key, sourceValue, targetValue = getDictionaryValueAtPath(this.cache, key)) {
4832
+ let materializationPromise = this.pendingMaterializations.get(key);
4833
+ if (!materializationPromise) {
4834
+ materializationPromise = materializeDictionaryValue({
4835
+ key,
4836
+ sourceValue,
4837
+ targetValue,
4838
+ translateEntry: async (entryKey, sourceEntry) => getDictionaryValue(await this.materializeEntry(entryKey, sourceEntry))
4839
+ }).then((value) => {
4840
+ this.setValue(key, value);
4841
+ return value;
4842
+ });
4843
+ this.pendingMaterializations.set(key, materializationPromise);
4844
+ }
4845
+ try {
4846
+ return await materializationPromise;
4847
+ } finally {
4848
+ this.pendingMaterializations.delete(key);
4849
+ }
4723
4850
  }
4724
- /**
4725
- * Get the mutable cache for subclasses that need custom read/write behavior.
4726
- */
4727
- getMutableCache() {
4728
- return this.cache;
4851
+ async materializeEntry(key, sourceEntry) {
4852
+ let translationPromise = this.pendingTranslations.get(key);
4853
+ if (!translationPromise) {
4854
+ translationPromise = this.runtimeTranslate(key, sourceEntry).then((value) => {
4855
+ setDictionaryValueAtPath(this.cache, key, value);
4856
+ const entry = getDictionaryEntry(value);
4857
+ if (entry === void 0) throw new Error("DictionaryCache materializeEntry did not return a DictionaryEntry");
4858
+ this.lifecycle.onMiss?.({
4859
+ inputKey: key,
4860
+ cacheKey: key,
4861
+ cacheValue: value,
4862
+ outputValue: cloneDictionaryEntry(entry)
4863
+ });
4864
+ return cloneDictionaryEntry(entry);
4865
+ });
4866
+ this.pendingTranslations.set(key, translationPromise);
4867
+ }
4868
+ try {
4869
+ return cloneDictionaryEntry(await translationPromise);
4870
+ } finally {
4871
+ this.pendingTranslations.delete(key);
4872
+ }
4729
4873
  }
4730
- /**
4731
- * Fallback to the value from the fallback function on a cache miss
4732
- * @important assumes that the fallback error handling done upstream
4733
- */
4734
- async missCache(...args) {
4735
- const key = args[0];
4736
- const cacheKey = this.genKey(key);
4737
- if (this.fallbackPromises[cacheKey] !== void 0) return await this.fallbackPromises[cacheKey];
4738
- const fallbackPromise = this.fallback(...args);
4739
- this.fallbackPromises[cacheKey] = fallbackPromise;
4874
+ };
4875
+ var ResourceCache = class {
4876
+ constructor({ load, lifecycle = {}, ttl }) {
4877
+ this.cache = /* @__PURE__ */ new Map();
4878
+ this.pendingLoads = /* @__PURE__ */ new Map();
4879
+ this.loadResource = load;
4880
+ this.lifecycle = lifecycle;
4881
+ this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4882
+ }
4883
+ get(key) {
4884
+ const entry = this.cache.get(key);
4885
+ if (!entry || this.isExpired(entry)) return;
4886
+ this.lifecycle.onHit?.({
4887
+ inputKey: key,
4888
+ cacheKey: key,
4889
+ cacheValue: entry,
4890
+ outputValue: entry.value
4891
+ });
4892
+ return entry.value;
4893
+ }
4894
+ set(key, value, { expiresAt = this.getExpiresAt() } = {}) {
4895
+ this.cache.set(key, {
4896
+ expiresAt,
4897
+ value
4898
+ });
4899
+ }
4900
+ async getOrLoad(key) {
4901
+ return this.get(key) ?? await this.load(key);
4902
+ }
4903
+ async load(key) {
4904
+ let loadPromise = this.pendingLoads.get(key);
4905
+ if (!loadPromise) {
4906
+ loadPromise = this.loadResource(key).then((value) => {
4907
+ const entry = {
4908
+ expiresAt: this.getExpiresAt(),
4909
+ value
4910
+ };
4911
+ this.cache.set(key, entry);
4912
+ this.lifecycle.onMiss?.({
4913
+ inputKey: key,
4914
+ cacheKey: key,
4915
+ cacheValue: entry,
4916
+ outputValue: entry.value
4917
+ });
4918
+ return entry;
4919
+ });
4920
+ this.pendingLoads.set(key, loadPromise);
4921
+ }
4740
4922
  try {
4741
- const value = await fallbackPromise;
4742
- this.setCache(cacheKey, value);
4743
- return value;
4923
+ return (await loadPromise).value;
4744
4924
  } finally {
4745
- delete this.fallbackPromises[cacheKey];
4925
+ this.pendingLoads.delete(key);
4746
4926
  }
4747
4927
  }
4928
+ getExpiresAt() {
4929
+ return this.ttl < 0 ? this.ttl : Date.now() + this.ttl;
4930
+ }
4931
+ isExpired(entry) {
4932
+ return entry.expiresAt > 0 && entry.expiresAt < Date.now();
4933
+ }
4748
4934
  };
4749
4935
  /**
4750
4936
  * Hash a message string
@@ -4789,20 +4975,22 @@ function normalizeBatchConfig(batchConfig) {
4789
4975
  * Locale logic is handled at the LocalesCache level. Use a callback function that has the
4790
4976
  * locale parameter embedded if you wish to use the locale code.
4791
4977
  */
4792
- var TranslationsCache = class extends Cache {
4978
+ var TranslationsCache = class {
4793
4979
  /**
4794
4980
  * Constructor
4795
4981
  * @param {Object} params - The parameters for the cache
4796
4982
  * @param {Record<Hash, TranslationValue>} params.init - The initial cache
4797
4983
  * @param {Function} params.fallback - Get the fallback value for a cache miss
4798
4984
  */
4799
- constructor({ init, translateMany, lifecycle, batchConfig }) {
4800
- super(init, lifecycle);
4801
- this._queue = [];
4802
- this._batchTimer = null;
4803
- this._activeRequests = 0;
4804
- this._translateMany = translateMany;
4805
- this._batchConfig = normalizeBatchConfig(batchConfig);
4985
+ constructor({ init, translateMany, lifecycle = {}, batchConfig }) {
4986
+ this.pendingTranslations = /* @__PURE__ */ new Map();
4987
+ this.queue = [];
4988
+ this.batchTimer = null;
4989
+ this.activeRequests = 0;
4990
+ this.cache = structuredClone(init);
4991
+ this.translateMany = translateMany;
4992
+ this.batchConfig = normalizeBatchConfig(batchConfig);
4993
+ this.lifecycle = lifecycle;
4806
4994
  }
4807
4995
  /**
4808
4996
  * Get the translation value for a given key
@@ -4810,10 +4998,11 @@ var TranslationsCache = class extends Cache {
4810
4998
  * @returns The translation value
4811
4999
  */
4812
5000
  get(key) {
4813
- const value = this.getCache(key);
4814
- if (value != null && this.onHit) this.onHit({
5001
+ const cacheKey = this.getCacheKey(key);
5002
+ const value = this.cache[cacheKey];
5003
+ if (value != null) this.lifecycle.onHit?.({
4815
5004
  inputKey: key,
4816
- cacheKey: this.genKey(key),
5005
+ cacheKey,
4817
5006
  cacheValue: value,
4818
5007
  outputValue: value
4819
5008
  });
@@ -4825,75 +5014,64 @@ var TranslationsCache = class extends Cache {
4825
5014
  * @returns The translation value
4826
5015
  */
4827
5016
  async miss(key) {
4828
- const value = await this.missCache(key);
4829
- if (value != null && this.onMiss) this.onMiss({
4830
- inputKey: key,
4831
- cacheKey: this.genKey(key),
4832
- cacheValue: value,
4833
- outputValue: value
4834
- });
4835
- return value;
5017
+ const cacheKey = this.getCacheKey(key);
5018
+ let translationPromise = this.pendingTranslations.get(cacheKey);
5019
+ if (!translationPromise) {
5020
+ translationPromise = this.translate(key);
5021
+ this.pendingTranslations.set(cacheKey, translationPromise);
5022
+ }
5023
+ try {
5024
+ const value = await translationPromise;
5025
+ if (value != null) this.lifecycle.onMiss?.({
5026
+ inputKey: key,
5027
+ cacheKey,
5028
+ cacheValue: value,
5029
+ outputValue: value
5030
+ });
5031
+ return value;
5032
+ } finally {
5033
+ this.pendingTranslations.delete(cacheKey);
5034
+ }
4836
5035
  }
4837
- /**
4838
- * Generate a key for the cache
4839
- * @param key - The translation key
4840
- * @returns The key
4841
- */
4842
- genKey(key) {
5036
+ getInternalCache() {
5037
+ return structuredClone(this.cache);
5038
+ }
5039
+ getCacheKey(key) {
4843
5040
  return hashMessage(key.message, key.options);
4844
5041
  }
4845
- /**
4846
- * Get the fallback value for a cache miss
4847
- * @param key - The translation key
4848
- * @returns The fallback value
4849
- */
4850
- fallback(key) {
4851
- const translationPromise = this._enqueueTranslation(key);
4852
- if (this._queue.length >= this._batchConfig.maxBatchSize) this._flushNow();
4853
- else this._scheduleBatch();
5042
+ translate(key) {
5043
+ const translationPromise = this.enqueueTranslation(key);
5044
+ if (this.queue.length >= this.batchConfig.maxBatchSize) this.flushNow();
5045
+ else this.scheduleBatch();
4854
5046
  return translationPromise;
4855
5047
  }
4856
- /**
4857
- * Flush the queue now
4858
- */
4859
- _flushNow() {
4860
- if (this._batchTimer) {
4861
- clearTimeout(this._batchTimer);
4862
- this._batchTimer = null;
5048
+ flushNow() {
5049
+ if (this.batchTimer) {
5050
+ clearTimeout(this.batchTimer);
5051
+ this.batchTimer = null;
4863
5052
  }
4864
- this._drainQueue();
4865
- }
4866
- /**
4867
- * Schedule a batch of translations
4868
- */
4869
- _scheduleBatch() {
4870
- if (this._batchTimer) return;
4871
- this._batchTimer = setTimeout(() => {
4872
- this._batchTimer = null;
4873
- this._drainQueue();
4874
- }, this._batchConfig.batchInterval);
4875
- }
4876
- /**
4877
- * Drain the queue
4878
- */
4879
- _drainQueue() {
4880
- while (this._queue.length > 0 && this._activeRequests < this._batchConfig.maxConcurrentRequests) {
4881
- const batch = this._queue.splice(0, this._batchConfig.maxBatchSize);
4882
- this._sendBatchRequest(batch);
5053
+ this.drainQueue();
5054
+ }
5055
+ scheduleBatch() {
5056
+ if (this.batchTimer) return;
5057
+ this.batchTimer = setTimeout(() => {
5058
+ this.batchTimer = null;
5059
+ this.drainQueue();
5060
+ }, this.batchConfig.batchInterval);
5061
+ }
5062
+ drainQueue() {
5063
+ while (this.queue.length > 0 && this.activeRequests < this.batchConfig.maxConcurrentRequests) {
5064
+ const batch = this.queue.splice(0, this.batchConfig.maxBatchSize);
5065
+ this.sendBatchRequest(batch);
4883
5066
  }
4884
- if (this._queue.length > 0) this._scheduleBatch();
5067
+ if (this.queue.length > 0) this.scheduleBatch();
4885
5068
  }
4886
- /**
4887
- * Enqueue translation request and return a promise that resolves when the translation is ready
4888
- * @param {TranslationKey<TranslationValue>} key - The translation key
4889
- * @returns {Promise<TranslationValue>} The translation promise
4890
- */
4891
- _enqueueTranslation(key) {
4892
- const hash = this.genKey(key);
5069
+ enqueueTranslation(key) {
5070
+ const hash = this.getCacheKey(key);
4893
5071
  const options = key.options;
4894
5072
  const metadataOptions = options;
4895
5073
  return new Promise((resolve, reject) => {
4896
- this._queue.push({
5074
+ this.queue.push({
4897
5075
  key: hash,
4898
5076
  source: key.message,
4899
5077
  metadata: {
@@ -4908,38 +5086,28 @@ var TranslationsCache = class extends Cache {
4908
5086
  });
4909
5087
  });
4910
5088
  }
4911
- /**
4912
- * Send a batch request for translations
4913
- * @param {QueueEntry<TranslationValue>[]} batch - The batch of requests to send
4914
- */
4915
- async _sendBatchRequest(batch) {
4916
- this._activeRequests++;
5089
+ async sendBatchRequest(batch) {
5090
+ this.activeRequests++;
4917
5091
  const requests = convertBatchToTranslateManyParams(batch);
4918
- const response = await this._sendBatchRequestWithErrorHandling(batch, requests);
4919
- if (response) this._handleTranslationResponse(batch, response);
4920
- this._activeRequests--;
5092
+ const response = await this.sendBatchRequestWithErrorHandling(batch, requests);
5093
+ if (response) this.handleTranslationResponse(batch, response);
5094
+ this.activeRequests--;
4921
5095
  }
4922
- /**
4923
- * Send a translation request with error handling
4924
- */
4925
- async _sendBatchRequestWithErrorHandling(batch, requests) {
5096
+ async sendBatchRequestWithErrorHandling(batch, requests) {
4926
5097
  try {
4927
- return await this._translateMany(requests);
5098
+ return await this.translateMany(requests);
4928
5099
  } catch (error) {
4929
5100
  for (const entry of batch) entry.reject(error);
4930
5101
  return;
4931
5102
  }
4932
5103
  }
4933
- /**
4934
- * Handle a translation response
4935
- */
4936
- _handleTranslationResponse(batch, response) {
5104
+ handleTranslationResponse(batch, response) {
4937
5105
  for (const entry of batch) {
4938
5106
  const { key } = entry;
4939
5107
  const result = response[key];
4940
5108
  if (result && result.success) {
4941
5109
  const translation = result.translation;
4942
- this.setCache(key, translation);
5110
+ this.cache[key] = translation;
4943
5111
  entry.resolve(translation);
4944
5112
  } else entry.reject(result?.error);
4945
5113
  }
@@ -4957,415 +5125,87 @@ function convertBatchToTranslateManyParams(batch) {
4957
5125
  return acc;
4958
5126
  }, {});
4959
5127
  }
4960
- /**
4961
- * Default cache expiry time in milliseconds
4962
- */
4963
- const DEFAULT_CACHE_EXPIRY_TIME = 6e4;
4964
- /**
4965
- * Cache for looking up translations by locale
4966
- */
4967
- var LocalesCache = class extends Cache {
4968
- /**
4969
- * Constructor
4970
- * @param {Object} params - The parameters for the cache
4971
- * @param {Record<string, CacheEntry<TranslationValue>>} params.init - The initial cache
4972
- * @param {number | null} params.ttl - The time to live for cache entries
4973
- * @param {SafeTranslationsLoader<TranslationValue>} params.loadTranslations - The translation loader function
4974
- * @param {CreateTranslateMany} params.createTranslateMany - Factory function for creating a translate many function
4975
- */
4976
- constructor({ init = {}, ttl, batchConfig, loadTranslations, createTranslateMany, lifecycle: { onLocalesCacheHit: onHit, onLocalesCacheMiss: onMiss, onTranslationsCacheHit, onTranslationsCacheMiss } }) {
4977
- super(init, {
4978
- onHit,
4979
- onMiss
5128
+ var LocalesCache = class {
5129
+ constructor({ ttl, batchConfig, defaultLocale, dictionary = {}, loadTranslations, loadDictionary, createTranslateMany, translateDictionaryEntry, lifecycle }) {
5130
+ this.translations = new ResourceCache({
5131
+ ttl,
5132
+ load: async (locale) => new TranslationsCache({
5133
+ init: await loadTranslations(locale),
5134
+ lifecycle: createTranslationsCacheLifecycle(locale, lifecycle),
5135
+ translateMany: createTranslateMany(locale),
5136
+ batchConfig
5137
+ }),
5138
+ lifecycle: {
5139
+ onHit: lifecycle.onLocalesCacheHit,
5140
+ onMiss: lifecycle.onLocalesCacheMiss
5141
+ }
4980
5142
  });
4981
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
4982
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
4983
- this._translationLoader = loadTranslations;
4984
- this._createTranslateMany = createTranslateMany;
4985
- this._batchConfig = batchConfig;
4986
- this._onTranslationsCacheHit = onTranslationsCacheHit;
4987
- this._onTranslationsCacheMiss = onTranslationsCacheMiss;
4988
- }
4989
- /**
4990
- * Get the translations for a given locale
4991
- * @param key - The locale
4992
- * @returns The translations
4993
- */
4994
- get(key) {
4995
- const entry = this.getCache(key);
4996
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
4997
- const value = entry.translationsCache;
4998
- if (value != null && this.onHit) this.onHit({
4999
- inputKey: key,
5000
- cacheKey: this.genKey(key),
5001
- cacheValue: entry,
5002
- outputValue: value
5143
+ this.dictionaries = new ResourceCache({
5144
+ ttl,
5145
+ load: async (locale) => createDictionaryCache({
5146
+ locale,
5147
+ dictionary: await loadDictionary(locale),
5148
+ translate: translateDictionaryEntry,
5149
+ lifecycle
5150
+ }),
5151
+ lifecycle: {
5152
+ onHit: lifecycle.onLocalesDictionaryCacheHit,
5153
+ onMiss: lifecycle.onLocalesDictionaryCacheMiss
5154
+ }
5003
5155
  });
5004
- return value;
5156
+ this.dictionaries.set(defaultLocale, createDictionaryCache({
5157
+ locale: defaultLocale,
5158
+ dictionary,
5159
+ translate: translateDictionaryEntry,
5160
+ lifecycle
5161
+ }), { expiresAt: -1 });
5005
5162
  }
5006
- /**
5007
- * Miss the cache
5008
- * @param key - The locale
5009
- * @returns The translations cache
5010
- */
5011
- async miss(key) {
5012
- const cacheValue = await this.missCache(key);
5013
- const value = cacheValue.translationsCache;
5014
- if (value != null && this.onMiss) this.onMiss({
5015
- inputKey: key,
5016
- cacheKey: this.genKey(key),
5017
- cacheValue,
5018
- outputValue: value
5019
- });
5020
- return value;
5163
+ getTranslations(locale) {
5164
+ return this.translations.get(locale);
5021
5165
  }
5022
- /**
5023
- * Generate the cache key for a given locale
5024
- * @param key - The locale
5025
- * @returns The cache key
5026
- *
5027
- * This is just an identity function, no transformation needed
5028
- */
5029
- genKey(key) {
5030
- return key;
5166
+ getOrLoadTranslations(locale) {
5167
+ return this.translations.getOrLoad(locale);
5031
5168
  }
5032
- /**
5033
- * Fallback for a cache miss
5034
- * @param locale - The locale
5035
- * @returns The cache entry
5036
- */
5037
- async fallback(locale) {
5038
- return {
5039
- translationsCache: new TranslationsCache({
5040
- init: await this._translationLoader(locale),
5041
- lifecycle: this._createTranslationsCacheLifecycle(locale),
5042
- translateMany: this._createTranslateMany(locale),
5043
- batchConfig: this._batchConfig
5044
- }),
5045
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
5046
- };
5169
+ getDictionary(locale) {
5170
+ return this.dictionaries.get(locale);
5047
5171
  }
5048
- /**
5049
- * Create the translations cache lifecycle
5050
- * @param locale - The locale
5051
- * @returns The translations cache lifecycle
5052
- */
5053
- _createTranslationsCacheLifecycle(locale) {
5054
- return {
5055
- onHit: this._onTranslationsCacheHit ? (params) => this._onTranslationsCacheHit({
5056
- locale,
5057
- ...params
5058
- }) : void 0,
5059
- onMiss: this._onTranslationsCacheMiss ? (params) => this._onTranslationsCacheMiss({
5060
- locale,
5061
- ...params
5062
- }) : void 0
5063
- };
5172
+ getOrLoadDictionary(locale) {
5173
+ return this.dictionaries.getOrLoad(locale);
5064
5174
  }
5065
5175
  };
5066
- function getDictionaryPath(id) {
5067
- if (!id) return [];
5068
- return id.split(".");
5069
- }
5070
- function isDictionaryValue(value) {
5071
- return typeof value === "object" && value != null && !Array.isArray(value);
5072
- }
5073
- function getDictionaryEntry(value) {
5074
- if (!isDictionaryLeafNode(value)) return;
5075
- return {
5076
- entry: Array.isArray(value) ? value[0] : value,
5077
- options: Array.isArray(value) ? value[1] ?? {} : {}
5078
- };
5079
- }
5080
- function getDictionaryValue(value) {
5081
- if (Object.keys(value.options).length === 0) return value.entry;
5082
- return [value.entry, value.options];
5083
- }
5084
- function resolveDictionaryLookupOptions(options) {
5085
- const { $format, ...rest } = options;
5176
+ function createTranslationsCacheLifecycle(locale, lifecycle) {
5086
5177
  return {
5087
- ...rest,
5088
- $format: isStringFormat($format) ? $format : "ICU",
5089
- ...rest.$context === void 0 && typeof rest.context === "string" && { $context: rest.context }
5178
+ onHit: withLocale(locale, lifecycle.onTranslationsCacheHit),
5179
+ onMiss: withLocale(locale, lifecycle.onTranslationsCacheMiss)
5090
5180
  };
5091
5181
  }
5092
- function isDictionaryLeafNode(value) {
5093
- if (typeof value === "string") return true;
5094
- if (!Array.isArray(value) || typeof value[0] !== "string") return false;
5095
- if (value.length === 1) return true;
5096
- return value.length === 2 && isDictionaryOptions(value[1]);
5097
- }
5098
- function isDictionaryOptions(value) {
5099
- if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
5100
- const options = value;
5101
- 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");
5102
- }
5103
- function isStringFormat(value) {
5104
- return value === "ICU" || value === "I18NEXT" || value === "STRING";
5182
+ function createDictionaryCache({ locale, dictionary, translate, lifecycle }) {
5183
+ const { onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } = lifecycle;
5184
+ return new DictionaryCache({
5185
+ init: dictionary,
5186
+ runtimeTranslate: (key, sourceEntry) => translate(locale, key, sourceEntry),
5187
+ lifecycle: {
5188
+ onHit: withLocale(locale, onDictionaryCacheHit),
5189
+ onMiss: withLocale(locale, onDictionaryCacheMiss),
5190
+ onDictionaryObjectCacheHit: withLocale(locale, onDictionaryObjectCacheHit)
5191
+ }
5192
+ });
5105
5193
  }
5106
- function replaceDictionary(target, source) {
5107
- for (const key of Object.keys(target)) delete target[key];
5108
- Object.assign(target, source);
5194
+ function withLocale(locale, callback) {
5195
+ return callback ? (params) => callback({
5196
+ locale,
5197
+ ...params
5198
+ }) : void 0;
5109
5199
  }
5110
- var DictionarySourceNotFoundError = class extends Error {
5111
- constructor(id) {
5112
- super(`I18nManager: source dictionary entry ${id} is not defined`);
5113
- this.name = "DictionarySourceNotFoundError";
5114
- }
5115
- };
5116
- /**
5117
- * A cache for a single locale's dictionary
5118
- *
5119
- * Principles:
5120
- * - This class is language agnostic, and should never store the locale code as a parameter.
5121
- * Locale logic is handled at the LocalesDictionaryCache level. Use a callback function
5122
- * that has the locale parameter embedded if you wish to use the locale code.
5123
- */
5124
- var DictionaryCache = class extends Cache {
5125
- /**
5126
- * Constructor
5127
- * @param {Object} params - The parameters for the cache
5128
- * @param {Dictionary} params.init - The initial cache
5129
- */
5130
- constructor({ init, lifecycle, runtimeTranslate }) {
5131
- super(init, lifecycle);
5132
- this._runtimeTranslate = runtimeTranslate;
5133
- this.onHitObj = lifecycle?.onHitObj;
5134
- this.onMissObj = lifecycle?.onMissObj;
5135
- }
5136
- /**
5137
- * Get the dictionary value for a given key
5138
- * @param key - The dictionary key
5139
- * @returns The dictionary value
5140
- */
5141
- get(key) {
5142
- const value = this.getCache(key);
5143
- const entry = getDictionaryEntry(value);
5144
- if (entry === void 0) return;
5145
- if (this.onHit) this.onHit({
5146
- inputKey: key,
5147
- cacheKey: this.genKey(key),
5148
- cacheValue: value,
5149
- outputValue: entry
5150
- });
5151
- return entry;
5152
- }
5153
- set(key, value) {
5154
- const dictionaryValue = getDictionaryValue(value);
5155
- this.setCache(this.genKey(key), dictionaryValue);
5156
- }
5157
- getObj(key) {
5158
- const value = this.getCache(key);
5159
- if (value === void 0) return;
5160
- const outputValue = structuredClone(value);
5161
- if (this.onHitObj) this.onHitObj({
5162
- inputKey: key,
5163
- cacheKey: this.genKey(key),
5164
- cacheValue: value,
5165
- outputValue
5166
- });
5167
- return outputValue;
5168
- }
5169
- setObj(key, value) {
5170
- this.setCache(this.genKey(key), structuredClone(value));
5171
- }
5172
- async missObj(key, sourceObject) {
5173
- const sourceEntry = getDictionaryEntry(sourceObject);
5174
- if (sourceEntry !== void 0) return getDictionaryValue(await this.miss(key, sourceEntry));
5175
- if (!isDictionaryValue(sourceObject)) throw new DictionarySourceNotFoundError(key);
5176
- const translatedEntries = await Promise.all(Object.entries(sourceObject).map(async ([childKey, childSource]) => {
5177
- const childPath = key ? `${key}.${childKey}` : childKey;
5178
- return [childKey, await this.missObj(childPath, childSource)];
5179
- }));
5180
- const translatedObject = Object.fromEntries(translatedEntries);
5181
- this.setObj(key, translatedObject);
5182
- return translatedObject;
5183
- }
5184
- /**
5185
- * Miss the cache
5186
- * @param key - The dictionary key
5187
- * @returns The dictionary value
5188
- */
5189
- async miss(key, sourceEntry) {
5190
- const value = await this.missCache(key, sourceEntry);
5191
- const entry = getDictionaryEntry(value);
5192
- if (entry === void 0) throw new Error("DictionaryCache missCache did not return a DictionaryEntry");
5193
- if (this.onMiss) this.onMiss({
5194
- inputKey: key,
5195
- cacheKey: this.genKey(key),
5196
- cacheValue: value,
5197
- outputValue: entry
5198
- });
5199
- return entry;
5200
- }
5201
- /**
5202
- * Set the value for a key
5203
- */
5204
- setCache(cacheKey, value) {
5205
- const cache = this.getMutableCache();
5206
- const dictionaryPath = getDictionaryPath(cacheKey);
5207
- if (dictionaryPath.length === 0) {
5208
- if (isDictionaryValue(value)) replaceDictionary(cache, value);
5209
- return;
5210
- }
5211
- let current = cache;
5212
- for (const key of dictionaryPath.slice(0, -1)) {
5213
- const next = current[key];
5214
- if (!isDictionaryValue(next)) current[key] = {};
5215
- current = current[key];
5216
- }
5217
- current[dictionaryPath[dictionaryPath.length - 1]] = value;
5218
- }
5219
- /**
5220
- * Look up the key
5221
- */
5222
- getCache(key) {
5223
- const dictionaryPath = getDictionaryPath(this.genKey(key));
5224
- let current = this.getMutableCache();
5225
- if (dictionaryPath.length === 0) return current;
5226
- for (const pathSegment of dictionaryPath) {
5227
- if (!isDictionaryValue(current)) return;
5228
- current = current[pathSegment];
5229
- }
5230
- return current;
5231
- }
5232
- /**
5233
- * Generate a key for the cache
5234
- * @param key - The dictionary key
5235
- * @returns The key
5236
- */
5237
- genKey(key) {
5238
- return key;
5239
- }
5240
- /**
5241
- * Get the fallback value for a cache miss
5242
- * @param key - The dictionary key
5243
- * @returns The fallback value
5244
- *
5245
- * @throws {Error} - If the fallback is not implemented
5246
- */
5247
- fallback(key, sourceEntry) {
5248
- return this._runtimeTranslate(key, sourceEntry);
5249
- }
5250
- };
5251
- /**
5252
- * Cache for looking up dictionaries by locale
5253
- */
5254
- var LocalesDictionaryCache = class extends Cache {
5255
- /**
5256
- * Constructor
5257
- * @param {Object} params - The parameters for the cache
5258
- * @param {number | null} params.ttl - The time to live for cache entries
5259
- * @param {DictionaryLoader} params.loadDictionary - The dictionary loader function
5260
- */
5261
- constructor({ ttl, defaultLocale, dictionary = {}, loadDictionary, runtimeTranslate, lifecycle: { onLocalesDictionaryCacheHit: onHit, onLocalesDictionaryCacheMiss: onMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit } }) {
5262
- super({}, {
5263
- onHit,
5264
- onMiss
5265
- });
5266
- this.ttl = DEFAULT_CACHE_EXPIRY_TIME;
5267
- this.ttl = ttl === null ? -1 : ttl ?? 6e4;
5268
- this._dictionaryLoader = loadDictionary;
5269
- this._runtimeTranslate = runtimeTranslate;
5270
- this._onDictionaryCacheHit = onDictionaryCacheHit;
5271
- this._onDictionaryCacheMiss = onDictionaryCacheMiss;
5272
- this._onDictionaryObjectCacheHit = onDictionaryObjectCacheHit;
5273
- this.setCache(defaultLocale, {
5274
- dictionaryCache: new DictionaryCache({
5275
- init: dictionary,
5276
- runtimeTranslate: this._createDictionaryRuntimeTranslate(defaultLocale),
5277
- lifecycle: this._createDictionaryCacheLifecycle(defaultLocale)
5278
- }),
5279
- expiresAt: -1
5280
- });
5281
- }
5282
- /**
5283
- * Get the dictionary for a given locale
5284
- * @param key - The locale
5285
- * @returns The dictionary
5286
- */
5287
- get(key) {
5288
- const entry = this.getCache(key);
5289
- if (!entry || entry.expiresAt > 0 && entry.expiresAt < Date.now()) return;
5290
- const value = entry.dictionaryCache;
5291
- if (value != null && this.onHit) this.onHit({
5292
- inputKey: key,
5293
- cacheKey: this.genKey(key),
5294
- cacheValue: entry,
5295
- outputValue: value
5296
- });
5297
- return value;
5298
- }
5299
- /**
5300
- * Miss the cache
5301
- * @param key - The locale
5302
- * @returns The dictionary cache
5303
- */
5304
- async miss(key) {
5305
- const cacheValue = await this.missCache(key);
5306
- const value = cacheValue.dictionaryCache;
5307
- if (value != null && this.onMiss) this.onMiss({
5308
- inputKey: key,
5309
- cacheKey: this.genKey(key),
5310
- cacheValue,
5311
- outputValue: value
5312
- });
5313
- return value;
5314
- }
5315
- /**
5316
- * Generate the cache key for a given locale
5317
- * @param key - The locale
5318
- * @returns The cache key
5319
- *
5320
- * This is just an identity function, no transformation needed
5321
- */
5322
- genKey(key) {
5323
- return key;
5324
- }
5325
- /**
5326
- * Fallback for a cache miss
5327
- * @param locale - The locale
5328
- * @returns The cache entry
5329
- */
5330
- async fallback(locale) {
5331
- return {
5332
- dictionaryCache: new DictionaryCache({
5333
- init: await this._dictionaryLoader(locale),
5334
- runtimeTranslate: this._createDictionaryRuntimeTranslate(locale),
5335
- lifecycle: this._createDictionaryCacheLifecycle(locale)
5336
- }),
5337
- expiresAt: this.ttl < 0 ? this.ttl : Date.now() + this.ttl
5338
- };
5339
- }
5340
- /**
5341
- * Create the dictionary cache lifecycle
5342
- * @param locale - The locale
5343
- * @returns The dictionary cache lifecycle
5344
- */
5345
- _createDictionaryCacheLifecycle(locale) {
5346
- return {
5347
- onHit: this._onDictionaryCacheHit ? (params) => this._onDictionaryCacheHit({
5348
- locale,
5349
- ...params
5350
- }) : void 0,
5351
- onMiss: this._onDictionaryCacheMiss ? (params) => this._onDictionaryCacheMiss({
5352
- locale,
5353
- ...params
5354
- }) : void 0,
5355
- onHitObj: this._onDictionaryObjectCacheHit ? (params) => this._onDictionaryObjectCacheHit({
5356
- locale,
5357
- ...params
5358
- }) : void 0
5359
- };
5360
- }
5361
- _createDictionaryRuntimeTranslate(locale) {
5362
- return (key, sourceEntry) => this._runtimeTranslate(locale, key, sourceEntry);
5363
- }
5364
- };
5200
+ const LOCALES_CACHE_HIT_EVENT_NAME = "locales-cache-hit";
5365
5201
  const LOCALES_CACHE_MISS_EVENT_NAME = "locales-cache-miss";
5202
+ const TRANSLATIONS_CACHE_HIT_EVENT_NAME = "translations-cache-hit";
5366
5203
  const TRANSLATIONS_CACHE_MISS_EVENT_NAME = "translations-cache-miss";
5204
+ const LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME = "locales-dictionary-cache-hit";
5367
5205
  const LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME = "locales-dictionary-cache-miss";
5206
+ const DICTIONARY_CACHE_HIT_EVENT_NAME = "dictionary-cache-hit";
5368
5207
  const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5208
+ const DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME = "dictionary-object-cache-hit";
5369
5209
  /**
5370
5210
  * Maps consumer-facing lifecycle callbacks to internal locales cache lifecycle callbacks.
5371
5211
  * The consumer API exposes simplified params (locale, hash, value) while the internal
@@ -5373,22 +5213,24 @@ const DICTIONARY_CACHE_MISS_EVENT_NAME = "dictionary-cache-miss";
5373
5213
  *
5374
5214
  * @deprecated - move to subscription api instead
5375
5215
  */
5376
- function createLifecycleCallbacks(emit) {
5216
+ function createLifecycleCallbacks(emit, hasListeners = () => true) {
5377
5217
  return {
5378
5218
  onLocalesCacheHit: (params) => {
5379
- emit("locales-cache-hit", {
5219
+ if (!hasListeners("locales-cache-hit")) return;
5220
+ emit(LOCALES_CACHE_HIT_EVENT_NAME, {
5380
5221
  locale: params.inputKey,
5381
5222
  translations: params.outputValue.getInternalCache()
5382
5223
  });
5383
5224
  },
5384
5225
  onLocalesCacheMiss: (params) => {
5226
+ if (!hasListeners("locales-cache-miss")) return;
5385
5227
  emit(LOCALES_CACHE_MISS_EVENT_NAME, {
5386
5228
  locale: params.inputKey,
5387
5229
  translations: params.outputValue.getInternalCache()
5388
5230
  });
5389
5231
  },
5390
5232
  onTranslationsCacheHit: (params) => {
5391
- emit("translations-cache-hit", {
5233
+ emit(TRANSLATIONS_CACHE_HIT_EVENT_NAME, {
5392
5234
  locale: params.locale,
5393
5235
  hash: params.cacheKey,
5394
5236
  translation: params.outputValue
@@ -5402,19 +5244,21 @@ function createLifecycleCallbacks(emit) {
5402
5244
  });
5403
5245
  },
5404
5246
  onLocalesDictionaryCacheHit: (params) => {
5405
- emit("locales-dictionary-cache-hit", {
5247
+ if (!hasListeners("locales-dictionary-cache-hit")) return;
5248
+ emit(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, {
5406
5249
  locale: params.inputKey,
5407
5250
  dictionary: params.outputValue.getInternalCache()
5408
5251
  });
5409
5252
  },
5410
5253
  onLocalesDictionaryCacheMiss: (params) => {
5254
+ if (!hasListeners("locales-dictionary-cache-miss")) return;
5411
5255
  emit(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, {
5412
5256
  locale: params.inputKey,
5413
5257
  dictionary: params.outputValue.getInternalCache()
5414
5258
  });
5415
5259
  },
5416
5260
  onDictionaryCacheHit: (params) => {
5417
- emit("dictionary-cache-hit", {
5261
+ emit(DICTIONARY_CACHE_HIT_EVENT_NAME, {
5418
5262
  locale: params.locale,
5419
5263
  id: params.cacheKey,
5420
5264
  dictionaryEntry: params.outputValue
@@ -5428,7 +5272,7 @@ function createLifecycleCallbacks(emit) {
5428
5272
  });
5429
5273
  },
5430
5274
  onDictionaryObjectCacheHit: (params) => {
5431
- emit("dictionary-object-cache-hit", {
5275
+ emit(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, {
5432
5276
  locale: params.locale,
5433
5277
  id: params.cacheKey,
5434
5278
  dictionaryValue: params.outputValue
@@ -5463,6 +5307,9 @@ var EventEmitter = class {
5463
5307
  emit(eventName, event) {
5464
5308
  this.listeners[eventName]?.forEach((subscriber) => subscriber(event));
5465
5309
  }
5310
+ hasListeners(eventName) {
5311
+ return (this.listeners[eventName]?.size ?? 0) > 0;
5312
+ }
5466
5313
  };
5467
5314
  /**
5468
5315
  * Subscribes to the lifecycle callbacks and emits the events to the event emitter
@@ -5472,7 +5319,7 @@ var EventEmitter = class {
5472
5319
  * and is only used internally
5473
5320
  */
5474
5321
  function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, onTranslationsCacheHit, onTranslationsCacheMiss, onLocalesDictionaryCacheHit, onLocalesDictionaryCacheMiss, onDictionaryCacheHit, onDictionaryCacheMiss, onDictionaryObjectCacheHit }, subscribe) {
5475
- if (onLocalesCacheHit) subscribe("locales-cache-hit", (event) => {
5322
+ if (onLocalesCacheHit) subscribe(LOCALES_CACHE_HIT_EVENT_NAME, (event) => {
5476
5323
  onLocalesCacheHit({
5477
5324
  ...event,
5478
5325
  value: event.translations
@@ -5484,7 +5331,7 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5484
5331
  value: event.translations
5485
5332
  });
5486
5333
  });
5487
- if (onTranslationsCacheHit) subscribe("translations-cache-hit", (event) => {
5334
+ if (onTranslationsCacheHit) subscribe(TRANSLATIONS_CACHE_HIT_EVENT_NAME, (event) => {
5488
5335
  onTranslationsCacheHit({
5489
5336
  ...event,
5490
5337
  value: event.translation
@@ -5496,19 +5343,19 @@ function subscribeLifecycleCallbacks({ onLocalesCacheHit, onLocalesCacheMiss, on
5496
5343
  value: event.translation
5497
5344
  });
5498
5345
  });
5499
- if (onLocalesDictionaryCacheHit) subscribe("locales-dictionary-cache-hit", (event) => {
5346
+ if (onLocalesDictionaryCacheHit) subscribe(LOCALES_DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5500
5347
  onLocalesDictionaryCacheHit(event);
5501
5348
  });
5502
5349
  if (onLocalesDictionaryCacheMiss) subscribe(LOCALES_DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5503
5350
  onLocalesDictionaryCacheMiss(event);
5504
5351
  });
5505
- if (onDictionaryCacheHit) subscribe("dictionary-cache-hit", (event) => {
5352
+ if (onDictionaryCacheHit) subscribe(DICTIONARY_CACHE_HIT_EVENT_NAME, (event) => {
5506
5353
  onDictionaryCacheHit(event);
5507
5354
  });
5508
5355
  if (onDictionaryCacheMiss) subscribe(DICTIONARY_CACHE_MISS_EVENT_NAME, (event) => {
5509
5356
  onDictionaryCacheMiss(event);
5510
5357
  });
5511
- if (onDictionaryObjectCacheHit) subscribe("dictionary-object-cache-hit", (event) => {
5358
+ if (onDictionaryObjectCacheHit) subscribe(DICTIONARY_OBJECT_CACHE_HIT_EVENT_NAME, (event) => {
5512
5359
  onDictionaryObjectCacheHit(event);
5513
5360
  });
5514
5361
  }
@@ -5539,26 +5386,32 @@ var I18nManager = class extends EventEmitter {
5539
5386
  locales: this.config.locales,
5540
5387
  customMapping: this.config.customMapping
5541
5388
  });
5542
- const loadTranslations = createTranslationLoader(params);
5543
- const loadDictionary = createDictionaryLoader(params);
5389
+ const loadTranslations = routeCreateTranslationLoader({
5390
+ loadTranslations: params.loadTranslations,
5391
+ type: getLoadTranslationsType(params),
5392
+ remoteTranslationLoaderParams: {
5393
+ cacheUrl: params.cacheUrl,
5394
+ projectId: params.projectId,
5395
+ _versionId: params._versionId,
5396
+ _branchId: params._branchId,
5397
+ customMapping: params.customMapping
5398
+ }
5399
+ });
5400
+ const loadDictionary = params.loadDictionary ?? (() => Promise.resolve({}));
5544
5401
  const runtimeTranslationTimeout = this.config.runtimeTranslation?.timeout ?? DEFAULT_TRANSLATION_TIMEOUT;
5545
5402
  const runtimeTranslationMetadata = this.config.runtimeTranslation?.metadata ?? {};
5546
5403
  const createTranslateMany = createTranslateManyFactory(this.getGTClassClean(), runtimeTranslationTimeout, runtimeTranslationMetadata);
5547
5404
  subscribeLifecycleCallbacks(params.lifecycle ?? {}, (...args) => this.subscribe(...args));
5548
- const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args));
5405
+ const lifecycle = createLifecycleCallbacks((...args) => this.emit(...args), (eventName) => this.hasListeners(eventName));
5549
5406
  this.localesCache = new LocalesCache({
5550
- loadTranslations,
5551
- createTranslateMany,
5552
- lifecycle,
5553
- ttl: this.config.cacheExpiryTime,
5554
- batchConfig: this.config.batchConfig
5555
- });
5556
- this.localesDictionaryCache = new LocalesDictionaryCache({
5557
5407
  defaultLocale: this.config.defaultLocale,
5558
5408
  dictionary: params.dictionary,
5409
+ loadTranslations,
5559
5410
  loadDictionary,
5560
- runtimeTranslate: (locale, id, sourceEntry) => this.dictionaryRuntimeTranslate(locale, id, sourceEntry),
5411
+ createTranslateMany,
5412
+ translateDictionaryEntry: (locale, id, sourceEntry) => this.translateDictionaryEntry(locale, id, sourceEntry),
5561
5413
  ttl: this.config.cacheExpiryTime,
5414
+ batchConfig: this.config.batchConfig,
5562
5415
  lifecycle
5563
5416
  });
5564
5417
  }
@@ -5630,9 +5483,7 @@ var I18nManager = class extends EventEmitter {
5630
5483
  try {
5631
5484
  const translationLocale = this.resolveCacheLocale(locale);
5632
5485
  if (!translationLocale) return {};
5633
- let txCache = this.localesCache.get(translationLocale);
5634
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5635
- return txCache.getInternalCache();
5486
+ return (await this.localesCache.getOrLoadTranslations(translationLocale)).getInternalCache();
5636
5487
  } catch (error) {
5637
5488
  this.handleError(error);
5638
5489
  return {};
@@ -5645,10 +5496,8 @@ var I18nManager = class extends EventEmitter {
5645
5496
  async loadDictionary(locale) {
5646
5497
  try {
5647
5498
  const dictionaryLocale = this.resolveCacheLocale(locale);
5648
- if (!dictionaryLocale) return this.localesDictionaryCache.get(this.config.defaultLocale)?.getInternalCache() ?? {};
5649
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5650
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5651
- return dictionaryCache.getInternalCache();
5499
+ if (!dictionaryLocale) return this.getDefaultDictionaryCache()?.getInternalCache() ?? {};
5500
+ return (await this.localesCache.getOrLoadDictionary(dictionaryLocale)).getInternalCache();
5652
5501
  } catch (error) {
5653
5502
  this.handleError(error);
5654
5503
  return {};
@@ -5659,8 +5508,8 @@ var I18nManager = class extends EventEmitter {
5659
5508
  */
5660
5509
  lookupDictionary(locale, id) {
5661
5510
  try {
5662
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5663
- return this.localesDictionaryCache.get(dictionaryLocale)?.get(id);
5511
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5512
+ return this.localesCache.getDictionary(dictionaryLocale)?.getEntry(id);
5664
5513
  } catch (error) {
5665
5514
  this.handleError(error);
5666
5515
  return;
@@ -5671,8 +5520,8 @@ var I18nManager = class extends EventEmitter {
5671
5520
  */
5672
5521
  lookupDictionaryObj(locale, id) {
5673
5522
  try {
5674
- const dictionaryLocale = this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5675
- return this.localesDictionaryCache.get(dictionaryLocale)?.getObj(id);
5523
+ const dictionaryLocale = this.resolveDictionaryCacheLocale(locale);
5524
+ return this.localesCache.getDictionary(dictionaryLocale)?.getValue(id);
5676
5525
  } catch (error) {
5677
5526
  this.handleError(error);
5678
5527
  return;
@@ -5685,19 +5534,10 @@ var I18nManager = class extends EventEmitter {
5685
5534
  async lookupDictionaryWithFallback(locale, id) {
5686
5535
  try {
5687
5536
  const dictionaryLocale = this.resolveCacheLocale(locale);
5688
- if (!dictionaryLocale) {
5689
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5690
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5691
- return sourceEntry;
5692
- }
5693
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5694
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5695
- let dictionaryEntry = dictionaryCache.get(id);
5696
- if (dictionaryEntry === void 0) {
5697
- const sourceEntry = this.localesDictionaryCache.get(this.config.defaultLocale)?.get(id);
5698
- if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5699
- dictionaryEntry = await dictionaryCache.miss(id, sourceEntry);
5700
- }
5537
+ if (!dictionaryLocale) return this.getSourceDictionaryEntry(id);
5538
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5539
+ let dictionaryEntry = dictionaryCache.getEntry(id);
5540
+ if (dictionaryEntry === void 0) dictionaryEntry = await dictionaryCache.materializeEntry(id, this.getSourceDictionaryEntry(id));
5701
5541
  return dictionaryEntry;
5702
5542
  } catch (error) {
5703
5543
  this.handleError(error);
@@ -5711,25 +5551,41 @@ var I18nManager = class extends EventEmitter {
5711
5551
  async lookupDictionaryObjWithFallback(locale, id) {
5712
5552
  try {
5713
5553
  const dictionaryLocale = this.resolveCacheLocale(locale);
5714
- if (!dictionaryLocale) {
5715
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5716
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5717
- return sourceObject;
5554
+ if (!dictionaryLocale) return this.getSourceDictionaryObject(id);
5555
+ const dictionaryCache = await this.localesCache.getOrLoadDictionary(dictionaryLocale);
5556
+ const targetObject = dictionaryCache.getValue(id);
5557
+ const sourceObject = this.getSourceDictionaryObject(id, { throwOnMissing: false });
5558
+ if (sourceObject === void 0) {
5559
+ if (targetObject !== void 0) return targetObject;
5560
+ throw new DictionarySourceNotFoundError(id);
5718
5561
  }
5719
- let dictionaryCache = this.localesDictionaryCache.get(dictionaryLocale);
5720
- if (!dictionaryCache) dictionaryCache = await this.localesDictionaryCache.miss(dictionaryLocale);
5721
- let dictionaryObject = dictionaryCache.getObj(id);
5722
- if (dictionaryObject === void 0) {
5723
- const sourceObject = this.localesDictionaryCache.get(this.config.defaultLocale)?.getObj(id);
5724
- if (sourceObject === void 0) throw new DictionarySourceNotFoundError(id);
5725
- dictionaryObject = await dictionaryCache.missObj(id, sourceObject);
5726
- }
5727
- return dictionaryObject;
5562
+ return await dictionaryCache.materializeValue(id, sourceObject, targetObject);
5728
5563
  } catch (error) {
5729
5564
  this.handleError(error);
5730
5565
  return;
5731
5566
  }
5732
5567
  }
5568
+ async translateDictionaryEntry(locale, id, sourceEntry) {
5569
+ const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5570
+ 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.`);
5571
+ return translation;
5572
+ }
5573
+ getSourceDictionaryEntry(id) {
5574
+ const sourceEntry = this.getDefaultDictionaryCache()?.getEntry(id);
5575
+ if (sourceEntry === void 0) throw new DictionarySourceNotFoundError(id);
5576
+ return sourceEntry;
5577
+ }
5578
+ getSourceDictionaryObject(id, { throwOnMissing = true } = {}) {
5579
+ const sourceObject = this.getDefaultDictionaryCache()?.getValue(id);
5580
+ if (sourceObject === void 0 && throwOnMissing) throw new DictionarySourceNotFoundError(id);
5581
+ return sourceObject;
5582
+ }
5583
+ getDefaultDictionaryCache() {
5584
+ return this.localesCache.getDictionary(this.config.defaultLocale);
5585
+ }
5586
+ resolveDictionaryCacheLocale(locale) {
5587
+ return this.resolveCacheLocale(locale) ?? this.config.defaultLocale;
5588
+ }
5733
5589
  /**
5734
5590
  * Just lookup a translation
5735
5591
  */
@@ -5737,7 +5593,7 @@ var I18nManager = class extends EventEmitter {
5737
5593
  try {
5738
5594
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5739
5595
  if (!translationLocale) return message;
5740
- const txCache = this.localesCache.get(translationLocale);
5596
+ const txCache = this.localesCache.getTranslations(translationLocale);
5741
5597
  if (!txCache) return void 0;
5742
5598
  return txCache.get({
5743
5599
  message,
@@ -5775,9 +5631,7 @@ var I18nManager = class extends EventEmitter {
5775
5631
  if (!translationLocale) return (message) => message;
5776
5632
  const resolvedPrefetchEntries = resolvePrefetchEntriesByLocale(prefetchEntries, translationLocale, (entryLocale) => this.resolveCacheLocale(entryLocale) ?? this.resolveLocale(entryLocale));
5777
5633
  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}`);
5778
- let txCache = this.localesCache.get(translationLocale);
5779
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5780
- if (!txCache) return () => void 0;
5634
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5781
5635
  await Promise.all(resolvedPrefetchEntries.filter((entry) => txCache.get(entry) == null).map((entry) => txCache.miss(entry)));
5782
5636
  return (message, options = {}) => {
5783
5637
  return txCache.get({
@@ -5879,8 +5733,7 @@ var I18nManager = class extends EventEmitter {
5879
5733
  async lookupTranslationWithFallbackResolved(locale, message, options) {
5880
5734
  const { translationLocale, options: lookupOptions } = this.resolveLookupParams(locale, options);
5881
5735
  if (!translationLocale) return message;
5882
- let txCache = this.localesCache.get(translationLocale);
5883
- if (!txCache) txCache = await this.localesCache.miss(translationLocale);
5736
+ const txCache = await this.localesCache.getOrLoadTranslations(translationLocale);
5884
5737
  let translation = txCache.get({
5885
5738
  message,
5886
5739
  options: lookupOptions
@@ -5892,14 +5745,6 @@ var I18nManager = class extends EventEmitter {
5892
5745
  return translation;
5893
5746
  }
5894
5747
  /**
5895
- * Runtime lookup function for dictionaries
5896
- */
5897
- async dictionaryRuntimeTranslate(locale, id, sourceEntry) {
5898
- const translation = await this.lookupTranslationWithFallbackResolved(locale, sourceEntry.entry, resolveDictionaryLookupOptions(sourceEntry.options));
5899
- 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.`);
5900
- return translation;
5901
- }
5902
- /**
5903
5748
  * A helper function to create a gt class that is locale agnostic
5904
5749
  * This is helpful for when our getLocale function is bound to a
5905
5750
  * specific context
@@ -5996,28 +5841,6 @@ function resolvePrefetchEntriesByLocale(prefetchEntries, locale, resolveLocale)
5996
5841
  }
5997
5842
  });
5998
5843
  }
5999
- /**
6000
- * Helper function for creating a translation loader
6001
- */
6002
- function createTranslationLoader(params) {
6003
- return routeCreateTranslationLoader({
6004
- loadTranslations: params.loadTranslations,
6005
- type: getLoadTranslationsType(params),
6006
- remoteTranslationLoaderParams: {
6007
- cacheUrl: params.cacheUrl,
6008
- projectId: params.projectId,
6009
- _versionId: params._versionId,
6010
- _branchId: params._branchId,
6011
- customMapping: params.customMapping
6012
- }
6013
- });
6014
- }
6015
- /**
6016
- * Helper function for creating a dictionary loader
6017
- */
6018
- function createDictionaryLoader(params) {
6019
- return params.loadDictionary ?? (() => Promise.resolve({}));
6020
- }
6021
5844
  let i18nManager = void 0;
6022
5845
  let fallbackDefaultLocale = "en";
6023
5846
  const fallbackConditionStore = { getLocale: () => fallbackDefaultLocale };