@rangojs/router 0.0.0-experimental.2

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 (155) hide show
  1. package/CLAUDE.md +7 -0
  2. package/README.md +19 -0
  3. package/dist/vite/index.js +1298 -0
  4. package/package.json +140 -0
  5. package/skills/caching/SKILL.md +319 -0
  6. package/skills/document-cache/SKILL.md +152 -0
  7. package/skills/hooks/SKILL.md +359 -0
  8. package/skills/intercept/SKILL.md +292 -0
  9. package/skills/layout/SKILL.md +216 -0
  10. package/skills/loader/SKILL.md +365 -0
  11. package/skills/middleware/SKILL.md +442 -0
  12. package/skills/parallel/SKILL.md +255 -0
  13. package/skills/route/SKILL.md +141 -0
  14. package/skills/router-setup/SKILL.md +403 -0
  15. package/skills/theme/SKILL.md +54 -0
  16. package/skills/typesafety/SKILL.md +352 -0
  17. package/src/__mocks__/version.ts +6 -0
  18. package/src/__tests__/component-utils.test.ts +76 -0
  19. package/src/__tests__/route-definition.test.ts +63 -0
  20. package/src/__tests__/urls.test.tsx +436 -0
  21. package/src/browser/event-controller.ts +876 -0
  22. package/src/browser/index.ts +18 -0
  23. package/src/browser/link-interceptor.ts +121 -0
  24. package/src/browser/lru-cache.ts +69 -0
  25. package/src/browser/merge-segment-loaders.ts +126 -0
  26. package/src/browser/navigation-bridge.ts +893 -0
  27. package/src/browser/navigation-client.ts +162 -0
  28. package/src/browser/navigation-store.ts +823 -0
  29. package/src/browser/partial-update.ts +559 -0
  30. package/src/browser/react/Link.tsx +248 -0
  31. package/src/browser/react/NavigationProvider.tsx +275 -0
  32. package/src/browser/react/ScrollRestoration.tsx +94 -0
  33. package/src/browser/react/context.ts +53 -0
  34. package/src/browser/react/index.ts +52 -0
  35. package/src/browser/react/location-state-shared.ts +120 -0
  36. package/src/browser/react/location-state.ts +62 -0
  37. package/src/browser/react/use-action.ts +240 -0
  38. package/src/browser/react/use-client-cache.ts +56 -0
  39. package/src/browser/react/use-handle.ts +178 -0
  40. package/src/browser/react/use-href.tsx +208 -0
  41. package/src/browser/react/use-link-status.ts +134 -0
  42. package/src/browser/react/use-navigation.ts +150 -0
  43. package/src/browser/react/use-segments.ts +188 -0
  44. package/src/browser/request-controller.ts +164 -0
  45. package/src/browser/rsc-router.tsx +353 -0
  46. package/src/browser/scroll-restoration.ts +324 -0
  47. package/src/browser/server-action-bridge.ts +747 -0
  48. package/src/browser/shallow.ts +35 -0
  49. package/src/browser/types.ts +464 -0
  50. package/src/cache/__tests__/document-cache.test.ts +522 -0
  51. package/src/cache/__tests__/memory-segment-store.test.ts +487 -0
  52. package/src/cache/__tests__/memory-store.test.ts +484 -0
  53. package/src/cache/cache-scope.ts +565 -0
  54. package/src/cache/cf/__tests__/cf-cache-store.test.ts +428 -0
  55. package/src/cache/cf/cf-cache-store.ts +428 -0
  56. package/src/cache/cf/index.ts +19 -0
  57. package/src/cache/document-cache.ts +340 -0
  58. package/src/cache/index.ts +58 -0
  59. package/src/cache/memory-segment-store.ts +150 -0
  60. package/src/cache/memory-store.ts +253 -0
  61. package/src/cache/types.ts +387 -0
  62. package/src/client.rsc.tsx +88 -0
  63. package/src/client.tsx +621 -0
  64. package/src/component-utils.ts +76 -0
  65. package/src/components/DefaultDocument.tsx +23 -0
  66. package/src/default-error-boundary.tsx +88 -0
  67. package/src/deps/browser.ts +8 -0
  68. package/src/deps/html-stream-client.ts +2 -0
  69. package/src/deps/html-stream-server.ts +2 -0
  70. package/src/deps/rsc.ts +10 -0
  71. package/src/deps/ssr.ts +2 -0
  72. package/src/errors.ts +259 -0
  73. package/src/handle.ts +120 -0
  74. package/src/handles/MetaTags.tsx +193 -0
  75. package/src/handles/index.ts +6 -0
  76. package/src/handles/meta.ts +247 -0
  77. package/src/href-client.ts +128 -0
  78. package/src/href-context.ts +33 -0
  79. package/src/href.ts +177 -0
  80. package/src/index.rsc.ts +79 -0
  81. package/src/index.ts +87 -0
  82. package/src/loader.rsc.ts +204 -0
  83. package/src/loader.ts +47 -0
  84. package/src/network-error-thrower.tsx +21 -0
  85. package/src/outlet-context.ts +15 -0
  86. package/src/root-error-boundary.tsx +277 -0
  87. package/src/route-content-wrapper.tsx +198 -0
  88. package/src/route-definition.ts +1371 -0
  89. package/src/route-map-builder.ts +146 -0
  90. package/src/route-types.ts +198 -0
  91. package/src/route-utils.ts +89 -0
  92. package/src/router/__tests__/match-context.test.ts +104 -0
  93. package/src/router/__tests__/match-pipelines.test.ts +537 -0
  94. package/src/router/__tests__/match-result.test.ts +566 -0
  95. package/src/router/__tests__/on-error.test.ts +935 -0
  96. package/src/router/__tests__/pattern-matching.test.ts +577 -0
  97. package/src/router/error-handling.ts +287 -0
  98. package/src/router/handler-context.ts +158 -0
  99. package/src/router/loader-resolution.ts +326 -0
  100. package/src/router/manifest.ts +138 -0
  101. package/src/router/match-context.ts +264 -0
  102. package/src/router/match-middleware/background-revalidation.ts +236 -0
  103. package/src/router/match-middleware/cache-lookup.ts +261 -0
  104. package/src/router/match-middleware/cache-store.ts +266 -0
  105. package/src/router/match-middleware/index.ts +81 -0
  106. package/src/router/match-middleware/intercept-resolution.ts +268 -0
  107. package/src/router/match-middleware/segment-resolution.ts +174 -0
  108. package/src/router/match-pipelines.ts +214 -0
  109. package/src/router/match-result.ts +214 -0
  110. package/src/router/metrics.ts +62 -0
  111. package/src/router/middleware.test.ts +1355 -0
  112. package/src/router/middleware.ts +748 -0
  113. package/src/router/pattern-matching.ts +272 -0
  114. package/src/router/revalidation.ts +190 -0
  115. package/src/router/router-context.ts +299 -0
  116. package/src/router/types.ts +96 -0
  117. package/src/router.ts +3876 -0
  118. package/src/rsc/__tests__/helpers.test.ts +175 -0
  119. package/src/rsc/handler.ts +1060 -0
  120. package/src/rsc/helpers.ts +64 -0
  121. package/src/rsc/index.ts +56 -0
  122. package/src/rsc/nonce.ts +18 -0
  123. package/src/rsc/types.ts +237 -0
  124. package/src/segment-system.tsx +456 -0
  125. package/src/server/__tests__/request-context.test.ts +171 -0
  126. package/src/server/context.ts +417 -0
  127. package/src/server/handle-store.ts +230 -0
  128. package/src/server/loader-registry.ts +174 -0
  129. package/src/server/request-context.ts +554 -0
  130. package/src/server/root-layout.tsx +10 -0
  131. package/src/server/tsconfig.json +14 -0
  132. package/src/server.ts +146 -0
  133. package/src/ssr/__tests__/ssr-handler.test.tsx +188 -0
  134. package/src/ssr/index.tsx +234 -0
  135. package/src/theme/ThemeProvider.tsx +291 -0
  136. package/src/theme/ThemeScript.tsx +61 -0
  137. package/src/theme/__tests__/theme.test.ts +120 -0
  138. package/src/theme/constants.ts +55 -0
  139. package/src/theme/index.ts +58 -0
  140. package/src/theme/theme-context.ts +70 -0
  141. package/src/theme/theme-script.ts +152 -0
  142. package/src/theme/types.ts +182 -0
  143. package/src/theme/use-theme.ts +44 -0
  144. package/src/types.ts +1561 -0
  145. package/src/urls.ts +726 -0
  146. package/src/use-loader.tsx +346 -0
  147. package/src/vite/__tests__/expose-loader-id.test.ts +117 -0
  148. package/src/vite/expose-action-id.ts +344 -0
  149. package/src/vite/expose-handle-id.ts +209 -0
  150. package/src/vite/expose-loader-id.ts +357 -0
  151. package/src/vite/expose-location-state-id.ts +177 -0
  152. package/src/vite/index.ts +787 -0
  153. package/src/vite/package-resolution.ts +125 -0
  154. package/src/vite/version.d.ts +12 -0
  155. package/src/vite/virtual-entries.ts +109 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * RSC Handler Helpers
