@rangojs/router 0.0.0-experimental.002d056c

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 (305) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +5153 -0
  5. package/package.json +177 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +253 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +638 -0
  43. package/src/browser/navigation-client.ts +261 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +582 -0
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +145 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +128 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +368 -0
  55. package/src/browser/react/NavigationProvider.tsx +413 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +464 -0
  79. package/src/browser/scroll-restoration.ts +397 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +547 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +479 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +982 -0
  105. package/src/cache/cf/index.ts +29 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +44 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +281 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +160 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +397 -0
  172. package/src/router/lazy-includes.ts +236 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +251 -0
  175. package/src/router/manifest.ts +269 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +193 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +749 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +320 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1242 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +291 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1006 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +237 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +920 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +109 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +108 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +48 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +363 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +266 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +445 -0
  298. package/src/vite/router-discovery.ts +777 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Router Manifest Loading
3
+ *
4
+ * Handles lazy loading and validation of route manifests.
5
+ */
6
+
7
+ import { invariant, RouteNotFoundError } from "../errors";
8
+ import { createRouteHelpers } from "../route-definition";
9
+ import {
10
+ getContext,
11
+ runWithPrefixes,
12
+ type EntryData,
13
+ type MetricsStore,
14
+ } from "../server/context";
15
+ import MapRootLayout from "../server/root-layout";
16
+ import type { RouteEntry } from "../types";
17
+ import type { UrlPatterns } from "../urls";
18
+ import { VERSION } from "@rangojs/router:version";
19
+
20
+ // Module-level manifest cache: avoids re-executing DSL handler on every request.
21
+ // Handler execution is deterministic (components, loaders, middleware are module-level
22
+ // stable references), so the resulting EntryData tree can be safely cached and reused
23
+ // across requests within the same isolate.
24
+ //
25
+ // Cache is keyed by (VERSION, mountIndex, routeKey, isSSR). VERSION comes from the
26
+ // @rangojs/router:version virtual module which Vite invalidates on RSC module HMR.
27
+ // When VERSION changes, this module re-evaluates and the cache is recreated empty.
28
+ // Including VERSION in the key is additional defense against stale entries.
29
+ const manifestModuleCache = new Map<string, Map<string, EntryData>>();
30
+
31
+ /**
32
+ * Load manifest from route entry with AsyncLocalStorage context
33
+ * Handles lazy imports, unwrapping, and validation
34
+ *
35
+ * Results are cached at module level after first execution. Subsequent calls
36
+ * for the same (routeKey, isSSR) within the same isolate return cached data
37
+ * without re-executing the DSL handler.
38
+ */
39
+ /**
40
+ * Clear the module-level manifest cache.
41
+ * Called on HMR to ensure stale handler references are discarded.
42
+ */
43
+ export function clearManifestCache(): void {
44
+ manifestModuleCache.clear();
45
+ }
46
+
47
+ export async function loadManifest(
48
+ entry: RouteEntry<any>,
49
+ routeKey: string,
50
+ path: string,
51
+ metricsStore?: MetricsStore,
52
+ isSSR?: boolean,
53
+ ): Promise<EntryData> {
54
+ // Helper to push a metric entry
55
+ const pushMetric = metricsStore
56
+ ? (label: string, start: number) => {
57
+ metricsStore.metrics.push({
58
+ label,
59
+ duration: performance.now() - start,
60
+ startTime: start - metricsStore.requestStart,
61
+ });
62
+ }
63
+ : undefined;
64
+
65
+ const mountIndex = entry.mountIndex;
66
+
67
+ // Check module-level cache (persists across requests within same isolate)
68
+ // Include routerId so multi-router setups (host routing) don't share cached
69
+ // EntryData across routers with overlapping mountIndex + routeKey combinations.
70
+ const cacheKey = `${VERSION}:${entry.routerId ?? ""}:${mountIndex ?? ""}:${routeKey}:${isSSR ? 1 : 0}`;
71
+ const cached = manifestModuleCache.get(cacheKey);
72
+ if (cached) {
73
+ const cacheStart = performance.now();
74
+ // Set up Store for downstream consumers (segment resolution reads Store.manifest)
75
+ const Store = getContext().getOrCreateStore(routeKey);
76
+ Store.mountIndex = mountIndex;
77
+ Store.isSSR = isSSR;
78
+ if (metricsStore) Store.metrics = metricsStore;
79
+ // Restore cached manifest into Store
80
+ for (const [k, v] of cached) {
81
+ Store.manifest.set(k, v);
82
+ }
83
+ pushMetric?.("manifest:cache-hit", cacheStart);
84
+ return cached.get(routeKey)!;
85
+ }
86
+
87
+ const storeSetupStart = performance.now();
88
+ const Store = getContext().getOrCreateStore(routeKey);
89
+
90
+ // Set mount index in store for unique shortCode prefixes
91
+ Store.mountIndex = mountIndex;
92
+
93
+ // Set isSSR flag so loading() can check if we're in SSR
94
+ Store.isSSR = isSSR;
95
+
96
+ // Attach metrics store to context if provided
97
+ if (metricsStore) {
98
+ Store.metrics = metricsStore;
99
+ }
100
+
101
+ pushMetric?.("manifest:store-setup", storeSetupStart);
102
+
103
+ // Clear manifest before rebuilding to prevent stale entry mutations
104
+ const clearStart = performance.now();
105
+ Store.manifest.clear();
106
+ pushMetric?.("manifest:clear", clearStart);
107
+
108
+ try {
109
+ // Include mountIndex in namespace to ensure unique cache keys per mount
110
+ const namespaceWithMount =
111
+ mountIndex !== undefined ? `#router.M${mountIndex}` : "#router";
112
+
113
+ // For lazy entries, use the captured parent from include() context
114
+ // This ensures routes are registered under the correct layout hierarchy
115
+ const lazyContext =
116
+ entry.lazy && entry.lazyPatterns ? entry.lazyContext : null;
117
+ const parentForContext =
118
+ (lazyContext?.parent as EntryData | null) ?? Store.parent;
119
+
120
+ // For lazy entries, merge captured counters from include() so the
121
+ // handler's entries get shortCode indices after sibling entries that
122
+ // were created during pattern extraction. This prevents shortCode
123
+ // collisions between lazy and non-lazy entries under the same parent
124
+ // (e.g., ArticlesLayout and BlogLayout both under NavLayout).
125
+ if (lazyContext && (lazyContext as any).counters) {
126
+ const captured = (lazyContext as any).counters as Record<string, number>;
127
+ for (const [key, value] of Object.entries(captured)) {
128
+ Store.counters[key] = Math.max(Store.counters[key] ?? 0, value);
129
+ }
130
+ }
131
+
132
+ // Propagate cache profiles for DSL-time cache("profileName") resolution.
133
+ // Non-lazy entries carry profiles directly; lazy entries carry them
134
+ // in the captured lazyContext from include() time.
135
+ const entryProfiles =
136
+ entry.cacheProfiles ?? (lazyContext as any)?.cacheProfiles;
137
+ if (entryProfiles) {
138
+ Store.cacheProfiles = entryProfiles;
139
+ }
140
+
141
+ // Propagate rootScoped from lazyContext so that routes inside
142
+ // nested { name: "sub" } under { name: "" } keep inherited root scope
143
+ // when the manifest is rebuilt on each request.
144
+ if (lazyContext && (lazyContext as any).rootScoped !== undefined) {
145
+ Store.rootScoped = (lazyContext as any).rootScoped;
146
+ }
147
+
148
+ const handlerExecStart = performance.now();
149
+ const useItems = await getContext().runWithStore(
150
+ Store,
151
+ Store.namespace || namespaceWithMount,
152
+ parentForContext,
153
+ async () => {
154
+ // Create helpers for lazy-loaded handlers that need them
155
+ const helpers = createRouteHelpers();
156
+
157
+ // For lazy entries, use lazyPatterns.handler() with proper prefixes.
158
+ // Do NOT wrap in MapRootLayout here: the captured parent chain from
159
+ // pattern extraction already includes the synthetic MapRootLayout
160
+ // parent, so adding another would create an extra level that does
161
+ // not exist in the non-lazy (root handler) path and would produce
162
+ // mismatched shortCodes.
163
+ if (entry.lazy && entry.lazyPatterns) {
164
+ const lazyPatterns = entry.lazyPatterns as UrlPatterns<any>;
165
+ const includePrefix = (entry as any)._lazyPrefix || "";
166
+ const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
167
+
168
+ if (fullPrefix || lazyContext?.namePrefix) {
169
+ return runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () =>
170
+ lazyPatterns.handler(),
171
+ );
172
+ }
173
+ return lazyPatterns.handler();
174
+ }
175
+
176
+ // Wrap handler execution in root layout so routes get correct parent
177
+ // This ensures all routes are registered with the layout as their parent
178
+ let promiseResult: Promise<any> | null = null;
179
+ const wrappedItems = helpers.layout(MapRootLayout, () => {
180
+ const result = entry.handler();
181
+ if (result instanceof Promise) {
182
+ // Lazy handler detected - capture promise for async handling
183
+ promiseResult = result;
184
+ return []; // Return empty, we'll discard this wrapped result
185
+ }
186
+ return result;
187
+ });
188
+
189
+ // Handle lazy (Promise-based) handlers
190
+ if (promiseResult !== null) {
191
+ const load = await (promiseResult as Promise<any>);
192
+ if (
193
+ load &&
194
+ load !== null &&
195
+ typeof load === "object" &&
196
+ "default" in load
197
+ ) {
198
+ // Promise<{ default: () => Array }> - e.g., dynamic import
199
+ if (typeof load.default !== "function") {
200
+ throw new Error(
201
+ `[@rangojs/router] Unsupported async handler: { default } must be a function, ` +
202
+ `got ${typeof load.default}. Use () => import('./urls') for lazy loading.`,
203
+ );
204
+ }
205
+ return (load.default as (h?: any) => any)(helpers);
206
+ }
207
+ if (typeof load === "function") {
208
+ // Promise<() => Array>
209
+ return (load as (h?: any) => any)(helpers);
210
+ }
211
+ // Reject unsupported async handler results. Supported shapes are:
212
+ // Promise<{ default: fn }> — dynamic import
213
+ // Promise<fn> — lazy function
214
+ // Direct Promise<Array> is not supported; use a function wrapper.
215
+ throw new Error(
216
+ `[@rangojs/router] Unsupported async handler result (${typeof load}). ` +
217
+ `Lazy route handlers must resolve to a function or { default: fn }, ` +
218
+ `not a direct array. Wrap your handler: () => import('./urls') or ` +
219
+ `() => Promise.resolve((h) => [...])`,
220
+ );
221
+ }
222
+
223
+ // Inline handler - routes were registered with correct parent inside layout
224
+ return [wrappedItems].flat(3);
225
+ },
226
+ );
227
+ pushMetric?.("manifest:handler-exec", handlerExecStart);
228
+
229
+ const validationStart = performance.now();
230
+ invariant(
231
+ useItems && useItems.length > 0,
232
+ "Did not receive any handler from router.map()",
233
+ );
234
+ // For non-lazy entries the root handler is wrapped in MapRootLayout,
235
+ // so the result always contains a layout item. Lazy entries run the
236
+ // included patterns handler directly (no MapRootLayout wrapper) so
237
+ // we skip this check -- the layout is in the captured parent chain.
238
+ if (!lazyContext) {
239
+ invariant(
240
+ useItems.some((item: { type: string }) => item.type === "layout"),
241
+ "Top-level handler must be a layout",
242
+ );
243
+ }
244
+
245
+ invariant(
246
+ Store.manifest.has(routeKey),
247
+ `Route must be registered for ${routeKey}`,
248
+ );
249
+ pushMetric?.("manifest:validation", validationStart);
250
+
251
+ // Cache manifest for future requests in this isolate
252
+ manifestModuleCache.set(cacheKey, new Map(Store.manifest));
253
+
254
+ return Store.manifest.get(routeKey)!;
255
+ } catch (e) {
256
+ throw new RouteNotFoundError(
257
+ `Failed to load route handlers for ${path}: ${(e as Error).message}`,
258
+ {
259
+ cause: {
260
+ error: e,
261
+ state: {
262
+ path,
263
+ routeKey,
264
+ },
265
+ },
266
+ },
267
+ );
268
+ }
269
+ }