@rangojs/router 0.0.0-experimental.002d056c
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.
- package/AGENTS.md +9 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1606 -0
- package/dist/vite/index.js +5153 -0
- package/package.json +177 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +253 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +638 -0
- package/src/browser/navigation-client.ts +261 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +582 -0
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +145 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +368 -0
- package/src/browser/react/NavigationProvider.tsx +413 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +464 -0
- package/src/browser/scroll-restoration.ts +397 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +547 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +479 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +982 -0
- package/src/cache/cf/index.ts +29 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +44 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +281 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +397 -0
- package/src/router/lazy-includes.ts +236 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +269 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +193 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +749 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +320 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1242 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1006 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +920 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +48 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +363 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"lib": ["ES2020"],
|
|
5
|
+
"types": ["node", "vite/client"],
|
|
6
|
+
"typeRoots": ["../../node_modules/@types"],
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"outDir": "../../dist/server",
|
|
10
|
+
"composite": true,
|
|
11
|
+
"verbatimModuleSyntax": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["./**/*"]
|
|
14
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rangojs/router/server — Internal subpath
|
|
3
|
+
*
|
|
4
|
+
* This module is NOT user-facing. Import from "@rangojs/router" instead.
|
|
5
|
+
*
|
|
6
|
+
* Exports here are consumed by the Vite plugin (discovery, manifest injection,
|
|
7
|
+
* virtual modules) and the RSC handler internals. They are not part of the
|
|
8
|
+
* public API and may change without notice.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Router registry (used by Vite plugin for build-time discovery)
|
|
12
|
+
export { RSC_ROUTER_BRAND, RouterRegistry } from "./router.js";
|
|
13
|
+
|
|
14
|
+
// Host router registry (used by Vite plugin for host-router lazy discovery)
|
|
15
|
+
export {
|
|
16
|
+
HostRouterRegistry,
|
|
17
|
+
type HostRouterRegistryEntry,
|
|
18
|
+
} from "./host/router.js";
|
|
19
|
+
|
|
20
|
+
// Route map builder (Vite plugin injects these via virtual modules)
|
|
21
|
+
export {
|
|
22
|
+
registerRouteMap,
|
|
23
|
+
setCachedManifest,
|
|
24
|
+
clearCachedManifest,
|
|
25
|
+
clearAllRouterData,
|
|
26
|
+
getGlobalRouteMap,
|
|
27
|
+
getRouterManifest,
|
|
28
|
+
setPrecomputedEntries,
|
|
29
|
+
setRouteTrie,
|
|
30
|
+
setManifestReadyPromise,
|
|
31
|
+
setRouterManifest,
|
|
32
|
+
setRouterTrie,
|
|
33
|
+
setRouterPrecomputedEntries,
|
|
34
|
+
registerRouterManifestLoader,
|
|
35
|
+
ensureRouterManifest,
|
|
36
|
+
} from "./route-map-builder.js";
|
|
37
|
+
|
|
38
|
+
// Loader registry (Vite plugin registers lazy loader imports)
|
|
39
|
+
export {
|
|
40
|
+
registerLoaderById,
|
|
41
|
+
setLoaderImports,
|
|
42
|
+
} from "./server/loader-registry.js";
|
|
43
|
+
|
|
44
|
+
// Request context creation (used by RSC handler, not user-facing)
|
|
45
|
+
export {
|
|
46
|
+
createRequestContext,
|
|
47
|
+
type CreateRequestContextOptions,
|
|
48
|
+
} from "./server/request-context.js";
|
|
49
|
+
|
|
50
|
+
// Component utilities (used internally for server/client boundary checks)
|
|
51
|
+
export { isClientComponent, assertClientComponent } from "./component-utils.js";
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderSegments } from "../segment-system.js";
|
|
3
|
+
import { filterSegmentOrder } from "../browser/react/filter-segment-order.js";
|
|
4
|
+
import { ThemeProvider } from "../theme/ThemeProvider.js";
|
|
5
|
+
import { NonceContext } from "../browser/react/nonce-context.js";
|
|
6
|
+
import { NavigationStoreContext } from "../browser/react/context.js";
|
|
7
|
+
import type { NavigationStoreContextValue } from "../browser/react/context.js";
|
|
8
|
+
import type { HandleData } from "../browser/types.js";
|
|
9
|
+
import type { ErrorPhase } from "../types.js";
|
|
10
|
+
import type { ResolvedSegment } from "../types.js";
|
|
11
|
+
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
12
|
+
import type {
|
|
13
|
+
EventController,
|
|
14
|
+
DerivedNavigationState,
|
|
15
|
+
} from "../browser/event-controller.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Options for injectRSCPayload
|
|
19
|
+
*/
|
|
20
|
+
export interface InjectRSCPayloadOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Nonce for Content Security Policy (CSP)
|
|
23
|
+
*/
|
|
24
|
+
nonce?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for renderToReadableStream from react-dom/server
|
|
29
|
+
*/
|
|
30
|
+
interface RenderToReadableStreamOptions {
|
|
31
|
+
bootstrapScriptContent?: string;
|
|
32
|
+
nonce?: string;
|
|
33
|
+
formState?: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ReadableStream with the allReady promise added by react-dom/server.edge.
|
|
38
|
+
*/
|
|
39
|
+
interface ReactDOMReadableStream extends ReadableStream<Uint8Array> {
|
|
40
|
+
allReady: Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Options for the renderHTML function
|
|
45
|
+
*/
|
|
46
|
+
export interface SSRRenderOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Form state for useActionState progressive enhancement.
|
|
49
|
+
* This is the result of decodeFormState() and should be passed to
|
|
50
|
+
* react-dom's renderToReadableStream to enable useActionState to
|
|
51
|
+
* receive the action result during SSR.
|
|
52
|
+
*/
|
|
53
|
+
formState?: unknown;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Nonce for Content Security Policy (CSP)
|
|
57
|
+
*/
|
|
58
|
+
nonce?: string;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* SSR stream mode.
|
|
62
|
+
*
|
|
63
|
+
* - `"stream"` (default) — start flushing HTML immediately.
|
|
64
|
+
* - `"allReady"` — await `stream.allReady` before returning.
|
|
65
|
+
*/
|
|
66
|
+
streamMode?: import("../router/router-options.js").SSRStreamMode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* SSR dependencies from external packages
|
|
71
|
+
*/
|
|
72
|
+
export interface SSRDependencies<TEnv = unknown> {
|
|
73
|
+
/**
|
|
74
|
+
* createFromReadableStream from @vitejs/plugin-rsc/ssr
|
|
75
|
+
*/
|
|
76
|
+
createFromReadableStream: <T>(
|
|
77
|
+
stream: ReadableStream<Uint8Array>,
|
|
78
|
+
) => Promise<T>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* renderToReadableStream from react-dom/server.edge
|
|
82
|
+
*/
|
|
83
|
+
renderToReadableStream: (
|
|
84
|
+
element: React.ReactNode,
|
|
85
|
+
options?: RenderToReadableStreamOptions,
|
|
86
|
+
) => Promise<ReactDOMReadableStream>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* injectRSCPayload from rsc-html-stream/server
|
|
90
|
+
*/
|
|
91
|
+
injectRSCPayload: (
|
|
92
|
+
rscStream: ReadableStream<Uint8Array>,
|
|
93
|
+
options?: InjectRSCPayloadOptions,
|
|
94
|
+
) => TransformStream<Uint8Array, Uint8Array>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Function to load bootstrap script content
|
|
98
|
+
* Typically: () => import.meta.viteRsc.loadBootstrapScriptContent("index")
|
|
99
|
+
*/
|
|
100
|
+
loadBootstrapScriptContent: () => Promise<string>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Optional callback invoked when an error occurs during SSR rendering.
|
|
104
|
+
*
|
|
105
|
+
* This callback is for notification/logging purposes.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* export const renderHTML = createSSRHandler({
|
|
110
|
+
* // ... other deps
|
|
111
|
+
* onError: (error, context) => {
|
|
112
|
+
* console.error('[SSR] Rendering error:', error);
|
|
113
|
+
* Sentry.captureException(error);
|
|
114
|
+
* },
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
onError?: (error: Error, context: { phase: ErrorPhase }) => void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* RSC payload type (minimal interface for SSR)
|
|
123
|
+
*/
|
|
124
|
+
interface RscPayload {
|
|
125
|
+
metadata?: {
|
|
126
|
+
segments?: ResolvedSegment[];
|
|
127
|
+
rootLayout?: React.ComponentType<{ children: React.ReactNode }>;
|
|
128
|
+
handles?: AsyncGenerator<HandleData, void, unknown>;
|
|
129
|
+
matched?: string[];
|
|
130
|
+
pathname?: string;
|
|
131
|
+
params?: Record<string, string>;
|
|
132
|
+
themeConfig?: ResolvedThemeConfig | null;
|
|
133
|
+
initialTheme?: Theme;
|
|
134
|
+
version?: string;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Consume an async generator and return a Promise that resolves with the final value.
|
|
140
|
+
* Used for SSR where we need to await all handle data before rendering.
|
|
141
|
+
*/
|
|
142
|
+
async function consumeAsyncGenerator(
|
|
143
|
+
generator: AsyncGenerator<HandleData, void, unknown>,
|
|
144
|
+
): Promise<HandleData> {
|
|
145
|
+
let lastData: HandleData = {};
|
|
146
|
+
for await (const data of generator) {
|
|
147
|
+
lastData = data;
|
|
148
|
+
}
|
|
149
|
+
return lastData;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create a minimal event controller for SSR.
|
|
154
|
+
* This provides the correct pathname so useNavigation returns the right value during SSR.
|
|
155
|
+
*/
|
|
156
|
+
function createSsrEventController(opts: {
|
|
157
|
+
pathname: string;
|
|
158
|
+
params?: Record<string, string>;
|
|
159
|
+
handleData?: HandleData;
|
|
160
|
+
matched?: string[];
|
|
161
|
+
}): EventController {
|
|
162
|
+
const location = new URL(opts.pathname, "http://localhost");
|
|
163
|
+
let params = opts.params ?? {};
|
|
164
|
+
const handleState = {
|
|
165
|
+
data: opts.handleData ?? {},
|
|
166
|
+
segmentOrder: filterSegmentOrder(opts.matched ?? []),
|
|
167
|
+
};
|
|
168
|
+
const state: DerivedNavigationState = {
|
|
169
|
+
state: "idle",
|
|
170
|
+
isStreaming: false,
|
|
171
|
+
location,
|
|
172
|
+
pendingUrl: null,
|
|
173
|
+
inflightActions: [],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
getState: () => state,
|
|
178
|
+
getLocation: () => location,
|
|
179
|
+
subscribe: () => () => {},
|
|
180
|
+
getActionState: () => ({
|
|
181
|
+
state: "idle",
|
|
182
|
+
actionId: null,
|
|
183
|
+
payload: null,
|
|
184
|
+
error: null,
|
|
185
|
+
result: null,
|
|
186
|
+
}),
|
|
187
|
+
subscribeToAction: () => () => {},
|
|
188
|
+
subscribeToHandles: () => () => {},
|
|
189
|
+
setHandleData: () => {},
|
|
190
|
+
getHandleState: () => handleState,
|
|
191
|
+
setParams: (nextParams) => {
|
|
192
|
+
params = nextParams;
|
|
193
|
+
},
|
|
194
|
+
getParams: () => params,
|
|
195
|
+
setLocation: () => {},
|
|
196
|
+
startNavigation: () => {
|
|
197
|
+
throw new Error("Navigation not supported during SSR");
|
|
198
|
+
},
|
|
199
|
+
abortNavigation: () => {},
|
|
200
|
+
startAction: () => {
|
|
201
|
+
throw new Error("Actions not supported during SSR");
|
|
202
|
+
},
|
|
203
|
+
abortAllActions: () => {},
|
|
204
|
+
getCurrentNavigation: () => null,
|
|
205
|
+
getInflightActions: () => new Map(),
|
|
206
|
+
hadAnyConcurrentActions: () => false,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create an SSR handler that converts RSC streams to HTML.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```tsx
|
|
215
|
+
* import { createSSRHandler } from "rsc-router/ssr";
|
|
216
|
+
* import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
|
|
217
|
+
* import { renderToReadableStream } from "react-dom/server.edge";
|
|
218
|
+
* import { injectRSCPayload } from "rsc-html-stream/server";
|
|
219
|
+
*
|
|
220
|
+
* export const renderHTML = createSSRHandler({
|
|
221
|
+
* createFromReadableStream,
|
|
222
|
+
* renderToReadableStream,
|
|
223
|
+
* injectRSCPayload,
|
|
224
|
+
* loadBootstrapScriptContent: () =>
|
|
225
|
+
* import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
226
|
+
* });
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
|
|
230
|
+
const {
|
|
231
|
+
createFromReadableStream,
|
|
232
|
+
renderToReadableStream,
|
|
233
|
+
injectRSCPayload,
|
|
234
|
+
loadBootstrapScriptContent,
|
|
235
|
+
onError,
|
|
236
|
+
} = deps;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Render RSC stream to HTML stream
|
|
240
|
+
*
|
|
241
|
+
* @param rscStream - The RSC stream to render
|
|
242
|
+
* @param options - Optional render options including formState for useActionState and nonce for CSP
|
|
243
|
+
*/
|
|
244
|
+
return async function renderHTML(
|
|
245
|
+
rscStream: ReadableStream<Uint8Array>,
|
|
246
|
+
options?: SSRRenderOptions,
|
|
247
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
248
|
+
const { nonce, formState, streamMode } = options ?? {};
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
// Tee the stream:
|
|
252
|
+
// - rscStream1: For SSR rendering (deserialize to React VDOM)
|
|
253
|
+
// - rscStream2: For browser hydration (inject as __FLIGHT_DATA__)
|
|
254
|
+
const [rscStream1, rscStream2] = rscStream.tee();
|
|
255
|
+
|
|
256
|
+
// Deserialize RSC stream to React tree
|
|
257
|
+
let payload: Promise<RscPayload> | undefined;
|
|
258
|
+
let handlesPromise: Promise<HandleData> | undefined;
|
|
259
|
+
let ssrContextValue: NavigationStoreContextValue | undefined;
|
|
260
|
+
function SsrRoot() {
|
|
261
|
+
payload ??= createFromReadableStream<RscPayload>(rscStream1);
|
|
262
|
+
const resolved = React.use(payload);
|
|
263
|
+
const themeConfig = resolved.metadata?.themeConfig ?? null;
|
|
264
|
+
const pathname = resolved.metadata?.pathname ?? "/";
|
|
265
|
+
|
|
266
|
+
// Await handles before creating SSR event controller so hooks can
|
|
267
|
+
// read request-local handle data via NavigationStoreContext.
|
|
268
|
+
// The handles property is an async generator that yields on each push
|
|
269
|
+
// Memoize the promise since async generators can only be iterated once
|
|
270
|
+
let handleData: HandleData = {};
|
|
271
|
+
if (resolved.metadata?.handles) {
|
|
272
|
+
handlesPromise ??= consumeAsyncGenerator(resolved.metadata.handles);
|
|
273
|
+
handleData = React.use(handlesPromise);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Create SSR context with request-local pathname/params/handles.
|
|
277
|
+
ssrContextValue ??= {
|
|
278
|
+
store: null as any,
|
|
279
|
+
eventController: createSsrEventController({
|
|
280
|
+
pathname,
|
|
281
|
+
params: resolved.metadata?.params,
|
|
282
|
+
handleData,
|
|
283
|
+
matched: resolved.metadata?.matched,
|
|
284
|
+
}),
|
|
285
|
+
navigate: async () => {},
|
|
286
|
+
refresh: async () => {},
|
|
287
|
+
version: resolved.metadata?.version,
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Build content tree from segments.
|
|
291
|
+
// Order must match NavigationProvider: NavigationStoreContext > ThemeProvider > content
|
|
292
|
+
const reconstructedRoot = renderSegments(
|
|
293
|
+
resolved.metadata?.segments ?? [],
|
|
294
|
+
{
|
|
295
|
+
rootLayout: resolved.metadata?.rootLayout,
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
let content: React.ReactNode =
|
|
299
|
+
reconstructedRoot instanceof Promise
|
|
300
|
+
? React.use(reconstructedRoot)
|
|
301
|
+
: reconstructedRoot;
|
|
302
|
+
|
|
303
|
+
// Wrap content with ThemeProvider if theme is enabled
|
|
304
|
+
if (themeConfig) {
|
|
305
|
+
content = (
|
|
306
|
+
<ThemeProvider
|
|
307
|
+
config={themeConfig}
|
|
308
|
+
initialTheme={resolved.metadata?.initialTheme}
|
|
309
|
+
>
|
|
310
|
+
{content}
|
|
311
|
+
</ThemeProvider>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Wrap with NonceContext so client components (e.g. MetaTags) can
|
|
316
|
+
// apply CSP nonces to inline scripts during SSR. Always present to
|
|
317
|
+
// match the browser-side NavigationProvider tree shape for hydration.
|
|
318
|
+
content = (
|
|
319
|
+
<NonceContext.Provider value={nonce}>{content}</NonceContext.Provider>
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Wrap with NavigationStoreContext for useNavigation hook
|
|
323
|
+
return (
|
|
324
|
+
<NavigationStoreContext.Provider value={ssrContextValue!}>
|
|
325
|
+
{content}
|
|
326
|
+
</NavigationStoreContext.Provider>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Get bootstrap script content
|
|
331
|
+
const bootstrapScriptContent = await loadBootstrapScriptContent();
|
|
332
|
+
|
|
333
|
+
// Render React tree to HTML stream
|
|
334
|
+
// Pass formState for useActionState progressive enhancement if provided
|
|
335
|
+
// Pass nonce for CSP if provided
|
|
336
|
+
const htmlStream = await renderToReadableStream(<SsrRoot />, {
|
|
337
|
+
bootstrapScriptContent,
|
|
338
|
+
formState,
|
|
339
|
+
nonce,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Wait for all Suspense boundaries to resolve when streamMode is "allReady".
|
|
343
|
+
// This buffers the entire HTML before flushing — used for bots that
|
|
344
|
+
// cannot process streamed HTML.
|
|
345
|
+
if (streamMode === "allReady") {
|
|
346
|
+
await htmlStream.allReady;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Inject RSC payload into HTML as <script nonce="...">__FLIGHT_DATA__</script>
|
|
350
|
+
return htmlStream.pipeThrough(injectRSCPayload(rscStream2, { nonce }));
|
|
351
|
+
} catch (error) {
|
|
352
|
+
// Invoke onError callback if provided
|
|
353
|
+
if (onError) {
|
|
354
|
+
const errorObj =
|
|
355
|
+
error instanceof Error ? error : new Error(String(error));
|
|
356
|
+
try {
|
|
357
|
+
onError(errorObj, { phase: "rendering" });
|
|
358
|
+
} catch (callbackError) {
|
|
359
|
+
console.error("[SSRHandler.onError] Callback error:", callbackError);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static handler definition for build-time rendering of individual segments.
|
|
3
|
+
*
|
|
4
|
+
* Static wraps a handler so that in production the segment is
|
|
5
|
+
* rendered once at build time. The handler is then replaced with a static
|
|
6
|
+
* asset import -- no runtime store lookup needed.
|
|
7
|
+
*
|
|
8
|
+
* In dev mode, Static behaves as a normal handler: the wrapped
|
|
9
|
+
* function runs on every request, identical to a regular layout/path handler.
|
|
10
|
+
*
|
|
11
|
+
* The $$id is auto-generated by the Vite exposeInternalIds plugin based on
|
|
12
|
+
* file path and export name. No manual naming required.
|
|
13
|
+
*
|
|
14
|
+
* Key difference from Prerender:
|
|
15
|
+
* - Prerender: route-scoped, produces URLs via getParams, renders subtree per-params
|
|
16
|
+
* - Static: segment-scoped, renders once, no URLs, no params
|
|
17
|
+
*
|
|
18
|
+
* Works on: layout(), parallel(), and path().
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* export const DocsNav = Static((ctx) => <Nav docs={readDocsSync()} />);
|
|
23
|
+
* export const DocShell = Static((ctx) => <Shell />);
|
|
24
|
+
*
|
|
25
|
+
* urls(({ path, layout }) => [
|
|
26
|
+
* layout(DocsNav, () => [
|
|
27
|
+
* path("/getting-started", DocShell, { name: "doc.gs" }),
|
|
28
|
+
* path("/:slug", Prerender(getParams, DocPageHandler)),
|
|
29
|
+
* ]),
|
|
30
|
+
* ]);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import type { ReactNode } from "react";
|
|
34
|
+
import type { Handler } from "./types.js";
|
|
35
|
+
import type { PrerenderOptions, StaticBuildContext } from "./prerender.js";
|
|
36
|
+
import { isCachedFunction } from "./cache/taint.js";
|
|
37
|
+
|
|
38
|
+
// -- Types ------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export interface StaticHandlerDefinition<
|
|
41
|
+
TParams extends Record<string, any> = any,
|
|
42
|
+
> {
|
|
43
|
+
readonly __brand: "staticHandler";
|
|
44
|
+
/** Auto-generated unique ID (injected by Vite plugin). */
|
|
45
|
+
$$id: string;
|
|
46
|
+
/** In dev mode, the actual handler function that layout/path/parallel can call. */
|
|
47
|
+
handler: Handler<TParams>;
|
|
48
|
+
/** Static handler options (passthrough support). */
|
|
49
|
+
options?: PrerenderOptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// -- Function ---------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export function Static<TParams extends Record<string, any> = {}>(
|
|
55
|
+
handler: (ctx: StaticBuildContext) => ReactNode | Promise<ReactNode>,
|
|
56
|
+
options?: PrerenderOptions,
|
|
57
|
+
__injectedId?: string,
|
|
58
|
+
): StaticHandlerDefinition<TParams>;
|
|
59
|
+
|
|
60
|
+
// -- Implementation ---------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export function Static<TParams extends Record<string, any>>(
|
|
63
|
+
handler: Function,
|
|
64
|
+
optionsOrId?: PrerenderOptions | string,
|
|
65
|
+
maybeId?: string,
|
|
66
|
+
): StaticHandlerDefinition<TParams> {
|
|
67
|
+
if (isCachedFunction(handler)) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
'A "use cache" function cannot be used as a Static() handler. ' +
|
|
70
|
+
"Static handlers are rendered once at build time. Remove the " +
|
|
71
|
+
'"use cache" directive — Static already provides caching.',
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let options: PrerenderOptions | undefined;
|
|
76
|
+
let id: string;
|
|
77
|
+
|
|
78
|
+
if (typeof optionsOrId === "string") {
|
|
79
|
+
id = optionsOrId;
|
|
80
|
+
} else {
|
|
81
|
+
options = optionsOrId as PrerenderOptions | undefined;
|
|
82
|
+
id = maybeId ?? "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!id) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"[rsc-router] Static: missing $$id. " +
|
|
88
|
+
"Ensure the exposeInternalIds Vite plugin is configured.",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
__brand: "staticHandler" as const,
|
|
94
|
+
$$id: id,
|
|
95
|
+
handler: handler as Handler<TParams>,
|
|
96
|
+
...(options ? { options } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// -- Type guard -------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Type guard to check if a value is a StaticHandlerDefinition.
|
|
104
|
+
*/
|
|
105
|
+
export function isStaticHandler(
|
|
106
|
+
value: unknown,
|
|
107
|
+
): value is StaticHandlerDefinition {
|
|
108
|
+
return (
|
|
109
|
+
typeof value === "object" &&
|
|
110
|
+
value !== null &&
|
|
111
|
+
"__brand" in value &&
|
|
112
|
+
(value as { __brand: unknown }).__brand === "staticHandler"
|
|
113
|
+
);
|
|
114
|
+
}
|