intor 2.3.26 → 2.3.28

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.
Files changed (179) hide show
  1. package/dist/core/export/index.js +1 -1
  2. package/dist/core/export/server/index.js +6 -2
  3. package/dist/core/src/config/constants/cookie.js +0 -1
  4. package/dist/core/src/config/resolvers/resolve-fallback-locales.js +1 -0
  5. package/dist/core/src/config/resolvers/resolve-routing-options.js +1 -0
  6. package/dist/core/src/config/validators/validate-default-locale.js +1 -0
  7. package/dist/core/src/config/validators/validate-id.js +1 -0
  8. package/dist/core/src/config/validators/validate-supported-locales.js +1 -0
  9. package/dist/core/src/core/error/intor-error.js +0 -2
  10. package/dist/core/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  11. package/dist/core/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  12. package/dist/core/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  13. package/dist/core/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  14. package/dist/core/src/routing/pathname/canonicalize-pathname.js +1 -0
  15. package/dist/core/src/routing/pathname/materialize-pathname.js +1 -0
  16. package/dist/core/src/routing/pathname/standardize-pathname.js +1 -0
  17. package/dist/core/src/server/helpers/get-translator.js +4 -8
  18. package/dist/core/src/server/intor/intor.js +8 -7
  19. package/dist/core/src/server/messages/load-local-messages/cache/messages-pool.js +17 -0
  20. package/dist/core/src/server/messages/load-local-messages/load-local-messages.js +2 -2
  21. package/dist/core/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +1 -0
  22. package/dist/core/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +4 -4
  23. package/dist/core/src/server/messages/load-messages.js +5 -5
  24. package/dist/core/src/server/translator/init-translator.js +36 -0
  25. package/dist/express/export/express/index.js +1 -1
  26. package/dist/express/src/adapters/express/helpers/get-translator.js +1 -1
  27. package/dist/express/src/adapters/express/middleware/{create-intor.js → create-intor-middleware.js} +5 -4
  28. package/dist/express/src/core/error/intor-error.js +1 -15
  29. package/dist/express/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  30. package/dist/express/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  31. package/dist/express/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  32. package/dist/express/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  33. package/dist/express/src/routing/inbound/resolve-locale/resolve-locale.js +1 -0
  34. package/dist/express/src/routing/locale/get-locale-from-pathname.js +1 -0
  35. package/dist/express/src/routing/pathname/canonicalize-pathname.js +1 -0
  36. package/dist/express/src/routing/pathname/materialize-pathname.js +1 -0
  37. package/dist/express/src/routing/pathname/standardize-pathname.js +1 -0
  38. package/dist/express/src/server/helpers/get-translator.js +4 -8
  39. package/dist/express/src/server/messages/load-local-messages/cache/messages-pool.js +11 -0
  40. package/dist/express/src/server/messages/load-local-messages/load-local-messages.js +2 -2
  41. package/dist/express/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +1 -0
  42. package/dist/express/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +4 -4
  43. package/dist/express/src/server/messages/load-messages.js +5 -5
  44. package/dist/express/src/server/shared/utils/parse-cookie-header.js +22 -0
  45. package/dist/express/src/server/translator/init-translator.js +36 -0
  46. package/dist/next/src/adapters/next/navigation/redirect.js +1 -0
  47. package/dist/next/src/adapters/next/navigation/use-pathname.js +1 -0
  48. package/dist/next/src/adapters/next/proxy/intor-proxy.js +1 -0
  49. package/dist/next/src/adapters/next/server/get-locale.js +16 -4
  50. package/dist/next/src/adapters/next/server/get-pathname.js +1 -0
  51. package/dist/next/src/adapters/next/server/get-translator.js +1 -1
  52. package/dist/next/src/adapters/next/server/intor.js +4 -4
  53. package/dist/next/src/core/error/intor-error.js +1 -15
  54. package/dist/next/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  55. package/dist/next/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  56. package/dist/next/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  57. package/dist/next/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  58. package/dist/next/src/policies/shoud-full-reload.js +1 -0
  59. package/dist/next/src/routing/inbound/resolve-locale/resolve-locale.js +1 -0
  60. package/dist/next/src/routing/locale/get-locale-from-pathname.js +1 -0
  61. package/dist/next/src/routing/pathname/canonicalize-pathname.js +1 -0
  62. package/dist/next/src/routing/pathname/materialize-pathname.js +1 -0
  63. package/dist/next/src/routing/pathname/standardize-pathname.js +1 -0
  64. package/dist/next/src/server/helpers/get-translator.js +4 -8
  65. package/dist/next/src/server/intor/intor.js +8 -7
  66. package/dist/next/src/server/messages/load-local-messages/cache/messages-pool.js +11 -0
  67. package/dist/next/src/server/messages/load-local-messages/load-local-messages.js +2 -2
  68. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/collect-file-entries/collect-file-entries.js +1 -0
  69. package/dist/next/src/server/messages/load-local-messages/read-locale-messages/parse-file-entries/parse-file-entries.js +4 -4
  70. package/dist/next/src/server/messages/load-messages.js +5 -5
  71. package/dist/next/src/server/translator/init-translator.js +36 -0
  72. package/dist/react/src/client/react/helpers/use-intor.js +1 -0
  73. package/dist/react/src/client/react/navigation/use-execute-navigation.js +6 -5
  74. package/dist/react/src/client/react/navigation/use-resolve-navigation.js +1 -0
  75. package/dist/react/src/client/react/provider/effects/use-locale-effects.js +3 -3
  76. package/dist/react/src/client/shared/helpers/get-client-locale.js +1 -0
  77. package/dist/react/src/client/shared/messages/create-refetch-messages.js +1 -1
  78. package/dist/react/src/core/error/intor-error.js +0 -2
  79. package/dist/react/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  80. package/dist/react/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  81. package/dist/react/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  82. package/dist/react/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  83. package/dist/react/src/core/messages/utils/nest-object-from-path.js +21 -0
  84. package/dist/react/src/policies/shoud-full-reload.js +1 -0
  85. package/dist/react/src/routing/pathname/canonicalize-pathname.js +1 -0
  86. package/dist/react/src/routing/pathname/materialize-pathname.js +1 -0
  87. package/dist/react/src/routing/pathname/standardize-pathname.js +1 -0
  88. package/dist/svelte/src/client/shared/helpers/get-client-locale.js +1 -0
  89. package/dist/svelte/src/client/shared/messages/create-refetch-messages.js +1 -1
  90. package/dist/svelte/src/client/svelte/helpers/create-intor.js +1 -0
  91. package/dist/svelte/src/client/svelte/store/effects/locale-effects.js +3 -3
  92. package/dist/svelte/src/core/error/intor-error.js +0 -2
  93. package/dist/svelte/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  94. package/dist/svelte/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  95. package/dist/svelte/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  96. package/dist/svelte/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  97. package/dist/svelte/src/core/messages/utils/nest-object-from-path.js +21 -0
  98. package/dist/types/export/express/index.d.ts +1 -1
  99. package/dist/types/export/index.d.ts +1 -1
  100. package/dist/types/export/server/index.d.ts +1 -1
  101. package/dist/types/src/adapters/express/index.d.ts +1 -1
  102. package/dist/types/src/adapters/express/middleware/{create-intor.d.ts → create-intor-middleware.d.ts} +1 -1
  103. package/dist/types/src/adapters/express/middleware/index.d.ts +1 -1
  104. package/dist/types/src/adapters/next/server/get-locale.d.ts +1 -1
  105. package/dist/types/src/adapters/next/server/intor.d.ts +4 -1
  106. package/dist/types/src/config/types/cookie.d.ts +0 -2
  107. package/dist/types/src/config/types/loader.d.ts +4 -4
  108. package/dist/types/src/core/error/intor-error.d.ts +1 -2
  109. package/dist/types/src/core/index.d.ts +1 -1
  110. package/dist/types/src/core/messages/index.d.ts +1 -2
  111. package/dist/types/src/core/messages/load-remote-messages/collect-remote-resources.d.ts +19 -0
  112. package/dist/types/src/core/messages/load-remote-messages/fetch-remote-resource.d.ts +21 -0
  113. package/dist/types/src/core/messages/load-remote-messages/load-remote-messages.d.ts +7 -4
  114. package/dist/types/src/core/messages/load-remote-messages/resolve-remote-resources.d.ts +14 -0
  115. package/dist/types/src/core/messages/load-remote-messages/types.d.ts +1 -1
  116. package/dist/types/src/core/messages/types.d.ts +9 -6
  117. package/dist/types/src/core/messages/utils/index.d.ts +2 -0
  118. package/dist/types/src/policies/index.d.ts +0 -1
  119. package/dist/types/src/server/helpers/get-translator.d.ts +2 -2
  120. package/dist/types/src/server/helpers/index.d.ts +0 -1
  121. package/dist/types/src/server/index.d.ts +3 -2
  122. package/dist/types/src/server/intor/intor.d.ts +5 -3
  123. package/dist/types/src/server/messages/index.d.ts +2 -0
  124. package/dist/types/src/server/messages/load-local-messages/cache/index.d.ts +1 -0
  125. package/dist/types/src/server/messages/load-local-messages/cache/messages-pool.d.ts +16 -0
  126. package/dist/types/src/server/messages/load-local-messages/index.d.ts +1 -0
  127. package/dist/types/src/server/messages/load-local-messages/types.d.ts +2 -1
  128. package/dist/types/src/server/shared/utils/index.d.ts +1 -0
  129. package/dist/types/src/server/shared/utils/parse-cookie-header.d.ts +4 -0
  130. package/dist/types/src/server/translator/create-translator.d.ts +3 -5
  131. package/dist/types/src/server/translator/index.d.ts +1 -1
  132. package/dist/types/src/server/translator/init-translator.d.ts +14 -0
  133. package/dist/vue/src/client/shared/helpers/get-client-locale.js +1 -0
  134. package/dist/vue/src/client/shared/messages/create-refetch-messages.js +1 -1
  135. package/dist/vue/src/client/vue/helpers/use-intor.js +1 -0
  136. package/dist/vue/src/client/vue/provider/effects/use-locale-effects.js +3 -3
  137. package/dist/vue/src/core/error/intor-error.js +0 -2
  138. package/dist/vue/src/core/messages/load-remote-messages/collect-remote-resources.js +25 -0
  139. package/dist/vue/src/core/messages/load-remote-messages/fetch-remote-resource.js +47 -0
  140. package/dist/vue/src/core/messages/load-remote-messages/load-remote-messages.js +41 -27
  141. package/dist/vue/src/core/messages/load-remote-messages/resolve-remote-resources.js +24 -0
  142. package/dist/vue/src/core/messages/utils/nest-object-from-path.js +21 -0
  143. package/package.json +1 -1
  144. package/dist/core/src/core/messages/global-messages-pool.js +0 -21
  145. package/dist/core/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  146. package/dist/core/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  147. package/dist/core/src/server/helpers/local-messages-from-url.js +0 -53
  148. package/dist/core/src/server/runtime/create-intor-runtime.js +0 -56
  149. package/dist/express/src/core/messages/global-messages-pool.js +0 -16
  150. package/dist/express/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  151. package/dist/express/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  152. package/dist/express/src/server/runtime/create-intor-runtime.js +0 -56
  153. package/dist/next/src/core/messages/global-messages-pool.js +0 -16
  154. package/dist/next/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  155. package/dist/next/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  156. package/dist/next/src/server/runtime/create-intor-runtime.js +0 -56
  157. package/dist/react/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  158. package/dist/react/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  159. package/dist/react/src/policies/should-persist.js +0 -8
  160. package/dist/svelte/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  161. package/dist/svelte/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  162. package/dist/svelte/src/policies/should-persist.js +0 -8
  163. package/dist/types/src/core/messages/global-messages-pool.d.ts +0 -25
  164. package/dist/types/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.d.ts +0 -9
  165. package/dist/types/src/core/messages/load-remote-messages/fetch-locale-messages/index.d.ts +0 -1
  166. package/dist/types/src/core/messages/load-remote-messages/fetch-locale-messages/types.d.ts +0 -12
  167. package/dist/types/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.d.ts +0 -5
  168. package/dist/types/src/policies/should-persist.d.ts +0 -7
  169. package/dist/types/src/server/helpers/local-messages-from-url.d.ts +0 -21
  170. package/dist/types/src/server/runtime/create-intor-runtime.d.ts +0 -12
  171. package/dist/types/src/server/runtime/index.d.ts +0 -2
  172. package/dist/types/src/server/runtime/types.d.ts +0 -21
  173. package/dist/vue/src/core/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.js +0 -55
  174. package/dist/vue/src/core/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.js +0 -25
  175. package/dist/vue/src/policies/should-persist.js +0 -8
  176. /package/dist/core/src/{server/messages/load-local-messages/read-locale-messages/parse-file-entries → core/messages}/utils/nest-object-from-path.js +0 -0
  177. /package/dist/express/src/{server/messages/load-local-messages/read-locale-messages/parse-file-entries → core/messages}/utils/nest-object-from-path.js +0 -0
  178. /package/dist/next/src/{server/messages/load-local-messages/read-locale-messages/parse-file-entries → core/messages}/utils/nest-object-from-path.js +0 -0
  179. /package/dist/types/src/{server/messages/load-local-messages/read-locale-messages/parse-file-entries → core/messages}/utils/nest-object-from-path.d.ts +0 -0
