intor 2.1.0 → 2.2.1

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.js CHANGED
@@ -23,33 +23,6 @@ var DEFAULT_CACHE_OPTIONS = {
23
23
  // 1 hour
24
24
  };
25
25
 
26
- // src/shared/error/intor-error.ts
27
- var IntorError = class extends Error {
28
- constructor({ message, code, id }) {
29
- const fullMessage = id ? `[${id}] ${message}` : message;
30
- super(fullMessage);
31
- this.name = "IntorError";
32
- this.id = id;
33
- this.code = code;
34
- Object.setPrototypeOf(this, new.target.prototype);
35
- }
36
- };
37
-
38
- // src/modules/messages/load-local-messages/utils/read-message-record-file.ts
39
- var readMessageRecordFile = async (filePath, loggerOptions) => {
40
- const fileName = path.basename(filePath, ".json");
41
- const content = await fs.readFile(filePath, "utf-8");
42
- const parsed = JSON.parse(content);
43
- if (typeof parsed !== "object" || parsed === null) {
44
- throw new IntorError({
45
- id: loggerOptions.id,
46
- code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
47
- message: "Invalid message format"
48
- });
49
- }
50
- return { fileName, content: parsed };
51
- };
52
-
53
26
  // src/shared/logger/global-logger-pool.ts
54
27
  function getGlobalLoggerPool() {
55
28
  if (!globalThis.__INTOR_LOGGER_POOL__) {
@@ -83,13 +56,25 @@ function getLogger({
83
56
  });
84
57
  pool.set(id, logger);
85
58
  if (pool.size > 1e3) {
86
- const keys = Array.from(pool.keys());
59
+ const keys = [...pool.keys()];
87
60
  for (const key of keys.slice(0, 200)) pool.delete(key);
88
61
  }
89
62
  }
90
63
  return logger;
91
64
  }
92
65
 
66
+ // src/shared/error/intor-error.ts
67
+ var IntorError = class extends Error {
68
+ constructor({ message, code, id }) {
69
+ const fullMessage = id ? `[${id}] ${message}` : message;
70
+ super(fullMessage);
71
+ this.name = "IntorError";
72
+ this.id = id;
73
+ this.code = code;
74
+ Object.setPrototypeOf(this, new.target.prototype);
75
+ }
76
+ };
77
+
93
78
  // src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
94
79
  var MAX_PATH_LENGTH = 260;
