@rangojs/router 0.0.0-experimental.77 → 0.0.0-experimental.77ed8945

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 (239) hide show
  1. package/README.md +120 -25
  2. package/dist/bin/rango.js +147 -57
  3. package/dist/vite/index.js +2103 -861
  4. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  5. package/package.json +13 -8
  6. package/skills/api-client/SKILL.md +211 -0
  7. package/skills/breadcrumbs/SKILL.md +3 -1
  8. package/skills/bundle-analysis/SKILL.md +159 -0
  9. package/skills/cache-guide/SKILL.md +220 -30
  10. package/skills/caching/SKILL.md +116 -8
  11. package/skills/composability/SKILL.md +27 -2
  12. package/skills/css/SKILL.md +76 -0
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +3 -1
  15. package/skills/hooks/SKILL.md +229 -20
  16. package/skills/host-router/SKILL.md +66 -20
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +26 -4
  19. package/skills/layout/SKILL.md +6 -7
  20. package/skills/links/SKILL.md +247 -17
  21. package/skills/loader/SKILL.md +219 -9
  22. package/skills/middleware/SKILL.md +47 -12
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +27 -0
  26. package/skills/observability/SKILL.md +137 -0
  27. package/skills/parallel/SKILL.md +12 -6
  28. package/skills/prerender/SKILL.md +14 -33
  29. package/skills/rango/SKILL.md +238 -22
  30. package/skills/react-compiler/SKILL.md +168 -0
  31. package/skills/response-routes/SKILL.md +122 -47
  32. package/skills/route/SKILL.md +33 -4
  33. package/skills/router-setup/SKILL.md +3 -3
  34. package/skills/server-actions/SKILL.md +751 -0
  35. package/skills/streams-and-websockets/SKILL.md +283 -0
  36. package/skills/tailwind/SKILL.md +27 -3
  37. package/skills/typesafety/SKILL.md +319 -27
  38. package/skills/use-cache/SKILL.md +34 -5
  39. package/skills/view-transitions/SKILL.md +294 -0
  40. package/src/__augment-tests__/augment.ts +81 -0
  41. package/src/__augment-tests__/augmented.check.ts +116 -0
  42. package/src/browser/action-coordinator.ts +53 -36
  43. package/src/browser/app-shell.ts +39 -0
  44. package/src/browser/event-controller.ts +86 -70
  45. package/src/browser/history-state.ts +21 -0
  46. package/src/browser/index.ts +3 -3
  47. package/src/browser/navigation-bridge.ts +29 -9
  48. package/src/browser/navigation-client.ts +99 -77
  49. package/src/browser/navigation-store.ts +7 -8
  50. package/src/browser/navigation-transaction.ts +10 -28
  51. package/src/browser/partial-update.ts +60 -40
  52. package/src/browser/prefetch/cache.ts +196 -49
  53. package/src/browser/prefetch/fetch.ts +203 -59
  54. package/src/browser/prefetch/queue.ts +36 -5
  55. package/src/browser/rango-state.ts +37 -13
  56. package/src/browser/react/Link.tsx +18 -13
  57. package/src/browser/react/NavigationProvider.tsx +75 -31
  58. package/src/browser/react/filter-segment-order.ts +51 -7
  59. package/src/browser/react/index.ts +3 -0
  60. package/src/browser/react/location-state-shared.ts +175 -4
  61. package/src/browser/react/location-state.ts +39 -13
  62. package/src/browser/react/use-handle.ts +17 -9
  63. package/src/browser/react/use-navigation.ts +22 -2
  64. package/src/browser/react/use-params.ts +20 -8
  65. package/src/browser/react/use-reverse.ts +106 -0
  66. package/src/browser/react/use-router.ts +23 -2
  67. package/src/browser/react/use-segments.ts +11 -8
  68. package/src/browser/response-adapter.ts +52 -1
  69. package/src/browser/rsc-router.tsx +71 -22
  70. package/src/browser/scroll-restoration.ts +22 -14
  71. package/src/browser/segment-reconciler.ts +10 -14
  72. package/src/browser/segment-structure-assert.ts +2 -2
  73. package/src/browser/server-action-bridge.ts +44 -30
  74. package/src/browser/types.ts +12 -2
  75. package/src/build/collect-fallback-refs.ts +107 -0
  76. package/src/build/generate-manifest.ts +60 -35
  77. package/src/build/generate-route-types.ts +2 -0
  78. package/src/build/index.ts +8 -1
  79. package/src/build/prefix-tree-utils.ts +123 -0
  80. package/src/build/route-trie.ts +45 -1
  81. package/src/build/route-types/codegen.ts +4 -4
  82. package/src/build/route-types/include-resolution.ts +1 -1
  83. package/src/build/route-types/per-module-writer.ts +7 -4
  84. package/src/build/route-types/router-processing.ts +55 -14
  85. package/src/build/route-types/scan-filter.ts +1 -1
  86. package/src/build/route-types/source-scan.ts +118 -0
  87. package/src/build/runtime-discovery.ts +9 -20
  88. package/src/cache/cache-runtime.ts +17 -5
  89. package/src/cache/cache-scope.ts +51 -49
  90. package/src/cache/cf/cf-cache-store.ts +502 -32
  91. package/src/cache/cf/index.ts +3 -0
  92. package/src/cache/handle-snapshot.ts +103 -0
  93. package/src/cache/index.ts +3 -0
  94. package/src/cache/memory-segment-store.ts +3 -2
  95. package/src/cache/types.ts +10 -6
  96. package/src/client.rsc.tsx +3 -0
  97. package/src/client.tsx +96 -205
  98. package/src/context-var.ts +5 -5
  99. package/src/decode-loader-results.ts +36 -0
  100. package/src/errors.ts +30 -4
  101. package/src/handle.ts +4 -6
  102. package/src/host/index.ts +2 -2
  103. package/src/host/router.ts +129 -57
  104. package/src/host/types.ts +31 -2
  105. package/src/host/utils.ts +1 -1
  106. package/src/href-client.ts +140 -21
  107. package/src/index.rsc.ts +10 -6
  108. package/src/index.ts +17 -8
  109. package/src/loader-store.ts +500 -0
  110. package/src/loader.rsc.ts +2 -5
  111. package/src/loader.ts +3 -10
  112. package/src/missing-id-error.ts +68 -0
  113. package/src/outlet-context.ts +1 -1
  114. package/src/prerender/store.ts +9 -7
  115. package/src/prerender.ts +4 -4
  116. package/src/response-utils.ts +37 -0
  117. package/src/reverse.ts +65 -39
  118. package/src/route-content-wrapper.tsx +6 -28
  119. package/src/route-definition/dsl-helpers.ts +253 -265
  120. package/src/route-definition/helper-factories.ts +29 -139
  121. package/src/route-definition/helpers-types.ts +43 -15
  122. package/src/route-definition/resolve-handler-use.ts +6 -0
  123. package/src/route-definition/use-item-types.ts +32 -0
  124. package/src/route-types.ts +26 -41
  125. package/src/router/content-negotiation.ts +15 -2
  126. package/src/router/error-handling.ts +1 -1
  127. package/src/router/find-match.ts +54 -6
  128. package/src/router/handler-context.ts +21 -41
  129. package/src/router/intercept-resolution.ts +4 -18
  130. package/src/router/lazy-includes.ts +41 -22
  131. package/src/router/loader-resolution.ts +82 -36
  132. package/src/router/manifest.ts +41 -19
  133. package/src/router/match-api.ts +4 -3
  134. package/src/router/match-handlers.ts +1 -0
  135. package/src/router/match-middleware/cache-lookup.ts +57 -95
  136. package/src/router/match-middleware/cache-store.ts +3 -2
  137. package/src/router/match-result.ts +53 -32
  138. package/src/router/metrics.ts +1 -1
  139. package/src/router/middleware-types.ts +15 -26
  140. package/src/router/middleware.ts +99 -84
  141. package/src/router/pattern-matching.ts +116 -19
  142. package/src/router/prerender-match.ts +40 -15
  143. package/src/router/preview-match.ts +3 -1
  144. package/src/router/request-classification.ts +40 -37
  145. package/src/router/revalidation.ts +58 -2
  146. package/src/router/router-interfaces.ts +51 -35
  147. package/src/router/router-options.ts +25 -1
  148. package/src/router/router-registry.ts +2 -5
  149. package/src/router/segment-resolution/fresh.ts +27 -6
  150. package/src/router/segment-resolution/revalidation.ts +147 -106
  151. package/src/router/segment-resolution/static-store.ts +19 -5
  152. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  153. package/src/router/substitute-pattern-params.ts +56 -0
  154. package/src/router/trie-matching.ts +40 -16
  155. package/src/router/types.ts +8 -0
  156. package/src/router/url-params.ts +49 -0
  157. package/src/router.ts +37 -25
  158. package/src/rsc/handler-context.ts +2 -2
  159. package/src/rsc/handler.ts +58 -77
  160. package/src/rsc/helpers.ts +72 -43
  161. package/src/rsc/index.ts +1 -1
  162. package/src/rsc/manifest-init.ts +28 -41
  163. package/src/rsc/origin-guard.ts +30 -10
  164. package/src/rsc/progressive-enhancement.ts +4 -0
  165. package/src/rsc/response-error.ts +79 -12
  166. package/src/rsc/response-route-handler.ts +76 -61
  167. package/src/rsc/rsc-rendering.ts +45 -51
  168. package/src/rsc/runtime-warnings.ts +9 -10
  169. package/src/rsc/server-action.ts +33 -39
  170. package/src/rsc/ssr-setup.ts +16 -0
  171. package/src/rsc/types.ts +8 -2
  172. package/src/search-params.ts +4 -4
  173. package/src/segment-content-promise.ts +67 -0
  174. package/src/segment-loader-promise.ts +122 -0
  175. package/src/segment-system.tsx +132 -116
  176. package/src/serialize.ts +243 -0
  177. package/src/server/context.ts +175 -53
  178. package/src/server/cookie-store.ts +28 -4
  179. package/src/server/request-context.ts +57 -51
  180. package/src/ssr/index.tsx +5 -1
  181. package/src/static-handler.ts +1 -1
  182. package/src/types/global-namespace.ts +39 -26
  183. package/src/types/handler-context.ts +68 -50
  184. package/src/types/index.ts +1 -0
  185. package/src/types/loader-types.ts +11 -9
  186. package/src/types/request-scope.ts +126 -0
  187. package/src/types/route-entry.ts +11 -0
  188. package/src/types/segments.ts +35 -2
  189. package/src/urls/include-helper.ts +34 -67
  190. package/src/urls/index.ts +1 -5
  191. package/src/urls/path-helper-types.ts +17 -3
  192. package/src/urls/path-helper.ts +17 -52
  193. package/src/urls/pattern-types.ts +36 -19
  194. package/src/urls/response-types.ts +22 -29
  195. package/src/urls/type-extraction.ts +58 -139
  196. package/src/urls/urls-function.ts +1 -5
  197. package/src/use-loader.tsx +413 -42
  198. package/src/vite/debug.ts +185 -0
  199. package/src/vite/discovery/bundle-postprocess.ts +6 -6
  200. package/src/vite/discovery/discover-routers.ts +106 -75
  201. package/src/vite/discovery/discovery-errors.ts +194 -0
  202. package/src/vite/discovery/gate-state.ts +171 -0
  203. package/src/vite/discovery/prerender-collection.ts +72 -31
  204. package/src/vite/discovery/route-types-writer.ts +40 -84
  205. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  206. package/src/vite/discovery/state.ts +33 -0
  207. package/src/vite/discovery/virtual-module-codegen.ts +13 -23
  208. package/src/vite/index.ts +2 -0
  209. package/src/vite/plugin-types.ts +67 -0
  210. package/src/vite/plugins/cjs-to-esm.ts +8 -7
  211. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  212. package/src/vite/plugins/client-ref-hashing.ts +28 -5
  213. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  214. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  215. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  216. package/src/vite/plugins/expose-action-id.ts +54 -30
  217. package/src/vite/plugins/expose-id-utils.ts +12 -8
  218. package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
  219. package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
  220. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
  221. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  222. package/src/vite/plugins/expose-internal-ids.ts +496 -486
  223. package/src/vite/plugins/performance-tracks.ts +29 -25
  224. package/src/vite/plugins/use-cache-transform.ts +65 -50
  225. package/src/vite/plugins/version-injector.ts +39 -23
  226. package/src/vite/plugins/version-plugin.ts +59 -2
  227. package/src/vite/plugins/virtual-entries.ts +2 -2
  228. package/src/vite/rango.ts +116 -29
  229. package/src/vite/router-discovery.ts +753 -104
  230. package/src/vite/utils/ast-handler-extract.ts +15 -15
  231. package/src/vite/utils/banner.ts +1 -1
  232. package/src/vite/utils/bundle-analysis.ts +4 -2
  233. package/src/vite/utils/client-chunks.ts +190 -0
  234. package/src/vite/utils/forward-user-plugins.ts +193 -0
  235. package/src/vite/utils/manifest-utils.ts +8 -59
  236. package/src/vite/utils/package-resolution.ts +41 -1
  237. package/src/vite/utils/prerender-utils.ts +5 -4
  238. package/src/vite/utils/shared-utils.ts +107 -26
  239. package/src/browser/action-response-classifier.ts +0 -99