@@ -4,8 +4,8 @@ export { deepMerge } from '../src/core/utils/deep-merge.js';
4
4
  export { resolveLoaderOptions } from '../src/core/utils/resolve-loader-options.js';
5
5
  import 'logry';
6
6
  export { clearLoggerPool } from '../src/core/logger/global-logger-pool.js';
7
+ import 'p-limit';
7
8
  export { isValidMessages } from '../src/core/messages/utils/is-valid-messages.js';
8
- export { clearMessagesPool } from '../src/core/messages/global-messages-pool.js';
9
9
  export { mergeMessages } from '../src/core/messages/merge-messages.js';
10
10
  export { INTOR_MESSAGES_KIND, INTOR_MESSAGES_KIND_KEY, INTOR_PREFIX, getMessagesKind } from '../src/core/messages/internal-metadata.js';
11
11
  export { defineIntorConfig } from '../src/config/define-intor-config.js';
@@ -1,6 +1,10 @@
1
1
  export { intor } from '../../src/server/intor/intor.js';
2
2
  export { loadMessages } from '../../src/server/messages/load-messages.js';
3
- import 'intor-translator';
3
+ import 'node:path';
4
+ import 'p-limit';
5
+ import '../../src/core/error/intor-error.js';
4
6
  import 'logry';
