@timber-js/app 0.2.0-alpha.97 → 0.2.0-alpha.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
- package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
- package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
- package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
- package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
- package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
- package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
- package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
- package/dist/_chunks/logger-0m8MsKdc.js +291 -0
- package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
- package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
- package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
- package/dist/_chunks/registry-I2ss-lvy.js +20 -0
- package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
- package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
- package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
- package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
- package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
- package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
- package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
- package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
- package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
- package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
- package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
- package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
- package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
- package/dist/_chunks/{interception-BbqMCVXa.js → walkers-Tg0Alwcg.js} +66 -87
- package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
- package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
- package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +28 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +8 -28
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +63 -31
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +16 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/cache/index.js +9 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.js +2 -1
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/form.d.ts +10 -24
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +41 -91
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +2 -1
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +81 -7
- package/dist/client/internal.js.map +1 -1
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/state.d.ts +1 -1
- package/dist/client/use-cookie.d.ts +8 -0
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +1 -1
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +2 -2
- package/dist/config-types.d.ts +28 -0
- package/dist/config-types.d.ts.map +1 -1
- package/dist/cookies/define-cookie.d.ts +87 -35
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -1
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +48 -2
- package/dist/cookies/index.js.map +1 -0
- package/dist/cookies/json-cookie.d.ts +64 -0
- package/dist/cookies/json-cookie.d.ts.map +1 -0
- package/dist/cookies/validation.d.ts +46 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +9 -19
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +5 -3
- package/dist/dev-tools/holding-server.d.ts.map +1 -0
- package/dist/dev-tools/index.d.ts +31 -0
- package/dist/dev-tools/index.d.ts.map +1 -0
- package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
- package/dist/dev-tools/instrumentation.d.ts.map +1 -0
- package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -0
- package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
- package/dist/dev-tools/overlay.d.ts.map +1 -0
- package/dist/dev-tools/stack-classifier.d.ts +34 -0
- package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
- package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +285 -133
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +1 -1
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +6 -4
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/index.d.ts +5 -3
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -3
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +37 -8
- package/dist/routing/segment-classify.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +63 -23
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +25 -7
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.js +5 -3
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/wrappers.d.ts +2 -2
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/segment-params/define.d.ts +23 -6
- package/dist/segment-params/define.d.ts.map +1 -1
- package/dist/segment-params/index.js +1 -1
- package/dist/server/access-gate.d.ts +4 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts +15 -6
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +5 -5
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +1 -15
- package/dist/server/asset-headers.d.ts.map +1 -1
- package/dist/server/cookie-context.d.ts +170 -0
- package/dist/server/cookie-context.d.ts.map +1 -0
- package/dist/server/cookie-parsing.d.ts +51 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- package/dist/server/deny-boundary.d.ts +90 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/early-hints-sender.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +212 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +59 -59
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -149
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +6 -4
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +852 -852
- package/dist/server/internal.js.map +1 -1
- package/dist/server/logger.d.ts +14 -0
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +17 -0
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +46 -49
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/param-coercion.d.ts +26 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +95 -0
- package/dist/server/pipeline-helpers.d.ts.map +1 -0
- package/dist/server/pipeline-outcome.d.ts +49 -0
- package/dist/server/pipeline-outcome.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +52 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +51 -32
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +22 -159
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +20 -47
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
- package/dist/server/rsc-entry/helpers.d.ts +1 -1
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +50 -0
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +16 -11
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +1 -1
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +45 -16
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +48 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/escape-html.d.ts +14 -0
- package/dist/server/utils/escape-html.d.ts.map +1 -0
- package/dist/shims/headers.d.ts +2 -2
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +3 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +9 -4
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +0 -45
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare.ts +10 -50
- package/src/adapters/nitro.ts +66 -50
- package/src/adapters/shared.ts +40 -0
- package/src/cache/timber-cache.ts +3 -2
- package/src/client/form.tsx +17 -25
- package/src/client/index.ts +16 -9
- package/src/client/internal.ts +3 -2
- package/src/client/router.ts +1 -1
- package/src/client/rsc-fetch.ts +15 -0
- package/src/client/state.ts +2 -2
- package/src/client/use-cookie.ts +29 -0
- package/src/codec.ts +3 -7
- package/src/config-types.ts +28 -0
- package/src/cookies/define-cookie.ts +271 -78
- package/src/cookies/index.ts +11 -8
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +17 -48
- package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
- package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +4 -2
- package/src/dev-tools/index.ts +90 -0
- package/src/dev-tools/instrumentation.ts +176 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
- package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
- package/src/index.ts +95 -34
- package/src/plugin-context.ts +1 -1
- package/src/plugins/adapter-build.ts +3 -1
- package/src/plugins/build-report.ts +13 -22
- package/src/plugins/dev-server.ts +3 -3
- package/src/plugins/routing.ts +14 -12
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +1 -1
- package/src/routing/codegen.ts +1 -1
- package/src/routing/convention-lint.ts +9 -8
- package/src/routing/index.ts +5 -3
- package/src/routing/interception.ts +1 -1
- package/src/routing/scanner.ts +22 -95
- package/src/routing/segment-classify.ts +107 -8
- package/src/routing/status-file-lint.ts +7 -5
- package/src/routing/types.ts +63 -23
- package/src/routing/walkers.ts +90 -0
- package/src/search-params/define.ts +71 -15
- package/src/search-params/wrappers.ts +9 -2
- package/src/segment-params/define.ts +66 -13
- package/src/server/access-gate.tsx +9 -8
- package/src/server/action-handler.ts +34 -38
- package/src/server/als-registry.ts +5 -5
- package/src/server/asset-headers.ts +8 -34
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
- package/src/server/deny-renderer.ts +7 -12
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/fallback-error.ts +2 -2
- package/src/server/html-injector-core.ts +403 -0
- package/src/server/html-injectors.ts +158 -297
- package/src/server/index.ts +13 -14
- package/src/server/internal.ts +10 -3
- package/src/server/logger.ts +23 -0
- package/src/server/middleware-runner.ts +44 -0
- package/src/server/node-stream-transforms.ts +108 -248
- package/src/server/param-coercion.ts +76 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-outcome.ts +167 -0
- package/src/server/pipeline-phases.ts +409 -0
- package/src/server/pipeline.ts +70 -540
- package/src/server/port-resolution.ts +215 -0
- package/src/server/request-context.ts +46 -451
- package/src/server/route-element-builder.ts +8 -4
- package/src/server/route-matcher.ts +28 -60
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +2 -2
- package/src/server/rsc-entry/helpers.ts +2 -7
- package/src/server/rsc-entry/index.ts +81 -366
- package/src/server/rsc-entry/render-route.ts +304 -0
- package/src/server/rsc-entry/rsc-payload.ts +1 -1
- package/src/server/rsc-entry/ssr-renderer.ts +2 -2
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/sitemap-generator.ts +1 -1
- package/src/server/slot-resolver.ts +1 -1
- package/src/server/ssr-entry.ts +1 -1
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/tracing.ts +3 -3
- package/src/server/tree-builder.ts +134 -56
- package/src/server/types.ts +52 -0
- package/src/server/utils/escape-html.ts +20 -0
- package/src/shims/headers.ts +3 -3
- package/src/shims/navigation-client.ts +4 -3
- package/src/shims/navigation.ts +9 -7
- package/src/utils/directive-parser.ts +0 -392
- package/dist/_chunks/actions-DLnUaR65.js +0 -421
- package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
- package/dist/_chunks/debug-ECi_61pb.js +0 -108
- package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
- package/dist/_chunks/define-C77ScO0m.js.map +0 -1
- package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
- package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
- package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
- package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
- package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
- package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
- package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
- package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
- package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
- package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
- package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
- package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
- package/dist/client/use-params.d.ts.map +0 -1
- package/dist/plugins/dev-404-page.d.ts.map +0 -1
- package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-error-page.d.ts.map +0 -1
- package/dist/plugins/dev-logs.d.ts.map +0 -1
- package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
- package/dist/server/deny-page-resolver.d.ts +0 -52
- package/dist/server/deny-page-resolver.d.ts.map +0 -1
- package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
- package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
- package/dist/server/dev-holding-server.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/dist/server/page-deny-boundary.d.ts +0 -31
- package/dist/server/page-deny-boundary.d.ts.map +0 -1
- package/src/server/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/page-deny-boundary.tsx +0 -56
- /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
- /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
package/src/routing/scanner.ts
CHANGED
|
@@ -18,9 +18,10 @@ import type {
|
|
|
18
18
|
ScannerConfig,
|
|
19
19
|
InterceptionMarker,
|
|
20
20
|
} from './types.js';
|
|
21
|
-
import {
|
|
22
|
-
import { DEFAULT_PAGE_EXTENSIONS
|
|
21
|
+
import { classifySegment } from './segment-classify.js';
|
|
22
|
+
import { DEFAULT_PAGE_EXTENSIONS } from './types.js';
|
|
23
23
|
import { classifyMetadataRoute, isDynamicMetadataExtension } from '../server/metadata-routes.js';
|
|
24
|
+
import { swallow } from '../server/logger.js';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Pattern matching encoded path delimiters that must be rejected during route discovery.
|
|
@@ -127,86 +128,10 @@ function createSegmentNode(
|
|
|
127
128
|
interceptionMarker,
|
|
128
129
|
interceptedSegmentName,
|
|
129
130
|
children: [],
|
|
130
|
-
slots:
|
|
131
|
+
slots: {},
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
/**
|
|
135
|
-
* Classify a directory name into its segment type.
|
|
136
|
-
*/
|
|
137
|
-
export function classifySegment(dirName: string): {
|
|
138
|
-
type: SegmentType;
|
|
139
|
-
paramName?: string;
|
|
140
|
-
interceptionMarker?: InterceptionMarker;
|
|
141
|
-
interceptedSegmentName?: string;
|
|
142
|
-
} {
|
|
143
|
-
// Private folder: _name (excluded from routing)
|
|
144
|
-
if (dirName.startsWith('_')) {
|
|
145
|
-
return { type: 'private' };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Parallel route slot: @name
|
|
149
|
-
if (dirName.startsWith('@')) {
|
|
150
|
-
return { type: 'slot' };
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Intercepting routes: (.)name, (..)name, (...)name, (..)(..)name
|
|
154
|
-
// Check before route groups since intercepting markers also start with (
|
|
155
|
-
const interception = parseInterceptionMarker(dirName);
|
|
156
|
-
if (interception) {
|
|
157
|
-
return {
|
|
158
|
-
type: 'intercepting',
|
|
159
|
-
interceptionMarker: interception.marker,
|
|
160
|
-
interceptedSegmentName: interception.segmentName,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Route group: (name)
|
|
165
|
-
if (dirName.startsWith('(') && dirName.endsWith(')')) {
|
|
166
|
-
return { type: 'group' };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Bracket-syntax segments: [param], [...param], [[...param]]
|
|
170
|
-
// Delegated to the shared character-based classifier. If you change
|
|
171
|
-
// bracket syntax, update segment-classify.ts — not here.
|
|
172
|
-
const urlSeg = classifyUrlSegment(dirName);
|
|
173
|
-
if (urlSeg.kind !== 'static') {
|
|
174
|
-
return { type: urlSeg.kind, paramName: urlSeg.name };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return { type: 'static' };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Parse an interception marker from a directory name.
|
|
182
|
-
*
|
|
183
|
-
* Returns the marker and the remaining segment name, or null if not an
|
|
184
|
-
* intercepting route. Markers are checked longest-first to avoid (..)
|
|
185
|
-
* matching before (..)(..).
|
|
186
|
-
*
|
|
187
|
-
* Examples:
|
|
188
|
-
* "(.)photo" → { marker: "(.)", segmentName: "photo" }
|
|
189
|
-
* "(..)feed" → { marker: "(..)", segmentName: "feed" }
|
|
190
|
-
* "(...)photos" → { marker: "(...)", segmentName: "photos" }
|
|
191
|
-
* "(..)(..)admin" → { marker: "(..)(..)", segmentName: "admin" }
|
|
192
|
-
* "(marketing)" → null (route group, not interception)
|
|
193
|
-
*/
|
|
194
|
-
function parseInterceptionMarker(
|
|
195
|
-
dirName: string
|
|
196
|
-
): { marker: InterceptionMarker; segmentName: string } | null {
|
|
197
|
-
for (const marker of INTERCEPTION_MARKERS) {
|
|
198
|
-
if (dirName.startsWith(marker)) {
|
|
199
|
-
const rest = dirName.slice(marker.length);
|
|
200
|
-
// Must have a segment name after the marker, and the rest must not
|
|
201
|
-
// be empty or end with ) (which would be a route group like "(auth)")
|
|
202
|
-
if (rest.length > 0 && !rest.endsWith(')')) {
|
|
203
|
-
return { marker, segmentName: rest };
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
135
|
/**
|
|
211
136
|
* Compute the URL path for a child segment given its parent's URL path.
|
|
212
137
|
* Route groups, slots, and intercepting routes do NOT add URL depth.
|
|
@@ -228,7 +153,8 @@ function scanSegmentFiles(dirPath: string, node: SegmentNode, extSet: Set<string
|
|
|
228
153
|
let entries: string[];
|
|
229
154
|
try {
|
|
230
155
|
entries = readdirSync(dirPath);
|
|
231
|
-
} catch {
|
|
156
|
+
} catch (err) {
|
|
157
|
+
swallow(err, `scanSegmentFiles: unreadable directory ${dirPath}`, { level: 'warn' });
|
|
232
158
|
return;
|
|
233
159
|
}
|
|
234
160
|
|
|
@@ -292,27 +218,27 @@ function scanSegmentFiles(dirPath: string, node: SegmentNode, extSet: Set<string
|
|
|
292
218
|
// Recognized regardless of pageExtensions — .json is a data format, not a page extension.
|
|
293
219
|
if (STATUS_CODE_PATTERN.test(name) && ext === 'json') {
|
|
294
220
|
if (!node.jsonStatusFiles) {
|
|
295
|
-
node.jsonStatusFiles =
|
|
221
|
+
node.jsonStatusFiles = {};
|
|
296
222
|
}
|
|
297
|
-
node.jsonStatusFiles
|
|
223
|
+
node.jsonStatusFiles[name] = { filePath: fullPath, extension: ext };
|
|
298
224
|
continue;
|
|
299
225
|
}
|
|
300
226
|
|
|
301
227
|
// Status-code files (401.tsx, 4xx.tsx, 503.tsx, 5xx.tsx)
|
|
302
228
|
if (STATUS_CODE_PATTERN.test(name) && extSet.has(ext)) {
|
|
303
229
|
if (!node.statusFiles) {
|
|
304
|
-
node.statusFiles =
|
|
230
|
+
node.statusFiles = {};
|
|
305
231
|
}
|
|
306
|
-
node.statusFiles
|
|
232
|
+
node.statusFiles[name] = { filePath: fullPath, extension: ext };
|
|
307
233
|
continue;
|
|
308
234
|
}
|
|
309
235
|
|
|
310
236
|
// Legacy compat files (not-found.tsx, forbidden.tsx, unauthorized.tsx)
|
|
311
237
|
if (name in LEGACY_STATUS_FILES && extSet.has(ext)) {
|
|
312
238
|
if (!node.legacyStatusFiles) {
|
|
313
|
-
node.legacyStatusFiles =
|
|
239
|
+
node.legacyStatusFiles = {};
|
|
314
240
|
}
|
|
315
|
-
node.legacyStatusFiles
|
|
241
|
+
node.legacyStatusFiles[name] = { filePath: fullPath, extension: ext };
|
|
316
242
|
continue;
|
|
317
243
|
}
|
|
318
244
|
|
|
@@ -323,19 +249,19 @@ function scanSegmentFiles(dirPath: string, node: SegmentNode, extSet: Set<string
|
|
|
323
249
|
const metaInfo = classifyMetadataRoute(entry);
|
|
324
250
|
if (metaInfo) {
|
|
325
251
|
if (!node.metadataRoutes) {
|
|
326
|
-
node.metadataRoutes =
|
|
252
|
+
node.metadataRoutes = {};
|
|
327
253
|
}
|
|
328
|
-
const existing = node.metadataRoutes
|
|
254
|
+
const existing = node.metadataRoutes[name];
|
|
329
255
|
if (existing) {
|
|
330
256
|
// Dynamic > static precedence: only overwrite if the new file is dynamic
|
|
331
257
|
// or the existing file is static (dynamic always wins).
|
|
332
258
|
const existingIsDynamic = isDynamicMetadataExtension(name, existing.extension);
|
|
333
259
|
const newIsDynamic = isDynamicMetadataExtension(name, ext);
|
|
334
260
|
if (newIsDynamic || !existingIsDynamic) {
|
|
335
|
-
node.metadataRoutes
|
|
261
|
+
node.metadataRoutes[name] = { filePath: fullPath, extension: ext };
|
|
336
262
|
}
|
|
337
263
|
} else {
|
|
338
|
-
node.metadataRoutes
|
|
264
|
+
node.metadataRoutes[name] = { filePath: fullPath, extension: ext };
|
|
339
265
|
}
|
|
340
266
|
}
|
|
341
267
|
}
|
|
@@ -358,7 +284,8 @@ function scanChildren(dirPath: string, parentNode: SegmentNode, extSet: Set<stri
|
|
|
358
284
|
let entries: string[];
|
|
359
285
|
try {
|
|
360
286
|
entries = readdirSync(dirPath);
|
|
361
|
-
} catch {
|
|
287
|
+
} catch (err) {
|
|
288
|
+
swallow(err, `scanChildren: unreadable directory ${dirPath}`, { level: 'warn' });
|
|
362
289
|
return;
|
|
363
290
|
}
|
|
364
291
|
|
|
@@ -412,10 +339,10 @@ function scanChildren(dirPath: string, parentNode: SegmentNode, extSet: Set<stri
|
|
|
412
339
|
// Recurse into subdirectories
|
|
413
340
|
scanChildren(fullPath, childNode, extSet);
|
|
414
341
|
|
|
415
|
-
// Attach to parent: slots go into slots
|
|
342
|
+
// Attach to parent: slots go into slots record, everything else is a child
|
|
416
343
|
if (type === 'slot') {
|
|
417
344
|
const slotName = entry.slice(1); // remove @
|
|
418
|
-
parentNode.slots
|
|
345
|
+
parentNode.slots[slotName] = childNode;
|
|
419
346
|
} else {
|
|
420
347
|
parentNode.children.push(childNode);
|
|
421
348
|
}
|
|
@@ -477,7 +404,7 @@ function collectRoutableLeaves(
|
|
|
477
404
|
}
|
|
478
405
|
|
|
479
406
|
// Recurse into slots — each slot is its own parallel route space
|
|
480
|
-
for (const
|
|
407
|
+
for (const slotNode of Object.values(node.slots)) {
|
|
481
408
|
collectRoutableLeaves(slotNode, seen, currentPath, true);
|
|
482
409
|
}
|
|
483
410
|
}
|
|
@@ -525,7 +452,7 @@ function walkForDuplicateParams(node: SegmentNode, seen: Map<string, string>): v
|
|
|
525
452
|
|
|
526
453
|
// Slots are independent parallel routes — start fresh param tracking
|
|
527
454
|
// (a slot's params don't conflict with the main route's params)
|
|
528
|
-
for (const
|
|
455
|
+
for (const slotNode of Object.values(node.slots)) {
|
|
529
456
|
walkForDuplicateParams(slotNode, new Map(seen));
|
|
530
457
|
}
|
|
531
458
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared URL
|
|
2
|
+
* Shared segment classifier — both URL tokens and filesystem directory names.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (e.g. "dashboard", "[id]", "[...slug]",
|
|
6
|
-
* discriminated union.
|
|
7
|
-
* Link interpolation.
|
|
4
|
+
* `classifyUrlSegment(token)` is a pure single-pass character parser that
|
|
5
|
+
* classifies a route segment token (e.g. "dashboard", "[id]", "[...slug]",
|
|
6
|
+
* "[[...path]]") into a typed discriminated union. NO regex, NO Node.js-only
|
|
7
|
+
* APIs — safe to import from browser code (used by `Link` interpolation).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* `classifySegment(dirName)` is the build-time directory-name classifier
|
|
10
|
+
* used by the scanner. It recognizes timber-only conventions (private
|
|
11
|
+
* `_*`, parallel `@*`, route groups `(name)`, intercepting routes
|
|
12
|
+
* `(.)`/`(..)`/`(...)`/`(..)(..)`) and delegates bracket syntax to
|
|
13
|
+
* `classifyUrlSegment`. It is the **single source of truth** for what
|
|
14
|
+
* counts as a routing segment — there is no separate copy in the
|
|
15
|
+
* scanner. (TIM-848.)
|
|
10
16
|
*
|
|
11
|
-
* Malformed input
|
|
12
|
-
* to { kind: 'static' } — the safe default.
|
|
17
|
+
* Malformed input falls through to `{ kind: 'static' }` — the safe default.
|
|
13
18
|
*
|
|
14
19
|
* If you change the bracket syntax, update ONLY this file. Every
|
|
15
20
|
* consumer imports from here.
|
|
@@ -17,6 +22,9 @@
|
|
|
17
22
|
* See design/07-routing.md §"Route Segments"
|
|
18
23
|
*/
|
|
19
24
|
|
|
25
|
+
import type { InterceptionMarker, SegmentType } from './types.js';
|
|
26
|
+
import { INTERCEPTION_MARKERS } from './types.js';
|
|
27
|
+
|
|
20
28
|
export type UrlSegment =
|
|
21
29
|
| { kind: 'static'; value: string }
|
|
22
30
|
| { kind: 'dynamic'; name: string }
|
|
@@ -87,3 +95,94 @@ export function classifyUrlSegment(token: string): UrlSegment {
|
|
|
87
95
|
}
|
|
88
96
|
return { kind: 'dynamic', name };
|
|
89
97
|
}
|
|
98
|
+
|
|
99
|
+
// ─── Directory-name classifier (build-time scanner) ─────────────────────────
|
|
100
|
+
|
|
101
|
+
/** Result of classifying a filesystem directory name. */
|
|
102
|
+
export interface SegmentClassification {
|
|
103
|
+
type: SegmentType;
|
|
104
|
+
paramName?: string;
|
|
105
|
+
interceptionMarker?: InterceptionMarker;
|
|
106
|
+
interceptedSegmentName?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Classify a directory name into its segment type.
|
|
111
|
+
*
|
|
112
|
+
* Recognizes all timber file-system conventions in priority order:
|
|
113
|
+
* 1. Private folders: `_name` (excluded from routing)
|
|
114
|
+
* 2. Parallel route slots: `@name`
|
|
115
|
+
* 3. Intercepting routes: `(.)name`, `(..)name`, `(...)name`, `(..)(..)name`
|
|
116
|
+
* 4. Route groups: `(name)`
|
|
117
|
+
* 5. Bracket syntax: `[id]`, `[...slug]`, `[[...path]]` (delegated to
|
|
118
|
+
* `classifyUrlSegment`)
|
|
119
|
+
* 6. Static: anything else
|
|
120
|
+
*
|
|
121
|
+
* If you change the bracket syntax, update only `classifyUrlSegment`.
|
|
122
|
+
* If you change the directory-prefix conventions, update this function.
|
|
123
|
+
*/
|
|
124
|
+
export function classifySegment(dirName: string): SegmentClassification {
|
|
125
|
+
// Private folder: _name (excluded from routing)
|
|
126
|
+
if (dirName.startsWith('_')) {
|
|
127
|
+
return { type: 'private' };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Parallel route slot: @name
|
|
131
|
+
if (dirName.startsWith('@')) {
|
|
132
|
+
return { type: 'slot' };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Intercepting routes: (.)name, (..)name, (...)name, (..)(..)name
|
|
136
|
+
// Check before route groups since intercepting markers also start with (
|
|
137
|
+
const interception = parseInterceptionMarker(dirName);
|
|
138
|
+
if (interception) {
|
|
139
|
+
return {
|
|
140
|
+
type: 'intercepting',
|
|
141
|
+
interceptionMarker: interception.marker,
|
|
142
|
+
interceptedSegmentName: interception.segmentName,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Route group: (name)
|
|
147
|
+
if (dirName.startsWith('(') && dirName.endsWith(')')) {
|
|
148
|
+
return { type: 'group' };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Bracket-syntax segments: [param], [...param], [[...param]]
|
|
152
|
+
const urlSeg = classifyUrlSegment(dirName);
|
|
153
|
+
if (urlSeg.kind !== 'static') {
|
|
154
|
+
return { type: urlSeg.kind, paramName: urlSeg.name };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { type: 'static' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Parse an interception marker from a directory name.
|
|
162
|
+
*
|
|
163
|
+
* Returns the marker and the remaining segment name, or null if not an
|
|
164
|
+
* intercepting route. Markers are checked longest-first to avoid `(..)`
|
|
165
|
+
* matching before `(..)(..)`.
|
|
166
|
+
*
|
|
167
|
+
* Examples:
|
|
168
|
+
* "(.)photo" → { marker: "(.)", segmentName: "photo" }
|
|
169
|
+
* "(..)feed" → { marker: "(..)", segmentName: "feed" }
|
|
170
|
+
* "(...)photos" → { marker: "(...)", segmentName: "photos" }
|
|
171
|
+
* "(..)(..)admin" → { marker: "(..)(..)", segmentName: "admin" }
|
|
172
|
+
* "(marketing)" → null (route group, not interception)
|
|
173
|
+
*/
|
|
174
|
+
function parseInterceptionMarker(
|
|
175
|
+
dirName: string
|
|
176
|
+
): { marker: InterceptionMarker; segmentName: string } | null {
|
|
177
|
+
for (const marker of INTERCEPTION_MARKERS) {
|
|
178
|
+
if (dirName.startsWith(marker)) {
|
|
179
|
+
const rest = dirName.slice(marker.length);
|
|
180
|
+
// Must have a segment name after the marker, and the rest must not
|
|
181
|
+
// be empty or end with ) (which would be a route group like "(auth)")
|
|
182
|
+
if (rest.length > 0 && !rest.endsWith(')')) {
|
|
183
|
+
return { marker, segmentName: rest };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
17
|
import type { RouteTree, SegmentNode } from './types.js';
|
|
18
18
|
import { detectFileDirective } from '../utils/directive-parser.js';
|
|
19
|
+
import { swallow } from '../server/logger.js';
|
|
19
20
|
|
|
20
21
|
/** Extensions that require 'use client' (component files, not MDX/JSON). */
|
|
21
22
|
const CLIENT_REQUIRED_EXTENSIONS = new Set(['tsx', 'jsx', 'ts', 'js']);
|
|
@@ -48,14 +49,14 @@ function walkNode(node: SegmentNode, warnings: StatusFileLintWarning[]): void {
|
|
|
48
49
|
|
|
49
50
|
// Check status-code files (404.tsx, 4xx.tsx, 5xx.tsx, etc.)
|
|
50
51
|
if (node.statusFiles) {
|
|
51
|
-
for (const [code, file] of node.statusFiles) {
|
|
52
|
+
for (const [code, file] of Object.entries(node.statusFiles)) {
|
|
52
53
|
checkFile(file.filePath, file.extension, code, warnings);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// Check legacy compat files (not-found.tsx, forbidden.tsx, unauthorized.tsx)
|
|
57
58
|
if (node.legacyStatusFiles) {
|
|
58
|
-
for (const [name, file] of node.legacyStatusFiles) {
|
|
59
|
+
for (const [name, file] of Object.entries(node.legacyStatusFiles)) {
|
|
59
60
|
checkFile(file.filePath, file.extension, name, warnings);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -64,7 +65,7 @@ function walkNode(node: SegmentNode, warnings: StatusFileLintWarning[]): void {
|
|
|
64
65
|
for (const child of node.children) {
|
|
65
66
|
walkNode(child, warnings);
|
|
66
67
|
}
|
|
67
|
-
for (const
|
|
68
|
+
for (const slotNode of Object.values(node.slots)) {
|
|
68
69
|
walkNode(slotNode, warnings);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
@@ -80,8 +81,9 @@ function checkFile(
|
|
|
80
81
|
let code: string;
|
|
81
82
|
try {
|
|
82
83
|
code = readFileSync(filePath, 'utf-8');
|
|
83
|
-
} catch {
|
|
84
|
-
|
|
84
|
+
} catch (err) {
|
|
85
|
+
swallow(err, `status-file-lint: unreadable file ${filePath}`);
|
|
86
|
+
return;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
const directive = detectFileDirective(code, ['use client']);
|
package/src/routing/types.ts
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The route tree is built by scanning the app/ directory and recognizing
|
|
5
5
|
* file conventions (page.*, layout.*, middleware.ts, access.ts, route.ts, etc.).
|
|
6
|
+
*
|
|
7
|
+
* **Single shape, two specializations** (TIM-848):
|
|
8
|
+
*
|
|
9
|
+
* `SegmentNode<TFile>` is the one canonical in-memory shape for the
|
|
10
|
+
* timber route tree. The same interface is used at build time (with
|
|
11
|
+
* `TFile = RouteFile`) and at request time (with `TFile = ManifestFile`,
|
|
12
|
+
* see `server/route-matcher.ts`). Walkers parameterized over `TFile`
|
|
13
|
+
* work on either, eliminating the previous duplication between
|
|
14
|
+
* `SegmentNode` (Map-based) and `ManifestSegmentNode` (object-based).
|
|
15
|
+
*
|
|
16
|
+
* Keyed groups (`slots`, `statusFiles`, `jsonStatusFiles`,
|
|
17
|
+
* `legacyStatusFiles`, `metadataRoutes`) are plain `Record<string, …>`
|
|
18
|
+
* objects rather than `Map`s so that the build-time tree can be
|
|
19
|
+
* serialized into the virtual route manifest with no shape transform.
|
|
20
|
+
*
|
|
21
|
+
* See design/07-routing.md §"Route Tree Shape" and design/18-build-system.md
|
|
22
|
+
* §"Route Manifest Shape".
|
|
6
23
|
*/
|
|
7
24
|
|
|
8
25
|
/** Segment type classification */
|
|
@@ -27,7 +44,14 @@ export type InterceptionMarker = '(.)' | '(..)' | '(...)' | '(..)(..)';
|
|
|
27
44
|
/** All recognized interception markers, ordered longest-first for parsing. */
|
|
28
45
|
export const INTERCEPTION_MARKERS: InterceptionMarker[] = ['(..)(..)', '(.)', '(..)', '(...)'];
|
|
29
46
|
|
|
30
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* A single file discovered in a route segment at build time.
|
|
49
|
+
*
|
|
50
|
+
* The runtime equivalent (`ManifestFile`, defined in
|
|
51
|
+
* `server/route-matcher.ts`) replaces `extension` with a lazy `load`
|
|
52
|
+
* function. Walkers that only need `filePath` are parameterized over
|
|
53
|
+
* `TFile` and accept either.
|
|
54
|
+
*/
|
|
31
55
|
export interface RouteFile {
|
|
32
56
|
/** Absolute path to the file */
|
|
33
57
|
filePath: string;
|
|
@@ -35,8 +59,17 @@ export interface RouteFile {
|
|
|
35
59
|
extension: string;
|
|
36
60
|
}
|
|
37
61
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
62
|
+
/**
|
|
63
|
+
* A node in the segment tree.
|
|
64
|
+
*
|
|
65
|
+
* Generic over `TFile` so the same interface describes both the
|
|
66
|
+
* build-time tree (`SegmentNode<RouteFile>`, the default) and the
|
|
67
|
+
* runtime manifest tree (`SegmentNode<ManifestFile>`, aliased as
|
|
68
|
+
* `ManifestSegmentNode`). All keyed groups use `Record` (not `Map`)
|
|
69
|
+
* so the build-time tree serializes to the virtual route manifest
|
|
70
|
+
* with no shape transform.
|
|
71
|
+
*/
|
|
72
|
+
export interface SegmentNode<TFile = RouteFile> {
|
|
40
73
|
/** The raw directory name (e.g. "dashboard", "[id]", "(auth)", "@sidebar") */
|
|
41
74
|
segmentName: string;
|
|
42
75
|
/** Classified segment type */
|
|
@@ -54,43 +87,50 @@ export interface SegmentNode {
|
|
|
54
87
|
interceptedSegmentName?: string;
|
|
55
88
|
|
|
56
89
|
// --- File conventions ---
|
|
57
|
-
page?:
|
|
58
|
-
layout?:
|
|
59
|
-
middleware?:
|
|
60
|
-
access?:
|
|
61
|
-
route?:
|
|
90
|
+
page?: TFile;
|
|
91
|
+
layout?: TFile;
|
|
92
|
+
middleware?: TFile;
|
|
93
|
+
access?: TFile;
|
|
94
|
+
route?: TFile;
|
|
62
95
|
/**
|
|
63
96
|
* params.ts — isomorphic convention file exporting segmentParams and/or searchParams.
|
|
64
97
|
* Discovered by the scanner like middleware.ts and access.ts.
|
|
65
98
|
* See design/07-routing.md §"params.ts Convention File"
|
|
66
99
|
*/
|
|
67
|
-
params?:
|
|
68
|
-
error?:
|
|
69
|
-
default?:
|
|
100
|
+
params?: TFile;
|
|
101
|
+
error?: TFile;
|
|
102
|
+
default?: TFile;
|
|
70
103
|
/** Status-code files: 4xx.tsx, 5xx.tsx, {status}.tsx (component format) */
|
|
71
|
-
statusFiles?:
|
|
104
|
+
statusFiles?: Record<string, TFile>;
|
|
72
105
|
/** JSON status-code files: 4xx.json, 5xx.json, {status}.json */
|
|
73
|
-
jsonStatusFiles?:
|
|
106
|
+
jsonStatusFiles?: Record<string, TFile>;
|
|
74
107
|
/** denied.tsx — slot-only denial rendering */
|
|
75
|
-
denied?:
|
|
108
|
+
denied?: TFile;
|
|
76
109
|
/** Legacy compat: not-found.tsx (maps to 404), forbidden.tsx (403), unauthorized.tsx (401) */
|
|
77
|
-
legacyStatusFiles?:
|
|
110
|
+
legacyStatusFiles?: Record<string, TFile>;
|
|
78
111
|
|
|
79
112
|
/** Metadata route files (sitemap.ts, robots.ts, icon.tsx, etc.) keyed by base name */
|
|
80
|
-
metadataRoutes?:
|
|
113
|
+
metadataRoutes?: Record<string, TFile>;
|
|
81
114
|
|
|
82
115
|
// --- Children ---
|
|
83
|
-
children: SegmentNode[];
|
|
116
|
+
children: SegmentNode<TFile>[];
|
|
84
117
|
/** Parallel route slots (keyed by slot name without @) */
|
|
85
|
-
slots:
|
|
118
|
+
slots: Record<string, SegmentNode<TFile>>;
|
|
86
119
|
}
|
|
87
120
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
121
|
+
/**
|
|
122
|
+
* The full route tree output from the scanner (or the root of the
|
|
123
|
+
* runtime route manifest, when `TFile = ManifestFile`).
|
|
124
|
+
*
|
|
125
|
+
* Generic so the same wrapper carries app-root metadata for both
|
|
126
|
+
* shapes. The runtime manifest extends this with `viteRoot` (see
|
|
127
|
+
* `ManifestRoot` in `server/route-matcher.ts`).
|
|
128
|
+
*/
|
|
129
|
+
export interface RouteTree<TFile = RouteFile> {
|
|
90
130
|
/** The root segment node (representing app/) */
|
|
91
|
-
root: SegmentNode
|
|
131
|
+
root: SegmentNode<TFile>;
|
|
92
132
|
/** All discovered proxy.ts files (should be at most one, in app/) */
|
|
93
|
-
proxy?:
|
|
133
|
+
proxy?: TFile;
|
|
94
134
|
/**
|
|
95
135
|
* Global error page: app/global-error.{tsx,ts,jsx,js}
|
|
96
136
|
*
|
|
@@ -100,7 +140,7 @@ export interface RouteTree {
|
|
|
100
140
|
*
|
|
101
141
|
* See design/10-error-handling.md §"Tier 2 — Global Error Page"
|
|
102
142
|
*/
|
|
103
|
-
globalError?:
|
|
143
|
+
globalError?: TFile;
|
|
104
144
|
}
|
|
105
145
|
|
|
106
146
|
/** Configuration passed to the scanner */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared route-tree walkers (TIM-848).
|
|
3
|
+
*
|
|
4
|
+
* Tiny helpers that walk a `SegmentNode<TFile>` tree generically. Both the
|
|
5
|
+
* build-time tree (`SegmentNode<RouteFile>`) and the runtime manifest
|
|
6
|
+
* tree (`SegmentNode<ManifestFile>`) flow through these helpers because
|
|
7
|
+
* the walker only reads the structural fields shared by both shapes
|
|
8
|
+
* (`children`, `slots`, `page`, `route`, `urlPath`).
|
|
9
|
+
*
|
|
10
|
+
* Before this module, three near-identical `collectRoutes` functions
|
|
11
|
+
* lived in `plugins/dev-404-page.ts`, `plugins/build-report.ts`, and
|
|
12
|
+
* `routing/codegen.ts`. The codegen one is special-purpose (it
|
|
13
|
+
* accumulates `ParamEntry[]` and resolves codec chains) and stays
|
|
14
|
+
* local; the other two now share `collectLeafRoutes` from this file.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { SegmentNode } from './types.js';
|
|
18
|
+
|
|
19
|
+
/** A leaf route discovered while walking the segment tree. */
|
|
20
|
+
export interface LeafRoute<TFile> {
|
|
21
|
+
/** URL path of the leaf (root is "/"). */
|
|
22
|
+
urlPath: string;
|
|
23
|
+
/** Segment chain from root to this leaf, inclusive. */
|
|
24
|
+
segments: SegmentNode<TFile>[];
|
|
25
|
+
/** The page file at this leaf, if any. */
|
|
26
|
+
page?: TFile;
|
|
27
|
+
/** The route handler file at this leaf, if any. */
|
|
28
|
+
route?: TFile;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Options for `collectLeafRoutes`. */
|
|
32
|
+
export interface CollectLeafRoutesOptions {
|
|
33
|
+
/**
|
|
34
|
+
* If true, recurse into parallel slots and emit slot leaves alongside
|
|
35
|
+
* the main route tree. Defaults to `false` because slots render
|
|
36
|
+
* alongside their parent at the same URL and are not separately
|
|
37
|
+
* URL-addressable. The build report excludes slots; route-listing
|
|
38
|
+
* UIs that want to show "all leaves with a page handler" can opt in.
|
|
39
|
+
*/
|
|
40
|
+
includeSlots?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Walk a segment tree and collect every leaf with a `page` or `route`
|
|
45
|
+
* handler. Generic over `TFile` so it works on both the build-time
|
|
46
|
+
* scanner output and the runtime manifest tree.
|
|
47
|
+
*
|
|
48
|
+
* - Pages and route handlers at the same URL produce two distinct
|
|
49
|
+
* entries (the build report deduplicates by URL afterward).
|
|
50
|
+
* - Parallel slots are skipped unless `includeSlots: true` (slots
|
|
51
|
+
* share their parent's URL and are not addressable on their own).
|
|
52
|
+
* - Result is sorted by `urlPath` for deterministic output.
|
|
53
|
+
*/
|
|
54
|
+
export function collectLeafRoutes<TFile>(
|
|
55
|
+
root: SegmentNode<TFile>,
|
|
56
|
+
options: CollectLeafRoutesOptions = {}
|
|
57
|
+
): LeafRoute<TFile>[] {
|
|
58
|
+
const { includeSlots = false } = options;
|
|
59
|
+
const result: LeafRoute<TFile>[] = [];
|
|
60
|
+
walk(root, [], result, includeSlots);
|
|
61
|
+
result.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function walk<TFile>(
|
|
66
|
+
node: SegmentNode<TFile>,
|
|
67
|
+
chain: SegmentNode<TFile>[],
|
|
68
|
+
result: LeafRoute<TFile>[],
|
|
69
|
+
includeSlots: boolean
|
|
70
|
+
): void {
|
|
71
|
+
const currentChain = [...chain, node];
|
|
72
|
+
const path = node.urlPath || '/';
|
|
73
|
+
|
|
74
|
+
if (node.page) {
|
|
75
|
+
result.push({ urlPath: path, segments: currentChain, page: node.page });
|
|
76
|
+
}
|
|
77
|
+
if (node.route) {
|
|
78
|
+
result.push({ urlPath: path, segments: currentChain, route: node.route });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const child of node.children) {
|
|
82
|
+
walk(child, currentChain, result, includeSlots);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (includeSlots) {
|
|
86
|
+
for (const slotNode of Object.values(node.slots)) {
|
|
87
|
+
walk(slotNode, currentChain, result, includeSlots);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|