@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.
@@ -99,12 +99,28 @@ function useLinkStatus() {
99
99
  * The context is created lazily to avoid calling createContext at module
100
100
  * level. In the RSC environment, React.createContext doesn't exist —
101
101
  * calling it at import time would crash the server.
102
+ *
103
+ * IMPORTANT: Context instances are stored on globalThis, NOT in module-
104
+ * level variables. The RSC client bundler duplicates this module across
105
+ * the browser-entry chunk (index) and client-reference chunk (shared-app)
106
+ * because both entry graphs import it. Module-level variables would create
107
+ * separate singleton instances per chunk — the provider in TransitionRoot
108
+ * (index chunk) would use context A while the consumer in LinkStatusProvider
109
+ * (shared-app chunk) reads from context B. globalThis guarantees a single
110
+ * instance regardless of how many times the module is duplicated.
111
+ *
112
+ * See design/19-client-navigation.md §"Singleton Context Guarantee"
102
113
  */
103
- var _context;
114
+ var NAV_CTX_KEY = Symbol.for("__timber_nav_ctx");
115
+ var PENDING_CTX_KEY = Symbol.for("__timber_pending_nav_ctx");
104
116
  function getOrCreateContext() {
105
- if (_context !== void 0) return _context;
106
- if (typeof React.createContext === "function") _context = React.createContext(null);
107
- return _context;
117
+ const existing = globalThis[NAV_CTX_KEY];
118
+ if (existing !== void 0) return existing;
119
+ if (typeof React.createContext === "function") {
120
+ const ctx = React.createContext(null);
121
+ globalThis[NAV_CTX_KEY] = ctx;
122
+ return ctx;
123
+ }
108
124
  }
109
125
  /**
110
126
  * Read the navigation context. Returns null during SSR (no provider)
@@ -129,40 +145,50 @@ function NavigationProvider({ value, children }) {
129
145
  return createElement(ctx.Provider, { value }, children);
130
146
  }
131
147
  /**
132
- * Module-level navigation state. Updated by the router before calling
133
- * renderRoot(). The renderRoot callback reads this to create the
134
- * NavigationProvider with the correct values.
148
+ * Navigation state communicated between the router and renderRoot.
149
+ *
150
+ * The router calls setNavigationState() before renderRoot(). The
151
+ * renderRoot callback reads via getNavigationState() to create the
152
+ * NavigationProvider with the correct params/pathname.
135
153
  *
136
154
  * This is NOT used by hooks directly — hooks read from React context.
137
- * This exists only as a communication channel between the router
138
- * (which knows the new nav state) and renderRoot (which wraps the element).
155
+ *
156
+ * Stored on globalThis (like the context instances above) because the
157
+ * router lives in the shared-app chunk while renderRoot lives in the
158
+ * index chunk. Module-level variables would be separate per chunk.
139
159
  */
140
- var _currentNavState = {
141
- params: {},
142
- pathname: "/"
143
- };
160
+ var NAV_STATE_KEY = Symbol.for("__timber_nav_state");
161
+ function _getNavStateStore() {
162
+ const g = globalThis;
163
+ if (!g[NAV_STATE_KEY]) g[NAV_STATE_KEY] = { current: {
164
+ params: {},
165
+ pathname: "/"
166
+ } };
167
+ return g[NAV_STATE_KEY];
168
+ }
144
169
  function setNavigationState(state) {
145
- _currentNavState = state;
170
+ _getNavStateStore().current = state;
146
171
  }
147
172
  function getNavigationState() {
148
- return _currentNavState;
173
+ return _getNavStateStore().current;
149
174
  }
150
175
  /**
151
176
  * Separate context for the in-flight navigation URL. Provided by
152
- * TransitionRoot (useOptimistic state), consumed by LinkStatusProvider
177
+ * TransitionRoot (urgent useState), consumed by LinkStatusProvider
153
178
  * and useNavigationPending.
154
179
  *
155
- * Lives in this module (not a separate file) to guarantee singleton
156
- * identity across chunks. The `'use client'` LinkStatusProvider and
157
- * the non-directive TransitionRoot both import from this module —
158
- * if they were in separate files, the bundler could duplicate the
159
- * module-level context variable across chunks.
180
+ * Uses globalThis via Symbol.for for the same reason as NavigationContext
181
+ * above the bundler duplicates this module across chunks, and module-
182
+ * level variables would create separate context instances.
160
183
  */
161
- var _pendingContext;
162
184
  function getOrCreatePendingContext() {
163
- if (_pendingContext !== void 0) return _pendingContext;
164
- if (typeof React.createContext === "function") _pendingContext = React.createContext(null);
165
- return _pendingContext;
185
+ const existing = globalThis[PENDING_CTX_KEY];
186
+ if (existing !== void 0) return existing;
187
+ if (typeof React.createContext === "function") {
188
+ const ctx = React.createContext(null);
189
+ globalThis[PENDING_CTX_KEY] = ctx;
190
+ return ctx;
191
+ }
166
192
  }
167
193
  /**
168
194
  * Read the pending navigation URL from context.