@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,36 @@
1
+ /**
2
+ * Build-time utilities for @rangojs/router
3
+ *
4
+ * Note: Route manifest is automatically generated at runtime on first request.
5
+ * These build-time utilities are only needed for advanced use cases like
6
+ * custom tooling or pre-generating manifests.
7
+ *
8
+ * @example Generate manifest programmatically (for custom tooling)
9
+ * ```typescript
10
+ * import { generateManifest } from "@rangojs/router/build";
11
+ * import { urlpatterns } from "./urls";
12
+ *
13
+ * const manifest = generateManifest(urlpatterns);
14
+ * console.log(manifest.routeManifest); // { home: "/", about: "/about", ... }
15
+ * ```
16
+ */
17
+
18
+ export {
19
+ generateManifest,
20
+ generateManifestCode,
21
+ type GeneratedManifest,
22
+ } from "./generate-manifest.js";
23
+
24
+ export {
25
+ buildRouteTrie,
26
+ type TrieNode,
27
+ type TrieLeaf,
28
+ } from "./route-trie.js";
29
+
30
+ export {
31
+ writePerModuleRouteTypes,
32
+ extractRoutesFromSource,
33
+ generatePerModuleTypesSource,
34
+ createScanFilter,
35
+ type ScanFilter,
36
+ } from "./generate-route-types.js";
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Build-time Route Trie Construction
3
+ *
4
+ * Builds a serializable trie from the route manifest for O(path_length)
5
+ * route matching at runtime. Each trie leaf embeds the route's ancestry
6
+ * shortCodes for layout pruning.
7
+ */
8
+
9
+ import { parsePattern, type ParsedSegment } from "../router/pattern-matching.js";
10
+
11
+ // -- Trie data structures (compact keys for JSON serialization) --
12
+
13
+ export interface TrieLeaf {
14
+ /** Route name (e.g., "site.l1_500") */
15
+ n: string;
16
+ /** Static prefix of the entry (e.g., "/site") */
17
+ sp: string;
18
+ /** Ancestry shortCodes from root to route [M0L0, M0L0L0, M0L0L0R499] */
19
+ a: string[];
20
+ /** Optional param names (absent params get empty string value) */
21
+ op?: string[];
22
+ /** Constraint validation: paramName -> allowed values */
23
+ cv?: Record<string, string[]>;
24
+ /** Trailing slash mode */
25
+ ts?: string;
26
+ /** Route has pre-rendered data available */
27
+ pr?: true;
28
+ /** Passthrough: handler kept in bundle for live fallback on unknown params */
29
+ pt?: true;
30
+ /** Response type for non-RSC routes (json, text, image, any) */
31
+ rt?: string;
32
+ /** Negotiate variants: response-type routes sharing this path */
33
+ nv?: Array<{ routeKey: string; responseType: string }>;
34
+ /** RSC-first: RSC route was defined before response-type variants */
35
+ rf?: true;
36
+ }
37
+
38
+ export interface TrieNode {
39
+ /** Route terminal at this node */
40
+ r?: TrieLeaf;
41
+ /** Static segment children */
42
+ s?: Record<string, TrieNode>;
43
+ /** Param child: { n: paramName, c: child node } */
44
+ p?: { n: string; c: TrieNode };
45
+ /** Wildcard terminal: leaf + paramName */
46
+ w?: TrieLeaf & { pn: string };
47
+ }
48
+
49
+ /**
50
+ * Build a route trie from build-time manifest data.
51
+ *
52
+ * @param routeManifest - Map of route name to full URL pattern
53
+ * @param routeAncestry - Map of route name to ancestry shortCodes
54
+ * @param routeToStaticPrefix - Map of route name to its entry's staticPrefix
55
+ * @param routeTrailingSlash - Optional map of route name to trailing slash mode
56
+ */
57
+ export function buildRouteTrie(
58
+ routeManifest: Record<string, string>,
59
+ routeAncestry: Record<string, string[]>,
60
+ routeToStaticPrefix: Record<string, string>,
61
+ routeTrailingSlash?: Record<string, string>,
62
+ prerenderRouteNames?: Set<string>,
63
+ passthroughRouteNames?: Set<string>,
64
+ responseTypeRoutes?: Record<string, string>,
65
+ ): TrieNode {
66
+ const root: TrieNode = {};
67
+
68
+ for (const [routeName, pattern] of Object.entries(routeManifest)) {
69
+ const ancestry = routeAncestry[routeName] || [];
70
+ const staticPrefix = routeToStaticPrefix[routeName] || "";
71
+ const trailingSlash = routeTrailingSlash?.[routeName];
72
+ const responseType = responseTypeRoutes?.[routeName];
73
+
74
+ // Detect and strip trailing slash from pattern for parsing
75
+ const hasTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
76
+ const normalizedPattern = hasTrailingSlash ? pattern.slice(0, -1) : pattern;
77
+
78
+ const segments = parsePattern(normalizedPattern);
79
+ insertRoute(root, segments, 0, {
80
+ n: routeName,
81
+ sp: staticPrefix,
82
+ a: ancestry,
83
+ ...(trailingSlash ? { ts: trailingSlash } : {}),
84
+ ...(prerenderRouteNames?.has(routeName) ? { pr: true } : {}),
85
+ ...(passthroughRouteNames?.has(routeName) ? { pt: true } : {}),
86
+ ...(responseType ? { rt: responseType } : {}),
87
+ });
88
+ }
89
+
90
+ return root;
91
+ }
92
+
93
+ /**
94
+ * Insert a route into the trie, handling optional params by forking
95
+ * the insertion path (one terminal without the param, one with).
96
+ */
97
+ function insertRoute(
98
+ node: TrieNode,
99
+ segments: ParsedSegment[],
100
+ index: number,
101
+ leaf: Omit<TrieLeaf, "op" | "cv">,
102
+ ): void {
103
+ // Collect optional param names and constraints across all segments
104
+ const optionalParams: string[] = [];
105
+ const constraints: Record<string, string[]> = {};
106
+
107
+ for (const seg of segments) {
108
+ if (seg.type === "param" && seg.optional) {
109
+ optionalParams.push(seg.value);
110
+ }
111
+ if (seg.type === "param" && seg.constraint) {
112
+ constraints[seg.value] = seg.constraint;
113
+ }
114
+ }
115
+
116
+ const fullLeaf: TrieLeaf = {
117
+ ...leaf,
118
+ ...(optionalParams.length > 0 ? { op: optionalParams } : {}),
119
+ ...(Object.keys(constraints).length > 0 ? { cv: constraints } : {}),
120
+ };
121
+
122
+ insertSegments(node, segments, index, fullLeaf);
123
+ }
124
+
125
+ /**
126
+ * Recursively insert segments into the trie.
127
+ * For optional params, we add a terminal at the current node (param absent)
128
+ * AND continue inserting into the param child (param present).
129
+ */
130
+ /**
131
+ * Extract ancestry map from a built trie by visiting all leaf nodes.
132
+ * Returns { routeName: ancestryShortCodes[] } for every route in the trie.
133
+ */
134
+ export function extractAncestryFromTrie(
135
+ root: TrieNode,
136
+ ): Record<string, string[]> {
137
+ const result: Record<string, string[]> = {};
138
+
139
+ function visit(node: TrieNode): void {
140
+ if (node.r) {
141
+ result[node.r.n] = node.r.a;
142
+ }
143
+ if (node.w) {
144
+ result[node.w.n] = node.w.a;
145
+ }
146
+ if (node.s) {
147
+ for (const child of Object.values(node.s)) {
148
+ visit(child);
149
+ }
150
+ }
151
+ if (node.p) {
152
+ visit(node.p.c);
153
+ }
154
+ }
155
+
156
+ visit(root);
157
+ return result;
158
+ }
159
+
160
+ /**
161
+ * Merge a new leaf with an existing leaf, handling content negotiation.
162
+ * When an RSC route and response-type routes share the same URL pattern,
163
+ * the RSC route becomes the primary leaf and response-type routes are
164
+ * appended to the nv (negotiate variants) array.
165
+ * Multiple response types on the same path are supported (json + text + xml).
166
+ */
167
+ function mergeLeaves(existing: TrieLeaf | undefined, leaf: TrieLeaf): TrieLeaf {
168
+ if (!existing) return leaf;
169
+
170
+ if (existing.rt && leaf.rt) {
171
+ // Both are response-type: preserve old as variant
172
+ const merged = leaf;
173
+ merged.nv = existing.nv || [];
174
+ merged.nv.push({ routeKey: existing.n, responseType: existing.rt });
175
+ return merged;
176
+ }
177
+ if (leaf.rt && !existing.rt) {
178
+ // RSC primary exists, new leaf is response-type: append variant
179
+ // RSC was defined first (it was already the existing leaf)
180
+ if (!existing.nv) {
181
+ existing.nv = [];
182
+ existing.rf = true;
183
+ }
184
+ existing.nv.push({ routeKey: leaf.n, responseType: leaf.rt });
185
+ return existing;
186
+ }
187
+ if (!leaf.rt && existing.rt) {
188
+ // Response-type was primary, new leaf is RSC: swap and move old to variants
189
+ // RSC was defined second (response-type was already the existing leaf)
190
+ if (!leaf.nv) leaf.nv = [];
191
+ leaf.nv.push({ routeKey: existing.n, responseType: existing.rt });
192
+ if (existing.nv) leaf.nv.push(...existing.nv);
193
+ // rf intentionally not set — RSC came after response-type variants
194
+ return leaf;
195
+ }
196
+ // Both RSC (last wins): overwrite
197
+ return leaf;
198
+ }
199
+
200
+ function mergeLeaf(node: TrieNode, leaf: TrieLeaf): void {
201
+ node.r = mergeLeaves(node.r, leaf);
202
+ }
203
+
204
+ function insertSegments(
205
+ node: TrieNode,
206
+ segments: ParsedSegment[],
207
+ index: number,
208
+ leaf: TrieLeaf,
209
+ ): void {
210
+ // Base case: all segments consumed, add terminal
211
+ if (index >= segments.length) {
212
+ mergeLeaf(node, leaf);
213
+ return;
214
+ }
215
+
216
+ const segment = segments[index];
217
+
218
+ if (segment.type === "static") {
219
+ if (!node.s) node.s = {};
220
+ if (!node.s[segment.value]) node.s[segment.value] = {};
221
+ insertSegments(node.s[segment.value], segments, index + 1, leaf);
222
+ } else if (segment.type === "param") {
223
+ if (segment.optional) {
224
+ // Optional param: add terminal at current node (param absent)
225
+ mergeLeaf(node, leaf);
226
+ // AND continue with param child (param present)
227
+ }
228
+ if (!node.p) {
229
+ node.p = { n: segment.value, c: {} };
230
+ }
231
+ insertSegments(node.p.c, segments, index + 1, leaf);
232
+ } else if (segment.type === "wildcard") {
233
+ // Wildcard consumes all remaining segments
234
+ const wildLeaf = { ...leaf, pn: "*" };
235
+ const existing = node.w ? { ...node.w } as TrieLeaf : undefined;
236
+ const merged = mergeLeaves(existing, wildLeaf);
237
+ node.w = merged as TrieLeaf & { pn: string };
238
+ }
239
+ }