@timber-js/app 0.2.0-alpha.12 → 0.2.0-alpha.121
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/dist/_chunks/actions-Dy8vOjFt.js +1101 -0
- package/dist/_chunks/actions-Dy8vOjFt.js.map +1 -0
- package/dist/_chunks/build-output-helper-B1n0sUqj.js +61 -0
- package/dist/_chunks/build-output-helper-B1n0sUqj.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-ByZPGGmS.js +724 -0
- package/dist/_chunks/cli-schema-sync-ByZPGGmS.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-DHQLjFu3.js +2 -0
- package/dist/_chunks/codec-registry-G9hD5LOq.js +21 -0
- package/dist/_chunks/codec-registry-G9hD5LOq.js.map +1 -0
- package/dist/_chunks/define-CPVQaWbx.js +124 -0
- package/dist/_chunks/define-CPVQaWbx.js.map +1 -0
- package/dist/_chunks/define-jeHDXZGq.js +195 -0
- package/dist/_chunks/define-jeHDXZGq.js.map +1 -0
- package/dist/_chunks/format-BjN9HLu9.js +14 -0
- package/dist/_chunks/format-BjN9HLu9.js.map +1 -0
- package/dist/_chunks/handler-store-CcwNX-gk.js +54 -0
- package/dist/_chunks/handler-store-CcwNX-gk.js.map +1 -0
- package/dist/_chunks/logger-DDmK5skc.js +291 -0
- package/dist/_chunks/logger-DDmK5skc.js.map +1 -0
- package/dist/_chunks/merge-search-params-az6ZiBeU.js +41 -0
- package/dist/_chunks/merge-search-params-az6ZiBeU.js.map +1 -0
- package/dist/_chunks/navigation-root-DoRTeBm5.js +96 -0
- package/dist/_chunks/navigation-root-DoRTeBm5.js.map +1 -0
- package/dist/_chunks/registry-BI9cgCt7.js +20 -0
- package/dist/_chunks/registry-BI9cgCt7.js.map +1 -0
- package/dist/_chunks/resolve-schema-Bf8eC6q6.js +73 -0
- package/dist/_chunks/resolve-schema-Bf8eC6q6.js.map +1 -0
- package/dist/_chunks/router-ref-CpvjBqdu.js +28 -0
- package/dist/_chunks/router-ref-CpvjBqdu.js.map +1 -0
- package/dist/_chunks/schema-bridge-o0SwFtXW.js +86 -0
- package/dist/_chunks/schema-bridge-o0SwFtXW.js.map +1 -0
- package/dist/_chunks/segment-classify-CnfLLvir.js +137 -0
- package/dist/_chunks/segment-classify-CnfLLvir.js.map +1 -0
- package/dist/_chunks/segment-context-EsfQpS--.js +34 -0
- package/dist/_chunks/segment-context-EsfQpS--.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-m9-cNEIw.js} +1 -1
- package/dist/_chunks/ssr-data-m9-cNEIw.js.map +1 -0
- package/dist/_chunks/stale-reload-BN2kcE06.js +64 -0
- package/dist/_chunks/stale-reload-BN2kcE06.js.map +1 -0
- package/dist/_chunks/tracing-DvKLzCFa.js +329 -0
- package/dist/_chunks/tracing-DvKLzCFa.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-C9G6LP0F.js} +7 -20
- package/dist/_chunks/use-query-states-C9G6LP0F.js.map +1 -0
- package/dist/_chunks/use-segment-params-CtIAjqK0.js +185 -0
- package/dist/_chunks/use-segment-params-CtIAjqK0.js.map +1 -0
- package/dist/_chunks/validation-BMU5MPzP.js +84 -0
- package/dist/_chunks/validation-BMU5MPzP.js.map +1 -0
- package/dist/_chunks/walkers-DNp9tHyV.js +719 -0
- package/dist/_chunks/walkers-DNp9tHyV.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → warnings-B30wpfaS.js} +6 -45
- package/dist/_chunks/warnings-B30wpfaS.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +28 -0
- package/dist/adapters/build-output-helper.d.ts.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 +142 -38
- 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 +143 -45
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +16 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- 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 +151 -81
- 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-schema-sync.d.ts +37 -0
- package/dist/cli-schema-sync.d.ts.map +1 -0
- package/dist/cli.d.ts +14 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +42 -6
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +34 -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 +29 -6
- 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 +16 -27
- 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 +9 -24
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +172 -1100
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +19 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +964 -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 +62 -55
- 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/state.d.ts +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-cookie.d.ts +12 -0
- package/dist/client/use-cookie.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-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/client/{use-params.d.ts → use-segment-params.d.ts} +8 -6
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +69 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +75 -0
- package/dist/codec.js.map +1 -0
- package/dist/config-types.d.ts +294 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-validation.d.ts +51 -0
- package/dist/config-validation.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 +58 -25
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -1
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +251 -36
- package/dist/cookies/index.js.map +1 -1
- package/dist/cookies/json-cookie.d.ts +64 -0
- package/dist/cookies/json-cookie.d.ts.map +1 -0
- package/dist/cookies/validation.d.ts +46 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/dev-tools/404-page.d.ts +46 -0
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/dev-tools/browser-logs.d.ts +84 -0
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/dev-tools/error-page.d.ts +58 -0
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/dev-tools/holding-server.d.ts +54 -0
- package/dist/dev-tools/holding-server.d.ts.map +1 -0
- package/dist/dev-tools/index.d.ts +31 -0
- package/dist/dev-tools/index.d.ts.map +1 -0
- package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
- package/dist/dev-tools/instrumentation.d.ts.map +1 -0
- package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -0
- package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/dev-tools/overlay.d.ts +91 -0
- package/dist/dev-tools/overlay.d.ts.map +1 -0
- package/dist/dev-tools/stack-classifier.d.ts +34 -0
- package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
- package/dist/dev-tools/terminal.d.ts +28 -0
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +2 -22
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/fonts/ast.d.ts +98 -6
- package/dist/fonts/ast.d.ts.map +1 -1
- package/dist/fonts/bundle.d.ts +48 -0
- package/dist/fonts/bundle.d.ts.map +1 -0
- package/dist/fonts/dev-middleware.d.ts +22 -0
- package/dist/fonts/dev-middleware.d.ts.map +1 -0
- package/dist/fonts/local.d.ts +1 -1
- package/dist/fonts/pipeline.d.ts +167 -0
- package/dist/fonts/pipeline.d.ts.map +1 -0
- package/dist/fonts/transform.d.ts +78 -0
- package/dist/fonts/transform.d.ts.map +1 -0
- package/dist/fonts/types.d.ts +45 -1
- package/dist/fonts/types.d.ts.map +1 -1
- package/dist/fonts/virtual-modules.d.ts +94 -0
- package/dist/fonts/virtual-modules.d.ts.map +1 -0
- package/dist/index.d.ts +46 -190
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4807 -12729
- package/dist/index.js.map +1 -1
- package/dist/params/codec-registry.d.ts +26 -0
- package/dist/params/codec-registry.d.ts.map +1 -0
- package/dist/params/define-schema.d.ts +81 -0
- package/dist/params/define-schema.d.ts.map +1 -0
- package/dist/params/index.d.ts +5 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +34 -0
- package/dist/params/index.js.map +1 -0
- package/dist/params/resolve-schema.d.ts +34 -0
- package/dist/params/resolve-schema.d.ts.map +1 -0
- package/dist/plugin-context.d.ts +107 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +9 -7
- 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-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 +17 -73
- 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 +5 -26
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +63 -0
- package/dist/routing/codegen-shared.d.ts.map +1 -0
- package/dist/routing/codegen-types.d.ts +60 -0
- package/dist/routing/codegen-types.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts +7 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts +41 -0
- package/dist/routing/convention-lint.d.ts.map +1 -0
- package/dist/routing/index.d.ts +7 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +4 -2
- package/dist/routing/link-codegen.d.ts +90 -0
- package/dist/routing/link-codegen.d.ts.map +1 -0
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/schema-validation.d.ts +35 -0
- package/dist/routing/schema-validation.d.ts.map +1 -0
- package/dist/routing/segment-classify.d.ts +75 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/segment-codegen.d.ts +38 -0
- package/dist/routing/segment-codegen.d.ts.map +1 -0
- package/dist/routing/types.d.ts +77 -25
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- 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 +157 -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 +34 -441
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +95 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +8 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +41 -6
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts +35 -1
- 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 +53 -5
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +1 -15
- package/dist/server/asset-headers.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/coalesce-tick.d.ts +40 -0
- package/dist/server/coalesce-tick.d.ts.map +1 -0
- package/dist/server/cookie-context.d.ts +170 -0
- package/dist/server/cookie-context.d.ts.map +1 -0
- package/dist/server/cookie-parsing.d.ts +51 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- 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-boundary.d.ts +90 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-source-map.d.ts +22 -0
- package/dist/server/dev-source-map.d.ts.map +1 -0
- package/dist/server/early-hints-sender.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 +12 -7
- 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-injector-core.d.ts +213 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +81 -41
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +10 -46
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +155 -2905
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +48 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2947 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +39 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +36 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +112 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +54 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +95 -0
- package/dist/server/pipeline-helpers.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-outcome.d.ts +49 -0
- package/dist/server/pipeline-outcome.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +52 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +118 -23
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- 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 +62 -107
- 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 +21 -34
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
- 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 +49 -6
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +19 -8
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +50 -0
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
- 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 +17 -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/rsc-entry/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sensitive-fields.d.ts +74 -0
- package/dist/server/sensitive-fields.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 +30 -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/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +16 -11
- 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 +5 -5
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +62 -30
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/tree-match.d.ts +41 -0
- package/dist/server/tree-match.d.ts.map +1 -0
- package/dist/server/types.d.ts +49 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/escape-html.d.ts +14 -0
- package/dist/server/utils/escape-html.d.ts.map +1 -0
- 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 +3 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +11 -5
- 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 +62 -21
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +350 -76
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +142 -59
- package/src/adapters/shared.ts +40 -0
- 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 +33 -0
- package/src/cache/timber-cache.ts +25 -20
- package/src/cli-schema-sync.ts +207 -0
- package/src/cli.ts +65 -8
- package/src/client/browser-dev.ts +153 -1
- package/src/client/browser-entry/action-dispatch.ts +126 -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 +149 -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 +99 -22
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +31 -32
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +69 -44
- package/src/client/internal.ts +58 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +366 -113
- 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 +300 -73
- package/src/client/rsc-fetch.ts +112 -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/state.ts +2 -2
- package/src/client/top-loader.tsx +18 -6
- package/src/client/use-cookie.ts +31 -2
- package/src/client/use-link-status.ts +7 -7
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +9 -3
- package/src/client/use-router.ts +1 -1
- package/src/client/{use-params.ts → use-segment-params.ts} +8 -6
- package/src/codec.ts +147 -0
- package/src/config-types.ts +292 -0
- package/src/config-validation.ts +303 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +233 -53
- package/src/cookies/index.ts +12 -1
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/dev-tools/404-page.ts +387 -0
- package/src/dev-tools/browser-logs.ts +291 -0
- package/src/dev-tools/error-page.ts +506 -0
- package/src/dev-tools/holding-server.ts +187 -0
- package/src/dev-tools/index.ts +90 -0
- package/src/dev-tools/instrumentation.ts +176 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +9 -5
- package/src/dev-tools/overlay.ts +469 -0
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/dev-tools/terminal.ts +183 -0
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +5 -50
- package/src/fonts/ast.ts +525 -16
- package/src/fonts/bundle.ts +142 -0
- package/src/fonts/dev-middleware.ts +74 -0
- package/src/fonts/local.ts +1 -1
- package/src/fonts/pipeline.ts +314 -0
- package/src/fonts/transform.ts +317 -0
- package/src/fonts/types.ts +50 -1
- package/src/fonts/virtual-modules.ts +233 -0
- package/src/index.ts +409 -357
- package/src/params/codec-registry.ts +34 -0
- package/src/params/define-schema.ts +114 -0
- package/src/params/index.ts +17 -0
- package/src/params/resolve-schema.ts +101 -0
- package/src/plugin-context.ts +240 -0
- package/src/plugins/adapter-build.ts +12 -4
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +16 -25
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-server.ts +175 -35
- package/src/plugins/entries.ts +111 -9
- package/src/plugins/fonts.ts +157 -654
- package/src/plugins/mdx.ts +47 -10
- package/src/plugins/routing.ts +371 -45
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +136 -35
- package/src/plugins/static-build.ts +15 -89
- package/src/routing/codegen-shared.ts +109 -0
- package/src/routing/codegen-types.ts +61 -0
- package/src/routing/codegen.ts +316 -177
- package/src/routing/convention-lint.ts +357 -0
- package/src/routing/index.ts +10 -1
- package/src/routing/interception.ts +1 -1
- package/src/routing/link-codegen.ts +308 -0
- package/src/routing/scanner.ts +165 -110
- package/src/routing/schema-validation.ts +149 -0
- package/src/routing/segment-classify.ts +188 -0
- package/src/routing/segment-codegen.ts +98 -0
- package/src/routing/types.ts +78 -25
- package/src/routing/walkers.ts +90 -0
- 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 +538 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +92 -0
- package/src/segment-params/define.ts +336 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +76 -34
- package/src/server/action-client.ts +88 -15
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +112 -14
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +55 -7
- package/src/server/asset-headers.ts +8 -34
- package/src/server/build-manifest.ts +10 -4
- package/src/server/coalesce-tick.ts +75 -0
- package/src/server/compress.ts +25 -7
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-boundary.ts +218 -0
- package/src/server/deny-renderer.ts +31 -50
- package/src/server/dev-source-map.ts +31 -0
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +74 -102
- 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-injector-core.ts +417 -0
- package/src/server/html-injectors.ts +235 -223
- package/src/server/index.ts +29 -183
- package/src/server/internal.ts +176 -0
- package/src/server/logger.ts +67 -36
- package/src/server/middleware-runner.ts +75 -4
- package/src/server/node-stream-transforms.ts +361 -0
- package/src/server/param-coercion.ts +206 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline-outcome.ts +176 -0
- package/src/server/pipeline-phases.ts +460 -0
- package/src/server/pipeline.ts +154 -394
- package/src/server/port-resolution.ts +215 -0
- package/src/server/primitives.ts +111 -28
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +168 -333
- package/src/server/route-element-builder.ts +309 -195
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +50 -231
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +16 -17
- package/src/server/rsc-entry/error-renderer.ts +305 -89
- package/src/server/rsc-entry/helpers.ts +137 -12
- package/src/server/rsc-entry/index.ts +316 -352
- package/src/server/rsc-entry/render-route.ts +307 -0
- package/src/server/rsc-entry/rsc-payload.ts +65 -18
- package/src/server/rsc-entry/rsc-stream.ts +85 -13
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +187 -38
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/safe-load.ts +60 -0
- package/src/server/sensitive-fields.ts +230 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +228 -246
- package/src/server/ssr-entry.ts +229 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +21 -10
- package/src/server/tree-builder.ts +202 -90
- package/src/server/tree-match.ts +198 -0
- package/src/server/types.ts +55 -6
- package/src/server/utils/escape-html.ts +20 -0
- 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 +4 -3
- package/src/shims/navigation.ts +14 -7
- package/src/utils/state-machine.ts +111 -0
- package/LICENSE +0 -8
- package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js +0 -108
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js +0 -676
- 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/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js +0 -187
- 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/client/use-params.d.ts.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/dev-error-overlay.d.ts +0 -60
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-logs.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/plugins/server-action-exports.d.ts +0 -26
- package/dist/plugins/server-action-exports.d.ts.map +0 -1
- package/dist/routing/status-file-lint.d.ts +0 -33
- package/dist/routing/status-file-lint.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
- package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -54
- package/dist/server/response-cache.d.ts.map +0 -1
- package/dist/utils/directive-parser.d.ts +0 -70
- package/dist/utils/directive-parser.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/dev-error-overlay.ts +0 -230
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/plugins/server-action-exports.ts +0 -220
- package/src/routing/status-file-lint.ts +0 -112
- 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/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -410
- package/src/utils/directive-parser.ts +0 -529
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Middleware runner — executes a route's middleware.ts before rendering.
|
|
2
|
+
* Middleware runner — executes a route's middleware.ts chain before rendering.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* All middleware.ts files in the segment chain run, root to leaf (top-down).
|
|
5
|
+
* The first middleware that returns a Response short-circuits the chain.
|
|
6
|
+
* There is no next() — each middleware is independent.
|
|
7
7
|
*
|
|
8
8
|
* See design/07-routing.md §"middleware.ts"
|
|
9
9
|
*/
|
|
@@ -30,3 +30,74 @@ export async function runMiddleware(
|
|
|
30
30
|
}
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Run all middleware functions in the segment chain, root to leaf.
|
|
36
|
+
*
|
|
37
|
+
* Execution is top-down: root middleware runs first, leaf middleware runs last.
|
|
38
|
+
* All middleware share the same MiddlewareContext — a parent that sets
|
|
39
|
+
* ctx.requestHeaders makes it visible to child middleware and downstream components.
|
|
40
|
+
*
|
|
41
|
+
* Short-circuits on the first middleware that returns a Response.
|
|
42
|
+
* Remaining middleware in the chain do not execute.
|
|
43
|
+
*
|
|
44
|
+
* @param chain - Middleware functions ordered root-to-leaf
|
|
45
|
+
* @param ctx - Shared middleware context
|
|
46
|
+
* @returns A Response if any middleware short-circuited, or undefined to continue
|
|
47
|
+
*/
|
|
48
|
+
export async function runMiddlewareChain(
|
|
49
|
+
chain: MiddlewareFn[],
|
|
50
|
+
ctx: MiddlewareContext
|
|
51
|
+
): Promise<Response | undefined> {
|
|
52
|
+
for (const fn of chain) {
|
|
53
|
+
const result = await fn(ctx);
|
|
54
|
+
if (result instanceof Response) {
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Per-Request Middleware Bypass ─────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Per-request marker for synthetic re-render requests that should NOT
|
|
65
|
+
* re-execute `middleware.ts`. The action-dispatch wrapper runs middleware
|
|
66
|
+
* once on the inbound action POST; when validation fails on the no-JS
|
|
67
|
+
* path, it builds a synthetic GET that flows through the normal pipeline
|
|
68
|
+
* to render the page with `getFormFlash()` data. Without this marker, the
|
|
69
|
+
* pipeline would run middleware a second time on that synthetic GET.
|
|
70
|
+
*
|
|
71
|
+
* The set is keyed by the synthetic Request object itself, so the entry
|
|
72
|
+
* lives exactly as long as the request and is garbage-collected with it.
|
|
73
|
+
* Cannot be set or detected by user code — there is no header, no URL
|
|
74
|
+
* parameter, nothing on the wire that an attacker could spoof.
|
|
75
|
+
*
|
|
76
|
+
* See TIM-871.
|
|
77
|
+
*
|
|
78
|
+
* @internal — framework use only.
|
|
79
|
+
*/
|
|
80
|
+
const middlewareBypassRequests = new WeakSet<Request>();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Mark a request so the pipeline skips its middleware phase.
|
|
84
|
+
*
|
|
85
|
+
* Used by `wrap-action-dispatch.ts` for the no-JS form-rerender path.
|
|
86
|
+
*
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
export function markRequestBypassMiddleware(req: Request): void {
|
|
90
|
+
middlewareBypassRequests.add(req);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check whether a request was marked to bypass middleware.
|
|
95
|
+
*
|
|
96
|
+
* Called by `handleRequest` in pipeline-phases.ts before invoking the
|
|
97
|
+
* middleware phase. Returns false for any request not explicitly marked.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export function shouldBypassMiddleware(req: Request): boolean {
|
|
102
|
+
return middlewareBypassRequests.has(req);
|
|
103
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js native stream transforms for SSR HTML post-processing.
|
|
3
|
+
*
|
|
4
|
+
* Node.js `Transform` wrappers around the pure helpers in
|
|
5
|
+
* `html-injector-core.ts`. Used on Node.js / Bun where C++-backed
|
|
6
|
+
* native streams are significantly faster than the JS-implemented
|
|
7
|
+
* Web Streams. The Web `TransformStream` equivalents live in
|
|
8
|
+
* `html-injectors.ts` and wrap the same core — keep the two files
|
|
9
|
+
* byte-for-byte equivalent in behavior. If you fix a streaming bug
|
|
10
|
+
* here, it almost certainly belongs in the core (`html-injector-core.ts`),
|
|
11
|
+
* not in this wrapper.
|
|
12
|
+
*
|
|
13
|
+
* Architecture:
|
|
14
|
+
* renderToPipeableStream → pipe(errorHandler) → pipe(headInjector)
|
|
15
|
+
* → pipe(flightInjector) → Readable.toWeb() → Response
|
|
16
|
+
*
|
|
17
|
+
* All chunks stay in C++ Node.js stream buffers until the final
|
|
18
|
+
* Readable.toWeb() conversion for the Response body.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Transform } from 'node:stream';
|
|
22
|
+
import { createGzip, constants } from 'node:zlib';
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
BufferAggregator,
|
|
26
|
+
FlightInjectorCore,
|
|
27
|
+
SUFFIX_BYTES,
|
|
28
|
+
createInjectorState,
|
|
29
|
+
createSuffixState,
|
|
30
|
+
injectorFlush,
|
|
31
|
+
processInjectorChunk,
|
|
32
|
+
processSuffixChunk,
|
|
33
|
+
suffixFlush,
|
|
34
|
+
} from './html-injector-core.js';
|
|
35
|
+
import { logStreamingError } from './logger.js';
|
|
36
|
+
import { createTickCoalescer } from './coalesce-tick.js';
|
|
37
|
+
|
|
38
|
+
// ─── Buffered Transform ──────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export interface NodeBufferedTransformOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Flush synchronously once the buffer reaches this many bytes.
|
|
43
|
+
* Prevents unbounded memory growth for very large Fizz flushes.
|
|
44
|
+
* Default: Infinity (no byte limit — flush only on tick boundary).
|
|
45
|
+
*/
|
|
46
|
+
readonly maxBufferByteLength?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Node.js `Transform` that buffers incoming chunks and coalesces them
|
|
51
|
+
* within a single event loop tick.
|
|
52
|
+
*
|
|
53
|
+
* Equivalent to `createBufferedTransformStream` in `html-injectors.ts` —
|
|
54
|
+
* the actual buffer math lives in `BufferAggregator`. This wrapper only
|
|
55
|
+
* owns the `setImmediate` scheduling and the push to the Node `Transform`.
|
|
56
|
+
*
|
|
57
|
+
* **Not a polling loop.** Single-shot `setImmediate` per flush cycle —
|
|
58
|
+
* no recursive scheduling. See design/02 §"No Polling".
|
|
59
|
+
*/
|
|
60
|
+
export function createNodeBufferedTransform(options: NodeBufferedTransformOptions = {}): Transform {
|
|
61
|
+
const buffer = new BufferAggregator(options.maxBufferByteLength ?? Infinity);
|
|
62
|
+
|
|
63
|
+
const flushBuffer = () => {
|
|
64
|
+
const merged = buffer.drain();
|
|
65
|
+
if (merged) transform.push(merged);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const coalescer = createTickCoalescer(flushBuffer);
|
|
69
|
+
|
|
70
|
+
const transform = new Transform({
|
|
71
|
+
transform(chunk: Buffer, _encoding, callback) {
|
|
72
|
+
const overCap = buffer.append(chunk);
|
|
73
|
+
if (overCap) {
|
|
74
|
+
flushBuffer();
|
|
75
|
+
} else {
|
|
76
|
+
coalescer.schedule();
|
|
77
|
+
}
|
|
78
|
+
callback();
|
|
79
|
+
},
|
|
80
|
+
flush(callback) {
|
|
81
|
+
coalescer.flushSync();
|
|
82
|
+
callback();
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return transform;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Move Suffix Transform ───────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Node.js `Transform` that moves `</body></html>` to the end of the stream.
|
|
93
|
+
*
|
|
94
|
+
* Equivalent to `createMoveSuffixStream` in `html-injectors.ts`. Strips
|
|
95
|
+
* the suffix on first sight and re-emits it from `flush()`. If the
|
|
96
|
+
* suffix never appeared in the input, it is appended anyway so output
|
|
97
|
+
* is well-formed.
|
|
98
|
+
*/
|
|
99
|
+
export function createNodeMoveSuffixTransform(): Transform {
|
|
100
|
+
const state = createSuffixState();
|
|
101
|
+
|
|
102
|
+
return new Transform({
|
|
103
|
+
transform(chunk: Buffer, _encoding, callback) {
|
|
104
|
+
const result = processSuffixChunk(state, chunk);
|
|
105
|
+
if (result.kind === 'passthrough' || result.kind === 'noSuffix') {
|
|
106
|
+
this.push(chunk);
|
|
107
|
+
callback();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (result.before) this.push(Buffer.from(result.before));
|
|
111
|
+
if (result.after) this.push(Buffer.from(result.after));
|
|
112
|
+
callback();
|
|
113
|
+
},
|
|
114
|
+
flush(callback) {
|
|
115
|
+
this.push(Buffer.from(suffixFlush(state)));
|
|
116
|
+
callback();
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Re-export for tests / consumers that need the suffix bytes constant.
|
|
122
|
+
export { SUFFIX_BYTES };
|
|
123
|
+
|
|
124
|
+
// ─── Head Injection ──────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Node.js `Transform` that injects HTML content before `</head>`.
|
|
128
|
+
*
|
|
129
|
+
* Equivalent to `injectHead` in `html-injectors.ts`. Streams chunks
|
|
130
|
+
* through immediately, keeping only a small trailing buffer to handle
|
|
131
|
+
* `</head>` split across chunk boundaries.
|
|
132
|
+
*/
|
|
133
|
+
export function createNodeHeadInjector(headHtml: string): Transform {
|
|
134
|
+
if (!headHtml) {
|
|
135
|
+
return new Transform({
|
|
136
|
+
transform(chunk, _enc, cb) {
|
|
137
|
+
cb(null, chunk);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const state = createInjectorState({ content: headHtml, targetTag: '</head>' });
|
|
143
|
+
|
|
144
|
+
return new Transform({
|
|
145
|
+
transform(chunk: Buffer, _encoding, callback) {
|
|
146
|
+
const result = processInjectorChunk(state, chunk);
|
|
147
|
+
if (result.kind === 'passthrough') {
|
|
148
|
+
callback(null, chunk);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (result.bytes) this.push(Buffer.from(result.bytes));
|
|
152
|
+
callback();
|
|
153
|
+
},
|
|
154
|
+
flush(callback) {
|
|
155
|
+
const leftover = injectorFlush(state);
|
|
156
|
+
if (leftover) this.push(Buffer.from(leftover));
|
|
157
|
+
callback();
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── RSC Flight Injection ────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Options for the Node.js flight injector.
|
|
166
|
+
*/
|
|
167
|
+
export interface NodeFlightInjectorOptions {
|
|
168
|
+
/**
|
|
169
|
+
* Timeout in milliseconds for individual RSC stream reads. If a single
|
|
170
|
+
* `rscReader.read()` call does not resolve within this duration, the
|
|
171
|
+
* read is aborted and the stream errors with a `RenderTimeoutError`.
|
|
172
|
+
* Default: 30000 (30s).
|
|
173
|
+
*/
|
|
174
|
+
renderTimeoutMs?: number;
|
|
175
|
+
/** Signal that fires when an unhandled RSC error is detected. */
|
|
176
|
+
errorSignal?: AbortSignal;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Node.js `Transform` that merges RSC script tags into the HTML stream.
|
|
181
|
+
*
|
|
182
|
+
* Equivalent to `createFlightInjectionTransform` in `html-injectors.ts`.
|
|
183
|
+
* The state machine, pending queue, and pull loop all live in
|
|
184
|
+
* `FlightInjectorCore` — this wrapper only shuffles bytes between the
|
|
185
|
+
* core and the Node `Transform.push()` API.
|
|
186
|
+
*
|
|
187
|
+
* Suffix stripping (`</body></html>`) is handled upstream by
|
|
188
|
+
* `createNodeMoveSuffixTransform`. RSC scripts are buffered in
|
|
189
|
+
* `core.pending[]` and only drained from `transform()` (after a complete
|
|
190
|
+
* HTML chunk) or `flush()` — never mid-tag. See design/02
|
|
191
|
+
* §"Scripts at Chunk Boundaries Only".
|
|
192
|
+
*
|
|
193
|
+
* The RSC stream is a Web `ReadableStream` (from the tee'd RSC Flight
|
|
194
|
+
* stream). The core reads from it using the Web API — this is the one
|
|
195
|
+
* bridge point between Web Streams and Node.js streams in the pipeline.
|
|
196
|
+
*/
|
|
197
|
+
export function createNodeFlightInjector(
|
|
198
|
+
rscStream: ReadableStream<Uint8Array> | undefined,
|
|
199
|
+
options?: NodeFlightInjectorOptions
|
|
200
|
+
): Transform {
|
|
201
|
+
if (!rscStream) {
|
|
202
|
+
return new Transform({
|
|
203
|
+
transform(chunk, _enc, cb) {
|
|
204
|
+
cb(null, chunk);
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const core = new FlightInjectorCore(rscStream, options?.renderTimeoutMs, options?.errorSignal);
|
|
210
|
+
|
|
211
|
+
const drain = (): void => {
|
|
212
|
+
while (core.pending.length > 0) transform.push(Buffer.from(core.pending.shift()!));
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const transform = new Transform({
|
|
216
|
+
transform(chunk: Buffer, _encoding, callback) {
|
|
217
|
+
const wasInit = core.isInit;
|
|
218
|
+
if (wasInit) core.notifyFirstChunk();
|
|
219
|
+
|
|
220
|
+
// Emit the HTML chunk, then drain any buffered RSC scripts.
|
|
221
|
+
// Scripts always come AFTER a complete HTML chunk — never mid-tag.
|
|
222
|
+
// The buffered transform upstream (TIM-528) ensures coherent chunks.
|
|
223
|
+
// Suffix stripping is upstream via createNodeMoveSuffixTransform (TIM-530).
|
|
224
|
+
transform.push(chunk);
|
|
225
|
+
drain();
|
|
226
|
+
|
|
227
|
+
// Start the pull loop on the first HTML chunk.
|
|
228
|
+
if (wasInit) core.ensurePullLoop(drain);
|
|
229
|
+
callback();
|
|
230
|
+
},
|
|
231
|
+
flush(callback) {
|
|
232
|
+
// All HTML chunks have been emitted. Pull loop stops yielding.
|
|
233
|
+
core.notifyHtmlDone();
|
|
234
|
+
|
|
235
|
+
const finish = () => {
|
|
236
|
+
drain();
|
|
237
|
+
const err = core.terminalError;
|
|
238
|
+
if (err !== null) {
|
|
239
|
+
transform.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
callback();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if (core.isPullDone) {
|
|
246
|
+
finish();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Wait on the pull-loop promise instead of polling. Zero CPU cost
|
|
250
|
+
// while waiting. See design/02 §"No Polling".
|
|
251
|
+
core.ensurePullLoop(drain).then(finish, (err) => {
|
|
252
|
+
// ensurePullLoop's underlying loop catches its own errors and
|
|
253
|
+
// sends PULL_ERROR — this catch is defence-in-depth for any
|
|
254
|
+
// synchronous throws inside the .then() chain itself.
|
|
255
|
+
transform.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
return transform;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ─── Error Handling ──────────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
const NOINDEX_SCRIPT =
|
|
266
|
+
'<script>document.head.appendChild(Object.assign(document.createElement("meta"),{name:"robots",content:"noindex"}))</script>';
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Node.js Transform that catches post-shell streaming errors.
|
|
270
|
+
*
|
|
271
|
+
* Equivalent to wrapStreamWithErrorHandling() in ssr-render.ts.
|
|
272
|
+
* Catches errors from React's streaming phase (deny/throw inside Suspense
|
|
273
|
+
* after the shell has flushed) and closes the stream cleanly.
|
|
274
|
+
*/
|
|
275
|
+
export function createNodeErrorHandler(signal?: AbortSignal): Transform {
|
|
276
|
+
const transform = new Transform({
|
|
277
|
+
transform(chunk, _encoding, callback) {
|
|
278
|
+
callback(null, chunk);
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
transform.on('error', (error) => {
|
|
283
|
+
const isAbort =
|
|
284
|
+
(error instanceof DOMException && error.name === 'AbortError') ||
|
|
285
|
+
(error instanceof Error && error.name === 'AbortError') ||
|
|
286
|
+
signal?.aborted;
|
|
287
|
+
|
|
288
|
+
if (isAbort) {
|
|
289
|
+
transform.end();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
logStreamingError({ error });
|
|
294
|
+
transform.push(Buffer.from(NOINDEX_SCRIPT, 'utf-8'));
|
|
295
|
+
transform.end();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return transform;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─── Compression ─────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
const COMPRESSIBLE_TYPES = new Set([
|
|
304
|
+
'text/html',
|
|
305
|
+
'text/css',
|
|
306
|
+
'text/plain',
|
|
307
|
+
'text/xml',
|
|
308
|
+
'text/javascript',
|
|
309
|
+
'text/x-component',
|
|
310
|
+
'application/json',
|
|
311
|
+
'application/javascript',
|
|
312
|
+
'application/xml',
|
|
313
|
+
'application/xhtml+xml',
|
|
314
|
+
'application/rss+xml',
|
|
315
|
+
'image/svg+xml',
|
|
316
|
+
]);
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Create a Node.js gzip Transform using native node:zlib.
|
|
320
|
+
*
|
|
321
|
+
* Uses `createGzip()` which is backed by C++ zlib — significantly faster
|
|
322
|
+
* than the Web Streams `CompressionStream` API (which is a JS wrapper
|
|
323
|
+
* around the same zlib but with per-chunk Promise overhead).
|
|
324
|
+
*
|
|
325
|
+
* Returns null if the response shouldn't be compressed (wrong content type,
|
|
326
|
+
* client doesn't accept gzip, already encoded, etc.).
|
|
327
|
+
*/
|
|
328
|
+
export function createNodeGzipCompressor(
|
|
329
|
+
requestHeaders: Headers,
|
|
330
|
+
responseHeaders: Headers
|
|
331
|
+
): Transform | null {
|
|
332
|
+
// Check Accept-Encoding
|
|
333
|
+
const acceptEncoding = requestHeaders.get('accept-encoding') || '';
|
|
334
|
+
if (!acceptEncoding.includes('gzip')) return null;
|
|
335
|
+
|
|
336
|
+
// Check content type is compressible
|
|
337
|
+
const contentType = responseHeaders.get('content-type') || '';
|
|
338
|
+
const mimeType = contentType.split(';')[0].trim().toLowerCase();
|
|
339
|
+
if (!COMPRESSIBLE_TYPES.has(mimeType)) return null;
|
|
340
|
+
|
|
341
|
+
// Don't double-compress
|
|
342
|
+
if (responseHeaders.has('content-encoding')) return null;
|
|
343
|
+
|
|
344
|
+
// Set response headers for gzip
|
|
345
|
+
responseHeaders.set('content-encoding', 'gzip');
|
|
346
|
+
responseHeaders.delete('content-length');
|
|
347
|
+
const existingVary = responseHeaders.get('vary');
|
|
348
|
+
if (existingVary) {
|
|
349
|
+
if (!existingVary.toLowerCase().includes('accept-encoding')) {
|
|
350
|
+
responseHeaders.set('vary', existingVary + ', Accept-Encoding');
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
responseHeaders.set('vary', 'Accept-Encoding');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Z_SYNC_FLUSH ensures each chunk is flushed to the output immediately.
|
|
357
|
+
// Without it, gzip buffers internally and the browser doesn't receive
|
|
358
|
+
// the HTML shell until the gzip stream closes — breaking streaming.
|
|
359
|
+
// ~2–5% size overhead vs Z_NO_FLUSH but preserves correct streaming.
|
|
360
|
+
return createGzip({ flush: constants.Z_SYNC_FLUSH });
|
|
361
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Segment param coercion — runs the matched route's `params.ts` codecs
|
|
3
|
+
* over the raw matcher output before middleware and rendering.
|
|
4
|
+
*
|
|
5
|
+
* Lifted out of `pipeline-phases.ts` (TIM-853) so the coercer can be
|
|
6
|
+
* imported directly by other entry points (the action-dispatch wrapper,
|
|
7
|
+
* the revalidation renderer in `rsc-entry/index.ts`) without pulling
|
|
8
|
+
* the entire pipeline phase module along with it.
|
|
9
|
+
*
|
|
10
|
+
* The function throws `ParamCoercionError` from `route-element-builder.ts`
|
|
11
|
+
* on any codec failure; the pipeline catches that and dispatches to the
|
|
12
|
+
* 404 page. See design/07-routing.md §"Where Coercion Runs".
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Codec } from '../codec.js';
|
|
16
|
+
import { toBracketKey } from '../params/resolve-schema.js';
|
|
17
|
+
import type { RouteMatch } from './pipeline.js';
|
|
18
|
+
import { sanitizeParamValue } from './pipeline-helpers.js';
|
|
19
|
+
import { loadModule } from './safe-load.js';
|
|
20
|
+
import { ParamCoercionError } from './route-element-builder.js';
|
|
21
|
+
import { isDebug } from './debug.js';
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Module-level global codec store (TIM-931)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/** Global schema codecs, set once at startup from virtual:timber-schema. */
|
|
28
|
+
let _globalCodecs: Record<string, Codec<unknown>> | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Register the global schema codecs. Called once from the RSC entry
|
|
32
|
+
* at server startup when app/schema.ts provides segment param codecs.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export function setGlobalSchemaCodecs(codecs: Record<string, Codec<unknown>> | null): void {
|
|
36
|
+
_globalCodecs = codecs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Coerce raw slot params through global schema codecs.
|
|
41
|
+
*
|
|
42
|
+
* Each slot segment that has a paramName is looked up in the global codec
|
|
43
|
+
* map by its bracket key (e.g. '[...year]' for catch-all). If a codec
|
|
44
|
+
* exists, the raw value is parsed through it. If no codec exists or no
|
|
45
|
+
* global codecs are registered, the raw value passes through unchanged.
|
|
46
|
+
*
|
|
47
|
+
* Unlike main route coercion, this does NOT throw ParamCoercionError on
|
|
48
|
+
* failure — slots degrade gracefully. A failed coercion logs a warning
|
|
49
|
+
* in dev mode and keeps the raw value.
|
|
50
|
+
*
|
|
51
|
+
* @internal — framework use only
|
|
52
|
+
*/
|
|
53
|
+
export function coerceSlotParams(
|
|
54
|
+
slotChain: Array<{ segmentType: string; paramName?: string }>,
|
|
55
|
+
rawParams: Record<string, string | string[]>
|
|
56
|
+
): Record<string, string | string[]> {
|
|
57
|
+
const globalCodecs = _globalCodecs;
|
|
58
|
+
if (!globalCodecs) return rawParams;
|
|
59
|
+
|
|
60
|
+
const result: Record<string, string | string[]> = Object.create(null);
|
|
61
|
+
for (const key of Object.keys(rawParams)) {
|
|
62
|
+
result[key] = rawParams[key];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const segment of slotChain) {
|
|
66
|
+
if (!segment.paramName) continue;
|
|
67
|
+
const bracketKey = toBracketKey(segment.segmentType, segment.paramName);
|
|
68
|
+
const codec = globalCodecs[bracketKey];
|
|
69
|
+
if (!codec) continue;
|
|
70
|
+
|
|
71
|
+
const key = segment.paramName;
|
|
72
|
+
if (!(key in result)) continue;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
result[key] = sanitizeParamValue(codec.parse(result[key] as string | string[])) as
|
|
76
|
+
| string
|
|
77
|
+
| string[];
|
|
78
|
+
} catch (err) {
|
|
79
|
+
// Slot param coercion failures are non-fatal — keep the raw value.
|
|
80
|
+
// The main route already validated the URL; the slot just interprets
|
|
81
|
+
// the same parts differently.
|
|
82
|
+
if (isDebug()) {
|
|
83
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84
|
+
console.warn(
|
|
85
|
+
`[timber] Slot param coercion failed for "${key}" (codec: ${bracketKey})\n` +
|
|
86
|
+
` Error: ${message}\n` +
|
|
87
|
+
` Raw value: ${JSON.stringify(result[key])}\n` +
|
|
88
|
+
` Keeping raw value.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result as Record<string, string | string[]>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Run segment param coercion on the matched route's segments.
|
|
99
|
+
*
|
|
100
|
+
* When `globalCodecs` is provided (from app/schema.ts), uses the global
|
|
101
|
+
* codec map keyed by bare param name. Otherwise falls back to loading
|
|
102
|
+
* per-segment params.ts modules.
|
|
103
|
+
*
|
|
104
|
+
* Throws ParamCoercionError if any codec fails (→ 404).
|
|
105
|
+
*
|
|
106
|
+
* This runs BEFORE middleware, so ctx.segmentParams is already typed.
|
|
107
|
+
* See design/07-routing.md §"Where Coercion Runs"
|
|
108
|
+
* See design/41-global-params.md §"Pipeline Integration"
|
|
109
|
+
*/
|
|
110
|
+
export async function coerceSegmentParams(match: RouteMatch): Promise<void> {
|
|
111
|
+
const globalCodecs = _globalCodecs;
|
|
112
|
+
// Unconditionally install a null-prototype target so the invariant
|
|
113
|
+
// "match.segmentParams is null-prototype" holds from the first line,
|
|
114
|
+
// regardless of whether any segment has a codec.
|
|
115
|
+
const mergeTarget: Record<string, unknown> = Object.create(null);
|
|
116
|
+
for (const key of Object.keys(match.segmentParams)) {
|
|
117
|
+
if (key !== '__proto__') {
|
|
118
|
+
mergeTarget[key] = match.segmentParams[key as keyof typeof match.segmentParams];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
match.segmentParams = mergeTarget as RouteMatch['segmentParams'];
|
|
122
|
+
|
|
123
|
+
// TIM-931/TIM-936: When a global schema is available, use it for coercion
|
|
124
|
+
// instead of per-segment params.ts files. The global codec map is keyed
|
|
125
|
+
// by bracket name (e.g. '[id]', '[...slug]') to avoid collisions between
|
|
126
|
+
// distinct segment types that share a param name.
|
|
127
|
+
if (globalCodecs) {
|
|
128
|
+
for (const segment of match.segments) {
|
|
129
|
+
if (!segment.paramName) continue;
|
|
130
|
+
const bracketKey = toBracketKey(segment.segmentType, segment.paramName);
|
|
131
|
+
const codec = globalCodecs[bracketKey];
|
|
132
|
+
if (!codec) continue; // no codec in schema → keep raw string
|
|
133
|
+
|
|
134
|
+
const key = segment.paramName;
|
|
135
|
+
try {
|
|
136
|
+
mergeTarget[key] = sanitizeParamValue(codec.parse(mergeTarget[key] as string | string[]));
|
|
137
|
+
} catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
if (isDebug()) {
|
|
140
|
+
console.warn(
|
|
141
|
+
`[timber] Global schema codec rejected value for param "${key}"\n` +
|
|
142
|
+
` Error: ${message}\n` +
|
|
143
|
+
` Raw value: ${JSON.stringify(mergeTarget[key])}\n` +
|
|
144
|
+
` Hint: check the codec in app/schema.ts for key '${bracketKey}'.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
throw new ParamCoercionError(message);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Legacy path: per-segment params.ts coercion
|
|
154
|
+
for (const segment of match.segments) {
|
|
155
|
+
// Only process segments that have a params.ts convention file
|
|
156
|
+
if (!segment.params) continue;
|
|
157
|
+
|
|
158
|
+
let mod: Record<string, unknown>;
|
|
159
|
+
try {
|
|
160
|
+
mod = await loadModule(segment.params);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
const message = `Failed to load params module for segment "${segment.segmentName}": ${err instanceof Error ? err.message : String(err)}`;
|
|
163
|
+
if (isDebug()) {
|
|
164
|
+
console.warn(
|
|
165
|
+
`[timber] Param coercion error: ${message}\n` +
|
|
166
|
+
` Segment: ${segment.segmentName}\n` +
|
|
167
|
+
` Params file: ${segment.params}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
throw new ParamCoercionError(message);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const segmentParamsDef = mod.segmentParams as
|
|
174
|
+
| { parse(raw: Record<string, string | string[]>): Record<string, unknown> }
|
|
175
|
+
| undefined;
|
|
176
|
+
|
|
177
|
+
if (!segmentParamsDef || typeof segmentParamsDef.parse !== 'function') continue;
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const coerced = segmentParamsDef.parse(match.segmentParams);
|
|
181
|
+
|
|
182
|
+
// Deep-sanitize codec output: every nested plain object becomes
|
|
183
|
+
// null-prototype with dangerous keys stripped at every depth.
|
|
184
|
+
// See TIM-873, design/13-security.md
|
|
185
|
+
for (const key of Object.keys(coerced as Record<string, unknown>)) {
|
|
186
|
+
if (key !== '__proto__') {
|
|
187
|
+
mergeTarget[key] = sanitizeParamValue((coerced as Record<string, unknown>)[key]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
192
|
+
if (isDebug()) {
|
|
193
|
+
const rawKeys = Object.keys(match.segmentParams).join(', ');
|
|
194
|
+
console.warn(
|
|
195
|
+
`[timber] Param codec rejected values for segment "${segment.segmentName}"\n` +
|
|
196
|
+
` Error: ${message}\n` +
|
|
197
|
+
` Available raw params: { ${rawKeys} }\n` +
|
|
198
|
+
` Params file: ${segment.params}\n` +
|
|
199
|
+
` Hint: this usually means a codec threw for the raw URL value.\n` +
|
|
200
|
+
` Check that the regex/schema accepts the actual path segment string.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
throw new ParamCoercionError(message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|