@timber-js/app 0.2.0-alpha.71 → 0.2.0-alpha.73

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 (248) hide show
  1. package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
  2. package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
  3. package/dist/_chunks/{als-registry-BJARkOcu.js → als-registry-HS0LGUl2.js} +1 -1
  4. package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
  5. package/dist/_chunks/{define-Dz1bqwaS.js → define-C77ScO0m.js} +14 -14
  6. package/dist/_chunks/define-C77ScO0m.js.map +1 -0
  7. package/dist/_chunks/{define-CGuYoRHU.js → define-CZqDwhSu.js} +15 -15
  8. package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
  9. package/dist/_chunks/{define-cookie-B5mewxwM.js → define-cookie-C2IkoFGN.js} +9 -8
  10. package/dist/_chunks/{define-cookie-B5mewxwM.js.map → define-cookie-C2IkoFGN.js.map} +1 -1
  11. package/dist/_chunks/{format-Rn922VH2.js → dev-warnings-DpGRGoDi.js} +4 -26
  12. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
  13. package/dist/_chunks/format-CYBGxKtc.js +14 -0
  14. package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
  15. package/dist/_chunks/{interception-CEdHHviP.js → interception-Dpn_UfAD.js} +2 -2
  16. package/dist/_chunks/{interception-CEdHHviP.js.map → interception-Dpn_UfAD.js.map} +1 -1
  17. package/dist/_chunks/{segment-context-hzuJ048X.js → merge-search-params-Cm_KIWDX.js} +2 -33
  18. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
  19. package/dist/_chunks/{request-context-CywiO4jV.js → request-context-qMsWgy9C.js} +72 -36
  20. package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
  21. package/dist/_chunks/{schema-bridge-C4SwjCQD.js → schema-bridge-C3xl_vfb.js} +1 -1
  22. package/dist/_chunks/{schema-bridge-C4SwjCQD.js.map → schema-bridge-C3xl_vfb.js.map} +1 -1
  23. package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
  24. package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
  25. package/dist/_chunks/ssr-data-DzuI0bIV.js +88 -0
  26. package/dist/_chunks/ssr-data-DzuI0bIV.js.map +1 -0
  27. package/dist/_chunks/{stale-reload-BLUC_Pl_.js → stale-reload-C2plcNtG.js} +1 -1
  28. package/dist/_chunks/{stale-reload-BLUC_Pl_.js.map → stale-reload-C2plcNtG.js.map} +1 -1
  29. package/dist/_chunks/{handler-store-BVePM7hp.js → tracing-CCYbKn5n.js} +60 -60
  30. package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
  31. package/dist/_chunks/use-params-B1AuhI1p.js +307 -0
  32. package/dist/_chunks/use-params-B1AuhI1p.js.map +1 -0
  33. package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-Lo_s_pw2.js} +4 -4
  34. package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
  35. package/dist/_chunks/{wrappers-LZbghvn0.js → wrappers-_DTmImGt.js} +1 -1
  36. package/dist/_chunks/{wrappers-LZbghvn0.js.map → wrappers-_DTmImGt.js.map} +1 -1
  37. package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
  38. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  39. package/dist/adapters/cloudflare-kv-cache.js +95 -0
  40. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  41. package/dist/cache/index.d.ts +18 -4
  42. package/dist/cache/index.d.ts.map +1 -1
  43. package/dist/cache/index.js +78 -12
  44. package/dist/cache/index.js.map +1 -1
  45. package/dist/cache/sizeof.d.ts +22 -0
  46. package/dist/cache/sizeof.d.ts.map +1 -0
  47. package/dist/cli.d.ts +6 -1
  48. package/dist/cli.d.ts.map +1 -1
  49. package/dist/cli.js +6 -1
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client/browser-dev.d.ts +27 -1
  52. package/dist/client/browser-dev.d.ts.map +1 -1
  53. package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
  54. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  55. package/dist/client/browser-entry/hmr.d.ts +21 -0
  56. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  57. package/dist/client/browser-entry/hydrate.d.ts +46 -0
  58. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  59. package/dist/client/browser-entry/index.d.ts +30 -0
  60. package/dist/client/browser-entry/index.d.ts.map +1 -0
  61. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  62. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  63. package/dist/client/browser-entry/router-init.d.ts +23 -0
  64. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  65. package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
  66. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  67. package/dist/client/browser-entry/scroll.d.ts +19 -0
  68. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  69. package/dist/client/error-boundary.js +131 -1
  70. package/dist/client/error-boundary.js.map +1 -0
  71. package/dist/client/index.d.ts +4 -19
  72. package/dist/client/index.d.ts.map +1 -1
  73. package/dist/client/index.js +14 -1191
  74. package/dist/client/index.js.map +1 -1
  75. package/dist/client/internal.d.ts +18 -0
  76. package/dist/client/internal.d.ts.map +1 -0
  77. package/dist/client/internal.js +890 -0
  78. package/dist/client/internal.js.map +1 -0
  79. package/dist/client/navigation-context.d.ts.map +1 -1
  80. package/dist/client/router-ref.d.ts +1 -1
  81. package/dist/client/top-loader.d.ts +2 -2
  82. package/dist/client/use-link-status.d.ts +1 -1
  83. package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
  84. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  85. package/dist/client/use-router.d.ts +1 -1
  86. package/dist/codec.d.ts +10 -0
  87. package/dist/codec.d.ts.map +1 -1
  88. package/dist/codec.js +1 -1
  89. package/dist/config-types.d.ts +210 -0
  90. package/dist/config-types.d.ts.map +1 -0
  91. package/dist/content/index.d.ts +1 -10
  92. package/dist/content/index.d.ts.map +1 -1
  93. package/dist/content/index.js +0 -2
  94. package/dist/cookies/define-cookie.d.ts.map +1 -1
  95. package/dist/cookies/index.d.ts +0 -2
  96. package/dist/cookies/index.d.ts.map +1 -1
  97. package/dist/cookies/index.js +2 -3
  98. package/dist/index.d.ts +25 -288
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +261 -43
  101. package/dist/index.js.map +1 -1
  102. package/dist/plugin-context.d.ts +84 -0
  103. package/dist/plugin-context.d.ts.map +1 -0
  104. package/dist/plugins/adapter-build.d.ts +1 -1
  105. package/dist/plugins/adapter-build.d.ts.map +1 -1
  106. package/dist/plugins/build-manifest.d.ts +1 -1
  107. package/dist/plugins/build-manifest.d.ts.map +1 -1
  108. package/dist/plugins/build-report.d.ts +1 -1
  109. package/dist/plugins/build-report.d.ts.map +1 -1
  110. package/dist/plugins/content.d.ts +1 -1
  111. package/dist/plugins/content.d.ts.map +1 -1
  112. package/dist/plugins/dev-browser-logs.d.ts +1 -1
  113. package/dist/plugins/dev-browser-logs.d.ts.map +1 -1
  114. package/dist/plugins/dev-logs.d.ts +1 -1
  115. package/dist/plugins/dev-logs.d.ts.map +1 -1
  116. package/dist/plugins/dev-server.d.ts +1 -1
  117. package/dist/plugins/dev-server.d.ts.map +1 -1
  118. package/dist/plugins/entries.d.ts +1 -1
  119. package/dist/plugins/entries.d.ts.map +1 -1
  120. package/dist/plugins/fonts.d.ts +1 -1
  121. package/dist/plugins/fonts.d.ts.map +1 -1
  122. package/dist/plugins/mdx.d.ts +1 -1
  123. package/dist/plugins/mdx.d.ts.map +1 -1
  124. package/dist/plugins/routing.d.ts +1 -1
  125. package/dist/plugins/routing.d.ts.map +1 -1
  126. package/dist/plugins/shims.d.ts +1 -1
  127. package/dist/plugins/shims.d.ts.map +1 -1
  128. package/dist/plugins/static-build.d.ts +4 -4
  129. package/dist/plugins/static-build.d.ts.map +1 -1
  130. package/dist/routing/index.js +1 -1
  131. package/dist/search-params/define.d.ts +6 -6
  132. package/dist/search-params/define.d.ts.map +1 -1
  133. package/dist/search-params/index.d.ts +1 -2
  134. package/dist/search-params/index.d.ts.map +1 -1
  135. package/dist/search-params/index.js +4 -4
  136. package/dist/search-params/registry.d.ts +1 -1
  137. package/dist/search-params/registry.d.ts.map +1 -1
  138. package/dist/segment-params/define.d.ts +6 -6
  139. package/dist/segment-params/define.d.ts.map +1 -1
  140. package/dist/segment-params/index.d.ts +0 -1
  141. package/dist/segment-params/index.d.ts.map +1 -1
  142. package/dist/segment-params/index.js +3 -3
  143. package/dist/server/als-registry.d.ts +1 -1
  144. package/dist/server/dev-holding-server.d.ts +52 -0
  145. package/dist/server/dev-holding-server.d.ts.map +1 -0
  146. package/dist/server/dev-warnings.d.ts +1 -7
  147. package/dist/server/dev-warnings.d.ts.map +1 -1
  148. package/dist/server/index.d.ts +6 -45
  149. package/dist/server/index.d.ts.map +1 -1
  150. package/dist/server/index.js +7 -3272
  151. package/dist/server/index.js.map +1 -1
  152. package/dist/server/internal.d.ts +46 -0
  153. package/dist/server/internal.d.ts.map +1 -0
  154. package/dist/server/internal.js +2865 -0
  155. package/dist/server/internal.js.map +1 -0
  156. package/dist/server/pipeline.d.ts.map +1 -1
  157. package/dist/server/primitives.d.ts +41 -17
  158. package/dist/server/primitives.d.ts.map +1 -1
  159. package/dist/server/request-context.d.ts +45 -15
  160. package/dist/server/request-context.d.ts.map +1 -1
  161. package/dist/server/tracing.d.ts +4 -4
  162. package/dist/server/tracing.d.ts.map +1 -1
  163. package/dist/shims/headers.d.ts +2 -1
  164. package/dist/shims/headers.d.ts.map +1 -1
  165. package/dist/shims/navigation.d.ts +2 -1
  166. package/dist/shims/navigation.d.ts.map +1 -1
  167. package/package.json +19 -13
  168. package/src/adapters/cloudflare-kv-cache.ts +142 -0
  169. package/src/cache/handler-store.ts +2 -2
  170. package/src/cache/index.ts +74 -15
  171. package/src/cache/sizeof.ts +31 -0
  172. package/src/cli.ts +6 -1
  173. package/src/client/browser-dev.ts +128 -1
  174. package/src/client/browser-entry/action-dispatch.ts +116 -0
  175. package/src/client/browser-entry/hmr.ts +81 -0
  176. package/src/client/browser-entry/hydrate.ts +145 -0
  177. package/src/client/browser-entry/index.ts +138 -0
  178. package/src/client/browser-entry/post-hydration.ts +119 -0
  179. package/src/client/browser-entry/router-init.ts +184 -0
  180. package/src/client/browser-entry/rsc-stream.ts +157 -0
  181. package/src/client/browser-entry/scroll.ts +27 -0
  182. package/src/client/index.ts +10 -38
  183. package/src/client/internal.ts +57 -0
  184. package/src/client/navigation-context.ts +6 -2
  185. package/src/client/navigation-root.tsx +1 -1
  186. package/src/client/router-ref.ts +1 -1
  187. package/src/client/top-loader.tsx +2 -2
  188. package/src/client/use-link-status.ts +1 -1
  189. package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +5 -5
  190. package/src/client/use-query-states.ts +2 -2
  191. package/src/client/use-router.ts +1 -1
  192. package/src/codec.ts +15 -0
  193. package/src/config-types.ts +208 -0
  194. package/src/content/index.ts +5 -13
  195. package/src/cookies/define-cookie.ts +9 -7
  196. package/src/cookies/index.ts +6 -5
  197. package/src/index.ts +84 -416
  198. package/src/plugin-context.ts +200 -0
  199. package/src/plugins/adapter-build.ts +1 -1
  200. package/src/plugins/build-manifest.ts +1 -1
  201. package/src/plugins/build-report.ts +1 -1
  202. package/src/plugins/content.ts +1 -1
  203. package/src/plugins/dev-browser-logs.ts +1 -1
  204. package/src/plugins/dev-logs.ts +1 -1
  205. package/src/plugins/dev-server.ts +16 -1
  206. package/src/plugins/entries.ts +2 -2
  207. package/src/plugins/fonts.ts +4 -3
  208. package/src/plugins/mdx.ts +1 -1
  209. package/src/plugins/routing.ts +1 -1
  210. package/src/plugins/shims.ts +53 -5
  211. package/src/plugins/static-build.ts +8 -6
  212. package/src/search-params/define.ts +22 -22
  213. package/src/search-params/index.ts +3 -3
  214. package/src/search-params/registry.ts +1 -1
  215. package/src/segment-params/define.ts +18 -18
  216. package/src/segment-params/index.ts +2 -1
  217. package/src/server/action-handler.ts +1 -1
  218. package/src/server/als-registry.ts +3 -3
  219. package/src/server/dev-holding-server.ts +185 -0
  220. package/src/server/dev-warnings.ts +2 -21
  221. package/src/server/html-injectors.ts +3 -3
  222. package/src/server/index.ts +25 -180
  223. package/src/server/internal.ts +169 -0
  224. package/src/server/pipeline.ts +12 -7
  225. package/src/server/primitives.ts +71 -30
  226. package/src/server/request-context.ts +77 -39
  227. package/src/server/route-element-builder.ts +1 -1
  228. package/src/server/rsc-entry/index.ts +2 -2
  229. package/src/server/rsc-entry/ssr-renderer.ts +1 -1
  230. package/src/server/slot-resolver.ts +1 -1
  231. package/src/server/tracing.ts +6 -6
  232. package/src/server/tree-builder.ts +1 -1
  233. package/src/shims/headers.ts +5 -1
  234. package/src/shims/navigation.ts +5 -1
  235. package/dist/_chunks/als-registry-BJARkOcu.js.map +0 -1
  236. package/dist/_chunks/define-CGuYoRHU.js.map +0 -1
  237. package/dist/_chunks/define-Dz1bqwaS.js.map +0 -1
  238. package/dist/_chunks/error-boundary-D9hzsveV.js +0 -216
  239. package/dist/_chunks/error-boundary-D9hzsveV.js.map +0 -1
  240. package/dist/_chunks/format-Rn922VH2.js.map +0 -1
  241. package/dist/_chunks/handler-store-BVePM7hp.js.map +0 -1
  242. package/dist/_chunks/request-context-CywiO4jV.js.map +0 -1
  243. package/dist/_chunks/segment-context-hzuJ048X.js.map +0 -1
  244. package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +0 -1
  245. package/dist/client/browser-entry.d.ts +0 -21
  246. package/dist/client/browser-entry.d.ts.map +0 -1
  247. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  248. package/src/client/browser-entry.ts +0 -846
