@timber-js/app 0.2.0-alpha.6 → 0.2.0-alpha.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
- package/dist/_chunks/als-registry-Ba7URUIn.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-CT98cU9c.js +121 -0
- package/dist/_chunks/define-CT98cU9c.js.map +1 -0
- package/dist/_chunks/define-TK8C1M3x.js +279 -0
- package/dist/_chunks/define-TK8C1M3x.js.map +1 -0
- package/dist/_chunks/define-cookie-BWr_52kY.js +93 -0
- package/dist/_chunks/define-cookie-BWr_52kY.js.map +1 -0
- package/dist/_chunks/error-boundary-DpZJBCqh.js +211 -0
- package/dist/_chunks/error-boundary-DpZJBCqh.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → format-cX7wzEp2.js} +2 -2
- package/dist/_chunks/{format-DviM89f0.js.map → format-cX7wzEp2.js.map} +1 -1
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-Cey5DCGr.js} +129 -77
- package/dist/_chunks/interception-Cey5DCGr.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-rju2rbga.js} +97 -69
- package/dist/_chunks/request-context-rju2rbga.js.map +1 -0
- package/dist/_chunks/segment-context-CyaM1mrD.js +72 -0
- package/dist/_chunks/segment-context-CyaM1mrD.js.map +1 -0
- package/dist/_chunks/stale-reload-BSSym1MJ.js +64 -0
- package/dist/_chunks/stale-reload-BSSym1MJ.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → tracing-VYETCQsg.js} +17 -3
- package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-VYETCQsg.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-wEXY2JQB.js} +1 -1
- package/dist/_chunks/use-query-states-wEXY2JQB.js.map +1 -0
- package/dist/_chunks/wrappers-BaG1bnM3.js +63 -0
- package/dist/_chunks/wrappers-BaG1bnM3.js.map +1 -0
- 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/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/index.d.ts +5 -2
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +90 -20
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/register-cached-function.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +10 -1
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +433 -252
- package/dist/client/index.js.map +1 -1
- package/dist/client/link-pending-store.d.ts +78 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +23 -9
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +2 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/router.d.ts +25 -3
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +36 -2
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +1 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +1 -1
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +3 -3
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/codec.d.ts +21 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/cookies/define-cookie.d.ts +34 -13
- 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 +127 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +665 -242
- package/dist/index.js.map +1 -1
- package/dist/params/define.d.ts +100 -0
- package/dist/params/define.d.ts.map +1 -0
- package/dist/params/index.d.ts +8 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +4 -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 +9 -2
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- 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/search-params/codecs.d.ts +1 -1
- package/dist/search-params/define.d.ts +159 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -5
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +1 -1
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +25 -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-renderer.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 +4 -0
- 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 +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1977 -1648
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.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.d.ts +20 -6
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +65 -38
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +7 -0
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +10 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +20 -13
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +17 -17
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/index.ts +5 -2
- package/src/cache/register-cached-function.ts +7 -3
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +40 -29
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +151 -99
- package/src/client/error-boundary.tsx +18 -1
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +2 -2
- package/src/client/index.ts +10 -1
- package/src/client/link-pending-store.ts +136 -0
- package/src/client/link.tsx +137 -22
- package/src/client/navigation-context.ts +6 -5
- package/src/client/router.ts +117 -60
- package/src/client/rsc-fetch.ts +90 -2
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/stale-reload.ts +73 -6
- package/src/client/top-loader.tsx +10 -9
- package/src/client/transition-root.tsx +20 -2
- package/src/client/use-params.ts +4 -4
- package/src/client/use-query-states.ts +2 -2
- package/src/codec.ts +21 -0
- package/src/cookies/define-cookie.ts +71 -20
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +297 -85
- package/src/params/define.ts +327 -0
- package/src/params/index.ts +28 -0
- package/src/plugins/adapter-build.ts +8 -2
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/cache-transform.ts +1 -1
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +284 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +41 -7
- package/src/plugins/entries.ts +6 -8
- package/src/plugins/fonts.ts +102 -55
- 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 +69 -31
- package/src/plugins/static-build.ts +10 -6
- package/src/routing/codegen.ts +109 -88
- package/src/routing/scanner.ts +86 -7
- 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 +1 -1
- package/src/search-params/define.ts +518 -0
- package/src/search-params/index.ts +12 -18
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/server/access-gate.tsx +40 -9
- package/src/server/action-client.ts +8 -2
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +20 -3
- package/src/server/actions.ts +1 -1
- package/src/server/als-registry.ts +25 -4
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-renderer.ts +5 -3
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +58 -15
- package/src/server/fallback-error.ts +29 -14
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +277 -117
- package/src/server/index.ts +9 -4
- package/src/server/logger.ts +44 -36
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/pipeline-interception.ts +1 -1
- package/src/server/pipeline.ts +148 -41
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +125 -119
- package/src/server/route-element-builder.ts +107 -115
- package/src/server/route-handler.ts +2 -1
- package/src/server/route-matcher.ts +9 -2
- package/src/server/rsc-entry/api-handler.ts +8 -8
- package/src/server/rsc-entry/error-renderer.ts +286 -81
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +177 -76
- package/src/server/rsc-entry/rsc-payload.ts +91 -18
- package/src/server/rsc-entry/rsc-stream.ts +74 -18
- package/src/server/rsc-entry/ssr-bridge.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +152 -34
- package/src/server/slot-resolver.ts +231 -220
- 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 +23 -0
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +1 -3
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +2 -1
- 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/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/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/client/error-boundary.js.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/cookies/index.js.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/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/client/link-status-provider.tsx +0 -30
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -277
|
@@ -16,16 +16,129 @@
|
|
|
16
16
|
* See design/02-rendering-pipeline.md §"Parallel Slots"
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { TimberErrorBoundary } from '
|
|
20
|
-
import SlotErrorFallback from '
|
|
19
|
+
import { TimberErrorBoundary } from '../client/error-boundary.js';
|
|
20
|
+
import SlotErrorFallback from '../client/slot-error-fallback.js';
|
|
21
21
|
import { SlotAccessGate } from './access-gate.js';
|
|
22
22
|
import { wrapSegmentWithErrorBoundaries } from './error-boundary-wrapper.js';
|
|
23
23
|
import type { InterceptionContext, RouteMatch } from './pipeline.js';
|
|
24
|
-
import { DenySignal } from './primitives.js';
|
|
24
|
+
import { DenySignal, RedirectSignal } from './primitives.js';
|
|
25
|
+
import { logRenderError } from './logger.js';
|
|
25
26
|
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
26
27
|
|
|
27
28
|
type CreateElementFn = (...args: unknown[]) => React.ReactElement;
|
|
28
29
|
|
|
30
|
+
// ─── Module Loading Helpers ─────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load a module and extract its default export as a component function.
|
|
34
|
+
* Returns undefined if no default export exists.
|
|
35
|
+
*/
|
|
36
|
+
async function loadComponent(loader: {
|
|
37
|
+
load: () => Promise<unknown>;
|
|
38
|
+
}): Promise<((...args: unknown[]) => unknown) | undefined> {
|
|
39
|
+
const mod = (await loader.load()) as Record<string, unknown>;
|
|
40
|
+
return mod.default as ((...args: unknown[]) => unknown) | undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load and render the default.tsx fallback for a slot node.
|
|
45
|
+
* Returns null if the slot has no default.tsx or it has no default export.
|
|
46
|
+
*/
|
|
47
|
+
async function renderDefaultFallback(
|
|
48
|
+
slotNode: ManifestSegmentNode,
|
|
49
|
+
h: CreateElementFn
|
|
50
|
+
): Promise<React.ReactElement | null> {
|
|
51
|
+
if (!slotNode.default) return null;
|
|
52
|
+
const DefaultComp = await loadComponent(slotNode.default);
|
|
53
|
+
if (!DefaultComp) return null;
|
|
54
|
+
return h(DefaultComp, {});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─── Segment Tree Matching ──────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find a matching child node for a URL segment name.
|
|
61
|
+
*
|
|
62
|
+
* Tries matches in priority order:
|
|
63
|
+
* 1. Static segment (exact name match)
|
|
64
|
+
* 2. Dynamic segment ([param])
|
|
65
|
+
* 3. Catch-all or optional-catch-all ([...param] / [[...param]])
|
|
66
|
+
* 4. Group children (transparent wrappers)
|
|
67
|
+
*
|
|
68
|
+
* Returns `{ node, consumesRest }` where `consumesRest` is true for catch-all
|
|
69
|
+
* segments that consume all remaining URL parts.
|
|
70
|
+
*/
|
|
71
|
+
function findMatchingChild(
|
|
72
|
+
children: ManifestSegmentNode[],
|
|
73
|
+
segmentName: string
|
|
74
|
+
): { node: ManifestSegmentNode; consumesRest: boolean } | null {
|
|
75
|
+
// 1. Static match
|
|
76
|
+
for (const child of children) {
|
|
77
|
+
if (child.segmentType === 'static' && child.segmentName === segmentName) {
|
|
78
|
+
return { node: child, consumesRest: false };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 2. Dynamic match
|
|
83
|
+
for (const child of children) {
|
|
84
|
+
if (child.segmentType === 'dynamic') {
|
|
85
|
+
return { node: child, consumesRest: false };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 3. Catch-all match — consumes all remaining segments
|
|
90
|
+
for (const child of children) {
|
|
91
|
+
if (child.segmentType === 'catch-all' || child.segmentType === 'optional-catch-all') {
|
|
92
|
+
return { node: child, consumesRest: true };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 4. Group children (transparent)
|
|
97
|
+
for (const child of children) {
|
|
98
|
+
if (child.segmentType === 'group') {
|
|
99
|
+
for (const groupChild of child.children ?? []) {
|
|
100
|
+
if (groupChild.segmentName === segmentName) {
|
|
101
|
+
return { node: groupChild, consumesRest: false };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Walk a segment tree from `startNode`, matching each part in `parts` against
|
|
112
|
+
* child nodes. Returns the chain of matched nodes (including startNode) and the
|
|
113
|
+
* final node, or null if any part fails to match.
|
|
114
|
+
*/
|
|
115
|
+
function walkSegmentTree(
|
|
116
|
+
startNode: ManifestSegmentNode,
|
|
117
|
+
parts: { segmentName: string }[] | string[],
|
|
118
|
+
initialChain: ManifestSegmentNode[] = [startNode]
|
|
119
|
+
): { chain: ManifestSegmentNode[]; leaf: ManifestSegmentNode } | null {
|
|
120
|
+
const chain = [...initialChain];
|
|
121
|
+
let currentNode = startNode;
|
|
122
|
+
|
|
123
|
+
for (const part of parts) {
|
|
124
|
+
const segName = typeof part === 'string' ? part : part.segmentName;
|
|
125
|
+
const directChildren = currentNode.children ?? [];
|
|
126
|
+
const match = findMatchingChild(directChildren, segName);
|
|
127
|
+
|
|
128
|
+
if (!match) return null;
|
|
129
|
+
|
|
130
|
+
chain.push(match.node);
|
|
131
|
+
currentNode = match.node;
|
|
132
|
+
|
|
133
|
+
// Catch-all segments consume all remaining parts
|
|
134
|
+
if (match.consumesRest) break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { chain, leaf: currentNode };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ─── Slot Element Resolution ────────────────────────────────────────────────
|
|
141
|
+
|
|
29
142
|
/**
|
|
30
143
|
* Resolve the element for a parallel slot.
|
|
31
144
|
*
|
|
@@ -40,7 +153,6 @@ type CreateElementFn = (...args: unknown[]) => React.ReactElement;
|
|
|
40
153
|
export async function resolveSlotElement(
|
|
41
154
|
slotNode: ManifestSegmentNode,
|
|
42
155
|
match: RouteMatch,
|
|
43
|
-
paramsPromise: Promise<Record<string, string | string[]>>,
|
|
44
156
|
h: CreateElementFn,
|
|
45
157
|
interception?: InterceptionContext
|
|
46
158
|
): Promise<React.ReactElement | null> {
|
|
@@ -54,125 +166,67 @@ export async function resolveSlotElement(
|
|
|
54
166
|
: findSlotMatch(slotNode, match);
|
|
55
167
|
|
|
56
168
|
if (slotMatch) {
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
const SlotPage = mod.default as (...args: unknown[]) => unknown;
|
|
60
|
-
|
|
169
|
+
const SlotPage = await loadComponent(slotMatch.page);
|
|
170
|
+
if (SlotPage) {
|
|
61
171
|
// Load default.tsx fallback for notFound() handling in the slot page.
|
|
62
172
|
// When a slot page calls notFound() or deny(), it should gracefully
|
|
63
173
|
// degrade to default.tsx or null — not crash the page. This matches
|
|
64
174
|
// Next.js behavior. See design/02-rendering-pipeline.md
|
|
65
175
|
// §"Slot Access Failure = Graceful Degradation"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
176
|
+
const denyFallback = await renderDefaultFallback(slotNode, h);
|
|
177
|
+
|
|
178
|
+
// Wrap the slot page to catch ALL errors at the component level.
|
|
179
|
+
// This prevents errors from leaving unresolved Flight rows in the
|
|
180
|
+
// RSC stream — when a slot component throws and the error propagates
|
|
181
|
+
// to React's Flight renderer, it may not emit a resolution row for
|
|
182
|
+
// the slot's lazy reference. The client's createFromReadableStream
|
|
183
|
+
// then throws "Connection closed" when the stream ends with pending
|
|
184
|
+
// references. By catching all errors here and returning a fallback,
|
|
185
|
+
// React sees a resolved component and emits a proper Flight row.
|
|
186
|
+
//
|
|
187
|
+
// DenySignal (from notFound() or deny()) returns the deny fallback.
|
|
188
|
+
// All other errors return the deny fallback or null — the slot
|
|
189
|
+
// gracefully degrades rather than breaking the entire page.
|
|
190
|
+
// See TIM-524.
|
|
81
191
|
const SafeSlotPage = async (props: Record<string, unknown>) => {
|
|
82
192
|
try {
|
|
83
193
|
return await (SlotPage as (props: Record<string, unknown>) => unknown)(props);
|
|
84
194
|
} catch (error) {
|
|
195
|
+
// RedirectSignal must propagate — the pipeline handles redirects
|
|
196
|
+
// at the top level. Swallowing it here would silently return
|
|
197
|
+
// fallback content instead of redirecting. See TIM-554.
|
|
198
|
+
if (error instanceof RedirectSignal) {
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
85
201
|
if (error instanceof DenySignal) {
|
|
86
|
-
return
|
|
202
|
+
return denyFallback;
|
|
87
203
|
}
|
|
88
|
-
throw
|
|
204
|
+
// Log the error but don't re-throw — returning fallback ensures
|
|
205
|
+
// the Flight row is resolved and the page hydrates correctly.
|
|
206
|
+
logRenderError({
|
|
207
|
+
method: '',
|
|
208
|
+
path: '',
|
|
209
|
+
error,
|
|
210
|
+
});
|
|
211
|
+
return denyFallback;
|
|
89
212
|
}
|
|
90
213
|
};
|
|
91
214
|
|
|
92
|
-
let element: React.ReactElement = h(SafeSlotPage, {
|
|
93
|
-
params: paramsPromise,
|
|
94
|
-
searchParams: {},
|
|
95
|
-
});
|
|
215
|
+
let element: React.ReactElement = h(SafeSlotPage, {});
|
|
96
216
|
|
|
97
217
|
// Wrap with error boundaries and layouts from intermediate slot segments
|
|
98
218
|
// (everything between slot root and leaf). Process innermost-first, same
|
|
99
219
|
// order as route-element-builder.ts handles main segments. The slot root
|
|
100
220
|
// (index 0) is handled separately after the access gate below.
|
|
101
|
-
|
|
102
|
-
const seg = slotMatch.chain[i];
|
|
103
|
-
|
|
104
|
-
// Error boundaries from this segment
|
|
105
|
-
element = await wrapSegmentWithErrorBoundaries(seg, element, h);
|
|
106
|
-
|
|
107
|
-
// Layout from this segment
|
|
108
|
-
if (seg.layout) {
|
|
109
|
-
const layoutMod = (await seg.layout.load()) as Record<string, unknown>;
|
|
110
|
-
if (layoutMod.default) {
|
|
111
|
-
const Layout = layoutMod.default as (...args: unknown[]) => unknown;
|
|
112
|
-
element = h(Layout, {
|
|
113
|
-
params: paramsPromise,
|
|
114
|
-
searchParams: {},
|
|
115
|
-
children: element,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
221
|
+
element = await wrapWithIntermediateSegments(slotMatch.chain, element, h);
|
|
120
222
|
|
|
121
223
|
// Wrap in SlotAccessGate if slot root has access.ts.
|
|
122
224
|
// On denial: denied.tsx → default.tsx → null (graceful degradation).
|
|
123
225
|
// See design/04-authorization.md §"Slot-Level Auth".
|
|
124
|
-
|
|
125
|
-
const accessMod = (await slotNode.access.load()) as Record<string, unknown>;
|
|
126
|
-
const accessFn = accessMod.default as
|
|
127
|
-
| ((ctx: { params: Record<string, string | string[]>; searchParams: unknown }) => unknown)
|
|
128
|
-
| undefined;
|
|
129
|
-
if (accessFn) {
|
|
130
|
-
// Load denied.tsx fallback
|
|
131
|
-
let deniedFallback: React.ReactElement | null = null;
|
|
132
|
-
if (slotNode.denied) {
|
|
133
|
-
const deniedMod = (await slotNode.denied.load()) as Record<string, unknown>;
|
|
134
|
-
const DeniedComponent = deniedMod.default as
|
|
135
|
-
| ((...args: unknown[]) => unknown)
|
|
136
|
-
| undefined;
|
|
137
|
-
if (DeniedComponent) {
|
|
138
|
-
deniedFallback = h(DeniedComponent, {});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Load default.tsx fallback
|
|
143
|
-
let defaultFallback: React.ReactElement | null = null;
|
|
144
|
-
if (slotNode.default) {
|
|
145
|
-
const defaultMod = (await slotNode.default.load()) as Record<string, unknown>;
|
|
146
|
-
const DefaultComp = defaultMod.default as ((...args: unknown[]) => unknown) | undefined;
|
|
147
|
-
if (DefaultComp) {
|
|
148
|
-
defaultFallback = h(DefaultComp, { params: paramsPromise, searchParams: {} });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const params = await paramsPromise;
|
|
153
|
-
element = h(SlotAccessGate, {
|
|
154
|
-
accessFn,
|
|
155
|
-
params,
|
|
156
|
-
searchParams: {},
|
|
157
|
-
deniedFallback,
|
|
158
|
-
defaultFallback,
|
|
159
|
-
children: element,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
226
|
+
element = await wrapWithAccessGate(slotNode, element, h);
|
|
163
227
|
|
|
164
228
|
// Wrap with slot root's layout (outermost, outside access gate)
|
|
165
|
-
|
|
166
|
-
const layoutMod = (await slotNode.layout.load()) as Record<string, unknown>;
|
|
167
|
-
if (layoutMod.default) {
|
|
168
|
-
const Layout = layoutMod.default as (...args: unknown[]) => unknown;
|
|
169
|
-
element = h(Layout, {
|
|
170
|
-
params: paramsPromise,
|
|
171
|
-
searchParams: {},
|
|
172
|
-
children: element,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
229
|
+
element = await wrapWithLayout(slotNode, element, h);
|
|
176
230
|
|
|
177
231
|
// Wrap with slot root's error boundaries (outermost)
|
|
178
232
|
element = await wrapSegmentWithErrorBoundaries(slotNode, element, h);
|
|
@@ -195,18 +249,81 @@ export async function resolveSlotElement(
|
|
|
195
249
|
}
|
|
196
250
|
|
|
197
251
|
// No matching page — render default.tsx fallback
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
252
|
+
return renderDefaultFallback(slotNode, h);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ─── Element Wrapping Helpers ───────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Wrap an element with error boundaries and layouts from intermediate
|
|
259
|
+
* slot segments (indices 1..n, skipping the slot root at index 0).
|
|
260
|
+
* Processes innermost-first to match route-element-builder.ts ordering.
|
|
261
|
+
*/
|
|
262
|
+
async function wrapWithIntermediateSegments(
|
|
263
|
+
chain: ManifestSegmentNode[],
|
|
264
|
+
element: React.ReactElement,
|
|
265
|
+
h: CreateElementFn
|
|
266
|
+
): Promise<React.ReactElement> {
|
|
267
|
+
for (let i = chain.length - 1; i > 0; i--) {
|
|
268
|
+
const seg = chain[i];
|
|
269
|
+
element = await wrapSegmentWithErrorBoundaries(seg, element, h);
|
|
270
|
+
element = await wrapWithLayout(seg, element, h);
|
|
204
271
|
}
|
|
272
|
+
return element;
|
|
273
|
+
}
|
|
205
274
|
|
|
206
|
-
|
|
207
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Wrap an element with a segment's layout component, if present.
|
|
277
|
+
*/
|
|
278
|
+
async function wrapWithLayout(
|
|
279
|
+
node: ManifestSegmentNode,
|
|
280
|
+
element: React.ReactElement,
|
|
281
|
+
h: CreateElementFn
|
|
282
|
+
): Promise<React.ReactElement> {
|
|
283
|
+
if (!node.layout) return element;
|
|
284
|
+
const Layout = await loadComponent(node.layout);
|
|
285
|
+
if (!Layout) return element;
|
|
286
|
+
return h(Layout, { children: element });
|
|
208
287
|
}
|
|
209
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Wrap an element with a SlotAccessGate if the node has access.ts.
|
|
291
|
+
* On denial: denied.tsx → default.tsx → null (graceful degradation).
|
|
292
|
+
*/
|
|
293
|
+
async function wrapWithAccessGate(
|
|
294
|
+
slotNode: ManifestSegmentNode,
|
|
295
|
+
element: React.ReactElement,
|
|
296
|
+
h: CreateElementFn
|
|
297
|
+
): Promise<React.ReactElement> {
|
|
298
|
+
if (!slotNode.access) return element;
|
|
299
|
+
|
|
300
|
+
const accessFn = await loadComponent(slotNode.access);
|
|
301
|
+
if (!accessFn) return element;
|
|
302
|
+
|
|
303
|
+
// Pass the component (not pre-built element) so SlotAccessGate can
|
|
304
|
+
// forward DenySignal.data as dangerouslyPassData dynamically. See TIM-488.
|
|
305
|
+
let DeniedComponent: ((...args: unknown[]) => unknown) | null = null;
|
|
306
|
+
if (slotNode.denied) {
|
|
307
|
+
DeniedComponent = (await loadComponent(slotNode.denied)) ?? null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Extract slot name from the directory name (strip @ prefix)
|
|
311
|
+
const slotName = slotNode.segmentName?.replace(/^@/, '') ?? '';
|
|
312
|
+
|
|
313
|
+
const defaultFallback = await renderDefaultFallback(slotNode, h);
|
|
314
|
+
|
|
315
|
+
return h(SlotAccessGate, {
|
|
316
|
+
accessFn,
|
|
317
|
+
DeniedComponent,
|
|
318
|
+
slotName,
|
|
319
|
+
createElement: h,
|
|
320
|
+
defaultFallback,
|
|
321
|
+
children: element,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ─── Slot Matching ──────────────────────────────────────────────────────────
|
|
326
|
+
|
|
210
327
|
/** Result of matching a slot's sub-tree against the current route. */
|
|
211
328
|
interface SlotMatchResult {
|
|
212
329
|
/** The page file at the matched leaf. */
|
|
@@ -267,74 +384,10 @@ function findSlotMatch(slotNode: ManifestSegmentNode, match: RouteMatch): SlotMa
|
|
|
267
384
|
return null;
|
|
268
385
|
}
|
|
269
386
|
|
|
270
|
-
// Walk the slot's children to match remaining URL segments
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
for (const seg of remainingSegments) {
|
|
275
|
-
const childName = seg.segmentName;
|
|
276
|
-
const directChildren = currentNode.children ?? [];
|
|
277
|
-
|
|
278
|
-
let found: ManifestSegmentNode | null = null;
|
|
279
|
-
for (const child of directChildren) {
|
|
280
|
-
// Exact static match
|
|
281
|
-
if (child.segmentType === 'static' && child.segmentName === childName) {
|
|
282
|
-
found = child;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Try dynamic segments if no static match
|
|
288
|
-
if (!found) {
|
|
289
|
-
for (const child of directChildren) {
|
|
290
|
-
if (child.segmentType === 'dynamic') {
|
|
291
|
-
found = child;
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Try catch-all segments — these consume ALL remaining URL segments,
|
|
298
|
-
// so we break out of the outer loop immediately.
|
|
299
|
-
if (!found) {
|
|
300
|
-
for (const child of directChildren) {
|
|
301
|
-
if (child.segmentType === 'catch-all' || child.segmentType === 'optional-catch-all') {
|
|
302
|
-
found = child;
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (found) {
|
|
307
|
-
chain.push(found);
|
|
308
|
-
currentNode = found;
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Try group children (transparent)
|
|
314
|
-
if (!found) {
|
|
315
|
-
for (const child of directChildren) {
|
|
316
|
-
if (child.segmentType === 'group') {
|
|
317
|
-
for (const groupChild of child.children ?? []) {
|
|
318
|
-
if (groupChild.segmentName === childName) {
|
|
319
|
-
found = groupChild;
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (found) break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (!found) {
|
|
329
|
-
// No matching child in slot tree — slot doesn't match this URL
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
chain.push(found);
|
|
333
|
-
currentNode = found;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (currentNode.page) {
|
|
337
|
-
return { page: currentNode.page, chain };
|
|
387
|
+
// Walk the slot's children to match remaining URL segments
|
|
388
|
+
const result = walkSegmentTree(slotNode, remainingSegments);
|
|
389
|
+
if (result && result.leaf.page) {
|
|
390
|
+
return { page: result.leaf.page, chain: result.chain };
|
|
338
391
|
}
|
|
339
392
|
return null;
|
|
340
393
|
}
|
|
@@ -374,59 +427,17 @@ function findInterceptingMatch(
|
|
|
374
427
|
|
|
375
428
|
// Walk the intercepting child's sub-tree to match remaining target parts
|
|
376
429
|
const remaining = targetParts.slice(matchIdx + 1);
|
|
377
|
-
const chain: ManifestSegmentNode[] = [slotNode, child];
|
|
378
430
|
|
|
379
431
|
if (remaining.length === 0) {
|
|
380
432
|
if (child.page) {
|
|
381
|
-
return { page: child.page, chain };
|
|
433
|
+
return { page: child.page, chain: [slotNode, child] };
|
|
382
434
|
}
|
|
383
435
|
continue;
|
|
384
436
|
}
|
|
385
437
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const children = currentNode.children ?? [];
|
|
390
|
-
let found: ManifestSegmentNode | null = null;
|
|
391
|
-
|
|
392
|
-
// Static match
|
|
393
|
-
for (const c of children) {
|
|
394
|
-
if (c.segmentType === 'static' && c.segmentName === part) {
|
|
395
|
-
found = c;
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Dynamic match
|
|
401
|
-
if (!found) {
|
|
402
|
-
for (const c of children) {
|
|
403
|
-
if (c.segmentType === 'dynamic') {
|
|
404
|
-
found = c;
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Catch-all match
|
|
411
|
-
if (!found) {
|
|
412
|
-
for (const c of children) {
|
|
413
|
-
if (c.segmentType === 'catch-all' || c.segmentType === 'optional-catch-all') {
|
|
414
|
-
found = c;
|
|
415
|
-
break;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (!found) {
|
|
421
|
-
matched = false;
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
chain.push(found);
|
|
425
|
-
currentNode = found;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (matched && currentNode.page) {
|
|
429
|
-
return { page: currentNode.page, chain };
|
|
438
|
+
const result = walkSegmentTree(child, remaining, [slotNode, child]);
|
|
439
|
+
if (result && result.leaf.page) {
|
|
440
|
+
return { page: result.leaf.page, chain: result.chain };
|
|
430
441
|
}
|
|
431
442
|
}
|
|
432
443
|
|