@rangojs/router 0.0.0-experimental.0f44aca1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +5214 -0
  5. package/package.json +176 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +220 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +645 -0
  43. package/src/browser/navigation-client.ts +215 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +295 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +550 -0
  48. package/src/browser/prefetch/cache.ts +146 -0
  49. package/src/browser/prefetch/fetch.ts +135 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +42 -0
  52. package/src/browser/prefetch/queue.ts +88 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +360 -0
  55. package/src/browser/react/NavigationProvider.tsx +386 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +431 -0
  79. package/src/browser/scroll-restoration.ts +400 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +538 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +469 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +540 -0
  105. package/src/cache/cf/index.ts +25 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +43 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +275 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +158 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +395 -0
  172. package/src/router/lazy-includes.ts +234 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +248 -0
  175. package/src/router/manifest.ts +267 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +192 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +748 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +316 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1239 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +289 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1002 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +235 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +914 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +102 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +110 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +131 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +365 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +254 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +510 -0
  298. package/src/vite/router-discovery.ts +785 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,231 @@
1
+ import { dirname, join, basename, resolve } from "node:path";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import {
4
+ generateRouteTypesSource,
5
+ buildCombinedRouteMapForRouterFile,
6
+ } from "./generate-route-types.ts";
7
+ import { isAutoGeneratedRouteName } from "../route-name.js";
8
+
9
+ export interface RuntimeDiscoveryOptions {
10
+ /** Project root directory (where package.json / node_modules live). */
11
+ root: string;
12
+ /** Path to vite.config.ts. Auto-detected from root if omitted. */
13
+ configFile?: string;
14
+ /** Absolute path to the router entry file. */
15
+ entry: string;
16
+ /** Override resolve.alias (skips loading from vite config when provided). */
17
+ resolveAlias?: Record<string, string>;
18
+ }
19
+
20
+ /**
21
+ * Standalone Vite-based route discovery for the CLI.
22
+ * Creates a temporary Vite server with the RSC plugin, imports the entry via
23
+ * the module runner, reads the RouterRegistry, generates manifests, and writes
24
+ * named-routes.gen.ts files.
25
+ *
26
+ * This mirrors the logic in the Vite plugin's discoverRouters() + writeRouteTypesFiles()
27
+ * but without coupling to plugin closure state.
28
+ */
29
+ export async function discoverAndWriteRouteTypes(
30
+ opts: RuntimeDiscoveryOptions,
31
+ ): Promise<{
32
+ routerCount: number;
33
+ routeCount: number;
34
+ outputFiles: string[];
35
+ }> {
36
+ let createViteServer: typeof import("vite").createServer;
37
+ let loadConfigFromFile: typeof import("vite").loadConfigFromFile;
38
+ let rsc: any;
39
+
40
+ try {
41
+ const vite = await import("vite");
42
+ createViteServer = vite.createServer;
43
+ loadConfigFromFile = vite.loadConfigFromFile;
44
+ } catch {
45
+ throw new Error(
46
+ "Runtime discovery requires 'vite'. Install it with: pnpm add -D vite",
47
+ );
48
+ }
49
+
50
+ try {
51
+ const rscMod = await import("@vitejs/plugin-rsc");
52
+ rsc = rscMod.default;
53
+ } catch {
54
+ throw new Error(
55
+ "Runtime discovery requires '@vitejs/plugin-rsc'. Install it with: pnpm add -D @vitejs/plugin-rsc",
56
+ );
57
+ }
58
+
59
+ const { createVersionPlugin } =
60
+ await import("../vite/plugins/version-plugin.ts");
61
+ const { createVirtualStubPlugin } =
62
+ await import("../vite/plugins/virtual-stub-plugin.ts");
63
+
64
+ // Load user's vite config to get resolve.alias (unless provided directly)
65
+ let userResolveAlias: any = opts.resolveAlias;
66
+ if (!userResolveAlias) {
67
+ const configPath = opts.configFile;
68
+ try {
69
+ const loaded = await loadConfigFromFile(
70
+ { command: "serve", mode: "development" },
71
+ configPath,
72
+ opts.root,
73
+ );
74
+ if (loaded?.config?.resolve?.alias) {
75
+ userResolveAlias = loaded.config.resolve.alias;
76
+ }
77
+ } catch {
78
+ // Config loading failed; proceed without aliases
79
+ }
80
+ }
81
+
82
+ const entryPath = resolve(opts.entry);
83
+ let tempServer: any = null;
84
+
85
+ try {
86
+ tempServer = await createViteServer({
87
+ root: opts.root,
88
+ configFile: false,
89
+ server: { middlewareMode: true },
90
+ appType: "custom",
91
+ logLevel: "silent",
92
+ cacheDir: "node_modules/.vite_rango_generate",
93
+ resolve: userResolveAlias ? { alias: userResolveAlias } : undefined,
94
+ esbuild: { jsx: "automatic", jsxImportSource: "react" },
95
+ plugins: [
96
+ rsc({
97
+ entries: {
98
+ client: "virtual:entry-client",
99
+ ssr: "virtual:entry-ssr",
100
+ rsc: entryPath,
101
+ },
102
+ }),
103
+ createVersionPlugin(),
104
+ createVirtualStubPlugin(),
105
+ ],
106
+ });
107
+
108
+ const rscEnv = (tempServer.environments as any)?.rsc;
109
+ if (!rscEnv?.runner) {
110
+ throw new Error("RSC environment runner not available");
111
+ }
112
+
113
+ // Import the entry to trigger createRouter() registration
114
+ await rscEnv.runner.import(entryPath);
115
+
116
+ // Read the RouterRegistry
117
+ const serverMod = await rscEnv.runner.import("@rangojs/router/server");
118
+ const registry: Map<string, any> = serverMod.RouterRegistry;
119
+
120
+ if (!registry || registry.size === 0) {
121
+ throw new Error(
122
+ `No routers found in registry after importing ${opts.entry}`,
123
+ );
124
+ }
125
+
126
+ // Import build utilities for manifest generation
127
+ const buildMod = await rscEnv.runner.import("@rangojs/router/build");
128
+ const generateManifest = buildMod.generateManifest;
129
+
130
+ if (!generateManifest) {
131
+ throw new Error("generateManifest not found in @rangojs/router/build");
132
+ }
133
+
134
+ const outputFiles: string[] = [];
135
+ let totalRouteCount = 0;
136
+ let routerMountIndex = 0;
137
+
138
+ for (const [id, router] of registry) {
139
+ if (!router.urlpatterns) continue;
140
+
141
+ const manifest = generateManifest(router.urlpatterns, routerMountIndex);
142
+ routerMountIndex++;
143
+
144
+ // Filter out auto-generated route names that the runtime creates for
145
+ // unnamed routes (path() with no name option). These get names like
146
+ // "$path__health" at root level or "docs.$path__health" under include().
147
+ // Match the Vite discovery writer's predicate: any name starting with "$"
148
+ // is internal. For prefixed names, check each dot-separated segment.
149
+ const rawManifest: Record<string, string> = manifest.routeManifest;
150
+ const routeManifest: Record<string, string> = {};
151
+ for (const [name, pattern] of Object.entries(rawManifest)) {
152
+ if (!isAutoGeneratedRouteName(name)) {
153
+ routeManifest[name] = pattern;
154
+ }
155
+ }
156
+ let routeSearchSchemas:
157
+ | Record<string, Record<string, string>>
158
+ | undefined = manifest.routeSearchSchemas;
159
+
160
+ // Determine output location from __sourceFile
161
+ const sourceFile: string | undefined = router.__sourceFile;
162
+ if (!sourceFile) {
163
+ console.warn(
164
+ `[rango] Router "${id}" has no __sourceFile, skipping gen file`,
165
+ );
166
+ continue;
167
+ }
168
+
169
+ // Guard against writing gen files into node_modules
170
+ if (sourceFile.includes("node_modules")) {
171
+ throw new Error(
172
+ `[rango] Router "${id}" has sourceFile inside node_modules: ${sourceFile}\n` +
173
+ `This means createRouter() stack trace parsing matched an internal frame.\n` +
174
+ `Set an explicit \`id\` on createRouter() or check the call site.`,
175
+ );
176
+ }
177
+
178
+ // Search schema fallback: runtime manifest may omit search schema metadata
179
+ // in some module-runner flows. Fall back to static source parsing.
180
+ if (!routeSearchSchemas || Object.keys(routeSearchSchemas).length === 0) {
181
+ const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
182
+ if (Object.keys(staticParsed.searchSchemas).length > 0) {
183
+ const filtered: Record<string, Record<string, string>> = {};
184
+ for (const name of Object.keys(routeManifest)) {
185
+ const schema = staticParsed.searchSchemas[name];
186
+ if (schema) filtered[name] = schema;
187
+ }
188
+ if (Object.keys(filtered).length > 0) {
189
+ routeSearchSchemas = filtered;
190
+ }
191
+ }
192
+ }
193
+
194
+ const routerDir = dirname(sourceFile);
195
+ const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
196
+ const outPath = join(routerDir, `${routerBasename}.named-routes.gen.ts`);
197
+
198
+ const source = generateRouteTypesSource(
199
+ routeManifest,
200
+ routeSearchSchemas && Object.keys(routeSearchSchemas).length > 0
201
+ ? routeSearchSchemas
202
+ : undefined,
203
+ );
204
+
205
+ const existing = existsSync(outPath)
206
+ ? readFileSync(outPath, "utf-8")
207
+ : null;
208
+ if (existing !== source) {
209
+ writeFileSync(outPath, source);
210
+ }
211
+
212
+ const routeCount = Object.keys(routeManifest).length;
213
+ totalRouteCount += routeCount;
214
+ outputFiles.push(outPath);
215
+
216
+ console.log(
217
+ `[rango] Generated route types (${routeCount} routes) -> ${outPath}`,
218
+ );
219
+ }
220
+
221
+ return {
222
+ routerCount: routerMountIndex,
223
+ routeCount: totalRouteCount,
224
+ outputFiles,
225
+ };
226
+ } finally {
227
+ if (tempServer) {
228
+ await tempServer.close();
229
+ }
230
+ }
231
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Background Task Runner
3
+ *
4
+ * Unified helper for scheduling async work via waitUntil.
5
+ * When waitUntil is unavailable, falls back to blocking or skipping.
6
+ */
7
+
8
+ interface WaitUntilHost {
9
+ waitUntil?: (fn: () => Promise<void>) => void;
10
+ }
11
+
12
+ /**
13
+ * Schedule an async task in the background via waitUntil.
14
+ *
15
+ * @param host - Object with optional waitUntil (request context or similar)
16
+ * @param task - Async function to execute
17
+ * @param blockWhenNoWaitUntil - If true, awaits the task when waitUntil is
18
+ * unavailable (e.g., Node.js dev server). If false (default), the task
19
+ * is silently skipped when waitUntil is unavailable.
20
+ * @returns A promise when blocking fallback is used, void otherwise.
21
+ */
22
+ export function runBackground(
23
+ host: WaitUntilHost | null | undefined,
24
+ task: () => Promise<void>,
25
+ blockWhenNoWaitUntil = false,
26
+ ): Promise<void> | void {
27
+ if (host?.waitUntil) {
28
+ host.waitUntil(task);
29
+ return;
30
+ }
31
+ if (blockWhenNoWaitUntil) {
32
+ return task();
33
+ }
34
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared Cache Key Utilities
3
+ *
4
+ * Deterministic normalization of search params and route params
5
+ * for cache key generation. Used by cache-runtime, cache-scope,
6
+ * document-cache, and loader-cache.
7
+ */
8
+
9
+ /**
10
+ * Build a sorted, deterministic query string from URLSearchParams,
11
+ * excluding internal _rsc* and __* params.
12
+ *
13
+ * Returns empty string when no user-facing params exist.
14
+ */
15
+ export function sortedSearchString(searchParams: URLSearchParams): string {
16
+ const pairs: [string, string][] = [];
17
+ for (const [k, v] of searchParams) {
18
+ if (!k.startsWith("_rsc") && !k.startsWith("__")) {
19
+ pairs.push([k, v]);
20
+ }
21
+ }
22
+ if (pairs.length === 0) return "";
23
+ pairs.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
24
+ return pairs
25
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
26
+ .join("&");
27
+ }
28
+
29
+ /**
30
+ * Build a sorted, deterministic string from route params.
31
+ *
32
+ * Returns empty string when params is empty or undefined.
33
+ */
34
+ export function sortedRouteParams(
35
+ params: Record<string, string> | undefined,
36
+ ): string {
37
+ if (!params) return "";
38
+ const entries = Object.entries(params);
39
+ if (entries.length === 0) return "";
40
+ return entries
41
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
42
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
43
+ .join("&");
44
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Shared Cache Policy Utilities
3
+ *
4
+ * Resolution cascades for TTL, SWR, cache key, and cache store.
5
+ * Consolidates the multi-tier resolution pattern:
6
+ * explicit option → store defaults → fallback constant
7
+ */
8
+
9
+ import type { CacheDefaults, SegmentCacheStore } from "./types.js";
10
+ import { _getRequestContext } from "../server/request-context.js";
11
+ import type { RequestContext } from "../server/request-context.js";
12
+
13
+ /**
14
+ * Default TTL for route-level cache() DSL and loader cache.
15
+ * Applied when neither the cache options nor the store defaults specify a TTL.
16
+ */
17
+ export const DEFAULT_ROUTE_TTL = 60;
18
+
19
+ /**
20
+ * Default TTL for function-level "use cache" (setItem).
21
+ * Applied when neither the item options nor the store defaults specify a TTL.
22
+ */
23
+ export const DEFAULT_FUNCTION_TTL = 900;
24
+
25
+ /**
26
+ * Resolve effective TTL from the 3-tier cascade:
27
+ * explicit → store defaults → fallback.
28
+ */
29
+ export function resolveTtl(
30
+ explicit: number | undefined,
31
+ defaults: CacheDefaults | undefined,
32
+ fallback: number,
33
+ ): number {
34
+ if (explicit !== undefined) return explicit;
35
+ if (defaults?.ttl !== undefined) return defaults.ttl;
36
+ return fallback;
37
+ }
38
+
39
+ /**
40
+ * Resolve effective SWR window from the 2-tier cascade:
41
+ * explicit → store defaults.
42
+ * Returns 0 when unset (no SWR window).
43
+ */
44
+ export function resolveSwrWindow(
45
+ explicit: number | undefined,
46
+ defaults: CacheDefaults | undefined,
47
+ ): number {
48
+ if (explicit !== undefined) return explicit;
49
+ if (defaults?.swr !== undefined) return defaults.swr;
50
+ return 0;
51
+ }
52
+
53
+ /**
54
+ * Compute staleAt and expiresAt timestamps from TTL and SWR window.
55
+ *
56
+ * - staleAt: when the entry becomes stale (TTL boundary)
57
+ * - expiresAt: when the entry should be evicted (TTL + SWR)
58
+ *
59
+ * When swrWindow is 0, staleAt === expiresAt (no SWR).
60
+ */
61
+ export function computeExpiration(
62
+ ttlSeconds: number,
63
+ swrSeconds: number = 0,
64
+ ): { staleAt: number; expiresAt: number } {
65
+ const now = Date.now();
66
+ const staleAt = now + ttlSeconds * 1000;
67
+ const expiresAt = staleAt + swrSeconds * 1000;
68
+ return { staleAt, expiresAt };
69
+ }
70
+
71
+ // ============================================================================
72
+ // Cache Key Resolution
73
+ // ============================================================================
74
+
75
+ /**
76
+ * Resolve cache key using the 3-tier priority:
77
+ * 1. keyFn (full override from route/loader cache options)
78
+ * 2. store.keyGenerator (modifies default key)
79
+ * 3. defaultKey (used when neither keyFn nor keyGenerator is provided)
80
+ *
81
+ * Errors from keyFn and store.keyGenerator propagate to the caller.
82
+ * Cache identity is correctness-critical: if explicit key logic throws,
83
+ * silently remapping to a different key could cause cache collisions or
84
+ * serve stale/wrong data. Callers must handle the error or let it surface.
85
+ *
86
+ * Uses _getRequestContext (non-throwing) so that calls outside ALS
87
+ * (e.g. build-time) gracefully fall back to defaultKey.
88
+ */
89
+ export async function resolveCacheKey(
90
+ keyFn: ((ctx: RequestContext) => string | Promise<string>) | undefined,
91
+ store: SegmentCacheStore | null,
92
+ defaultKey: string,
93
+ _label: string,
94
+ ): Promise<string> {
95
+ const requestCtx = _getRequestContext();
96
+
97
+ // Priority 1: Route/loader-level key function (full override)
98
+ if (keyFn && requestCtx) {
99
+ return await keyFn(requestCtx);
100
+ }
101
+
102
+ // Priority 2: Store-level keyGenerator (modifies default key)
103
+ if (store?.keyGenerator && requestCtx) {
104
+ return await store.keyGenerator(requestCtx, defaultKey);
105
+ }
106
+
107
+ // Priority 3: Default key (no custom key logic provided)
108
+ return defaultKey;
109
+ }
110
+
111
+ // ============================================================================
112
+ // Cache Store Resolution
113
+ // ============================================================================
114
+
115
+ /**
116
+ * Resolve cache store from the 2-tier priority:
117
+ * 1. Explicit store from cache options
118
+ * 2. App-level store from request context
119
+ */
120
+ export function resolveCacheStore(
121
+ explicitStore: SegmentCacheStore | undefined,
122
+ ): SegmentCacheStore | null {
123
+ if (explicitStore) return explicitStore;
124
+ return _getRequestContext()?._cacheStore ?? null;
125
+ }