@timber-js/app 0.2.0-alpha.8 → 0.2.0-alpha.80
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-C2plcNtG.js +64 -0
- package/dist/_chunks/stale-reload-C2plcNtG.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/_chunks/wrappers-_DTmImGt.js +63 -0
- package/dist/_chunks/wrappers-_DTmImGt.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 +6 -1
- 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 +2 -2
- 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 +210 -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 +12296 -11901
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +84 -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 +4 -474
- 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 +6 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +4 -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 +12 -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 +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 +6 -43
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +38 -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 +53 -23
- 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 +6 -1
- 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 +2 -2
- 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 +208 -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 +258 -354
- package/src/plugin-context.ts +200 -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/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 +29 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +21 -2
- 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 +26 -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/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 -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/pipeline.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { canonicalize } from './canonicalize.js';
|
|
15
15
|
import { runProxy, type ProxyExport } from './proxy.js';
|
|
16
|
-
import {
|
|
16
|
+
import { runMiddlewareChain, type MiddlewareFn } from './middleware-runner.js';
|
|
17
17
|
import { runWithTimingCollector, withTiming, getServerTimingHeader } from './server-timing.js';
|
|
18
18
|
import {
|
|
19
19
|
runWithRequestContext,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
setMutableCookieContext,
|
|
22
22
|
getSetCookieHeaders,
|
|
23
23
|
markResponseFlushed,
|
|
24
|
+
setSegmentParams,
|
|
24
25
|
} from './request-context.js';
|
|
25
26
|
import {
|
|
26
27
|
generateTraceId,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
replaceTraceId,
|
|
30
31
|
withSpan,
|
|
31
32
|
setSpanAttribute,
|
|
32
|
-
|
|
33
|
+
getTraceId,
|
|
33
34
|
} from './tracing.js';
|
|
34
35
|
import {
|
|
35
36
|
logRequestReceived,
|
|
@@ -42,10 +43,35 @@ import {
|
|
|
42
43
|
} from './logger.js';
|
|
43
44
|
import { callOnRequestError } from './instrumentation.js';
|
|
44
45
|
import { RedirectSignal, DenySignal } from './primitives.js';
|
|
46
|
+
import { ParamCoercionError } from './route-element-builder.js';
|
|
47
|
+
import { checkVersionSkew, applyReloadHeaders } from './version-skew.js';
|
|
45
48
|
import { serveStaticMetadataFile, serializeSitemap } from './pipeline-metadata.js';
|
|
49
|
+
import { loadModule } from './safe-load.js';
|
|
46
50
|
import { findInterceptionMatch } from './pipeline-interception.js';
|
|
47
51
|
import type { MiddlewareContext } from './types.js';
|
|
48
|
-
import type { SegmentNode } from '
|
|
52
|
+
import type { SegmentNode } from '../routing/types.js';
|
|
53
|
+
|
|
54
|
+
// ─── Prototype-Pollution-Safe Merge ────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/** Keys that must never be merged via Object.assign — they pollute Object.prototype. */
|
|
57
|
+
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Shallow merge that skips prototype-polluting keys.
|
|
61
|
+
*
|
|
62
|
+
* Used instead of Object.assign when the source object comes from
|
|
63
|
+
* user-authored codec output (segmentParams.parse), which could
|
|
64
|
+
* contain __proto__, constructor, or prototype keys.
|
|
65
|
+
*
|
|
66
|
+
* See TIM-655, design/13-security.md
|
|
67
|
+
*/
|
|
68
|
+
export function safeMerge(target: Record<string, unknown>, source: Record<string, unknown>): void {
|
|
69
|
+
for (const key of Object.keys(source)) {
|
|
70
|
+
if (!DANGEROUS_KEYS.has(key)) {
|
|
71
|
+
target[key] = source[key];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
49
75
|
|
|
50
76
|
// ─── Route Match Result ────────────────────────────────────────────────────
|
|
51
77
|
|
|
@@ -53,10 +79,10 @@ import type { SegmentNode } from '#/routing/types.js';
|
|
|
53
79
|
export interface RouteMatch {
|
|
54
80
|
/** The matched segment chain from root to leaf. */
|
|
55
81
|
segments: SegmentNode[];
|
|
56
|
-
/** Extracted
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
82
|
+
/** Extracted segment params (catch-all segments produce string[]). */
|
|
83
|
+
segmentParams: Record<string, string | string[]>;
|
|
84
|
+
/** Middleware chain from the segment tree, ordered root-to-leaf. */
|
|
85
|
+
middlewareChain: MiddlewareFn[];
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
/** Function that matches a canonical pathname to a route. */
|
|
@@ -115,14 +141,25 @@ export interface PipelineConfig {
|
|
|
115
141
|
* Generated at build time from intercepting route directories.
|
|
116
142
|
* See design/07-routing.md §"Intercepting Routes"
|
|
117
143
|
*/
|
|
118
|
-
interceptionRewrites?: import('
|
|
144
|
+
interceptionRewrites?: import('../routing/interception.js').InterceptionRewrite[];
|
|
145
|
+
/**
|
|
146
|
+
* Control Server-Timing header output.
|
|
147
|
+
*
|
|
148
|
+
* - `'detailed'` — per-phase breakdown (proxy, middleware, render).
|
|
149
|
+
* - `'total'` — single `total;dur=N` entry (production-safe).
|
|
150
|
+
* - `false` — no Server-Timing header at all.
|
|
151
|
+
*
|
|
152
|
+
* Default: `'total'`.
|
|
153
|
+
*/
|
|
154
|
+
serverTiming?: 'detailed' | 'total' | false;
|
|
119
155
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
156
|
+
* Auto-generated sitemap handler. When provided, the pipeline intercepts
|
|
157
|
+
* `/sitemap.xml` and `/sitemap/N.xml` requests and delegates to this
|
|
158
|
+
* function. Returns a Response or null (pass-through to regular routing).
|
|
122
159
|
*
|
|
123
|
-
*
|
|
160
|
+
* See design/16-metadata.md §"Auto-generated Sitemap"
|
|
124
161
|
*/
|
|
125
|
-
|
|
162
|
+
autoSitemapHandler?: (pathname: string) => Promise<Response | null>;
|
|
126
163
|
/**
|
|
127
164
|
* Dev pipeline error callback — called when a pipeline phase (proxy,
|
|
128
165
|
* middleware, render) catches an unhandled error. Used to wire the error
|
|
@@ -149,6 +186,51 @@ export interface PipelineConfig {
|
|
|
149
186
|
) => Response | Promise<Response>;
|
|
150
187
|
}
|
|
151
188
|
|
|
189
|
+
// ─── Param Coercion ────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Run segment param coercion on the matched route's segments.
|
|
193
|
+
*
|
|
194
|
+
* Loads params.ts modules from segments that have them, extracts the
|
|
195
|
+
* segmentParams definition, and coerces raw string params through codecs.
|
|
196
|
+
* Throws ParamCoercionError if any codec fails (→ 404).
|
|
197
|
+
*
|
|
198
|
+
* This runs BEFORE middleware, so ctx.segmentParams is already typed.
|
|
199
|
+
* See design/07-routing.md §"Where Coercion Runs"
|
|
200
|
+
*/
|
|
201
|
+
export async function coerceSegmentParams(match: RouteMatch): Promise<void> {
|
|
202
|
+
const segments = match.segments as unknown as import('./route-matcher.js').ManifestSegmentNode[];
|
|
203
|
+
|
|
204
|
+
for (const segment of segments) {
|
|
205
|
+
// Only process segments that have a params.ts convention file
|
|
206
|
+
if (!segment.params) continue;
|
|
207
|
+
|
|
208
|
+
let mod: Record<string, unknown>;
|
|
209
|
+
try {
|
|
210
|
+
mod = await loadModule(segment.params);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
throw new ParamCoercionError(
|
|
213
|
+
`Failed to load params module for segment "${segment.segmentName}": ${err instanceof Error ? err.message : String(err)}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const segmentParamsDef = mod.segmentParams as
|
|
218
|
+
| { parse(raw: Record<string, string | string[]>): Record<string, unknown> }
|
|
219
|
+
| undefined;
|
|
220
|
+
|
|
221
|
+
if (!segmentParamsDef || typeof segmentParamsDef.parse !== 'function') continue;
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const coerced = segmentParamsDef.parse(match.segmentParams);
|
|
225
|
+
// Merge coerced values back — use safeMerge to prevent prototype pollution
|
|
226
|
+
// from malicious/buggy codec output. See TIM-655.
|
|
227
|
+
safeMerge(match.segmentParams, coerced as Record<string, unknown>);
|
|
228
|
+
} catch (err) {
|
|
229
|
+
throw new ParamCoercionError(err instanceof Error ? err.message : String(err));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
152
234
|
// ─── Pipeline ──────────────────────────────────────────────────────────────
|
|
153
235
|
|
|
154
236
|
/**
|
|
@@ -165,7 +247,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
165
247
|
earlyHints,
|
|
166
248
|
stripTrailingSlash = true,
|
|
167
249
|
slowRequestMs = 3000,
|
|
168
|
-
|
|
250
|
+
serverTiming = 'total',
|
|
169
251
|
onPipelineError,
|
|
170
252
|
} = config;
|
|
171
253
|
|
|
@@ -186,7 +268,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
186
268
|
const traceIdValue = generateTraceId();
|
|
187
269
|
|
|
188
270
|
return runWithTraceId(traceIdValue, async () => {
|
|
189
|
-
// Establish request context ALS scope so
|
|
271
|
+
// Establish request context ALS scope so getHeaders() and getCookies() work
|
|
190
272
|
// throughout the entire request lifecycle (proxy, middleware, render).
|
|
191
273
|
return runWithRequestContext(req, async () => {
|
|
192
274
|
// In dev mode, wrap with timing collector for Server-Timing header.
|
|
@@ -216,25 +298,25 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
216
298
|
// DevSpanProcessor reads this for tree/summary output.
|
|
217
299
|
await setSpanAttribute('http.response.status_code', result.status);
|
|
218
300
|
|
|
219
|
-
// Append Server-Timing header.
|
|
220
|
-
// In dev mode: detailed per-phase breakdown (proxy, middleware, render).
|
|
221
|
-
// In production: single total duration — safe to expose, no phase names.
|
|
301
|
+
// Append Server-Timing header based on configured mode.
|
|
222
302
|
// Response.redirect() creates immutable headers, so we must
|
|
223
303
|
// ensure mutability before writing Server-Timing.
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
304
|
+
if (serverTiming === 'detailed') {
|
|
305
|
+
// Detailed: per-phase breakdown (proxy, middleware, render).
|
|
306
|
+
const timingHeader = getServerTimingHeader();
|
|
307
|
+
if (timingHeader) {
|
|
227
308
|
result = ensureMutableResponse(result);
|
|
228
|
-
result.headers.set('Server-Timing',
|
|
309
|
+
result.headers.set('Server-Timing', timingHeader);
|
|
229
310
|
}
|
|
230
|
-
} else {
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
311
|
+
} else if (serverTiming === 'total') {
|
|
312
|
+
// Total only: single `total;dur=N` — no phase names.
|
|
313
|
+
// Prevents information disclosure while giving browser
|
|
314
|
+
// DevTools useful timing data.
|
|
234
315
|
const totalMs = Math.round(performance.now() - startTime);
|
|
235
316
|
result = ensureMutableResponse(result);
|
|
236
317
|
result.headers.set('Server-Timing', `total;dur=${totalMs}`);
|
|
237
318
|
}
|
|
319
|
+
// serverTiming === false: no header at all
|
|
238
320
|
|
|
239
321
|
return result;
|
|
240
322
|
}
|
|
@@ -254,7 +336,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
254
336
|
return response;
|
|
255
337
|
};
|
|
256
338
|
|
|
257
|
-
return
|
|
339
|
+
return serverTiming === 'detailed' ? runWithTimingCollector(runRequest) : runRequest();
|
|
258
340
|
});
|
|
259
341
|
});
|
|
260
342
|
};
|
|
@@ -272,7 +354,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
272
354
|
}
|
|
273
355
|
const proxyFn = () => runProxy(proxyExport, req, () => handleRequest(req, method, path));
|
|
274
356
|
return await withSpan('timber.proxy', {}, () =>
|
|
275
|
-
|
|
357
|
+
serverTiming === 'detailed' ? withTiming('proxy', 'proxy.ts', proxyFn) : proxyFn()
|
|
276
358
|
);
|
|
277
359
|
} catch (error) {
|
|
278
360
|
// Uncaught proxy.ts error → bare HTTP 500
|
|
@@ -283,6 +365,24 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
283
365
|
}
|
|
284
366
|
}
|
|
285
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Build a redirect Response from a RedirectSignal.
|
|
370
|
+
*
|
|
371
|
+
* For RSC payload requests (client navigation), returns 204 + X-Timber-Redirect
|
|
372
|
+
* so the client router can perform a soft SPA redirect. A raw 302 would be
|
|
373
|
+
* turned into an opaque redirect by fetch({redirect:'manual'}), crashing
|
|
374
|
+
* createFromFetch. See design/19-client-navigation.md.
|
|
375
|
+
*/
|
|
376
|
+
function buildRedirectResponse(signal: RedirectSignal, req: Request, headers: Headers): Response {
|
|
377
|
+
const isRsc = (req.headers.get('Accept') ?? '').includes('text/x-component');
|
|
378
|
+
if (isRsc) {
|
|
379
|
+
headers.set('X-Timber-Redirect', signal.location);
|
|
380
|
+
return new Response(null, { status: 204, headers });
|
|
381
|
+
}
|
|
382
|
+
headers.set('Location', signal.location);
|
|
383
|
+
return new Response(null, { status: signal.status, headers });
|
|
384
|
+
}
|
|
385
|
+
|
|
286
386
|
async function handleRequest(req: Request, method: string, path: string): Promise<Response> {
|
|
287
387
|
// Stage 1: URL canonicalization
|
|
288
388
|
const url = new URL(req.url);
|
|
@@ -306,7 +406,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
306
406
|
return await serveStaticMetadataFile(metaMatch);
|
|
307
407
|
}
|
|
308
408
|
|
|
309
|
-
const mod =
|
|
409
|
+
const mod = await loadModule<{ default?: Function }>(metaMatch.file);
|
|
310
410
|
if (typeof mod.default !== 'function') {
|
|
311
411
|
return new Response('Metadata route must export a default function', { status: 500 });
|
|
312
412
|
}
|
|
@@ -339,6 +439,36 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
339
439
|
}
|
|
340
440
|
}
|
|
341
441
|
|
|
442
|
+
// Stage 1b.2: Auto-generated sitemap — serves /sitemap.xml and /sitemap/N.xml
|
|
443
|
+
// when sitemap generation is enabled and no user-authored sitemap exists.
|
|
444
|
+
// Runs after metadata route matching so user sitemaps always take precedence.
|
|
445
|
+
// See design/16-metadata.md §"Auto-generated Sitemap"
|
|
446
|
+
if (config.autoSitemapHandler) {
|
|
447
|
+
try {
|
|
448
|
+
const sitemapResponse = await config.autoSitemapHandler(canonicalPathname);
|
|
449
|
+
if (sitemapResponse) return sitemapResponse;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
logRenderError({ method, path, error });
|
|
452
|
+
if (onPipelineError && error instanceof Error) onPipelineError(error, 'auto-sitemap');
|
|
453
|
+
return new Response(null, { status: 500 });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Stage 1c: Version skew detection (TIM-446).
|
|
458
|
+
// For RSC payload requests (client navigation), check if the client's
|
|
459
|
+
// deployment ID matches the current build. On mismatch, signal the
|
|
460
|
+
// client to do a full page reload instead of returning an RSC payload
|
|
461
|
+
// that references mismatched module IDs.
|
|
462
|
+
const isRscRequest = (req.headers.get('Accept') ?? '').includes('text/x-component');
|
|
463
|
+
if (isRscRequest) {
|
|
464
|
+
const skewCheck = checkVersionSkew(req);
|
|
465
|
+
if (!skewCheck.ok) {
|
|
466
|
+
const reloadHeaders = new Headers();
|
|
467
|
+
applyReloadHeaders(reloadHeaders);
|
|
468
|
+
return new Response(null, { status: 204, headers: reloadHeaders });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
342
472
|
// Stage 2: Route matching
|
|
343
473
|
let match = matchRoute(canonicalPathname);
|
|
344
474
|
let interception: InterceptionContext | undefined;
|
|
@@ -397,18 +527,57 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
397
527
|
}
|
|
398
528
|
}
|
|
399
529
|
|
|
400
|
-
// Stage
|
|
401
|
-
|
|
530
|
+
// Stage 2c: Param coercion (before middleware)
|
|
531
|
+
// Load params.ts modules from matched segments and coerce raw string
|
|
532
|
+
// params through defineSegmentParams codecs. Coercion failure → 404
|
|
533
|
+
// (middleware never runs). See design/07-routing.md §"Where Coercion Runs"
|
|
534
|
+
try {
|
|
535
|
+
await coerceSegmentParams(match);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
if (error instanceof ParamCoercionError) {
|
|
538
|
+
// For API routes (route.ts), return a bare 404 — not an HTML page.
|
|
539
|
+
// API consumers expect JSON/empty responses, not rendered HTML.
|
|
540
|
+
const leafSegment = match.segments[match.segments.length - 1];
|
|
541
|
+
if (
|
|
542
|
+
(leafSegment as { route?: unknown }).route &&
|
|
543
|
+
!(leafSegment as { page?: unknown }).page
|
|
544
|
+
) {
|
|
545
|
+
return new Response(null, { status: 404 });
|
|
546
|
+
}
|
|
547
|
+
// Route through the app's 404 page (404.tsx in root layout) instead of
|
|
548
|
+
// returning a bare empty 404 Response. Falls back to bare 404 only if
|
|
549
|
+
// no renderNoMatch renderer is configured.
|
|
550
|
+
if (config.renderNoMatch) {
|
|
551
|
+
return config.renderNoMatch(req, responseHeaders);
|
|
552
|
+
}
|
|
553
|
+
return new Response(null, { status: 404 });
|
|
554
|
+
}
|
|
555
|
+
throw error;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Store coerced segment params in ALS so components can access them
|
|
559
|
+
// via getSegmentParams() instead of receiving them as a prop.
|
|
560
|
+
// See design/07-routing.md §"params.ts — Convention File for Typed Params"
|
|
561
|
+
setSegmentParams(match.segmentParams);
|
|
562
|
+
|
|
563
|
+
// Stage 3: Middleware chain (root-to-leaf, short-circuits on first Response)
|
|
564
|
+
if (match.middlewareChain.length > 0) {
|
|
402
565
|
const ctx: MiddlewareContext = {
|
|
403
566
|
req,
|
|
404
567
|
requestHeaders: requestHeaderOverlay,
|
|
405
568
|
headers: responseHeaders,
|
|
406
|
-
|
|
407
|
-
searchParams: new URL(req.url).searchParams,
|
|
569
|
+
segmentParams: match.segmentParams,
|
|
408
570
|
earlyHints: (hints) => {
|
|
409
571
|
for (const hint of hints) {
|
|
410
|
-
|
|
411
|
-
|
|
572
|
+
// Match Cloudflare's cached Early Hints attribute order: `as` before `rel`.
|
|
573
|
+
// Cloudflare caches Link headers and re-emits them on subsequent 200s.
|
|
574
|
+
// If our order differs, the browser sees duplicate preloads and warns.
|
|
575
|
+
let value: string;
|
|
576
|
+
if (hint.as !== undefined) {
|
|
577
|
+
value = `<${hint.href}>; as=${hint.as}; rel=${hint.rel}`;
|
|
578
|
+
} else {
|
|
579
|
+
value = `<${hint.href}>; rel=${hint.rel}`;
|
|
580
|
+
}
|
|
412
581
|
if (hint.crossOrigin !== undefined) value += `; crossorigin=${hint.crossOrigin}`;
|
|
413
582
|
if (hint.fetchPriority !== undefined) value += `; fetchpriority=${hint.fetchPriority}`;
|
|
414
583
|
responseHeaders.append('Link', value);
|
|
@@ -419,9 +588,9 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
419
588
|
try {
|
|
420
589
|
// Enable cookie mutation during middleware (design/29-cookies.md §"Context Tracking")
|
|
421
590
|
setMutableCookieContext(true);
|
|
422
|
-
const
|
|
591
|
+
const chainFn = () => runMiddlewareChain(match.middlewareChain, ctx);
|
|
423
592
|
const middlewareResponse = await withSpan('timber.middleware', {}, () =>
|
|
424
|
-
|
|
593
|
+
serverTiming === 'detailed' ? withTiming('mw', 'middleware.ts', chainFn) : chainFn()
|
|
425
594
|
);
|
|
426
595
|
setMutableCookieContext(false);
|
|
427
596
|
if (middlewareResponse) {
|
|
@@ -430,28 +599,30 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
430
599
|
// mutability before appending Set-Cookie entries.
|
|
431
600
|
const finalResponse = ensureMutableResponse(middlewareResponse);
|
|
432
601
|
applyCookieJar(finalResponse.headers);
|
|
602
|
+
// Merge parent-set responseHeaders onto the short-circuit response.
|
|
603
|
+
// Child-set headers take precedence — only add headers not already present.
|
|
604
|
+
// Snapshot existing keys first so multi-value headers (Set-Cookie, Link)
|
|
605
|
+
// from the parent are all appended when the child didn't set that key.
|
|
606
|
+
const existingKeys = new Set(
|
|
607
|
+
[...finalResponse.headers.keys()].map((k) => k.toLowerCase())
|
|
608
|
+
);
|
|
609
|
+
for (const [key, value] of responseHeaders.entries()) {
|
|
610
|
+
if (!existingKeys.has(key.toLowerCase())) {
|
|
611
|
+
finalResponse.headers.append(key, value);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
433
614
|
logMiddlewareShortCircuit({ method, path, status: finalResponse.status });
|
|
434
615
|
return finalResponse;
|
|
435
616
|
}
|
|
436
|
-
// Middleware
|
|
437
|
-
// injected request headers so
|
|
617
|
+
// Middleware chain completed without short-circuiting — apply any
|
|
618
|
+
// injected request headers so getHeaders() returns them downstream.
|
|
438
619
|
applyRequestHeaderOverlay(requestHeaderOverlay);
|
|
439
620
|
} catch (error) {
|
|
440
621
|
setMutableCookieContext(false);
|
|
441
|
-
// RedirectSignal from middleware → HTTP redirect (not an error)
|
|
442
|
-
// For RSC payload requests (client navigation), return 204 + X-Timber-Redirect
|
|
443
|
-
// so the client router can perform a soft SPA redirect. A raw 302 would be
|
|
444
|
-
// turned into an opaque redirect by fetch({redirect:'manual'}), crashing
|
|
445
|
-
// createFromFetch. See design/19-client-navigation.md.
|
|
622
|
+
// RedirectSignal from middleware → HTTP redirect (not an error)
|
|
446
623
|
if (error instanceof RedirectSignal) {
|
|
447
624
|
applyCookieJar(responseHeaders);
|
|
448
|
-
|
|
449
|
-
if (isRsc) {
|
|
450
|
-
responseHeaders.set('X-Timber-Redirect', error.location);
|
|
451
|
-
return new Response(null, { status: 204, headers: responseHeaders });
|
|
452
|
-
}
|
|
453
|
-
responseHeaders.set('Location', error.location);
|
|
454
|
-
return new Response(null, { status: error.status, headers: responseHeaders });
|
|
625
|
+
return buildRedirectResponse(error, req, responseHeaders);
|
|
455
626
|
}
|
|
456
627
|
// DenySignal from middleware → HTTP deny status
|
|
457
628
|
if (error instanceof DenySignal) {
|
|
@@ -476,7 +647,9 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
476
647
|
const renderFn = () =>
|
|
477
648
|
render(req, match, responseHeaders, requestHeaderOverlay, interception);
|
|
478
649
|
const response = await withSpan('timber.render', { 'http.route': canonicalPathname }, () =>
|
|
479
|
-
|
|
650
|
+
serverTiming === 'detailed'
|
|
651
|
+
? withTiming('render', 'RSC + SSR render', renderFn)
|
|
652
|
+
: renderFn()
|
|
480
653
|
);
|
|
481
654
|
markResponseFlushed();
|
|
482
655
|
return response;
|
|
@@ -486,17 +659,9 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
486
659
|
if (error instanceof DenySignal) {
|
|
487
660
|
return new Response(null, { status: error.status });
|
|
488
661
|
}
|
|
489
|
-
// RedirectSignal leaked from render — honour the redirect
|
|
490
|
-
// For RSC payload requests, return 204 + X-Timber-Redirect so the
|
|
491
|
-
// client router can perform a soft SPA redirect (same as middleware path).
|
|
662
|
+
// RedirectSignal leaked from render — honour the redirect
|
|
492
663
|
if (error instanceof RedirectSignal) {
|
|
493
|
-
|
|
494
|
-
if (isRsc) {
|
|
495
|
-
responseHeaders.set('X-Timber-Redirect', error.location);
|
|
496
|
-
return new Response(null, { status: 204, headers: responseHeaders });
|
|
497
|
-
}
|
|
498
|
-
responseHeaders.set('Location', error.location);
|
|
499
|
-
return new Response(null, { status: error.status, headers: responseHeaders });
|
|
664
|
+
return buildRedirectResponse(error, req, responseHeaders);
|
|
500
665
|
}
|
|
501
666
|
logRenderError({ method, path, error });
|
|
502
667
|
await fireOnRequestError(error, req, 'render');
|
|
@@ -532,7 +697,7 @@ async function fireOnRequestError(
|
|
|
532
697
|
await callOnRequestError(
|
|
533
698
|
error,
|
|
534
699
|
{ method: req.method, path: url.pathname, headers: headersObj },
|
|
535
|
-
{ phase, routePath: url.pathname, routeType: 'page', traceId:
|
|
700
|
+
{ phase, routePath: url.pathname, routeType: 'page', traceId: getTraceId() }
|
|
536
701
|
);
|
|
537
702
|
}
|
|
538
703
|
|
package/src/server/primitives.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import type { JsonSerializable } from './types.js';
|
|
7
7
|
import { getWaitUntil as _getWaitUntil } from './waituntil-bridge.js';
|
|
8
8
|
import { isDebug } from './debug.js';
|
|
9
|
+
import { getRequestSearchString } from './request-context.js';
|
|
10
|
+
import { mergePreservedSearchParams } from '../shared/merge-search-params.js';
|
|
9
11
|
|
|
10
12
|
// ─── Dev-mode validation ────────────────────────────────────────────────────
|
|
11
13
|
|
|
@@ -143,37 +145,65 @@ export class DenySignal extends Error {
|
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/** Options for deny() when using the object form. */
|
|
149
|
+
export interface DenyOptions {
|
|
150
|
+
/** HTTP status code (4xx or 5xx). Default: 403. */
|
|
151
|
+
status?: number;
|
|
152
|
+
/** Human-readable message (logged server-side, not sent to client). */
|
|
153
|
+
message?: string;
|
|
154
|
+
/** JSON-serializable data passed as `dangerouslyPassData` prop to status-code files. */
|
|
155
|
+
data?: JsonSerializable;
|
|
156
|
+
}
|
|
157
|
+
|
|
146
158
|
/**
|
|
147
|
-
* Universal denial primitive. Throws a `DenySignal` that the framework catches.
|
|
159
|
+
* Universal denial/error primitive. Throws a `DenySignal` that the framework catches.
|
|
148
160
|
*
|
|
149
161
|
* - In segment context (outside Suspense): produces HTTP status code
|
|
150
162
|
* - In slot context: graceful degradation → denied.tsx → default.tsx → null
|
|
151
163
|
* - Inside Suspense (hold window): promoted to pre-flush behavior
|
|
152
164
|
* - Inside Suspense (after flush): error boundary + noindex meta
|
|
153
165
|
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
166
|
+
* Supports both positional and object signatures:
|
|
167
|
+
* ```ts
|
|
168
|
+
* deny() // 403 (default)
|
|
169
|
+
* deny(404) // 404
|
|
170
|
+
* deny(503, { retry: true }) // 503 with data
|
|
171
|
+
* deny({ status: 503, message: 'Maintenance' }) // object form
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* Accepts any 4xx or 5xx status code. This replaces the need for
|
|
175
|
+
* `throw new RenderError(...)` in user code — RenderError is now an
|
|
176
|
+
* internal pipeline detail.
|
|
177
|
+
*
|
|
178
|
+
* @param statusOrOptions - Status code (number) or options object. Default: 403.
|
|
179
|
+
* @param data - Optional JSON-serializable data (positional form only).
|
|
156
180
|
*/
|
|
157
|
-
export function deny(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
181
|
+
export function deny(statusOrOptions?: number | DenyOptions, data?: JsonSerializable): never {
|
|
182
|
+
let status: number;
|
|
183
|
+
let resolvedData: JsonSerializable | undefined;
|
|
184
|
+
|
|
185
|
+
if (typeof statusOrOptions === 'object' && statusOrOptions !== null) {
|
|
186
|
+
status = statusOrOptions.status ?? 403;
|
|
187
|
+
resolvedData = statusOrOptions.data;
|
|
188
|
+
} else {
|
|
189
|
+
status = statusOrOptions ?? 403;
|
|
190
|
+
resolvedData = data;
|
|
163
191
|
}
|
|
164
|
-
|
|
165
|
-
|
|
192
|
+
|
|
193
|
+
if (status < 400 || status > 599) {
|
|
194
|
+
throw new Error(`deny() requires a 4xx or 5xx status code, got ${status}.`);
|
|
195
|
+
}
|
|
196
|
+
warnIfNotSerializable(resolvedData, 'deny()');
|
|
197
|
+
throw new DenySignal(status, resolvedData);
|
|
166
198
|
}
|
|
167
199
|
|
|
168
200
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* call `notFound()` from `next/navigation` get the same behavior as
|
|
173
|
-
* `deny(404)` in timber.
|
|
201
|
+
* @deprecated Use `deny(404)` instead.
|
|
202
|
+
* Kept for internal use by the Next.js shim layer.
|
|
203
|
+
* @internal
|
|
174
204
|
*/
|
|
175
205
|
export function notFound(): never {
|
|
176
|
-
|
|
206
|
+
deny(404);
|
|
177
207
|
}
|
|
178
208
|
|
|
179
209
|
/**
|
|
@@ -209,14 +239,63 @@ export class RedirectSignal extends Error {
|
|
|
209
239
|
/** Pattern matching absolute URLs: http(s):// or protocol-relative // */
|
|
210
240
|
const ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/)/;
|
|
211
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Options for redirect() — alternative to passing a bare status code.
|
|
244
|
+
*/
|
|
245
|
+
export interface RedirectOptions {
|
|
246
|
+
/** HTTP redirect status code (3xx). Defaults to 302 (or 308 when `permanent: true`). */
|
|
247
|
+
status?: number;
|
|
248
|
+
/**
|
|
249
|
+
* When true, defaults the status to 308 (Permanent Redirect, preserves HTTP method).
|
|
250
|
+
* If `status` is also provided, `status` takes precedence.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* redirect('/new-path', { permanent: true }); // 308
|
|
254
|
+
* redirect('/new-path', { permanent: true, status: 301 }); // 301
|
|
255
|
+
*/
|
|
256
|
+
permanent?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Preserve search params from the current request URL on the redirect target.
|
|
259
|
+
*
|
|
260
|
+
* - `true` — preserve ALL current search params (target params take precedence)
|
|
261
|
+
* - `string[]` — preserve only the named params (e.g. `['private', 'token']`)
|
|
262
|
+
*
|
|
263
|
+
* Target path's own query params always take precedence over preserved ones.
|
|
264
|
+
*/
|
|
265
|
+
preserveSearchParams?: true | string[];
|
|
266
|
+
}
|
|
267
|
+
|
|
212
268
|
/**
|
|
213
269
|
* Redirect to a relative path. Rejects absolute and protocol-relative URLs.
|
|
214
270
|
* Use `redirectExternal()` for external redirects with an allow-list.
|
|
215
271
|
*
|
|
216
272
|
* @param path - Relative path (e.g. '/login', 'settings', '/login?returnTo=/dash')
|
|
217
|
-
* @param
|
|
273
|
+
* @param statusOrOptions - HTTP status code (3xx, default 302) or options object.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* // Simple redirect
|
|
277
|
+
* redirect('/login');
|
|
278
|
+
*
|
|
279
|
+
* // With status code
|
|
280
|
+
* redirect('/login', 301);
|
|
281
|
+
*
|
|
282
|
+
* // With preserved search params
|
|
283
|
+
* redirect(`/docs/${version}/${slug}`, { preserveSearchParams: ['foo'] });
|
|
218
284
|
*/
|
|
219
|
-
export function redirect(path: string,
|
|
285
|
+
export function redirect(path: string, statusOrOptions?: number | RedirectOptions): never {
|
|
286
|
+
let status: number;
|
|
287
|
+
let preserveSearchParams: true | string[] | undefined;
|
|
288
|
+
|
|
289
|
+
if (typeof statusOrOptions === 'number') {
|
|
290
|
+
status = statusOrOptions;
|
|
291
|
+
} else if (statusOrOptions) {
|
|
292
|
+
// Explicit status wins. Otherwise permanent: true → 308, default → 302.
|
|
293
|
+
status = statusOrOptions.status ?? (statusOrOptions.permanent ? 308 : 302);
|
|
294
|
+
preserveSearchParams = statusOrOptions.preserveSearchParams;
|
|
295
|
+
} else {
|
|
296
|
+
status = 302;
|
|
297
|
+
}
|
|
298
|
+
|
|
220
299
|
if (status < 300 || status > 399) {
|
|
221
300
|
throw new Error(`redirect() requires a 3xx status code, got ${status}.`);
|
|
222
301
|
}
|
|
@@ -226,19 +305,23 @@ export function redirect(path: string, status: number = 302): never {
|
|
|
226
305
|
'Use redirectExternal(url, allowList) for external redirects.'
|
|
227
306
|
);
|
|
228
307
|
}
|
|
229
|
-
|
|
308
|
+
|
|
309
|
+
let resolvedPath = path;
|
|
310
|
+
if (preserveSearchParams) {
|
|
311
|
+
const currentSearch = getRequestSearchString();
|
|
312
|
+
resolvedPath = mergePreservedSearchParams(path, currentSearch, preserveSearchParams);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
throw new RedirectSignal(resolvedPath, status);
|
|
230
316
|
}
|
|
231
317
|
|
|
232
318
|
/**
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* will replay POST requests to the new location. This matches Next.js behavior.
|
|
237
|
-
*
|
|
238
|
-
* @param path - Relative path (e.g. '/new-page', '/dashboard')
|
|
319
|
+
* @deprecated Use `redirect(path, { permanent: true })` instead.
|
|
320
|
+
* Kept for internal use by the Next.js shim layer.
|
|
321
|
+
* @internal
|
|
239
322
|
*/
|
|
240
|
-
export function permanentRedirect(path: string): never {
|
|
241
|
-
redirect(path,
|
|
323
|
+
export function permanentRedirect(path: string, options?: Omit<RedirectOptions, 'status'>): never {
|
|
324
|
+
redirect(path, { permanent: true, ...options });
|
|
242
325
|
}
|
|
243
326
|
|
|
244
327
|
/**
|