@timber-js/app 0.2.0-alpha.8 → 0.2.0-alpha.81
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/LICENSE +8 -0
- package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
- package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-HS0LGUl2.js} +1 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-C77ScO0m.js +106 -0
- package/dist/_chunks/define-C77ScO0m.js.map +1 -0
- package/dist/_chunks/define-CZqDwhSu.js +199 -0
- package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js +94 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → dev-warnings-DpGRGoDi.js} +5 -44
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
- package/dist/_chunks/format-CYBGxKtc.js +14 -0
- package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-Dpn_UfAD.js} +171 -97
- package/dist/_chunks/interception-Dpn_UfAD.js.map +1 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +41 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/request-context-qMsWgy9C.js +478 -0
- package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js +86 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js.map +1 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-DzuI0bIV.js} +1 -1
- package/dist/_chunks/{ssr-data-MjmprTmO.js.map → ssr-data-DzuI0bIV.js.map} +1 -1
- package/dist/_chunks/stale-reload-BX5gL1r-.js +64 -0
- package/dist/_chunks/stale-reload-BX5gL1r-.js.map +1 -0
- package/dist/_chunks/{tracing-CemImE6h.js → tracing-CCYbKn5n.js} +60 -9
- package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
- package/dist/_chunks/use-params-Br9YSUFV.js +295 -0
- package/dist/_chunks/use-params-Br9YSUFV.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-Lo_s_pw2.js} +4 -4
- package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +95 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +135 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +23 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +142 -80
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +27 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +30 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +23 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +19 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +10 -4
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +3 -3
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +7 -21
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +210 -1017
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +18 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +890 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link-pending-store.d.ts +63 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +90 -32
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +13 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +5 -5
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-link-status.d.ts +5 -5
- package/dist/client/use-link-status.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +6 -4
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/codec.d.ts +33 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/config-types.d.ts +227 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts +35 -14
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -83
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/index.d.ts +45 -192
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12357 -11925
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +107 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +3 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +84 -0
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -5
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +4 -4
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +16 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +139 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -7
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +32 -441
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +78 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +25 -6
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +32 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-page-resolver.d.ts +52 -0
- package/dist/server/deny-page-resolver.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-holding-server.d.ts +52 -0
- package/dist/server/dev-holding-server.d.ts.map +1 -0
- package/dist/server/dev-warnings.d.ts +1 -21
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +4 -3
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +51 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -43
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +37 -2798
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +46 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2883 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +19 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/page-deny-boundary.d.ts +31 -0
- package/dist/server/page-deny-boundary.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +42 -10
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +69 -18
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +112 -43
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +4 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +4 -4
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +22 -19
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +3 -2
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +5 -2
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +56 -22
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +342 -28
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +52 -8
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +81 -18
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/sizeof.ts +31 -0
- package/src/cache/timber-cache.ts +24 -20
- package/src/cli.ts +16 -6
- package/src/client/browser-dev.ts +128 -1
- package/src/client/browser-entry/action-dispatch.ts +116 -0
- package/src/client/browser-entry/hmr.ts +81 -0
- package/src/client/browser-entry/hydrate.ts +145 -0
- package/src/client/browser-entry/index.ts +138 -0
- package/src/client/browser-entry/post-hydration.ts +119 -0
- package/src/client/browser-entry/router-init.ts +193 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +27 -0
- package/src/client/error-boundary.tsx +48 -16
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +9 -7
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +19 -38
- package/src/client/internal.ts +57 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +329 -97
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +31 -6
- package/src/client/navigation-root.tsx +342 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +299 -72
- package/src/client/rsc-fetch.ts +97 -8
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +72 -3
- package/src/client/top-loader.tsx +16 -8
- package/src/client/use-link-status.ts +7 -7
- package/src/client/use-params.ts +7 -5
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +3 -3
- package/src/client/use-router.ts +1 -1
- package/src/codec.ts +49 -0
- package/src/config-types.ts +225 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +78 -25
- package/src/cookies/index.ts +8 -0
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +295 -354
- package/src/plugin-context.ts +240 -0
- package/src/plugins/adapter-build.ts +9 -3
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +288 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +70 -9
- package/src/plugins/entries.ts +71 -10
- package/src/plugins/fonts.ts +168 -61
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +57 -17
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +135 -35
- package/src/plugins/static-build.ts +17 -11
- package/src/routing/codegen.ts +165 -105
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +93 -23
- package/src/routing/segment-classify.ts +89 -0
- package/src/routing/status-file-lint.ts +3 -2
- package/src/routing/types.ts +17 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +482 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +85 -0
- package/src/segment-params/define.ts +279 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +46 -11
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +21 -4
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +34 -6
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-page-resolver.ts +154 -0
- package/src/server/deny-renderer.ts +24 -38
- package/src/server/dev-holding-server.ts +185 -0
- package/src/server/dev-warnings.ts +4 -49
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +31 -15
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +280 -120
- package/src/server/index.ts +25 -177
- package/src/server/internal.ts +169 -0
- package/src/server/logger.ts +44 -36
- package/src/server/middleware-runner.ts +31 -4
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/page-deny-boundary.tsx +56 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline.ts +227 -62
- package/src/server/primitives.ts +111 -28
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +293 -132
- package/src/server/route-element-builder.ts +283 -191
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +24 -20
- package/src/server/rsc-entry/api-handler.ts +15 -16
- package/src/server/rsc-entry/error-renderer.ts +300 -89
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +200 -112
- package/src/server/rsc-entry/rsc-payload.ts +65 -18
- package/src/server/rsc-entry/rsc-stream.ts +65 -13
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +168 -38
- package/src/server/safe-load.ts +60 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +244 -229
- package/src/server/ssr-entry.ts +211 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +20 -9
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +3 -6
- package/src/server/version-skew.ts +104 -0
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +5 -1
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +7 -2
- package/src/utils/directive-parser.ts +5 -2
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js +0 -330
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/browser-entry.d.ts +0 -21
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -54
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -103
- package/src/client/browser-entry.ts +0 -678
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -166
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -410
package/src/server/ssr-render.ts
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
* independently of the Vite RSC plugin runtime (which provides
|
|
6
6
|
* createFromReadableStream for decoding RSC streams).
|
|
7
7
|
*
|
|
8
|
+
* Uses a platform-adaptive rendering strategy:
|
|
9
|
+
* - **Node.js / Bun**: `renderToPipeableStream` — React pipes HTML chunks
|
|
10
|
+
* through Node.js native streams (C++ implementation). Each chunk flows
|
|
11
|
+
* through libuv buffers with zero Promise overhead.
|
|
12
|
+
* - **Cloudflare Workers / Edge**: `renderToReadableStream` — React outputs
|
|
13
|
+
* to Web Streams which are V8-native C++ built-ins on these platforms.
|
|
14
|
+
*
|
|
15
|
+
* The detection is automatic at runtime. Both paths produce a Web
|
|
16
|
+
* `ReadableStream<Uint8Array>` so downstream transforms (injectHead,
|
|
17
|
+
* injectRscPayload, compression) work identically regardless of platform.
|
|
18
|
+
*
|
|
8
19
|
* Design docs: 02-rendering-pipeline.md §"Single-Pass Rendering",
|
|
9
20
|
* 18-build-system.md §"Entry Files"
|
|
10
21
|
*/
|
|
@@ -12,7 +23,25 @@
|
|
|
12
23
|
import type { ReactNode } from 'react';
|
|
13
24
|
import { renderToReadableStream } from 'react-dom/server';
|
|
14
25
|
|
|
15
|
-
import {
|
|
26
|
+
import { createRenderTimeout, RenderTimeoutError } from './render-timeout.js';
|
|
27
|
+
import { logRenderError, logStreamingError } from './logger.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* React's production error sanitization message. When NODE_ENV=production,
|
|
31
|
+
* React replaces all error messages with this string in renderToReadableStream
|
|
32
|
+
* and renderToPipeableStream. The original error was already logged by the
|
|
33
|
+
* RSC onError handler — logging the sanitized version in SSR is duplicate noise.
|
|
34
|
+
*/
|
|
35
|
+
const REACT_PROD_ERROR_PREFIX = 'An error occurred in the Server Components render';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns true if the error is React's sanitized production error.
|
|
39
|
+
* These are duplicates of errors already logged by the RSC onError handler
|
|
40
|
+
* with the full original message — logging them again in SSR adds noise.
|
|
41
|
+
*/
|
|
42
|
+
function isReactSanitizedError(error: unknown): boolean {
|
|
43
|
+
return error instanceof Error && error.message.startsWith(REACT_PROD_ERROR_PREFIX);
|
|
44
|
+
}
|
|
16
45
|
|
|
17
46
|
/**
|
|
18
47
|
* Inline script that injects <meta name="robots" content="noindex"> into <head>.
|
|
@@ -28,60 +57,277 @@ import { formatSsrError } from './error-formatter.js';
|
|
|
28
57
|
const NOINDEX_SCRIPT =
|
|
29
58
|
'<script>document.head.appendChild(Object.assign(document.createElement("meta"),{name:"robots",content:"noindex"}))</script>';
|
|
30
59
|
|
|
60
|
+
// ─── Platform Detection ──────────────────────────────────────────────────────
|
|
61
|
+
//
|
|
62
|
+
// Detect whether we're running on a platform with native Node.js streams.
|
|
63
|
+
// On Node.js/Bun, `node:stream` is backed by C++ (libuv). On Cloudflare
|
|
64
|
+
// Workers, `node:stream` via nodejs_compat is a JS polyfill — Web Streams
|
|
65
|
+
// are the faster path there (V8-native C++ built-ins).
|
|
66
|
+
//
|
|
67
|
+
// We detect once at module load to avoid per-request overhead.
|
|
68
|
+
// The check: process.versions.node exists AND we can import node:stream.
|
|
69
|
+
// Cloudflare Workers with nodejs_compat may polyfill process.versions but
|
|
70
|
+
// the streams won't be native. The Readable.toWeb check confirms native support.
|
|
71
|
+
|
|
72
|
+
let _useNodeStreams = false;
|
|
73
|
+
let _PassThrough: typeof import('node:stream').PassThrough | null = null;
|
|
74
|
+
let _ReadableToWeb: ((readable: import('node:stream').Readable) => ReadableStream) | null = null;
|
|
75
|
+
let _renderToPipeableStream: typeof import('react-dom/server').renderToPipeableStream | null = null;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Dynamic imports to avoid bundling node:stream and the Node.js-specific
|
|
79
|
+
// react-dom/server entry for CF Workers builds.
|
|
80
|
+
// On Node.js/Bun this resolves to native C++ streams.
|
|
81
|
+
// On CF Workers this either fails or returns a JS polyfill.
|
|
82
|
+
const nodeStream = await import('node:stream');
|
|
83
|
+
if (
|
|
84
|
+
typeof nodeStream.PassThrough === 'function' &&
|
|
85
|
+
typeof nodeStream.Readable.toWeb === 'function' &&
|
|
86
|
+
// Real Node.js — not a polyfill. Polyfills typically don't set
|
|
87
|
+
// process.release.name to 'node'.
|
|
88
|
+
typeof process !== 'undefined' &&
|
|
89
|
+
process.release?.name === 'node'
|
|
90
|
+
) {
|
|
91
|
+
// Dynamically import renderToPipeableStream from the Node.js-specific
|
|
92
|
+
// entry point. The SSR bundle resolves react-dom/server to the edge/browser
|
|
93
|
+
// export (which lacks renderToPipeableStream), so we import the .node file
|
|
94
|
+
// directly. This is safe because we're inside a process.release.name === 'node'
|
|
95
|
+
// guard — this code only runs on real Node.js.
|
|
96
|
+
const reactDomServer = await import('react-dom/server.node');
|
|
97
|
+
if (typeof reactDomServer.renderToPipeableStream === 'function') {
|
|
98
|
+
_useNodeStreams = true;
|
|
99
|
+
_PassThrough = nodeStream.PassThrough;
|
|
100
|
+
_ReadableToWeb = nodeStream.Readable.toWeb as (
|
|
101
|
+
readable: import('node:stream').Readable
|
|
102
|
+
) => ReadableStream;
|
|
103
|
+
_renderToPipeableStream = reactDomServer.renderToPipeableStream;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// node:stream or renderToPipeableStream not available — use Web Streams path
|
|
108
|
+
}
|
|
109
|
+
|
|
31
110
|
/**
|
|
32
111
|
* Render a React element tree to a ReadableStream of HTML.
|
|
33
112
|
*
|
|
34
|
-
*
|
|
113
|
+
* Automatically selects the optimal rendering path for the platform:
|
|
114
|
+
* - Node.js/Bun: `renderToPipeableStream` → Node.js native streams → `Readable.toWeb()`
|
|
115
|
+
* - CF Workers/Edge: `renderToReadableStream` → native Web Streams
|
|
116
|
+
*
|
|
35
117
|
* The returned stream begins yielding after onShellReady — everything
|
|
36
118
|
* outside <Suspense> boundaries is in the shell.
|
|
37
119
|
*
|
|
38
|
-
* With progressive streaming, the RSC stream is piped directly to SSR
|
|
39
|
-
* without buffering. If deny() was called outside a Suspense boundary,
|
|
40
|
-
* the RSC stream encodes an error in the shell — renderToReadableStream
|
|
41
|
-
* rejects, and the RSC entry catches this to render a deny page with
|
|
42
|
-
* the correct HTTP status code. If deny() was inside Suspense, the shell
|
|
43
|
-
* succeeds (200 committed) and the error streams as an error boundary.
|
|
44
|
-
*
|
|
45
120
|
* @param element - The React element tree decoded from the RSC stream
|
|
46
121
|
* @param options - Optional configuration
|
|
47
|
-
* @param options.bootstrapScriptContent - Inline JS injected by React as a
|
|
48
|
-
* non-deferred `<script>` in the shell HTML. Executes immediately during
|
|
49
|
-
* parsing — even while Suspense boundaries are still streaming. Used to
|
|
50
|
-
* kick off module loading via dynamic `import()` so hydration can start
|
|
51
|
-
* before the HTML stream closes.
|
|
52
122
|
* @returns A ReadableStream of HTML bytes with hydration markers
|
|
53
123
|
*/
|
|
54
124
|
export async function renderSsrStream(
|
|
55
125
|
element: ReactNode,
|
|
56
|
-
options?: {
|
|
126
|
+
options?: {
|
|
127
|
+
bootstrapScriptContent?: string;
|
|
128
|
+
deferSuspenseFor?: number;
|
|
129
|
+
signal?: AbortSignal;
|
|
130
|
+
renderTimeoutMs?: number;
|
|
131
|
+
}
|
|
57
132
|
): Promise<ReadableStream<Uint8Array>> {
|
|
133
|
+
return renderViaReadableStream(element, options);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Whether the current platform uses native Node.js streams for SSR. */
|
|
137
|
+
export const useNodeStreams = _useNodeStreams;
|
|
138
|
+
|
|
139
|
+
// ─── Node.js Path: renderToPipeableStream ────────────────────────────────────
|
|
140
|
+
//
|
|
141
|
+
// Uses React's Node.js-native API. HTML chunks flow through C++ stream
|
|
142
|
+
// buffers with zero Promise allocations per chunk. Returns a Node.js
|
|
143
|
+
// Readable — the caller (ssr-entry.ts) pipes through Node.js Transform
|
|
144
|
+
// streams for injectHead/injectRscPayload before converting to Web
|
|
145
|
+
// ReadableStream at the Response boundary.
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Render via renderToPipeableStream, returning a Node.js Readable.
|
|
149
|
+
*
|
|
150
|
+
* The entire HTML rendering + post-processing pipeline stays in native
|
|
151
|
+
* Node.js streams (C++ backed). Only converted to Web ReadableStream
|
|
152
|
+
* at the very end for the Response body.
|
|
153
|
+
*/
|
|
154
|
+
export async function renderSsrNodeStream(
|
|
155
|
+
element: ReactNode,
|
|
156
|
+
options?: {
|
|
157
|
+
bootstrapScriptContent?: string;
|
|
158
|
+
deferSuspenseFor?: number;
|
|
159
|
+
signal?: AbortSignal;
|
|
160
|
+
renderTimeoutMs?: number;
|
|
161
|
+
}
|
|
162
|
+
): Promise<import('node:stream').Readable> {
|
|
58
163
|
const signal = options?.signal;
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
164
|
+
const deferMs = options?.deferSuspenseFor;
|
|
165
|
+
const timeoutMs = options?.renderTimeoutMs;
|
|
166
|
+
|
|
167
|
+
return new Promise<import('node:stream').Readable>((resolve, reject) => {
|
|
168
|
+
const passthrough = new _PassThrough!();
|
|
169
|
+
|
|
170
|
+
let allReadyResolve: (() => void) | null = null;
|
|
171
|
+
let allReadyReject: ((reason?: unknown) => void) | null = null;
|
|
172
|
+
const allReady = new Promise<void>((resolve, reject) => {
|
|
173
|
+
allReadyResolve = resolve;
|
|
174
|
+
allReadyReject = reject;
|
|
175
|
+
});
|
|
176
|
+
allReady.catch(() => {});
|
|
177
|
+
|
|
178
|
+
const { pipe, abort } = _renderToPipeableStream!(element, {
|
|
179
|
+
bootstrapScriptContent: options?.bootstrapScriptContent || undefined,
|
|
180
|
+
|
|
181
|
+
onShellReady() {
|
|
182
|
+
if (deferMs && deferMs > 0) {
|
|
183
|
+
Promise.race([allReady, new Promise<void>((r) => setTimeout(r, deferMs))]).then(() => {
|
|
184
|
+
pipe(passthrough);
|
|
185
|
+
resolve(passthrough);
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
pipe(passthrough);
|
|
189
|
+
resolve(passthrough);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
onAllReady() {
|
|
194
|
+
allReadyResolve?.();
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
onShellError(error: unknown) {
|
|
198
|
+
// Reject allReady so the render timeout is cancelled.
|
|
199
|
+
// Without this, a pre-shell failure leaves the timer
|
|
200
|
+
// running for the full timeout window.
|
|
201
|
+
allReadyReject?.(error);
|
|
202
|
+
reject(error);
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
onError(error: unknown) {
|
|
206
|
+
if (isAbortError(error) || signal?.aborted) return;
|
|
207
|
+
// Skip React's sanitized production errors — the original was
|
|
208
|
+
// already logged by the RSC onError with full details.
|
|
209
|
+
if (isReactSanitizedError(error)) return;
|
|
210
|
+
logRenderError({ method: '', path: '', error });
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Wire abort to both request signal AND render timeout.
|
|
215
|
+
// If the client stays connected but a downstream fetch hangs,
|
|
216
|
+
// the timeout ensures abort() is eventually called.
|
|
217
|
+
if (signal) {
|
|
218
|
+
if (signal.aborted) {
|
|
219
|
+
abort();
|
|
220
|
+
} else {
|
|
221
|
+
signal.addEventListener('abort', () => abort(), { once: true });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
226
|
+
const renderTimeout = createRenderTimeout(timeoutMs, signal);
|
|
227
|
+
renderTimeout.signal.addEventListener(
|
|
228
|
+
'abort',
|
|
229
|
+
() => {
|
|
230
|
+
logRenderError({
|
|
231
|
+
method: '',
|
|
232
|
+
path: '',
|
|
233
|
+
error: new Error(
|
|
234
|
+
`SSR render timed out after ${timeoutMs}ms — aborting. ` +
|
|
235
|
+
'A Suspense component or downstream fetch may be hanging.'
|
|
236
|
+
),
|
|
237
|
+
});
|
|
238
|
+
abort(renderTimeout.signal.reason);
|
|
239
|
+
},
|
|
240
|
+
{ once: true }
|
|
241
|
+
);
|
|
242
|
+
// Cancel the timeout when the render completes OR on pre-shell
|
|
243
|
+
// failure. Without the catch branch, onShellError → reject()
|
|
244
|
+
// would leave the timer running for the full timeout window.
|
|
245
|
+
allReady.then(
|
|
246
|
+
() => renderTimeout.cancel(),
|
|
247
|
+
() => renderTimeout.cancel()
|
|
248
|
+
);
|
|
249
|
+
}
|
|
68
250
|
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Convert a Node.js Readable to a Web ReadableStream (zero-copy bridge). */
|
|
254
|
+
export function nodeReadableToWeb(
|
|
255
|
+
readable: import('node:stream').Readable
|
|
256
|
+
): ReadableStream<Uint8Array> {
|
|
257
|
+
return _ReadableToWeb!(readable) as ReadableStream<Uint8Array>;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ─── Web Streams Path: renderToReadableStream ────────────────────────────────
|
|
261
|
+
//
|
|
262
|
+
// Uses React's Web Streams API. On Cloudflare Workers, ReadableStream is a
|
|
263
|
+
// V8-native C++ built-in, making this the fastest path for that platform.
|
|
264
|
+
// On Node.js, Web Streams are a JS reimplementation — slower, but this path
|
|
265
|
+
// is only used as a fallback when Node.js native streams aren't available.
|
|
266
|
+
|
|
267
|
+
async function renderViaReadableStream(
|
|
268
|
+
element: ReactNode,
|
|
269
|
+
options?: {
|
|
270
|
+
bootstrapScriptContent?: string;
|
|
271
|
+
deferSuspenseFor?: number;
|
|
272
|
+
signal?: AbortSignal;
|
|
273
|
+
renderTimeoutMs?: number;
|
|
274
|
+
}
|
|
275
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
276
|
+
const signal = options?.signal;
|
|
277
|
+
const timeoutMs = options?.renderTimeoutMs;
|
|
278
|
+
|
|
279
|
+
// If a render timeout is configured, create a combined signal that
|
|
280
|
+
// fires on either request abort OR timeout — whichever comes first.
|
|
281
|
+
let renderTimeout: import('./render-timeout.js').RenderTimeout | null = null;
|
|
282
|
+
let effectiveSignal = signal;
|
|
283
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
284
|
+
renderTimeout = createRenderTimeout(timeoutMs, signal);
|
|
285
|
+
effectiveSignal = renderTimeout.signal;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let stream: Awaited<ReturnType<typeof renderToReadableStream>>;
|
|
289
|
+
try {
|
|
290
|
+
stream = await renderToReadableStream(element, {
|
|
291
|
+
bootstrapScriptContent: options?.bootstrapScriptContent || undefined,
|
|
292
|
+
signal: effectiveSignal,
|
|
293
|
+
onError(error: unknown) {
|
|
294
|
+
if (isAbortError(error) || effectiveSignal?.aborted) return;
|
|
295
|
+
if (error instanceof RenderTimeoutError) {
|
|
296
|
+
logRenderError({
|
|
297
|
+
method: '',
|
|
298
|
+
path: '',
|
|
299
|
+
error: new Error(
|
|
300
|
+
`SSR render timed out after ${timeoutMs}ms — aborting. ` +
|
|
301
|
+
'A Suspense component or downstream fetch may be hanging.'
|
|
302
|
+
),
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Skip React's sanitized production errors — the original was
|
|
307
|
+
// already logged by the RSC onError with full details.
|
|
308
|
+
if (isReactSanitizedError(error)) return;
|
|
309
|
+
logRenderError({ method: '', path: '', error });
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
} catch (error) {
|
|
313
|
+
// Pre-shell failure (e.g. RSC stream error). Cancel the render
|
|
314
|
+
// timeout so it doesn't leak a timer + abort callback for the
|
|
315
|
+
// full timeout window. Under repeated pre-shell failures this
|
|
316
|
+
// would accumulate unnecessary timers.
|
|
317
|
+
renderTimeout?.cancel();
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
69
320
|
|
|
70
321
|
// Prevent unhandled promise rejection from streaming-phase errors.
|
|
71
|
-
// React DOM Server exposes `allReady` — a promise that resolves when
|
|
72
|
-
// ALL content (including Suspense boundaries) has been rendered. If a
|
|
73
|
-
// streaming-phase error occurs (e.g. React boundary flush failure),
|
|
74
|
-
// `allReady` rejects independently of the stream. Without this catch,
|
|
75
|
-
// the rejection becomes an unhandled promise rejection that crashes
|
|
76
|
-
// the Node.js process.
|
|
77
322
|
stream.allReady.catch(() => {});
|
|
78
323
|
|
|
324
|
+
// Cancel the render timeout once allReady resolves (render completed).
|
|
325
|
+
if (renderTimeout) {
|
|
326
|
+
stream.allReady.then(() => renderTimeout!.cancel()).catch(() => renderTimeout!.cancel());
|
|
327
|
+
}
|
|
328
|
+
|
|
79
329
|
// deferSuspenseFor hold: delay the first read so React can resolve
|
|
80
330
|
// fast-completing Suspense boundaries before we read the shell HTML.
|
|
81
|
-
// renderToReadableStream generates HTML lazily on pull — if we wait
|
|
82
|
-
// before reading, React resolves pending boundaries and inlines their
|
|
83
|
-
// content instead of serializing fallbacks. Race allReady against
|
|
84
|
-
// deferSuspenseFor so we don't wait longer than necessary.
|
|
85
331
|
// See design/05-streaming.md §"deferSuspenseFor"
|
|
86
332
|
const deferMs = options?.deferSuspenseFor;
|
|
87
333
|
if (deferMs && deferMs > 0) {
|
|
@@ -91,30 +337,19 @@ export async function renderSsrStream(
|
|
|
91
337
|
]);
|
|
92
338
|
}
|
|
93
339
|
|
|
94
|
-
// renderToReadableStream resolves after onShellReady by default.
|
|
95
|
-
// The stream is ready to read — the shell (everything outside
|
|
96
|
-
// Suspense boundaries) is available. Suspense content streams
|
|
97
|
-
// into the open connection as it resolves.
|
|
98
|
-
//
|
|
99
|
-
// Wrap the stream in an error-resilient transform. With progressive
|
|
100
|
-
// streaming, errors inside Suspense boundaries (e.g. deny() or throws
|
|
101
|
-
// in async components) cause React's stream to error during the flush
|
|
102
|
-
// phase. The onError callback logs the error, but the stream error
|
|
103
|
-
// would become an unhandled promise rejection and crash the process.
|
|
104
|
-
// The transform catches these post-shell streaming errors and closes
|
|
105
|
-
// the stream cleanly — the shell (with correct status code) has
|
|
106
|
-
// already been sent.
|
|
107
340
|
return wrapStreamWithErrorHandling(stream, signal);
|
|
108
341
|
}
|
|
109
342
|
|
|
343
|
+
// ─── Shared Utilities ────────────────────────────────────────────────────────
|
|
344
|
+
|
|
110
345
|
/**
|
|
111
346
|
* Wrap an HTML stream with error handling for the streaming phase.
|
|
112
347
|
*
|
|
113
348
|
* During progressive RSC→SSR streaming, errors in Suspense boundaries
|
|
114
349
|
* (e.g. deny() inside Suspense, throws in async components) cause
|
|
115
|
-
* React DOM's
|
|
116
|
-
*
|
|
117
|
-
*
|
|
350
|
+
* React DOM's stream to error after the shell has been flushed. Without
|
|
351
|
+
* this wrapper, the stream error becomes an unhandled promise rejection
|
|
352
|
+
* that crashes the process.
|
|
118
353
|
*
|
|
119
354
|
* The wrapper catches streaming-phase errors, logs them, and closes
|
|
120
355
|
* the output stream cleanly. The shell (headers, status code, content
|
|
@@ -138,18 +373,11 @@ export function wrapStreamWithErrorHandling(
|
|
|
138
373
|
}
|
|
139
374
|
controller.enqueue(value);
|
|
140
375
|
} catch (error) {
|
|
141
|
-
// Connection abort (user refreshed or navigated away) — close
|
|
142
|
-
// silently without logging. This is not an application error.
|
|
143
376
|
if (isAbortError(error) || signal?.aborted) {
|
|
144
377
|
controller.close();
|
|
145
378
|
return;
|
|
146
379
|
}
|
|
147
|
-
|
|
148
|
-
// deny() or throw inside Suspense after flush).
|
|
149
|
-
// The shell has already been sent with status 200. Inject a
|
|
150
|
-
// noindex meta tag so search engines don't index this error page,
|
|
151
|
-
// then close cleanly. See design/05-streaming.md.
|
|
152
|
-
console.error('[timber] SSR streaming error (post-shell):', formatSsrError(error));
|
|
380
|
+
logStreamingError({ error });
|
|
153
381
|
controller.enqueue(encoder.encode(NOINDEX_SCRIPT));
|
|
154
382
|
controller.close();
|
|
155
383
|
}
|
|
@@ -162,10 +390,6 @@ export function wrapStreamWithErrorHandling(
|
|
|
162
390
|
|
|
163
391
|
/**
|
|
164
392
|
* Check if an error is an abort error (connection closed by client).
|
|
165
|
-
*
|
|
166
|
-
* When the browser aborts a request (page refresh, navigation away),
|
|
167
|
-
* the AbortSignal fires and React/streams throw an AbortError. This
|
|
168
|
-
* is not an application error — suppress it from error boundaries and logs.
|
|
169
393
|
*/
|
|
170
394
|
function isAbortError(error: unknown): boolean {
|
|
171
395
|
if (error instanceof DOMException && error.name === 'AbortError') return true;
|
|
@@ -177,12 +401,10 @@ function isAbortError(error: unknown): boolean {
|
|
|
177
401
|
* Build a Response from the SSR HTML stream with the correct
|
|
178
402
|
* status code and headers from the navigation context.
|
|
179
403
|
*
|
|
180
|
-
*
|
|
404
|
+
* React 19.3+ automatically emits `<!DOCTYPE html>` when the root
|
|
405
|
+
* element is `<html>`, so no framework-level doctype prepending is needed.
|
|
181
406
|
*
|
|
182
|
-
*
|
|
183
|
-
* @param statusCode - The committed HTTP status code from RSC
|
|
184
|
-
* @param responseHeaders - Response headers from middleware/proxy
|
|
185
|
-
* @returns A Response ready to send to the client
|
|
407
|
+
* Sets content-type to text/html if not already set by middleware.
|
|
186
408
|
*/
|
|
187
409
|
export function buildSsrResponse(
|
|
188
410
|
htmlStream: ReadableStream<Uint8Array>,
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR wrapper components that match the client-side component tree depth.
|
|
3
|
+
*
|
|
4
|
+
* During hydration, React's useId() generates deterministic IDs based on
|
|
5
|
+
* the component's position in the fiber tree. If the SSR tree has fewer
|
|
6
|
+
* wrapper components than the client hydration tree, every useId() call
|
|
7
|
+
* produces different IDs — causing hydration mismatches in libraries like
|
|
8
|
+
* Radix UI that rely on useId() internally.
|
|
9
|
+
*
|
|
10
|
+
* The client tree (browser-entry.ts) wraps the RSC element with:
|
|
11
|
+
* NavigationRoot → PendingNavigationProvider → Fragment(TopLoader, ...) →
|
|
12
|
+
* TimberNuqsAdapter → NuqsAdapterProvider → NavigationProvider → element
|
|
13
|
+
*
|
|
14
|
+
* The SSR tree must produce the same component boundaries. These wrappers
|
|
15
|
+
* are no-op components that render children directly but exist as fiber
|
|
16
|
+
* nodes to match the client's tree depth.
|
|
17
|
+
*
|
|
18
|
+
* Related issues: facebook/react#24669, radix-ui/primitives#3700
|
|
19
|
+
* See design/02-rendering-pipeline.md §"RSC → SSR → Client Hydration"
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createElement, Fragment, type ReactNode } from 'react';
|
|
23
|
+
import { withNuqsSsrAdapter } from './nuqs-ssr-provider.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* SSR equivalent of NavigationRoot.
|
|
27
|
+
*
|
|
28
|
+
* On the client, NavigationRoot uses useState and standalone startTransition, rendering:
|
|
29
|
+
* PendingNavigationProvider(Fragment(TopLoader, element))
|
|
30
|
+
*
|
|
31
|
+
* This SSR version matches the component boundary depth without client
|
|
32
|
+
* hooks. It renders SsrPendingProvider → Fragment(SsrTopLoader, children).
|
|
33
|
+
*/
|
|
34
|
+
function SsrNavigationRoot({
|
|
35
|
+
children,
|
|
36
|
+
hasTopLoader,
|
|
37
|
+
}: {
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
hasTopLoader: boolean;
|
|
40
|
+
}): ReactNode {
|
|
41
|
+
if (hasTopLoader) {
|
|
42
|
+
return createElement(
|
|
43
|
+
SsrPendingProvider,
|
|
44
|
+
null,
|
|
45
|
+
createElement(Fragment, null, createElement(SsrTopLoader, null), children)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return createElement(SsrPendingProvider, null, children);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* SSR equivalent of PendingNavigationProvider.
|
|
53
|
+
* Matches the context.Provider component boundary on the client.
|
|
54
|
+
*/
|
|
55
|
+
function SsrPendingProvider({ children }: { children: ReactNode }): ReactNode {
|
|
56
|
+
return children as ReactNode;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* SSR equivalent of TopLoader.
|
|
61
|
+
* Exists as a fiber node to match the client tree depth; renders nothing.
|
|
62
|
+
*/
|
|
63
|
+
function SsrTopLoader(): ReactNode {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* SSR equivalent of NavigationProvider.
|
|
69
|
+
* Matches the context.Provider component boundary on the client.
|
|
70
|
+
*/
|
|
71
|
+
function SsrNavigationProvider({ children }: { children: ReactNode }): ReactNode {
|
|
72
|
+
return children as ReactNode;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* SSR equivalent of TimberNuqsAdapter component boundary.
|
|
77
|
+
* The actual NuqsAdapterProvider (from createAdapterProvider) is added
|
|
78
|
+
* inside this wrapper via withNuqsSsrAdapter, matching the client's
|
|
79
|
+
* TimberNuqsAdapter → NuqsAdapterProvider nesting.
|
|
80
|
+
*/
|
|
81
|
+
function SsrNuqsWrapper({
|
|
82
|
+
searchParams,
|
|
83
|
+
children,
|
|
84
|
+
}: {
|
|
85
|
+
searchParams: Record<string, string>;
|
|
86
|
+
children: ReactNode;
|
|
87
|
+
}): ReactNode {
|
|
88
|
+
// withNuqsSsrAdapter creates a NuqsAdapterProvider (from createAdapterProvider),
|
|
89
|
+
// which is the same component factory used by TimberNuqsAdapter on the client.
|
|
90
|
+
// Both produce the same internal component boundary depth.
|
|
91
|
+
return withNuqsSsrAdapter(searchParams, children);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wrap the SSR element tree with the same component boundaries as the
|
|
96
|
+
* client hydration tree. This ensures useId() generates matching IDs
|
|
97
|
+
* on both sides.
|
|
98
|
+
*
|
|
99
|
+
* Client tree (browser-entry.ts):
|
|
100
|
+
* NavigationRoot
|
|
101
|
+
* → PendingNavigationProvider
|
|
102
|
+
* → Fragment(TopLoader, element)
|
|
103
|
+
* → TimberNuqsAdapter
|
|
104
|
+
* → NuqsAdapterProvider (from createAdapterProvider)
|
|
105
|
+
* → NavigationProvider
|
|
106
|
+
* → [RSC element]
|
|
107
|
+
*
|
|
108
|
+
* SSR tree (this function):
|
|
109
|
+
* SsrNavigationRoot
|
|
110
|
+
* → SsrPendingProvider
|
|
111
|
+
* → Fragment(SsrTopLoader, element)
|
|
112
|
+
* → SsrNuqsWrapper
|
|
113
|
+
* → NuqsAdapterProvider (from withNuqsSsrAdapter → createAdapterProvider)
|
|
114
|
+
* → SsrNavigationProvider
|
|
115
|
+
* → [RSC element]
|
|
116
|
+
*
|
|
117
|
+
* @param element - The decoded RSC element
|
|
118
|
+
* @param searchParams - Request search params for nuqs SSR adapter
|
|
119
|
+
* @param hasTopLoader - Whether the top loader is enabled (must match client config)
|
|
120
|
+
*/
|
|
121
|
+
export function wrapSsrElement(
|
|
122
|
+
element: ReactNode,
|
|
123
|
+
searchParams: Record<string, string>,
|
|
124
|
+
hasTopLoader: boolean
|
|
125
|
+
): ReactNode {
|
|
126
|
+
// Build inside-out to match the client's createElement chain:
|
|
127
|
+
// NavigationProvider(TimberNuqsAdapter(element))
|
|
128
|
+
// → passed as initial to NavigationRoot
|
|
129
|
+
// → NavigationRoot renders PendingNavigationProvider(Fragment(TopLoader, initial))
|
|
130
|
+
|
|
131
|
+
// 1. Innermost: NavigationProvider equivalent
|
|
132
|
+
const withNav = createElement(SsrNavigationProvider, null, element);
|
|
133
|
+
|
|
134
|
+
// 2. TimberNuqsAdapter equivalent (wraps withNuqsSsrAdapter for the actual nuqs provider)
|
|
135
|
+
const withNuqs = createElement(SsrNuqsWrapper, { searchParams, children: withNav });
|
|
136
|
+
|
|
137
|
+
// 3. Outermost: NavigationRoot equivalent (PendingNavigationProvider + TopLoader)
|
|
138
|
+
return createElement(SsrNavigationRoot, { hasTopLoader, children: withNuqs });
|
|
139
|
+
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* Then framework default (returns null)
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import type { SegmentNode, RouteFile } from '
|
|
30
|
+
import type { SegmentNode, RouteFile } from '../routing/types.js';
|
|
31
31
|
|
|
32
32
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
33
33
|
|