@@ -1,10 +1,8 @@
1
1
  import type { AllUseItems, IncludeItem } from "../route-types.js";
2
2
  import {
3
- getContext,
4
- runWithPrefixes,
5
3
  getUrlPrefix,
6
4
  getNamePrefix,
7
- getRootScoped,
5
+ requireDslContext,
8
6
  } from "../server/context";
9
7
  import {
10
8
  INTERNAL_INCLUDE_SCOPE_PREFIX,
@@ -27,28 +25,10 @@ function allocateInternalIncludeScopeId(
27
25
  }
28
26
 
29
27
  /**
30
- * Process an IncludeItem by executing its nested patterns with prefixes
31
- * This expands the include into actual route registrations
32
- */
33
- function processIncludeItem(item: IncludeItem): AllUseItems[] {
34
- const { prefix, patterns } = item;
35
- const namePrefix =
36
- (item as IncludeItem & { _lazyContext?: { namePrefix?: string } })
37
- ._lazyContext?.namePrefix ?? item.options?.name;
38
-
39
- // Execute the nested patterns' handler with URL and name prefixes
40
- // The urlPrefix being set tells nested urls() to skip RootLayout wrapping
41
- return runWithPrefixes(prefix, namePrefix, () => {
42
- // Call the nested patterns' handler - this registers routes with prefixed patterns/names
43
- return (patterns as UrlPatterns).handler();
44
- });
45
- }
46
-
47
- /**
48
- * Recursively process items, expanding any IncludeItems
49
- * Returns items with IncludeItems expanded into actual route items
28
+ * Recursively walk items, recursing into layout children.
50
29
  *
51
- * Lazy includes are kept as-is (not expanded) for the router to handle later.
30
+ * All includes are lazy and kept as-is; the router expands them on the first
31
+ * matching request.
52
32
  */
53
33
  export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
54
34
  const result: AllUseItems[] = [];
@@ -57,26 +37,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
57
37
  if (!item) continue;
58
38
 
59
39
  if (item.type === "include") {
60
- const includeItem = item as IncludeItem & {
61
- _expanded?: AllUseItems[];
62
- lazy?: boolean;
63
- };
64
-
65
- // Lazy includes are NOT expanded here - kept for router to handle
66
- if (includeItem.lazy) {
67
- result.push(item);
68
- continue;
69
- }
70
-
71
- // Eager includes are already expanded during include() call
72
- if (includeItem._expanded) {
73
- // Items were expanded immediately - just process them recursively
74
- result.push(...processItems(includeItem._expanded));
75
- } else {
76
- // Fallback for legacy include items without _expanded
77
- const expanded = processIncludeItem(item as IncludeItem);
78
- result.push(...processItems(expanded));
79
- }
40
+ // All includes are lazy; the router expands them on first matching request.
41
+ result.push(item);
80
42
  } else if (item.type === "layout" && (item as any).uses) {
81
43
  // Process nested items in layout
82
44
  const layoutItem = item as any;
@@ -93,13 +55,9 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
93
55
  /**
94
56
  * Create include() helper for composing URL patterns
95
57
  *
96
- * By default, include() IMMEDIATELY expands the nested patterns. This ensures
97
- * that routes from included patterns inherit the correct parent context
98
- * (the layout they're included in).
99
- *
100
- * With `lazy: true`, patterns are NOT expanded at definition time. Instead,
101
- * they're evaluated on first request that matches the prefix. This improves
102
- * cold start time for apps with many routes.
58
+ * All includes are lazy: the nested patterns are NOT expanded at definition
59
+ * time. Instead they are evaluated on the first request that matches the
60
+ * prefix, which improves cold start time for apps with many routes.
103
61
  */
104
62
  export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
105
63
  return (
@@ -107,9 +65,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
107
65
  patterns: UrlPatterns<TEnv>,
108
66
  options?: IncludeOptions,
109
67
  ): IncludeItem => {
110
- const store = getContext();
111
- const ctx = store.getStore();
112
- if (!ctx) throw new Error("include() must be called inside urls()");
68
+ const { ctx } = requireDslContext("include() must be called inside urls()");
113
69
 
114
70
  const explicitName = options?.name;
115
71
  const hasExplicitName = hasExplicitNameOption(options);
@@ -149,22 +105,32 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
149
105
  });
150
106
  }