3
+ *
4
+ * Utility functions for RSC request handling.
5
+ */
6
+
7
+ import { getRequestContext } from "../server/request-context.js";
8
+
9
+ /**
10
+ * Check if a request body has content to decode
11
+ */
12
+ export function hasBodyContent(body: FormData | string): boolean {
13
+ if (body instanceof FormData) {
14
+ let hasContent = false;
15
+ body.forEach(() => {
16
+ hasContent = true;
17
+ });
18
+ return hasContent;
19
+ }
20
+ return typeof body === "string" && body.length > 0;
21
+ }
22
+
23
+ /**
24
+ * Create a Response with headers merged from the request context's stub response.
25
+ * This ensures headers/cookies set during middleware or handler execution are included.
26
+ * Also triggers any registered onResponse callbacks.
27
+ */
28
+ export function createResponseWithMergedHeaders(
29
+ body: BodyInit | null,
30
+ init: ResponseInit
31
+ ): Response {
32
+ const ctx = getRequestContext();
33
+ if (!ctx) {
34
+ return new Response(body, init);
35
+ }
36
+
37
+ // Merge headers from stub response into the new response
38
+ const mergedHeaders = new Headers(init.headers);
39
+ ctx.res.headers.forEach((value, name) => {
40
+ if (name.toLowerCase() === "set-cookie") {
41
+ mergedHeaders.append(name, value);
42
+ } else if (!mergedHeaders.has(name)) {
43
+ // Only set if not already present in init.headers
44
+ mergedHeaders.set(name, value);
45
+ }
46
+ });
47
+
48
+ // Use ctx.res.status if it was set (e.g., 404 for notFound, 500 for error)
49
+ // Otherwise use the status from init
50
+ const status = ctx.res.status !== 200 ? ctx.res.status : init.status;
51
+
52
+ let response = new Response(body, {
53
+ ...init,
54
+ status,
55
+ headers: mergedHeaders,
56
+ });
57
+
58
+ // Run onResponse callbacks - each can inspect/modify the response
59
+ for (const callback of ctx._onResponseCallbacks) {
60
+ response = callback(response) ?? response;
61
+ }
62
+
63
+ return response;
64
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * RSC Router - RSC Handler Entry Point
3
+ *
4
+ * This module provides the RSC request handler for server-side rendering,
5
+ * server actions, loader fetching, and progressive enhancement.
6
+ *
7
+ * @example Basic usage
8
+ * ```tsx
9
+ * import { createRSCHandler } from "rsc-router/rsc";
10
+ * import { router } from "./router.js";
11
+ *
12
+ * export default createRSCHandler({ router });
13
+ * ```
14
+ */
15
+
16
+ // Re-export handler
17
+ export { createRSCHandler } from "./handler.js";
18
+
19
+ // Re-export types
20
+ export type {
21
+ RscPayload,
22
+ ReactFormState,
23
+ RSCDependencies,
24
+ SSRRenderOptions,
25
+ SSRModule,
26
+ LoadSSRModule,
27
+ CreateRSCHandlerOptions,
28
+ HandlerCacheConfig,
29
+ NonceProvider,
30
+ } from "./types.js";
31
+
32
+ // Re-export HandleStore types for consumers who need custom handling
33
+ export {
34
+ createHandleStore,
35
+ type HandleStore,
36
+ type HandleData,
37
+ } from "../server/handle-store.js";
38
+
39
+ // Re-export request context utilities for server-side access to env/request/params
40
+ export {
41
+ getRequestContext,
42
+ requireRequestContext,
43
+ setRequestContextParams,
44
+ } from "../server/request-context.js";
45
+
46
+ // Re-export cache store types and implementations
47
+ export type {
48
+ SegmentCacheStore,
49
+ CachedEntryData,
50
+ CachedEntryResult,
51
+ SegmentCacheProvider,
52
+ SegmentHandleData,
53
+ } from "../cache/types.js";
54
+
55
+ export { MemorySegmentCacheStore } from "../cache/memory-segment-store.js";
56
+ export { CFCacheStore, type CFCacheStoreOptions } from "../cache/cf/index.js";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Nonce generation for Content Security Policy (CSP)
3
+ */
4
+
5
+ /**
6
+ * Generate a cryptographic nonce for CSP.
7
+ * Returns a 16-byte random value encoded as base64.
8
+ */
9
+ export function generateNonce(): string {
10
+ const array = new Uint8Array(16);
11
+ crypto.getRandomValues(array);
12
+ // Convert to base64
13
+ let binary = "";
14
+ for (let i = 0; i < array.length; i++) {
15
+ binary += String.fromCharCode(array[i]);
16
+ }
17
+ return btoa(binary);
18
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * RSC Handler Types
3
+ *
4
+ * Type definitions for the RSC request handler, payload structures,
5
+ * and SSR integration.
6
+ */
7
+
8
+ import type { ResolvedSegment, SlotState } from "../types.js";
9
+ import type { HandleData } from "../server/handle-store.js";
10
+ import type { RSCRouter } from "../router.js";
11
+ import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
12
+
13
+ /**
14
+ * RSC payload sent to the client
15
+ */
16
+ export interface RscPayload {
17
+ root: React.ReactNode | Promise<React.ReactNode>;
18
+ metadata?: {
19
+ pathname: string;
20
+ segments: ResolvedSegment[];
21
+ isPartial?: boolean;
22
+ isError?: boolean;
23
+ matched?: string[];
24
+ diff?: string[];
25
+ slots?: Record<string, SlotState>;
26
+ /** Root layout component for browser-side re-renders (client component reference) */
27
+ rootLayout?: React.ComponentType<{ children: React.ReactNode }>;
28
+ /** Handle data accumulated across route segments (async generator that yields on each push) */
29
+ handles?: AsyncGenerator<HandleData, void, unknown>;
30
+ /** RSC version string for cache invalidation */
31
+ version?: string;
32
+ /** Theme configuration for FOUC prevention */
33
+ themeConfig?: ResolvedThemeConfig | null;
34
+ /** Initial theme from cookie (for SSR hydration) */
35
+ initialTheme?: Theme;
36
+ /** Route map for useHref() - maps route names to URL patterns */
37
+ routeMap?: Record<string, string>;
38
+ /** Current matched route name (for local name resolution in useHref) */
39
+ routeName?: string;
40
+ };
41
+ returnValue?: { ok: boolean; data: unknown };
42
+ formState?: unknown;
43
+ }
44
+
45
+ /**
46
+ * React form state type for useActionState progressive enhancement
47
+ */
48
+ export type ReactFormState = unknown;
49
+
50
+ /**
51
+ * RSC dependencies from @vitejs/plugin-rsc/rsc
52
+ */
53
+ export interface RSCDependencies {
54
+ /**
55
+ * renderToReadableStream from @vitejs/plugin-rsc/rsc
56
+ */
57
+ renderToReadableStream: <T>(
58
+ payload: T,
59
+ options?: { temporaryReferences?: unknown }
60
+ ) => ReadableStream<Uint8Array>;
61
+
62
+ /**
63
+ * decodeReply from @vitejs/plugin-rsc/rsc
64
+ */
65
+ decodeReply: (
66
+ body: FormData | string,
67
+ options?: { temporaryReferences?: unknown }
68
+ ) => Promise<unknown[]>;
69
+
70
+ /**
71
+ * createTemporaryReferenceSet from @vitejs/plugin-rsc/rsc
72
+ */
73
+ createTemporaryReferenceSet: () => unknown;
74
+
75
+ /**
76
+ * loadServerAction from @vitejs/plugin-rsc/rsc
77
+ */
78
+ loadServerAction: (actionId: string) => Promise<Function>;
79
+
80
+ /**
81
+ * decodeAction from @vitejs/plugin-rsc/rsc
82
+ * Decodes a FormData into a bound action function (for useActionState forms)
83
+ */
84
+ decodeAction: (body: FormData) => Promise<() => Promise<unknown>>;
85
+
86
+ /**
87
+ * decodeFormState from @vitejs/plugin-rsc/rsc
88
+ * Decodes the action result into a ReactFormState for useActionState progressive enhancement
89
+ */
90
+ decodeFormState: (
91
+ actionResult: unknown,
92
+ body: FormData
93
+ ) => Promise<ReactFormState | null>;
94
+ }
95
+
96
+ /**
97
+ * Options for SSR HTML rendering
98
+ */
99
+ export interface SSRRenderOptions {
100
+ /**
101
+ * Form state for useActionState progressive enhancement.
102
+ * This is the result of decodeFormState() and should be passed to
103
+ * react-dom's renderToReadableStream to enable useActionState to
104
+ * receive the action result during SSR.
105
+ */
106
+ formState?: ReactFormState | null;
107
+
108
+ /**
109
+ * Nonce for Content Security Policy (CSP)
110
+ */
111
+ nonce?: string;
112
+ }
113
+
114
+ /**
115
+ * SSR module interface for HTML rendering
116
+ */
117
+ export interface SSRModule {
118
+ renderHTML: (
119
+ rscStream: ReadableStream<Uint8Array>,
120
+ options?: SSRRenderOptions
121
+ ) => Promise<ReadableStream<Uint8Array>>;
122
+ }
123
+
124
+ /**
125
+ * Function to load SSR module dynamically
126
+ */
127
+ export type LoadSSRModule = () => Promise<SSRModule>;
128
+
129
+ /**
130
+ * Cache configuration for handler.
131
+ * TTL is configured via store.defaults or cache() boundaries.
132
+ */
133
+ export interface HandlerCacheConfig {
134
+ /** Cache store implementation */
135
+ store: import("../cache/types.js").SegmentCacheStore;
136
+ /** Enable/disable caching (default: true) */
137
+ enabled?: boolean;
138
+ }
139
+
140
+ /**
141
+ * Nonce provider function type.
142
+ * Can return a nonce string, or true to auto-generate one.
143
+ */
144
+ export type NonceProvider<TEnv = unknown> = (
145
+ request: Request,
146
+ env: TEnv
147
+ ) => string | true | Promise<string | true>;
148
+
149
+ /**
150
+ * Options for creating an RSC handler
151
+ */
152
+ export interface CreateRSCHandlerOptions<
153
+ TEnv = unknown,
154
+ TRoutes extends Record<string, string> = Record<string, string>,
155
+ > {
156
+ /**
157
+ * The RSC router instance
158
+ */
159
+ router: RSCRouter<TEnv, TRoutes>;
160
+
161
+ /**
162
+ * RSC dependencies from @vitejs/plugin-rsc/rsc.
163
+ * Defaults to the exports from @vitejs/plugin-rsc/rsc.
164
+ */
165
+ deps?: RSCDependencies;
166
+
167
+ /**
168
+ * Function to load the SSR module for HTML rendering.
169
+ * Defaults to: () => import.meta.viteRsc.loadModule("ssr", "index")
170
+ */
171
+ loadSSRModule?: LoadSSRModule;
172
+
173
+ /**
174
+ * Cache configuration for segment caching.
175
+ *
176
+ * Can be a static config object or a function that receives the env
177
+ * (useful for accessing Cloudflare bindings).
178
+ *
179
+ * If not provided, caching is disabled. TTL is configured via store.defaults
180
+ * or cache() boundaries in the route definition.
181
+ *
182
+ * @example Static config
183
+ * ```typescript
184
+ * cache: {
185
+ * store: new MemorySegmentCacheStore({ defaults: { ttl: 60 } }),
186
+ * }
187
+ * ```
188
+ *
189
+ * @example Dynamic config with env
190
+ * ```typescript
191
+ * cache: (env) => ({
192
+ * store: new KVSegmentCacheStore(env.Bindings.MY_CACHE, { defaults: { ttl: 60 } }),
193
+ * })
194
+ * ```
195
+ */
196
+ cache?: HandlerCacheConfig | ((env: TEnv) => HandlerCacheConfig);
197
+
198
+ /**
199
+ * RSC version string included in metadata.
200
+ * The browser sends this back on partial requests to detect version mismatches.
201
+ *
202
+ * Defaults to the auto-generated VERSION from `rsc-router:version` virtual module.
203
+ * Only set this if you need a custom versioning strategy.
204
+ *
205
+ * @default VERSION from rsc-router:version
206
+ */
207
+ version?: string;
208
+
209
+ /**
210
+ * Nonce provider for Content Security Policy (CSP).
211
+ *
212
+ * Can be:
213
+ * - A function that returns a nonce string
214
+ * - A function that returns `true` to auto-generate a nonce
215
+ * - Undefined to disable nonce (default)
216
+ *
217
+ * The nonce will be applied to inline scripts injected by the RSC payload.
218
+ * It's also available to middleware via `ctx.get('nonce')`.
219
+ *
220
+ * @example Auto-generate nonce
221
+ * ```tsx
222
+ * createRSCHandler({
223
+ * router,
224
+ * nonce: () => true,
225
+ * });
226
+ * ```
227
+ *
228
+ * @example Custom nonce from request context
229
+ * ```tsx
230
+ * createRSCHandler({
231
+ * router,
232
+ * nonce: (request, env) => env.nonce,
233
+ * });
234
+ * ```
235
+ */
236
+ nonce?: NonceProvider<TEnv>;
237
+ }