@timber-js/app 0.2.0-alpha.6 → 0.2.0-alpha.61
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/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
- package/dist/_chunks/als-registry-Ba7URUIn.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-CT98cU9c.js +121 -0
- package/dist/_chunks/define-CT98cU9c.js.map +1 -0
- package/dist/_chunks/define-TK8C1M3x.js +279 -0
- package/dist/_chunks/define-TK8C1M3x.js.map +1 -0
- package/dist/_chunks/define-cookie-BWr_52kY.js +93 -0
- package/dist/_chunks/define-cookie-BWr_52kY.js.map +1 -0
- package/dist/_chunks/error-boundary-DpZJBCqh.js +211 -0
- package/dist/_chunks/error-boundary-DpZJBCqh.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-DviM89f0.js.map → format-cX7wzEp2.js.map} +1 -1
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-Cey5DCGr.js} +129 -77
- package/dist/_chunks/interception-Cey5DCGr.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-rju2rbga.js} +97 -69
- package/dist/_chunks/request-context-rju2rbga.js.map +1 -0
- package/dist/_chunks/segment-context-CyaM1mrD.js +72 -0
- package/dist/_chunks/segment-context-CyaM1mrD.js.map +1 -0
- package/dist/_chunks/stale-reload-BSSym1MJ.js +64 -0
- package/dist/_chunks/stale-reload-BSSym1MJ.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → tracing-VYETCQsg.js} +17 -3
- package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-VYETCQsg.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-wEXY2JQB.js} +1 -1
- package/dist/_chunks/use-query-states-wEXY2JQB.js.map +1 -0
- package/dist/_chunks/wrappers-BaG1bnM3.js +63 -0
- package/dist/_chunks/wrappers-BaG1bnM3.js.map +1 -0
- 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/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/index.d.ts +5 -2
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +90 -20
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/register-cached-function.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +10 -1
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- 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 +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +433 -252
- package/dist/client/index.js.map +1 -1
- package/dist/client/link-pending-store.d.ts +78 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +23 -9
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +2 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/router.d.ts +25 -3
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +36 -2
- 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-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.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/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +1 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +1 -1
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +3 -3
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/codec.d.ts +21 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/cookies/define-cookie.d.ts +34 -13
- 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 +127 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +665 -242
- package/dist/index.js.map +1 -1
- package/dist/params/define.d.ts +100 -0
- package/dist/params/define.d.ts.map +1 -0
- package/dist/params/index.d.ts +8 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +4 -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 +9 -2
- 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 +1 -1
- 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.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- 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/search-params/codecs.d.ts +1 -1
- package/dist/search-params/define.d.ts +159 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -5
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -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.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 +1 -1
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +25 -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-renderer.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 +4 -0
- 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 +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1977 -1648
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.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/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +20 -6
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- 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 +65 -38
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +7 -0
- 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 +10 -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/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 +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +20 -13
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -3
- 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/server/waituntil-bridge.d.ts.map +1 -1
- 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/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.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 +17 -17
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/index.ts +5 -2
- package/src/cache/register-cached-function.ts +7 -3
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +40 -29
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +151 -99
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +2 -2
- package/src/client/index.ts +10 -1
- package/src/client/link-pending-store.ts +136 -0
- package/src/client/link.tsx +137 -22
- package/src/client/navigation-context.ts +6 -5
- package/src/client/router.ts +117 -60
- package/src/client/rsc-fetch.ts +90 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/stale-reload.ts +73 -6
- package/src/client/top-loader.tsx +10 -9
- package/src/client/transition-root.tsx +20 -2
- package/src/client/use-params.ts +4 -4
- package/src/client/use-query-states.ts +2 -2
- package/src/codec.ts +21 -0
- package/src/cookies/define-cookie.ts +71 -20
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +297 -85
- package/src/params/define.ts +327 -0
- package/src/params/index.ts +28 -0
- package/src/plugins/adapter-build.ts +8 -2
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/cache-transform.ts +1 -1
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +284 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +41 -7
- package/src/plugins/entries.ts +6 -8
- package/src/plugins/fonts.ts +102 -55
- 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 +69 -31
- package/src/plugins/static-build.ts +10 -6
- package/src/routing/codegen.ts +109 -88
- package/src/routing/scanner.ts +86 -7
- 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 +1 -1
- package/src/search-params/define.ts +518 -0
- package/src/search-params/index.ts +12 -18
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/server/access-gate.tsx +40 -9
- package/src/server/action-client.ts +8 -2
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +20 -3
- package/src/server/actions.ts +1 -1
- package/src/server/als-registry.ts +25 -4
- 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-renderer.ts +5 -3
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +58 -15
- package/src/server/fallback-error.ts +29 -14
- 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 +277 -117
- package/src/server/index.ts +9 -4
- package/src/server/logger.ts +44 -36
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/pipeline-interception.ts +1 -1
- package/src/server/pipeline.ts +148 -41
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +125 -119
- package/src/server/route-element-builder.ts +107 -115
- package/src/server/route-handler.ts +2 -1
- package/src/server/route-matcher.ts +9 -2
- package/src/server/rsc-entry/api-handler.ts +8 -8
- package/src/server/rsc-entry/error-renderer.ts +286 -81
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +177 -76
- package/src/server/rsc-entry/rsc-payload.ts +91 -18
- package/src/server/rsc-entry/rsc-stream.ts +74 -18
- package/src/server/rsc-entry/ssr-bridge.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +152 -34
- package/src/server/slot-resolver.ts +231 -220
- 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 +23 -0
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +2 -1
- 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/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.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/client/error-boundary.js.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/cookies/index.js.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/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.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 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/client/link-status-provider.tsx +0 -30
- 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 -277
|
@@ -24,36 +24,38 @@ import buildManifest from 'virtual:timber-build-manifest';
|
|
|
24
24
|
// @ts-expect-error — virtual module provided by timber-entries plugin
|
|
25
25
|
import loadUserInstrumentation from 'virtual:timber-instrumentation';
|
|
26
26
|
|
|
27
|
-
import type { FormRerender } from '
|
|
28
|
-
import { handleActionRequest, isActionRequest } from '
|
|
29
|
-
import type { BodyLimitsConfig } from '
|
|
30
|
-
import type { BuildManifest } from '
|
|
27
|
+
import type { FormRerender } from '../action-handler.js';
|
|
28
|
+
import { handleActionRequest, isActionRequest } from '../action-handler.js';
|
|
29
|
+
import type { BodyLimitsConfig } from '../body-limits.js';
|
|
30
|
+
import type { BuildManifest } from '../build-manifest.js';
|
|
31
31
|
import {
|
|
32
|
-
buildCssLinkTags,
|
|
33
32
|
buildFontPreloadTags,
|
|
34
33
|
buildModulepreloadTags,
|
|
35
|
-
collectRouteCss,
|
|
36
34
|
collectRouteFonts,
|
|
37
35
|
collectRouteModulepreloads,
|
|
38
|
-
} from '
|
|
39
|
-
import type { LayoutEntry } from '
|
|
40
|
-
import { renderDenyPage, renderDenyPageAsRsc } from '
|
|
41
|
-
import { resolveLogMode } from '
|
|
42
|
-
import { sendEarlyHints103 } from '
|
|
43
|
-
import { collectEarlyHintHeaders } from '
|
|
44
|
-
import { runWithFormFlash } from '
|
|
45
|
-
import type { ClientBootstrapConfig } from '
|
|
46
|
-
import { buildClientScripts } from '
|
|
47
|
-
import type { InterceptionContext, PipelineConfig, RouteMatch } from '
|
|
48
|
-
import { createPipeline } from '
|
|
49
|
-
import { DenySignal, RedirectSignal } from '
|
|
50
|
-
import {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
36
|
+
} from '../build-manifest.js';
|
|
37
|
+
import type { LayoutEntry } from '../deny-renderer.js';
|
|
38
|
+
import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.js';
|
|
39
|
+
import { resolveLogMode } from '../dev-logger.js';
|
|
40
|
+
import { sendEarlyHints103 } from '../early-hints-sender.js';
|
|
41
|
+
import { collectEarlyHintHeaders } from '../early-hints.js';
|
|
42
|
+
import { runWithFormFlash } from '../form-flash.js';
|
|
43
|
+
import type { ClientBootstrapConfig } from '../html-injectors.js';
|
|
44
|
+
import { buildClientScripts } from '../html-injectors.js';
|
|
45
|
+
import type { InterceptionContext, PipelineConfig, RouteMatch } from '../pipeline.js';
|
|
46
|
+
import { createPipeline, coerceSegmentParams } from '../pipeline.js';
|
|
47
|
+
import { DenySignal, RedirectSignal } from '../primitives.js';
|
|
48
|
+
import {
|
|
49
|
+
buildRouteElement,
|
|
50
|
+
RouteSignalWithContext,
|
|
51
|
+
ParamCoercionError,
|
|
52
|
+
} from '../route-element-builder.js';
|
|
53
|
+
import type { ManifestSegmentNode } from '../route-matcher.js';
|
|
54
|
+
import { createMetadataRouteMatcher, createRouteMatcher } from '../route-matcher.js';
|
|
55
|
+
import { initDevTracing } from '../tracing.js';
|
|
56
|
+
|
|
57
|
+
import { renderFallbackError as renderFallback } from '../fallback-error.js';
|
|
58
|
+
import { loadInstrumentation } from '../instrumentation.js';
|
|
57
59
|
import { handleApiRoute } from './api-handler.js';
|
|
58
60
|
import { renderErrorPage, renderNoMatchPage } from './error-renderer.js';
|
|
59
61
|
import {
|
|
@@ -61,33 +63,61 @@ import {
|
|
|
61
63
|
createDebugChannelSink,
|
|
62
64
|
escapeHtml,
|
|
63
65
|
isRscPayloadRequest,
|
|
66
|
+
type DebugComponentEntry,
|
|
64
67
|
} from './helpers.js';
|
|
65
|
-
import { parseClientStateTree } from '
|
|
66
|
-
import {
|
|
67
|
-
createResponseCache,
|
|
68
|
-
resolveResponseCacheConfig,
|
|
69
|
-
type ResponseCache,
|
|
70
|
-
} from '#/server/response-cache.js';
|
|
68
|
+
import { parseClientStateTree } from '../state-tree-diff.js';
|
|
71
69
|
import { buildRscPayloadResponse } from './rsc-payload.js';
|
|
72
70
|
import { renderRscStream } from './rsc-stream.js';
|
|
73
71
|
import { renderSsrResponse } from './ssr-renderer.js';
|
|
74
72
|
import { callSsr } from './ssr-bridge.js';
|
|
75
|
-
import { isDebug, isDevMode, setDebugFromConfig } from '
|
|
73
|
+
import { isDebug, isDevMode, setDebugFromConfig } from '../debug.js';
|
|
74
|
+
import { recordTiming } from '../server-timing.js';
|
|
75
|
+
import { requestContextAls } from '../als-registry.js';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the Server-Timing mode from timber.config.ts.
|
|
79
|
+
*
|
|
80
|
+
* If the user set `serverTiming` explicitly, use that value.
|
|
81
|
+
* Otherwise: `'detailed'` in dev, `'total'` in production.
|
|
82
|
+
*/
|
|
83
|
+
function resolveServerTimingMode(
|
|
84
|
+
config: Record<string, unknown>,
|
|
85
|
+
isDev: boolean
|
|
86
|
+
): 'detailed' | 'total' | false {
|
|
87
|
+
const userValue = config.serverTiming as 'detailed' | 'total' | false | undefined;
|
|
88
|
+
if (userValue !== undefined) return userValue;
|
|
89
|
+
return isDev ? 'detailed' : 'total';
|
|
90
|
+
}
|
|
76
91
|
|
|
77
92
|
// Dev-only pipeline error handler, set by the dev server after import.
|
|
78
93
|
// In production this is always undefined — no overhead.
|
|
79
|
-
|
|
94
|
+
// The third argument provides RSC debug component data (from the Flight
|
|
95
|
+
// debug channel) when available — used by the error overlay to show the
|
|
96
|
+
// server component tree context for render errors.
|
|
97
|
+
let _devPipelineErrorHandler:
|
|
98
|
+
| ((error: Error, phase: string, debugComponents?: DebugComponentEntry[]) => void)
|
|
99
|
+
| undefined;
|
|
80
100
|
|
|
81
101
|
/**
|
|
82
102
|
* Set the dev pipeline error handler.
|
|
83
103
|
*
|
|
84
104
|
* Called by the dev server after importing this module to wire pipeline
|
|
85
105
|
* errors into the Vite browser error overlay. No-op in production.
|
|
106
|
+
*
|
|
107
|
+
* The handler receives an optional third argument with RSC debug component
|
|
108
|
+
* info — component names, environments, and stack frames from the Flight
|
|
109
|
+
* debug channel. This is only populated for render-phase errors.
|
|
86
110
|
*/
|
|
87
|
-
export function setDevPipelineErrorHandler(
|
|
111
|
+
export function setDevPipelineErrorHandler(
|
|
112
|
+
handler: (error: Error, phase: string, debugComponents?: DebugComponentEntry[]) => void
|
|
113
|
+
): void {
|
|
88
114
|
_devPipelineErrorHandler = handler;
|
|
89
115
|
}
|
|
90
116
|
|
|
117
|
+
// Dev-only: debug components getter is stored per-request in ALS
|
|
118
|
+
// (RequestContextStore.debugComponentsGetter) to avoid cross-request
|
|
119
|
+
// race conditions. See TIM-557.
|
|
120
|
+
|
|
91
121
|
/**
|
|
92
122
|
* Create the RSC request handler from the route manifest.
|
|
93
123
|
*
|
|
@@ -101,13 +131,15 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
101
131
|
// See design/17-logging.md §"register() — Server Startup"
|
|
102
132
|
await loadInstrumentation(loadUserInstrumentation);
|
|
103
133
|
|
|
104
|
-
// Initialize
|
|
105
|
-
|
|
106
|
-
|
|
134
|
+
// Initialize deployment ID for version skew detection (TIM-446).
|
|
135
|
+
// The manifest init module sets globalThis.__TIMBER_DEPLOYMENT_ID__ at startup.
|
|
136
|
+
// In dev mode this is undefined — skew checks are skipped.
|
|
137
|
+
const deploymentId = (globalThis as Record<string, unknown>).__TIMBER_DEPLOYMENT_ID__ as
|
|
138
|
+
| string
|
|
107
139
|
| undefined;
|
|
108
|
-
if (
|
|
109
|
-
const {
|
|
110
|
-
|
|
140
|
+
if (deploymentId) {
|
|
141
|
+
const { setDeploymentId } = await import('../version-skew.js');
|
|
142
|
+
setDeploymentId(deploymentId);
|
|
111
143
|
}
|
|
112
144
|
|
|
113
145
|
const matchRoute = createRouteMatcher(manifest);
|
|
@@ -164,17 +196,6 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
164
196
|
|
|
165
197
|
const typedBuildManifest = buildManifest as BuildManifest;
|
|
166
198
|
|
|
167
|
-
// Initialize response-level caching and singleflight deduplication.
|
|
168
|
-
// See design/31-benchmarking.md for performance motivation.
|
|
169
|
-
const responseCacheRaw = (runtimeConfig as Record<string, unknown>).responseCache as
|
|
170
|
-
| { maxSize?: number; ttlMs?: number; publicOnly?: boolean }
|
|
171
|
-
| false
|
|
172
|
-
| undefined;
|
|
173
|
-
const responseCacheConfig = resolveResponseCacheConfig(responseCacheRaw);
|
|
174
|
-
const responseCache: ResponseCache | null = responseCacheConfig
|
|
175
|
-
? createResponseCache(responseCacheConfig)
|
|
176
|
-
: null;
|
|
177
|
-
|
|
178
199
|
const pipelineConfig: PipelineConfig = {
|
|
179
200
|
proxyLoader: manifest.proxy?.load,
|
|
180
201
|
matchRoute,
|
|
@@ -205,17 +226,16 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
205
226
|
_requestHeaderOverlay: Headers,
|
|
206
227
|
interception?: InterceptionContext
|
|
207
228
|
) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return doRender();
|
|
229
|
+
return renderRoute(
|
|
230
|
+
req,
|
|
231
|
+
match,
|
|
232
|
+
responseHeaders,
|
|
233
|
+
clientBootstrap,
|
|
234
|
+
clientJsDisabled,
|
|
235
|
+
interception,
|
|
236
|
+
manifest.root,
|
|
237
|
+
manifest.globalError
|
|
238
|
+
);
|
|
219
239
|
},
|
|
220
240
|
renderNoMatch: async (req: Request, responseHeaders: Headers) => {
|
|
221
241
|
return renderNoMatchPage(req, manifest.root, responseHeaders, clientBootstrap);
|
|
@@ -224,14 +244,31 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
224
244
|
// Slow request threshold from timber.config.ts. Default 3000ms, 0 to disable.
|
|
225
245
|
// See design/17-logging.md §"slowRequestMs"
|
|
226
246
|
slowRequestMs: (runtimeConfig as Record<string, unknown>).slowRequestMs as number | undefined,
|
|
227
|
-
|
|
247
|
+
serverTiming: resolveServerTimingMode(runtimeConfig, isDev),
|
|
228
248
|
onPipelineError: isDev
|
|
229
249
|
? (error: Error, phase: string) => {
|
|
230
|
-
if (_devPipelineErrorHandler)
|
|
250
|
+
if (_devPipelineErrorHandler) {
|
|
251
|
+
// For render-phase errors, include RSC debug component data
|
|
252
|
+
// from the Flight debug channel (if available from the current request).
|
|
253
|
+
const store = requestContextAls.getStore();
|
|
254
|
+
const debugComponents =
|
|
255
|
+
phase === 'render' && store?.debugComponentsGetter
|
|
256
|
+
? store.debugComponentsGetter()
|
|
257
|
+
: undefined;
|
|
258
|
+
_devPipelineErrorHandler(error, phase, debugComponents);
|
|
259
|
+
}
|
|
231
260
|
}
|
|
232
261
|
: undefined,
|
|
233
262
|
renderFallbackError: (error, req, responseHeaders) =>
|
|
234
|
-
renderFallback(
|
|
263
|
+
renderFallback(
|
|
264
|
+
error,
|
|
265
|
+
req,
|
|
266
|
+
responseHeaders,
|
|
267
|
+
isDev,
|
|
268
|
+
manifest.root,
|
|
269
|
+
clientBootstrap,
|
|
270
|
+
manifest.globalError
|
|
271
|
+
),
|
|
235
272
|
};
|
|
236
273
|
|
|
237
274
|
const pipeline = createPipeline(pipelineConfig);
|
|
@@ -267,6 +304,9 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
267
304
|
if (!revalidateMatch) {
|
|
268
305
|
throw new Error(`revalidatePath('${path}') — no matching route`);
|
|
269
306
|
}
|
|
307
|
+
// Coerce segment params (params.ts) before building the element tree.
|
|
308
|
+
// Without this, components receive raw strings instead of typed values.
|
|
309
|
+
await coerceSegmentParams(revalidateMatch);
|
|
270
310
|
const routeResult = await buildRouteElement(revalidateReq, revalidateMatch);
|
|
271
311
|
return {
|
|
272
312
|
element: routeResult.element,
|
|
@@ -307,7 +347,9 @@ async function renderRoute(
|
|
|
307
347
|
responseHeaders: Headers,
|
|
308
348
|
clientBootstrap: ClientBootstrapConfig,
|
|
309
349
|
clientJsDisabled: boolean,
|
|
310
|
-
interception?: InterceptionContext
|
|
350
|
+
interception?: InterceptionContext,
|
|
351
|
+
rootSegment?: ManifestSegmentNode,
|
|
352
|
+
globalError?: { load: () => Promise<unknown>; filePath: string }
|
|
311
353
|
): Promise<Response> {
|
|
312
354
|
const segments = match.segments as unknown as ManifestSegmentNode[];
|
|
313
355
|
const leaf = segments[segments.length - 1];
|
|
@@ -329,6 +371,7 @@ async function renderRoute(
|
|
|
329
371
|
// Build the React element tree — loads modules, runs access checks,
|
|
330
372
|
// resolves metadata. DenySignal/RedirectSignal propagate for HTTP handling.
|
|
331
373
|
let routeResult;
|
|
374
|
+
const _buildStart = performance.now();
|
|
332
375
|
try {
|
|
333
376
|
routeResult = await buildRouteElement(_req, match, interception, clientStateTree);
|
|
334
377
|
} catch (error) {
|
|
@@ -361,14 +404,29 @@ async function renderRoute(
|
|
|
361
404
|
return buildRedirectResponse(_req, signal, responseHeaders);
|
|
362
405
|
}
|
|
363
406
|
}
|
|
364
|
-
//
|
|
407
|
+
// Param coercion failed — render the custom 404 page (status files / not-found).
|
|
408
|
+
// Previously returned a bare Response(null, { status: 404 }) which bypassed
|
|
409
|
+
// custom not-found pages. Now routes through renderNoMatchPage so apps with
|
|
410
|
+
// 404.tsx / not-found status files render their custom page.
|
|
411
|
+
if (error instanceof ParamCoercionError) {
|
|
412
|
+
return renderNoMatchPage(_req, rootSegment!, responseHeaders, clientBootstrap);
|
|
413
|
+
}
|
|
414
|
+
// No PageComponent found — same treatment as param coercion: render custom 404.
|
|
365
415
|
if (error instanceof Error && error.message.startsWith('No page component')) {
|
|
366
|
-
return
|
|
416
|
+
return renderNoMatchPage(_req, rootSegment!, responseHeaders, clientBootstrap);
|
|
367
417
|
}
|
|
368
418
|
throw error;
|
|
369
419
|
}
|
|
370
420
|
|
|
371
|
-
const
|
|
421
|
+
const _buildEnd = performance.now();
|
|
422
|
+
recordTiming({
|
|
423
|
+
name: 'build',
|
|
424
|
+
dur: Math.round(_buildEnd - _buildStart),
|
|
425
|
+
desc: 'build element tree',
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const { element, headElements, layoutComponents, deferSuspenseFor, skippedSegments } =
|
|
429
|
+
routeResult;
|
|
372
430
|
|
|
373
431
|
// Build head HTML for injection into the SSR output.
|
|
374
432
|
// Collects CSS, fonts, and modulepreload from the build manifest for matched segments.
|
|
@@ -380,9 +438,24 @@ async function renderRoute(
|
|
|
380
438
|
const typedManifest = buildManifest as BuildManifest;
|
|
381
439
|
let headHtml = '';
|
|
382
440
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
441
|
+
// CSS is handled by the RSC plugin via ReactDOM.preinit() with
|
|
442
|
+
// data-precedence attributes. This injects <link rel="stylesheet">
|
|
443
|
+
// tags during the RSC render phase — before our headHtml injection.
|
|
444
|
+
// We do NOT emit additional <link rel="stylesheet"> tags here because:
|
|
445
|
+
// 1. React's Float system deduplicates them, making ours redundant
|
|
446
|
+
// 2. The duplicate reference confuses React's client-side preload
|
|
447
|
+
// deduplication, causing "preload ignored" browser warnings
|
|
448
|
+
//
|
|
449
|
+
// CSS URLs are still collected for Early Hints (Link headers) in
|
|
450
|
+
// buildEarlyHintsHeaders() — those are HTTP headers, not DOM elements,
|
|
451
|
+
// so they don't conflict with Float.
|
|
452
|
+
|
|
453
|
+
// Inline font CSS as a <style> tag — @font-face rules and scoped classes.
|
|
454
|
+
// The font CSS is set on globalThis by the transformed font file's
|
|
455
|
+
// side-effect import of virtual:timber-font-css-register.
|
|
456
|
+
const fontCss = (globalThis as Record<string, unknown>).__timber_font_css as string | undefined;
|
|
457
|
+
if (fontCss) {
|
|
458
|
+
headHtml += `<style data-timber-fonts>${fontCss}</style>`;
|
|
386
459
|
}
|
|
387
460
|
|
|
388
461
|
const fontEntries = collectRouteFonts(segments, typedManifest);
|
|
@@ -410,8 +483,34 @@ async function renderRoute(
|
|
|
410
483
|
}
|
|
411
484
|
}
|
|
412
485
|
|
|
486
|
+
// In dev mode, inject the browser log forwarding script so console
|
|
487
|
+
// errors/warnings from the browser appear in the server terminal.
|
|
488
|
+
// Set by timber-dev-browser-logs plugin via globalThis (TIM-575).
|
|
489
|
+
if (isDevMode()) {
|
|
490
|
+
const devLogScript = (globalThis as Record<string, unknown>).__timber_dev_browser_log_script as
|
|
491
|
+
| string
|
|
492
|
+
| undefined;
|
|
493
|
+
if (devLogScript) {
|
|
494
|
+
headHtml += devLogScript;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
413
498
|
// Render to RSC Flight stream with signal tracking.
|
|
414
|
-
const
|
|
499
|
+
const _rscStart = performance.now();
|
|
500
|
+
const { rscStream, signals, getDebugComponents } = renderRscStream(element, _req);
|
|
501
|
+
|
|
502
|
+
// Store the debug components getter in ALS so onPipelineError can
|
|
503
|
+
// include component tree context for render-phase errors (dev mode only).
|
|
504
|
+
// Per-request via ALS — no cross-request race. See TIM-557.
|
|
505
|
+
const alsStore = requestContextAls.getStore();
|
|
506
|
+
if (alsStore) {
|
|
507
|
+
alsStore.debugComponentsGetter = getDebugComponents;
|
|
508
|
+
}
|
|
509
|
+
recordTiming({
|
|
510
|
+
name: 'rsc-init',
|
|
511
|
+
dur: Math.round(performance.now() - _rscStart),
|
|
512
|
+
desc: 'RSC stream init',
|
|
513
|
+
});
|
|
415
514
|
|
|
416
515
|
// Synchronous redirect — redirect() in access.ts or a non-async component
|
|
417
516
|
// throws during renderToReadableStream creation. Return HTTP redirect.
|
|
@@ -456,7 +555,8 @@ async function renderRoute(
|
|
|
456
555
|
_req,
|
|
457
556
|
match,
|
|
458
557
|
responseHeaders,
|
|
459
|
-
clientBootstrap
|
|
558
|
+
clientBootstrap,
|
|
559
|
+
globalError
|
|
460
560
|
);
|
|
461
561
|
}
|
|
462
562
|
|
|
@@ -490,15 +590,16 @@ async function renderRoute(
|
|
|
490
590
|
clientJsDisabled,
|
|
491
591
|
headHtml,
|
|
492
592
|
deferSuspenseFor,
|
|
593
|
+
globalError,
|
|
493
594
|
});
|
|
494
595
|
}
|
|
495
596
|
|
|
496
597
|
// Re-export for generated entry points (e.g., Nitro node-server/bun) to wrap
|
|
497
598
|
// the handler with per-request 103 Early Hints sender via ALS.
|
|
498
|
-
export { runWithEarlyHintsSender } from '
|
|
599
|
+
export { runWithEarlyHintsSender } from '../early-hints-sender.js';
|
|
499
600
|
|
|
500
601
|
// Re-export for generated entry points to wrap the handler with per-request
|
|
501
602
|
// waitUntil support via ALS. See design/11-platform.md §"waitUntil()".
|
|
502
|
-
export { runWithWaitUntil } from '
|
|
603
|
+
export { runWithWaitUntil } from '../waituntil-bridge.js';
|
|
503
604
|
|
|
504
605
|
export default await createRequestHandler(routeManifest, config);
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
* 16-metadata.md §"Head Elements"
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { LayoutEntry } from '
|
|
13
|
-
import { renderDenyPageAsRsc } from '
|
|
14
|
-
import type { RouteMatch } from '
|
|
15
|
-
import type { RedirectSignal } from '
|
|
16
|
-
import type { HeadElement, LayoutComponentEntry } from '
|
|
17
|
-
import type { ManifestSegmentNode } from '
|
|
12
|
+
import type { LayoutEntry } from '../deny-renderer.js';
|
|
13
|
+
import { renderDenyPageAsRsc } from '../deny-renderer.js';
|
|
14
|
+
import type { RouteMatch } from '../pipeline.js';
|
|
15
|
+
import type { RedirectSignal } from '../primitives.js';
|
|
16
|
+
import type { HeadElement, LayoutComponentEntry } from '../route-element-builder.js';
|
|
17
|
+
import type { ManifestSegmentNode } from '../route-matcher.js';
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
20
|
buildRedirectResponse,
|
|
@@ -45,18 +45,60 @@ export async function buildRscPayloadResponse(
|
|
|
45
45
|
skippedSegments?: string[]
|
|
46
46
|
): Promise<Response> {
|
|
47
47
|
// Read the first chunk from the RSC stream before committing headers.
|
|
48
|
+
// Race the first read against signal detection — if an async component
|
|
49
|
+
// throws a RedirectSignal or DenySignal, the onError callback fires
|
|
50
|
+
// signals.onSignal() and we can react immediately without waiting for
|
|
51
|
+
// the full macrotask queue.
|
|
52
|
+
//
|
|
53
|
+
// The rejection chain for an async-wrapped page component:
|
|
54
|
+
// 1. PageComponent throws RedirectSignal
|
|
55
|
+
// 2. withSpan catches and re-throws (microtask 1)
|
|
56
|
+
// 3. TracedPage promise rejects (microtask 2)
|
|
57
|
+
// 4. React Flight rejection handler → onError (microtask 3+)
|
|
58
|
+
//
|
|
59
|
+
// Promise.race reacts the instant onError fires, eliminating the
|
|
60
|
+
// per-request setTimeout(0) macrotask delay for the common case
|
|
61
|
+
// (no signal). A 50ms ceiling timeout guards against edge cases
|
|
62
|
+
// where onError never fires.
|
|
48
63
|
const reader = rscStream.getReader();
|
|
49
|
-
const
|
|
64
|
+
const signalDetected = new Promise<void>((resolve) => {
|
|
65
|
+
signals.onSignal = resolve;
|
|
66
|
+
});
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
type RaceResult =
|
|
69
|
+
| { type: 'data'; chunk: ReadableStreamReadResult<Uint8Array> }
|
|
70
|
+
| { type: 'signal' };
|
|
71
|
+
|
|
72
|
+
let first: RaceResult;
|
|
73
|
+
try {
|
|
74
|
+
first = await Promise.race([
|
|
75
|
+
reader.read().then((chunk) => ({ type: 'data' as const, chunk })),
|
|
76
|
+
signalDetected.then(() => ({ type: 'signal' as const })),
|
|
77
|
+
]);
|
|
78
|
+
} catch (_readError) {
|
|
79
|
+
// RSC stream failed on first read — unhandled error in the page component.
|
|
80
|
+
// Return an error response so the client can hard-navigate for the
|
|
81
|
+
// SSR-rendered error page. See design/10-error-handling.md.
|
|
82
|
+
reader.cancel().catch(() => {});
|
|
83
|
+
signals.onSignal = undefined;
|
|
84
|
+
responseHeaders.set('X-Timber-Error', '1');
|
|
85
|
+
responseHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
86
|
+
return new Response(JSON.stringify({ error: true, status: 500 }), {
|
|
87
|
+
status: 500,
|
|
88
|
+
headers: responseHeaders,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If data arrived first, still check signals — they may have fired
|
|
93
|
+
// concurrently. Also do a final ceiling timeout check for edge cases
|
|
94
|
+
// where the signal fires just after the first read resolves.
|
|
95
|
+
if (first.type === 'data' && !signals.redirectSignal && !signals.denySignal) {
|
|
96
|
+
// Brief yield to let any in-flight microtask rejections complete.
|
|
97
|
+
await new Promise<void>((r) => setTimeout(r, 0));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Detach the callback — no longer needed after this point.
|
|
101
|
+
signals.onSignal = undefined;
|
|
60
102
|
|
|
61
103
|
// Check for redirect/deny signals detected during initial rendering
|
|
62
104
|
const trackedRedirect = signals.redirectSignal as RedirectSignal | null;
|
|
@@ -75,6 +117,37 @@ export async function buildRscPayloadResponse(
|
|
|
75
117
|
);
|
|
76
118
|
}
|
|
77
119
|
|
|
120
|
+
// Check for unhandled errors (RenderError or plain Error).
|
|
121
|
+
// These aren't redirect/deny signals but the RSC stream will be broken.
|
|
122
|
+
// Return an error response so the client can hard-navigate for the
|
|
123
|
+
// SSR-rendered error page. See design/10-error-handling.md
|
|
124
|
+
// §"Error Page Rendering for Client Navigation".
|
|
125
|
+
if (signals.renderError) {
|
|
126
|
+
reader.cancel().catch(() => {});
|
|
127
|
+
responseHeaders.set('X-Timber-Error', '1');
|
|
128
|
+
responseHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
129
|
+
return new Response(
|
|
130
|
+
JSON.stringify({
|
|
131
|
+
error: true,
|
|
132
|
+
status: signals.renderError.status,
|
|
133
|
+
}),
|
|
134
|
+
{ status: signals.renderError.status, headers: responseHeaders }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Extract the first chunk from the race result.
|
|
139
|
+
// If the signal won the race but neither redirect nor deny was detected
|
|
140
|
+
// (edge case), cancel the reader immediately rather than issuing a bare
|
|
141
|
+
// read() that could hang forever if the RSC stream has stalled.
|
|
142
|
+
// See TIM-519.
|
|
143
|
+
let firstRead: ReadableStreamReadResult<Uint8Array>;
|
|
144
|
+
if (first.type === 'data') {
|
|
145
|
+
firstRead = first.chunk;
|
|
146
|
+
} else {
|
|
147
|
+
await reader.cancel();
|
|
148
|
+
firstRead = { done: true, value: undefined };
|
|
149
|
+
}
|
|
150
|
+
|
|
78
151
|
// Reconstruct the stream: prepend the buffered first chunk,
|
|
79
152
|
// then continue piping from the original reader.
|
|
80
153
|
const patchedStream = new ReadableStream<Uint8Array>({
|
|
@@ -123,8 +196,8 @@ export async function buildRscPayloadResponse(
|
|
|
123
196
|
responseHeaders.set('X-Timber-Skipped-Segments', JSON.stringify(skippedSegments));
|
|
124
197
|
}
|
|
125
198
|
|
|
126
|
-
// Send route params so the client can populate
|
|
127
|
-
// SPA navigation. Without this,
|
|
199
|
+
// Send route params so the client can populate useSegmentParams() after
|
|
200
|
+
// SPA navigation. Without this, useSegmentParams() returns {}.
|
|
128
201
|
if (Object.keys(match.params).length > 0) {
|
|
129
202
|
responseHeaders.set('X-Timber-Params', JSON.stringify(match.params));
|
|
130
203
|
}
|