@timber-js/app 0.2.0-alpha.7 → 0.2.0-alpha.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-BJARkOcu.js} +1 -1
- package/dist/_chunks/als-registry-BJARkOcu.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-CGuYoRHU.js +199 -0
- package/dist/_chunks/define-CGuYoRHU.js.map +1 -0
- package/dist/_chunks/define-Dz1bqwaS.js +106 -0
- package/dist/_chunks/define-Dz1bqwaS.js.map +1 -0
- package/dist/_chunks/define-cookie-B5mewxwM.js +93 -0
- package/dist/_chunks/define-cookie-B5mewxwM.js.map +1 -0
- package/dist/_chunks/error-boundary-D9hzsveV.js +216 -0
- package/dist/_chunks/error-boundary-D9hzsveV.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → format-Rn922VH2.js} +3 -20
- package/dist/_chunks/format-Rn922VH2.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → handler-store-BVePM7hp.js} +68 -3
- package/dist/_chunks/handler-store-BVePM7hp.js.map +1 -0
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-CEdHHviP.js} +171 -97
- package/dist/_chunks/interception-CEdHHviP.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-CywiO4jV.js} +181 -69
- package/dist/_chunks/request-context-CywiO4jV.js.map +1 -0
- package/dist/_chunks/schema-bridge-C4SwjCQD.js +86 -0
- package/dist/_chunks/schema-bridge-C4SwjCQD.js.map +1 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/segment-context-hzuJ048X.js +72 -0
- package/dist/_chunks/segment-context-hzuJ048X.js.map +1 -0
- package/dist/_chunks/stale-reload-BLUC_Pl_.js +64 -0
- package/dist/_chunks/stale-reload-BLUC_Pl_.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-DAhgj8Gx.js} +1 -1
- package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +1 -0
- package/dist/_chunks/wrappers-LZbghvn0.js +63 -0
- package/dist/_chunks/wrappers-LZbghvn0.js.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +135 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +7 -5
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +111 -73
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +6 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +537 -166
- package/dist/client/index.js.map +1 -1
- package/dist/client/link-pending-store.d.ts +78 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +90 -32
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +13 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +3 -3
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +6 -4
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/codec.d.ts +23 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/cookies/define-cookie.d.ts +35 -14
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -0
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +3 -84
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/index.d.ts +154 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12092 -11916
- package/dist/index.js.map +1 -1
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +3 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +84 -0
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -5
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +16 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +139 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -6
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +78 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +7 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +4 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +12 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +32 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-page-resolver.d.ts +52 -0
- package/dist/server/deny-page-resolver.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-warnings.d.ts +0 -14
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +4 -3
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +51 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2176 -1663
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +19 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/page-deny-boundary.d.ts +31 -0
- package/dist/server/page-deny-boundary.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +32 -10
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +76 -37
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +10 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +22 -19
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +5 -2
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +37 -17
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare.ts +342 -28
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +9 -5
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +40 -29
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +314 -142
- package/src/client/error-boundary.tsx +48 -16
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +2 -2
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +13 -4
- package/src/client/link-pending-store.ts +136 -0
- package/src/client/link.tsx +346 -105
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +27 -6
- package/src/client/navigation-root.tsx +346 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router.ts +302 -77
- package/src/client/rsc-fetch.ts +93 -5
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +73 -6
- package/src/client/top-loader.tsx +22 -13
- package/src/client/use-navigation-pending.ts +1 -1
- package/src/client/use-params.ts +7 -5
- package/src/client/use-query-states.ts +2 -2
- package/src/codec.ts +34 -0
- package/src/cookies/define-cookie.ts +72 -21
- package/src/cookies/index.ts +7 -0
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +328 -92
- package/src/plugins/adapter-build.ts +8 -2
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +288 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +55 -9
- package/src/plugins/entries.ts +70 -9
- package/src/plugins/fonts.ts +167 -61
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +57 -17
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +76 -33
- package/src/plugins/static-build.ts +10 -6
- package/src/routing/codegen.ts +165 -105
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +93 -23
- package/src/routing/segment-classify.ts +89 -0
- package/src/routing/status-file-lint.ts +3 -2
- package/src/routing/types.ts +17 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +482 -0
- package/src/search-params/index.ts +13 -19
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/segment-params/define.ts +279 -0
- package/src/segment-params/index.ts +28 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +28 -3
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +20 -3
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +32 -4
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-page-resolver.ts +154 -0
- package/src/server/deny-renderer.ts +24 -38
- package/src/server/dev-warnings.ts +2 -28
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +31 -15
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +277 -117
- package/src/server/index.ts +9 -5
- package/src/server/logger.ts +44 -36
- package/src/server/middleware-runner.ts +31 -4
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/page-deny-boundary.tsx +56 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline.ts +195 -51
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +240 -117
- package/src/server/route-element-builder.ts +284 -197
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +24 -20
- package/src/server/rsc-entry/api-handler.ts +15 -16
- package/src/server/rsc-entry/error-renderer.ts +300 -89
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +202 -113
- package/src/server/rsc-entry/rsc-payload.ts +100 -21
- package/src/server/rsc-entry/rsc-stream.ts +74 -18
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +173 -40
- package/src/server/safe-load.ts +60 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +243 -228
- package/src/server/ssr-entry.ts +211 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +37 -3
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +3 -6
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +2 -1
- package/src/utils/directive-parser.ts +5 -2
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/tracing-Cwn7697K.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -99
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -160
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -277
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* See design/29-cookies.md for cookie mutation semantics.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
14
|
-
import type { Routes } from '#/index.js';
|
|
15
13
|
import { requestContextAls, type RequestContextStore, type CookieEntry } from './als-registry.js';
|
|
16
14
|
import { isDebug } from './debug.js';
|
|
15
|
+
import { _setRawSearchParamsFn } from '../search-params/define.js';
|
|
16
|
+
import { _setRawSegmentParamsFn } from '../segment-params/define.js';
|
|
17
17
|
|
|
18
18
|
// Re-export the ALS for framework-internal consumers that need direct access.
|
|
19
19
|
export { requestContextAls };
|
|
@@ -22,30 +22,6 @@ export { requestContextAls };
|
|
|
22
22
|
// the ALS context persists for the entire request lifecycle including
|
|
23
23
|
// async stream consumption by React's renderToReadableStream.
|
|
24
24
|
|
|
25
|
-
// ─── Cookie Signing Secrets ──────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Module-level cookie signing secrets. Index 0 is the newest (used for signing).
|
|
29
|
-
* All entries are tried for verification (key rotation support).
|
|
30
|
-
*
|
|
31
|
-
* Set by the framework at startup via `setCookieSecrets()`.
|
|
32
|
-
* See design/29-cookies.md §"Signed Cookies"
|
|
33
|
-
*/
|
|
34
|
-
let _cookieSecrets: string[] = [];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Configure the cookie signing secrets.
|
|
38
|
-
*
|
|
39
|
-
* Called by the framework during server initialization with values from
|
|
40
|
-
* `cookies.secret` or `cookies.secrets` in timber.config.ts.
|
|
41
|
-
*
|
|
42
|
-
* The first secret (index 0) is used for signing new cookies.
|
|
43
|
-
* All secrets are tried for verification (supports key rotation).
|
|
44
|
-
*/
|
|
45
|
-
export function setCookieSecrets(secrets: string[]): void {
|
|
46
|
-
_cookieSecrets = secrets.filter(Boolean);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
25
|
// ─── Public API ───────────────────────────────────────────────────────────
|
|
50
26
|
|
|
51
27
|
/**
|
|
@@ -109,12 +85,6 @@ export function cookies(): RequestCookies {
|
|
|
109
85
|
return map.size;
|
|
110
86
|
},
|
|
111
87
|
|
|
112
|
-
getSigned(name: string): string | undefined {
|
|
113
|
-
const raw = map.get(name);
|
|
114
|
-
if (!raw || _cookieSecrets.length === 0) return undefined;
|
|
115
|
-
return verifySignedCookie(raw, _cookieSecrets);
|
|
116
|
-
},
|
|
117
|
-
|
|
118
88
|
set(name: string, value: string, options?: CookieOptions): void {
|
|
119
89
|
assertMutable(store, 'set');
|
|
120
90
|
if (store.flushed) {
|
|
@@ -127,21 +97,33 @@ export function cookies(): RequestCookies {
|
|
|
127
97
|
}
|
|
128
98
|
return;
|
|
129
99
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
const opts = { ...DEFAULT_COOKIE_OPTIONS, ...options };
|
|
101
|
+
store.cookieJar.set(name, { name, value, options: opts });
|
|
102
|
+
// Read-your-own-writes: update the parsed cookies map
|
|
103
|
+
map.set(name, value);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
setFromHeaders(headers: Headers): void {
|
|
107
|
+
assertMutable(store, 'setFromHeaders');
|
|
108
|
+
if (store.flushed) {
|
|
109
|
+
console.warn(
|
|
110
|
+
`[timber] warn: cookies().setFromHeaders() called after response headers were committed.\n` +
|
|
111
|
+
` The cookies will NOT be sent. Move cookie mutations to middleware.ts, a server action,\n` +
|
|
112
|
+
` or a route.ts handler.`
|
|
113
|
+
);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Headers.getSetCookie() returns individual Set-Cookie strings,
|
|
117
|
+
// avoiding the fragile comma-splitting that raw .get() requires.
|
|
118
|
+
for (const raw of headers.getSetCookie()) {
|
|
119
|
+
const parsed = parseSetCookie(raw);
|
|
120
|
+
if (parsed) {
|
|
121
|
+
// Use setRaw to preserve the original header's attributes without
|
|
122
|
+
// merging DEFAULT_COOKIE_OPTIONS (parseSetCookie intentionally
|
|
123
|
+
// does not apply defaults — see its doc comment).
|
|
124
|
+
setRaw(store, map, parsed.name, parsed.value, parsed.options);
|
|
137
125
|
}
|
|
138
|
-
storedValue = signCookieValue(value, _cookieSecrets[0]);
|
|
139
126
|
}
|
|
140
|
-
const opts = { ...DEFAULT_COOKIE_OPTIONS, ...options };
|
|
141
|
-
store.cookieJar.set(name, { name, value: storedValue, options: opts });
|
|
142
|
-
// Read-your-own-writes: update the parsed cookies map with the signed value
|
|
143
|
-
// so getSigned() can verify it in the same request
|
|
144
|
-
map.set(name, storedValue);
|
|
145
127
|
},
|
|
146
128
|
|
|
147
129
|
delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void {
|
|
@@ -190,41 +172,112 @@ export function cookies(): RequestCookies {
|
|
|
190
172
|
}
|
|
191
173
|
|
|
192
174
|
/**
|
|
193
|
-
* Returns a Promise resolving to the current request's
|
|
175
|
+
* Returns a Promise resolving to the current request's raw URLSearchParams.
|
|
176
|
+
*
|
|
177
|
+
* For typed, parsed search params, import the definition from params.ts
|
|
178
|
+
* and call `.load()` or `.parse()`:
|
|
194
179
|
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
180
|
+
* ```ts
|
|
181
|
+
* import { searchParams } from './params'
|
|
182
|
+
* const parsed = await searchParams.load()
|
|
183
|
+
* ```
|
|
199
184
|
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
185
|
+
* Or explicitly:
|
|
186
|
+
*
|
|
187
|
+
* ```ts
|
|
188
|
+
* import { rawSearchParams } from '@timber-js/app/server'
|
|
189
|
+
* import { searchParams } from './params'
|
|
190
|
+
* const parsed = searchParams.parse(await rawSearchParams())
|
|
191
|
+
* ```
|
|
202
192
|
*
|
|
203
193
|
* Throws if called outside a request context.
|
|
204
194
|
*/
|
|
205
|
-
export function
|
|
206
|
-
export function searchParams(): Promise<URLSearchParams | Record<string, unknown>>;
|
|
207
|
-
export function searchParams(): Promise<URLSearchParams | Record<string, unknown>> {
|
|
195
|
+
export function rawSearchParams(): Promise<URLSearchParams> {
|
|
208
196
|
const store = requestContextAls.getStore();
|
|
209
197
|
if (!store) {
|
|
210
198
|
throw new Error(
|
|
211
|
-
'[timber]
|
|
199
|
+
'[timber] rawSearchParams() called outside of a request context. ' +
|
|
212
200
|
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
213
201
|
);
|
|
214
202
|
}
|
|
215
203
|
return store.searchParamsPromise;
|
|
216
204
|
}
|
|
217
205
|
|
|
206
|
+
// Eagerly register rawSearchParams with the search-params module so
|
|
207
|
+
// searchParams.load() can call it synchronously without a dynamic import.
|
|
208
|
+
// Dynamic imports lose ALS context in React's RSC Flight renderer,
|
|
209
|
+
// breaking rawSearchParams() in parallel slot pages. See TIM-523.
|
|
210
|
+
_setRawSearchParamsFn(rawSearchParams);
|
|
211
|
+
|
|
212
|
+
// Eagerly register rawSegmentParams with the segment-params module so
|
|
213
|
+
// segmentParams.load() can call it synchronously without a dynamic import.
|
|
214
|
+
// Same pattern as search params — dynamic imports lose ALS context. See TIM-523.
|
|
215
|
+
_setRawSegmentParamsFn(rawSegmentParams);
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Returns a Promise resolving to the current request's coerced segment params.
|
|
219
|
+
*
|
|
220
|
+
* Segment params are set by the pipeline after route matching and param
|
|
221
|
+
* coercion (via params.ts codecs). When no params.ts exists, values are
|
|
222
|
+
* raw strings. When codecs are defined, values are already coerced
|
|
223
|
+
* (e.g., `id` is a `number` if `defineSegmentParams({ id: z.coerce.number() })`).
|
|
224
|
+
*
|
|
225
|
+
* This is the primary way page and layout components access route params:
|
|
226
|
+
*
|
|
227
|
+
* ```ts
|
|
228
|
+
* import { rawSegmentParams } from '@timber-js/app/server'
|
|
229
|
+
*
|
|
230
|
+
* export default async function Page() {
|
|
231
|
+
* const { slug } = await rawSegmentParams()
|
|
232
|
+
* // ...
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* Throws if called outside a request context.
|
|
237
|
+
*/
|
|
238
|
+
export function rawSegmentParams(): Promise<Record<string, string | string[]>> {
|
|
239
|
+
const store = requestContextAls.getStore();
|
|
240
|
+
if (!store) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
'[timber] rawSegmentParams() called outside of a request context. ' +
|
|
243
|
+
'It can only be used in middleware, access checks, server components, and server actions.'
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (!store.segmentParamsPromise) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
'[timber] rawSegmentParams() called before route matching completed. ' +
|
|
249
|
+
'Segment params are not available until after the route is matched.'
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return store.segmentParamsPromise;
|
|
253
|
+
}
|
|
254
|
+
|
|
218
255
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
256
|
+
* Set the segment params promise on the current request context.
|
|
257
|
+
* Called by the pipeline after route matching and param coercion.
|
|
258
|
+
*
|
|
259
|
+
* @internal — framework use only
|
|
222
260
|
*/
|
|
223
|
-
export function
|
|
261
|
+
export function setSegmentParams(params: Record<string, string | string[]>): void {
|
|
224
262
|
const store = requestContextAls.getStore();
|
|
225
|
-
if (store) {
|
|
226
|
-
|
|
263
|
+
if (!store) {
|
|
264
|
+
throw new Error('[timber] setSegmentParams() called outside of a request context.');
|
|
227
265
|
}
|
|
266
|
+
store.segmentParamsPromise = Promise.resolve(params);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Returns the raw search string from the current request URL (e.g. "?foo=bar").
|
|
271
|
+
* Synchronous — safe for use in `redirect()` which throws synchronously.
|
|
272
|
+
*
|
|
273
|
+
* Returns empty string if called outside a request context (non-throwing for
|
|
274
|
+
* use in redirect's optional preserveSearchParams path).
|
|
275
|
+
*
|
|
276
|
+
* @internal — used by redirect() for preserveSearchParams support.
|
|
277
|
+
*/
|
|
278
|
+
export function getRequestSearchString(): string {
|
|
279
|
+
const store = requestContextAls.getStore();
|
|
280
|
+
return store?.searchString ?? '';
|
|
228
281
|
}
|
|
229
282
|
|
|
230
283
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
@@ -257,12 +310,6 @@ export interface CookieOptions {
|
|
|
257
310
|
sameSite?: 'strict' | 'lax' | 'none';
|
|
258
311
|
/** Partitioned (CHIPS) — isolate cookie per top-level site. Default: false. */
|
|
259
312
|
partitioned?: boolean;
|
|
260
|
-
/**
|
|
261
|
-
* Sign the cookie value with HMAC-SHA256 for integrity verification.
|
|
262
|
-
* Requires `cookies.secret` or `cookies.secrets` in timber.config.ts.
|
|
263
|
-
* See design/29-cookies.md §"Signed Cookies".
|
|
264
|
-
*/
|
|
265
|
-
signed?: boolean;
|
|
266
313
|
}
|
|
267
314
|
|
|
268
315
|
const DEFAULT_COOKIE_OPTIONS: CookieOptions = {
|
|
@@ -272,6 +319,88 @@ const DEFAULT_COOKIE_OPTIONS: CookieOptions = {
|
|
|
272
319
|
sameSite: 'lax',
|
|
273
320
|
};
|
|
274
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Write a cookie to the jar WITHOUT merging DEFAULT_COOKIE_OPTIONS.
|
|
324
|
+
* Used by setFromHeaders to preserve the original header's attributes exactly.
|
|
325
|
+
*
|
|
326
|
+
* For deletion cookies (maxAge=0), the jar entry is still created so the
|
|
327
|
+
* Set-Cookie header is emitted, but the cookie is NOT added to the read map
|
|
328
|
+
* (it would be misleading — the cookie is being deleted).
|
|
329
|
+
*/
|
|
330
|
+
function setRaw(
|
|
331
|
+
store: RequestContextStore,
|
|
332
|
+
readMap: Map<string, string>,
|
|
333
|
+
name: string,
|
|
334
|
+
value: string,
|
|
335
|
+
options: CookieOptions
|
|
336
|
+
): void {
|
|
337
|
+
store.cookieJar.set(name, { name, value, options });
|
|
338
|
+
// Deletion cookies (Max-Age=0) should not appear in the read map
|
|
339
|
+
if (options.maxAge === 0) {
|
|
340
|
+
readMap.delete(name);
|
|
341
|
+
} else {
|
|
342
|
+
readMap.set(name, value);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Parse a raw `Set-Cookie` header string into name, value, and options.
|
|
348
|
+
* Handles all standard attributes: Path, Domain, Max-Age, Expires,
|
|
349
|
+
* SameSite, Secure, HttpOnly, Partitioned.
|
|
350
|
+
*
|
|
351
|
+
* Does NOT apply DEFAULT_COOKIE_OPTIONS — the caller decides whether
|
|
352
|
+
* to merge defaults (e.g. `set()` does, but `setRaw()` should preserve
|
|
353
|
+
* the original header's intent).
|
|
354
|
+
*/
|
|
355
|
+
function parseSetCookie(
|
|
356
|
+
header: string
|
|
357
|
+
): { name: string; value: string; options: CookieOptions } | null {
|
|
358
|
+
const segments = header.split(';');
|
|
359
|
+
const nameValue = segments[0];
|
|
360
|
+
const eqIdx = nameValue.indexOf('=');
|
|
361
|
+
if (eqIdx <= 0) return null;
|
|
362
|
+
|
|
363
|
+
const name = nameValue.slice(0, eqIdx).trim();
|
|
364
|
+
const value = nameValue.slice(eqIdx + 1).trim();
|
|
365
|
+
const options: CookieOptions = {};
|
|
366
|
+
|
|
367
|
+
for (let i = 1; i < segments.length; i++) {
|
|
368
|
+
const seg = segments[i].trim();
|
|
369
|
+
if (!seg) continue;
|
|
370
|
+
const [attrName, ...rest] = seg.split('=');
|
|
371
|
+
const key = attrName.trim().toLowerCase();
|
|
372
|
+
const val = rest.join('=').trim();
|
|
373
|
+
switch (key) {
|
|
374
|
+
case 'path':
|
|
375
|
+
options.path = val || '/';
|
|
376
|
+
break;
|
|
377
|
+
case 'domain':
|
|
378
|
+
options.domain = val;
|
|
379
|
+
break;
|
|
380
|
+
case 'max-age':
|
|
381
|
+
options.maxAge = Number(val);
|
|
382
|
+
break;
|
|
383
|
+
case 'expires':
|
|
384
|
+
options.expires = new Date(val);
|
|
385
|
+
break;
|
|
386
|
+
case 'samesite':
|
|
387
|
+
options.sameSite = val.toLowerCase() as 'strict' | 'lax' | 'none';
|
|
388
|
+
break;
|
|
389
|
+
case 'secure':
|
|
390
|
+
options.secure = true;
|
|
391
|
+
break;
|
|
392
|
+
case 'httponly':
|
|
393
|
+
options.httpOnly = true;
|
|
394
|
+
break;
|
|
395
|
+
case 'partitioned':
|
|
396
|
+
options.partitioned = true;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return { name, value, options };
|
|
402
|
+
}
|
|
403
|
+
|
|
275
404
|
/**
|
|
276
405
|
* Cookie accessor returned by `cookies()`.
|
|
277
406
|
*
|
|
@@ -287,16 +416,20 @@ export interface RequestCookies {
|
|
|
287
416
|
getAll(): Array<{ name: string; value: string }>;
|
|
288
417
|
/** Number of cookies. */
|
|
289
418
|
readonly size: number;
|
|
419
|
+
/** Set a cookie. Only available in mutable contexts (middleware, actions, route handlers). */
|
|
420
|
+
set(name: string, value: string, options?: CookieOptions): void;
|
|
290
421
|
/**
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
422
|
+
* Copy all `Set-Cookie` headers from a `Headers` object.
|
|
423
|
+
* Parses each header and forwards name, value, and all attributes
|
|
424
|
+
* (path, domain, max-age, expires, sameSite, secure, httpOnly, partitioned).
|
|
294
425
|
*
|
|
295
|
-
*
|
|
426
|
+
* Useful when forwarding cookies from an internal `fetch()` or auth handler:
|
|
427
|
+
* ```ts
|
|
428
|
+
* const response = await auth.handler(req);
|
|
429
|
+
* cookies().setFromHeaders(response.headers);
|
|
430
|
+
* ```
|
|
296
431
|
*/
|
|
297
|
-
|
|
298
|
-
/** Set a cookie. Only available in mutable contexts (middleware, actions, route handlers). */
|
|
299
|
-
set(name: string, value: string, options?: CookieOptions): void;
|
|
432
|
+
setFromHeaders(headers: Headers): void;
|
|
300
433
|
/** Delete a cookie. Only available in mutable contexts. */
|
|
301
434
|
delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void;
|
|
302
435
|
/** Delete all cookies. Only available in mutable contexts. */
|
|
@@ -316,11 +449,13 @@ export interface RequestCookies {
|
|
|
316
449
|
*/
|
|
317
450
|
export function runWithRequestContext<T>(req: Request, fn: () => T): T {
|
|
318
451
|
const originalCopy = new Headers(req.headers);
|
|
452
|
+
const parsedUrl = new URL(req.url);
|
|
319
453
|
const store: RequestContextStore = {
|
|
320
454
|
headers: freezeHeaders(req.headers),
|
|
321
455
|
originalHeaders: originalCopy,
|
|
322
456
|
cookieHeader: req.headers.get('cookie') ?? '',
|
|
323
|
-
searchParamsPromise: Promise.resolve(
|
|
457
|
+
searchParamsPromise: Promise.resolve(parsedUrl.searchParams),
|
|
458
|
+
searchString: parsedUrl.search,
|
|
324
459
|
cookieJar: new Map(),
|
|
325
460
|
flushed: false,
|
|
326
461
|
mutableContext: false,
|
|
@@ -354,6 +489,35 @@ export function markResponseFlushed(): void {
|
|
|
354
489
|
}
|
|
355
490
|
}
|
|
356
491
|
|
|
492
|
+
/**
|
|
493
|
+
* Build a Map of cookie name → value reflecting the current request's
|
|
494
|
+
* read-your-own-writes state. Includes incoming cookies plus any
|
|
495
|
+
* mutations from cookies().set() / cookies().delete() in the same request.
|
|
496
|
+
*
|
|
497
|
+
* Used by SSR renderers to populate NavContext.cookies so that
|
|
498
|
+
* useCookie()'s server snapshot matches the actual response state.
|
|
499
|
+
*
|
|
500
|
+
* See design/29-cookies.md §"Read-Your-Own-Writes"
|
|
501
|
+
* See design/triage/TIM-441-cookie-api-triage.md §4
|
|
502
|
+
*/
|
|
503
|
+
export function getCookiesForSsr(): Map<string, string> {
|
|
504
|
+
const store = requestContextAls.getStore();
|
|
505
|
+
if (!store) {
|
|
506
|
+
throw new Error('[timber] getCookiesForSsr() called outside of a request context.');
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Trigger lazy parsing if not yet done
|
|
510
|
+
if (!store.parsedCookies) {
|
|
511
|
+
store.parsedCookies = parseCookieHeader(store.cookieHeader);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// The parsedCookies map already reflects read-your-own-writes:
|
|
515
|
+
// - cookies().set() updates the map via map.set(name, value)
|
|
516
|
+
// - cookies().delete() removes from the map via map.delete(name)
|
|
517
|
+
// Return a copy so callers can't mutate the internal map.
|
|
518
|
+
return new Map(store.parsedCookies);
|
|
519
|
+
}
|
|
520
|
+
|
|
357
521
|
/**
|
|
358
522
|
* Collect all Set-Cookie headers from the cookie jar.
|
|
359
523
|
* Called by the framework at flush time to apply cookies to the response.
|
|
@@ -467,47 +631,6 @@ function parseCookieHeader(header: string): Map<string, string> {
|
|
|
467
631
|
return map;
|
|
468
632
|
}
|
|
469
633
|
|
|
470
|
-
// ─── Cookie Signing ──────────────────────────────────────────────────────
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Sign a cookie value with HMAC-SHA256.
|
|
474
|
-
* Returns `value.hex_signature`.
|
|
475
|
-
*/
|
|
476
|
-
function signCookieValue(value: string, secret: string): string {
|
|
477
|
-
const signature = createHmac('sha256', secret).update(value).digest('hex');
|
|
478
|
-
return `${value}.${signature}`;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Verify a signed cookie value against an array of secrets.
|
|
483
|
-
* Returns the original value if any secret produces a matching signature,
|
|
484
|
-
* or undefined if none match. Uses timing-safe comparison.
|
|
485
|
-
*
|
|
486
|
-
* The signed format is `value.hex_signature` — split at the last `.`.
|
|
487
|
-
*/
|
|
488
|
-
function verifySignedCookie(raw: string, secrets: string[]): string | undefined {
|
|
489
|
-
const lastDot = raw.lastIndexOf('.');
|
|
490
|
-
if (lastDot <= 0 || lastDot === raw.length - 1) return undefined;
|
|
491
|
-
|
|
492
|
-
const value = raw.slice(0, lastDot);
|
|
493
|
-
const signature = raw.slice(lastDot + 1);
|
|
494
|
-
|
|
495
|
-
// Hex-encoded SHA-256 is always 64 chars
|
|
496
|
-
if (signature.length !== 64) return undefined;
|
|
497
|
-
|
|
498
|
-
const signatureBuffer = Buffer.from(signature, 'hex');
|
|
499
|
-
// If the hex decode produced fewer bytes, the signature was not valid hex
|
|
500
|
-
if (signatureBuffer.length !== 32) return undefined;
|
|
501
|
-
|
|
502
|
-
for (const secret of secrets) {
|
|
503
|
-
const expected = createHmac('sha256', secret).update(value).digest();
|
|
504
|
-
if (timingSafeEqual(expected, signatureBuffer)) {
|
|
505
|
-
return value;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return undefined;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
634
|
/** Serialize a CookieEntry into a Set-Cookie header value. */
|
|
512
635
|
function serializeCookieEntry(entry: CookieEntry): string {
|
|
513
636
|
const parts = [`${entry.name}=${entry.value}`];
|