@rangojs/router 0.0.0-experimental.10

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 (172) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +19 -0
  3. package/dist/bin/rango.js +227 -0
  4. package/dist/vite/index.js +3039 -0
  5. package/package.json +171 -0
  6. package/skills/caching/SKILL.md +191 -0
  7. package/skills/debug-manifest/SKILL.md +108 -0
  8. package/skills/document-cache/SKILL.md +180 -0
  9. package/skills/fonts/SKILL.md +165 -0
  10. package/skills/hooks/SKILL.md +442 -0
  11. package/skills/intercept/SKILL.md +190 -0
  12. package/skills/layout/SKILL.md +213 -0
  13. package/skills/links/SKILL.md +180 -0
  14. package/skills/loader/SKILL.md +246 -0
  15. package/skills/middleware/SKILL.md +202 -0
  16. package/skills/mime-routes/SKILL.md +124 -0
  17. package/skills/parallel/SKILL.md +228 -0
  18. package/skills/prerender/SKILL.md +283 -0
  19. package/skills/rango/SKILL.md +54 -0
  20. package/skills/response-routes/SKILL.md +358 -0
  21. package/skills/route/SKILL.md +173 -0
  22. package/skills/router-setup/SKILL.md +346 -0
  23. package/skills/tailwind/SKILL.md +129 -0
  24. package/skills/theme/SKILL.md +78 -0
  25. package/skills/typesafety/SKILL.md +394 -0
  26. package/src/__internal.ts +175 -0
  27. package/src/bin/rango.ts +24 -0
  28. package/src/browser/event-controller.ts +876 -0
  29. package/src/browser/index.ts +18 -0
  30. package/src/browser/link-interceptor.ts +121 -0
  31. package/src/browser/lru-cache.ts +69 -0
  32. package/src/browser/merge-segment-loaders.ts +126 -0
  33. package/src/browser/navigation-bridge.ts +913 -0
  34. package/src/browser/navigation-client.ts +165 -0
  35. package/src/browser/navigation-store.ts +823 -0
  36. package/src/browser/partial-update.ts +600 -0
  37. package/src/browser/react/Link.tsx +248 -0
  38. package/src/browser/react/NavigationProvider.tsx +346 -0
  39. package/src/browser/react/ScrollRestoration.tsx +94 -0
  40. package/src/browser/react/context.ts +53 -0
  41. package/src/browser/react/index.ts +52 -0
  42. package/src/browser/react/location-state-shared.ts +120 -0
  43. package/src/browser/react/location-state.ts +62 -0
  44. package/src/browser/react/mount-context.ts +32 -0
  45. package/src/browser/react/use-action.ts +240 -0
  46. package/src/browser/react/use-client-cache.ts +56 -0
  47. package/src/browser/react/use-handle.ts +203 -0
  48. package/src/browser/react/use-href.tsx +40 -0
  49. package/src/browser/react/use-link-status.ts +134 -0
  50. package/src/browser/react/use-mount.ts +31 -0
  51. package/src/browser/react/use-navigation.ts +140 -0
  52. package/src/browser/react/use-segments.ts +188 -0
  53. package/src/browser/request-controller.ts +164 -0
  54. package/src/browser/rsc-router.tsx +352 -0
  55. package/src/browser/scroll-restoration.ts +324 -0
  56. package/src/browser/segment-structure-assert.ts +67 -0
  57. package/src/browser/server-action-bridge.ts +762 -0
  58. package/src/browser/shallow.ts +35 -0
  59. package/src/browser/types.ts +478 -0
  60. package/src/build/generate-manifest.ts +377 -0
  61. package/src/build/generate-route-types.ts +828 -0
  62. package/src/build/index.ts +36 -0
  63. package/src/build/route-trie.ts +239 -0
  64. package/src/cache/cache-scope.ts +563 -0
  65. package/src/cache/cf/cf-cache-store.ts +428 -0
  66. package/src/cache/cf/index.ts +19 -0
  67. package/src/cache/document-cache.ts +340 -0
  68. package/src/cache/index.ts +58 -0
  69. package/src/cache/memory-segment-store.ts +150 -0
  70. package/src/cache/memory-store.ts +253 -0
  71. package/src/cache/types.ts +392 -0
  72. package/src/client.rsc.tsx +83 -0
  73. package/src/client.tsx +643 -0
  74. package/src/component-utils.ts +76 -0
  75. package/src/components/DefaultDocument.tsx +23 -0
  76. package/src/debug.ts +233 -0
  77. package/src/default-error-boundary.tsx +88 -0
  78. package/src/deps/browser.ts +8 -0
  79. package/src/deps/html-stream-client.ts +2 -0
  80. package/src/deps/html-stream-server.ts +2 -0
  81. package/src/deps/rsc.ts +10 -0
  82. package/src/deps/ssr.ts +2 -0
  83. package/src/errors.ts +295 -0
  84. package/src/handle.ts +130 -0
  85. package/src/handles/MetaTags.tsx +193 -0
  86. package/src/handles/index.ts +6 -0
  87. package/src/handles/meta.ts +247 -0
  88. package/src/host/cookie-handler.ts +159 -0
  89. package/src/host/errors.ts +97 -0
  90. package/src/host/index.ts +56 -0
  91. package/src/host/pattern-matcher.ts +214 -0
  92. package/src/host/router.ts +330 -0
  93. package/src/host/testing.ts +79 -0
  94. package/src/host/types.ts +138 -0
  95. package/src/host/utils.ts +25 -0
  96. package/src/href-client.ts +202 -0
  97. package/src/href-context.ts +33 -0
  98. package/src/index.rsc.ts +121 -0
  99. package/src/index.ts +165 -0
  100. package/src/loader.rsc.ts +207 -0
  101. package/src/loader.ts +47 -0
  102. package/src/network-error-thrower.tsx +21 -0
  103. package/src/outlet-context.ts +15 -0
  104. package/src/prerender/param-hash.ts +35 -0
  105. package/src/prerender/store.ts +40 -0
  106. package/src/prerender.ts +156 -0
  107. package/src/reverse.ts +267 -0
  108. package/src/root-error-boundary.tsx +277 -0
  109. package/src/route-content-wrapper.tsx +193 -0
  110. package/src/route-definition.ts +1431 -0
  111. package/src/route-map-builder.ts +242 -0
  112. package/src/route-types.ts +220 -0
  113. package/src/router/error-handling.ts +287 -0
  114. package/src/router/handler-context.ts +158 -0
  115. package/src/router/intercept-resolution.ts +387 -0
  116. package/src/router/loader-resolution.ts +327 -0
  117. package/src/router/manifest.ts +216 -0
  118. package/src/router/match-api.ts +621 -0
  119. package/src/router/match-context.ts +264 -0
  120. package/src/router/match-middleware/background-revalidation.ts +236 -0
  121. package/src/router/match-middleware/cache-lookup.ts +382 -0
  122. package/src/router/match-middleware/cache-store.ts +276 -0
  123. package/src/router/match-middleware/index.ts +81 -0
  124. package/src/router/match-middleware/intercept-resolution.ts +281 -0
  125. package/src/router/match-middleware/segment-resolution.ts +184 -0
  126. package/src/router/match-pipelines.ts +214 -0
  127. package/src/router/match-result.ts +213 -0
  128. package/src/router/metrics.ts +62 -0
  129. package/src/router/middleware.ts +791 -0
  130. package/src/router/pattern-matching.ts +407 -0
  131. package/src/router/revalidation.ts +190 -0
  132. package/src/router/router-context.ts +301 -0
  133. package/src/router/segment-resolution.ts +1315 -0
  134. package/src/router/trie-matching.ts +172 -0
  135. package/src/router/types.ts +163 -0
  136. package/src/router.gen.ts +6 -0
  137. package/src/router.ts +2423 -0
  138. package/src/rsc/handler.ts +1443 -0
  139. package/src/rsc/helpers.ts +64 -0
  140. package/src/rsc/index.ts +56 -0
  141. package/src/rsc/nonce.ts +18 -0
  142. package/src/rsc/types.ts +236 -0
  143. package/src/segment-system.tsx +442 -0
  144. package/src/server/context.ts +466 -0
  145. package/src/server/handle-store.ts +229 -0
  146. package/src/server/loader-registry.ts +174 -0
  147. package/src/server/request-context.ts +554 -0
  148. package/src/server/root-layout.tsx +10 -0
  149. package/src/server/tsconfig.json +14 -0
  150. package/src/server.ts +171 -0
  151. package/src/ssr/index.tsx +296 -0
  152. package/src/theme/ThemeProvider.tsx +291 -0
  153. package/src/theme/ThemeScript.tsx +61 -0
  154. package/src/theme/constants.ts +59 -0
  155. package/src/theme/index.ts +58 -0
  156. package/src/theme/theme-context.ts +70 -0
  157. package/src/theme/theme-script.ts +152 -0
  158. package/src/theme/types.ts +182 -0
  159. package/src/theme/use-theme.ts +44 -0
  160. package/src/types.ts +1757 -0
  161. package/src/urls.gen.ts +8 -0
  162. package/src/urls.ts +1282 -0
  163. package/src/use-loader.tsx +346 -0
  164. package/src/vite/expose-action-id.ts +344 -0
  165. package/src/vite/expose-handle-id.ts +209 -0
  166. package/src/vite/expose-loader-id.ts +426 -0
  167. package/src/vite/expose-location-state-id.ts +177 -0
  168. package/src/vite/expose-prerender-handler-id.ts +429 -0
  169. package/src/vite/index.ts +2068 -0
  170. package/src/vite/package-resolution.ts +125 -0
  171. package/src/vite/version.d.ts +12 -0
  172. package/src/vite/virtual-entries.ts +114 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Router Manifest Loading
