@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c

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 (392) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +126 -16
  3. package/dist/bin/rango.js +319 -95
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +2724 -1053
  6. package/package.json +68 -14
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +64 -2
  9. package/skills/bundle-analysis/SKILL.md +159 -0
  10. package/skills/cache-guide/SKILL.md +224 -32
  11. package/skills/caching/SKILL.md +279 -17
  12. package/skills/composability/SKILL.md +27 -3
  13. package/skills/css/SKILL.md +76 -0
  14. package/skills/debug-manifest/SKILL.md +4 -2
  15. package/skills/document-cache/SKILL.md +78 -55
  16. package/skills/handler-use/SKILL.md +11 -9
  17. package/skills/hooks/SKILL.md +243 -29
  18. package/skills/host-router/SKILL.md +83 -23
  19. package/skills/i18n/SKILL.md +276 -0
  20. package/skills/intercept/SKILL.md +68 -19
  21. package/skills/layout/SKILL.md +13 -9
  22. package/skills/links/SKILL.md +190 -23
  23. package/skills/loader/SKILL.md +235 -9
  24. package/skills/middleware/SKILL.md +18 -10
  25. package/skills/migrate-nextjs/SKILL.md +43 -19
  26. package/skills/migrate-react-router/SKILL.md +8 -2
  27. package/skills/mime-routes/SKILL.md +28 -1
  28. package/skills/observability/SKILL.md +172 -0
  29. package/skills/parallel/SKILL.md +18 -7
  30. package/skills/prerender/SKILL.md +65 -60
  31. package/skills/rango/SKILL.md +251 -24
  32. package/skills/react-compiler/SKILL.md +168 -0
  33. package/skills/response-routes/SKILL.md +115 -48
  34. package/skills/route/SKILL.md +46 -5
  35. package/skills/router-setup/SKILL.md +30 -8
  36. package/skills/scripts/SKILL.md +179 -0
  37. package/skills/server-actions/SKILL.md +775 -0
  38. package/skills/tailwind/SKILL.md +27 -3
  39. package/skills/testing/SKILL.md +130 -0
  40. package/skills/testing/bindings.md +103 -0
  41. package/skills/testing/cache-prerender.md +127 -0
  42. package/skills/testing/client-components.md +124 -0
  43. package/skills/testing/e2e-parity.md +125 -0
  44. package/skills/testing/flight.md +91 -0
  45. package/skills/testing/handles.md +129 -0
  46. package/skills/testing/loader.md +128 -0
  47. package/skills/testing/middleware.md +99 -0
  48. package/skills/testing/render-handler.md +122 -0
  49. package/skills/testing/response-routes.md +95 -0
  50. package/skills/testing/reverse-and-types.md +84 -0
  51. package/skills/testing/server-actions.md +107 -0
  52. package/skills/testing/server-tree.md +128 -0
  53. package/skills/testing/setup.md +123 -0
  54. package/skills/typesafety/SKILL.md +322 -29
  55. package/skills/use-cache/SKILL.md +57 -14
  56. package/skills/view-transitions/SKILL.md +337 -0
  57. package/src/__augment-tests__/augment.ts +81 -0
  58. package/src/__augment-tests__/augmented.check.ts +116 -0
  59. package/src/__internal.ts +0 -65
  60. package/src/browser/action-coordinator.ts +53 -36
  61. package/src/browser/action-fence.ts +47 -0
  62. package/src/browser/app-shell.ts +39 -0
  63. package/src/browser/connection-warmup.ts +134 -0
  64. package/src/browser/cookie-name.ts +140 -0
  65. package/src/browser/event-controller.ts +192 -150
  66. package/src/browser/history-state.ts +21 -0
  67. package/src/browser/index.ts +3 -3
  68. package/src/browser/invalidate-client-cache.ts +52 -0
  69. package/src/browser/navigation-bridge.ts +94 -25
  70. package/src/browser/navigation-client.ts +121 -84
  71. package/src/browser/navigation-store-handle.ts +38 -0
  72. package/src/browser/navigation-store.ts +115 -67
  73. package/src/browser/navigation-transaction.ts +9 -59
  74. package/src/browser/network-error-handler.ts +34 -7
  75. package/src/browser/partial-update.ts +147 -128
  76. package/src/browser/prefetch/cache.ts +107 -56
  77. package/src/browser/prefetch/fetch.ts +204 -34
  78. package/src/browser/prefetch/queue.ts +6 -3
  79. package/src/browser/rango-state.ts +158 -76
  80. package/src/browser/react/Link.tsx +30 -7
  81. package/src/browser/react/NavigationProvider.tsx +283 -118
  82. package/src/browser/react/ScrollRestoration.tsx +10 -6
  83. package/src/browser/react/deferred-handle-resolution.ts +75 -0
  84. package/src/browser/react/filter-segment-order.ts +66 -7
  85. package/src/browser/react/index.ts +0 -48
  86. package/src/browser/react/location-state-shared.ts +178 -8
  87. package/src/browser/react/location-state.ts +39 -14
  88. package/src/browser/react/use-action.ts +6 -15
  89. package/src/browser/react/use-handle.ts +17 -14
  90. package/src/browser/react/use-href.tsx +8 -1
  91. package/src/browser/react/use-link-status.ts +33 -8
  92. package/src/browser/react/use-navigation.ts +10 -5
  93. package/src/browser/react/use-params.ts +11 -11
  94. package/src/browser/react/use-reverse.ts +106 -0
  95. package/src/browser/react/use-router.ts +25 -3
  96. package/src/browser/react/use-search-params.ts +0 -5
  97. package/src/browser/react/use-segments.ts +11 -21
  98. package/src/browser/response-adapter.ts +99 -8
  99. package/src/browser/rsc-router.tsx +91 -24
  100. package/src/browser/scroll-restoration.ts +30 -17
  101. package/src/browser/segment-structure-assert.ts +2 -2
  102. package/src/browser/server-action-bridge.ts +214 -55
  103. package/src/browser/types.ts +80 -9
  104. package/src/browser/validate-redirect-origin.ts +43 -16
  105. package/src/build/collect-fallback-refs.ts +107 -0
  106. package/src/build/generate-manifest.ts +60 -35
  107. package/src/build/generate-route-types.ts +2 -1
  108. package/src/build/index.ts +8 -2
  109. package/src/build/prefix-tree-utils.ts +123 -0
  110. package/src/build/route-trie.ts +117 -14
  111. package/src/build/route-types/ast-route-extraction.ts +15 -8
  112. package/src/build/route-types/codegen.ts +16 -5
  113. package/src/build/route-types/include-resolution.ts +117 -23
  114. package/src/build/route-types/param-extraction.ts +6 -3
  115. package/src/build/route-types/per-module-writer.ts +22 -6
  116. package/src/build/route-types/router-processing.ts +55 -28
  117. package/src/build/route-types/scan-filter.ts +1 -1
  118. package/src/build/route-types/source-scan.ts +216 -0
  119. package/src/build/runtime-discovery.ts +9 -20
  120. package/src/cache/cache-error.ts +104 -0
  121. package/src/cache/cache-key-utils.ts +29 -13
  122. package/src/cache/cache-policy.ts +108 -34
  123. package/src/cache/cache-runtime.ts +224 -41
  124. package/src/cache/cache-scope.ts +188 -82
  125. package/src/cache/cache-tag.ts +103 -0
  126. package/src/cache/cf/cf-base64.ts +33 -0
  127. package/src/cache/cf/cf-cache-constants.ts +127 -0
  128. package/src/cache/cf/cf-cache-store.ts +1989 -378
  129. package/src/cache/cf/cf-cache-types.ts +349 -0
  130. package/src/cache/cf/cf-kv-utils.ts +46 -0
  131. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  132. package/src/cache/cf/index.ts +6 -16
  133. package/src/cache/document-cache.ts +89 -21
  134. package/src/cache/handle-snapshot.ts +70 -0
  135. package/src/cache/index.ts +10 -20
  136. package/src/cache/memory-segment-store.ts +136 -37
  137. package/src/cache/profile-registry.ts +46 -31
  138. package/src/cache/read-through-swr.ts +56 -12
  139. package/src/cache/segment-codec.ts +9 -17
  140. package/src/cache/tag-invalidation.ts +230 -0
  141. package/src/cache/types.ts +37 -100
  142. package/src/client.rsc.tsx +44 -21
  143. package/src/client.tsx +36 -61
  144. package/src/cloudflare/index.ts +11 -0
  145. package/src/cloudflare/tracing.ts +109 -0
  146. package/src/component-utils.ts +19 -0
  147. package/src/components/DefaultDocument.tsx +8 -2
  148. package/src/context-var.ts +18 -6
  149. package/src/decode-loader-results.ts +52 -0
  150. package/src/defer.ts +196 -0
  151. package/src/deps/ssr.ts +0 -1
  152. package/src/encode-kv.ts +49 -0
  153. package/src/errors.ts +30 -4
  154. package/src/escape-script.ts +52 -0
  155. package/src/handle.ts +31 -23
  156. package/src/handles/MetaTags.tsx +62 -19
  157. package/src/handles/Scripts.tsx +183 -0
  158. package/src/handles/breadcrumbs.ts +37 -8
  159. package/src/handles/is-thenable.ts +19 -0
  160. package/src/handles/meta.ts +51 -40
  161. package/src/handles/script.ts +244 -0
  162. package/src/host/cookie-handler.ts +9 -60
  163. package/src/host/errors.ts +0 -24
  164. package/src/host/index.ts +8 -2
  165. package/src/host/pattern-matcher.ts +23 -52
  166. package/src/host/router.ts +107 -99
  167. package/src/host/testing.ts +40 -27
  168. package/src/host/types.ts +37 -4
  169. package/src/host/utils.ts +1 -1
  170. package/src/href-client.ts +137 -22
  171. package/src/index.rsc.ts +96 -12
  172. package/src/index.ts +94 -14
  173. package/src/internal-debug.ts +11 -10
  174. package/src/loader-store.ts +500 -0
  175. package/src/loader.rsc.ts +20 -13
  176. package/src/loader.ts +12 -11
  177. package/src/missing-id-error.ts +68 -0
  178. package/src/outlet-context.ts +1 -1
  179. package/src/outlet-provider.tsx +1 -5
  180. package/src/prerender/param-hash.ts +16 -16
  181. package/src/prerender/store.ts +32 -37
  182. package/src/prerender.ts +61 -6
  183. package/src/redirect-origin.ts +100 -0
  184. package/src/regex-escape.ts +8 -0
  185. package/src/render-error-thrower.tsx +20 -0
  186. package/src/response-utils.ts +34 -0
  187. package/src/reverse.ts +65 -40
  188. package/src/root-error-boundary.tsx +1 -19
  189. package/src/route-content-wrapper.tsx +19 -77
  190. package/src/route-definition/dsl-helpers.ts +304 -309
  191. package/src/route-definition/helper-factories.ts +28 -140
  192. package/src/route-definition/helpers-types.ts +82 -55
  193. package/src/route-definition/index.ts +1 -2
  194. package/src/route-definition/redirect.ts +44 -11
  195. package/src/route-definition/resolve-handler-use.ts +12 -1
  196. package/src/route-definition/use-item-types.ts +29 -0
  197. package/src/route-map-builder.ts +0 -16
  198. package/src/route-types.ts +19 -46
  199. package/src/router/basename.ts +14 -0
  200. package/src/router/content-negotiation.ts +73 -25
  201. package/src/router/error-handling.ts +45 -18
  202. package/src/router/find-match.ts +44 -23
  203. package/src/router/handler-context.ts +27 -43
  204. package/src/router/instrument.ts +350 -0
  205. package/src/router/intercept-resolution.ts +39 -20
  206. package/src/router/lazy-includes.ts +10 -47
  207. package/src/router/loader-resolution.ts +155 -72
  208. package/src/router/logging.ts +0 -6
  209. package/src/router/manifest.ts +18 -29
  210. package/src/router/match-api.ts +9 -24
  211. package/src/router/match-context.ts +0 -22
  212. package/src/router/match-handlers.ts +58 -58
  213. package/src/router/match-middleware/background-revalidation.ts +40 -24
  214. package/src/router/match-middleware/cache-lookup.ts +159 -285
  215. package/src/router/match-middleware/cache-store.ts +64 -52
  216. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  217. package/src/router/match-middleware/segment-resolution.ts +0 -22
  218. package/src/router/match-pipelines.ts +1 -42
  219. package/src/router/match-result.ts +44 -74
  220. package/src/router/metrics.ts +0 -34
  221. package/src/router/middleware-types.ts +7 -134
  222. package/src/router/middleware.ts +247 -166
  223. package/src/router/navigation-snapshot.ts +0 -51
  224. package/src/router/params-util.ts +23 -0
  225. package/src/router/pattern-matching.ts +85 -94
  226. package/src/router/prefetch-cache-ttl.ts +51 -0
  227. package/src/router/prerender-match.ts +104 -65
  228. package/src/router/preview-match.ts +3 -1
  229. package/src/router/request-classification.ts +28 -62
  230. package/src/router/revalidation.ts +123 -73
  231. package/src/router/route-snapshot.ts +0 -1
  232. package/src/router/router-context.ts +3 -28
  233. package/src/router/router-interfaces.ts +83 -35
  234. package/src/router/router-options.ts +136 -5
  235. package/src/router/router-registry.ts +2 -5
  236. package/src/router/segment-resolution/fresh.ts +97 -84
  237. package/src/router/segment-resolution/helpers.ts +86 -6
  238. package/src/router/segment-resolution/loader-cache.ts +76 -39
  239. package/src/router/segment-resolution/revalidation.ts +272 -320
  240. package/src/router/segment-resolution/static-store.ts +19 -5
  241. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  242. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  243. package/src/router/segment-resolution.ts +5 -1
  244. package/src/router/segment-wrappers.ts +6 -5
  245. package/src/router/state-cookie-name.ts +33 -0
  246. package/src/router/substitute-pattern-params.ts +56 -0
  247. package/src/router/telemetry-otel.ts +161 -199
  248. package/src/router/telemetry.ts +96 -19
  249. package/src/router/timeout.ts +0 -20
  250. package/src/router/tracing.ts +206 -0
  251. package/src/router/trie-matching.ts +162 -64
  252. package/src/router/types.ts +9 -63
  253. package/src/router/url-params.ts +0 -5
  254. package/src/router.ts +110 -55
  255. package/src/rsc/handler-context.ts +3 -2
  256. package/src/rsc/handler.ts +264 -220
  257. package/src/rsc/helpers.ts +100 -6
  258. package/src/rsc/index.ts +2 -5
  259. package/src/rsc/json-route-result.ts +38 -0
  260. package/src/rsc/loader-fetch.ts +114 -38
  261. package/src/rsc/manifest-init.ts +28 -41
  262. package/src/rsc/origin-guard.ts +39 -25
  263. package/src/rsc/progressive-enhancement.ts +117 -11
  264. package/src/rsc/redirect-guard.ts +99 -0
  265. package/src/rsc/response-cache-serve.ts +238 -0
  266. package/src/rsc/response-error.ts +79 -12
  267. package/src/rsc/response-route-handler.ts +88 -188
  268. package/src/rsc/rsc-rendering.ts +98 -76
  269. package/src/rsc/runtime-warnings.ts +23 -10
  270. package/src/rsc/server-action.ts +281 -117
  271. package/src/rsc/ssr-setup.ts +16 -0
  272. package/src/rsc/transition-gate.ts +89 -0
  273. package/src/rsc/types.ts +23 -5
  274. package/src/runtime-env.ts +18 -0
  275. package/src/search-params.ts +35 -30
  276. package/src/segment-loader-promise.ts +31 -4
  277. package/src/segment-system.tsx +254 -143
  278. package/src/serialize.ts +243 -0
  279. package/src/server/context.ts +163 -51
  280. package/src/server/cookie-parse.ts +32 -0
  281. package/src/server/cookie-store.ts +80 -5
  282. package/src/server/handle-store.ts +21 -38
  283. package/src/server/loader-registry.ts +33 -42
  284. package/src/server/request-context.ts +287 -178
  285. package/src/ssr/index.tsx +21 -16
  286. package/src/static-handler.ts +10 -13
  287. package/src/testing/cache-status.ts +162 -0
  288. package/src/testing/collect-handle.ts +40 -0
  289. package/src/testing/dispatch.ts +701 -0
  290. package/src/testing/dom.entry.ts +22 -0
  291. package/src/testing/e2e/fixture.ts +188 -0
  292. package/src/testing/e2e/index.ts +128 -0
  293. package/src/testing/e2e/matchers.ts +35 -0
  294. package/src/testing/e2e/page-helpers.ts +272 -0
  295. package/src/testing/e2e/parity.ts +387 -0
  296. package/src/testing/e2e/server.ts +195 -0
  297. package/src/testing/flight-matchers.ts +97 -0
  298. package/src/testing/flight-normalize.ts +11 -0
  299. package/src/testing/flight-runtime.d.ts +57 -0
  300. package/src/testing/flight-tree.ts +682 -0
  301. package/src/testing/flight.entry.ts +52 -0
  302. package/src/testing/flight.ts +257 -0
  303. package/src/testing/generated-routes.ts +183 -0
  304. package/src/testing/index.ts +105 -0
  305. package/src/testing/internal/context.ts +371 -0
  306. package/src/testing/internal/flight-client-globals.ts +30 -0
  307. package/src/testing/internal/seed-vars.ts +54 -0
  308. package/src/testing/render-handler.ts +357 -0
  309. package/src/testing/render-route.tsx +581 -0
  310. package/src/testing/run-loader.ts +385 -0
  311. package/src/testing/run-middleware.ts +205 -0
  312. package/src/testing/run-transition-when.ts +164 -0
  313. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  314. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  315. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  316. package/src/testing/vitest-stubs/version.ts +5 -0
  317. package/src/testing/vitest.ts +305 -0
  318. package/src/theme/ThemeProvider.tsx +20 -58
  319. package/src/theme/ThemeScript.tsx +7 -9
  320. package/src/theme/constants.ts +52 -13
  321. package/src/theme/index.ts +0 -7
  322. package/src/theme/theme-context.ts +1 -5
  323. package/src/theme/theme-script.ts +22 -21
  324. package/src/theme/use-theme.ts +0 -3
  325. package/src/types/boundaries.ts +0 -35
  326. package/src/types/cache-types.ts +13 -4
  327. package/src/types/error-types.ts +30 -90
  328. package/src/types/global-namespace.ts +54 -41
  329. package/src/types/handler-context.ts +110 -62
  330. package/src/types/index.ts +3 -10
  331. package/src/types/loader-types.ts +11 -9
  332. package/src/types/request-scope.ts +112 -0
  333. package/src/types/route-config.ts +6 -50
  334. package/src/types/route-entry.ts +0 -6
  335. package/src/types/segments.ts +135 -14
  336. package/src/urls/include-helper.ts +9 -56
  337. package/src/urls/index.ts +1 -11
  338. package/src/urls/path-helper-types.ts +29 -12
  339. package/src/urls/path-helper.ts +17 -106
  340. package/src/urls/pattern-types.ts +36 -19
  341. package/src/urls/response-types.ts +22 -29
  342. package/src/urls/type-extraction.ts +58 -139
  343. package/src/urls/urls-function.ts +1 -19
  344. package/src/use-loader.tsx +292 -107
  345. package/src/vite/debug.ts +185 -0
  346. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  347. package/src/vite/discovery/discover-routers.ts +126 -85
  348. package/src/vite/discovery/discovery-errors.ts +194 -0
  349. package/src/vite/discovery/gate-state.ts +171 -0
  350. package/src/vite/discovery/prerender-collection.ts +96 -68
  351. package/src/vite/discovery/route-types-writer.ts +40 -84
  352. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  353. package/src/vite/discovery/state.ts +44 -0
  354. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  355. package/src/vite/index.ts +2 -0
  356. package/src/vite/inject-client-debug.ts +36 -0
  357. package/src/vite/plugin-types.ts +126 -8
  358. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  359. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  360. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  361. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  362. package/src/vite/plugins/expose-action-id.ts +48 -95
  363. package/src/vite/plugins/expose-id-utils.ts +88 -55
  364. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  365. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  366. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  367. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  368. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  369. package/src/vite/plugins/performance-tracks.ts +26 -25
  370. package/src/vite/plugins/refresh-cmd.ts +1 -1
  371. package/src/vite/plugins/use-cache-transform.ts +73 -83
  372. package/src/vite/plugins/version-injector.ts +40 -29
  373. package/src/vite/plugins/version-plugin.ts +37 -40
  374. package/src/vite/plugins/virtual-entries.ts +39 -25
  375. package/src/vite/rango.ts +109 -118
  376. package/src/vite/router-discovery.ts +718 -119
  377. package/src/vite/utils/ast-handler-extract.ts +26 -35
  378. package/src/vite/utils/banner.ts +1 -1
  379. package/src/vite/utils/bundle-analysis.ts +10 -15
  380. package/src/vite/utils/client-chunks.ts +184 -0
  381. package/src/vite/utils/directive-prologue.ts +40 -0
  382. package/src/vite/utils/forward-user-plugins.ts +171 -0
  383. package/src/vite/utils/manifest-utils.ts +4 -59
  384. package/src/vite/utils/package-resolution.ts +20 -52
  385. package/src/vite/utils/prerender-utils.ts +54 -39
  386. package/src/vite/utils/shared-utils.ts +90 -41
  387. package/src/browser/action-response-classifier.ts +0 -99
  388. package/src/browser/react/use-client-cache.ts +0 -58
  389. package/src/browser/shallow.ts +0 -40
  390. package/src/handles/index.ts +0 -7
  391. package/src/network-error-thrower.tsx +0 -23
  392. package/src/router/middleware-cookies.ts +0 -55