7
+ export { clearMessagesPool } from '../../src/server/messages/load-local-messages/cache/messages-pool.js';
8
+ import 'node:fs/promises';
9
+ import 'intor-translator';
5
10
  export { getTranslator } from '../../src/server/helpers/get-translator.js';
6
- export { loadMessagesFromUrl } from '../../src/server/helpers/local-messages-from-url.js';
@@ -1,6 +1,5 @@
1
1
  // Default cookie options
2
2
  const DEFAULT_COOKIE_OPTIONS = {
3
- enabled: true,
4
3
  persist: true,
5
4
  name: "intor.locale",
6
5
  domain: undefined,
@@ -1,5 +1,6 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { getLogger } from '../../core/logger/get-logger.js';
3
+ import 'p-limit';
3
4
 
4
5
  /**
5
6
  * Resolves fallbackLocales into a runtime-safe mapping.
@@ -1,6 +1,7 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { deepMerge } from '../../core/utils/deep-merge.js';
3
3
  import 'logry';
4
+ import 'p-limit';
4
5
  import { DEFAULT_ROUTING_OPTIONS } from '../constants/routing.js';
5
6
  import '../constants/cookie.js';
6
7
 
@@ -1,5 +1,6 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
+ import 'p-limit';
3
4
 
4
5
  /**
5
6
  * Validates that the configured defaultLocale is supported.
@@ -1,5 +1,6 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
+ import 'p-limit';
3
4
 
4
5
  /**
5
6
  * Validates that the given id is a non-empty string.
@@ -1,5 +1,6 @@
1
1
  import { IntorError, IntorErrorCode } from '../../core/error/intor-error.js';
2
2
  import 'logry';
3
+ import 'p-limit';
3
4
 
4
5
  /**
5
6
  * Validates that supportedLocales is provided and non-empty.
@@ -16,8 +16,6 @@ var IntorErrorCode;
16
16
  IntorErrorCode["INVALID_CONFIG_ID"] = "INTOR_INVALID_CONFIG_ID";
17
17
  IntorErrorCode["MISSING_SUPPORTED_LOCALES"] = "INTOR_MISSING_SUPPORTED_LOCALES";
18
18
  IntorErrorCode["UNSUPPORTED_DEFAULT_LOCALE"] = "INTOR_UNSUPPORTED_DEFAULT_LOCALE";
19
- // runtime
20
- IntorErrorCode["RUNTIME_NOT_INITIALIZED"] = "INTOR_RUNTIME_NOT_INITIALIZED";
21
19
  })(IntorErrorCode || (IntorErrorCode = {}));
22
20
 
23
21
  export { IntorError, IntorErrorCode };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Collect remote message resources for a given locale.
3
+ *
4
+ * - Always includes the root `index.json`
5
+ * - Optionally includes namespace-specific resources
6
+ * - Produces semantic paths for later message nesting
7
+ *
8
+ * This function performs no I/O and does not validate resource existence.
9
+ */
10
+ function collectRemoteResources({ locale, baseUrl, namespaces, }) {
11
+ const basePath = `${baseUrl}/${locale}`;
12
+ // Root translation resource (always loaded)
13
+ const indexResource = { url: `${basePath}/index.json`, path: [] };
14
+ // When no namespaces are provided, the locale domain consists of index only
15
+ if (!namespaces || namespaces.length === 0)
16
+ return [indexResource];
17
+ // Namespace-specific resources are nested under their namespace key
18
+ const nsResources = namespaces.map((ns) => ({
19
+ url: `${basePath}/${ns}.json`,
20
+ path: [ns],
21
+ }));
22
+ return [indexResource, ...nsResources];
23
+ }
24
+
25
+ export { collectRemoteResources };
@@ -0,0 +1,47 @@
1
+ import { getLogger } from '../../logger/get-logger.js';
2
+ import { isValidMessages } from '../utils/is-valid-messages.js';
3
+
4
+ /**
5
+ * Fetch a single remote messages resource.
6
+ *
7
+ * This function performs a single HTTP request to retrieve
8
+ * a remote translation messages payload.
9
+ *
10
+ * It is responsible for:
11
+ * - Issuing the network request
12
+ * - Validating the returned message structure
13
+ * - Handling abort and network errors
14
+ */
15
+ async function fetchRemoteResource({ url, headers, signal, loggerOptions, }) {
16
+ const baseLogger = getLogger(loggerOptions);
17
+ const logger = baseLogger.child({ scope: "fetch-locale-messages" });
18
+ try {
19
+ // Fetch
20
+ const response = await fetch(url, {
21
+ method: "GET",
22
+ headers: { "Content-Type": "application/json", ...headers },
23
+ cache: "no-store",
24
+ signal,
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`HTTP ${response.status} ${response.statusText}`);
28
+ }
29
+ // Parse JSON body
30
+ const data = await response.json();
31
+ // Validate messages structure
32
+ if (!isValidMessages(data)) {
33
+ throw new Error("Invalid messages structure");
34
+ }
35
+ return data;
36
+ }
37
+ catch (error) {
38
+ if (error instanceof Error && error.name === "AbortError") {
39
+ logger.debug("Remote fetch aborted.", { url });
40
+ return;
41
+ }
42
+ logger.warn("Failed to fetch remote messages.", { url, error });
43
+ return;
44
+ }
45
+ }
46
+
47
+ export { fetchRemoteResource };
@@ -1,18 +1,24 @@
1
+ import pLimit from 'p-limit';
1
2
  import { getLogger } from '../../logger/get-logger.js';
2
- import { fetchLocaleMessages } from './fetch-locale-messages/fetch-locale-messages.js';
3
+ import { collectRemoteResources } from './collect-remote-resources.js';
4
+ import { fetchRemoteResource } from './fetch-remote-resource.js';
5
+ import { resolveRemoteResources } from './resolve-remote-resources.js';
3
6
 
4
7
  /**
5
- * Load locale messages from a remote API.
8
+ * Load locale messages from a remote source.
6
9
  *
7
10
  * This function serves as the orchestration layer for remote message loading.
8
11
  * It coordinates:
9
12
  *
10
13
  * - Locale resolution with fallbacks
11
- * - Respecting abort signals across the entire async flow
14
+ * - Concurrency control for network requests
15
+ * - Remote resource fetching and message merging
12
16
  *
13
- * Network fetching and data validation are delegated to lower-level utilities.
17
+ * Remote messages are fetched on demand and are not memoized at the process level.
18
+ *
19
+ * Network requests and response validation are delegated to lower-level utilities.
14
20
  */
15
- const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, rootDir, url, headers, signal, loggerOptions, }) => {
21
+ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, concurrency, url: baseUrl, headers, signal, loggerOptions, }) => {
16
22
  const baseLogger = getLogger(loggerOptions);
17
23
  const logger = baseLogger.child({ scope: "load-remote-messages" });
18
24
  // Abort early if the request has already been cancelled
@@ -21,33 +27,45 @@ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, rootDir
21
27
  return;
22
28
  }