@@ -0,0 +1,307 @@
1
+ import { a as _setCurrentParams, l as currentParams, n as getSsrData, o as _setGlobalRouter, u as globalRouter } from "./ssr-data-DzuI0bIV.js";
2
+ import React, { createElement } from "react";
3
+ //#region src/client/router-ref.ts
4
+ /**
5
+ * Set the global router instance. Called once during bootstrap.
6
+ */
7
+ function setGlobalRouter(router) {
8
+ _setGlobalRouter(router);
9
+ }
10
+ /**
11
+ * Get the global router instance. Throws if called before bootstrap.
12
+ * Used by client-side hooks (usePendingNavigation, etc.)
13
+ */
14
+ function getRouter() {
15
+ if (!globalRouter) throw new Error("[timber] Router not initialized. getRouter() was called before bootstrap().");
16
+ return globalRouter;
17
+ }
18
+ /**
19
+ * Get the global router instance or null if not yet initialized.
20
+ * Used by useRouter() methods to avoid silent failures — callers
21
+ * can log a meaningful warning instead of silently no-oping.
22
+ */
23
+ function getRouterOrNull() {
24
+ return globalRouter;
25
+ }
26
+ //#endregion
27
+ //#region src/client/link-pending-store.ts
28
+ var LINK_PENDING_KEY = Symbol.for("__timber_link_pending");
29
+ /** Status object indicating link is pending — shared reference */
30
+ var PENDING_LINK_STATUS = { pending: true };
31
+ /** Status object indicating link is idle — shared reference */
32
+ var IDLE_LINK_STATUS = { pending: false };
33
+ function getStore() {
34
+ const g = globalThis;
35
+ if (!g[LINK_PENDING_KEY]) g[LINK_PENDING_KEY] = {
36
+ current: null,
37
+ navId: 0
38
+ };
39
+ return g[LINK_PENDING_KEY];
40
+ }
41
+ /**
42
+ * Register the link instance that initiated the current navigation.
43
+ *
44
+ * Called from <Link>'s click handler before router.navigate().
45
+ * - Resets the previous pending link to IDLE (urgent update, immediate)
46
+ * - Does NOT set the new link to PENDING here — the Link's click handler
47
+ * calls setLinkStatus(PENDING) directly for the eager show
48
+ * - Increments the navId counter for stale-clear protection
49
+ *
50
+ * Pass `null` to clear (e.g., for programmatic navigations).
51
+ */
52
+ function setLinkForCurrentNavigation(link) {
53
+ const store = getStore();
54
+ const prev = store.current;
55
+ if (prev && prev !== link) prev.setLinkStatus(IDLE_LINK_STATUS);
56
+ store.current = link;
57
+ store.navId++;
58
+ }
59
+ /**
60
+ * Unmount a link instance from navigation tracking. Called when a Link
61
+ * component unmounts while it is the current navigation link. Prevents
62
+ * calling setState on an unmounted component.
63
+ */
64
+ function unmountLinkForCurrentNavigation(link) {
65
+ const store = getStore();
66
+ if (store.current === link) store.current = null;
67
+ }
68
+ //#endregion
69
+ //#region src/client/navigation-context.ts
70
+ /**
71
+ * NavigationContext — React context for navigation state.
72
+ *
73
+ * Holds the current route params and pathname, updated atomically
74
+ * with the RSC tree on each navigation. This replaces the previous
75
+ * useSyncExternalStore approach for useSegmentParams() and usePathname(),
76
+ * which suffered from a timing gap: the new tree could commit before
77
+ * the external store re-renders fired, causing a frame where both
78
+ * old and new active states were visible simultaneously.
79
+ *
80
+ * By wrapping the RSC payload element in NavigationProvider inside
81
+ * renderRoot(), the context value and the element tree are passed to
82
+ * reactRoot.render() in the same call — atomic by construction.
83
+ * All consumers (useParams, usePathname) see the new values in the
84
+ * same render pass as the new tree.
85
+ *
86
+ * During SSR, no NavigationProvider is mounted. Hooks fall back to
87
+ * the ALS-backed getSsrData() for per-request isolation.
88
+ *
89
+ * IMPORTANT: createContext and useContext are NOT available in the RSC
90
+ * environment (React Server Components use a stripped-down React).
91
+ * The context is lazily initialized on first access, and all functions
92
+ * that depend on these APIs are safe to call from any environment —
93
+ * they return null or no-op when the APIs aren't available.
94
+ *
95
+ * SINGLETON GUARANTEE: All shared mutable state uses globalThis via
96
+ * Symbol.for keys. The RSC client bundler can duplicate this module
97
+ * across chunks (browser-entry graph + client-reference graph). With
98
+ * ESM output, each chunk gets its own module scope — module-level
99
+ * variables would create separate singleton instances per chunk.
100
+ * globalThis guarantees a single instance regardless of duplication.
101
+ *
102
+ * This workaround will be removed when Rolldown ships `format: 'app'`
103
+ * (module registry format that deduplicates like webpack/Turbopack).
104
+ * See design/27-chunking-strategy.md.
105
+ *
106
+ * See design/19-client-navigation.md §"NavigationContext"
107
+ */
108
+ /**
109
+ * The context is created lazily to avoid calling createContext at module
110
+ * level. In the RSC environment, React.createContext doesn't exist —
111
+ * calling it at import time would crash the server.
112
+ *
113
+ * Context instances are stored on globalThis (NOT in module-level
114
+ * variables) because the ESM bundler can duplicate this module across
115
+ * chunks. Module-level variables would create separate instances per
116
+ * chunk — the provider in NavigationRoot (index chunk) would use
117
+ * context A while the consumer in usePendingNavigation (shared chunk)
118
+ * reads from context B. globalThis guarantees a single instance.
119
+ *
120
+ * See design/27-chunking-strategy.md §"Singleton Safety"
121
+ *
122
+ * NOTE: Despite similar naming, `usePendingNavigationUrl()` here is an
123
+ * internal helper — the public hook is `usePendingNavigation()` in
124
+ * use-pending-navigation.ts.
125
+ */
126
+ var NAV_CTX_KEY = Symbol.for("__timber_nav_ctx");
127
+ var PENDING_CTX_KEY = Symbol.for("__timber_pending_nav_ctx");
128
+ function getOrCreateContext() {
129
+ const existing = globalThis[NAV_CTX_KEY];
130
+ if (existing !== void 0) return existing;
131
+ if (typeof React.createContext === "function") {
132
+ const ctx = React.createContext(null);
133
+ globalThis[NAV_CTX_KEY] = ctx;
134
+ return ctx;
135
+ }
136
+ }
137
+ /**
138
+ * Read the navigation context. Returns null during SSR (no provider)
139
+ * or in the RSC environment (no context available).
140
+ * Internal — used by useSegmentParams() and usePathname().
141
+ */
142
+ function useNavigationContext() {
143
+ const ctx = getOrCreateContext();
144
+ if (!ctx) return null;
145
+ if (typeof React.useContext !== "function") return null;
146
+ return React.useContext(ctx);
147
+ }
148
+ /**
149
+ * Wraps children with NavigationContext.Provider.
150
+ *
151
+ * Used in browser-entry.ts renderRoot to wrap the RSC payload element
152
+ * so that navigation state updates atomically with the tree render.
153
+ */
154
+ function NavigationProvider({ value, children }) {
155
+ const ctx = getOrCreateContext();
156
+ if (!ctx) return children;
157
+ return createElement(ctx.Provider, { value }, children);
158
+ }
159
+ /**
160
+ * Navigation state communicated between the router and renderRoot.
161
+ *
162
+ * The router calls setNavigationState() before renderRoot(). The
163
+ * renderRoot callback reads via getNavigationState() to create the
164
+ * NavigationProvider with the correct params/pathname.
165
+ *
166
+ * This is NOT used by hooks directly — hooks read from React context.
167
+ *
168
+ * Stored on globalThis (like the context instances above) because the
169
+ * router lives in one chunk while renderRoot lives in another. Module-
170
+ * level variables would be separate per chunk.
171
+ */
172
+ var NAV_STATE_KEY = Symbol.for("__timber_nav_state");
173
+ function _getNavStateStore() {
174
+ const g = globalThis;
175
+ if (!g[NAV_STATE_KEY]) g[NAV_STATE_KEY] = { current: {
176
+ params: {},
177
+ pathname: "/"
178
+ } };
179
+ return g[NAV_STATE_KEY];
180
+ }
181
+ function setNavigationState(state) {
182
+ _getNavStateStore().current = state;
183
+ }
184
+ function getNavigationState() {
185
+ return _getNavStateStore().current;
186
+ }
187
+ /**
188
+ * Separate context for the in-flight navigation URL. Provided by
189
+ * NavigationRoot (urgent useState), consumed by usePendingNavigation
190
+ * and TopLoader. Per-link pending state uses useOptimistic instead
191
+ * (see link-pending-store.ts).
192
+ *
193
+ * Uses globalThis via Symbol.for for the same reason as NavigationContext
194
+ * above — the bundler may duplicate this module across chunks, and module-
195
+ * level variables would create separate context instances.
196
+ */
197
+ function getOrCreatePendingContext() {
198
+ const existing = globalThis[PENDING_CTX_KEY];
199
+ if (existing !== void 0) return existing;
200
+ if (typeof React.createContext === "function") {
201
+ const ctx = React.createContext(null);
202
+ globalThis[PENDING_CTX_KEY] = ctx;
203
+ return ctx;
204
+ }
205
+ }
206
+ /**
207
+ * Read the pending navigation URL from context.
208
+ * Returns null during SSR (no provider) or in the RSC environment.
209
+ */
210
+ function usePendingNavigationUrl() {
211
+ const ctx = getOrCreatePendingContext();
212
+ if (!ctx) return null;
213
+ if (typeof React.useContext !== "function") return null;
214
+ return React.useContext(ctx);
215
+ }
216
+ //#endregion
217
+ //#region src/client/top-loader.tsx
218
+ /**
219
+ * TopLoader — Built-in progress bar for client navigations.
220
+ *
221
+ * Shows an animated progress bar at the top of the viewport while an RSC
222
+ * navigation is in flight. Injected automatically by the framework into
223
+ * NavigationRoot — users never render this component directly.
224
+ *
225
+ * Configuration is via timber.config.ts `topLoader` key. Enabled by default.
226
+ * Users who want a fully custom progress indicator disable the built-in one
227
+ * (`topLoader: { enabled: false }`) and use `usePendingNavigation()` directly.
228
+ *
229
+ * Animation approach: pure CSS @keyframes. The bar crawls from 0% to ~90%
230
+ * width over ~30s using ease-out timing. When navigation completes, the bar
231
+ * snaps to 100% and fades out over 200ms. No JS animation loops (RAF, setInterval).
232
+ *
233
+ * Phase transitions are derived synchronously during render (React's
234
+ * getDerivedStateFromProps pattern) — no useEffect needed for state tracking.
235
+ * The finishing → hidden cleanup uses onTransitionEnd from the CSS transition.
236
+ *
237
+ * When delay > 0, CSS animation-delay + a visibility keyframe ensure the bar
238
+ * stays invisible during the delay period. If navigation finishes before the
239
+ * delay, the bar was never visible so the finish transition is also invisible.
240
+ *
241
+ * See design/19-client-navigation.md §"usePendingNavigation()"
242
+ * See LOCAL-336 for design decisions.
243
+ */
244
+ //#endregion
245
+ //#region src/client/navigation-root.tsx
246
+ /**
247
+ * Module-level flag indicating a hard (MPA) navigation is in progress.
248
+ *
249
+ * When true:
250
+ * - NavigationRoot throws an unresolved thenable to suspend forever,
251
+ * preventing React from rendering children during page teardown
252
+ * (avoids "Rendered more hooks" crashes).
253
+ * - The Navigation API handler skips interception, letting the browser
254
+ * perform a full page load (prevents infinite loops where
255
+ * window.location.href → navigate event → router.navigate → 500 →
256
+ * window.location.href → ...).
257
+ *
258
+ * Uses globalThis for singleton guarantee across chunks (same pattern
259
+ * as NavigationContext). See design/19-client-navigation.md §"Singleton
260
+ * Guarantee via globalThis".
261
+ */
262
+ var HARD_NAV_KEY = Symbol.for("__timber_hard_navigating");
263
+ function getHardNavStore() {
264
+ const g = globalThis;
265
+ if (!g[HARD_NAV_KEY]) g[HARD_NAV_KEY] = { value: false };
266
+ return g[HARD_NAV_KEY];
267
+ }
268
+ /**
269
+ * Set the hard-navigating flag. Call this BEFORE setting
270
+ * window.location.href or window.location.reload() to prevent:
271
+ * 1. React from rendering children during page teardown
272
+ * 2. Navigation API from intercepting the hard navigation
273
+ */
274
+ function setHardNavigating(value) {
275
+ getHardNavStore().value = value;
276
+ }
277
+ //#endregion
278
+ //#region src/client/use-params.ts
279
+ /**
280
+ * Set the current route params in the module-level store.
281
+ *
282
+ * Called by the router on each navigation. This updates the fallback
283
+ * snapshot used by tests and by the hook when called outside a React
284
+ * component (no NavigationContext available).
285
+ *
286
+ * On the client, the primary reactivity path is NavigationContext —
287
+ * the router calls setNavigationState() then renderRoot() which wraps
288
+ * the element in NavigationProvider. setCurrentParams is still called
289
+ * for the module-level fallback.
290
+ *
291
+ * During SSR, params are also available via getSsrData().params
292
+ * (ALS-backed).
293
+ */
294
+ function setCurrentParams(params) {
295
+ _setCurrentParams(params);
296
+ }
297
+ function useSegmentParams(_route) {
298
+ try {
299
+ const navContext = useNavigationContext();
300
+ if (navContext !== null) return navContext.params;
301
+ } catch {}
302
+ return getSsrData()?.params ?? currentParams;
303
+ }
304
+ //#endregion
305
+ export { getNavigationState as a, usePendingNavigationUrl as c, setLinkForCurrentNavigation as d, unmountLinkForCurrentNavigation as f, setGlobalRouter as h, NavigationProvider as i, IDLE_LINK_STATUS as l, getRouterOrNull as m, useSegmentParams as n, setNavigationState as o, getRouter as p, setHardNavigating as r, useNavigationContext as s, setCurrentParams as t, PENDING_LINK_STATUS as u };
306
+
307
+ //# sourceMappingURL=use-params-B1AuhI1p.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-params-B1AuhI1p.js","names":[],"sources":["../../src/client/router-ref.ts","../../src/client/link-pending-store.ts","../../src/client/navigation-context.ts","../../src/client/top-loader.tsx","../../src/client/navigation-root.tsx","../../src/client/use-params.ts"],"sourcesContent":["// Global router reference — shared between browser-entry and client hooks.\n//\n// Delegates to client/state.ts for the actual module-level variable.\n// This ensures singleton semantics regardless of import path — all\n// callers converge on the same state.ts instance via the barrel.\n//\n// See design/18-build-system.md §\"Module Singleton Strategy\"\n\nimport type { RouterInstance } from './router.js';\nimport { globalRouter, _setGlobalRouter } from './state.js';\n\n/**\n * Set the global router instance. Called once during bootstrap.\n */\nexport function setGlobalRouter(router: RouterInstance): void {\n _setGlobalRouter(router);\n}\n\n/**\n * Get the global router instance. Throws if called before bootstrap.\n * Used by client-side hooks (usePendingNavigation, etc.)\n */\nexport function getRouter(): RouterInstance {\n if (!globalRouter) {\n throw new Error('[timber] Router not initialized. getRouter() was called before bootstrap().');\n }\n return globalRouter;\n}\n\n/**\n * Get the global router instance or null if not yet initialized.\n * Used by useRouter() methods to avoid silent failures — callers\n * can log a meaningful warning instead of silently no-oping.\n */\nexport function getRouterOrNull(): RouterInstance | null {\n return globalRouter;\n}\n\n/**\n * Reset the global router to null. Used only in tests to isolate\n * module-level state between test cases.\n * @internal\n */\nexport function resetGlobalRouter(): void {\n _setGlobalRouter(null);\n}\n","/**\n * Link Pending Store — module-level singleton for per-link pending state.\n *\n * Tracks which link instance is currently navigating so that only the\n * clicked link shows pending. All other links remain unaffected — zero\n * wasted re-renders.\n *\n * The store holds:\n * - A reference to the currently navigating link's state setter\n * - A navigation counter to guard against stale clears (race condition\n * when the user clicks Link B before Link A's navigation completes)\n *\n * Flow:\n * 1. Link click handler: setLinkForCurrentNavigation(instance) →\n * resets previous link (urgent), sets new link pending (urgent),\n * stores setter + increments navId\n * 2. NavigationRoot startTransition: captures navId, does async work\n * 3. NavigationRoot commit: resetLinkPending(capturedNavId) →\n * calls setter(IDLE) inside the transition (batched, atomic with tree)\n * Only clears if navId matches (prevents stale T1 from clearing T2's link)\n *\n * SINGLETON GUARANTEE: Uses `globalThis` via `Symbol.for` keys (same\n * pattern as NavigationContext) because the RSC client bundler can\n * duplicate this module across chunks. Module-level variables would\n * create separate instances per chunk.\n *\n * See design/19-client-navigation.md §\"Per-Link Pending State\"\n */\n\nimport type { LinkStatus } from './use-link-status.js';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface LinkPendingInstance {\n setLinkStatus: (status: LinkStatus) => void;\n}\n\n// ─── Singleton Storage ───────────────────────────────────────────\n\nconst LINK_PENDING_KEY = Symbol.for('__timber_link_pending');\n\n/** Status object indicating link is pending — shared reference */\nexport const PENDING_LINK_STATUS: LinkStatus = { pending: true };\n\n/** Status object indicating link is idle — shared reference */\nexport const IDLE_LINK_STATUS: LinkStatus = { pending: false };\n\ninterface LinkPendingState {\n /** The link instance that initiated the current navigation, or null */\n current: LinkPendingInstance | null;\n /** Monotonically increasing counter — guards against stale clears */\n navId: number;\n}\n\nfunction getStore(): LinkPendingState {\n const g = globalThis as Record<symbol, unknown>;\n if (!g[LINK_PENDING_KEY]) {\n g[LINK_PENDING_KEY] = { current: null, navId: 0 };\n }\n return g[LINK_PENDING_KEY] as LinkPendingState;\n}\n\n// ─── Public API ──────────────────────────────────────────────────\n\n/**\n * Register the link instance that initiated the current navigation.\n *\n * Called from <Link>'s click handler before router.navigate().\n * - Resets the previous pending link to IDLE (urgent update, immediate)\n * - Does NOT set the new link to PENDING here — the Link's click handler\n * calls setLinkStatus(PENDING) directly for the eager show\n * - Increments the navId counter for stale-clear protection\n *\n * Pass `null` to clear (e.g., for programmatic navigations).\n */\nexport function setLinkForCurrentNavigation(link: LinkPendingInstance | null): void {\n const store = getStore();\n const prev = store.current;\n\n // Reset previous pending link to idle (urgent update — shows immediately)\n if (prev && prev !== link) {\n prev.setLinkStatus(IDLE_LINK_STATUS);\n }\n\n store.current = link;\n store.navId++;\n}\n\n/**\n * Get the current navigation ID. Called at the start of the transition\n * to capture the ID before async work begins.\n */\nexport function getCurrentNavId(): number {\n return getStore().navId;\n}\n\n/**\n * Reset the current link's pending state to IDLE, but only if the navId\n * matches. Called inside NavigationRoot's startTransition after the async\n * work completes — the setter call is a transition update, so it commits\n * atomically with the new tree.\n *\n * The navId guard prevents stale transitions from clearing a newer\n * navigation's pending state. Scenario:\n * Click A → navId=1, T1 starts\n * Click B → navId=2, A reset to IDLE, T2 starts\n * T1 completes → resetLinkPending(1) → navId is 2, skip ✓\n * T2 completes → resetLinkPending(2) → navId is 2, clear ✓\n */\nexport function resetLinkPending(forNavId: number): void {\n const store = getStore();\n if (store.navId === forNavId && store.current) {\n store.current.setLinkStatus(IDLE_LINK_STATUS);\n store.current = null;\n }\n}\n\n/**\n * Clean up the link pending store entirely. Safety net for error paths.\n */\nexport function clearLinkPendingSetter(): void {\n const store = getStore();\n store.current = null;\n}\n\n/**\n * Unmount a link instance from navigation tracking. Called when a Link\n * component unmounts while it is the current navigation link. Prevents\n * calling setState on an unmounted component.\n */\nexport function unmountLinkForCurrentNavigation(link: LinkPendingInstance): void {\n const store = getStore();\n if (store.current === link) {\n store.current = null;\n }\n}\n","'use client';\n\n/**\n * NavigationContext — React context for navigation state.\n *\n * Holds the current route params and pathname, updated atomically\n * with the RSC tree on each navigation. This replaces the previous\n * useSyncExternalStore approach for useSegmentParams() and usePathname(),\n * which suffered from a timing gap: the new tree could commit before\n * the external store re-renders fired, causing a frame where both\n * old and new active states were visible simultaneously.\n *\n * By wrapping the RSC payload element in NavigationProvider inside\n * renderRoot(), the context value and the element tree are passed to\n * reactRoot.render() in the same call — atomic by construction.\n * All consumers (useParams, usePathname) see the new values in the\n * same render pass as the new tree.\n *\n * During SSR, no NavigationProvider is mounted. Hooks fall back to\n * the ALS-backed getSsrData() for per-request isolation.\n *\n * IMPORTANT: createContext and useContext are NOT available in the RSC\n * environment (React Server Components use a stripped-down React).\n * The context is lazily initialized on first access, and all functions\n * that depend on these APIs are safe to call from any environment —\n * they return null or no-op when the APIs aren't available.\n *\n * SINGLETON GUARANTEE: All shared mutable state uses globalThis via\n * Symbol.for keys. The RSC client bundler can duplicate this module\n * across chunks (browser-entry graph + client-reference graph). With\n * ESM output, each chunk gets its own module scope — module-level\n * variables would create separate singleton instances per chunk.\n * globalThis guarantees a single instance regardless of duplication.\n *\n * This workaround will be removed when Rolldown ships `format: 'app'`\n * (module registry format that deduplicates like webpack/Turbopack).\n * See design/27-chunking-strategy.md.\n *\n * See design/19-client-navigation.md §\"NavigationContext\"\n */\n\nimport React, { createElement, type ReactNode } from 'react';\n\n// ---------------------------------------------------------------------------\n// Context type\n// ---------------------------------------------------------------------------\n\nexport interface NavigationState {\n params: Record<string, string | string[]>;\n pathname: string;\n}\n\n// ---------------------------------------------------------------------------\n// Lazy context initialization\n// ---------------------------------------------------------------------------\n\n/**\n * The context is created lazily to avoid calling createContext at module\n * level. In the RSC environment, React.createContext doesn't exist —\n * calling it at import time would crash the server.\n *\n * Context instances are stored on globalThis (NOT in module-level\n * variables) because the ESM bundler can duplicate this module across\n * chunks. Module-level variables would create separate instances per\n * chunk — the provider in NavigationRoot (index chunk) would use\n * context A while the consumer in usePendingNavigation (shared chunk)\n * reads from context B. globalThis guarantees a single instance.\n *\n * See design/27-chunking-strategy.md §\"Singleton Safety\"\n *\n * NOTE: Despite similar naming, `usePendingNavigationUrl()` here is an\n * internal helper — the public hook is `usePendingNavigation()` in\n * use-pending-navigation.ts.\n */\n\n// Symbol keys for globalThis storage — prevents collisions with user code\nconst NAV_CTX_KEY = Symbol.for('__timber_nav_ctx');\nconst PENDING_CTX_KEY = Symbol.for('__timber_pending_nav_ctx');\n\nfunction getOrCreateContext(): React.Context<NavigationState | null> | undefined {\n const existing = (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] as\n | React.Context<NavigationState | null>\n | undefined;\n if (existing !== undefined) return existing;\n // createContext may not exist in the RSC environment\n if (typeof React.createContext === 'function') {\n const ctx = React.createContext<NavigationState | null>(null);\n (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;\n return ctx;\n }\n return undefined;\n}\n\n/**\n * Read the navigation context. Returns null during SSR (no provider)\n * or in the RSC environment (no context available).\n * Internal — used by useSegmentParams() and usePathname().\n */\nexport function useNavigationContext(): NavigationState | null {\n const ctx = getOrCreateContext();\n if (!ctx) return null;\n // useContext may not exist in the RSC environment — caller wraps in try/catch\n if (typeof React.useContext !== 'function') return null;\n return React.useContext(ctx);\n}\n\n// ---------------------------------------------------------------------------\n// Provider component\n// ---------------------------------------------------------------------------\n\nexport interface NavigationProviderProps {\n value: NavigationState;\n children?: ReactNode;\n}\n\n/**\n * Wraps children with NavigationContext.Provider.\n *\n * Used in browser-entry.ts renderRoot to wrap the RSC payload element\n * so that navigation state updates atomically with the tree render.\n */\nexport function NavigationProvider({\n value,\n children,\n}: NavigationProviderProps): React.ReactElement {\n const ctx = getOrCreateContext();\n if (!ctx) {\n // RSC environment — no context available. Return children as-is.\n return children as React.ReactElement;\n }\n return createElement(ctx.Provider, { value }, children);\n}\n\n// ---------------------------------------------------------------------------\n// Module-level state for renderRoot to read\n// ---------------------------------------------------------------------------\n\n/**\n * Navigation state communicated between the router and renderRoot.\n *\n * The router calls setNavigationState() before renderRoot(). The\n * renderRoot callback reads via getNavigationState() to create the\n * NavigationProvider with the correct params/pathname.\n *\n * This is NOT used by hooks directly — hooks read from React context.\n *\n * Stored on globalThis (like the context instances above) because the\n * router lives in one chunk while renderRoot lives in another. Module-\n * level variables would be separate per chunk.\n */\nconst NAV_STATE_KEY = Symbol.for('__timber_nav_state');\n\nfunction _getNavStateStore(): { current: NavigationState } {\n const g = globalThis as Record<symbol, unknown>;\n if (!g[NAV_STATE_KEY]) {\n g[NAV_STATE_KEY] = { current: { params: {}, pathname: '/' } };\n }\n return g[NAV_STATE_KEY] as { current: NavigationState };\n}\n\nexport function setNavigationState(state: NavigationState): void {\n _getNavStateStore().current = state;\n}\n\nexport function getNavigationState(): NavigationState {\n return _getNavStateStore().current;\n}\n\n// ---------------------------------------------------------------------------\n// Pending Navigation Context (same module for singleton guarantee)\n// ---------------------------------------------------------------------------\n\n/**\n * Separate context for the in-flight navigation URL. Provided by\n * NavigationRoot (urgent useState), consumed by usePendingNavigation\n * and TopLoader. Per-link pending state uses useOptimistic instead\n * (see link-pending-store.ts).\n *\n * Uses globalThis via Symbol.for for the same reason as NavigationContext\n * above — the bundler may duplicate this module across chunks, and module-\n * level variables would create separate context instances.\n */\n\nfunction getOrCreatePendingContext(): React.Context<string | null> | undefined {\n const existing = (globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] as\n | React.Context<string | null>\n | undefined;\n if (existing !== undefined) return existing;\n if (typeof React.createContext === 'function') {\n const ctx = React.createContext<string | null>(null);\n (globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] = ctx;\n return ctx;\n }\n return undefined;\n}\n\n/**\n * Read the pending navigation URL from context.\n * Returns null during SSR (no provider) or in the RSC environment.\n */\nexport function usePendingNavigationUrl(): string | null {\n const ctx = getOrCreatePendingContext();\n if (!ctx) return null;\n if (typeof React.useContext !== 'function') return null;\n return React.useContext(ctx);\n}\n\n/**\n * Provider for the pending navigation URL. Wraps children with\n * the pending context Provider.\n */\nexport function PendingNavigationProvider({\n value,\n children,\n}: {\n value: string | null;\n children?: ReactNode;\n}): React.ReactElement {\n const ctx = getOrCreatePendingContext();\n if (!ctx) {\n return children as React.ReactElement;\n }\n return createElement(ctx.Provider, { value }, children);\n}\n\n// ---------------------------------------------------------------------------\n// Navigation API transition state (optional progressive enhancement)\n// ---------------------------------------------------------------------------\n\n/**\n * Check if the browser's Navigation API has an active transition.\n *\n * When the Navigation API is available and a navigation has been intercepted\n * via event.intercept(), `navigation.transition` is non-null until the\n * handler resolves. This provides browser-native progress tracking that\n * can be used alongside the existing pendingUrl mechanism.\n *\n * Returns false when Navigation API is unavailable or no transition is active.\n */\nexport function hasNativeNavigationTransition(): boolean {\n if (typeof window === 'undefined') return false;\n const nav = (window as unknown as { navigation?: { transition?: unknown } }).navigation;\n return nav?.transition != null;\n}\n","/**\n * TopLoader — Built-in progress bar for client navigations.\n *\n * Shows an animated progress bar at the top of the viewport while an RSC\n * navigation is in flight. Injected automatically by the framework into\n * NavigationRoot — users never render this component directly.\n *\n * Configuration is via timber.config.ts `topLoader` key. Enabled by default.\n * Users who want a fully custom progress indicator disable the built-in one\n * (`topLoader: { enabled: false }`) and use `usePendingNavigation()` directly.\n *\n * Animation approach: pure CSS @keyframes. The bar crawls from 0% to ~90%\n * width over ~30s using ease-out timing. When navigation completes, the bar\n * snaps to 100% and fades out over 200ms. No JS animation loops (RAF, setInterval).\n *\n * Phase transitions are derived synchronously during render (React's\n * getDerivedStateFromProps pattern) — no useEffect needed for state tracking.\n * The finishing → hidden cleanup uses onTransitionEnd from the CSS transition.\n *\n * When delay > 0, CSS animation-delay + a visibility keyframe ensure the bar\n * stays invisible during the delay period. If navigation finishes before the\n * delay, the bar was never visible so the finish transition is also invisible.\n *\n * See design/19-client-navigation.md §\"usePendingNavigation()\"\n * See LOCAL-336 for design decisions.\n */\n\n'use client';\n\nimport { useState, createElement } from 'react';\nimport { usePendingNavigationUrl, hasNativeNavigationTransition } from './navigation-context.js';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface TopLoaderConfig {\n /** Whether the top-loader is enabled. Default: true. */\n enabled?: boolean;\n /** Bar color. Default: '#2299DD'. */\n color?: string;\n /** Bar height in pixels. Default: 3. */\n height?: number;\n /** Show subtle glow/shadow effect. Default: false. */\n shadow?: boolean;\n /** Delay in ms before showing the bar. Default: 0. */\n delay?: number;\n /** CSS z-index. Default: 1600. */\n zIndex?: number;\n}\n\n// ─── Defaults ────────────────────────────────────────────────────\n\nconst DEFAULT_COLOR = '#2299DD';\nconst DEFAULT_HEIGHT = 3;\nconst DEFAULT_SHADOW = false;\nconst DEFAULT_DELAY = 0;\nconst DEFAULT_Z_INDEX = 1600;\n\n// ─── Keyframes ───────────────────────────────────────────────────\n\n// Unique keyframes name to avoid collisions with user styles.\nconst CRAWL_KEYFRAMES = '__timber_top_loader_crawl';\nconst APPEAR_KEYFRAMES = '__timber_top_loader_appear';\nconst FINISH_KEYFRAMES = '__timber_top_loader_finish';\n\n// Track whether the @keyframes rules have been injected into the document.\nlet keyframesInjected = false;\n\n/**\n * Inject the @keyframes rules into the document head once.\n * Called during render (idempotent). Uses a <style> tag so the\n * animations are available for inline-styled elements.\n */\nfunction ensureKeyframes(): void {\n if (keyframesInjected) return;\n if (typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.textContent = `\n@keyframes ${CRAWL_KEYFRAMES} {\n 0% { width: 0%; }\n 100% { width: 90%; }\n}\n@keyframes ${APPEAR_KEYFRAMES} {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n@keyframes ${FINISH_KEYFRAMES} {\n 0% { width: 90%; opacity: 1; }\n 50% { width: 100%; opacity: 1; }\n 100% { width: 100%; opacity: 0; }\n}\n`;\n document.head.appendChild(style);\n keyframesInjected = true;\n}\n\n// ─── Component ───────────────────────────────────────────────────\n\n/**\n * Internal top-loader component. Injected by NavigationRoot.\n *\n * Reads pending navigation state from PendingNavigationContext.\n * Phase transitions are derived synchronously during render:\n *\n * hidden → crawling: when isPending becomes true\n * crawling → finishing: when isPending becomes false\n * finishing → hidden: when CSS transition ends (onTransitionEnd)\n * finishing → crawling: when isPending becomes true again\n *\n * No useEffect — all state changes are either derived during render\n * (getDerivedStateFromProps pattern) or triggered by DOM events.\n */\nexport function TopLoader({ config }: { config?: TopLoaderConfig }): React.ReactElement | null {\n const pendingUrl = usePendingNavigationUrl();\n // Navigation is pending when either:\n // 1. Our React-based pending URL is set (standard path), OR\n // 2. The Navigation API has an active transition (external navigations\n // intercepted by the navigate event that haven't completed yet).\n // In practice these are almost always in sync — the Navigation API\n // transition is active while our pendingUrl is set. This check ensures\n // the top-loader also shows for external navigations caught by the\n // Navigation API before our React state updates.\n const isPending = pendingUrl !== null || hasNativeNavigationTransition();\n\n const color = config?.color ?? DEFAULT_COLOR;\n const height = config?.height ?? DEFAULT_HEIGHT;\n const shadow = config?.shadow ?? DEFAULT_SHADOW;\n const delay = config?.delay ?? DEFAULT_DELAY;\n const zIndex = config?.zIndex ?? DEFAULT_Z_INDEX;\n\n const [phase, setPhase] = useState<'hidden' | 'crawling' | 'finishing'>('hidden');\n\n // ─── Synchronous phase derivation (getDerivedStateFromProps) ──\n // React allows setState during render if the value changes — it\n // immediately re-renders with the updated state before committing.\n\n if (isPending && (phase === 'hidden' || phase === 'finishing')) {\n setPhase('crawling');\n }\n if (!isPending && phase === 'crawling') {\n setPhase('finishing');\n }\n\n // Inject keyframes on first visible render (idempotent)\n if (phase !== 'hidden') {\n ensureKeyframes();\n }\n\n if (phase === 'hidden') return null;\n\n // ─── Styles ──────────────────────────────────────────────────\n\n const containerStyle: React.CSSProperties = {\n position: 'fixed',\n top: 0,\n left: 0,\n width: '100%',\n height: `${height}px`,\n zIndex,\n pointerEvents: 'none',\n };\n\n const barStyle: React.CSSProperties = {\n height: '100%',\n backgroundColor: color,\n ...(phase === 'crawling'\n ? {\n // Crawl from 0% to 90% over 30s. When delay > 0, both the crawl\n // and a visibility animation are delayed — the bar stays at width 0%\n // and opacity 0 during the delay, then appears and starts crawling.\n // With delay 0, the appear animation is instant (0s duration, no delay).\n animation: [\n `${CRAWL_KEYFRAMES} 30s ease-out ${delay}ms forwards`,\n `${APPEAR_KEYFRAMES} 0s ${delay}ms both`,\n ].join(', '),\n }\n : {\n // Finishing: fill to 100% then fade out via a keyframe animation.\n // We use a keyframe instead of a CSS transition because the\n // animation-to-transition handoff is unreliable — the browser\n // may not capture the animated width as the transition's \"from\"\n // value when both the animation removal and transition are\n // applied in the same render frame.\n animation: `${FINISH_KEYFRAMES} 400ms ease forwards`,\n }),\n ...(shadow\n ? {\n boxShadow: `0 0 10px ${color}, 0 0 5px ${color}`,\n }\n : {}),\n };\n\n // Clean up the finishing phase when the finish animation completes.\n const handleAnimationEnd =\n phase === 'finishing'\n ? (e: React.AnimationEvent) => {\n if (e.animationName === FINISH_KEYFRAMES) {\n setPhase('hidden');\n }\n }\n : undefined;\n\n return createElement(\n 'div',\n {\n 'style': containerStyle,\n 'aria-hidden': 'true',\n 'data-timber-top-loader': '',\n },\n createElement('div', { style: barStyle, onAnimationEnd: handleAnimationEnd })\n );\n}\n","/**\n * NavigationRoot — Wrapper component for transition-based rendering.\n *\n * Solves the \"new boundary has no old content\" problem for client-side\n * navigation. When React renders a completely new Suspense boundary via\n * root.render(), it shows the fallback immediately — root.render() is\n * always an urgent update regardless of startTransition.\n *\n * NavigationRoot holds the current element in React state. Navigation\n * updates call startTransition(() => setState(newElement)), which IS\n * a transition update. React keeps the old committed tree visible while\n * any new Suspense boundaries in the transition resolve.\n *\n * Also manages `pendingUrl` as React state with an urgent/transition split:\n * - Navigation START: `setPendingUrl(url)` is an urgent update — React\n * commits it before the next paint, showing the spinner immediately.\n * - Navigation END: `setPendingUrl(null)` is inside `startTransition`\n * alongside `setElement(newTree)` — both commit atomically, so the\n * spinner disappears in the same frame as the new content appears.\n *\n * Hard navigation guard: When a hard navigation is triggered (500 error,\n * version skew), the component throws an unresolved thenable AFTER all\n * hooks to suspend forever — preventing React from rendering children\n * during page teardown. The throw must come after hooks to satisfy\n * React's rules (same hook count every render) while still preventing\n * child renders that could hit hook count mismatches in components\n * whose positions shift during teardown. This pattern is borrowed from\n * Next.js (app-router.tsx pushRef.mpaNavigation — also after hooks).\n *\n * See design/05-streaming.md §\"deferSuspenseFor\"\n * See design/19-client-navigation.md §\"NavigationContext\"\n */\n\nimport { createElement, Fragment, startTransition, useState, type ReactNode } from 'react';\nimport { getCurrentNavId, resetLinkPending } from './link-pending-store.js';\nimport { PendingNavigationProvider } from './navigation-context.js';\nimport { TopLoader, type TopLoaderConfig } from './top-loader.js';\n\n// ─── Navigation Transition Counter ──────────────────────────────\n// Monotonically increasing counter that increments each time\n// navigateTransition() is called. Used to detect stale transitions:\n// if a newer transition started while the current one's perform()\n// was in flight, the current transition is stale and should reject.\n//\n// Separate from the link-pending navId (which only increments on\n// link clicks). This counter covers all navigation types: link clicks,\n// programmatic navigate(), refresh(), and handlePopState().\n//\n// Uses globalThis for singleton guarantee across chunks — same pattern\n// as NavigationContext and the link pending store.\n\nconst NAV_TRANSITION_KEY = Symbol.for('__timber_nav_transition_counter');\n\nfunction getTransitionCounter(): { id: number } {\n const g = globalThis as Record<symbol, unknown>;\n if (!g[NAV_TRANSITION_KEY]) {\n g[NAV_TRANSITION_KEY] = { id: 0 };\n }\n return g[NAV_TRANSITION_KEY] as { id: number };\n}\n\n// ─── Hard Navigation Guard ──────────────────────────────────────\n\n/**\n * Module-level flag indicating a hard (MPA) navigation is in progress.\n *\n * When true:\n * - NavigationRoot throws an unresolved thenable to suspend forever,\n * preventing React from rendering children during page teardown\n * (avoids \"Rendered more hooks\" crashes).\n * - The Navigation API handler skips interception, letting the browser\n * perform a full page load (prevents infinite loops where\n * window.location.href → navigate event → router.navigate → 500 →\n * window.location.href → ...).\n *\n * Uses globalThis for singleton guarantee across chunks (same pattern\n * as NavigationContext). See design/19-client-navigation.md §\"Singleton\n * Guarantee via globalThis\".\n */\nconst HARD_NAV_KEY = Symbol.for('__timber_hard_navigating');\n\nfunction getHardNavStore(): { value: boolean } {\n const g = globalThis as Record<symbol, unknown>;\n if (!g[HARD_NAV_KEY]) {\n g[HARD_NAV_KEY] = { value: false };\n }\n return g[HARD_NAV_KEY] as { value: boolean };\n}\n\n/**\n * Set the hard-navigating flag. Call this BEFORE setting\n * window.location.href or window.location.reload() to prevent:\n * 1. React from rendering children during page teardown\n * 2. Navigation API from intercepting the hard navigation\n */\nexport function setHardNavigating(value: boolean): void {\n getHardNavStore().value = value;\n}\n\n/**\n * Check if a hard navigation is in progress.\n * Used by NavigationRoot (throw unresolvedThenable) and by the\n * Navigation API handler (skip interception).\n */\nexport function isHardNavigating(): boolean {\n return getHardNavStore().value;\n}\n\n/**\n * A thenable that never resolves. When thrown during React render,\n * it causes the component to suspend forever — React keeps the\n * old committed tree visible and never attempts to render children.\n *\n * This is the same pattern Next.js uses in app-router.tsx for MPA\n * navigations (pushRef.mpaNavigation → throw unresolvedThenable).\n */\n// for React's Suspense mechanism. Same pattern as Next.js's unresolvedThenable.\n// eslint-disable-next-line unicorn/no-thenable -- Intentionally a never-resolving thenable\nconst unresolvedThenable = { then() {} } as PromiseLike<never>;\n\n// ─── Module-level functions ──────────────────────────────────────\n\n/**\n * Module-level reference to the state setter wrapped in startTransition.\n * Used for non-navigation renders (applyRevalidation, popstate replay).\n */\nlet _transitionRender: ((element: ReactNode) => void) | null = null;\n\n/**\n * Module-level reference to the navigation transition function.\n * Wraps a full navigation (fetch + render) in a single startTransition\n * with the pending URL.\n */\nlet _navigateTransition:\n | ((pendingUrl: string, perform: () => Promise<ReactNode>) => Promise<void>)\n | null = null;\n\n// ─── Component ───────────────────────────────────────────────────\n\n/**\n * Root wrapper component that enables transition-based rendering.\n *\n * Renders PendingNavigationProvider around children for the pending URL\n * context. The DOM tree matches the server-rendered HTML during hydration\n * (the provider renders no extra DOM elements).\n *\n * Usage in browser-entry.ts:\n * const rootEl = createElement(NavigationRoot, { initial: wrapped });\n * reactRoot = hydrateRoot(document, rootEl);\n *\n * Subsequent navigations:\n * navigateTransition(url, async () => { fetch; return wrappedElement; });\n *\n * Non-navigation renders:\n * transitionRender(newWrappedElement);\n */\nexport function NavigationRoot({\n initial,\n topLoaderConfig,\n}: {\n initial: ReactNode;\n topLoaderConfig?: TopLoaderConfig;\n}): ReactNode {\n const [element, setElement] = useState<ReactNode>(initial);\n const [pendingUrl, setPendingUrl] = useState<string | null>(null);\n\n // NOTE: We use standalone `startTransition` (imported from 'react'),\n // NOT `useTransition`. The `useTransition` hook's `startTransition`\n // is tied to a single fiber and tracks one async callback at a time.\n // When two navigations overlap (click slow-page, then click dashboard),\n // calling useTransition's startTransition twice with concurrent async\n // callbacks corrupts React's internal hook tracking — causing\n // \"Rendered more hooks than during the previous render.\"\n //\n // Standalone `startTransition` creates independent transition lanes\n // for each call, so concurrent navigations don't interfere. We don't\n // need useTransition's `isPending` — we track pending state via our\n // own `pendingUrl` useState.\n //\n // This matches the Next.js pattern (TIM-625): \"No useTransition in\n // the router at all — only standalone startTransition.\"\n\n // Non-navigation render (revalidation, popstate cached replay).\n _transitionRender = (newElement: ReactNode) => {\n startTransition(() => {\n setElement(newElement);\n });\n };\n\n // Full navigation transition.\n // setPendingUrl(url) is an URGENT update — React commits it before the next\n // paint, so the pending spinner appears immediately when navigation starts.\n // Inside startTransition: the async fetch + setElement + setPendingUrl(null)\n // are deferred. When the transition commits, the new tree and pendingUrl=null\n // both apply in the same React commit — making the pending→active transition\n // atomic (no frame where pending is false but the old tree is still visible).\n _navigateTransition = (url: string, perform: () => Promise<ReactNode>) => {\n // Urgent: show pending state immediately (for TopLoader / usePendingNavigation)\n setPendingUrl(url);\n\n // Increment the transition counter SYNCHRONOUSLY (before startTransition\n // schedules the async work). Each call gets a unique transId; the counter\n // is the same globalThis singleton, so a newer call always has a higher id.\n const counter = getTransitionCounter();\n const transId = ++counter.id;\n\n return new Promise<void>((resolve, reject) => {\n startTransition(async () => {\n // Capture the link-level nav ID for resetLinkPending (which has its\n // own guard). The transition counter (transId) is the primary stale\n // detection — it covers all navigation types (link clicks, programmatic\n // navigate, refresh, handlePopState), not just link-initiated ones.\n const linkNavId = getCurrentNavId();\n try {\n const newElement = await perform();\n // Only commit state if this is still the active navigation.\n // A superseded transition's updates must be dropped entirely.\n if (counter.id === transId) {\n setElement(newElement);\n setPendingUrl(null);\n resetLinkPending(linkNavId);\n resolve();\n } else {\n // Stale transition — a newer navigation has superseded this one.\n // Reject so the caller (navigate/refresh/handlePopState) doesn't\n // run post-transition side effects (applyHead, scroll, event\n // dispatch) with stale data. All callers catch AbortError.\n reject(new DOMException('Navigation superseded', 'AbortError'));\n }\n } catch (err) {\n // Only clear pending if this is still the active navigation.\n // Stale transitions must not touch state — doing so corrupts\n // React's transition tracking and causes hook count mismatches.\n if (counter.id === transId) {\n setPendingUrl(null);\n resetLinkPending(linkNavId);\n }\n reject(err);\n }\n });\n });\n };\n\n // ─── Hard navigation guard ─────────────────────────────────\n // When a hard navigation is in progress (500 error, version skew),\n // suspend forever to prevent React from rendering children during\n // page teardown. This avoids \"Rendered more hooks\" crashes in\n // CHILD components whose hook counts may shift during teardown.\n //\n // CRITICAL: This throw MUST come AFTER all hooks (the two\n // useState calls above). React requires the same hooks to run on\n // every render. If we threw before hooks, React would see 0 hooks\n // on the re-render vs 2 hooks on the initial render — triggering\n // the exact \"Rendered more hooks\" error we're trying to prevent.\n //\n // By placing it after hooks but before the return, all hooks\n // satisfy React's rules, but the thrown thenable prevents any\n // children from rendering. Same pattern as Next.js app-router.tsx\n // (pushRef.mpaNavigation — also placed after all hooks).\n if (isHardNavigating()) {\n throw unresolvedThenable;\n }\n\n // Inject TopLoader alongside the element tree inside PendingNavigationProvider.\n // The TopLoader reads pendingUrl from context to show/hide the progress bar.\n // It is rendered only when not explicitly disabled via config.\n const showTopLoader = topLoaderConfig?.enabled !== false;\n const children = showTopLoader\n ? createElement(Fragment, null, createElement(TopLoader, { config: topLoaderConfig }), element)\n : element;\n return createElement(PendingNavigationProvider, { value: pendingUrl }, children);\n}\n\n// ─── Public API ──────────────────────────────────────────────────\n\n/**\n * Trigger a transition render for non-navigation updates.\n * React keeps the old committed tree visible while any new Suspense\n * boundaries in the update resolve.\n *\n * Used for: applyRevalidation, popstate replay with cached payload.\n */\nexport function transitionRender(element: ReactNode): void {\n if (_transitionRender) {\n _transitionRender(element);\n }\n}\n\n/**\n * Run a full navigation inside a React transition with optimistic pending URL.\n *\n * The `perform` callback runs inside `startTransition` — it should fetch the\n * RSC payload, update router state, and return the wrapped React element.\n * The pending URL shows immediately (urgent update) and reverts\n * to null when the transition commits (atomic with the new tree).\n *\n * Returns a Promise that resolves when the async work completes (note: the\n * React transition may not have committed yet, but all state updates are done).\n *\n * Used for: navigate(), refresh(), popstate with fetch.\n */\nexport function navigateTransition(\n pendingUrl: string,\n perform: () => Promise<ReactNode>\n): Promise<void> {\n if (_navigateTransition) {\n return _navigateTransition(pendingUrl, perform);\n }\n // Fallback: no NavigationRoot mounted (shouldn't happen in production)\n return perform().then(() => {});\n}\n\n/**\n * Check if the NavigationRoot is mounted and ready for renders.\n * Used by browser-entry.ts to guard against renders before hydration.\n */\nexport function isNavigationRootReady(): boolean {\n return _transitionRender !== null;\n}\n\n/**\n * Install one-shot deferred callbacks for the no-RSC bootstrap path (TIM-600).\n *\n * When there's no RSC payload, we can't create a React root immediately —\n * `createRoot(document).render(...)` would blank the SSR HTML. Instead,\n * this sets up `_transitionRender` and `_navigateTransition` so that the\n * first client navigation triggers root creation via `createAndMount`.\n *\n * After `createAndMount` runs, NavigationRoot renders and overwrites these\n * callbacks with its real `startTransition`-based implementations.\n */\nexport function installDeferredNavigation(createAndMount: (initial: ReactNode) => void): void {\n let mounted = false;\n const mountOnce = (element: ReactNode) => {\n if (mounted) return;\n mounted = true;\n createAndMount(element);\n };\n _transitionRender = (element: ReactNode) => {\n mountOnce(element);\n };\n _navigateTransition = async (_pendingUrl: string, perform: () => Promise<ReactNode>) => {\n const element = await perform();\n mountOnce(element);\n };\n}\n","/**\n * useParams() — client-side hook for accessing route params.\n *\n * Returns the dynamic route parameters for the current URL.\n * When called with a route pattern argument, TypeScript narrows\n * the return type to the exact params shape for that route.\n *\n * Two layers of type narrowing work together:\n * 1. The generic overload here uses the Routes interface directly —\n * `useParams<R>()` returns `Routes[R]['segmentParams']`.\n * 2. Build-time codegen generates per-route string-literal overloads\n * in the .d.ts file for IDE autocomplete (see routing/codegen.ts).\n *\n * When the Routes interface is empty (no codegen yet), the generic\n * overload has `keyof Routes = never`, so only the fallback matches.\n *\n * During SSR, params are read from the ALS-backed SSR data context\n * (populated by ssr-entry.ts) to ensure correct per-request isolation\n * across concurrent requests with streaming Suspense.\n *\n * Reactivity: On the client, useParams() reads from NavigationContext\n * which is updated atomically with the RSC tree render. This replaces\n * the previous useSyncExternalStore approach that suffered from a\n * timing gap between tree render and store notification — causing\n * preserved layout components to briefly show stale active state.\n *\n * All mutable state is delegated to client/state.ts for singleton guarantees.\n * See design/18-build-system.md §\"Singleton State Registry\"\n *\n * Design doc: design/09-typescript.md §\"Typed Routes\"\n */\n\nimport type { Routes } from '../index.js';\nimport { getSsrData } from './ssr-data.js';\nimport { currentParams, _setCurrentParams, paramsListeners } from './state.js';\nimport { useNavigationContext } from './navigation-context.js';\n\n// ---------------------------------------------------------------------------\n// Module-level subscribe/notify pattern — kept for backward compat and tests\n// ---------------------------------------------------------------------------\n\n/**\n * Subscribe to params changes.\n * Retained for backward compatibility with tests that verify the\n * subscribe/notify contract. On the client, useParams() reads from\n * NavigationContext instead.\n */\nexport function subscribe(callback: () => void): () => void {\n paramsListeners.add(callback);\n return () => paramsListeners.delete(callback);\n}\n\n/**\n * Get the current params snapshot (module-level fallback).\n * Used by tests and by the hook when called outside a React component.\n */\nexport function getSnapshot(): Record<string, string | string[]> {\n return currentParams;\n}\n\n// ---------------------------------------------------------------------------\n// Framework API — called by the segment router on each navigation\n// ---------------------------------------------------------------------------\n\n/**\n * Set the current route params in the module-level store.\n *\n * Called by the router on each navigation. This updates the fallback\n * snapshot used by tests and by the hook when called outside a React\n * component (no NavigationContext available).\n *\n * On the client, the primary reactivity path is NavigationContext —\n * the router calls setNavigationState() then renderRoot() which wraps\n * the element in NavigationProvider. setCurrentParams is still called\n * for the module-level fallback.\n *\n * During SSR, params are also available via getSsrData().params\n * (ALS-backed).\n */\nexport function setCurrentParams(params: Record<string, string | string[]>): void {\n _setCurrentParams(params);\n}\n\n/**\n * Notify all legacy subscribers that params have changed.\n *\n * Retained for backward compatibility with tests. On the client,\n * the NavigationContext + renderRoot pattern replaces this — params\n * update atomically with the tree render, so explicit notification\n * is no longer needed.\n */\nexport function notifyParamsListeners(): void {\n for (const listener of paramsListeners) {\n listener();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public hook\n// ---------------------------------------------------------------------------\n\n/**\n * Read the current route's dynamic params.\n *\n * The optional `_route` argument exists only for TypeScript narrowing —\n * it does not affect the runtime return value.\n *\n * On the client, reads from NavigationContext (provided by\n * NavigationProvider in renderRoot). This ensures params update\n * atomically with the RSC tree — no timing gap.\n *\n * During SSR, reads from the ALS-backed SSR data context to ensure\n * per-request isolation across concurrent requests with streaming Suspense.\n *\n * When called outside a React component (e.g., in test assertions),\n * falls back to the module-level snapshot.\n *\n * @overload Typed — when a known route path is passed, returns the\n * exact params shape from the generated Routes interface.\n * @overload Fallback — returns the generic params record.\n */\nexport function useSegmentParams<R extends keyof Routes>(\n route: R\n): Routes[R] extends { segmentParams: infer P } ? P : Record<string, string | string[]>;\nexport function useSegmentParams(route?: string): Record<string, string | string[]>;\nexport function useSegmentParams(_route?: string): Record<string, string | string[]> {\n // Try reading from NavigationContext (client-side, inside React tree).\n // During SSR, no NavigationProvider is mounted, so this returns null.\n // When called outside a React component, useContext throws — caught below.\n try {\n const navContext = useNavigationContext();\n if (navContext !== null) {\n return navContext.params;\n }\n } catch {\n // No React dispatcher available (called outside a component).\n // Fall through to module-level snapshot below.\n }\n\n // SSR path: read from ALS-backed SSR data context.\n // Falls back to module-level currentParams for tests.\n return getSsrData()?.params ?? currentParams;\n}\n"],"mappings":";;;;;;AAcA,SAAgB,gBAAgB,QAA8B;AAC5D,kBAAiB,OAAO;;;;;;AAO1B,SAAgB,YAA4B;AAC1C,KAAI,CAAC,aACH,OAAM,IAAI,MAAM,8EAA8E;AAEhG,QAAO;;;;;;;AAQT,SAAgB,kBAAyC;AACvD,QAAO;;;;ACIT,IAAM,mBAAmB,OAAO,IAAI,wBAAwB;;AAG5D,IAAa,sBAAkC,EAAE,SAAS,MAAM;;AAGhE,IAAa,mBAA+B,EAAE,SAAS,OAAO;AAS9D,SAAS,WAA6B;CACpC,MAAM,IAAI;AACV,KAAI,CAAC,EAAE,kBACL,GAAE,oBAAoB;EAAE,SAAS;EAAM,OAAO;EAAG;AAEnD,QAAO,EAAE;;;;;;;;;;;;;AAgBX,SAAgB,4BAA4B,MAAwC;CAClF,MAAM,QAAQ,UAAU;CACxB,MAAM,OAAO,MAAM;AAGnB,KAAI,QAAQ,SAAS,KACnB,MAAK,cAAc,iBAAiB;AAGtC,OAAM,UAAU;AAChB,OAAM;;;;;;;AA6CR,SAAgB,gCAAgC,MAAiC;CAC/E,MAAM,QAAQ,UAAU;AACxB,KAAI,MAAM,YAAY,KACpB,OAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzDpB,IAAM,cAAc,OAAO,IAAI,mBAAmB;AAClD,IAAM,kBAAkB,OAAO,IAAI,2BAA2B;AAE9D,SAAS,qBAAwE;CAC/E,MAAM,WAAY,WAAuC;AAGzD,KAAI,aAAa,KAAA,EAAW,QAAO;AAEnC,KAAI,OAAO,MAAM,kBAAkB,YAAY;EAC7C,MAAM,MAAM,MAAM,cAAsC,KAAK;AAC5D,aAAuC,eAAe;AACvD,SAAO;;;;;;;;AAUX,SAAgB,uBAA+C;CAC7D,MAAM,MAAM,oBAAoB;AAChC,KAAI,CAAC,IAAK,QAAO;AAEjB,KAAI,OAAO,MAAM,eAAe,WAAY,QAAO;AACnD,QAAO,MAAM,WAAW,IAAI;;;;;;;;AAkB9B,SAAgB,mBAAmB,EACjC,OACA,YAC8C;CAC9C,MAAM,MAAM,oBAAoB;AAChC,KAAI,CAAC,IAEH,QAAO;AAET,QAAO,cAAc,IAAI,UAAU,EAAE,OAAO,EAAE,SAAS;;;;;;;;;;;;;;;AAoBzD,IAAM,gBAAgB,OAAO,IAAI,qBAAqB;AAEtD,SAAS,oBAAkD;CACzD,MAAM,IAAI;AACV,KAAI,CAAC,EAAE,eACL,GAAE,iBAAiB,EAAE,SAAS;EAAE,QAAQ,EAAE;EAAE,UAAU;EAAK,EAAE;AAE/D,QAAO,EAAE;;AAGX,SAAgB,mBAAmB,OAA8B;AAC/D,oBAAmB,CAAC,UAAU;;AAGhC,SAAgB,qBAAsC;AACpD,QAAO,mBAAmB,CAAC;;;;;;;;;;;;AAkB7B,SAAS,4BAAsE;CAC7E,MAAM,WAAY,WAAuC;AAGzD,KAAI,aAAa,KAAA,EAAW,QAAO;AACnC,KAAI,OAAO,MAAM,kBAAkB,YAAY;EAC7C,MAAM,MAAM,MAAM,cAA6B,KAAK;AACnD,aAAuC,mBAAmB;AAC3D,SAAO;;;;;;;AASX,SAAgB,0BAAyC;CACvD,MAAM,MAAM,2BAA2B;AACvC,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI,OAAO,MAAM,eAAe,WAAY,QAAO;AACnD,QAAO,MAAM,WAAW,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AE7H9B,IAAM,eAAe,OAAO,IAAI,2BAA2B;AAE3D,SAAS,kBAAsC;CAC7C,MAAM,IAAI;AACV,KAAI,CAAC,EAAE,cACL,GAAE,gBAAgB,EAAE,OAAO,OAAO;AAEpC,QAAO,EAAE;;;;;;;;AASX,SAAgB,kBAAkB,OAAsB;AACtD,kBAAiB,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;ACjB5B,SAAgB,iBAAiB,QAAiD;AAChF,mBAAkB,OAAO;;AA6C3B,SAAgB,iBAAiB,QAAoD;AAInF,KAAI;EACF,MAAM,aAAa,sBAAsB;AACzC,MAAI,eAAe,KACjB,QAAO,WAAW;SAEd;AAOR,QAAO,YAAY,EAAE,UAAU"}
@@ -12,7 +12,7 @@ function registerSearchParams(route, definition) {
12
12
  * Look up a route's search params definition.
13
13
  * Returns undefined if the route hasn't been loaded yet.
14
14
  */
15
- function getSearchParams(route) {
15
+ function getSearchParamsDefinition(route) {
16
16
  return registry.get(route);
17
17
  }
18
18
  //#endregion
@@ -69,7 +69,7 @@ function useQueryStates$1(codecsOrRoute, _options, urlKeys) {
69
69
  let codecs;
70
70
  let resolvedUrlKeys = urlKeys;
71
71
  if (typeof codecsOrRoute === "string") {
72
- const definition = getSearchParams(codecsOrRoute);
72
+ const definition = getSearchParamsDefinition(codecsOrRoute);
73
73
  if (!definition) throw new Error(`useQueryStates('${codecsOrRoute}'): no search params registered for this route. Either the route has no search-params.ts file, or it hasn't been loaded yet. For cross-route usage, import the definition explicitly.`);
74
74
  codecs = definition.codecs;
75
75
  resolvedUrlKeys = definition.urlKeys;
@@ -104,6 +104,6 @@ function bindUseQueryStates(definition) {
104
104
  };
105
105
  }
106
106
  //#endregion
107
- export { registerSearchParams as i, useQueryStates$1 as n, getSearchParams as r, bindUseQueryStates as t };
107
+ export { registerSearchParams as i, useQueryStates$1 as n, getSearchParamsDefinition as r, bindUseQueryStates as t };
108
108
 
109
- //# sourceMappingURL=use-query-states-DAhgj8Gx.js.map
109
+ //# sourceMappingURL=use-query-states-Lo_s_pw2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-query-states-Lo_s_pw2.js","names":[],"sources":["../../src/search-params/registry.ts","../../src/client/use-query-states.ts"],"sourcesContent":["/**\n * Runtime registry for route-scoped search params definitions.\n *\n * When a route's modules load, the framework registers its search-params\n * definition here. useQueryStates('/route') resolves codecs from this map.\n *\n * Design doc: design/23-search-params.md §\"Runtime: Registration at Route Load\"\n */\n\nimport type { SearchParamsDefinition } from './define.js';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst registry = new Map<string, SearchParamsDefinition<any>>();\n\n/**\n * Register a route's search params definition.\n * Called by the generated route manifest loader when a route's modules load.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerSearchParams(route: string, definition: SearchParamsDefinition<any>): void {\n registry.set(route, definition);\n}\n\n/**\n * Look up a route's search params definition.\n * Returns undefined if the route hasn't been loaded yet.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getSearchParamsDefinition(route: string): SearchParamsDefinition<any> | undefined {\n return registry.get(route);\n}\n","/**\n * useQueryStates — client-side hook for URL-synced search params.\n *\n * Delegates to nuqs for URL synchronization, batching, React 19 transitions,\n * and throttled URL writes. Bridges timber's SearchParamCodec protocol to\n * nuqs-compatible parsers.\n *\n * Design doc: design/23-search-params.md §\"Codec Bridge\"\n */\n\n'use client';\n\nimport { useQueryStates as nuqsUseQueryStates } from 'nuqs';\nimport type { SingleParser } from 'nuqs';\nimport type {\n SearchParamCodec,\n SearchParamsDefinition,\n SetParams,\n QueryStatesOptions,\n} from '../search-params/define.js';\nimport { getSearchParamsDefinition } from '../search-params/registry.js';\n\n// ─── Codec Bridge ─────────────────────────────────────────────────\n\n/**\n * Bridge a timber SearchParamCodec to a nuqs-compatible SingleParser.\n *\n * nuqs parsers: { parse(string) → T|null, serialize?(T) → string, eq?, defaultValue? }\n * timber codecs: { parse(string|string[]|undefined) → T, serialize(T) → string|null }\n */\nfunction bridgeCodec<T>(codec: SearchParamCodec<T>): SingleParser<T> & { defaultValue: T } {\n return {\n parse: (v: string) => codec.parse(v),\n serialize: (v: T) => codec.serialize(v) ?? '',\n defaultValue: codec.parse(undefined) as T,\n eq: (a: T, b: T) => codec.serialize(a) === codec.serialize(b),\n };\n}\n\n/**\n * Bridge an entire codec map to nuqs-compatible parsers.\n */\nfunction bridgeCodecs<T extends Record<string, unknown>>(codecs: {\n [K in keyof T]: SearchParamCodec<T[K]>;\n}) {\n const result: Record<string, SingleParser<unknown> & { defaultValue: unknown }> = {};\n for (const key of Object.keys(codecs)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result[key] = bridgeCodec(codecs[key as keyof T]) as any;\n }\n return result as { [K in keyof T]: SingleParser<T[K]> & { defaultValue: T[K] } };\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\n/**\n * Read and write typed search params from/to the URL.\n *\n * Delegates to nuqs internally. The timber nuqs adapter (auto-injected in\n * browser-entry.ts) handles RSC navigation on non-shallow updates.\n *\n * Usage:\n * ```ts\n * // Via a SearchParamsDefinition\n * const [params, setParams] = definition.useQueryStates()\n *\n * // Standalone with inline codecs\n * const [params, setParams] = useQueryStates({\n * page: fromSchema(z.coerce.number().int().min(1).default(1)),\n * })\n * ```\n */\nexport function useQueryStates<T extends Record<string, unknown>>(\n codecsOrRoute: { [K in keyof T]: SearchParamCodec<T[K]> } | string,\n _options?: QueryStatesOptions,\n urlKeys?: Readonly<Record<string, string>>\n): [T, SetParams<T>] {\n // Route-string overload: resolve codecs from the registry\n let codecs: { [K in keyof T]: SearchParamCodec<T[K]> };\n let resolvedUrlKeys = urlKeys;\n if (typeof codecsOrRoute === 'string') {\n const definition = getSearchParamsDefinition(codecsOrRoute);\n if (!definition) {\n throw new Error(\n `useQueryStates('${codecsOrRoute}'): no search params registered for this route. ` +\n `Either the route has no search-params.ts file, or it hasn't been loaded yet. ` +\n `For cross-route usage, import the definition explicitly.`\n );\n }\n codecs = definition.codecs as { [K in keyof T]: SearchParamCodec<T[K]> };\n resolvedUrlKeys = definition.urlKeys;\n } else {\n codecs = codecsOrRoute;\n }\n\n const bridged = bridgeCodecs(codecs);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nuqsOptions: any = {};\n if (resolvedUrlKeys && Object.keys(resolvedUrlKeys).length > 0) {\n nuqsOptions.urlKeys = resolvedUrlKeys;\n }\n\n let values: Record<string, unknown>;\n let setValues: Function;\n try {\n [values, setValues] = nuqsUseQueryStates(bridged, nuqsOptions);\n } catch (err) {\n if (\n err instanceof Error &&\n /Invalid hook call|cannot be called|Cannot read properties of null/i.test(err.message)\n ) {\n throw new Error(\n 'useQueryStates is a client component hook and cannot be called outside a React component. ' +\n 'Use definition.parse(searchParams) in server components instead.'\n );\n }\n throw err;\n }\n\n // Wrap the nuqs setter to match timber's SetParams<T> signature.\n // nuqs's setter accepts Partial<Nullable<Values>> | UpdaterFn | null.\n // timber's setter accepts Partial<T> with optional SetParamsOptions.\n const setParams: SetParams<T> = (partial, setOptions?) => {\n const nuqsSetOptions: Record<string, unknown> = {};\n if (setOptions?.shallow !== undefined) nuqsSetOptions.shallow = setOptions.shallow;\n if (setOptions?.scroll !== undefined) nuqsSetOptions.scroll = setOptions.scroll;\n if (setOptions?.history !== undefined) nuqsSetOptions.history = setOptions.history;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n void setValues(partial as any, nuqsSetOptions);\n };\n\n return [values as T, setParams];\n}\n\n// ─── Definition binding ───────────────────────────────────────────\n\n/**\n * Create a useQueryStates binding for a SearchParamsDefinition.\n * This is used internally by SearchParamsDefinition.useQueryStates().\n */\nexport function bindUseQueryStates<T extends Record<string, unknown>>(\n definition: SearchParamsDefinition<T>\n): (options?: QueryStatesOptions) => [T, SetParams<T>] {\n return (options?: QueryStatesOptions) => {\n return useQueryStates<T>(definition.codecs, options, definition.urlKeys);\n };\n}\n"],"mappings":";;AAYA,IAAM,2BAAW,IAAI,KAA0C;;;;;AAO/D,SAAgB,qBAAqB,OAAe,YAA+C;AACjG,UAAS,IAAI,OAAO,WAAW;;;;;;AAQjC,SAAgB,0BAA0B,OAAwD;AAChG,QAAO,SAAS,IAAI,MAAM;;;;;;;;;;;;;;;;;;;ACC5B,SAAS,YAAe,OAAmE;AACzF,QAAO;EACL,QAAQ,MAAc,MAAM,MAAM,EAAE;EACpC,YAAY,MAAS,MAAM,UAAU,EAAE,IAAI;EAC3C,cAAc,MAAM,MAAM,KAAA,EAAU;EACpC,KAAK,GAAM,MAAS,MAAM,UAAU,EAAE,KAAK,MAAM,UAAU,EAAE;EAC9D;;;;;AAMH,SAAS,aAAgD,QAEtD;CACD,MAAM,SAA4E,EAAE;AACpF,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CAEnC,QAAO,OAAO,YAAY,OAAO,KAAgB;AAEnD,QAAO;;;;;;;;;;;;;;;;;;;AAsBT,SAAgB,iBACd,eACA,UACA,SACmB;CAEnB,IAAI;CACJ,IAAI,kBAAkB;AACtB,KAAI,OAAO,kBAAkB,UAAU;EACrC,MAAM,aAAa,0BAA0B,cAAc;AAC3D,MAAI,CAAC,WACH,OAAM,IAAI,MACR,mBAAmB,cAAc,uLAGlC;AAEH,WAAS,WAAW;AACpB,oBAAkB,WAAW;OAE7B,UAAS;CAGX,MAAM,UAAU,aAAa,OAAO;CAGpC,MAAM,cAAmB,EAAE;AAC3B,KAAI,mBAAmB,OAAO,KAAK,gBAAgB,CAAC,SAAS,EAC3D,aAAY,UAAU;CAGxB,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,GAAC,QAAQ,aAAa,eAAmB,SAAS,YAAY;UACvD,KAAK;AACZ,MACE,eAAe,SACf,qEAAqE,KAAK,IAAI,QAAQ,CAEtF,OAAM,IAAI,MACR,6JAED;AAEH,QAAM;;CAMR,MAAM,aAA2B,SAAS,eAAgB;EACxD,MAAM,iBAA0C,EAAE;AAClD,MAAI,YAAY,YAAY,KAAA,EAAW,gBAAe,UAAU,WAAW;AAC3E,MAAI,YAAY,WAAW,KAAA,EAAW,gBAAe,SAAS,WAAW;AACzE,MAAI,YAAY,YAAY,KAAA,EAAW,gBAAe,UAAU,WAAW;AAEtE,YAAU,SAAgB,eAAe;;AAGhD,QAAO,CAAC,QAAa,UAAU;;;;;;AASjC,SAAgB,mBACd,YACqD;AACrD,SAAQ,YAAiC;AACvC,SAAO,iBAAkB,WAAW,QAAQ,SAAS,WAAW,QAAQ"}
@@ -60,4 +60,4 @@ function withUrlKey(codec, urlKey) {
60
60
  //#endregion
61
61
  export { withUrlKey as n, withDefault as t };
62
62
 
63
- //# sourceMappingURL=wrappers-LZbghvn0.js.map
63
+ //# sourceMappingURL=wrappers-_DTmImGt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrappers-LZbghvn0.js","names":[],"sources":["../../src/search-params/wrappers.ts"],"sourcesContent":["/**\n * Codec wrappers — withDefault and withUrlKey.\n *\n * These are timber-specific utilities that work with any SearchParamCodec.\n * For actual codecs (string, integer, boolean, etc.), use nuqs parsers\n * or Standard Schema objects (Zod, Valibot, ArkType) with auto-detection.\n *\n * Design doc: design/23-search-params.md\n */\n\nimport type { SearchParamCodec, SearchParamCodecWithUrlKey } from './define.js';\n\n// ---------------------------------------------------------------------------\n// withDefault\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a nullable codec with a default value. When the inner codec returns\n * null, the default is used instead. The output type becomes non-nullable.\n *\n * Works with any codec — nuqs parsers, custom codecs, fromSchema results.\n *\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * import { withDefault } from '@timber-js/app/search-params'\n *\n * const page = withDefault(parseAsInteger, 1)\n * // page.parse(undefined) → 1 (not null)\n * // page.parse('5') → 5\n * ```\n */\nexport function withDefault<T>(\n codec: SearchParamCodec<T | null>,\n defaultValue: T\n): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n const result = codec.parse(value);\n return result === null ? defaultValue : result;\n },\n serialize(value: T): string | null {\n return codec.serialize(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// withUrlKey\n// ---------------------------------------------------------------------------\n\n/**\n * Attach a URL key alias to a codec. The alias determines what query\n * parameter key is used in the URL, while the TypeScript property name\n * stays descriptive.\n *\n * Aliases travel with codecs through object spread composition — when\n * you spread a bundle containing aliased codecs into defineSearchParams,\n * the aliases come along automatically.\n *\n * ```ts\n * import { parseAsString } from 'nuqs'\n * import { withUrlKey } from '@timber-js/app/search-params'\n *\n * export const searchable = {\n * q: withUrlKey(parseAsString, 'search'),\n * // ?search=shoes → { q: 'shoes' }\n * }\n * ```\n *\n * Composes with withDefault:\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * withUrlKey(withDefault(parseAsInteger, 1), 'p')\n * ```\n */\nexport function withUrlKey<T>(\n codec: SearchParamCodec<T>,\n urlKey: string\n): SearchParamCodecWithUrlKey<T> {\n return {\n parse: codec.parse.bind(codec),\n serialize: codec.serialize.bind(codec),\n urlKey,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA+BA,SAAgB,YACd,OACA,cACqB;AACrB,QAAO;EACL,MAAM,OAAyC;GAC7C,MAAM,SAAS,MAAM,MAAM,MAAM;AACjC,UAAO,WAAW,OAAO,eAAe;;EAE1C,UAAU,OAAyB;AACjC,UAAO,MAAM,UAAU,MAAM;;EAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,SAAgB,WACd,OACA,QAC+B;AAC/B,QAAO;EACL,OAAO,MAAM,MAAM,KAAK,MAAM;EAC9B,WAAW,MAAM,UAAU,KAAK,MAAM;EACtC;EACD"}
1
+ {"version":3,"file":"wrappers-_DTmImGt.js","names":[],"sources":["../../src/search-params/wrappers.ts"],"sourcesContent":["/**\n * Codec wrappers — withDefault and withUrlKey.\n *\n * These are timber-specific utilities that work with any SearchParamCodec.\n * For actual codecs (string, integer, boolean, etc.), use nuqs parsers\n * or Standard Schema objects (Zod, Valibot, ArkType) with auto-detection.\n *\n * Design doc: design/23-search-params.md\n */\n\nimport type { SearchParamCodec, SearchParamCodecWithUrlKey } from './define.js';\n\n// ---------------------------------------------------------------------------\n// withDefault\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a nullable codec with a default value. When the inner codec returns\n * null, the default is used instead. The output type becomes non-nullable.\n *\n * Works with any codec — nuqs parsers, custom codecs, fromSchema results.\n *\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * import { withDefault } from '@timber-js/app/search-params'\n *\n * const page = withDefault(parseAsInteger, 1)\n * // page.parse(undefined) → 1 (not null)\n * // page.parse('5') → 5\n * ```\n */\nexport function withDefault<T>(\n codec: SearchParamCodec<T | null>,\n defaultValue: T\n): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n const result = codec.parse(value);\n return result === null ? defaultValue : result;\n },\n serialize(value: T): string | null {\n return codec.serialize(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// withUrlKey\n// ---------------------------------------------------------------------------\n\n/**\n * Attach a URL key alias to a codec. The alias determines what query\n * parameter key is used in the URL, while the TypeScript property name\n * stays descriptive.\n *\n * Aliases travel with codecs through object spread composition — when\n * you spread a bundle containing aliased codecs into defineSearchParams,\n * the aliases come along automatically.\n *\n * ```ts\n * import { parseAsString } from 'nuqs'\n * import { withUrlKey } from '@timber-js/app/search-params'\n *\n * export const searchable = {\n * q: withUrlKey(parseAsString, 'search'),\n * // ?search=shoes → { q: 'shoes' }\n * }\n * ```\n *\n * Composes with withDefault:\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * withUrlKey(withDefault(parseAsInteger, 1), 'p')\n * ```\n */\nexport function withUrlKey<T>(\n codec: SearchParamCodec<T>,\n urlKey: string\n): SearchParamCodecWithUrlKey<T> {\n return {\n parse: codec.parse.bind(codec),\n serialize: codec.serialize.bind(codec),\n urlKey,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA+BA,SAAgB,YACd,OACA,cACqB;AACrB,QAAO;EACL,MAAM,OAAyC;GAC7C,MAAM,SAAS,MAAM,MAAM,MAAM;AACjC,UAAO,WAAW,OAAO,eAAe;;EAE1C,UAAU,OAAyB;AACjC,UAAO,MAAM,UAAU,MAAM;;EAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,SAAgB,WACd,OACA,QAC+B;AAC/B,QAAO;EACL,OAAO,MAAM,MAAM,KAAK,MAAM;EAC9B,WAAW,MAAM,UAAU,KAAK,MAAM;EACtC;EACD"}
@@ -0,0 +1,64 @@
1
+ import type { CacheHandler } from '../cache/index';
2
+ /**
3
+ * Cloudflare Workers KV interface — the subset of KVNamespace we depend on.
4
+ * Users don't need to import this; the handler resolves the binding internally
5
+ * via `getCloudflareBindings()`.
6
+ */
7
+ export interface CloudflareKVNamespace {
8
+ get(key: string, options: {
9
+ type: 'json';
10
+ }): Promise<unknown>;
11
+ get(key: string, options?: {
12
+ type?: string;
13
+ }): Promise<string | null>;
14
+ put(key: string, value: string, options?: {
15
+ expirationTtl?: number;
16
+ }): Promise<void>;
17
+ delete(key: string): Promise<void>;
18
+ }
19
+ /**
20
+ * Cloudflare Workers KV–backed CacheHandler.
21
+ *
22
+ * Uses tag index keys for tag-based invalidation (KV has no secondary indexes).
23
+ * Stores entries with 2× TTL so stale entries survive for `staleWhileRevalidate`
24
+ * reads. The logical staleness is determined by the `expiresAt` timestamp inside
25
+ * the entry payload.
26
+ *
27
+ * **Consistency:** KV is eventually consistent (~60s propagation). TTL is the
28
+ * primary expiry mechanism. Tag invalidation is best-effort — do not rely on it
29
+ * for security-critical cache purging (use short TTLs instead).
30
+ *
31
+ * ```ts
32
+ * // timber.config.ts
33
+ * import { CloudflareKVCacheHandler } from '@timber-js/app/adapters/cloudflare/cache';
34
+ *
35
+ * export default {
36
+ * cacheHandler: new CloudflareKVCacheHandler({ bindingName: 'TIMBER_CACHE' }),
37
+ * };
38
+ * ```
39
+ */
40
+ export declare class CloudflareKVCacheHandler implements CacheHandler {
41
+ private bindingName;
42
+ private prefix;
43
+ constructor(options: {
44
+ bindingName: string;
45
+ prefix?: string;
46
+ });
47
+ /** Resolve the KV binding from the current request's Cloudflare env. */
48
+ private getKV;
49
+ private dataKey;
50
+ private tagKey;
51
+ get(key: string): Promise<{
52
+ value: unknown;
53
+ stale: boolean;
54
+ } | null>;
55
+ set(key: string, value: unknown, opts: {
56
+ ttl: number;
57
+ tags: string[];
58
+ }): Promise<void>;
59
+ invalidate(opts: {
60
+ key?: string;
61
+ tag?: string;
62
+ }): Promise<void>;
63
+ }
64
+ //# sourceMappingURL=cloudflare-kv-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-kv-cache.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare-kv-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAQD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAK7D,wEAAwE;IACxE,OAAO,CAAC,KAAK;IAYb,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,MAAM;IAIR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAUpE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BtF,UAAU,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAgCtE"}