@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
|
@@ -23,18 +23,18 @@ import type { Codec } from '../codec.js';
|
|
|
23
23
|
// dynamic `await import()` at call time because the async microtask from the
|
|
24
24
|
// dynamic import loses AsyncLocalStorage context in React's RSC Flight renderer,
|
|
25
25
|
// breaking getSearchParams() in parallel slot pages. See TIM-523.
|
|
26
|
-
let _getSearchParamsFn: (() =>
|
|
26
|
+
let _getSearchParamsFn: (() => URLSearchParams) | undefined;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Register the getSearchParams function. Called once at module load time
|
|
30
30
|
* from request-context.ts to avoid dynamic import at call time.
|
|
31
31
|
* @internal
|
|
32
32
|
*/
|
|
33
|
-
export function _setGetSearchParamsFn(fn: () =>
|
|
33
|
+
export function _setGetSearchParamsFn(fn: () => URLSearchParams): void {
|
|
34
34
|
_getSearchParamsFn = fn;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function getSearchParamsFromAls():
|
|
37
|
+
function getSearchParamsFromAls(): URLSearchParams {
|
|
38
38
|
if (!_getSearchParamsFn) {
|
|
39
39
|
throw new Error(
|
|
40
40
|
'[timber] searchParams.get() is only available on the server. ' +
|
|
@@ -111,19 +111,18 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
|
|
|
111
111
|
/**
|
|
112
112
|
* Get typed search params from the current request context (ALS-backed).
|
|
113
113
|
*
|
|
114
|
-
* Server-only
|
|
115
|
-
* Throws on client.
|
|
116
|
-
* export and the server helper.
|
|
114
|
+
* Server-only, sync. Reads getSearchParams() from ALS and parses through codecs.
|
|
115
|
+
* Throws on client.
|
|
117
116
|
*
|
|
118
117
|
* ```tsx
|
|
119
118
|
* // app/products/page.tsx
|
|
120
119
|
* import { searchParams } from './params'
|
|
121
|
-
* export default
|
|
122
|
-
* const { page, category } =
|
|
120
|
+
* export default function Page() {
|
|
121
|
+
* const { page, category } = searchParams.get()
|
|
123
122
|
* }
|
|
124
123
|
* ```
|
|
125
124
|
*/
|
|
126
|
-
get():
|
|
125
|
+
get(): T;
|
|
127
126
|
|
|
128
127
|
/** Client hook — reads current URL params and returns typed values + setter. */
|
|
129
128
|
useQueryStates(options?: QueryStatesOptions): [T, SetParams<T>];
|
|
@@ -271,11 +270,68 @@ function validateDefaults(codecMap: Record<string, SearchParamCodec<unknown>>):
|
|
|
271
270
|
* })
|
|
272
271
|
* ```
|
|
273
272
|
*/
|
|
273
|
+
/**
|
|
274
|
+
* Overload: accept a Standard Schema object schema (e.g., z.object({...})).
|
|
275
|
+
*
|
|
276
|
+
* The schema must have a `.shape` property whose values are themselves
|
|
277
|
+
* Standard Schema objects. Each shape property becomes a field codec
|
|
278
|
+
* via fromSchema().
|
|
279
|
+
*
|
|
280
|
+
* ```ts
|
|
281
|
+
* const searchParams = defineSearchParams(
|
|
282
|
+
* z.object({ page: z.coerce.number().default(1), q: z.string().optional() })
|
|
283
|
+
* )
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
export function defineSearchParams<T extends Record<string, unknown>>(
|
|
287
|
+
schema: StandardSchemaV1<T> & { shape: Record<string, StandardSchemaV1<unknown>> }
|
|
288
|
+
): SearchParamsDefinition<T>;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Overload: accept a map of codecs and/or Standard Schema objects.
|
|
292
|
+
*/
|
|
274
293
|
export function defineSearchParams<C extends Record<string, SearchParamField>>(
|
|
275
294
|
codecs: C
|
|
276
|
-
): SearchParamsDefinition<{ [K in keyof C]: InferField<C[K]> }
|
|
277
|
-
|
|
295
|
+
): SearchParamsDefinition<{ [K in keyof C]: InferField<C[K]> }>;
|
|
296
|
+
|
|
297
|
+
export function defineSearchParams(
|
|
298
|
+
codecsOrSchema:
|
|
299
|
+
| Record<string, SearchParamField>
|
|
300
|
+
| (StandardSchemaV1<unknown> & { shape: Record<string, StandardSchemaV1<unknown>> })
|
|
301
|
+
): SearchParamsDefinition<Record<string, unknown>> {
|
|
302
|
+
// Detect Standard Schema object with .shape (e.g., z.object(...))
|
|
303
|
+
if (isStandardSchema(codecsOrSchema) && hasShape(codecsOrSchema)) {
|
|
304
|
+
const fieldCodecs: Record<string, SearchParamField> = {};
|
|
305
|
+
for (const [key, fieldSchema] of Object.entries(codecsOrSchema.shape)) {
|
|
306
|
+
if (isStandardSchema(fieldSchema)) {
|
|
307
|
+
fieldCodecs[key] = fieldSchema;
|
|
308
|
+
} else {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`[timber] defineSearchParams: field '${key}' in schema.shape is not a Standard Schema. ` +
|
|
311
|
+
`All shape properties must be Standard Schema objects (Zod, Valibot, ArkType).`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return defineSearchParamsFromMap(fieldCodecs);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return defineSearchParamsFromMap(codecsOrSchema as Record<string, SearchParamField>);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Check if a schema has a .shape property with object-type values. */
|
|
322
|
+
function hasShape(schema: unknown): schema is { shape: Record<string, unknown> } {
|
|
323
|
+
return (
|
|
324
|
+
typeof schema === 'object' &&
|
|
325
|
+
schema !== null &&
|
|
326
|
+
'shape' in schema &&
|
|
327
|
+
typeof (schema as { shape: unknown }).shape === 'object' &&
|
|
328
|
+
(schema as { shape: unknown }).shape !== null
|
|
329
|
+
);
|
|
330
|
+
}
|
|
278
331
|
|
|
332
|
+
function defineSearchParamsFromMap(
|
|
333
|
+
codecs: Record<string, SearchParamField>
|
|
334
|
+
): SearchParamsDefinition<Record<string, unknown>> {
|
|
279
335
|
const resolvedCodecs: Record<string, SearchParamCodec<unknown>> = {};
|
|
280
336
|
const urlKeys: Record<string, string> = {};
|
|
281
337
|
|
|
@@ -290,7 +346,7 @@ export function defineSearchParams<C extends Record<string, SearchParamField>>(
|
|
|
290
346
|
// Validate that all codecs handle absent params
|
|
291
347
|
validateDefaults(resolvedCodecs);
|
|
292
348
|
|
|
293
|
-
return buildDefinition
|
|
349
|
+
return buildDefinition(resolvedCodecs as unknown as CodecMap<Record<string, unknown>>, urlKeys);
|
|
294
350
|
}
|
|
295
351
|
|
|
296
352
|
// ---------------------------------------------------------------------------
|
|
@@ -453,15 +509,15 @@ function buildDefinition<T extends Record<string, unknown>>(
|
|
|
453
509
|
|
|
454
510
|
// ---- get ----
|
|
455
511
|
// ALS-backed: reads getSearchParams() from the current request context
|
|
456
|
-
// and parses through codecs. Server-only
|
|
457
|
-
|
|
512
|
+
// and parses through codecs. Server-only, sync.
|
|
513
|
+
function get(): T {
|
|
458
514
|
if (typeof window !== 'undefined') {
|
|
459
515
|
throw new Error(
|
|
460
516
|
'[timber] searchParams.get() is server-only. ' +
|
|
461
517
|
'Use searchParams.useQueryStates() on the client.'
|
|
462
518
|
);
|
|
463
519
|
}
|
|
464
|
-
const raw =
|
|
520
|
+
const raw = getSearchParamsFromAls();
|
|
465
521
|
return parseSync(raw);
|
|
466
522
|
}
|
|
467
523
|
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* Design doc: design/23-search-params.md
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { SearchParamCodec, SearchParamCodecWithUrlKey } from './define.js';
|
|
11
|
+
import type { SearchParamCodec, SearchParamCodecWithUrlKey, SearchParamField } from './define.js';
|
|
12
|
+
import { isCodec, isStandardSchema, fromSchema } from '../schema-bridge.js';
|
|
12
13
|
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// withDefault
|
|
@@ -74,9 +75,15 @@ export function withDefault<T>(
|
|
|
74
75
|
* ```
|
|
75
76
|
*/
|
|
76
77
|
export function withUrlKey<T>(
|
|
77
|
-
|
|
78
|
+
codecOrSchema: SearchParamField<T>,
|
|
78
79
|
urlKey: string
|
|
79
80
|
): SearchParamCodecWithUrlKey<T> {
|
|
81
|
+
// Auto-detect Standard Schema (Zod, Valibot, ArkType) and wrap
|
|
82
|
+
const codec: SearchParamCodec<T> = isCodec(codecOrSchema)
|
|
83
|
+
? codecOrSchema
|
|
84
|
+
: isStandardSchema(codecOrSchema)
|
|
85
|
+
? fromSchema(codecOrSchema)
|
|
86
|
+
: (codecOrSchema as SearchParamCodec<T>);
|
|
80
87
|
return {
|
|
81
88
|
parse: codec.parse.bind(codec),
|
|
82
89
|
serialize: codec.serialize.bind(codec),
|
|
@@ -27,22 +27,44 @@ import {
|
|
|
27
27
|
|
|
28
28
|
// Same pattern as search-params: eagerly registered at server startup
|
|
29
29
|
// to avoid dynamic imports that lose ALS context. See TIM-523.
|
|
30
|
-
let _getSegmentParamsFn: (() =>
|
|
30
|
+
let _getSegmentParamsFn: (() => Record<string, string | string[]>) | undefined;
|
|
31
|
+
let _useSegmentParamsHook: (() => Record<string, string | string[]>) | undefined;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register the client useSegmentParams hook.
|
|
35
|
+
* Called by client entry at module load time to avoid require() at call time.
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export function _registerUseSegmentParams(hook: () => Record<string, string | string[]>): void {
|
|
39
|
+
_useSegmentParamsHook = hook;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Self-register on the client when the barrel hasn't been imported yet.
|
|
43
|
+
// Handles the edge case where a client component imports only from params.ts
|
|
44
|
+
// without importing anything from @timber-js/app/client (which runs the
|
|
45
|
+
// registration). The dynamic import resolves before React hydration in Vite.
|
|
46
|
+
if (typeof window !== 'undefined' && !_useSegmentParamsHook) {
|
|
47
|
+
import('../client/use-segment-params.js').then((mod) => {
|
|
48
|
+
if (!_useSegmentParamsHook) {
|
|
49
|
+
_useSegmentParamsHook = mod.useSegmentParams;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
31
53
|
|
|
32
54
|
/**
|
|
33
55
|
* Register the getSegmentParams function. Called once at module load time
|
|
34
56
|
* from request-context.ts to avoid dynamic import at call time.
|
|
35
57
|
* @internal
|
|
36
58
|
*/
|
|
37
|
-
export function _setGetSegmentParamsFn(fn: () =>
|
|
59
|
+
export function _setGetSegmentParamsFn(fn: () => Record<string, string | string[]>): void {
|
|
38
60
|
_getSegmentParamsFn = fn;
|
|
39
61
|
}
|
|
40
62
|
|
|
41
|
-
function getSegmentParamsFromAls():
|
|
63
|
+
function getSegmentParamsFromAls(): Record<string, string | string[]> {
|
|
42
64
|
if (!_getSegmentParamsFn) {
|
|
43
65
|
throw new Error(
|
|
44
66
|
'[timber] segmentParams.get() is only available on the server. ' +
|
|
45
|
-
'Use useSegmentParams() on the client.'
|
|
67
|
+
'Use segmentParams.useSegmentParams() on the client.'
|
|
46
68
|
);
|
|
47
69
|
}
|
|
48
70
|
return _getSegmentParamsFn();
|
|
@@ -77,8 +99,7 @@ export interface ParamsDefinition<T extends Record<string, unknown>> {
|
|
|
77
99
|
/**
|
|
78
100
|
* Get typed segment params from the current request context (ALS).
|
|
79
101
|
*
|
|
80
|
-
* Server-only.
|
|
81
|
-
* this definition's codecs, returning fully typed params.
|
|
102
|
+
* Server-only, sync.
|
|
82
103
|
*
|
|
83
104
|
* ```ts
|
|
84
105
|
* // app/products/[id]/params.ts
|
|
@@ -86,12 +107,25 @@ export interface ParamsDefinition<T extends Record<string, unknown>> {
|
|
|
86
107
|
*
|
|
87
108
|
* // app/products/[id]/page.tsx
|
|
88
109
|
* import { segmentParams } from './params'
|
|
89
|
-
* export default
|
|
90
|
-
* const { id } =
|
|
110
|
+
* export default function Page() {
|
|
111
|
+
* const { id } = segmentParams.get() // id: number
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
get(): T;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Client hook for accessing typed segment params reactively.
|
|
119
|
+
*
|
|
120
|
+
* ```tsx
|
|
121
|
+
* 'use client'
|
|
122
|
+
* import { segmentParams } from './params'
|
|
123
|
+
* export function ProductHeader() {
|
|
124
|
+
* const { id } = segmentParams.useSegmentParams()
|
|
91
125
|
* }
|
|
92
126
|
* ```
|
|
93
127
|
*/
|
|
94
|
-
|
|
128
|
+
useSegmentParams(): T;
|
|
95
129
|
|
|
96
130
|
/** Read-only codec map. */
|
|
97
131
|
codecs: { [K in keyof T]: Codec<T[K]> };
|
|
@@ -250,28 +284,47 @@ export function defineSegmentParams<C extends Record<string, ParamField>>(
|
|
|
250
284
|
|
|
251
285
|
// ---- get ----
|
|
252
286
|
// ALS-backed: reads segment params from the current request context.
|
|
253
|
-
// Server-only
|
|
287
|
+
// Server-only, sync.
|
|
254
288
|
//
|
|
255
289
|
// The pipeline already coerces params via coerceSegmentParams() which
|
|
256
290
|
// calls parse() and stores typed values in ALS via setSegmentParams().
|
|
257
291
|
// We return those directly instead of re-parsing, because codecs may
|
|
258
292
|
// not be idempotent (e.g., a codec that only accepts raw strings would
|
|
259
293
|
// throw if given an already-parsed value). See TIM-574.
|
|
260
|
-
|
|
294
|
+
function get(): T {
|
|
261
295
|
if (typeof window !== 'undefined') {
|
|
262
296
|
throw new Error(
|
|
263
|
-
'[timber] segmentParams.get() is server-only. ' +
|
|
297
|
+
'[timber] segmentParams.get() is server-only. ' +
|
|
298
|
+
'Use segmentParams.useSegmentParams() on the client.'
|
|
264
299
|
);
|
|
265
300
|
}
|
|
266
|
-
const params =
|
|
301
|
+
const params = getSegmentParamsFromAls();
|
|
267
302
|
// params are already coerced by the pipeline — return as-is.
|
|
268
303
|
return params as unknown as T;
|
|
269
304
|
}
|
|
270
305
|
|
|
306
|
+
// ---- useSegmentParams ----
|
|
307
|
+
// Client hook that reads typed params from the navigation context.
|
|
308
|
+
// Delegates to the existing useSegmentParams hook from use-segment-params.ts.
|
|
309
|
+
function useSegmentParams(): T {
|
|
310
|
+
if (!_useSegmentParamsHook) {
|
|
311
|
+
throw new Error(
|
|
312
|
+
'[timber] segmentParams.useSegmentParams() requires @timber-js/app/client to be loaded. ' +
|
|
313
|
+
'This hook can only be used in client components.'
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const raw = _useSegmentParamsHook();
|
|
317
|
+
// Params are already coerced by the server pipeline before being sent
|
|
318
|
+
// to the client — return as-is. Re-parsing would break non-idempotent
|
|
319
|
+
// codecs (e.g., z.coerce.number() on an already-parsed number). See TIM-574.
|
|
320
|
+
return raw as unknown as T;
|
|
321
|
+
}
|
|
322
|
+
|
|
271
323
|
const definition: ParamsDefinition<T> = {
|
|
272
324
|
parse,
|
|
273
325
|
serialize,
|
|
274
326
|
get,
|
|
327
|
+
useSegmentParams,
|
|
275
328
|
codecs: resolvedCodecs as { [K in keyof T]: Codec<T[K]> },
|
|
276
329
|
};
|
|
277
330
|
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { DenySignal, RedirectSignal } from './primitives.js';
|
|
17
|
-
import type { AccessGateProps, SlotAccessGateProps
|
|
17
|
+
import type { AccessGateProps, SlotAccessGateProps } from './tree-builder.js';
|
|
18
18
|
import { withSpan, setSpanAttribute } from './tracing.js';
|
|
19
19
|
import { isDebug } from './debug.js';
|
|
20
|
-
import type { DenyPageEntry } from './deny-
|
|
21
|
-
import { renderMatchingDenyPage, setDenyStatus } from './deny-
|
|
20
|
+
import type { DenyPageEntry } from './deny-boundary.js';
|
|
21
|
+
import { renderMatchingDenyPage, setDenyStatus } from './deny-boundary.js';
|
|
22
|
+
import type { ReactNode } from 'react';
|
|
22
23
|
|
|
23
24
|
// ─── AccessGate ─────────────────────────────────────────────────────────────
|
|
24
25
|
|
|
@@ -36,7 +37,7 @@ import { renderMatchingDenyPage, setDenyStatus } from './deny-page-resolver.js';
|
|
|
36
37
|
* access.ts is a pure gate — return values are discarded. The layout below
|
|
37
38
|
* gets the same data by calling the same cached functions (React.cache dedup).
|
|
38
39
|
*/
|
|
39
|
-
export function AccessGate(props: AccessGateProps):
|
|
40
|
+
export function AccessGate(props: AccessGateProps): ReactNode | Promise<ReactNode> {
|
|
40
41
|
const { accessFn, segmentName, verdict, denyPages, children } = props;
|
|
41
42
|
|
|
42
43
|
// Fast path: replay pre-computed verdict from the pre-render pass.
|
|
@@ -61,8 +62,8 @@ async function accessGateFallback(
|
|
|
61
62
|
accessFn: AccessGateProps['accessFn'],
|
|
62
63
|
segmentName: AccessGateProps['segmentName'],
|
|
63
64
|
denyPages: DenyPageEntry[] | undefined,
|
|
64
|
-
children:
|
|
65
|
-
): Promise<
|
|
65
|
+
children: ReactNode
|
|
66
|
+
): Promise<ReactNode> {
|
|
66
67
|
try {
|
|
67
68
|
await withSpan('timber.access', { 'timber.segment': segmentName ?? 'unknown' }, async () => {
|
|
68
69
|
try {
|
|
@@ -114,7 +115,7 @@ async function accessGateFallback(
|
|
|
114
115
|
* redirect() in slot access.ts is a dev-mode error — redirecting from a
|
|
115
116
|
* slot doesn't make architectural sense.
|
|
116
117
|
*/
|
|
117
|
-
export async function SlotAccessGate(props: SlotAccessGateProps): Promise<
|
|
118
|
+
export async function SlotAccessGate(props: SlotAccessGateProps): Promise<ReactNode> {
|
|
118
119
|
const { accessFn, DeniedComponent, slotName, createElement, defaultFallback, children } = props;
|
|
119
120
|
|
|
120
121
|
try {
|
|
@@ -175,7 +176,7 @@ function buildDeniedFallback(
|
|
|
175
176
|
slotName: string,
|
|
176
177
|
data: unknown,
|
|
177
178
|
createElement: SlotAccessGateProps['createElement']
|
|
178
|
-
):
|
|
179
|
+
): ReactNode | null {
|
|
179
180
|
if (!DeniedComponent) return null;
|
|
180
181
|
return createElement(DeniedComponent, {
|
|
181
182
|
slot: slotName,
|
|
@@ -22,12 +22,8 @@ import {
|
|
|
22
22
|
|
|
23
23
|
import { validateCsrf, type CsrfConfig } from './csrf.js';
|
|
24
24
|
import { executeAction, type RevalidateRenderer } from './actions.js';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
setMutableCookieContext,
|
|
28
|
-
getSetCookieHeaders,
|
|
29
|
-
getCookiesForSsr,
|
|
30
|
-
} from './request-context.js';
|
|
25
|
+
import { runWithRequestContext, setMutableCookieContext } from './request-context.js';
|
|
26
|
+
import { getSetCookieHeaders, getCookiesForSsr } from './cookie-context.js';
|
|
31
27
|
import { handleActionError } from './action-client.js';
|
|
32
28
|
import { enforceBodyLimits, enforceFieldLimit, type BodyLimitsConfig } from './body-limits.js';
|
|
33
29
|
import { parseFormData } from './form-data.js';
|
|
@@ -72,6 +68,12 @@ const RSC_CONTENT_TYPE = 'text/x-component';
|
|
|
72
68
|
* - With JS: POST with `x-rsc-action` header (client callServer dispatch)
|
|
73
69
|
* - Without JS: POST with form data containing `$ACTION_REF` or `$ACTION_KEY`
|
|
74
70
|
* (React's progressive enhancement hidden fields)
|
|
71
|
+
*
|
|
72
|
+
* **Important:** This function returns true for ANY POST with a form
|
|
73
|
+
* Content-Type, including non-action POSTs to route.ts API handlers.
|
|
74
|
+
* The caller (wrap-action-dispatch.ts) MUST check the matched route type
|
|
75
|
+
* before entering the action path — route.ts matches skip action detection
|
|
76
|
+
* entirely so their body is not pre-parsed. See TIM-870.
|
|
75
77
|
*/
|
|
76
78
|
export function isActionRequest(req: Request): boolean {
|
|
77
79
|
if (req.method !== 'POST') return false;
|
|
@@ -90,25 +92,6 @@ export function isActionRequest(req: Request): boolean {
|
|
|
90
92
|
|
|
91
93
|
// ─── Handler ──────────────────────────────────────────────────────────────
|
|
92
94
|
|
|
93
|
-
/**
|
|
94
|
-
* Serialize a `Map<string, string>` of cookie name → value as a `Cookie:`
|
|
95
|
-
* request header value. Format: `name1=value1; name2=value2`. Matches the
|
|
96
|
-
* format `parseCookieHeader` in `request-context.ts` reads with, so the
|
|
97
|
-
* rerender pipeline can parse it back into the same RYW state.
|
|
98
|
-
*
|
|
99
|
-
* Used to thread the post-action cookie state from the action's ALS scope
|
|
100
|
-
* into the rerender pipeline's fresh ALS scope. Cookies marked for deletion
|
|
101
|
-
* are already absent from `getCookiesForSsr()`'s map, so they naturally drop
|
|
102
|
-
* out of the synthesized header. See TIM-837.
|
|
103
|
-
*/
|
|
104
|
-
function serializeCookieMapAsHeader(cookies: Map<string, string>): string {
|
|
105
|
-
const parts: string[] = [];
|
|
106
|
-
for (const [name, value] of cookies) {
|
|
107
|
-
parts.push(`${name}=${value}`);
|
|
108
|
-
}
|
|
109
|
-
return parts.join('; ');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
95
|
/**
|
|
113
96
|
* Signal from handleFormAction to re-render the page with flash data instead of redirecting.
|
|
114
97
|
*
|
|
@@ -120,16 +103,19 @@ function serializeCookieMapAsHeader(cookies: Map<string, string>): string {
|
|
|
120
103
|
* final HTML response. Without this, cookies set inside the action are
|
|
121
104
|
* silently dropped from the response. See TIM-836 (LOCAL-740).
|
|
122
105
|
*
|
|
123
|
-
* - `
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
106
|
+
* - `cookies`: the post-action read-your-own-writes view of the cookie
|
|
107
|
+
* jar, as the same `Map<string, string>` shape used by the request
|
|
108
|
+
* context's `parsedCookies`. The rerender dispatcher seeds this map
|
|
109
|
+
* directly into the rerender request context via `seedRequestCookies`,
|
|
110
|
+
* bypassing any string round-trip through a `Cookie:` header. The
|
|
111
|
+
* direct-Map seed eliminates the value-smuggling primitive that the
|
|
112
|
+
* previous `cookieHeader: string` shape carried — see
|
|
113
|
+
* ONGOING_SECURITY.md H-3 (TIM-868) and TIM-837.
|
|
128
114
|
*/
|
|
129
115
|
export interface FormRerender {
|
|
130
116
|
rerender: FormFlashData;
|
|
131
117
|
setCookieHeaders: string[];
|
|
132
|
-
|
|
118
|
+
cookies: Map<string, string>;
|
|
133
119
|
}
|
|
134
120
|
|
|
135
121
|
/**
|
|
@@ -158,6 +144,12 @@ export async function handleActionRequest(
|
|
|
158
144
|
}
|
|
159
145
|
|
|
160
146
|
// CSRF validation — reject cross-origin mutation requests.
|
|
147
|
+
//
|
|
148
|
+
// Defense-in-depth: the pipeline boundary in rsc-entry/index.ts already
|
|
149
|
+
// validates Origin on every unsafe-method request before dispatch reaches
|
|
150
|
+
// here, so this call is a no-op on the happy path. It is intentionally
|
|
151
|
+
// retained so that handleActionRequest remains safe to call from any
|
|
152
|
+
// future entry point that bypasses the wrapper. See LOCAL-773.
|
|
161
153
|
const csrfResult = validateCsrf(req, config.csrf);
|
|
162
154
|
if (!csrfResult.ok) {
|
|
163
155
|
return new Response(null, { status: csrfResult.status });
|
|
@@ -202,9 +194,13 @@ export async function handleActionRequest(
|
|
|
202
194
|
}
|
|
203
195
|
} else if (result && 'rerender' in result) {
|
|
204
196
|
result.setCookieHeaders = getSetCookieHeaders();
|
|
205
|
-
// Snapshot the post-action RYW cookie state so the rerender
|
|
206
|
-
// can
|
|
207
|
-
|
|
197
|
+
// Snapshot the post-action RYW cookie state as a Map so the rerender
|
|
198
|
+
// dispatcher can seed it directly into the rerender request context
|
|
199
|
+
// via `seedRequestCookies`, with no string round-trip. See TIM-837
|
|
200
|
+
// and ONGOING_SECURITY.md H-3 (TIM-868). `getCookiesForSsr` already
|
|
201
|
+
// returns a defensive copy, so the rerender scope cannot mutate the
|
|
202
|
+
// snapshot through this reference.
|
|
203
|
+
result.cookies = getCookiesForSsr();
|
|
208
204
|
}
|
|
209
205
|
return result;
|
|
210
206
|
});
|
|
@@ -391,7 +387,7 @@ async function handleFormAction(
|
|
|
391
387
|
},
|
|
392
388
|
// Filled in by handleActionRequest before the ALS scope exits.
|
|
393
389
|
setCookieHeaders: [],
|
|
394
|
-
|
|
390
|
+
cookies: new Map(),
|
|
395
391
|
};
|
|
396
392
|
}
|
|
397
393
|
|
|
@@ -421,7 +417,7 @@ async function handleFormAction(
|
|
|
421
417
|
);
|
|
422
418
|
}
|
|
423
419
|
|
|
424
|
-
// setCookieHeaders +
|
|
425
|
-
//
|
|
426
|
-
return { rerender: actionResult, setCookieHeaders: [],
|
|
420
|
+
// setCookieHeaders + cookies are filled in by handleActionRequest before
|
|
421
|
+
// the ALS scope exits.
|
|
422
|
+
return { rerender: actionResult, setCookieHeaders: [], cookies: new Map() };
|
|
427
423
|
}
|
|
@@ -40,25 +40,25 @@ export interface RequestContextStore {
|
|
|
40
40
|
/** Original (pre-overlay) frozen headers, kept for overlay merging. */
|
|
41
41
|
originalHeaders: Headers;
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* Raw URLSearchParams for the current request.
|
|
44
44
|
* To get typed parsed params, import a search params definition and
|
|
45
45
|
* call `.parse(searchParams())`.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
searchParams: URLSearchParams;
|
|
48
48
|
/**
|
|
49
49
|
* Raw search string from the request URL (e.g. "?foo=bar&baz=1").
|
|
50
50
|
* Available synchronously for use in `redirect()` with `preserveSearchParams`.
|
|
51
51
|
*/
|
|
52
52
|
searchString: string;
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
* Coerced segment params for the current request.
|
|
55
55
|
* Set by the pipeline after route matching and param coercion, before
|
|
56
56
|
* middleware and rendering. Pages and layouts read params via
|
|
57
57
|
* `getSegmentParams()` instead of receiving them as a prop.
|
|
58
58
|
*
|
|
59
59
|
* See design/07-routing.md §"params.ts — Convention File for Typed Params"
|
|
60
60
|
*/
|
|
61
|
-
|
|
61
|
+
segmentParams?: Record<string, string | string[]>;
|
|
62
62
|
/** Outgoing Set-Cookie entries (name → serialized value + options). Last write wins. */
|
|
63
63
|
cookieJar: Map<string, CookieEntry>;
|
|
64
64
|
/** Whether the response has flushed (headers committed). */
|
|
@@ -84,7 +84,7 @@ export interface RequestContextStore {
|
|
|
84
84
|
export interface CookieEntry {
|
|
85
85
|
name: string;
|
|
86
86
|
value: string;
|
|
87
|
-
options: import('./
|
|
87
|
+
options: import('./cookie-context.js').CookieOptions;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// ─── Tracing ──────────────────────────────────────────────────────────────
|
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
* Design docs: 18-build-system.md, 06-caching.md
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { IMMUTABLE_CACHE, STATIC_CACHE } from '../adapters/shared.js';
|
|
19
|
+
|
|
20
|
+
// Re-export cache constants and generateHeadersFile from the shared adapter
|
|
21
|
+
// module. The canonical definitions live in adapters/shared.ts because
|
|
22
|
+
// adapters are loaded by Node before Vite's resolver is available — they
|
|
23
|
+
// can only import from within their own directory.
|
|
24
|
+
export { IMMUTABLE_CACHE, STATIC_CACHE, generateHeadersFile } from '../adapters/shared.js';
|
|
25
|
+
|
|
18
26
|
/**
|
|
19
27
|
* Regex matching Vite-hashed asset filenames.
|
|
20
28
|
*
|
|
@@ -30,18 +38,6 @@
|
|
|
30
38
|
*/
|
|
31
39
|
const HASHED_ASSET_RE = /[-.][\da-f]{8,}\.\w+$/;
|
|
32
40
|
|
|
33
|
-
/** One year in seconds (365 days). */
|
|
34
|
-
const ONE_YEAR = 31_536_000;
|
|
35
|
-
|
|
36
|
-
/** One hour in seconds. */
|
|
37
|
-
const ONE_HOUR = 3_600;
|
|
38
|
-
|
|
39
|
-
/** Cache-Control value for hashed (immutable) assets. */
|
|
40
|
-
export const IMMUTABLE_CACHE = `public, max-age=${ONE_YEAR}, immutable`;
|
|
41
|
-
|
|
42
|
-
/** Cache-Control value for unhashed static assets. */
|
|
43
|
-
export const STATIC_CACHE = `public, max-age=${ONE_HOUR}, must-revalidate`;
|
|
44
|
-
|
|
45
41
|
/**
|
|
46
42
|
* Check if a URL path looks like a hashed asset.
|
|
47
43
|
*/
|
|
@@ -57,25 +53,3 @@ export function isHashedAsset(pathname: string): boolean {
|
|
|
57
53
|
export function getAssetCacheControl(pathname: string): string {
|
|
58
54
|
return isHashedAsset(pathname) ? IMMUTABLE_CACHE : STATIC_CACHE;
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Generate a `_headers` file for static asset cache control.
|
|
63
|
-
*
|
|
64
|
-
* The `_headers` file is a platform convention supported by Cloudflare Workers
|
|
65
|
-
* Static Assets, Cloudflare Pages, and Netlify. It maps URL patterns to
|
|
66
|
-
* HTTP response headers.
|
|
67
|
-
*
|
|
68
|
-
* Vite places all hashed chunks under `/assets/` — these get immutable caching.
|
|
69
|
-
* Everything else (favicon.ico, robots.txt, etc.) gets a shorter cache.
|
|
70
|
-
*/
|
|
71
|
-
export function generateHeadersFile(): string {
|
|
72
|
-
return `# Auto-generated by @timber-js/app — static asset cache headers.
|
|
73
|
-
# See design/25-production-deployments.md §"CDN / Edge Cache"
|
|
74
|
-
|
|
75
|
-
/assets/*
|
|
76
|
-
Cache-Control: ${IMMUTABLE_CACHE}
|
|
77
|
-
|
|
78
|
-
/*
|
|
79
|
-
Cache-Control: ${STATIC_CACHE}
|
|
80
|
-
`;
|
|
81
|
-
}
|