@rangojs/router 0.0.0-experimental.5 → 0.0.0-experimental.50

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 (301) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +4567 -769
  5. package/package.json +77 -58
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +85 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +89 -30
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +318 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  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 +92 -64
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/intercept-utils.ts +52 -0
  38. package/src/browser/link-interceptor.ts +24 -4
  39. package/src/browser/logging.ts +55 -0
  40. package/src/browser/merge-segment-loaders.ts +20 -12
  41. package/src/browser/navigation-bridge.ts +282 -557
  42. package/src/browser/navigation-client.ts +157 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +297 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +303 -310
  47. package/src/browser/prefetch/cache.ts +206 -0
  48. package/src/browser/prefetch/fetch.ts +144 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +48 -0
  51. package/src/browser/prefetch/queue.ts +128 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +193 -73
  54. package/src/browser/react/NavigationProvider.tsx +160 -13
  55. package/src/browser/react/context.ts +6 -0
  56. package/src/browser/react/filter-segment-order.ts +11 -0
  57. package/src/browser/react/index.ts +12 -12
  58. package/src/browser/react/location-state-shared.ts +95 -53
  59. package/src/browser/react/location-state.ts +60 -15
  60. package/src/browser/react/mount-context.ts +24 -1
  61. package/src/browser/react/nonce-context.ts +23 -0
  62. package/src/browser/react/shallow-equal.ts +27 -0
  63. package/src/browser/react/use-action.ts +29 -51
  64. package/src/browser/react/use-client-cache.ts +5 -3
  65. package/src/browser/react/use-handle.ts +32 -79
  66. package/src/browser/react/use-href.tsx +2 -2
  67. package/src/browser/react/use-link-status.ts +6 -5
  68. package/src/browser/react/use-navigation.ts +22 -63
  69. package/src/browser/react/use-params.ts +65 -0
  70. package/src/browser/react/use-pathname.ts +47 -0
  71. package/src/browser/react/use-router.ts +63 -0
  72. package/src/browser/react/use-search-params.ts +56 -0
  73. package/src/browser/react/use-segments.ts +80 -97
  74. package/src/browser/response-adapter.ts +73 -0
  75. package/src/browser/rsc-router.tsx +188 -55
  76. package/src/browser/scroll-restoration.ts +117 -44
  77. package/src/browser/segment-reconciler.ts +221 -0
  78. package/src/browser/segment-structure-assert.ts +16 -0
  79. package/src/browser/server-action-bridge.ts +504 -599
  80. package/src/browser/shallow.ts +6 -1
  81. package/src/browser/types.ts +118 -47
  82. package/src/browser/validate-redirect-origin.ts +29 -0
  83. package/src/build/generate-manifest.ts +235 -24
  84. package/src/build/generate-route-types.ts +36 -0
  85. package/src/build/index.ts +13 -0
  86. package/src/build/route-trie.ts +265 -0
  87. package/src/build/route-types/ast-helpers.ts +25 -0
  88. package/src/build/route-types/ast-route-extraction.ts +98 -0
  89. package/src/build/route-types/codegen.ts +102 -0
  90. package/src/build/route-types/include-resolution.ts +411 -0
  91. package/src/build/route-types/param-extraction.ts +48 -0
  92. package/src/build/route-types/per-module-writer.ts +128 -0
  93. package/src/build/route-types/router-processing.ts +479 -0
  94. package/src/build/route-types/scan-filter.ts +78 -0
  95. package/src/build/runtime-discovery.ts +231 -0
  96. package/src/cache/background-task.ts +34 -0
  97. package/src/cache/cache-key-utils.ts +44 -0
  98. package/src/cache/cache-policy.ts +125 -0
  99. package/src/cache/cache-runtime.ts +342 -0
  100. package/src/cache/cache-scope.ts +167 -309
  101. package/src/cache/cf/cf-cache-store.ts +571 -17
  102. package/src/cache/cf/index.ts +13 -3
  103. package/src/cache/document-cache.ts +116 -77
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +1 -15
  107. package/src/cache/memory-segment-store.ts +191 -13
  108. package/src/cache/profile-registry.ts +73 -0
  109. package/src/cache/read-through-swr.ts +134 -0
  110. package/src/cache/segment-codec.ts +256 -0
  111. package/src/cache/taint.ts +98 -0
  112. package/src/cache/types.ts +72 -122
  113. package/src/client.rsc.tsx +3 -1
  114. package/src/client.tsx +106 -126
  115. package/src/component-utils.ts +4 -4
  116. package/src/components/DefaultDocument.tsx +5 -1
  117. package/src/context-var.ts +86 -0
  118. package/src/debug.ts +19 -9
  119. package/src/errors.ts +108 -2
  120. package/src/handle.ts +15 -29
  121. package/src/handles/MetaTags.tsx +73 -20
  122. package/src/handles/breadcrumbs.ts +66 -0
  123. package/src/handles/index.ts +1 -0
  124. package/src/handles/meta.ts +30 -13
  125. package/src/host/cookie-handler.ts +165 -0
  126. package/src/host/errors.ts +97 -0
  127. package/src/host/index.ts +53 -0
  128. package/src/host/pattern-matcher.ts +214 -0
  129. package/src/host/router.ts +352 -0
  130. package/src/host/testing.ts +79 -0
  131. package/src/host/types.ts +146 -0
  132. package/src/host/utils.ts +25 -0
  133. package/src/href-client.ts +119 -29
  134. package/src/index.rsc.ts +153 -19
  135. package/src/index.ts +211 -30
  136. package/src/internal-debug.ts +11 -0
  137. package/src/loader.rsc.ts +26 -147
  138. package/src/loader.ts +27 -10
  139. package/src/network-error-thrower.tsx +3 -1
  140. package/src/outlet-provider.tsx +45 -0
  141. package/src/prerender/param-hash.ts +37 -0
  142. package/src/prerender/store.ts +185 -0
  143. package/src/prerender.ts +463 -0
  144. package/src/reverse.ts +330 -0
  145. package/src/root-error-boundary.tsx +41 -29
  146. package/src/route-content-wrapper.tsx +7 -4
  147. package/src/route-definition/dsl-helpers.ts +959 -0
  148. package/src/route-definition/helper-factories.ts +200 -0
  149. package/src/route-definition/helpers-types.ts +430 -0
  150. package/src/route-definition/index.ts +52 -0
  151. package/src/route-definition/redirect.ts +93 -0
  152. package/src/route-definition.ts +1 -1428
  153. package/src/route-map-builder.ts +217 -123
  154. package/src/route-name.ts +53 -0
  155. package/src/route-types.ts +59 -8
  156. package/src/router/content-negotiation.ts +116 -0
  157. package/src/router/debug-manifest.ts +72 -0
  158. package/src/router/error-handling.ts +9 -9
  159. package/src/router/find-match.ts +160 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +397 -0
  162. package/src/router/lazy-includes.ts +237 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +251 -0
  165. package/src/router/manifest.ts +154 -35
  166. package/src/router/match-api.ts +620 -0
  167. package/src/router/match-context.ts +5 -3
  168. package/src/router/match-handlers.ts +440 -0
  169. package/src/router/match-middleware/background-revalidation.ts +108 -93
  170. package/src/router/match-middleware/cache-lookup.ts +440 -10
  171. package/src/router/match-middleware/cache-store.ts +98 -26
  172. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  173. package/src/router/match-middleware/segment-resolution.ts +27 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +55 -33
  176. package/src/router/metrics.ts +240 -15
  177. package/src/router/middleware-cookies.ts +55 -0
  178. package/src/router/middleware-types.ts +222 -0
  179. package/src/router/middleware.ts +327 -369
  180. package/src/router/pattern-matching.ts +211 -43
  181. package/src/router/prerender-match.ts +402 -0
  182. package/src/router/preview-match.ts +170 -0
  183. package/src/router/revalidation.ts +137 -38
  184. package/src/router/router-context.ts +41 -21
  185. package/src/router/router-interfaces.ts +452 -0
  186. package/src/router/router-options.ts +592 -0
  187. package/src/router/router-registry.ts +24 -0
  188. package/src/router/segment-resolution/fresh.ts +677 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +199 -0
  191. package/src/router/segment-resolution/revalidation.ts +1296 -0
  192. package/src/router/segment-resolution/static-store.ts +67 -0
  193. package/src/router/segment-resolution.ts +21 -0
  194. package/src/router/segment-wrappers.ts +291 -0
  195. package/src/router/telemetry-otel.ts +299 -0
  196. package/src/router/telemetry.ts +300 -0
  197. package/src/router/timeout.ts +148 -0
  198. package/src/router/trie-matching.ts +239 -0
  199. package/src/router/types.ts +77 -3
  200. package/src/router.ts +665 -4182
  201. package/src/rsc/handler-context.ts +45 -0
  202. package/src/rsc/handler.ts +764 -754
  203. package/src/rsc/helpers.ts +140 -6
  204. package/src/rsc/index.ts +0 -20
  205. package/src/rsc/loader-fetch.ts +209 -0
  206. package/src/rsc/manifest-init.ts +86 -0
  207. package/src/rsc/nonce.ts +14 -0
  208. package/src/rsc/origin-guard.ts +141 -0
  209. package/src/rsc/progressive-enhancement.ts +379 -0
  210. package/src/rsc/response-error.ts +37 -0
  211. package/src/rsc/response-route-handler.ts +347 -0
  212. package/src/rsc/rsc-rendering.ts +237 -0
  213. package/src/rsc/runtime-warnings.ts +42 -0
  214. package/src/rsc/server-action.ts +348 -0
  215. package/src/rsc/ssr-setup.ts +128 -0
  216. package/src/rsc/types.ts +38 -11
  217. package/src/search-params.ts +230 -0
  218. package/src/segment-system.tsx +172 -21
  219. package/src/server/context.ts +266 -58
  220. package/src/server/cookie-store.ts +190 -0
  221. package/src/server/fetchable-loader-store.ts +37 -0
  222. package/src/server/handle-store.ts +94 -15
  223. package/src/server/loader-registry.ts +15 -56
  224. package/src/server/request-context.ts +439 -73
  225. package/src/server.ts +35 -128
  226. package/src/ssr/index.tsx +101 -31
  227. package/src/static-handler.ts +114 -0
  228. package/src/theme/ThemeProvider.tsx +21 -15
  229. package/src/theme/ThemeScript.tsx +5 -5
  230. package/src/theme/constants.ts +5 -2
  231. package/src/theme/index.ts +4 -14
  232. package/src/theme/theme-context.ts +4 -30
  233. package/src/theme/theme-script.ts +21 -18
  234. package/src/types/boundaries.ts +158 -0
  235. package/src/types/cache-types.ts +198 -0
  236. package/src/types/error-types.ts +192 -0
  237. package/src/types/global-namespace.ts +100 -0
  238. package/src/types/handler-context.ts +773 -0
  239. package/src/types/index.ts +88 -0
  240. package/src/types/loader-types.ts +183 -0
  241. package/src/types/route-config.ts +170 -0
  242. package/src/types/route-entry.ts +109 -0
  243. package/src/types/segments.ts +150 -0
  244. package/src/types.ts +1 -1623
  245. package/src/urls/include-helper.ts +197 -0
  246. package/src/urls/index.ts +53 -0
  247. package/src/urls/path-helper-types.ts +339 -0
  248. package/src/urls/path-helper.ts +329 -0
  249. package/src/urls/pattern-types.ts +95 -0
  250. package/src/urls/response-types.ts +106 -0
  251. package/src/urls/type-extraction.ts +372 -0
  252. package/src/urls/urls-function.ts +98 -0
  253. package/src/urls.ts +1 -802
  254. package/src/use-loader.tsx +85 -77
  255. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  256. package/src/vite/discovery/discover-routers.ts +344 -0
  257. package/src/vite/discovery/prerender-collection.ts +385 -0
  258. package/src/vite/discovery/route-types-writer.ts +258 -0
  259. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  260. package/src/vite/discovery/state.ts +108 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -782
  263. package/src/vite/plugin-types.ts +48 -0
  264. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  265. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  266. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  267. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  268. package/src/vite/plugins/expose-id-utils.ts +287 -0
  269. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  270. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  271. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  272. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  273. package/src/vite/plugins/expose-ids/types.ts +45 -0
  274. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  275. package/src/vite/plugins/refresh-cmd.ts +65 -0
  276. package/src/vite/plugins/use-cache-transform.ts +323 -0
  277. package/src/vite/plugins/version-injector.ts +83 -0
  278. package/src/vite/plugins/version-plugin.ts +266 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +27 -16
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +445 -0
  282. package/src/vite/router-discovery.ts +777 -0
  283. package/src/vite/utils/ast-handler-extract.ts +517 -0
  284. package/src/vite/utils/banner.ts +36 -0
  285. package/src/vite/utils/bundle-analysis.ts +137 -0
  286. package/src/vite/utils/manifest-utils.ts +70 -0
  287. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  288. package/src/vite/utils/prerender-utils.ts +189 -0
  289. package/src/vite/utils/shared-utils.ts +169 -0
  290. package/CLAUDE.md +0 -43
  291. package/src/browser/lru-cache.ts +0 -69
  292. package/src/browser/request-controller.ts +0 -164
  293. package/src/cache/memory-store.ts +0 -253
  294. package/src/href-context.ts +0 -33
  295. package/src/href.ts +0 -255
  296. package/src/server/route-manifest-cache.ts +0 -173
  297. package/src/vite/expose-handle-id.ts +0 -209
  298. package/src/vite/expose-loader-id.ts +0 -426
  299. package/src/vite/expose-location-state-id.ts +0 -177
  300. package/src/warmup/connection-warmup.tsx +0 -94
  301. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,440 @@
