@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.187
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{actions-O_LsyCE4.js → actions-35jnMdeJ.js} +4 -3
- package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-35jnMdeJ.js.map} +1 -1
- package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
- package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
- package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
- package/dist/_chunks/base-path-DaQrzbez.js +80 -0
- package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
- package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
- package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
- package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
- package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
- package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-DjNrIWRR.js} +8 -14
- package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-CGP6BZKO.js} +92 -14
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
- package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
- package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
- package/dist/_chunks/error-boundary-D-ODYX41.js +353 -0
- package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
- package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
- package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
- package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
- package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
- package/dist/_chunks/{logger-AWfuX-KJ.js → logger-D8xJZXIN.js} +34 -51
- package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
- package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
- package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
- package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-rCinWLiE.js} +8 -2
- package/dist/_chunks/plugin-context-rCinWLiE.js.map +1 -0
- package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
- package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
- package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
- package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
- package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
- package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
- package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
- package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
- package/dist/_chunks/scanner-C8b0Gcw3.js +1564 -0
- package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
- package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
- package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
- package/dist/_chunks/segment-context-CjOlyB8Y.js +97 -0
- package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
- package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
- package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
- package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
- package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
- package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
- package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
- package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
- package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
- package/dist/_chunks/use-segment-params-C4r4BD9T.js +398 -0
- package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
- package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
- package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +37 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/compress-module.d.ts +12 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/fs-identity.d.ts +64 -0
- package/dist/adapters/fs-identity.d.ts.map +1 -0
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -5
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +57 -1
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +13 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cache/fast-hash.d.ts +5 -0
- package/dist/cache/fast-hash.d.ts.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cdn/cloudflare-purge.js +30 -0
- package/dist/cdn/cloudflare-purge.js.map +1 -0
- package/dist/cdn/fastly-purge.js +33 -0
- package/dist/cdn/fastly-purge.js.map +1 -0
- package/dist/cdn/index.js +94 -0
- package/dist/cdn/index.js.map +1 -0
- package/dist/cdn/workers-cache-purge.js +35 -0
- package/dist/cdn/workers-cache-purge.js.map +1 -0
- package/dist/cli-check.d.ts +153 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli.d.ts +34 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +46 -21
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/hydrate.d.ts +20 -9
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
- package/dist/client/browser-entry/index.d.ts +1 -1
- package/dist/client/browser-entry/index.d.ts.map +1 -1
- package/dist/client/browser-entry/post-hydration.d.ts +4 -1
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts +3 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +7 -0
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -162
- package/dist/client/history.d.ts +13 -6
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +42 -41
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +4 -3
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +877 -311
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts +1 -7
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-api.d.ts +2 -2
- package/dist/client/navigation-commit.d.ts +87 -0
- package/dist/client/navigation-commit.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +37 -20
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +63 -23
- package/dist/client/navigation-root.d.ts.map +1 -1
- package/dist/client/params-context.d.ts +81 -0
- package/dist/client/params-context.d.ts.map +1 -0
- package/dist/client/router-effects.d.ts +129 -0
- package/dist/client/router-effects.d.ts.map +1 -0
- package/dist/client/router-lifecycle.d.ts +55 -0
- package/dist/client/router-lifecycle.d.ts.map +1 -0
- package/dist/client/router-pipeline.d.ts +84 -0
- package/dist/client/router-pipeline.d.ts.map +1 -0
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router-ref.d.ts.map +1 -1
- package/dist/client/router-skew.d.ts +21 -0
- package/dist/client/router-skew.d.ts.map +1 -0
- package/dist/client/router-types.d.ts +207 -0
- package/dist/client/router-types.d.ts.map +1 -0
- package/dist/client/router.d.ts +1 -182
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +47 -26
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +87 -34
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/ssr-data.d.ts +6 -0
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-client.d.ts +69 -0
- package/dist/client/stale-client.d.ts.map +1 -0
- package/dist/client/state.d.ts +10 -1
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +2 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/unload-guard.d.ts +5 -0
- package/dist/client/unload-guard.d.ts.map +1 -1
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +9 -3
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/client/use-segment-params.d.ts +18 -8
- package/dist/client/use-segment-params.d.ts.map +1 -1
- package/dist/codec.js +1 -1
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +5 -5
- package/dist/cookies/index.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1009 -759
- package/dist/index.js.map +1 -1
- package/dist/params/index.js +1 -1
- package/dist/plugin-context.d.ts +46 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/request-dep/analysis.d.ts +106 -0
- package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
- package/dist/plugins/request-dep/index.d.ts +75 -0
- package/dist/plugins/request-dep/index.d.ts.map +1 -0
- package/dist/plugins/request-dep/origin.d.ts +61 -0
- package/dist/plugins/request-dep/origin.d.ts.map +1 -0
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +160 -8
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +3 -44
- package/dist/routing/codegen-shared.d.ts.map +1 -1
- package/dist/routing/codegen-types.d.ts +28 -31
- package/dist/routing/codegen-types.d.ts.map +1 -1
- package/dist/routing/codegen-write.d.ts +51 -0
- package/dist/routing/codegen-write.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/collision-probe.d.ts +104 -0
- package/dist/routing/collision-probe.d.ts.map +1 -0
- package/dist/routing/collision-spaces.d.ts +26 -0
- package/dist/routing/collision-spaces.d.ts.map +1 -0
- package/dist/routing/convention-lint.d.ts +18 -4
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/export-detect.d.ts +16 -0
- package/dist/routing/export-detect.d.ts.map +1 -1
- package/dist/routing/index.js +4 -3
- package/dist/routing/interception.d.ts +235 -6
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/routing/link-codegen.d.ts +19 -4
- package/dist/routing/link-codegen.d.ts.map +1 -1
- package/dist/routing/manifest-codegen.d.ts +1 -7
- package/dist/routing/manifest-codegen.d.ts.map +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +33 -0
- package/dist/routing/segment-classify.d.ts.map +1 -1
- package/dist/routing/segment-keys.d.ts +95 -0
- package/dist/routing/segment-keys.d.ts.map +1 -0
- package/dist/routing/slot-placement.d.ts +48 -0
- package/dist/routing/slot-placement.d.ts.map +1 -0
- package/dist/routing/types.d.ts +0 -6
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +6 -0
- package/dist/routing/walkers.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +60 -9
- package/dist/schema-bridge.d.ts.map +1 -1
- package/dist/search-params/define.d.ts +62 -14
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.d.ts +0 -1
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +221 -10
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/parse-total.d.ts +70 -0
- package/dist/search-params/parse-total.d.ts.map +1 -0
- package/dist/search-params/wrappers.d.ts +26 -3
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/server/access-gate.d.ts +19 -8
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +6 -3
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +18 -1
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +13 -1
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/chain-url-parts.d.ts +82 -0
- package/dist/server/chain-url-parts.d.ts.map +1 -0
- package/dist/server/children-interception.d.ts +49 -0
- package/dist/server/children-interception.d.ts.map +1 -0
- package/dist/server/compress.d.ts.map +1 -1
- package/dist/server/default-status-page.d.ts +45 -0
- package/dist/server/default-status-page.d.ts.map +1 -0
- package/dist/server/deny-boundary.d.ts +148 -15
- package/dist/server/deny-boundary.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts +15 -4
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +85 -15
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/head-response.d.ts +35 -0
- package/dist/server/head-response.d.ts.map +1 -0
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +3 -1
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +712 -418
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-collector.d.ts +52 -0
- package/dist/server/metadata-collector.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +22 -6
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +0 -18
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline-interception.d.ts +59 -9
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +11 -0
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +23 -0
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/publish-params.d.ts +33 -0
- package/dist/server/publish-params.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +48 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +22 -3
- 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 +13 -0
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-cache-key-guard.d.ts +78 -0
- package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +15 -3
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +2 -17
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +0 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +1 -2
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +15 -2
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/skippable-prefix.d.ts +23 -0
- package/dist/server/skippable-prefix.d.ts.map +1 -0
- package/dist/server/slot-interception.d.ts +80 -0
- package/dist/server/slot-interception.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +70 -8
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +17 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +0 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +24 -13
- package/dist/server/ssr-wrappers.d.ts.map +1 -1
- package/dist/server/state-tree-diff.d.ts +6 -27
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/static-generator.d.ts +6 -0
- package/dist/server/static-generator.d.ts.map +1 -1
- package/dist/server/static-not-found.d.ts +52 -0
- package/dist/server/static-not-found.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +8 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +28 -36
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +12 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/element-type.d.ts +40 -0
- package/dist/server/utils/element-type.d.ts.map +1 -0
- package/dist/shared/als-slots.d.ts +41 -0
- package/dist/shared/als-slots.d.ts.map +1 -0
- package/dist/shared/base-path.d.ts +56 -0
- package/dist/shared/base-path.d.ts.map +1 -0
- package/dist/shared/param-value.d.ts +80 -0
- package/dist/shared/param-value.d.ts.map +1 -0
- package/dist/shared/payload-root.d.ts +108 -0
- package/dist/shared/payload-root.d.ts.map +1 -0
- package/dist/shared/rsc-cache-key.d.ts +163 -0
- package/dist/shared/rsc-cache-key.d.ts.map +1 -0
- package/dist/shared/rsc-error-envelope.d.ts +25 -0
- package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
- package/dist/shared/rsc-manifest.d.ts +28 -0
- package/dist/shared/rsc-manifest.d.ts.map +1 -0
- package/dist/shared/rsc-media-type.d.ts +40 -0
- package/dist/shared/rsc-media-type.d.ts.map +1 -0
- package/dist/shared/rsc-payload-path.d.ts +42 -0
- package/dist/shared/rsc-payload-path.d.ts.map +1 -0
- package/dist/shared/segment-info.d.ts +59 -0
- package/dist/shared/segment-info.d.ts.map +1 -0
- package/dist/shared/slot-params.d.ts +51 -0
- package/dist/shared/slot-params.d.ts.map +1 -0
- package/dist/shared/static-platform-files.d.ts +71 -0
- package/dist/shared/static-platform-files.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +1 -1
- package/docs/api/31-api-client.mdx +4 -2
- package/docs/api/33-api-search-params.mdx +38 -16
- package/docs/api/35-api-typescript.mdx +3 -3
- package/docs/api/36-cli.mdx +34 -7
- package/docs/learn/00-introduction.mdx +1 -1
- package/docs/learn/02-pages-and-layouts.mdx +2 -2
- package/docs/learn/05-typed-params.mdx +8 -8
- package/docs/learn/07-typed-routes.mdx +12 -7
- package/docs/learn/11-error-handling.mdx +18 -0
- package/docs/learn/12-client-navigation.mdx +2 -2
- package/docs/learn/14-deploying.mdx +23 -0
- package/docs/more/01-advanced-routing.mdx +173 -3
- package/docs/more/03-coming-from-nextjs.mdx +2 -2
- package/docs/more/04-metadata-and-fonts.mdx +1 -1
- package/docs/more/50-ai-agent-instructions.mdx +6 -4
- package/package.json +9 -6
- package/src/adapters/build-output-helper.ts +116 -3
- package/src/adapters/cloudflare.ts +90 -12
- package/src/adapters/compress-module.ts +79 -1
- package/src/adapters/fs-identity.ts +156 -0
- package/src/adapters/nitro.ts +6 -6
- package/src/adapters/shared.ts +196 -21
- package/src/adapters/types.ts +13 -1
- package/src/cache/fast-hash.ts +5 -0
- package/src/cli-check.ts +458 -0
- package/src/cli.ts +59 -24
- package/src/client/browser-entry/action-dispatch.ts +11 -5
- package/src/client/browser-entry/hydrate.ts +40 -29
- package/src/client/browser-entry/index.ts +15 -6
- package/src/client/browser-entry/post-hydration.ts +10 -9
- package/src/client/browser-entry/router-init.ts +104 -48
- package/src/client/browser-entry/rsc-stream.ts +11 -2
- package/src/client/error-boundary.tsx +85 -16
- package/src/client/history.ts +13 -6
- package/src/client/index.ts +14 -3
- package/src/client/internal.ts +4 -3
- package/src/client/link.tsx +120 -72
- package/src/client/navigation-api.ts +3 -3
- package/src/client/navigation-commit.ts +197 -0
- package/src/client/navigation-context.ts +40 -67
- package/src/client/navigation-root.tsx +326 -101
- package/src/client/params-context.ts +192 -0
- package/src/client/router-effects.ts +231 -0
- package/src/client/router-lifecycle.ts +288 -0
- package/src/client/router-pipeline.ts +393 -0
- package/src/client/router-ref.ts +1 -1
- package/src/client/router-skew.ts +30 -0
- package/src/client/router-types.ts +230 -0
- package/src/client/router.ts +243 -707
- package/src/client/rsc-fetch.ts +183 -106
- package/src/client/segment-cache.ts +129 -44
- package/src/client/ssr-data.ts +6 -0
- package/src/client/stale-client.ts +191 -0
- package/src/client/state.ts +14 -1
- package/src/client/top-loader.tsx +2 -1
- package/src/client/unload-guard.ts +34 -2
- package/src/client/use-cookie.ts +9 -2
- package/src/client/use-query-states.ts +102 -39
- package/src/client/use-router.ts +1 -1
- package/src/client/use-segment-params.ts +41 -18
- package/src/cookies/define-cookie.ts +6 -1
- package/src/index.ts +25 -4
- package/src/plugin-context.ts +58 -0
- package/src/plugins/adapter-build.ts +4 -0
- package/src/plugins/build-manifest.ts +34 -13
- package/src/plugins/entries.ts +4 -0
- package/src/plugins/fonts.ts +11 -8
- package/src/plugins/request-dep/analysis.ts +447 -0
- package/src/plugins/request-dep/index.ts +376 -0
- package/src/plugins/request-dep/origin.ts +310 -0
- package/src/plugins/routing.ts +97 -102
- package/src/plugins/shims.ts +0 -1
- package/src/plugins/static-build.ts +391 -95
- package/src/routing/codegen-shared.ts +3 -79
- package/src/routing/codegen-types.ts +28 -31
- package/src/routing/codegen-write.ts +139 -0
- package/src/routing/codegen.ts +86 -172
- package/src/routing/collision-probe.ts +438 -0
- package/src/routing/collision-spaces.ts +281 -0
- package/src/routing/convention-lint.ts +139 -40
- package/src/routing/export-detect.ts +151 -7
- package/src/routing/interception.ts +896 -50
- package/src/routing/link-codegen.ts +32 -65
- package/src/routing/manifest-codegen.ts +6 -62
- package/src/routing/scanner.ts +117 -92
- package/src/routing/segment-classify.ts +57 -0
- package/src/routing/segment-keys.ts +146 -0
- package/src/routing/slot-placement.ts +97 -0
- package/src/routing/types.ts +0 -6
- package/src/routing/walkers.ts +8 -0
- package/src/schema-bridge.ts +180 -58
- package/src/search-params/define.ts +108 -66
- package/src/search-params/index.ts +0 -1
- package/src/search-params/parse-total.ts +78 -0
- package/src/search-params/wrappers.ts +60 -11
- package/src/server/access-gate.tsx +60 -40
- package/src/server/action-handler.ts +4 -5
- package/src/server/actions.ts +6 -3
- package/src/server/als-registry.ts +18 -1
- package/src/server/build-manifest.ts +14 -1
- package/src/server/chain-url-parts.ts +163 -0
- package/src/server/children-interception.ts +115 -0
- package/src/server/compress.ts +9 -1
- package/src/server/default-status-page.ts +127 -0
- package/src/server/deny-boundary.ts +269 -41
- package/src/server/deny-renderer.ts +111 -39
- package/src/server/error-boundary-wrapper.ts +166 -79
- package/src/server/head-response.ts +54 -0
- package/src/server/index.ts +1 -3
- package/src/server/internal.ts +2 -2
- package/src/server/metadata-collector.ts +115 -0
- package/src/server/param-coercion.ts +51 -69
- package/src/server/pipeline-helpers.ts +2 -47
- package/src/server/pipeline-interception.ts +81 -32
- package/src/server/pipeline-outcome.ts +4 -5
- package/src/server/pipeline-phases.ts +77 -25
- package/src/server/pipeline.ts +57 -10
- package/src/server/prebuilt-runtime.ts +4 -4
- package/src/server/primitives.ts +25 -0
- package/src/server/publish-params.ts +42 -0
- package/src/server/request-context.ts +97 -18
- package/src/server/route-element-builder.ts +241 -253
- package/src/server/route-handler.ts +8 -8
- package/src/server/route-matcher.ts +21 -9
- package/src/server/rsc-cache-key-guard.ts +164 -0
- package/src/server/rsc-entry/api-handler.ts +6 -8
- package/src/server/rsc-entry/deny-fallback.ts +3 -2
- package/src/server/rsc-entry/error-renderer.ts +94 -35
- package/src/server/rsc-entry/helpers.ts +33 -28
- package/src/server/rsc-entry/index.ts +36 -33
- package/src/server/rsc-entry/render-route.ts +18 -10
- package/src/server/rsc-entry/revalidate-renderer.ts +3 -2
- package/src/server/rsc-entry/rsc-payload.ts +113 -40
- package/src/server/rsc-entry/rsc-stream.ts +29 -5
- package/src/server/rsc-entry/ssr-renderer.ts +38 -22
- package/src/server/skippable-prefix.ts +92 -0
- package/src/server/slot-interception.ts +168 -0
- package/src/server/slot-resolver.ts +263 -158
- package/src/server/ssr-bridge-types.ts +18 -0
- package/src/server/ssr-entry.ts +20 -10
- package/src/server/ssr-wrappers.tsx +33 -32
- package/src/server/state-tree-diff.ts +14 -55
- package/src/server/static-generator.ts +68 -29
- package/src/server/static-not-found.ts +134 -0
- package/src/server/status-code-resolver.ts +28 -11
- package/src/server/tree-builder.ts +35 -218
- package/src/server/types.ts +12 -7
- package/src/server/utils/element-type.ts +72 -0
- package/src/shared/als-slots.ts +72 -0
- package/src/shared/base-path.ts +80 -0
- package/src/shared/param-value.ts +244 -0
- package/src/shared/payload-root.ts +151 -0
- package/src/shared/rsc-cache-key.ts +269 -0
- package/src/shared/rsc-error-envelope.ts +31 -0
- package/src/shared/rsc-manifest.ts +29 -0
- package/src/shared/rsc-media-type.ts +43 -0
- package/src/shared/rsc-payload-path.ts +45 -0
- package/src/shared/segment-info.ts +60 -0
- package/src/shared/slot-params.ts +111 -0
- package/src/shared/static-platform-files.ts +75 -0
- package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
- package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
- package/dist/_chunks/cache-api-B-lhk9p4.js.map +0 -1
- package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
- package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
- package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
- package/dist/_chunks/define-COtkxMRT.js +0 -191
- package/dist/_chunks/define-COtkxMRT.js.map +0 -1
- package/dist/_chunks/define-c4au4I9R.js +0 -82
- package/dist/_chunks/define-c4au4I9R.js.map +0 -1
- package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
- package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
- package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
- package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
- package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
- package/dist/_chunks/registry-DbJPKoBp.js +0 -20
- package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
- package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
- package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
- package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
- package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
- package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
- package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
- package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
- package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
- package/dist/_chunks/walkers-_6zKFlch.js +0 -1002
- package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/plugins/request-dep.d.ts +0 -32
- package/dist/plugins/request-dep.d.ts.map +0 -1
- package/dist/search-params/registry.d.ts +0 -20
- package/dist/search-params/registry.d.ts.map +0 -1
- package/dist/segment-params/define.d.ts +0 -89
- package/dist/segment-params/define.d.ts.map +0 -1
- package/dist/segment-params/index.d.ts +0 -3
- package/dist/segment-params/index.d.ts.map +0 -1
- package/dist/segment-params/index.js +0 -2
- package/src/plugins/request-dep.ts +0 -499
- package/src/search-params/registry.ts +0 -31
- package/src/segment-params/define.ts +0 -249
- package/src/segment-params/index.ts +0 -9
package/src/server/ssr-entry.ts
CHANGED
|
@@ -14,10 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
// @ts-expect-error — virtual module provided by timber-entries plugin
|
|
16
16
|
import config from 'virtual:timber-config';
|
|
17
|
-
// TIM-830: Populate the search-params registry eagerly so <Link> rendered
|
|
18
|
-
// during SSR can serialize flat `Partial<T>` values via registry lookup.
|
|
19
|
-
// @ts-expect-error — virtual module provided by timber-routing plugin
|
|
20
|
-
import 'virtual:timber-search-params-registry';
|
|
21
17
|
// TIM-949: Populate the codec registry so server-rendered <Link> components
|
|
22
18
|
// use codec.serialize() instead of String() fallback for URL interpolation.
|
|
23
19
|
// @ts-expect-error — virtual module provided by timber-routing plugin
|
|
@@ -45,6 +41,7 @@ import { wrapSsrElement } from './ssr-wrappers.js';
|
|
|
45
41
|
import { withSpan } from './tracing.js';
|
|
46
42
|
import { setCurrentParams } from '../client/use-segment-params.js';
|
|
47
43
|
import { registerSsrDataProvider, type SsrData } from '../client/ssr-data.js';
|
|
44
|
+
import { readPayloadTree } from '../shared/payload-root.js';
|
|
48
45
|
import type { NavContext } from './ssr-bridge-types.js';
|
|
49
46
|
|
|
50
47
|
// Pre-import Node.js stream modules at module load time — not per-request.
|
|
@@ -129,6 +126,7 @@ export async function handleSsr(
|
|
|
129
126
|
searchParams: navContext.searchParams,
|
|
130
127
|
cookies: navContext.cookies ?? new Map(),
|
|
131
128
|
params: navContext.segmentParams,
|
|
129
|
+
slotParams: navContext.slotParams,
|
|
132
130
|
_navContext: navContext,
|
|
133
131
|
};
|
|
134
132
|
|
|
@@ -154,19 +152,25 @@ export async function handleSsr(
|
|
|
154
152
|
// On Node.js: convert Web ReadableStream → Node Readable → createFromNodeStream
|
|
155
153
|
// (eliminates Promise-per-chunk overhead from Web Streams reader)
|
|
156
154
|
// On Workers: createFromReadableStream (Web Streams are V8-native C++ there)
|
|
157
|
-
let
|
|
155
|
+
let root: unknown;
|
|
158
156
|
if (hasNodeStreamDecode && _nodeStreamImports) {
|
|
159
157
|
const nodeRscStream = _nodeStreamImports.ReadableFromWeb(
|
|
160
158
|
rscStream as import('stream/web').ReadableStream
|
|
161
159
|
);
|
|
162
|
-
|
|
160
|
+
root = createFromNodeStream(nodeRscStream);
|
|
163
161
|
} else {
|
|
164
|
-
|
|
162
|
+
root = createFromReadableStream(rscStream);
|
|
165
163
|
}
|
|
164
|
+
// The payload root is `{ tree, params, slotParams }` — params ride
|
|
165
|
+
// beside the tree rather than in a provider around it (TIM-1297). SSR
|
|
166
|
+
// renders the tree; the params it needs are already in `ssrData` above,
|
|
167
|
+
// which is where `useSegmentParams()` reads them from during SSR. The
|
|
168
|
+
// client renders its own provider over the same records.
|
|
169
|
+
const element = Promise.resolve(root).then(readPayloadTree) as React.ReactNode;
|
|
166
170
|
const _decodeEnd = performance.now();
|
|
167
171
|
|
|
168
172
|
// Wrap with the same component tree structure as the client hydration
|
|
169
|
-
// tree (NavigationRoot →
|
|
173
|
+
// tree (NavigationRoot → TopLoader →
|
|
170
174
|
// TimberNuqsAdapter → NuqsAdapterProvider → NavigationProvider).
|
|
171
175
|
// This ensures useId() produces matching IDs on both sides, preventing
|
|
172
176
|
// hydration mismatches in libraries like Radix UI. See TIM-532.
|
|
@@ -228,8 +232,11 @@ export async function handleSsr(
|
|
|
228
232
|
// stage: pipeline() destroys every stage on failure, so only an
|
|
229
233
|
// output kept outside the pipeline can append the noindex script
|
|
230
234
|
// and close cleanly (TIM-1019).
|
|
235
|
+
// Resolve the lazy segment script now that the shell is ready
|
|
236
|
+
// (AccessGates have settled, ALS deny index is authoritative).
|
|
237
|
+
const resolvedHeadHtml = navContext.headHtml + (navContext.getHeadSegmentScript?.() ?? '');
|
|
231
238
|
const bufferedTransform = createNodeBufferedTransform();
|
|
232
|
-
const headInjector = createNodeHeadInjector(
|
|
239
|
+
const headInjector = createNodeHeadInjector(resolvedHeadHtml);
|
|
233
240
|
const flightInjector = createNodeFlightInjector(navContext.rscStream, {
|
|
234
241
|
renderTimeoutMs,
|
|
235
242
|
errorSignal: navContext._errorSignal,
|
|
@@ -288,8 +295,11 @@ export async function handleSsr(
|
|
|
288
295
|
// HTML fragments so downstream transforms never see chunk boundaries
|
|
289
296
|
// mid-tag. This prevents RSC script injection from breaking HTML
|
|
290
297
|
// structure (TIM-527).
|
|
298
|
+
// Resolve the lazy segment script now that the shell is ready
|
|
299
|
+
// (AccessGates have settled, ALS deny index is authoritative).
|
|
300
|
+
const resolvedHeadHtml = navContext.headHtml + (navContext.getHeadSegmentScript?.() ?? '');
|
|
291
301
|
let outputStream = htmlStream.pipeThrough(createBufferedTransformStream());
|
|
292
|
-
outputStream = injectHead(outputStream,
|
|
302
|
+
outputStream = injectHead(outputStream, resolvedHeadHtml);
|
|
293
303
|
outputStream = injectRscPayload(
|
|
294
304
|
outputStream,
|
|
295
305
|
navContext.rscStream,
|
|
@@ -8,13 +8,26 @@
|
|
|
8
8
|
* Radix UI that rely on useId() internally.
|
|
9
9
|
*
|
|
10
10
|
* The client tree (browser-entry.ts) wraps the RSC element with:
|
|
11
|
-
* NavigationRoot →
|
|
11
|
+
* NavigationRoot → Fragment(TopLoader, ...) →
|
|
12
12
|
* TimberNuqsAdapter → NuqsAdapterProvider → NavigationProvider → element
|
|
13
13
|
*
|
|
14
14
|
* The SSR tree must produce the same component boundaries. These wrappers
|
|
15
15
|
* are no-op components that render children directly but exist as fiber
|
|
16
16
|
* nodes to match the client's tree depth.
|
|
17
17
|
*
|
|
18
|
+
* **What actually shifts an id is a fork, not depth.** `useId` derives from
|
|
19
|
+
* React's tree context, which only changes where a fiber has more than one
|
|
20
|
+
* child — here, `Fragment(TopLoader, element)`. A single-child wrapper, plain
|
|
21
|
+
* component or context provider, leaves every id below it unchanged; that is
|
|
22
|
+
* verified, not assumed. So the client-only wrappers that take exactly one
|
|
23
|
+
* child — `SegmentUpdateContext.Provider`, and `PayloadRoot` with the params
|
|
24
|
+
* provider it renders (TIM-1297) — have no mirror here and need none. Mirror a
|
|
25
|
+
* new wrapper only if it branches.
|
|
26
|
+
*
|
|
27
|
+
* `PendingNavigationProvider` and its `SsrPendingProvider` mirror were both
|
|
28
|
+
* deleted with the dead pending context (TIM-1307). Removing them shifts no
|
|
29
|
+
* id: it was a single-child provider, the case the rule above covers.
|
|
30
|
+
*
|
|
18
31
|
* Related issues: facebook/react#24669, radix-ui/primitives#3700
|
|
19
32
|
* See design/02-rendering-pipeline.md §"RSC → SSR → Client Hydration"
|
|
20
33
|
*/
|
|
@@ -25,11 +38,13 @@ import { withNuqsSsrAdapter } from './nuqs-ssr-provider.js';
|
|
|
25
38
|
/**
|
|
26
39
|
* SSR equivalent of NavigationRoot.
|
|
27
40
|
*
|
|
28
|
-
* On the client, NavigationRoot uses useState and standalone startTransition,
|
|
29
|
-
*
|
|
41
|
+
* On the client, NavigationRoot uses useState and standalone startTransition,
|
|
42
|
+
* rendering `Fragment(TopLoader, element)` — or the element alone when the top
|
|
43
|
+
* loader is disabled.
|
|
30
44
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
45
|
+
* The Fragment is the part that matters: it forks, so it advances React's tree
|
|
46
|
+
* context. Both sides must agree on whether it is there, which is why
|
|
47
|
+
* `hasTopLoader` is threaded in from the same config the client reads.
|
|
33
48
|
*/
|
|
34
49
|
function SsrNavigationRoot({
|
|
35
50
|
children,
|
|
@@ -39,20 +54,8 @@ function SsrNavigationRoot({
|
|
|
39
54
|
hasTopLoader: boolean;
|
|
40
55
|
}): ReactNode {
|
|
41
56
|
if (hasTopLoader) {
|
|
42
|
-
return createElement(
|
|
43
|
-
SsrPendingProvider,
|
|
44
|
-
null,
|
|
45
|
-
createElement(Fragment, null, createElement(SsrTopLoader, null), children)
|
|
46
|
-
);
|
|
57
|
+
return createElement(Fragment, null, createElement(SsrTopLoader, null), children);
|
|
47
58
|
}
|
|
48
|
-
return createElement(SsrPendingProvider, null, children);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* SSR equivalent of PendingNavigationProvider.
|
|
53
|
-
* Matches the context.Provider component boundary on the client.
|
|
54
|
-
*/
|
|
55
|
-
function SsrPendingProvider({ children }: { children: ReactNode }): ReactNode {
|
|
56
59
|
return children as ReactNode;
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -98,21 +101,19 @@ function SsrNuqsWrapper({
|
|
|
98
101
|
*
|
|
99
102
|
* Client tree (browser-entry.ts):
|
|
100
103
|
* NavigationRoot
|
|
101
|
-
* →
|
|
102
|
-
* →
|
|
103
|
-
* →
|
|
104
|
-
* →
|
|
105
|
-
* →
|
|
106
|
-
* → [RSC element]
|
|
104
|
+
* → Fragment(TopLoader, element)
|
|
105
|
+
* → TimberNuqsAdapter
|
|
106
|
+
* → NuqsAdapterProvider (from createAdapterProvider)
|
|
107
|
+
* → NavigationProvider
|
|
108
|
+
* → [RSC element]
|
|
107
109
|
*
|
|
108
110
|
* SSR tree (this function):
|
|
109
111
|
* SsrNavigationRoot
|
|
110
|
-
* →
|
|
111
|
-
* →
|
|
112
|
-
* →
|
|
113
|
-
* →
|
|
114
|
-
* →
|
|
115
|
-
* → [RSC element]
|
|
112
|
+
* → Fragment(SsrTopLoader, element)
|
|
113
|
+
* → SsrNuqsWrapper
|
|
114
|
+
* → NuqsAdapterProvider (from withNuqsSsrAdapter → createAdapterProvider)
|
|
115
|
+
* → SsrNavigationProvider
|
|
116
|
+
* → [RSC element]
|
|
116
117
|
*
|
|
117
118
|
* @param element - The decoded RSC element
|
|
118
119
|
* @param searchParams - Request search params for nuqs SSR adapter
|
|
@@ -126,7 +127,7 @@ export function wrapSsrElement(
|
|
|
126
127
|
// Build inside-out to match the client's createElement chain:
|
|
127
128
|
// NavigationProvider(TimberNuqsAdapter(element))
|
|
128
129
|
// → passed as initial to NavigationRoot
|
|
129
|
-
// → NavigationRoot renders
|
|
130
|
+
// → NavigationRoot renders Fragment(TopLoader, initial)
|
|
130
131
|
|
|
131
132
|
// 1. Innermost: NavigationProvider equivalent
|
|
132
133
|
const withNav = createElement(SsrNavigationProvider, null, element);
|
|
@@ -135,7 +136,7 @@ export function wrapSsrElement(
|
|
|
135
136
|
// eslint-disable-next-line react/no-children-prop -- createElement API
|
|
136
137
|
const withNuqs = createElement(SsrNuqsWrapper, { searchParams, children: withNav });
|
|
137
138
|
|
|
138
|
-
// 3. Outermost: NavigationRoot equivalent (
|
|
139
|
+
// 3. Outermost: NavigationRoot equivalent (the TopLoader fork)
|
|
139
140
|
// eslint-disable-next-line react/no-children-prop -- createElement API
|
|
140
141
|
return createElement(SsrNavigationRoot, { hasTopLoader, children: withNuqs });
|
|
141
142
|
}
|
|
@@ -18,64 +18,17 @@ import { swallow } from './logger.js';
|
|
|
18
18
|
import { matchUrlParts } from './tree-match.js';
|
|
19
19
|
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
20
20
|
|
|
21
|
-
// ─── Segment Key Computation ─────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Segment node shape expected by computeSegmentKeys.
|
|
25
|
-
* Matches the relevant fields from ManifestSegmentNode without
|
|
26
|
-
* importing it (avoids circular deps from routing → server).
|
|
27
|
-
*/
|
|
28
|
-
interface SegmentKeyInput {
|
|
29
|
-
urlPath: string;
|
|
30
|
-
segmentName?: string;
|
|
31
|
-
segmentType?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Compute state-tree keys for a segment chain.
|
|
36
|
-
*
|
|
37
|
-
* Non-group segments use their urlPath as-is. Route groups accumulate
|
|
38
|
-
* ancestor group names to produce globally unique keys:
|
|
39
|
-
* app/(a)/(shared)/dashboard → keys: ['/', '/(a)', '/(a)/(shared)', '/dashboard']
|
|
40
|
-
*
|
|
41
|
-
* This is the single source of truth for segment keys — used by the
|
|
42
|
-
* element builder (skip decisions, SegmentOutlet props), segment info
|
|
43
|
-
* (X-Timber-Segments header), and the client cache/state tree.
|
|
44
|
-
*/
|
|
45
|
-
export function computeSegmentKeys(segments: SegmentKeyInput[]): string[] {
|
|
46
|
-
const keys: string[] = [];
|
|
47
|
-
let prevKey = '';
|
|
48
|
-
|
|
49
|
-
for (const segment of segments) {
|
|
50
|
-
if (segment.segmentType === 'group') {
|
|
51
|
-
const base = prevKey === '/' ? '' : prevKey;
|
|
52
|
-
const key = `${base}/${segment.segmentName}`;
|
|
53
|
-
keys.push(key);
|
|
54
|
-
prevKey = key;
|
|
55
|
-
} else {
|
|
56
|
-
keys.push(segment.urlPath);
|
|
57
|
-
prevKey = segment.urlPath;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return keys;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Compute a unique key for a parallel route slot.
|
|
66
|
-
* Format: `{parentSegmentId}/@{slotName}`, e.g. `/@sidebar` or `/dashboard/@modal`.
|
|
67
|
-
*/
|
|
68
|
-
export function computeSlotKey(parentSegmentId: string, slotName: string): string {
|
|
69
|
-
const name = slotName.startsWith('@') ? slotName : `@${slotName}`;
|
|
70
|
-
const prefix = parentSegmentId === '/' ? '' : parentSegmentId;
|
|
71
|
-
return `${prefix}/${name}`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
21
|
// ─── State Tree Parsing ──────────────────────────────────────────
|
|
75
22
|
|
|
76
23
|
/** Parsed client state tree with segment paths and optional slot paths. */
|
|
77
24
|
export interface ClientStateTree extends Set<string> {
|
|
78
25
|
slots?: Set<string> | null;
|
|
26
|
+
/**
|
|
27
|
+
* `app/` directory paths of the segments currently mounted on the client.
|
|
28
|
+
* Read only by interception scoping — never by the skip decisions.
|
|
29
|
+
* See design/07-routing.md §"Intercepting Routes" (TIM-1282).
|
|
30
|
+
*/
|
|
31
|
+
treePaths?: Set<string> | null;
|
|
79
32
|
}
|
|
80
33
|
|
|
81
34
|
/**
|
|
@@ -93,14 +46,20 @@ export function parseClientStateTree(req: Request): ClientStateTree | null {
|
|
|
93
46
|
if (!header) return null;
|
|
94
47
|
|
|
95
48
|
try {
|
|
96
|
-
const parsed = JSON.parse(header) as {
|
|
49
|
+
const parsed = JSON.parse(header) as {
|
|
50
|
+
segments?: unknown;
|
|
51
|
+
slots?: unknown;
|
|
52
|
+
treePaths?: unknown;
|
|
53
|
+
};
|
|
97
54
|
const hasSegments = Array.isArray(parsed.segments) && parsed.segments.length > 0;
|
|
98
55
|
const hasSlots = Array.isArray(parsed.slots) && parsed.slots.length > 0;
|
|
99
|
-
|
|
56
|
+
const hasTreePaths = Array.isArray(parsed.treePaths) && parsed.treePaths.length > 0;
|
|
57
|
+
if (!hasSegments && !hasSlots && !hasTreePaths) {
|
|
100
58
|
return null;
|
|
101
59
|
}
|
|
102
60
|
const result = new Set(hasSegments ? (parsed.segments as string[]) : []) as ClientStateTree;
|
|
103
61
|
result.slots = hasSlots ? new Set(parsed.slots as string[]) : null;
|
|
62
|
+
result.treePaths = hasTreePaths ? new Set(parsed.treePaths as string[]) : null;
|
|
104
63
|
return result;
|
|
105
64
|
} catch (err) {
|
|
106
65
|
swallow(err, 'malformed X-Timber-State-Tree header');
|
|
@@ -23,6 +23,13 @@ import type { ManifestRoot, ManifestSegmentNode, ManifestFile } from './route-ma
|
|
|
23
23
|
import { hasDynamicSegments, hasUserSitemap, resolvePathWithParams } from './sitemap-generator.js';
|
|
24
24
|
import { loadModule } from './safe-load.js';
|
|
25
25
|
import { METADATA_ROUTE_CONVENTIONS, resolveServePathForFile } from './metadata-routes.js';
|
|
26
|
+
import { RSC_CONTENT_TYPE } from '../shared/rsc-media-type.js';
|
|
27
|
+
import {
|
|
28
|
+
NOT_FOUND_ENTRY_PATH,
|
|
29
|
+
NOT_FOUND_PROBE_PATH,
|
|
30
|
+
resolveNotFoundPage,
|
|
31
|
+
type NotFoundProbe,
|
|
32
|
+
} from './static-not-found.js';
|
|
26
33
|
|
|
27
34
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
28
35
|
|
|
@@ -70,6 +77,12 @@ export interface StaticGenerationSummary {
|
|
|
70
77
|
errors: StaticGenerationError[];
|
|
71
78
|
/** Redirects from middleware/proxy that need platform redirect rules. */
|
|
72
79
|
redirects: StaticRedirectEntry[];
|
|
80
|
+
/**
|
|
81
|
+
* Non-fatal problems the build should surface. Unlike `errors`, these do not
|
|
82
|
+
* fail the build — they name a degraded outcome the developer can act on
|
|
83
|
+
* (e.g. the not-found probe answering with something other than a 404).
|
|
84
|
+
*/
|
|
85
|
+
warnings: string[];
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
export interface StaticGenerationOptions {
|
|
@@ -211,7 +224,22 @@ export async function enumerateStaticUrls(
|
|
|
211
224
|
|
|
212
225
|
await walk(root);
|
|
213
226
|
urls.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
|
|
214
|
-
|
|
227
|
+
|
|
228
|
+
// Drop repeats of the same (path, kind). `generateStaticSegmentParams` is
|
|
229
|
+
// an application function returning whatever a CMS query gave it, and a
|
|
230
|
+
// repeated combo asks for the same page twice — identical bytes to the same
|
|
231
|
+
// file, so it is waste rather than ambiguity. Removing it here keeps the
|
|
232
|
+
// output-path collision check (TIM-1266) meaning what it says: two
|
|
233
|
+
// *different* routes competing for one file.
|
|
234
|
+
const seen = new Set<string>();
|
|
235
|
+
const unique = urls.filter(({ urlPath, kind }) => {
|
|
236
|
+
const key = `${kind}:${urlPath}`;
|
|
237
|
+
if (seen.has(key)) return false;
|
|
238
|
+
seen.add(key);
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return { urls: unique, errors };
|
|
215
243
|
}
|
|
216
244
|
|
|
217
245
|
/**
|
|
@@ -401,7 +429,18 @@ export async function generateStaticSite(
|
|
|
401
429
|
): Promise<StaticGenerationSummary> {
|
|
402
430
|
const log = options.log ?? ((msg: string) => console.log(msg));
|
|
403
431
|
const timeoutMs = options.renderTimeoutMs ?? DEFAULT_RENDER_TIMEOUT_MS;
|
|
404
|
-
const summary: StaticGenerationSummary = {
|
|
432
|
+
const summary: StaticGenerationSummary = {
|
|
433
|
+
rendered: 0,
|
|
434
|
+
errors: [],
|
|
435
|
+
redirects: [],
|
|
436
|
+
warnings: [],
|
|
437
|
+
};
|
|
438
|
+
// Warnings are recorded and logged from one place so the summary the build
|
|
439
|
+
// reports and the line the developer reads can never describe different sets.
|
|
440
|
+
const warn = (message: string) => {
|
|
441
|
+
summary.warnings.push(message);
|
|
442
|
+
log(`[timber] Static generation: ${message}`);
|
|
443
|
+
};
|
|
405
444
|
|
|
406
445
|
log('[timber] Static generation: enumerating routes…');
|
|
407
446
|
|
|
@@ -477,7 +516,7 @@ export async function generateStaticSite(
|
|
|
477
516
|
// success so a transient write error doesn't orphan the flight file.
|
|
478
517
|
if (htmlOk) {
|
|
479
518
|
try {
|
|
480
|
-
const rscResponse = await renderUrl(handler, urlPath,
|
|
519
|
+
const rscResponse = await renderUrl(handler, urlPath, RSC_CONTENT_TYPE, timeoutMs);
|
|
481
520
|
|
|
482
521
|
if (rscResponse.status < 400) {
|
|
483
522
|
await options.onEntry({
|
|
@@ -485,7 +524,7 @@ export async function generateStaticSite(
|
|
|
485
524
|
status: rscResponse.status,
|
|
486
525
|
headers: headersToRecord(rscResponse.headers),
|
|
487
526
|
body: rscResponse.body,
|
|
488
|
-
contentType: rscResponse.headers.get('content-type') ??
|
|
527
|
+
contentType: rscResponse.headers.get('content-type') ?? RSC_CONTENT_TYPE,
|
|
489
528
|
kind: 'rsc',
|
|
490
529
|
});
|
|
491
530
|
summary.rendered++;
|
|
@@ -548,34 +587,34 @@ export async function generateStaticSite(
|
|
|
548
587
|
}
|
|
549
588
|
}
|
|
550
589
|
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
|
|
554
|
-
// Uses /_timber/__not_found as the probe path: _timber/ is the framework's
|
|
555
|
-
// reserved prefix and __not_found is a private segment (double underscore),
|
|
556
|
-
// so user routes (including catch-all [..slug]) cannot claim this path.
|
|
590
|
+
// Write 404.html. Always — the classification of what to write, and what
|
|
591
|
+
// to tell the developer about it, lives in static-not-found.ts.
|
|
592
|
+
let probe: NotFoundProbe;
|
|
557
593
|
try {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
'
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
headers: headersToRecord(notFoundResponse.headers),
|
|
569
|
-
body: notFoundResponse.body,
|
|
570
|
-
contentType: notFoundResponse.headers.get('content-type') ?? 'text/html',
|
|
571
|
-
kind: 'html',
|
|
572
|
-
});
|
|
573
|
-
log('[timber] Static generation: rendered 404.html');
|
|
574
|
-
}
|
|
575
|
-
} catch {
|
|
576
|
-
log('[timber] Static generation: 404 page render failed — skipping 404.html');
|
|
594
|
+
const response = await renderUrl(handler, NOT_FOUND_PROBE_PATH, 'text/html', timeoutMs);
|
|
595
|
+
probe = {
|
|
596
|
+
kind: 'response',
|
|
597
|
+
status: response.status,
|
|
598
|
+
body: response.body,
|
|
599
|
+
headers: headersToRecord(response.headers),
|
|
600
|
+
contentType: response.headers.get('content-type') ?? 'text/html',
|
|
601
|
+
};
|
|
602
|
+
} catch (error) {
|
|
603
|
+
probe = { kind: 'threw', error };
|
|
577
604
|
}
|
|
578
605
|
|
|
606
|
+
const notFound = resolveNotFoundPage(probe, manifest.root);
|
|
607
|
+
if (notFound.warning) warn(notFound.warning);
|
|
608
|
+
await options.onEntry({
|
|
609
|
+
urlPath: NOT_FOUND_ENTRY_PATH,
|
|
610
|
+
status: 404,
|
|
611
|
+
headers: notFound.headers,
|
|
612
|
+
body: notFound.body,
|
|
613
|
+
contentType: notFound.contentType,
|
|
614
|
+
kind: 'html',
|
|
615
|
+
});
|
|
616
|
+
log(notFound.logLine);
|
|
617
|
+
|
|
579
618
|
// Discover and render paginated sub-sitemaps. If /sitemap.xml was an
|
|
580
619
|
// index (contains /sitemap/N.xml references), render each sub-sitemap.
|
|
581
620
|
if (options.autoSitemapEnabled && !hasUserSitemap(manifest.root)) {
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 404.html generation for static builds.
|
|
3
|
+
*
|
|
4
|
+
* `404.html` is written unconditionally (design/11-platform.md item 7). When
|
|
5
|
+
* the not-found probe does not produce a usable page, the framework default
|
|
6
|
+
* status page is written instead and the reason is reported as a warning —
|
|
7
|
+
* a static deploy that silently shipped no 404 page fell back to the host's
|
|
8
|
+
* generic one, and nothing in the build output said so (TIM-1258).
|
|
9
|
+
*
|
|
10
|
+
* The classification lives here, apart from the generation orchestration, so
|
|
11
|
+
* it can be exercised directly: every branch is a different diagnosis handed
|
|
12
|
+
* to a developer, and a wrong one sends them to the wrong file.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { resolveStatusFile } from './status-code-resolver.js';
|
|
16
|
+
import { DEFAULT_STATUS_PAGE_CONTENT_TYPE, defaultStatusPageHtml } from './default-status-page.js';
|
|
17
|
+
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The probe path. `_timber/` is the framework's reserved prefix and
|
|
21
|
+
* `__not_found` is a private segment (double underscore), so user routes —
|
|
22
|
+
* including a catch-all `[...slug]` — cannot claim it.
|
|
23
|
+
*/
|
|
24
|
+
export const NOT_FOUND_PROBE_PATH = '/_timber/__not_found';
|
|
25
|
+
|
|
26
|
+
/** URL path the static build maps to `404.html`. See plugins/static-build.ts. */
|
|
27
|
+
export const NOT_FOUND_ENTRY_PATH = '/_timber_not_found';
|
|
28
|
+
|
|
29
|
+
/** What the probe produced: a response, or the failure that replaced one. */
|
|
30
|
+
export type NotFoundProbe =
|
|
31
|
+
| {
|
|
32
|
+
kind: 'response';
|
|
33
|
+
status: number;
|
|
34
|
+
body: Uint8Array;
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
contentType: string;
|
|
37
|
+
}
|
|
38
|
+
| { kind: 'threw'; error: unknown };
|
|
39
|
+
|
|
40
|
+
export interface NotFoundResult {
|
|
41
|
+
/** Body to write as `404.html` — never empty. */
|
|
42
|
+
body: Uint8Array;
|
|
43
|
+
headers: Record<string, string>;
|
|
44
|
+
contentType: string;
|
|
45
|
+
/** Non-fatal problem to report, or null when the app rendered its own page. */
|
|
46
|
+
warning: string | null;
|
|
47
|
+
/** Build log line. Names *which* page was written. */
|
|
48
|
+
logLine: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Whether the not-found chain has anything to render for this root.
|
|
53
|
+
*
|
|
54
|
+
* Asks the resolver rather than inspecting file names: `renderNoMatchPage`
|
|
55
|
+
* builds its chain from the root segment alone and resolves through exactly
|
|
56
|
+
* these two calls, so this cannot drift from what the request path does. A
|
|
57
|
+
* hand-rolled key check gets it wrong — a root that declares only `500.tsx`
|
|
58
|
+
* has status files but nothing a 404 can select.
|
|
59
|
+
*/
|
|
60
|
+
function rootDeclaresNotFoundPage(root: ManifestSegmentNode): boolean {
|
|
61
|
+
return (
|
|
62
|
+
resolveStatusFile(404, [root], 'component') !== null ||
|
|
63
|
+
resolveStatusFile(404, [root], 'json') !== null
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The framework default page, as a `404.html` payload. */
|
|
68
|
+
function frameworkDefault(warning: string): NotFoundResult {
|
|
69
|
+
return {
|
|
70
|
+
body: new TextEncoder().encode(defaultStatusPageHtml(404)),
|
|
71
|
+
headers: { 'content-type': DEFAULT_STATUS_PAGE_CONTENT_TYPE },
|
|
72
|
+
contentType: DEFAULT_STATUS_PAGE_CONTENT_TYPE,
|
|
73
|
+
warning,
|
|
74
|
+
// Say which page was written — "rendered" would claim the app produced it.
|
|
75
|
+
logLine: '[timber] Static generation: wrote the framework default 404.html',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Decide what to write as `404.html`, and what to tell the developer.
|
|
81
|
+
*
|
|
82
|
+
* @param probe What requesting {@link NOT_FOUND_PROBE_PATH} produced.
|
|
83
|
+
* @param root Root segment of the route manifest — the only thing that can
|
|
84
|
+
* distinguish an absent not-found page from a broken one.
|
|
85
|
+
*/
|
|
86
|
+
export function resolveNotFoundPage(
|
|
87
|
+
probe: NotFoundProbe,
|
|
88
|
+
root: ManifestSegmentNode
|
|
89
|
+
): NotFoundResult {
|
|
90
|
+
if (probe.kind === 'threw') {
|
|
91
|
+
const message = probe.error instanceof Error ? probe.error.message : String(probe.error);
|
|
92
|
+
return frameworkDefault(
|
|
93
|
+
`the not-found page failed to render — writing the framework default 404.html. ${message}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (probe.status !== 404) {
|
|
98
|
+
return frameworkDefault(
|
|
99
|
+
`the not-found probe ${NOT_FOUND_PROBE_PATH} answered ${probe.status}, not 404 — ` +
|
|
100
|
+
`writing the framework default 404.html instead. A catch-all route or middleware is ` +
|
|
101
|
+
`claiming unmatched URLs.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The pipeline produced nothing, or produced the framework default itself.
|
|
106
|
+
// Compared by identity against the same builder rather than by a proxy like
|
|
107
|
+
// "the body is empty", which the default page invalidated.
|
|
108
|
+
const isDefault =
|
|
109
|
+
probe.body.byteLength === 0 ||
|
|
110
|
+
new TextDecoder().decode(probe.body) === defaultStatusPageHtml(404);
|
|
111
|
+
|
|
112
|
+
if (isDefault) {
|
|
113
|
+
// Which of the two it is cannot be read off the response: the deny chain
|
|
114
|
+
// answers "no status file" and "the status file threw" with the same
|
|
115
|
+
// bytes. Telling a developer whose 404.tsx just crashed to "add
|
|
116
|
+
// app/404.tsx" sends them to a file that already exists and buries the
|
|
117
|
+
// real failure, which the chain logged on its way past.
|
|
118
|
+
return frameworkDefault(
|
|
119
|
+
rootDeclaresNotFoundPage(root)
|
|
120
|
+
? `the app's not-found page did not render — writing the framework default 404.html. ` +
|
|
121
|
+
`A root status file exists but produced no page; see the render error logged above.`
|
|
122
|
+
: `the app has no not-found page — writing the framework default 404.html. ` +
|
|
123
|
+
`Add app/404.tsx (or 4xx.tsx / error.tsx) to control it.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
body: probe.body,
|
|
129
|
+
headers: probe.headers,
|
|
130
|
+
contentType: probe.contentType,
|
|
131
|
+
warning: null,
|
|
132
|
+
logLine: '[timber] Static generation: rendered 404.html',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -105,16 +105,24 @@ function lookupInGroup<TFile>(
|
|
|
105
105
|
* @param status - The HTTP status code (4xx or 5xx).
|
|
106
106
|
* @param segments - The matched segment chain from root (index 0) to leaf (last).
|
|
107
107
|
* @param format - The response format family ('component' or 'json'). Defaults to 'component'.
|
|
108
|
+
* @param maxIndex - Optional upper bound on the segment index to search.
|
|
109
|
+
* When set, the resolver only considers segments `0..maxIndex` (inclusive),
|
|
110
|
+
* skipping any segments deeper in the tree. Used by the deny renderers to
|
|
111
|
+
* prevent resolving a status file below a denied segment — matching
|
|
112
|
+
* `buildDenyPageChain`'s start-at-i semantics. The error renderers omit
|
|
113
|
+
* this parameter, so the full chain is searched — a 500 from a leaf page
|
|
114
|
+
* should use the leaf's own `5xx.tsx`. See TIM-1358.
|
|
108
115
|
*/
|
|
109
116
|
export function resolveStatusFile<TFile>(
|
|
110
117
|
status: number,
|
|
111
118
|
segments: ReadonlyArray<SegmentNode<TFile>>,
|
|
112
|
-
format: StatusFileFormat = 'component'
|
|
119
|
+
format: StatusFileFormat = 'component',
|
|
120
|
+
maxIndex?: number
|
|
113
121
|
): StatusFileResolution<TFile> | null {
|
|
114
122
|
if (status < 400 || status > 599) return null;
|
|
115
|
-
if (format === 'json') return resolveJson(status, segments);
|
|
116
|
-
if (status <= 499) return resolve4xx(status, segments);
|
|
117
|
-
return resolve5xx(status, segments);
|
|
123
|
+
if (format === 'json') return resolveJson(status, segments, maxIndex);
|
|
124
|
+
if (status <= 499) return resolve4xx(status, segments, maxIndex);
|
|
125
|
+
return resolve5xx(status, segments, maxIndex);
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
/**
|
|
@@ -131,16 +139,19 @@ export function resolveStatusFile<TFile>(
|
|
|
131
139
|
*/
|
|
132
140
|
function resolve4xx<TFile>(
|
|
133
141
|
status: number,
|
|
134
|
-
segments: ReadonlyArray<SegmentNode<TFile
|
|
142
|
+
segments: ReadonlyArray<SegmentNode<TFile>>,
|
|
143
|
+
maxIndex?: number
|
|
135
144
|
): StatusFileResolution<TFile> | null {
|
|
136
145
|
const statusStr = String(status);
|
|
146
|
+
const start =
|
|
147
|
+
maxIndex !== undefined ? Math.min(segments.length - 1, maxIndex) : segments.length - 1;
|
|
137
148
|
|
|
138
|
-
for (let i =
|
|
149
|
+
for (let i = start; i >= 0; i--) {
|
|
139
150
|
const r = lookupInGroup(segments[i].statusFiles, statusStr, '4xx', i, status);
|
|
140
151
|
if (r) return r;
|
|
141
152
|
}
|
|
142
153
|
|
|
143
|
-
for (let i =
|
|
154
|
+
for (let i = start; i >= 0; i--) {
|
|
144
155
|
const errorFile = segments[i].error;
|
|
145
156
|
if (errorFile) {
|
|
146
157
|
return { file: errorFile, status, kind: 'error', segmentIndex: i };
|
|
@@ -159,11 +170,14 @@ function resolve4xx<TFile>(
|
|
|
159
170
|
*/
|
|
160
171
|
function resolve5xx<TFile>(
|
|
161
172
|
status: number,
|
|
162
|
-
segments: ReadonlyArray<SegmentNode<TFile
|
|
173
|
+
segments: ReadonlyArray<SegmentNode<TFile>>,
|
|
174
|
+
maxIndex?: number
|
|
163
175
|
): StatusFileResolution<TFile> | null {
|
|
164
176
|
const statusStr = String(status);
|
|
177
|
+
const start =
|
|
178
|
+
maxIndex !== undefined ? Math.min(segments.length - 1, maxIndex) : segments.length - 1;
|
|
165
179
|
|
|
166
|
-
for (let i =
|
|
180
|
+
for (let i = start; i >= 0; i--) {
|
|
167
181
|
const segment = segments[i];
|
|
168
182
|
const r = lookupInGroup(segment.statusFiles, statusStr, '5xx', i, status);
|
|
169
183
|
if (r) return r;
|
|
@@ -184,12 +198,15 @@ function resolve5xx<TFile>(
|
|
|
184
198
|
*/
|
|
185
199
|
function resolveJson<TFile>(
|
|
186
200
|
status: number,
|
|
187
|
-
segments: ReadonlyArray<SegmentNode<TFile
|
|
201
|
+
segments: ReadonlyArray<SegmentNode<TFile>>,
|
|
202
|
+
maxIndex?: number
|
|
188
203
|
): StatusFileResolution<TFile> | null {
|
|
189
204
|
const statusStr = String(status);
|
|
190
205
|
const categoryKey = status >= 500 ? '5xx' : '4xx';
|
|
206
|
+
const start =
|
|
207
|
+
maxIndex !== undefined ? Math.min(segments.length - 1, maxIndex) : segments.length - 1;
|
|
191
208
|
|
|
192
|
-
for (let i =
|
|
209
|
+
for (let i = start; i >= 0; i--) {
|
|
193
210
|
const r = lookupInGroup(segments[i].jsonStatusFiles, statusStr, categoryKey, i, status);
|
|
194
211
|
if (r) return r;
|
|
195
212
|
}
|