@timber-js/app 0.2.0-alpha.192 → 0.2.0-alpha.195
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/agent-skill.md +2 -0
- package/dist/_chunks/{actions-BjbbNRFN.js → actions-CWYtq6ii.js} +3 -3
- package/dist/_chunks/actions-CWYtq6ii.js.map +1 -0
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
- package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -1
- package/dist/_chunks/{build-output-helper-C3DrfzZR.js → build-output-helper-9dqCv1pZ.js} +3 -2
- package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +1 -0
- package/dist/_chunks/{cache-api-DllJ-Lyw.js → cache-api-CQeYzA5g.js} +6 -8
- package/dist/_chunks/cache-api-CQeYzA5g.js.map +1 -0
- package/dist/_chunks/chains-h7EO-u3n.js +412 -0
- package/dist/_chunks/chains-h7EO-u3n.js.map +1 -0
- package/dist/_chunks/{cli-check-DJZDc22E.js → cli-check-BVthpfLS.js} +43 -6
- package/dist/_chunks/cli-check-BVthpfLS.js.map +1 -0
- package/dist/_chunks/{cli-schema-sync-D4_AZVUb.js → cli-schema-sync-3Wutm8pH.js} +7 -6
- package/dist/_chunks/{cli-schema-sync-D4_AZVUb.js.map → cli-schema-sync-3Wutm8pH.js.map} +1 -1
- package/dist/_chunks/{cloudflare-CGP6BZKO.js → cloudflare-BKJC3SC_.js} +28 -22
- package/dist/_chunks/{cloudflare-CGP6BZKO.js.map → cloudflare-BKJC3SC_.js.map} +1 -1
- package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -1
- package/dist/_chunks/{convention-lint-CpteIpTm.js → convention-lint-DO10_pVl.js} +93 -7
- package/dist/_chunks/convention-lint-DO10_pVl.js.map +1 -0
- package/dist/_chunks/dist-DtrJyBbw.js +386 -0
- package/dist/_chunks/dist-DtrJyBbw.js.map +1 -0
- package/dist/_chunks/{error-boundary-D-ODYX41.js → error-boundary-D-lkwyaD.js} +3 -3
- package/dist/_chunks/{error-boundary-D-ODYX41.js.map → error-boundary-D-lkwyaD.js.map} +1 -1
- package/dist/_chunks/{file-cache-DmX7OqZP.js → file-cache-Dw6BJPG7.js} +5 -1
- package/dist/_chunks/{file-cache-DmX7OqZP.js.map → file-cache-Dw6BJPG7.js.map} +1 -1
- package/dist/_chunks/{format-CfwjgPz9.js → format-BKclgVbk.js} +3 -3
- package/dist/_chunks/{format-CfwjgPz9.js.map → format-BKclgVbk.js.map} +1 -1
- package/dist/_chunks/graph-cache-CP4GEmf9.js +304 -0
- package/dist/_chunks/graph-cache-CP4GEmf9.js.map +1 -0
- package/dist/_chunks/href-validation-CMc5JRls.js.map +1 -1
- package/dist/_chunks/live-graph-Bx4HodF1.js +150 -0
- package/dist/_chunks/live-graph-Bx4HodF1.js.map +1 -0
- package/dist/_chunks/{logger-CH7IcMmg.js → logger-pumCm3Il.js} +25 -7
- package/dist/_chunks/{logger-CH7IcMmg.js.map → logger-pumCm3Il.js.map} +1 -1
- package/dist/_chunks/{mdx-file-C005ay-P.js → mdx-file-CXyHGUpS.js} +3 -3
- package/dist/_chunks/mdx-file-CXyHGUpS.js.map +1 -0
- package/dist/_chunks/{plugin-context-rCinWLiE.js → plugin-context-8idfR3UZ.js} +2 -2
- package/dist/_chunks/plugin-context-8idfR3UZ.js.map +1 -0
- package/dist/_chunks/poison-rules-DoEhbqaY.js +110 -0
- package/dist/_chunks/poison-rules-DoEhbqaY.js.map +1 -0
- package/dist/_chunks/poison-scan-BAxfTT5L.js +101 -0
- package/dist/_chunks/poison-scan-BAxfTT5L.js.map +1 -0
- package/dist/_chunks/resolve-schema-CBR6Lm4i.js.map +1 -1
- package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/_chunks/{router-ref-DuYuV_0Q.js → router-ref-BzqbPwYC.js} +2 -2
- package/dist/_chunks/{router-ref-DuYuV_0Q.js.map → router-ref-BzqbPwYC.js.map} +1 -1
- package/dist/_chunks/{rsc-media-type-DRqE_lD_.js → rsc-media-type-DDc7duTD.js} +17 -2
- package/dist/_chunks/{rsc-media-type-DRqE_lD_.js.map → rsc-media-type-DDc7duTD.js.map} +1 -1
- package/dist/_chunks/scan-BoP17W3w.js +140 -0
- package/dist/_chunks/scan-BoP17W3w.js.map +1 -0
- package/dist/_chunks/{scanner-CAietmj4.js → scanner-BRIOmHE2.js} +43 -4
- package/dist/_chunks/scanner-BRIOmHE2.js.map +1 -0
- package/dist/_chunks/schema-bridge-C83xa9lT.js.map +1 -1
- package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
- package/dist/_chunks/{segment-keys-BawYuNFO.js → segment-keys-BhqoHiLc.js} +14 -2
- package/dist/_chunks/{segment-keys-BawYuNFO.js.map → segment-keys-BhqoHiLc.js.map} +1 -1
- package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -1
- package/dist/_chunks/{ssr-data-14MXm7Pj.js → ssr-data-Ya2HJPFp.js} +1 -7
- package/dist/_chunks/{ssr-data-14MXm7Pj.js.map → ssr-data-Ya2HJPFp.js.map} +1 -1
- package/dist/_chunks/use-query-states-I3JMng6J.js.map +1 -1
- package/dist/_chunks/{use-segment-params-C4r4BD9T.js → use-segment-params-DzTBpkvj.js} +3 -3
- package/dist/_chunks/use-segment-params-DzTBpkvj.js.map +1 -0
- package/dist/_chunks/{walkers-BsVLmD1S.js → walkers-BU6z9xRV.js} +9 -6
- package/dist/_chunks/{walkers-BsVLmD1S.js.map → walkers-BU6z9xRV.js.map} +1 -1
- package/dist/adapters/build-output-helper.d.ts +1 -1
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -1
- package/dist/adapters/cloudflare-dev.js +2 -1
- package/dist/adapters/cloudflare-dev.js.map +1 -1
- package/dist/adapters/cloudflare-kv-cache.d.ts +1 -1
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +7 -8
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts +1 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/nitro-presets.d.ts +37 -0
- package/dist/adapters/nitro-presets.d.ts.map +1 -0
- package/dist/adapters/nitro-preview.d.ts +32 -0
- package/dist/adapters/nitro-preview.d.ts.map +1 -0
- package/dist/adapters/nitro.d.ts +28 -51
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +224 -171
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/analyze/chains.d.ts +124 -0
- package/dist/analyze/chains.d.ts.map +1 -0
- package/dist/analyze/classify.d.ts +150 -0
- package/dist/analyze/classify.d.ts.map +1 -0
- package/dist/analyze/crawl-entry.d.ts +17 -0
- package/dist/analyze/crawl-entry.d.ts.map +1 -0
- package/dist/analyze/crawl-entry.js +313 -0
- package/dist/analyze/crawl-entry.js.map +1 -0
- package/dist/analyze/crawl.d.ts +134 -0
- package/dist/analyze/crawl.d.ts.map +1 -0
- package/dist/analyze/graph-cache.d.ts +104 -0
- package/dist/analyze/graph-cache.d.ts.map +1 -0
- package/dist/analyze/graph-command.d.ts +43 -0
- package/dist/analyze/graph-command.d.ts.map +1 -0
- package/dist/analyze/graph-command.js +472 -0
- package/dist/analyze/graph-command.js.map +1 -0
- package/dist/analyze/live-graph.d.ts +81 -0
- package/dist/analyze/live-graph.d.ts.map +1 -0
- package/dist/analyze/poison-rules.d.ts +72 -0
- package/dist/analyze/poison-rules.d.ts.map +1 -0
- package/dist/analyze/poison-scan.d.ts +48 -0
- package/dist/analyze/poison-scan.d.ts.map +1 -0
- package/dist/analyze/scan.d.ts +73 -0
- package/dist/analyze/scan.d.ts.map +1 -0
- package/dist/cache/cache-api.d.ts +2 -2
- package/dist/cache/cache-api.d.ts.map +1 -1
- package/dist/cache/index.d.ts +8 -8
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cache/redis-handler.d.ts +1 -1
- package/dist/cache/redis-handler.d.ts.map +1 -1
- package/dist/cache/stores/cloudflare-kv.d.ts +1 -1
- package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -1
- package/dist/cache/stores/cloudflare-kv.js.map +1 -1
- package/dist/cache/stores/memory.d.ts +1 -1
- package/dist/cache/stores/memory.d.ts.map +1 -1
- package/dist/cache/stores/memory.js.map +1 -1
- package/dist/cache/stores/redis.d.ts +1 -1
- package/dist/cache/stores/redis.d.ts.map +1 -1
- package/dist/cache/stores/redis.js.map +1 -1
- package/dist/cache/stores/vercel.d.ts +1 -1
- package/dist/cache/stores/vercel.d.ts.map +1 -1
- package/dist/cache/stores/vercel.js.map +1 -1
- package/dist/cache/tag-aware-handler.d.ts +2 -2
- package/dist/cache/tag-aware-handler.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/cdn/cloudflare-purge.d.ts +1 -1
- package/dist/cdn/cloudflare-purge.d.ts.map +1 -1
- package/dist/cdn/cloudflare-purge.js.map +1 -1
- package/dist/cdn/fastly-purge.d.ts +1 -1
- package/dist/cdn/fastly-purge.d.ts.map +1 -1
- package/dist/cdn/fastly-purge.js.map +1 -1
- package/dist/cdn/index.d.ts +4 -4
- package/dist/cdn/index.d.ts.map +1 -1
- package/dist/cdn/workers-cache-purge.d.ts +5 -1
- package/dist/cdn/workers-cache-purge.d.ts.map +1 -1
- package/dist/cdn/workers-cache-purge.js.map +1 -1
- package/dist/cli-check.d.ts +10 -0
- package/dist/cli-check.d.ts.map +1 -1
- package/dist/cli.d.ts +12 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +30 -6
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-entry/hydrate.d.ts +2 -2
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
- package/dist/client/browser-entry/post-hydration.d.ts +2 -2
- package/dist/client/browser-entry/router-init.d.ts +2 -2
- package/dist/client/browser-entry/rsc-stream.d.ts +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/form.d.ts +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +2 -2
- package/dist/client/index.d.ts +18 -18
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +19 -19
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +15 -19
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts +3 -3
- package/dist/client/navigation-commit.d.ts +5 -5
- package/dist/client/navigation-root.d.ts +1 -1
- package/dist/client/navigation-root.d.ts.map +1 -1
- package/dist/client/params-context.d.ts +3 -3
- package/dist/client/router-pipeline.d.ts +7 -7
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router-types.d.ts +6 -6
- package/dist/client/router-types.d.ts.map +1 -1
- package/dist/client/router.d.ts +1 -1
- package/dist/client/rsc-fetch.d.ts +4 -4
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +2 -2
- package/dist/client/slot-provider.d.ts +1 -1
- package/dist/client/ssr-data.d.ts +2 -2
- package/dist/client/state.d.ts +4 -4
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-segment-params.d.ts +3 -3
- package/dist/codec.d.ts +2 -2
- package/dist/codec.js.map +1 -1
- package/dist/config-types.d.ts +12 -9
- package/dist/config-types.d.ts.map +1 -1
- package/dist/config-validation.d.ts +1 -1
- package/dist/cookies/define-cookie.d.ts +3 -3
- package/dist/cookies/index.d.ts +3 -3
- package/dist/cookies/index.js +4 -5
- package/dist/cookies/index.js.map +1 -1
- package/dist/cookies/json-cookie.d.ts +1 -1
- package/dist/dev-tools/404-page.d.ts +2 -2
- package/dist/dev-tools/error-page.d.ts +2 -2
- package/dist/dev-tools/index.d.ts +12 -12
- package/dist/dev-tools/instrumentation.d.ts +1 -1
- package/dist/dev-tools/logs.d.ts +1 -1
- package/dist/dev-tools/overlay.d.ts +2 -2
- package/dist/dev-tools/terminal.d.ts +1 -1
- package/dist/fonts/ast.d.ts +4 -4
- package/dist/fonts/bundle.d.ts +3 -3
- package/dist/fonts/css.d.ts +1 -1
- package/dist/fonts/dev-middleware.d.ts +1 -1
- package/dist/fonts/google.d.ts +1 -1
- package/dist/fonts/local.d.ts +1 -1
- package/dist/fonts/pipeline.d.ts +3 -3
- package/dist/fonts/transform.d.ts +2 -2
- package/dist/fonts/types.d.ts +1 -1
- package/dist/fonts/virtual-modules.d.ts +1 -1
- package/dist/index.d.ts +3 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +861 -314
- package/dist/index.js.map +1 -1
- package/dist/params/codec-registry.d.ts +1 -1
- package/dist/params/define-schema.d.ts +2 -2
- package/dist/params/index.d.ts +4 -4
- package/dist/params/index.js.map +1 -1
- package/dist/params/resolve-schema.d.ts +1 -1
- package/dist/plugin-context.d.ts +9 -9
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-report.d.ts +5 -5
- package/dist/plugins/cache.d.ts +1 -1
- package/dist/plugins/callsite-jsx.d.ts +2 -2
- package/dist/plugins/chunks.d.ts +1 -1
- package/dist/plugins/chunks.d.ts.map +1 -1
- package/dist/plugins/cloudflare-wasm.d.ts +1 -1
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-discovery.d.ts +123 -0
- package/dist/plugins/dev-discovery.d.ts.map +1 -0
- 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/fonts.d.ts +3 -3
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/graph-endpoint.d.ts +112 -0
- package/dist/plugins/graph-endpoint.d.ts.map +1 -0
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/prebuilt-capture.d.ts +2 -2
- package/dist/plugins/prebuilt-options-analysis.d.ts +1 -1
- package/dist/plugins/prebuilt.d.ts +1 -1
- package/dist/plugins/prerender-sugar.d.ts +11 -14
- package/dist/plugins/prerender-sugar.d.ts.map +1 -1
- package/dist/plugins/request-dep/analysis.d.ts +1 -1
- package/dist/plugins/request-dep/index.d.ts +5 -5
- package/dist/plugins/request-dep/origin.d.ts +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 +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +5 -5
- package/dist/routing/codegen-shared.d.ts +1 -1
- package/dist/routing/codegen-write.d.ts +1 -1
- package/dist/routing/codegen.d.ts +1 -1
- package/dist/routing/collision-probe.d.ts +1 -1
- package/dist/routing/collision-spaces.d.ts +1 -1
- package/dist/routing/convention-lint.d.ts +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/index.d.ts +13 -13
- package/dist/routing/index.js +3 -3
- package/dist/routing/interception.d.ts +1 -1
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/routing/link-codegen.d.ts +1 -1
- package/dist/routing/manifest-codegen.d.ts +1 -1
- package/dist/routing/scanner.d.ts +14 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/schema-validation.d.ts +1 -1
- package/dist/routing/segment-classify.d.ts +1 -1
- package/dist/routing/segment-codegen.d.ts +1 -1
- package/dist/routing/segment-keys.d.ts +1 -1
- package/dist/routing/slot-placement.d.ts +1 -1
- package/dist/routing/walkers.d.ts +1 -1
- package/dist/rsc-runtime/transforms.d.ts +13 -0
- package/dist/rsc-runtime/transforms.d.ts.map +1 -0
- package/dist/schema-bridge.d.ts +1 -1
- package/dist/search-params/define.d.ts +3 -3
- package/dist/search-params/index.d.ts +3 -3
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/wrappers.d.ts +1 -1
- package/dist/server/access-gate.d.ts +1 -1
- package/dist/server/action-client.d.ts +1 -1
- package/dist/server/action-handler.d.ts +5 -5
- package/dist/server/actions.d.ts +2 -2
- package/dist/server/als-registry.d.ts +8 -8
- package/dist/server/asset-headers.d.ts +1 -1
- package/dist/server/chain-url-parts.d.ts +3 -3
- package/dist/server/children-interception.d.ts +2 -2
- package/dist/server/cookie-context.d.ts +1 -1
- package/dist/server/cookie-parsing.d.ts +2 -2
- package/dist/server/default-logger.d.ts +1 -1
- package/dist/server/deny-boundary.d.ts +2 -2
- package/dist/server/deny-renderer.d.ts +5 -5
- package/dist/server/early-hints.d.ts +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +2 -4
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +5 -5
- package/dist/server/flight-injection-state.d.ts +1 -1
- package/dist/server/form-flash.d.ts +1 -1
- package/dist/server/html-injector-core.d.ts +2 -2
- package/dist/server/html-injectors.d.ts +1 -1
- package/dist/server/index.d.ts +18 -18
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +34 -38
- package/dist/server/index.js.map +1 -1
- package/dist/server/instrumentation.d.ts +1 -1
- package/dist/server/internal.d.ts +50 -50
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +1024 -1026
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-collector.d.ts +2 -2
- package/dist/server/metadata-platform.d.ts +2 -2
- package/dist/server/metadata-render.d.ts +2 -2
- package/dist/server/metadata-routes.d.ts +3 -3
- package/dist/server/metadata-social.d.ts +2 -2
- package/dist/server/metadata.d.ts +2 -2
- package/dist/server/middleware-runner.d.ts +1 -1
- package/dist/server/node-stream-transforms.d.ts +1 -1
- package/dist/server/param-coercion.d.ts +4 -4
- package/dist/server/pipeline-helpers.d.ts +3 -3
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-metadata.d.ts +1 -1
- package/dist/server/pipeline-outcome.d.ts +2 -2
- package/dist/server/pipeline-phases.d.ts +3 -3
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +9 -9
- package/dist/server/prebuilt/cache-key.d.ts +1 -1
- package/dist/server/prebuilt/capture-state.d.ts +1 -1
- package/dist/server/prebuilt/key-discipline.d.ts +1 -1
- package/dist/server/prebuilt/manifest-tag-index.d.ts +1 -1
- package/dist/server/prebuilt/overlay.d.ts +2 -2
- package/dist/server/prebuilt/payload-source.d.ts +1 -1
- package/dist/server/prebuilt/slots.d.ts +1 -1
- package/dist/server/prebuilt/synthetic-store.d.ts +2 -2
- package/dist/server/prebuilt-builder.d.ts +2 -2
- package/dist/server/primitives.d.ts +2 -2
- package/dist/server/publish-params.d.ts +1 -1
- package/dist/server/request-context.d.ts +3 -3
- package/dist/server/route-element-builder.d.ts +16 -6
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts +1 -1
- package/dist/server/route-matcher.d.ts +4 -4
- package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/deny-fallback.d.ts +3 -3
- package/dist/server/rsc-entry/error-renderer.d.ts +4 -4
- package/dist/server/rsc-entry/helpers.d.ts +6 -6
- package/dist/server/rsc-entry/index.d.ts +7 -6
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +4 -4
- package/dist/server/rsc-entry/revalidate-renderer.d.ts +4 -4
- package/dist/server/rsc-entry/rsc-payload.d.ts +4 -4
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +3 -3
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +7 -7
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +5 -5
- package/dist/server/sitemap-generator.d.ts +2 -2
- package/dist/server/sitemap-handler.d.ts +1 -1
- package/dist/server/skippable-prefix.d.ts +3 -3
- package/dist/server/slot-interception.d.ts +2 -2
- package/dist/server/slot-resolver.d.ts +3 -7
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +2 -2
- package/dist/server/ssr-entry.d.ts +1 -1
- package/dist/server/static-generator.d.ts +1 -1
- package/dist/server/static-not-found.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/tracing.d.ts +3 -3
- package/dist/server/tree-builder.d.ts +3 -3
- package/dist/server/tree-match.d.ts +1 -1
- package/dist/server/types.d.ts +2 -2
- package/dist/server/utils/mdx-file.d.ts +2 -0
- package/dist/server/utils/mdx-file.d.ts.map +1 -1
- package/dist/server/workers-cache-bridge.d.ts +1 -1
- package/dist/shared/als-slots.d.ts +1 -1
- package/dist/shared/payload-root.d.ts +2 -2
- package/dist/shared/rsc-media-type.d.ts +12 -0
- package/dist/shared/rsc-media-type.d.ts.map +1 -1
- package/dist/shared/slot-params.d.ts +1 -1
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.js.map +1 -1
- package/dist/shims/font-local.d.ts +1 -1
- package/dist/shims/font-local.js.map +1 -1
- package/dist/shims/navigation-client.d.ts +4 -4
- package/dist/shims/navigation.d.ts +2 -2
- package/docs/api/34-api-config.mdx +7 -4
- package/docs/api/36-cli.mdx +23 -3
- package/docs/learn/11-error-handling.mdx +11 -0
- package/docs/learn/13-configuration.mdx +1 -1
- package/docs/more/01-advanced-routing.mdx +1 -0
- package/package.json +11 -10
- package/src/adapters/build-output-helper.ts +4 -4
- package/src/adapters/cloudflare-dev.ts +7 -1
- package/src/adapters/cloudflare-kv-cache.ts +11 -8
- package/src/adapters/cloudflare.ts +21 -15
- package/src/adapters/compress-module.ts +2 -2
- package/src/adapters/nitro-presets.ts +139 -0
- package/src/adapters/nitro-preview.ts +340 -0
- package/src/adapters/nitro.ts +100 -465
- package/src/adapters/shared.ts +1 -1
- package/src/analyze/chains.ts +411 -0
- package/src/analyze/classify.ts +258 -0
- package/src/analyze/crawl-entry.ts +68 -0
- package/src/analyze/crawl.ts +500 -0
- package/src/analyze/graph-cache.ts +332 -0
- package/src/analyze/graph-command.ts +762 -0
- package/src/analyze/live-graph.ts +232 -0
- package/src/analyze/poison-rules.ts +165 -0
- package/src/analyze/poison-scan.ts +162 -0
- package/src/analyze/scan.ts +149 -0
- package/src/cache/cache-api.ts +7 -7
- package/src/cache/index.ts +9 -9
- package/src/cache/redis-handler.ts +2 -2
- package/src/cache/stores/cloudflare-kv.ts +1 -1
- package/src/cache/stores/memory.ts +2 -2
- package/src/cache/stores/redis.ts +1 -1
- package/src/cache/stores/vercel.ts +1 -1
- package/src/cache/tag-aware-handler.ts +2 -2
- package/src/cache/timber-cache.ts +8 -8
- package/src/cdn/cloudflare-purge.ts +1 -1
- package/src/cdn/fastly-purge.ts +1 -1
- package/src/cdn/index.ts +10 -4
- package/src/cdn/workers-cache-purge.ts +6 -2
- package/src/cli-check.ts +53 -4
- package/src/cli-schema-sync.ts +3 -3
- package/src/cli.ts +50 -11
- package/src/client/browser-dev.ts +1 -1
- package/src/client/browser-entry/action-dispatch.ts +5 -5
- package/src/client/browser-entry/hmr.ts +1 -1
- package/src/client/browser-entry/hydrate.ts +9 -9
- package/src/client/browser-entry/index.ts +10 -10
- package/src/client/browser-entry/post-hydration.ts +5 -5
- package/src/client/browser-entry/router-init.ts +9 -9
- package/src/client/browser-entry/rsc-stream.ts +3 -3
- package/src/client/error-boundary.tsx +4 -4
- package/src/client/form.tsx +7 -2
- package/src/client/history.ts +2 -2
- package/src/client/index.ts +21 -18
- package/src/client/internal.ts +19 -19
- package/src/client/link.tsx +9 -9
- package/src/client/navigation-api.ts +1 -1
- package/src/client/navigation-commit.ts +5 -5
- package/src/client/navigation-context.ts +1 -1
- package/src/client/navigation-root.tsx +1 -1
- package/src/client/nuqs-adapter.tsx +2 -2
- package/src/client/params-context.ts +4 -4
- package/src/client/router-effects.ts +3 -3
- package/src/client/router-lifecycle.ts +2 -2
- package/src/client/router-pipeline.ts +10 -10
- package/src/client/router-ref.ts +2 -2
- package/src/client/router-skew.ts +2 -2
- package/src/client/router-types.ts +6 -6
- package/src/client/router.ts +13 -13
- package/src/client/rsc-fetch.ts +11 -16
- package/src/client/segment-cache.ts +2 -2
- package/src/client/segment-outlet.tsx +1 -1
- package/src/client/shallow-url.ts +1 -1
- package/src/client/slot-outlet.tsx +1 -1
- package/src/client/slot-provider.tsx +1 -1
- package/src/client/ssr-data.ts +3 -3
- package/src/client/stale-client.ts +1 -1
- package/src/client/state.ts +4 -4
- package/src/client/top-loader.tsx +1 -1
- package/src/client/unload-guard.ts +1 -1
- package/src/client/use-cookie.ts +2 -2
- package/src/client/use-pathname.ts +2 -2
- package/src/client/use-pending-navigation.ts +1 -1
- package/src/client/use-query-states.ts +3 -3
- package/src/client/use-router.ts +2 -2
- package/src/client/use-search-params.ts +3 -3
- package/src/client/use-segment-params.ts +6 -6
- package/src/client/use-selected-layout-segment.ts +2 -2
- package/src/codec.ts +2 -2
- package/src/config-types.ts +12 -9
- package/src/config-validation.ts +1 -1
- package/src/cookies/define-cookie.ts +6 -6
- package/src/cookies/index.ts +3 -3
- package/src/cookies/json-cookie.ts +1 -1
- package/src/dev-tools/404-page.ts +4 -4
- package/src/dev-tools/dev-page-shell.ts +1 -1
- package/src/dev-tools/error-page.ts +4 -4
- package/src/dev-tools/index.ts +12 -12
- package/src/dev-tools/instrumentation.ts +1 -1
- package/src/dev-tools/logs.ts +1 -1
- package/src/dev-tools/overlay.ts +3 -3
- package/src/dev-tools/terminal.ts +2 -2
- package/src/fonts/ast.ts +5 -5
- package/src/fonts/bundle.ts +5 -5
- package/src/fonts/css.ts +1 -1
- package/src/fonts/dev-middleware.ts +2 -2
- package/src/fonts/google.ts +2 -2
- package/src/fonts/local.ts +4 -4
- package/src/fonts/pipeline.ts +8 -8
- package/src/fonts/transform.ts +5 -5
- package/src/fonts/types.ts +1 -1
- package/src/fonts/virtual-modules.ts +1 -1
- package/src/index.ts +90 -95
- package/src/params/codec-registry.ts +1 -1
- package/src/params/define-schema.ts +2 -2
- package/src/params/index.ts +4 -4
- package/src/params/resolve-schema.ts +2 -2
- package/src/plugin-context.ts +10 -10
- package/src/plugins/adapter-build.ts +5 -5
- package/src/plugins/build-manifest.ts +4 -4
- package/src/plugins/build-report.ts +6 -6
- package/src/plugins/cache.ts +3 -3
- package/src/plugins/callsite-ast.ts +1 -1
- package/src/plugins/callsite-jsx.ts +2 -2
- package/src/plugins/chunks.ts +1 -1
- package/src/plugins/cloudflare-wasm.ts +1 -1
- package/src/plugins/content.ts +5 -1
- package/src/plugins/dev-discovery.ts +218 -0
- package/src/plugins/dev-server.ts +97 -12
- package/src/plugins/entries.ts +1 -1
- package/src/plugins/fonts.ts +17 -10
- package/src/plugins/graph-endpoint.ts +532 -0
- package/src/plugins/mdx.ts +4 -3
- package/src/plugins/prebuilt-capture.ts +3 -3
- package/src/plugins/prebuilt-options-analysis.ts +1 -1
- package/src/plugins/prebuilt.ts +7 -7
- package/src/plugins/prerender-sugar.ts +11 -19
- package/src/plugins/request-dep/analysis.ts +1 -1
- package/src/plugins/request-dep/index.ts +8 -8
- package/src/plugins/request-dep/origin.ts +1 -1
- package/src/plugins/routing.ts +12 -7
- package/src/plugins/server-bundle.ts +9 -2
- package/src/plugins/shims.ts +16 -31
- package/src/plugins/static-build.ts +10 -10
- package/src/routing/codegen-shared.ts +1 -1
- package/src/routing/codegen-write.ts +3 -3
- package/src/routing/codegen.ts +14 -5
- package/src/routing/collision-probe.ts +3 -3
- package/src/routing/collision-spaces.ts +3 -3
- package/src/routing/convention-lint.ts +136 -6
- package/src/routing/export-detect.ts +1 -1
- package/src/routing/index.ts +13 -13
- package/src/routing/interception.ts +41 -4
- package/src/routing/link-codegen.ts +3 -3
- package/src/routing/manifest-codegen.ts +2 -2
- package/src/routing/scanner.ts +33 -9
- package/src/routing/schema-validation.ts +4 -4
- package/src/routing/segment-classify.ts +2 -2
- package/src/routing/segment-codegen.ts +1 -1
- package/src/routing/segment-keys.ts +1 -1
- package/src/routing/slot-placement.ts +1 -1
- package/src/routing/walkers.ts +1 -1
- package/src/rsc-runtime/transforms.ts +13 -0
- package/src/rsc-runtime/vendor-types.d.ts +12 -0
- package/src/schema-bridge.ts +1 -1
- package/src/search-params/define.ts +8 -8
- package/src/search-params/index.ts +3 -3
- package/src/search-params/wrappers.ts +3 -3
- package/src/server/access-gate.tsx +6 -6
- package/src/server/action-client.ts +5 -5
- package/src/server/action-handler.ts +16 -16
- package/src/server/actions.ts +5 -5
- package/src/server/als-registry.ts +8 -8
- package/src/server/asset-headers.ts +2 -2
- package/src/server/chain-url-parts.ts +5 -5
- package/src/server/children-interception.ts +4 -4
- package/src/server/compress.ts +1 -1
- package/src/server/cookie-context.ts +4 -4
- package/src/server/cookie-parsing.ts +2 -2
- package/src/server/default-logger.ts +3 -3
- package/src/server/deny-boundary.ts +10 -10
- package/src/server/deny-renderer.ts +38 -25
- package/src/server/early-hints-sender.ts +2 -2
- package/src/server/early-hints.ts +2 -2
- package/src/server/error-boundary-wrapper.ts +4 -12
- package/src/server/fallback-error.ts +13 -13
- package/src/server/flight-injection-state.ts +1 -1
- package/src/server/form-flash.ts +2 -2
- package/src/server/html-injector-core.ts +4 -4
- package/src/server/html-injectors.ts +5 -5
- package/src/server/index.ts +18 -18
- package/src/server/instrumentation.ts +1 -1
- package/src/server/internal.ts +50 -50
- package/src/server/logger.ts +4 -4
- package/src/server/metadata-collector.ts +7 -7
- package/src/server/metadata-platform.ts +2 -2
- package/src/server/metadata-render.ts +4 -4
- package/src/server/metadata-routes.ts +3 -3
- package/src/server/metadata-social.ts +2 -2
- package/src/server/metadata.ts +2 -2
- package/src/server/middleware-runner.ts +1 -1
- package/src/server/node-stream-transforms.ts +4 -4
- package/src/server/param-coercion.ts +8 -8
- package/src/server/pipeline-helpers.ts +7 -7
- package/src/server/pipeline-interception.ts +2 -2
- package/src/server/pipeline-metadata.ts +1 -1
- package/src/server/pipeline-outcome.ts +8 -8
- package/src/server/pipeline-phases.ts +24 -28
- package/src/server/pipeline.ts +22 -22
- package/src/server/prebuilt/cache-key.ts +1 -1
- package/src/server/prebuilt/capture-state.ts +1 -1
- package/src/server/prebuilt/key-discipline.ts +1 -1
- package/src/server/prebuilt/manifest-tag-index.ts +1 -1
- package/src/server/prebuilt/overlay.ts +6 -6
- package/src/server/prebuilt/payload-source.ts +2 -2
- package/src/server/prebuilt/slots.ts +5 -5
- package/src/server/prebuilt/synthetic-store.ts +2 -2
- package/src/server/prebuilt-builder.ts +16 -16
- package/src/server/prebuilt-runtime.ts +12 -12
- package/src/server/primitives.ts +7 -7
- package/src/server/publish-params.ts +3 -3
- package/src/server/request-context.ts +7 -7
- package/src/server/route-element-builder.ts +108 -50
- package/src/server/route-handler.ts +4 -4
- package/src/server/route-matcher.ts +5 -5
- package/src/server/rsc-cache-key-guard.ts +3 -5
- package/src/server/rsc-entry/action-middleware-runner.ts +7 -7
- package/src/server/rsc-entry/api-handler.ts +18 -9
- package/src/server/rsc-entry/deny-fallback.ts +10 -10
- package/src/server/rsc-entry/error-renderer.ts +27 -27
- package/src/server/rsc-entry/helpers.ts +10 -10
- package/src/server/rsc-entry/index.ts +51 -47
- package/src/server/rsc-entry/render-route.ts +20 -20
- package/src/server/rsc-entry/revalidate-renderer.ts +12 -12
- package/src/server/rsc-entry/rsc-payload.ts +19 -15
- package/src/server/rsc-entry/rsc-stream.ts +8 -8
- package/src/server/rsc-entry/ssr-bridge.ts +3 -3
- package/src/server/rsc-entry/ssr-renderer.ts +23 -23
- package/src/server/rsc-entry/wrap-action-dispatch.ts +9 -9
- package/src/server/rsc-prop-warnings.ts +1 -1
- package/src/server/sensitive-fields.ts +1 -1
- package/src/server/server-timing.ts +1 -1
- package/src/server/sitemap-generator.ts +3 -3
- package/src/server/sitemap-handler.ts +3 -3
- package/src/server/skippable-prefix.ts +7 -7
- package/src/server/slot-interception.ts +5 -5
- package/src/server/slot-resolver.ts +20 -28
- package/src/server/ssr-bridge-types.ts +2 -2
- package/src/server/ssr-entry.ts +18 -18
- package/src/server/ssr-render.ts +4 -4
- package/src/server/ssr-wrappers.tsx +1 -1
- package/src/server/state-tree-diff.ts +1 -1
- package/src/server/static-generator.ts +6 -6
- package/src/server/static-not-found.ts +3 -3
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/tracing.ts +5 -5
- package/src/server/tree-builder.ts +4 -4
- package/src/server/tree-match.ts +1 -1
- package/src/server/types.ts +2 -2
- package/src/server/utils/mdx-file.ts +2 -2
- package/src/server/waituntil-bridge.ts +1 -1
- package/src/server/workers-cache-bridge.ts +2 -2
- package/src/shared/als-slots.ts +1 -1
- package/src/shared/payload-root.ts +2 -2
- package/src/shared/rsc-media-type.ts +16 -0
- package/src/shared/slot-params.ts +1 -1
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +1 -1
- package/src/shims/navigation-client.ts +5 -5
- package/src/shims/navigation.ts +2 -2
- package/dist/_chunks/actions-BjbbNRFN.js.map +0 -1
- package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +0 -1
- package/dist/_chunks/cache-api-DllJ-Lyw.js.map +0 -1
- package/dist/_chunks/cli-check-DJZDc22E.js.map +0 -1
- package/dist/_chunks/convention-lint-CpteIpTm.js.map +0 -1
- package/dist/_chunks/dist-BA3u1z3W.js +0 -423
- package/dist/_chunks/dist-BA3u1z3W.js.map +0 -1
- package/dist/_chunks/mdx-file-C005ay-P.js.map +0 -1
- package/dist/_chunks/plugin-context-rCinWLiE.js.map +0 -1
- package/dist/_chunks/scanner-CAietmj4.js.map +0 -1
- package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +0 -1
- package/dist/content/index.d.ts +0 -2
- package/dist/content/index.d.ts.map +0 -1
- package/dist/content/index.js +0 -0
- package/src/content/index.ts +0 -5
- package/src/shims/server-only-noop.js +0 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-cache-CP4GEmf9.js","names":[],"sources":["../../src/plugins/dev-discovery.ts","../../src/analyze/graph-cache.ts"],"sourcesContent":["/**\n * Dev-server discovery records — `.timber/dev/<pid>-<port>-<token>.json`.\n *\n * On startup each dev server writes `{ port, pid, host, protocol }` to its own record\n * file under the app's `.timber/dev/` so the `timber graph` CLI and the\n * VS Code extension can discover a running dev server for the\n * live-graph fast path.\n *\n * One record file per server, one writer per file. This is what makes\n * multi-server roots (Vite auto-increments the port across processes;\n * programmatic embeddings run several servers in one process) safe\n * without any coordination: there is no shared file to displace, no\n * ownership to arbitrate, and no shutdown-order dependency — each\n * server removes exactly its own record.\n *\n * Consumers must list the directory and trust a record only if its pid\n * is alive AND a probe request to the endpoint succeeds; anything else\n * is treated as absent (silent fallback), never an error. Stale records\n * (crashed servers) are therefore the normal case — startup sweeps\n * dead-pid records best-effort, and consumers validate the rest away.\n *\n * The directory layout and file format are implementation details, not\n * a public contract.\n *\n * Design doc: 47-module-environment-tooling.md §5, 21-dev-server.md.\n */\n\nimport type { Server } from 'node:net';\nimport { Server as TlsServer } from 'node:tls';\nimport { randomUUID } from 'node:crypto';\nimport { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Relative path of the discovery directory within the project root. */\nexport const DEV_DISCOVERY_DIR = join('.timber', 'dev');\n\nexport interface DevRecord {\n port: number;\n pid: number;\n /**\n * The bound address, as reported by the socket. Consumers probing the\n * endpoint need it for servers bound to non-default interfaces;\n * wildcard addresses (`0.0.0.0`, `::`) should be probed via loopback.\n */\n host: string;\n /**\n * Probe scheme. `server.https` dev servers (including HTTP/2) answer\n * only TLS — an http:// probe of one would fail and wrongly trigger\n * the cold-crawl fallback.\n */\n protocol: 'http' | 'https';\n /**\n * Vite `base` path. When non-root (e.g. `/my-app/`), the WebSocket\n * upgrade path is `base + hmrPath`.\n */\n base?: string;\n /**\n * HMR WebSocket path (from `server.ws.path` or deprecated\n * `server.hmr.path`). Absent when no path is configured (default `/`).\n */\n hmrPath?: string;\n /**\n * Custom WebSocket host override (from `server.ws.host` or deprecated\n * `server.hmr.host`). When set, the WS connection targets this host\n * instead of the HTTP server's bound address.\n */\n hmrHost?: string;\n /**\n * Custom WebSocket port override (from `server.ws.clientPort` ??\n * `server.ws.port`, or deprecated `server.hmr.clientPort` ??\n * `server.hmr.port`). When set, the WS connection targets this port\n * instead of the HTTP server's bound port.\n */\n hmrPort?: number;\n /**\n * Custom WebSocket protocol override (from `server.ws.protocol` or\n * deprecated `server.hmr.protocol`). When set (e.g. `'wss'`), the\n * upgrade request uses the corresponding HTTP scheme.\n */\n hmrProtocol?: string;\n /**\n * Vite 8 WebSocket token (from `config.webSocketToken`). Required for\n * browser WS upgrades; the extension passes it as `?token=` on the\n * WebSocket URL.\n */\n webSocketToken?: string;\n /**\n * Absolute path to the Vite config file this server was started with.\n * When multiple dev servers share a project root but use different\n * configs, the fast path must only accept the record matching the\n * config the CLI resolved (TIM-1393).\n */\n configFile?: string;\n}\n\n/** Best-effort \"is this pid alive\" — signal 0 probes without killing. */\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but is owned by someone else.\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\n/**\n * Remove sibling records whose pid is no longer alive. Purely a\n * tidiness sweep — consumers already ignore records that fail the\n * pid + probe validation, so missing a stale record here is harmless.\n */\nfunction sweepDeadRecords(dir: string): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.endsWith('.json')) continue;\n const filePath = join(dir, entry);\n try {\n const record = JSON.parse(readFileSync(filePath, 'utf8')) as Partial<DevRecord>;\n // Remove only records that parse AND name a dead pid. An\n // unreadable file may be a sibling's record mid-write — leave it;\n // consumers ignore records that fail validation anyway.\n if (typeof record.pid === 'number' && !isPidAlive(record.pid)) {\n rmSync(filePath, { force: true });\n }\n } catch {\n // Unreadable — skip, never delete another writer's in-flight file.\n }\n }\n}\n\n/**\n * Register discovery-record lifecycle handlers on the dev server's HTTP\n * server. Writes `.timber/dev/<pid>-<port>-<token>.json` once the server is\n * listening (the bound port is only known then) and removes exactly\n * that record on clean shutdown.\n *\n * All filesystem work is best-effort: a failure to write or remove a\n * record must never break the dev server. `httpServer` is null in\n * middleware mode — no port exists, so there is nothing to advertise.\n * Typed as `net.Server` — the common base of Vite's `http.Server` and\n * `Http2SecureServer` variants, which carries the events and `address()`.\n */\nexport function registerDevDiscovery(\n httpServer: Server | null,\n root: string,\n config?: {\n configFile?: string | false;\n base?: string;\n server?: {\n hmr?:\n | { path?: string; host?: string; port?: number; clientPort?: number; protocol?: string }\n | boolean;\n ws?:\n | { path?: string; host?: string; port?: number; clientPort?: number; protocol?: string }\n | false;\n };\n webSocketToken?: string;\n }\n): void {\n if (!httpServer) return;\n\n const dir = join(root, DEV_DISCOVERY_DIR);\n let recordPath: string | null = null;\n\n httpServer.on('listening', () => {\n const address = httpServer.address();\n // Unix-socket listeners have a string address — no port to advertise.\n if (address === null || typeof address === 'string') return;\n const hmrOpts = typeof config?.server?.hmr === 'object' ? config.server.hmr : undefined;\n const wsOpts = typeof config?.server?.ws === 'object' ? config.server.ws : undefined;\n const record: DevRecord = {\n port: address.port,\n pid: process.pid,\n host: address.address,\n // Vite's https/h2 servers are tls.Server subclasses; plain HTTP\n // and h2c are not.\n protocol: httpServer instanceof TlsServer ? 'https' : 'http',\n base: config?.base && config.base !== '/' ? config.base : undefined,\n hmrPath: wsOpts?.path ?? hmrOpts?.path,\n hmrHost: wsOpts?.host ?? hmrOpts?.host,\n hmrPort:\n wsOpts?.clientPort || wsOpts?.port || hmrOpts?.clientPort || hmrOpts?.port || undefined,\n hmrProtocol: wsOpts?.protocol ?? hmrOpts?.protocol,\n webSocketToken: config?.webSocketToken,\n configFile: typeof config?.configFile === 'string' ? config.configFile : undefined,\n };\n // pid+port alone is not a unique server identity (same port on two\n // interfaces; worker threads and duplicate module copies share a\n // pid but not module state, so a counter cannot disambiguate\n // either). A random token makes the filename unique regardless of\n // process/isolate topology; pid and port stay in the name only for\n // human debuggability.\n recordPath = join(dir, `${record.pid}-${record.port}-${randomUUID()}.json`);\n try {\n mkdirSync(dir, { recursive: true });\n writeFileSync(recordPath, JSON.stringify(record));\n sweepDeadRecords(dir);\n } catch {\n // Best-effort — consumers fall back to the cold crawl.\n }\n });\n\n httpServer.on('close', () => {\n if (!recordPath) return;\n try {\n // Only ever this server's own record — sibling servers' records\n // are separate files and survive any shutdown order untouched.\n rmSync(recordPath, { force: true });\n } catch {\n // Best-effort — a stale record is validated away by consumers.\n }\n });\n}\n","/**\n * graph-cache — the mtime-keyed output cache for `timber graph`.\n *\n * The cold crawl costs seconds (design/47 §6) and its cost is setup +\n * dependency compilation, not project-file count — so the cacheable\n * unit is the classification OUTPUT, never Vite's transforms. One\n * crawl per project state: the first question after a clone pays for\n * the crawl, every subsequent question is a cache hit until the\n * project changes.\n *\n * Validity is a fingerprint over (path, mtime, size) of every project\n * source file — the same disk walk the crawl itself uses (scan.ts), so\n * cache coverage and crawl coverage cannot drift — plus the config\n * files and the nearest lockfile, since dependency changes change\n * resolution (export conditions can flip a file's environment without\n * touching project code).\n *\n * Deliberately vite-free: a cache hit answers without importing Vite.\n *\n * Design docs: 47-module-environment-tooling.md §\"Cold-path policy\".\n */\n\nimport { createHash } from 'node:crypto';\nimport { createRequire } from 'node:module';\nimport { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport { buildSourceExtensions, scanProjectSourceFiles } from './scan.ts';\nimport type { CrawlReport } from './crawl.ts';\n\n/**\n * JSON contract version (design/47 §9) — one constant for every\n * frontend (CLI and dev-server endpoint), so the two cannot drift.\n * Lives here rather than graph-command.ts because this module is\n * vite-free and both frontends already depend on the analyze core.\n */\nexport const GRAPH_JSON_VERSION = 1;\n\n/** Bump when the report shape or fingerprint inputs change. */\nexport const GRAPH_CACHE_VERSION = 2;\n\n/** Cache location under the app root. */\nconst CACHE_RELATIVE_PATH = join('.timber', 'graph-cache.json');\n\n/**\n * Config/manifest files at the root that participate in the\n * fingerprint. The `.env` family is included because a Vite config\n * using `loadEnv(mode, …)` can select aliases, plugins, the app root,\n * or pageExtensions from it — the disk scan skips dot-prefixed files,\n * so without these entries an `.env.production` edit would never miss\n * the cache (codex review, PR #1056). The build resolves under mode\n * `production`; the development variants are tracked anyway — they are\n * loadable through `loadEnv` with any mode argument, and an absent\n * file contributes nothing.\n */\nconst ROOT_MANIFEST_FILES = [\n 'package.json',\n 'tsconfig.json',\n 'timber.config.ts',\n 'timber.config.js',\n 'timber.config.mjs',\n '.env',\n '.env.local',\n '.env.production',\n '.env.production.local',\n '.env.development',\n '.env.development.local',\n];\n\n/** Lockfile names, searched upward from the root (monorepo workspaces). */\nconst LOCKFILE_NAMES = [\n 'pnpm-lock.yaml',\n 'package-lock.json',\n 'yarn.lock',\n 'bun.lock',\n 'bun.lockb',\n];\n\nexport interface GraphCacheFile {\n version: number;\n fingerprint: string;\n /** The Vite config file the cached crawl booted with. */\n configFile: string;\n /**\n * The resolved pageExtensions from the timber config at crawl time.\n * Stored so the vite-free hit path can reproduce the same scan\n * extension set (custom extensions like 'foo' must be admitted on\n * both the write and the hit path, or the fingerprint disagrees\n * with what was cached).\n */\n pageExtensions?: string[];\n /**\n * Files the fingerprint tracks BEYOND the disk scan, discovered at\n * crawl time: Vite's `configFileDependencies` (a config helper\n * imported from outside the app root can reshape the whole app) and\n * every file the report itself classifies (the graph can reach files\n * the scan skips — `app/.well-known/page.tsx`, a route under a\n * directory named `dist`). The hit path re-stats exactly this list,\n * so an edit to any of them misses the cache.\n */\n trackedExtras: string[];\n report: CrawlReport;\n}\n\n/**\n * Follow a tsconfig/jsconfig `extends` chain, returning every file on\n * it (the start file included). A base config outside the app —\n * `\"extends\": \"../tsconfig.base.json\"` with a `paths` mapping — can\n * reshape Vite's resolution without touching anything else the\n * fingerprint tracks. Parsing is a targeted regex, not JSONC: tsconfig\n * allows comments, and all we need is the `extends` value. Package\n * specifiers resolve through Node resolution (an approximation of\n * TypeScript's — the `.json` append covers the common shapes): a\n * WORKSPACE package like `@repo/tsconfig/base.json` is a link, so its\n * edits change neither the lockfile nor any scanned file and must be\n * fingerprinted directly; registry packages resolve too, harmlessly.\n * Cycles and unresolvable targets just end the walk (bounded at 16).\n */\n/**\n * Strip JSONC comments so a commented-out `\"extends\"` line cannot\n * shadow the real one. String-aware: comment markers inside string\n * literals (a path containing `//`) survive.\n */\nexport function stripJsonComments(raw: string): string {\n let out = '';\n let inString = false;\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i];\n if (inString) {\n out += ch;\n if (ch === '\\\\') {\n out += raw[i + 1] ?? '';\n i++;\n } else if (ch === '\"') {\n inString = false;\n }\n } else if (ch === '\"') {\n inString = true;\n out += ch;\n } else if (ch === '/' && raw[i + 1] === '/') {\n while (i < raw.length && raw[i] !== '\\n') i++;\n out += '\\n';\n } else if (ch === '/' && raw[i + 1] === '*') {\n i += 2;\n while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++;\n i++;\n } else {\n out += ch;\n }\n }\n return out;\n}\n\nasync function collectTsconfigChain(startFile: string): Promise<string[]> {\n const chain: string[] = [];\n const queue = [startFile];\n const seen = new Set(queue);\n while (queue.length > 0 && chain.length < 16) {\n const current = queue.shift()!;\n let raw: string;\n try {\n raw = stripJsonComments(await readFile(current, 'utf8'));\n } catch {\n continue;\n }\n chain.push(current);\n const match = raw.match(/\"extends\"\\s*:\\s*(\"([^\"]+)\"|\\[([^\\]]*)\\])/);\n if (!match) continue;\n const targets = match[2]\n ? [match[2]]\n : (match[3]?.match(/\"([^\"]+)\"/g) ?? []).map((t) => t.slice(1, -1));\n for (const target of targets) {\n let next: string | null = null;\n if (isAbsolute(target)) {\n // An absolute extends target names the base directly — joining\n // it under the current config's directory would fabricate a\n // path that does not exist and drop the real base.\n next = target;\n if (!/\\.json$/.test(next)) next += '.json';\n } else if (target.startsWith('.')) {\n next = join(dirname(current), target);\n if (!/\\.json$/.test(next)) next += '.json';\n } else {\n const req = createRequire(current);\n for (const candidate of [target, `${target}.json`, `${target}/tsconfig.json`]) {\n try {\n next = req.resolve(candidate);\n break;\n } catch {\n // try the next shape\n }\n }\n }\n if (next && !seen.has(next)) {\n seen.add(next);\n queue.push(next);\n }\n }\n }\n return chain;\n}\n\n/** Nearest lockfile at or above `root`, or null. */\nasync function findLockfile(root: string): Promise<string | null> {\n let dir = root;\n for (;;) {\n for (const name of LOCKFILE_NAMES) {\n const candidate = join(dir, name);\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // keep looking\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/** `path\\0mtime\\0size` entries for a file list; absent files contribute nothing. */\nexport async function fingerprintEntriesForFiles(files: Iterable<string>): Promise<string[]> {\n const entries: string[] = [];\n for (const file of new Set(files)) {\n try {\n const stats = await stat(file);\n entries.push(`${file}\\0${stats.mtimeMs}\\0${stats.size}`);\n } catch {\n // Absent files simply contribute nothing; when one appears\n // later, its entry appears and the fingerprint changes.\n }\n }\n return entries;\n}\n\n/**\n * Entries for the scan-derived base set: project source files, the\n * config, root manifests, and the nearest lockfile. Scan failures are\n * folded into the entry list rather than thrown: an unreadable\n * directory changes the answer's completeness, so it must change the\n * key too.\n */\nexport async function collectBaseFingerprintEntries(\n root: string,\n configFile: string,\n pageExtensions?: readonly string[]\n): Promise<string[]> {\n const extensions = buildSourceExtensions(pageExtensions);\n const sourceFiles: string[] = [];\n const failures: string[] = [];\n await scanProjectSourceFiles(\n root,\n sourceFiles,\n (failure) => failures.push(`${failure.dir}:${failure.message}`),\n 0,\n extensions\n );\n const tracked = [...sourceFiles, configFile, ...ROOT_MANIFEST_FILES.map((f) => join(root, f))];\n // Manifest/.env files beside the CONFIG too, when it lives outside\n // the app root — `loadEnv(mode, process.cwd())` in a monorepo config\n // reads the .env next to the config, not the one under root.\n const configDir = dirname(configFile);\n if (configDir !== root) {\n tracked.push(...ROOT_MANIFEST_FILES.map((f) => join(configDir, f)));\n }\n // ts/jsconfig extends chains: a base config outside the app can carry\n // the `paths` mappings resolution depends on.\n for (const name of ['tsconfig.json', 'jsconfig.json']) {\n tracked.push(...(await collectTsconfigChain(join(root, name))));\n if (configDir !== root) tracked.push(...(await collectTsconfigChain(join(configDir, name))));\n }\n const lockfile = await findLockfile(root);\n if (lockfile) tracked.push(lockfile);\n return [...failures, ...(await fingerprintEntriesForFiles(tracked))];\n}\n\n/** Hash an entry list order-independently. */\nexport function hashFingerprintEntries(entries: string[]): string {\n const unique = [...new Set(entries)].sort();\n return createHash('sha256').update(unique.join('\\n')).digest('hex');\n}\n\n/**\n * Fingerprint the project state `timber graph`'s answers depend on:\n * the scan-derived base set plus `extraFiles` (the cache's\n * trackedExtras on the hit path; config dependencies + report files on\n * the write path).\n */\nexport async function computeProjectFingerprint(\n root: string,\n configFile: string,\n extraFiles: readonly string[] = [],\n pageExtensions?: readonly string[]\n): Promise<string> {\n const entries = await collectBaseFingerprintEntries(root, configFile, pageExtensions);\n entries.push(...(await fingerprintEntriesForFiles(extraFiles)));\n return hashFingerprintEntries(entries);\n}\n\n/** Read the cache file; anything unreadable or version-mismatched is a miss. */\nexport async function readGraphCache(root: string): Promise<GraphCacheFile | null> {\n try {\n const raw = await readFile(join(root, CACHE_RELATIVE_PATH), 'utf8');\n const parsed = JSON.parse(raw) as Partial<GraphCacheFile>;\n if (\n parsed.version !== GRAPH_CACHE_VERSION ||\n typeof parsed.fingerprint !== 'string' ||\n typeof parsed.configFile !== 'string' ||\n !Array.isArray(parsed.trackedExtras) ||\n typeof parsed.report !== 'object' ||\n parsed.report === null\n ) {\n return null;\n }\n return parsed as GraphCacheFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Write the cache atomically (temp file + rename) so a concurrent\n * reader — parallel agent invocations are the expected workload —\n * never sees a torn JSON file.\n */\nexport async function writeGraphCache(root: string, cache: GraphCacheFile): Promise<void> {\n const target = join(root, CACHE_RELATIVE_PATH);\n await mkdir(dirname(target), { recursive: true });\n const temp = `${target}.${process.pid}.tmp`;\n await writeFile(temp, JSON.stringify(cache));\n await rename(temp, target);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"href-validation-CMc5JRls.js","names":[],"sources":["../../src/shared/merge-search-params.ts","../../src/shared/href-validation.ts"],"sourcesContent":["/**\n * Shared utility for merging preserved search params into a target URL.\n *\n * Used by both <Link> (client) and redirect() (server). Extracted to a shared\n * module to avoid importing client code ('use client') from server modules.\n */\n\n/**\n * Merge preserved search params from the current URL into a target href.\n *\n * When `preserve` is `true`, all current search params are merged.\n * When `preserve` is a `string[]`, only the named params are merged.\n *\n * The target href's own search params take precedence — preserved params\n * are only added if the target doesn't already define them.\n *\n * @param targetHref - The resolved target href (may already contain query string)\n * @param currentSearch - The current URL's search string (e.g. \"?private=access&page=2\")\n * @param preserve - `true` to preserve all, or `string[]` to preserve specific params\n * @returns The target href with preserved search params merged in\n */\nexport function mergePreservedSearchParams(\n targetHref: string,\n currentSearch: string,\n preserve: true | string[]\n): string {\n const currentParams = new URLSearchParams(currentSearch);\n if (currentParams.size === 0) return targetHref;\n\n // Split hash fragment from target before processing query params.\n // Hash must come after query string: /path?query=value#hash\n const hashIdx = targetHref.indexOf('#');\n const hrefWithoutHash = hashIdx >= 0 ? targetHref.slice(0, hashIdx) : targetHref;\n const hash = hashIdx >= 0 ? targetHref.slice(hashIdx) : '';\n\n // Split target into path and existing query\n const qIdx = hrefWithoutHash.indexOf('?');\n const targetPath = qIdx >= 0 ? hrefWithoutHash.slice(0, qIdx) : hrefWithoutHash;\n const targetParams = new URLSearchParams(qIdx >= 0 ? hrefWithoutHash.slice(qIdx + 1) : '');\n\n // Collect params to preserve (that aren't already in the target)\n const merged = new URLSearchParams(targetParams);\n for (const [key, value] of currentParams) {\n // Only preserve if: (a) not already in target, and (b) included in preserve list\n if (!targetParams.has(key)) {\n if (preserve === true || preserve.includes(key)) {\n merged.append(key, value);\n }\n }\n }\n\n const qs = merged.toString();\n const pathWithQuery = qs ? `${targetPath}?${qs}` : targetPath;\n return pathWithQuery + hash;\n}\n","/**\n * Shared href/URL validation for all navigation sinks.\n *\n * Used by <Link>, useRouter().push/replace, and redirectExternal().\n * Single source of truth for dangerous-scheme detection — prevents\n * control-character smuggling that bypasses naive regex checks.\n *\n * See design/13-security.md §\"Link scheme injection\" (test #9)\n * and design/07-routing.md §\"<Link> Scheme Validation\".\n */\n\n/**\n * All C0 control characters (U+0000–U+001F) that browsers strip during\n * URL preprocessing. Browsers and `new URL()` strip the full C0 range,\n * not just tab/LF/CR — so we must match that behavior to prevent\n * bypass vectors like `\\x00javascript:alert(1)`.\n */\n// eslint-disable-next-line no-control-regex -- intentional: must match all C0 controls to prevent bypass\nconst CONTROL_CHARS_RE = /[\\x00-\\x1f]/g;\n\nconst DANGEROUS_SCHEMES = new Set(['javascript:', 'data:', 'vbscript:']);\n\n/**\n * Strip C0 control chars and leading whitespace, then extract the scheme.\n * Returns the lowercase scheme (e.g. \"javascript:\") or null if no scheme.\n */\nfunction extractScheme(href: string): string | null {\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart();\n const match = /^([a-z][a-z0-9+.-]*:)/i.exec(cleaned);\n return match ? match[1].toLowerCase() : null;\n}\n\n/**\n * Validate an href for navigation sinks (<Link>, useRouter, etc.).\n *\n * Rejects dangerous schemes (javascript:, data:, vbscript:) even when\n * control characters are embedded to bypass naive regex checks.\n * Also rejects protocol-relative URLs (//evil.com).\n *\n * Throws on rejection. Safe hrefs pass through silently.\n */\nexport function validateNavigationHref(href: string): void {\n // Normalize for validation: strip C0 controls, trim, then replace backslashes\n // with forward slashes. Browsers normalize \\ → / during URL resolution, so\n // \"/\\evil.com\" becomes \"//evil.com\" (protocol-relative open redirect).\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n // Protocol-relative URLs can redirect to attacker-controlled origins\n if (cleaned.startsWith('//')) {\n throw new Error(\n `Navigation received a protocol-relative or backslash-prefixed URL: \"${href}\". ` +\n 'Use an absolute URL with an explicit scheme or a relative path.'\n );\n }\n\n const scheme = extractScheme(href);\n if (scheme && DANGEROUS_SCHEMES.has(scheme)) {\n throw new Error(\n `Navigation received a dangerous href: \"${href}\". ` +\n 'javascript:, data:, and vbscript: URLs are not allowed.'\n );\n }\n}\n\n/**\n * Check if an href is internal (same-origin navigation) or external.\n *\n * Strips control characters before checking so that \"java\\tscript:\" is\n * correctly identified as having a scheme (and thus not internal).\n */\nexport function isInternalHref(href: string): boolean {\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n if (cleaned.startsWith('//')) return false;\n if (cleaned.startsWith('/') || cleaned.startsWith('#') || cleaned.startsWith('?')) return true;\n\n const scheme = extractScheme(href);\n if (scheme) return false;\n\n return true;\n}\n\n/** Pattern matching absolute URLs: any scheme (http:, javascript:, …) or protocol-relative // */\nconst ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\\d+\\-.]*:|\\/\\/)/;\n\n/**\n * Assert that a redirect target is a relative path — rejects absolute and\n * protocol-relative URLs. Used by both the server `redirect()` primitive and\n * the client next/navigation shim so they enforce identical semantics.\n *\n * Normalizes for validation only: strips C0 controls, trims, then replaces\n * backslashes with forward slashes. Browsers normalize \\ → / during URL\n * resolution, so \"/\\evil.com\" becomes \"//evil.com\" (protocol-relative open\n * redirect). See design/13-security.md tests #8, #8a–#8c.\n */\nexport function assertRelativeRedirectPath(path: string): void {\n const cleaned = path.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n if (ABSOLUTE_URL_RE.test(cleaned)) {\n throw new Error(\n `redirect() only accepts relative URLs. Got absolute URL: \"${path}\". ` +\n 'Use redirectExternal(url, allowList) for external redirects.'\n );\n }\n\n // eslint-disable-next-line no-control-regex -- intentional: must match C0 controls\n if (/[\\x00-\\x1f]/.test(path)) {\n throw new Error(\n `redirect() received a path containing control characters (U+0000–U+001F). ` +\n 'Sanitize user input before passing it to redirect().'\n );\n }\n}\n\nconst SAFE_REDIRECT_SCHEMES = new Set(['http:', 'https:']);\n\n/**\n * Validate and canonicalize an external redirect URL.\n *\n * Requires http: or https: scheme and checks origin against an allow-list.\n * Returns the parsed canonical URL string (from URL.href) to prevent\n * raw user input from reaching the Location header.\n *\n * @throws On invalid URL, dangerous scheme, or origin not in allow-list.\n */\nexport function validateExternalRedirectUrl(url: string, allowList: string[]): string {\n // Strip control chars before parsing to prevent scheme smuggling\n const cleaned = url.replace(CONTROL_CHARS_RE, '').trimStart();\n\n if (cleaned.startsWith('//')) {\n throw new Error(\n `redirectExternal() received a protocol-relative URL: \"${url}\". ` +\n 'Use an absolute URL with http: or https: scheme.'\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(cleaned);\n } catch {\n throw new Error(`redirectExternal() received an invalid URL: \"${url}\"`);\n }\n\n if (!SAFE_REDIRECT_SCHEMES.has(parsed.protocol)) {\n throw new Error(\n `redirectExternal() received a non-http(s) URL: \"${url}\". ` +\n 'Only http: and https: schemes are allowed for external redirects.'\n );\n }\n\n if (!allowList.includes(parsed.origin)) {\n throw new Error(\n `redirectExternal() target \"${parsed.origin}\" is not in the allow-list. ` +\n `Allowed: [${allowList.join(', ')}]`\n );\n }\n\n return parsed.href;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"href-validation-CMc5JRls.js","names":[],"sources":["../../src/shared/merge-search-params.ts","../../src/shared/href-validation.ts"],"sourcesContent":["/**\n * Shared utility for merging preserved search params into a target URL.\n *\n * Used by both <Link> (client) and redirect() (server). Extracted to a shared\n * module to avoid importing client code ('use client') from server modules.\n */\n\n/**\n * Merge preserved search params from the current URL into a target href.\n *\n * When `preserve` is `true`, all current search params are merged.\n * When `preserve` is a `string[]`, only the named params are merged.\n *\n * The target href's own search params take precedence — preserved params\n * are only added if the target doesn't already define them.\n *\n * @param targetHref - The resolved target href (may already contain query string)\n * @param currentSearch - The current URL's search string (e.g. \"?private=access&page=2\")\n * @param preserve - `true` to preserve all, or `string[]` to preserve specific params\n * @returns The target href with preserved search params merged in\n */\nexport function mergePreservedSearchParams(\n targetHref: string,\n currentSearch: string,\n preserve: true | string[]\n): string {\n const currentParams = new URLSearchParams(currentSearch);\n if (currentParams.size === 0) return targetHref;\n\n // Split hash fragment from target before processing query params.\n // Hash must come after query string: /path?query=value#hash\n const hashIdx = targetHref.indexOf('#');\n const hrefWithoutHash = hashIdx >= 0 ? targetHref.slice(0, hashIdx) : targetHref;\n const hash = hashIdx >= 0 ? targetHref.slice(hashIdx) : '';\n\n // Split target into path and existing query\n const qIdx = hrefWithoutHash.indexOf('?');\n const targetPath = qIdx >= 0 ? hrefWithoutHash.slice(0, qIdx) : hrefWithoutHash;\n const targetParams = new URLSearchParams(qIdx >= 0 ? hrefWithoutHash.slice(qIdx + 1) : '');\n\n // Collect params to preserve (that aren't already in the target)\n const merged = new URLSearchParams(targetParams);\n for (const [key, value] of currentParams) {\n // Only preserve if: (a) not already in target, and (b) included in preserve list\n if (!targetParams.has(key)) {\n if (preserve === true || preserve.includes(key)) {\n merged.append(key, value);\n }\n }\n }\n\n const qs = merged.toString();\n const pathWithQuery = qs ? `${targetPath}?${qs}` : targetPath;\n return pathWithQuery + hash;\n}\n","/**\n * Shared href/URL validation for all navigation sinks.\n *\n * Used by <Link>, useRouter().push/replace, and redirectExternal().\n * Single source of truth for dangerous-scheme detection — prevents\n * control-character smuggling that bypasses naive regex checks.\n *\n * See design/13-security.md §\"Link scheme injection\" (test #9)\n * and design/07-routing.md §\"<Link> Scheme Validation\".\n */\n\n/**\n * All C0 control characters (U+0000–U+001F) that browsers strip during\n * URL preprocessing. Browsers and `new URL()` strip the full C0 range,\n * not just tab/LF/CR — so we must match that behavior to prevent\n * bypass vectors like `\\x00javascript:alert(1)`.\n */\n// eslint-disable-next-line no-control-regex -- intentional: must match all C0 controls to prevent bypass\nconst CONTROL_CHARS_RE = /[\\x00-\\x1f]/g;\n\nconst DANGEROUS_SCHEMES = new Set(['javascript:', 'data:', 'vbscript:']);\n\n/**\n * Strip C0 control chars and leading whitespace, then extract the scheme.\n * Returns the lowercase scheme (e.g. \"javascript:\") or null if no scheme.\n */\nfunction extractScheme(href: string): string | null {\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart();\n const match = /^([a-z][a-z0-9+.-]*:)/i.exec(cleaned);\n return match ? match[1].toLowerCase() : null;\n}\n\n/**\n * Validate an href for navigation sinks (<Link>, useRouter, etc.).\n *\n * Rejects dangerous schemes (javascript:, data:, vbscript:) even when\n * control characters are embedded to bypass naive regex checks.\n * Also rejects protocol-relative URLs (//evil.com).\n *\n * Throws on rejection. Safe hrefs pass through silently.\n */\nexport function validateNavigationHref(href: string): void {\n // Normalize for validation: strip C0 controls, trim, then replace backslashes\n // with forward slashes. Browsers normalize \\ → / during URL resolution, so\n // \"/\\evil.com\" becomes \"//evil.com\" (protocol-relative open redirect).\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n // Protocol-relative URLs can redirect to attacker-controlled origins\n if (cleaned.startsWith('//')) {\n throw new Error(\n `Navigation received a protocol-relative or backslash-prefixed URL: \"${href}\". ` +\n 'Use an absolute URL with an explicit scheme or a relative path.'\n );\n }\n\n const scheme = extractScheme(href);\n if (scheme && DANGEROUS_SCHEMES.has(scheme)) {\n throw new Error(\n `Navigation received a dangerous href: \"${href}\". ` +\n 'javascript:, data:, and vbscript: URLs are not allowed.'\n );\n }\n}\n\n/**\n * Check if an href is internal (same-origin navigation) or external.\n *\n * Strips control characters before checking so that \"java\\tscript:\" is\n * correctly identified as having a scheme (and thus not internal).\n */\nexport function isInternalHref(href: string): boolean {\n const cleaned = href.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n if (cleaned.startsWith('//')) return false;\n if (cleaned.startsWith('/') || cleaned.startsWith('#') || cleaned.startsWith('?')) return true;\n\n const scheme = extractScheme(href);\n if (scheme) return false;\n\n return true;\n}\n\n/** Pattern matching absolute URLs: any scheme (http:, javascript:, …) or protocol-relative // */\nconst ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\\d+\\-.]*:|\\/\\/)/;\n\n/**\n * Assert that a redirect target is a relative path — rejects absolute and\n * protocol-relative URLs. Used by both the server `redirect()` primitive and\n * the client next/navigation shim so they enforce identical semantics.\n *\n * Normalizes for validation only: strips C0 controls, trims, then replaces\n * backslashes with forward slashes. Browsers normalize \\ → / during URL\n * resolution, so \"/\\evil.com\" becomes \"//evil.com\" (protocol-relative open\n * redirect). See design/13-security.md tests #8, #8a–#8c.\n */\nexport function assertRelativeRedirectPath(path: string): void {\n const cleaned = path.replace(CONTROL_CHARS_RE, '').trimStart().replaceAll('\\\\', '/');\n\n if (ABSOLUTE_URL_RE.test(cleaned)) {\n throw new Error(\n `redirect() only accepts relative URLs. Got absolute URL: \"${path}\". ` +\n 'Use redirectExternal(url, allowList) for external redirects.'\n );\n }\n\n // eslint-disable-next-line no-control-regex -- intentional: must match C0 controls\n if (/[\\x00-\\x1f]/.test(path)) {\n throw new Error(\n `redirect() received a path containing control characters (U+0000–U+001F). ` +\n 'Sanitize user input before passing it to redirect().'\n );\n }\n}\n\nconst SAFE_REDIRECT_SCHEMES = new Set(['http:', 'https:']);\n\n/**\n * Validate and canonicalize an external redirect URL.\n *\n * Requires http: or https: scheme and checks origin against an allow-list.\n * Returns the parsed canonical URL string (from URL.href) to prevent\n * raw user input from reaching the Location header.\n *\n * @throws On invalid URL, dangerous scheme, or origin not in allow-list.\n */\nexport function validateExternalRedirectUrl(url: string, allowList: string[]): string {\n // Strip control chars before parsing to prevent scheme smuggling\n const cleaned = url.replace(CONTROL_CHARS_RE, '').trimStart();\n\n if (cleaned.startsWith('//')) {\n throw new Error(\n `redirectExternal() received a protocol-relative URL: \"${url}\". ` +\n 'Use an absolute URL with http: or https: scheme.'\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(cleaned);\n } catch {\n throw new Error(`redirectExternal() received an invalid URL: \"${url}\"`);\n }\n\n if (!SAFE_REDIRECT_SCHEMES.has(parsed.protocol)) {\n throw new Error(\n `redirectExternal() received a non-http(s) URL: \"${url}\". ` +\n 'Only http: and https: schemes are allowed for external redirects.'\n );\n }\n\n if (!allowList.includes(parsed.origin)) {\n throw new Error(\n `redirectExternal() target \"${parsed.origin}\" is not in the allow-list. ` +\n `Allowed: [${allowList.join(', ')}]`\n );\n }\n\n return parsed.href;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { i as scanProjectSourceFiles, t as buildSourceExtensions } from "./scan-BoP17W3w.js";
|
|
2
|
+
import { a as detectDirective, n as stitchImportChains, r as classifyModule } from "./chains-h7EO-u3n.js";
|
|
3
|
+
import { n as importSignals, r as poisoningsFor, t as collectImportSpecifiers } from "./poison-scan-BAxfTT5L.js";
|
|
4
|
+
import { normalizePath } from "vite";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
//#region src/analyze/live-graph.ts
|
|
8
|
+
/**
|
|
9
|
+
* live-graph — classification from a running dev server's environment
|
|
10
|
+
* module graphs (the fast path).
|
|
11
|
+
*
|
|
12
|
+
* When a dev server runs, the env module graphs are already resident —
|
|
13
|
+
* classification is a lookup, not a crawl. This module adapts those
|
|
14
|
+
* graphs to the SAME classification pipeline the cold crawl uses
|
|
15
|
+
* (classify.ts + chains.ts + poison-scan.ts — design/47's "one
|
|
16
|
+
* classification module, thin frontends" requirement), so the
|
|
17
|
+
* dev-server endpoint and the CLI can never disagree about a file.
|
|
18
|
+
*
|
|
19
|
+
* Differences from the crawl, both inherent to the live source:
|
|
20
|
+
*
|
|
21
|
+
* - The browser (`client`) graph can be non-empty here (a real browser
|
|
22
|
+
* has requested modules), so `envs.client` is confirmable.
|
|
23
|
+
* - Rendering populates graphs lazily — only visited routes' code is
|
|
24
|
+
* present, and modules behind unexecuted dynamic imports carry
|
|
25
|
+
* `transformResult == null` (the §8 frontier). Those are reported as
|
|
26
|
+
* `subtreeUnknown` rather than transformed on demand: a hint must
|
|
27
|
+
* never mutate the dev server's graphs as a side effect of asking.
|
|
28
|
+
*
|
|
29
|
+
* Design docs: 47-module-environment-tooling.md §8, §"Dev-server
|
|
30
|
+
* endpoint".
|
|
31
|
+
*/
|
|
32
|
+
/** Non-empty node set for a file in one graph. */
|
|
33
|
+
function memberOf(graph, file) {
|
|
34
|
+
return (graph.getModulesByFile(file)?.size ?? 0) > 0;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* §8: an SSR-graph member whose every node was never transformed is a
|
|
38
|
+
* module behind an unexecuted dynamic import — reachable (it ships to
|
|
39
|
+
* the browser) but its subtree may be missing from the graphs.
|
|
40
|
+
*
|
|
41
|
+
* Live-source caveat: Vite also clears `transformResult` when a watched
|
|
42
|
+
* edit invalidates a node, so on the live graphs `subtreeUnknown`
|
|
43
|
+
* means "not currently transformed" — a freshly-edited module reads
|
|
44
|
+
* the same as a dormant dynamic-import payload until the next render
|
|
45
|
+
* re-transforms it. The two states are indistinguishable from the
|
|
46
|
+
* graph alone, and both honestly mean the subtree is not currently
|
|
47
|
+
* known; consumers refreshing on `timber:graph-update` events see the
|
|
48
|
+
* flag clear once the module is re-transformed.
|
|
49
|
+
*/
|
|
50
|
+
function ssrTransformed(graph, file) {
|
|
51
|
+
const nodes = graph.getModulesByFile(file);
|
|
52
|
+
if (!nodes || nodes.size === 0) return void 0;
|
|
53
|
+
for (const node of nodes) if (node.transformResult != null) return true;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Classify one file from the live graphs. `file` must be an absolute
|
|
58
|
+
* realpath (graph nodes carry realpaths); it is slash-normalized here
|
|
59
|
+
* to match Vite's node keying.
|
|
60
|
+
*/
|
|
61
|
+
async function classifyLiveFile(rawFile, ctx, getDirective = makeDirectiveReader(), errors) {
|
|
62
|
+
const file = normalizePath(rawFile);
|
|
63
|
+
let code = null;
|
|
64
|
+
try {
|
|
65
|
+
code = await readFile(file, "utf8");
|
|
66
|
+
} catch (error) {
|
|
67
|
+
errors?.push({
|
|
68
|
+
environment: "scan",
|
|
69
|
+
id: file,
|
|
70
|
+
message: `source read failed: ${error instanceof Error ? error.message : String(error)}`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const directive = code === null ? null : detectDirective(code, file);
|
|
74
|
+
const signals = code === null ? [] : importSignals(collectImportSpecifiers(code, file));
|
|
75
|
+
const classifyCtx = {
|
|
76
|
+
appDir: ctx.appDir,
|
|
77
|
+
pageExtensions: ctx.pageExtensions
|
|
78
|
+
};
|
|
79
|
+
const classification = classifyModule({
|
|
80
|
+
file,
|
|
81
|
+
inRsc: memberOf(ctx.rsc, file),
|
|
82
|
+
inSsr: memberOf(ctx.ssr, file),
|
|
83
|
+
inClient: memberOf(ctx.client, file),
|
|
84
|
+
ssrTransformed: ssrTransformed(ctx.ssr, file),
|
|
85
|
+
directive
|
|
86
|
+
}, classifyCtx);
|
|
87
|
+
const chains = classification.pending ? [] : stitchImportChains(file, {
|
|
88
|
+
rsc: ctx.rsc,
|
|
89
|
+
ssr: ctx.ssr,
|
|
90
|
+
appDir: ctx.appDir,
|
|
91
|
+
pageExtensions: ctx.pageExtensions,
|
|
92
|
+
getDirective
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
...classification,
|
|
96
|
+
boundary: directive === "use client",
|
|
97
|
+
chains,
|
|
98
|
+
poisonings: poisoningsFor(classification.kind, classification.pending, signals)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Synchronous directive reader with a per-call cache. chains.ts wants a
|
|
103
|
+
* sync callback; node:fs readFileSync per unique hop file is acceptable
|
|
104
|
+
* for a walk bounded by chain length.
|
|
105
|
+
*/
|
|
106
|
+
function makeDirectiveReader() {
|
|
107
|
+
const cache = /* @__PURE__ */ new Map();
|
|
108
|
+
return (file) => {
|
|
109
|
+
const hit = cache.get(file);
|
|
110
|
+
if (hit !== void 0) return hit;
|
|
111
|
+
let directive = null;
|
|
112
|
+
try {
|
|
113
|
+
directive = detectDirective(readFileSync(file, "utf8"), file);
|
|
114
|
+
} catch {
|
|
115
|
+
directive = null;
|
|
116
|
+
}
|
|
117
|
+
cache.set(file, directive);
|
|
118
|
+
return directive;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Classify every project source file from the live graphs. Files on
|
|
123
|
+
* disk that no graph reached get the §2 provisional/pending
|
|
124
|
+
* classification — on a lazily-warmed dev server that includes routes
|
|
125
|
+
* nobody has visited yet, which is honest: the graphs genuinely have
|
|
126
|
+
* not confirmed them.
|
|
127
|
+
*/
|
|
128
|
+
async function classifyLiveProject(ctx) {
|
|
129
|
+
const files = [];
|
|
130
|
+
const errors = [];
|
|
131
|
+
const extensions = buildSourceExtensions(ctx.pageExtensions);
|
|
132
|
+
await scanProjectSourceFiles(ctx.root, files, (failure) => errors.push({
|
|
133
|
+
environment: "scan",
|
|
134
|
+
id: failure.dir,
|
|
135
|
+
message: failure.message
|
|
136
|
+
}), 0, extensions);
|
|
137
|
+
const modules = [];
|
|
138
|
+
const getDirective = makeDirectiveReader();
|
|
139
|
+
for (const file of files) modules.push(await classifyLiveFile(file, ctx, getDirective, errors));
|
|
140
|
+
modules.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0);
|
|
141
|
+
return {
|
|
142
|
+
root: ctx.root,
|
|
143
|
+
modules,
|
|
144
|
+
errors
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { classifyLiveFile, classifyLiveProject };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=live-graph-Bx4HodF1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-graph-Bx4HodF1.js","names":[],"sources":["../../src/analyze/live-graph.ts"],"sourcesContent":["/**\n * live-graph — classification from a running dev server's environment\n * module graphs (the fast path).\n *\n * When a dev server runs, the env module graphs are already resident —\n * classification is a lookup, not a crawl. This module adapts those\n * graphs to the SAME classification pipeline the cold crawl uses\n * (classify.ts + chains.ts + poison-scan.ts — design/47's \"one\n * classification module, thin frontends\" requirement), so the\n * dev-server endpoint and the CLI can never disagree about a file.\n *\n * Differences from the crawl, both inherent to the live source:\n *\n * - The browser (`client`) graph can be non-empty here (a real browser\n * has requested modules), so `envs.client` is confirmable.\n * - Rendering populates graphs lazily — only visited routes' code is\n * present, and modules behind unexecuted dynamic imports carry\n * `transformResult == null` (the §8 frontier). Those are reported as\n * `subtreeUnknown` rather than transformed on demand: a hint must\n * never mutate the dev server's graphs as a side effect of asking.\n *\n * Design docs: 47-module-environment-tooling.md §8, §\"Dev-server\n * endpoint\".\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport { normalizePath } from 'vite';\nimport {\n classifyModule,\n detectDirective,\n type ClassifyContext,\n type Directive,\n} from './classify.ts';\nimport type { GraphModuleReport } from './crawl.ts';\nimport { stitchImportChains, type ChainGraph, type ImportChain } from './chains.ts';\nimport {\n collectImportSpecifiers,\n importSignals,\n poisoningsFor,\n type Poisoning,\n} from './poison-scan.ts';\nimport { buildSourceExtensions, scanProjectSourceFiles, type ScanFailure } from './scan.ts';\n\n/**\n * Duck-typed node view for liveness checks — the `transformResult`\n * member ChainModuleNode deliberately omits (§8 detection is a\n * classification concern, not a chain concern).\n */\ninterface LiveModuleNode {\n transformResult: unknown;\n}\n\nimport type { ChainModuleNode } from './chains.ts';\n\n/** Duck-typed module graph — structural supertype of Vite's. */\nexport interface LiveGraph extends ChainGraph {\n getModulesByFile(file: string): Set<ChainModuleNode & LiveModuleNode> | undefined;\n}\n\nexport interface LiveGraphContext {\n rsc: LiveGraph;\n ssr: LiveGraph;\n client: LiveGraph;\n /** Realpath'd project root. */\n root: string;\n appDir: string;\n pageExtensions?: readonly string[];\n}\n\n/**\n * Per-module answer — the crawl's shape, aliased rather than restated\n * so a field added to one frontend cannot silently miss the other.\n */\nexport type LiveModuleReport = GraphModuleReport;\n\n/** Non-empty node set for a file in one graph. */\nfunction memberOf(graph: LiveGraph, file: string): boolean {\n return (graph.getModulesByFile(file)?.size ?? 0) > 0;\n}\n\n/**\n * §8: an SSR-graph member whose every node was never transformed is a\n * module behind an unexecuted dynamic import — reachable (it ships to\n * the browser) but its subtree may be missing from the graphs.\n *\n * Live-source caveat: Vite also clears `transformResult` when a watched\n * edit invalidates a node, so on the live graphs `subtreeUnknown`\n * means \"not currently transformed\" — a freshly-edited module reads\n * the same as a dormant dynamic-import payload until the next render\n * re-transforms it. The two states are indistinguishable from the\n * graph alone, and both honestly mean the subtree is not currently\n * known; consumers refreshing on `timber:graph-update` events see the\n * flag clear once the module is re-transformed.\n */\nfunction ssrTransformed(graph: LiveGraph, file: string): boolean | undefined {\n const nodes = graph.getModulesByFile(file);\n if (!nodes || nodes.size === 0) return undefined;\n for (const node of nodes) {\n if (node.transformResult != null) return true;\n }\n return false;\n}\n\n/**\n * Classify one file from the live graphs. `file` must be an absolute\n * realpath (graph nodes carry realpaths); it is slash-normalized here\n * to match Vite's node keying.\n */\nexport async function classifyLiveFile(\n rawFile: string,\n ctx: LiveGraphContext,\n // Shared across a ?all=1 sweep (classifyLiveProject) — a per-call\n // cache would re-read every chain hop's source per target file,\n // quadratic sync IO on well-connected graphs.\n getDirective: (file: string) => Directive = makeDirectiveReader(),\n // Failures are recorded, not swallowed (crawl parity): a lost source\n // read means the directive/poisoning half of the answer is missing,\n // and consumers deserve to see that beside the classification.\n errors?: LiveGraphError[]\n): Promise<LiveModuleReport> {\n const file = normalizePath(rawFile);\n let code: string | null = null;\n try {\n code = await readFile(file, 'utf8');\n } catch (error) {\n // Unreadable (deleted mid-query): classification proceeds on graph\n // membership alone; directive/poisonings default to none.\n errors?.push({\n environment: 'scan',\n id: file,\n message: `source read failed: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n const directive: Directive = code === null ? null : detectDirective(code, file);\n const signals = code === null ? [] : importSignals(collectImportSpecifiers(code, file));\n\n const classifyCtx: ClassifyContext = {\n appDir: ctx.appDir,\n pageExtensions: ctx.pageExtensions,\n };\n const classification = classifyModule(\n {\n file,\n inRsc: memberOf(ctx.rsc, file),\n inSsr: memberOf(ctx.ssr, file),\n inClient: memberOf(ctx.client, file),\n ssrTransformed: ssrTransformed(ctx.ssr, file),\n directive,\n },\n classifyCtx\n );\n\n const chains = classification.pending\n ? []\n : stitchImportChains(file, {\n rsc: ctx.rsc,\n ssr: ctx.ssr,\n appDir: ctx.appDir,\n pageExtensions: ctx.pageExtensions,\n getDirective,\n });\n\n return {\n ...classification,\n boundary: directive === 'use client',\n chains,\n poisonings: poisoningsFor(classification.kind, classification.pending, signals),\n };\n}\n\n/**\n * Synchronous directive reader with a per-call cache. chains.ts wants a\n * sync callback; node:fs readFileSync per unique hop file is acceptable\n * for a walk bounded by chain length.\n */\nfunction makeDirectiveReader(): (file: string) => Directive {\n const cache = new Map<string, Directive>();\n return (file) => {\n const hit = cache.get(file);\n if (hit !== undefined) return hit;\n let directive: Directive = null;\n try {\n directive = detectDirective(readFileSync(file, 'utf8'), file);\n } catch {\n directive = null;\n }\n cache.set(file, directive);\n return directive;\n };\n}\n\n/** Live-source error entry — the crawl's error vocabulary. */\nexport interface LiveGraphError {\n environment: 'scan';\n id: string;\n message: string;\n}\n\nexport interface LiveProjectReport {\n root: string;\n modules: LiveModuleReport[];\n errors: LiveGraphError[];\n}\n\n/**\n * Classify every project source file from the live graphs. Files on\n * disk that no graph reached get the §2 provisional/pending\n * classification — on a lazily-warmed dev server that includes routes\n * nobody has visited yet, which is honest: the graphs genuinely have\n * not confirmed them.\n */\nexport async function classifyLiveProject(ctx: LiveGraphContext): Promise<LiveProjectReport> {\n const files: string[] = [];\n const errors: LiveProjectReport['errors'] = [];\n const extensions = buildSourceExtensions(ctx.pageExtensions);\n await scanProjectSourceFiles(\n ctx.root,\n files,\n (failure: ScanFailure) =>\n errors.push({ environment: 'scan', id: failure.dir, message: failure.message }),\n 0,\n extensions\n );\n const modules: LiveModuleReport[] = [];\n const getDirective = makeDirectiveReader();\n for (const file of files) {\n modules.push(await classifyLiveFile(file, ctx, getDirective, errors));\n }\n modules.sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : 0));\n return { root: ctx.root, modules, errors };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6EA,SAAS,SAAS,OAAkB,MAAuB;CACzD,QAAQ,MAAM,iBAAiB,IAAI,CAAC,EAAE,QAAQ,KAAK;AACrD;;;;;;;;;;;;;;;AAgBA,SAAS,eAAe,OAAkB,MAAmC;CAC3E,MAAM,QAAQ,MAAM,iBAAiB,IAAI;CACzC,IAAI,CAAC,SAAS,MAAM,SAAS,GAAG,OAAO,KAAA;CACvC,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,mBAAmB,MAAM,OAAO;CAE3C,OAAO;AACT;;;;;;AAOA,eAAsB,iBACpB,SACA,KAIA,eAA4C,oBAAoB,GAIhE,QAC2B;CAC3B,MAAM,OAAO,cAAc,OAAO;CAClC,IAAI,OAAsB;CAC1B,IAAI;EACF,OAAO,MAAM,SAAS,MAAM,MAAM;CACpC,SAAS,OAAO;EAGd,QAAQ,KAAK;GACX,aAAa;GACb,IAAI;GACJ,SAAS,uBAAuB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACvF,CAAC;CACH;CACA,MAAM,YAAuB,SAAS,OAAO,OAAO,gBAAgB,MAAM,IAAI;CAC9E,MAAM,UAAU,SAAS,OAAO,CAAC,IAAI,cAAc,wBAAwB,MAAM,IAAI,CAAC;CAEtF,MAAM,cAA+B;EACnC,QAAQ,IAAI;EACZ,gBAAgB,IAAI;CACtB;CACA,MAAM,iBAAiB,eACrB;EACE;EACA,OAAO,SAAS,IAAI,KAAK,IAAI;EAC7B,OAAO,SAAS,IAAI,KAAK,IAAI;EAC7B,UAAU,SAAS,IAAI,QAAQ,IAAI;EACnC,gBAAgB,eAAe,IAAI,KAAK,IAAI;EAC5C;CACF,GACA,WACF;CAEA,MAAM,SAAS,eAAe,UAC1B,CAAC,IACD,mBAAmB,MAAM;EACvB,KAAK,IAAI;EACT,KAAK,IAAI;EACT,QAAQ,IAAI;EACZ,gBAAgB,IAAI;EACpB;CACF,CAAC;CAEL,OAAO;EACL,GAAG;EACH,UAAU,cAAc;EACxB;EACA,YAAY,cAAc,eAAe,MAAM,eAAe,SAAS,OAAO;CAChF;AACF;;;;;;AAOA,SAAS,sBAAmD;CAC1D,MAAM,wBAAQ,IAAI,IAAuB;CACzC,QAAQ,SAAS;EACf,MAAM,MAAM,MAAM,IAAI,IAAI;EAC1B,IAAI,QAAQ,KAAA,GAAW,OAAO;EAC9B,IAAI,YAAuB;EAC3B,IAAI;GACF,YAAY,gBAAgB,aAAa,MAAM,MAAM,GAAG,IAAI;EAC9D,QAAQ;GACN,YAAY;EACd;EACA,MAAM,IAAI,MAAM,SAAS;EACzB,OAAO;CACT;AACF;;;;;;;;AAsBA,eAAsB,oBAAoB,KAAmD;CAC3F,MAAM,QAAkB,CAAC;CACzB,MAAM,SAAsC,CAAC;CAC7C,MAAM,aAAa,sBAAsB,IAAI,cAAc;CAC3D,MAAM,uBACJ,IAAI,MACJ,QACC,YACC,OAAO,KAAK;EAAE,aAAa;EAAQ,IAAI,QAAQ;EAAK,SAAS,QAAQ;CAAQ,CAAC,GAChF,GACA,UACF;CACA,MAAM,UAA8B,CAAC;CACrC,MAAM,eAAe,oBAAoB;CACzC,KAAK,MAAM,QAAQ,OACjB,QAAQ,KAAK,MAAM,iBAAiB,MAAM,KAAK,cAAc,MAAM,CAAC;CAEtE,QAAQ,MAAM,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,CAAE;CACvE,OAAO;EAAE,MAAM,IAAI;EAAM;EAAS;CAAO;AAC3C"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as assertValidCookieValue, n as assertValidCookieName, r as assertValidCookieOptions, t as require_dist } from "./dist-BA3u1z3W.js";
|
|
2
1
|
import { o as traceAls, r as requestContextAls, s as waitUntilAls } from "./als-registry-C6kcfprT.js";
|
|
3
2
|
import { n as _setGetSegmentParamsFn, t as _setGetSearchParamsFn } from "./als-slots-BEEIPKYm.js";
|
|
3
|
+
import { a as assertValidCookieOptions, i as assertValidCookieName, n as parseSetCookie$1, o as assertValidCookieValue, r as stringifySetCookie, t as parseCookie } from "./dist-DtrJyBbw.js";
|
|
4
4
|
import { r as appVisibleSearch } from "./rsc-cache-key-DD0fl_-s.js";
|
|
5
5
|
import { t as resolveSegmentParams } from "./slot-params-BCTmZkQB.js";
|
|
6
6
|
import { a as mergePreservedSearchParams, r as validateExternalRedirectUrl, t as assertRelativeRedirectPath } from "./href-validation-CMc5JRls.js";
|
|
@@ -526,7 +526,22 @@ function getWaitUntil() {
|
|
|
526
526
|
}
|
|
527
527
|
//#endregion
|
|
528
528
|
//#region src/server/cookie-parsing.ts
|
|
529
|
-
|
|
529
|
+
/**
|
|
530
|
+
* Cookie parsing and serialization helpers — pure string ↔ structure
|
|
531
|
+
* functions with no ALS dependency. Split out of `cookie-context.ts`
|
|
532
|
+
* (TIM-853) so the API surface and the wire-format codecs can each be
|
|
533
|
+
* read on their own.
|
|
534
|
+
*
|
|
535
|
+
* The functions in this module are total over arbitrary input. They
|
|
536
|
+
* never throw and never call `assertValid*` (the security validators
|
|
537
|
+
* live in the API surface — `cookie-context.ts` invokes them at every
|
|
538
|
+
* jar entry point so the smuggling-primitive invariant from TIM-868
|
|
539
|
+
* is enforced regardless of which path produced the bytes).
|
|
540
|
+
*
|
|
541
|
+
* Delegates to the `cookie` package (RFC 6265, dependency-free,
|
|
542
|
+
* browser-safe) for wire codecs. Adapts at the boundary to preserve
|
|
543
|
+
* timber's Map-based API and never-throw contract.
|
|
544
|
+
*/
|
|
530
545
|
/**
|
|
531
546
|
* Parse a Cookie header string into a Map of name → value pairs.
|
|
532
547
|
* Follows RFC 6265 §4.2.1: cookies are semicolon-separated key=value pairs.
|
|
@@ -540,7 +555,7 @@ var import_dist = require_dist();
|
|
|
540
555
|
function parseCookieHeader(header) {
|
|
541
556
|
const map = /* @__PURE__ */ new Map();
|
|
542
557
|
if (!header) return map;
|
|
543
|
-
const parsed =
|
|
558
|
+
const parsed = parseCookie(header);
|
|
544
559
|
for (const name in parsed) {
|
|
545
560
|
const value = parsed[name];
|
|
546
561
|
if (value !== void 0) map.set(name, value);
|
|
@@ -575,7 +590,7 @@ function safeDecodeCookieValue(raw) {
|
|
|
575
590
|
*/
|
|
576
591
|
function serializeCookieEntry(entry) {
|
|
577
592
|
try {
|
|
578
|
-
return
|
|
593
|
+
return stringifySetCookie({
|
|
579
594
|
name: entry.name,
|
|
580
595
|
value: entry.value,
|
|
581
596
|
domain: entry.options.domain,
|
|
@@ -614,7 +629,7 @@ function serializeCookieEntryFallback(entry) {
|
|
|
614
629
|
* the original header's intent).
|
|
615
630
|
*/
|
|
616
631
|
function parseSetCookie(header) {
|
|
617
|
-
const parsed =
|
|
632
|
+
const parsed = parseSetCookie$1(header, { decode: (v) => v });
|
|
618
633
|
if (!parsed.name) return null;
|
|
619
634
|
const options = {};
|
|
620
635
|
if (parsed.path !== void 0) options.path = parsed.path || "/";
|
|
@@ -1250,7 +1265,10 @@ function redirect(path, statusOrOptions) {
|
|
|
1250
1265
|
if (status < 300 || status > 399) throw new Error(`redirect() requires a 3xx status code, got ${status}.`);
|
|
1251
1266
|
assertRelativeRedirectPath(path);
|
|
1252
1267
|
let resolvedPath = path;
|
|
1253
|
-
if (preserveSearchParams)
|
|
1268
|
+
if (preserveSearchParams) {
|
|
1269
|
+
const currentSearch = getRequestSearchString();
|
|
1270
|
+
resolvedPath = mergePreservedSearchParams(path, currentSearch, preserveSearchParams);
|
|
1271
|
+
}
|
|
1254
1272
|
throw new RedirectSignal(resolvedPath, status);
|
|
1255
1273
|
}
|
|
1256
1274
|
/**
|
|
@@ -1433,4 +1451,4 @@ function swallow(err, reason, opts) {
|
|
|
1433
1451
|
//#endregion
|
|
1434
1452
|
export { setMatchedSegmentPath as A, addSpanEventSync as B, applyRequestHeaderOverlay as C, getSegmentParams as D, getSearchParams as E, getSetCookieHeaders as F, replaceTraceId as G, getOtelTraceId as H, getWaitUntil as I, withSpan as J, runWithTraceId as K, isDebug as L, setSegmentParams as M, getCookie as N, markResponseFlushed as O, getCookieJar as P, isDevMode as R, waitUntil as S, getHeaders as T, getSpanId as U, generateTraceId as V, getTraceId as W, RedirectSignal as _, logProxyError as a, redirect as b, logRequestReceived as c, logSwrRefetchFailed as d, logWaitUntilRejected as f, DenySignal as g, swallow as h, logMiddlewareShortCircuit as i, setMutableCookieContext as j, runWithRequestContext as k, logRouteError as l, setLogger as m, logCacheMiss as n, logRenderError as o, logWaitUntilUnsupported as p, setSpanAttribute as q, logMiddlewareError as r, logRequestCompleted as s, getLogger as t, logSlowRequest as u, RenderError as v, getHeader as w, redirectExternal as x, deny as y, addSpanEvent as z };
|
|
1435
1453
|
|
|
1436
|
-
//# sourceMappingURL=logger-
|
|
1454
|
+
//# sourceMappingURL=logger-pumCm3Il.js.map
|