intor 2.2.2 → 2.2.4

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/index.cjs CHANGED
@@ -137,11 +137,11 @@ async function collectFileEntries({
137
137
  return results;
138
138
  }
139
139
 
140
- // src/modules/messages/shared/utils/is-namespace-messages.ts
140
+ // src/modules/messages/shared/utils/is-valid-messages.ts
141
141
  function isPlainObject(value) {
142
142
  return typeof value === "object" && value !== null && !Array.isArray(value);
143
143
  }
144
- function isNamespaceMessages(value) {
144
+ function isValidMessages(value) {
145
145
  if (!isPlainObject(value)) return false;
146
146
  const stack = [value];
147
147
  while (stack.length > 0) {
@@ -157,22 +157,17 @@ function isNamespaceMessages(value) {
157
157
  }
158
158
  return true;
159
159
  }
160
-
161
- // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
162
160
  async function jsonReader(filePath, readFile = fs__default.default.readFile) {
163
161
  const raw = await readFile(filePath, "utf8");
164
162
  const parsed = JSON.parse(raw);
165
- if (!isNamespaceMessages(parsed)) {
166
- throw new Error("JSON file does not match NamespaceMessages structure");
167
- }
168
163
  return parsed;
169
164
  }
170
165
 
171
166
  // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
172
- function nestObjectFromPath(path4, value) {
167
+ function nestObjectFromPath(path5, value) {
173
168
  let obj = value;
174
- for (let i = path4.length - 1; i >= 0; i--) {
175
- obj = { [path4[i]]: obj };
169
+ for (let i = path5.length - 1; i >= 0; i--) {
170
+ obj = { [path5[i]]: obj };
176
171
  }
177
172
  return obj;
178
173
  }
@@ -181,7 +176,7 @@ function nestObjectFromPath(path4, value) {
181
176
  async function parseFileEntries({
182
177
  fileEntries,
183
178
  limit,
184
- extraOptions: { messageFileReader, loggerOptions } = {}
179
+ extraOptions: { messagesReader, loggerOptions } = {}
185
180
  }) {
186
181
  const baseLogger = getLogger({ ...loggerOptions });
187
182
  const logger = baseLogger.child({ scope: "parse-file-entries" });
@@ -190,11 +185,17 @@ async function parseFileEntries({
190
185
  ({ namespace, segments, basename, fullPath }) => limit(async () => {
191
186
  try {
192
187
  const segsWithoutNs = segments.slice(1);
193
- const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
188
+ const ext = path__default.default.extname(fullPath);
189
+ const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
190
+ if (!isValidMessages(json)) {
191
+ throw new Error(
192
+ "JSON file does not match NamespaceMessages structure"
193
+ );
194
+ }
194
195
  const isIndex = basename === "index";
195
196
  const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
196
- const namespaceMessages = nestObjectFromPath(keyPath, json);
197
- parsedFileEntries.push({ namespace, namespaceMessages });
197
+ const nested = nestObjectFromPath(keyPath, json);
198
+ parsedFileEntries.push({ namespace, messages: nested });
198
199
  logger.trace("Parsed file.", { path: fullPath });
199
200
  } catch (error) {
200
201
  logger.error("Failed to read or parse file.", {
@@ -206,13 +207,13 @@ async function parseFileEntries({
206
207
  );
207
208
  await Promise.all(tasks);
208
209
  const result = {};
209
- for (const { namespace, namespaceMessages } of parsedFileEntries) {
210
+ for (const { namespace, messages } of parsedFileEntries) {
210
211
  if (namespace === "index") {
211
- merge__default.default(result, namespaceMessages);
212
+ merge__default.default(result, messages);
212
213
  } else {
213
214
  result[namespace] = merge__default.default(
214
215
  result[namespace] ?? {},
215
- namespaceMessages
216
+ messages
216
217
  );
217
218
  }
218
219
  }
@@ -225,7 +226,7 @@ var readLocaleMessages = async ({
225
226
  rootDir = "messages",
226
227
  locale,
227
228
  namespaces,
228
- extraOptions: { exts, messageFileReader, loggerOptions } = {}
229
+ extraOptions: { exts, messagesReader, loggerOptions } = {}
229
230
  }) => {
230
231
  const fileEntries = await collectFileEntries({
231
232
  rootDir: path__default.default.resolve(process.cwd(), rootDir, locale),
@@ -236,7 +237,7 @@ var readLocaleMessages = async ({
236
237
  const namespaceMessages = await parseFileEntries({
237
238
  fileEntries,
238
239
  limit,
239
- extraOptions: { messageFileReader, loggerOptions }
240
+ extraOptions: { messagesReader, loggerOptions }
240
241
  });
241
242
  const localeMessages = { [locale]: namespaceMessages };
242
243
  return localeMessages;
@@ -454,8 +455,9 @@ var loadLocalMessages = async ({
454
455
  cacheOptions = DEFAULT_CACHE_OPTIONS,
455
456
  loggerOptions = { id: "default" },
456
457
  exts,
457
- messageFileReader
458
- } = {}
458
+ messagesReader
459
+ } = {},
460
+ allowCacheWrite = false
459
461
  }) => {
460
462
  const baseLogger = getLogger({ ...loggerOptions });
461
463
  const logger = baseLogger.child({ scope: "load-local-messages" });
@@ -489,7 +491,7 @@ var loadLocalMessages = async ({
489
491
  rootDir,
490
492
  locale: candidateLocale,
491
493
  namespaces,
492
- extraOptions: { loggerOptions, exts, messageFileReader }
494
+ extraOptions: { loggerOptions, exts, messagesReader }
493
495
  });
494
496
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
495
497
  messages = result;
@@ -502,7 +504,7 @@ var loadLocalMessages = async ({
502
504
  });
503
505
  }
504
506
  }
505
- if (cacheOptions.enabled && key && messages) {
507
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
506
508
  await pool?.set(key, messages, cacheOptions.ttl);
507
509
  }
508
510
  const end = perf_hooks.performance.now();
@@ -541,7 +543,7 @@ var fetchLocaleMessages = async ({
541
543
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
542
544
  }
543
545
  const data = await response.json();
544
- if (!isNamespaceMessages(data[locale])) {
546
+ if (!isValidMessages(data[locale])) {
545
547
  throw new Error("JSON file does not match NamespaceMessages structure");
546
548
  }
547
549
  return data;
@@ -586,7 +588,8 @@ var loadRemoteMessages = async ({
586
588
  extraOptions: {
587
589
  cacheOptions = DEFAULT_CACHE_OPTIONS,
588
590
  loggerOptions = { id: "default" }
589
- } = {}
591
+ } = {},
592
+ allowCacheWrite
590
593
  }) => {
591
594
  const baseLogger = getLogger({ ...loggerOptions });
592
595
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -630,7 +633,7 @@ var loadRemoteMessages = async ({
630
633
  });
631
634
  }
632
635
  }
633
- if (cacheOptions.enabled && key && messages) {
636
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
634
637
  await pool?.set(key, messages, cacheOptions.ttl);
635
638
  }
636
639
  const end = performance.now();
@@ -647,7 +650,8 @@ var loadMessages = async ({
647
650
  config,
648
651
  locale,
649
652
  pathname = "",
650
- extraOptions: { exts, messageFileReader } = {}
653
+ extraOptions: { exts, messagesReader } = {},
654
+ allowCacheWrite = false
651
655
  }) => {
652
656
  const baseLogger = getLogger({ id: config.id, ...config.logger });
653
657
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -683,8 +687,9 @@ var loadMessages = async ({
683
687
  cacheOptions: config.cache,
684
688
  loggerOptions: { id: config.id, ...config.logger },
685
689
  exts,
686
- messageFileReader
687
- }
690
+ messagesReader
691
+ },
692
+ allowCacheWrite
688
693
  });
689
694
  } else if (type === "remote") {
690
695
  loadedMessages = await loadRemoteMessages({
@@ -728,8 +733,9 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
728
733
  pathname,
729
734
  extraOptions: {
730
735
  exts: loadMessagesOptions.exts,
731
- messageFileReader: loadMessagesOptions.messageFileReader
732
- }
736
+ messagesReader: loadMessagesOptions.messagesReader
737
+ },
738
+ allowCacheWrite: true
733
739
  });
734
740
  }
735
741
  const mergedMessages = mergeMessages(messages, loadedMessages);
package/dist/index.d.cts CHANGED
@@ -165,8 +165,44 @@ interface IntorResult {
165
165
  messages: LocaleMessages;
166
166
  }
167
167
 
168
- type NamespaceMessages = Record<string, NestedMessage>;
169
- type MessageFileReader = (filePath: string) => Promise<NamespaceMessages>;
168
+ /**
169
+ * Represents a collection of localized messages.
170
+ *
171
+ * - Each key is a namespace or message identifier, and the value is a
172
+ * `NestedMessage` object that can contain nested message structures.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * const messages: Messages = {
177
+ * ui: {
178
+ * greeting: "Hello",
179
+ * farewell: "Goodbye"
180
+ * },
181
+ * errors: {
182
+ * network: "Network error occurred"
183
+ * }
184
+ * };
185
+ * ```
186
+ */
187
+ type Messages = Record<string, NestedMessage>;
188
+ /**
189
+ * A function that reads messages from a given file path.
190
+ *
191
+ * - This function is expected to return a `Promise` that resolves to a `Messages` object.
192
+ * - It can be implemented to support different file formats such as JSON, YAML, or others.
193
+ *
194
+ * @param filePath - The path to the message file to read.
195
+ * @returns A Promise that resolves to a `Messages` object.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * const reader: MessagesReader = async (filePath) => {
200
+ * const content = await fs.promises.readFile(filePath, "utf-8");
201
+ * return JSON.parse(content) as Messages;
202
+ * };
203
+ * ```
204
+ */
205
+ type MessagesReader = (filePath: string) => Promise<Messages>;
170
206
 
171
207
  type LoadMessagesOptions = {
172
208
  config: IntorResolvedConfig;
@@ -174,8 +210,9 @@ type LoadMessagesOptions = {
174
210
  pathname?: string;
175
211
  extraOptions?: {
176
212
  exts?: string[];
177
- messageFileReader?: MessageFileReader;
213
+ messagesReader?: MessagesReader;
178
214
  };
215
+ allowCacheWrite?: boolean;
179
216
  };
180
217
  type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
181
218
 
@@ -187,7 +224,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
187
224
  * - Apply fallback locales if needed.
188
225
  * - Cache messages if enabled (handled by underlying loader, not this function directly).
189
226
  */
190
- declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messageFileReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
227
+ declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, allowCacheWrite, }: LoadMessagesOptions) => LoadMessagesResult<C>;
191
228
 
192
229
  /**
193
230
  * Global messages pool (cross-module + hot-reload safe)
@@ -215,8 +252,9 @@ interface LoadLocalMessagesOptions {
215
252
  id?: string;
216
253
  };
217
254
  exts?: string[];
218
- messageFileReader?: MessageFileReader;
255
+ messagesReader?: MessagesReader;
219
256
  };
257
+ allowCacheWrite?: boolean;
220
258
  }
221
259
 
222
260
  /**
@@ -227,7 +265,7 @@ interface LoadLocalMessagesOptions {
227
265
  * - Cache messages if enabled.
228
266
  * - Limit concurrent file reads for performance.
229
267
  */
230
- declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messageFileReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
268
+ declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, allowCacheWrite, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
231
269
 
232
270
  interface LoadRemoteMessagesOptions {
233
271
  pool?: MessagesPool;
@@ -243,6 +281,7 @@ interface LoadRemoteMessagesOptions {
243
281
  id?: string;
244
282
  };
245
283
  };
284
+ allowCacheWrite?: boolean;
246
285
  }
247
286
 
248
287
  /**
@@ -251,7 +290,7 @@ interface LoadRemoteMessagesOptions {
251
290
  * - Fetch messages for a target locale with optional fallback locales.
252
291
  * - Cache messages if enabled.
253
292
  */
254
- declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
293
+ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, allowCacheWrite, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
255
294
 
256
295
  /**
257
296
  * Entry point for initializing Intor.
@@ -262,7 +301,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
262
301
  */
263
302
  declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
264
303
  exts?: string[];
265
- messageFileReader?: MessageFileReader;
304
+ messagesReader?: MessagesReader;
266
305
  }) => Promise<IntorResult>;
267
306
 
268
307
  /** Base properties shared by all translator instances. */
@@ -416,4 +455,4 @@ declare global {
416
455
  */
417
456
  declare function clearLoggerPool(): void;
418
457
 
419
- export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type MessageFileReader, type NamespaceMessages, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
458
+ export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type Messages, type MessagesReader, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
package/dist/index.d.ts CHANGED
@@ -165,8 +165,44 @@ interface IntorResult {
165
165
  messages: LocaleMessages;
166
166
  }
167
167
 
168
- type NamespaceMessages = Record<string, NestedMessage>;
169
- type MessageFileReader = (filePath: string) => Promise<NamespaceMessages>;
168
+ /**
169
+ * Represents a collection of localized messages.
170
+ *
171
+ * - Each key is a namespace or message identifier, and the value is a
172
+ * `NestedMessage` object that can contain nested message structures.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * const messages: Messages = {
177
+ * ui: {
178
+ * greeting: "Hello",
179
+ * farewell: "Goodbye"
180
+ * },
181
+ * errors: {
182
+ * network: "Network error occurred"
183
+ * }
184
+ * };
185
+ * ```
186
+ */
187
+ type Messages = Record<string, NestedMessage>;
188
+ /**
189
+ * A function that reads messages from a given file path.
190
+ *
191
+ * - This function is expected to return a `Promise` that resolves to a `Messages` object.
192
+ * - It can be implemented to support different file formats such as JSON, YAML, or others.
193
+ *
194
+ * @param filePath - The path to the message file to read.
195
+ * @returns A Promise that resolves to a `Messages` object.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * const reader: MessagesReader = async (filePath) => {
200
+ * const content = await fs.promises.readFile(filePath, "utf-8");
201
+ * return JSON.parse(content) as Messages;
202
+ * };
203
+ * ```
204
+ */
205
+ type MessagesReader = (filePath: string) => Promise<Messages>;
170
206
 
171
207
  type LoadMessagesOptions = {
172
208
  config: IntorResolvedConfig;
@@ -174,8 +210,9 @@ type LoadMessagesOptions = {
174
210
  pathname?: string;
175
211
  extraOptions?: {
176
212
  exts?: string[];
177
- messageFileReader?: MessageFileReader;
213
+ messagesReader?: MessagesReader;
178
214
  };
215
+ allowCacheWrite?: boolean;
179
216
  };
180
217
  type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMessages<C> | undefined>;
181
218
 
@@ -187,7 +224,7 @@ type LoadMessagesResult<C extends GenConfigKeys = "__default__"> = Promise<GenMe
187
224
  * - Apply fallback locales if needed.
188
225
  * - Cache messages if enabled (handled by underlying loader, not this function directly).
189
226
  */
190
- declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messageFileReader }, }: LoadMessagesOptions) => LoadMessagesResult<C>;
227
+ declare const loadMessages: <C extends GenConfigKeys = "__default__">({ config, locale, pathname, extraOptions: { exts, messagesReader }, allowCacheWrite, }: LoadMessagesOptions) => LoadMessagesResult<C>;
191
228
 
192
229
  /**
193
230
  * Global messages pool (cross-module + hot-reload safe)
@@ -215,8 +252,9 @@ interface LoadLocalMessagesOptions {
215
252
  id?: string;
216
253
  };
217
254
  exts?: string[];
218
- messageFileReader?: MessageFileReader;
255
+ messagesReader?: MessagesReader;
219
256
  };
257
+ allowCacheWrite?: boolean;
220
258
  }
221
259
 
222
260
  /**
@@ -227,7 +265,7 @@ interface LoadLocalMessagesOptions {
227
265
  * - Cache messages if enabled.
228
266
  * - Limit concurrent file reads for performance.
229
267
  */
230
- declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messageFileReader, }, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
268
+ declare const loadLocalMessages: ({ pool, rootDir, locale, fallbackLocales, namespaces, extraOptions: { concurrency, cacheOptions, loggerOptions, exts, messagesReader, }, allowCacheWrite, }: LoadLocalMessagesOptions) => Promise<LocaleMessages | undefined>;
231
269
 
232
270
  interface LoadRemoteMessagesOptions {
233
271
  pool?: MessagesPool;
@@ -243,6 +281,7 @@ interface LoadRemoteMessagesOptions {
243
281
  id?: string;
244
282
  };
245
283
  };
284
+ allowCacheWrite?: boolean;
246
285
  }
247
286
 
248
287
  /**
@@ -251,7 +290,7 @@ interface LoadRemoteMessagesOptions {
251
290
  * - Fetch messages for a target locale with optional fallback locales.
252
291
  * - Cache messages if enabled.
253
292
  */
254
- declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
293
+ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, locale, fallbackLocales, namespaces, extraOptions: { cacheOptions, loggerOptions, }, allowCacheWrite, }: LoadRemoteMessagesOptions) => Promise<LocaleMessages | undefined>;
255
294
 
256
295
  /**
257
296
  * Entry point for initializing Intor.
@@ -262,7 +301,7 @@ declare const loadRemoteMessages: ({ pool, rootDir, remoteUrl, remoteHeaders, lo
262
301
  */
263
302
  declare const intor: (config: IntorResolvedConfig, i18nContext: GetI18nContext | Partial<I18nContext>, loadMessagesOptions?: {
264
303
  exts?: string[];
265
- messageFileReader?: MessageFileReader;
304
+ messagesReader?: MessagesReader;
266
305
  }) => Promise<IntorResult>;
267
306
 
268
307
  /** Base properties shared by all translator instances. */
@@ -416,4 +455,4 @@ declare global {
416
455
  */
417
456
  declare function clearLoggerPool(): void;
418
457
 
419
- export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type MessageFileReader, type NamespaceMessages, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
458
+ export { type I18nContext, type IntorResult, type LoadLocalMessagesOptions, type LoadRemoteMessagesOptions, type Messages, type MessagesReader, PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadLocalMessages, loadMessages, loadRemoteMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };
package/dist/index.js CHANGED
@@ -128,11 +128,11 @@ async function collectFileEntries({
128
128
  return results;
129
129
  }
130
130
 
131
- // src/modules/messages/shared/utils/is-namespace-messages.ts
131
+ // src/modules/messages/shared/utils/is-valid-messages.ts
132
132
  function isPlainObject(value) {
133
133
  return typeof value === "object" && value !== null && !Array.isArray(value);
134
134
  }
135
- function isNamespaceMessages(value) {
135
+ function isValidMessages(value) {
136
136
  if (!isPlainObject(value)) return false;
137
137
  const stack = [value];
138
138
  while (stack.length > 0) {
@@ -148,22 +148,17 @@ function isNamespaceMessages(value) {
148
148
  }
149
149
  return true;
150
150
  }
151
-
152
- // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
153
151
  async function jsonReader(filePath, readFile = fs.readFile) {
154
152
  const raw = await readFile(filePath, "utf8");
155
153
  const parsed = JSON.parse(raw);
156
- if (!isNamespaceMessages(parsed)) {
157
- throw new Error("JSON file does not match NamespaceMessages structure");
158
- }
159
154
  return parsed;
160
155
  }
161
156
 
162
157
  // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
163
- function nestObjectFromPath(path4, value) {
158
+ function nestObjectFromPath(path5, value) {
164
159
  let obj = value;
165
- for (let i = path4.length - 1; i >= 0; i--) {
166
- obj = { [path4[i]]: obj };
160
+ for (let i = path5.length - 1; i >= 0; i--) {
161
+ obj = { [path5[i]]: obj };
167
162
  }
168
163
  return obj;
169
164
  }
@@ -172,7 +167,7 @@ function nestObjectFromPath(path4, value) {
172
167
  async function parseFileEntries({
173
168
  fileEntries,
174
169
  limit,
175
- extraOptions: { messageFileReader, loggerOptions } = {}
170
+ extraOptions: { messagesReader, loggerOptions } = {}
176
171
  }) {
177
172
  const baseLogger = getLogger({ ...loggerOptions });
178
173
  const logger = baseLogger.child({ scope: "parse-file-entries" });
@@ -181,11 +176,17 @@ async function parseFileEntries({
181
176
  ({ namespace, segments, basename, fullPath }) => limit(async () => {
182
177
  try {
183
178
  const segsWithoutNs = segments.slice(1);
184
- const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
179
+ const ext = path.extname(fullPath);
180
+ const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
181
+ if (!isValidMessages(json)) {
182
+ throw new Error(
183
+ "JSON file does not match NamespaceMessages structure"
184
+ );
185
+ }
185
186
  const isIndex = basename === "index";
186
187
  const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
187
- const namespaceMessages = nestObjectFromPath(keyPath, json);
188
- parsedFileEntries.push({ namespace, namespaceMessages });
188
+ const nested = nestObjectFromPath(keyPath, json);
189
+ parsedFileEntries.push({ namespace, messages: nested });
189
190
  logger.trace("Parsed file.", { path: fullPath });
190
191
  } catch (error) {
191
192
  logger.error("Failed to read or parse file.", {
@@ -197,13 +198,13 @@ async function parseFileEntries({
197
198
  );
198
199
  await Promise.all(tasks);
199
200
  const result = {};
200
- for (const { namespace, namespaceMessages } of parsedFileEntries) {
201
+ for (const { namespace, messages } of parsedFileEntries) {
201
202
  if (namespace === "index") {
202
- merge(result, namespaceMessages);
203
+ merge(result, messages);
203
204
  } else {
204
205
  result[namespace] = merge(
205
206
  result[namespace] ?? {},
206
- namespaceMessages
207
+ messages
207
208
  );
208
209
  }
209
210
  }
@@ -216,7 +217,7 @@ var readLocaleMessages = async ({
216
217
  rootDir = "messages",
217
218
  locale,
218
219
  namespaces,
219
- extraOptions: { exts, messageFileReader, loggerOptions } = {}
220
+ extraOptions: { exts, messagesReader, loggerOptions } = {}
220
221
  }) => {
221
222
  const fileEntries = await collectFileEntries({
222
223
  rootDir: path.resolve(process.cwd(), rootDir, locale),
@@ -227,7 +228,7 @@ var readLocaleMessages = async ({
227
228
  const namespaceMessages = await parseFileEntries({
228
229
  fileEntries,
229
230
  limit,
230
- extraOptions: { messageFileReader, loggerOptions }
231
+ extraOptions: { messagesReader, loggerOptions }
231
232
  });
232
233
  const localeMessages = { [locale]: namespaceMessages };
233
234
  return localeMessages;
@@ -445,8 +446,9 @@ var loadLocalMessages = async ({
445
446
  cacheOptions = DEFAULT_CACHE_OPTIONS,
446
447
  loggerOptions = { id: "default" },
447
448
  exts,
448
- messageFileReader
449
- } = {}
449
+ messagesReader
450
+ } = {},
451
+ allowCacheWrite = false
450
452
  }) => {
451
453
  const baseLogger = getLogger({ ...loggerOptions });
452
454
  const logger = baseLogger.child({ scope: "load-local-messages" });
@@ -480,7 +482,7 @@ var loadLocalMessages = async ({
480
482
  rootDir,
481
483
  locale: candidateLocale,
482
484
  namespaces,
483
- extraOptions: { loggerOptions, exts, messageFileReader }
485
+ extraOptions: { loggerOptions, exts, messagesReader }
484
486
  });
485
487
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
486
488
  messages = result;
@@ -493,7 +495,7 @@ var loadLocalMessages = async ({
493
495
  });
494
496
  }
495
497
  }
496
- if (cacheOptions.enabled && key && messages) {
498
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
497
499
  await pool?.set(key, messages, cacheOptions.ttl);
498
500
  }
499
501
  const end = performance$1.now();
@@ -532,7 +534,7 @@ var fetchLocaleMessages = async ({
532
534
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
533
535
  }
534
536
  const data = await response.json();
535
- if (!isNamespaceMessages(data[locale])) {
537
+ if (!isValidMessages(data[locale])) {
536
538
  throw new Error("JSON file does not match NamespaceMessages structure");
537
539
  }
538
540
  return data;
@@ -577,7 +579,8 @@ var loadRemoteMessages = async ({
577
579
  extraOptions: {
578
580
  cacheOptions = DEFAULT_CACHE_OPTIONS,
579
581
  loggerOptions = { id: "default" }
580
- } = {}
582
+ } = {},
583
+ allowCacheWrite
581
584
  }) => {
582
585
  const baseLogger = getLogger({ ...loggerOptions });
583
586
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -621,7 +624,7 @@ var loadRemoteMessages = async ({
621
624
  });
622
625
  }
623
626
  }
624
- if (cacheOptions.enabled && key && messages) {
627
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
625
628
  await pool?.set(key, messages, cacheOptions.ttl);
626
629
  }
627
630
  const end = performance.now();
@@ -638,7 +641,8 @@ var loadMessages = async ({
638
641
  config,
639
642
  locale,
640
643
  pathname = "",
641
- extraOptions: { exts, messageFileReader } = {}
644
+ extraOptions: { exts, messagesReader } = {},
645
+ allowCacheWrite = false
642
646
  }) => {
643
647
  const baseLogger = getLogger({ id: config.id, ...config.logger });
644
648
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -674,8 +678,9 @@ var loadMessages = async ({
674
678
  cacheOptions: config.cache,
675
679
  loggerOptions: { id: config.id, ...config.logger },
676
680
  exts,
677
- messageFileReader
678
- }
681
+ messagesReader
682
+ },
683
+ allowCacheWrite
679
684
  });
680
685
  } else if (type === "remote") {
681
686
  loadedMessages = await loadRemoteMessages({
@@ -719,8 +724,9 @@ var intor = async (config, i18nContext, loadMessagesOptions = {}) => {
719
724
  pathname,
720
725
  extraOptions: {
721
726
  exts: loadMessagesOptions.exts,
722
- messageFileReader: loadMessagesOptions.messageFileReader
723
- }
727
+ messagesReader: loadMessagesOptions.messagesReader
728
+ },
729
+ allowCacheWrite: true
724
730
  });
725
731
  }
726
732
  const mergedMessages = mergeMessages(messages, loadedMessages);
@@ -96,11 +96,11 @@ function getLogger({
96
96
  return logger;
97
97
  }
98
98
 
99
- // src/modules/messages/shared/utils/is-namespace-messages.ts
99
+ // src/modules/messages/shared/utils/is-valid-messages.ts
100
100
  function isPlainObject(value) {
101
101
  return typeof value === "object" && value !== null && !Array.isArray(value);
102
102
  }
103
- function isNamespaceMessages(value) {
103
+ function isValidMessages(value) {
104
104
  if (!isPlainObject(value)) return false;
105
105
  const stack = [value];
106
106
  while (stack.length > 0) {
@@ -340,7 +340,7 @@ var fetchLocaleMessages = async ({
340
340
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
341
341
  }
342
342
  const data = await response.json();
343
- if (!isNamespaceMessages(data[locale])) {
343
+ if (!isValidMessages(data[locale])) {
344
344
  throw new Error("JSON file does not match NamespaceMessages structure");
345
345
  }
346
346
  return data;
@@ -385,7 +385,8 @@ var loadRemoteMessages = async ({
385
385
  extraOptions: {
386
386
  cacheOptions = DEFAULT_CACHE_OPTIONS,
387
387
  loggerOptions = { id: "default" }
388
- } = {}
388
+ } = {},
389
+ allowCacheWrite
389
390
  }) => {
390
391
  const baseLogger = getLogger({ ...loggerOptions });
391
392
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -429,7 +430,7 @@ var loadRemoteMessages = async ({
429
430
  });
430
431
  }
431
432
  }
432
- if (cacheOptions.enabled && key && messages) {
433
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
433
434
  await pool?.set(key, messages, cacheOptions.ttl);
434
435
  }
435
436
  const end = performance.now();
@@ -69,11 +69,11 @@ function getLogger({
69
69
  return logger;
70
70
  }
71
71
 
72
- // src/modules/messages/shared/utils/is-namespace-messages.ts
72
+ // src/modules/messages/shared/utils/is-valid-messages.ts
73
73
  function isPlainObject(value) {
74
74
  return typeof value === "object" && value !== null && !Array.isArray(value);
75
75
  }
76
- function isNamespaceMessages(value) {
76
+ function isValidMessages(value) {
77
77
  if (!isPlainObject(value)) return false;
78
78
  const stack = [value];
79
79
  while (stack.length > 0) {
@@ -313,7 +313,7 @@ var fetchLocaleMessages = async ({
313
313
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
314
314
  }
315
315
  const data = await response.json();
316
- if (!isNamespaceMessages(data[locale])) {
316
+ if (!isValidMessages(data[locale])) {
317
317
  throw new Error("JSON file does not match NamespaceMessages structure");
318
318
  }
319
319
  return data;
@@ -358,7 +358,8 @@ var loadRemoteMessages = async ({
358
358
  extraOptions: {
359
359
  cacheOptions = DEFAULT_CACHE_OPTIONS,
360
360
  loggerOptions = { id: "default" }
361
- } = {}
361
+ } = {},
362
+ allowCacheWrite
362
363
  }) => {
363
364
  const baseLogger = getLogger({ ...loggerOptions });
364
365
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -402,7 +403,7 @@ var loadRemoteMessages = async ({
402
403
  });
403
404
  }
404
405
  }
405
- if (cacheOptions.enabled && key && messages) {
406
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
406
407
  await pool?.set(key, messages, cacheOptions.ttl);
407
408
  }
408
409
  const end = performance.now();
@@ -309,11 +309,11 @@ async function collectFileEntries({
309
309
  return results;
310
310
  }
311
311
 
312
- // src/modules/messages/shared/utils/is-namespace-messages.ts
312
+ // src/modules/messages/shared/utils/is-valid-messages.ts
313
313
  function isPlainObject(value) {
314
314
  return typeof value === "object" && value !== null && !Array.isArray(value);
315
315
  }
316
- function isNamespaceMessages(value) {
316
+ function isValidMessages(value) {
317
317
  if (!isPlainObject(value)) return false;
318
318
  const stack = [value];
319
319
  while (stack.length > 0) {
@@ -329,22 +329,17 @@ function isNamespaceMessages(value) {
329
329
  }
330
330
  return true;
331
331
  }
332
-
333
- // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
334
332
  async function jsonReader(filePath, readFile = fs__default.default.readFile) {
335
333
  const raw = await readFile(filePath, "utf8");
336
334
  const parsed = JSON.parse(raw);
337
- if (!isNamespaceMessages(parsed)) {
338
- throw new Error("JSON file does not match NamespaceMessages structure");
339
- }
340
335
  return parsed;
341
336
  }
342
337
 
343
338
  // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
344
- function nestObjectFromPath(path4, value) {
339
+ function nestObjectFromPath(path5, value) {
345
340
  let obj = value;
346
- for (let i = path4.length - 1; i >= 0; i--) {
347
- obj = { [path4[i]]: obj };
341
+ for (let i = path5.length - 1; i >= 0; i--) {
342
+ obj = { [path5[i]]: obj };
348
343
  }
349
344
  return obj;
350
345
  }
@@ -353,7 +348,7 @@ function nestObjectFromPath(path4, value) {
353
348
  async function parseFileEntries({
354
349
  fileEntries,
355
350
  limit,
356
- extraOptions: { messageFileReader, loggerOptions } = {}
351
+ extraOptions: { messagesReader, loggerOptions } = {}
357
352
  }) {
358
353
  const baseLogger = getLogger({ ...loggerOptions });
359
354
  const logger = baseLogger.child({ scope: "parse-file-entries" });
@@ -362,11 +357,17 @@ async function parseFileEntries({
362
357
  ({ namespace, segments, basename, fullPath }) => limit(async () => {
363
358
  try {
364
359
  const segsWithoutNs = segments.slice(1);
365
- const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
360
+ const ext = path__default.default.extname(fullPath);
361
+ const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
362
+ if (!isValidMessages(json)) {
363
+ throw new Error(
364
+ "JSON file does not match NamespaceMessages structure"
365
+ );
366
+ }
366
367
  const isIndex = basename === "index";
367
368
  const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
368
- const namespaceMessages = nestObjectFromPath(keyPath, json);
369
- parsedFileEntries.push({ namespace, namespaceMessages });
369
+ const nested = nestObjectFromPath(keyPath, json);
370
+ parsedFileEntries.push({ namespace, messages: nested });
370
371
  logger.trace("Parsed file.", { path: fullPath });
371
372
  } catch (error) {
372
373
  logger.error("Failed to read or parse file.", {
@@ -378,13 +379,13 @@ async function parseFileEntries({
378
379
  );
379
380
  await Promise.all(tasks);
380
381
  const result = {};
381
- for (const { namespace, namespaceMessages } of parsedFileEntries) {
382
+ for (const { namespace, messages } of parsedFileEntries) {
382
383
  if (namespace === "index") {
383
- merge__default.default(result, namespaceMessages);
384
+ merge__default.default(result, messages);
384
385
  } else {
385
386
  result[namespace] = merge__default.default(
386
387
  result[namespace] ?? {},
387
- namespaceMessages
388
+ messages
388
389
  );
389
390
  }
390
391
  }
@@ -397,7 +398,7 @@ var readLocaleMessages = async ({
397
398
  rootDir = "messages",
398
399
  locale,
399
400
  namespaces,
400
- extraOptions: { exts, messageFileReader, loggerOptions } = {}
401
+ extraOptions: { exts, messagesReader, loggerOptions } = {}
401
402
  }) => {
402
403
  const fileEntries = await collectFileEntries({
403
404
  rootDir: path__default.default.resolve(process.cwd(), rootDir, locale),
@@ -408,7 +409,7 @@ var readLocaleMessages = async ({
408
409
  const namespaceMessages = await parseFileEntries({
409
410
  fileEntries,
410
411
  limit,
411
- extraOptions: { messageFileReader, loggerOptions }
412
+ extraOptions: { messagesReader, loggerOptions }
412
413
  });
413
414
  const localeMessages = { [locale]: namespaceMessages };
414
415
  return localeMessages;
@@ -432,8 +433,9 @@ var loadLocalMessages = async ({
432
433
  cacheOptions = DEFAULT_CACHE_OPTIONS,
433
434
  loggerOptions = { id: "default" },
434
435
  exts,
435
- messageFileReader
436
- } = {}
436
+ messagesReader
437
+ } = {},
438
+ allowCacheWrite = false
437
439
  }) => {
438
440
  const baseLogger = getLogger({ ...loggerOptions });
439
441
  const logger = baseLogger.child({ scope: "load-local-messages" });
@@ -467,7 +469,7 @@ var loadLocalMessages = async ({
467
469
  rootDir,
468
470
  locale: candidateLocale,
469
471
  namespaces,
470
- extraOptions: { loggerOptions, exts, messageFileReader }
472
+ extraOptions: { loggerOptions, exts, messagesReader }
471
473
  });
472
474
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
473
475
  messages = result;
@@ -480,7 +482,7 @@ var loadLocalMessages = async ({
480
482
  });
481
483
  }
482
484
  }
483
- if (cacheOptions.enabled && key && messages) {
485
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
484
486
  await pool?.set(key, messages, cacheOptions.ttl);
485
487
  }
486
488
  const end = perf_hooks.performance.now();
@@ -519,7 +521,7 @@ var fetchLocaleMessages = async ({
519
521
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
520
522
  }
521
523
  const data = await response.json();
522
- if (!isNamespaceMessages(data[locale])) {
524
+ if (!isValidMessages(data[locale])) {
523
525
  throw new Error("JSON file does not match NamespaceMessages structure");
524
526
  }
525
527
  return data;
@@ -564,7 +566,8 @@ var loadRemoteMessages = async ({
564
566
  extraOptions: {
565
567
  cacheOptions = DEFAULT_CACHE_OPTIONS,
566
568
  loggerOptions = { id: "default" }
567
- } = {}
569
+ } = {},
570
+ allowCacheWrite
568
571
  }) => {
569
572
  const baseLogger = getLogger({ ...loggerOptions });
570
573
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -608,7 +611,7 @@ var loadRemoteMessages = async ({
608
611
  });
609
612
  }
610
613
  }
611
- if (cacheOptions.enabled && key && messages) {
614
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
612
615
  await pool?.set(key, messages, cacheOptions.ttl);
613
616
  }
614
617
  const end = performance.now();
@@ -625,7 +628,8 @@ var loadMessages = async ({
625
628
  config,
626
629
  locale,
627
630
  pathname = "",
628
- extraOptions: { exts, messageFileReader } = {}
631
+ extraOptions: { exts, messagesReader } = {},
632
+ allowCacheWrite = false
629
633
  }) => {
630
634
  const baseLogger = getLogger({ id: config.id, ...config.logger });
631
635
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -661,8 +665,9 @@ var loadMessages = async ({
661
665
  cacheOptions: config.cache,
662
666
  loggerOptions: { id: config.id, ...config.logger },
663
667
  exts,
664
- messageFileReader
665
- }
668
+ messagesReader
669
+ },
670
+ allowCacheWrite
666
671
  });
667
672
  } else if (type === "remote") {
668
673
  loadedMessages = await loadRemoteMessages({
@@ -160,7 +160,7 @@ interface I18nContext {
160
160
  *
161
161
  * Next.js adapter implementation: uses `next/headers` and `next/cookies`.
162
162
  */
163
- declare const getI18nContext: <CK extends string = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
163
+ declare const getI18nContext: <CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
164
164
 
165
165
  /** Base properties shared by all translator instances. */
166
166
  interface TranslatorBaseProps<M = unknown> {
@@ -160,7 +160,7 @@ interface I18nContext {
160
160
  *
161
161
  * Next.js adapter implementation: uses `next/headers` and `next/cookies`.
162
162
  */
163
- declare const getI18nContext: <CK extends string = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
163
+ declare const getI18nContext: <CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
164
164
 
165
165
  /** Base properties shared by all translator instances. */
166
166
  interface TranslatorBaseProps<M = unknown> {
@@ -299,11 +299,11 @@ async function collectFileEntries({
299
299
  return results;
300
300
  }
301
301
 
302
- // src/modules/messages/shared/utils/is-namespace-messages.ts
302
+ // src/modules/messages/shared/utils/is-valid-messages.ts
303
303
  function isPlainObject(value) {
304
304
  return typeof value === "object" && value !== null && !Array.isArray(value);
305
305
  }
306
- function isNamespaceMessages(value) {
306
+ function isValidMessages(value) {
307
307
  if (!isPlainObject(value)) return false;
308
308
  const stack = [value];
309
309
  while (stack.length > 0) {
@@ -319,22 +319,17 @@ function isNamespaceMessages(value) {
319
319
  }
320
320
  return true;
321
321
  }
322
-
323
- // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/json-reader.ts
324
322
  async function jsonReader(filePath, readFile = fs.readFile) {
325
323
  const raw = await readFile(filePath, "utf8");
326
324
  const parsed = JSON.parse(raw);
327
- if (!isNamespaceMessages(parsed)) {
328
- throw new Error("JSON file does not match NamespaceMessages structure");
329
- }
330
325
  return parsed;
331
326
  }
332
327
 
333
328
  // src/modules/messages/load-local-messages/read-locale-messages/parse-file-entries/utils/nest-object-from-path.ts
334
- function nestObjectFromPath(path4, value) {
329
+ function nestObjectFromPath(path5, value) {
335
330
  let obj = value;
336
- for (let i = path4.length - 1; i >= 0; i--) {
337
- obj = { [path4[i]]: obj };
331
+ for (let i = path5.length - 1; i >= 0; i--) {
332
+ obj = { [path5[i]]: obj };
338
333
  }
339
334
  return obj;
340
335
  }
@@ -343,7 +338,7 @@ function nestObjectFromPath(path4, value) {
343
338
  async function parseFileEntries({
344
339
  fileEntries,
345
340
  limit,
346
- extraOptions: { messageFileReader, loggerOptions } = {}
341
+ extraOptions: { messagesReader, loggerOptions } = {}
347
342
  }) {
348
343
  const baseLogger = getLogger({ ...loggerOptions });
349
344
  const logger = baseLogger.child({ scope: "parse-file-entries" });
@@ -352,11 +347,17 @@ async function parseFileEntries({
352
347
  ({ namespace, segments, basename, fullPath }) => limit(async () => {
353
348
  try {
354
349
  const segsWithoutNs = segments.slice(1);
355
- const json = await (messageFileReader ? messageFileReader(fullPath) : jsonReader(fullPath));
350
+ const ext = path.extname(fullPath);
351
+ const json = ext !== ".json" && messagesReader ? await messagesReader(fullPath) : await jsonReader(fullPath);
352
+ if (!isValidMessages(json)) {
353
+ throw new Error(
354
+ "JSON file does not match NamespaceMessages structure"
355
+ );
356
+ }
356
357
  const isIndex = basename === "index";
357
358
  const keyPath = isIndex ? segsWithoutNs.slice(0, -1) : segsWithoutNs;
358
- const namespaceMessages = nestObjectFromPath(keyPath, json);
359
- parsedFileEntries.push({ namespace, namespaceMessages });
359
+ const nested = nestObjectFromPath(keyPath, json);
360
+ parsedFileEntries.push({ namespace, messages: nested });
360
361
  logger.trace("Parsed file.", { path: fullPath });
361
362
  } catch (error) {
362
363
  logger.error("Failed to read or parse file.", {
@@ -368,13 +369,13 @@ async function parseFileEntries({
368
369
  );
369
370
  await Promise.all(tasks);
370
371
  const result = {};
371
- for (const { namespace, namespaceMessages } of parsedFileEntries) {
372
+ for (const { namespace, messages } of parsedFileEntries) {
372
373
  if (namespace === "index") {
373
- merge(result, namespaceMessages);
374
+ merge(result, messages);
374
375
  } else {
375
376
  result[namespace] = merge(
376
377
  result[namespace] ?? {},
377
- namespaceMessages
378
+ messages
378
379
  );
379
380
  }
380
381
  }
@@ -387,7 +388,7 @@ var readLocaleMessages = async ({
387
388
  rootDir = "messages",
388
389
  locale,
389
390
  namespaces,
390
- extraOptions: { exts, messageFileReader, loggerOptions } = {}
391
+ extraOptions: { exts, messagesReader, loggerOptions } = {}
391
392
  }) => {
392
393
  const fileEntries = await collectFileEntries({
393
394
  rootDir: path.resolve(process.cwd(), rootDir, locale),
@@ -398,7 +399,7 @@ var readLocaleMessages = async ({
398
399
  const namespaceMessages = await parseFileEntries({
399
400
  fileEntries,
400
401
  limit,
401
- extraOptions: { messageFileReader, loggerOptions }
402
+ extraOptions: { messagesReader, loggerOptions }
402
403
  });
403
404
  const localeMessages = { [locale]: namespaceMessages };
404
405
  return localeMessages;
@@ -422,8 +423,9 @@ var loadLocalMessages = async ({
422
423
  cacheOptions = DEFAULT_CACHE_OPTIONS,
423
424
  loggerOptions = { id: "default" },
424
425
  exts,
425
- messageFileReader
426
- } = {}
426
+ messagesReader
427
+ } = {},
428
+ allowCacheWrite = false
427
429
  }) => {
428
430
  const baseLogger = getLogger({ ...loggerOptions });
429
431
  const logger = baseLogger.child({ scope: "load-local-messages" });
@@ -457,7 +459,7 @@ var loadLocalMessages = async ({
457
459
  rootDir,
458
460
  locale: candidateLocale,
459
461
  namespaces,
460
- extraOptions: { loggerOptions, exts, messageFileReader }
462
+ extraOptions: { loggerOptions, exts, messagesReader }
461
463
  });
462
464
  if (result && Object.values(result[candidateLocale] || {}).length > 0) {
463
465
  messages = result;
@@ -470,7 +472,7 @@ var loadLocalMessages = async ({
470
472
  });
471
473
  }
472
474
  }
473
- if (cacheOptions.enabled && key && messages) {
475
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
474
476
  await pool?.set(key, messages, cacheOptions.ttl);
475
477
  }
476
478
  const end = performance$1.now();
@@ -509,7 +511,7 @@ var fetchLocaleMessages = async ({
509
511
  throw new Error(`HTTP error ${response.status} ${response.statusText}`);
510
512
  }
511
513
  const data = await response.json();
512
- if (!isNamespaceMessages(data[locale])) {
514
+ if (!isValidMessages(data[locale])) {
513
515
  throw new Error("JSON file does not match NamespaceMessages structure");
514
516
  }
515
517
  return data;
@@ -554,7 +556,8 @@ var loadRemoteMessages = async ({
554
556
  extraOptions: {
555
557
  cacheOptions = DEFAULT_CACHE_OPTIONS,
556
558
  loggerOptions = { id: "default" }
557
- } = {}
559
+ } = {},
560
+ allowCacheWrite
558
561
  }) => {
559
562
  const baseLogger = getLogger({ ...loggerOptions });
560
563
  const logger = baseLogger.child({ scope: "load-remote-messages" });
@@ -598,7 +601,7 @@ var loadRemoteMessages = async ({
598
601
  });
599
602
  }
600
603
  }
601
- if (cacheOptions.enabled && key && messages) {
604
+ if (allowCacheWrite && cacheOptions.enabled && key && messages) {
602
605
  await pool?.set(key, messages, cacheOptions.ttl);
603
606
  }
604
607
  const end = performance.now();
@@ -615,7 +618,8 @@ var loadMessages = async ({
615
618
  config,
616
619
  locale,
617
620
  pathname = "",
618
- extraOptions: { exts, messageFileReader } = {}
621
+ extraOptions: { exts, messagesReader } = {},
622
+ allowCacheWrite = false
619
623
  }) => {
620
624
  const baseLogger = getLogger({ id: config.id, ...config.logger });
621
625
  const logger = baseLogger.child({ scope: "load-messages" });
@@ -651,8 +655,9 @@ var loadMessages = async ({
651
655
  cacheOptions: config.cache,
652
656
  loggerOptions: { id: config.id, ...config.logger },
653
657
  exts,
654
- messageFileReader
655
- }
658
+ messagesReader
659
+ },
660
+ allowCacheWrite
656
661
  });
657
662
  } else if (type === "remote") {
658
663
  loadedMessages = await loadRemoteMessages({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intor",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "description": "A modular and extensible i18n core designed for TypeScript and JavaScript projects. Intor enables custom translation logic with support for both frontend and backend environments, featuring runtime configuration, caching, adapters, and message loaders.",
5
5
  "author": "Yiming Liao",
6
6
  "license": "MIT",
@@ -77,6 +77,7 @@
77
77
  "p-limit": "^6.2.0"
78
78
  },
79
79
  "devDependencies": {
80
+ "@intor/cli": "../@intor:cli",
80
81
  "@types/lodash.merge": "^4.6.9",
81
82
  "@types/node": "^24.10.1",
82
83
  "@types/react": "^19.1.4",
@@ -94,6 +95,8 @@
94
95
  "prettier": "^3.5.3",
95
96
  "react": "^19.1.0",
96
97
  "react-dom": "^19.1.0",
98
+ "remark": "^15.0.1",
99
+ "remark-gfm": "^4.0.1",
97
100
  "tsup": "^8.4.0",
98
101
  "typescript": "^5.8.3",
99
102
  "typescript-eslint": "^8.32.1",