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