151
107
 
152
- // Snapshot parent's counters so lazy manifest generation starts
153
- // at the correct index, preventing shortCode collisions with
154
- // sibling entries (e.g., BlogLayout and ArticlesLayout under NavLayout).
155
- const capturedCounters = { ...ctx.counters };
156
-
157
- // Reserve a layout slot in the parent's counter so sibling lazy includes
158
- // produce different shortCode indices for their root layout.
159
- // Without this, consecutive include() calls capture identical counters
160
- // and their first child layouts get the same shortCode (e.g., both M0L0L0),
161
- // causing the client partial-update diff to see no changes on navigation.
108
+ // Allocate an include-scope token for this include() call. The token is
109
+ // appended to the parent's shortCode prefix whenever the include's
110
+ // direct-descendant shortCodes are generated (see getShortCode in
111
+ // context.ts), partitioning the parent's counter namespace so routes
112
+ // inside an include cannot collide with siblings declared outside it.
113
+ //
114
+ // Scopes compose: a nested include inside an outer include with scope
115
+ // "I0" allocates against the `${parent.shortCode}I0_include` counter
116
+ // and produces scope "I0I0", "I0I1", etc.
117
+ const parentScope = ctx.includeScope ?? "";
118
+ let includeScope = parentScope;
162
119
  if (capturedParent?.shortCode) {
163
- const layoutCounterKey = `${capturedParent.shortCode}_layout`;
164
- ctx.counters[layoutCounterKey] ??= 0;
165
- ctx.counters[layoutCounterKey]++;
120
+ const includeCounterKey = `${capturedParent.shortCode}${parentScope}_include`;
121
+ ctx.counters[includeCounterKey] ??= 0;
122
+ const includeIdx = ctx.counters[includeCounterKey];
123
+ ctx.counters[includeCounterKey] = includeIdx + 1;
124
+ includeScope = `${parentScope}I${includeIdx}`;
166
125
  }
