@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,172 @@
1
+ /**
2
+ * Runtime Trie Matching
3
+ *
4
+ * Walks the pre-built trie by path segments in O(path_length) time.
5
+ * Falls back to null when no match is found (caller uses regex fallback).
6
+ */
7
+
8
+ import type { TrieNode, TrieLeaf } from "../build/route-trie.js";
9
+
10
+ export interface TrieMatchResult {
11
+ /** Route name */
12
+ routeKey: string;
13
+ /** Static prefix of the matched entry */
14
+ sp: string;
15
+ /** Matched route params */
16
+ params: Record<string, string>;
17
+ /** Optional param names (absent params have empty string value) */
18
+ optionalParams?: string[];
19
+ /** Ancestry shortCodes for layout pruning */
20
+ ancestry: string[];
21
+ /** Redirect target if trailing slash requires it */
22
+ redirectTo?: string;
23
+ /** Route has pre-rendered data available */
24
+ pr?: true;
25
+ /** Passthrough: handler kept for live fallback on unknown params */
26
+ pt?: true;
27
+ /** Response type for non-RSC routes (json, text, image, any) */
28
+ responseType?: string;
29
+ /** Negotiate variants: response-type routes sharing this path */
30
+ negotiateVariants?: Array<{ routeKey: string; responseType: string }>;
31
+ /** RSC-first: RSC route was defined before response-type variants */
32
+ rscFirst?: true;
33
+ }
34
+
35
+ /**
36
+ * Try to match a pathname against the trie.
37
+ * Returns null if no match found (caller should fall back to regex).
38
+ */
39
+ export function tryTrieMatch(
40
+ trie: TrieNode | null,
41
+ pathname: string,
42
+ ): TrieMatchResult | null {
43
+ if (!trie) return null;
44
+
45
+ // Split pathname into segments, filtering empty strings from leading/trailing slashes
46
+ const pathnameHasTrailingSlash = pathname.length > 1 && pathname.endsWith("/");
47
+ const normalizedPath = pathnameHasTrailingSlash ? pathname.slice(0, -1) : pathname;
48
+
49
+ // Handle root path
50
+ if (normalizedPath === "" || normalizedPath === "/") {
51
+ if (trie.r) {
52
+ return validateAndBuild(trie.r, {}, pathname, pathnameHasTrailingSlash);
53
+ }
54
+ return null;
55
+ }
56
+
57
+ // Remove leading slash and split
58
+ const segments = normalizedPath.slice(1).split("/");
59
+
60
+ // Try exact match with normalized path (no trailing slash)
61
+ const result = walkTrie(trie, segments, 0, {});
62
+ if (result) {
63
+ return validateAndBuild(result.leaf, result.params, pathname, pathnameHasTrailingSlash);
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ interface WalkResult {
70
+ leaf: TrieLeaf;
71
+ params: Record<string, string>;
72
+ }
73
+
74
+ /**
75
+ * Walk the trie by segments with priority: static > param > wildcard.
76
+ * Uses backtracking to try all possible matches.
77
+ */
78
+ function walkTrie(
79
+ node: TrieNode,
80
+ segments: string[],
81
+ index: number,
82
+ params: Record<string, string>,
83
+ ): WalkResult | null {
84
+ // All segments consumed: check for terminal
85
+ if (index === segments.length) {
86
+ if (node.r) {
87
+ return { leaf: node.r, params };
88
+ }
89
+ return null;
90
+ }
91
+
92
+ const segment = segments[index];
93
+
94
+ // Priority 1: Static match
95
+ if (node.s?.[segment]) {
96
+ const result = walkTrie(node.s[segment], segments, index + 1, params);
97
+ if (result) return result;
98
+ }
99
+
100
+ // Priority 2: Param match
101
+ if (node.p) {
102
+ const result = walkTrie(node.p.c, segments, index + 1, {
103
+ ...params,
104
+ [node.p.n]: segment,
105
+ });
106
+ if (result) return result;
107
+ }
108
+
109
+ // Priority 3: Wildcard match (consumes rest)
110
+ if (node.w) {
111
+ const rest = segments.slice(index).join("/");
112
+ return {
113
+ leaf: node.w,
114
+ params: { ...params, [node.w.pn]: rest },
115
+ };
116
+ }
117
+
118
+ return null;
119
+ }
120
+
121
+ /**
122
+ * Post-match: validate constraints and handle trailing slash logic.
123
+ */
124
+ function validateAndBuild(
125
+ leaf: TrieLeaf,
126
+ params: Record<string, string>,
127
+ originalPathname: string,
128
+ pathnameHasTrailingSlash: boolean,
129
+ ): TrieMatchResult | null {
130
+ // Validate constraints
131
+ if (leaf.cv) {
132
+ for (const [paramName, allowed] of Object.entries(leaf.cv)) {
133
+ const value = params[paramName];
134
+ if (value !== undefined && value !== "" && !allowed.includes(value)) {
135
+ return null;
136
+ }
137
+ }
138
+ }
139
+
140
+ // Fill in empty strings for optional params that weren't matched
141
+ if (leaf.op) {
142
+ for (const name of leaf.op) {
143
+ if (!(name in params)) {
144
+ params[name] = "";
145
+ }
146
+ }
147
+ }
148
+
149
+ // Trailing slash handling
150
+ const tsMode = leaf.ts as "never" | "always" | "ignore" | undefined;
151
+ let redirectTo: string | undefined;
152
+
153
+ if (tsMode === "always" && !pathnameHasTrailingSlash && originalPathname !== "/") {
154
+ redirectTo = originalPathname + "/";
155
+ } else if (tsMode === "never" && pathnameHasTrailingSlash) {
156
+ redirectTo = originalPathname.slice(0, -1);
157
+ }
158
+
159
+ return {
160
+ routeKey: leaf.n,
161
+ sp: leaf.sp,
162
+ params,
163
+ optionalParams: leaf.op,
164
+ ancestry: leaf.a,
165
+ ...(redirectTo ? { redirectTo } : {}),
166
+ ...(leaf.pr ? { pr: true } : {}),
167
+ ...(leaf.pt ? { pt: true } : {}),
168
+ ...(leaf.rt ? { responseType: leaf.rt } : {}),
169
+ ...(leaf.nv ? { negotiateVariants: leaf.nv } : {}),
170
+ ...(leaf.rf ? { rscFirst: true } : {}),
171
+ };
172
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Router Internal Types
3
+ *
4
+ * Shared types for router module utilities.
5
+ */
6
+
7
+ import type { ReactNode } from "react";
8
+ import type { EntryData, InterceptEntry, InterceptSelectorContext } from "../server/context";
9
+ import type {
10
+ ErrorInfo,
11
+ ErrorPhase,
12
+ LoaderDataResult,
13
+ ResolvedSegment,
14
+ HandlerContext,
15
+ InternalHandlerContext,
16
+ ErrorBoundaryHandler,
17
+ NotFoundBoundaryHandler,
18
+ ShouldRevalidateFn,
19
+ } from "../types";
20
+
21
+ /**
22
+ * Result of resolving loaders with revalidation
23
+ * Contains both segments to render and all matched segment IDs
24
+ */
25
+ export interface LoaderRevalidationResult {
26
+ segments: ResolvedSegment[];
27
+ matchedIds: string[];
28
+ }
29
+
30
+ /**
31
+ * Result of resolving segments with revalidation
32
+ * Contains both segments to render and all matched segment IDs
33
+ */
34
+ export interface SegmentRevalidationResult {
35
+ segments: ResolvedSegment[];
36
+ matchedIds: string[];
37
+ }
38
+
39
+ /**
40
+ * Action context type for revalidation
41
+ */
42
+ export type ActionContext = {
43
+ actionId?: string;
44
+ actionUrl?: URL;
45
+ actionResult?: any;
46
+ formData?: FormData;
47
+ };
48
+
49
+ /**
50
+ * Dependencies passed to segment resolution functions
51
+ * These are created within createRouter and passed to extracted utilities
52
+ */
53
+ export interface RouterDependencies<TEnv> {
54
+ findNearestErrorBoundary: (
55
+ entry: EntryData | null
56
+ ) => ReactNode | ErrorBoundaryHandler | null;
57
+ findNearestNotFoundBoundary: (
58
+ entry: EntryData | null
59
+ ) => ReactNode | NotFoundBoundaryHandler | null;
60
+ }
61
+
62
+ /**
63
+ * Dependencies injected from createRouter closure into extracted segment resolution functions.
64
+ * These are the closure-bound helpers that cannot be imported directly.
65
+ */
66
+ export interface SegmentResolutionDeps<TEnv = any> {
67
+ wrapLoaderPromise: <T>(
68
+ promise: Promise<T>,
69
+ entry: EntryData,
70
+ segmentId: string,
71
+ pathname: string,
72
+ errorContext?: {
73
+ request: Request;
74
+ url: URL;
75
+ routeKey?: string;
76
+ params?: Record<string, string>;
77
+ env?: TEnv;
78
+ isPartial?: boolean;
79
+ requestStartTime?: number;
80
+ },
81
+ ) => Promise<LoaderDataResult<T>>;
82
+ trackHandler: <T>(promise: Promise<T>) => Promise<T>;
83
+ findNearestErrorBoundary: (
84
+ entry: EntryData | null,
85
+ ) => ReactNode | ErrorBoundaryHandler | null;
86
+ findNearestNotFoundBoundary: (
87
+ entry: EntryData | null,
88
+ ) => ReactNode | NotFoundBoundaryHandler | null;
89
+ callOnError: (
90
+ error: unknown,
91
+ phase: ErrorPhase,
92
+ context: any,
93
+ ) => void;
94
+ }
95
+
96
+ /**
97
+ * Dependencies injected from createRouter closure into extracted intercept resolution functions.
98
+ */
99
+ export interface InterceptResolutionDeps<TEnv = any> {
100
+ wrapLoaderPromise: SegmentResolutionDeps<TEnv>["wrapLoaderPromise"];
101
+ evaluateInterceptWhen: (
102
+ intercept: InterceptEntry,
103
+ selectorContext: InterceptSelectorContext | null,
104
+ isAction: boolean,
105
+ ) => boolean;
106
+ }
107
+
108
+ /**
109
+ * Dependencies injected from createRouter closure into extracted match API functions.
110
+ */
111
+ export interface MatchApiDeps<TEnv = any> {
112
+ findMatch: (pathname: string, ms?: any) => any;
113
+ getMetricsStore: () => any;
114
+ findInterceptForRoute: (
115
+ routeKey: string,
116
+ parentEntry: EntryData | null,
117
+ selectorContext: InterceptSelectorContext | null,
118
+ isAction: boolean,
119
+ ) => { intercept: InterceptEntry; entry: EntryData } | null;
120
+ callOnError: SegmentResolutionDeps<TEnv>["callOnError"];
121
+ findNearestErrorBoundary: SegmentResolutionDeps<TEnv>["findNearestErrorBoundary"];
122
+ }
123
+
124
+ /**
125
+ * Title descriptor types for template support
126
+ */
127
+ export type TitleDescriptor =
128
+ | string
129
+ | { template: string; default: string } // For layouts - template applied to child titles
130
+ | { absolute: string }; // Bypass parent template
131
+
132
+ /**
133
+ * Unset descriptor to remove inherited meta
134
+ * Key format matches getMetaKey output: "title", "name:description", "property:og:image"
135
+ */
136
+ export type UnsetDescriptor = { unset: string };
137
+
138
+ /**
139
+ * Base meta descriptor types (sync values)
140
+ */
141
+ export type MetaDescriptorBase =
142
+ | { charSet: "utf-8" }
143
+ | { title: TitleDescriptor }
144
+ | { name: string; content: string }
145
+ | { property: string; content: string }
146
+ | { httpEquiv: string; content: string }
147
+ | { "script:ld+json": LdJsonObject }
148
+ | { tagName: "meta" | "link"; [name: string]: string }
149
+ | UnsetDescriptor
150
+ | { [name: string]: unknown };
151
+
152
+ /**
153
+ * Meta descriptor that can be sync or async.
154
+ * Use Promise<MetaDescriptorBase> for streaming meta that resolves after initial render.
155
+ */
156
+ export type MetaDescriptor = MetaDescriptorBase | Promise<MetaDescriptorBase>;
157
+
158
+ type LdJsonObject = { [Key in string]: LdJsonValue } & {
159
+ [Key in string]?: LdJsonValue | undefined;
160
+ };
161
+ type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
162
+ type LdJsonPrimitive = string | number | boolean | null;
163
+ type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
@@ -0,0 +1,6 @@
1
+ // Auto-generated by @rangojs/router - do not edit
2
+ export const routes = {
3
+ about: "/about",
4
+ home: "/",
5
+ } as const;
6
+ export type routes = typeof routes;