1
+ import type { ReactNode } from "react";
2
+ import { sanitizeError } from "../errors";
3
+ import type { ErrorInfo, ErrorPhase, MatchResult } from "../types";
4
+ import type {
5
+ EntryData,
6
+ InterceptEntry,
7
+ InterceptSelectorContext,
8
+ } from "../server/context";
9
+ import type { MatchApiDeps } from "./types.js";
10
+ import type { RouterContext } from "./router-context.js";
11
+ import { runWithRouterContext } from "./router-context.js";
12
+ import {
13
+ type ActionContext,
14
+ type MatchContext,
15
+ createPipelineState,
16
+ } from "./match-context.js";
17
+ import { createMatchPartialPipeline } from "./match-pipelines.js";
18
+ import { collectMatchResult } from "./match-result.js";
19
+ import {
20
+ createMatchContextForFull as _createMatchContextForFull,
21
+ createMatchContextForPartial as _createMatchContextForPartial,
22
+ matchError as _matchError,
23
+ } from "./match-api.js";
24
+ import { previewMatch as _previewMatch } from "./preview-match.js";
25
+ import {
26
+ runWithRouterLogContext,
27
+ withRouterLogScope,
28
+ isRouterDebugEnabled,
29
+ startRevalidationTrace,
30
+ flushRevalidationTrace,
31
+ } from "./logging.js";
32
+ import type { ErrorBoundaryHandler, NotFoundBoundaryHandler } from "../types";
33
+ import type { MiddlewareFn } from "./middleware.js";
34
+ import {
35
+ type TelemetrySink,
36
+ safeEmit,
37
+ resolveSink,
38
+ getRequestId,
39
+ } from "./telemetry.js";
40
+
41
+ export interface MatchHandlerDeps<TEnv = any> {
42
+ buildRouterContext: () => RouterContext<TEnv>;
43
+ callOnError: (error: unknown, phase: ErrorPhase, context: any) => void;
44
+ matchApiDeps: MatchApiDeps<TEnv>;
45
+ defaultErrorBoundary: ReactNode | ErrorBoundaryHandler | undefined;
46
+ findMatch: (pathname: string, ms?: any) => any;
47
+ findInterceptForRoute: (
48
+ routeKey: string,
49
+ parentEntry: EntryData | null,
50
+ selectorContext: InterceptSelectorContext | null,
51
+ isAction: boolean,
52
+ ) => { intercept: InterceptEntry; entry: EntryData } | null;
53
+ telemetry?: TelemetrySink;
54
+ }
55
+
56
+ export interface MatchHandlers<TEnv = any> {
57
+ match: (request: Request, env: TEnv) => Promise<MatchResult>;
58
+ matchPartial: (
59
+ request: Request,
60
+ context: TEnv,
61
+ actionContext?: ActionContext,
62
+ ) => Promise<MatchResult | null>;
63
+ matchError: (
64
+ request: Request,
65
+ _context: TEnv,
66
+ error: unknown,
67
+ segmentType?: ErrorInfo["segmentType"],
68
+ ) => Promise<MatchResult | null>;
69
+ previewMatch: (
70
+ request: Request,
71
+ _context: TEnv,
72
+ ) => Promise<{
73
+ routeMiddleware?: Array<{
74
+ handler: MiddlewareFn;
75
+ params: Record<string, string>;
76
+ }>;
77
+ responseType?: string;
78
+ handler?: Function;
79
+ params?: Record<string, string>;
80
+ negotiated?: boolean;
81
+ manifestEntry?: EntryData;
82
+ } | null>;
83
+ createMatchContextForFull: (
84
+ request: Request,
85
+ env: TEnv,
86
+ ) => Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }>;
87
+ createMatchContextForPartial: (
88
+ request: Request,
89
+ env: TEnv,
90
+ actionContext?: {
91
+ actionId?: string;
92
+ actionUrl?: URL;
93
+ actionResult?: any;
94
+ formData?: FormData;
95
+ },
96
+ ) => Promise<MatchContext<TEnv> | null>;
97
+ }
98
+
99
+ /**
100
+ * Create match handler functions bound to router closure state.
101
+ * These are the main request-handling entry points for SSR, navigation,
102
+ * error recovery, and preview matching.
103
+ */
104
+ export function createMatchHandlers<TEnv = any>(
105
+ deps: MatchHandlerDeps<TEnv>,
106
+ ): MatchHandlers<TEnv> {
107
+ const {
108
+ buildRouterContext,
109
+ callOnError,
110
+ matchApiDeps,
111
+ defaultErrorBoundary,
112
+ findInterceptForRoute,
113
+ } = deps;
114
+ const hasTelemetry = !!deps.telemetry;
115
+ const telemetry = resolveSink(deps.telemetry);
116
+
117
+ async function createMatchContextForFull(
118
+ request: Request,
119
+ env: TEnv,
120
+ ): Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }> {
121
+ return _createMatchContextForFull(
122
+ request,
123
+ env,
124
+ matchApiDeps,
125
+ findInterceptForRoute,
126
+ );
127
+ }
128
+
129
+ async function createMatchContextForPartial(
130
+ request: Request,
131
+ env: TEnv,
132
+ actionContext?: {
133
+ actionId?: string;
134
+ actionUrl?: URL;
135
+ actionResult?: any;
136
+ formData?: FormData;
137
+ },
138
+ ): Promise<MatchContext<TEnv> | null> {
139
+ return _createMatchContextForPartial(
140
+ request,
141
+ env,
142
+ matchApiDeps,
143
+ findInterceptForRoute,
144
+ actionContext,
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Match request and return segments (document/SSR requests)
150
+ *
151
+ * Uses generator middleware pipeline for clean separation of concerns:
152
+ * - cache-lookup: Check cache first
153
+ * - segment-resolution: Resolve segments on cache miss
154
+ * - cache-store: Store results in cache
155
+ * - background-revalidation: SWR revalidation
156
+ */
157
+ async function match(request: Request, env: TEnv): Promise<MatchResult> {
158
+ const requestId = hasTelemetry ? getRequestId(request) : undefined;
159
+ return runWithRouterLogContext({ request, transaction: "match" }, () => {
160
+ const routerCtx = buildRouterContext();
161
+ routerCtx.requestId = requestId;
162
+ return runWithRouterContext(routerCtx, async () =>
163
+ withRouterLogScope("match", async () => {
164
+ const matchStart = performance.now();
165
+ const pathname = new URL(request.url).pathname;
166
+ if (hasTelemetry) {
167
+ safeEmit(telemetry, {
168
+ type: "request.start",
169
+ timestamp: matchStart,
170
+ requestId,
171
+ method: request.method,
172
+ pathname,
173
+ transaction: "match",
174
+ isPartial: false,
175
+ });
176
+ }
177
+
178
+ const result = await createMatchContextForFull(request, env);
179
+
180
+ // Handle redirect case
181
+ if ("type" in result && result.type === "redirect") {
182
+ if (hasTelemetry) {
183
+ safeEmit(telemetry, {
184
+ type: "request.end",
185
+ timestamp: performance.now(),
186
+ requestId,
187
+ method: request.method,
188
+ pathname,
189
+ transaction: "match",
190
+ durationMs: performance.now() - matchStart,
191
+ segmentCount: 0,
192
+ cacheHit: false,
193
+ });
194
+ }
195
+ return {
196
+ segments: [],
197
+ matched: [],
198
+ diff: [],
199
+ params: {},
200
+ redirect: result.redirectUrl,
201
+ };
202
+ }
203
+
204
+ const ctx = result as MatchContext<TEnv>;
205
+
206
+ try {
207
+ const state = createPipelineState();
208
+ const pipeline = createMatchPartialPipeline(ctx, state);
209
+ const matchResult = await collectMatchResult(pipeline, ctx, state);
210
+ if (hasTelemetry) {
211
+ safeEmit(telemetry, {
212
+ type: "cache.decision",
213
+ timestamp: performance.now(),
214
+ requestId,
215
+ pathname,
216
+ routeKey: ctx.routeKey,
217
+ hit: state.cacheHit,
218
+ shouldRevalidate: !!state.shouldRevalidate,
219
+ source: state.cacheSource,
220
+ });
221
+ safeEmit(telemetry, {
222
+ type: "request.end",
223
+ timestamp: performance.now(),
224
+ requestId,
225
+ method: request.method,
226
+ pathname,
227
+ transaction: "match",
228
+ durationMs: performance.now() - matchStart,
229
+ segmentCount: matchResult.segments.length,
230
+ cacheHit: state.cacheHit,
231
+ });
232
+ }
233
+ return matchResult;
234
+ } catch (error) {
235
+ if (hasTelemetry) {
236
+ const errorObj =
237
+ error instanceof Error ? error : new Error(String(error));
238
+ safeEmit(telemetry, {
239
+ type: "request.error",
240
+ timestamp: performance.now(),
241
+ requestId,
242
+ method: request.method,
243
+ pathname,
244
+ transaction: "match",
245
+ error: errorObj,
246
+ phase: error instanceof Response ? "redirect" : "routing",
247
+ durationMs: performance.now() - matchStart,
248
+ });
249
+ }
250
+ if (error instanceof Response) throw error;
251
+ // Report unhandled errors during full match pipeline
252
+ callOnError(error, "routing", {
253
+ request,
254
+ url: ctx.url,
255
+ env,
256
+ isPartial: false,
257
+ handledByBoundary: false,
258
+ });
259
+ throw sanitizeError(error);
260
+ }
261
+ }),
262
+ );
263
+ });
264
+ }
265
+
266
+ async function matchError(
267
+ request: Request,
268
+ _context: TEnv,
269
+ error: unknown,
270
+ segmentType: ErrorInfo["segmentType"] = "route",
271
+ ): Promise<MatchResult | null> {
272
+ return runWithRouterLogContext({ request, transaction: "matchError" }, () =>
273
+ withRouterLogScope("matchError", () =>
274
+ _matchError(
275
+ request,
276
+ _context,
277
+ error,
278
+ matchApiDeps,
279
+ defaultErrorBoundary,
280
+ segmentType,
281
+ ),
282
+ ),
283
+ );
284
+ }
285
+
286
+ /**
287
+ * Match partial request with revalidation
288
+ *
289
+ * Uses generator middleware pipeline for clean separation of concerns:
290
+ * - cache-lookup: Check cache first
291
+ * - segment-resolution: Resolve segments on cache miss
292
+ * - intercept-resolution: Handle intercept routes
293
+ * - cache-store: Store results in cache
294
+ * - background-revalidation: SWR revalidation
295
+ */
296
+ async function matchPartial(
297
+ request: Request,
298
+ context: TEnv,
299
+ actionContext?: ActionContext,
300
+ ): Promise<MatchResult | null> {
301
+ const partialRequestId = hasTelemetry ? getRequestId(request) : undefined;
302
+ return runWithRouterLogContext(
303
+ { request, transaction: "matchPartial" },
304
+ () => {
305
+ const routerCtx = buildRouterContext();
306
+ routerCtx.requestId = partialRequestId;
307
+ return runWithRouterContext(routerCtx, async () =>
308
+ withRouterLogScope("matchPartial", async () => {
309
+ const matchStart = performance.now();
310
+ const pathname = new URL(request.url).pathname;
311
+ if (hasTelemetry) {
312
+ safeEmit(telemetry, {
313
+ type: "request.start",
314
+ timestamp: matchStart,
315
+ requestId: partialRequestId,
316
+ method: request.method,
317
+ pathname,
318
+ transaction: "matchPartial",
319
+ isPartial: true,
320
+ });
321
+ }
322
+
323
+ const ctx = await createMatchContextForPartial(
324
+ request,
325
+ context,
326
+ actionContext,
327
+ );
328
+ if (!ctx) {
329
+ if (hasTelemetry) {
330
+ safeEmit(telemetry, {
331
+ type: "request.end",
332
+ timestamp: performance.now(),
333
+ requestId: partialRequestId,
334
+ method: request.method,
335
+ pathname,
336
+ transaction: "matchPartial",
337
+ durationMs: performance.now() - matchStart,
338
+ segmentCount: 0,
339
+ cacheHit: false,
340
+ });
341
+ }
342
+ return null;
343
+ }
344
+
345
+ if (isRouterDebugEnabled()) {
346
+ startRevalidationTrace({
347
+ method: request.method,
348
+ prevUrl: ctx.prevUrl.href,
349
+ nextUrl: ctx.url.href,
350
+ routeKey: ctx.routeKey,
351
+ isAction: !!actionContext,
352
+ stale: ctx.stale || undefined,
353
+ });
354
+ }
355
+
356
+ try {
357
+ const state = createPipelineState();
358
+ const pipeline = createMatchPartialPipeline(ctx, state);
359
+ const matchResult = await collectMatchResult(
360
+ pipeline,
361
+ ctx,
362
+ state,
363
+ );
364
+ flushRevalidationTrace();
365
+ if (hasTelemetry) {
366
+ safeEmit(telemetry, {
367
+ type: "cache.decision",
368
+ timestamp: performance.now(),
369
+ requestId: partialRequestId,
370
+ pathname,
371
+ routeKey: ctx.routeKey,
372
+ hit: state.cacheHit,
373
+ shouldRevalidate: !!state.shouldRevalidate,
374
+ source: state.cacheSource,
375
+ });
376
+ safeEmit(telemetry, {
377
+ type: "request.end",
378
+ timestamp: performance.now(),
379
+ requestId: partialRequestId,
380
+ method: request.method,
381
+ pathname,
382
+ transaction: "matchPartial",
383
+ durationMs: performance.now() - matchStart,
384
+ segmentCount: matchResult.segments.length,
385
+ cacheHit: state.cacheHit,
386
+ });
387
+ }
388
+ return matchResult;
389
+ } catch (error) {
390
+ flushRevalidationTrace();
391
+ if (hasTelemetry) {
392
+ const errorObj =
393
+ error instanceof Error ? error : new Error(String(error));
394
+ const phase = actionContext ? "action" : "revalidation";
395
+ safeEmit(telemetry, {
396
+ type: "request.error",
397
+ timestamp: performance.now(),
398
+ requestId: partialRequestId,
399
+ method: request.method,
400
+ pathname,
401
+ transaction: "matchPartial",
402
+ error: errorObj,
403
+ phase: error instanceof Response ? "redirect" : phase,
404
+ durationMs: performance.now() - matchStart,
405
+ });
406
+ }
407
+ if (error instanceof Response) throw error;
408
+ // Report unhandled errors during partial match pipeline
409
+ callOnError(error, actionContext ? "action" : "revalidation", {
410
+ request,
411
+ url: ctx.url,
412
+ env: context,
413
+ actionId: actionContext?.actionId,
414
+ isPartial: true,
415
+ handledByBoundary: false,
416
+ });
417
+ throw sanitizeError(error);
418
+ }
419
+ }),
420
+ );
421
+ },
422
+ );
423
+ }
424
+
425
+ async function previewMatch(
426
+ request: Request,
427
+ _context: TEnv,
428
+ ): ReturnType<typeof _previewMatch> {
429
+ return _previewMatch(request, _context, { findMatch: deps.findMatch });
430
+ }
431
+
432
+ return {
433
+ match: match,
434
+ matchPartial: matchPartial,
435
+ matchError: matchError,
436
+ previewMatch: previewMatch,
437
+ createMatchContextForFull: createMatchContextForFull,
438
+ createMatchContextForPartial: createMatchContextForPartial,
439
+ };
440
+ }