i18n-keyless-react 2.3.0 → 2.3.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/request-scope.js +25 -11
- package/package.json +2 -2
package/dist/request-scope.js
CHANGED
|
@@ -7,23 +7,36 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
// A single AsyncLocalStorage instance, created lazily on the server only. Each
|
|
10
|
-
// `run()` creates an isolated store, so the instance is correctly a
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
// `run()` creates an isolated store, so the instance is correctly a process singleton.
|
|
11
|
+
//
|
|
12
|
+
// CRITICAL: the instance is stored on `globalThis` under a `Symbol.for(...)` key, NOT in a
|
|
13
|
+
// module-level variable. Bundlers that build separate server-entry and SSR-render
|
|
14
|
+
// environments (e.g. TanStack Start / Vite SSR) instantiate this package more than once in
|
|
15
|
+
// the same process. With a module-local `als`, `runWithI18nKeyless` (called from the server
|
|
16
|
+
// entry) would set `als` on copy A, while `getRequestScope`/`recordUsedKey` (called during
|
|
17
|
+
// the React render) would read copy B's `als` — `undefined` — and SSR would silently fall
|
|
18
|
+
// back to the primary language with a hydration mismatch. Routing every read and write
|
|
19
|
+
// through the shared `globalThis` slot guarantees all module copies use ONE ALS.
|
|
20
|
+
const ALS_KEY = Symbol.for("i18n-keyless.als");
|
|
21
|
+
const ALS_INIT_KEY = Symbol.for("i18n-keyless.alsInit");
|
|
22
|
+
const registry = globalThis;
|
|
23
|
+
function getAls() {
|
|
24
|
+
return registry[ALS_KEY];
|
|
25
|
+
}
|
|
13
26
|
async function ensureALS() {
|
|
14
27
|
// Browser: no request scoping (single user → the store is correct). Also avoids
|
|
15
28
|
// pulling a Node builtin into client bundles.
|
|
16
|
-
if (
|
|
29
|
+
if (registry[ALS_KEY] || typeof window !== "undefined") {
|
|
17
30
|
return;
|
|
18
31
|
}
|
|
19
|
-
if (!
|
|
20
|
-
|
|
32
|
+
if (!registry[ALS_INIT_KEY]) {
|
|
33
|
+
registry[ALS_INIT_KEY] = (async () => {
|
|
21
34
|
try {
|
|
22
35
|
// Variable specifier + ignore hints keep bundlers from trying to resolve a Node
|
|
23
36
|
// builtin into the browser graph. tsc keeps these comments (removeComments=false).
|
|
24
37
|
const specifier = "node:async_hooks";
|
|
25
38
|
const mod = (await import(__rewriteRelativeImportExtension(/* @vite-ignore */ /* webpackIgnore: true */ specifier)));
|
|
26
|
-
|
|
39
|
+
registry[ALS_KEY] = new mod.AsyncLocalStorage();
|
|
27
40
|
}
|
|
28
41
|
catch {
|
|
29
42
|
// AsyncLocalStorage unavailable (e.g. some edge runtimes). Scoping degrades to a
|
|
@@ -31,7 +44,7 @@ async function ensureALS() {
|
|
|
31
44
|
}
|
|
32
45
|
})();
|
|
33
46
|
}
|
|
34
|
-
await
|
|
47
|
+
await registry[ALS_INIT_KEY];
|
|
35
48
|
}
|
|
36
49
|
/**
|
|
37
50
|
* Runs `fn` with a per-request translation scope active. Every `getTranslation(...)`
|
|
@@ -51,6 +64,7 @@ async function ensureALS() {
|
|
|
51
64
|
*/
|
|
52
65
|
export async function runWithI18nKeyless(scope, fn) {
|
|
53
66
|
await ensureALS();
|
|
67
|
+
const als = getAls();
|
|
54
68
|
if (!als) {
|
|
55
69
|
return fn();
|
|
56
70
|
}
|
|
@@ -64,7 +78,7 @@ export async function runWithI18nKeyless(scope, fn) {
|
|
|
64
78
|
* exported for advanced/server use.
|
|
65
79
|
*/
|
|
66
80
|
export function getRequestScope() {
|
|
67
|
-
const s =
|
|
81
|
+
const s = getAls()?.getStore();
|
|
68
82
|
return s ? { lang: s.lang, translations: s.translations } : undefined;
|
|
69
83
|
}
|
|
70
84
|
/**
|
|
@@ -74,7 +88,7 @@ export function getRequestScope() {
|
|
|
74
88
|
* `<I18nKeylessText>`.
|
|
75
89
|
*/
|
|
76
90
|
export function recordUsedKey(key) {
|
|
77
|
-
|
|
91
|
+
getAls()?.getStore()?.used.add(key);
|
|
78
92
|
}
|
|
79
93
|
/**
|
|
80
94
|
* Returns a snapshot containing ONLY the keys used during the current render (∩ the keys
|
|
@@ -87,7 +101,7 @@ export function recordUsedKey(key) {
|
|
|
87
101
|
* client-side navigation has every key. Returns `undefined` outside a scope. See docs/SSR.md.
|
|
88
102
|
*/
|
|
89
103
|
export function getUsedTranslationsSnapshot() {
|
|
90
|
-
const s =
|
|
104
|
+
const s = getAls()?.getStore();
|
|
91
105
|
if (!s) {
|
|
92
106
|
return undefined;
|
|
93
107
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18n-keyless-react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"postpublish": "rm -rf ./dist && rm *.tgz"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"i18n-keyless-core": "2.3.
|
|
27
|
+
"i18n-keyless-core": "2.3.1",
|
|
28
28
|
"zustand": "^5.0.3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|