23
29
  const start = performance.now();
24
- logger.debug("Loading remote messages.", { url });
25
- // ---------------------------------------------------------------------------
30
+ logger.debug("Loading remote messages.", { baseUrl });
31
+ // ----------------------------------------------------------------
26
32
  // Resolve locale messages with ordered fallback strategy
27
- // ---------------------------------------------------------------------------
33
+ // ----------------------------------------------------------------
34
+ const limit = concurrency ? pLimit(concurrency) : undefined;
28
35
  const candidateLocales = [locale, ...(fallbackLocales || [])];
29
36
  let messages;
30
37
  for (let i = 0; i < candidateLocales.length; i++) {
31
38
  const candidateLocale = candidateLocales[i];
32
39
  const isLast = i === candidateLocales.length - 1;
33
40
  try {
34
- const fetched = await fetchLocaleMessages({
41
+ // -----------------------------------------------------------------
42
+ // Collect remote message resources for the locale
43
+ // -----------------------------------------------------------------
44
+ const resources = collectRemoteResources({
35
45
  locale: candidateLocale,
46
+ baseUrl,
36
47
  namespaces,
37
- rootDir,
38
- url,
39
- headers,
40
- signal,
41
- extraOptions: { loggerOptions },
42
48
  });
43
- // Stop at the first locale that yields non-empty messages
44
- if (fetched && Object.values(fetched[candidateLocale] || {}).length > 0) {
45
- messages = fetched;
46
- break;
47
- }
49
+ // -----------------------------------------------------------------
50
+ // Fetch all message chunks in parallel
51
+ // -----------------------------------------------------------------
52
+ const fetchUrl = (url) => fetchRemoteResource({ url, headers, signal, loggerOptions });
53
+ const results = await Promise.all(resources.map(({ url }) => limit ? limit(() => fetchUrl(url)) : fetchUrl(url)));
54
+ // Guard: no valid remote resources
55
+ if (!results.some(Boolean))
56
+ continue;
57
+ // -----------------------------------------------------------------
58
+ // Resolve and merge remote message resources
59
+ // -----------------------------------------------------------------
60
+ const resolved = resolveRemoteResources(resources.map((res, i) => ({ path: res.path, data: results[i] })));
61
+ // -----------------------------------------------------------------
62
+ // Wrap resolved messages into locale-scoped LocaleMessages
63
+ // -----------------------------------------------------------------
64
+ messages = { [candidateLocale]: resolved };
65
+ break;
48
66
  }
49
- catch (error) {
50
- if (error instanceof Error && error.name === "AbortError") {
67
+ catch {
68
+ if (signal?.aborted) {
51
69
  logger.debug("Remote message loading aborted.");
52
70
  return;
53
71
  }
@@ -58,18 +76,14 @@ const loadRemoteMessages = async ({ locale, fallbackLocales, namespaces, rootDir
58
76
  });
59
77
  }
60
78
  else {
61
- logger.warn(`Failed to fetch locale messages for "${candidateLocale}", trying next fallback.`);
79
+ logger.warn(`Failed to load locale messages for "${candidateLocale}", trying next fallback.`);
62
80
  }
63
- logger.trace("Remote fetch error detail.", {
64
- locale: candidateLocale,
65
- error,
66
- });
67
81
  }
68
82
  }
69
83
  // Final success log with resolved locale and timing
70
84
  if (messages) {
71
85
  logger.trace("Finished loading remote messages.", {
72
- loadedLocale: messages ? Object.keys(messages)[0] : undefined,
86
+ loadedLocale: Object.keys(messages)[0],
73
87
  duration: `${Math.round(performance.now() - start)} ms`,
74
88
  });
75
89
  }
@@ -0,0 +1,24 @@
1
+ import { deepMerge } from '../../utils/deep-merge.js';
2
+ import { nestObjectFromPath } from '../utils/nest-object-from-path.js';
3
+
4
+ /**
5
+ * Resolve remote message resources into a single MessageObject.
6
+ *
7
+ * - Applies semantic nesting based on resource path
8
+ * - Merges all resolved message chunks
9
+ *
10
+ * Always returns a MessageObject.
11
+ * An empty object represents an empty translation domain.
12
+ */
13
+ function resolveRemoteResources(resources) {
14
+ let result = {};
15
+ for (const { path, data } of resources) {
16
+ if (!data)
17
+ continue;
18
+ const resolved = path.length > 0 ? nestObjectFromPath(path, data) : data;
19
+ result = deepMerge(result, resolved);
20
+ }
21
+ return result;
22
+ }
23
+
24
+ export { resolveRemoteResources };
@@ -1,6 +1,7 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
3
3
  import 'logry';
4
+ import 'p-limit';
4
5
 
5
6
  /**
6
7
  * Returns a canonical, locale-agnostic pathname.
@@ -2,6 +2,7 @@ import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
2
2
  import '../../core/error/intor-error.js';
3
3
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
4
4
  import 'logry';
5
+ import 'p-limit';
5
6
 
6
7
  /**
7
8
  * Materializes a standardized pathname by applying
@@ -2,6 +2,7 @@ import { PREFIX_PLACEHOLDER } from '../../core/constants/prefix-placeholder.js';
2
2
  import '../../core/error/intor-error.js';
3
3
  import { normalizePathname } from '../../core/utils/normalizers/normalize-pathname.js';
4
4
  import 'logry';
5
+ import 'p-limit';
5
6
 
6
7
  /**
7
8
  * Standardizes a canonical pathname into an internal routing template
@@ -1,24 +1,20 @@
1
- import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
1
+ import { initTranslator } from '../translator/init-translator.js';
2
2
 
3
3
  // Implementation
4
4
  async function getTranslator(config, params) {
5
5
  const { readers, allowCacheWrite, preKey, handlers, plugins } = params;
6
6
  const locale = params.locale;
7
- // Create runtime (request-scoped, no cache write)
8
- const runtime = createIntorRuntime(config, {
7
+ // Initialize a locale-bound translator snapshot with messages loaded
8
+ const translator = await initTranslator(config, locale, {
9
9
  readers,
10
10
  allowCacheWrite,
11
- });
12
- // Ensure messages & create translator snapshot
13
- await runtime.ensureMessages(locale);
14
- const translator = runtime.translator(locale, {
15
11
  preKey,
16
12
  plugins,
17
13
  handlers,
18
14
  });
19
15
  return {
20
16
  messages: translator.messages,
21
- locale,
17
+ locale: translator.locale,
22
18
  hasKey: translator.hasKey,
23
19
  t: translator.t,
24
20
  };
@@ -1,6 +1,7 @@
1
1
  import '../../core/error/intor-error.js';
2
2
  import { getLogger } from '../../core/logger/get-logger.js';
3
- import { createIntorRuntime } from '../runtime/create-intor-runtime.js';
3
+ import 'p-limit';
4
+ import { initTranslator } from '../translator/init-translator.js';
4
5
 
5
6
  /**
6
7
  * Initializes Intor for the current execution context.
@@ -12,8 +13,6 @@ async function intor(config, localeOrResolver, options) {
12
13
  const baseLogger = getLogger(config.logger);
13
14
  const logger = baseLogger.child({ scope: "intor" });
14
15
  logger.info("Start Intor initialization.");
15
- // Create runtime (request-scoped)
16
- const runtime = createIntorRuntime(config, options);
17
16
  // Resolve locale
18
17
  const isLocaleFunction = typeof localeOrResolver === "function";
19
18
  const locale = isLocaleFunction
@@ -21,13 +20,15 @@ async function intor(config, localeOrResolver, options) {
21
20
  : localeOrResolver || config.defaultLocale;
22
21
  const source = typeof localeOrResolver === "function" ? "resolver" : "static";
23
22
  logger.debug(`Initial locale resolved as "${locale}" via "${source}".`);
24
- // Ensure messages & create translator snapshot
25
- await runtime.ensureMessages(locale);
26
- const translator = runtime.translator(locale);
23
+ // Initialize a locale-bound translator snapshot with messages loaded
24
+ const translator = await initTranslator(config, locale, {
25
+ readers: options?.readers,
26
+ allowCacheWrite: options?.allowCacheWrite,
27
+ });
27
28
  logger.info("Intor initialized.");
28
29
  return {
29
30
  config,
30
- locale,
31
+ locale: translator.locale,
31
32
  messages: translator.messages,
32
33
  };
33
34
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Get the messages pool.
3
+ */
4
+ function getMessagesPool() {
5
+ if (!globalThis.__INTOR_MESSAGES_POOL__) {
6
+ globalThis.__INTOR_MESSAGES_POOL__ = new Map();
7
+ }
8
+ return globalThis.__INTOR_MESSAGES_POOL__;
9
+ }
10
+ /**
11
+ * Clear all cached messages.
12
+ */
13
+ function clearMessagesPool() {
14
+ getMessagesPool().clear();
15
+ }
16
+
17
+ export { clearMessagesPool, getMessagesPool };
@@ -3,7 +3,7 @@ import pLimit from 'p-limit';
3
3
  import '../../../core/error/intor-error.js';
4
4
  import { normalizeCacheKey } from '../../../core/utils/normalizers/normalize-cache-key.js';
5
5
  import { getLogger } from '../../../core/logger/get-logger.js';
6
- import { getGlobalMessagesPool } from '../../../core/messages/global-messages-pool.js';
6
+ import { getMessagesPool } from './cache/messages-pool.js';
7
7
  import { readLocaleMessages } from './read-locale-messages/read-locale-messages.js';
8
8
 
9
9
  /**
@@ -21,7 +21,7 @@ import { readLocaleMessages } from './read-locale-messages/read-locale-messages.
21
21
  *
22
22
  * File traversal, parsing, and validation are delegated to lower-level utilities.
23
23
  */
24
- const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readers, pool = getGlobalMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
24
+ const loadLocalMessages = async ({ id, locale, fallbackLocales, namespaces, rootDir = "messages", concurrency = 10, readers, pool = getMessagesPool(), allowCacheWrite = false, loggerOptions, }) => {
25
25
  const baseLogger = getLogger(loggerOptions);
26
26
  const logger = baseLogger.child({ scope: "load-local-messages" });
27
27
  const start = performance.now();
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import '../../../../../core/error/intor-error.js';
4
4
  import { getLogger } from '../../../../../core/logger/get-logger.js';
5
+ import 'p-limit';
5
6
 
6
7
  /**
7
8
  * Recursively collects message file metadata under a given locale root.
@@ -2,9 +2,10 @@ import path from 'node:path';
2
2
  import '../../../../../core/error/intor-error.js';
3
3
  import { deepMerge } from '../../../../../core/utils/deep-merge.js';
4
4
  import { getLogger } from '../../../../../core/logger/get-logger.js';
5
+ import 'p-limit';
5
6
  import { isValidMessages } from '../../../../../core/messages/utils/is-valid-messages.js';
7
+ import { nestObjectFromPath } from '../../../../../core/messages/utils/nest-object-from-path.js';
6
8
  import { jsonReader } from './utils/json-reader.js';
7
- import { nestObjectFromPath } from './utils/nest-object-from-path.js';
8
9
 
9
10
  /**
10
11
  * Parse locale message files into a unified Messages object (single-locale).
@@ -40,7 +41,7 @@ async function parseFileEntries({ fileEntries, limit, readers, loggerOptions, })
40
41
  const logger = baseLogger.child({ scope: "parse-file-entries" });
41
42
  // Read and parse all file entries
42
43
  const parsedFileEntries = [];
43
- const tasks = fileEntries.map(({ namespace, segments, basename, fullPath, relativePath }) => limit(async () => {
44
+ const tasks = fileEntries.map(({ namespace, segments, basename, fullPath }) => limit(async () => {
44
45
  try {
45
46
  // -------------------------------------------------------------------
46
47
  // Read and validate file content
@@ -73,10 +74,9 @@ async function parseFileEntries({ fileEntries, limit, readers, loggerOptions, })
73
74
  // Nest the parsed content based on the path segments
74
75
  const nestedMessages = nestObjectFromPath(keyPath, raw);
75
76
  parsedFileEntries.push({ namespace, messages: nestedMessages });
76
- logger.trace(`Parsed message file: ${relativePath}`);
77
77
  }
78
78
  catch (error) {
79
- logger.error("Failed to read or parse file.", {
79
+ logger.warn("Failed to read or parse file.", {
80
80
  path: fullPath,
81
81
  error,
82
82
  });
@@ -28,12 +28,12 @@ const loadMessages = async ({ config, locale, readers, allowCacheWrite = false,
28
28
  logger.warn("No loader options have been configured in the current config.");
29
29
  return;
30
30
  }
31
- const { type, namespaces, rootDir } = loader;
31
+ const { type, namespaces, concurrency } = loader;
32
32
  const fallbackLocales = config.fallbackLocales[locale] || [];
33
33
  logger.info(`Loading messages for locale "${locale}".`);
34
34
  logger.trace("Starting to load messages with runtime context.", {
35
35
  loaderType: type,
36
- rootDir,
36
+ ...(type === "local" ? { rootDir: loader.rootDir } : {}),
37
37
  locale,
38
38
  fallbackLocales: fallbackLocales.join(", "),
39
39
  namespaces: namespaces && namespaces.length > 0 ? [...namespaces] : "*",
@@ -48,8 +48,8 @@ const loadMessages = async ({ config, locale, readers, allowCacheWrite = false,
48
48
  locale,
49
49
  fallbackLocales,
50
50
  namespaces,
51
- rootDir,
52
- concurrency: loader.concurrency,
51
+ rootDir: loader.rootDir,
52
+ concurrency,
53
53
  readers,
54
54
  allowCacheWrite,
55
55
  loggerOptions: config.logger,
@@ -60,7 +60,7 @@ const loadMessages = async ({ config, locale, readers, allowCacheWrite = false,
60
60
  locale,
61
61
  fallbackLocales,
62
62
  namespaces,
63
- rootDir,
63
+ concurrency,
64
64
  url: loader.url,
65
65
  headers: loader.headers,
66
66
  loggerOptions: config.logger,
@@ -0,0 +1,36 @@
1
+ import '../../core/error/intor-error.js';
2
+ import { resolveLoaderOptions } from '../../core/utils/resolve-loader-options.js';
3
+ import 'logry';
4
+ import 'p-limit';
5
+ import { loadMessages } from '../messages/load-messages.js';
6
+ import 'node:path';
7
+ import 'node:fs/promises';
8
+ import { createTranslator } from './create-translator.js';
9
+
10
+ /**
11
+ * Initializes a server-side translator for a specific locale.
12
+ *
13
+ * - Performs message loading during initialization.
14
+ * - Returns an immutable translator snapshot.
15
+ */
16
+ async function initTranslator(config, locale, options) {
17
+ const { readers, allowCacheWrite = false, preKey, handlers, plugins, } = options || {};
18
+ const loader = resolveLoaderOptions(config, "server");
19
+ // Load messages
20
+ let messages = {};
21
+ if (loader) {
22
+ messages =
23
+ (await loadMessages({ config, locale, readers, allowCacheWrite })) || {};
24
+ }
25
+ // Create immutable translator snapshot
26
+ return createTranslator({
27
+ config,
28
+ locale,
29
+ messages,
30
+ preKey,
31
+ handlers,
32
+ plugins,
33
+ });
34
+ }
35
+
36
+ export { initTranslator };
@@ -1,2 +1,2 @@
1
- export { createIntor } from '../../src/adapters/express/middleware/create-intor.js';
1
+ export { createIntorMiddleware } from '../../src/adapters/express/middleware/create-intor-middleware.js';
2
2
  export { getTranslator } from '../../src/adapters/express/helpers/get-translator.js';
@@ -1,7 +1,7 @@
1
1
  import '../../../core/error/intor-error.js';
2
2
  import 'logry';
3
- import 'node:path';
4
3
  import 'p-limit';
4
+ import 'node:path';
5
5
  import 'node:fs/promises';
6
6
  import 'intor-translator';
7
7
  import { getTranslator as getTranslator$1 } from '../../../server/helpers/get-translator.js';
@@ -2,13 +2,14 @@ import { INTOR_HEADERS } from '../../../core/constants/headers.js';
2
2
  import '../../../core/error/intor-error.js';
3
3
  import { normalizeQuery } from '../../../core/utils/normalizers/normalize-query.js';
4
4
  import 'logry';
5
+ import 'p-limit';
5
6
  import { resolveInbound } from '../../../routing/inbound/resolve-inbound.js';
6
7
  import { getLocaleFromAcceptLanguage } from '../../../routing/locale/get-locale-from-accept-language.js';
7
8
  import 'node:path';
8
- import 'p-limit';
9
9
  import 'node:fs/promises';
10
10
  import 'intor-translator';
11
11
  import { getTranslator } from '../../../server/helpers/get-translator.js';
12
+ import { parseCookieHeader } from '../../../server/shared/utils/parse-cookie-header.js';
12
13
 
13
14
  /**
14
15
  * Resolves locale-aware routing for the current execution context.
@@ -20,7 +21,7 @@ import { getTranslator } from '../../../server/helpers/get-translator.js';
20
21
  *
21
22
  * @platform Express
22
23
  */
23
- function createIntor(config, options) {
24
+ function createIntorMiddleware(config, options) {
24
25
  return async function intorMiddleware(req, _res, next) {
25
26
  // locale from accept-language header
26
27
  const acceptLanguage = req.headers["accept-language"];
@@ -31,7 +32,7 @@ function createIntor(config, options) {
31
32
  const { locale, localeSource, pathname } = await resolveInbound(config, req.path, false, {
32
33
  host: req.hostname,
33
34
  query: normalizeQuery(req.query),
34
- cookie: req.cookies?.[config.cookie.name],
35
+ cookie: parseCookieHeader(req.headers.cookie)[config.cookie.name],
35
36
  detected: localeFromAcceptLanguage || config.defaultLocale,
36
37
  });
37
38
  // --------------------------------------------------
@@ -59,4 +60,4 @@ function createIntor(config, options) {
59
60
  };
60
61
  }
61
62
 
62
- export { createIntor };
63
+ export { createIntorMiddleware };
@@ -1,23 +1,9 @@
1
- class IntorError extends Error {
2
- code;
3
- id;
4
- constructor({ message, code, id }) {
5
- const fullMessage = id ? `[${id}] ${message}` : message;
6
- super(fullMessage);
7
- this.name = "IntorError";
8
- this.id = id;
9
- this.code = code;
10
- Object.setPrototypeOf(this, new.target.prototype); // Fix prototype
11
- }
12
- }
13
1
  var IntorErrorCode;
14
2
  (function (IntorErrorCode) {
15
3
  // config
16
4
  IntorErrorCode["INVALID_CONFIG_ID"] = "INTOR_INVALID_CONFIG_ID";
17
5
  IntorErrorCode["MISSING_SUPPORTED_LOCALES"] = "INTOR_MISSING_SUPPORTED_LOCALES";
18
6
  IntorErrorCode["UNSUPPORTED_DEFAULT_LOCALE"] = "INTOR_UNSUPPORTED_DEFAULT_LOCALE";
19
- // runtime
20
- IntorErrorCode["RUNTIME_NOT_INITIALIZED"] = "INTOR_RUNTIME_NOT_INITIALIZED";
21
7
  })(IntorErrorCode || (IntorErrorCode = {}));
22
8
 
23
- export { IntorError, IntorErrorCode };
9
+ export { IntorErrorCode };