95
80
  var parseMessageFile = async (filePath, loggerOptions) => {
@@ -110,9 +95,17 @@ var parseMessageFile = async (filePath, loggerOptions) => {
110
95
  return null;
111
96
  }
112
97
  try {
113
- const { content } = await readMessageRecordFile(trimmedPath, loggerOptions);
114
- logger.trace(`Message file loaded.`, { filePath: trimmedPath });
115
- return content;
98
+ const content = await fs.readFile(trimmedPath, "utf8");
99
+ const parsed = JSON.parse(content);
100
+ if (typeof parsed !== "object" || parsed === null) {
101
+ throw new IntorError({
102
+ id: loggerOptions.id,
103
+ code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
104
+ message: "Invalid message format"
105
+ });
106
+ }
107
+ logger.trace("Message file loaded.", { filePath: trimmedPath });
108
+ return parsed;
116
109
  } catch (error) {
117
110
  logger.warn("Failed to parse message file.", {
118
111
  filePath: trimmedPath,
@@ -206,7 +199,7 @@ var addToNamespaceGroup = ({
206
199
  const filePathsSet = new Set(group.filePaths);
207
200
  if (!filePathsSet.has(filePath)) {
208
201
  filePathsSet.add(filePath);
209
- group.filePaths = Array.from(filePathsSet);
202
+ group.filePaths = [...filePathsSet];
210
203
  namespaceGroups.set(nsKey, group);
211
204
  }
212
205
  };
@@ -216,14 +209,15 @@ var traverseDirectory = async ({
216
209
  options,
217
210
  currentDirPath,
218
211
  namespaceGroups,
219
- namespacePathSegments
212
+ namespacePathSegments,
213
+ readdir = fs.readdir
220
214
  }) => {
221
215
  const { limit } = options;
222
216
  const loggerOptions = options.logger || { id: "default" };
223
217
  const baseLogger = getLogger({ ...loggerOptions });
224
218
  const logger = baseLogger.child({ scope: "traverse-directory" });
225
219
  try {
226
- const dirents = await fs.readdir(currentDirPath, { withFileTypes: true });
220
+ const dirents = await readdir(currentDirPath, { withFileTypes: true });
227
221
  const dirPromises = dirents.map(
228
222
  (dirent) => limit(async () => {
229
223
  const filePath = path.join(currentDirPath, dirent.name);
@@ -239,7 +233,8 @@ var traverseDirectory = async ({
239
233
  namespaceGroups,
240
234
  currentDirPath: filePath,
241
235
  namespacePathSegments: [...namespacePathSegments, dirent.name],
242
- options
236
+ options,
237
+ readdir
243
238
  });
244
239
  }
245
240
  }).catch((error) => {
@@ -314,7 +309,7 @@ var loadSingleLocale = async ({
314
309
  namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
315
310
  namespace: ns,
316
311
  isAtRoot: val.isAtRoot,
317
- fileCounts: val.filePaths.length
312
+ fileCount: val.filePaths.length
318
313
  }))
319
314
  });
320
315
  const namespaceGroupTasks = [...namespaceGroups.entries()].filter(
@@ -345,8 +340,8 @@ var loadLocaleWithFallback = async ({
345
340
  }) => {
346
341
  const baseLogger = getLogger({ ...loggerOptions });
347
342
  const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
348
- const localesToTry = [targetLocale, ...fallbackLocales];
349
- for (const locale of localesToTry) {
343
+ const candidateLocales = [targetLocale, ...fallbackLocales];
344
+ for (const locale of candidateLocales) {
350
345
  try {
351
346
  const validNamespaces = await loadSingleLocale({
352
347
  basePath,
@@ -365,7 +360,7 @@ var loadLocaleWithFallback = async ({
365
360
  }
366
361
  }
367
362
  logger.warn("All fallback locales failed.", {
368
- attemptedLocales: localesToTry
363
+ attemptedLocales: candidateLocales
369
364
  });
370
365
  return;
371
366
  };
@@ -382,7 +377,7 @@ function clearMessagesPool() {
382
377
 
383
378
  // src/shared/utils/merge-messages.ts
384
379
  var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
385
- const result = Object.keys(staticMessages).length ? { ...staticMessages } : {};
380
+ const result = Object.keys(staticMessages).length > 0 ? { ...staticMessages } : {};
386
381
  for (const locale in loadedMessages) {
387
382
  const loaded = loadedMessages[locale];
388
383
  if (!result[locale]) {
@@ -401,7 +396,7 @@ var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
401
396
  var CACHE_KEY_DELIMITER = "|";
402
397
  var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
403
398
  var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
404
- if (!key) return null;
399
+ if (key === null || key === void 0) return null;
405
400
  if (Array.isArray(key)) {
406
401
  if (key.length === 0) return null;
407
402
  const normalized = key.map((k) => {
@@ -425,56 +420,44 @@ var resolveNamespaces = ({
425
420
  pathname
426
421
  }) => {
427
422
  const { loader } = config;
428
- const {
429
- routeNamespaces = {},
430
- namespaces: fallbackNamespaces
431
- } = loader;
432
- const { unprefixedPathname } = extractPathname({ config, pathname });
433
- const standardizedPathname = standardizePathname({
434
- config,
435
- pathname: unprefixedPathname
436
- });
423
+ const { routeNamespaces = {}, namespaces } = loader || {};
424
+ const standardizedPathname = standardizePathname({ config, pathname });
437
425
  const placeholderRemovedPathname = standardizedPathname.replace(
438
426
  `/${PREFIX_PLACEHOLDER}`,
439
427
  ""
440
428
  );
441
- const defaultNamespaces = routeNamespaces.default ?? [];
442
- const exactMatchNamespaces = routeNamespaces[standardizedPathname] ?? routeNamespaces[placeholderRemovedPathname];
443
- if (exactMatchNamespaces) {
444
- return [...defaultNamespaces, ...exactMatchNamespaces];
445
- }
446
- let bestMatch = "";
447
- let bestNamespaces;
429
+ const collected = [
430
+ ...routeNamespaces.default || [],
431
+ // default
432
+ ...namespaces || [],
433
+ // default
434
+ ...routeNamespaces[standardizedPathname] || [],
435
+ // exact match
436
+ ...routeNamespaces[placeholderRemovedPathname] || []
437
+ // exact match
438
+ ];
448
439
  const prefixPatterns = Object.keys(routeNamespaces).filter(
449
440
  (pattern) => pattern.endsWith("/*")
450
441
  );
451
442
  for (const pattern of prefixPatterns) {
452
443
  const basePath = pattern.replace(/\/\*$/, "");
453
- if (standardizedPathname.startsWith(basePath)) {
454
- if (basePath.length > bestMatch.length) {
455
- bestMatch = basePath;
456
- bestNamespaces = routeNamespaces[pattern];
457
- }
444
+ if (standardizedPathname.startsWith(basePath) || placeholderRemovedPathname.startsWith(basePath)) {
445
+ collected.push(...routeNamespaces[pattern] || []);
458
446
  }
459
447
  }
460
- const matchedNamespaces = bestNamespaces ?? routeNamespaces["/*"] ?? fallbackNamespaces ?? [];
461
- if (matchedNamespaces.length > 0) {
462
- return [...defaultNamespaces, ...matchedNamespaces];
463
- } else {
464
- return [...defaultNamespaces];
465
- }
448
+ return [...new Set(collected)];
466
449
  };
467
450
 
468
451
  // src/shared/utils/locale/normalize-locale.ts
452
+ var toCanonical = (input) => {
453
+ try {
454
+ return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
455
+ } catch {
456
+ return;
457
+ }
458
+ };
469
459
  var normalizeLocale = (locale = "", supportedLocales = []) => {
470
460
  if (!locale || supportedLocales.length === 0) return;
471
- const toCanonical = (input) => {
472
- try {
473
- return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
474
- } catch {
475
- return;
476
- }
477
- };
478
461
  const canonicalLocale = toCanonical(locale);
479
462
  if (!canonicalLocale) return;
480
463
  const supportedCanonicalMap = /* @__PURE__ */ new Map();
@@ -505,12 +488,12 @@ var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
505
488
  const supportedLocalesSet = new Set(supportedLocales);
506
489
  const preferred = acceptLanguageHeader.split(",").map((part) => {
507
490
  const [lang, qValue] = part.split(";");
508
- const q = qValue ? parseFloat(qValue.split("=")[1]) : 1;
509
- if (isNaN(q)) {
491
+ const q = qValue ? Number.parseFloat(qValue.split("=")[1]) : 1;
492
+ if (Number.isNaN(q)) {
510
493
  return { lang: lang.trim(), q: 0 };
511
494
  }
512
495
  return { lang: lang.trim(), q };
513
- }).sort((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
496
+ }).toSorted((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
514
497
  return preferred;
515
498
  };
516
499
 
@@ -519,8 +502,8 @@ var normalizePathname = (rawPathname, options = {}) => {
519
502
  const length = rawPathname.length;
520
503
  let start = 0;
521
504
  let end = length - 1;
522
- while (start <= end && rawPathname.charCodeAt(start) <= 32) start++;
523
- while (end >= start && rawPathname.charCodeAt(end) <= 32) end--;
505
+ while (start <= end && (rawPathname.codePointAt(start) ?? 0) <= 32) start++;
506
+ while (end >= start && (rawPathname.codePointAt(end) ?? 0) <= 32) end--;
524
507
  if (start > end) return "/";
525
508
  let result = "";
526
509
  let hasSlash = false;
@@ -531,11 +514,7 @@ var normalizePathname = (rawPathname, options = {}) => {
531
514
  hasSlash = true;
532
515
  }
533
516
  } else {
534
- if (hasSlash || result === "") {
535
- result += "/" + char;
536
- } else {
537
- result += char;
538
- }
517
+ result += hasSlash || result === "" ? "/" + char : char;
539
518
  hasSlash = false;
540
519
  }
541
520
  }
@@ -559,8 +538,8 @@ var extractPathname = ({
559
538
  } else if (basePath && normalizedPathname === basePath) {
560
539
  prefixedPathname = "/";
561
540
  }
562
- const pathParts = prefixedPathname.split("/").filter(Boolean);
563
- const maybeLocale = pathParts[0] || "";
541
+ const pathPart = prefixedPathname.split("/").find(Boolean);
542
+ const maybeLocale = pathPart || "";
564
543
  const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
565
544
  let unprefixedPathname = prefixedPathname;
566
545
  if (prefix === "all") {
@@ -593,7 +572,7 @@ var standardizePathname = ({
593
572
  PREFIX_PLACEHOLDER,
594
573
  normalizePathname(pathname)
595
574
  ];
596
- const standardizedPathname = parts.join("/").replace(/\/{2,}/g, "/");
575
+ const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
597
576
  return normalizePathname(standardizedPathname);
598
577
  };
599
578
 
@@ -632,8 +611,8 @@ var loadLocalMessages = async ({
632
611
  loggerOptions.id,
633
612
  resolvedBasePath,
634
613
  locale,
635
- [...fallbackLocales ?? []].sort().join(","),
636
- [...namespaces ?? []].sort().join(",")
614
+ (fallbackLocales ?? []).toSorted().join(","),
615
+ (namespaces ?? []).toSorted().join(",")
637
616
  ]);
638
617
  if (cache.enabled && key) {
639
618
  const cached = await pool?.get(key);
@@ -778,8 +757,8 @@ var loadApiMessages = async ({
778
757
  loggerOptions.id,
779
758
  basePath,
780
759
  locale,
781
- [...fallbackLocales ?? []].sort().join(","),
782
- [...namespaces ?? []].sort().join(",")
760
+ (fallbackLocales ?? []).toSorted().join(","),
761
+ (namespaces ?? []).toSorted().join(",")
783
762
  ]);
784
763
  if (cache.enabled && key) {
785
764
  const cached = await pool?.get(key);
@@ -841,7 +820,7 @@ var loadMessages = async ({
841
820
  );
842
821
  return;
843
822
  }
844
- const { loader } = config;
823
+ const { id, loader, cache } = config;
845
824
  const fallbackLocales = config.fallbackLocales[locale] || [];
846
825
  const namespaces = resolveNamespaces({ config, pathname });
847
826
  logger.debug("Namespaces ready for loading.", {
@@ -856,8 +835,8 @@ var loadMessages = async ({
856
835
  locale,
857
836
  fallbackLocales,
858
837
  namespaces,
859
- cache: config.cache,
860
- logger: { id: config.id }
838
+ cache,
839
+ logger: { id }
861
840
  });
862
841
  } else if (loader.type === "api") {
863
842
  loadedMessages = await loadApiMessages({
@@ -865,7 +844,7 @@ var loadMessages = async ({
865
844
  locale,
866
845
  fallbackLocales,
867
846
  namespaces,
868
- logger: { id: config.id }
847
+ logger: { id }
869
848
  });
870
849
  }
871
850
  if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
@@ -881,15 +860,10 @@ var intor = async (config, i18nContext) => {
881
860
  logger.info("Start Intor initialization.");
882
861
  const { messages, loader } = config;
883
862
  const isI18nContextFunction = typeof i18nContext === "function";
884
- let context;
885
- if (isI18nContextFunction) {
886
- context = await i18nContext(config);
887
- } else {
888
- context = {
889
- locale: i18nContext?.locale || config.defaultLocale,
890
- pathname: i18nContext?.pathname || ""
891
- };
892
- }
863
+ const context = isI18nContextFunction ? await i18nContext(config) : {
864
+ locale: i18nContext?.locale || config.defaultLocale,
865
+ pathname: i18nContext?.pathname || ""
866
+ };
893
867
  const { locale, pathname } = context;
894
868
  const source = isI18nContextFunction ? "[function]" : "[static object]";
895
869
  logger.debug(`Context resolved via ${source}.`, context);
@@ -914,29 +888,23 @@ var intor = async (config, i18nContext) => {
914
888
  };
915
889
  };
916
890
  async function getTranslator(opts) {
917
- const { config, locale, pathname = "", preKey } = opts;
891
+ const { config, locale, pathname = "", preKey, handlers } = opts;
918
892
  const messages = await loadMessages({ config, locale, pathname });
919
893
  const translator = new Translator({
920
894
  locale,
921
895
  messages,
922
896
  fallbackLocales: config.fallbackLocales,
923
897
  loadingMessage: config.translator?.loadingMessage,
924
- placeholder: config.translator?.placeholder
898
+ placeholder: config.translator?.placeholder,
899
+ handlers
925
900
  });
926
- const props = {
927
- messages,
928
- locale
901
+ const props = { messages, locale };
902
+ const scoped = translator.scoped(preKey);
903
+ return {
904
+ ...props,
905
+ hasKey: preKey ? scoped.hasKey : translator.hasKey,
906
+ t: preKey ? scoped.t : translator.t
929
907
  };
930
- if (preKey) {
931
- const scoped = translator.scoped(preKey);
932
- return { ...props, ...scoped };
933
- } else {
934
- return {
935
- ...props,
936
- t: translator.t,
937
- hasKey: translator.hasKey
938
- };
939
- }
940
908
  }
941
909
 
942
910
  export { PREFIX_PLACEHOLDER, clearLoggerPool, clearMessagesPool, extractPathname, getTranslator, intor, loadApiMessages, loadLocalMessages, loadMessages, mergeMessages, normalizeCacheKey, normalizeLocale, normalizePathname, resolveNamespaces, resolvePreferredLocale, standardizePathname };