3
+ *
4
+ * Handles lazy loading and validation of route manifests.
5
+ */
6
+
7
+ import { invariant, RouteNotFoundError } from "../errors";
8
+ import { createRouteHelpers } from "../route-definition";
9
+ import { getContext, runWithPrefixes, type EntryData, type MetricsStore } from "../server/context";
10
+ import MapRootLayout from "../server/root-layout";
11
+ import type { RouteEntry } from "../types";
12
+ import type { UrlPatterns } from "../urls";
13
+ import { VERSION } from "@rangojs/router:version";
14
+
15
+ // Module-level manifest cache: avoids re-executing DSL handler on every request.
16
+ // Handler execution is deterministic (components, loaders, middleware are module-level
17
+ // stable references), so the resulting EntryData tree can be safely cached and reused
18
+ // across requests within the same isolate.
19
+ //
20
+ // Cache is keyed by (VERSION, mountIndex, routeKey, isSSR). VERSION comes from the
21
+ // @rangojs/router:version virtual module which Vite invalidates on RSC module HMR.
22
+ // When VERSION changes, this module re-evaluates and the cache is recreated empty.
23
+ // Including VERSION in the key is additional defense against stale entries.
24
+ const manifestModuleCache = new Map<string, Map<string, EntryData>>();
25
+
26
+ /**
27
+ * Load manifest from route entry with AsyncLocalStorage context
28
+ * Handles lazy imports, unwrapping, and validation
29
+ *
30
+ * Results are cached at module level after first execution. Subsequent calls
31
+ * for the same (routeKey, isSSR) within the same isolate return cached data
32
+ * without re-executing the DSL handler.
33
+ */
34
+ /**
35
+ * Clear the module-level manifest cache.
36
+ * Called on HMR to ensure stale handler references are discarded.
37
+ */
38
+ export function clearManifestCache(): void {
39
+ manifestModuleCache.clear();
40
+ }
41
+
42
+ export async function loadManifest(
43
+ entry: RouteEntry<any>,
44
+ routeKey: string,
45
+ path: string,
46
+ metricsStore?: MetricsStore,
47
+ isSSR?: boolean,
48
+ ): Promise<EntryData> {
49
+ // Helper to push a metric entry
50
+ const pushMetric = metricsStore
51
+ ? (label: string, start: number) => {
52
+ metricsStore.metrics.push({
53
+ label,
54
+ duration: performance.now() - start,
55
+ startTime: start - metricsStore.requestStart,
56
+ });
57
+ }
58
+ : undefined;
59
+
60
+ const mountIndex = entry.mountIndex;
61
+
62
+ // Check module-level cache (persists across requests within same isolate)
63
+ const cacheKey = `${VERSION}:${mountIndex ?? ''}:${routeKey}:${isSSR ? 1 : 0}`;
64
+ const cached = manifestModuleCache.get(cacheKey);
65
+ if (cached) {
66
+ const cacheStart = performance.now();
67
+ // Set up Store for downstream consumers (segment resolution reads Store.manifest)
68
+ const Store = getContext().getOrCreateStore(routeKey);
69
+ Store.mountIndex = mountIndex;
70
+ Store.isSSR = isSSR;
71
+ if (metricsStore) Store.metrics = metricsStore;
72
+ // Restore cached manifest into Store
73
+ for (const [k, v] of cached) {
74
+ Store.manifest.set(k, v);
75
+ }
76
+ pushMetric?.("manifest:cache-hit", cacheStart);
77
+ return cached.get(routeKey)!;
78
+ }
79
+
80
+ const storeSetupStart = performance.now();
81
+ const Store = getContext().getOrCreateStore(routeKey);
82
+
83
+ // Set mount index in store for unique shortCode prefixes
84
+ Store.mountIndex = mountIndex;
85
+
86
+ // Set isSSR flag so loading() can check if we're in SSR
87
+ Store.isSSR = isSSR;
88
+
89
+ // Attach metrics store to context if provided
90
+ if (metricsStore) {
91
+ Store.metrics = metricsStore;
92
+ }
93
+
94
+ pushMetric?.("manifest:store-setup", storeSetupStart);
95
+
96
+ // Clear manifest before rebuilding to prevent stale entry mutations
97
+ const clearStart = performance.now();
98
+ Store.manifest.clear();
99
+ pushMetric?.("manifest:clear", clearStart);
100
+
101
+ try {
102
+ // Include mountIndex in namespace to ensure unique cache keys per mount
103
+ const namespaceWithMount = mountIndex !== undefined
104
+ ? `#router.M${mountIndex}`
105
+ : "#router";
106
+
107
+ // For lazy entries, use the captured parent from include() context
108
+ // This ensures routes are registered under the correct layout hierarchy
109
+ const lazyContext = entry.lazy && entry.lazyPatterns ? entry.lazyContext : null;
110
+ const parentForContext = lazyContext?.parent as EntryData | null ?? Store.parent;
111
+
112
+ const handlerExecStart = performance.now();
113
+ const useItems = await getContext().runWithStore(
114
+ Store,
115
+ Store.namespace || namespaceWithMount,
116
+ parentForContext,
117
+ async () => {
118
+ // Create helpers for lazy-loaded handlers that need them
119
+ const helpers = createRouteHelpers();
120
+
121
+ // For lazy entries, use lazyPatterns.handler() with proper prefixes
122
+ if (entry.lazy && entry.lazyPatterns) {
123
+ const lazyPatterns = entry.lazyPatterns as UrlPatterns<any>;
124
+ const includePrefix = (entry as any)._lazyPrefix || "";
125
+ const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
126
+
127
+ // Wrap in root layout and run with prefixes
128
+ const wrappedItems = helpers.layout(MapRootLayout, () => {
129
+ if (fullPrefix || lazyContext?.namePrefix) {
130
+ return runWithPrefixes(
131
+ fullPrefix,
132
+ lazyContext?.namePrefix,
133
+ () => lazyPatterns.handler()
134
+ );
135
+ }
136
+ return lazyPatterns.handler();
137
+ });
138
+
139
+ return [wrappedItems].flat(3);
140
+ }
141
+
142
+ // Wrap handler execution in root layout so routes get correct parent
143
+ // This ensures all routes are registered with the layout as their parent
144
+ let promiseResult: Promise<any> | null = null;
145
+ const wrappedItems = helpers.layout(MapRootLayout, () => {
146
+ const result = entry.handler();
147
+ if (result instanceof Promise) {
148
+ // Lazy handler detected - capture promise for async handling
149
+ promiseResult = result;
150
+ return []; // Return empty, we'll discard this wrapped result
151
+ }
152
+ return result;
153
+ });
154
+
155
+ // Handle lazy (Promise-based) handlers
156
+ if (promiseResult !== null) {
157
+ const load = await (promiseResult as Promise<any>);
158
+ if (
159
+ load &&
160
+ load !== null &&
161
+ typeof load === "object" &&
162
+ "default" in load
163
+ ) {
164
+ // Promise<{ default: () => Array }> - e.g., dynamic import
165
+ // Lazy-loaded handlers may need helpers (passed as optional arg)
166
+ return (load.default as (h?: any) => any)(helpers);
167
+ }
168
+ if (typeof load === "function") {
169
+ // Promise<() => Array>
170
+ return (load as (h?: any) => any)(helpers);
171
+ }
172
+ // Promise<Array> - direct array from async handler
173
+ return load;
174
+ }
175
+
176
+ // Inline handler - routes were registered with correct parent inside layout
177
+ return [wrappedItems].flat(3);
178
+ }
179
+ );
180
+ pushMetric?.("manifest:handler-exec", handlerExecStart);
181
+
182
+ const validationStart = performance.now();
183
+ invariant(
184
+ useItems && useItems.length > 0,
185
+ "Did not receive any handler from router.map()"
186
+ );
187
+ invariant(
188
+ useItems.some((item: { type: string }) => item.type === "layout"),
189
+ "Top-level handler must be a layout"
190
+ );
191
+
192
+ invariant(
193
+ Store.manifest.has(routeKey),
194
+ `Route must be registered for ${routeKey}`
195
+ );
196
+ pushMetric?.("manifest:validation", validationStart);
197
+
198
+ // Cache manifest for future requests in this isolate
199
+ manifestModuleCache.set(cacheKey, new Map(Store.manifest));
200
+
201
+ return Store.manifest.get(routeKey)!;
202
+ } catch (e) {
203
+ throw new RouteNotFoundError(
204
+ `Failed to load route handlers for ${path}: ${(e as Error).message}`,
205
+ {
206
+ cause: {
207
+ error: e,
208
+ state: {
209
+ path,
210
+ routeKey,
211
+ },
212
+ },
213
+ }
214
+ );
215
+ }
216
+ }