@lovrozagar/honey 0.6.8 → 0.7.0
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 +776 -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 +562 -424
- 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/type-extractor.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
2
|
-
import { dirname, relative, resolve } from "node:path"
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
2
|
+
import { dirname, join, relative, resolve, sep } from "node:path"
|
|
3
3
|
import type { ts } from "ts-morph"
|
|
4
|
+
import { canonical, expandOptional, joinPatterns, parsePattern } from "./pattern.ts"
|
|
5
|
+
import { quoteKey } from "./type-emitter.ts"
|
|
4
6
|
|
|
5
7
|
export type ExtractedBaseCtx = {
|
|
6
8
|
envType: string
|
|
@@ -8,85 +10,105 @@ export type ExtractedBaseCtx = {
|
|
|
8
10
|
tapsType: string | null
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
/**
|
|
14
|
+
* The tsconfig that governs the entry: the nearest one walking up, but never past the
|
|
15
|
+
* project root (the nearest directory with a package.json) — a stray tsconfig in a parent
|
|
16
|
+
* directory or the home folder is not this project's.
|
|
17
|
+
*/
|
|
11
18
|
function findTsConfig(fromPath: string): string | undefined {
|
|
12
19
|
let dir = dirname(resolve(fromPath))
|
|
13
20
|
let prev = ""
|
|
14
21
|
while (dir !== prev) {
|
|
15
22
|
const p = resolve(dir, "tsconfig.json")
|
|
16
23
|
if (existsSync(p)) return p
|
|
24
|
+
if (existsSync(resolve(dir, "package.json"))) return undefined
|
|
17
25
|
prev = dir
|
|
18
26
|
dir = dirname(dir)
|
|
19
27
|
}
|
|
20
28
|
return undefined
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
/* ts-morph bundles a different TS version — structurally identical at runtime */
|
|
32
|
+
function bridge<T>(value: unknown): T {
|
|
33
|
+
return value as T
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type LoadedEntry = {
|
|
37
|
+
/** The app's type */
|
|
38
|
+
appType: ts.Type
|
|
39
|
+
checker: ts.TypeChecker
|
|
40
|
+
compiler: typeof ts
|
|
41
|
+
/** A node inside the entry, for type lookups that need a location */
|
|
42
|
+
node: ts.Node
|
|
43
|
+
project: import("ts-morph").Project
|
|
44
|
+
sourceFile: import("ts-morph").SourceFile
|
|
45
|
+
}
|
|
46
|
+
|
|
23
47
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
48
|
+
* Load the entry and find the app export: `export const app = …`, any other exported
|
|
49
|
+
* declaration of that name, or `export default honey()` for `exportName: "default"`.
|
|
26
50
|
*/
|
|
27
|
-
|
|
51
|
+
async function loadEntry(options: {
|
|
28
52
|
entryPath: string
|
|
29
53
|
exportName: string
|
|
30
|
-
outputDir?: string
|
|
31
54
|
tsconfigPath?: string
|
|
32
|
-
}): Promise<
|
|
33
|
-
const { Project, ts } = await import("ts-morph")
|
|
34
|
-
|
|
55
|
+
}): Promise<LoadedEntry> {
|
|
56
|
+
const { Project, ts: tsLib } = await import("ts-morph")
|
|
35
57
|
const tsConfigFilePath = options.tsconfigPath ?? findTsConfig(options.entryPath)
|
|
36
|
-
|
|
37
|
-
const project = new Project({
|
|
38
|
-
skipAddingFilesFromTsConfig: true,
|
|
39
|
-
tsConfigFilePath,
|
|
40
|
-
})
|
|
41
|
-
|
|
58
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true, tsConfigFilePath })
|
|
42
59
|
project.addSourceFilesAtPaths(options.entryPath)
|
|
43
60
|
project.resolveSourceFileDependencies()
|
|
44
61
|
|
|
45
62
|
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
46
|
-
const
|
|
47
|
-
|
|
63
|
+
const declarations = sourceFile.getExportedDeclarations().get(options.exportName)
|
|
64
|
+
const decl = declarations?.[0] ?? sourceFile.getVariableDeclaration(options.exportName)
|
|
65
|
+
if (!decl) {
|
|
48
66
|
throw new Error(`Export "${options.exportName}" not found in ${options.entryPath}`)
|
|
49
67
|
}
|
|
50
|
-
|
|
51
|
-
const type = varDecl.getType()
|
|
52
|
-
/* ts-morph bundles different TS version — structurally identical at runtime */
|
|
53
|
-
function bridge<T>(value: unknown): T {
|
|
54
|
-
return value as T
|
|
55
|
-
}
|
|
56
68
|
const checker: ts.TypeChecker = bridge(project.getTypeChecker().compilerObject)
|
|
57
|
-
const node: ts.Node = bridge(
|
|
58
|
-
const
|
|
69
|
+
const node: ts.Node = bridge(decl.compilerNode)
|
|
70
|
+
const compiler: typeof ts = bridge(tsLib)
|
|
71
|
+
/* `export default <expr>` declares an ExportAssignment; its type is the expression's */
|
|
72
|
+
const target = compiler.isExportAssignment(node) ? node.expression : node
|
|
73
|
+
const appType = checker.getTypeAtLocation(target)
|
|
74
|
+
return { appType, checker, compiler, node: target, project, sourceFile }
|
|
75
|
+
}
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Uses ts-morph to extract the middleware context type from a Honey app export.
|
|
79
|
+
* Returns inline type strings so the gen file has zero imports from user code.
|
|
80
|
+
*/
|
|
81
|
+
export async function extractBaseCtx(options: {
|
|
82
|
+
entryPath: string
|
|
83
|
+
exportName: string
|
|
84
|
+
outputDir?: string
|
|
85
|
+
tsconfigPath?: string
|
|
86
|
+
}): Promise<ExtractedBaseCtx> {
|
|
87
|
+
const { appType, checker, compiler, node } = await loadEntry(options)
|
|
88
|
+
const ser = new Serializer(checker, node, compiler)
|
|
89
|
+
|
|
90
|
+
const typeOfProp = (name: string): ts.Type | null => {
|
|
91
|
+
const sym = appType.getProperty(name)
|
|
92
|
+
return sym ? checker.getTypeOfSymbolAtLocation(sym, node) : null
|
|
63
93
|
}
|
|
64
94
|
|
|
65
95
|
/* extract $env */
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
if (envProp) {
|
|
69
|
-
envType = serialize(getType(envProp), checker, node, tsLib)
|
|
70
|
-
}
|
|
96
|
+
const envT = typeOfProp("$env")
|
|
97
|
+
const envType = envT ? ser.serialize(envT) : "Record<string, unknown>"
|
|
71
98
|
|
|
72
99
|
/* extract $ctx and isolate middleware additions */
|
|
73
|
-
const
|
|
74
|
-
if (!
|
|
100
|
+
const ctxType = typeOfProp("$ctx")
|
|
101
|
+
if (!ctxType) {
|
|
75
102
|
throw new Error(`Export "${options.exportName}" has no $ctx — not a Honey instance`)
|
|
76
103
|
}
|
|
77
|
-
|
|
78
|
-
const ctxType = getType(ctxProp)
|
|
79
|
-
const middlewareType = extractMiddleware(ctxType, checker, node, tsLib)
|
|
104
|
+
const middlewareType = propsType(extractMiddlewareProps(ctxType, ser))
|
|
80
105
|
|
|
81
106
|
/* extract $taps — typed tap payloads declared via .taps<T>() */
|
|
82
107
|
let tapsType: string | null = null
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") {
|
|
88
|
-
tapsType = tapsStr
|
|
89
|
-
}
|
|
108
|
+
const tapsT = typeOfProp("$taps")
|
|
109
|
+
if (tapsT) {
|
|
110
|
+
const tapsStr = ser.serialize(tapsT)
|
|
111
|
+
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") tapsType = tapsStr
|
|
90
112
|
}
|
|
91
113
|
|
|
92
114
|
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
@@ -116,47 +138,27 @@ const HONEY_CTX_PROPS = new Set([
|
|
|
116
138
|
"tap",
|
|
117
139
|
])
|
|
118
140
|
|
|
119
|
-
|
|
120
|
-
function resolveTypeRefFromDecl(
|
|
121
|
-
decl: ts.Declaration | undefined,
|
|
122
|
-
checker: ts.TypeChecker,
|
|
123
|
-
compiler: typeof ts,
|
|
124
|
-
): string | undefined {
|
|
125
|
-
if (!decl) return undefined
|
|
126
|
-
const typeRef = (decl as { type?: ts.TypeNode }).type
|
|
127
|
-
if (!typeRef || !compiler.isTypeReferenceNode(typeRef)) return undefined
|
|
128
|
-
const typeName = (typeRef as ts.TypeReferenceNode).typeName
|
|
129
|
-
|
|
130
|
-
let refSym = checker.getSymbolAtLocation(typeName)
|
|
131
|
-
while (refSym && refSym.flags & compiler.SymbolFlags.Alias) {
|
|
132
|
-
refSym = checker.getAliasedSymbol(refSym)
|
|
133
|
-
}
|
|
134
|
-
if (!refSym) return undefined
|
|
135
|
-
|
|
136
|
-
const refDecls = refSym.declarations
|
|
137
|
-
if (!refDecls?.length) return undefined
|
|
138
|
-
|
|
139
|
-
const srcFile = refDecls[0].getSourceFile()
|
|
140
|
-
if (!isExportedFromFile(refSym, srcFile, checker)) return undefined
|
|
141
|
+
type MwPropEntry = { name: string; opt: boolean; type: string }
|
|
141
142
|
|
|
142
|
-
|
|
143
|
+
function propsType(props: readonly MwPropEntry[]): string | null {
|
|
144
|
+
if (props.length === 0) return null
|
|
145
|
+
return `{ ${props.map((p) => `${quoteKey(p.name)}${p.opt ? "?" : ""}: ${p.type}`).join("; ")} }`
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
|
|
148
|
+
/** Is a property name something a type literal can spell and a consumer can see? */
|
|
149
|
+
function isPublicName(name: string): boolean {
|
|
150
|
+
/* `#secret` private fields surface as `__#123@#secret` — never part of a type */
|
|
151
|
+
return !name.startsWith("#") && !name.startsWith("__#")
|
|
152
|
+
}
|
|
146
153
|
|
|
147
|
-
function extractMiddlewareProps(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
node: ts.Node,
|
|
151
|
-
compiler: typeof ts,
|
|
152
|
-
): MwPropEntry[] {
|
|
153
|
-
const allProps = ctxType.getProperties()
|
|
154
|
-
const mwProps = allProps.filter((p) => {
|
|
154
|
+
function extractMiddlewareProps(ctxType: ts.Type, ser: Serializer): MwPropEntry[] {
|
|
155
|
+
const { checker, compiler, node } = ser
|
|
156
|
+
const mwProps = ctxType.getProperties().filter((p) => {
|
|
155
157
|
const name = p.getName()
|
|
156
158
|
if (HONEY_CTX_PROPS.has(name)) return false
|
|
157
159
|
/* skip internal/private properties (underscore-prefixed) */
|
|
158
160
|
if (name.startsWith("_")) return false
|
|
159
|
-
return
|
|
161
|
+
return isPublicName(name)
|
|
160
162
|
})
|
|
161
163
|
|
|
162
164
|
return mwProps.map((p) => {
|
|
@@ -166,14 +168,14 @@ function extractMiddlewareProps(
|
|
|
166
168
|
/* 1. try declaration-site TypeReference (explicit annotation on property or variable) */
|
|
167
169
|
let typeStr: string | undefined
|
|
168
170
|
const mwDecl = p.valueDeclaration ?? p.declarations?.[0]
|
|
169
|
-
typeStr =
|
|
171
|
+
typeStr = ser.refFromDeclaration(mwDecl)
|
|
170
172
|
|
|
171
173
|
/* 1b. shorthand property — trace to the underlying variable's type annotation */
|
|
172
174
|
if (!typeStr && mwDecl && compiler.isShorthandPropertyAssignment(mwDecl)) {
|
|
173
175
|
const varSym = checker.getShorthandAssignmentValueSymbol(mwDecl)
|
|
174
176
|
if (varSym) {
|
|
175
177
|
const varDecl = varSym.valueDeclaration ?? varSym.declarations?.[0]
|
|
176
|
-
typeStr =
|
|
178
|
+
typeStr = ser.refFromDeclaration(varDecl)
|
|
177
179
|
|
|
178
180
|
/* 1c. destructured binding (e.g. const { shardDb } = openShardSession(...))
|
|
179
181
|
* — trace through the function return type to find the original type annotation */
|
|
@@ -189,7 +191,7 @@ function extractMiddlewareProps(
|
|
|
189
191
|
const innerSym = checker.getShorthandAssignmentValueSymbol(retProp.valueDeclaration)
|
|
190
192
|
if (innerSym) {
|
|
191
193
|
const innerDecl = innerSym.valueDeclaration ?? innerSym.declarations?.[0]
|
|
192
|
-
typeStr =
|
|
194
|
+
typeStr = ser.refFromDeclaration(innerDecl)
|
|
193
195
|
}
|
|
194
196
|
}
|
|
195
197
|
}
|
|
@@ -198,32 +200,13 @@ function extractMiddlewareProps(
|
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
/* 2.
|
|
202
|
-
|
|
203
|
-
typeStr = serializeAsRef(t, checker, node, compiler) ?? undefined
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/* 3. structural fallback */
|
|
207
|
-
if (!typeStr) {
|
|
208
|
-
typeStr = serialize(t, checker, node, compiler)
|
|
209
|
-
}
|
|
203
|
+
/* 2. a named type as an import() reference; 3. structural */
|
|
204
|
+
typeStr ??= ser.asRef(t) ?? ser.serialize(t)
|
|
210
205
|
|
|
211
206
|
return { name: p.getName(), opt, type: typeStr }
|
|
212
207
|
})
|
|
213
208
|
}
|
|
214
209
|
|
|
215
|
-
function extractMiddleware(
|
|
216
|
-
ctxType: ts.Type,
|
|
217
|
-
checker: ts.TypeChecker,
|
|
218
|
-
node: ts.Node,
|
|
219
|
-
compiler: typeof ts,
|
|
220
|
-
): string | null {
|
|
221
|
-
const props = extractMiddlewareProps(ctxType, checker, node, compiler)
|
|
222
|
-
if (props.length === 0) return null
|
|
223
|
-
const entries = props.map((p) => `${p.name}${p.opt ? "?" : ""}: ${p.type}`)
|
|
224
|
-
return `{ ${entries.join("; ")} }`
|
|
225
|
-
}
|
|
226
|
-
|
|
227
210
|
/** Check if a symbol is exported from its source file */
|
|
228
211
|
function isExportedFromFile(sym: ts.Symbol, sf: ts.SourceFile, checker: ts.TypeChecker): boolean {
|
|
229
212
|
/* .d.ts module types are always accessible */
|
|
@@ -234,216 +217,225 @@ function isExportedFromFile(sym: ts.Symbol, sf: ts.SourceFile, checker: ts.TypeC
|
|
|
234
217
|
return exports.some((e) => e.getName() === sym.getName())
|
|
235
218
|
}
|
|
236
219
|
|
|
220
|
+
/** Lib types every program has in scope; their names are written as is, never imported. */
|
|
221
|
+
function isLibFile(fileName: string): boolean {
|
|
222
|
+
return /(?:^|[\\/])lib\.[\w.-]*\.d\.ts$/.test(fileName) || /[\\/]typescript[\\/]lib[\\/]/.test(fileName)
|
|
223
|
+
}
|
|
224
|
+
|
|
237
225
|
/**
|
|
238
|
-
*
|
|
239
|
-
* Returns null if the type has no usable named symbol.
|
|
226
|
+
* TypeScript types → type source text.
|
|
240
227
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
228
|
+
* Named types become `import("…").Name<Args>` references when the declaring module exports
|
|
229
|
+
* them; everything else is written structurally. The structural writer is precise about the
|
|
230
|
+
* things a reader relies on: optional and rest parameters, generic signatures, tuple rest and
|
|
231
|
+
* optional elements, literal types (quoted), boolean literals, quoted keys, and precedence
|
|
232
|
+
* (`(() => void)[]`, `(A | B) & C`). A type it is still expanding when it meets itself again
|
|
233
|
+
* is written as `unknown` there, never expanded without end.
|
|
245
234
|
*/
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
node: ts.Node
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (type.isUnion()) {
|
|
257
|
-
const nonNever = (type as ts.UnionType).types.filter((t) => !(t.flags & compiler.TypeFlags.Never))
|
|
258
|
-
if (nonNever.length === 1) {
|
|
259
|
-
return serializeAsRef(nonNever[0], checker, node, compiler, depth)
|
|
260
|
-
}
|
|
235
|
+
class Serializer {
|
|
236
|
+
readonly checker: ts.TypeChecker
|
|
237
|
+
readonly compiler: typeof ts
|
|
238
|
+
readonly node: ts.Node
|
|
239
|
+
readonly #active = new Set<ts.Type>()
|
|
240
|
+
|
|
241
|
+
constructor(checker: ts.TypeChecker, node: ts.Node, compiler: typeof ts) {
|
|
242
|
+
this.checker = checker
|
|
243
|
+
this.node = node
|
|
244
|
+
this.compiler = compiler
|
|
261
245
|
}
|
|
262
246
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
(
|
|
247
|
+
/** An `import()` reference with type arguments for an annotation like `x: Foo<Bar>`. */
|
|
248
|
+
refFromDeclaration(decl: ts.Declaration | undefined): string | undefined {
|
|
249
|
+
const { checker, compiler } = this
|
|
250
|
+
if (!decl) return undefined
|
|
251
|
+
const typeRef = (decl as { type?: ts.TypeNode }).type
|
|
252
|
+
if (!typeRef || !compiler.isTypeReferenceNode(typeRef)) return undefined
|
|
253
|
+
let refSym = checker.getSymbolAtLocation(typeRef.typeName)
|
|
254
|
+
while (refSym && refSym.flags & compiler.SymbolFlags.Alias) refSym = checker.getAliasedSymbol(refSym)
|
|
255
|
+
if (!refSym?.declarations?.length) return undefined
|
|
256
|
+
const srcFile = refSym.declarations[0].getSourceFile()
|
|
257
|
+
if (isLibFile(srcFile.fileName)) return undefined
|
|
258
|
+
if (!isExportedFromFile(refSym, srcFile, checker)) return undefined
|
|
259
|
+
const args = typeRef.typeArguments?.map((a) => {
|
|
260
|
+
const t = checker.getTypeFromTypeNode(a)
|
|
261
|
+
return this.asRef(t) ?? this.serialize(t)
|
|
262
|
+
})
|
|
263
|
+
const argText = args && args.length > 0 ? `<${args.join(", ")}>` : ""
|
|
264
|
+
return `import("${srcFile.fileName}").${refSym.getName()}${argText}`
|
|
265
|
+
}
|
|
281
266
|
|
|
282
|
-
|
|
283
|
-
|
|
267
|
+
/**
|
|
268
|
+
* `import("path").TypeName<Args>` for a named type whose module exports it, else null.
|
|
269
|
+
* Globals (Promise, Map, …) are left to `serialize`.
|
|
270
|
+
*/
|
|
271
|
+
asRef(type: ts.Type, depth = 0): string | null {
|
|
272
|
+
const { checker, compiler } = this
|
|
273
|
+
if (depth > 6) return null
|
|
274
|
+
|
|
275
|
+
/* union with never members (from intersection narrowing) — simplify first */
|
|
276
|
+
if (type.isUnion()) {
|
|
277
|
+
const nonNever = type.types.filter((t) => !(t.flags & compiler.TypeFlags.Never))
|
|
278
|
+
if (nonNever.length === 1) return this.asRef(nonNever[0], depth)
|
|
279
|
+
}
|
|
284
280
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
281
|
+
const sym = type.aliasSymbol ?? type.getSymbol()
|
|
282
|
+
if (!sym) return null
|
|
283
|
+
const name = sym.getName()
|
|
284
|
+
if (!name || name.startsWith("__") || name === "Object") return null
|
|
285
|
+
const decls = sym.declarations
|
|
286
|
+
if (!decls?.length) return null
|
|
287
|
+
const sf = decls[0].getSourceFile()
|
|
288
|
+
if (isLibFile(sf.fileName)) return null
|
|
289
|
+
|
|
290
|
+
const named =
|
|
291
|
+
(sym.flags &
|
|
292
|
+
(compiler.SymbolFlags.TypeAlias |
|
|
293
|
+
compiler.SymbolFlags.Interface |
|
|
294
|
+
compiler.SymbolFlags.Class |
|
|
295
|
+
compiler.SymbolFlags.Enum)) !==
|
|
296
|
+
0
|
|
297
|
+
/* only reference .d.ts symbols or explicitly declared named types from .ts files */
|
|
298
|
+
if (!sf.isDeclarationFile && !named) return null
|
|
299
|
+
/* skip ambient .d.ts scripts (no exports) — import() can't resolve them */
|
|
300
|
+
if (sf.isDeclarationFile && !(sf as unknown as { externalModuleIndicator?: unknown }).externalModuleIndicator) {
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
/* skip non-exported symbols — import() on them resolves to `any` */
|
|
304
|
+
if (!isExportedFromFile(sym, sf, checker)) return null
|
|
305
|
+
|
|
306
|
+
const typeArgs = type.aliasSymbol ? type.aliasTypeArguments : (type as ts.TypeReference).typeArguments
|
|
307
|
+
/* a class or interface reference carries its `this` type as a trailing argument */
|
|
308
|
+
const declared = sym.declarations?.[0]
|
|
309
|
+
const paramCount =
|
|
310
|
+
declared && "typeParameters" in declared
|
|
311
|
+
? ((declared as { typeParameters?: unknown[] }).typeParameters?.length ?? 0)
|
|
312
|
+
: (typeArgs?.length ?? 0)
|
|
313
|
+
const args = (typeArgs ?? []).slice(0, paramCount)
|
|
314
|
+
if (args.length > 0) {
|
|
315
|
+
const text = args.map((arg) => this.asRef(arg, depth + 1) ?? this.serialize(arg))
|
|
316
|
+
return `import("${sf.fileName}").${name}<${text.join(", ")}>`
|
|
317
|
+
}
|
|
318
|
+
return `import("${sf.fileName}").${name}`
|
|
289
319
|
}
|
|
290
320
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const argRef = serializeAsRef(arg, checker, node, compiler, depth + 1)
|
|
300
|
-
return argRef ?? serialize(arg, checker, node, compiler)
|
|
301
|
-
})
|
|
302
|
-
return `import("${fileName}").${name}<${args.join(", ")}>`
|
|
321
|
+
serialize(type: ts.Type): string {
|
|
322
|
+
if (this.#active.has(type)) return "unknown"
|
|
323
|
+
this.#active.add(type)
|
|
324
|
+
try {
|
|
325
|
+
return this.#serialize(type)
|
|
326
|
+
} finally {
|
|
327
|
+
this.#active.delete(type)
|
|
328
|
+
}
|
|
303
329
|
}
|
|
304
330
|
|
|
305
|
-
|
|
306
|
-
}
|
|
331
|
+
#serialize(type: ts.Type): string {
|
|
332
|
+
const { checker, compiler, node } = this
|
|
333
|
+
const F = compiler.TypeFlags
|
|
334
|
+
const next = (t: ts.Type) => this.serialize(t)
|
|
307
335
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (type.flags & compiler.TypeFlags.String) return "string"
|
|
315
|
-
if (type.flags & compiler.TypeFlags.Number) return "number"
|
|
316
|
-
if (type.flags & compiler.TypeFlags.Boolean) return "boolean"
|
|
317
|
-
if (type.flags & compiler.TypeFlags.BigInt) return "bigint"
|
|
318
|
-
if (type.flags & compiler.TypeFlags.ESSymbol) return "symbol"
|
|
319
|
-
if (type.flags & compiler.TypeFlags.Void) return "void"
|
|
320
|
-
if (type.flags & compiler.TypeFlags.Undefined) return "undefined"
|
|
321
|
-
if (type.flags & compiler.TypeFlags.Null) return "null"
|
|
322
|
-
if (type.flags & compiler.TypeFlags.Never) return "never"
|
|
323
|
-
if (type.flags & compiler.TypeFlags.Unknown) return "unknown"
|
|
324
|
-
if (type.flags & compiler.TypeFlags.Any) return "unknown"
|
|
325
|
-
|
|
326
|
-
/* literals */
|
|
327
|
-
if (type.isStringLiteral()) return `"${(type as ts.StringLiteralType).value}"`
|
|
328
|
-
if (type.isNumberLiteral()) return `${(type as ts.NumberLiteralType).value}`
|
|
329
|
-
|
|
330
|
-
/* union */
|
|
331
|
-
if (type.isUnion()) {
|
|
332
|
-
return type.types
|
|
333
|
-
.map((t) => {
|
|
334
|
-
const s = next(t)
|
|
335
|
-
/* parenthesize function types inside unions — TS syntax requires it */
|
|
336
|
-
return s.includes("=>") ? `(${s})` : s
|
|
337
|
-
})
|
|
338
|
-
.join(" | ")
|
|
339
|
-
}
|
|
336
|
+
/* an enum is nominal: keep its name when it can be referenced */
|
|
337
|
+
const enumSym = type.getSymbol()
|
|
338
|
+
if (enumSym && enumSym.flags & compiler.SymbolFlags.Enum) {
|
|
339
|
+
const ref = this.asRef(type)
|
|
340
|
+
if (ref) return ref
|
|
341
|
+
}
|
|
340
342
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
+
/* primitives */
|
|
344
|
+
if (type.flags & F.String) return "string"
|
|
345
|
+
if (type.flags & F.Number) return "number"
|
|
346
|
+
if (type.flags & F.Boolean) return "boolean"
|
|
347
|
+
if (type.flags & F.BigInt) return "bigint"
|
|
348
|
+
if (type.flags & F.ESSymbol) return "symbol"
|
|
349
|
+
if (type.flags & F.UniqueESSymbol) return "symbol"
|
|
350
|
+
if (type.flags & F.Void) return "void"
|
|
351
|
+
if (type.flags & F.Undefined) return "undefined"
|
|
352
|
+
if (type.flags & F.Null) return "null"
|
|
353
|
+
if (type.flags & F.Never) return "never"
|
|
354
|
+
if (type.flags & F.Unknown) return "unknown"
|
|
355
|
+
if (type.flags & F.Any) return "unknown"
|
|
356
|
+
if (type.flags & F.NonPrimitive) return "object"
|
|
357
|
+
|
|
358
|
+
/* literals */
|
|
359
|
+
if (type.isStringLiteral()) return JSON.stringify(type.value)
|
|
360
|
+
if (type.isNumberLiteral()) return `${type.value}`
|
|
361
|
+
if (type.flags & F.BigIntLiteral) {
|
|
362
|
+
const v = (type as ts.BigIntLiteralType).value
|
|
363
|
+
return `${v.negative ? "-" : ""}${v.base10Value}n`
|
|
364
|
+
}
|
|
365
|
+
if (type.flags & F.BooleanLiteral) return checker.typeToString(type)
|
|
366
|
+
if (type.flags & F.TemplateLiteral) return "string"
|
|
343
367
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const el = next(args[0])
|
|
349
|
-
return el.includes("|") || el.includes("&") ? `(${el})[]` : `${el}[]`
|
|
368
|
+
/* a type parameter with no binding — its constraint, or unknown */
|
|
369
|
+
if (type.flags & F.TypeParameter) {
|
|
370
|
+
const constraint = checker.getBaseConstraintOfType(type)
|
|
371
|
+
return constraint && constraint !== type ? next(constraint) : "unknown"
|
|
350
372
|
}
|
|
351
|
-
return "unknown[]"
|
|
352
|
-
}
|
|
353
373
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const args = checker.getTypeArguments(type as ts.TypeReference)
|
|
357
|
-
return `[${args.map(next).join(", ")}]`
|
|
358
|
-
}
|
|
374
|
+
/* union */
|
|
375
|
+
if (type.isUnion()) return unionText(type.types.map(next))
|
|
359
376
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (sigs.length > 0 && type.getProperties().length === 0) {
|
|
363
|
-
const sig = sigs[0]
|
|
364
|
-
const params = sig.getParameters().map((p) => {
|
|
365
|
-
const pt = checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node)
|
|
366
|
-
const decl = p.valueDeclaration
|
|
367
|
-
const isRest =
|
|
368
|
-
decl !== undefined &&
|
|
369
|
-
compiler.isParameter(decl) &&
|
|
370
|
-
(decl as ts.ParameterDeclaration).dotDotDotToken !== undefined
|
|
371
|
-
return `${isRest ? "..." : ""}${p.getName()}: ${next(pt)}`
|
|
372
|
-
})
|
|
373
|
-
const ret = checker.getReturnTypeOfSignature(sig)
|
|
374
|
-
return `(${params.join(", ")}) => ${next(ret)}`
|
|
375
|
-
}
|
|
377
|
+
/* intersection */
|
|
378
|
+
if (type.isIntersection()) return intersectionText(type.types.map(next))
|
|
376
379
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
"Date",
|
|
388
|
-
"Error",
|
|
389
|
-
"File",
|
|
390
|
-
"FormData",
|
|
391
|
-
"Headers",
|
|
392
|
-
"Map",
|
|
393
|
-
"Promise",
|
|
394
|
-
"ReadableStream",
|
|
395
|
-
"RegExp",
|
|
396
|
-
"Request",
|
|
397
|
-
"Response",
|
|
398
|
-
"Set",
|
|
399
|
-
"SharedArrayBuffer",
|
|
400
|
-
"TransformStream",
|
|
401
|
-
"URL",
|
|
402
|
-
"URLSearchParams",
|
|
403
|
-
"WeakMap",
|
|
404
|
-
"WeakSet",
|
|
405
|
-
"WritableStream",
|
|
406
|
-
])
|
|
407
|
-
if (globals.has(name)) {
|
|
380
|
+
/* array */
|
|
381
|
+
if (checker.isArrayType(type)) {
|
|
382
|
+
const args = checker.getTypeArguments(type as ts.TypeReference)
|
|
383
|
+
const readonlyArray = type.getSymbol()?.getName() === "ReadonlyArray"
|
|
384
|
+
const el = args.length > 0 ? arrayText(next(args[0])) : "unknown[]"
|
|
385
|
+
return readonlyArray ? `readonly ${el}` : el
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* tuple — rest and optional elements kept */
|
|
389
|
+
if (checker.isTupleType(type)) {
|
|
408
390
|
const ref = type as ts.TypeReference
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
391
|
+
const args = checker.getTypeArguments(ref)
|
|
392
|
+
const target = ref.target as ts.TupleType
|
|
393
|
+
const parts = args.map((arg, i) => {
|
|
394
|
+
const flags = target.elementFlags?.[i] ?? compiler.ElementFlags.Required
|
|
395
|
+
if (flags & compiler.ElementFlags.Rest) return `...${arrayText(next(arg))}`
|
|
396
|
+
if (flags & compiler.ElementFlags.Variadic) return `...${next(arg)}`
|
|
397
|
+
if (flags & compiler.ElementFlags.Optional) return `${next(arg)}?`
|
|
398
|
+
return next(arg)
|
|
399
|
+
})
|
|
400
|
+
return `${target.readonly ? "readonly " : ""}[${parts.join(", ")}]`
|
|
413
401
|
}
|
|
414
402
|
|
|
415
|
-
/*
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
403
|
+
/* function (pure callable, no data properties) */
|
|
404
|
+
const sigs = type.getCallSignatures()
|
|
405
|
+
if (sigs.length > 0 && type.getProperties().length === 0) {
|
|
406
|
+
const parts = sigs.map((sig) => this.#signature(sig))
|
|
407
|
+
return parts.length === 1 ? parts[0] : parts.map((p) => `(${p})`).join(" & ")
|
|
408
|
+
}
|
|
419
409
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
410
|
+
/* well-known globals — keep name, serialize type args */
|
|
411
|
+
const sym = type.getSymbol()
|
|
412
|
+
if (sym) {
|
|
413
|
+
const name = sym.getName()
|
|
414
|
+
const decl = sym.declarations?.[0]
|
|
415
|
+
if (decl && isLibFile(decl.getSourceFile().fileName) && name !== "Object" && !name.startsWith("__")) {
|
|
416
|
+
const args = (type as ts.TypeReference).typeArguments ?? []
|
|
417
|
+
const declaredParams = (decl as { typeParameters?: unknown[] }).typeParameters?.length ?? args.length
|
|
418
|
+
const kept = args.slice(0, declaredParams)
|
|
419
|
+
return kept.length > 0 ? `${name}<${kept.map(next).join(", ")}>` : name
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/* named types — prefer import() reference over structural expansion */
|
|
423
|
+
if (name && !name.startsWith("__") && name !== "Object") {
|
|
424
|
+
const ref = this.asRef(type)
|
|
425
|
+
if (ref) return ref
|
|
431
426
|
}
|
|
432
427
|
}
|
|
433
|
-
|
|
428
|
+
const aliasRef = type.aliasSymbol ? this.asRef(type) : null
|
|
429
|
+
if (aliasRef) return aliasRef
|
|
434
430
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
431
|
+
/* Record<string, V> — index signature only */
|
|
432
|
+
const stringIdx = type.getStringIndexType()
|
|
433
|
+
if (stringIdx && type.getProperties().length === 0) {
|
|
434
|
+
return `Record<string, ${next(stringIdx)}>`
|
|
435
|
+
}
|
|
440
436
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (props.length > 50) {
|
|
444
|
-
return checker.typeToString(type, node, compiler.TypeFormatFlags.NoTruncation)
|
|
445
|
-
}
|
|
446
|
-
if (props.length > 0) {
|
|
437
|
+
/* object — expand to structural form */
|
|
438
|
+
const props = type.getProperties().filter((p) => isPublicName(p.getName()))
|
|
447
439
|
const entries: string[] = []
|
|
448
440
|
for (const p of props) {
|
|
449
441
|
const decls = p.getDeclarations()
|
|
@@ -455,37 +447,210 @@ function serialize(type: ts.Type, checker: ts.TypeChecker, node: ts.Node, compil
|
|
|
455
447
|
const opt = (p.flags & compiler.SymbolFlags.Optional) !== 0
|
|
456
448
|
const ro =
|
|
457
449
|
decls?.some((d) => (compiler.getCombinedModifierFlags(d) & compiler.ModifierFlags.Readonly) !== 0) ?? false
|
|
458
|
-
entries.push(`${ro ? "readonly " : ""}${p.getName()}${opt ? "?" : ""}: ${next(pt)}`)
|
|
450
|
+
entries.push(`${ro ? "readonly " : ""}${quoteKey(p.getName())}${opt ? "?" : ""}: ${next(pt)}`)
|
|
459
451
|
}
|
|
452
|
+
if (stringIdx) entries.push(`[key: string]: ${next(stringIdx)}`)
|
|
453
|
+
for (const sig of sigs) entries.push(this.#signature(sig, true))
|
|
460
454
|
if (entries.length > 0) return `{ ${entries.join("; ")} }`
|
|
455
|
+
if (props.length === 0) return "{}"
|
|
456
|
+
return "unknown"
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** `<T extends X>(a: A, b?: B, ...rest: C[]) => R`, or the call-signature member form */
|
|
460
|
+
#signature(sig: ts.Signature, member = false): string {
|
|
461
|
+
const { checker, compiler, node } = this
|
|
462
|
+
const typeParams = sig.getTypeParameters() ?? []
|
|
463
|
+
const tp =
|
|
464
|
+
typeParams.length > 0
|
|
465
|
+
? `<${typeParams
|
|
466
|
+
.map((t) => {
|
|
467
|
+
const c = t.getConstraint()
|
|
468
|
+
return `${t.symbol.getName()}${c ? ` extends ${this.#typeParamText(c)}` : ""}`
|
|
469
|
+
})
|
|
470
|
+
.join(", ")}>`
|
|
471
|
+
: ""
|
|
472
|
+
const params = sig.getParameters().map((p) => {
|
|
473
|
+
const pt = checker.getTypeOfSymbolAtLocation(p, p.valueDeclaration ?? node)
|
|
474
|
+
const decl = p.valueDeclaration
|
|
475
|
+
const param = decl && compiler.isParameter(decl) ? (decl as ts.ParameterDeclaration) : undefined
|
|
476
|
+
const isRest = param?.dotDotDotToken !== undefined
|
|
477
|
+
const optional = !isRest && param !== undefined && checker.isOptionalParameter(param)
|
|
478
|
+
/* an optional parameter's type carries `| undefined`; `?` already says so */
|
|
479
|
+
const text = optional ? this.#typeParamText(checker.getNonNullableType(pt)) : this.#typeParamText(pt)
|
|
480
|
+
return `${isRest ? "..." : ""}${p.getName()}${optional ? "?" : ""}: ${text}`
|
|
481
|
+
})
|
|
482
|
+
const ret = this.#typeParamText(checker.getReturnTypeOfSignature(sig))
|
|
483
|
+
return member ? `${tp}(${params.join(", ")}): ${ret}` : `${tp}(${params.join(", ")}) => ${ret}`
|
|
461
484
|
}
|
|
462
485
|
|
|
463
|
-
|
|
464
|
-
|
|
486
|
+
/** A type inside a signature: a bare type parameter stays by name, bound by the signature. */
|
|
487
|
+
#typeParamText(t: ts.Type): string {
|
|
488
|
+
if (t.flags & this.compiler.TypeFlags.TypeParameter) return t.symbol.getName()
|
|
489
|
+
return this.asRef(t) ?? this.serialize(t)
|
|
490
|
+
}
|
|
465
491
|
}
|
|
466
492
|
|
|
467
493
|
/**
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
* - node_modules paths → bare package specifiers
|
|
471
|
-
* - project-internal paths → relative from outputDir
|
|
494
|
+
* Does `type` contain `token` outside every bracket and string? Used to parenthesize only
|
|
495
|
+
* where precedence needs it.
|
|
472
496
|
*/
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
497
|
+
function hasTopLevel(type: string, token: "|" | "&" | "=>"): boolean {
|
|
498
|
+
let depth = 0
|
|
499
|
+
let quote: string | null = null
|
|
500
|
+
for (let i = 0; i < type.length; i++) {
|
|
501
|
+
const c = type[i]
|
|
502
|
+
if (quote !== null) {
|
|
503
|
+
if (c === "\\") i++
|
|
504
|
+
else if (c === quote) quote = null
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
if (c === '"' || c === "'" || c === "`") quote = c
|
|
508
|
+
else if (c === "(" || c === "{" || c === "[" || c === "<") depth++
|
|
509
|
+
else if (c === ")" || c === "}" || c === "]" || (c === ">" && type[i - 1] !== "=")) depth--
|
|
510
|
+
else if (depth === 0 && type.startsWith(token, i)) return true
|
|
511
|
+
}
|
|
512
|
+
return false
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function arrayText(el: string): string {
|
|
516
|
+
return hasTopLevel(el, "|") || hasTopLevel(el, "&") || hasTopLevel(el, "=>") || el.startsWith("readonly ")
|
|
517
|
+
? `(${el})[]`
|
|
518
|
+
: `${el}[]`
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function unionText(parts: string[]): string {
|
|
522
|
+
return [...new Set(parts)].map((p) => (hasTopLevel(p, "=>") ? `(${p})` : p)).join(" | ")
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function intersectionText(parts: string[]): string {
|
|
526
|
+
return parts.map((p) => (hasTopLevel(p, "|") || hasTopLevel(p, "=>") ? `(${p})` : p)).join(" & ")
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* ---- module specifiers ---- */
|
|
530
|
+
|
|
531
|
+
type PackageInfo = { dir: string; json: Record<string, unknown> }
|
|
532
|
+
|
|
533
|
+
const packageCache = new Map<string, PackageInfo | null>()
|
|
534
|
+
|
|
535
|
+
function nearestPackage(fromFile: string): PackageInfo | null {
|
|
536
|
+
let dir = dirname(fromFile)
|
|
537
|
+
const visited: string[] = []
|
|
538
|
+
let found: PackageInfo | null = null
|
|
539
|
+
let prev = ""
|
|
540
|
+
while (dir !== prev) {
|
|
541
|
+
const cached = packageCache.get(dir)
|
|
542
|
+
if (cached !== undefined) {
|
|
543
|
+
found = cached
|
|
544
|
+
break
|
|
545
|
+
}
|
|
546
|
+
visited.push(dir)
|
|
547
|
+
const file = join(dir, "package.json")
|
|
548
|
+
if (existsSync(file)) {
|
|
549
|
+
try {
|
|
550
|
+
const json = JSON.parse(readFileSync(file, "utf8")) as Record<string, unknown>
|
|
551
|
+
/* a package.json without a name (a "type": "module" marker) is not a package boundary */
|
|
552
|
+
if (typeof json.name === "string") {
|
|
553
|
+
found = { dir, json }
|
|
554
|
+
break
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
/* unreadable — keep walking */
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
prev = dir
|
|
561
|
+
dir = dirname(dir)
|
|
562
|
+
}
|
|
563
|
+
for (const d of visited) packageCache.set(d, found)
|
|
564
|
+
return found
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** Every file an `exports` value can point at, with the subpath that names it. */
|
|
568
|
+
function exportTargets(exports: unknown): Array<{ subpath: string; target: string }> {
|
|
569
|
+
const out: Array<{ subpath: string; target: string }> = []
|
|
570
|
+
const collect = (subpath: string, value: unknown): void => {
|
|
571
|
+
if (typeof value === "string") out.push({ subpath, target: value })
|
|
572
|
+
else if (Array.isArray(value)) for (const v of value) collect(subpath, v)
|
|
573
|
+
else if (value !== null && typeof value === "object") {
|
|
574
|
+
for (const v of Object.values(value as Record<string, unknown>)) collect(subpath, v)
|
|
478
575
|
}
|
|
479
|
-
|
|
480
|
-
|
|
576
|
+
}
|
|
577
|
+
if (typeof exports === "string" || Array.isArray(exports)) collect(".", exports)
|
|
578
|
+
else if (exports !== null && typeof exports === "object") {
|
|
579
|
+
const entries = Object.entries(exports as Record<string, unknown>)
|
|
580
|
+
if (entries.length > 0 && entries.every(([k]) => !k.startsWith("."))) collect(".", exports)
|
|
581
|
+
else for (const [k, v] of entries) collect(k, v)
|
|
582
|
+
}
|
|
583
|
+
return out
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function stripTypeExtension(path: string): string {
|
|
587
|
+
return path
|
|
588
|
+
.replace(/\.d\.[mc]?ts$/, "")
|
|
589
|
+
.replace(/\.[mc]?tsx?$/, "")
|
|
590
|
+
.replace(/\.[mc]?js$/, "")
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** `@types/foo` types the `foo` package; `@types/scope__name` types `@scope/name`. */
|
|
594
|
+
function typedPackageName(name: string): string {
|
|
595
|
+
if (!name.startsWith("@types/")) return name
|
|
596
|
+
const bare = name.slice("@types/".length)
|
|
597
|
+
return bare.includes("__") ? `@${bare.replace("__", "/")}` : bare
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* The specifier a generated file should use to import `filePath`.
|
|
602
|
+
*
|
|
603
|
+
* A file of another package is named through that package — its `exports` subpath when one
|
|
604
|
+
* points at the file (any condition: `types`, `default`, or a source condition), its `types`
|
|
605
|
+
* entry, or (no `exports` map) its path without extension; an `@types` package by the package it
|
|
606
|
+
* types. A file of the output's own package is a relative path. Lib files never get here.
|
|
607
|
+
*/
|
|
608
|
+
function moduleSpecifier(filePath: string, outputDir: string | undefined): string {
|
|
609
|
+
const pkg = nearestPackage(filePath)
|
|
610
|
+
const ownPkg = outputDir ? nearestPackage(join(outputDir, "_")) : null
|
|
611
|
+
const relativeSpecifier = (): string => {
|
|
612
|
+
if (!outputDir) return filePath
|
|
613
|
+
let rel = relative(outputDir, filePath).split(sep).join("/")
|
|
481
614
|
if (!rel.startsWith(".")) rel = `./${rel}`
|
|
482
|
-
return
|
|
615
|
+
return rel
|
|
616
|
+
}
|
|
617
|
+
if (!pkg || (ownPkg && pkg.dir === ownPkg.dir)) return relativeSpecifier()
|
|
618
|
+
const name = typedPackageName(String(pkg.json.name))
|
|
619
|
+
const rel = `./${relative(pkg.dir, filePath).split(sep).join("/")}`
|
|
620
|
+
|
|
621
|
+
if (pkg.json.exports !== undefined) {
|
|
622
|
+
const match = exportTargets(pkg.json.exports).find(
|
|
623
|
+
(e) => e.target === rel || stripTypeExtension(e.target) === stripTypeExtension(rel),
|
|
624
|
+
)
|
|
625
|
+
if (match) return match.subpath === "." ? name : `${name}/${match.subpath.replace(/^\.\//, "")}`
|
|
626
|
+
/* the package does not export this file — a path into it would not resolve */
|
|
627
|
+
return relativeSpecifier()
|
|
628
|
+
}
|
|
629
|
+
const typesEntry = pkg.json.types ?? pkg.json.typings
|
|
630
|
+
if (
|
|
631
|
+
typeof typesEntry === "string" &&
|
|
632
|
+
stripTypeExtension(`./${typesEntry.replace(/^\.\//, "")}`) === stripTypeExtension(rel)
|
|
633
|
+
) {
|
|
634
|
+
return name
|
|
635
|
+
}
|
|
636
|
+
const sub = stripTypeExtension(rel.slice(2)).replace(/\/index$/, "")
|
|
637
|
+
return sub === "index" || sub === "" ? name : `${name}/${sub}`
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Rewrite absolute `import("…")` paths into resolvable module specifiers, and drop the
|
|
642
|
+
* `import()` around lib types (`Date`, `Promise`), which are global.
|
|
643
|
+
*/
|
|
644
|
+
function sanitizeImportPaths(str: string, outputDir?: string): string {
|
|
645
|
+
return str.replace(/import\("([^"]+)"\)\./g, (_match, filePath: string) => {
|
|
646
|
+
if (isLibFile(filePath)) return ""
|
|
647
|
+
return `import("${moduleSpecifier(filePath, outputDir)}").`
|
|
483
648
|
})
|
|
484
649
|
}
|
|
485
650
|
|
|
486
651
|
export type ExtractedChainTypes = {
|
|
487
652
|
base: ExtractedBaseCtx
|
|
488
|
-
/** per-route middleware additions keyed by "method /path" */
|
|
653
|
+
/** per-route middleware additions keyed by "method /full/path" (basePath included) */
|
|
489
654
|
routeMiddleware: Record<string, string>
|
|
490
655
|
/** structured per-property middleware data for sub-type dedup */
|
|
491
656
|
routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>>
|
|
@@ -495,9 +660,13 @@ const HTTP_METHODS = new Set(["all", "delete", "get", "head", "options", "patch"
|
|
|
495
660
|
|
|
496
661
|
/**
|
|
497
662
|
* Extracts base ctx and per-route middleware additions from sub-chains.
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
663
|
+
*
|
|
664
|
+
* Every source file of the program is scanned for HTTP method calls (`.get()`, `.post()`, …)
|
|
665
|
+
* on a Honey instance. A route is keyed by its full path — the receiver's `$basePath` type
|
|
666
|
+
* joined with the literal path, exactly as the router registers it — so `v1.get("/items")`
|
|
667
|
+
* and `pub.get("/items")` never share an entry. A receiver whose base path is not a literal
|
|
668
|
+
* type cannot be keyed and is skipped: a route without an entry gets the base ctx, never
|
|
669
|
+
* another route's.
|
|
501
670
|
*/
|
|
502
671
|
export async function extractChainTypes(options: {
|
|
503
672
|
entryPath: string
|
|
@@ -505,30 +674,8 @@ export async function extractChainTypes(options: {
|
|
|
505
674
|
outputDir?: string
|
|
506
675
|
tsconfigPath?: string
|
|
507
676
|
}): Promise<ExtractedChainTypes> {
|
|
508
|
-
const {
|
|
509
|
-
|
|
510
|
-
const tsConfigFilePath = options.tsconfigPath ?? findTsConfig(options.entryPath)
|
|
511
|
-
|
|
512
|
-
const project = new Project({
|
|
513
|
-
skipAddingFilesFromTsConfig: true,
|
|
514
|
-
tsConfigFilePath,
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
project.addSourceFilesAtPaths(options.entryPath)
|
|
518
|
-
project.resolveSourceFileDependencies()
|
|
519
|
-
|
|
520
|
-
const sourceFile = project.getSourceFileOrThrow(options.entryPath)
|
|
521
|
-
const baseVarDecl = sourceFile.getVariableDeclaration(options.exportName)
|
|
522
|
-
if (!baseVarDecl) {
|
|
523
|
-
throw new Error(`Export "${options.exportName}" not found in ${options.entryPath}`)
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function bridge<T>(value: unknown): T {
|
|
527
|
-
return value as T
|
|
528
|
-
}
|
|
529
|
-
const checker: ts.TypeChecker = bridge(project.getTypeChecker().compilerObject)
|
|
530
|
-
const baseNode: ts.Node = bridge(baseVarDecl.compilerNode)
|
|
531
|
-
const tsLib: typeof ts = bridge(ts)
|
|
677
|
+
const { appType, checker, compiler, node: baseNode, project } = await loadEntry(options)
|
|
678
|
+
const ser = new Serializer(checker, baseNode, compiler)
|
|
532
679
|
|
|
533
680
|
function getCtxType(tsType: ts.Type, locationNode?: ts.Node): ts.Type | null {
|
|
534
681
|
const sym = tsType.getProperty("$ctx")
|
|
@@ -536,131 +683,122 @@ export async function extractChainTypes(options: {
|
|
|
536
683
|
return checker.getTypeOfSymbolAtLocation(sym, locationNode ?? sym.valueDeclaration ?? baseNode)
|
|
537
684
|
}
|
|
538
685
|
|
|
686
|
+
function basePathOf(tsType: ts.Type, locationNode: ts.Node): string | null {
|
|
687
|
+
const sym = tsType.getProperty("$basePath")
|
|
688
|
+
if (!sym) return null
|
|
689
|
+
const t = checker.getTypeOfSymbolAtLocation(sym, locationNode)
|
|
690
|
+
return t.isStringLiteral() ? t.value : null
|
|
691
|
+
}
|
|
692
|
+
|
|
539
693
|
/* extract base ctx */
|
|
540
|
-
const baseType = bridge<ts.Type>(baseVarDecl.getType().compilerType)
|
|
541
694
|
let envType = "Record<string, unknown>"
|
|
542
|
-
const envSym =
|
|
543
|
-
if (envSym)
|
|
544
|
-
envType = serialize(checker.getTypeOfSymbolAtLocation(envSym, baseNode), checker, baseNode, tsLib)
|
|
545
|
-
}
|
|
695
|
+
const envSym = appType.getProperty("$env")
|
|
696
|
+
if (envSym) envType = ser.serialize(checker.getTypeOfSymbolAtLocation(envSym, baseNode))
|
|
546
697
|
|
|
547
|
-
const baseCtxType = getCtxType(
|
|
698
|
+
const baseCtxType = getCtxType(appType, baseNode)
|
|
548
699
|
if (!baseCtxType) {
|
|
549
700
|
throw new Error(`Export "${options.exportName}" has no $ctx — not a Honey instance`)
|
|
550
701
|
}
|
|
551
|
-
const baseMwProps = extractMiddlewareProps(baseCtxType,
|
|
552
|
-
const baseMiddlewareType =
|
|
553
|
-
baseMwProps.length === 0
|
|
554
|
-
? null
|
|
555
|
-
: `{ ${baseMwProps.map((p) => `${p.name}${p.opt ? "?" : ""}: ${p.type}`).join("; ")} }`
|
|
702
|
+
const baseMwProps = extractMiddlewareProps(baseCtxType, ser)
|
|
703
|
+
const baseMiddlewareType = propsType(baseMwProps)
|
|
556
704
|
|
|
557
705
|
/* build name→type map for reusing base type references in route middleware */
|
|
558
706
|
const basePropTypeByName = new Map<string, string>()
|
|
559
|
-
for (const p of baseMwProps)
|
|
560
|
-
basePropTypeByName.set(p.name, p.type)
|
|
561
|
-
}
|
|
707
|
+
for (const p of baseMwProps) basePropTypeByName.set(p.name, p.type)
|
|
562
708
|
|
|
563
709
|
/* collect base middleware property names for diffing */
|
|
564
710
|
const baseMwPropNames = new Set<string>()
|
|
565
711
|
for (const p of baseCtxType.getProperties()) {
|
|
566
712
|
const name = p.getName()
|
|
567
|
-
if (!HONEY_CTX_PROPS.has(name) && !name.startsWith("_"))
|
|
568
|
-
baseMwPropNames.add(name)
|
|
569
|
-
}
|
|
713
|
+
if (!HONEY_CTX_PROPS.has(name) && !name.startsWith("_")) baseMwPropNames.add(name)
|
|
570
714
|
}
|
|
571
715
|
|
|
572
716
|
const sanitize = (s: string) => sanitizeImportPaths(s, options.outputDir)
|
|
573
717
|
|
|
574
|
-
|
|
575
|
-
const
|
|
576
|
-
const routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>> = {}
|
|
577
|
-
const compilerSourceFile: ts.SourceFile = bridge(sourceFile.compilerNode)
|
|
718
|
+
const routeMiddleware: Record<string, string> = Object.create(null)
|
|
719
|
+
const routeMiddlewareProps: Record<string, Array<{ name: string; opt: boolean; type: string }>> = Object.create(null)
|
|
578
720
|
|
|
579
721
|
function visitNode(node: ts.Node): void {
|
|
580
|
-
if (
|
|
722
|
+
if (compiler.isCallExpression(node) && compiler.isPropertyAccessExpression(node.expression)) {
|
|
581
723
|
const methodName = node.expression.name.text
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const extraEntries: MwPropEntry[] = []
|
|
596
|
-
const dupeNames = new Set<string>()
|
|
597
|
-
const members = ctxType.isIntersection() ? (ctxType as ts.IntersectionType).types : [ctxType]
|
|
598
|
-
for (const member of members) {
|
|
599
|
-
const memberProps = extractMiddlewareProps(member, checker, baseNode, tsLib)
|
|
600
|
-
for (const rp of memberProps) {
|
|
601
|
-
if (baseMwPropNames.has(rp.name)) continue
|
|
602
|
-
if (extraEntries.some((e) => e.name === rp.name)) {
|
|
603
|
-
dupeNames.add(rp.name)
|
|
604
|
-
continue
|
|
605
|
-
}
|
|
606
|
-
extraEntries.push(rp)
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
/* for properties that appeared in multiple intersection members
|
|
610
|
-
* (e.g. auth narrowed by a later middleware), resolve from the
|
|
611
|
-
* flattened intersection so TypeScript computes the correct
|
|
612
|
-
* intersected type instead of using the first member's type */
|
|
613
|
-
for (const dupeName of dupeNames) {
|
|
614
|
-
const sym = ctxType.getProperty(dupeName)
|
|
615
|
-
if (!sym) continue
|
|
616
|
-
const t = checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? baseNode)
|
|
617
|
-
const typeStr = serializeAsRef(t, checker, baseNode, tsLib) ?? serialize(t, checker, baseNode, tsLib)
|
|
618
|
-
const idx = extraEntries.findIndex((e) => e.name === dupeName)
|
|
619
|
-
if (idx !== -1) {
|
|
620
|
-
extraEntries[idx] = { ...extraEntries[idx], type: typeStr }
|
|
621
|
-
}
|
|
622
|
-
}
|
|
724
|
+
const pathArg = node.arguments[0]
|
|
725
|
+
if (HTTP_METHODS.has(methodName) && pathArg && compiler.isStringLiteralLike(pathArg)) {
|
|
726
|
+
const receiverExpr = node.expression.expression
|
|
727
|
+
const receiverType = checker.getTypeAtLocation(receiverExpr)
|
|
728
|
+
const ctxType = getCtxType(receiverType, receiverExpr)
|
|
729
|
+
const basePath = ctxType ? basePathOf(receiverType, receiverExpr) : null
|
|
730
|
+
if (ctxType && basePath !== null) {
|
|
731
|
+
for (const variant of fullPaths(basePath, pathArg.text)) recordRoute(`${methodName} ${variant}`, ctxType)
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
compiler.forEachChild(node, visitNode)
|
|
736
|
+
}
|
|
623
737
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
738
|
+
/** The paths the router serves for this registration — an optional last param yields two. */
|
|
739
|
+
function fullPaths(basePath: string, path: string): string[] {
|
|
740
|
+
try {
|
|
741
|
+
return expandOptional(parsePattern(joinPatterns(basePath, path))).map((segments) => canonical(segments))
|
|
742
|
+
} catch {
|
|
743
|
+
/* a pattern the router would refuse registers nothing */
|
|
744
|
+
return []
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function recordRoute(key: string, ctxType: ts.Type): void {
|
|
749
|
+
/* extract extra middleware props by iterating intersection MEMBERS individually.
|
|
750
|
+
* This preserves source-level declarations (e.g. ShorthandPropertyAssignment)
|
|
751
|
+
* that get lost when extracting from the flattened intersection. */
|
|
752
|
+
const extraEntries: MwPropEntry[] = []
|
|
753
|
+
const dupeNames = new Set<string>()
|
|
754
|
+
const members = ctxType.isIntersection() ? (ctxType as ts.IntersectionType).types : [ctxType]
|
|
755
|
+
for (const member of members) {
|
|
756
|
+
for (const rp of extractMiddlewareProps(member, ser)) {
|
|
757
|
+
if (baseMwPropNames.has(rp.name)) continue
|
|
758
|
+
if (extraEntries.some((e) => e.name === rp.name)) {
|
|
759
|
+
dupeNames.add(rp.name)
|
|
760
|
+
continue
|
|
646
761
|
}
|
|
762
|
+
extraEntries.push(rp)
|
|
647
763
|
}
|
|
648
764
|
}
|
|
765
|
+
/* a property in several intersection members (e.g. auth narrowed by a later middleware)
|
|
766
|
+
* is read from the flattened intersection, so TypeScript computes the intersected type */
|
|
767
|
+
for (const dupeName of dupeNames) {
|
|
768
|
+
const sym = ctxType.getProperty(dupeName)
|
|
769
|
+
if (!sym) continue
|
|
770
|
+
const t = checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration ?? baseNode)
|
|
771
|
+
const typeStr = ser.asRef(t) ?? ser.serialize(t)
|
|
772
|
+
const idx = extraEntries.findIndex((e) => e.name === dupeName)
|
|
773
|
+
if (idx !== -1) extraEntries[idx] = { ...extraEntries[idx], type: typeStr }
|
|
774
|
+
}
|
|
775
|
+
if (extraEntries.length === 0) return
|
|
649
776
|
|
|
650
|
-
|
|
777
|
+
/* reuse compact base type references when the route has the same property
|
|
778
|
+
* (e.g. shardDb: ShardDb from base → reuse import ref instead of structural expansion) */
|
|
779
|
+
const entries = extraEntries.map((e) => {
|
|
780
|
+
const baseRef = basePropTypeByName.get(e.name)
|
|
781
|
+
return baseRef && baseRef.length < e.type.length ? { ...e, type: baseRef } : e
|
|
782
|
+
})
|
|
783
|
+
routeMiddleware[key] = sanitize(propsType(entries) ?? "{}")
|
|
784
|
+
routeMiddlewareProps[key] = entries.map((e) => ({ name: e.name, opt: e.opt, type: sanitize(e.type) }))
|
|
651
785
|
}
|
|
652
786
|
|
|
653
|
-
|
|
787
|
+
/* every source file of the program, not only the entry: routes are often registered in
|
|
788
|
+
modules the entry imports */
|
|
789
|
+
for (const sf of project.getSourceFiles()) {
|
|
790
|
+
const compilerSf: ts.SourceFile = bridge(sf.compilerNode)
|
|
791
|
+
if (compilerSf.isDeclarationFile) continue
|
|
792
|
+
if (/[\\/]node_modules[\\/]/.test(compilerSf.fileName)) continue
|
|
793
|
+
visitNode(compilerSf)
|
|
794
|
+
}
|
|
654
795
|
|
|
655
796
|
/* extract $taps */
|
|
656
797
|
let tapsType: string | null = null
|
|
657
|
-
const tapsSym =
|
|
798
|
+
const tapsSym = appType.getProperty("$taps")
|
|
658
799
|
if (tapsSym) {
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") {
|
|
662
|
-
tapsType = tapsStr
|
|
663
|
-
}
|
|
800
|
+
const tapsStr = ser.serialize(checker.getTypeOfSymbolAtLocation(tapsSym, baseNode))
|
|
801
|
+
if (tapsStr !== "{}" && tapsStr !== "Record<string, unknown>") tapsType = tapsStr
|
|
664
802
|
}
|
|
665
803
|
|
|
666
804
|
return {
|
|
@@ -669,7 +807,7 @@ export async function extractChainTypes(options: {
|
|
|
669
807
|
middlewareType: baseMiddlewareType ? sanitize(baseMiddlewareType) : null,
|
|
670
808
|
tapsType: tapsType ? sanitize(tapsType) : null,
|
|
671
809
|
},
|
|
672
|
-
routeMiddleware,
|
|
673
|
-
routeMiddlewareProps,
|
|
810
|
+
routeMiddleware: { ...routeMiddleware },
|
|
811
|
+
routeMiddlewareProps: { ...routeMiddlewareProps },
|
|
674
812
|
}
|
|
675
813
|
}
|