@rangojs/router 0.0.0-experimental.8 → 0.0.0-experimental.8a4d0430

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 (300) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +4474 -867
  5. package/package.json +60 -51
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +50 -21
  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 +78 -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 +87 -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 +285 -553
  42. package/src/browser/navigation-client.ts +124 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +295 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +258 -308
  47. package/src/browser/prefetch/cache.ts +146 -0
  48. package/src/browser/prefetch/fetch.ts +135 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +42 -0
  51. package/src/browser/prefetch/queue.ts +88 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +185 -73
  54. package/src/browser/react/NavigationProvider.tsx +51 -11
  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 +6 -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 +107 -26
  76. package/src/browser/scroll-restoration.ts +92 -16
  77. package/src/browser/segment-reconciler.ts +216 -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 +109 -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 +469 -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 +338 -0
  100. package/src/cache/cache-scope.ts +120 -303
  101. package/src/cache/cf/cf-cache-store.ts +119 -7
  102. package/src/cache/cf/index.ts +8 -2
  103. package/src/cache/document-cache.ts +101 -72
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +0 -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 +17 -7
  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 +21 -15
  126. package/src/host/errors.ts +8 -8
  127. package/src/host/index.ts +4 -7
  128. package/src/host/pattern-matcher.ts +27 -27
  129. package/src/host/router.ts +61 -39
  130. package/src/host/testing.ts +8 -8
  131. package/src/host/types.ts +15 -7
  132. package/src/host/utils.ts +1 -1
  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 -157
  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 +934 -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 +211 -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 +158 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +395 -0
  162. package/src/router/lazy-includes.ts +234 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +248 -0
  165. package/src/router/manifest.ts +148 -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 +80 -93
  170. package/src/router/match-middleware/cache-lookup.ts +382 -9
  171. package/src/router/match-middleware/cache-store.ts +51 -22
  172. package/src/router/match-middleware/intercept-resolution.ts +55 -17
  173. package/src/router/match-middleware/segment-resolution.ts +24 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +34 -28
  176. package/src/router/metrics.ts +235 -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 +324 -367
  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 +36 -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 +570 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +198 -0
  191. package/src/router/segment-resolution/revalidation.ts +1241 -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 +289 -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 +692 -4257
  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 +235 -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 +25 -13
  219. package/src/server/context.ts +182 -51
  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 +430 -70
  225. package/src/server.ts +35 -130
  226. package/src/ssr/index.tsx +100 -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 +687 -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 +102 -0
  243. package/src/types/segments.ts +148 -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 +110 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -1133
  263. package/src/vite/plugin-types.ts +131 -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 -51
  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 +254 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +510 -0
  282. package/src/vite/router-discovery.ts +785 -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/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,329 @@
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 } from "../errors";
16
+ import { validateUserRouteName } from "../route-name.js";
17
+ import {
18
+ isPrerenderHandler,
19
+ type PrerenderHandlerDefinition,
20
+ } from "../prerender.js";
21
+ import {
22
+ isStaticHandler,
23
+ type StaticHandlerDefinition,
24
+ } from "../static-handler.js";
25
+ import {
26
+ registerSearchSchema,
27
+ registerRouteRootScope,
28
+ } from "../route-map-builder.js";
29
+ import { RESPONSE_TYPE } from "./response-types.js";
30
+ import type { PathOptions } from "./pattern-types.js";
31
+ import type {
32
+ PathFn,
33
+ ResponsePathFn,
34
+ JsonResponsePathFn,
35
+ TextResponsePathFn,
36
+ } from "./path-helper-types.js";
37
+
38
+ /**
39
+ * Check if a value is a valid use item
40
+ */
41
+ const isValidUseItem = (item: any): item is AllUseItems | undefined | null => {
42
+ return (
43
+ typeof item === "undefined" ||
44
+ item === null ||
45
+ (item &&
46
+ typeof item === "object" &&
47
+ "type" in item &&
48
+ [
49
+ "layout",
50
+ "route",
51
+ "middleware",
52
+ "revalidate",
53
+ "parallel",
54
+ "intercept",
55
+ "loader",
56
+ "loading",
57
+ "errorBoundary",
58
+ "notFoundBoundary",
59
+ "when",
60
+ "cache",
61
+ "transition",
62
+ "include",
63
+ ].includes(item.type))
64
+ );
65
+ };
66
+
67
+ /**
68
+ * Apply URL prefix to a pattern
69
+ * Handles edge cases like "/" patterns and double slashes
70
+ */
71
+ function applyUrlPrefix(prefix: string, pattern: string): string {
72
+ if (!prefix) return pattern;
73
+ if (pattern === "/") return prefix;
74
+ if (prefix.endsWith("/") && pattern.startsWith("/")) {
75
+ return prefix + pattern.slice(1);
76
+ }
77
+ return prefix + pattern;
78
+ }
79
+
80
+ /**
81
+ * Apply name prefix to a route name
82
+ */
83
+ function applyNamePrefix(prefix: string | undefined, name: string): string {
84
+ if (!prefix) return name;
85
+ return `${prefix}.${name}`;
86
+ }
87
+
88
+ /**
89
+ * Resolve response type from path options (set by path.json(), path.text(), etc.)
90
+ */
91
+ function resolveResponseType(
92
+ options: PathOptions | undefined,
93
+ ): string | undefined {
94
+ return options?.[RESPONSE_TYPE];
95
+ }
96
+
97
+ /**
98
+ * Create path() helper
99
+ *
100
+ * The path() function is the key new feature - it combines URL pattern
101
+ * with handler at the definition site.
102
+ */
103
+ export function createPathHelper<TEnv>(): PathFn<TEnv> {
104
+ return ((
105
+ pattern: string,
106
+ handler: ReactNode | Handler<any, any, TEnv>,
107
+ optionsOrUse?: PathOptions | (() => UseItems<RouteUseItem>),
108
+ maybeUse?: () => UseItems<RouteUseItem>,
109
+ ): RouteItem => {
110
+ const store = getContext();
111
+ const ctx = store.getStore();
112
+ if (!ctx) throw new Error("path() must be called inside urls()");
113
+
114
+ invariant(
115
+ !ctx.parent || ctx.parent.type !== "parallel",
116
+ "path() cannot be used inside parallel()",
117
+ );
118
+
119
+ // Walk the parent chain to prevent path() nested under another path(),
120
+ // even when separated by intermediate layouts (e.g. path(layout(path())))
121
+ {
122
+ let ancestor = ctx.parent;
123
+ while (ancestor) {
124
+ invariant(
125
+ ancestor.type !== "route",
126
+ "path() cannot be nested inside another path()",
127
+ );
128
+ ancestor = ancestor.parent;
129
+ }
130
+ }
131
+
132
+ // Determine options and use based on argument types
133
+ let options: PathOptions | undefined;
134
+ let use: (() => UseItems<RouteUseItem>) | undefined;
135
+
136
+ if (typeof optionsOrUse === "function") {
137
+ // path(pattern, handler, use)
138
+ use = optionsOrUse as () => UseItems<RouteUseItem>;
139
+ } else if (typeof optionsOrUse === "object") {
140
+ // path(pattern, handler, options) or path(pattern, handler, options, use)
141
+ options = optionsOrUse as PathOptions;
142
+ use = maybeUse;
143
+ }
144
+
145
+ // Get prefixes from context (set by include())
146
+ const urlPrefix = getUrlPrefix();
147
+ const namePrefix = getNamePrefix();
148
+
149
+ // Apply URL prefix to pattern
150
+ const prefixedPattern = applyUrlPrefix(urlPrefix, pattern);
151
+
152
+ // Generate route name - use provided name or generate from pattern
153
+ const localName =
154
+ options?.name || `$path_${pattern.replace(/[/:*?]/g, "_")}`;
155
+ if (options?.name) {
156
+ validateUserRouteName(options.name);
157
+ }
158
+ // Apply name prefix if set (from include())
159
+ const routeName = applyNamePrefix(namePrefix, localName);
160
+
161
+ const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
162
+
163
+ // Per-request pruning: skip registration for routes that won't be rendered.
164
+ // forRoute is set by loadManifest() to the matched route name. During
165
+ // evaluateLazyEntry() (route matching), forRoute is unset so all routes
166
+ // register normally. We still increment counters to keep shortCodes stable
167
+ // across different routes (needed for segment reconciliation on navigation).
168
+ //
169
+ // include() does not need its own forRoute pruning. include() creates lazy
170
+ // entries that defer handler execution until route matching. When the lazy
171
+ // handler eventually runs inside loadManifest(), this path() check already
172
+ // covers all routes defined inside the include.
173
+ if (ctx.forRoute && routeName !== ctx.forRoute) {
174
+ store.getShortCode("route");
175
+ return { type: "route" } as RouteItem;
176
+ }
177
+
178
+ // Ensure handler is always a function (wrap ReactNode or extract from prerender/static def)
179
+ const wrappedHandler: Handler<any, any, TEnv> =
180
+ typeof handler === "function"
181
+ ? (handler as Handler<any, any, TEnv>)
182
+ : isPrerenderHandler(handler)
183
+ ? (handler.handler as Handler<any, any, TEnv>)
184
+ : isStaticHandler(handler)
185
+ ? (handler.handler as Handler<any, any, TEnv>)
186
+ : () => handler;
187
+
188
+ const entry = {
189
+ id: namespace,
190
+ shortCode: store.getShortCode("route"),
191
+ type: "route" as const,
192
+ parent: ctx.parent,
193
+ handler: wrappedHandler,
194
+ // Store the PREFIXED pattern for route matching
195
+ pattern: prefixedPattern,
196
+ loading: undefined,
197
+ middleware: [],
198
+ revalidate: [],
199
+ errorBoundary: [],
200
+ notFoundBoundary: [],
201
+ layout: [],
202
+ parallel: [],
203
+ intercept: [],
204
+ loader: [],
205
+ ...(urlPrefix ? { mountPath: urlPrefix } : {}),
206
+ ...(isPrerenderHandler(handler)
207
+ ? {
208
+ isPrerender: true as const,
209
+ prerenderDef: handler as PrerenderHandlerDefinition,
210
+ }
211
+ : {}),
212
+ ...(isStaticHandler(handler)
213
+ ? {
214
+ isStaticPrerender: true as const,
215
+ ...(handler.$$id ? { staticHandlerId: handler.$$id } : {}),
216
+ }
217
+ : {}),
218
+ ...(resolveResponseType(options)
219
+ ? { responseType: resolveResponseType(options) }
220
+ : {}),
221
+ };
222
+
223
+ // Capture namespace prefix on static handler for build-time reverse() resolution
224
+ if (isStaticHandler(handler) && handler.$$id && ctx.namePrefix) {
225
+ (handler as any).$$routePrefix = ctx.namePrefix;
226
+ }
227
+
228
+ // Check for duplicate route names (TypeScript should catch this, but runtime check too)
229
+ invariant(
230
+ ctx.manifest.get(routeName) === undefined,
231
+ `Duplicate route name: ${routeName} at ${namespace}`,
232
+ );
233
+
234
+ // Register route entry with prefixed name
235
+ ctx.manifest.set(routeName, entry);
236
+
237
+ // Register root-scope flag for dot-local reverse resolution
238
+ registerRouteRootScope(routeName, getRootScoped());
239
+
240
+ // Also store pattern in a separate map for URL generation
241
+ if (ctx.patterns) {
242
+ ctx.patterns.set(routeName, prefixedPattern);
243
+ }
244
+
245
+ // Store pattern grouped by URL prefix for separate entry creation
246
+ if (ctx.patternsByPrefix) {
247
+ const urlPrefix = getUrlPrefix() || "";
248
+ if (!ctx.patternsByPrefix.has(urlPrefix)) {
249
+ ctx.patternsByPrefix.set(urlPrefix, new Map());
250
+ }
251
+ ctx.patternsByPrefix.get(urlPrefix)!.set(routeName, prefixedPattern);
252
+ }
253
+
254
+ // Store trailing slash config if specified
255
+ if (options?.trailingSlash && ctx.trailingSlash) {
256
+ ctx.trailingSlash.set(routeName, options.trailingSlash);
257
+ }
258
+
259
+ // Store search schema if specified
260
+ if (options?.search) {
261
+ if (ctx.searchSchemas) {
262
+ ctx.searchSchemas.set(routeName, options.search);
263
+ }
264
+ registerSearchSchema(routeName, options.search);
265
+ }
266
+
267
+ // Run use callback if provided
268
+ if (use && typeof use === "function") {
269
+ const result = store.run(namespace, entry, use)?.flat(3);
270
+ invariant(
271
+ Array.isArray(result) && result.every((item) => isValidUseItem(item)),
272
+ `path() use() callback must return an array of use items [${namespace}]`,
273
+ );
274
+ return { name: namespace, type: "route", uses: result } as RouteItem;
275
+ }
276
+
277
+ return { name: namespace, type: "route" } as RouteItem;
278
+ }) as PathFn<TEnv>;
279
+ }
280
+
281
+ /**
282
+ * Attach response type tag methods (.json, .text, .html, .xml, .md, .image, .stream, .any) to a path helper.
283
+ * Each tag wraps the original path() call with the RESPONSE_TYPE option set.
284
+ */
285
+ export function attachPathResponseTags<TEnv>(
286
+ pathFn: PathFn<TEnv>,
287
+ ): PathFn<TEnv> & {
288
+ json: JsonResponsePathFn<TEnv>;
289
+ text: TextResponsePathFn<TEnv>;
290
+ html: TextResponsePathFn<TEnv>;
291
+ xml: TextResponsePathFn<TEnv>;
292
+ md: TextResponsePathFn<TEnv>;
293
+ image: ResponsePathFn<TEnv>;
294
+ stream: ResponsePathFn<TEnv>;
295
+ any: ResponsePathFn<TEnv>;
296
+ } {
297
+ function createTagged(responseType: string): ResponsePathFn<TEnv> {
298
+ return ((
299
+ pattern: string,
300
+ handler: any,
301
+ optionsOrUse?: any,
302
+ maybeUse?: any,
303
+ ) => {
304
+ let options: PathOptions;
305
+ let use: (() => any[]) | undefined;
306
+
307
+ if (typeof optionsOrUse === "function") {
308
+ options = { [RESPONSE_TYPE]: responseType };
309
+ use = optionsOrUse;
310
+ } else {
311
+ options = { ...optionsOrUse, [RESPONSE_TYPE]: responseType };
312
+ use = maybeUse;
313
+ }
314
+
315
+ return pathFn(pattern, handler, options, use);
316
+ }) as ResponsePathFn<TEnv>;
317
+ }
318
+
319
+ const extended = pathFn as any;
320
+ extended.json = createTagged("json");
321
+ extended.text = createTagged("text");
322
+ extended.html = createTagged("html");
323
+ extended.xml = createTagged("xml");
324
+ extended.md = createTagged("md");
325
+ extended.image = createTagged("image");
326
+ extended.stream = createTagged("stream");
327
+ extended.any = createTagged("any");
328
+ return extended;
329
+ }
@@ -0,0 +1,95 @@
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
+
11
+ /**
12
+ * Sentinel type for unnamed routes.
13
+ * Using a branded string instead of `never` prevents TypeScript from
14
+ * widening array type inference when mixing named and unnamed routes.
15
+ */
16
+ export type UnnamedRoute = "$unnamed";
17
+
18
+ /**
19
+ * Sentinel type for include() mounts that stay local to the mounted module.
20
+ * This keeps child route names out of the parent/global type map while still
21
+ * allowing the mounted module to use its own local route names internally.
22
+ *
23
+ * Branded with a symbol key so it cannot be accidentally produced by user code.
24
+ */
25
+ declare const LOCAL_ONLY_BRAND: unique symbol;
26
+ export type LocalOnlyInclude = string & { [LOCAL_ONLY_BRAND]: void };
27
+
28
+ /**
29
+ * Options for path() function
30
+ */
31
+ export interface PathOptions<
32
+ TName extends string = string,
33
+ TSearch extends SearchSchema = {},
34
+ > {
35
+ /** Route name for href() lookups */
36
+ name?: TName;
37
+ /** Search param schema for typed query parameters */
38
+ search?: TSearch;
39
+ /** Trailing slash behavior: "never" (redirect /path/ to /path), "always" (redirect /path to /path/), "ignore" (match both) */
40
+ trailingSlash?: TrailingSlashMode;
41
+ /** Response type marker (set by path.json(), etc.) */
42
+ [RESPONSE_TYPE]?: string;
43
+ }
44
+
45
+ /**
46
+ * Internal representation of a URL pattern definition
47
+ */
48
+ export interface PathDefinition {
49
+ pattern: string;
50
+ name?: string;
51
+ handler: ReactNode | Handler<any, any, any>;
52
+ use?: RouteUseItem[];
53
+ }
54
+
55
+ /**
56
+ * Result of urls() - contains the route definitions
57
+ */
58
+ export interface UrlPatterns<
59
+ TEnv = any,
60
+ TRoutes extends Record<string, any> = Record<string, string>,
61
+ TResponses extends Record<string, unknown> = Record<string, unknown>,
62
+ > {
63
+ /** Internal: route definitions */
64
+ readonly definitions: PathDefinition[];
65
+ /** Internal: compiled handler function */
66
+ readonly handler: () => AllUseItems[];
67
+ /** Internal: trailing slash config per route name */
68
+ readonly trailingSlash: Record<string, TrailingSlashMode>;
69
+ /** Brand for type checking */
70
+ readonly [UrlPatternsBrand]: void;
71
+ /** Environment type brand (phantom) */
72
+ readonly _env?: TEnv;
73
+ /** Routes type brand (phantom) - carries route name -> pattern mapping */
74
+ readonly _routes?: TRoutes;
75
+ /** Responses type brand (phantom) - carries route name -> response data type mapping */
76
+ readonly _responses?: TResponses;
77
+ }
78
+
79
+ /**
80
+ * Options for include()
81
+ */
82
+ export interface IncludeOptions<TNamePrefix extends string = string> {
83
+ /**
84
+ * Name prefix for all routes in this pattern set.
85
+ *
86
+ * - `{ name: "blog" }` — children become `blog.index`, `blog.detail`, etc.
87
+ * Visible in generated route types and resolvable globally via `reverse("blog.index")`.
88
+ * - `{ name: "" }` — children merge into the parent namespace with no prefix.
89
+ * Equivalent to defining the routes inline at the include site.
90
+ * - Omitted — children live in a private local scope, hidden from the
91
+ * generated route map and global reverse resolution. Only dot-local
92
+ * reverse (e.g. `reverse(".child")`) works from inside the module.
93
+ */
94
+ name?: TNamePrefix;
95
+ }
@@ -0,0 +1,106 @@
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
+
8
+ /**
9
+ * Reverse function for response handler contexts.
10
+ * Global names get autocomplete and param validation from the generated route map.
11
+ * Local `.name` calls are accepted but not validated (scope unknown at type level).
12
+ */
13
+ type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
14
+ Record<string, string>,
15
+ ]
16
+ ? (
17
+ name: string,
18
+ params?: Record<string, string>,
19
+ search?: Record<string, unknown>,
20
+ ) => string
21
+ : ReverseFunction<DefaultReverseRouteMap> & {
22
+ (
23
+ name: `.${string}`,
24
+ params?: Record<string, string>,
25
+ search?: Record<string, unknown>,
26
+ ): string;
27
+ };
28
+
29
+ /**
30
+ * Symbol marking a route as a response route (non-RSC).
31
+ * Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
32
+ */
33
+ export const RESPONSE_TYPE: unique symbol = Symbol.for(
34
+ "rangojs.responseType",
35
+ ) as any;
36
+
37
+ /**
38
+ * Handler that must return Response (not ReactNode).
39
+ * Used by path.image(), path.stream(), path.any() (binary/streaming data).
40
+ */
41
+ export type ResponseHandler<TParams = Record<string, string>, TEnv = any> = (
42
+ ctx: ResponseHandlerContext<TParams, TEnv>,
43
+ ) => Response | Promise<Response>;
44
+
45
+ /**
46
+ * JSON-serializable value type for auto-wrap support.
47
+ */
48
+ export type JsonValue =
49
+ | string
50
+ | number
51
+ | boolean
52
+ | null
53
+ | JsonValue[]
54
+ | { [key: string]: JsonValue };
55
+
56
+ /**
57
+ * Handler for JSON response routes.
58
+ * Can return a plain JSON-serializable value (auto-wrapped) or Response (pass-through).
59
+ */
60
+ export type JsonResponseHandler<
61
+ TParams = Record<string, string>,
62
+ TEnv = any,
63
+ > = (
64
+ ctx: ResponseHandlerContext<TParams, TEnv>,
65
+ ) => JsonValue | Response | Promise<JsonValue | Response>;
66
+
67
+ /**
68
+ * Handler for text-based response routes (text, html, xml).
69
+ * Can return a string (auto-wrapped) or Response (pass-through).
70
+ */
71
+ export type TextResponseHandler<
72
+ TParams = Record<string, string>,
73
+ TEnv = any,
74
+ > = (
75
+ ctx: ResponseHandlerContext<TParams, TEnv>,
76
+ ) => string | Response | Promise<string | Response>;
77
+
78
+ /**
79
+ * Lighter handler context for response routes.
80
+ * No ctx.use() (no loaders). Supports setting response headers via ctx.header().
81
+ * Use the standalone cookies() function for cookie mutations.
82
+ */
83
+ export interface ResponseHandlerContext<
84
+ TParams = Record<string, string>,
85
+ TEnv = any,
86
+ > {
87
+ request: Request;
88
+ params: TParams;
89
+ /** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
90
+ readonly _paramCheck?: (params: TParams) => TParams;
91
+ /** Platform bindings (DB, KV, secrets, etc.). */
92
+ env: TEnv;
93
+ /** Query parameters from the URL (system params like `_rsc*` are filtered). */
94
+ searchParams: URLSearchParams;
95
+ /** The full URL object (with system params filtered). */
96
+ url: URL;
97
+ /** The pathname portion of the request URL. */
98
+ pathname: string;
99
+ reverse: ResponseReverseFunction;
100
+ /** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
101
+ get: {
102
+ <T>(contextVar: ContextVar<T>): T | undefined;
103
+ } & (<K extends keyof DefaultVars>(key: K) => DefaultVars[K]);
104
+ /** Set a response header. Merged into the auto-wrapped or pass-through Response. */
105
+ header: (name: string, value: string) => void;
106
+ }