@rangojs/router 0.0.0-experimental.15 → 0.0.0-experimental.151
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 +13 -0
- package/README.md +308 -451
- package/dist/__internal.d.ts +83 -0
- package/dist/__internal.d.ts.map +1 -0
- package/dist/__internal.js +19 -0
- package/dist/__internal.js.map +1 -0
- package/dist/__mocks__/version.d.ts +7 -0
- package/dist/__mocks__/version.d.ts.map +1 -0
- package/dist/__mocks__/version.js +7 -0
- package/dist/__mocks__/version.js.map +1 -0
- package/dist/__tests__/client-href.test.d.ts +2 -0
- package/dist/__tests__/client-href.test.d.ts.map +1 -0
- package/dist/__tests__/client-href.test.js +74 -0
- package/dist/__tests__/client-href.test.js.map +1 -0
- package/dist/__tests__/component-utils.test.d.ts +2 -0
- package/dist/__tests__/component-utils.test.d.ts.map +1 -0
- package/dist/__tests__/component-utils.test.js +51 -0
- package/dist/__tests__/component-utils.test.js.map +1 -0
- package/dist/__tests__/event-controller.test.d.ts +2 -0
- package/dist/__tests__/event-controller.test.d.ts.map +1 -0
- package/dist/__tests__/event-controller.test.js +538 -0
- package/dist/__tests__/event-controller.test.js.map +1 -0
- package/dist/__tests__/helpers/route-tree.d.ts +118 -0
- package/dist/__tests__/helpers/route-tree.d.ts.map +1 -0
- package/dist/__tests__/helpers/route-tree.js +374 -0
- package/dist/__tests__/helpers/route-tree.js.map +1 -0
- package/dist/__tests__/match-result.test.d.ts +2 -0
- package/dist/__tests__/match-result.test.d.ts.map +1 -0
- package/dist/__tests__/match-result.test.js +154 -0
- package/dist/__tests__/match-result.test.js.map +1 -0
- package/dist/__tests__/navigation-store.test.d.ts +2 -0
- package/dist/__tests__/navigation-store.test.d.ts.map +1 -0
- package/dist/__tests__/navigation-store.test.js +440 -0
- package/dist/__tests__/navigation-store.test.js.map +1 -0
- package/dist/__tests__/partial-update.test.d.ts +2 -0
- package/dist/__tests__/partial-update.test.d.ts.map +1 -0
- package/dist/__tests__/partial-update.test.js +1009 -0
- package/dist/__tests__/partial-update.test.js.map +1 -0
- package/dist/__tests__/reverse-types.test.d.ts +8 -0
- package/dist/__tests__/reverse-types.test.d.ts.map +1 -0
- package/dist/__tests__/reverse-types.test.js +656 -0
- package/dist/__tests__/reverse-types.test.js.map +1 -0
- package/dist/__tests__/route-definition.test.d.ts +2 -0
- package/dist/__tests__/route-definition.test.d.ts.map +1 -0
- package/dist/__tests__/route-definition.test.js +55 -0
- package/dist/__tests__/route-definition.test.js.map +1 -0
- package/dist/__tests__/router-helpers.test.d.ts +2 -0
- package/dist/__tests__/router-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/router-helpers.test.js +377 -0
- package/dist/__tests__/router-helpers.test.js.map +1 -0
- package/dist/__tests__/router-integration-2.test.d.ts +2 -0
- package/dist/__tests__/router-integration-2.test.d.ts.map +1 -0
- package/dist/__tests__/router-integration-2.test.js +426 -0
- package/dist/__tests__/router-integration-2.test.js.map +1 -0
- package/dist/__tests__/router-integration.test.d.ts +2 -0
- package/dist/__tests__/router-integration.test.d.ts.map +1 -0
- package/dist/__tests__/router-integration.test.js +1051 -0
- package/dist/__tests__/router-integration.test.js.map +1 -0
- package/dist/__tests__/search-params.test.d.ts +5 -0
- package/dist/__tests__/search-params.test.d.ts.map +1 -0
- package/dist/__tests__/search-params.test.js +306 -0
- package/dist/__tests__/search-params.test.js.map +1 -0
- package/dist/__tests__/segment-system.test.d.ts +2 -0
- package/dist/__tests__/segment-system.test.d.ts.map +1 -0
- package/dist/__tests__/segment-system.test.js +627 -0
- package/dist/__tests__/segment-system.test.js.map +1 -0
- package/dist/__tests__/static-handler-types.test.d.ts +8 -0
- package/dist/__tests__/static-handler-types.test.d.ts.map +1 -0
- package/dist/__tests__/static-handler-types.test.js +63 -0
- package/dist/__tests__/static-handler-types.test.js.map +1 -0
- package/dist/__tests__/urls.test.d.ts +2 -0
- package/dist/__tests__/urls.test.d.ts.map +1 -0
- package/dist/__tests__/urls.test.js +421 -0
- package/dist/__tests__/urls.test.js.map +1 -0
- package/dist/__tests__/use-mount.test.d.ts +2 -0
- package/dist/__tests__/use-mount.test.d.ts.map +1 -0
- package/dist/__tests__/use-mount.test.js +35 -0
- package/dist/__tests__/use-mount.test.js.map +1 -0
- package/dist/bin/rango.d.ts +2 -0
- package/dist/bin/rango.d.ts.map +1 -0
- package/dist/bin/rango.js +1844 -237
- package/dist/bin/rango.js.map +1 -0
- package/dist/browser/event-controller.d.ts +191 -0
- package/dist/browser/event-controller.d.ts.map +1 -0
- package/dist/browser/event-controller.js +559 -0
- package/dist/browser/event-controller.js.map +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +14 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/link-interceptor.d.ts +38 -0
- package/dist/browser/link-interceptor.d.ts.map +1 -0
- package/dist/browser/link-interceptor.js +99 -0
- package/dist/browser/link-interceptor.js.map +1 -0
- package/dist/browser/logging.d.ts +10 -0
- package/dist/browser/logging.d.ts.map +1 -0
- package/dist/browser/logging.js +29 -0
- package/dist/browser/logging.js.map +1 -0
- package/dist/browser/lru-cache.d.ts +17 -0
- package/dist/browser/lru-cache.d.ts.map +1 -0
- package/dist/browser/lru-cache.js +50 -0
- package/dist/browser/lru-cache.js.map +1 -0
- package/dist/browser/merge-segment-loaders.d.ts +39 -0
- package/dist/browser/merge-segment-loaders.d.ts.map +1 -0
- package/dist/browser/merge-segment-loaders.js +102 -0
- package/dist/browser/merge-segment-loaders.js.map +1 -0
- package/dist/browser/navigation-bridge.d.ts +102 -0
- package/dist/browser/navigation-bridge.d.ts.map +1 -0
- package/dist/browser/navigation-bridge.js +708 -0
- package/dist/browser/navigation-bridge.js.map +1 -0
- package/dist/browser/navigation-client.d.ts +25 -0
- package/dist/browser/navigation-client.d.ts.map +1 -0
- package/dist/browser/navigation-client.js +157 -0
- package/dist/browser/navigation-client.js.map +1 -0
- package/dist/browser/navigation-store.d.ts +101 -0
- package/dist/browser/navigation-store.d.ts.map +1 -0
- package/dist/browser/navigation-store.js +625 -0
- package/dist/browser/navigation-store.js.map +1 -0
- package/dist/browser/partial-update.d.ts +75 -0
- package/dist/browser/partial-update.d.ts.map +1 -0
- package/dist/browser/partial-update.js +426 -0
- package/dist/browser/partial-update.js.map +1 -0
- package/dist/browser/react/Link.d.ts +86 -0
- package/dist/browser/react/Link.d.ts.map +1 -0
- package/dist/browser/react/Link.js +128 -0
- package/dist/browser/react/Link.js.map +1 -0
- package/dist/browser/react/NavigationProvider.d.ts +63 -0
- package/dist/browser/react/NavigationProvider.d.ts.map +1 -0
- package/dist/browser/react/NavigationProvider.js +216 -0
- package/dist/browser/react/NavigationProvider.js.map +1 -0
- package/dist/browser/react/ScrollRestoration.d.ts +75 -0
- package/dist/browser/react/ScrollRestoration.d.ts.map +1 -0
- package/dist/browser/react/ScrollRestoration.js +57 -0
- package/dist/browser/react/ScrollRestoration.js.map +1 -0
- package/dist/browser/react/context.d.ts +46 -0
- package/dist/browser/react/context.d.ts.map +1 -0
- package/dist/browser/react/context.js +10 -0
- package/dist/browser/react/context.js.map +1 -0
- package/dist/browser/react/index.d.ts +11 -0
- package/dist/browser/react/index.d.ts.map +1 -0
- package/dist/browser/react/index.js +22 -0
- package/dist/browser/react/index.js.map +1 -0
- package/dist/browser/react/location-state-shared.d.ts +63 -0
- package/dist/browser/react/location-state-shared.d.ts.map +1 -0
- package/dist/browser/react/location-state-shared.js +81 -0
- package/dist/browser/react/location-state-shared.js.map +1 -0
- package/dist/browser/react/location-state.d.ts +23 -0
- package/dist/browser/react/location-state.d.ts.map +1 -0
- package/dist/browser/react/location-state.js +29 -0
- package/dist/browser/react/location-state.js.map +1 -0
- package/dist/browser/react/mount-context.d.ts +24 -0
- package/dist/browser/react/mount-context.d.ts.map +1 -0
- package/dist/browser/react/mount-context.js +24 -0
- package/dist/browser/react/mount-context.js.map +1 -0
- package/dist/browser/react/use-action.d.ts +64 -0
- package/dist/browser/react/use-action.d.ts.map +1 -0
- package/dist/browser/react/use-action.js +134 -0
- package/dist/browser/react/use-action.js.map +1 -0
- package/dist/browser/react/use-client-cache.d.ts +41 -0
- package/dist/browser/react/use-client-cache.d.ts.map +1 -0
- package/{src/browser/react/use-client-cache.ts → dist/browser/react/use-client-cache.js} +9 -26
- package/dist/browser/react/use-client-cache.js.map +1 -0
- package/dist/browser/react/use-handle.d.ts +31 -0
- package/dist/browser/react/use-handle.d.ts.map +1 -0
- package/dist/browser/react/use-handle.js +144 -0
- package/dist/browser/react/use-handle.js.map +1 -0
- package/dist/browser/react/use-href.d.ts +33 -0
- package/dist/browser/react/use-href.d.ts.map +1 -0
- package/dist/browser/react/use-href.js +39 -0
- package/dist/browser/react/use-href.js.map +1 -0
- package/dist/browser/react/use-link-status.d.ts +37 -0
- package/dist/browser/react/use-link-status.d.ts.map +1 -0
- package/dist/browser/react/use-link-status.js +99 -0
- package/dist/browser/react/use-link-status.js.map +1 -0
- package/dist/browser/react/use-mount.d.ts +25 -0
- package/dist/browser/react/use-mount.d.ts.map +1 -0
- package/dist/browser/react/use-mount.js +30 -0
- package/dist/browser/react/use-mount.js.map +1 -0
- package/dist/browser/react/use-navigation.d.ts +27 -0
- package/dist/browser/react/use-navigation.d.ts.map +1 -0
- package/dist/browser/react/use-navigation.js +87 -0
- package/dist/browser/react/use-navigation.js.map +1 -0
- package/dist/browser/react/use-segments.d.ts +38 -0
- package/dist/browser/react/use-segments.d.ts.map +1 -0
- package/dist/browser/react/use-segments.js +130 -0
- package/dist/browser/react/use-segments.js.map +1 -0
- package/dist/browser/request-controller.d.ts +26 -0
- package/dist/browser/request-controller.d.ts.map +1 -0
- package/dist/browser/request-controller.js +147 -0
- package/dist/browser/request-controller.js.map +1 -0
- package/dist/browser/rsc-router.d.ts +129 -0
- package/dist/browser/rsc-router.d.ts.map +1 -0
- package/dist/browser/rsc-router.js +195 -0
- package/dist/browser/rsc-router.js.map +1 -0
- package/dist/browser/scroll-restoration.d.ts +93 -0
- package/dist/browser/scroll-restoration.d.ts.map +1 -0
- package/dist/browser/scroll-restoration.js +321 -0
- package/dist/browser/scroll-restoration.js.map +1 -0
- package/dist/browser/segment-structure-assert.d.ts +17 -0
- package/dist/browser/segment-structure-assert.d.ts.map +1 -0
- package/dist/browser/segment-structure-assert.js +59 -0
- package/dist/browser/segment-structure-assert.js.map +1 -0
- package/dist/browser/server-action-bridge.d.ts +26 -0
- package/dist/browser/server-action-bridge.d.ts.map +1 -0
- package/dist/browser/server-action-bridge.js +668 -0
- package/dist/browser/server-action-bridge.js.map +1 -0
- package/dist/browser/shallow.d.ts +12 -0
- package/dist/browser/shallow.d.ts.map +1 -0
- package/dist/browser/shallow.js +34 -0
- package/dist/browser/shallow.js.map +1 -0
- package/dist/browser/types.d.ts +369 -0
- package/dist/browser/types.d.ts.map +1 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/build/__tests__/generate-cli.test.d.ts +2 -0
- package/dist/build/__tests__/generate-cli.test.d.ts.map +1 -0
- package/dist/build/__tests__/generate-cli.test.js +237 -0
- package/dist/build/__tests__/generate-cli.test.js.map +1 -0
- package/dist/build/__tests__/generate-manifest.test.d.ts +2 -0
- package/dist/build/__tests__/generate-manifest.test.d.ts.map +1 -0
- package/dist/build/__tests__/generate-manifest.test.js +119 -0
- package/dist/build/__tests__/generate-manifest.test.js.map +1 -0
- package/dist/build/__tests__/generate-route-types.test.d.ts +2 -0
- package/dist/build/__tests__/generate-route-types.test.d.ts.map +1 -0
- package/dist/build/__tests__/generate-route-types.test.js +620 -0
- package/dist/build/__tests__/generate-route-types.test.js.map +1 -0
- package/dist/build/__tests__/per-router-manifest.test.d.ts +2 -0
- package/dist/build/__tests__/per-router-manifest.test.d.ts.map +1 -0
- package/dist/build/__tests__/per-router-manifest.test.js +308 -0
- package/dist/build/__tests__/per-router-manifest.test.js.map +1 -0
- package/dist/build/generate-manifest.d.ts +81 -0
- package/dist/build/generate-manifest.d.ts.map +1 -0
- package/dist/build/generate-manifest.js +276 -0
- package/dist/build/generate-manifest.js.map +1 -0
- package/dist/build/generate-route-types.d.ts +115 -0
- package/dist/build/generate-route-types.d.ts.map +1 -0
- package/dist/build/generate-route-types.js +740 -0
- package/dist/build/generate-route-types.js.map +1 -0
- package/dist/build/index.d.ts +21 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +21 -0
- package/dist/build/index.js.map +1 -0
- package/dist/build/route-trie.d.ts +71 -0
- package/dist/build/route-trie.d.ts.map +1 -0
- package/dist/build/route-trie.js +175 -0
- package/dist/build/route-trie.js.map +1 -0
- package/dist/cache/__tests__/cache-scope.test.d.ts +2 -0
- package/dist/cache/__tests__/cache-scope.test.d.ts.map +1 -0
- package/dist/cache/__tests__/cache-scope.test.js +208 -0
- package/dist/cache/__tests__/cache-scope.test.js.map +1 -0
- package/dist/cache/__tests__/document-cache.test.d.ts +2 -0
- package/dist/cache/__tests__/document-cache.test.d.ts.map +1 -0
- package/dist/cache/__tests__/document-cache.test.js +345 -0
- package/dist/cache/__tests__/document-cache.test.js.map +1 -0
- package/dist/cache/__tests__/memory-segment-store.test.d.ts +2 -0
- package/dist/cache/__tests__/memory-segment-store.test.d.ts.map +1 -0
- package/dist/cache/__tests__/memory-segment-store.test.js +425 -0
- package/dist/cache/__tests__/memory-segment-store.test.js.map +1 -0
- package/dist/cache/__tests__/memory-store.test.d.ts +2 -0
- package/dist/cache/__tests__/memory-store.test.d.ts.map +1 -0
- package/dist/cache/__tests__/memory-store.test.js +367 -0
- package/dist/cache/__tests__/memory-store.test.js.map +1 -0
- package/dist/cache/cache-scope.d.ts +102 -0
- package/dist/cache/cache-scope.d.ts.map +1 -0
- package/dist/cache/cache-scope.js +440 -0
- package/dist/cache/cache-scope.js.map +1 -0
- package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts +2 -0
- package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts.map +1 -0
- package/dist/cache/cf/__tests__/cf-cache-store.test.js +330 -0
- package/dist/cache/cf/__tests__/cf-cache-store.test.js.map +1 -0
- package/dist/cache/cf/cf-cache-store.d.ts +165 -0
- package/dist/cache/cf/cf-cache-store.d.ts.map +1 -0
- package/dist/cache/cf/cf-cache-store.js +242 -0
- package/dist/cache/cf/cf-cache-store.js.map +1 -0
- package/dist/cache/cf/index.d.ts +14 -0
- package/dist/cache/cf/index.d.ts.map +1 -0
- package/dist/cache/cf/index.js +17 -0
- package/dist/cache/cf/index.js.map +1 -0
- package/dist/cache/document-cache.d.ts +64 -0
- package/dist/cache/document-cache.d.ts.map +1 -0
- package/dist/cache/document-cache.js +228 -0
- package/dist/cache/document-cache.js.map +1 -0
- package/dist/cache/index.d.ts +19 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +21 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory-segment-store.d.ts +110 -0
- package/dist/cache/memory-segment-store.d.ts.map +1 -0
- package/dist/cache/memory-segment-store.js +117 -0
- package/dist/cache/memory-segment-store.js.map +1 -0
- package/dist/cache/memory-store.d.ts +41 -0
- package/dist/cache/memory-store.d.ts.map +1 -0
- package/dist/cache/memory-store.js +191 -0
- package/dist/cache/memory-store.js.map +1 -0
- package/dist/cache/types.d.ts +317 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +12 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/client.d.ts +248 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +367 -0
- package/dist/client.js.map +1 -0
- package/dist/client.rsc.d.ts +26 -0
- package/dist/client.rsc.d.ts.map +1 -0
- package/dist/client.rsc.js +46 -0
- package/dist/client.rsc.js.map +1 -0
- package/dist/component-utils.d.ts +36 -0
- package/dist/component-utils.d.ts.map +1 -0
- package/dist/component-utils.js +61 -0
- package/dist/component-utils.js.map +1 -0
- package/dist/components/DefaultDocument.d.ts +13 -0
- package/dist/components/DefaultDocument.d.ts.map +1 -0
- package/dist/components/DefaultDocument.js +15 -0
- package/dist/components/DefaultDocument.js.map +1 -0
- package/dist/debug.d.ts +58 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +157 -0
- package/dist/debug.js.map +1 -0
- package/dist/default-error-boundary.d.ts +11 -0
- package/dist/default-error-boundary.d.ts.map +1 -0
- package/dist/default-error-boundary.js +45 -0
- package/dist/default-error-boundary.js.map +1 -0
- package/dist/deps/browser.d.ts +2 -0
- package/dist/deps/browser.d.ts.map +1 -0
- package/dist/deps/browser.js +3 -0
- package/dist/deps/browser.js.map +1 -0
- package/dist/deps/html-stream-client.d.ts +2 -0
- package/dist/deps/html-stream-client.d.ts.map +1 -0
- package/dist/deps/html-stream-client.js +3 -0
- package/dist/deps/html-stream-client.js.map +1 -0
- package/dist/deps/html-stream-server.d.ts +2 -0
- package/dist/deps/html-stream-server.d.ts.map +1 -0
- package/dist/deps/html-stream-server.js +3 -0
- package/dist/deps/html-stream-server.js.map +1 -0
- package/dist/deps/rsc.d.ts +2 -0
- package/dist/deps/rsc.d.ts.map +1 -0
- package/dist/deps/rsc.js +4 -0
- package/dist/deps/rsc.js.map +1 -0
- package/dist/deps/ssr.d.ts +2 -0
- package/dist/deps/ssr.d.ts.map +1 -0
- package/dist/deps/ssr.js +3 -0
- package/dist/deps/ssr.js.map +1 -0
- package/dist/errors.d.ts +174 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +241 -0
- package/dist/errors.js.map +1 -0
- package/dist/handle.d.ts +78 -0
- package/dist/handle.d.ts.map +1 -0
- package/dist/handle.js +82 -0
- package/dist/handle.js.map +1 -0
- package/dist/handles/MetaTags.d.ts +14 -0
- package/dist/handles/MetaTags.d.ts.map +1 -0
- package/dist/handles/MetaTags.js +136 -0
- package/dist/handles/MetaTags.js.map +1 -0
- package/dist/handles/index.d.ts +6 -0
- package/dist/handles/index.d.ts.map +1 -0
- package/{src/handles/index.ts → dist/handles/index.js} +1 -1
- package/dist/handles/index.js.map +1 -0
- package/dist/handles/meta.d.ts +39 -0
- package/dist/handles/meta.d.ts.map +1 -0
- package/dist/handles/meta.js +202 -0
- package/dist/handles/meta.js.map +1 -0
- package/dist/host/__tests__/errors.test.d.ts +2 -0
- package/dist/host/__tests__/errors.test.d.ts.map +1 -0
- package/dist/host/__tests__/errors.test.js +76 -0
- package/dist/host/__tests__/errors.test.js.map +1 -0
- package/dist/host/__tests__/pattern-comprehensive.test.d.ts +2 -0
- package/dist/host/__tests__/pattern-comprehensive.test.d.ts.map +1 -0
- package/dist/host/__tests__/pattern-comprehensive.test.js +732 -0
- package/dist/host/__tests__/pattern-comprehensive.test.js.map +1 -0
- package/dist/host/__tests__/pattern-matcher.test.d.ts +2 -0
- package/dist/host/__tests__/pattern-matcher.test.d.ts.map +1 -0
- package/dist/host/__tests__/pattern-matcher.test.js +251 -0
- package/dist/host/__tests__/pattern-matcher.test.js.map +1 -0
- package/dist/host/__tests__/router.test.d.ts +2 -0
- package/dist/host/__tests__/router.test.d.ts.map +1 -0
- package/dist/host/__tests__/router.test.js +241 -0
- package/dist/host/__tests__/router.test.js.map +1 -0
- package/dist/host/__tests__/testing.test.d.ts +2 -0
- package/dist/host/__tests__/testing.test.d.ts.map +1 -0
- package/dist/host/__tests__/testing.test.js +64 -0
- package/dist/host/__tests__/testing.test.js.map +1 -0
- package/dist/host/__tests__/utils.test.d.ts +2 -0
- package/dist/host/__tests__/utils.test.d.ts.map +1 -0
- package/dist/host/__tests__/utils.test.js +29 -0
- package/dist/host/__tests__/utils.test.js.map +1 -0
- package/dist/host/cookie-handler.d.ts +34 -0
- package/dist/host/cookie-handler.d.ts.map +1 -0
- package/dist/host/cookie-handler.js +124 -0
- package/dist/host/cookie-handler.js.map +1 -0
- package/dist/host/errors.d.ts +56 -0
- package/dist/host/errors.d.ts.map +1 -0
- package/dist/host/errors.js +79 -0
- package/dist/host/errors.js.map +1 -0
- package/dist/host/index.d.ts +29 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +32 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/pattern-matcher.d.ts +36 -0
- package/dist/host/pattern-matcher.d.ts.map +1 -0
- package/dist/host/pattern-matcher.js +172 -0
- package/dist/host/pattern-matcher.js.map +1 -0
- package/dist/host/router.d.ts +26 -0
- package/dist/host/router.d.ts.map +1 -0
- package/dist/host/router.js +218 -0
- package/dist/host/router.js.map +1 -0
- package/dist/host/testing.d.ts +36 -0
- package/dist/host/testing.d.ts.map +1 -0
- package/dist/host/testing.js +55 -0
- package/dist/host/testing.js.map +1 -0
- package/dist/host/types.d.ts +115 -0
- package/dist/host/types.d.ts.map +1 -0
- package/dist/host/types.js +7 -0
- package/dist/host/types.js.map +1 -0
- package/dist/host/utils.d.ts +21 -0
- package/dist/host/utils.d.ts.map +1 -0
- package/dist/host/utils.js +23 -0
- package/dist/host/utils.js.map +1 -0
- package/dist/href-client.d.ts +131 -0
- package/dist/href-client.d.ts.map +1 -0
- package/dist/href-client.js +64 -0
- package/dist/href-client.js.map +1 -0
- package/{src/href-context.ts → dist/href-context.d.ts} +7 -11
- package/dist/href-context.d.ts.map +1 -0
- package/dist/href-context.js +21 -0
- package/dist/href-context.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/index.rsc.d.ts +32 -0
- package/dist/index.rsc.d.ts.map +1 -0
- package/dist/index.rsc.js +40 -0
- package/dist/index.rsc.js.map +1 -0
- package/dist/internal-debug.d.ts +2 -0
- package/dist/internal-debug.d.ts.map +1 -0
- package/dist/internal-debug.js +5 -0
- package/dist/internal-debug.js.map +1 -0
- package/dist/loader.d.ts +14 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +20 -0
- package/dist/loader.js.map +1 -0
- package/dist/loader.rsc.d.ts +19 -0
- package/dist/loader.rsc.d.ts.map +1 -0
- package/dist/loader.rsc.js +99 -0
- package/dist/loader.rsc.js.map +1 -0
- package/{src/network-error-thrower.tsx → dist/network-error-thrower.d.ts} +4 -8
- package/dist/network-error-thrower.d.ts.map +1 -0
- package/dist/network-error-thrower.js +14 -0
- package/dist/network-error-thrower.js.map +1 -0
- package/dist/outlet-context.d.ts +13 -0
- package/dist/outlet-context.d.ts.map +1 -0
- package/dist/outlet-context.js +3 -0
- package/dist/outlet-context.js.map +1 -0
- package/dist/prerender/__tests__/param-hash.test.d.ts +2 -0
- package/dist/prerender/__tests__/param-hash.test.d.ts.map +1 -0
- package/dist/prerender/__tests__/param-hash.test.js +148 -0
- package/dist/prerender/__tests__/param-hash.test.js.map +1 -0
- package/dist/prerender/param-hash.d.ts +16 -0
- package/dist/prerender/param-hash.d.ts.map +1 -0
- package/dist/prerender/param-hash.js +36 -0
- package/dist/prerender/param-hash.js.map +1 -0
- package/dist/prerender/store.d.ts +38 -0
- package/dist/prerender/store.d.ts.map +1 -0
- package/dist/prerender/store.js +61 -0
- package/dist/prerender/store.js.map +1 -0
- package/dist/prerender.d.ts +66 -0
- package/dist/prerender.d.ts.map +1 -0
- package/dist/prerender.js +57 -0
- package/dist/prerender.js.map +1 -0
- package/dist/reverse.d.ts +196 -0
- package/dist/reverse.d.ts.map +1 -0
- package/dist/reverse.js +78 -0
- package/dist/reverse.js.map +1 -0
- package/dist/root-error-boundary.d.ts +33 -0
- package/dist/root-error-boundary.d.ts.map +1 -0
- package/dist/root-error-boundary.js +165 -0
- package/dist/root-error-boundary.js.map +1 -0
- package/dist/route-content-wrapper.d.ts +46 -0
- package/dist/route-content-wrapper.d.ts.map +1 -0
- package/dist/route-content-wrapper.js +77 -0
- package/dist/route-content-wrapper.js.map +1 -0
- package/dist/route-definition.d.ts +421 -0
- package/dist/route-definition.d.ts.map +1 -0
- package/dist/route-definition.js +868 -0
- package/dist/route-definition.js.map +1 -0
- package/dist/route-map-builder.d.ts +155 -0
- package/dist/route-map-builder.d.ts.map +1 -0
- package/dist/route-map-builder.js +237 -0
- package/dist/route-map-builder.js.map +1 -0
- package/dist/route-types.d.ts +165 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +7 -0
- package/dist/route-types.js.map +1 -0
- package/dist/router/__tests__/handler-context.test.d.ts +2 -0
- package/dist/router/__tests__/handler-context.test.d.ts.map +1 -0
- package/dist/router/__tests__/handler-context.test.js +65 -0
- package/dist/router/__tests__/handler-context.test.js.map +1 -0
- package/dist/router/__tests__/loader-cycle-detection.test.d.ts +2 -0
- package/dist/router/__tests__/loader-cycle-detection.test.d.ts.map +1 -0
- package/dist/router/__tests__/loader-cycle-detection.test.js +221 -0
- package/dist/router/__tests__/loader-cycle-detection.test.js.map +1 -0
- package/dist/router/__tests__/match-context.test.d.ts +2 -0
- package/dist/router/__tests__/match-context.test.d.ts.map +1 -0
- package/dist/router/__tests__/match-context.test.js +92 -0
- package/dist/router/__tests__/match-context.test.js.map +1 -0
- package/dist/router/__tests__/match-pipelines.test.d.ts +2 -0
- package/dist/router/__tests__/match-pipelines.test.d.ts.map +1 -0
- package/dist/router/__tests__/match-pipelines.test.js +417 -0
- package/dist/router/__tests__/match-pipelines.test.js.map +1 -0
- package/dist/router/__tests__/match-result.test.d.ts +2 -0
- package/dist/router/__tests__/match-result.test.d.ts.map +1 -0
- package/dist/router/__tests__/match-result.test.js +457 -0
- package/dist/router/__tests__/match-result.test.js.map +1 -0
- package/dist/router/__tests__/on-error.test.d.ts +2 -0
- package/dist/router/__tests__/on-error.test.d.ts.map +1 -0
- package/dist/router/__tests__/on-error.test.js +678 -0
- package/dist/router/__tests__/on-error.test.js.map +1 -0
- package/dist/router/__tests__/pattern-matching.test.d.ts +2 -0
- package/dist/router/__tests__/pattern-matching.test.d.ts.map +1 -0
- package/dist/router/__tests__/pattern-matching.test.js +629 -0
- package/dist/router/__tests__/pattern-matching.test.js.map +1 -0
- package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts +2 -0
- package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts.map +1 -0
- package/dist/router/__tests__/segment-resolution-parallel-loading.test.js +155 -0
- package/dist/router/__tests__/segment-resolution-parallel-loading.test.js.map +1 -0
- package/dist/router/error-handling.d.ts +77 -0
- package/dist/router/error-handling.d.ts.map +1 -0
- package/dist/router/error-handling.js +202 -0
- package/dist/router/error-handling.js.map +1 -0
- package/dist/router/handler-context.d.ts +20 -0
- package/dist/router/handler-context.d.ts.map +1 -0
- package/dist/router/handler-context.js +198 -0
- package/dist/router/handler-context.js.map +1 -0
- package/dist/router/intercept-resolution.d.ts +66 -0
- package/dist/router/intercept-resolution.d.ts.map +1 -0
- package/dist/router/intercept-resolution.js +246 -0
- package/dist/router/intercept-resolution.js.map +1 -0
- package/dist/router/loader-resolution.d.ts +64 -0
- package/dist/router/loader-resolution.d.ts.map +1 -0
- package/dist/router/loader-resolution.js +284 -0
- package/dist/router/loader-resolution.js.map +1 -0
- package/dist/router/logging.d.ts +15 -0
- package/dist/router/logging.d.ts.map +1 -0
- package/dist/router/logging.js +99 -0
- package/dist/router/logging.js.map +1 -0
- package/dist/router/manifest.d.ts +22 -0
- package/dist/router/manifest.d.ts.map +1 -0
- package/dist/router/manifest.js +181 -0
- package/dist/router/manifest.js.map +1 -0
- package/dist/router/match-api.d.ts +35 -0
- package/dist/router/match-api.d.ts.map +1 -0
- package/dist/router/match-api.js +406 -0
- package/dist/router/match-api.js.map +1 -0
- package/dist/router/match-context.d.ts +206 -0
- package/dist/router/match-context.d.ts.map +1 -0
- package/dist/router/match-context.js +17 -0
- package/dist/router/match-context.js.map +1 -0
- package/dist/router/match-middleware/background-revalidation.d.ts +127 -0
- package/dist/router/match-middleware/background-revalidation.d.ts.map +1 -0
- package/dist/router/match-middleware/background-revalidation.js +75 -0
- package/dist/router/match-middleware/background-revalidation.js.map +1 -0
- package/dist/router/match-middleware/cache-lookup.d.ts +112 -0
- package/dist/router/match-middleware/cache-lookup.d.ts.map +1 -0
- package/dist/router/match-middleware/cache-lookup.js +257 -0
- package/dist/router/match-middleware/cache-lookup.js.map +1 -0
- package/dist/router/match-middleware/cache-store.d.ts +113 -0
- package/dist/router/match-middleware/cache-store.d.ts.map +1 -0
- package/dist/router/match-middleware/cache-store.js +108 -0
- package/dist/router/match-middleware/cache-store.js.map +1 -0
- package/dist/router/match-middleware/index.d.ts +81 -0
- package/dist/router/match-middleware/index.d.ts.map +1 -0
- package/dist/router/match-middleware/index.js +80 -0
- package/dist/router/match-middleware/index.js.map +1 -0
- package/dist/router/match-middleware/intercept-resolution.d.ts +117 -0
- package/dist/router/match-middleware/intercept-resolution.d.ts.map +1 -0
- package/dist/router/match-middleware/intercept-resolution.js +134 -0
- package/dist/router/match-middleware/intercept-resolution.js.map +1 -0
- package/dist/router/match-middleware/segment-resolution.d.ts +99 -0
- package/dist/router/match-middleware/segment-resolution.d.ts.map +1 -0
- package/dist/router/match-middleware/segment-resolution.js +53 -0
- package/dist/router/match-middleware/segment-resolution.js.map +1 -0
- package/dist/router/match-pipelines.d.ts +147 -0
- package/dist/router/match-pipelines.d.ts.map +1 -0
- package/dist/router/match-pipelines.js +82 -0
- package/dist/router/match-pipelines.js.map +1 -0
- package/dist/router/match-result.d.ts +126 -0
- package/dist/router/match-result.d.ts.map +1 -0
- package/dist/router/match-result.js +93 -0
- package/dist/router/match-result.js.map +1 -0
- package/dist/router/metrics.d.ts +20 -0
- package/dist/router/metrics.d.ts.map +1 -0
- package/dist/router/metrics.js +47 -0
- package/dist/router/metrics.js.map +1 -0
- package/dist/router/middleware.d.ts +249 -0
- package/dist/router/middleware.d.ts.map +1 -0
- package/dist/router/middleware.js +434 -0
- package/dist/router/middleware.js.map +1 -0
- package/dist/router/middleware.test.d.ts +2 -0
- package/dist/router/middleware.test.d.ts.map +1 -0
- package/dist/router/middleware.test.js +816 -0
- package/dist/router/middleware.test.js.map +1 -0
- package/dist/router/pattern-matching.d.ts +149 -0
- package/dist/router/pattern-matching.d.ts.map +1 -0
- package/dist/router/pattern-matching.js +349 -0
- package/dist/router/pattern-matching.js.map +1 -0
- package/dist/router/revalidation.d.ts +44 -0
- package/dist/router/revalidation.d.ts.map +1 -0
- package/dist/router/revalidation.js +147 -0
- package/dist/router/revalidation.js.map +1 -0
- package/dist/router/router-context.d.ts +135 -0
- package/dist/router/router-context.d.ts.map +1 -0
- package/dist/router/router-context.js +36 -0
- package/dist/router/router-context.js.map +1 -0
- package/dist/router/segment-resolution.d.ts +127 -0
- package/dist/router/segment-resolution.d.ts.map +1 -0
- package/dist/router/segment-resolution.js +919 -0
- package/dist/router/segment-resolution.js.map +1 -0
- package/dist/router/trie-matching.d.ts +40 -0
- package/dist/router/trie-matching.d.ts.map +1 -0
- package/dist/router/trie-matching.js +127 -0
- package/dist/router/trie-matching.js.map +1 -0
- package/dist/router/types.d.ts +136 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +7 -0
- package/dist/router/types.js.map +1 -0
- package/dist/router.d.ts +753 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.gen.d.ts +6 -0
- package/dist/router.gen.d.ts.map +1 -0
- package/dist/router.gen.js +6 -0
- package/dist/router.gen.js.map +1 -0
- package/dist/router.js +1304 -0
- package/dist/router.js.map +1 -0
- package/dist/rsc/__tests__/helpers.test.d.ts +2 -0
- package/dist/rsc/__tests__/helpers.test.d.ts.map +1 -0
- package/dist/rsc/__tests__/helpers.test.js +140 -0
- package/dist/rsc/__tests__/helpers.test.js.map +1 -0
- package/dist/rsc/handler.d.ts +45 -0
- package/dist/rsc/handler.d.ts.map +1 -0
- package/dist/rsc/handler.js +1172 -0
- package/dist/rsc/handler.js.map +1 -0
- package/dist/rsc/helpers.d.ts +16 -0
- package/dist/rsc/helpers.d.ts.map +1 -0
- package/dist/rsc/helpers.js +55 -0
- package/dist/rsc/helpers.js.map +1 -0
- package/dist/rsc/index.d.ts +22 -0
- package/dist/rsc/index.d.ts.map +1 -0
- package/dist/rsc/index.js +23 -0
- package/dist/rsc/index.js.map +1 -0
- package/dist/rsc/nonce.d.ts +9 -0
- package/dist/rsc/nonce.d.ts.map +1 -0
- package/dist/rsc/nonce.js +18 -0
- package/dist/rsc/nonce.js.map +1 -0
- package/dist/rsc/types.d.ts +206 -0
- package/dist/rsc/types.d.ts.map +1 -0
- package/dist/rsc/types.js +8 -0
- package/dist/rsc/types.js.map +1 -0
- package/dist/search-params.d.ts +103 -0
- package/dist/search-params.d.ts.map +1 -0
- package/dist/search-params.js +74 -0
- package/dist/search-params.js.map +1 -0
- package/dist/segment-system.d.ts +75 -0
- package/dist/segment-system.d.ts.map +1 -0
- package/dist/segment-system.js +336 -0
- package/dist/segment-system.js.map +1 -0
- package/dist/server/context.d.ts +245 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +197 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/fetchable-loader-store.d.ts +18 -0
- package/dist/server/fetchable-loader-store.d.ts.map +1 -0
- package/dist/server/fetchable-loader-store.js +18 -0
- package/dist/server/fetchable-loader-store.js.map +1 -0
- package/dist/server/handle-store.d.ts +85 -0
- package/dist/server/handle-store.d.ts.map +1 -0
- package/dist/server/handle-store.js +142 -0
- package/dist/server/handle-store.js.map +1 -0
- package/dist/server/loader-registry.d.ts +55 -0
- package/dist/server/loader-registry.d.ts.map +1 -0
- package/dist/server/loader-registry.js +132 -0
- package/dist/server/loader-registry.js.map +1 -0
- package/dist/server/request-context.d.ts +226 -0
- package/dist/server/request-context.d.ts.map +1 -0
- package/dist/server/request-context.js +290 -0
- package/dist/server/request-context.js.map +1 -0
- package/dist/server/root-layout.d.ts +4 -0
- package/dist/server/root-layout.d.ts.map +1 -0
- package/dist/server/root-layout.js +5 -0
- package/dist/server/root-layout.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +20 -0
- package/dist/server.js.map +1 -0
- package/dist/ssr/__tests__/ssr-handler.test.d.ts +2 -0
- package/dist/ssr/__tests__/ssr-handler.test.d.ts.map +1 -0
- package/dist/ssr/__tests__/ssr-handler.test.js +132 -0
- package/dist/ssr/__tests__/ssr-handler.test.js.map +1 -0
- package/dist/ssr/index.d.ts +98 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/index.js +158 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/static-handler.d.ts +50 -0
- package/dist/static-handler.d.ts.map +1 -0
- package/dist/static-handler.gen.d.ts +5 -0
- package/dist/static-handler.gen.d.ts.map +1 -0
- package/dist/static-handler.gen.js +5 -0
- package/dist/static-handler.gen.js.map +1 -0
- package/dist/static-handler.js +29 -0
- package/dist/static-handler.js.map +1 -0
- package/dist/testing/vitest.js +82 -0
- package/dist/theme/ThemeProvider.d.ts +20 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.js +240 -0
- package/dist/theme/ThemeProvider.js.map +1 -0
- package/dist/theme/ThemeScript.d.ts +48 -0
- package/dist/theme/ThemeScript.d.ts.map +1 -0
- package/dist/theme/ThemeScript.js +13 -0
- package/dist/theme/ThemeScript.js.map +1 -0
- package/dist/theme/__tests__/theme.test.d.ts +2 -0
- package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
- package/dist/theme/__tests__/theme.test.js +103 -0
- package/dist/theme/__tests__/theme.test.js.map +1 -0
- package/dist/theme/constants.d.ts +29 -0
- package/dist/theme/constants.d.ts.map +1 -0
- package/dist/theme/constants.js +48 -0
- package/dist/theme/constants.js.map +1 -0
- package/dist/theme/index.d.ts +31 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +36 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/theme-context.d.ts +40 -0
- package/dist/theme/theme-context.d.ts.map +1 -0
- package/dist/theme/theme-context.js +60 -0
- package/dist/theme/theme-context.js.map +1 -0
- package/dist/theme/theme-script.d.ts +27 -0
- package/dist/theme/theme-script.d.ts.map +1 -0
- package/dist/theme/theme-script.js +147 -0
- package/dist/theme/theme-script.js.map +1 -0
- package/dist/theme/types.d.ts +163 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/types.js +11 -0
- package/dist/theme/types.js.map +1 -0
- package/dist/theme/use-theme.d.ts +12 -0
- package/dist/theme/use-theme.d.ts.map +1 -0
- package/dist/theme/use-theme.js +40 -0
- package/dist/theme/use-theme.js.map +1 -0
- package/dist/types.d.ts +1479 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/urls.d.ts +441 -0
- package/dist/urls.d.ts.map +1 -0
- package/dist/urls.gen.d.ts +8 -0
- package/dist/urls.gen.d.ts.map +1 -0
- package/dist/urls.gen.js +8 -0
- package/dist/urls.gen.js.map +1 -0
- package/dist/urls.js +443 -0
- package/dist/urls.js.map +1 -0
- package/dist/use-loader.d.ts +127 -0
- package/dist/use-loader.d.ts.map +1 -0
- package/dist/use-loader.js +237 -0
- package/dist/use-loader.js.map +1 -0
- package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
- package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
- package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
- package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
- package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
- package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
- package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
- package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
- package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
- package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
- package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
- package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
- package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
- package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
- package/dist/vite/__tests__/expose-router-id.test.js +39 -0
- package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
- package/dist/vite/ast-handler-extract.d.ts +49 -0
- package/dist/vite/ast-handler-extract.d.ts.map +1 -0
- package/dist/vite/ast-handler-extract.js +249 -0
- package/dist/vite/ast-handler-extract.js.map +1 -0
- package/dist/vite/expose-action-id.d.ts +19 -0
- package/dist/vite/expose-action-id.d.ts.map +1 -0
- package/dist/vite/expose-action-id.js +250 -0
- package/dist/vite/expose-action-id.js.map +1 -0
- package/dist/vite/expose-id-utils.d.ts +69 -0
- package/dist/vite/expose-id-utils.d.ts.map +1 -0
- package/dist/vite/expose-id-utils.js +289 -0
- package/dist/vite/expose-id-utils.js.map +1 -0
- package/dist/vite/expose-internal-ids.d.ts +22 -0
- package/dist/vite/expose-internal-ids.d.ts.map +1 -0
- package/dist/vite/expose-internal-ids.js +886 -0
- package/dist/vite/expose-internal-ids.js.map +1 -0
- package/dist/vite/index.d.ts +149 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +7554 -2070
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/package-resolution.d.ts +43 -0
- package/dist/vite/package-resolution.d.ts.map +1 -0
- package/{src/vite/package-resolution.ts → dist/vite/package-resolution.js} +53 -66
- package/dist/vite/package-resolution.js.map +1 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/dist/vite/virtual-entries.d.ts +25 -0
- package/dist/vite/virtual-entries.d.ts.map +1 -0
- package/{src/vite/virtual-entries.ts → dist/vite/virtual-entries.js} +12 -16
- package/dist/vite/virtual-entries.js.map +1 -0
- package/package.json +142 -61
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +329 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +489 -0
- package/skills/caching/SKILL.md +413 -26
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +278 -0
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +16 -18
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +96 -63
- package/skills/fonts/SKILL.md +7 -5
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +76 -436
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +320 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +215 -16
- package/skills/layout/SKILL.md +147 -7
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +629 -55
- package/skills/middleware/SKILL.md +251 -38
- package/skills/migrate-nextjs/SKILL.md +745 -0
- package/skills/migrate-react-router/SKILL.md +153 -0
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +60 -21
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +296 -4
- package/skills/ppr/SKILL.md +658 -0
- package/skills/prerender/SKILL.md +487 -52
- package/skills/rango/SKILL.md +324 -29
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +264 -122
- package/skills/route/SKILL.md +359 -22
- package/skills/router-setup/SKILL.md +247 -34
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +126 -222
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +10 -9
- package/skills/typesafety/SKILL.md +45 -496
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +383 -0
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +77 -44
- package/src/bin/rango.ts +275 -29
- package/src/browser/action-coordinator.ts +114 -0
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +348 -212
- package/src/browser/history-state.ts +101 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +39 -0
- package/src/browser/merge-segment-loaders.ts +23 -13
- package/src/browser/navigation-bridge.ts +385 -576
- package/src/browser/navigation-client.ts +249 -75
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +230 -124
- package/src/browser/navigation-transaction.ts +247 -0
- package/src/browser/network-error-handler.ts +88 -0
- package/src/browser/partial-update.ts +432 -365
- package/src/browser/prefetch/cache.ts +381 -0
- package/src/browser/prefetch/fetch.ts +462 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +209 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +194 -0
- package/src/browser/react/Link.tsx +284 -67
- package/src/browser/react/NavigationProvider.tsx +267 -109
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +70 -0
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +272 -60
- package/src/browser/react/location-state.ts +90 -20
- package/src/browser/react/mount-context.ts +6 -1
- 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 +35 -66
- package/src/browser/react/use-handle.ts +39 -126
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +39 -13
- package/src/browser/react/use-navigation.ts +53 -69
- package/src/browser/react/use-params.ts +75 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +98 -0
- package/src/browser/react/use-search-params.ts +51 -0
- package/src/browser/react/use-segments.ts +72 -99
- package/src/browser/response-adapter.ts +164 -0
- package/src/browser/rsc-router.tsx +355 -73
- package/src/browser/scroll-restoration.ts +140 -50
- package/src/browser/segment-reconciler.ts +253 -0
- package/src/browser/segment-structure-assert.ts +17 -1
- package/src/browser/server-action-bridge.ts +668 -613
- package/src/browser/types.ts +235 -51
- package/src/browser/validate-redirect-origin.ts +56 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +243 -183
- package/src/build/generate-route-types.ts +41 -848
- package/src/build/index.ts +12 -7
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +218 -80
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +105 -0
- package/src/build/route-types/codegen.ts +113 -0
- package/src/build/route-types/include-resolution.ts +812 -0
- package/src/build/route-types/param-extraction.ts +51 -0
- package/src/build/route-types/per-module-writer.ts +144 -0
- package/src/build/route-types/router-processing.ts +695 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +223 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +88 -0
- package/src/cache/cache-policy.ts +199 -0
- package/src/cache/cache-runtime.ts +695 -0
- package/src/cache/cache-scope.ts +324 -335
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2602 -156
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +17 -17
- package/src/cache/document-cache.ts +226 -102
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +132 -0
- package/src/cache/index.ts +24 -35
- package/src/cache/memory-segment-store.ts +446 -30
- package/src/cache/profile-registry.ts +88 -0
- package/src/cache/read-through-swr.ts +178 -0
- package/src/cache/segment-codec.ts +295 -0
- package/src/cache/shell-snapshot.ts +464 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +283 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1201 -0
- package/src/client.rsc.tsx +43 -21
- package/src/client.tsx +131 -347
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +23 -4
- package/src/components/DefaultDocument.tsx +13 -3
- package/src/context-var.ts +168 -0
- package/src/debug.ts +19 -9
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +4 -2
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +106 -10
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +110 -35
- package/src/handles/MetaTags.tsx +83 -59
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +93 -0
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +44 -53
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +20 -65
- package/src/host/errors.ts +21 -30
- package/src/host/index.ts +13 -9
- package/src/host/pattern-matcher.ts +50 -79
- package/src/host/router.ts +151 -121
- package/src/host/testing.ts +45 -32
- package/src/host/types.ts +52 -11
- package/src/host/utils.ts +2 -2
- package/src/href-client.ts +192 -57
- package/src/index.rsc.ts +180 -35
- package/src/index.ts +245 -73
- package/src/internal-debug.ts +9 -2
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +31 -99
- package/src/loader.ts +30 -12
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +41 -0
- package/src/prerender/build-shell-capture.ts +447 -0
- package/src/prerender/param-hash.ts +16 -14
- package/src/prerender/shell-manifest-key.ts +20 -0
- package/src/prerender/store.ts +131 -22
- package/src/prerender.ts +460 -26
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +198 -128
- package/src/root-error-boundary.tsx +42 -48
- package/src/route-content-wrapper.tsx +22 -77
- package/src/route-definition/dsl-helpers.ts +1116 -0
- package/src/route-definition/helper-factories.ts +88 -0
- package/src/route-definition/helpers-types.ts +506 -0
- package/src/route-definition/index.ts +54 -0
- package/src/route-definition/redirect.ts +134 -0
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-definition.ts +1 -1481
- package/src/route-map-builder.ts +120 -145
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +71 -45
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +305 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +54 -27
- package/src/router/find-match.ts +259 -0
- package/src/router/handler-context.ts +385 -125
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +88 -28
- package/src/router/lazy-includes.ts +260 -0
- package/src/router/loader-resolution.ts +449 -157
- package/src/router/logging.ts +106 -6
- package/src/router/manifest.ts +144 -62
- package/src/router/match-api.ts +224 -256
- package/src/router/match-context.ts +4 -24
- package/src/router/match-handlers.ts +499 -0
- package/src/router/match-middleware/background-revalidation.ts +117 -93
- package/src/router/match-middleware/cache-lookup.ts +308 -150
- package/src/router/match-middleware/cache-store.ts +123 -51
- package/src/router/match-middleware/intercept-resolution.ts +44 -43
- package/src/router/match-middleware/segment-resolution.ts +64 -22
- package/src/router/match-pipelines.ts +11 -87
- package/src/router/match-result.ts +143 -52
- package/src/router/metrics.ts +235 -29
- package/src/router/middleware-types.ts +110 -0
- package/src/router/middleware.ts +526 -441
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +311 -142
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +575 -0
- package/src/router/preview-match.ts +102 -0
- package/src/router/request-classification.ts +291 -0
- package/src/router/revalidation.ts +203 -62
- package/src/router/route-snapshot.ts +256 -0
- package/src/router/router-context.ts +45 -48
- package/src/router/router-interfaces.ts +564 -0
- package/src/router/router-options.ts +784 -0
- package/src/router/router-registry.ts +21 -0
- package/src/router/segment-resolution/fresh.ts +812 -0
- package/src/router/segment-resolution/helpers.ts +348 -0
- package/src/router/segment-resolution/loader-cache.ts +315 -0
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +99 -0
- package/src/router/segment-resolution/revalidation.ts +1340 -0
- package/src/router/segment-resolution/static-store.ts +81 -0
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +25 -1354
- package/src/router/segment-wrappers.ts +292 -0
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +259 -0
- package/src/router/telemetry.ts +385 -0
- package/src/router/timeout.ts +128 -0
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +240 -61
- package/src/router/types.ts +23 -70
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +795 -2390
- package/src/rsc/capture-queue.ts +67 -0
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +46 -0
- package/src/rsc/handler.ts +956 -1164
- package/src/rsc/helpers.ts +736 -19
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +305 -0
- package/src/rsc/manifest-init.ts +77 -0
- package/src/rsc/nonce.ts +23 -0
- package/src/rsc/origin-guard.ts +155 -0
- package/src/rsc/progressive-enhancement.ts +541 -0
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +104 -0
- package/src/rsc/response-route-handler.ts +257 -0
- package/src/rsc/rsc-rendering.ts +856 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +525 -0
- package/src/rsc/shell-build-manifest.ts +316 -0
- package/src/rsc/shell-capture-constants.ts +27 -0
- package/src/rsc/shell-capture.ts +1716 -0
- package/src/rsc/shell-serve.ts +226 -0
- package/src/rsc/ssr-setup.ts +176 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +95 -12
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +99 -82
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-fragments.ts +124 -0
- package/src/segment-loader-promise.ts +167 -0
- package/src/segment-system.tsx +464 -135
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +640 -85
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +337 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +123 -42
- package/src/server/loader-registry.ts +51 -100
- package/src/server/request-context.ts +1069 -152
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +661 -140
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/preinit-client-references.ts +106 -0
- package/src/ssr/ssr-root.tsx +261 -0
- package/src/static-handler.ts +45 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +199 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +380 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +371 -0
- package/src/testing/render-route.tsx +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +219 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +76 -98
- package/src/theme/ThemeScript.tsx +12 -14
- package/src/theme/constants.ts +57 -15
- package/src/theme/index.ts +3 -20
- package/src/theme/theme-context.ts +5 -35
- package/src/theme/theme-script.ts +43 -39
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +123 -0
- package/src/types/cache-types.ts +207 -0
- package/src/types/error-types.ts +132 -0
- package/src/types/global-namespace.ts +113 -0
- package/src/types/handler-context.ts +850 -0
- package/src/types/index.ts +81 -0
- package/src/types/loader-types.ts +212 -0
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +138 -0
- package/src/types/route-entry.ts +114 -0
- package/src/types/segments.ts +271 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +162 -0
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +44 -0
- package/src/urls/path-helper-types.ts +418 -0
- package/src/urls/path-helper.ts +280 -0
- package/src/urls/pattern-types.ts +189 -0
- package/src/urls/response-types.ts +109 -0
- package/src/urls/type-extraction.ts +316 -0
- package/src/urls/urls-function.ts +80 -0
- package/src/urls.ts +1 -1352
- package/src/use-loader.tsx +406 -141
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +182 -0
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +408 -0
- package/src/vite/discovery/discovery-errors.ts +255 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +483 -0
- package/src/vite/discovery/route-types-writer.ts +214 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/shell-prerender-phase.ts +397 -0
- package/src/vite/discovery/state.ts +205 -0
- package/src/vite/discovery/virtual-module-codegen.ts +293 -0
- package/src/vite/index.ts +31 -2255
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +336 -0
- package/src/vite/plugins/cjs-to-esm.ts +90 -0
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- package/src/vite/plugins/client-ref-hashing.ts +118 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
- package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +805 -0
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +313 -0
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +94 -0
- package/src/vite/plugins/version-plugin.ts +271 -0
- package/src/vite/plugins/virtual-entries.ts +267 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +600 -0
- package/src/vite/router-discovery.ts +2120 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +132 -0
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +15 -0
- package/src/vite/utils/package-resolution.ts +89 -0
- package/src/vite/utils/prerender-utils.ts +268 -0
- package/src/vite/utils/shared-utils.ts +271 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/browser/shallow.ts +0 -35
- package/src/cache/memory-store.ts +0 -253
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/rsc/handler.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="@vitejs/plugin-rsc/types" />
|
|
2
|
-
/// <reference path="../vite/version.d.ts" />
|
|
2
|
+
/// <reference path="../vite/plugins/version.d.ts" />
|
|
3
3
|
/**
|
|
4
4
|
* RSC Request Handler
|
|
5
5
|
*
|
|
@@ -8,72 +8,99 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createElement } from "react";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import type { ResponseError } from "../urls.js";
|
|
14
|
-
import { getLoaderLazy } from "../server/loader-registry.js";
|
|
15
|
-
import {
|
|
16
|
-
matchMiddleware,
|
|
17
|
-
executeMiddleware,
|
|
18
|
-
executeLoaderMiddleware,
|
|
19
|
-
} from "../router/middleware.js";
|
|
11
|
+
import { isRouteNotFoundError } from "../errors.js";
|
|
12
|
+
import { matchMiddleware, executeMiddleware } from "../router/middleware.js";
|
|
20
13
|
import {
|
|
21
14
|
runWithRequestContext,
|
|
22
15
|
setRequestContextParams,
|
|
23
|
-
|
|
16
|
+
getRequestContext,
|
|
17
|
+
_getRequestContext,
|
|
24
18
|
createRequestContext,
|
|
25
|
-
type ExecutionContext,
|
|
26
19
|
} from "../server/request-context.js";
|
|
27
20
|
import * as rscDeps from "@vitejs/plugin-rsc/rsc";
|
|
28
|
-
|
|
29
21
|
import type {
|
|
30
22
|
RscPayload,
|
|
31
|
-
ReactFormState,
|
|
32
23
|
CreateRSCHandlerOptions,
|
|
24
|
+
LoadSSRModule,
|
|
25
|
+
SSRModule,
|
|
33
26
|
} from "./types.js";
|
|
34
|
-
import {
|
|
35
|
-
|
|
27
|
+
import {
|
|
28
|
+
RSC_FLIGHT_HTML_PHASES,
|
|
29
|
+
RSC_FLIGHT_ONLY_PHASES,
|
|
30
|
+
createResponseWithMergedHeaders,
|
|
31
|
+
finalizeResponse,
|
|
32
|
+
interceptRedirectForPartial,
|
|
33
|
+
buildRouteMiddlewareEntries,
|
|
34
|
+
observeRscHtmlStage,
|
|
35
|
+
renderRscFlightStage,
|
|
36
|
+
} from "./helpers.js";
|
|
37
|
+
import { guardOutgoingRedirect } from "./redirect-guard.js";
|
|
38
|
+
import { resolvedHandleStream } from "../handles/deferred-resolution.js";
|
|
39
|
+
import {
|
|
40
|
+
isWebSocketUpgradeResponse,
|
|
41
|
+
appendVaryAccept,
|
|
42
|
+
} from "../response-utils.js";
|
|
43
|
+
import {
|
|
44
|
+
handleResponseRoute,
|
|
45
|
+
type ResponseRouteMatch,
|
|
46
|
+
} from "./response-route-handler.js";
|
|
47
|
+
import { generateNonce, nonce as nonceToken } from "./nonce.js";
|
|
36
48
|
import { VERSION } from "@rangojs/router:version";
|
|
37
49
|
import type { ErrorPhase } from "../types.js";
|
|
50
|
+
import type { RouterRequestInput } from "../router/router-interfaces.js";
|
|
38
51
|
import { invokeOnError } from "../router/error-handling.js";
|
|
39
52
|
import {
|
|
40
|
-
|
|
53
|
+
createReverseFunction,
|
|
54
|
+
stripInternalParams,
|
|
55
|
+
} from "../router/handler-context.js";
|
|
56
|
+
import { contextSet } from "../context-var.js";
|
|
57
|
+
import {
|
|
41
58
|
hasCachedManifest,
|
|
42
|
-
setCachedManifest,
|
|
43
|
-
getRouteTrie,
|
|
44
|
-
setRouteTrie,
|
|
45
|
-
getPrecomputedEntries,
|
|
46
59
|
waitForManifestReady,
|
|
47
60
|
getRouterManifest,
|
|
48
61
|
getRouterTrie,
|
|
49
|
-
setRouterManifest,
|
|
50
|
-
setRouterTrie,
|
|
51
62
|
} from "../route-map-builder.js";
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
import type { HandlerContext } from "./handler-context.js";
|
|
64
|
+
import type { CacheErrorCategory } from "../cache/cache-error.js";
|
|
65
|
+
import type { SegmentCacheStore } from "../cache/types.js";
|
|
66
|
+
import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
|
|
67
|
+
import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
|
|
68
|
+
import {
|
|
69
|
+
executeServerAction,
|
|
70
|
+
revalidateAfterAction,
|
|
71
|
+
type ActionContinuation,
|
|
72
|
+
} from "./server-action.js";
|
|
73
|
+
import { handleLoaderFetch } from "./loader-fetch.js";
|
|
74
|
+
import {
|
|
75
|
+
checkRequestOrigin,
|
|
76
|
+
ORIGIN_CHECK_PHASE_BY_MODE,
|
|
77
|
+
} from "./origin-guard.js";
|
|
78
|
+
import { handleRscRendering } from "./rsc-rendering.js";
|
|
79
|
+
import {
|
|
80
|
+
withTimeout,
|
|
81
|
+
RouterTimeoutError,
|
|
82
|
+
createDefaultTimeoutResponse,
|
|
83
|
+
type TimeoutPhase,
|
|
84
|
+
} from "../router/timeout.js";
|
|
85
|
+
import {
|
|
86
|
+
createMetricsStore,
|
|
87
|
+
appendMetric,
|
|
88
|
+
buildMetricsTiming,
|
|
89
|
+
} from "../router/metrics.js";
|
|
90
|
+
import { observePhase, PHASES } from "../router/instrument.js";
|
|
91
|
+
import { safeEmit, resolveSink, getRequestId } from "../router/telemetry.js";
|
|
92
|
+
import {
|
|
93
|
+
startSSRSetup,
|
|
94
|
+
getSSRSetup,
|
|
95
|
+
mayNeedSSR,
|
|
96
|
+
isRscRequest,
|
|
97
|
+
SSR_SETUP_VAR,
|
|
98
|
+
} from "./ssr-setup.js";
|
|
99
|
+
import {
|
|
100
|
+
classifyRequest,
|
|
101
|
+
type RequestPlan,
|
|
102
|
+
type ExecutableRequestPlan,
|
|
103
|
+
} from "../router/request-classification.js";
|
|
77
104
|
|
|
78
105
|
/**
|
|
79
106
|
* Create an RSC request handler.
|
|
@@ -108,12 +135,35 @@ function createResponseErrorPayload(error: unknown, isDev: boolean): ResponseErr
|
|
|
108
135
|
* });
|
|
109
136
|
* ```
|
|
110
137
|
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Response that tells the client to do a full document navigation. Shared by
|
|
141
|
+
* the terminal reload plans (version-mismatch and app-switch): an empty 200
|
|
142
|
+
* carrying X-RSC-Reload, which the client turns into window.location.href.
|
|
143
|
+
*/
|
|
144
|
+
function createReloadResponse(reloadUrl: string) {
|
|
145
|
+
return createResponseWithMergedHeaders(null, {
|
|
146
|
+
status: 200,
|
|
147
|
+
headers: {
|
|
148
|
+
"X-RSC-Reload": reloadUrl,
|
|
149
|
+
"content-type": "text/x-component;charset=utf-8",
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
111
154
|
export function createRSCHandler<
|
|
112
155
|
TEnv = unknown,
|
|
113
156
|
TRoutes extends Record<string, string> = Record<string, string>,
|
|
114
157
|
>(options: CreateRSCHandlerOptions<TEnv, TRoutes>) {
|
|
115
158
|
const { router, version = VERSION, nonce: nonceProvider } = options;
|
|
116
159
|
|
|
160
|
+
// Handler-owned registry of explicit per-scope stores from cache({ store }).
|
|
161
|
+
// Lives in the closure so it is scoped per handler (multi-router deployments
|
|
162
|
+
// get separate registries) and accumulates every explicit store this handler
|
|
163
|
+
// resolves across requests. updateTag()/revalidateTag() iterate it to reach
|
|
164
|
+
// stores not covered by the app-level ctx._cacheStore.
|
|
165
|
+
const explicitTaggedStores = new Set<SegmentCacheStore>();
|
|
166
|
+
|
|
117
167
|
// Use provided deps or default to @vitejs/plugin-rsc/rsc exports
|
|
118
168
|
const deps = options.deps ?? rscDeps;
|
|
119
169
|
const {
|
|
@@ -125,33 +175,197 @@ export function createRSCHandler<
|
|
|
125
175
|
decodeFormState,
|
|
126
176
|
} = deps;
|
|
127
177
|
|
|
128
|
-
// Use provided loadSSRModule or default to vite RSC module loader
|
|
129
|
-
|
|
178
|
+
// Use provided loadSSRModule or default to vite RSC module loader.
|
|
179
|
+
// In production the SSR module is stable across requests, so memoize
|
|
180
|
+
// the dynamic import to avoid repeated module resolution overhead.
|
|
181
|
+
// In dev mode Vite may hot-reload the module, so skip memoization.
|
|
182
|
+
const rawLoadSSRModule: LoadSSRModule =
|
|
130
183
|
options.loadSSRModule ??
|
|
131
184
|
(() => import.meta.viteRsc.loadModule("ssr", "index"));
|
|
185
|
+
let _ssrModulePromise: Promise<SSRModule> | undefined;
|
|
186
|
+
const loadSSRModule: LoadSSRModule =
|
|
187
|
+
process.env.NODE_ENV === "production"
|
|
188
|
+
? () =>
|
|
189
|
+
(_ssrModulePromise ??= rawLoadSSRModule().catch((err) => {
|
|
190
|
+
_ssrModulePromise = undefined;
|
|
191
|
+
throw err;
|
|
192
|
+
}))
|
|
193
|
+
: rawLoadSSRModule;
|
|
132
194
|
|
|
133
195
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
196
|
+
* Per-request error reporter that deduplicates via the ALS request context.
|
|
197
|
+
*
|
|
198
|
+
* Uses the same _reportedErrors WeakSet as the router layer so errors
|
|
199
|
+
* that propagate across layers are only reported once per request.
|
|
136
200
|
*/
|
|
137
201
|
function callOnError(
|
|
138
202
|
error: unknown,
|
|
139
203
|
phase: ErrorPhase,
|
|
140
204
|
context: Parameters<typeof invokeOnError<TEnv>>[3],
|
|
141
205
|
): void {
|
|
206
|
+
// Guard: abort signal handlers fire asynchronously outside the ALS
|
|
207
|
+
// request scope, so the context may be gone. Skip dedup in that
|
|
208
|
+
// case — the error is from a cancelled stream, not a real failure.
|
|
209
|
+
const reqCtx = _getRequestContext();
|
|
210
|
+
if (error != null && typeof error === "object" && reqCtx) {
|
|
211
|
+
if (reqCtx._reportedErrors.has(error)) return;
|
|
212
|
+
reqCtx._reportedErrors.add(error);
|
|
213
|
+
}
|
|
142
214
|
invokeOnError(router.onError, error, phase, context, "RSC");
|
|
143
215
|
}
|
|
144
216
|
|
|
145
|
-
|
|
217
|
+
function getRequiredRouteMap(): Record<string, string> {
|
|
218
|
+
const routeMap = getRouterManifest(router.id);
|
|
219
|
+
if (!routeMap) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Route manifest for router "${router.id}" is not available.`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return routeMap;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Handle a timeout by reporting the error, emitting telemetry,
|
|
229
|
+
* and returning either the custom onTimeout response or a default 504.
|
|
230
|
+
*/
|
|
231
|
+
async function handleTimeoutResponse(
|
|
146
232
|
request: Request,
|
|
147
|
-
env: TEnv
|
|
148
|
-
|
|
233
|
+
env: TEnv,
|
|
234
|
+
url: URL,
|
|
235
|
+
phase: TimeoutPhase,
|
|
236
|
+
durationMs: number,
|
|
237
|
+
routeKey?: string,
|
|
238
|
+
actionId?: string,
|
|
239
|
+
): Promise<Response> {
|
|
240
|
+
const timeoutError = new RouterTimeoutError(phase, durationMs);
|
|
241
|
+
|
|
242
|
+
callOnError(timeoutError, phase === "action" ? "action" : "handler", {
|
|
243
|
+
request,
|
|
244
|
+
url,
|
|
245
|
+
env,
|
|
246
|
+
routeKey,
|
|
247
|
+
actionId,
|
|
248
|
+
handledByBoundary: false,
|
|
249
|
+
metadata: { timeout: true, phase, durationMs },
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (router.telemetry) {
|
|
253
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
254
|
+
type: "request.timeout",
|
|
255
|
+
timestamp: performance.now(),
|
|
256
|
+
requestId: getRequestId(request),
|
|
257
|
+
phase,
|
|
258
|
+
pathname: url.pathname,
|
|
259
|
+
routeKey,
|
|
260
|
+
actionId,
|
|
261
|
+
durationMs,
|
|
262
|
+
customHandler: !!router.onTimeout,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (router.onTimeout) {
|
|
267
|
+
try {
|
|
268
|
+
return await router.onTimeout({
|
|
269
|
+
phase,
|
|
270
|
+
request,
|
|
271
|
+
url,
|
|
272
|
+
env,
|
|
273
|
+
routeKey,
|
|
274
|
+
actionId,
|
|
275
|
+
durationMs,
|
|
276
|
+
});
|
|
277
|
+
} catch (e) {
|
|
278
|
+
if (process.env.NODE_ENV !== "production") {
|
|
279
|
+
console.error("[RSC] onTimeout callback error:", e);
|
|
280
|
+
}
|
|
281
|
+
return createDefaultTimeoutResponse(phase);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return createDefaultTimeoutResponse(phase);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Build a 200 Flight response that carries a redirect URL and optional state.
|
|
290
|
+
* Used when a partial/action request results in a redirect -- fetch
|
|
291
|
+
* auto-follows 3xx so we send the redirect as payload metadata instead.
|
|
292
|
+
*/
|
|
293
|
+
function createRedirectFlightResponse(
|
|
294
|
+
redirectUrl: string,
|
|
295
|
+
locationState?: Record<string, unknown>,
|
|
296
|
+
external?: boolean,
|
|
297
|
+
): Response {
|
|
298
|
+
const redirectPayload: RscPayload = {
|
|
299
|
+
metadata: {
|
|
300
|
+
pathname: redirectUrl,
|
|
301
|
+
segments: [],
|
|
302
|
+
redirect: { url: redirectUrl, ...(external && { external: true }) },
|
|
303
|
+
...(locationState && { locationState }),
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
const reqCtx = _getRequestContext<TEnv>();
|
|
307
|
+
const rscStream = reqCtx
|
|
308
|
+
? renderRscFlightStage(
|
|
309
|
+
{
|
|
310
|
+
ctx: { renderToReadableStream, callOnError },
|
|
311
|
+
request: reqCtx.request,
|
|
312
|
+
url: reqCtx.url,
|
|
313
|
+
env: reqCtx.env,
|
|
314
|
+
payload: redirectPayload,
|
|
315
|
+
tracking: {
|
|
316
|
+
mode: reqCtx.url.searchParams.has("_rsc_action")
|
|
317
|
+
? "action-revalidation"
|
|
318
|
+
: "partial",
|
|
319
|
+
routeKey: reqCtx._routeName,
|
|
320
|
+
phases: RSC_FLIGHT_ONLY_PHASES,
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
performance.now(),
|
|
324
|
+
).stream
|
|
325
|
+
: renderToReadableStream<RscPayload>(redirectPayload);
|
|
326
|
+
return createResponseWithMergedHeaders(rscStream, {
|
|
327
|
+
status: 200,
|
|
328
|
+
headers: { "content-type": "text/x-component;charset=utf-8" },
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Bundle shared dependencies for extracted handler functions.
|
|
333
|
+
// callOnError reads from ALS so it's inherently per-request scoped.
|
|
334
|
+
const handlerCtx: HandlerContext<TEnv> = {
|
|
335
|
+
router,
|
|
336
|
+
version,
|
|
337
|
+
renderToReadableStream,
|
|
338
|
+
decodeReply,
|
|
339
|
+
createTemporaryReferenceSet,
|
|
340
|
+
loadServerAction,
|
|
341
|
+
decodeAction,
|
|
342
|
+
decodeFormState,
|
|
343
|
+
loadSSRModule,
|
|
344
|
+
callOnError,
|
|
345
|
+
getRequiredRouteMap,
|
|
346
|
+
createRedirectFlightResponse,
|
|
347
|
+
resolveStreamMode: async (request, env, url) => {
|
|
348
|
+
const resolver = router.ssr?.resolveStreaming;
|
|
349
|
+
if (!resolver) return "stream";
|
|
350
|
+
return resolver({ request, env, url });
|
|
149
351
|
},
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
return async function handler(
|
|
355
|
+
request: Request,
|
|
356
|
+
input: RouterRequestInput<TEnv> = {},
|
|
150
357
|
): Promise<Response> {
|
|
151
358
|
const handlerStart = performance.now();
|
|
359
|
+
// Create the metrics store at handler start so handler:total has startTime=0
|
|
360
|
+
// and all metrics are relative to the request entry point.
|
|
361
|
+
const earlyMetricsStore = router.debugPerformance
|
|
362
|
+
? createMetricsStore(true, handlerStart)
|
|
363
|
+
: undefined;
|
|
364
|
+
|
|
365
|
+
const { env = {} as TEnv, vars: initialVars, ctx: executionCtx } = input;
|
|
152
366
|
|
|
153
367
|
// Connection warmup: return 204 immediately before any processing
|
|
154
|
-
if (router
|
|
368
|
+
if (router?.warmupEnabled && request.method === "HEAD") {
|
|
155
369
|
const warmupUrl = new URL(request.url);
|
|
156
370
|
if (warmupUrl.searchParams.has("_rsc_warmup")) {
|
|
157
371
|
return new Response(null, { status: 204 });
|
|
@@ -175,24 +389,27 @@ export function createRSCHandler<
|
|
|
175
389
|
const mwMatchDur = performance.now() - mwMatchStart;
|
|
176
390
|
|
|
177
391
|
// Shared variables between middleware and route handlers
|
|
178
|
-
// Initialize from
|
|
179
|
-
const variables: Record<string, any> =
|
|
180
|
-
|
|
181
|
-
|
|
392
|
+
// Initialize from input.vars if provided (allows pre-seeding from worker entry)
|
|
393
|
+
const variables: Record<string, any> = initialVars
|
|
394
|
+
? { ...initialVars }
|
|
395
|
+
: {};
|
|
182
396
|
|
|
183
|
-
// Store nonce
|
|
397
|
+
// Store nonce via ContextVar token and string key for backward compat
|
|
184
398
|
if (nonce) {
|
|
399
|
+
contextSet(variables, nonceToken, nonce);
|
|
185
400
|
variables.nonce = nonce;
|
|
186
401
|
}
|
|
187
402
|
|
|
188
403
|
// Resolve cache store configuration
|
|
189
404
|
// Priority: options.cache (handler override) > router.cache (router default)
|
|
190
405
|
// Store is enabled only if: config provided, enabled, and no ?__no_cache query param
|
|
191
|
-
let cacheStore
|
|
406
|
+
let cacheStore: SegmentCacheStore | undefined;
|
|
192
407
|
const cacheOption = options.cache ?? router.cache;
|
|
193
408
|
if (cacheOption && !url.searchParams.has("__no_cache")) {
|
|
194
409
|
const cacheConfig =
|
|
195
|
-
typeof cacheOption === "function"
|
|
410
|
+
typeof cacheOption === "function"
|
|
411
|
+
? cacheOption(env, executionCtx)
|
|
412
|
+
: cacheOption;
|
|
196
413
|
|
|
197
414
|
if (cacheConfig.enabled !== false) {
|
|
198
415
|
cacheStore = cacheConfig.store;
|
|
@@ -223,56 +440,7 @@ export function createRSCHandler<
|
|
|
223
440
|
// Cloudflare dev: generate manifest inline for this router.
|
|
224
441
|
// Each router generates its own manifest independently so
|
|
225
442
|
// multi-router setups (host routing) work correctly.
|
|
226
|
-
|
|
227
|
-
await import("../build/generate-manifest.js");
|
|
228
|
-
const generated = generateManifest(router.urlpatterns);
|
|
229
|
-
if (
|
|
230
|
-
generated._routeAncestry &&
|
|
231
|
-
Object.keys(generated._routeAncestry).length > 0
|
|
232
|
-
) {
|
|
233
|
-
const { buildRouteTrie } = await import("../build/route-trie.js");
|
|
234
|
-
// Map each route to its include() staticPrefix so the trie
|
|
235
|
-
// returns the correct sp for lazy entry lookup in findMatch.
|
|
236
|
-
const routeToStaticPrefix: Record<string, string> = {};
|
|
237
|
-
for (const name of Object.keys(generated.routeManifest)) {
|
|
238
|
-
routeToStaticPrefix[name] = "";
|
|
239
|
-
}
|
|
240
|
-
// Override with prefix from include() entries so the trie
|
|
241
|
-
// returns the correct sp for lazy entry lookup in findMatch.
|
|
242
|
-
// Walk recursively to include routes in nested includes.
|
|
243
|
-
if (generated.prefixTree) {
|
|
244
|
-
const visitPrefixNode = (node: any): void => {
|
|
245
|
-
const sp = node.staticPrefix || "";
|
|
246
|
-
for (const route of (node.routes || [])) {
|
|
247
|
-
routeToStaticPrefix[route] = sp;
|
|
248
|
-
}
|
|
249
|
-
for (const child of Object.values(node.children || {})) {
|
|
250
|
-
visitPrefixNode(child);
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
for (const node of Object.values(generated.prefixTree)) {
|
|
254
|
-
visitPrefixNode(node);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const trie = buildRouteTrie(
|
|
258
|
-
generated.routeManifest,
|
|
259
|
-
generated._routeAncestry,
|
|
260
|
-
routeToStaticPrefix,
|
|
261
|
-
generated.routeTrailingSlash,
|
|
262
|
-
generated.prerenderRoutes ? new Set(generated.prerenderRoutes) : undefined,
|
|
263
|
-
generated.passthroughRoutes ? new Set(generated.passthroughRoutes) : undefined,
|
|
264
|
-
generated.responseTypeRoutes,
|
|
265
|
-
);
|
|
266
|
-
setRouterTrie(router.id, trie);
|
|
267
|
-
// Set global trie only if not already set by another router
|
|
268
|
-
if (!getRouteTrie()) {
|
|
269
|
-
setRouteTrie(trie);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
setRouterManifest(router.id, generated.routeManifest);
|
|
273
|
-
// Merge into global manifest (needed for reverse/href across routers)
|
|
274
|
-
const existing = hasCachedManifest() ? getGlobalRouteMap() : {};
|
|
275
|
-
setCachedManifest({ ...existing, ...generated.routeManifest });
|
|
443
|
+
await buildRouterTrieFromUrlpatterns(router);
|
|
276
444
|
}
|
|
277
445
|
if (!getRouterManifest(router.id) && !hasCachedManifest()) {
|
|
278
446
|
throw new Error(
|
|
@@ -280,10 +448,17 @@ export function createRSCHandler<
|
|
|
280
448
|
);
|
|
281
449
|
}
|
|
282
450
|
}
|
|
283
|
-
const manifestCacheDur = performance.now() - manifestCacheStart;
|
|
284
451
|
|
|
285
|
-
//
|
|
286
|
-
// This
|
|
452
|
+
// Rebuild the trie when the manifest exists but the per-router trie is
|
|
453
|
+
// missing. This happens in dev mode after HMR: the virtual module sets
|
|
454
|
+
// the manifest (from fresh gen files) but the trie is intentionally not
|
|
455
|
+
// injected to avoid stale discovery-time data. Without the trie, route
|
|
456
|
+
// matching falls back to regex iteration which does not handle wildcard
|
|
457
|
+
// priority correctly (catch-all patterns match before specific routes).
|
|
458
|
+
if (!getRouterTrie(router.id) && router.urlpatterns) {
|
|
459
|
+
await buildRouterTrieFromUrlpatterns(router);
|
|
460
|
+
}
|
|
461
|
+
const manifestCacheDur = performance.now() - manifestCacheStart;
|
|
287
462
|
|
|
288
463
|
// Create unified request context with all methods
|
|
289
464
|
// Includes: stub response, handle store, loader memoization, use(), cookies, headers, cache store
|
|
@@ -295,9 +470,35 @@ export function createRSCHandler<
|
|
|
295
470
|
url,
|
|
296
471
|
variables,
|
|
297
472
|
cacheStore,
|
|
298
|
-
|
|
473
|
+
explicitTaggedStores,
|
|
474
|
+
cacheProfiles: router.cacheProfiles,
|
|
475
|
+
executionContext: executionCtx,
|
|
299
476
|
themeConfig: router.themeConfig,
|
|
477
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
478
|
+
version,
|
|
300
479
|
});
|
|
480
|
+
// Thread the true request entry timestamp onto the context so a metrics
|
|
481
|
+
// store created MID-request (ctx.debugPerformance() / getMetricsStore) anchors
|
|
482
|
+
// to the real start, not the opt-in moment — phases that began earlier then
|
|
483
|
+
// report non-negative offsets. Set unconditionally: debug may be enabled later.
|
|
484
|
+
requestContext._handlerStart = handlerStart;
|
|
485
|
+
if (earlyMetricsStore) {
|
|
486
|
+
requestContext._debugPerformance = true;
|
|
487
|
+
requestContext._metricsStore = earlyMetricsStore;
|
|
488
|
+
}
|
|
489
|
+
// Wire background error reporting so "use cache" and other subsystems
|
|
490
|
+
// can surface non-fatal errors through the router's onError callback.
|
|
491
|
+
requestContext._reportBackgroundError = (
|
|
492
|
+
error: unknown,
|
|
493
|
+
category: CacheErrorCategory,
|
|
494
|
+
) => {
|
|
495
|
+
callOnError(error, "cache", {
|
|
496
|
+
request,
|
|
497
|
+
url,
|
|
498
|
+
metadata: { category },
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
|
|
301
502
|
const ctxCreateDur = performance.now() - ctxCreateStart;
|
|
302
503
|
|
|
303
504
|
// Accumulate handler-level timing for Server-Timing header
|
|
@@ -319,1159 +520,750 @@ export function createRSCHandler<
|
|
|
319
520
|
// - Server components during rendering
|
|
320
521
|
// - Error boundaries
|
|
321
522
|
// - Streaming
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
url: URL,
|
|
348
|
-
variables: Record<string, any>,
|
|
349
|
-
nonce: string | undefined,
|
|
350
|
-
): Promise<Response> {
|
|
351
|
-
// First, check for route-level middleware
|
|
352
|
-
const previewStart = performance.now();
|
|
353
|
-
const preview = await router.previewMatch(request, env);
|
|
354
|
-
const previewDur = performance.now() - previewStart;
|
|
355
|
-
const handlerTiming: string[] = variables.__handlerTiming || [];
|
|
356
|
-
handlerTiming.push(`handler-preview-match;dur=${previewDur.toFixed(2)}`);
|
|
357
|
-
// Response route short-circuit: skip entire RSC pipeline
|
|
358
|
-
if (preview?.responseType && preview.handler) {
|
|
359
|
-
const isPartial = url.searchParams.has("_rsc_partial");
|
|
360
|
-
|
|
361
|
-
// Partial requests (client-side navigation) to response routes
|
|
362
|
-
// get X-RSC-Reload to trigger hard navigation in the browser
|
|
363
|
-
if (isPartial) {
|
|
364
|
-
const cleanUrl = new URL(url);
|
|
365
|
-
cleanUrl.searchParams.delete("_rsc_partial");
|
|
366
|
-
cleanUrl.searchParams.delete("_rsc_segments");
|
|
367
|
-
cleanUrl.searchParams.delete("_rsc_v");
|
|
368
|
-
cleanUrl.searchParams.delete("_rsc_stale");
|
|
369
|
-
cleanUrl.searchParams.delete("_rsc_action");
|
|
370
|
-
cleanUrl.searchParams.delete("_rsc_prev");
|
|
371
|
-
|
|
372
|
-
return createResponseWithMergedHeaders(null, {
|
|
373
|
-
status: 200,
|
|
374
|
-
headers: {
|
|
375
|
-
"X-RSC-Reload": cleanUrl.toString(),
|
|
376
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
}
|
|
523
|
+
// Store basename on request context (scoped per-request via existing ALS)
|
|
524
|
+
requestContext._basename = router.basename;
|
|
525
|
+
|
|
526
|
+
// Resolved span tracing for this request (read at each traced phase).
|
|
527
|
+
requestContext._tracing = router.tracing;
|
|
528
|
+
|
|
529
|
+
// The "rango.request" span is opened inside the request context so the
|
|
530
|
+
// Cloudflare runner can read executionContext.tracing, and so every nested
|
|
531
|
+
// phase span (and the platform's automatic KV/D1/fetch spans) nests under
|
|
532
|
+
// it. Construction-bound: the span ends when the Response is built, never
|
|
533
|
+
// wrapping the streamed body. metric:false — handler:total is metered
|
|
534
|
+
// directly below (a grand total incl. the pre-context bootstrap timings).
|
|
535
|
+
// When tracing is off this is a direct pass-through.
|
|
536
|
+
return runWithRequestContext(requestContext, () =>
|
|
537
|
+
observePhase(PHASES.request, async (span) => {
|
|
538
|
+
span.setAttribute("http.method", request.method);
|
|
539
|
+
// The matched route template is not known until match() runs later, so
|
|
540
|
+
// emit the concrete path as url.path (low-level), NOT http.route — the
|
|
541
|
+
// latter is reserved for the low-cardinality template (OTel convention).
|
|
542
|
+
span.setAttribute("url.path", url.pathname);
|
|
543
|
+
|
|
544
|
+
// Core handler logic (wrapped by middleware)
|
|
545
|
+
const coreHandler = async (): Promise<Response> => {
|
|
546
|
+
return coreRequestHandler(request, env, url, variables, nonce);
|
|
547
|
+
};
|
|
380
548
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (name.startsWith("/")) {
|
|
393
|
-
if (!hrefParams) return name;
|
|
394
|
-
return name.replace(/:([^/]+)/g, (_, key) => {
|
|
395
|
-
const value = hrefParams[key];
|
|
396
|
-
if (value === undefined) throw new Error(`Missing param "${key}" for path "${name}"`);
|
|
397
|
-
return encodeURIComponent(value);
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
return name;
|
|
401
|
-
},
|
|
402
|
-
get: (key: string) => variables[key],
|
|
403
|
-
header: (name: string, value: string) => reqCtx.header(name, value),
|
|
404
|
-
setCookie: (name: string, value: string, options?: any) => reqCtx.setCookie(name, value, options),
|
|
405
|
-
};
|
|
549
|
+
// Execute middleware chain if any, otherwise call core handler directly
|
|
550
|
+
let response: Response;
|
|
551
|
+
if (matchedMiddleware.length > 0) {
|
|
552
|
+
const mwResponse = await executeMiddleware(
|
|
553
|
+
matchedMiddleware,
|
|
554
|
+
request,
|
|
555
|
+
env,
|
|
556
|
+
variables,
|
|
557
|
+
coreHandler,
|
|
558
|
+
createReverseFunction(getRequiredRouteMap()),
|
|
559
|
+
);
|
|
406
560
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (result instanceof Response) {
|
|
415
|
-
const mergedHeaders: Record<string, string> = {};
|
|
416
|
-
result.headers.forEach((value, key) => {
|
|
417
|
-
mergedHeaders[key] = value;
|
|
418
|
-
});
|
|
419
|
-
return createResponseWithMergedHeaders(result.body, {
|
|
420
|
-
status: result.status,
|
|
421
|
-
headers: mergedHeaders,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
return createResponseWithMergedHeaders(
|
|
425
|
-
JSON.stringify({ data: result }),
|
|
426
|
-
{ status: 200, headers: { "content-type": "application/json;charset=utf-8" } },
|
|
427
|
-
);
|
|
428
|
-
} catch (error) {
|
|
429
|
-
callOnError(error, "handler", errorCtx);
|
|
430
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
431
|
-
const status = error instanceof RouterError ? error.status : 500;
|
|
432
|
-
return createResponseWithMergedHeaders(
|
|
433
|
-
JSON.stringify({ error: createResponseErrorPayload(error, isDev) }),
|
|
434
|
-
{ status, headers: { "content-type": "application/json;charset=utf-8" } },
|
|
561
|
+
if (
|
|
562
|
+
url.searchParams.has("_rsc_partial") ||
|
|
563
|
+
url.searchParams.has("_rsc_action")
|
|
564
|
+
) {
|
|
565
|
+
const intercepted = interceptRedirectForPartial(
|
|
566
|
+
mwResponse,
|
|
567
|
+
createRedirectFlightResponse,
|
|
435
568
|
);
|
|
569
|
+
response = intercepted ?? finalizeResponse(mwResponse);
|
|
570
|
+
} else {
|
|
571
|
+
response = finalizeResponse(mwResponse);
|
|
436
572
|
}
|
|
573
|
+
} else {
|
|
574
|
+
response = await coreHandler();
|
|
437
575
|
}
|
|
438
576
|
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
case "md":
|
|
474
|
-
return createResponseWithMergedHeaders(
|
|
475
|
-
String(result),
|
|
476
|
-
{ status: 200, headers: { "content-type": "text/markdown;charset=utf-8" } },
|
|
477
|
-
);
|
|
478
|
-
default:
|
|
479
|
-
// image, stream, any -- must return Response
|
|
480
|
-
throw new Error(
|
|
481
|
-
`Response route handler for "${preview.responseType}" must return a Response object, got ${typeof result}`
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
} catch (error) {
|
|
485
|
-
callOnError(error, "handler", errorCtx);
|
|
486
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
487
|
-
const status = error instanceof RouterError ? error.status : 500;
|
|
488
|
-
const message = error instanceof RouterError
|
|
489
|
-
? error.message
|
|
490
|
-
: isDev && error instanceof Error
|
|
491
|
-
? error.message
|
|
492
|
-
: "Internal Server Error";
|
|
493
|
-
return createResponseWithMergedHeaders(message, {
|
|
494
|
-
status,
|
|
495
|
-
headers: { "content-type": "text/plain;charset=utf-8" },
|
|
496
|
-
});
|
|
577
|
+
// Finalize metrics after all middleware (including post-next work)
|
|
578
|
+
// has completed so :post spans are captured in the timeline.
|
|
579
|
+
// Handler timing parts are always emitted (even without debug metrics)
|
|
580
|
+
// so non-debug requests still get bootstrap Server-Timing entries.
|
|
581
|
+
const handlerTimingArr: string[] = variables.__handlerTiming || [];
|
|
582
|
+
// Preserve any existing Server-Timing set by response routes or middleware
|
|
583
|
+
const existingTiming = response.headers.get("Server-Timing");
|
|
584
|
+
const timingParts = existingTiming
|
|
585
|
+
? [existingTiming, ...handlerTimingArr]
|
|
586
|
+
: [...handlerTimingArr];
|
|
587
|
+
|
|
588
|
+
const metricsStore = requestContext._metricsStore;
|
|
589
|
+
if (metricsStore) {
|
|
590
|
+
// When the store was created at handler start (earlyMetricsStore),
|
|
591
|
+
// handler:total covers the full request. When ctx.debugPerformance()
|
|
592
|
+
// created the store mid-request its requestStart is now the threaded
|
|
593
|
+
// _handlerStart (== handlerStart), so both branches yield the true
|
|
594
|
+
// request entry; reading the store's own anchor keeps this correct even
|
|
595
|
+
// if a store ever lands without the threading (falls back to its start).
|
|
596
|
+
const totalStart = earlyMetricsStore
|
|
597
|
+
? handlerStart
|
|
598
|
+
: metricsStore.requestStart;
|
|
599
|
+
appendMetric(
|
|
600
|
+
metricsStore,
|
|
601
|
+
"handler:total",
|
|
602
|
+
totalStart,
|
|
603
|
+
performance.now() - totalStart,
|
|
604
|
+
);
|
|
605
|
+
const metricsTiming = buildMetricsTiming(
|
|
606
|
+
request.method,
|
|
607
|
+
url.pathname,
|
|
608
|
+
metricsStore,
|
|
609
|
+
);
|
|
610
|
+
if (metricsTiming) timingParts.push(metricsTiming);
|
|
497
611
|
}
|
|
498
|
-
};
|
|
499
612
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
613
|
+
const fullTiming = timingParts.join(", ");
|
|
614
|
+
if (fullTiming && !isWebSocketUpgradeResponse(response)) {
|
|
615
|
+
try {
|
|
616
|
+
response.headers.set("Server-Timing", fullTiming);
|
|
617
|
+
} catch {
|
|
618
|
+
// Immutable headers (e.g. a passed-through platform Response) — drop
|
|
619
|
+
// the timing header, never the response. Instrumentation must not
|
|
620
|
+
// 500 a request.
|
|
621
|
+
}
|
|
505
622
|
}
|
|
506
|
-
return response;
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
if (preview.routeMiddleware && preview.routeMiddleware.length > 0) {
|
|
510
|
-
const middlewareEntries = preview.routeMiddleware.map((mw) => ({
|
|
511
|
-
entry: {
|
|
512
|
-
pattern: null,
|
|
513
|
-
regex: null,
|
|
514
|
-
paramNames: [],
|
|
515
|
-
handler: mw.handler,
|
|
516
|
-
mountPrefix: null,
|
|
517
|
-
},
|
|
518
|
-
params: mw.params,
|
|
519
|
-
}));
|
|
520
|
-
return executeMiddleware(middlewareEntries, request, env, variables, callHandlerWithVary);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return callHandlerWithVary();
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Wrap RSC handler to append Vary: Accept on content-negotiated routes
|
|
527
|
-
const rscHandler = async () => {
|
|
528
|
-
const response = await coreRequestHandlerInner(request, env, url, variables, nonce);
|
|
529
|
-
if (preview?.negotiated) {
|
|
530
|
-
response.headers.append("Vary", "Accept");
|
|
531
|
-
}
|
|
532
|
-
return response;
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
if (preview?.routeMiddleware && preview.routeMiddleware.length > 0) {
|
|
536
|
-
// Convert route middleware to app middleware format for execution
|
|
537
|
-
const middlewareEntries = preview.routeMiddleware.map((mw) => ({
|
|
538
|
-
entry: {
|
|
539
|
-
pattern: null,
|
|
540
|
-
regex: null,
|
|
541
|
-
paramNames: [],
|
|
542
|
-
handler: mw.handler,
|
|
543
|
-
mountPrefix: null,
|
|
544
|
-
},
|
|
545
|
-
params: mw.params,
|
|
546
|
-
}));
|
|
547
|
-
|
|
548
|
-
// Execute route middleware wrapping the actual request handling
|
|
549
|
-
return executeMiddleware(middlewareEntries, request, env, variables, rscHandler);
|
|
550
|
-
}
|
|
551
623
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
624
|
+
// Single open-redirect chokepoint: every response (PE, full-page,
|
|
625
|
+
// middleware short-circuit, response-route) funnels through here, so
|
|
626
|
+
// guarding browser-followed (3xx) redirects once covers them all and any
|
|
627
|
+
// future redirect exit. Soft SPA/Flight redirects are 200/204 and pass
|
|
628
|
+
// through untouched (validated client-side instead).
|
|
629
|
+
return guardOutgoingRedirect(response, url.origin, router.basename);
|
|
630
|
+
}),
|
|
631
|
+
);
|
|
632
|
+
};
|
|
555
633
|
|
|
556
|
-
//
|
|
557
|
-
|
|
634
|
+
// Core request handling logic (separated for middleware wrapping).
|
|
635
|
+
// Uses the classify → execute model: classifyRequest produces a RequestPlan,
|
|
636
|
+
// then execution dispatches on the plan mode.
|
|
637
|
+
async function coreRequestHandler(
|
|
558
638
|
request: Request,
|
|
559
639
|
env: TEnv,
|
|
560
640
|
url: URL,
|
|
561
641
|
variables: Record<string, any>,
|
|
562
642
|
nonce: string | undefined,
|
|
563
643
|
): Promise<Response> {
|
|
564
|
-
const
|
|
565
|
-
const isAction =
|
|
566
|
-
request.headers.has("rsc-action") || url.searchParams.has("_rsc_action");
|
|
567
|
-
const actionId =
|
|
568
|
-
request.headers.get("rsc-action") || url.searchParams.get("_rsc_action");
|
|
569
|
-
|
|
570
|
-
// Version mismatch detection - client may have stale code after HMR/deployment
|
|
571
|
-
// If versions don't match, tell the client to reload
|
|
572
|
-
const clientVersion = url.searchParams.get("_rsc_v");
|
|
573
|
-
if (version && clientVersion && clientVersion !== version) {
|
|
574
|
-
console.log(
|
|
575
|
-
`[RSC] Version mismatch: client=${clientVersion}, server=${version}. Forcing reload.`,
|
|
576
|
-
);
|
|
644
|
+
const handlerTiming: string[] = variables.__handlerTiming || [];
|
|
577
645
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const reloadUrl = isAction
|
|
590
|
-
? request.headers.get("referer") || cleanUrl.toString()
|
|
591
|
-
: cleanUrl.toString();
|
|
592
|
-
|
|
593
|
-
// Return special response that tells client to reload
|
|
594
|
-
return createResponseWithMergedHeaders(null, {
|
|
595
|
-
status: 200,
|
|
596
|
-
headers: {
|
|
597
|
-
"X-RSC-Reload": reloadUrl,
|
|
598
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
599
|
-
},
|
|
646
|
+
// ---- 1. Classify ----
|
|
647
|
+
// classifyRequest may throw RouteNotFoundError for unknown routes.
|
|
648
|
+
// In that case, fall through to a full-render plan so the pipeline
|
|
649
|
+
// can render the 404 page via the existing error handling path.
|
|
650
|
+
const classifyStart = performance.now();
|
|
651
|
+
let plan: RequestPlan<TEnv>;
|
|
652
|
+
try {
|
|
653
|
+
plan = await classifyRequest<TEnv>(request, url, {
|
|
654
|
+
findMatch: router.findMatch,
|
|
655
|
+
routerVersion: version,
|
|
656
|
+
routerId: router.id,
|
|
600
657
|
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
routeTrie: trie,
|
|
619
|
-
precomputedEntries: getPrecomputedEntries(),
|
|
658
|
+
} catch (error) {
|
|
659
|
+
if (isRouteNotFoundError(error)) {
|
|
660
|
+
// Let the render path handle 404 — match()/matchPartial() will
|
|
661
|
+
// re-throw RouteNotFoundError and the catch block in
|
|
662
|
+
// executeRenderWithMiddleware renders the not-found page.
|
|
663
|
+
plan = {
|
|
664
|
+
mode: "full-render",
|
|
665
|
+
route: {
|
|
666
|
+
matched: null as any,
|
|
667
|
+
manifestEntry: null as any,
|
|
668
|
+
entries: [],
|
|
669
|
+
routeKey: "",
|
|
670
|
+
localRouteName: "",
|
|
671
|
+
params: {},
|
|
672
|
+
routeMiddleware: [],
|
|
673
|
+
cacheScope: null,
|
|
674
|
+
isPassthrough: false,
|
|
620
675
|
},
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
},
|
|
627
|
-
);
|
|
676
|
+
negotiated: false,
|
|
677
|
+
};
|
|
678
|
+
} else {
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
628
681
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
//
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
request,
|
|
639
|
-
env,
|
|
640
|
-
url,
|
|
641
|
-
isAction,
|
|
642
|
-
handleStore,
|
|
643
|
-
nonce,
|
|
644
|
-
);
|
|
645
|
-
if (progressiveResult) {
|
|
646
|
-
return progressiveResult;
|
|
682
|
+
const classifyDur = performance.now() - classifyStart;
|
|
683
|
+
handlerTiming.push(`handler-classify;dur=${classifyDur.toFixed(2)}`);
|
|
684
|
+
|
|
685
|
+
// ---- 2. Terminal plans (no execution needed) ----
|
|
686
|
+
if (plan.mode === "redirect") {
|
|
687
|
+
// Redirects are handled by the pipeline (match/matchPartial),
|
|
688
|
+
// but for partial requests we short-circuit with a Flight redirect.
|
|
689
|
+
if (url.searchParams.has("_rsc_partial")) {
|
|
690
|
+
return createRedirectFlightResponse(plan.redirectUrl);
|
|
647
691
|
}
|
|
692
|
+
// Full requests: let the pipeline handle the redirect via match()
|
|
693
|
+
// which returns { redirect: url }. Fall through to full-render.
|
|
694
|
+
}
|
|
648
695
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
696
|
+
if (plan.mode === "version-mismatch") {
|
|
697
|
+
console.log(
|
|
698
|
+
`[RSC] Version mismatch: client=${url.searchParams.get("_rsc_v")}, server=${version}. Forcing reload.`,
|
|
699
|
+
);
|
|
700
|
+
return createReloadResponse(plan.reloadUrl);
|
|
701
|
+
}
|
|
655
702
|
|
|
656
|
-
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
703
|
+
if (plan.mode === "app-switch") {
|
|
704
|
+
// Cross-app SPA navigation crossed a host-router app boundary. Force a
|
|
705
|
+
// real document navigation so the target app's document is re-established
|
|
706
|
+
// (stylesheets, theme, warmup, prefetch-TTL). See request-classification.
|
|
707
|
+
return createReloadResponse(plan.reloadUrl);
|
|
708
|
+
}
|
|
663
709
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return await handleRscRendering(
|
|
710
|
+
// ---- 3. Origin guard (gate for action/loader/PE modes) ----
|
|
711
|
+
const originPhase = ORIGIN_CHECK_PHASE_BY_MODE[plan.mode];
|
|
712
|
+
if (originPhase) {
|
|
713
|
+
const originResult = await checkRequestOrigin(
|
|
669
714
|
request,
|
|
670
|
-
env,
|
|
671
715
|
url,
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
716
|
+
router.originCheck,
|
|
717
|
+
env,
|
|
718
|
+
router.id,
|
|
719
|
+
originPhase,
|
|
675
720
|
);
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
// Signal the browser to hard-navigate so it renders the raw response.
|
|
682
|
-
// Only for 200 — redirects (3xx) work already because the browser follows
|
|
683
|
-
// them automatically to a URL that serves Flight data.
|
|
684
|
-
if (isPartial && error.status === 200) {
|
|
685
|
-
console.warn(
|
|
686
|
-
`[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
|
|
687
|
-
`Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
|
|
688
|
-
);
|
|
689
|
-
const cleanUrl = new URL(url);
|
|
690
|
-
cleanUrl.searchParams.delete("_rsc_partial");
|
|
691
|
-
cleanUrl.searchParams.delete("_rsc_segments");
|
|
692
|
-
cleanUrl.searchParams.delete("_rsc_v");
|
|
693
|
-
cleanUrl.searchParams.delete("_rsc_stale");
|
|
694
|
-
cleanUrl.searchParams.delete("_rsc_action");
|
|
695
|
-
cleanUrl.searchParams.delete("_rsc_prev");
|
|
696
|
-
return createResponseWithMergedHeaders(null, {
|
|
697
|
-
status: 200,
|
|
698
|
-
headers: {
|
|
699
|
-
"X-RSC-Reload": cleanUrl.toString(),
|
|
700
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
701
|
-
},
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
return error;
|
|
705
|
-
}
|
|
721
|
+
if (originResult) {
|
|
722
|
+
const originError = new Error(
|
|
723
|
+
`Origin check rejected: ${request.headers.get("origin") ?? "none"} vs ${request.headers.get("host") ?? "none"}`,
|
|
724
|
+
);
|
|
725
|
+
originError.name = "OriginCheckError";
|
|
706
726
|
|
|
707
|
-
|
|
708
|
-
// Check both instanceof and error.name for cross-bundle compatibility
|
|
709
|
-
const isRouteNotFound =
|
|
710
|
-
error instanceof RouteNotFoundError ||
|
|
711
|
-
(error instanceof Error && error.name === "RouteNotFoundError");
|
|
712
|
-
if (isRouteNotFound) {
|
|
713
|
-
callOnError(error, "routing", {
|
|
727
|
+
callOnError(originError, "origin", {
|
|
714
728
|
request,
|
|
715
729
|
url,
|
|
716
730
|
env,
|
|
717
|
-
handledByBoundary:
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
// Get notFound component from router options or use default
|
|
721
|
-
const notFoundOption = router.notFound;
|
|
722
|
-
const notFoundComponent =
|
|
723
|
-
typeof notFoundOption === "function"
|
|
724
|
-
? notFoundOption({ pathname: url.pathname })
|
|
725
|
-
: (notFoundOption ?? createElement("h1", null, "Not Found"));
|
|
726
|
-
|
|
727
|
-
// Create a simple segment for the 404 page
|
|
728
|
-
const notFoundSegment = {
|
|
729
|
-
id: "notFound",
|
|
730
|
-
namespace: "notFound",
|
|
731
|
-
type: "route" as const,
|
|
732
|
-
index: 0,
|
|
733
|
-
component: notFoundComponent,
|
|
734
|
-
params: {},
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
// Render with rootLayout to maintain app shell
|
|
738
|
-
const root = await renderSegments([notFoundSegment], {
|
|
739
|
-
rootLayout: router.rootLayout,
|
|
740
|
-
// No routeName for not-found routes
|
|
741
|
-
});
|
|
742
|
-
|
|
743
|
-
const payload: RscPayload = {
|
|
744
|
-
root,
|
|
731
|
+
handledByBoundary: false,
|
|
745
732
|
metadata: {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
diff: [],
|
|
750
|
-
isPartial: false,
|
|
751
|
-
handles: handleStore.stream(),
|
|
752
|
-
version,
|
|
753
|
-
themeConfig: router.themeConfig,
|
|
754
|
-
warmupEnabled: router.warmupEnabled,
|
|
755
|
-
initialTheme: requireRequestContext().theme,
|
|
756
|
-
// No routeName for not-found routes
|
|
733
|
+
phase: originPhase,
|
|
734
|
+
origin: request.headers.get("origin"),
|
|
735
|
+
host: request.headers.get("host"),
|
|
757
736
|
},
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
const rscStream = renderToReadableStream(payload);
|
|
761
|
-
|
|
762
|
-
// Determine if this is an RSC request or HTML request
|
|
763
|
-
const isRscRequest =
|
|
764
|
-
(!request.headers.get("accept")?.includes("text/html") &&
|
|
765
|
-
!url.searchParams.has("__html")) ||
|
|
766
|
-
url.searchParams.has("__rsc");
|
|
737
|
+
});
|
|
767
738
|
|
|
768
|
-
if (
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
739
|
+
if (router.telemetry) {
|
|
740
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
741
|
+
type: "request.origin-rejected",
|
|
742
|
+
timestamp: performance.now(),
|
|
743
|
+
requestId: getRequestId(request),
|
|
744
|
+
method: request.method,
|
|
745
|
+
pathname: url.pathname,
|
|
746
|
+
phase: originPhase,
|
|
747
|
+
origin: request.headers.get("origin"),
|
|
748
|
+
host: request.headers.get("host"),
|
|
772
749
|
});
|
|
773
750
|
}
|
|
774
751
|
|
|
775
|
-
|
|
776
|
-
const ssrModule = await loadSSRModule();
|
|
777
|
-
const htmlStream = await ssrModule.renderHTML(rscStream, { nonce });
|
|
778
|
-
|
|
779
|
-
return createResponseWithMergedHeaders(htmlStream, {
|
|
780
|
-
status: 404,
|
|
781
|
-
headers: { "content-type": "text/html;charset=utf-8" },
|
|
782
|
-
});
|
|
752
|
+
return originResult;
|
|
783
753
|
}
|
|
784
|
-
|
|
785
|
-
// Report unhandled errors
|
|
786
|
-
callOnError(error, "routing", {
|
|
787
|
-
request,
|
|
788
|
-
url,
|
|
789
|
-
env,
|
|
790
|
-
handledByBoundary: false,
|
|
791
|
-
});
|
|
792
|
-
console.error(`[RSC] Error:`, error);
|
|
793
|
-
throw error;
|
|
794
754
|
}
|
|
755
|
+
|
|
756
|
+
// ---- 4. Execute ----
|
|
757
|
+
return executeRequest(
|
|
758
|
+
plan as ExecutableRequestPlan<TEnv>,
|
|
759
|
+
request,
|
|
760
|
+
env,
|
|
761
|
+
url,
|
|
762
|
+
variables,
|
|
763
|
+
nonce,
|
|
764
|
+
);
|
|
795
765
|
}
|
|
796
766
|
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
async function handleProgressiveEnhancement(
|
|
767
|
+
// Execute a classified request plan. Dispatches to the appropriate handler
|
|
768
|
+
// based on plan.mode. Lives in the createRSCHandler closure for access to
|
|
769
|
+
// handlerCtx, router, callOnError, etc.
|
|
770
|
+
// Only receives executable plans (version-mismatch is handled above).
|
|
771
|
+
async function executeRequest(
|
|
772
|
+
plan: ExecutableRequestPlan<TEnv>,
|
|
804
773
|
request: Request,
|
|
805
774
|
env: TEnv,
|
|
806
775
|
url: URL,
|
|
807
|
-
|
|
808
|
-
handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
|
|
776
|
+
variables: Record<string, any>,
|
|
809
777
|
nonce: string | undefined,
|
|
810
|
-
): Promise<Response
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
contentType.includes("multipart/form-data") ||
|
|
814
|
-
contentType.includes("application/x-www-form-urlencoded");
|
|
815
|
-
|
|
816
|
-
if (request.method !== "POST" || isAction || !isFormSubmission) {
|
|
817
|
-
return null;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Clone the request to read FormData without consuming it
|
|
821
|
-
const formData = await request.clone().formData();
|
|
822
|
-
|
|
823
|
-
// Look for React's progressive enhancement hidden fields
|
|
824
|
-
let isDirectAction = false;
|
|
825
|
-
let isUseActionState = false;
|
|
826
|
-
let directActionId: string | null = null;
|
|
778
|
+
): Promise<Response> {
|
|
779
|
+
// Common setup
|
|
780
|
+
const handleStore = getRequestContext()._handleStore;
|
|
827
781
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
782
|
+
// Wire up error reporting for late streaming-handle failures
|
|
783
|
+
handleStore.onError = (error: Error) => {
|
|
784
|
+
const reqCtx = getRequestContext();
|
|
785
|
+
callOnError(error, "handler", {
|
|
786
|
+
request,
|
|
787
|
+
url,
|
|
788
|
+
routeKey: reqCtx._routeName,
|
|
789
|
+
params: reqCtx.params as Record<string, string>,
|
|
790
|
+
handledByBoundary: true,
|
|
791
|
+
});
|
|
792
|
+
if (router.telemetry) {
|
|
793
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
794
|
+
type: "handler.error",
|
|
795
|
+
timestamp: performance.now(),
|
|
796
|
+
requestId: getRequestId(request),
|
|
797
|
+
error,
|
|
798
|
+
handledByBoundary: true,
|
|
799
|
+
pathname: url.pathname,
|
|
800
|
+
routeKey: reqCtx._routeName,
|
|
801
|
+
params: reqCtx.params as Record<string, string>,
|
|
802
|
+
});
|
|
834
803
|
}
|
|
835
|
-
}
|
|
804
|
+
};
|
|
836
805
|
|
|
837
|
-
|
|
838
|
-
|
|
806
|
+
// Set route params early so all execution paths can access ctx.params.
|
|
807
|
+
// Also store the classified snapshot so match/matchPartial can reuse it
|
|
808
|
+
// instead of calling resolveRoute again.
|
|
809
|
+
if (plan.mode !== "redirect") {
|
|
810
|
+
setRequestContextParams(plan.route.params, plan.route.routeKey);
|
|
811
|
+
getRequestContext()._classifiedRoute = plan.route;
|
|
839
812
|
}
|
|
840
813
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
814
|
+
const routeReverse = createReverseFunction(getRequiredRouteMap());
|
|
815
|
+
|
|
816
|
+
// ---- Response route: skip entire RSC pipeline ----
|
|
817
|
+
if (plan.mode === "response") {
|
|
818
|
+
// Build ResponseRouteMatch from plan fields. handleResponseRoute
|
|
819
|
+
// expects a flat object with params at the top level.
|
|
820
|
+
const responseMatch: ResponseRouteMatch = {
|
|
821
|
+
responseType: plan.responseType,
|
|
822
|
+
handler: plan.handler,
|
|
823
|
+
params: plan.route.params,
|
|
824
|
+
negotiated: plan.negotiated,
|
|
825
|
+
manifestEntry: plan.manifestEntry,
|
|
826
|
+
routeMiddleware: plan.routeMiddleware,
|
|
827
|
+
};
|
|
828
|
+
const responseOutcome = await withTimeout(
|
|
829
|
+
handleResponseRoute(
|
|
830
|
+
handlerCtx,
|
|
831
|
+
responseMatch,
|
|
851
832
|
request,
|
|
852
|
-
url,
|
|
853
833
|
env,
|
|
854
|
-
handledByBoundary: false,
|
|
855
|
-
});
|
|
856
|
-
console.error("[RSC] Progressive enhancement action error:", error);
|
|
857
|
-
}
|
|
858
|
-
} else if (isDirectAction && directActionId) {
|
|
859
|
-
const temporaryReferences = createTemporaryReferenceSet();
|
|
860
|
-
|
|
861
|
-
let args: unknown[] = [];
|
|
862
|
-
try {
|
|
863
|
-
args = await decodeReply(formData, { temporaryReferences });
|
|
864
|
-
} catch {
|
|
865
|
-
args = [formData];
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
try {
|
|
869
|
-
const loadedAction = await loadServerAction(directActionId);
|
|
870
|
-
actionResult = await loadedAction.apply(null, args);
|
|
871
|
-
} catch (error) {
|
|
872
|
-
callOnError(error, "action", {
|
|
873
|
-
request,
|
|
874
834
|
url,
|
|
835
|
+
variables,
|
|
836
|
+
),
|
|
837
|
+
router.timeouts.renderStartMs,
|
|
838
|
+
"render-start",
|
|
839
|
+
);
|
|
840
|
+
if (responseOutcome.timedOut) {
|
|
841
|
+
return handleTimeoutResponse(
|
|
842
|
+
request,
|
|
875
843
|
env,
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
844
|
+
url,
|
|
845
|
+
"render-start",
|
|
846
|
+
responseOutcome.durationMs,
|
|
847
|
+
plan.route.routeKey,
|
|
848
|
+
);
|
|
880
849
|
}
|
|
850
|
+
const response = responseOutcome.result;
|
|
851
|
+
if (plan.negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
852
|
+
// handleResponseRoute (callHandlerWithVary) already appends Vary: Accept
|
|
853
|
+
// for negotiated responses; dedup so we don't emit Vary: Accept, Accept.
|
|
854
|
+
appendVaryAccept(response);
|
|
855
|
+
}
|
|
856
|
+
return response;
|
|
881
857
|
}
|
|
882
858
|
|
|
883
|
-
//
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
859
|
+
// SSR setup: kick off in parallel for modes that need HTML rendering.
|
|
860
|
+
// Placed after response-route short-circuit so response/mime routes
|
|
861
|
+
// never pay for SSR work.
|
|
862
|
+
//
|
|
863
|
+
// Only kick off when the request will actually render HTML, so the
|
|
864
|
+
// eager loadSSRModule() + user resolveStreaming() are not started (and
|
|
865
|
+
// never consumed) for a request that returns an RSC stream — that wasted
|
|
866
|
+
// work also leaves an orphaned Promise.all that can reject (D7). PE form
|
|
867
|
+
// submissions always render HTML (handleProgressiveEnhancement renders via
|
|
868
|
+
// getSSRSetup regardless of Accept). For full/partial-render and action,
|
|
869
|
+
// the render-time HTML decision is exactly !isRscRequest — mayNeedSSR is
|
|
870
|
+
// the coarse transport pre-filter, isRscRequest adds the partial/__rsc
|
|
871
|
+
// flags; both share the same Accept rule (acceptsFlightExplicitly in
|
|
872
|
+
// ssr-setup.ts), so the Accept call cannot drift between them. Both must
|
|
873
|
+
// pass.
|
|
874
|
+
const willRenderHtml =
|
|
875
|
+
plan.mode === "pe-render" ||
|
|
876
|
+
(mayNeedSSR(request, url) &&
|
|
877
|
+
!isRscRequest(request, url, plan.mode === "partial-render"));
|
|
878
|
+
if (plan.mode !== "loader" && willRenderHtml) {
|
|
879
|
+
variables[SSR_SETUP_VAR] = startSSRSetup(
|
|
880
|
+
handlerCtx,
|
|
888
881
|
request,
|
|
889
|
-
url,
|
|
890
882
|
env,
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
// Re-render the page and return HTML
|
|
897
|
-
const renderRequest = new Request(url.toString(), {
|
|
898
|
-
method: "GET",
|
|
899
|
-
headers: new Headers({ accept: "text/html" }),
|
|
900
|
-
});
|
|
901
|
-
|
|
902
|
-
const match = await router.match(renderRequest, env);
|
|
903
|
-
|
|
904
|
-
if (match.redirect) {
|
|
905
|
-
return new Response(null, {
|
|
906
|
-
status: 308,
|
|
907
|
-
headers: { Location: match.redirect },
|
|
908
|
-
});
|
|
883
|
+
url,
|
|
884
|
+
router.debugPerformance
|
|
885
|
+
? () => getRequestContext()._metricsStore
|
|
886
|
+
: undefined,
|
|
887
|
+
);
|
|
909
888
|
}
|
|
910
889
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const payload: RscPayload = {
|
|
916
|
-
root,
|
|
917
|
-
metadata: {
|
|
918
|
-
pathname: url.pathname,
|
|
919
|
-
segments: match.segments,
|
|
920
|
-
matched: match.matched,
|
|
921
|
-
diff: match.diff,
|
|
922
|
-
isPartial: false,
|
|
923
|
-
rootLayout: router.rootLayout,
|
|
924
|
-
handles: handleStore.stream(),
|
|
925
|
-
version,
|
|
926
|
-
themeConfig: router.themeConfig,
|
|
927
|
-
warmupEnabled: router.warmupEnabled,
|
|
928
|
-
initialTheme: requireRequestContext().theme,
|
|
929
|
-
},
|
|
930
|
-
formState: actionResult,
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
const rscStream = renderToReadableStream<RscPayload>(payload);
|
|
934
|
-
const ssrModule = await loadSSRModule();
|
|
935
|
-
const htmlStream = await ssrModule.renderHTML(rscStream, {
|
|
936
|
-
formState: reactFormState,
|
|
937
|
-
nonce,
|
|
938
|
-
});
|
|
939
|
-
|
|
940
|
-
return new Response(htmlStream, {
|
|
941
|
-
headers: { "content-type": "text/html;charset=utf-8" },
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// ============================================================================
|
|
946
|
-
// SERVER ACTION HANDLER
|
|
947
|
-
// ============================================================================
|
|
948
|
-
async function handleServerAction(
|
|
949
|
-
request: Request,
|
|
950
|
-
env: TEnv,
|
|
951
|
-
url: URL,
|
|
952
|
-
actionId: string,
|
|
953
|
-
handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
|
|
954
|
-
): Promise<Response> {
|
|
955
|
-
const temporaryReferences = createTemporaryReferenceSet();
|
|
956
|
-
|
|
957
|
-
// Decode action arguments from request body
|
|
958
|
-
const contentType = request.headers.get("content-type") || "";
|
|
959
|
-
let args: unknown[] = [];
|
|
960
|
-
let actionFormData: FormData | undefined;
|
|
961
|
-
|
|
962
|
-
try {
|
|
963
|
-
const body = contentType.includes("multipart/form-data")
|
|
964
|
-
? await request.formData()
|
|
965
|
-
: await request.text();
|
|
966
|
-
|
|
967
|
-
if (body instanceof FormData) {
|
|
968
|
-
actionFormData = body;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
if (hasBodyContent(body)) {
|
|
972
|
-
args = await decodeReply(body, { temporaryReferences });
|
|
973
|
-
}
|
|
974
|
-
} catch (error) {
|
|
975
|
-
callOnError(error, "action", {
|
|
890
|
+
// ---- Loader fetch ----
|
|
891
|
+
if (plan.mode === "loader") {
|
|
892
|
+
return handleLoaderFetch(
|
|
893
|
+
handlerCtx,
|
|
976
894
|
request,
|
|
977
|
-
url,
|
|
978
895
|
env,
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
cause: error,
|
|
984
|
-
});
|
|
896
|
+
url,
|
|
897
|
+
variables,
|
|
898
|
+
plan.route.params,
|
|
899
|
+
);
|
|
985
900
|
}
|
|
986
901
|
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
try {
|
|
993
|
-
loadedAction = await loadServerAction(actionId);
|
|
994
|
-
const data = await loadedAction!.apply(null, args);
|
|
995
|
-
returnValue = { ok: true, data };
|
|
996
|
-
} catch (error) {
|
|
997
|
-
returnValue = { ok: false, data: error };
|
|
998
|
-
actionStatus = 500;
|
|
999
|
-
|
|
1000
|
-
// Try to render error boundary
|
|
1001
|
-
const errorResult = await router.matchError(request, env, error, "route");
|
|
1002
|
-
|
|
1003
|
-
// Report the action error (handledByBoundary indicates if error boundary will render)
|
|
1004
|
-
callOnError(error, "action", {
|
|
902
|
+
// ---- Progressive enhancement ----
|
|
903
|
+
if (plan.mode === "pe-render") {
|
|
904
|
+
const peResult = await handleProgressiveEnhancement(
|
|
905
|
+
handlerCtx,
|
|
1005
906
|
request,
|
|
1006
|
-
url,
|
|
1007
907
|
env,
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
isPartial: true,
|
|
1021
|
-
matched: errorResult.matched,
|
|
1022
|
-
diff: errorResult.diff,
|
|
1023
|
-
isError: true,
|
|
1024
|
-
handles: handleStore.stream(),
|
|
1025
|
-
version,
|
|
1026
|
-
},
|
|
1027
|
-
returnValue,
|
|
1028
|
-
};
|
|
1029
|
-
|
|
1030
|
-
const rscStream = renderToReadableStream<RscPayload>(payload, {
|
|
1031
|
-
temporaryReferences,
|
|
1032
|
-
});
|
|
1033
|
-
|
|
1034
|
-
return createResponseWithMergedHeaders(rscStream, {
|
|
1035
|
-
status: actionStatus,
|
|
1036
|
-
headers: { "content-type": "text/x-component;charset=utf-8" },
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
908
|
+
url,
|
|
909
|
+
false, // isAction = false for PE
|
|
910
|
+
handleStore,
|
|
911
|
+
nonce,
|
|
912
|
+
{
|
|
913
|
+
routeMiddleware: plan.route.routeMiddleware,
|
|
914
|
+
variables,
|
|
915
|
+
routeReverse,
|
|
916
|
+
},
|
|
917
|
+
);
|
|
918
|
+
if (peResult) return peResult;
|
|
919
|
+
// PE handler returned null (not a PE form) — fall through to render
|
|
1039
920
|
}
|
|
1040
921
|
|
|
1041
|
-
//
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
922
|
+
// ---- Action: execute action, then revalidate wrapped in route middleware ----
|
|
923
|
+
if (plan.mode === "action") {
|
|
924
|
+
let actionContinuation: ActionContinuation | undefined;
|
|
925
|
+
try {
|
|
926
|
+
// Instrument the action execution as its own phase (action:<actionId> +
|
|
927
|
+
// rango.action), so a POST shows the mutation time AND which action ran,
|
|
928
|
+
// not just the downstream revalidation render. The action's own
|
|
929
|
+
// loaders/fetches nest under rango.action.
|
|
930
|
+
const actionOutcome = await withTimeout(
|
|
931
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
932
|
+
executeServerAction(
|
|
933
|
+
handlerCtx,
|
|
934
|
+
request,
|
|
935
|
+
env,
|
|
936
|
+
url,
|
|
937
|
+
plan.actionId,
|
|
938
|
+
handleStore,
|
|
939
|
+
),
|
|
940
|
+
),
|
|
941
|
+
router.timeouts.actionMs,
|
|
942
|
+
"action",
|
|
943
|
+
);
|
|
944
|
+
if (actionOutcome.timedOut) {
|
|
945
|
+
return handleTimeoutResponse(
|
|
946
|
+
request,
|
|
947
|
+
env,
|
|
948
|
+
url,
|
|
949
|
+
"action",
|
|
950
|
+
actionOutcome.durationMs,
|
|
951
|
+
plan.route.routeKey,
|
|
952
|
+
plan.actionId,
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
const result = actionOutcome.result;
|
|
956
|
+
// Response means redirect or error boundary — done.
|
|
957
|
+
if (result instanceof Response) return result;
|
|
958
|
+
actionContinuation = result;
|
|
959
|
+
} catch (error) {
|
|
960
|
+
callOnError(error, "action", {
|
|
961
|
+
request,
|
|
962
|
+
url,
|
|
963
|
+
env,
|
|
964
|
+
actionId: plan.actionId,
|
|
965
|
+
handledByBoundary: false,
|
|
1064
966
|
});
|
|
967
|
+
console.error(`[RSC] Action error:`, error);
|
|
968
|
+
throw error;
|
|
1065
969
|
}
|
|
1066
970
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
},
|
|
1087
|
-
returnValue,
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
const rscStream = renderToReadableStream<RscPayload>(payload, {
|
|
1091
|
-
temporaryReferences,
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
const headers: Record<string, string> = {
|
|
1095
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
1096
|
-
};
|
|
1097
|
-
if (serverTiming) {
|
|
1098
|
-
headers["Server-Timing"] = serverTiming;
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
return createResponseWithMergedHeaders(rscStream, {
|
|
1102
|
-
status: actionStatus,
|
|
1103
|
-
headers,
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// Return updated segments
|
|
1108
|
-
setRequestContextParams(matchResult.params);
|
|
1109
|
-
|
|
1110
|
-
const renderStart = performance.now();
|
|
1111
|
-
|
|
1112
|
-
const renderDuration = performance.now() - renderStart;
|
|
1113
|
-
const serverTiming = matchResult.serverTiming
|
|
1114
|
-
? `${matchResult.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
|
|
1115
|
-
: `rendering;dur=${renderDuration.toFixed(2)}`;
|
|
1116
|
-
|
|
1117
|
-
const payload: RscPayload = {
|
|
1118
|
-
root: null,
|
|
1119
|
-
metadata: {
|
|
1120
|
-
pathname: url.pathname,
|
|
1121
|
-
segments: matchResult.segments,
|
|
1122
|
-
isPartial: true,
|
|
1123
|
-
matched: matchResult.matched,
|
|
1124
|
-
diff: matchResult.diff,
|
|
1125
|
-
slots: matchResult.slots,
|
|
1126
|
-
handles: handleStore.stream(),
|
|
1127
|
-
version,
|
|
1128
|
-
},
|
|
1129
|
-
returnValue,
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
const rscStream = renderToReadableStream<RscPayload>(payload, {
|
|
1133
|
-
temporaryReferences,
|
|
1134
|
-
});
|
|
1135
|
-
|
|
1136
|
-
const actionHeaders: Record<string, string> = {
|
|
1137
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
1138
|
-
};
|
|
1139
|
-
if (serverTiming) {
|
|
1140
|
-
actionHeaders["Server-Timing"] = serverTiming;
|
|
971
|
+
// Revalidation render wrapped in route middleware.
|
|
972
|
+
// Actions from client-side navigation include _rsc_partial — preserve
|
|
973
|
+
// the partial flag so the revalidation returns a Flight stream, not HTML.
|
|
974
|
+
// App-switch is already excluded by classifyRequest (would be full-render).
|
|
975
|
+
const isPartialAction = url.searchParams.has("_rsc_partial");
|
|
976
|
+
return executeRenderWithMiddleware(
|
|
977
|
+
plan.route.routeMiddleware,
|
|
978
|
+
plan.negotiated,
|
|
979
|
+
plan.route.routeKey,
|
|
980
|
+
routeReverse,
|
|
981
|
+
request,
|
|
982
|
+
env,
|
|
983
|
+
url,
|
|
984
|
+
variables,
|
|
985
|
+
nonce,
|
|
986
|
+
handleStore,
|
|
987
|
+
isPartialAction,
|
|
988
|
+
actionContinuation,
|
|
989
|
+
);
|
|
1141
990
|
}
|
|
1142
991
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
992
|
+
// Full render, partial render, fallen-through PE, and full-page redirect all
|
|
993
|
+
// render through the same middleware-wrapped path. Only full/partial-render
|
|
994
|
+
// carry negotiation + the partial flag; pe/redirect render plainly.
|
|
995
|
+
const isPartial = plan.mode === "partial-render";
|
|
996
|
+
const negotiated =
|
|
997
|
+
plan.mode === "full-render" || plan.mode === "partial-render"
|
|
998
|
+
? plan.negotiated
|
|
999
|
+
: false;
|
|
1000
|
+
return executeRenderWithMiddleware(
|
|
1001
|
+
plan.route.routeMiddleware,
|
|
1002
|
+
negotiated,
|
|
1003
|
+
plan.route.routeKey,
|
|
1004
|
+
routeReverse,
|
|
1005
|
+
request,
|
|
1006
|
+
env,
|
|
1007
|
+
url,
|
|
1008
|
+
variables,
|
|
1009
|
+
nonce,
|
|
1010
|
+
handleStore,
|
|
1011
|
+
isPartial,
|
|
1012
|
+
);
|
|
1147
1013
|
}
|
|
1148
1014
|
|
|
1149
|
-
//
|
|
1150
|
-
//
|
|
1151
|
-
//
|
|
1152
|
-
|
|
1153
|
-
|
|
1015
|
+
// Shared render execution: wraps handleRscRendering (or revalidateAfterAction)
|
|
1016
|
+
// in route middleware and timeout handling. Consolidates the pattern used by
|
|
1017
|
+
// action-revalidate, full-render, and partial-render modes.
|
|
1018
|
+
async function executeRenderWithMiddleware(
|
|
1019
|
+
routeMiddleware: import("../router/middleware-types.js").CollectedMiddleware[],
|
|
1020
|
+
negotiated: boolean,
|
|
1021
|
+
routeKey: string,
|
|
1022
|
+
routeReverse: ReturnType<typeof createReverseFunction>,
|
|
1154
1023
|
request: Request,
|
|
1155
1024
|
env: TEnv,
|
|
1156
1025
|
url: URL,
|
|
1157
1026
|
variables: Record<string, any>,
|
|
1027
|
+
nonce: string | undefined,
|
|
1028
|
+
handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
|
|
1029
|
+
isPartial: boolean,
|
|
1030
|
+
actionContinuation?: ActionContinuation,
|
|
1158
1031
|
): Promise<Response> {
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
if (!loaderId) {
|
|
1162
|
-
return createResponseWithMergedHeaders("Missing _rsc_loader parameter", {
|
|
1163
|
-
status: 400,
|
|
1164
|
-
});
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
// Look up loader lazily
|
|
1168
|
-
const registeredLoader = await getLoaderLazy(loaderId);
|
|
1169
|
-
if (!registeredLoader) {
|
|
1170
|
-
return createResponseWithMergedHeaders(
|
|
1171
|
-
`Loader "${loaderId}" not found in registry`,
|
|
1172
|
-
{ status: 404 },
|
|
1173
|
-
);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
// Parse params and body based on request method
|
|
1177
|
-
let loaderParams: Record<string, string> = {};
|
|
1178
|
-
let loaderBody: unknown = undefined;
|
|
1179
|
-
const isBodyMethod = request.method !== "GET" && request.method !== "HEAD";
|
|
1180
|
-
|
|
1181
|
-
if (isBodyMethod) {
|
|
1032
|
+
const renderHandler = async (): Promise<Response> => {
|
|
1182
1033
|
try {
|
|
1183
|
-
|
|
1184
|
-
if (
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
} catch {
|
|
1203
|
-
return createResponseWithMergedHeaders(
|
|
1204
|
-
"Invalid _rsc_loader_params JSON",
|
|
1205
|
-
{ status: 400 },
|
|
1034
|
+
let response: Response;
|
|
1035
|
+
if (actionContinuation) {
|
|
1036
|
+
response = await revalidateAfterAction(
|
|
1037
|
+
handlerCtx,
|
|
1038
|
+
request,
|
|
1039
|
+
env,
|
|
1040
|
+
url,
|
|
1041
|
+
handleStore,
|
|
1042
|
+
actionContinuation,
|
|
1043
|
+
);
|
|
1044
|
+
} else {
|
|
1045
|
+
response = await handleRscRendering(
|
|
1046
|
+
handlerCtx,
|
|
1047
|
+
request,
|
|
1048
|
+
env,
|
|
1049
|
+
url,
|
|
1050
|
+
isPartial,
|
|
1051
|
+
handleStore,
|
|
1052
|
+
nonce,
|
|
1206
1053
|
);
|
|
1207
1054
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1055
|
+
if (negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
1056
|
+
// handleRscRendering bakes `accept` into the RSC response's Vary list;
|
|
1057
|
+
// dedup so the negotiated append does not list accept twice.
|
|
1058
|
+
appendVaryAccept(response);
|
|
1059
|
+
}
|
|
1060
|
+
return response;
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
// Check if middleware/handler returned Response
|
|
1063
|
+
if (error instanceof Response) {
|
|
1064
|
+
// An action revalidation render is delivered to the client over the
|
|
1065
|
+
// same Flight-parsing path as a partial navigation, so a Response
|
|
1066
|
+
// thrown during it must be converted exactly like a partial one
|
|
1067
|
+
// (raw 200 -> hard-nav hint, 3xx -> Flight redirect). Without this,
|
|
1068
|
+
// the no-middleware path returns the raw Response (the with-middleware
|
|
1069
|
+
// path is already covered by the isPartial || actionContinuation
|
|
1070
|
+
// guard below).
|
|
1071
|
+
const treatAsPartial = isPartial || actionContinuation != null;
|
|
1072
|
+
|
|
1073
|
+
// During partial (client-side navigation), a 200 Response from a handler
|
|
1074
|
+
// means the route serves raw content (JSON, text, etc.), not JSX.
|
|
1075
|
+
// Signal the browser to hard-navigate so it renders the raw response.
|
|
1076
|
+
if (treatAsPartial && error.status === 200) {
|
|
1077
|
+
console.warn(
|
|
1078
|
+
`[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
|
|
1079
|
+
`Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
|
|
1080
|
+
);
|
|
1081
|
+
return createResponseWithMergedHeaders(null, {
|
|
1082
|
+
status: 200,
|
|
1083
|
+
headers: {
|
|
1084
|
+
"X-RSC-Reload": stripInternalParams(url).toString(),
|
|
1085
|
+
"content-type": "text/x-component;charset=utf-8",
|
|
1086
|
+
},
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1230
1089
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1090
|
+
if (treatAsPartial) {
|
|
1091
|
+
const intercepted = interceptRedirectForPartial(
|
|
1092
|
+
error,
|
|
1093
|
+
createRedirectFlightResponse,
|
|
1094
|
+
);
|
|
1095
|
+
if (intercepted) return intercepted;
|
|
1233
1096
|
}
|
|
1234
|
-
const loaderPayload: LoaderPayload = { loaderResult: result };
|
|
1235
|
-
const rscStream =
|
|
1236
|
-
renderToReadableStream<LoaderPayload>(loaderPayload);
|
|
1237
1097
|
|
|
1238
|
-
return
|
|
1239
|
-
|
|
1098
|
+
return error;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Render 404 page for unmatched routes
|
|
1102
|
+
if (isRouteNotFoundError(error)) {
|
|
1103
|
+
callOnError(error, "routing", {
|
|
1104
|
+
request,
|
|
1105
|
+
url,
|
|
1106
|
+
env,
|
|
1107
|
+
handledByBoundary: true,
|
|
1240
1108
|
});
|
|
1241
|
-
},
|
|
1242
|
-
);
|
|
1243
|
-
} catch (error) {
|
|
1244
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1245
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
1246
1109
|
|
|
1247
|
-
|
|
1110
|
+
const notFoundOption = router.notFound;
|
|
1111
|
+
const notFoundComponent =
|
|
1112
|
+
typeof notFoundOption === "function"
|
|
1113
|
+
? notFoundOption({ pathname: url.pathname })
|
|
1114
|
+
: (notFoundOption ?? createElement("h1", null, "Not Found"));
|
|
1115
|
+
|
|
1116
|
+
const notFoundSegment = {
|
|
1117
|
+
id: "notFound",
|
|
1118
|
+
namespace: "notFound",
|
|
1119
|
+
type: "route" as const,
|
|
1120
|
+
index: 0,
|
|
1121
|
+
component: notFoundComponent,
|
|
1122
|
+
params: {},
|
|
1123
|
+
};
|
|
1248
1124
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1125
|
+
const payload: RscPayload = {
|
|
1126
|
+
metadata: {
|
|
1127
|
+
pathname: url.pathname,
|
|
1128
|
+
routerId: router.id,
|
|
1129
|
+
basename: router.basename,
|
|
1130
|
+
segments: [notFoundSegment],
|
|
1131
|
+
matched: [],
|
|
1132
|
+
diff: [],
|
|
1133
|
+
// Shape parity with buildFullPayload (rsc-rendering.ts): the 404
|
|
1134
|
+
// payload carries params/resolvedIds/prefetchCacheTTL the same way a
|
|
1135
|
+
// matched full render does. resolvedIds mirrors the rendered segment
|
|
1136
|
+
// list (the single notFound segment) like the error-boundary path
|
|
1137
|
+
// (match-api.ts) does for its boundary segment.
|
|
1138
|
+
resolvedIds: [notFoundSegment.id],
|
|
1139
|
+
params: {},
|
|
1140
|
+
isPartial: false,
|
|
1141
|
+
rootLayout: router.rootLayout,
|
|
1142
|
+
// Full (404) render: resolve deferred handle values server-side.
|
|
1143
|
+
handles: resolvedHandleStream(handleStore),
|
|
1144
|
+
version,
|
|
1145
|
+
prefetchCacheTTL: router.prefetchCacheTTL,
|
|
1146
|
+
prefetchCacheSize: router.prefetchCacheSize,
|
|
1147
|
+
prefetchConcurrency: router.prefetchConcurrency,
|
|
1148
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
1149
|
+
themeConfig: router.themeConfig,
|
|
1150
|
+
warmupEnabled: router.warmupEnabled,
|
|
1151
|
+
strictMode: router.strictMode,
|
|
1152
|
+
initialTheme: getRequestContext().theme,
|
|
1153
|
+
},
|
|
1154
|
+
};
|
|
1256
1155
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1156
|
+
const isNotFoundFlightResponse = isRscRequest(
|
|
1157
|
+
request,
|
|
1158
|
+
url,
|
|
1159
|
+
isPartial,
|
|
1160
|
+
);
|
|
1161
|
+
const notFoundStageTracking = {
|
|
1162
|
+
mode: isPartial ? ("partial" as const) : ("full" as const),
|
|
1163
|
+
routeKey,
|
|
1164
|
+
phases: isNotFoundFlightResponse
|
|
1165
|
+
? RSC_FLIGHT_ONLY_PHASES
|
|
1166
|
+
: RSC_FLIGHT_HTML_PHASES,
|
|
1167
|
+
};
|
|
1168
|
+
const rscStream = renderRscFlightStage(
|
|
1169
|
+
{
|
|
1170
|
+
ctx: { renderToReadableStream, callOnError },
|
|
1171
|
+
request,
|
|
1172
|
+
url,
|
|
1173
|
+
env,
|
|
1174
|
+
payload,
|
|
1175
|
+
tracking: notFoundStageTracking,
|
|
1176
|
+
},
|
|
1177
|
+
performance.now(),
|
|
1178
|
+
).stream;
|
|
1179
|
+
|
|
1180
|
+
if (isNotFoundFlightResponse) {
|
|
1181
|
+
return createResponseWithMergedHeaders(rscStream, {
|
|
1182
|
+
status: 404,
|
|
1183
|
+
headers: {
|
|
1184
|
+
"content-type": "text/x-component;charset=utf-8",
|
|
1185
|
+
// Router identity for the client's pre-decode integrity check; a
|
|
1186
|
+
// same-app 404 matches and applies in place. See response-adapter.
|
|
1187
|
+
"X-RSC-Router-Id": router.id,
|
|
1188
|
+
},
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1265
1191
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1192
|
+
const [ssrModule, streamMode] = await getSSRSetup(
|
|
1193
|
+
handlerCtx,
|
|
1194
|
+
request,
|
|
1195
|
+
env,
|
|
1196
|
+
url,
|
|
1197
|
+
getRequestContext()._metricsStore,
|
|
1198
|
+
);
|
|
1199
|
+
const htmlStream = await observeRscHtmlStage(
|
|
1200
|
+
{ url, tracking: notFoundStageTracking },
|
|
1201
|
+
() =>
|
|
1202
|
+
ssrModule.renderHTML(rscStream, {
|
|
1203
|
+
nonce,
|
|
1204
|
+
streamMode,
|
|
1205
|
+
}),
|
|
1206
|
+
);
|
|
1272
1207
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
async function handleRscRendering(
|
|
1277
|
-
request: Request,
|
|
1278
|
-
env: TEnv,
|
|
1279
|
-
url: URL,
|
|
1280
|
-
isPartial: boolean,
|
|
1281
|
-
handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
|
|
1282
|
-
nonce: string | undefined,
|
|
1283
|
-
): Promise<Response> {
|
|
1284
|
-
// Retrieve handler-level timing from variables
|
|
1285
|
-
const reqCtx = requireRequestContext();
|
|
1286
|
-
const handlerTimingArr: string[] = reqCtx.var.__handlerTiming || [];
|
|
1287
|
-
const handlerStart: number = reqCtx.var.__handlerStart || 0;
|
|
1288
|
-
|
|
1289
|
-
let payload: RscPayload;
|
|
1290
|
-
let serverTiming: string | undefined;
|
|
1291
|
-
|
|
1292
|
-
if (isPartial) {
|
|
1293
|
-
// Partial render (navigation)
|
|
1294
|
-
const result = await router.matchPartial(request, env);
|
|
1295
|
-
|
|
1296
|
-
if (!result) {
|
|
1297
|
-
// Fall back to full render
|
|
1298
|
-
const match = await router.match(request, env);
|
|
1299
|
-
setRequestContextParams(match.params);
|
|
1300
|
-
|
|
1301
|
-
if (match.redirect) {
|
|
1302
|
-
return createResponseWithMergedHeaders(null, {
|
|
1303
|
-
status: 308,
|
|
1304
|
-
headers: { Location: match.redirect },
|
|
1208
|
+
return createResponseWithMergedHeaders(htmlStream, {
|
|
1209
|
+
status: 404,
|
|
1210
|
+
headers: { "content-type": "text/html;charset=utf-8" },
|
|
1305
1211
|
});
|
|
1306
1212
|
}
|
|
1307
1213
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
? `${match.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
|
|
1315
|
-
: `rendering;dur=${renderDuration.toFixed(2)}`;
|
|
1316
|
-
|
|
1317
|
-
payload = {
|
|
1318
|
-
root,
|
|
1319
|
-
metadata: {
|
|
1320
|
-
pathname: url.pathname,
|
|
1321
|
-
segments: match.segments,
|
|
1322
|
-
matched: match.matched,
|
|
1323
|
-
diff: match.diff,
|
|
1324
|
-
isPartial: false,
|
|
1325
|
-
handles: handleStore.stream(),
|
|
1326
|
-
version,
|
|
1327
|
-
themeConfig: router.themeConfig,
|
|
1328
|
-
initialTheme: reqCtx.theme,
|
|
1329
|
-
},
|
|
1330
|
-
};
|
|
1331
|
-
} else {
|
|
1332
|
-
setRequestContextParams(result.params);
|
|
1333
|
-
serverTiming = result.serverTiming;
|
|
1334
|
-
|
|
1335
|
-
payload = {
|
|
1336
|
-
root: null,
|
|
1337
|
-
metadata: {
|
|
1338
|
-
pathname: url.pathname,
|
|
1339
|
-
segments: result.segments,
|
|
1340
|
-
matched: result.matched,
|
|
1341
|
-
diff: result.diff,
|
|
1342
|
-
isPartial: true,
|
|
1343
|
-
slots: result.slots,
|
|
1344
|
-
handles: handleStore.stream(),
|
|
1345
|
-
version,
|
|
1346
|
-
},
|
|
1347
|
-
};
|
|
1348
|
-
}
|
|
1349
|
-
} else {
|
|
1350
|
-
// Full render (initial page load)
|
|
1351
|
-
const match = await router.match(request, env);
|
|
1352
|
-
setRequestContextParams(match.params);
|
|
1353
|
-
|
|
1354
|
-
if (match.redirect) {
|
|
1355
|
-
return createResponseWithMergedHeaders(null, {
|
|
1356
|
-
status: 308,
|
|
1357
|
-
headers: { Location: match.redirect },
|
|
1214
|
+
// Report unhandled errors
|
|
1215
|
+
callOnError(error, "routing", {
|
|
1216
|
+
request,
|
|
1217
|
+
url,
|
|
1218
|
+
env,
|
|
1219
|
+
handledByBoundary: false,
|
|
1358
1220
|
});
|
|
1221
|
+
console.error(`[RSC] Error:`, error);
|
|
1222
|
+
throw error;
|
|
1359
1223
|
}
|
|
1224
|
+
};
|
|
1360
1225
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
const serializedSegments = await serializeSegments(nonLoaderSegments);
|
|
1372
|
-
const handles: Record<string, Record<string, unknown[]>> = {};
|
|
1373
|
-
for (const seg of nonLoaderSegments) {
|
|
1374
|
-
const segHandles = handleStore.getDataForSegment(seg.id);
|
|
1375
|
-
if (Object.keys(segHandles).length > 0) {
|
|
1376
|
-
handles[seg.id] = segHandles;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
return new Response(
|
|
1380
|
-
JSON.stringify({
|
|
1381
|
-
segments: serializedSegments,
|
|
1382
|
-
handles,
|
|
1383
|
-
routeName: match.routeName,
|
|
1384
|
-
params: match.params,
|
|
1385
|
-
}),
|
|
1386
|
-
{ headers: { "Content-Type": "application/json" } },
|
|
1226
|
+
// Wrap the render path in a renderStartMs timeout
|
|
1227
|
+
const executeRender = async (): Promise<Response> => {
|
|
1228
|
+
if (routeMiddleware.length > 0) {
|
|
1229
|
+
const mwResponse = await executeMiddleware(
|
|
1230
|
+
buildRouteMiddlewareEntries<TEnv>(routeMiddleware),
|
|
1231
|
+
request,
|
|
1232
|
+
env,
|
|
1233
|
+
variables,
|
|
1234
|
+
renderHandler,
|
|
1235
|
+
routeReverse,
|
|
1387
1236
|
);
|
|
1388
|
-
} else {
|
|
1389
|
-
const renderStart = performance.now();
|
|
1390
|
-
const root = renderSegments(match.segments, {
|
|
1391
|
-
rootLayout: router.rootLayout,
|
|
1392
|
-
});
|
|
1393
|
-
const renderDuration = performance.now() - renderStart;
|
|
1394
|
-
serverTiming = match.serverTiming
|
|
1395
|
-
? `${match.serverTiming}, rendering;dur=${renderDuration.toFixed(2)}`
|
|
1396
|
-
: `rendering;dur=${renderDuration.toFixed(2)}`;
|
|
1397
|
-
|
|
1398
|
-
payload = {
|
|
1399
|
-
root,
|
|
1400
|
-
metadata: {
|
|
1401
|
-
pathname: url.pathname,
|
|
1402
|
-
segments: match.segments,
|
|
1403
|
-
matched: match.matched,
|
|
1404
|
-
diff: match.diff,
|
|
1405
|
-
isPartial: false,
|
|
1406
|
-
rootLayout: router.rootLayout,
|
|
1407
|
-
handles: handleStore.stream(),
|
|
1408
|
-
version,
|
|
1409
|
-
themeConfig: router.themeConfig,
|
|
1410
|
-
initialTheme: reqCtx.theme,
|
|
1411
|
-
},
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
1237
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
(!request.headers.get("accept")?.includes("text/html") &&
|
|
1424
|
-
!url.searchParams.has("__html")) ||
|
|
1425
|
-
url.searchParams.has("__rsc");
|
|
1426
|
-
|
|
1427
|
-
// Build complete Server-Timing: handler phases + match/manifest + rendering + RSC serialize
|
|
1428
|
-
const timingParts: string[] = [...handlerTimingArr];
|
|
1429
|
-
if (serverTiming) {
|
|
1430
|
-
timingParts.push(serverTiming);
|
|
1431
|
-
}
|
|
1432
|
-
timingParts.push(`rsc-serialize;dur=${rscSerializeDur.toFixed(2)}`);
|
|
1238
|
+
if (isPartial || actionContinuation) {
|
|
1239
|
+
const intercepted = interceptRedirectForPartial(
|
|
1240
|
+
mwResponse,
|
|
1241
|
+
createRedirectFlightResponse,
|
|
1242
|
+
);
|
|
1243
|
+
if (intercepted) return intercepted;
|
|
1244
|
+
}
|
|
1433
1245
|
|
|
1434
|
-
|
|
1435
|
-
const fullTiming = timingParts.join(", ");
|
|
1436
|
-
const rscHeaders: Record<string, string> = {
|
|
1437
|
-
"content-type": "text/x-component;charset=utf-8",
|
|
1438
|
-
vary: "accept",
|
|
1439
|
-
};
|
|
1440
|
-
if (fullTiming) {
|
|
1441
|
-
rscHeaders["Server-Timing"] = fullTiming;
|
|
1246
|
+
return finalizeResponse(mwResponse);
|
|
1442
1247
|
}
|
|
1443
|
-
return createResponseWithMergedHeaders(rscStream, {
|
|
1444
|
-
headers: rscHeaders,
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
// Delegate to SSR for HTML response
|
|
1449
|
-
const ssrModuleStart = performance.now();
|
|
1450
|
-
const ssrModule = await loadSSRModule();
|
|
1451
|
-
const ssrModuleDur = performance.now() - ssrModuleStart;
|
|
1452
|
-
timingParts.push(`ssr-module-load;dur=${ssrModuleDur.toFixed(2)}`);
|
|
1453
|
-
|
|
1454
|
-
const ssrRenderStart = performance.now();
|
|
1455
|
-
const htmlStream = await ssrModule.renderHTML(rscStream, { nonce });
|
|
1456
|
-
const ssrRenderDur = performance.now() - ssrRenderStart;
|
|
1457
|
-
timingParts.push(`ssr-render-html;dur=${ssrRenderDur.toFixed(2)}`);
|
|
1458
|
-
|
|
1459
|
-
// Add total handler duration
|
|
1460
|
-
if (handlerStart) {
|
|
1461
|
-
const totalHandler = performance.now() - handlerStart;
|
|
1462
|
-
timingParts.push(`handler-total;dur=${totalHandler.toFixed(2)}`);
|
|
1463
|
-
}
|
|
1464
1248
|
|
|
1465
|
-
|
|
1466
|
-
const htmlHeaders: Record<string, string> = {
|
|
1467
|
-
"content-type": "text/html;charset=utf-8",
|
|
1249
|
+
return renderHandler();
|
|
1468
1250
|
};
|
|
1469
|
-
if (fullTiming) {
|
|
1470
|
-
htmlHeaders["Server-Timing"] = fullTiming;
|
|
1471
|
-
}
|
|
1472
1251
|
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1252
|
+
const renderOutcome = await withTimeout(
|
|
1253
|
+
executeRender(),
|
|
1254
|
+
router.timeouts.renderStartMs,
|
|
1255
|
+
"render-start",
|
|
1256
|
+
);
|
|
1257
|
+
if (renderOutcome.timedOut) {
|
|
1258
|
+
return handleTimeoutResponse(
|
|
1259
|
+
request,
|
|
1260
|
+
env,
|
|
1261
|
+
url,
|
|
1262
|
+
"render-start",
|
|
1263
|
+
renderOutcome.durationMs,
|
|
1264
|
+
routeKey,
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
return renderOutcome.result;
|
|
1476
1268
|
}
|
|
1477
1269
|
}
|