@timber-js/app 0.2.0-alpha.97 → 0.2.0-alpha.99
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-CQ8Z8VGL.js +1061 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
- package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
- package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
- package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
- package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
- package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
- package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
- package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
- package/dist/_chunks/logger-0m8MsKdc.js +291 -0
- package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
- package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
- package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
- package/dist/_chunks/registry-I2ss-lvy.js +20 -0
- package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
- package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
- package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
- package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
- package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
- package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
- package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
- package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
- package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
- package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
- package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
- package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
- package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
- package/dist/_chunks/{interception-BbqMCVXa.js → walkers-Tg0Alwcg.js} +66 -87
- package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
- package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
- package/dist/_chunks/warnings-Cg47l5sk.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.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +8 -28
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +63 -31
- 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/index.js +9 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.js +2 -1
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/form.d.ts +10 -24
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +41 -91
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +2 -1
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +81 -7
- package/dist/client/internal.js.map +1 -1
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/state.d.ts +1 -1
- package/dist/client/use-cookie.d.ts +8 -0
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +1 -1
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +2 -2
- package/dist/config-types.d.ts +28 -0
- package/dist/config-types.d.ts.map +1 -1
- package/dist/cookies/define-cookie.d.ts +87 -35
- 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 +48 -2
- package/dist/cookies/index.js.map +1 -0
- 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/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +9 -19
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +5 -3
- 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} +1 -1
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
- 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/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +285 -133
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +1 -1
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +6 -4
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/index.d.ts +5 -3
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -3
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +37 -8
- package/dist/routing/segment-classify.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +63 -23
- 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/search-params/define.d.ts +25 -7
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.js +5 -3
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/wrappers.d.ts +2 -2
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/segment-params/define.d.ts +23 -6
- package/dist/segment-params/define.d.ts.map +1 -1
- package/dist/segment-params/index.js +1 -1
- package/dist/server/access-gate.d.ts +4 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts +15 -6
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +5 -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/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/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/early-hints-sender.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +212 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +59 -59
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -149
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +6 -4
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +852 -852
- package/dist/server/internal.js.map +1 -1
- package/dist/server/logger.d.ts +14 -0
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +17 -0
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +46 -49
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/param-coercion.d.ts +26 -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-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 +51 -32
- 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/request-context.d.ts +22 -159
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +20 -47
- 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/helpers.d.ts +1 -1
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- 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/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.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/tracing.d.ts +1 -1
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +45 -16
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +48 -0
- 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/shims/headers.d.ts +2 -2
- 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 +9 -4
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +0 -45
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare.ts +10 -50
- package/src/adapters/nitro.ts +66 -50
- package/src/adapters/shared.ts +40 -0
- package/src/cache/timber-cache.ts +3 -2
- package/src/client/form.tsx +17 -25
- package/src/client/index.ts +16 -9
- package/src/client/internal.ts +3 -2
- package/src/client/router.ts +1 -1
- package/src/client/rsc-fetch.ts +15 -0
- package/src/client/state.ts +2 -2
- package/src/client/use-cookie.ts +29 -0
- package/src/codec.ts +3 -7
- package/src/config-types.ts +28 -0
- package/src/cookies/define-cookie.ts +271 -78
- package/src/cookies/index.ts +11 -8
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +17 -48
- package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
- package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +4 -2
- 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} +2 -2
- package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
- package/src/index.ts +95 -34
- package/src/plugin-context.ts +1 -1
- package/src/plugins/adapter-build.ts +3 -1
- package/src/plugins/build-report.ts +13 -22
- package/src/plugins/dev-server.ts +3 -3
- package/src/plugins/routing.ts +14 -12
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +1 -1
- package/src/routing/codegen.ts +1 -1
- package/src/routing/convention-lint.ts +9 -8
- package/src/routing/index.ts +5 -3
- package/src/routing/interception.ts +1 -1
- package/src/routing/scanner.ts +22 -95
- package/src/routing/segment-classify.ts +107 -8
- package/src/routing/status-file-lint.ts +7 -5
- package/src/routing/types.ts +63 -23
- package/src/routing/walkers.ts +90 -0
- package/src/search-params/define.ts +71 -15
- package/src/search-params/wrappers.ts +9 -2
- package/src/segment-params/define.ts +66 -13
- package/src/server/access-gate.tsx +9 -8
- package/src/server/action-handler.ts +34 -38
- package/src/server/als-registry.ts +5 -5
- package/src/server/asset-headers.ts +8 -34
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
- package/src/server/deny-renderer.ts +7 -12
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/fallback-error.ts +2 -2
- package/src/server/html-injector-core.ts +403 -0
- package/src/server/html-injectors.ts +158 -297
- package/src/server/index.ts +13 -14
- package/src/server/internal.ts +10 -3
- package/src/server/logger.ts +23 -0
- package/src/server/middleware-runner.ts +44 -0
- package/src/server/node-stream-transforms.ts +108 -248
- package/src/server/param-coercion.ts +76 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-outcome.ts +167 -0
- package/src/server/pipeline-phases.ts +409 -0
- package/src/server/pipeline.ts +70 -540
- package/src/server/port-resolution.ts +215 -0
- package/src/server/request-context.ts +46 -451
- package/src/server/route-element-builder.ts +8 -4
- package/src/server/route-matcher.ts +28 -60
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +2 -2
- package/src/server/rsc-entry/helpers.ts +2 -7
- package/src/server/rsc-entry/index.ts +81 -366
- package/src/server/rsc-entry/render-route.ts +304 -0
- package/src/server/rsc-entry/rsc-payload.ts +1 -1
- package/src/server/rsc-entry/ssr-renderer.ts +2 -2
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/sitemap-generator.ts +1 -1
- package/src/server/slot-resolver.ts +1 -1
- package/src/server/ssr-entry.ts +1 -1
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/tracing.ts +3 -3
- package/src/server/tree-builder.ts +134 -56
- package/src/server/types.ts +52 -0
- package/src/server/utils/escape-html.ts +20 -0
- package/src/shims/headers.ts +3 -3
- package/src/shims/navigation-client.ts +4 -3
- package/src/shims/navigation.ts +9 -7
- package/src/utils/directive-parser.ts +0 -392
- package/dist/_chunks/actions-DLnUaR65.js +0 -421
- package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
- package/dist/_chunks/debug-ECi_61pb.js +0 -108
- package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
- package/dist/_chunks/define-C77ScO0m.js.map +0 -1
- package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
- package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
- package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
- package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
- package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
- package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
- package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
- package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
- package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
- package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
- package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
- package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
- package/dist/client/use-params.d.ts.map +0 -1
- package/dist/plugins/dev-404-page.d.ts.map +0 -1
- package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-error-page.d.ts.map +0 -1
- package/dist/plugins/dev-logs.d.ts.map +0 -1
- package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
- package/dist/server/deny-page-resolver.d.ts +0 -52
- package/dist/server/deny-page-resolver.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-holding-server.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/page-deny-boundary.d.ts +0 -31
- package/dist/server/page-deny-boundary.d.ts.map +0 -1
- 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/page-deny-boundary.tsx +0 -56
- /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
- /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Request Context — per-request ALS store for
|
|
2
|
+
* Request Context — per-request ALS store for headers, search params,
|
|
3
|
+
* segment params, and request scope lifecycle.
|
|
3
4
|
*
|
|
4
5
|
* Follows the same pattern as tracing.ts: a module-level AsyncLocalStorage
|
|
5
6
|
* instance, public accessor functions that throw outside request scope,
|
|
6
7
|
* and a framework-internal `runWithRequestContext()` to establish scope.
|
|
7
8
|
*
|
|
9
|
+
* Cookie state lives in `cookie-context.ts` (split out in TIM-853). The
|
|
10
|
+
* scope set up here owns the cookie jar / parsedCookies fields on the
|
|
11
|
+
* store, but the cookie API and helpers are in the cookie module.
|
|
12
|
+
*
|
|
8
13
|
* See design/04-authorization.md §"AccessContext does not include cookies or headers"
|
|
9
14
|
* and design/11-platform.md §"AsyncLocalStorage".
|
|
10
|
-
* See design/29-cookies.md for cookie mutation semantics.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
|
-
import { requestContextAls, type RequestContextStore
|
|
14
|
-
import { isDebug } from './debug.js';
|
|
17
|
+
import { requestContextAls, type RequestContextStore } from './als-registry.js';
|
|
15
18
|
import { _setGetSearchParamsFn } from '../search-params/define.js';
|
|
16
19
|
import { _setGetSegmentParamsFn } from '../segment-params/define.js';
|
|
20
|
+
import { consumeSeededCookies, getCookieJar } from './cookie-context.js';
|
|
21
|
+
import { _registerServerCookieImpl, _registerFromSchema } from '../cookies/define-cookie.js';
|
|
22
|
+
import { fromSchema } from '../schema-bridge.js';
|
|
17
23
|
|
|
18
24
|
// Re-export the ALS for framework-internal consumers that need direct access.
|
|
19
25
|
export { requestContextAls };
|
|
@@ -30,7 +36,7 @@ export { requestContextAls };
|
|
|
30
36
|
* Available in middleware, access checks, server components, and server actions.
|
|
31
37
|
* Throws if called outside a request context (security principle #2: no global fallback).
|
|
32
38
|
*/
|
|
33
|
-
export function getHeaders():
|
|
39
|
+
export function getHeaders(): ReadonlyHeaders {
|
|
34
40
|
const store = requestContextAls.getStore();
|
|
35
41
|
if (!store) {
|
|
36
42
|
throw new Error(
|
|
@@ -38,199 +44,28 @@ export function getHeaders(): Promise<ReadonlyHeaders> {
|
|
|
38
44
|
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
39
45
|
);
|
|
40
46
|
}
|
|
41
|
-
return
|
|
47
|
+
return store.headers;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Returns the value of a single request header, or undefined if absent.
|
|
46
52
|
*
|
|
47
|
-
* Thin wrapper over `
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* ```ts
|
|
51
|
-
* import { getHeader } from '@timber-js/app/server'
|
|
53
|
+
* Thin wrapper over `getHeaders().get(name)` for the common case where
|
|
54
|
+
* you need exactly one header.
|
|
52
55
|
*
|
|
53
|
-
*
|
|
54
|
-
* const auth = await getHeader('authorization');
|
|
55
|
-
* }
|
|
56
|
-
* ```
|
|
56
|
+
* @internal — not part of the public API. Use `getHeaders().get(name)` instead.
|
|
57
57
|
*/
|
|
58
|
-
export
|
|
59
|
-
const headers =
|
|
58
|
+
export function getHeader(name: string): string | undefined {
|
|
59
|
+
const headers = getHeaders();
|
|
60
60
|
return headers.get(name) ?? undefined;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* Returns
|
|
65
|
-
*
|
|
66
|
-
* Available in middleware, access checks, server components, and server actions.
|
|
67
|
-
* Throws if called outside a request context (security principle #2: no global fallback).
|
|
68
|
-
*
|
|
69
|
-
* Read methods (.get, .has, .getAll) are always available and reflect
|
|
70
|
-
* read-your-own-writes from .set() calls in the same request.
|
|
71
|
-
*
|
|
72
|
-
* Mutation methods (.set, .delete, .clear) are only available in mutable
|
|
73
|
-
* contexts (middleware.ts, server actions, route.ts handlers). Calling them
|
|
74
|
-
* in read-only contexts (access.ts, server components) throws.
|
|
75
|
-
*
|
|
76
|
-
* See design/29-cookies.md
|
|
77
|
-
*/
|
|
78
|
-
export function getCookies(): Promise<RequestCookies> {
|
|
79
|
-
const store = requestContextAls.getStore();
|
|
80
|
-
if (!store) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
'[timber] getCookies() called outside of a request context. ' +
|
|
83
|
-
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Parse cookies lazily on first access
|
|
88
|
-
if (!store.parsedCookies) {
|
|
89
|
-
store.parsedCookies = parseCookieHeader(store.cookieHeader);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const map = store.parsedCookies;
|
|
93
|
-
return Promise.resolve({
|
|
94
|
-
get(name: string): string | undefined {
|
|
95
|
-
return map.get(name);
|
|
96
|
-
},
|
|
97
|
-
has(name: string): boolean {
|
|
98
|
-
return map.has(name);
|
|
99
|
-
},
|
|
100
|
-
getAll(): Array<{ name: string; value: string }> {
|
|
101
|
-
return Array.from(map.entries()).map(([name, value]) => ({ name, value }));
|
|
102
|
-
},
|
|
103
|
-
get size(): number {
|
|
104
|
-
return map.size;
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
set(name: string, value: string, options?: CookieOptions): void {
|
|
108
|
-
assertMutable(store, 'set');
|
|
109
|
-
if (store.flushed) {
|
|
110
|
-
if (isDebug()) {
|
|
111
|
-
console.warn(
|
|
112
|
-
`[timber] warn: getCookies().set('${name}') called after response headers were committed.\n` +
|
|
113
|
-
` The cookie will NOT be sent. Move cookie mutations to middleware.ts, a server action,\n` +
|
|
114
|
-
` or a route.ts handler.`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
const opts = { ...DEFAULT_COOKIE_OPTIONS, ...options };
|
|
120
|
-
store.cookieJar.set(name, { name, value, options: opts });
|
|
121
|
-
// Read-your-own-writes: update the parsed cookies map
|
|
122
|
-
map.set(name, value);
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
setFromHeaders(headers: Headers): void {
|
|
126
|
-
assertMutable(store, 'setFromHeaders');
|
|
127
|
-
if (store.flushed) {
|
|
128
|
-
console.warn(
|
|
129
|
-
`[timber] warn: getCookies().setFromHeaders() called after response headers were committed.\n` +
|
|
130
|
-
` The cookies will NOT be sent. Move cookie mutations to middleware.ts, a server action,\n` +
|
|
131
|
-
` or a route.ts handler.`
|
|
132
|
-
);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
// Headers.getSetCookie() returns individual Set-Cookie strings,
|
|
136
|
-
// avoiding the fragile comma-splitting that raw .get() requires.
|
|
137
|
-
for (const raw of headers.getSetCookie()) {
|
|
138
|
-
const parsed = parseSetCookie(raw);
|
|
139
|
-
if (parsed) {
|
|
140
|
-
// Use setRaw to preserve the original header's attributes without
|
|
141
|
-
// merging DEFAULT_COOKIE_OPTIONS (parseSetCookie intentionally
|
|
142
|
-
// does not apply defaults — see its doc comment).
|
|
143
|
-
setRaw(store, map, parsed.name, parsed.value, parsed.options);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void {
|
|
149
|
-
assertMutable(store, 'delete');
|
|
150
|
-
if (store.flushed) {
|
|
151
|
-
if (isDebug()) {
|
|
152
|
-
console.warn(
|
|
153
|
-
`[timber] warn: getCookies().delete('${name}') called after response headers were committed.\n` +
|
|
154
|
-
` The cookie will NOT be deleted. Move cookie mutations to middleware.ts, a server action,\n` +
|
|
155
|
-
` or a route.ts handler.`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
const opts: CookieOptions = {
|
|
161
|
-
...DEFAULT_COOKIE_OPTIONS,
|
|
162
|
-
...options,
|
|
163
|
-
maxAge: 0,
|
|
164
|
-
expires: new Date(0),
|
|
165
|
-
};
|
|
166
|
-
store.cookieJar.set(name, { name, value: '', options: opts });
|
|
167
|
-
// Remove from read view
|
|
168
|
-
map.delete(name);
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
clear(): void {
|
|
172
|
-
assertMutable(store, 'clear');
|
|
173
|
-
if (store.flushed) return;
|
|
174
|
-
// Delete every incoming cookie
|
|
175
|
-
for (const name of Array.from(map.keys())) {
|
|
176
|
-
store.cookieJar.set(name, {
|
|
177
|
-
name,
|
|
178
|
-
value: '',
|
|
179
|
-
options: { ...DEFAULT_COOKIE_OPTIONS, maxAge: 0, expires: new Date(0) },
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
map.clear();
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
toString(): string {
|
|
186
|
-
return Array.from(map.entries())
|
|
187
|
-
.map(([name, value]) => `${name}=${value}`)
|
|
188
|
-
.join('; ');
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Returns the value of a single cookie, or undefined if absent.
|
|
195
|
-
*
|
|
196
|
-
* Thin wrapper over `(await getCookies()).get(name)` for the common
|
|
197
|
-
* case where you need exactly one cookie.
|
|
198
|
-
*
|
|
199
|
-
* ```ts
|
|
200
|
-
* import { getCookie } from '@timber-js/app/server'
|
|
201
|
-
*
|
|
202
|
-
* export default async function Page() {
|
|
203
|
-
* const session = await getCookie('session_id');
|
|
204
|
-
* }
|
|
205
|
-
* ```
|
|
206
|
-
*/
|
|
207
|
-
export async function getCookie(name: string): Promise<string | undefined> {
|
|
208
|
-
const cookies = await getCookies();
|
|
209
|
-
return cookies.get(name);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Returns a Promise resolving to the current request's raw URLSearchParams.
|
|
214
|
-
*
|
|
215
|
-
* For typed, parsed search params, import the definition from params.ts
|
|
216
|
-
* and call `.load()` or `.parse()`:
|
|
217
|
-
*
|
|
218
|
-
* ```ts
|
|
219
|
-
* import { searchParams } from './params'
|
|
220
|
-
* const parsed = await searchParams.get()
|
|
221
|
-
* ```
|
|
222
|
-
*
|
|
223
|
-
* Or explicitly:
|
|
64
|
+
* Returns the current request's raw URLSearchParams.
|
|
224
65
|
*
|
|
225
|
-
*
|
|
226
|
-
* import { getSearchParams } from '@timber-js/app/server'
|
|
227
|
-
* import { searchParams } from './params'
|
|
228
|
-
* const parsed = searchParams.parse(await getSearchParams())
|
|
229
|
-
* ```
|
|
230
|
-
*
|
|
231
|
-
* Throws if called outside a request context.
|
|
66
|
+
* @internal — not part of the public API. Use `defineSearchParams().get()` instead.
|
|
232
67
|
*/
|
|
233
|
-
export function getSearchParams():
|
|
68
|
+
export function getSearchParams(): URLSearchParams {
|
|
234
69
|
const store = requestContextAls.getStore();
|
|
235
70
|
if (!store) {
|
|
236
71
|
throw new Error(
|
|
@@ -238,42 +73,31 @@ export function getSearchParams(): Promise<URLSearchParams> {
|
|
|
238
73
|
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
239
74
|
);
|
|
240
75
|
}
|
|
241
|
-
return store.
|
|
76
|
+
return store.searchParams;
|
|
242
77
|
}
|
|
243
78
|
|
|
244
79
|
// Eagerly register getSearchParams with the search-params module so
|
|
245
|
-
// searchParams.get() can call it
|
|
80
|
+
// searchParams.get() can call it without a dynamic import.
|
|
246
81
|
// Dynamic imports lose ALS context in React's RSC Flight renderer,
|
|
247
82
|
// breaking getSearchParams() in parallel slot pages. See TIM-523.
|
|
248
83
|
_setGetSearchParamsFn(getSearchParams);
|
|
249
84
|
|
|
250
85
|
// Eagerly register getSegmentParams with the segment-params module so
|
|
251
|
-
// segmentParams.get() can call it
|
|
86
|
+
// segmentParams.get() can call it without a dynamic import.
|
|
252
87
|
// Same pattern as search params — dynamic imports lose ALS context. See TIM-523.
|
|
253
88
|
_setGetSegmentParamsFn(getSegmentParams);
|
|
254
89
|
|
|
90
|
+
// Eagerly register the server cookie impl with defineCookie so
|
|
91
|
+
// .get()/.set()/.delete() are sync without dynamic imports.
|
|
92
|
+
_registerServerCookieImpl({ getCookieJar });
|
|
93
|
+
_registerFromSchema(fromSchema);
|
|
94
|
+
|
|
255
95
|
/**
|
|
256
|
-
* Returns
|
|
257
|
-
*
|
|
258
|
-
* Segment params are set by the pipeline after route matching and param
|
|
259
|
-
* coercion (via params.ts codecs). When no params.ts exists, values are
|
|
260
|
-
* raw strings. When codecs are defined, values are already coerced
|
|
261
|
-
* (e.g., `id` is a `number` if `defineSegmentParams({ id: z.coerce.number() })`).
|
|
262
|
-
*
|
|
263
|
-
* This is the primary way page and layout components access route params:
|
|
96
|
+
* Returns the current request's coerced segment params.
|
|
264
97
|
*
|
|
265
|
-
*
|
|
266
|
-
* import { getSegmentParams } from '@timber-js/app/server'
|
|
267
|
-
*
|
|
268
|
-
* export default async function Page() {
|
|
269
|
-
* const { slug } = await getSegmentParams()
|
|
270
|
-
* // ...
|
|
271
|
-
* }
|
|
272
|
-
* ```
|
|
273
|
-
*
|
|
274
|
-
* Throws if called outside a request context.
|
|
98
|
+
* @internal — not part of the public API. Use `defineSegmentParams().get()` instead.
|
|
275
99
|
*/
|
|
276
|
-
export function getSegmentParams():
|
|
100
|
+
export function getSegmentParams(): Record<string, string | string[]> {
|
|
277
101
|
const store = requestContextAls.getStore();
|
|
278
102
|
if (!store) {
|
|
279
103
|
throw new Error(
|
|
@@ -281,13 +105,13 @@ export function getSegmentParams(): Promise<Record<string, string | string[]>> {
|
|
|
281
105
|
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
282
106
|
);
|
|
283
107
|
}
|
|
284
|
-
if (!store.
|
|
108
|
+
if (!store.segmentParams) {
|
|
285
109
|
throw new Error(
|
|
286
110
|
'[timber] getSegmentParams() called before route matching completed. ' +
|
|
287
111
|
'Segment params are not available until after the route is matched.'
|
|
288
112
|
);
|
|
289
113
|
}
|
|
290
|
-
return store.
|
|
114
|
+
return store.segmentParams;
|
|
291
115
|
}
|
|
292
116
|
|
|
293
117
|
/**
|
|
@@ -301,7 +125,7 @@ export function setSegmentParams(params: Record<string, string | string[]>): voi
|
|
|
301
125
|
if (!store) {
|
|
302
126
|
throw new Error('[timber] setSegmentParams() called outside of a request context.');
|
|
303
127
|
}
|
|
304
|
-
store.
|
|
128
|
+
store.segmentParams = params;
|
|
305
129
|
}
|
|
306
130
|
|
|
307
131
|
/**
|
|
@@ -330,169 +154,33 @@ export type ReadonlyHeaders = Pick<
|
|
|
330
154
|
'get' | 'has' | 'entries' | 'keys' | 'values' | 'forEach' | typeof Symbol.iterator
|
|
331
155
|
>;
|
|
332
156
|
|
|
333
|
-
/** Options for setting a cookie. See design/29-cookies.md. */
|
|
334
|
-
export interface CookieOptions {
|
|
335
|
-
/** Domain scope. Default: omitted (current domain only). */
|
|
336
|
-
domain?: string;
|
|
337
|
-
/** URL path scope. Default: '/'. */
|
|
338
|
-
path?: string;
|
|
339
|
-
/** Expiration date. Mutually exclusive with maxAge. */
|
|
340
|
-
expires?: Date;
|
|
341
|
-
/** Max age in seconds. Mutually exclusive with expires. */
|
|
342
|
-
maxAge?: number;
|
|
343
|
-
/** Prevent client-side JS access. Default: true. */
|
|
344
|
-
httpOnly?: boolean;
|
|
345
|
-
/** Only send over HTTPS. Default: true. */
|
|
346
|
-
secure?: boolean;
|
|
347
|
-
/** Cross-site request policy. Default: 'lax'. */
|
|
348
|
-
sameSite?: 'strict' | 'lax' | 'none';
|
|
349
|
-
/** Partitioned (CHIPS) — isolate cookie per top-level site. Default: false. */
|
|
350
|
-
partitioned?: boolean;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const DEFAULT_COOKIE_OPTIONS: CookieOptions = {
|
|
354
|
-
path: '/',
|
|
355
|
-
httpOnly: true,
|
|
356
|
-
secure: true,
|
|
357
|
-
sameSite: 'lax',
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Write a cookie to the jar WITHOUT merging DEFAULT_COOKIE_OPTIONS.
|
|
362
|
-
* Used by setFromHeaders to preserve the original header's attributes exactly.
|
|
363
|
-
*
|
|
364
|
-
* For deletion cookies (maxAge=0), the jar entry is still created so the
|
|
365
|
-
* Set-Cookie header is emitted, but the cookie is NOT added to the read map
|
|
366
|
-
* (it would be misleading — the cookie is being deleted).
|
|
367
|
-
*/
|
|
368
|
-
function setRaw(
|
|
369
|
-
store: RequestContextStore,
|
|
370
|
-
readMap: Map<string, string>,
|
|
371
|
-
name: string,
|
|
372
|
-
value: string,
|
|
373
|
-
options: CookieOptions
|
|
374
|
-
): void {
|
|
375
|
-
store.cookieJar.set(name, { name, value, options });
|
|
376
|
-
// Deletion cookies (Max-Age=0) should not appear in the read map
|
|
377
|
-
if (options.maxAge === 0) {
|
|
378
|
-
readMap.delete(name);
|
|
379
|
-
} else {
|
|
380
|
-
readMap.set(name, value);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Parse a raw `Set-Cookie` header string into name, value, and options.
|
|
386
|
-
* Handles all standard attributes: Path, Domain, Max-Age, Expires,
|
|
387
|
-
* SameSite, Secure, HttpOnly, Partitioned.
|
|
388
|
-
*
|
|
389
|
-
* Does NOT apply DEFAULT_COOKIE_OPTIONS — the caller decides whether
|
|
390
|
-
* to merge defaults (e.g. `set()` does, but `setRaw()` should preserve
|
|
391
|
-
* the original header's intent).
|
|
392
|
-
*/
|
|
393
|
-
function parseSetCookie(
|
|
394
|
-
header: string
|
|
395
|
-
): { name: string; value: string; options: CookieOptions } | null {
|
|
396
|
-
const segments = header.split(';');
|
|
397
|
-
const nameValue = segments[0];
|
|
398
|
-
const eqIdx = nameValue.indexOf('=');
|
|
399
|
-
if (eqIdx <= 0) return null;
|
|
400
|
-
|
|
401
|
-
const name = nameValue.slice(0, eqIdx).trim();
|
|
402
|
-
const value = nameValue.slice(eqIdx + 1).trim();
|
|
403
|
-
const options: CookieOptions = {};
|
|
404
|
-
|
|
405
|
-
for (let i = 1; i < segments.length; i++) {
|
|
406
|
-
const seg = segments[i].trim();
|
|
407
|
-
if (!seg) continue;
|
|
408
|
-
const [attrName, ...rest] = seg.split('=');
|
|
409
|
-
const key = attrName.trim().toLowerCase();
|
|
410
|
-
const val = rest.join('=').trim();
|
|
411
|
-
switch (key) {
|
|
412
|
-
case 'path':
|
|
413
|
-
options.path = val || '/';
|
|
414
|
-
break;
|
|
415
|
-
case 'domain':
|
|
416
|
-
options.domain = val;
|
|
417
|
-
break;
|
|
418
|
-
case 'max-age':
|
|
419
|
-
options.maxAge = Number(val);
|
|
420
|
-
break;
|
|
421
|
-
case 'expires':
|
|
422
|
-
options.expires = new Date(val);
|
|
423
|
-
break;
|
|
424
|
-
case 'samesite':
|
|
425
|
-
options.sameSite = val.toLowerCase() as 'strict' | 'lax' | 'none';
|
|
426
|
-
break;
|
|
427
|
-
case 'secure':
|
|
428
|
-
options.secure = true;
|
|
429
|
-
break;
|
|
430
|
-
case 'httponly':
|
|
431
|
-
options.httpOnly = true;
|
|
432
|
-
break;
|
|
433
|
-
case 'partitioned':
|
|
434
|
-
options.partitioned = true;
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return { name, value, options };
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Cookie accessor returned by `getCookies()`.
|
|
444
|
-
*
|
|
445
|
-
* Read methods are always available. Mutation methods throw in read-only
|
|
446
|
-
* contexts (access.ts, server components).
|
|
447
|
-
*/
|
|
448
|
-
export interface RequestCookies {
|
|
449
|
-
/** Get a cookie value by name. Returns undefined if not present. */
|
|
450
|
-
get(name: string): string | undefined;
|
|
451
|
-
/** Check if a cookie exists. */
|
|
452
|
-
has(name: string): boolean;
|
|
453
|
-
/** Get all cookies as an array of { name, value } pairs. */
|
|
454
|
-
getAll(): Array<{ name: string; value: string }>;
|
|
455
|
-
/** Number of cookies. */
|
|
456
|
-
readonly size: number;
|
|
457
|
-
/** Set a cookie. Only available in mutable contexts (middleware, actions, route handlers). */
|
|
458
|
-
set(name: string, value: string, options?: CookieOptions): void;
|
|
459
|
-
/**
|
|
460
|
-
* Copy all `Set-Cookie` headers from a `Headers` object.
|
|
461
|
-
* Parses each header and forwards name, value, and all attributes
|
|
462
|
-
* (path, domain, max-age, expires, sameSite, secure, httpOnly, partitioned).
|
|
463
|
-
*
|
|
464
|
-
* Useful when forwarding cookies from an internal `fetch()` or auth handler:
|
|
465
|
-
* ```ts
|
|
466
|
-
* const response = await auth.handler(req);
|
|
467
|
-
* getCookies().then(c => c.setFromHeaders(response.headers));
|
|
468
|
-
* ```
|
|
469
|
-
*/
|
|
470
|
-
setFromHeaders(headers: Headers): void;
|
|
471
|
-
/** Delete a cookie. Only available in mutable contexts. */
|
|
472
|
-
delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void;
|
|
473
|
-
/** Delete all cookies. Only available in mutable contexts. */
|
|
474
|
-
clear(): void;
|
|
475
|
-
/** Serialize cookies as a Cookie header string. */
|
|
476
|
-
toString(): string;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
157
|
// ─── Framework-Internal Helpers ───────────────────────────────────────────
|
|
480
158
|
|
|
481
159
|
/**
|
|
482
160
|
* Run a callback within a request context. Used by the pipeline to establish
|
|
483
161
|
* per-request ALS scope so that `getHeaders()` and `getCookies()` work.
|
|
484
162
|
*
|
|
163
|
+
* If the request was previously registered via `seedRequestCookies`, the
|
|
164
|
+
* resulting context's `parsedCookies` map is initialized from the seed and
|
|
165
|
+
* the raw `cookieHeader` is left empty — `parseCookieHeader` is never called
|
|
166
|
+
* for that request. This is the no-JS form-rerender path. See TIM-868.
|
|
167
|
+
*
|
|
485
168
|
* @param req - The incoming Request object.
|
|
486
169
|
* @param fn - The function to run within the request context.
|
|
487
170
|
*/
|
|
488
171
|
export function runWithRequestContext<T>(req: Request, fn: () => T): T {
|
|
489
172
|
const originalCopy = new Headers(req.headers);
|
|
490
173
|
const parsedUrl = new URL(req.url);
|
|
174
|
+
const seed = consumeSeededCookies(req);
|
|
491
175
|
const store: RequestContextStore = {
|
|
492
176
|
headers: freezeHeaders(req.headers),
|
|
493
177
|
originalHeaders: originalCopy,
|
|
494
|
-
|
|
495
|
-
|
|
178
|
+
// When seeded, leave the raw header empty — parseCookieHeader is the
|
|
179
|
+
// exact code path the smuggling primitive abused, and lazy parsing is
|
|
180
|
+
// gated on `parsedCookies` being undefined.
|
|
181
|
+
cookieHeader: seed ? '' : (req.headers.get('cookie') ?? ''),
|
|
182
|
+
parsedCookies: seed,
|
|
183
|
+
searchParams: parsedUrl.searchParams,
|
|
496
184
|
searchString: parsedUrl.search,
|
|
497
185
|
cookieJar: new Map(),
|
|
498
186
|
flushed: false,
|
|
@@ -527,47 +215,6 @@ export function markResponseFlushed(): void {
|
|
|
527
215
|
}
|
|
528
216
|
}
|
|
529
217
|
|
|
530
|
-
/**
|
|
531
|
-
* Build a Map of cookie name → value reflecting the current request's
|
|
532
|
-
* read-your-own-writes state. Includes incoming cookies plus any
|
|
533
|
-
* mutations from getCookies().set() / getCookies().delete() in the same request.
|
|
534
|
-
*
|
|
535
|
-
* Used by SSR renderers to populate NavContext.cookies so that
|
|
536
|
-
* useCookie()'s server snapshot matches the actual response state.
|
|
537
|
-
*
|
|
538
|
-
* See design/29-cookies.md §"Read-Your-Own-Writes"
|
|
539
|
-
* See design/triage/TIM-441-cookie-api-triage.md §4
|
|
540
|
-
*/
|
|
541
|
-
export function getCookiesForSsr(): Map<string, string> {
|
|
542
|
-
const store = requestContextAls.getStore();
|
|
543
|
-
if (!store) {
|
|
544
|
-
throw new Error('[timber] getCookiesForSsr() called outside of a request context.');
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Trigger lazy parsing if not yet done
|
|
548
|
-
if (!store.parsedCookies) {
|
|
549
|
-
store.parsedCookies = parseCookieHeader(store.cookieHeader);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// The parsedCookies map already reflects read-your-own-writes:
|
|
553
|
-
// - getCookies().set() updates the map via map.set(name, value)
|
|
554
|
-
// - getCookies().delete() removes from the map via map.delete(name)
|
|
555
|
-
// Return a copy so callers can't mutate the internal map.
|
|
556
|
-
return new Map(store.parsedCookies);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Collect all Set-Cookie headers from the cookie jar.
|
|
561
|
-
* Called by the framework at flush time to apply cookies to the response.
|
|
562
|
-
*
|
|
563
|
-
* Returns an array of serialized Set-Cookie header values.
|
|
564
|
-
*/
|
|
565
|
-
export function getSetCookieHeaders(): string[] {
|
|
566
|
-
const store = requestContextAls.getStore();
|
|
567
|
-
if (!store) return [];
|
|
568
|
-
return Array.from(store.cookieJar.values()).map(serializeCookieEntry);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
218
|
/**
|
|
572
219
|
* Apply middleware-injected request headers to the current request context.
|
|
573
220
|
*
|
|
@@ -635,55 +282,3 @@ function freezeHeaders(source: Headers): Headers {
|
|
|
635
282
|
},
|
|
636
283
|
});
|
|
637
284
|
}
|
|
638
|
-
|
|
639
|
-
// ─── Cookie Helpers ───────────────────────────────────────────────────────
|
|
640
|
-
|
|
641
|
-
/** Throw if cookie mutation is attempted in a read-only context. */
|
|
642
|
-
function assertMutable(store: RequestContextStore, method: string): void {
|
|
643
|
-
if (!store.mutableContext) {
|
|
644
|
-
throw new Error(
|
|
645
|
-
`(timber] getCookies().${method}() cannot be called in this context.\n` +
|
|
646
|
-
` Set cookies in middleware.ts, server actions, or route.ts handlers.`
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/**
|
|
652
|
-
* Parse a Cookie header string into a Map of name → value pairs.
|
|
653
|
-
* Follows RFC 6265 §4.2.1: cookies are semicolon-separated key=value pairs.
|
|
654
|
-
*/
|
|
655
|
-
function parseCookieHeader(header: string): Map<string, string> {
|
|
656
|
-
const map = new Map<string, string>();
|
|
657
|
-
if (!header) return map;
|
|
658
|
-
|
|
659
|
-
for (const pair of header.split(';')) {
|
|
660
|
-
const eqIndex = pair.indexOf('=');
|
|
661
|
-
if (eqIndex === -1) continue;
|
|
662
|
-
const name = pair.slice(0, eqIndex).trim();
|
|
663
|
-
const value = pair.slice(eqIndex + 1).trim();
|
|
664
|
-
if (name) {
|
|
665
|
-
map.set(name, value);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return map;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/** Serialize a CookieEntry into a Set-Cookie header value. */
|
|
673
|
-
function serializeCookieEntry(entry: CookieEntry): string {
|
|
674
|
-
const parts = [`${entry.name}=${entry.value}`];
|
|
675
|
-
const opts = entry.options;
|
|
676
|
-
|
|
677
|
-
if (opts.domain) parts.push(`Domain=${opts.domain}`);
|
|
678
|
-
if (opts.path) parts.push(`Path=${opts.path}`);
|
|
679
|
-
if (opts.expires) parts.push(`Expires=${opts.expires.toUTCString()}`);
|
|
680
|
-
if (opts.maxAge !== undefined) parts.push(`Max-Age=${opts.maxAge}`);
|
|
681
|
-
if (opts.httpOnly) parts.push('HttpOnly');
|
|
682
|
-
if (opts.secure) parts.push('Secure');
|
|
683
|
-
if (opts.sameSite) {
|
|
684
|
-
parts.push(`SameSite=${opts.sameSite.charAt(0).toUpperCase()}${opts.sameSite.slice(1)}`);
|
|
685
|
-
}
|
|
686
|
-
if (opts.partitioned) parts.push('Partitioned');
|
|
687
|
-
|
|
688
|
-
return parts.join('; ');
|
|
689
|
-
}
|
|
@@ -27,9 +27,13 @@ import type { Metadata } from './types.js';
|
|
|
27
27
|
import { METADATA_ROUTE_CONVENTIONS, getMetadataRouteAutoLink } from './metadata-routes.js';
|
|
28
28
|
import { DenySignal, RedirectSignal } from './primitives.js';
|
|
29
29
|
import { AccessGate } from './access-gate.js';
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
import {
|
|
31
|
+
PageDenyBoundary,
|
|
32
|
+
buildDenyPageChain,
|
|
33
|
+
renderMatchingDenyPage,
|
|
34
|
+
setDenyStatus,
|
|
35
|
+
} from './deny-boundary.js';
|
|
36
|
+
import type { DenyPageEntry } from './deny-boundary.js';
|
|
33
37
|
import { resolveSlotElement } from './slot-resolver.js';
|
|
34
38
|
import { SegmentProvider } from '../client/segment-context.js';
|
|
35
39
|
|
|
@@ -213,7 +217,7 @@ export async function buildRouteElement(
|
|
|
213
217
|
interception?: InterceptionContext,
|
|
214
218
|
clientStateTree?: Set<string> | null
|
|
215
219
|
): Promise<RouteElementResult> {
|
|
216
|
-
const segments = match.segments
|
|
220
|
+
const segments = match.segments;
|
|
217
221
|
|
|
218
222
|
// Load all modules along the segment chain
|
|
219
223
|
const metadataEntries: Array<{ metadata: Metadata; isPage: boolean }> = [];
|