@timber-js/app 0.2.0-alpha.8 → 0.2.0-alpha.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
- package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-HS0LGUl2.js} +1 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-C77ScO0m.js +106 -0
- package/dist/_chunks/define-C77ScO0m.js.map +1 -0
- package/dist/_chunks/define-CZqDwhSu.js +199 -0
- package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js +94 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → dev-warnings-DpGRGoDi.js} +5 -44
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
- package/dist/_chunks/format-CYBGxKtc.js +14 -0
- package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-Dpn_UfAD.js} +171 -97
- package/dist/_chunks/interception-Dpn_UfAD.js.map +1 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +41 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/request-context-qMsWgy9C.js +478 -0
- package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js +86 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js.map +1 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-DzuI0bIV.js} +1 -1
- package/dist/_chunks/{ssr-data-MjmprTmO.js.map → ssr-data-DzuI0bIV.js.map} +1 -1
- package/dist/_chunks/stale-reload-BX5gL1r-.js +64 -0
- package/dist/_chunks/stale-reload-BX5gL1r-.js.map +1 -0
- package/dist/_chunks/{tracing-CemImE6h.js → tracing-CCYbKn5n.js} +60 -9
- package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
- package/dist/_chunks/use-params-Br9YSUFV.js +295 -0
- package/dist/_chunks/use-params-Br9YSUFV.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-Lo_s_pw2.js} +4 -4
- package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +95 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +135 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +23 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +142 -80
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +27 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +30 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +23 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +19 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +10 -4
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +3 -3
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +7 -21
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +210 -1017
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +18 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +890 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link-pending-store.d.ts +63 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +90 -32
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +13 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +5 -5
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-link-status.d.ts +5 -5
- package/dist/client/use-link-status.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +6 -4
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/codec.d.ts +33 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/config-types.d.ts +227 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts +35 -14
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -83
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/index.d.ts +45 -192
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12357 -11925
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +107 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +3 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +84 -0
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -5
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +4 -4
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +16 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +139 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -7
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +32 -441
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +78 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +25 -6
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +32 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-page-resolver.d.ts +52 -0
- package/dist/server/deny-page-resolver.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-holding-server.d.ts +52 -0
- package/dist/server/dev-holding-server.d.ts.map +1 -0
- package/dist/server/dev-warnings.d.ts +1 -21
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +4 -3
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +51 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -43
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +37 -2798
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +46 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2883 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +19 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/page-deny-boundary.d.ts +31 -0
- package/dist/server/page-deny-boundary.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +42 -10
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +69 -18
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +112 -43
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +4 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +4 -4
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +22 -19
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +3 -2
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +5 -2
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +56 -22
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +342 -28
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +52 -8
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +81 -18
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/sizeof.ts +31 -0
- package/src/cache/timber-cache.ts +24 -20
- package/src/cli.ts +16 -6
- package/src/client/browser-dev.ts +128 -1
- package/src/client/browser-entry/action-dispatch.ts +116 -0
- package/src/client/browser-entry/hmr.ts +81 -0
- package/src/client/browser-entry/hydrate.ts +145 -0
- package/src/client/browser-entry/index.ts +138 -0
- package/src/client/browser-entry/post-hydration.ts +119 -0
- package/src/client/browser-entry/router-init.ts +193 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +27 -0
- package/src/client/error-boundary.tsx +48 -16
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +9 -7
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +19 -38
- package/src/client/internal.ts +57 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +329 -97
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +31 -6
- package/src/client/navigation-root.tsx +342 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +299 -72
- package/src/client/rsc-fetch.ts +97 -8
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +72 -3
- package/src/client/top-loader.tsx +16 -8
- package/src/client/use-link-status.ts +7 -7
- package/src/client/use-params.ts +7 -5
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +3 -3
- package/src/client/use-router.ts +1 -1
- package/src/codec.ts +49 -0
- package/src/config-types.ts +225 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +78 -25
- package/src/cookies/index.ts +8 -0
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +295 -354
- package/src/plugin-context.ts +240 -0
- package/src/plugins/adapter-build.ts +9 -3
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +288 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +70 -9
- package/src/plugins/entries.ts +71 -10
- package/src/plugins/fonts.ts +168 -61
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +57 -17
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +135 -35
- package/src/plugins/static-build.ts +17 -11
- package/src/routing/codegen.ts +165 -105
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +93 -23
- package/src/routing/segment-classify.ts +89 -0
- package/src/routing/status-file-lint.ts +3 -2
- package/src/routing/types.ts +17 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +482 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +85 -0
- package/src/segment-params/define.ts +279 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +46 -11
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +21 -4
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +34 -6
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-page-resolver.ts +154 -0
- package/src/server/deny-renderer.ts +24 -38
- package/src/server/dev-holding-server.ts +185 -0
- package/src/server/dev-warnings.ts +4 -49
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +31 -15
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +280 -120
- package/src/server/index.ts +25 -177
- package/src/server/internal.ts +169 -0
- package/src/server/logger.ts +44 -36
- package/src/server/middleware-runner.ts +31 -4
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/page-deny-boundary.tsx +56 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline.ts +227 -62
- package/src/server/primitives.ts +111 -28
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +293 -132
- package/src/server/route-element-builder.ts +283 -191
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +24 -20
- package/src/server/rsc-entry/api-handler.ts +15 -16
- package/src/server/rsc-entry/error-renderer.ts +300 -89
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +200 -112
- package/src/server/rsc-entry/rsc-payload.ts +65 -18
- package/src/server/rsc-entry/rsc-stream.ts +65 -13
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +168 -38
- package/src/server/safe-load.ts +60 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +244 -229
- package/src/server/ssr-entry.ts +211 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +20 -9
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +3 -6
- package/src/server/version-skew.ts +104 -0
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +5 -1
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +7 -2
- package/src/utils/directive-parser.ts +5 -2
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js +0 -330
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/browser-entry.d.ts +0 -21
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -54
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -103
- package/src/client/browser-entry.ts +0 -678
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -166
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -410
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deny Page Resolver — resolves status-code file components for in-tree deny handling.
|
|
3
|
+
*
|
|
4
|
+
* When AccessGate or PageDenyBoundary catches a DenySignal, they need to
|
|
5
|
+
* render the matching deny page (403.tsx, 4xx.tsx, error.tsx) as a normal
|
|
6
|
+
* element in the React tree. This module resolves the deny page chain from
|
|
7
|
+
* the segment chain — a list of fallback components ordered by specificity.
|
|
8
|
+
*
|
|
9
|
+
* The chain is built during buildRouteElement and passed as a prop to
|
|
10
|
+
* AccessGate and PageDenyBoundary. At catch time, the first matching
|
|
11
|
+
* component is rendered.
|
|
12
|
+
*
|
|
13
|
+
* See design/10-error-handling.md §"Status-Code Files", TIM-666.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createElement } from 'react';
|
|
17
|
+
|
|
18
|
+
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
19
|
+
import { loadModule } from './safe-load.js';
|
|
20
|
+
import { requestContextAls } from './als-registry.js';
|
|
21
|
+
|
|
22
|
+
// ─── Types ────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/** A single entry in the deny page fallback chain. */
|
|
25
|
+
export interface DenyPageEntry {
|
|
26
|
+
/** Status code filter: specific (403), category (400 = any 4xx), or null (catch-all). */
|
|
27
|
+
status: number | null;
|
|
28
|
+
/** The component to render (server or client — both work). */
|
|
29
|
+
component: (...args: unknown[]) => unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ─── Resolver ─────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build the deny page fallback chain from the segment chain.
|
|
36
|
+
*
|
|
37
|
+
* Walks segments from `startIndex` outward (toward root) and collects
|
|
38
|
+
* status-code file components in fallback order:
|
|
39
|
+
* 1. Specific status files (403.tsx, 404.tsx) — exact match
|
|
40
|
+
* 2. Category catch-alls (4xx.tsx) — matches any 4xx
|
|
41
|
+
* 3. error.tsx — catches everything
|
|
42
|
+
*
|
|
43
|
+
* Each segment is checked in this order. The chain is ordered so the
|
|
44
|
+
* FIRST match wins at catch time.
|
|
45
|
+
*/
|
|
46
|
+
export async function buildDenyPageChain(
|
|
47
|
+
segments: ManifestSegmentNode[],
|
|
48
|
+
startIndex: number
|
|
49
|
+
): Promise<DenyPageEntry[]> {
|
|
50
|
+
const chain: DenyPageEntry[] = [];
|
|
51
|
+
|
|
52
|
+
// Pass 1: Status files (specific + category) across ALL segments.
|
|
53
|
+
// These have higher priority than error.tsx — a root 4xx.tsx should
|
|
54
|
+
// match before a leaf error.tsx. Walking inner → outer ensures the
|
|
55
|
+
// nearest match wins within each priority tier.
|
|
56
|
+
for (let i = startIndex; i >= 0; i--) {
|
|
57
|
+
const segment = segments[i];
|
|
58
|
+
if (!segment.statusFiles) continue;
|
|
59
|
+
|
|
60
|
+
// Specific status files (403.tsx, 404.tsx, etc.)
|
|
61
|
+
for (const [key, file] of Object.entries(segment.statusFiles)) {
|
|
62
|
+
if (key !== '4xx' && key !== '5xx') {
|
|
63
|
+
const status = parseInt(key, 10);
|
|
64
|
+
if (!isNaN(status)) {
|
|
65
|
+
const mod = await loadModule(file).catch(() => null);
|
|
66
|
+
if (mod?.default) {
|
|
67
|
+
chain.push({ status, component: mod.default as (...args: unknown[]) => unknown });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Category catch-alls (4xx.tsx, 5xx.tsx)
|
|
74
|
+
for (const [key, file] of Object.entries(segment.statusFiles)) {
|
|
75
|
+
if (key === '4xx' || key === '5xx') {
|
|
76
|
+
const mod = await loadModule(file).catch(() => null);
|
|
77
|
+
if (mod?.default) {
|
|
78
|
+
const categoryStatus = key === '4xx' ? 400 : 500;
|
|
79
|
+
chain.push({
|
|
80
|
+
status: categoryStatus,
|
|
81
|
+
component: mod.default as (...args: unknown[]) => unknown,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Pass 2: error.tsx files — lowest priority catch-all.
|
|
89
|
+
// Only added AFTER all status files so they never shadow a specific
|
|
90
|
+
// or category status file from an ancestor segment.
|
|
91
|
+
for (let i = startIndex; i >= 0; i--) {
|
|
92
|
+
const segment = segments[i];
|
|
93
|
+
if (segment.error) {
|
|
94
|
+
const mod = await loadModule(segment.error).catch(() => null);
|
|
95
|
+
if (mod?.default) {
|
|
96
|
+
chain.push({ status: null, component: mod.default as (...args: unknown[]) => unknown });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return chain;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Matcher ──────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Find the first deny page in the chain that matches the given status code.
|
|
108
|
+
* Returns a React element for the matching component, or null if no match.
|
|
109
|
+
*/
|
|
110
|
+
export function renderMatchingDenyPage(
|
|
111
|
+
chain: DenyPageEntry[],
|
|
112
|
+
status: number,
|
|
113
|
+
data: unknown
|
|
114
|
+
): React.ReactElement | null {
|
|
115
|
+
const h = createElement as (...args: unknown[]) => React.ReactElement;
|
|
116
|
+
|
|
117
|
+
for (const entry of chain) {
|
|
118
|
+
if (entry.status === status) {
|
|
119
|
+
return h(entry.component, { status, dangerouslyPassData: data });
|
|
120
|
+
}
|
|
121
|
+
if (entry.status === 400 && status >= 400 && status <= 499) {
|
|
122
|
+
return h(entry.component, { status, dangerouslyPassData: data });
|
|
123
|
+
}
|
|
124
|
+
if (entry.status === 500 && status >= 500 && status <= 599) {
|
|
125
|
+
return h(entry.component, { status, dangerouslyPassData: data });
|
|
126
|
+
}
|
|
127
|
+
if (entry.status === null) {
|
|
128
|
+
return h(entry.component, { status, dangerouslyPassData: data });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── ALS Helper ───────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set the deny status in the request context ALS.
|
|
138
|
+
* Called from AccessGate / PageDenyBoundary when a DenySignal is caught.
|
|
139
|
+
* The pipeline reads this after render to set the HTTP status code.
|
|
140
|
+
*/
|
|
141
|
+
export function setDenyStatus(status: number): void {
|
|
142
|
+
const store = requestContextAls.getStore();
|
|
143
|
+
if (store) {
|
|
144
|
+
store.denyStatus = status;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Read the deny status from the request context ALS.
|
|
150
|
+
* Returns undefined if no deny was caught during render.
|
|
151
|
+
*/
|
|
152
|
+
export function getDenyStatus(): number | undefined {
|
|
153
|
+
return requestContextAls.getStore()?.denyStatus;
|
|
154
|
+
}
|
|
@@ -16,18 +16,21 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { createElement } from 'react';
|
|
19
|
-
import { renderToReadableStream } from '
|
|
19
|
+
import { renderToReadableStream } from '../rsc-runtime/rsc.js';
|
|
20
20
|
|
|
21
21
|
import { DenySignal } from './primitives.js';
|
|
22
22
|
import { logRenderError } from './logger.js';
|
|
23
|
+
import { loadModule } from './safe-load.js';
|
|
23
24
|
import { isDebug } from './debug.js';
|
|
24
25
|
import { resolveMetadata, renderMetadataToElements } from './metadata.js';
|
|
25
26
|
import { resolveManifestStatusFile } from './manifest-status-resolver.js';
|
|
26
27
|
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
27
28
|
import type { RouteMatch } from './pipeline.js';
|
|
28
29
|
import type { NavContext } from './ssr-entry.js';
|
|
30
|
+
import { flightInitScript } from './flight-scripts.js';
|
|
29
31
|
import type { ClientBootstrapConfig } from './html-injectors.js';
|
|
30
32
|
import type { Metadata } from './types.js';
|
|
33
|
+
import { teeWithErrorPropagation } from './stream-utils.js';
|
|
31
34
|
|
|
32
35
|
/** RSC content type for client navigation payload requests. */
|
|
33
36
|
const RSC_CONTENT_TYPE = 'text/x-component';
|
|
@@ -108,7 +111,7 @@ export async function renderDenyPage(
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
// Load the status-code page component
|
|
111
|
-
const mod =
|
|
114
|
+
const mod = await loadModule(resolution.file);
|
|
112
115
|
if (!mod.default) {
|
|
113
116
|
return new Response(null, { status: deny.status, headers: responseHeaders });
|
|
114
117
|
}
|
|
@@ -116,9 +119,6 @@ export async function renderDenyPage(
|
|
|
116
119
|
const ErrorPageComponent = mod.default as (...args: unknown[]) => unknown;
|
|
117
120
|
const h = createElement as (...args: unknown[]) => React.ReactElement;
|
|
118
121
|
|
|
119
|
-
// Check shell opt-out: export const shell = false
|
|
120
|
-
const shellEnabled = mod.shell !== false;
|
|
121
|
-
|
|
122
122
|
// 4xx status-code pages receive { status, dangerouslyPassData }
|
|
123
123
|
// per design/10-error-handling.md §"Status-Code File Props"
|
|
124
124
|
let element = h(ErrorPageComponent, {
|
|
@@ -126,23 +126,14 @@ export async function renderDenyPage(
|
|
|
126
126
|
dangerouslyPassData: deny.data,
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
} else if (isDebug()) {
|
|
138
|
-
// Dev-mode: warn if shell=false might conflict with Suspense
|
|
139
|
-
// The actual Suspense boundary check happens at render time in the pipeline.
|
|
140
|
-
// This is a preemptive log for developer awareness.
|
|
141
|
-
console.warn(
|
|
142
|
-
`[timber] Status-code file ${resolution.file.filePath} exports shell = false. ` +
|
|
143
|
-
'If deny() fires inside a Suspense boundary, layouts are already committed and ' +
|
|
144
|
-
'cannot be unwrapped. The shell opt-out will be ignored in that case.'
|
|
145
|
-
);
|
|
129
|
+
// Always wrap in layout shell.
|
|
130
|
+
// NOTE: Shell opt-out (export const shell = false) is a future feature
|
|
131
|
+
// pending a proper design doc. See design backlog.
|
|
132
|
+
const resolvedSegments = new Set(segments.slice(0, resolution.segmentIndex + 1));
|
|
133
|
+
const layoutsToWrap = layoutComponents.filter((lc) => resolvedSegments.has(lc.segment));
|
|
134
|
+
for (let i = layoutsToWrap.length - 1; i >= 0; i--) {
|
|
135
|
+
const { component } = layoutsToWrap[i];
|
|
136
|
+
element = h(component, null, element);
|
|
146
137
|
}
|
|
147
138
|
|
|
148
139
|
// Build head HTML from error page metadata (if any)
|
|
@@ -170,15 +161,15 @@ export async function renderDenyPage(
|
|
|
170
161
|
debugChannel: createDebugChannelSink(),
|
|
171
162
|
});
|
|
172
163
|
|
|
173
|
-
const [ssrStream, inlineStream] = rscStream
|
|
164
|
+
const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
|
|
174
165
|
|
|
175
166
|
const navContext: NavContext = {
|
|
176
167
|
pathname: new URL(req.url).pathname,
|
|
177
|
-
params: match.
|
|
168
|
+
params: match.segmentParams,
|
|
178
169
|
searchParams: Object.fromEntries(new URL(req.url).searchParams),
|
|
179
170
|
statusCode: deny.status,
|
|
180
171
|
responseHeaders,
|
|
181
|
-
headHtml,
|
|
172
|
+
headHtml: headHtml + flightInitScript(),
|
|
182
173
|
bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
|
|
183
174
|
rscStream: inlineStream,
|
|
184
175
|
};
|
|
@@ -206,7 +197,7 @@ export async function renderDenyPageAsRsc(
|
|
|
206
197
|
return new Response(null, { status: deny.status, headers: responseHeaders });
|
|
207
198
|
}
|
|
208
199
|
|
|
209
|
-
const mod =
|
|
200
|
+
const mod = await loadModule(resolution.file);
|
|
210
201
|
if (!mod.default) {
|
|
211
202
|
responseHeaders.set('content-type', `${RSC_CONTENT_TYPE}; charset=utf-8`);
|
|
212
203
|
return new Response(null, { status: deny.status, headers: responseHeaders });
|
|
@@ -215,22 +206,17 @@ export async function renderDenyPageAsRsc(
|
|
|
215
206
|
const ErrorPageComponent = mod.default as (...args: unknown[]) => unknown;
|
|
216
207
|
const h = createElement as (...args: unknown[]) => React.ReactElement;
|
|
217
208
|
|
|
218
|
-
// Check shell opt-out
|
|
219
|
-
const shellEnabled = mod.shell !== false;
|
|
220
|
-
|
|
221
209
|
let element = h(ErrorPageComponent, {
|
|
222
210
|
status: deny.status,
|
|
223
211
|
dangerouslyPassData: deny.data,
|
|
224
212
|
});
|
|
225
213
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
element = h(component, null, element);
|
|
233
|
-
}
|
|
214
|
+
// Always wrap in layout shell.
|
|
215
|
+
const resolvedSegments = new Set(segments.slice(0, resolution.segmentIndex + 1));
|
|
216
|
+
const layoutsToWrap = layoutComponents.filter((lc) => resolvedSegments.has(lc.segment));
|
|
217
|
+
for (let i = layoutsToWrap.length - 1; i >= 0; i--) {
|
|
218
|
+
const { component } = layoutsToWrap[i];
|
|
219
|
+
element = h(component, null, element);
|
|
234
220
|
}
|
|
235
221
|
|
|
236
222
|
const rscStream = renderToReadableStream(element, {
|
|
@@ -272,7 +258,7 @@ async function renderDenyPageJson(
|
|
|
272
258
|
// JSON status files are loaded as modules that export the JSON content.
|
|
273
259
|
// The manifest's load() imports the .json file, which Vite handles as a
|
|
274
260
|
// default export of the parsed JSON object.
|
|
275
|
-
const mod =
|
|
261
|
+
const mod = await loadModule(resolution.file);
|
|
276
262
|
const jsonContent = mod.default ?? mod;
|
|
277
263
|
|
|
278
264
|
responseHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev holding server — serves a loading page while Vite initializes.
|
|
3
|
+
*
|
|
4
|
+
* When `timber dev` (or `vite dev`) starts, Vite takes several seconds
|
|
5
|
+
* to initialize all plugins, scan routes, and boot the RSC environment.
|
|
6
|
+
* During this window, the port is unbound and browsers get
|
|
7
|
+
* ERR_CONNECTION_REFUSED.
|
|
8
|
+
*
|
|
9
|
+
* This module creates a lightweight HTTP server that binds the port
|
|
10
|
+
* immediately and serves a branded holding page with auto-refresh.
|
|
11
|
+
* Once Vite is ready, the holding server is closed and Vite's server
|
|
12
|
+
* takes over the port.
|
|
13
|
+
*
|
|
14
|
+
* The holding server is started in rootSync's config() hook (the
|
|
15
|
+
* earliest Vite plugin lifecycle point where we know the port) and
|
|
16
|
+
* closed in timber-dev-server's configureServer() hook (the last
|
|
17
|
+
* plugin, right before Vite calls server.listen()).
|
|
18
|
+
*
|
|
19
|
+
* Browser requests (Accept: text/html) get the HTML holding page.
|
|
20
|
+
* API requests (Accept: application/json) get a 503 JSON response.
|
|
21
|
+
* All responses include Retry-After: 1.
|
|
22
|
+
*
|
|
23
|
+
* See design/21-dev-server.md, TIM-665.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import http from 'node:http';
|
|
27
|
+
|
|
28
|
+
// ─── Types ────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export interface HoldingServer {
|
|
31
|
+
/**
|
|
32
|
+
* Start listening on the given port.
|
|
33
|
+
* Returns the actual bound port (useful when port is 0 for OS assignment).
|
|
34
|
+
*/
|
|
35
|
+
listen(port: number): Promise<number>;
|
|
36
|
+
|
|
37
|
+
/** Gracefully close the server and stop accepting connections. */
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Holding Page HTML ────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Minimal self-contained HTML holding page.
|
|
45
|
+
*
|
|
46
|
+
* Matches the timber.js docs site theme:
|
|
47
|
+
* - Light mode: warm stone/grain background, timber/bark text
|
|
48
|
+
* - Dark mode: stone-900 background, stone-200 text
|
|
49
|
+
* - 🪵 timber.js branding
|
|
50
|
+
*
|
|
51
|
+
* Uses meta-refresh (1s) for auto-reload — works without JavaScript.
|
|
52
|
+
* Inline CSS only, no external dependencies.
|
|
53
|
+
*/
|
|
54
|
+
const HOLDING_PAGE_HTML = [
|
|
55
|
+
'<!DOCTYPE html>',
|
|
56
|
+
'<html lang="en">',
|
|
57
|
+
'<head>',
|
|
58
|
+
' <meta charset="utf-8">',
|
|
59
|
+
' <meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
60
|
+
' <meta http-equiv="refresh" content="2">',
|
|
61
|
+
' <title>timber — starting...</title>',
|
|
62
|
+
' <style>',
|
|
63
|
+
' *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }',
|
|
64
|
+
' body {',
|
|
65
|
+
' font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;',
|
|
66
|
+
' background: #fafaf9;',
|
|
67
|
+
' color: #44403c;',
|
|
68
|
+
' display: flex;',
|
|
69
|
+
' align-items: center;',
|
|
70
|
+
' justify-content: center;',
|
|
71
|
+
' min-height: 100vh;',
|
|
72
|
+
' -webkit-font-smoothing: antialiased;',
|
|
73
|
+
' }',
|
|
74
|
+
' @media (prefers-color-scheme: dark) {',
|
|
75
|
+
' body { background: #1c1917; color: #e7e5e4; }',
|
|
76
|
+
' .message { color: #a8a29e; }',
|
|
77
|
+
' .spinner { border-color: #57534e; border-top-color: #a1887f; }',
|
|
78
|
+
' .logo { color: #e7e5e4; }',
|
|
79
|
+
' }',
|
|
80
|
+
' .container { text-align: center; padding: 2rem; }',
|
|
81
|
+
' .logo {',
|
|
82
|
+
' font-size: 1.5rem;',
|
|
83
|
+
' font-weight: 700;',
|
|
84
|
+
' letter-spacing: -0.02em;',
|
|
85
|
+
' color: #5c3d2e;',
|
|
86
|
+
' margin-bottom: 1.5rem;',
|
|
87
|
+
' }',
|
|
88
|
+
' .message {',
|
|
89
|
+
' font-size: 0.95rem;',
|
|
90
|
+
' color: #78716c;',
|
|
91
|
+
' margin-bottom: 2rem;',
|
|
92
|
+
' }',
|
|
93
|
+
' .spinner {',
|
|
94
|
+
' width: 24px;',
|
|
95
|
+
' height: 24px;',
|
|
96
|
+
' border: 2.5px solid #d7ccc8;',
|
|
97
|
+
' border-top-color: #6d4c41;',
|
|
98
|
+
' border-radius: 50%;',
|
|
99
|
+
' animation: spin 0.8s linear infinite;',
|
|
100
|
+
' margin: 0 auto;',
|
|
101
|
+
' }',
|
|
102
|
+
' @keyframes spin { to { transform: rotate(360deg); } }',
|
|
103
|
+
' </style>',
|
|
104
|
+
'</head>',
|
|
105
|
+
'<body>',
|
|
106
|
+
' <div class="container">',
|
|
107
|
+
' <div class="logo">\u{1FAB5} timber.js</div>',
|
|
108
|
+
' <p class="message">Dev server starting…</p>',
|
|
109
|
+
' <div class="spinner"></div>',
|
|
110
|
+
' </div>',
|
|
111
|
+
'</body>',
|
|
112
|
+
'</html>',
|
|
113
|
+
].join('\n');
|
|
114
|
+
|
|
115
|
+
// ─── Factory ──────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a holding HTTP server that serves a loading page during startup.
|
|
119
|
+
*
|
|
120
|
+
* Usage (inside Vite plugin):
|
|
121
|
+
* ```ts
|
|
122
|
+
* // In config() hook — earliest point where port is known
|
|
123
|
+
* const holding = createHoldingServer();
|
|
124
|
+
* holding.listen(config.server?.port ?? 5173);
|
|
125
|
+
*
|
|
126
|
+
* // In last plugin's configureServer() — wrap listen for seamless handoff
|
|
127
|
+
* const originalListen = server.listen.bind(server);
|
|
128
|
+
* server.listen = async (...args) => {
|
|
129
|
+
* await holding.close();
|
|
130
|
+
* return originalListen(...args);
|
|
131
|
+
* };
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function createHoldingServer(): HoldingServer {
|
|
135
|
+
const server = http.createServer((req, res) => {
|
|
136
|
+
const accept = req.headers.accept ?? '';
|
|
137
|
+
|
|
138
|
+
// API requests: JSON 503
|
|
139
|
+
if (accept.includes('application/json') && !accept.includes('text/html')) {
|
|
140
|
+
res.writeHead(503, {
|
|
141
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
142
|
+
'Retry-After': '1',
|
|
143
|
+
'Cache-Control': 'no-store',
|
|
144
|
+
});
|
|
145
|
+
res.end(
|
|
146
|
+
JSON.stringify({
|
|
147
|
+
error: 'Service Unavailable',
|
|
148
|
+
message: 'timber dev server is starting — please retry shortly',
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Browser and other requests: HTML holding page
|
|
155
|
+
res.writeHead(503, {
|
|
156
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
157
|
+
'Retry-After': '1',
|
|
158
|
+
'Cache-Control': 'no-store',
|
|
159
|
+
});
|
|
160
|
+
res.end(HOLDING_PAGE_HTML);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
listen(port: number): Promise<number> {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
server.once('error', reject);
|
|
167
|
+
server.listen(port, () => {
|
|
168
|
+
server.removeListener('error', reject);
|
|
169
|
+
const addr = server.address();
|
|
170
|
+
const boundPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
171
|
+
resolve(boundPort);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
close(): Promise<void> {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
server.close((err) => {
|
|
179
|
+
if (err) reject(err);
|
|
180
|
+
else resolve();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -25,7 +25,6 @@ export const WarningId = {
|
|
|
25
25
|
REDIRECT_IN_SUSPENSE: 'REDIRECT_IN_SUSPENSE',
|
|
26
26
|
REDIRECT_IN_ACCESS: 'REDIRECT_IN_ACCESS',
|
|
27
27
|
STATIC_REQUEST_API: 'STATIC_REQUEST_API',
|
|
28
|
-
CACHE_REQUEST_PROPS: 'CACHE_REQUEST_PROPS',
|
|
29
28
|
SLOW_SLOT_NO_SUSPENSE: 'SLOW_SLOT_NO_SUSPENSE',
|
|
30
29
|
} as const;
|
|
31
30
|
|
|
@@ -111,7 +110,7 @@ export function warnSuspenseWrappingChildren(layoutFile: string): void {
|
|
|
111
110
|
'warn',
|
|
112
111
|
`Layout at ${layoutFile} wraps {children} in <Suspense>. ` +
|
|
113
112
|
'This prevents child pages from setting HTTP status codes. ' +
|
|
114
|
-
'Use
|
|
113
|
+
'Use usePendingNavigation() for loading states instead.'
|
|
115
114
|
);
|
|
116
115
|
}
|
|
117
116
|
|
|
@@ -179,7 +178,7 @@ export function warnRedirectInAccess(accessFile: string, line?: number): void {
|
|
|
179
178
|
}
|
|
180
179
|
|
|
181
180
|
/**
|
|
182
|
-
* Warn when
|
|
181
|
+
* Warn when getCookies() or getHeaders() is called during a static build.
|
|
183
182
|
*
|
|
184
183
|
* In output: 'static' mode, there is no per-request context — these APIs
|
|
185
184
|
* read build-time values only. This is almost always a mistake.
|
|
@@ -197,33 +196,8 @@ export function warnStaticRequestApi(api: 'cookies' | 'headers', file: string):
|
|
|
197
196
|
);
|
|
198
197
|
}
|
|
199
198
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
*
|
|
203
|
-
* Cached components should not depend on per-request data — a userId or
|
|
204
|
-
* sessionId in the props means the cache will either be ineffective
|
|
205
|
-
* (key per user) or dangerous (serve one user's data to another).
|
|
206
|
-
*
|
|
207
|
-
* @param componentName - Name of the cached component
|
|
208
|
-
* @param propName - Name of the suspicious prop
|
|
209
|
-
* @param file - Relative path to the component file
|
|
210
|
-
* @param line - Line number
|
|
211
|
-
*/
|
|
212
|
-
export function warnCacheRequestProps(
|
|
213
|
-
componentName: string,
|
|
214
|
-
propName: string,
|
|
215
|
-
file: string,
|
|
216
|
-
line?: number
|
|
217
|
-
): void {
|
|
218
|
-
const location = line ? `${file}:${line}` : file;
|
|
219
|
-
emitOnce(
|
|
220
|
-
WarningId.CACHE_REQUEST_PROPS,
|
|
221
|
-
`${componentName}:${propName}:${location}`,
|
|
222
|
-
'warn',
|
|
223
|
-
`Cached component ${componentName} receives prop "${propName}" which appears request-specific. ` +
|
|
224
|
-
'Cached components should not depend on per-request data.'
|
|
225
|
-
);
|
|
226
|
-
}
|
|
199
|
+
// NOTE: warnCacheRequestProps removed — 'use cache' directive is a future feature.
|
|
200
|
+
// See design/06-caching.md.
|
|
227
201
|
|
|
228
202
|
/**
|
|
229
203
|
* Warn when a parallel slot resolves slowly without a <Suspense> wrapper.
|
|
@@ -245,25 +219,6 @@ export function warnSlowSlotWithoutSuspense(slotName: string, durationMs: number
|
|
|
245
219
|
);
|
|
246
220
|
}
|
|
247
221
|
|
|
248
|
-
// ─── Legacy aliases ─────────────────────────────────────────────────────────
|
|
249
|
-
|
|
250
|
-
/** @deprecated Use warnStaticRequestApi instead */
|
|
251
|
-
export const warnDynamicApiInStaticBuild = warnStaticRequestApi;
|
|
252
|
-
|
|
253
|
-
/** @deprecated Use warnRedirectInAccess instead */
|
|
254
|
-
export function warnRedirectInSlotAccess(slotName: string): void {
|
|
255
|
-
warnRedirectInAccess(`${slotName}/access.ts`);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/** @deprecated Use warnDenyInSuspense / warnRedirectInSuspense instead */
|
|
259
|
-
export function warnDenyAfterFlush(signal: 'deny' | 'redirect'): void {
|
|
260
|
-
if (signal === 'deny') {
|
|
261
|
-
warnDenyInSuspense('unknown');
|
|
262
|
-
} else {
|
|
263
|
-
warnRedirectInSuspense('unknown');
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
222
|
// ─── Testing ────────────────────────────────────────────────────────────────
|
|
268
223
|
|
|
269
224
|
/**
|
|
@@ -58,15 +58,31 @@ export interface EarlyHint {
|
|
|
58
58
|
/**
|
|
59
59
|
* Format a single EarlyHint as a Link header value.
|
|
60
60
|
*
|
|
61
|
+
* Attribute order: `as` before `rel` to match Cloudflare CDN's cached
|
|
62
|
+
* Early Hints format. Cloudflare caches Link headers from 200 responses
|
|
63
|
+
* and re-emits them as 103 Early Hints on subsequent requests. If our
|
|
64
|
+
* attribute order differs from Cloudflare's cached copy, the browser
|
|
65
|
+
* sees two preload headers for the same URL (different attribute order)
|
|
66
|
+
* and warns "Preload was ignored." Matching the order ensures the
|
|
67
|
+
* browser deduplicates them correctly.
|
|
68
|
+
*
|
|
61
69
|
* Examples:
|
|
62
|
-
* `</styles/root.css>;
|
|
63
|
-
* `</fonts/inter.woff2>;
|
|
70
|
+
* `</styles/root.css>; as=style; rel=preload`
|
|
71
|
+
* `</fonts/inter.woff2>; as=font; rel=preload; crossorigin=anonymous`
|
|
64
72
|
* `</_timber/client.js>; rel=modulepreload`
|
|
65
73
|
* `<https://fonts.googleapis.com>; rel=preconnect`
|
|
66
74
|
*/
|
|
67
75
|
export function formatLinkHeader(hint: EarlyHint): string {
|
|
76
|
+
// For preload hints, emit `as` before `rel` to match Cloudflare's
|
|
77
|
+
// cached header format and avoid duplicate preload warnings.
|
|
78
|
+
if (hint.as !== undefined) {
|
|
79
|
+
let value = `<${hint.href}>; as=${hint.as}; rel=${hint.rel}`;
|
|
80
|
+
if (hint.crossOrigin !== undefined) value += `; crossorigin=${hint.crossOrigin}`;
|
|
81
|
+
if (hint.fetchPriority !== undefined) value += `; fetchpriority=${hint.fetchPriority}`;
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
// For modulepreload / preconnect (no `as`), emit rel first.
|
|
68
85
|
let value = `<${hint.href}>; rel=${hint.rel}`;
|
|
69
|
-
if (hint.as !== undefined) value += `; as=${hint.as}`;
|
|
70
86
|
if (hint.crossOrigin !== undefined) value += `; crossorigin=${hint.crossOrigin}`;
|
|
71
87
|
if (hint.fetchPriority !== undefined) value += `; fetchpriority=${hint.fetchPriority}`;
|
|
72
88
|
return value;
|
|
@@ -84,8 +100,8 @@ export interface EarlyHintOptions {
|
|
|
84
100
|
* Collect all Link header strings for a matched route's segment chain.
|
|
85
101
|
*
|
|
86
102
|
* Walks the build manifest to emit hints for:
|
|
87
|
-
* - CSS stylesheets (
|
|
88
|
-
* - Font assets (
|
|
103
|
+
* - CSS stylesheets (as=style; rel=preload)
|
|
104
|
+
* - Font assets (as=font; rel=preload; crossorigin)
|
|
89
105
|
* - JS modulepreload hints (rel=modulepreload) — unless skipJs is set
|
|
90
106
|
*
|
|
91
107
|
* Also emits global CSS from the `_global` manifest key. Route files
|
|
@@ -94,7 +110,7 @@ export interface EarlyHintOptions {
|
|
|
94
110
|
* key contains all CSS assets from the client build — fine for early
|
|
95
111
|
* hints since they're just prefetch signals.
|
|
96
112
|
*
|
|
97
|
-
* Returns formatted Link header strings, deduplicated, root → leaf order.
|
|
113
|
+
* Returns formatted Link header strings, deduplicated by URL, root → leaf order.
|
|
98
114
|
* Returns an empty array in dev mode (manifest is empty).
|
|
99
115
|
*/
|
|
100
116
|
export function collectEarlyHintHeaders(
|
|
@@ -103,30 +119,35 @@ export function collectEarlyHintHeaders(
|
|
|
103
119
|
options?: EarlyHintOptions
|
|
104
120
|
): string[] {
|
|
105
121
|
const result: string[] = [];
|
|
106
|
-
|
|
122
|
+
// Dedup by URL (href), not by full formatted header string.
|
|
123
|
+
// Different code paths can produce the same URL with different attribute
|
|
124
|
+
// ordering, which would bypass a full-string dedup and produce duplicate
|
|
125
|
+
// Link headers that trigger browser "preload was ignored" warnings.
|
|
126
|
+
const seenUrls = new Set<string>();
|
|
107
127
|
|
|
108
|
-
const add = (header: string) => {
|
|
109
|
-
if (!
|
|
110
|
-
|
|
128
|
+
const add = (url: string, header: string) => {
|
|
129
|
+
if (!seenUrls.has(url)) {
|
|
130
|
+
seenUrls.add(url);
|
|
111
131
|
result.push(header);
|
|
112
132
|
}
|
|
113
133
|
};
|
|
114
134
|
|
|
115
|
-
// Per-route CSS —
|
|
135
|
+
// Per-route CSS — as=style; rel=preload
|
|
116
136
|
for (const url of collectRouteCss(segments, manifest)) {
|
|
117
|
-
add(formatLinkHeader({ href: url, rel: 'preload', as: 'style' }));
|
|
137
|
+
add(url, formatLinkHeader({ href: url, rel: 'preload', as: 'style' }));
|
|
118
138
|
}
|
|
119
139
|
|
|
120
140
|
// Global CSS — all CSS assets from the client bundle.
|
|
121
141
|
// Covers CSS that the RSC plugin injects via data-rsc-css-href,
|
|
122
142
|
// which isn't keyed to route segments in our manifest.
|
|
123
143
|
for (const url of manifest.css['_global'] ?? []) {
|
|
124
|
-
add(formatLinkHeader({ href: url, rel: 'preload', as: 'style' }));
|
|
144
|
+
add(url, formatLinkHeader({ href: url, rel: 'preload', as: 'style' }));
|
|
125
145
|
}
|
|
126
146
|
|
|
127
|
-
// Fonts —
|
|
147
|
+
// Fonts — as=font; rel=preload; crossorigin (crossorigin required per spec)
|
|
128
148
|
for (const font of collectRouteFonts(segments, manifest)) {
|
|
129
149
|
add(
|
|
150
|
+
font.href,
|
|
130
151
|
formatLinkHeader({ href: font.href, rel: 'preload', as: 'font', crossOrigin: 'anonymous' })
|
|
131
152
|
);
|
|
132
153
|
}
|
|
@@ -134,7 +155,7 @@ export function collectEarlyHintHeaders(
|
|
|
134
155
|
// JS chunks — rel=modulepreload (skip when client JS is disabled)
|
|
135
156
|
if (!options?.skipJs) {
|
|
136
157
|
for (const url of collectRouteModulepreloads(segments, manifest)) {
|
|
137
|
-
add(formatLinkHeader({ href: url, rel: 'modulepreload' }));
|
|
158
|
+
add(url, formatLinkHeader({ href: url, rel: 'modulepreload' }));
|
|
138
159
|
}
|
|
139
160
|
}
|
|
140
161
|
|