@timber-js/app 0.1.42 → 0.1.44
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timber-js/app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.44",
|
|
4
4
|
"description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"nuqs": "^2.0.0",
|
|
101
101
|
"react": "^19.2.4",
|
|
102
102
|
"react-dom": "^19.2.4",
|
|
103
|
-
"vite": "^8.0.
|
|
103
|
+
"vite": "^8.0.1",
|
|
104
104
|
"zod": "^3.22.0 || ^4.0.0"
|
|
105
105
|
},
|
|
106
106
|
"peerDependenciesMeta": {
|
|
@@ -47,16 +47,35 @@ export interface NavigationState {
|
|
|
47
47
|
* The context is created lazily to avoid calling createContext at module
|
|
48
48
|
* level. In the RSC environment, React.createContext doesn't exist —
|
|
49
49
|
* calling it at import time would crash the server.
|
|
50
|
+
*
|
|
51
|
+
* IMPORTANT: Context instances are stored on globalThis, NOT in module-
|
|
52
|
+
* level variables. The RSC client bundler duplicates this module across
|
|
53
|
+
* the browser-entry chunk (index) and client-reference chunk (shared-app)
|
|
54
|
+
* because both entry graphs import it. Module-level variables would create
|
|
55
|
+
* separate singleton instances per chunk — the provider in TransitionRoot
|
|
56
|
+
* (index chunk) would use context A while the consumer in LinkStatusProvider
|
|
57
|
+
* (shared-app chunk) reads from context B. globalThis guarantees a single
|
|
58
|
+
* instance regardless of how many times the module is duplicated.
|
|
59
|
+
*
|
|
60
|
+
* See design/19-client-navigation.md §"Singleton Context Guarantee"
|
|
50
61
|
*/
|
|
51
|
-
|
|
62
|
+
|
|
63
|
+
// Symbol keys for globalThis storage — prevents collisions with user code
|
|
64
|
+
const NAV_CTX_KEY = Symbol.for('__timber_nav_ctx');
|
|
65
|
+
const PENDING_CTX_KEY = Symbol.for('__timber_pending_nav_ctx');
|
|
52
66
|
|
|
53
67
|
function getOrCreateContext(): React.Context<NavigationState | null> | undefined {
|
|
54
|
-
|
|
68
|
+
const existing = (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] as
|
|
69
|
+
| React.Context<NavigationState | null>
|
|
70
|
+
| undefined;
|
|
71
|
+
if (existing !== undefined) return existing;
|
|
55
72
|
// createContext may not exist in the RSC environment
|
|
56
73
|
if (typeof React.createContext === 'function') {
|
|
57
|
-
|
|
74
|
+
const ctx = React.createContext<NavigationState | null>(null);
|
|
75
|
+
(globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;
|
|
76
|
+
return ctx;
|
|
58
77
|
}
|
|
59
|
-
return
|
|
78
|
+
return undefined;
|
|
60
79
|
}
|
|
61
80
|
|
|
62
81
|
/**
|
|
@@ -101,22 +120,34 @@ export function NavigationProvider({ value, children }: NavigationProviderProps)
|
|
|
101
120
|
// ---------------------------------------------------------------------------
|
|
102
121
|
|
|
103
122
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
123
|
+
* Navigation state communicated between the router and renderRoot.
|
|
124
|
+
*
|
|
125
|
+
* The router calls setNavigationState() before renderRoot(). The
|
|
126
|
+
* renderRoot callback reads via getNavigationState() to create the
|
|
127
|
+
* NavigationProvider with the correct params/pathname.
|
|
107
128
|
*
|
|
108
129
|
* This is NOT used by hooks directly — hooks read from React context.
|
|
109
|
-
*
|
|
110
|
-
* (
|
|
130
|
+
*
|
|
131
|
+
* Stored on globalThis (like the context instances above) because the
|
|
132
|
+
* router lives in the shared-app chunk while renderRoot lives in the
|
|
133
|
+
* index chunk. Module-level variables would be separate per chunk.
|
|
111
134
|
*/
|
|
112
|
-
|
|
135
|
+
const NAV_STATE_KEY = Symbol.for('__timber_nav_state');
|
|
136
|
+
|
|
137
|
+
function _getNavStateStore(): { current: NavigationState } {
|
|
138
|
+
const g = globalThis as Record<symbol, unknown>;
|
|
139
|
+
if (!g[NAV_STATE_KEY]) {
|
|
140
|
+
g[NAV_STATE_KEY] = { current: { params: {}, pathname: '/' } };
|
|
141
|
+
}
|
|
142
|
+
return g[NAV_STATE_KEY] as { current: NavigationState };
|
|
143
|
+
}
|
|
113
144
|
|
|
114
145
|
export function setNavigationState(state: NavigationState): void {
|
|
115
|
-
|
|
146
|
+
_getNavStateStore().current = state;
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
export function getNavigationState(): NavigationState {
|
|
119
|
-
return
|
|
150
|
+
return _getNavStateStore().current;
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
// ---------------------------------------------------------------------------
|
|
@@ -125,23 +156,25 @@ export function getNavigationState(): NavigationState {
|
|
|
125
156
|
|
|
126
157
|
/**
|
|
127
158
|
* Separate context for the in-flight navigation URL. Provided by
|
|
128
|
-
* TransitionRoot (
|
|
159
|
+
* TransitionRoot (urgent useState), consumed by LinkStatusProvider
|
|
129
160
|
* and useNavigationPending.
|
|
130
161
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* if they were in separate files, the bundler could duplicate the
|
|
135
|
-
* module-level context variable across chunks.
|
|
162
|
+
* Uses globalThis via Symbol.for for the same reason as NavigationContext
|
|
163
|
+
* above — the bundler duplicates this module across chunks, and module-
|
|
164
|
+
* level variables would create separate context instances.
|
|
136
165
|
*/
|
|
137
|
-
let _pendingContext: React.Context<string | null> | undefined;
|
|
138
166
|
|
|
139
167
|
function getOrCreatePendingContext(): React.Context<string | null> | undefined {
|
|
140
|
-
|
|
168
|
+
const existing = (globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] as
|
|
169
|
+
| React.Context<string | null>
|
|
170
|
+
| undefined;
|
|
171
|
+
if (existing !== undefined) return existing;
|
|
141
172
|
if (typeof React.createContext === 'function') {
|
|
142
|
-
|
|
173
|
+
const ctx = React.createContext<string | null>(null);
|
|
174
|
+
(globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] = ctx;
|
|
175
|
+
return ctx;
|
|
143
176
|
}
|
|
144
|
-
return
|
|
177
|
+
return undefined;
|
|
145
178
|
}
|
|
146
179
|
|
|
147
180
|
/**
|