@@ -26,11 +26,8 @@ import type {
26
26
  ThemeContextValue,
27
27
  ThemeProviderProps,
28
28
  } from "./types.js";
29
- import { THEME_COOKIE } from "./constants.js";
29
+ import { THEME_COOKIE, isValidTheme, warnInvalidTheme } from "./constants.js";
30
30
 
31
- /**
32
- * Get system preference for color scheme
33
- */
34
31
  function getSystemTheme(): ResolvedTheme {
35
32
  if (typeof window !== "undefined" && window.matchMedia) {
36
33
  return window.matchMedia("(prefers-color-scheme: dark)").matches
@@ -40,9 +37,6 @@ function getSystemTheme(): ResolvedTheme {
40
37
  return "light";
41
38
  }
42
39
 
43
- /**
44
- * Read theme from cookie
45
- */
46
40
  function readThemeFromCookie(storageKey: string): string | null {
47
41
  if (typeof document === "undefined") return null;
48
42
 
@@ -61,9 +55,6 @@ function readThemeFromCookie(storageKey: string): string | null {
61
55
  return null;
62
56
  }
63
57
 
64
- /**
65
- * Read theme from localStorage
66
- */
67
58
  function readThemeFromStorage(storageKey: string): string | null {
68
59
  if (typeof localStorage === "undefined") return null;
69
60
 
@@ -74,9 +65,6 @@ function readThemeFromStorage(storageKey: string): string | null {
74
65
  }
75
66
  }
76
67
 
77
- /**
78
- * Write theme to cookie
79
- */
80
68
  function writeThemeToCookie(storageKey: string, theme: Theme): void {
81
69
  if (typeof document === "undefined") return;
82
70
 
@@ -85,9 +73,6 @@ function writeThemeToCookie(storageKey: string, theme: Theme): void {
85
73
  document.cookie = cookie;
86
74
  }
87
75
 
88
- /**
89
- * Write theme to localStorage
90
- */
91
76
  function writeThemeToStorage(storageKey: string, theme: Theme): void {
92
77
  if (typeof localStorage === "undefined") return;
93
78
 
@@ -98,9 +83,6 @@ function writeThemeToStorage(storageKey: string, theme: Theme): void {
98
83
  }
99
84
  }
100
85
 
101
- /**
102
- * Apply theme to HTML element
103
- */
104
86
  function applyThemeToDocument(theme: Theme, config: ResolvedThemeConfig): void {
105
87
  if (typeof document === "undefined") return;
106
88
 
@@ -112,40 +94,30 @@ function applyThemeToDocument(theme: Theme, config: ResolvedThemeConfig): void {
112
94
  const value = config.value[resolved] || resolved;
113
95
  const el = document.documentElement;
114
96
 
115
- // Apply attribute
116
97
  if (config.attribute === "class") {
117
- // Remove all theme classes
118
98
  for (const t of config.themes) {
119
99
  const v = config.value[t] || t;
120
100
  el.classList.remove(v);
121
101
  }
122
- // Add current theme class
123
102
  el.classList.add(value);
124
103
  } else {
125
104
  el.setAttribute(config.attribute, value);
126
105
  }
127
106
 
128
- // Set color-scheme for native dark mode support
129
107
  if (config.enableColorScheme) {
130
108
  el.style.colorScheme = resolved;
131
109
  }
132
110
  }
133
111
 
134
- /**
135
- * Get the resolved stored theme (validated against available themes)
136
- */
137
112
  function getStoredTheme(config: ResolvedThemeConfig): Theme {
138
113
  const { storageKey, themes, defaultTheme, enableSystem } = config;
139
114
 
140
- // Try cookie first (for SSR consistency)
141
115
  let stored = readThemeFromCookie(storageKey);
142
116
 
143
- // Fall back to localStorage
144
117
  if (!stored) {
145
118
  stored = readThemeFromStorage(storageKey);
146
119
  }
147
120
 
148
- // Validate stored value
149
121
  if (stored) {
150
122
  if (stored === "system" && enableSystem) {
151
123
  return "system";
@@ -158,40 +130,37 @@ function getStoredTheme(config: ResolvedThemeConfig): Theme {
158
130
  return defaultTheme;
159
131
  }
160
132
 
161
- /**
162
- * ThemeProvider component
163
- *
164
- * Provides theme state to the component tree via context.
165
- * Handles theme persistence, system preference detection, and cross-tab sync.
166
- */
167
133
  export function ThemeProvider({
168
134
  config,
169
135
  initialTheme,
170
136
  children,
171
137
  }: ThemeProviderProps): React.ReactNode {
172
- // Track mount state to avoid hydration mismatches
173
- // During SSR and initial hydration, mounted is false
174
138
  const [mounted, setMounted] = useState(false);
175
139
 
176
- // Initialize theme from prop, storage, or default
177
140
  const [theme, setThemeState] = useState<Theme>(() => {
178
141
  if (initialTheme) return initialTheme;
179
142
  if (typeof window === "undefined") return config.defaultTheme;
180
143
  return getStoredTheme(config);
181
144
  });
182
145
 
183
- // Track system preference - use stable default during SSR
184
146
  const [systemTheme, setSystemTheme] = useState<ResolvedTheme>("light");
185
147
 
186
- // Set mounted after hydration and detect actual system theme
187
148
  useEffect(() => {
188
149
  setMounted(true);
189
150
  setSystemTheme(getSystemTheme());
190
151
  }, []);
191
152
 
192
- // Set theme and persist to storage
193
153
  const setTheme = useCallback(
194
154
  (newTheme: Theme) => {
155
+ // Shared guard (isValidTheme) used by the server ctx.setTheme too: reject
156
+ // any value not in the configured theme set, AND reject "system" when
157
+ // system detection is off (applyThemeToDocument would write a bogus
158
+ // class="system"). Keeps the cookie from holding a value the server would
159
+ // reinterpret as defaultTheme on the next SSR (desyncing markup).
160
+ if (!isValidTheme(newTheme, config)) {
161
+ warnInvalidTheme(newTheme, config);
162
+ return;
163
+ }
195
164
  setThemeState(newTheme);
196
165
  writeThemeToCookie(config.storageKey, newTheme);
197
166
  writeThemeToStorage(config.storageKey, newTheme);
@@ -200,7 +169,6 @@ export function ThemeProvider({
200
169
  [config],
201
170
  );
202
171
 
203
- // Listen for system preference changes
204
172
  useEffect(() => {
205
173
  if (!config.enableSystem) return;
206
174
  if (typeof window === "undefined" || !window.matchMedia) return;
@@ -211,13 +179,11 @@ export function ThemeProvider({
211
179
  const newSystemTheme = e.matches ? "dark" : "light";
212
180
  setSystemTheme(newSystemTheme);
213
181
 
214
- // If current theme is "system", re-apply to update document
215
182
  if (theme === "system") {
216
183
  applyThemeToDocument("system", config);
217
184
  }
218
185
  };
219
186
 
220
- // Modern browsers
221
187
  mediaQuery.addEventListener("change", handleChange);
222
188
 
223
189
  return () => {
@@ -225,7 +191,6 @@ export function ThemeProvider({
225
191
  };
226
192
  }, [config, theme]);
227
193
 
228
- // Cross-tab synchronization via localStorage storage event
229
194
  useEffect(() => {
230
195
  if (typeof window === "undefined") return;
231
196
 
@@ -235,11 +200,16 @@ export function ThemeProvider({
235
200
  const newTheme = e.newValue;
236
201
  if (!newTheme) return;
237
202
 
238
- // Validate and apply
239
- if (newTheme === "system" || config.themes.includes(newTheme)) {
240
- setThemeState(newTheme as Theme);
241
- applyThemeToDocument(newTheme as Theme, config);
242
- }
203
+ // A cross-tab storage event can carry any value (another tab, or stale
204
+ // localStorage). Reuse the shared validity rule: reject anything not a
205
+ // configured theme, AND reject "system" when system detection is off (it
206
+ // would apply a bogus class="system"). An invalid received value falls back
207
+ // to defaultTheme rather than applying as-is.
208
+ const applied: Theme = isValidTheme(newTheme, config)
209
+ ? (newTheme as Theme)
210
+ : config.defaultTheme;
211
+ setThemeState(applied);
212
+ applyThemeToDocument(applied, config);
243
213
  };
244
214
 
245
215
  window.addEventListener("storage", handleStorageChange);
@@ -249,12 +219,8 @@ export function ThemeProvider({
249
219
  };
250
220
  }, [config]);
251
221
 
252
- // Compute resolved theme
253
- // During SSR (not mounted), use the initial theme or default to avoid hydration mismatch
254
222
  const resolvedTheme: ResolvedTheme = useMemo(() => {
255
223
  if (!mounted) {
256
- // During SSR, return the initial theme if it's not "system", otherwise "light"
257
- // The inline script will apply the correct class before hydration
258
224
  if (initialTheme && initialTheme !== "system") {
259
225
  return initialTheme as ResolvedTheme;
260
226
  }
@@ -266,7 +232,6 @@ export function ThemeProvider({
266
232
  return theme as ResolvedTheme;
267
233
  }, [theme, systemTheme, config.enableSystem, mounted, initialTheme]);
268
234
 
269
- // Build themes list (include "system" if enabled)
270
235
  const themes = useMemo(() => {
271
236
  if (config.enableSystem) {
272
237
  return ["system", ...config.themes.filter((t) => t !== "system")];
@@ -274,14 +239,11 @@ export function ThemeProvider({
274
239
  return config.themes;
275
240
  }, [config.themes, config.enableSystem]);
276
241
 
277
- // Context value
278
- // During SSR (not mounted), return stable values to avoid hydration mismatch
279
242
  const contextValue: ThemeContextValue = useMemo(
280
243
  () => ({
281
244
  theme,
282
245
  setTheme,
283
246
  resolvedTheme,
284
- // Return stable "light" for systemTheme during SSR - actual value updates after mount
285
247
  systemTheme: mounted ? systemTheme : "light",
286
248
  themes,
287
249
  config,
@@ -8,17 +8,21 @@
8
8
  *
9
9
  * Must be placed in the <head> element of your document, before any stylesheets.
10
10
  *
11
+ * Note: when theme is enabled in the router config, `<MetaTags />` ALREADY
12
+ * renders this FOUC script. Use `<ThemeScript />` only if you do NOT render
13
+ * `<MetaTags />`. Rendering both is safe — the inline script guards the
14
+ * matchMedia listener registration against double-running — but it is redundant.
15
+ *
11
16
  * @example
12
17
  * ```tsx
13
- * // In your document component
18
+ * // In your document component. Use ThemeScript only when you do not render MetaTags.
14
19
  * import { ThemeScript } from "@rangojs/router/theme";
15
20
  *
16
21
  * export function Document({ children }) {
17
22
  * return (
18
23
  * <html lang="en" suppressHydrationWarning>
19
24
  * <head>
20
- * <ThemeScript />
21
- * <MetaTags />
25
+ * <ThemeScript config={config} />
22
26
  * </head>
23
27
  * <body>{children}</body>
24
28
  * </html>
@@ -43,12 +47,6 @@ export interface ThemeScriptProps {
43
47
  nonce?: string;
44
48
  }
45
49
 
46
- /**
47
- * Server component that renders the theme initialization script.
48
- *
49
- * This renders a synchronous inline script that applies the theme
50
- * to the HTML element before React hydration, preventing FOUC.
51
- */
52
50
  export function ThemeScript({
53
51
  config,
54
52
  nonce,
@@ -2,11 +2,8 @@
2
2
  * Default values for theme configuration
3
3
  */
4
4
 
5
- import type { ResolvedThemeConfig, ThemeConfig } from "./types.js";
5
+ import type { ResolvedThemeConfig, Theme, ThemeConfig } from "./types.js";
6
6
 
7
- /**
8
- * Default theme configuration values
9
- */
10
7
  export const THEME_DEFAULTS = {
11
8
  defaultTheme: "system",
12
9
  themes: ["light", "dark"],
@@ -16,9 +13,6 @@ export const THEME_DEFAULTS = {
16
13
  enableColorScheme: true,
17
14
  } as const;
18
15
 
19
- /**
20
- * Cookie configuration for theme persistence
21
- */
22
16
  export const THEME_COOKIE: {
23
17
  readonly maxAge: number;
24
18
  readonly path: string;
@@ -30,31 +24,76 @@ export const THEME_COOKIE: {
30
24
  };
31
25
 
32
26
  /**
33
- * Resolve theme config by applying defaults.
34
- * Accepts `true` to enable with all defaults, or a config object.
27
+ * Single owner of the setTheme validity rule, shared by the client
28
+ * (ThemeProvider) and server (ctx.setTheme) guards so they cannot drift.
29
+ *
30
+ * A theme is valid when it is one of the configured concrete themes, OR
31
+ * "system" but only while system detection is enabled. Rejecting "system" when
32
+ * `enableSystem` is false is load-bearing: applyThemeToDocument would otherwise
33
+ * leave "system" unresolved and write a bogus class="system" / colorScheme
34
+ * ="system" on <html> (the same bogus value resolveThemeConfig coerces away for
35
+ * the default).
36
+ */
37
+ export function isValidTheme(
38
+ theme: string,
39
+ config: Pick<ResolvedThemeConfig, "themes" | "enableSystem">,
40
+ ): boolean {
41
+ if (theme === "system") return config.enableSystem;
42
+ return config.themes.includes(theme);
43
+ }
44
+
45
+ /**
46
+ * Emit the shared "[Theme] Invalid theme value" warning. One owner of the
47
+ * message string so the client and server guards stay byte-identical.
48
+ *
49
+ * The valid-values list mirrors isValidTheme: "system" is only listed when
50
+ * enableSystem is true, otherwise the message would advertise a value the guard
51
+ * itself rejects.
35
52
  */
53
+ export function warnInvalidTheme(
54
+ theme: string,
55
+ config: Pick<ResolvedThemeConfig, "themes" | "enableSystem">,
56
+ ): void {
57
+ const validValues = config.enableSystem
58
+ ? ["system", ...config.themes]
59
+ : config.themes;
60
+ console.warn(
61
+ `[Theme] Invalid theme value: "${theme}". Valid values: ${validValues.join(", ")}`,
62
+ );
63
+ }
64
+
36
65
  export function resolveThemeConfig(
37
66
  config: ThemeConfig | true,
38
67
  ): ResolvedThemeConfig {
39
- // Handle `theme: true` shorthand
40
68
  if (config === true) {
41
69
  config = {};
42
70
  }
43
71
 
44
72
  const themes = config.themes ?? [...THEME_DEFAULTS.themes];
45
73
 
46
- // Build value mapping - default to identity mapping
47
74
  const value: Record<string, string> = {};
48
75
  for (const theme of themes) {
49
76
  value[theme] = config.value?.[theme] ?? theme;
50
77
  }
51
78
 
79
+ const enableSystem = config.enableSystem ?? THEME_DEFAULTS.enableSystem;
80
+
81
+ // When system detection is disabled, "system" is not a valid resolved theme.
82
+ // Coerce both the unset default and an explicit defaultTheme:"system" to the
83
+ // first concrete theme, so the FOUC script / ThemeProvider never apply a bogus
84
+ // class="system" / colorScheme="system" on <html>.
85
+ const requestedDefault = config.defaultTheme ?? THEME_DEFAULTS.defaultTheme;
86
+ const defaultTheme =
87
+ !enableSystem && requestedDefault === "system"
88
+ ? (themes[0] as Theme)
89
+ : requestedDefault;
90
+
52
91
  return {
53
- defaultTheme: config.defaultTheme ?? THEME_DEFAULTS.defaultTheme,
92
+ defaultTheme,
54
93
  themes,
55
94
  attribute: config.attribute ?? THEME_DEFAULTS.attribute,
56
95
  storageKey: config.storageKey ?? THEME_DEFAULTS.storageKey,
57
- enableSystem: config.enableSystem ?? THEME_DEFAULTS.enableSystem,
96
+ enableSystem,
58
97
  enableColorScheme:
59
98
  config.enableColorScheme ?? THEME_DEFAULTS.enableColorScheme,
60
99
  value,
@@ -23,16 +23,10 @@
23
23
  * ```
24
24
  */
25
25
 
26
- // Main hook for accessing theme
27
26
  export { useTheme } from "./use-theme.js";
28
-
29
- // Provider (typically auto-included via NavigationProvider when theme is enabled)
30
27
  export { ThemeProvider } from "./ThemeProvider.js";
31
-
32
- // Script component for FOUC prevention (use in document head)
33
28
  export { ThemeScript, type ThemeScriptProps } from "./ThemeScript.js";
34
29
 
35
- // Types
36
30
  export type {
37
31
  Theme,
38
32
  ResolvedTheme,
@@ -44,5 +38,4 @@ export type {
44
38
  ThemeContextValue,
45
39
  } from "./types.js";
46
40
 
47
- // Constants
48
41
  export { THEME_DEFAULTS, THEME_COOKIE } from "./constants.js";
@@ -19,17 +19,13 @@ import type { ThemeContextValue } from "./types.js";
19
19
  export const ThemeContext: Context<ThemeContextValue | null> =
20
20
  createContext<ThemeContextValue | null>(null);
21
21
 
22
- /**
23
- * Get theme context (internal use)
24
- * Returns null if theme is not enabled
25
- */
26
22
  export function useThemeContext(): ThemeContextValue | null {
27
23
  return useContext(ThemeContext);
28
24
  }
29
25
 
30
26
  /**
31
27
  * Get theme context, throwing if not available
32
- * Use this in useTheme hook
28
+ * Used by useTheme hook
33
29
  */
34
30
  export function requireThemeContext(): ThemeContextValue {
35
31
  const ctx = useContext(ThemeContext);
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { ResolvedThemeConfig } from "./types.js";
14
+ import { escapeJsonForScript } from "../escape-script.js";
14
15
 
15
16
  /**
16
17
  * Generate the inline script for theme initialization
@@ -22,20 +23,17 @@ import type { ResolvedThemeConfig } from "./types.js";
22
23
  * - Handle all edge cases (no localStorage, no cookie, etc.)
23
24
  */
24
25
  export function generateThemeScript(config: ResolvedThemeConfig): string {
25
- // Build the script as a string, then minify
26
26
  const script = `
27
27
  (function() {
28
- var storageKey = ${JSON.stringify(config.storageKey)};
29
- var defaultTheme = ${JSON.stringify(config.defaultTheme)};
30
- var attribute = ${JSON.stringify(config.attribute)};
28
+ var storageKey = ${escapeJsonForScript(JSON.stringify(config.storageKey))};
29
+ var defaultTheme = ${escapeJsonForScript(JSON.stringify(config.defaultTheme))};
30
+ var attribute = ${escapeJsonForScript(JSON.stringify(config.attribute))};
31
31
  var enableSystem = ${config.enableSystem};
32
32
  var enableColorScheme = ${config.enableColorScheme};
33
- var valueMap = ${JSON.stringify(config.value)};
34
- var themes = ${JSON.stringify(config.themes)};
33
+ var valueMap = ${escapeJsonForScript(JSON.stringify(config.value))};
34
+ var themes = ${escapeJsonForScript(JSON.stringify(config.themes))};
35
35
 
36
- // Read theme from cookie or localStorage
37
36
  function getStoredTheme() {
38
- // Try cookie first (for SSR consistency)
39
37
  var cookies = document.cookie.split(';');
40
38
  for (var i = 0; i < cookies.length; i++) {
41
39
  var cookie = cookies[i].trim();
@@ -44,7 +42,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
44
42
  catch (e) { return cookie.substring(storageKey.length + 1); }
45
43
  }
46
44
  }
47
- // Fall back to localStorage
48
45
  try {
49
46
  return localStorage.getItem(storageKey);
50
47
  } catch (e) {
@@ -52,7 +49,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
52
49
  }
53
50
  }
54
51
 
55
- // Get system preference
56
52
  function getSystemTheme() {
57
53
  if (typeof window !== 'undefined' && window.matchMedia) {
58
54
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
@@ -60,7 +56,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
60
56
  return 'light';
61
57
  }
62
58
 
63
- // Resolve "system" to actual theme
64
59
  function resolveTheme(theme) {
65
60
  if (theme === 'system' && enableSystem) {
66
61
  return getSystemTheme();
@@ -68,15 +63,12 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
68
63
  return theme;
69
64
  }
70
65
 
71
- // Apply theme to HTML element
72
66
  function applyTheme(theme) {
73
67
  var resolved = resolveTheme(theme);
74
68
  var value = valueMap[resolved] || resolved;
75
69
  var el = document.documentElement;
76
70
 
77
- // Apply attribute
78
71
  if (attribute === 'class') {
79
- // Remove all theme classes, then add current
80
72
  for (var i = 0; i < themes.length; i++) {
81
73
  var v = valueMap[themes[i]] || themes[i];
82
74
  el.classList.remove(v);
@@ -86,23 +78,32 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
86
78
  el.setAttribute(attribute, value);
87
79
  }
88
80
 
89
- // Set color-scheme for native dark mode support
90
81
  if (enableColorScheme) {
91
82
  el.style.colorScheme = resolved;
92
83
  }
93
84
  }
94
85
 
95
- // Get stored theme or use default
96
86
  var stored = getStoredTheme();
97
- var theme = stored && (stored === 'system' || themes.indexOf(stored) !== -1)
87
+ // A stored value is valid when it is a configured theme, OR "system" but only
88
+ // while system detection is enabled. A stored "system" with enableSystem=false
89
+ // (an old cookie/localStorage, or a value pushed cross-tab) must fall back to
90
+ // defaultTheme — otherwise resolveTheme returns "system" unresolved and
91
+ // applyTheme writes a bogus class="system" / colorScheme="system" on <html>.
92
+ // Same rule as isValidTheme (constants.ts), inlined since this is a string.
93
+ var systemAllowed = stored === 'system' && enableSystem;
94
+ var theme = stored && (systemAllowed || themes.indexOf(stored) !== -1)
98
95
  ? stored
99
96
  : defaultTheme;
100
97
 
101
- // Apply immediately
102
98
  applyTheme(theme);
103
99
 
104
- // Listen for system preference changes (for "system" theme)
105
- if (enableSystem && typeof window !== 'undefined' && window.matchMedia) {
100
+ // Idempotency guard: MetaTags auto-injects this script when theme is enabled,
101
+ // and ThemeScript is also a public component for the same job. If a consumer
102
+ // renders both, the IIFE runs twice; without this guard the second run would
103
+ // register a SECOND, never-removed matchMedia('change') listener (a leak).
104
+ // The flag is keyed by storageKey so independent theme configs don't collide.
105
+ var flagKey = '__rangoThemeListener_' + storageKey;
106
+ if (enableSystem && typeof window !== 'undefined' && window.matchMedia && !window[flagKey]) {
106
107
  try {
107
108
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
108
109
  var current = getStoredTheme() || defaultTheme;
@@ -110,6 +111,7 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
110
111
  applyTheme('system');
111
112
  }
112
113
  });
114
+ window[flagKey] = true;
113
115
  } catch (e) {
114
116
  // Older browsers may not support addEventListener on MediaQueryList
115
117
  }
@@ -117,7 +119,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
117
119
  })();
118
120
  `;
119
121
 
120
- // Minify by removing comments, extra whitespace, and newlines
121
122
  return minifyScript(script);
122
123
  }
123
124
 
@@ -27,9 +27,6 @@ import type { UseThemeReturn } from "./types.js";
27
27
  *
28
28
  * Must be used within a ThemeProvider (which is automatically included
29
29
  * in NavigationProvider when theme is enabled in router config).
30
- *
31
- * @returns Theme state and methods
32
- * @throws Error if used outside ThemeProvider
33
30
  */
34
31
  export function useTheme(): UseThemeReturn {
35
32
  const ctx = requireThemeContext();
@@ -1,22 +1,12 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- /**
4
- * Error information passed to error boundary fallback components
5
- */
6
3
  export interface ErrorInfo {
7
- /** Error message (always available) */
8
4
  message: string;
9
- /** Error name/type (e.g., "RouteNotFoundError", "MiddlewareError") */
10
5
  name: string;
11
- /** Optional error code for programmatic handling */
12
6
  code?: string;
13
- /** Stack trace (only in development) */
14
7
  stack?: string;
15
- /** Original error cause if available */
16
8
  cause?: unknown;
17
- /** Segment ID where the error occurred */
18
9
  segmentId: string;
19
- /** Segment type where the error occurred */
20
10
  segmentType:
21
11
  | "layout"
22
12
  | "route"
@@ -46,13 +36,9 @@ export interface ErrorInfo {
46
36
  * ```
47
37
  */
48
38
  export interface ErrorBoundaryFallbackProps {
49
- /** Error information */
50
39
  error: ErrorInfo;
51
40
  }
52
41
 
53
- /**
54
- * Error boundary handler - receives error info and returns fallback UI
55
- */
56
42
  export type ErrorBoundaryHandler = (
57
43
  props: ErrorBoundaryFallbackProps,
58
44
  ) => ReactNode;
@@ -77,17 +63,10 @@ export type ErrorBoundaryHandler = (
77
63
  * ```
78
64
  */
79
65
  export interface ClientErrorBoundaryFallbackProps {
80
- /** Error information */
81
66
  error: ErrorInfo;
82
- /** Function to reset error state and retry rendering */
83
67
  reset: () => void;
84
68
  }
85
69
 
86
- /**
87
- * Wrapped loader data result for deferred resolution with error handling.
88
- * When loaders are deferred to client-side resolution, errors need to be
89
- * wrapped so the client can handle them appropriately.
90
- */
91
70
  export type LoaderDataResult<T = unknown> =
92
71
  | { __loaderResult: true; ok: true; data: T }
93
72
  | {
@@ -97,9 +76,6 @@ export type LoaderDataResult<T = unknown> =
97
76
  fallback: ReactNode | null;
98
77
  };
99
78
 
100
- /**
101
- * Type guard to check if a value is a wrapped loader result
102
- */
103
79
  export function isLoaderDataResult(value: unknown): value is LoaderDataResult {
104
80
  return (
105
81
  typeof value === "object" &&
@@ -109,15 +85,9 @@ export function isLoaderDataResult(value: unknown): value is LoaderDataResult {
109
85
  );
110
86
  }
111
87
 
112
- /**
113
- * Not found information passed to notFound boundary fallback components
114
- */
115
88
  export interface NotFoundInfo {
116
- /** Not found message */
117
89
  message: string;
118
- /** Segment ID where notFound was thrown */
119
90
  segmentId: string;
120
- /** Segment type where notFound was thrown */
121
91
  segmentType:
122
92
  | "layout"
123
93
  | "route"
@@ -125,7 +95,6 @@ export interface NotFoundInfo {
125
95
  | "loader"
126
96
  | "middleware"
127
97
  | "cache";
128
- /** The pathname that triggered the not found */
129
98
  pathname?: string;
130
99
  }
131
100
 
@@ -146,13 +115,9 @@ export interface NotFoundInfo {
146
115
  * ```
147
116
  */
148
117
  export interface NotFoundBoundaryFallbackProps {
149
- /** Not found information */
150
118
  notFound: NotFoundInfo;
151
119
  }
152
120
 
153
- /**
154
- * NotFound boundary handler - receives not found info and returns fallback UI
155
- */
156
121
  export type NotFoundBoundaryHandler = (
157
122
  props: NotFoundBoundaryFallbackProps,
158
123
  ) => ReactNode;