167
126
 
127
+ // Snapshot parent's counters AFTER allocating the include scope so lazy
128
+ // manifest generation starts with the same counter state this include
129
+ // observed — its descendants still get fresh per-scope counters because
130
+ // they key off `${parent.shortCode}${includeScope}_*` (not shared with
131
+ // siblings outside the include).
132
+ const capturedCounters = { ...ctx.counters };
133
+
168
134
  // Compute rootScoped at capture time, mirroring the logic in runWithPrefixes.
169
135
  // This ensures lazy evaluation restores the correct scope state.
170
136
  const parentRootScoped = ctx.rootScoped;
@@ -191,6 +157,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
191
157
  counters: capturedCounters,
192
158
  cacheProfiles: ctx.cacheProfiles,
193
159
  rootScoped: capturedRootScoped,
160
+ includeScope,
194
161
  },
195
162
  } as IncludeItem;
196
163
  };
package/src/urls/index.ts CHANGED
@@ -13,7 +13,6 @@ export type {
13
13
  UnnamedRoute,
14
14
  LocalOnlyInclude,
15
15
  PathOptions,
16
- PathDefinition,
17
16
  UrlPatterns,
18
17
  IncludeOptions,
19
18
  } from "./pattern-types.js";
@@ -22,10 +21,7 @@ export type {
22
21
  export type {
23
22
  ExtractRoutes,
24
23
  ExtractResponses,
25
- ExtractRouteNames,
26
- ExtractPathParams,
27
- ResponseError,
28
- ResponseEnvelope,
24
+ ProblemDetails,
29
25
  RouteResponse,
30
26
  } from "./type-extraction.js";
31
27
 
@@ -114,6 +114,12 @@ export type ResponsePathFn<TEnv> = <
114
114
  * Path function for JSON response routes (path.json()).
115
115
  * Handler can return plain JSON-serializable values or Response.
116
116
  * TData is inferred from the handler's return type (excluding Response/Promise wrappers).
117
+ *
118
+ * Note: a nested Promise in the return (a forgotten await) is caught at runtime
119
+ * by response-route-handler.ts (it throws instead of silently emitting `{}`). A
120
+ * compile-time JsonValue constraint was evaluated and rejected — it breaks
121
+ * interface-typed returns (interfaces lack the index signature JsonValue
122
+ * requires) and preserves literal types in the inferred response shape.
117
123
  */
118
124
  export type JsonResponsePathFn<TEnv> = <
119
125
  const TPattern extends string,
@@ -264,7 +270,7 @@ export type PathHelpers<TEnv> = {
264
270
  * Define an intercepting route for soft navigation
265
271
  * Note: routeName must match a named path() in this urlpatterns
266
272
  */
267
- intercept: keyof RSCRouter.GeneratedRouteMap extends never
273
+ intercept: keyof Rango.GeneratedRouteMap extends never
268
274
  ? (
269
275
  slotName: `@${string}`,
270
276
  routeName: string,
@@ -273,7 +279,7 @@ export type PathHelpers<TEnv> = {
273
279
  ) => InterceptItem
274
280
  : (
275
281
  slotName: `@${string}`,
276
- routeName: (keyof RSCRouter.GeneratedRouteMap & string) | `.${string}`,
282
+ routeName: (keyof Rango.GeneratedRouteMap & string) | `.${string}`,
277
283
  handler: ReactNode | Handler<any, any, TEnv>,
278
284
  use?: () => InterceptUseItem[],
279
285
  ) => InterceptItem;
@@ -350,7 +356,15 @@ export type PathHelpers<TEnv> = {
350
356
  };
351
357
 
352
358
  /**
353
- * Attach a ViewTransition boundary to the current segment or a group of routes
359
+ * Opt a route (or group of routes) into transition-driven navigation.
360
+ *
361
+ * Two independent layers: (1) startTransition, on all React versions, holds
362
+ * the previous content across a same-route nav (no skeleton flash) and is the
363
+ * precondition for any view transition; (2) on experimental React, an
364
+ * additional `<ViewTransition>` boundary cross-fades/morphs the swap. Pass
365
+ * `{ viewTransition: false }` to keep #1 without the router boundary. A view
366
+ * transition cannot fire without a startTransition. See
367
+ * skills/view-transitions for the startTransition x ViewTransition matrix.
354
368
  */
355
369
  transition: {
356
370
  (): TransitionItem;
@@ -1,16 +1,11 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { Handler } from "../types.js";
3
- import type {
4
- AllUseItems,
5
- RouteItem,
6
- RouteUseItem,
7
- UseItems,
8
- } from "../route-types.js";
3
+ import type { RouteItem, RouteUseItem, UseItems } from "../route-types.js";
9
4
  import {
10
- getContext,
11
5
  getUrlPrefix,
12
6
  getNamePrefix,
13
7
  getRootScoped,
8
+ requireDslContext,
14
9
  } from "../server/context";
15
10
  import { invariant, DataNotFoundError } from "../errors";
16
11
  import { validateUserRouteName } from "../route-name.js";
@@ -39,35 +34,10 @@ import {
39
34
  resolveHandlerUse,
40
35
  mergeHandlerUse,
41
36
  } 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
- };
37
+ import {
38
+ emptySegmentBase,
39
+ runAndValidateUseItems,
40
+ } from "../route-definition/dsl-helpers.js";
71
41
 
72
42
  /**
73
43
  * Apply URL prefix to a pattern
@@ -112,9 +82,9 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
112
82
  optionsOrUse?: PathOptions | (() => UseItems<RouteUseItem>),
113
83
  maybeUse?: () => UseItems<RouteUseItem>,
114
84
  ): RouteItem => {
115
- const store = getContext();
116
- const ctx = store.getStore();
117
- if (!ctx) throw new Error("path() must be called inside urls()");
85
+ const { store, ctx } = requireDslContext(
86
+ "path() must be called inside urls()",
87
+ );
118
88
 
119
89
  invariant(
120
90
  !ctx.parent || ctx.parent.type !== "parallel",
@@ -214,6 +184,7 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
214
184
  : () => handler;
215
185
 
216
186
  const entry = {
187
+ ...emptySegmentBase(),
217
188
  id: namespace,
218
189
  shortCode: store.getShortCode("route"),
219
190
  type: "route" as const,
@@ -221,15 +192,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
221
192
  handler: wrappedHandler,
222
193
  // Store the PREFIXED pattern for route matching
223
194
  pattern: prefixedPattern,
224
- loading: undefined,
225
- middleware: [],
226
- revalidate: [],
227
- errorBoundary: [],
228
- notFoundBoundary: [],
229
- layout: [],
230
- parallel: {},
231
- intercept: [],
232
- loader: [],
233
195
  ...(urlPrefix ? { mountPath: urlPrefix } : {}),
234
196
  ...(isPassthroughHandler(handler)
235
197
  ? {
@@ -301,10 +263,13 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
301
263
 
302
264
  // Run merged use callback (handler.use defaults + explicit use) if present
303
265
  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}]`,
266
+ const result = runAndValidateUseItems(
267
+ store,
268
+ namespace,
269
+ entry,
270
+ mergedUse,
271
+ "path",
272
+ "use",
308
273
  );
309
274
  return { name: namespace, type: "route", uses: result } as RouteItem;
310
275
  }
@@ -1,10 +1,5 @@
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";
1
+ import type { TrailingSlashMode } from "../types.js";
2
+ import type { AllUseItems, UrlPatternsBrand } from "../route-types.js";
8
3
  import type { SearchSchema } from "../search-params.js";
9
4
  import { RESPONSE_TYPE } from "./response-types.js";
10
5
  import type { DefaultEnv } from "../types.js";
@@ -54,16 +49,6 @@ export interface PathOptions<
54
49
  [RESPONSE_TYPE]?: string;
55
50
  }
56
51
 
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
52
  /**
68
53
  * Result of urls() - contains the route definitions
69
54
  */
@@ -72,8 +57,6 @@ export interface UrlPatterns<
72
57
  TRoutes extends Record<string, any> = Record<string, string>,
73
58
  TResponses extends Record<string, unknown> = Record<string, unknown>,
74
59
  > {
75
- /** Internal: route definitions */
76
- readonly definitions: PathDefinition[];
77
60
  /** Internal: compiled handler function */
78
61
  readonly handler: () => AllUseItems[];
79
62
  /** Internal: trailing slash config per route name */
@@ -88,6 +71,40 @@ export interface UrlPatterns<
88
71
  readonly _responses?: TResponses;
89
72
  }
90
73
 
74
+ /**
75
+ * Extract the phantom env type carried by a UrlPatterns value.
76
+ */
77
+ export type UrlPatternsEnv<T> =
78
+ T extends UrlPatterns<infer TEnv, any, any> ? TEnv : never;
79
+
80
+ /**
81
+ * Guards `routes()` env compatibility without over-constraining.
82
+ *
83
+ * - An env-agnostic block (its env is `unknown` — e.g. a shared urls() module,
84
+ * or an app that does not augment `Rango.Env`) attaches to any router.
85
+ * - A block carrying a concrete env is accepted only when the router env
86
+ * (`TRouterEnv`) satisfies it; resolves to `never` otherwise, so a
87
+ * `urls<{ DB: D1Database }>()` cannot be mounted on a `createRouter<{}>()`.
88
+ *
89
+ * Use as `patterns: T & EnvCompatible<T, TEnv>` so `T` still infers from the
90
+ * argument — a bare `EnvCompatible<T, TEnv>` parameter sits in a non-inferrable
91
+ * conditional position and would collapse `T` to its constraint.
92
+ *
93
+ * Known limitation: `TRouterEnv extends ...` distributes over a union router env,
94
+ * so a `urls<A>()` block is accepted on `createRouter<A | B>()` even though the
95
+ * `B` arm cannot supply `A`'s env. Suppressing distribution with
96
+ * `[TRouterEnv] extends [...]` would close that edge but breaks the common
97
+ * generic-`TEnv` call sites (a deferred type parameter can't resolve the tuple
98
+ * conditional, so the intersection stops reducing to `T`). A router has one env,
99
+ * so a union env is not a supported pattern; the distributive form is kept.
100
+ */
101
+ export type EnvCompatible<TPatterns, TRouterEnv> =
102
+ unknown extends UrlPatternsEnv<TPatterns>
103
+ ? TPatterns
104
+ : TRouterEnv extends UrlPatternsEnv<TPatterns>
105
+ ? TPatterns
106
+ : never;
107
+
91
108
  /**
92
109
  * Options for include()
93
110
  */
@@ -5,6 +5,7 @@ import type {
5
5
  DefaultVars,
6
6
  } from "../types/global-namespace.js";
7
7
  import type { UseItems, ResponseRouteUseItem } from "../route-types.js";
8
+ import type { RequestScope } from "../types/request-scope.js";
8
9
 
9
10
  /**
10
11
  * Reverse function for response handler contexts.
@@ -31,21 +32,32 @@ type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
31
32
  * Symbol marking a route as a response route (non-RSC).
32
33
  * Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
33
34
  */
34
- export const RESPONSE_TYPE: unique symbol = Symbol.for(
35
- "rangojs.responseType",
36
- ) as any;
35
+ export const RESPONSE_TYPE: unique symbol = Symbol.for("rangojs.responseType");
37
36
 
38
37
  /**
39
- * Handler that must return Response (not ReactNode).
40
- * Used by path.image(), path.stream(), path.any() (binary/streaming data).
38
+ * Shared shape of a response-route handler: a function returning TReturn (or a
39
+ * promise of it), plus an optional composable `use` thunk merged at mount time.
41
40
  */
42
- export type ResponseHandler<TParams = Record<string, string>, TEnv = any> = ((
41
+ type ResponseHandlerOf<
42
+ TReturn,
43
+ TParams = Record<string, string>,
44
+ TEnv = any,
45
+ > = ((
43
46
  ctx: ResponseHandlerContext<TParams, TEnv>,
44
- ) => Response | Promise<Response>) & {
47
+ ) => TReturn | Promise<TReturn>) & {
45
48
  /** Composable default DSL items merged when the handler is mounted. */
46
49
  use?: () => UseItems<ResponseRouteUseItem>;
47
50
  };
48
51
 
52
+ /**
53
+ * Handler that must return Response (not ReactNode).
54
+ * Used by path.image(), path.stream(), path.any() (binary/streaming data).
55
+ */
56
+ export type ResponseHandler<
57
+ TParams = Record<string, string>,
58
+ TEnv = any,
59
+ > = ResponseHandlerOf<Response, TParams, TEnv>;
60
+
49
61
  /**
50
62
  * JSON-serializable value type for auto-wrap support.
51
63
  */
@@ -64,12 +76,7 @@ export type JsonValue =
64
76
  export type JsonResponseHandler<
65
77
  TParams = Record<string, string>,
66
78
  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
- };
79
+ > = ResponseHandlerOf<JsonValue | Response, TParams, TEnv>;
73
80
 
74
81
  /**
75
82
  * Handler for text-based response routes (text, html, xml).
@@ -78,12 +85,7 @@ export type JsonResponseHandler<
78
85
  export type TextResponseHandler<
79
86
  TParams = Record<string, string>,
80
87
  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
- };
88
+ > = ResponseHandlerOf<string | Response, TParams, TEnv>;
87
89
 
88
90
  /**
89
91
  * Lighter handler context for response routes.
@@ -93,19 +95,10 @@ export type TextResponseHandler<
93
95
  export interface ResponseHandlerContext<
94
96
  TParams = Record<string, string>,
95
97
  TEnv = any,
96
- > {
97
- request: Request;
98
+ > extends RequestScope<TEnv> {
98
99
  params: TParams;
99
100
  /** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
100
101
  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
102
  reverse: ResponseReverseFunction;
110
103
  /** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
111
104
  get: {