@lovrozagar/honey 0.6.8 → 0.7.1
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/README.md +514 -192
- package/bin/honey.js +25 -0
- package/dist/accepts.d.ts +7 -1
- package/dist/accepts.d.ts.map +1 -1
- package/dist/accepts.js +126 -0
- package/dist/accepts.js.map +1 -0
- package/dist/body-kind.d.ts +26 -0
- package/dist/body-kind.d.ts.map +1 -0
- package/dist/body-kind.js +70 -0
- package/dist/body-kind.js.map +1 -0
- package/dist/body-limit.d.ts +5 -1
- package/dist/body-limit.d.ts.map +1 -1
- package/dist/body-limit.js +126 -0
- package/dist/body-limit.js.map +1 -0
- package/dist/build/index.d.ts +19 -3
- package/dist/build/index.d.ts.map +1 -1
- package/dist/build/index.js +173 -0
- package/dist/build/index.js.map +1 -0
- package/dist/cli-args.d.ts +33 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +102 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-go/auth.go +26 -0
- package/dist/cli-go/config.go +192 -0
- package/dist/cli-go/errors.go +75 -0
- package/dist/cli-go/multipart.go +79 -0
- package/dist/cli-go/output.go +163 -0
- package/dist/cli-go/root.go +30 -0
- package/dist/cli-go/stream.go +88 -0
- package/dist/cli-go/version.go +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +209 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/defaults.js +48 -0
- package/dist/client/defaults.js.map +1 -0
- package/dist/client/error.d.ts +35 -0
- package/dist/client/error.d.ts.map +1 -1
- package/dist/client/error.js +71 -0
- package/dist/client/error.js.map +1 -0
- package/dist/client/http.d.ts +67 -8
- package/dist/client/http.d.ts.map +1 -1
- package/dist/client/http.js +571 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +95 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/path.d.ts +26 -0
- package/dist/client/path.d.ts.map +1 -0
- package/dist/client/path.js +163 -0
- package/dist/client/path.js.map +1 -0
- package/dist/client/redirect-policy.d.ts +12 -0
- package/dist/client/redirect-policy.d.ts.map +1 -0
- package/dist/client/redirect-policy.js +20 -0
- package/dist/client/redirect-policy.js.map +1 -0
- package/dist/client/sdk.d.ts +36 -0
- package/dist/client/sdk.d.ts.map +1 -1
- package/dist/client/sdk.js +292 -0
- package/dist/client/sdk.js.map +1 -0
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.d.ts.map +1 -1
- package/dist/client/sse.js +129 -0
- package/dist/client/sse.js.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/ws.d.ts +9 -2
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js +112 -0
- package/dist/client/ws.js.map +1 -0
- package/dist/client-go/errors.go +236 -0
- package/dist/client-go/export.go +44 -0
- package/dist/client-go/invalidation.go +371 -0
- package/dist/client-go/nullable.go +62 -0
- package/dist/client-go/realtime.go +354 -0
- package/dist/client-go/result.go +20 -0
- package/dist/client-go/runtime.go +975 -0
- package/dist/client-go/sse.go +159 -0
- package/dist/client-go/transport.go +495 -0
- package/dist/client-go/ws.go +128 -0
- package/dist/client-info.d.ts +36 -0
- package/dist/client-info.d.ts.map +1 -0
- package/dist/client-info.js +53 -0
- package/dist/client-info.js.map +1 -0
- package/dist/client-mcp/README.md +10 -0
- package/dist/client-mcp/server.ts +221 -0
- package/dist/client-mcp/types.ts +19 -0
- package/dist/client-python/_errors.py +115 -0
- package/dist/client-python/_invalidation.py +494 -0
- package/dist/client-python/_realtime.py +179 -0
- package/dist/client-python/_runtime.py +860 -0
- package/dist/client-python/_sse.py +155 -0
- package/dist/client-python/_transport.py +366 -0
- package/dist/client-python/_ws.py +159 -0
- package/dist/client-python/py.typed +0 -0
- package/dist/client-rust/errors.rs +220 -0
- package/dist/client-rust/invalidation.rs +412 -0
- package/dist/client-rust/invalidation_sync.rs +93 -0
- package/dist/client-rust/nullable.rs +17 -0
- package/dist/client-rust/realtime.rs +702 -0
- package/dist/client-rust/result.rs +22 -0
- package/dist/client-rust/runtime.rs +980 -0
- package/dist/client-rust/runtime_sync.rs +484 -0
- package/dist/client-rust/sse.rs +188 -0
- package/dist/client-rust/ws.rs +167 -0
- package/dist/codegen-go-cli.d.ts.map +1 -1
- package/dist/codegen-go-cli.js +964 -0
- package/dist/codegen-go-cli.js.map +1 -0
- package/dist/codegen-go.d.ts +15 -3
- package/dist/codegen-go.d.ts.map +1 -1
- package/dist/codegen-go.js +872 -0
- package/dist/codegen-go.js.map +1 -0
- package/dist/codegen-hash.js +9 -0
- package/dist/codegen-hash.js.map +1 -0
- package/dist/codegen-ir.d.ts +69 -2
- package/dist/codegen-ir.d.ts.map +1 -1
- package/dist/codegen-ir.js +661 -0
- package/dist/codegen-ir.js.map +1 -0
- package/dist/codegen-lang.d.ts +65 -0
- package/dist/codegen-lang.d.ts.map +1 -0
- package/dist/codegen-lang.js +526 -0
- package/dist/codegen-lang.js.map +1 -0
- package/dist/codegen-load.d.ts +31 -0
- package/dist/codegen-load.d.ts.map +1 -0
- package/dist/codegen-load.js +138 -0
- package/dist/codegen-load.js.map +1 -0
- package/dist/codegen-loaders.js +55 -0
- package/dist/codegen-loaders.js.map +1 -0
- package/dist/codegen-mcp.d.ts.map +1 -1
- package/dist/codegen-mcp.js +368 -0
- package/dist/codegen-mcp.js.map +1 -0
- package/dist/codegen-python.d.ts +13 -1
- package/dist/codegen-python.d.ts.map +1 -1
- package/dist/codegen-python.js +828 -0
- package/dist/codegen-python.js.map +1 -0
- package/dist/codegen-route-tree-intern.d.ts +15 -5
- package/dist/codegen-route-tree-intern.d.ts.map +1 -1
- package/dist/codegen-route-tree-intern.js +221 -0
- package/dist/codegen-route-tree-intern.js.map +1 -0
- package/dist/codegen-rust.d.ts.map +1 -1
- package/dist/codegen-rust.js +1293 -0
- package/dist/codegen-rust.js.map +1 -0
- package/dist/codegen-sanitize.d.ts.map +1 -1
- package/dist/codegen-sanitize.js +56 -0
- package/dist/codegen-sanitize.js.map +1 -0
- package/dist/codegen-schema-naming.d.ts +5 -0
- package/dist/codegen-schema-naming.d.ts.map +1 -1
- package/dist/codegen-schema-naming.js +150 -0
- package/dist/codegen-schema-naming.js.map +1 -0
- package/dist/codegen-sdk-model.d.ts +71 -0
- package/dist/codegen-sdk-model.d.ts.map +1 -0
- package/dist/codegen-sdk-model.js +179 -0
- package/dist/codegen-sdk-model.js.map +1 -0
- package/dist/codegen.d.ts +41 -23
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +4610 -0
- package/dist/codegen.js.map +1 -0
- package/dist/context.d.ts +36 -6
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +242 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie-sign.d.ts +16 -2
- package/dist/cookie-sign.d.ts.map +1 -1
- package/dist/cookie-sign.js +104 -0
- package/dist/cookie-sign.js.map +1 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/cookie.d.ts.map +1 -1
- package/dist/cookie.js +138 -0
- package/dist/cookie.js.map +1 -0
- package/dist/cors.d.ts +1 -0
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js +87 -0
- package/dist/cors.js.map +1 -0
- package/dist/crypto.d.ts +6 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +38 -0
- package/dist/crypto.js.map +1 -0
- package/dist/csrf.d.ts +19 -0
- package/dist/csrf.d.ts.map +1 -1
- package/dist/csrf.js +79 -0
- package/dist/csrf.js.map +1 -0
- package/dist/curl-logger.d.ts +14 -3
- package/dist/curl-logger.d.ts.map +1 -1
- package/dist/curl-logger.js +199 -0
- package/dist/curl-logger.js.map +1 -0
- package/dist/detect-runtime.js +13 -0
- package/dist/detect-runtime.js.map +1 -0
- package/dist/dict.d.ts +11 -0
- package/dist/dict.d.ts.map +1 -0
- package/dist/dict.js +13 -0
- package/dist/dict.js.map +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +44 -0
- package/dist/error.js.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/etag.d.ts +2 -0
- package/dist/etag.d.ts.map +1 -1
- package/dist/etag.js +82 -0
- package/dist/etag.js.map +1 -0
- package/dist/feature-detect.d.ts +25 -0
- package/dist/feature-detect.d.ts.map +1 -0
- package/dist/feature-detect.js +144 -0
- package/dist/feature-detect.js.map +1 -0
- package/dist/feature-load.js +36 -0
- package/dist/feature-load.js.map +1 -0
- package/dist/feature-slots.d.ts +44 -0
- package/dist/feature-slots.d.ts.map +1 -0
- package/dist/feature-slots.js +48 -0
- package/dist/feature-slots.js.map +1 -0
- package/dist/fetch-request.d.ts +10 -0
- package/dist/fetch-request.d.ts.map +1 -0
- package/dist/fetch-request.js +13 -0
- package/dist/fetch-request.js.map +1 -0
- package/dist/gen-process.d.ts +15 -0
- package/dist/gen-process.d.ts.map +1 -0
- package/dist/gen-process.js +43 -0
- package/dist/gen-process.js.map +1 -0
- package/dist/gen-write.d.ts +19 -0
- package/dist/gen-write.d.ts.map +1 -0
- package/dist/gen-write.js +120 -0
- package/dist/gen-write.js.map +1 -0
- package/dist/glob.d.ts +21 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +115 -0
- package/dist/glob.js.map +1 -0
- package/dist/go-type-emitter.d.ts +18 -4
- package/dist/go-type-emitter.d.ts.map +1 -1
- package/dist/go-type-emitter.js +594 -0
- package/dist/go-type-emitter.js.map +1 -0
- package/dist/honey-response.d.ts.map +1 -1
- package/dist/honey-response.js +338 -0
- package/dist/honey-response.js.map +1 -0
- package/dist/i18n-slot.d.ts +8 -0
- package/dist/i18n-slot.d.ts.map +1 -1
- package/dist/i18n-slot.js +14 -0
- package/dist/i18n-slot.js.map +1 -0
- package/dist/i18n.d.ts +3 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +250 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +408 -148
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2324 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +140 -0
- package/dist/init.js.map +1 -0
- package/dist/input.js +4 -0
- package/dist/input.js.map +1 -0
- package/dist/invalidate-check.d.ts +11 -0
- package/dist/invalidate-check.d.ts.map +1 -1
- package/dist/invalidate-check.js +163 -0
- package/dist/invalidate-check.js.map +1 -0
- package/dist/invoke-user.d.ts +35 -0
- package/dist/invoke-user.d.ts.map +1 -0
- package/dist/invoke-user.js +73 -0
- package/dist/invoke-user.js.map +1 -0
- package/dist/ip-restrict.d.ts +13 -1
- package/dist/ip-restrict.d.ts.map +1 -1
- package/dist/ip-restrict.js +60 -0
- package/dist/ip-restrict.js.map +1 -0
- package/dist/ip.d.ts +39 -0
- package/dist/ip.d.ts.map +1 -0
- package/dist/ip.js +209 -0
- package/dist/ip.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +113 -0
- package/dist/logger.js.map +1 -0
- package/dist/media-type.d.ts +30 -0
- package/dist/media-type.d.ts.map +1 -0
- package/dist/media-type.js +97 -0
- package/dist/media-type.js.map +1 -0
- package/dist/meta-spec-merge.d.ts +32 -0
- package/dist/meta-spec-merge.d.ts.map +1 -0
- package/dist/meta-spec-merge.js +68 -0
- package/dist/meta-spec-merge.js.map +1 -0
- package/dist/meta-spec.d.ts +16 -3
- package/dist/meta-spec.d.ts.map +1 -1
- package/dist/meta-spec.js +535 -0
- package/dist/meta-spec.js.map +1 -0
- package/dist/middleware.d.ts +23 -6
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +253 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node-request.d.ts +57 -12
- package/dist/node-request.d.ts.map +1 -1
- package/dist/node-request.js +407 -0
- package/dist/node-request.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +396 -0
- package/dist/node.js.map +1 -0
- package/dist/openapi/collect.d.ts +6 -3
- package/dist/openapi/collect.d.ts.map +1 -1
- package/dist/openapi/collect.js +30 -0
- package/dist/openapi/collect.js.map +1 -0
- package/dist/openapi/docs-page.d.ts +42 -0
- package/dist/openapi/docs-page.d.ts.map +1 -0
- package/dist/openapi/docs-page.js +80 -0
- package/dist/openapi/docs-page.js.map +1 -0
- package/dist/openapi/document.d.ts +4 -0
- package/dist/openapi/document.d.ts.map +1 -1
- package/dist/openapi/document.js +785 -0
- package/dist/openapi/document.js.map +1 -0
- package/dist/openapi/json-schema-slot.d.ts +7 -0
- package/dist/openapi/json-schema-slot.d.ts.map +1 -1
- package/dist/openapi/json-schema-slot.js +38 -0
- package/dist/openapi/json-schema-slot.js.map +1 -0
- package/dist/openapi/register.d.ts.map +1 -1
- package/dist/openapi/register.js +20 -0
- package/dist/openapi/register.js.map +1 -0
- package/dist/openapi/scalar.d.ts +3 -1
- package/dist/openapi/scalar.d.ts.map +1 -1
- package/dist/openapi/scalar.js +21 -0
- package/dist/openapi/scalar.js.map +1 -0
- package/dist/openapi/serve.d.ts +9 -0
- package/dist/openapi/serve.d.ts.map +1 -0
- package/dist/openapi/serve.js +36 -0
- package/dist/openapi/serve.js.map +1 -0
- package/dist/openapi/spec-factory.d.ts +30 -1
- package/dist/openapi/spec-factory.d.ts.map +1 -1
- package/dist/openapi/spec-factory.js +52 -0
- package/dist/openapi/spec-factory.js.map +1 -0
- package/dist/openapi/spec.d.ts +14 -5
- package/dist/openapi/spec.d.ts.map +1 -1
- package/dist/openapi/spec.js +50 -0
- package/dist/openapi/spec.js.map +1 -0
- package/dist/openapi/swagger.d.ts +3 -1
- package/dist/openapi/swagger.d.ts.map +1 -1
- package/dist/openapi/swagger.js +22 -0
- package/dist/openapi/swagger.js.map +1 -0
- package/dist/pattern.d.ts +71 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +239 -0
- package/dist/pattern.js.map +1 -0
- package/dist/peer.d.ts +17 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +50 -0
- package/dist/peer.js.map +1 -0
- package/dist/plugin.d.ts +67 -16
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +649 -0
- package/dist/plugin.js.map +1 -0
- package/dist/powered-by.d.ts.map +1 -1
- package/dist/powered-by.js +11 -0
- package/dist/powered-by.js.map +1 -0
- package/dist/pretty-json.d.ts.map +1 -1
- package/dist/pretty-json.js +47 -0
- package/dist/pretty-json.js.map +1 -0
- package/dist/producer-stream.d.ts +47 -0
- package/dist/producer-stream.d.ts.map +1 -0
- package/dist/producer-stream.js +267 -0
- package/dist/producer-stream.js.map +1 -0
- package/dist/proxy.d.ts +47 -8
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +274 -0
- package/dist/proxy.js.map +1 -0
- package/dist/python-type-emitter.d.ts +14 -1
- package/dist/python-type-emitter.d.ts.map +1 -1
- package/dist/python-type-emitter.js +157 -0
- package/dist/python-type-emitter.js.map +1 -0
- package/dist/realtime/bus.d.ts +17 -1
- package/dist/realtime/bus.d.ts.map +1 -1
- package/dist/realtime/bus.js +112 -0
- package/dist/realtime/bus.js.map +1 -0
- package/dist/realtime/register.d.ts +3 -0
- package/dist/realtime/register.d.ts.map +1 -0
- package/dist/realtime/register.js +10 -0
- package/dist/realtime/register.js.map +1 -0
- package/dist/realtime/route.d.ts +65 -26
- package/dist/realtime/route.d.ts.map +1 -1
- package/dist/realtime/route.js +72 -0
- package/dist/realtime/route.js.map +1 -0
- package/dist/realtime/server.d.ts +43 -0
- package/dist/realtime/server.d.ts.map +1 -0
- package/dist/realtime/server.js +255 -0
- package/dist/realtime/server.js.map +1 -0
- package/dist/request-id.d.ts +7 -0
- package/dist/request-id.d.ts.map +1 -1
- package/dist/request-id.js +16 -0
- package/dist/request-id.js.map +1 -0
- package/dist/request-limits.d.ts +3 -0
- package/dist/request-limits.d.ts.map +1 -0
- package/dist/request-limits.js +3 -0
- package/dist/request-limits.js.map +1 -0
- package/dist/request-path.d.ts +33 -0
- package/dist/request-path.d.ts.map +1 -0
- package/dist/request-path.js +178 -0
- package/dist/request-path.js.map +1 -0
- package/dist/request-to-curl.d.ts +7 -1
- package/dist/request-to-curl.d.ts.map +1 -1
- package/dist/request-to-curl.js +48 -0
- package/dist/request-to-curl.js.map +1 -0
- package/dist/response.d.ts +25 -2
- package/dist/response.d.ts.map +1 -1
- package/dist/response.js +313 -0
- package/dist/response.js.map +1 -0
- package/dist/rust-type-emitter.d.ts +14 -2
- package/dist/rust-type-emitter.d.ts.map +1 -1
- package/dist/rust-type-emitter.js +463 -0
- package/dist/rust-type-emitter.js.map +1 -0
- package/dist/secure-headers.d.ts.map +1 -1
- package/dist/secure-headers.js +53 -0
- package/dist/secure-headers.js.map +1 -0
- package/dist/serve-register.d.ts +4 -0
- package/dist/serve-register.d.ts.map +1 -1
- package/dist/serve-register.js +9 -0
- package/dist/serve-register.js.map +1 -0
- package/dist/serve-slot.d.ts +3 -0
- package/dist/serve-slot.d.ts.map +1 -1
- package/dist/serve-slot.js +52 -0
- package/dist/serve-slot.js.map +1 -0
- package/dist/serve.d.ts +18 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +157 -0
- package/dist/serve.js.map +1 -0
- package/dist/server-timing.d.ts.map +1 -1
- package/dist/server-timing.js +51 -0
- package/dist/server-timing.js.map +1 -0
- package/dist/static.d.ts +9 -0
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +102 -0
- package/dist/static.js.map +1 -0
- package/dist/telemetry/otel.d.ts +2 -0
- package/dist/telemetry/otel.d.ts.map +1 -1
- package/dist/telemetry/otel.js +135 -0
- package/dist/telemetry/otel.js.map +1 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +124 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeout.d.ts +6 -0
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +36 -0
- package/dist/timeout.js.map +1 -0
- package/dist/tree.d.ts +133 -36
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +437 -0
- package/dist/tree.js.map +1 -0
- package/dist/trust.d.ts +21 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +100 -0
- package/dist/trust.js.map +1 -0
- package/dist/ts-type-emitter.d.ts.map +1 -1
- package/dist/ts-type-emitter.js +150 -0
- package/dist/ts-type-emitter.js.map +1 -0
- package/dist/type-emitter.d.ts +16 -2
- package/dist/type-emitter.d.ts.map +1 -1
- package/dist/type-emitter.js +729 -0
- package/dist/type-emitter.js.map +1 -0
- package/dist/type-extractor.d.ts +8 -4
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +819 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +96 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +14 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +335 -0
- package/dist/validation.js.map +1 -0
- package/dist/with-headers.d.ts +15 -0
- package/dist/with-headers.d.ts.map +1 -0
- package/dist/with-headers.js +45 -0
- package/dist/with-headers.js.map +1 -0
- package/dist/ws/bun.d.ts +11 -5
- package/dist/ws/bun.d.ts.map +1 -1
- package/dist/ws/bun.js +82 -0
- package/dist/ws/bun.js.map +1 -0
- package/dist/ws/cloudflare.d.ts +25 -3
- package/dist/ws/cloudflare.d.ts.map +1 -1
- package/dist/ws/cloudflare.js +117 -0
- package/dist/ws/cloudflare.js.map +1 -0
- package/dist/ws/deno.d.ts +4 -4
- package/dist/ws/deno.d.ts.map +1 -1
- package/dist/ws/deno.js +133 -0
- package/dist/ws/deno.js.map +1 -0
- package/dist/ws/node.d.ts +20 -3
- package/dist/ws/node.d.ts.map +1 -1
- package/dist/ws/node.js +173 -0
- package/dist/ws/node.js.map +1 -0
- package/dist/ws/session.d.ts +11 -0
- package/dist/ws/session.d.ts.map +1 -0
- package/dist/ws/session.js +60 -0
- package/dist/ws/session.js.map +1 -0
- package/dist/ws/shared.d.ts +65 -0
- package/dist/ws/shared.d.ts.map +1 -0
- package/dist/ws/shared.js +103 -0
- package/dist/ws/shared.js.map +1 -0
- package/dist/ws-origin.d.ts +20 -0
- package/dist/ws-origin.d.ts.map +1 -0
- package/dist/ws-origin.js +41 -0
- package/dist/ws-origin.js.map +1 -0
- package/dist/yaml.d.ts +15 -1
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +101 -0
- package/dist/yaml.js.map +1 -0
- package/docs/invalidate-spec.md +13 -1
- package/docs/meta-spec.md +36 -12
- package/docs/packaging.md +43 -24
- package/docs/regression-matrix/README.md +32 -0
- package/docs/regression-matrix/review.md +44 -0
- package/docs/regression-matrix/ws1-3.md +97 -0
- package/docs/regression-matrix/ws4-8.md +107 -0
- package/docs/regression-matrix/ws9-12.md +266 -0
- package/docs/sdk.md +102 -52
- package/package.json +183 -52
- package/src/accepts.ts +91 -33
- package/src/body-kind.ts +67 -0
- package/src/body-limit.ts +102 -87
- package/src/build/index.ts +104 -54
- package/src/cli-args.ts +123 -0
- package/src/cli-go/auth.go +12 -1
- package/src/cli-go/config.go +29 -2
- package/src/cli-go/errors.go +28 -10
- package/src/cli-go/multipart.go +51 -27
- package/src/cli-go/output.go +58 -4
- package/src/cli-go/root.go +1 -1
- package/src/cli-go/version.go +2 -2
- package/src/cli.ts +154 -148
- package/src/client/error.ts +56 -1
- package/src/client/http.ts +479 -256
- package/src/client/index.ts +55 -17
- package/src/client/path.ts +169 -0
- package/src/client/redirect-policy.ts +19 -0
- package/src/client/sdk.ts +180 -101
- package/src/client/sse.ts +92 -66
- package/src/client/ws.ts +67 -31
- package/src/client-go/errors.go +41 -12
- package/src/client-go/export.go +44 -0
- package/src/client-go/invalidation.go +52 -54
- package/src/client-go/nullable.go +62 -0
- package/src/client-go/realtime.go +147 -110
- package/src/client-go/runtime.go +646 -88
- package/src/client-go/sse.go +104 -94
- package/src/client-go/transport.go +67 -27
- package/src/client-go/ws.go +49 -25
- package/src/client-info.ts +79 -0
- package/src/client-mcp/server.ts +133 -3
- package/src/client-python/_invalidation.py +207 -291
- package/src/client-python/_realtime.py +63 -40
- package/src/client-python/_runtime.py +501 -119
- package/src/client-python/_sse.py +130 -74
- package/src/client-python/_transport.py +37 -9
- package/src/client-python/_ws.py +50 -22
- package/src/client-rust/errors.rs +21 -1
- package/src/client-rust/invalidation.rs +202 -231
- package/src/client-rust/invalidation_sync.rs +16 -229
- package/src/client-rust/nullable.rs +17 -0
- package/src/client-rust/realtime.rs +217 -217
- package/src/client-rust/runtime.rs +602 -177
- package/src/client-rust/runtime_sync.rs +169 -145
- package/src/client-rust/sse.rs +142 -121
- package/src/client-rust/ws.rs +61 -37
- package/src/codegen-go-cli.ts +607 -728
- package/src/codegen-go.ts +710 -770
- package/src/codegen-ir.ts +381 -75
- package/src/codegen-lang.ts +517 -0
- package/src/codegen-load.ts +150 -0
- package/src/codegen-mcp.ts +207 -108
- package/src/codegen-python.ts +684 -816
- package/src/codegen-route-tree-intern.ts +51 -25
- package/src/codegen-rust.ts +946 -1240
- package/src/codegen-sanitize.ts +0 -4
- package/src/codegen-schema-naming.ts +19 -6
- package/src/codegen-sdk-model.ts +228 -0
- package/src/codegen.ts +1488 -1140
- package/src/context.ts +130 -11
- package/src/cookie-sign.ts +85 -16
- package/src/cookie.ts +66 -11
- package/src/cors.ts +55 -54
- package/src/crypto.ts +27 -5
- package/src/csrf.ts +57 -25
- package/src/curl-logger.ts +155 -114
- package/src/dict.ts +12 -0
- package/src/error.ts +4 -2
- package/src/etag.ts +58 -34
- package/src/feature-detect.ts +147 -0
- package/src/feature-slots.ts +70 -0
- package/src/fetch-request.ts +13 -0
- package/src/gen-process.ts +42 -0
- package/src/gen-write.ts +127 -0
- package/src/glob.ts +109 -0
- package/src/go-type-emitter.ts +406 -155
- package/src/honey-response.ts +36 -9
- package/src/i18n-slot.ts +10 -0
- package/src/i18n.ts +98 -23
- package/src/index.ts +1887 -1756
- package/src/init.ts +40 -28
- package/src/invalidate-check.ts +42 -4
- package/src/invoke-user.ts +95 -0
- package/src/ip-restrict.ts +47 -161
- package/src/ip.ts +194 -0
- package/src/logger.ts +65 -36
- package/src/media-type.ts +104 -0
- package/src/meta-spec-merge.ts +80 -0
- package/src/meta-spec.ts +0 -0
- package/src/middleware.ts +112 -12
- package/src/node-request.ts +242 -52
- package/src/node.ts +318 -75
- package/src/openapi/collect.ts +19 -44
- package/src/openapi/docs-page.ts +89 -0
- package/src/openapi/document.ts +503 -224
- package/src/openapi/json-schema-slot.ts +40 -0
- package/src/openapi/register.ts +7 -2
- package/src/openapi/scalar.ts +11 -10
- package/src/openapi/serve.ts +39 -0
- package/src/openapi/spec-factory.ts +50 -1
- package/src/openapi/spec.ts +53 -30
- package/src/openapi/swagger.ts +12 -11
- package/src/pattern.ts +245 -0
- package/src/peer.ts +53 -0
- package/src/plugin.ts +270 -185
- package/src/powered-by.ts +2 -2
- package/src/pretty-json.ts +25 -9
- package/src/producer-stream.ts +296 -0
- package/src/proxy.ts +253 -55
- package/src/python-type-emitter.ts +68 -56
- package/src/realtime/bus.ts +56 -9
- package/src/realtime/register.ts +13 -0
- package/src/realtime/route.ts +129 -80
- package/src/realtime/server.ts +298 -0
- package/src/request-id.ts +13 -3
- package/src/request-limits.ts +2 -0
- package/src/request-path.ts +165 -0
- package/src/request-to-curl.ts +28 -7
- package/src/response.ts +143 -103
- package/src/rust-type-emitter.ts +265 -170
- package/src/secure-headers.ts +8 -4
- package/src/serve-register.ts +4 -0
- package/src/serve-slot.ts +38 -0
- package/src/serve.ts +113 -25
- package/src/server-timing.ts +11 -7
- package/src/static.ts +85 -32
- package/src/telemetry/otel.ts +29 -7
- package/src/testing.ts +76 -14
- package/src/timeout.ts +15 -7
- package/src/tree.ts +405 -333
- package/src/trust.ts +117 -0
- package/src/ts-type-emitter.ts +18 -12
- package/src/type-emitter.ts +227 -89
- package/src/type-extractor.ts +603 -423
- package/src/types.ts +8 -0
- package/src/validation.ts +221 -96
- package/src/with-headers.ts +45 -0
- package/src/ws/bun.ts +53 -15
- package/src/ws/cloudflare.ts +81 -19
- package/src/ws/deno.ts +124 -36
- package/src/ws/node.ts +173 -79
- package/src/ws/session.ts +73 -0
- package/src/ws/shared.ts +140 -0
- package/src/ws-origin.ts +53 -0
- package/src/yaml.ts +45 -22
- package/dist/client-mcp/server.d.ts +0 -10
- package/dist/client-mcp/server.d.ts.map +0 -1
- package/dist/client-mcp/types.d.ts +0 -16
- package/dist/client-mcp/types.d.ts.map +0 -1
- package/dist/cloudflare/realtime.d.ts +0 -30
- package/dist/cloudflare/realtime.d.ts.map +0 -1
- package/dist/realtime/buffer.d.ts +0 -18
- package/dist/realtime/buffer.d.ts.map +0 -1
- package/dist/realtime/longpoll.d.ts +0 -23
- package/dist/realtime/longpoll.d.ts.map +0 -1
- package/dist/realtime/protocol.d.ts +0 -50
- package/dist/realtime/protocol.d.ts.map +0 -1
- package/dist/realtime/runtime.d.ts +0 -74
- package/dist/realtime/runtime.d.ts.map +0 -1
- package/src/cloudflare/realtime.ts +0 -234
- package/src/realtime/buffer.ts +0 -101
- package/src/realtime/longpoll.ts +0 -148
- package/src/realtime/protocol.ts +0 -128
- package/src/realtime/runtime.ts +0 -427
package/src/tree.ts
CHANGED
|
@@ -1,92 +1,146 @@
|
|
|
1
|
+
import type { RouteId, Segment } from "./pattern.ts"
|
|
2
|
+
import { canonical, expandOptional, parsePattern, routeId } from "./pattern.ts"
|
|
1
3
|
import type { HttpMethod, InputSchemasDef, OutputSchemaDef } from "./types.ts"
|
|
2
4
|
import type { WSHandler } from "./ws/cloudflare.ts"
|
|
5
|
+
import type { WSOriginPolicy } from "./ws-origin.ts"
|
|
3
6
|
|
|
4
|
-
export type { HttpMethod }
|
|
7
|
+
export type { HttpMethod, RouteId }
|
|
5
8
|
|
|
6
9
|
export type OutputValidator = (statusKey: string, data: unknown) => Promise<void>
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
type ChainMiddleware = (
|
|
12
|
+
ctx: Record<string, unknown>,
|
|
13
|
+
next: (additions?: Record<string, unknown>) => Promise<Response>,
|
|
14
|
+
) => Promise<Response>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* What a route was registered with, before the app resolves it. Finalize derives the
|
|
18
|
+
* resolved fields (`ek`, `mt`, `mw`, `bek`, `ef`) from these, so they never depend on the
|
|
19
|
+
* order of registration and are recomputed whenever the route graph changes.
|
|
20
|
+
*/
|
|
21
|
+
type RecordSource = {
|
|
22
|
+
/** chain middleware captured at registration (plus any mounting chain) */
|
|
23
|
+
cm?: ChainMiddleware[]
|
|
24
|
+
/** `.context()` values of the registering handle (merged with the mounting handle's) */
|
|
25
|
+
cv?: Record<string, unknown> | null
|
|
26
|
+
/** error keys the route declared itself (`.errors()`, `.boundary()`) */
|
|
27
|
+
dk?: Set<string>
|
|
28
|
+
/** the settings of the app the route was registered on — opaque here */
|
|
29
|
+
own?: unknown
|
|
30
|
+
/** route-level boundary key (`.boundary()`) */
|
|
31
|
+
rb?: string | null
|
|
32
|
+
/** route-level middleware (`.get(...).use(mw)`) */
|
|
33
|
+
rm?: ChainMiddleware[]
|
|
34
|
+
/** explicit meta: chain `.meta()` overlaid by route `.meta()` */
|
|
35
|
+
xm?: Record<string, unknown> | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A route record — everything one registered route runs. Records live in a per-app table
|
|
40
|
+
* keyed by `RouteId`; the tree only holds ids, so two apps that load the same tree never
|
|
41
|
+
* share a record.
|
|
42
|
+
*/
|
|
43
|
+
export type RouteHandler = RecordSource & {
|
|
44
|
+
/** internal route (spec, docs, manifest) — excluded from codegen output, never mounted into a parent */
|
|
12
45
|
_skip?: boolean
|
|
13
46
|
/** boundary error key — wraps undeclared/unexpected errors (null = use default or internal_server_error) */
|
|
14
47
|
bek: string | null
|
|
15
|
-
/**
|
|
48
|
+
/** gateway catch-all: dispatches delegated routes, never matched for a path of its own */
|
|
49
|
+
ca?: boolean
|
|
50
|
+
/** delegated route: a tree leaf with no local handler, served by the gateway catch-all */
|
|
51
|
+
dl?: boolean
|
|
52
|
+
/** resolved error factory subset exposed as `ctx.errors` (omit/`null` = none) */
|
|
16
53
|
ef?: Record<string, (...args: never[]) => unknown> | null
|
|
17
|
-
/** declared error keys
|
|
54
|
+
/** declared error keys — resolved at finalize from the route, its middleware and the app defaults */
|
|
18
55
|
ek: Set<string>
|
|
56
|
+
/** resolved error factory — the route's own app's, or the serving app's */
|
|
57
|
+
fac?: Record<string, (...args: never[]) => unknown> | null
|
|
19
58
|
/** handler function */
|
|
20
59
|
fn: (ctx: unknown) => Response | Promise<Response>
|
|
21
|
-
/**
|
|
60
|
+
/** route identity: `METHOD /canonical/pattern` */
|
|
61
|
+
id?: RouteId
|
|
62
|
+
/** input validation schemas — missing/`null` = no validation */
|
|
22
63
|
iv?: InputSchemasDef | null
|
|
23
64
|
/** route metadata — frozen object, accessible via ctx.meta */
|
|
24
65
|
mt: Record<string, unknown> | null
|
|
25
|
-
/** middleware chain
|
|
26
|
-
mw:
|
|
27
|
-
|
|
28
|
-
ctx: Record<string, unknown>,
|
|
29
|
-
next: (additions?: Record<string, unknown>) => Promise<Response>,
|
|
30
|
-
) => Promise<Response>
|
|
31
|
-
>
|
|
32
|
-
/** output schemas by content-type — intern trees omit; missing/`null` = no validation */
|
|
66
|
+
/** resolved middleware chain, in run order: chain, scoped, route */
|
|
67
|
+
mw: ChainMiddleware[]
|
|
68
|
+
/** output schemas by content-type — missing/`null` = no validation */
|
|
33
69
|
os?: OutputSchemaDef | null
|
|
34
70
|
/** output validator function — validates response body against schema */
|
|
35
71
|
ov?: OutputValidator | null
|
|
36
|
-
/**
|
|
72
|
+
/** canonical route pattern (e.g. "/users/:id") */
|
|
37
73
|
rp?: string
|
|
38
74
|
}
|
|
39
75
|
|
|
40
76
|
export type { OutputSchemaDef }
|
|
41
77
|
|
|
42
|
-
export type WSRouteHandler = {
|
|
78
|
+
export type WSRouteHandler = RecordSource & {
|
|
43
79
|
bek: string | null
|
|
44
80
|
ek: Set<string>
|
|
81
|
+
ef?: Record<string, (...args: never[]) => unknown> | null
|
|
82
|
+
fac?: Record<string, (...args: never[]) => unknown> | null
|
|
45
83
|
fn: WSHandler<unknown>
|
|
84
|
+
id?: RouteId
|
|
46
85
|
iv: InputSchemasDef | null
|
|
47
86
|
mt: Record<string, unknown> | null
|
|
48
|
-
mw:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
) => Promise<Response>
|
|
53
|
-
>
|
|
54
|
-
/** registered ws path pattern — used by scoped-mw prefix filter */
|
|
87
|
+
mw: ChainMiddleware[]
|
|
88
|
+
/** browser origins allowed to open the socket (`.origins()`); `null` = same-origin when credentialed */
|
|
89
|
+
og?: WSOriginPolicy | null
|
|
90
|
+
/** canonical ws path pattern */
|
|
55
91
|
rp: string
|
|
56
92
|
}
|
|
57
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Router topology. Leaves are `RouteId`s, never handler objects: what a leaf runs is looked
|
|
96
|
+
* up in the serving app's own route table.
|
|
97
|
+
*/
|
|
58
98
|
export type TreeNode = {
|
|
59
99
|
/** dynamic param child — { n: param name, c: child subtree } */
|
|
60
100
|
d: { c: TreeNode; n: string } | null
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
m: Record<HttpMethod | "ALL", RouteHandler> | null
|
|
65
|
-
/** static children — maps literal path segment to child subtree */
|
|
101
|
+
/** method → route id at this exact path */
|
|
102
|
+
m: Record<string, RouteId> | null
|
|
103
|
+
/** static children — maps literal path segment to child subtree (null-prototype) */
|
|
66
104
|
s: Record<string, TreeNode>
|
|
67
|
-
/** wildcard
|
|
68
|
-
w: { m: Record<
|
|
69
|
-
/** websocket
|
|
70
|
-
ws:
|
|
105
|
+
/** wildcard — { n: wildcard name, m: method → route id } */
|
|
106
|
+
w: { m: Record<string, RouteId>; n: string } | null
|
|
107
|
+
/** websocket route id for this path */
|
|
108
|
+
ws: RouteId | null
|
|
71
109
|
}
|
|
72
110
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
111
|
+
/** Descriptive data for one route in a tree. Generated files carry only this. */
|
|
112
|
+
export type RouteEntry = {
|
|
113
|
+
/** boundary error key */
|
|
114
|
+
bek?: string | null
|
|
115
|
+
/** declared error keys */
|
|
116
|
+
ek?: readonly string[]
|
|
117
|
+
/** live record — only in `app.toRouteTree()` snapshots, never in generated files */
|
|
118
|
+
h?: RouteHandler | WSRouteHandler
|
|
119
|
+
/** internal route (spec, docs, manifest) */
|
|
120
|
+
i?: 1 | true
|
|
121
|
+
/** input schemas — only in live snapshots */
|
|
122
|
+
iv?: InputSchemasDef | null
|
|
123
|
+
/** route meta */
|
|
124
|
+
mt?: Record<string, unknown> | null
|
|
125
|
+
/** output schemas — only in live snapshots */
|
|
126
|
+
os?: OutputSchemaDef | null
|
|
77
127
|
}
|
|
78
128
|
|
|
79
129
|
export type MatchResult =
|
|
80
130
|
| { allowed: HttpMethod[]; matched: false }
|
|
81
|
-
| {
|
|
131
|
+
| { id: RouteId; matched: true; params: Record<string, string> }
|
|
82
132
|
| null
|
|
83
133
|
|
|
84
134
|
export interface RouteMeta {}
|
|
85
135
|
|
|
136
|
+
/** Format of route trees: topology + per-route data. */
|
|
137
|
+
export const ROUTE_TREE_VERSION = 2
|
|
138
|
+
|
|
86
139
|
export type RouteTree = {
|
|
87
|
-
handlers?: Record<string, RouteHandler>
|
|
88
140
|
meta: Record<string, RouteMeta>
|
|
89
141
|
root: TreeNode
|
|
142
|
+
routes: Record<RouteId, RouteEntry>
|
|
143
|
+
v?: number
|
|
90
144
|
}
|
|
91
145
|
|
|
92
146
|
function decode(s: string): string {
|
|
@@ -98,385 +152,403 @@ function decode(s: string): string {
|
|
|
98
152
|
}
|
|
99
153
|
}
|
|
100
154
|
|
|
155
|
+
function dict<T>(): Record<string, T> {
|
|
156
|
+
return Object.create(null) as Record<string, T>
|
|
157
|
+
}
|
|
158
|
+
|
|
101
159
|
export function createNode(): TreeNode {
|
|
102
|
-
return {
|
|
103
|
-
d: null,
|
|
104
|
-
m: null,
|
|
105
|
-
s: Object.create(null) as Record<string, TreeNode>,
|
|
106
|
-
w: null,
|
|
107
|
-
ws: null,
|
|
108
|
-
}
|
|
160
|
+
return { d: null, m: null, s: dict<TreeNode>(), w: null, ws: null }
|
|
109
161
|
}
|
|
110
162
|
|
|
111
|
-
function
|
|
112
|
-
return
|
|
163
|
+
function describe(method: string, path: string): string {
|
|
164
|
+
return method === "WS" ? `WebSocket route ${path}` : `route: ${method} ${path}`
|
|
113
165
|
}
|
|
114
166
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Put `id` at the leaf for `segments` × `method` (`"WS"` for the websocket slot). The same id
|
|
169
|
+
* already there is a no-op; any other id is a duplicate. Optional params must be expanded by
|
|
170
|
+
* the caller — every call places exactly one leaf.
|
|
171
|
+
*/
|
|
172
|
+
export function insertLeaf(root: TreeNode, segments: readonly Segment[], method: string, id: RouteId): void {
|
|
173
|
+
const path = canonical(segments)
|
|
118
174
|
let node = root
|
|
119
|
-
|
|
120
175
|
for (const seg of segments) {
|
|
121
|
-
if (seg.
|
|
122
|
-
|
|
123
|
-
if (node.w !== null && node.w.n !==
|
|
124
|
-
throw new Error(`Wildcard name conflict: "${node.w.n}" vs "${
|
|
125
|
-
}
|
|
126
|
-
if (node.w === null) {
|
|
127
|
-
node.w = {
|
|
128
|
-
m: Object.create(null) as Record<HttpMethod | "ALL", RouteHandler>,
|
|
129
|
-
n: name,
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (method in node.w.m) {
|
|
133
|
-
throw new Error(`Duplicate route: ${method} ${path}`)
|
|
176
|
+
if (seg.k === "wildcard") {
|
|
177
|
+
if (method === "WS") throw new Error("Wildcard segments not supported for WebSocket routes")
|
|
178
|
+
if (node.w !== null && node.w.n !== seg.n) {
|
|
179
|
+
throw new Error(`Wildcard name conflict: "${node.w.n}" vs "${seg.n}"`)
|
|
134
180
|
}
|
|
135
|
-
node.w.
|
|
181
|
+
if (node.w === null) node.w = { m: dict<RouteId>(), n: seg.n }
|
|
182
|
+
const prev = node.w.m[method]
|
|
183
|
+
if (prev !== undefined && prev !== id) throw new Error(`Duplicate ${describe(method, path)}`)
|
|
184
|
+
node.w.m[method] = id
|
|
136
185
|
return
|
|
137
186
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (node.d !== null && node.d.n !== name) {
|
|
142
|
-
throw new Error(`Route "${path}": param name conflict — expected ":${node.d.n}" but got ":${name}"`)
|
|
143
|
-
}
|
|
144
|
-
if (node.d === null) {
|
|
145
|
-
node.d = { c: createNode(), n: name }
|
|
187
|
+
if (seg.k === "param") {
|
|
188
|
+
if (node.d !== null && node.d.n !== seg.n) {
|
|
189
|
+
throw new Error(`Route "${path}": param name conflict — expected ":${node.d.n}" but got ":${seg.n}"`)
|
|
146
190
|
}
|
|
147
|
-
|
|
148
|
-
if (seg.endsWith("?")) {
|
|
149
|
-
/* optional param — register handler on parent too */
|
|
150
|
-
if (node.m === null) {
|
|
151
|
-
node.m = Object.create(null) as Record<HttpMethod | "ALL", RouteHandler>
|
|
152
|
-
}
|
|
153
|
-
if (method in node.m) {
|
|
154
|
-
throw new Error(`Duplicate route: ${method} ${path}`)
|
|
155
|
-
}
|
|
156
|
-
node.m[method] = handler
|
|
157
|
-
}
|
|
158
|
-
|
|
191
|
+
if (node.d === null) node.d = { c: createNode(), n: seg.n }
|
|
159
192
|
node = node.d.c
|
|
160
193
|
continue
|
|
161
194
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
node = staticChild
|
|
167
|
-
continue
|
|
195
|
+
let child = node.s[seg.v]
|
|
196
|
+
if (child === undefined) {
|
|
197
|
+
child = createNode()
|
|
198
|
+
node.s[seg.v] = child
|
|
168
199
|
}
|
|
169
|
-
|
|
170
|
-
node.s[seg] = nextNode
|
|
171
|
-
node = nextNode
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/* terminal node */
|
|
175
|
-
if (node.m === null) {
|
|
176
|
-
node.m = Object.create(null) as Record<HttpMethod | "ALL", RouteHandler>
|
|
200
|
+
node = child
|
|
177
201
|
}
|
|
178
|
-
if (method
|
|
179
|
-
throw new Error(`Duplicate route: ${
|
|
202
|
+
if (method === "WS") {
|
|
203
|
+
if (node.ws !== null && node.ws !== id) throw new Error(`Duplicate WebSocket route: ${path}`)
|
|
204
|
+
node.ws = id
|
|
205
|
+
return
|
|
180
206
|
}
|
|
181
|
-
node.m
|
|
207
|
+
if (node.m === null) node.m = dict<RouteId>()
|
|
208
|
+
const prev = node.m[method]
|
|
209
|
+
if (prev !== undefined && prev !== id) throw new Error(`Duplicate ${describe(method, path)}`)
|
|
210
|
+
node.m[method] = id
|
|
182
211
|
}
|
|
183
212
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
):
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return { handler: handlers["GET"], matched: true, params }
|
|
195
|
-
}
|
|
196
|
-
if ("ALL" in handlers) {
|
|
197
|
-
return { handler: handlers["ALL"], matched: true, params }
|
|
213
|
+
/**
|
|
214
|
+
* Place a route's leaves by pattern (an optional last param places two). The leaf holds
|
|
215
|
+
* `id`, by default the route's `RouteId`, which is returned.
|
|
216
|
+
*/
|
|
217
|
+
export function insertRoute(root: TreeNode, method: HttpMethod | "ALL", path: string, id?: RouteId): RouteId {
|
|
218
|
+
const segments = parsePattern(path)
|
|
219
|
+
const rid = id ?? routeId(method, canonical(segments))
|
|
220
|
+
for (const v of expandOptional(segments)) {
|
|
221
|
+
if (findLeaf(root, v, method) !== undefined) throw new Error(`Duplicate route: ${method} ${canonical(v)}`)
|
|
222
|
+
insertLeaf(root, v, method, rid)
|
|
198
223
|
}
|
|
199
|
-
|
|
200
|
-
return { allowed, matched: false }
|
|
224
|
+
return rid
|
|
201
225
|
}
|
|
202
226
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
let end = pos
|
|
214
|
-
while (end < len && path.charCodeAt(end) !== 47) end++
|
|
215
|
-
if (end === pos) {
|
|
216
|
-
pos++
|
|
217
|
-
continue
|
|
218
|
-
}
|
|
219
|
-
const seg = path.substring(pos, end)
|
|
220
|
-
pos = end + 1
|
|
227
|
+
/** Place a websocket route's leaf by pattern; returns its `RouteId`. */
|
|
228
|
+
export function insertWsRoute(root: TreeNode, path: string, id?: RouteId): RouteId {
|
|
229
|
+
const segments = parsePattern(path)
|
|
230
|
+
const rid = id ?? routeId("WS", canonical(segments))
|
|
231
|
+
for (const v of expandOptional(segments)) {
|
|
232
|
+
if (findLeaf(root, v, "WS") !== undefined) throw new Error(`Duplicate WebSocket route: ${canonical(v)}`)
|
|
233
|
+
insertLeaf(root, v, "WS", rid)
|
|
234
|
+
}
|
|
235
|
+
return rid
|
|
236
|
+
}
|
|
221
237
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
238
|
+
/** Exact lookup: the id at the leaf for `segments` × `method`, without matching params against values. */
|
|
239
|
+
export function findLeaf(root: TreeNode, segments: readonly Segment[], method: string): RouteId | undefined {
|
|
240
|
+
let node: TreeNode | undefined = root
|
|
241
|
+
for (const seg of segments) {
|
|
242
|
+
if (node === undefined) return undefined
|
|
243
|
+
if (seg.k === "wildcard") {
|
|
244
|
+
if (node.w === null || node.w.n !== seg.n) return undefined
|
|
245
|
+
return node.w.m[method]
|
|
227
246
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (node.d !== null) {
|
|
231
|
-
params[node.d.n] = decode(seg)
|
|
247
|
+
if (seg.k === "param") {
|
|
248
|
+
if (node.d === null || node.d.n !== seg.n) return undefined
|
|
232
249
|
node = node.d.c
|
|
233
250
|
continue
|
|
234
251
|
}
|
|
235
|
-
|
|
236
|
-
/* 3. wildcard — consumes remaining */
|
|
237
|
-
if (node.w !== null) {
|
|
238
|
-
params[node.w.n] = decode(pos <= len ? `${seg}/${path.substring(pos)}` : seg)
|
|
239
|
-
return resolveMethod(node.w.m, method, params)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return null
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/* terminal node */
|
|
246
|
-
if (node.m !== null) {
|
|
247
|
-
return resolveMethod(node.m, method, params)
|
|
252
|
+
node = node.s[seg.v]
|
|
248
253
|
}
|
|
254
|
+
if (node === undefined) return undefined
|
|
255
|
+
if (method === "WS") return node.ws ?? undefined
|
|
256
|
+
return node.m?.[method]
|
|
257
|
+
}
|
|
249
258
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
function pickMethod(m: Record<string, RouteId>, method: string): RouteId | undefined {
|
|
260
|
+
const exact = m[method]
|
|
261
|
+
if (exact !== undefined) return exact
|
|
262
|
+
if (method === "HEAD") {
|
|
263
|
+
const get = m["GET"]
|
|
264
|
+
if (get !== undefined) return get
|
|
254
265
|
}
|
|
266
|
+
return m["ALL"]
|
|
267
|
+
}
|
|
255
268
|
|
|
256
|
-
|
|
269
|
+
type MatchState = {
|
|
270
|
+
allowed: Set<string> | null
|
|
271
|
+
method: string
|
|
272
|
+
names: string[]
|
|
273
|
+
path: string
|
|
274
|
+
values: string[]
|
|
257
275
|
}
|
|
258
276
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
277
|
+
function addAllowed(st: MatchState, m: Record<string, RouteId>): void {
|
|
278
|
+
if (st.allowed === null) st.allowed = new Set()
|
|
279
|
+
for (const k in m) if (k !== "ALL") st.allowed.add(k)
|
|
280
|
+
}
|
|
263
281
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
282
|
+
/*
|
|
283
|
+
* Depth-first in precedence order — static, then param, then wildcard — retrying the next
|
|
284
|
+
* branch on a dead end and on a method miss. A trie node sits at one depth, so each node is
|
|
285
|
+
* visited at most once per request: the walk is linear in the route set, and recursion depth
|
|
286
|
+
* is bounded by the deepest registered route, not by the request path.
|
|
287
|
+
*/
|
|
288
|
+
function walk(node: TreeNode, pos: number, st: MatchState): RouteId | undefined {
|
|
289
|
+
const path = st.path
|
|
290
|
+
const len = path.length
|
|
291
|
+
while (pos < len && path.charCodeAt(pos) === 47) pos++
|
|
292
|
+
if (pos >= len) {
|
|
293
|
+
if (node.m !== null) {
|
|
294
|
+
const id = pickMethod(node.m, st.method)
|
|
295
|
+
if (id !== undefined) return id
|
|
296
|
+
addAllowed(st, node.m)
|
|
267
297
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (node.d === null) {
|
|
275
|
-
node.d = { c: createNode(), n: name }
|
|
298
|
+
if (node.w !== null) {
|
|
299
|
+
const id = pickMethod(node.w.m, st.method)
|
|
300
|
+
if (id !== undefined) {
|
|
301
|
+
st.names.push(node.w.n)
|
|
302
|
+
st.values.push("")
|
|
303
|
+
return id
|
|
276
304
|
}
|
|
277
|
-
|
|
278
|
-
continue
|
|
305
|
+
addAllowed(st, node.w.m)
|
|
279
306
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
307
|
+
return undefined
|
|
308
|
+
}
|
|
309
|
+
let end = path.indexOf("/", pos)
|
|
310
|
+
if (end === -1) end = len
|
|
311
|
+
const seg = path.substring(pos, end)
|
|
312
|
+
|
|
313
|
+
const staticChild = node.s[seg]
|
|
314
|
+
if (staticChild !== undefined) {
|
|
315
|
+
const id = walk(staticChild, end, st)
|
|
316
|
+
if (id !== undefined) return id
|
|
317
|
+
}
|
|
318
|
+
if (node.d !== null) {
|
|
319
|
+
const depth = st.names.length
|
|
320
|
+
st.names.push(node.d.n)
|
|
321
|
+
st.values.push(decode(seg))
|
|
322
|
+
const id = walk(node.d.c, end, st)
|
|
323
|
+
if (id !== undefined) return id
|
|
324
|
+
st.names.length = depth
|
|
325
|
+
st.values.length = depth
|
|
326
|
+
}
|
|
327
|
+
if (node.w !== null) {
|
|
328
|
+
const id = pickMethod(node.w.m, st.method)
|
|
329
|
+
if (id !== undefined) {
|
|
330
|
+
st.names.push(node.w.n)
|
|
331
|
+
st.values.push(decode(path.substring(pos)))
|
|
332
|
+
return id
|
|
285
333
|
}
|
|
286
|
-
|
|
287
|
-
node.s[seg] = nextNode
|
|
288
|
-
node = nextNode
|
|
334
|
+
addAllowed(st, node.w.m)
|
|
289
335
|
}
|
|
336
|
+
return undefined
|
|
337
|
+
}
|
|
290
338
|
|
|
291
|
-
|
|
292
|
-
|
|
339
|
+
function buildParams(st: MatchState): Record<string, string> {
|
|
340
|
+
const params = dict<string>()
|
|
341
|
+
for (let i = 0; i < st.names.length; i++) params[st.names[i]] = st.values[i]
|
|
342
|
+
return params
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function matchRoute(root: TreeNode, method: HttpMethod | string, path: string): MatchResult {
|
|
346
|
+
const st: MatchState = { allowed: null, method, names: [], path, values: [] }
|
|
347
|
+
const id = walk(root, 0, st)
|
|
348
|
+
if (id !== undefined) return { id, matched: true, params: buildParams(st) }
|
|
349
|
+
if (st.allowed !== null && st.allowed.size > 0) {
|
|
350
|
+
return { allowed: [...st.allowed] as HttpMethod[], matched: false }
|
|
293
351
|
}
|
|
294
|
-
|
|
352
|
+
return null
|
|
295
353
|
}
|
|
296
354
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
path: string,
|
|
300
|
-
): { handler: WSRouteHandler; params: Record<string, string> } | null {
|
|
301
|
-
let node = root
|
|
302
|
-
const params: Record<string, string> = Object.create(null)
|
|
355
|
+
function walkWs(node: TreeNode, pos: number, st: MatchState): RouteId | undefined {
|
|
356
|
+
const path = st.path
|
|
303
357
|
const len = path.length
|
|
358
|
+
while (pos < len && path.charCodeAt(pos) === 47) pos++
|
|
359
|
+
if (pos >= len) return node.ws ?? undefined
|
|
360
|
+
let end = path.indexOf("/", pos)
|
|
361
|
+
if (end === -1) end = len
|
|
362
|
+
const seg = path.substring(pos, end)
|
|
363
|
+
const staticChild = node.s[seg]
|
|
364
|
+
if (staticChild !== undefined) {
|
|
365
|
+
const id = walkWs(staticChild, end, st)
|
|
366
|
+
if (id !== undefined) return id
|
|
367
|
+
}
|
|
368
|
+
if (node.d !== null) {
|
|
369
|
+
const depth = st.names.length
|
|
370
|
+
st.names.push(node.d.n)
|
|
371
|
+
st.values.push(decode(seg))
|
|
372
|
+
const id = walkWs(node.d.c, end, st)
|
|
373
|
+
if (id !== undefined) return id
|
|
374
|
+
st.names.length = depth
|
|
375
|
+
st.values.length = depth
|
|
376
|
+
}
|
|
377
|
+
return undefined
|
|
378
|
+
}
|
|
304
379
|
|
|
305
|
-
|
|
380
|
+
export function matchWsRoute(root: TreeNode, path: string): { id: RouteId; params: Record<string, string> } | null {
|
|
381
|
+
const st: MatchState = { allowed: null, method: "WS", names: [], path, values: [] }
|
|
382
|
+
const id = walkWs(root, 0, st)
|
|
383
|
+
if (id === undefined) return null
|
|
384
|
+
return { id, params: buildParams(st) }
|
|
385
|
+
}
|
|
306
386
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
const seg = path.substring(pos, end)
|
|
315
|
-
pos = end + 1
|
|
387
|
+
/** True when the tree holds at least one websocket leaf. */
|
|
388
|
+
export function hasWsLeaf(node: TreeNode): boolean {
|
|
389
|
+
if (node.ws !== null) return true
|
|
390
|
+
for (const k in node.s) if (hasWsLeaf(node.s[k])) return true
|
|
391
|
+
return node.d !== null && hasWsLeaf(node.d.c)
|
|
392
|
+
}
|
|
316
393
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
394
|
+
/** Every leaf, with its concrete path pattern (optional params already expanded by insertion). */
|
|
395
|
+
export function forEachLeaf(
|
|
396
|
+
node: TreeNode,
|
|
397
|
+
cb: (method: string, path: string, id: RouteId) => void,
|
|
398
|
+
prefix = "",
|
|
399
|
+
): void {
|
|
400
|
+
if (node.m !== null) for (const method in node.m) cb(method, prefix || "/", node.m[method])
|
|
401
|
+
if (node.ws !== null) cb("WS", prefix || "/", node.ws)
|
|
402
|
+
for (const seg in node.s) forEachLeaf(node.s[seg], cb, `${prefix}/${seg}`)
|
|
403
|
+
if (node.d !== null) forEachLeaf(node.d.c, cb, `${prefix}/:${node.d.n}`)
|
|
404
|
+
if (node.w !== null) {
|
|
405
|
+
const w = node.w.n === "*" ? "*" : `*${node.w.n}`
|
|
406
|
+
for (const method in node.w.m) cb(method, `${prefix}/${w}`, node.w.m[method])
|
|
407
|
+
}
|
|
408
|
+
}
|
|
322
409
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
410
|
+
/** Deep copy into fresh null-prototype nodes. */
|
|
411
|
+
export function cloneTree(node: TreeNode): TreeNode {
|
|
412
|
+
const out = createNode()
|
|
413
|
+
for (const seg in node.s) out.s[seg] = cloneTree(node.s[seg])
|
|
414
|
+
if (node.m !== null) out.m = Object.assign(dict<RouteId>(), node.m)
|
|
415
|
+
if (node.d !== null) out.d = { c: cloneTree(node.d.c), n: node.d.n }
|
|
416
|
+
if (node.w !== null) out.w = { m: Object.assign(dict<RouteId>(), node.w.m), n: node.w.n }
|
|
417
|
+
out.ws = node.ws
|
|
418
|
+
return out
|
|
419
|
+
}
|
|
328
420
|
|
|
329
|
-
|
|
421
|
+
/** Deep-freeze a loaded tree so no app can mutate a module other apps also import. */
|
|
422
|
+
export function freezeTree(node: TreeNode): TreeNode {
|
|
423
|
+
if (Object.isFrozen(node)) return node
|
|
424
|
+
for (const seg in node.s) freezeTree(node.s[seg])
|
|
425
|
+
Object.freeze(node.s)
|
|
426
|
+
if (node.m !== null) Object.freeze(node.m)
|
|
427
|
+
if (node.d !== null) {
|
|
428
|
+
freezeTree(node.d.c)
|
|
429
|
+
Object.freeze(node.d)
|
|
330
430
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
431
|
+
if (node.w !== null) {
|
|
432
|
+
Object.freeze(node.w.m)
|
|
433
|
+
Object.freeze(node.w)
|
|
334
434
|
}
|
|
335
|
-
|
|
336
|
-
return null
|
|
435
|
+
return Object.freeze(node)
|
|
337
436
|
}
|
|
338
437
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Reject a tree whose leaves are not route ids — a `routes.gen.ts` written by an older honey
|
|
440
|
+
* holds handler objects there.
|
|
441
|
+
*/
|
|
442
|
+
export function assertTreeFormat(tree: { root?: unknown; routes?: unknown; v?: unknown }): void {
|
|
443
|
+
const stale = (): never => {
|
|
444
|
+
throw new Error(
|
|
445
|
+
"routeTree(): this route tree was generated by an older honey (leaves are handler objects, not route ids). Run `honey generate`.",
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
if (tree === null || typeof tree !== "object" || tree.root === null || typeof tree.root !== "object") stale()
|
|
449
|
+
if (tree.routes === null || typeof tree.routes !== "object") stale()
|
|
450
|
+
const check = (node: TreeNode): void => {
|
|
451
|
+
if (node.m !== null && typeof node.m === "object")
|
|
452
|
+
for (const k in node.m) if (typeof node.m[k] !== "string") stale()
|
|
453
|
+
if (node.w !== null && typeof node.w === "object") {
|
|
454
|
+
for (const k in node.w.m) if (typeof node.w.m[k] !== "string") stale()
|
|
352
455
|
}
|
|
456
|
+
if (node.ws !== null && node.ws !== undefined && typeof node.ws !== "string") stale()
|
|
457
|
+
if (node.s === null || typeof node.s !== "object") stale()
|
|
458
|
+
for (const seg in node.s) check(node.s[seg])
|
|
459
|
+
if (node.d !== null && node.d !== undefined) check(node.d.c)
|
|
353
460
|
}
|
|
461
|
+
check(tree.root as TreeNode)
|
|
462
|
+
}
|
|
354
463
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (!detached) {
|
|
364
|
-
target.s = Object.assign(Object.create(null) as Record<string, TreeNode>, target.s)
|
|
365
|
-
detached = true
|
|
366
|
-
}
|
|
367
|
-
target.s[seg] = child
|
|
368
|
-
}
|
|
369
|
-
}
|
|
464
|
+
function mergeMethodMap(
|
|
465
|
+
target: Record<string, RouteId>,
|
|
466
|
+
source: Record<string, RouteId>,
|
|
467
|
+
path: string,
|
|
468
|
+
): Record<string, RouteId> {
|
|
469
|
+
for (const method in source) {
|
|
470
|
+
if (target[method] !== undefined) throw new Error(`Merge conflict: duplicate ${method} ${path}`)
|
|
471
|
+
target[method] = source[method]
|
|
370
472
|
}
|
|
473
|
+
return target
|
|
474
|
+
}
|
|
371
475
|
|
|
372
|
-
|
|
373
|
-
if (source.
|
|
374
|
-
if (target.
|
|
476
|
+
function mergeNodes(target: TreeNode, source: TreeNode, path: string): void {
|
|
477
|
+
if (source.m !== null) {
|
|
478
|
+
if (target.m === null) target.m = dict<RouteId>()
|
|
479
|
+
mergeMethodMap(target.m, source.m, path || "/")
|
|
480
|
+
}
|
|
481
|
+
for (const seg in source.s) {
|
|
482
|
+
const child = source.s[seg]
|
|
483
|
+
const existing = target.s[seg]
|
|
484
|
+
if (existing !== undefined) mergeNodes(existing, child, `${path}/${seg}`)
|
|
485
|
+
else target.s[seg] = cloneTree(child)
|
|
486
|
+
}
|
|
487
|
+
if (source.d !== null) {
|
|
488
|
+
if (target.d !== null) {
|
|
375
489
|
if (target.d.n !== source.d.n) {
|
|
376
490
|
throw new Error(`Merge conflict: param name mismatch at ${path}: "${target.d.n}" vs "${source.d.n}"`)
|
|
377
491
|
}
|
|
378
492
|
mergeNodes(target.d.c, source.d.c, `${path}/:${target.d.n}`)
|
|
379
493
|
} else {
|
|
380
|
-
target.d = source.d
|
|
494
|
+
target.d = { c: cloneTree(source.d.c), n: source.d.n }
|
|
381
495
|
}
|
|
382
496
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (source.w !== null && source.w !== undefined) {
|
|
386
|
-
if (target.w !== null && target.w !== undefined) {
|
|
497
|
+
if (source.w !== null) {
|
|
498
|
+
if (target.w !== null) {
|
|
387
499
|
if (target.w.n !== source.w.n) {
|
|
388
500
|
throw new Error(`Merge conflict: wildcard name mismatch at ${path}: "${target.w.n}" vs "${source.w.n}"`)
|
|
389
501
|
}
|
|
390
|
-
|
|
391
|
-
if (method in target.w.m) {
|
|
392
|
-
throw new Error(`Merge conflict: duplicate ${method} ${path}/*${target.w.n}`)
|
|
393
|
-
}
|
|
394
|
-
target.w.m[method as HttpMethod | "ALL"] = handler
|
|
395
|
-
}
|
|
502
|
+
mergeMethodMap(target.w.m, source.w.m, `${path}/*${target.w.n}`)
|
|
396
503
|
} else {
|
|
397
|
-
target.w = source.w
|
|
504
|
+
target.w = { m: Object.assign(dict<RouteId>(), source.w.m), n: source.w.n }
|
|
398
505
|
}
|
|
399
506
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (source.ws !== null && source.ws !== undefined) {
|
|
403
|
-
if (target.ws !== null && target.ws !== undefined) {
|
|
404
|
-
throw new Error(`Merge conflict: duplicate WebSocket handler at ${path}`)
|
|
405
|
-
}
|
|
507
|
+
if (source.ws !== null) {
|
|
508
|
+
if (target.ws !== null) throw new Error(`Merge conflict: duplicate WebSocket handler at ${path || "/"}`)
|
|
406
509
|
target.ws = source.ws
|
|
407
510
|
}
|
|
408
511
|
}
|
|
409
512
|
|
|
410
|
-
|
|
411
|
-
target: Record<HttpMethod | "ALL", RouteHandler>,
|
|
412
|
-
source: Record<HttpMethod | "ALL", RouteHandler>,
|
|
413
|
-
): Record<HttpMethod | "ALL", RouteHandler> {
|
|
414
|
-
let next = target
|
|
415
|
-
for (const [method, handler] of Object.entries(target) as Array<[HttpMethod | "ALL", RouteHandler]>) {
|
|
416
|
-
const from = source[method]
|
|
417
|
-
if (from === undefined) continue
|
|
418
|
-
const iv = handler.iv ?? from.iv
|
|
419
|
-
const os = handler.os ?? from.os
|
|
420
|
-
if (iv === handler.iv && os === handler.os) continue
|
|
421
|
-
/* intern trees share handler objects across routes — copy instead of mutating */
|
|
422
|
-
if (next === target) next = { ...target }
|
|
423
|
-
next[method] = { ...handler, iv, os }
|
|
424
|
-
}
|
|
425
|
-
return next
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Copy input/output schemas from `source` onto matching routes in `target` that lack them.
|
|
430
|
-
* Generate-time only: a gateway app serves an intern tree (no schemas) while its merge source
|
|
431
|
-
* (`mergeTree` over downstream `app.toRouteTree()`) still carries them for OpenAPI.
|
|
432
|
-
*/
|
|
433
|
-
export function overlaySchemas(target: TreeNode, source: TreeNode): void {
|
|
434
|
-
if (target.m !== null && source.m !== null) target.m = withSchemas(target.m, source.m)
|
|
435
|
-
if (target.w !== null && source.w !== null) target.w = { ...target.w, m: withSchemas(target.w.m, source.w.m) }
|
|
436
|
-
for (const [seg, child] of Object.entries(target.s)) {
|
|
437
|
-
const from = source.s[seg]
|
|
438
|
-
if (from !== undefined) overlaySchemas(child, from)
|
|
439
|
-
}
|
|
440
|
-
if (target.d !== null && source.d !== null) overlaySchemas(target.d.c, source.d.c)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/** Merge source tree nodes into target tree (mutates target) */
|
|
513
|
+
/** Merge `source` topology into `target` (mutates target; never shares source nodes). */
|
|
444
514
|
export function mergeInto(target: TreeNode, source: TreeNode): void {
|
|
445
|
-
bumpGeneration(target)
|
|
446
515
|
mergeNodes(target, source, "")
|
|
447
516
|
}
|
|
448
517
|
|
|
449
518
|
type TreeInput = RouteTree | [RouteTree, Record<string, unknown>]
|
|
450
519
|
|
|
451
|
-
function
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
for (const child of Object.values(node.s)) {
|
|
458
|
-
tagHandlerMeta(child, extra)
|
|
459
|
-
}
|
|
460
|
-
if (node.d) tagHandlerMeta(node.d.c, extra)
|
|
461
|
-
if (node.w !== null) {
|
|
462
|
-
for (const handler of Object.values(node.w.m) as RouteHandler[]) {
|
|
463
|
-
handler.mt = handler.mt ? { ...handler.mt, ...extra } : { ...extra }
|
|
464
|
-
}
|
|
465
|
-
}
|
|
520
|
+
function withExtraMeta(entry: RouteEntry, extra: Record<string, unknown> | undefined): RouteEntry {
|
|
521
|
+
if (extra === undefined) return { ...entry }
|
|
522
|
+
const mt = Object.freeze(entry.mt ? { ...entry.mt, ...extra } : { ...extra })
|
|
523
|
+
const out: RouteEntry = { ...entry, mt }
|
|
524
|
+
if (entry.h !== undefined) out.h = { ...entry.h, mt } as RouteHandler
|
|
525
|
+
return out
|
|
466
526
|
}
|
|
467
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Merge several trees into a new one. Inputs are never mutated; extra meta given per tree is
|
|
530
|
+
* applied to copies of its route entries.
|
|
531
|
+
*/
|
|
468
532
|
export function mergeTree(...trees: TreeInput[]): RouteTree {
|
|
469
|
-
const merged: RouteTree = {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
for (const entry of trees) {
|
|
475
|
-
const [tree, extra] = Array.isArray(entry) ? entry : [entry, undefined]
|
|
476
|
-
if (extra) tagHandlerMeta(tree.root, extra)
|
|
533
|
+
const merged: RouteTree = { meta: {}, root: createNode(), routes: dict<RouteEntry>(), v: ROUTE_TREE_VERSION }
|
|
534
|
+
for (const input of trees) {
|
|
535
|
+
const [tree, extra] = Array.isArray(input) ? input : [input, undefined]
|
|
536
|
+
assertTreeFormat(tree)
|
|
477
537
|
mergeNodes(merged.root, tree.root, "")
|
|
538
|
+
for (const id in tree.routes) {
|
|
539
|
+
if (id in merged.routes) throw new Error(`Merge conflict: duplicate ${id}`)
|
|
540
|
+
merged.routes[id] = withExtraMeta(tree.routes[id], extra)
|
|
541
|
+
}
|
|
478
542
|
Object.assign(merged.meta, tree.meta)
|
|
479
543
|
}
|
|
480
|
-
|
|
481
544
|
return merged
|
|
482
545
|
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Copy input/output schemas from `source` onto `target`'s routes that lack them.
|
|
549
|
+
* Generate-time only: a gateway app serves its generated tree (no schemas) while its merge
|
|
550
|
+
* source (`mergeTree` over downstream `app.toRouteTree()`) still carries them for OpenAPI.
|
|
551
|
+
*/
|
|
552
|
+
export function overlaySchemas(target: { _overlaySchemas(source: RouteTree): void }, source: RouteTree): void {
|
|
553
|
+
target._overlaySchemas(source)
|
|
554
|
+
}
|