@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.70

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 (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -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 +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -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/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  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 +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -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 +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  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 +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +55 -33
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +743 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1373 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +150 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,364 @@
1
+ import type { ReactNode } from "react";
2
+ import type { Handler } from "../types.js";
3
+ import type {
4
+ AllUseItems,
5
+ RouteItem,
6
+ RouteUseItem,
7
+ UseItems,
8
+ } from "../route-types.js";
9
+ import {
10
+ getContext,
11
+ getUrlPrefix,
12
+ getNamePrefix,
13
+ getRootScoped,
14
+ } from "../server/context";
15
+ import { invariant, DataNotFoundError } from "../errors";
16
+ import { validateUserRouteName } from "../route-name.js";
17
+ import {
18
+ isPrerenderHandler,
19
+ isPassthroughHandler,
20
+ type PrerenderHandlerDefinition,
21
+ } from "../prerender.js";
22
+ import {
23
+ isStaticHandler,
24
+ type StaticHandlerDefinition,
25
+ } from "../static-handler.js";
26
+ import {
27
+ registerSearchSchema,
28
+ registerRouteRootScope,
29
+ } from "../route-map-builder.js";
30
+ import { RESPONSE_TYPE } from "./response-types.js";
31
+ import type { PathOptions } from "./pattern-types.js";
32
+ import type {
33
+ PathFn,
34
+ ResponsePathFn,
35
+ JsonResponsePathFn,
36
+ TextResponsePathFn,
37
+ } from "./path-helper-types.js";
38
+ import {
39
+ resolveHandlerUse,
40
+ mergeHandlerUse,
41
+ } from "../route-definition/resolve-handler-use.js";
42
+
43
+ /**
44
+ * Check if a value is a valid use item
45
+ */
46
+ const isValidUseItem = (item: any): item is AllUseItems | undefined | null => {
47
+ return (
48
+ typeof item === "undefined" ||
49
+ item === null ||
50
+ (item &&
51
+ typeof item === "object" &&
52
+ "type" in item &&
53
+ [
54
+ "layout",
55
+ "route",
56
+ "middleware",
57
+ "revalidate",
58
+ "parallel",
59
+ "intercept",
60
+ "loader",
61
+ "loading",
62
+ "errorBoundary",
63
+ "notFoundBoundary",
64
+ "when",
65
+ "cache",
66
+ "transition",
67
+ "include",
68
+ ].includes(item.type))
69
+ );
70
+ };
71
+
72
+ /**
73
+ * Apply URL prefix to a pattern
74
+ * Handles edge cases like "/" patterns and double slashes
75
+ */
76
+ function applyUrlPrefix(prefix: string, pattern: string): string {
77
+ if (!prefix) return pattern;
78
+ if (pattern === "/") return prefix;
79
+ if (prefix.endsWith("/") && pattern.startsWith("/")) {
80
+ return prefix + pattern.slice(1);
81
+ }
82
+ return prefix + pattern;
83
+ }
84
+
85
+ /**
86
+ * Apply name prefix to a route name
87
+ */
88
+ function applyNamePrefix(prefix: string | undefined, name: string): string {
89
+ if (!prefix) return name;
90
+ return `${prefix}.${name}`;
91
+ }
92
+
93
+ /**
94
+ * Resolve response type from path options (set by path.json(), path.text(), etc.)
95
+ */
96
+ function resolveResponseType(
97
+ options: PathOptions | undefined,
98
+ ): string | undefined {
99
+ return options?.[RESPONSE_TYPE];
100
+ }
101
+
102
+ /**
103
+ * Create path() helper
104
+ *
105
+ * The path() function is the key new feature - it combines URL pattern
106
+ * with handler at the definition site.
107
+ */
108
+ export function createPathHelper<TEnv>(): PathFn<TEnv> {
109
+ return ((
110
+ pattern: string,
111
+ handler: ReactNode | Handler<any, any, TEnv>,
112
+ optionsOrUse?: PathOptions | (() => UseItems<RouteUseItem>),
113
+ maybeUse?: () => UseItems<RouteUseItem>,
114
+ ): RouteItem => {
115
+ const store = getContext();
116
+ const ctx = store.getStore();
117
+ if (!ctx) throw new Error("path() must be called inside urls()");
118
+
119
+ invariant(
120
+ !ctx.parent || ctx.parent.type !== "parallel",
121
+ "path() cannot be used inside parallel()",
122
+ );
123
+
124
+ // Walk the parent chain to prevent path() nested under another path(),
125
+ // even when separated by intermediate layouts (e.g. path(layout(path())))
126
+ {
127
+ let ancestor = ctx.parent;
128
+ while (ancestor) {
129
+ invariant(
130
+ ancestor.type !== "route",
131
+ "path() cannot be nested inside another path()",
132
+ );
133
+ ancestor = ancestor.parent;
134
+ }
135
+ }
136
+
137
+ // Determine options and use based on argument types
138
+ let options: PathOptions | undefined;
139
+ let use: (() => UseItems<RouteUseItem>) | undefined;
140
+
141
+ if (typeof optionsOrUse === "function") {
142
+ // path(pattern, handler, use)
143
+ use = optionsOrUse as () => UseItems<RouteUseItem>;
144
+ } else if (typeof optionsOrUse === "object") {
145
+ // path(pattern, handler, options) or path(pattern, handler, options, use)
146
+ options = optionsOrUse as PathOptions;
147
+ use = maybeUse;
148
+ }
149
+
150
+ // Merge handler.use() defaults with explicit use()
151
+ // Response routes (path.json, path.text, etc.) only allow middleware + cache
152
+ const handlerUseFn = resolveHandlerUse(handler);
153
+ const mountSite = resolveResponseType(options) ? "response" : "path";
154
+ const mergedUse = mergeHandlerUse(handlerUseFn, use, mountSite);
155
+
156
+ // Get prefixes from context (set by include())
157
+ const urlPrefix = getUrlPrefix();
158
+ const namePrefix = getNamePrefix();
159
+
160
+ // Apply URL prefix to pattern
161
+ const prefixedPattern = applyUrlPrefix(urlPrefix, pattern);
162
+
163
+ // Generate route name - use provided name or generate from pattern
164
+ const localName =
165
+ options?.name || `$path_${pattern.replace(/[/:*?]/g, "_")}`;
166
+ if (options?.name) {
167
+ validateUserRouteName(options.name);
168
+ }
169
+ // Apply name prefix if set (from include())
170
+ const routeName = applyNamePrefix(namePrefix, localName);
171
+
172
+ const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
173
+
174
+ // Per-request pruning: skip registration for routes that won't be rendered.
175
+ // forRoute is set by loadManifest() to the matched route name. During
176
+ // evaluateLazyEntry() (route matching), forRoute is unset so all routes
177
+ // register normally. We still increment counters to keep shortCodes stable
178
+ // across different routes (needed for segment reconciliation on navigation).
179
+ //
180
+ // include() does not need its own forRoute pruning. include() creates lazy
181
+ // entries that defer handler execution until route matching. When the lazy
182
+ // handler eventually runs inside loadManifest(), this path() check already
183
+ // covers all routes defined inside the include.
184
+ if (ctx.forRoute && routeName !== ctx.forRoute) {
185
+ store.getShortCode("route");
186
+ return { type: "route" } as RouteItem;
187
+ }
188
+
189
+ // Ensure handler is always a function (wrap ReactNode or extract from prerender/static def)
190
+ // For prerender stubs (production builds where handler code is evicted),
191
+ // handler.handler is undefined — provide a notFound fallback so requests
192
+ // for non-prerendered params get 404 instead of "handler is not a function".
193
+ const wrappedHandler: Handler<any, any, TEnv> =
194
+ typeof handler === "function"
195
+ ? (handler as Handler<any, any, TEnv>)
196
+ : isPassthroughHandler(handler)
197
+ ? typeof handler.prerenderDef.handler === "function"
198
+ ? (handler.prerenderDef.handler as Handler<any, any, TEnv>)
199
+ : () => {
200
+ throw new DataNotFoundError(
201
+ "No prerender data found for this route",
202
+ );
203
+ }
204
+ : isPrerenderHandler(handler)
205
+ ? typeof handler.handler === "function"
206
+ ? (handler.handler as Handler<any, any, TEnv>)
207
+ : () => {
208
+ throw new DataNotFoundError(
209
+ "No prerender data found for this route",
210
+ );
211
+ }
212
+ : isStaticHandler(handler)
213
+ ? (handler.handler as Handler<any, any, TEnv>)
214
+ : () => handler;
215
+
216
+ const entry = {
217
+ id: namespace,
218
+ shortCode: store.getShortCode("route"),
219
+ type: "route" as const,
220
+ parent: ctx.parent,
221
+ handler: wrappedHandler,
222
+ // Store the PREFIXED pattern for route matching
223
+ pattern: prefixedPattern,
224
+ loading: undefined,
225
+ middleware: [],
226
+ revalidate: [],
227
+ errorBoundary: [],
228
+ notFoundBoundary: [],
229
+ layout: [],
230
+ parallel: {},
231
+ intercept: [],
232
+ loader: [],
233
+ ...(urlPrefix ? { mountPath: urlPrefix } : {}),
234
+ ...(isPassthroughHandler(handler)
235
+ ? {
236
+ isPrerender: true as const,
237
+ prerenderDef: handler.prerenderDef as PrerenderHandlerDefinition,
238
+ isPassthrough: true as const,
239
+ liveHandler: handler.liveHandler as Handler<any, any, TEnv>,
240
+ }
241
+ : isPrerenderHandler(handler)
242
+ ? {
243
+ isPrerender: true as const,
244
+ prerenderDef: handler as PrerenderHandlerDefinition,
245
+ }
246
+ : {}),
247
+ ...(isStaticHandler(handler)
248
+ ? {
249
+ isStaticPrerender: true as const,
250
+ ...(handler.$$id ? { staticHandlerId: handler.$$id } : {}),
251
+ }
252
+ : {}),
253
+ ...(resolveResponseType(options)
254
+ ? { responseType: resolveResponseType(options) }
255
+ : {}),
256
+ };
257
+
258
+ // Capture namespace prefix on static handler for build-time reverse() resolution
259
+ if (isStaticHandler(handler) && handler.$$id && ctx.namePrefix) {
260
+ (handler as any).$$routePrefix = ctx.namePrefix;
261
+ }
262
+
263
+ // Check for duplicate route names (TypeScript should catch this, but runtime check too)
264
+ invariant(
265
+ ctx.manifest.get(routeName) === undefined,
266
+ `Duplicate route name: ${routeName} at ${namespace}`,
267
+ );
268
+
269
+ // Register route entry with prefixed name
270
+ ctx.manifest.set(routeName, entry);
271
+
272
+ // Register root-scope flag for dot-local reverse resolution
273
+ registerRouteRootScope(routeName, getRootScoped());
274
+
275
+ // Also store pattern in a separate map for URL generation
276
+ if (ctx.patterns) {
277
+ ctx.patterns.set(routeName, prefixedPattern);
278
+ }
279
+
280
+ // Store pattern grouped by URL prefix for separate entry creation
281
+ if (ctx.patternsByPrefix) {
282
+ const urlPrefix = getUrlPrefix() || "";
283
+ if (!ctx.patternsByPrefix.has(urlPrefix)) {
284
+ ctx.patternsByPrefix.set(urlPrefix, new Map());
285
+ }
286
+ ctx.patternsByPrefix.get(urlPrefix)!.set(routeName, prefixedPattern);
287
+ }
288
+
289
+ // Store trailing slash config if specified
290
+ if (options?.trailingSlash && ctx.trailingSlash) {
291
+ ctx.trailingSlash.set(routeName, options.trailingSlash);
292
+ }
293
+
294
+ // Store search schema if specified
295
+ if (options?.search) {
296
+ if (ctx.searchSchemas) {
297
+ ctx.searchSchemas.set(routeName, options.search);
298
+ }
299
+ registerSearchSchema(routeName, options.search);
300
+ }
301
+
302
+ // Run merged use callback (handler.use defaults + explicit use) if present
303
+ if (mergedUse) {
304
+ const result = store.run(namespace, entry, mergedUse)?.flat(3);
305
+ invariant(
306
+ Array.isArray(result) && result.every((item) => isValidUseItem(item)),
307
+ `path() use() callback must return an array of use items [${namespace}]`,
308
+ );
309
+ return { name: namespace, type: "route", uses: result } as RouteItem;
310
+ }
311
+
312
+ return { name: namespace, type: "route" } as RouteItem;
313
+ }) as PathFn<TEnv>;
314
+ }
315
+
316
+ /**
317
+ * Attach response type tag methods (.json, .text, .html, .xml, .md, .image, .stream, .any) to a path helper.
318
+ * Each tag wraps the original path() call with the RESPONSE_TYPE option set.
319
+ */
320
+ export function attachPathResponseTags<TEnv>(
321
+ pathFn: PathFn<TEnv>,
322
+ ): PathFn<TEnv> & {
323
+ json: JsonResponsePathFn<TEnv>;
324
+ text: TextResponsePathFn<TEnv>;
325
+ html: TextResponsePathFn<TEnv>;
326
+ xml: TextResponsePathFn<TEnv>;
327
+ md: TextResponsePathFn<TEnv>;
328
+ image: ResponsePathFn<TEnv>;
329
+ stream: ResponsePathFn<TEnv>;
330
+ any: ResponsePathFn<TEnv>;
331
+ } {
332
+ function createTagged(responseType: string): ResponsePathFn<TEnv> {
333
+ return ((
334
+ pattern: string,
335
+ handler: any,
336
+ optionsOrUse?: any,
337
+ maybeUse?: any,
338
+ ) => {
339
+ let options: PathOptions;
340
+ let use: (() => any[]) | undefined;
341
+
342
+ if (typeof optionsOrUse === "function") {
343
+ options = { [RESPONSE_TYPE]: responseType };
344
+ use = optionsOrUse;
345
+ } else {
346
+ options = { ...optionsOrUse, [RESPONSE_TYPE]: responseType };
347
+ use = maybeUse;
348
+ }
349
+
350
+ return pathFn(pattern, handler, options, use);
351
+ }) as ResponsePathFn<TEnv>;
352
+ }
353
+
354
+ const extended = pathFn as any;
355
+ extended.json = createTagged("json");
356
+ extended.text = createTagged("text");
357
+ extended.html = createTagged("html");
358
+ extended.xml = createTagged("xml");
359
+ extended.md = createTagged("md");
360
+ extended.image = createTagged("image");
361
+ extended.stream = createTagged("stream");
362
+ extended.any = createTagged("any");
363
+ return extended;
364
+ }
@@ -0,0 +1,107 @@
1
+ import type { ReactNode } from "react";
2
+ import type { Handler, TrailingSlashMode } from "../types.js";
3
+ import type {
4
+ AllUseItems,
5
+ RouteUseItem,
6
+ UrlPatternsBrand,
7
+ } from "../route-types.js";
8
+ import type { SearchSchema } from "../search-params.js";
9
+ import { RESPONSE_TYPE } from "./response-types.js";
10
+ import type { DefaultEnv } from "../types.js";
11
+ import type { PathHelpers } from "./path-helper-types.js";
12
+
13
+ /**
14
+ * Builder function accepted by urls() and as a shorthand for routes()/urls option.
15
+ * When passed directly to routes() or createRouter({ urls }), it is wrapped in urls() automatically.
16
+ */
17
+ export type UrlBuilder<
18
+ TEnv = DefaultEnv,
19
+ TItems extends readonly (AllUseItems | readonly AllUseItems[])[] =
20
+ readonly AllUseItems[],
21
+ > = (helpers: PathHelpers<TEnv>) => TItems;
22
+
23
+ /**
24
+ * Sentinel type for unnamed routes.
25
+ * Using a branded string instead of `never` prevents TypeScript from
26
+ * widening array type inference when mixing named and unnamed routes.
27
+ */
28
+ export type UnnamedRoute = "$unnamed";
29
+
30
+ /**
31
+ * Sentinel type for include() mounts that stay local to the mounted module.
32
+ * This keeps child route names out of the parent/global type map while still
33
+ * allowing the mounted module to use its own local route names internally.
34
+ *
35
+ * Branded with a symbol key so it cannot be accidentally produced by user code.
36
+ */
37
+ declare const LOCAL_ONLY_BRAND: unique symbol;
38
+ export type LocalOnlyInclude = string & { [LOCAL_ONLY_BRAND]: void };
39
+
40
+ /**
41
+ * Options for path() function
42
+ */
43
+ export interface PathOptions<
44
+ TName extends string = string,
45
+ TSearch extends SearchSchema = {},
46
+ > {
47
+ /** Route name for href() lookups */
48
+ name?: TName;
49
+ /** Search param schema for typed query parameters */
50
+ search?: TSearch;
51
+ /** Trailing slash behavior: "never" (redirect /path/ to /path), "always" (redirect /path to /path/), "ignore" (match both) */
52
+ trailingSlash?: TrailingSlashMode;
53
+ /** Response type marker (set by path.json(), etc.) */
54
+ [RESPONSE_TYPE]?: string;
55
+ }
56
+
57
+ /**
58
+ * Internal representation of a URL pattern definition
59
+ */
60
+ export interface PathDefinition {
61
+ pattern: string;
62
+ name?: string;
63
+ handler: ReactNode | Handler<any, any, any>;
64
+ use?: RouteUseItem[];
65
+ }
66
+
67
+ /**
68
+ * Result of urls() - contains the route definitions
69
+ */
70
+ export interface UrlPatterns<
71
+ TEnv = any,
72
+ TRoutes extends Record<string, any> = Record<string, string>,
73
+ TResponses extends Record<string, unknown> = Record<string, unknown>,
74
+ > {
75
+ /** Internal: route definitions */
76
+ readonly definitions: PathDefinition[];
77
+ /** Internal: compiled handler function */
78
+ readonly handler: () => AllUseItems[];
79
+ /** Internal: trailing slash config per route name */
80
+ readonly trailingSlash: Record<string, TrailingSlashMode>;
81
+ /** Brand for type checking */
82
+ readonly [UrlPatternsBrand]: void;
83
+ /** Environment type brand (phantom) */
84
+ readonly _env?: TEnv;
85
+ /** Routes type brand (phantom) - carries route name -> pattern mapping */
86
+ readonly _routes?: TRoutes;
87
+ /** Responses type brand (phantom) - carries route name -> response data type mapping */
88
+ readonly _responses?: TResponses;
89
+ }
90
+
91
+ /**
92
+ * Options for include()
93
+ */
94
+ export interface IncludeOptions<TNamePrefix extends string = string> {
95
+ /**
96
+ * Name prefix for all routes in this pattern set.
97
+ *
98
+ * - `{ name: "blog" }` — children become `blog.index`, `blog.detail`, etc.
99
+ * Visible in generated route types and resolvable globally via `reverse("blog.index")`.
100
+ * - `{ name: "" }` — children merge into the parent namespace with no prefix.
101
+ * Equivalent to defining the routes inline at the include site.
102
+ * - Omitted — children live in a private local scope, hidden from the
103
+ * generated route map and global reverse resolution. Only dot-local
104
+ * reverse (e.g. `reverse(".child")`) works from inside the module.
105
+ */
106
+ name?: TNamePrefix;
107
+ }
@@ -0,0 +1,116 @@
1
+ import type { ContextVar } from "../context-var.js";
2
+ import type { ReverseFunction } from "../reverse.js";
3
+ import type {
4
+ DefaultReverseRouteMap,
5
+ DefaultVars,
6
+ } from "../types/global-namespace.js";
7
+ import type { UseItems, ResponseRouteUseItem } from "../route-types.js";
8
+
9
+ /**
10
+ * Reverse function for response handler contexts.
11
+ * Global names get autocomplete and param validation from the generated route map.
12
+ * Local `.name` calls are accepted but not validated (scope unknown at type level).
13
+ */
14
+ type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
15
+ Record<string, string>,
16
+ ]
17
+ ? (
18
+ name: string,
19
+ params?: Record<string, string>,
20
+ search?: Record<string, unknown>,
21
+ ) => string
22
+ : ReverseFunction<DefaultReverseRouteMap> & {
23
+ (
24
+ name: `.${string}`,
25
+ params?: Record<string, string>,
26
+ search?: Record<string, unknown>,
27
+ ): string;
28
+ };
29
+
30
+ /**
31
+ * Symbol marking a route as a response route (non-RSC).
32
+ * Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
33
+ */
34
+ export const RESPONSE_TYPE: unique symbol = Symbol.for(
35
+ "rangojs.responseType",
36
+ ) as any;
37
+
38
+ /**
39
+ * Handler that must return Response (not ReactNode).
40
+ * Used by path.image(), path.stream(), path.any() (binary/streaming data).
41
+ */
42
+ export type ResponseHandler<TParams = Record<string, string>, TEnv = any> = ((
43
+ ctx: ResponseHandlerContext<TParams, TEnv>,
44
+ ) => Response | Promise<Response>) & {
45
+ /** Composable default DSL items merged when the handler is mounted. */
46
+ use?: () => UseItems<ResponseRouteUseItem>;
47
+ };
48
+
49
+ /**
50
+ * JSON-serializable value type for auto-wrap support.
51
+ */
52
+ export type JsonValue =
53
+ | string
54
+ | number
55
+ | boolean
56
+ | null
57
+ | JsonValue[]
58
+ | { [key: string]: JsonValue };
59
+
60
+ /**
61
+ * Handler for JSON response routes.
62
+ * Can return a plain JSON-serializable value (auto-wrapped) or Response (pass-through).
63
+ */
64
+ export type JsonResponseHandler<
65
+ TParams = Record<string, string>,
66
+ TEnv = any,
67
+ > = ((
68
+ ctx: ResponseHandlerContext<TParams, TEnv>,
69
+ ) => JsonValue | Response | Promise<JsonValue | Response>) & {
70
+ /** Composable default DSL items merged when the handler is mounted. */
71
+ use?: () => UseItems<ResponseRouteUseItem>;
72
+ };
73
+
74
+ /**
75
+ * Handler for text-based response routes (text, html, xml).
76
+ * Can return a string (auto-wrapped) or Response (pass-through).
77
+ */
78
+ export type TextResponseHandler<
79
+ TParams = Record<string, string>,
80
+ TEnv = any,
81
+ > = ((
82
+ ctx: ResponseHandlerContext<TParams, TEnv>,
83
+ ) => string | Response | Promise<string | Response>) & {
84
+ /** Composable default DSL items merged when the handler is mounted. */
85
+ use?: () => UseItems<ResponseRouteUseItem>;
86
+ };
87
+
88
+ /**
89
+ * Lighter handler context for response routes.
90
+ * No ctx.use() (no loaders). Supports setting response headers via ctx.header().
91
+ * Use the standalone cookies() function for cookie mutations.
92
+ */
93
+ export interface ResponseHandlerContext<
94
+ TParams = Record<string, string>,
95
+ TEnv = any,
96
+ > {
97
+ request: Request;
98
+ params: TParams;
99
+ /** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
100
+ readonly _paramCheck?: (params: TParams) => TParams;
101
+ /** Platform bindings (DB, KV, secrets, etc.). */
102
+ env: TEnv;
103
+ /** Query parameters from the URL (system params like `_rsc*` are filtered). */
104
+ searchParams: URLSearchParams;
105
+ /** The full URL object (with system params filtered). */
106
+ url: URL;
107
+ /** The pathname portion of the request URL. */
108
+ pathname: string;
109
+ reverse: ResponseReverseFunction;
110
+ /** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
111
+ get: {
112
+ <T>(contextVar: ContextVar<T>): T | undefined;
113
+ } & (<K extends keyof DefaultVars>(key: K) => DefaultVars[K]);
114
+ /** Set a response header. Merged into the auto-wrapped or pass-through Response. */
115
+ header: (name: string, value: string) => void;
116
+ }