@shortlink-org/portolan 0.1.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/LICENSE +21 -0
- package/README.md +387 -0
- package/catalog/enum_test.go +46 -0
- package/catalog/model.go +932 -0
- package/catalog/roundtrip_test.go +185 -0
- package/catalog/via_test.go +38 -0
- package/cli/portolan.mjs +368 -0
- package/go.mod +14 -0
- package/go.sum +14 -0
- package/index.html +13 -0
- package/internal/commands/cargo.go +154 -0
- package/internal/commands/commands.go +143 -0
- package/internal/commands/commands_test.go +323 -0
- package/internal/commands/gradle.go +77 -0
- package/internal/commands/justfile.go +82 -0
- package/internal/commands/makefile.go +106 -0
- package/internal/commands/maven.go +93 -0
- package/internal/commands/packagejson.go +159 -0
- package/internal/commands/pyproject.go +169 -0
- package/internal/commands/taskfile.go +101 -0
- package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
- package/internal/commands/testdata/estate/Makefile +33 -0
- package/internal/commands/testdata/estate/Taskfile.yml +21 -0
- package/internal/commands/testdata/estate/build.gradle.kts +21 -0
- package/internal/commands/testdata/estate/justfile +20 -0
- package/internal/commands/testdata/estate/package.json +13 -0
- package/internal/commands/testdata/estate/pom.xml +29 -0
- package/internal/commands/testdata/estate/pyproject.toml +27 -0
- package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
- package/internal/commands/testdata/golden/commands.json +305 -0
- package/internal/gohttp/analyze.go +2500 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +143 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +36 -0
- package/internal/goscan/tree.go +155 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +62 -0
- package/internal/wsdl/parse.go +920 -0
- package/internal/wsdl/parse_test.go +133 -0
- package/package.json +117 -0
- package/plugin/describe.go +107 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +117 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +1160 -0
- package/plugins/extract-adr/describe.go +19 -0
- package/plugins/extract-adr/describe_test.go +11 -0
- package/plugins/extract-adr/extract.go +153 -0
- package/plugins/extract-adr/extract_test.go +350 -0
- package/plugins/extract-adr/history.go +99 -0
- package/plugins/extract-adr/main.go +65 -0
- package/plugins/extract-adr/options.schema.json +26 -0
- package/plugins/extract-adr/parse.go +642 -0
- package/plugins/extract-adr/parse_test.go +405 -0
- package/plugins/extract-asyncapi/describe.go +19 -0
- package/plugins/extract-asyncapi/describe_test.go +11 -0
- package/plugins/extract-asyncapi/extract.go +315 -0
- package/plugins/extract-asyncapi/extract_test.go +197 -0
- package/plugins/extract-asyncapi/main.go +48 -0
- package/plugins/extract-asyncapi/options.schema.json +26 -0
- package/plugins/extract-asyncapi/spec.go +151 -0
- package/plugins/extract-celery/README.md +169 -0
- package/plugins/extract-celery/extract.py +346 -0
- package/plugins/extract-celery/extract_test.py +192 -0
- package/plugins/extract-celery/main.py +66 -0
- package/plugins/extract-celery/options.py +31 -0
- package/plugins/extract-celery/options.schema.json +31 -0
- package/plugins/extract-celery/producers.py +156 -0
- package/plugins/extract-celery/routes_test.py +106 -0
- package/plugins/extract-commands/describe.go +19 -0
- package/plugins/extract-commands/describe_test.go +11 -0
- package/plugins/extract-commands/extract.go +79 -0
- package/plugins/extract-commands/extract_test.go +92 -0
- package/plugins/extract-commands/main.go +55 -0
- package/plugins/extract-commands/options.schema.json +20 -0
- package/plugins/extract-csr/avro.go +258 -0
- package/plugins/extract-csr/describe.go +19 -0
- package/plugins/extract-csr/describe_test.go +11 -0
- package/plugins/extract-csr/extract.go +338 -0
- package/plugins/extract-csr/extract_test.go +374 -0
- package/plugins/extract-csr/jsonschema.go +343 -0
- package/plugins/extract-csr/lock.go +25 -0
- package/plugins/extract-csr/main.go +84 -0
- package/plugins/extract-csr/options.schema.json +43 -0
- package/plugins/extract-csr/subject.go +102 -0
- package/plugins/extract-django/README.md +386 -0
- package/plugins/extract-django/apps.py +100 -0
- package/plugins/extract-django/auth.py +302 -0
- package/plugins/extract-django/auth_test.py +188 -0
- package/plugins/extract-django/clients.py +121 -0
- package/plugins/extract-django/contracts.py +458 -0
- package/plugins/extract-django/database.py +119 -0
- package/plugins/extract-django/domain.py +318 -0
- package/plugins/extract-django/events.py +86 -0
- package/plugins/extract-django/extract.py +601 -0
- package/plugins/extract-django/extract_test.py +397 -0
- package/plugins/extract-django/flows.py +1278 -0
- package/plugins/extract-django/flows_test.py +338 -0
- package/plugins/extract-django/ids.py +42 -0
- package/plugins/extract-django/ids_test.py +30 -0
- package/plugins/extract-django/lifecycle.py +222 -0
- package/plugins/extract-django/lifecycle_test.py +76 -0
- package/plugins/extract-django/main.py +66 -0
- package/plugins/extract-django/openapi.py +229 -0
- package/plugins/extract-django/openapi_test.py +60 -0
- package/plugins/extract-django/operations.py +96 -0
- package/plugins/extract-django/options.py +61 -0
- package/plugins/extract-django/options.schema.json +126 -0
- package/plugins/extract-django/routing.py +214 -0
- package/plugins/extract-django/routing_test.py +198 -0
- package/plugins/extract-django/serializers.py +558 -0
- package/plugins/extract-django/store.py +434 -0
- package/plugins/extract-django/transport.py +242 -0
- package/plugins/extract-flows/describe.go +19 -0
- package/plugins/extract-flows/describe_test.go +11 -0
- package/plugins/extract-flows/extract.go +91 -0
- package/plugins/extract-flows/main.go +45 -0
- package/plugins/extract-flows/options.schema.json +16 -0
- package/plugins/extract-flows/parse.go +593 -0
- package/plugins/extract-flows/parse_test.go +204 -0
- package/plugins/extract-glossary/describe.go +19 -0
- package/plugins/extract-glossary/describe_test.go +11 -0
- package/plugins/extract-glossary/extract.go +115 -0
- package/plugins/extract-glossary/extract_test.go +220 -0
- package/plugins/extract-glossary/main.go +59 -0
- package/plugins/extract-glossary/options.schema.json +21 -0
- package/plugins/extract-glossary/parse.go +214 -0
- package/plugins/extract-glossary/parse_test.go +203 -0
- package/plugins/extract-go/aggregate.go +214 -0
- package/plugins/extract-go/client.go +409 -0
- package/plugins/extract-go/client_test.go +266 -0
- package/plugins/extract-go/describe.go +19 -0
- package/plugins/extract-go/describe_test.go +11 -0
- package/plugins/extract-go/enum.go +195 -0
- package/plugins/extract-go/enum_test.go +82 -0
- package/plugins/extract-go/event.go +99 -0
- package/plugins/extract-go/extract.go +191 -0
- package/plugins/extract-go/extract_test.go +261 -0
- package/plugins/extract-go/flow.go +1441 -0
- package/plugins/extract-go/flow_test.go +609 -0
- package/plugins/extract-go/httpclient.go +174 -0
- package/plugins/extract-go/httpclient_test.go +296 -0
- package/plugins/extract-go/ids.go +92 -0
- package/plugins/extract-go/layout.go +225 -0
- package/plugins/extract-go/layout_test.go +125 -0
- package/plugins/extract-go/lifecycle.go +324 -0
- package/plugins/extract-go/lifecycle_test.go +108 -0
- package/plugins/extract-go/main.go +79 -0
- package/plugins/extract-go/operation.go +157 -0
- package/plugins/extract-go/options.schema.json +66 -0
- package/plugins/extract-go/source.go +316 -0
- package/plugins/extract-go/transport.go +321 -0
- package/plugins/extract-go/transport_test.go +145 -0
- package/plugins/extract-go/wiring.go +434 -0
- package/plugins/extract-go-nats/describe.go +19 -0
- package/plugins/extract-go-nats/describe_test.go +11 -0
- package/plugins/extract-go-nats/extract.go +177 -0
- package/plugins/extract-go-nats/extract_test.go +389 -0
- package/plugins/extract-go-nats/index.go +379 -0
- package/plugins/extract-go-nats/main.go +42 -0
- package/plugins/extract-go-nats/options.schema.json +21 -0
- package/plugins/extract-go-nats/resolve.go +161 -0
- package/plugins/extract-go-nats/sites.go +224 -0
- package/plugins/extract-graphql/describe.go +19 -0
- package/plugins/extract-graphql/describe_test.go +11 -0
- package/plugins/extract-graphql/extract.go +433 -0
- package/plugins/extract-graphql/extract_test.go +256 -0
- package/plugins/extract-graphql/ids.go +49 -0
- package/plugins/extract-graphql/lex.go +237 -0
- package/plugins/extract-graphql/main.go +51 -0
- package/plugins/extract-graphql/options.schema.json +31 -0
- package/plugins/extract-graphql/parse.go +621 -0
- package/plugins/extract-graphql/parse_test.go +122 -0
- package/plugins/extract-http-clients/describe.go +19 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +705 -0
- package/plugins/extract-http-clients/extract_test.go +1263 -0
- package/plugins/extract-http-clients/main.go +42 -0
- package/plugins/extract-http-clients/options.schema.json +33 -0
- package/plugins/extract-java/README.md +190 -0
- package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
- package/plugins/extract-java/options.schema.json +70 -0
- package/plugins/extract-openapi/describe.go +19 -0
- package/plugins/extract-openapi/describe_test.go +11 -0
- package/plugins/extract-openapi/discover.go +243 -0
- package/plugins/extract-openapi/extract.go +526 -0
- package/plugins/extract-openapi/extract_test.go +545 -0
- package/plugins/extract-openapi/main.go +75 -0
- package/plugins/extract-openapi/options.schema.json +62 -0
- package/plugins/extract-openapi/spec.go +350 -0
- package/plugins/extract-project/README.md +22 -0
- package/plugins/extract-project/describe.go +19 -0
- package/plugins/extract-project/describe_test.go +11 -0
- package/plugins/extract-project/extract.go +221 -0
- package/plugins/extract-project/extract_test.go +109 -0
- package/plugins/extract-project/main.go +41 -0
- package/plugins/extract-project/options.schema.json +48 -0
- package/plugins/extract-proto/ast.go +125 -0
- package/plugins/extract-proto/consumes.go +77 -0
- package/plugins/extract-proto/describe.go +19 -0
- package/plugins/extract-proto/extract.go +293 -0
- package/plugins/extract-proto/extract_test.go +459 -0
- package/plugins/extract-proto/ids.go +89 -0
- package/plugins/extract-proto/ids_test.go +57 -0
- package/plugins/extract-proto/lex.go +285 -0
- package/plugins/extract-proto/main.go +100 -0
- package/plugins/extract-proto/module.go +120 -0
- package/plugins/extract-proto/options.schema.json +51 -0
- package/plugins/extract-proto/parse.go +720 -0
- package/plugins/extract-proto/parse_test.go +307 -0
- package/plugins/extract-proto/provides.go +236 -0
- package/plugins/extract-proto/resolve.go +222 -0
- package/plugins/extract-proto/resolve_test.go +119 -0
- package/plugins/extract-python-kafka/README.md +36 -0
- package/plugins/extract-python-kafka/extract.py +211 -0
- package/plugins/extract-python-kafka/extract_test.py +68 -0
- package/plugins/extract-python-kafka/main.py +59 -0
- package/plugins/extract-python-kafka/options.py +29 -0
- package/plugins/extract-python-kafka/options.schema.json +31 -0
- package/plugins/extract-redis/README.md +11 -0
- package/plugins/extract-redis/describe.go +19 -0
- package/plugins/extract-redis/describe_test.go +11 -0
- package/plugins/extract-redis/extract.go +183 -0
- package/plugins/extract-redis/extract_test.go +168 -0
- package/plugins/extract-redis/keyspaces.go +469 -0
- package/plugins/extract-redis/main.go +44 -0
- package/plugins/extract-redis/options.schema.json +30 -0
- package/plugins/extract-river/describe.go +19 -0
- package/plugins/extract-river/describe_test.go +11 -0
- package/plugins/extract-river/extract.go +507 -0
- package/plugins/extract-river/extract_test.go +132 -0
- package/plugins/extract-river/main.go +42 -0
- package/plugins/extract-river/options.schema.json +21 -0
- package/plugins/extract-rust/Cargo.lock +133 -0
- package/plugins/extract-rust/Cargo.toml +23 -0
- package/plugins/extract-rust/README.md +204 -0
- package/plugins/extract-rust/options.schema.json +65 -0
- package/plugins/extract-rust/rustfmt.toml +1 -0
- package/plugins/extract-sql/ddl.go +893 -0
- package/plugins/extract-sql/ddl_test.go +401 -0
- package/plugins/extract-sql/describe.go +19 -0
- package/plugins/extract-sql/describe_test.go +11 -0
- package/plugins/extract-sql/layout.go +221 -0
- package/plugins/extract-sql/layout_test.go +55 -0
- package/plugins/extract-sql/lineage.go +107 -0
- package/plugins/extract-sql/main.go +141 -0
- package/plugins/extract-sql/maps.go +564 -0
- package/plugins/extract-sql/maps_java.go +117 -0
- package/plugins/extract-sql/maps_rust.go +333 -0
- package/plugins/extract-sql/maps_rust_test.go +70 -0
- package/plugins/extract-sql/maps_test.go +204 -0
- package/plugins/extract-sql/maps_ts.go +398 -0
- package/plugins/extract-sql/maps_ts_test.go +136 -0
- package/plugins/extract-sql/options.schema.json +53 -0
- package/plugins/extract-sql/projection.go +70 -0
- package/plugins/extract-sql/projection_test.go +50 -0
- package/plugins/extract-sql/store.go +420 -0
- package/plugins/extract-sql/store_test.go +233 -0
- package/plugins/extract-sql/view.go +295 -0
- package/plugins/extract-ts/README.md +306 -0
- package/plugins/extract-ts/ast.test.ts +178 -0
- package/plugins/extract-ts/ast.ts +648 -0
- package/plugins/extract-ts/clients.ts +185 -0
- package/plugins/extract-ts/domain.ts +164 -0
- package/plugins/extract-ts/extract.test.ts +208 -0
- package/plugins/extract-ts/extract.ts +200 -0
- package/plugins/extract-ts/flows.ts +783 -0
- package/plugins/extract-ts/graphql.test.ts +73 -0
- package/plugins/extract-ts/graphql.ts +181 -0
- package/plugins/extract-ts/ids.test.ts +21 -0
- package/plugins/extract-ts/ids.ts +52 -0
- package/plugins/extract-ts/lifecycle.ts +138 -0
- package/plugins/extract-ts/main.ts +48 -0
- package/plugins/extract-ts/openapi.test.ts +64 -0
- package/plugins/extract-ts/openapi.ts +99 -0
- package/plugins/extract-ts/operations.ts +93 -0
- package/plugins/extract-ts/options.schema.json +96 -0
- package/plugins/extract-ts/source.test.ts +174 -0
- package/plugins/extract-ts/source.ts +465 -0
- package/plugins/extract-ts/transport.ts +147 -0
- package/plugins/extract-ts/tsconfig.json +13 -0
- package/plugins/extract-ts/wiring.ts +190 -0
- package/plugins/extract-watermill/describe.go +19 -0
- package/plugins/extract-watermill/describe_test.go +11 -0
- package/plugins/extract-watermill/extract.go +1228 -0
- package/plugins/extract-watermill/extract_test.go +234 -0
- package/plugins/extract-watermill/main.go +41 -0
- package/plugins/extract-watermill/options.schema.json +21 -0
- package/plugins/extract-wsdl/describe.go +19 -0
- package/plugins/extract-wsdl/describe_test.go +11 -0
- package/plugins/extract-wsdl/extract.go +148 -0
- package/plugins/extract-wsdl/extract_test.go +45 -0
- package/plugins/extract-wsdl/main.go +53 -0
- package/plugins/extract-wsdl/options.schema.json +59 -0
- package/plugins/fetch-bsr/auth.go +114 -0
- package/plugins/fetch-bsr/bsr.go +240 -0
- package/plugins/fetch-bsr/cache.go +66 -0
- package/plugins/fetch-bsr/describe.go +19 -0
- package/plugins/fetch-bsr/fetch.go +192 -0
- package/plugins/fetch-bsr/fetch_test.go +465 -0
- package/plugins/fetch-bsr/lock.go +71 -0
- package/plugins/fetch-bsr/main.go +117 -0
- package/plugins/fetch-bsr/options.schema.json +42 -0
- package/plugins/fetch-csr/auth.go +95 -0
- package/plugins/fetch-csr/cache.go +69 -0
- package/plugins/fetch-csr/describe.go +19 -0
- package/plugins/fetch-csr/describe_test.go +11 -0
- package/plugins/fetch-csr/fetch.go +254 -0
- package/plugins/fetch-csr/fetch_test.go +494 -0
- package/plugins/fetch-csr/lock.go +95 -0
- package/plugins/fetch-csr/main.go +122 -0
- package/plugins/fetch-csr/options.schema.json +42 -0
- package/plugins/fetch-csr/registry.go +209 -0
- package/plugins/fetch-git/cache.go +65 -0
- package/plugins/fetch-git/describe.go +19 -0
- package/plugins/fetch-git/describe_test.go +11 -0
- package/plugins/fetch-git/fetch.go +181 -0
- package/plugins/fetch-git/fetch_test.go +332 -0
- package/plugins/fetch-git/git.go +169 -0
- package/plugins/fetch-git/lock.go +72 -0
- package/plugins/fetch-git/main.go +127 -0
- package/plugins/fetch-git/offline.go +39 -0
- package/plugins/fetch-git/options.schema.json +42 -0
- package/plugins/fetch-git/pin.go +90 -0
- package/plugins/fetch-git/pin_test.go +74 -0
- package/plugins/gen-backstage/describe.go +17 -0
- package/plugins/gen-backstage/main.go +22 -0
- package/plugins/gen-backstage/options.schema.json +30 -0
- package/plugins/gen-backstage/plugin.go +473 -0
- package/plugins/gen-backstage/plugin_test.go +145 -0
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +162 -0
- package/plugins/gen-markdown/aggregate.go +362 -0
- package/plugins/gen-markdown/canonical.go +93 -0
- package/plugins/gen-markdown/context.go +90 -0
- package/plugins/gen-markdown/coverage_test.go +89 -0
- package/plugins/gen-markdown/describe.go +19 -0
- package/plugins/gen-markdown/describe_test.go +11 -0
- package/plugins/gen-markdown/external.go +71 -0
- package/plugins/gen-markdown/flow.go +277 -0
- package/plugins/gen-markdown/glossary.go +76 -0
- package/plugins/gen-markdown/glossary_test.go +148 -0
- package/plugins/gen-markdown/llms.go +302 -0
- package/plugins/gen-markdown/main.go +22 -0
- package/plugins/gen-markdown/markdown.go +254 -0
- package/plugins/gen-markdown/markdown_test.go +100 -0
- package/plugins/gen-markdown/module.go +107 -0
- package/plugins/gen-markdown/options.schema.json +16 -0
- package/plugins/gen-markdown/plugin.go +39 -0
- package/plugins/gen-markdown/quality_test.go +176 -0
- package/plugins/gen-markdown/redis_test.go +38 -0
- package/plugins/gen-markdown/render.go +344 -0
- package/plugins/gen-markdown/render_test.go +186 -0
- package/plugins/gen-markdown/service.go +456 -0
- package/plugins/gen-markdown/source.go +177 -0
- package/plugins/gen-markdown/store.go +201 -0
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +17 -0
- package/plugins/gen-mermaid/main.go +22 -0
- package/plugins/gen-mermaid/options.schema.json +10 -0
- package/plugins/gen-mermaid/plugin.go +104 -0
- package/plugins/gen-mermaid/plugin_test.go +33 -0
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/pyplugin/catalog.py +209 -0
- package/plugins/pyplugin/celery_conf.py +436 -0
- package/plugins/pyplugin/celery_tasks.py +76 -0
- package/plugins/pyplugin/kafka.py +570 -0
- package/plugins/pyplugin/names.py +38 -0
- package/plugins/pyplugin/protocol.py +79 -0
- package/plugins/pyplugin/source.py +285 -0
- package/plugins/verify-codeowners/describe.go +19 -0
- package/plugins/verify-codeowners/describe_test.go +11 -0
- package/plugins/verify-codeowners/main.go +75 -0
- package/plugins/verify-codeowners/match.go +85 -0
- package/plugins/verify-codeowners/match_test.go +47 -0
- package/plugins/verify-codeowners/options.schema.json +15 -0
- package/plugins/verify-codeowners/owners.go +164 -0
- package/plugins/verify-codeowners/owners_test.go +225 -0
- package/plugins/verify-codeowners/parse.go +90 -0
- package/plugins/verify-codeowners/parse_test.go +62 -0
- package/plugins/verify-otel/describe.go +19 -0
- package/plugins/verify-otel/describe_test.go +11 -0
- package/plugins/verify-otel/main.go +53 -0
- package/plugins/verify-otel/match.go +336 -0
- package/plugins/verify-otel/options.schema.json +30 -0
- package/plugins/verify-otel/otlp.go +200 -0
- package/plugins/verify-otel/verify.go +734 -0
- package/plugins/verify-otel/verify_test.go +460 -0
- package/portolan.json +1073 -0
- package/public/favicon.svg +12 -0
- package/schema/portolan.schema.json +2558 -0
- package/scripts/README.md +53 -0
- package/scripts/build-report.mjs +60 -0
- package/scripts/build-report.test.mjs +44 -0
- package/scripts/builtin-plugins.mjs +49 -0
- package/scripts/catalog-sources.mjs +67 -0
- package/scripts/diff.mjs +315 -0
- package/scripts/diff.test.mjs +151 -0
- package/scripts/forge-comment.mjs +144 -0
- package/scripts/forge-comment.test.mjs +151 -0
- package/scripts/forge-release.mjs +138 -0
- package/scripts/forge-release.test.mjs +106 -0
- package/scripts/forge.mjs +61 -0
- package/scripts/gen-likec4.mjs +854 -0
- package/scripts/gen-likec4.test.mjs +258 -0
- package/scripts/gen.mjs +603 -0
- package/scripts/generated-docs.test.mjs +66 -0
- package/scripts/local-api.mjs +1192 -0
- package/scripts/local-api.test.mjs +387 -0
- package/scripts/manifest.mjs +214 -0
- package/scripts/manifest.test.mjs +148 -0
- package/scripts/output-path.mjs +79 -0
- package/scripts/output-path.test.mjs +45 -0
- package/scripts/package-smoke.mjs +37 -0
- package/scripts/plugin-host.mjs +419 -0
- package/scripts/plugin-host.test.mjs +109 -0
- package/scripts/plugin-wasm-worker.mjs +34 -0
- package/scripts/run-builtin.mjs +81 -0
- package/scripts/schema.mjs +357 -0
- package/scripts/site-docs.mjs +295 -0
- package/scripts/site-docs.test.mjs +181 -0
- package/scripts/vendor-lock.mjs +58 -0
- package/scripts/vendor-lock.test.mjs +69 -0
- package/src/README.md +38 -0
- package/src/app/App.tsx +448 -0
- package/src/app/BranchPicker.test.ts +46 -0
- package/src/app/BranchPicker.tsx +153 -0
- package/src/app/Breadcrumbs.test.ts +38 -0
- package/src/app/Breadcrumbs.tsx +223 -0
- package/src/app/BuildStamp.tsx +146 -0
- package/src/app/CommandPalette.tsx +292 -0
- package/src/app/HashScroll.tsx +45 -0
- package/src/app/Sidebar.tsx +1742 -0
- package/src/app/TopBar.tsx +393 -0
- package/src/app/density.tsx +64 -0
- package/src/app/forge-access.test.ts +27 -0
- package/src/app/forge-access.tsx +80 -0
- package/src/app/panels.tsx +141 -0
- package/src/app/pins.tsx +242 -0
- package/src/app/query-client.ts +24 -0
- package/src/app/responsive.ts +55 -0
- package/src/app/shortcuts.test.ts +39 -0
- package/src/app/shortcuts.tsx +282 -0
- package/src/app/theme.tsx +51 -0
- package/src/app/title.test.ts +14 -0
- package/src/app/title.ts +30 -0
- package/src/app/toast.tsx +59 -0
- package/src/app/tree-keys.test.ts +80 -0
- package/src/app/tree-keys.ts +87 -0
- package/src/app/ui-store.ts +52 -0
- package/src/catalog-error.test.ts +105 -0
- package/src/catalog-profile.test.ts +59 -0
- package/src/catalog-profile.ts +101 -0
- package/src/catalog-stores.test.ts +431 -0
- package/src/catalog.test.ts +1215 -0
- package/src/catalog.ts +3125 -0
- package/src/chat/Cards.tsx +408 -0
- package/src/chat/ChatPanel.tsx +79 -0
- package/src/chat/ChatSettings.tsx +219 -0
- package/src/chat/Composer.tsx +99 -0
- package/src/chat/Conversation.tsx +281 -0
- package/src/chat/Header.tsx +46 -0
- package/src/chat/Message.tsx +49 -0
- package/src/chat/Notice.tsx +195 -0
- package/src/chat/Starter.tsx +67 -0
- package/src/chat/Steps.tsx +45 -0
- package/src/chat/Waiting.tsx +87 -0
- package/src/chat/flags.test.ts +59 -0
- package/src/chat/flags.ts +155 -0
- package/src/chat/linkify.test.ts +30 -0
- package/src/chat/linkify.ts +45 -0
- package/src/chat/prefs.ts +56 -0
- package/src/chat/prompt.test.ts +43 -0
- package/src/chat/prompt.ts +157 -0
- package/src/chat/store.ts +35 -0
- package/src/chat/tools.ts +58 -0
- package/src/chat/transport.ts +70 -0
- package/src/components/AdrRow.tsx +29 -0
- package/src/components/AnchorLink.tsx +67 -0
- package/src/components/ApiReference.tsx +134 -0
- package/src/components/AsyncApiReference.tsx +120 -0
- package/src/components/CatalogStamp.tsx +155 -0
- package/src/components/ChannelRows.tsx +132 -0
- package/src/components/CommandRows.tsx +139 -0
- package/src/components/DiagramSkeleton.tsx +71 -0
- package/src/components/EditorLink.tsx +85 -0
- package/src/components/FieldTree.tsx +305 -0
- package/src/components/FlowTrigger.tsx +34 -0
- package/src/components/Ident.tsx +89 -0
- package/src/components/Integrations.tsx +243 -0
- package/src/components/LifecycleDiagram.tsx +110 -0
- package/src/components/MachineDocs.tsx +42 -0
- package/src/components/Markdown.tsx +173 -0
- package/src/components/MarkdownTable.tsx +206 -0
- package/src/components/Mermaid.tsx +211 -0
- package/src/components/MethodRows.tsx +309 -0
- package/src/components/Overlay.tsx +125 -0
- package/src/components/PageHeader.tsx +189 -0
- package/src/components/RowActions.tsx +97 -0
- package/src/components/SchemaDocument.tsx +89 -0
- package/src/components/Select.tsx +126 -0
- package/src/components/ShapeRows.tsx +118 -0
- package/src/components/SourceDoc.tsx +82 -0
- package/src/components/SourcePreview.tsx +441 -0
- package/src/components/TabRow.tsx +182 -0
- package/src/components/TechIcon.tsx +34 -0
- package/src/components/Toc.tsx +132 -0
- package/src/components/WhatLinksHere.tsx +273 -0
- package/src/components/WsdlReference.tsx +84 -0
- package/src/components/ddd-icons.tsx +107 -0
- package/src/components/kind.tsx +157 -0
- package/src/components/logo.tsx +61 -0
- package/src/components/primitives.tsx +264 -0
- package/src/data.test.ts +18 -0
- package/src/data.ts +202 -0
- package/src/enrich.test.ts +1150 -0
- package/src/enrich.ts +672 -0
- package/src/er/ErCanvas.tsx +533 -0
- package/src/er/RedisSchema.tsx +74 -0
- package/src/er/RelationCard.tsx +267 -0
- package/src/er/StoreHeader.tsx +155 -0
- package/src/er/TableNode.tsx +84 -0
- package/src/er/ViewNode.tsx +83 -0
- package/src/er/layout.test.ts +115 -0
- package/src/er/layout.ts +77 -0
- package/src/er/lineage.test.ts +140 -0
- package/src/er/lineage.ts +107 -0
- package/src/er/markers.tsx +79 -0
- package/src/er/spec.test.ts +344 -0
- package/src/er/spec.ts +426 -0
- package/src/flow/ChainList.tsx +162 -0
- package/src/flow/FlowTable.tsx +123 -0
- package/src/flow/FlowToolbar.tsx +372 -0
- package/src/flow/StepDetail.tsx +380 -0
- package/src/flow/StepRail.tsx +428 -0
- package/src/flow/answers.test.ts +50 -0
- package/src/flow/answers.ts +58 -0
- package/src/flow/chain.test.ts +383 -0
- package/src/flow/chain.ts +353 -0
- package/src/flow/chapters.test.ts +185 -0
- package/src/flow/chapters.ts +225 -0
- package/src/flow/continues.test.ts +102 -0
- package/src/flow/continues.ts +65 -0
- package/src/flow/cross-context.test.ts +97 -0
- package/src/flow/cross-context.ts +35 -0
- package/src/flow/mermaid.test.ts +22 -0
- package/src/flow/mermaid.ts +141 -0
- package/src/flow/outline.test.ts +212 -0
- package/src/flow/outline.ts +170 -0
- package/src/flow/paths.test.ts +129 -0
- package/src/flow/paths.ts +147 -0
- package/src/flow/prefs.test.ts +80 -0
- package/src/flow/prefs.ts +104 -0
- package/src/graph/DependencyGraph.tsx +283 -0
- package/src/graph/DependencyNodes.tsx +162 -0
- package/src/graph/EdgeLabel.tsx +61 -0
- package/src/graph/FocusedEventGraph.tsx +211 -0
- package/src/graph/GraphToolbar.tsx +187 -0
- package/src/graph/RoutedEdge.tsx +80 -0
- package/src/graph/dependency-layout.test.ts +360 -0
- package/src/graph/dependency-layout.ts +444 -0
- package/src/graph/elk.test.ts +70 -0
- package/src/graph/elk.ts +287 -0
- package/src/graph/nodes.tsx +97 -0
- package/src/graph/theme.ts +81 -0
- package/src/graph/useElkFlow.ts +125 -0
- package/src/index.css +1547 -0
- package/src/language/InLanguage.tsx +40 -0
- package/src/language/TermCard.tsx +132 -0
- package/src/language/cards.test.ts +114 -0
- package/src/language/cards.ts +111 -0
- package/src/lib/admonition.test.ts +49 -0
- package/src/lib/admonition.ts +48 -0
- package/src/lib/adr.test.ts +131 -0
- package/src/lib/adr.ts +68 -0
- package/src/lib/api.test.ts +122 -0
- package/src/lib/api.ts +135 -0
- package/src/lib/backlinks.test.ts +330 -0
- package/src/lib/backlinks.ts +567 -0
- package/src/lib/branch-compare.ts +16 -0
- package/src/lib/build-info.test.ts +76 -0
- package/src/lib/build-info.ts +80 -0
- package/src/lib/catalog-diff.test.ts +275 -0
- package/src/lib/catalog-diff.ts +655 -0
- package/src/lib/clipboard.ts +43 -0
- package/src/lib/comparison-memory.test.ts +40 -0
- package/src/lib/comparison-memory.ts +39 -0
- package/src/lib/context-color.ts +27 -0
- package/src/lib/context-map.test.ts +148 -0
- package/src/lib/context-map.ts +399 -0
- package/src/lib/data-model.test.ts +245 -0
- package/src/lib/data-model.ts +399 -0
- package/src/lib/data-problems.test.ts +178 -0
- package/src/lib/data-problems.ts +314 -0
- package/src/lib/derive.test.ts +251 -0
- package/src/lib/derive.ts +515 -0
- package/src/lib/editor-link.test.ts +125 -0
- package/src/lib/editor-link.ts +96 -0
- package/src/lib/editor-prefs.ts +27 -0
- package/src/lib/event-graph.test.ts +237 -0
- package/src/lib/event-graph.ts +327 -0
- package/src/lib/export-canvas.ts +37 -0
- package/src/lib/export-file.test.ts +30 -0
- package/src/lib/export-file.ts +20 -0
- package/src/lib/flow-tree.test.ts +177 -0
- package/src/lib/flow-tree.ts +160 -0
- package/src/lib/forge-refs.test.ts +50 -0
- package/src/lib/forge-refs.ts +60 -0
- package/src/lib/format.test.ts +35 -0
- package/src/lib/format.ts +64 -0
- package/src/lib/github-catalog.test.ts +301 -0
- package/src/lib/github-catalog.ts +610 -0
- package/src/lib/integrations.test.ts +119 -0
- package/src/lib/integrations.ts +133 -0
- package/src/lib/kinds.test.ts +91 -0
- package/src/lib/kinds.ts +204 -0
- package/src/lib/lifecycle.test.ts +93 -0
- package/src/lib/lifecycle.ts +165 -0
- package/src/lib/local-api.ts +194 -0
- package/src/lib/motion-features.ts +11 -0
- package/src/lib/motion.test.ts +120 -0
- package/src/lib/motion.tsx +269 -0
- package/src/lib/palette-recent.test.ts +80 -0
- package/src/lib/palette-recent.ts +90 -0
- package/src/lib/palette.test.ts +365 -0
- package/src/lib/palette.ts +593 -0
- package/src/lib/pins.test.ts +139 -0
- package/src/lib/pins.ts +130 -0
- package/src/lib/problems.test.ts +112 -0
- package/src/lib/project-preview.ts +5 -0
- package/src/lib/proto-problems.test.ts +121 -0
- package/src/lib/proto-problems.ts +65 -0
- package/src/lib/queries.test.ts +113 -0
- package/src/lib/queries.ts +131 -0
- package/src/lib/registry.test.ts +219 -0
- package/src/lib/registry.ts +208 -0
- package/src/lib/scenarios.ts +547 -0
- package/src/lib/setup-info.test.ts +199 -0
- package/src/lib/setup-info.ts +415 -0
- package/src/lib/shape.test.ts +289 -0
- package/src/lib/shape.ts +454 -0
- package/src/lib/sidebar-prefs.test.ts +32 -0
- package/src/lib/sidebar-prefs.ts +75 -0
- package/src/lib/source-code.test.ts +159 -0
- package/src/lib/source-code.ts +286 -0
- package/src/lib/source-doc.test.ts +131 -0
- package/src/lib/source-doc.ts +103 -0
- package/src/lib/source-highlight.test.ts +29 -0
- package/src/lib/source-highlight.ts +24 -0
- package/src/lib/source-link.test.ts +153 -0
- package/src/lib/source-link.ts +201 -0
- package/src/lib/spec-files.test.ts +24 -0
- package/src/lib/spec-files.ts +23 -0
- package/src/lib/table-directive.test.ts +139 -0
- package/src/lib/table-directive.ts +106 -0
- package/src/lib/tech.test.ts +120 -0
- package/src/lib/tech.ts +421 -0
- package/src/lib/terms.test.ts +67 -0
- package/src/lib/terms.ts +179 -0
- package/src/lib/wire-problems.test.ts +263 -0
- package/src/lib/wire-problems.ts +281 -0
- package/src/likec4/C4View.tsx +55 -0
- package/src/likec4/CanvasBridge.tsx +187 -0
- package/src/likec4/FlowView.tsx +235 -0
- package/src/likec4/InteractiveView.tsx +144 -0
- package/src/likec4/canvas-viewport.test.ts +149 -0
- package/src/likec4/canvas-viewport.ts +135 -0
- package/src/likec4/flow-edges.test.ts +51 -0
- package/src/likec4/flow-edges.ts +89 -0
- package/src/likec4/frame-theme.test.ts +76 -0
- package/src/likec4/frame-theme.ts +101 -0
- package/src/likec4/highlight-css.test.ts +49 -0
- package/src/likec4/highlight-css.ts +52 -0
- package/src/likec4/ids.test.ts +151 -0
- package/src/likec4/ids.ts +115 -0
- package/src/likec4/levels.tsx +70 -0
- package/src/likec4/mapping.test.ts +66 -0
- package/src/likec4/mapping.ts +54 -0
- package/src/likec4/reserved.json +141 -0
- package/src/likec4/view-index.ts +78 -0
- package/src/main.tsx +13 -0
- package/src/map/ContextMapGraph.tsx +274 -0
- package/src/map/ContextNode.tsx +70 -0
- package/src/map/FloatingEdge.tsx +86 -0
- package/src/merge.test.ts +1038 -0
- package/src/merge.ts +1004 -0
- package/src/pages/AdrDetail.tsx +242 -0
- package/src/pages/AdrIndex.tsx +114 -0
- package/src/pages/AggregatePage.tsx +727 -0
- package/src/pages/BlockPage.tsx +478 -0
- package/src/pages/CatalogFailure.tsx +111 -0
- package/src/pages/Changes.tsx +313 -0
- package/src/pages/ContextMap.tsx +534 -0
- package/src/pages/ContextPage.tsx +346 -0
- package/src/pages/EnumPage.tsx +282 -0
- package/src/pages/EventPage.tsx +686 -0
- package/src/pages/ExternalPage.tsx +204 -0
- package/src/pages/FlowDetail.tsx +663 -0
- package/src/pages/FlowIndex.tsx +303 -0
- package/src/pages/GraphPage.tsx +217 -0
- package/src/pages/Language.tsx +257 -0
- package/src/pages/ModulePage.tsx +366 -0
- package/src/pages/NotFound.tsx +54 -0
- package/src/pages/Overview.tsx +344 -0
- package/src/pages/Problems.tsx +360 -0
- package/src/pages/RegistryIndex.tsx +118 -0
- package/src/pages/ServicePage.tsx +746 -0
- package/src/pages/Settings.tsx +975 -0
- package/src/pages/StorePage.tsx +114 -0
- package/src/peek/PeekCard.test.ts +35 -0
- package/src/peek/PeekCard.tsx +118 -0
- package/src/peek/PeekLayer.tsx +147 -0
- package/src/peek/model.test.ts +156 -0
- package/src/peek/model.ts +253 -0
- package/src/routes.test.ts +222 -0
- package/src/routes.ts +449 -0
- package/src/selection/DetailPanel.tsx +1407 -0
- package/src/selection/hash.test.ts +73 -0
- package/src/selection/hash.ts +58 -0
- package/src/selection/model.test.ts +199 -0
- package/src/selection/model.ts +473 -0
- package/src/selection/pages.test.ts +115 -0
- package/src/selection/pages.ts +268 -0
- package/src/selection/store.test.ts +62 -0
- package/src/selection/store.ts +46 -0
- package/src/selection/sync.tsx +99 -0
- package/src/table/ColumnsMenu.tsx +72 -0
- package/src/table/DataTable.tsx +780 -0
- package/src/table/Facets.tsx +54 -0
- package/src/table/GroupMenu.tsx +69 -0
- package/src/table/Toolbar.tsx +190 -0
- package/src/table/cells.tsx +117 -0
- package/src/table/compare.test.ts +117 -0
- package/src/table/compare.ts +196 -0
- package/src/table/export.test.ts +88 -0
- package/src/table/export.ts +66 -0
- package/src/table/facet-groups.test.ts +74 -0
- package/src/table/facet-groups.ts +56 -0
- package/src/table/grouping.test.ts +84 -0
- package/src/table/grouping.ts +82 -0
- package/src/table/infer.test.ts +57 -0
- package/src/table/infer.ts +50 -0
- package/src/table/persist.test.ts +76 -0
- package/src/table/persist.ts +112 -0
- package/src/table/sort-url.test.ts +75 -0
- package/src/table/sort-url.ts +52 -0
- package/src/table/types.ts +80 -0
- package/src/table/useTableState.ts +220 -0
- package/src/test-catalog.ts +7 -0
- package/src/testing/estate/data__catalog.json +2847 -0
- package/src/testing/estate/data__flows.json +1724 -0
- package/src/testing/estate/examples__auth__portolan__api.json +212 -0
- package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
- package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
- package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
- package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
- package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
- package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
- package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
- package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
- package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
- package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
- package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
- package/src/testing/estate.ts +49 -0
- package/src/trail/Trail.tsx +81 -0
- package/src/trail/model.test.ts +157 -0
- package/src/trail/model.ts +189 -0
- package/src/trail/record.tsx +32 -0
- package/src/trail/store.test.ts +66 -0
- package/src/trail/store.ts +102 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +171 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
// Reading TypeScript files: the few shapes the extractor looks at, pulled out
|
|
2
|
+
// of the syntax tree once so the modules above it work on names and strings.
|
|
3
|
+
//
|
|
4
|
+
// No type checker: everything here is resolved by name and by import - the
|
|
5
|
+
// resolver is oxc's, so a tsconfig `paths` alias and a workspace package
|
|
6
|
+
// resolve as Node and TypeScript would - which is all a layout that is the
|
|
7
|
+
// claim needs. The tree itself is
|
|
8
|
+
// oxc-parser's, through `ast.ts`.
|
|
9
|
+
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
|
+
import { ResolverFactory } from "oxc-resolver";
|
|
13
|
+
import {
|
|
14
|
+
parse,
|
|
15
|
+
docBlock,
|
|
16
|
+
docParams,
|
|
17
|
+
docReturns,
|
|
18
|
+
docType,
|
|
19
|
+
docTypedefs,
|
|
20
|
+
interfaceFromDoc,
|
|
21
|
+
isAssign,
|
|
22
|
+
isExprStmt,
|
|
23
|
+
isIdent,
|
|
24
|
+
isSourceFile,
|
|
25
|
+
jsdoc as docOf,
|
|
26
|
+
firstTokenOf,
|
|
27
|
+
isArray,
|
|
28
|
+
isBoolean,
|
|
29
|
+
isClassDecl,
|
|
30
|
+
isExportNamed,
|
|
31
|
+
isFunctionDecl,
|
|
32
|
+
isInterface,
|
|
33
|
+
isMethod,
|
|
34
|
+
isNew,
|
|
35
|
+
isNumber,
|
|
36
|
+
isParamProperty,
|
|
37
|
+
isPropertyDef,
|
|
38
|
+
isString,
|
|
39
|
+
keyName,
|
|
40
|
+
lineOf,
|
|
41
|
+
paramIdent,
|
|
42
|
+
parseDoc,
|
|
43
|
+
text as textOf,
|
|
44
|
+
thisMember,
|
|
45
|
+
typeFromDoc,
|
|
46
|
+
typeText,
|
|
47
|
+
SOURCE_EXTENSIONS,
|
|
48
|
+
} from "./ast.ts";
|
|
49
|
+
import type { BlockStatement, ClassDeclaration, DocBlock, FunctionNode, InterfaceDeclaration, MethodDefinition, Node, Parsed, Typed } from "./ast.ts";
|
|
50
|
+
|
|
51
|
+
/** What a doc comment says about the thing it sits on, beyond its prose. */
|
|
52
|
+
export interface Documented {
|
|
53
|
+
doc: string;
|
|
54
|
+
/** The `@deprecated` reason, "" for a bare tag, absent when not deprecated. */
|
|
55
|
+
deprecated?: string;
|
|
56
|
+
/** The `@example` bodies, when the comment had any. */
|
|
57
|
+
examples?: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface Field extends Documented {
|
|
61
|
+
name: string;
|
|
62
|
+
type: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface Method {
|
|
66
|
+
name: string;
|
|
67
|
+
node: MethodDefinition;
|
|
68
|
+
/** The parameters, in order. */
|
|
69
|
+
params: Node[];
|
|
70
|
+
/** The body, or null for an overload signature. */
|
|
71
|
+
body: BlockStatement | null;
|
|
72
|
+
/** The return type as written, or "" when it is not. */
|
|
73
|
+
returns: string;
|
|
74
|
+
isStatic: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ClassInfo extends Documented {
|
|
78
|
+
name: string;
|
|
79
|
+
node: ClassDeclaration;
|
|
80
|
+
exported: boolean;
|
|
81
|
+
fields: Field[];
|
|
82
|
+
/** Constructor parameters, in order: what a use case holds as ports. */
|
|
83
|
+
params: { name: string; type: string }[];
|
|
84
|
+
methods: Map<string, Method>;
|
|
85
|
+
/** `readonly name = "x"` or `static readonly name = "x"`, when present. */
|
|
86
|
+
nameLiteral: string | undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface Import {
|
|
90
|
+
/** The local name, or the imported one for a namespace import. */
|
|
91
|
+
local: string;
|
|
92
|
+
/** What it is called in the module it came from; "*" for a namespace. */
|
|
93
|
+
imported: string;
|
|
94
|
+
specifier: string;
|
|
95
|
+
/** The file the specifier resolves to - relative, aliased or a workspace package - or undefined for a dependency. */
|
|
96
|
+
file: string | undefined;
|
|
97
|
+
typeOnly: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* An interface with the text its nodes point into: the file's own for one
|
|
102
|
+
* written in the syntax, its own for one declared by a `@typedef`, which is
|
|
103
|
+
* parsed apart from the file it sits in.
|
|
104
|
+
*/
|
|
105
|
+
export interface Iface {
|
|
106
|
+
p: Parsed;
|
|
107
|
+
node: InterfaceDeclaration;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface Source {
|
|
111
|
+
path: string;
|
|
112
|
+
parsed: Parsed;
|
|
113
|
+
classes: ClassInfo[];
|
|
114
|
+
imports: Import[];
|
|
115
|
+
/** Exported interfaces, by name - `interface X` in TypeScript, `@typedef {{…}} X` in JavaScript. */
|
|
116
|
+
interfaces: Map<string, Iface>;
|
|
117
|
+
/** Exported functions, by name. */
|
|
118
|
+
functions: Map<string, FunctionNode>;
|
|
119
|
+
/** `import("./x.js")` with a literal request: what the file loads lazily, resolved the same way. */
|
|
120
|
+
dynamicImports: { specifier: string; file: string | undefined }[];
|
|
121
|
+
/** Syntax errors, `file:line` and message. The tree past the first is partial, and so is what was read from it. */
|
|
122
|
+
errors: { at: string; message: string }[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const cache = new Map<string, Source | null>();
|
|
126
|
+
|
|
127
|
+
/** Every source read so far that sits under a root, for reporting what was wrong with them once the reading is done. */
|
|
128
|
+
export function sourcesUnder(root: string): Source[] {
|
|
129
|
+
if (!existsSync(root)) return [];
|
|
130
|
+
const prefix = realpathSync(root) + "/";
|
|
131
|
+
return [...cache.values()].filter((s): s is Source => s !== null && s.path.startsWith(prefix));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A file, read once. Keyed by its real path, because the resolver hands back
|
|
136
|
+
* real paths - a workspace package is followed through its symlink - and a
|
|
137
|
+
* file reached by two names is still one file.
|
|
138
|
+
*/
|
|
139
|
+
export function readSource(path: string): Source | null {
|
|
140
|
+
const asked = resolve(path);
|
|
141
|
+
if (!existsSync(asked)) return null;
|
|
142
|
+
const key = realpathSync(asked);
|
|
143
|
+
const hit = cache.get(key);
|
|
144
|
+
if (hit !== undefined) return hit;
|
|
145
|
+
const parsed = parse(key, readFileSync(key, "utf8"));
|
|
146
|
+
const source: Source = {
|
|
147
|
+
path: key,
|
|
148
|
+
parsed,
|
|
149
|
+
classes: [],
|
|
150
|
+
imports: importsOf(parsed, key),
|
|
151
|
+
interfaces: new Map(),
|
|
152
|
+
functions: new Map(),
|
|
153
|
+
dynamicImports: dynamicImportsOf(parsed, key),
|
|
154
|
+
errors: parsed.errors.map((e) => ({ at: `${key}:${lineOf(parsed, e.labels?.[0]?.start ?? 0)}`, message: e.message })),
|
|
155
|
+
};
|
|
156
|
+
for (const stmt of parsed.program.body) {
|
|
157
|
+
const exported = isExportNamed(stmt);
|
|
158
|
+
const decl = exported ? stmt.declaration : stmt;
|
|
159
|
+
if (!decl) continue;
|
|
160
|
+
if (isInterface(decl)) source.interfaces.set(decl.id.name, { p: parsed, node: decl });
|
|
161
|
+
else if (isFunctionDecl(decl) && decl.id) source.functions.set(decl.id.name, decl);
|
|
162
|
+
}
|
|
163
|
+
// Typedefs before classes: a class's `@param {Port}` may name a typedef,
|
|
164
|
+
// and the typedef's `import(...)` is what says where the port came from.
|
|
165
|
+
if (parsed.js) readTypedefs(source);
|
|
166
|
+
for (const stmt of parsed.program.body) {
|
|
167
|
+
const exported = isExportNamed(stmt);
|
|
168
|
+
const decl = exported ? stmt.declaration : stmt;
|
|
169
|
+
if (decl && isClassDecl(decl) && decl.id) source.classes.push(classInfo(source, decl, exported ? stmt : undefined));
|
|
170
|
+
}
|
|
171
|
+
cache.set(key, source);
|
|
172
|
+
return source;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Every `@typedef` in a JavaScript file, wherever its comment sits - a typedef
|
|
177
|
+
* is attached to nothing, so the comments are read rather than the tree.
|
|
178
|
+
* `@typedef {import("./port.js").Port} Port` is an import of a type and is
|
|
179
|
+
* filed as one. `@typedef {{ save(): Promise<void> }} Port`, and
|
|
180
|
+
* `@typedef {Object} Port` with `@property` lines under it, is an interface,
|
|
181
|
+
* and becomes the node `interface Port {…}` would have been. Any other
|
|
182
|
+
* typedef - a union, an alias of a primitive - names nothing the extractor
|
|
183
|
+
* follows, and is left alone.
|
|
184
|
+
*/
|
|
185
|
+
function readTypedefs(src: Source): void {
|
|
186
|
+
for (const c of src.parsed.comments) {
|
|
187
|
+
if (c.type !== "Block" || !c.value.startsWith("*")) continue;
|
|
188
|
+
for (const def of docTypedefs(parseDoc(c.value.slice(1)))) {
|
|
189
|
+
const imported = /^import\(\s*["']([^"']+)["']\s*\)\.([\w$]+)$/.exec(def.type);
|
|
190
|
+
if (imported) {
|
|
191
|
+
addImport(src, { local: def.name, imported: imported[2]!, specifier: imported[1]!, file: resolveImport(src.path, imported[1]!), typeOnly: true });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
let body: string | undefined;
|
|
195
|
+
if (/^\{[\s\S]*\}$/.test(def.type)) body = docTypeIn(src, def.type.slice(1, -1));
|
|
196
|
+
else if (/^object$/i.test(def.type)) body = def.properties.map((p) => `${p.name}: ${docTypeIn(src, p.type)};`).join(" ");
|
|
197
|
+
if (body === undefined) continue;
|
|
198
|
+
const iface = interfaceFromDoc(def.name, body);
|
|
199
|
+
if (iface) src.interfaces.set(def.name, iface);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** An import a doc comment implies, added once: the first mention of a local name wins, as it would in the syntax. */
|
|
205
|
+
function addImport(src: Source, imp: Import): void {
|
|
206
|
+
if (!src.imports.some((i) => i.local === imp.local)) src.imports.push(imp);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A type expression from a doc comment, with each `import("./x.js").Y` in it
|
|
211
|
+
* read as the import it is and shortened to `Y`, so that what is left is a
|
|
212
|
+
* name the rest of the reader resolves the way it resolves any other: through
|
|
213
|
+
* `src.imports`. Returns the expression as the syntax would have written it.
|
|
214
|
+
*/
|
|
215
|
+
export function docTypeIn(src: Source, expr: string): string {
|
|
216
|
+
return expr.replace(/\btypeof\s+import\(\s*["']([^"']+)["']\s*\)\.([\w$]+)|\bimport\(\s*["']([^"']+)["']\s*\)\.([\w$]+)/g, (_m, s1: string | undefined, n1: string | undefined, s2: string | undefined, n2: string | undefined) => {
|
|
217
|
+
const specifier = (s1 ?? s2)!;
|
|
218
|
+
const name = (n1 ?? n2)!;
|
|
219
|
+
addImport(src, { local: name, imported: name, specifier, file: resolveImport(src.path, specifier), typeOnly: true });
|
|
220
|
+
return s1 ? `typeof ${name}` : name;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** A doc-comment type as the annotation it stands in for, or undefined when there is none or it does not parse. */
|
|
225
|
+
function docTyped(src: Source, expr: string): Typed | undefined {
|
|
226
|
+
return expr ? typeFromDoc(docTypeIn(src, expr)) : undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** The files the extractor reads in a directory, sorted: TypeScript and JavaScript alike, tests and declarations left out. */
|
|
230
|
+
export function sourceFiles(dir: string): string[] {
|
|
231
|
+
if (!existsSync(dir)) return [];
|
|
232
|
+
return readdirSync(dir)
|
|
233
|
+
.filter(isSourceFile)
|
|
234
|
+
.sort()
|
|
235
|
+
.map((f) => join(dir, f));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** `<dir>/<base>.ts`, or the `.js`/`.mjs` beside where it would have been: the fixed names of the layout, in whichever language the tree is written. */
|
|
239
|
+
export function sourceNamed(dir: string, base: string): string {
|
|
240
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
241
|
+
const path = join(dir, base + ext);
|
|
242
|
+
if (existsSync(path)) return path;
|
|
243
|
+
}
|
|
244
|
+
return join(dir, `${base}.ts`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Node's resolution, with what a TypeScript tree adds to it: `./x.js` means
|
|
249
|
+
* `./x.ts`, a bare `./x` tries every source extension and then an index,
|
|
250
|
+
* `@app/domain/basket` is whatever the nearest tsconfig's `paths` say, and a
|
|
251
|
+
* workspace package is followed through its symlink to the source it is. The
|
|
252
|
+
* `types` condition comes first so a package that ships declarations resolves
|
|
253
|
+
* to the shape a port reader can walk rather than to compiled output.
|
|
254
|
+
*/
|
|
255
|
+
const resolver = new ResolverFactory({
|
|
256
|
+
tsconfig: "auto",
|
|
257
|
+
extensions: [...SOURCE_EXTENSIONS, ".d.ts", ".mts", ".cts"],
|
|
258
|
+
extensionAlias: { ".js": [".ts", ".tsx", ".d.ts", ".js"], ".mjs": [".mts", ".mjs"], ".cjs": [".cts", ".cjs"], ".jsx": [".tsx", ".jsx"] },
|
|
259
|
+
conditionNames: ["types", "import", "default"],
|
|
260
|
+
mainFields: ["types", "module", "main"],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The file a specifier names, or undefined for a package.
|
|
265
|
+
*
|
|
266
|
+
* A package is a specifier that lands in `node_modules`: what is there is a
|
|
267
|
+
* dependency, and the extractor does not read dependencies - a client library
|
|
268
|
+
* is recognised by the name it is imported under, not by its source. A
|
|
269
|
+
* workspace package is the exception, and needs none: its symlink resolves
|
|
270
|
+
* to a directory of the repository, outside any `node_modules`, and is read
|
|
271
|
+
* like the rest of the tree.
|
|
272
|
+
*/
|
|
273
|
+
export function resolveImport(from: string, specifier: string): string | undefined {
|
|
274
|
+
const found = resolver.resolveFileSync(from, specifier);
|
|
275
|
+
if (!found.path || /[\\/]node_modules[\\/]/.test(found.path)) return undefined;
|
|
276
|
+
return found.path;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The static imports as the parser records them: every declaration, every
|
|
281
|
+
* entry, with the name it is imported under, the name it had, and whether
|
|
282
|
+
* it is type-only - `import type { A }` and `import { type A }` alike.
|
|
283
|
+
* Nothing here walks the tree; the record is a by-product of parsing.
|
|
284
|
+
*/
|
|
285
|
+
function importsOf(parsed: Parsed, from: string): Import[] {
|
|
286
|
+
const out: Import[] = [];
|
|
287
|
+
for (const decl of parsed.module.staticImports) {
|
|
288
|
+
const specifier = decl.moduleRequest.value;
|
|
289
|
+
const file = resolveImport(from, specifier);
|
|
290
|
+
for (const entry of decl.entries) {
|
|
291
|
+
const imported = entry.importName.kind === "Default" ? "default" : entry.importName.kind === "NamespaceObject" ? "*" : (entry.importName.name ?? entry.localName.value);
|
|
292
|
+
out.push({ local: entry.localName.value, imported, specifier, file, typeOnly: entry.isType });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** `import("./x.js")` with a literal request, resolved; one with an expression names nothing that can be followed. */
|
|
299
|
+
function dynamicImportsOf(parsed: Parsed, from: string): { specifier: string; file: string | undefined }[] {
|
|
300
|
+
const out: { specifier: string; file: string | undefined }[] = [];
|
|
301
|
+
for (const d of parsed.module.dynamicImports) {
|
|
302
|
+
const request = parsed.text.slice(d.moduleRequest.start, d.moduleRequest.end);
|
|
303
|
+
const m = /^(["'])(.*)\1$/s.exec(request);
|
|
304
|
+
if (!m) continue;
|
|
305
|
+
out.push({ specifier: m[2]!, file: resolveImport(from, m[2]!) });
|
|
306
|
+
}
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** The doc comment above a node, allowing for a decorator or an `export` in front of it. */
|
|
311
|
+
export function jsdoc(src: Source, node: Node, exportNode?: Node): string {
|
|
312
|
+
return docOf(src.parsed, node, firstTokenOf(node, exportNode));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** A parsed doc block as the optional fields a `Documented` carries: nothing is written down when the tag is absent. */
|
|
316
|
+
function withTags(block: DocBlock): Documented {
|
|
317
|
+
const out: Documented = { doc: block.text };
|
|
318
|
+
if (block.deprecated !== undefined) out.deprecated = block.deprecated;
|
|
319
|
+
if (block.examples.length) out.examples = block.examples;
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The prose with the deprecation written into it, for a catalog field that
|
|
325
|
+
* holds only a sentence: "Deprecated: use `total` instead." is what a reader
|
|
326
|
+
* wants next to a struck-through name, and the boolean beside it is what the
|
|
327
|
+
* page strikes it through by.
|
|
328
|
+
*/
|
|
329
|
+
export function docWithDeprecation(d: Documented): string {
|
|
330
|
+
if (d.deprecated === undefined) return d.doc;
|
|
331
|
+
const note = d.deprecated ? `Deprecated: ${d.deprecated.replace(/[.\s]+$/, "")}.` : "Deprecated.";
|
|
332
|
+
return d.doc ? `${d.doc}\n\n${note}` : note;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* A class as the reader sees it. In TypeScript the types are in the syntax;
|
|
337
|
+
* in JavaScript they are in the doc comments - `@type` on a field, `@param`
|
|
338
|
+
* on the constructor, `@returns` on a method - and a field is what the
|
|
339
|
+
* constructor assigns to `this`, since there is no parameter property to
|
|
340
|
+
* declare it with. Each reading falls back to the next, so a TypeScript file
|
|
341
|
+
* with a JSDoc type on an unannotated field reads that too.
|
|
342
|
+
*/
|
|
343
|
+
function classInfo(src: Source, node: ClassDeclaration, exportNode: Node | undefined): ClassInfo {
|
|
344
|
+
const p = src.parsed;
|
|
345
|
+
const info: ClassInfo = {
|
|
346
|
+
name: node.id!.name,
|
|
347
|
+
node,
|
|
348
|
+
...withTags(docBlock(p, node, firstTokenOf(node, exportNode))),
|
|
349
|
+
exported: exportNode !== undefined,
|
|
350
|
+
fields: [],
|
|
351
|
+
params: [],
|
|
352
|
+
methods: new Map(),
|
|
353
|
+
nameLiteral: undefined,
|
|
354
|
+
};
|
|
355
|
+
for (const member of node.body.body) {
|
|
356
|
+
if (isPropertyDef(member) && !member.computed) {
|
|
357
|
+
const name = keyName(member.key);
|
|
358
|
+
if (name === undefined) continue;
|
|
359
|
+
const init = member.value;
|
|
360
|
+
if (name === "name" && isString(init)) {
|
|
361
|
+
info.nameLiteral = init.value;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (member.static) continue;
|
|
365
|
+
const block = docBlock(p, member, firstTokenOf(member));
|
|
366
|
+
info.fields.push({ name, type: typeText(p, member.typeAnnotation) || docTypeIn(src, docType(block)) || inferred(p, init), ...withTags(block) });
|
|
367
|
+
} else if (isMethod(member) && member.kind === "constructor") {
|
|
368
|
+
const documented = docParams(docBlock(p, member, firstTokenOf(member)));
|
|
369
|
+
const params = new Map<string, string>();
|
|
370
|
+
for (const param of member.value.params) {
|
|
371
|
+
const id = paramIdent(param);
|
|
372
|
+
if (!id) continue;
|
|
373
|
+
const type = typeText(p, id.typeAnnotation) || docTypeIn(src, documented.get(id.name) ?? "");
|
|
374
|
+
info.params.push({ name: id.name, type });
|
|
375
|
+
params.set(id.name, type);
|
|
376
|
+
if (isParamProperty(param)) info.fields.push({ name: id.name, type, ...withTags(docBlock(p, param, firstTokenOf(param))) });
|
|
377
|
+
}
|
|
378
|
+
if (p.js && member.value.body) {
|
|
379
|
+
// `this.id = id`: the field is declared by being assigned, typed by
|
|
380
|
+
// the parameter it takes or by what it is given.
|
|
381
|
+
for (const stmt of member.value.body.body) {
|
|
382
|
+
if (!isExprStmt(stmt) || !isAssign(stmt.expression) || stmt.expression.operator !== "=") continue;
|
|
383
|
+
const name = thisMember(stmt.expression.left);
|
|
384
|
+
if (name === undefined || name === "name" || info.fields.some((f) => f.name === name)) continue;
|
|
385
|
+
const value = stmt.expression.right;
|
|
386
|
+
const block = docBlock(p, stmt);
|
|
387
|
+
const fromParam = isIdent(value) ? params.get(value.name) : undefined;
|
|
388
|
+
info.fields.push({ name, type: docTypeIn(src, docType(block)) || fromParam || inferred(p, value), ...withTags(block) });
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else if (isMethod(member) && !member.computed && member.kind === "method") {
|
|
392
|
+
const name = keyName(member.key);
|
|
393
|
+
// An overload signature has no body; the implementation that follows is
|
|
394
|
+
// the one read, so the last declaration of a name wins.
|
|
395
|
+
if (name === undefined) continue;
|
|
396
|
+
info.methods.set(name, {
|
|
397
|
+
name,
|
|
398
|
+
node: member,
|
|
399
|
+
params: member.value.params,
|
|
400
|
+
body: member.value.body,
|
|
401
|
+
returns: typeText(p, member.value.returnType) || docTypeIn(src, docReturns(docBlock(p, member, firstTokenOf(member)))),
|
|
402
|
+
isStatic: member.static,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return info;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** The doc block above an exported function, found through its export statement when it has one. */
|
|
410
|
+
function functionDoc(src: Source, fn: FunctionNode): DocBlock {
|
|
411
|
+
for (const stmt of src.parsed.program.body) {
|
|
412
|
+
if (stmt === fn) return docBlock(src.parsed, fn);
|
|
413
|
+
if (isExportNamed(stmt) && stmt.declaration === fn) return docBlock(src.parsed, fn, firstTokenOf(fn, stmt));
|
|
414
|
+
}
|
|
415
|
+
return docBlock(src.parsed, fn);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** What a function returns: the annotation, or the `@returns {T}` above it, with the text the node points into. */
|
|
419
|
+
export function returnTypeOf(src: Source, fn: FunctionNode): Typed | undefined {
|
|
420
|
+
if (fn.returnType) return { p: src.parsed, ann: fn.returnType };
|
|
421
|
+
return docTyped(src, docReturns(functionDoc(src, fn)));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** The type of one of a function's parameters: the annotation, or the `@param {T} name` above the function. */
|
|
425
|
+
export function paramTypeOf(src: Source, fn: FunctionNode, param: Node): Typed | undefined {
|
|
426
|
+
const id = paramIdent(param);
|
|
427
|
+
if (!id) return undefined;
|
|
428
|
+
if (id.typeAnnotation) return { p: src.parsed, ann: id.typeAnnotation };
|
|
429
|
+
return docTyped(src, docParams(functionDoc(src, fn)).get(id.name) ?? "");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** The return type of a function as written, or "". */
|
|
433
|
+
export function returnTypeText(src: Source, fn: FunctionNode): string {
|
|
434
|
+
const t = returnTypeOf(src, fn);
|
|
435
|
+
return t ? typeText(t.p, t.ann) : "";
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function inferred(p: Parsed, init: Node | null): string {
|
|
439
|
+
if (!init) return "";
|
|
440
|
+
if (isString(init)) return "string";
|
|
441
|
+
if (isNumber(init)) return "number";
|
|
442
|
+
if (isBoolean(init)) return "boolean";
|
|
443
|
+
if (isArray(init)) return "[]";
|
|
444
|
+
if (isNew(init)) return textOf(p, init.callee);
|
|
445
|
+
return "";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** The source of a node, as written. */
|
|
449
|
+
export function text(src: Source, node: Node): string {
|
|
450
|
+
return textOf(src.parsed, node);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** `file:line` for a node, relative to the repository the way the catalog spells it. */
|
|
454
|
+
export function at(src: Source, node: Node, rel: (abs: string) => string): string {
|
|
455
|
+
return `${rel(src.path)}:${lineOf(src.parsed, node.start)}`;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** The type behind `Promise<X>`, `X | undefined`, `readonly X[]` and friends, as a bare name. */
|
|
459
|
+
export function bareType(type: string): string {
|
|
460
|
+
let t = type.trim();
|
|
461
|
+
const promise = /^Promise<(.*)>$/s.exec(t);
|
|
462
|
+
if (promise) t = promise[1]!.trim();
|
|
463
|
+
t = t.replace(/\s*\|\s*(undefined|null)\b/g, "").replace(/^(readonly\s+)/, "");
|
|
464
|
+
return t;
|
|
465
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Endpoints, read off src/infrastructure/transport.
|
|
2
|
+
//
|
|
3
|
+
// Over HTTP the document's operationIds name the handlers; over gRPC the
|
|
4
|
+
// contract vendored beside the handler does, and an rpc is named the same on
|
|
5
|
+
// both sides - `planRoute` answers `PlanRoute`. Either way a handler's body
|
|
6
|
+
// names the use cases it runs, in order, and that is what opens a flow.
|
|
7
|
+
|
|
8
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { readSpec, type Spec } from "./openapi.ts";
|
|
11
|
+
import { readProtos } from "./clients.ts";
|
|
12
|
+
import { readSource, type ClassInfo, type Source, at } from "./source.ts";
|
|
13
|
+
import { isCall, isMember, isSourceFile, memberName, thisMember, walk, type Node } from "./ast.ts";
|
|
14
|
+
import { useCaseKeyOf } from "./operations.ts";
|
|
15
|
+
import type { WarningSink } from "./domain.ts";
|
|
16
|
+
|
|
17
|
+
export interface Endpoint {
|
|
18
|
+
/** The operationId. */
|
|
19
|
+
id: string;
|
|
20
|
+
/** file:line of the handler. */
|
|
21
|
+
line: string;
|
|
22
|
+
source: string;
|
|
23
|
+
/** Use case keys, in the order the handler runs them. */
|
|
24
|
+
useCases: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Transport {
|
|
28
|
+
spec: Spec | undefined;
|
|
29
|
+
endpoints: Endpoint[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The gRPC half: one directory per aggregate, the contract it answers vendored
|
|
34
|
+
* under it, and a handler whose methods are that contract's rpcs. The endpoint
|
|
35
|
+
* is called what the proto calls it, which is the name `extract-proto` puts in
|
|
36
|
+
* `provides`, so an operation and the interface that exposes it meet.
|
|
37
|
+
*/
|
|
38
|
+
export function readGrpcTransport(grpcDir: string, rel: (abs: string) => string, b: WarningSink): Endpoint[] {
|
|
39
|
+
if (!existsSync(grpcDir)) return [];
|
|
40
|
+
const endpoints: Endpoint[] = [];
|
|
41
|
+
|
|
42
|
+
for (const pkg of readdirSync(grpcDir).sort()) {
|
|
43
|
+
const dir = join(grpcDir, pkg);
|
|
44
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
45
|
+
|
|
46
|
+
const contracts = readProtos(join(dir, "proto"), rel);
|
|
47
|
+
if (contracts.length === 0) {
|
|
48
|
+
b.warn(rel(dir), "a grpc handler with no contract vendored beside it: nothing says which rpc its methods answer");
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const rpcs = new Map<string, string>();
|
|
52
|
+
for (const contract of contracts) {
|
|
53
|
+
for (const rpc of contract.rpcs) rpcs.set(rpc.toLowerCase(), rpc);
|
|
54
|
+
}
|
|
55
|
+
const answered = new Set<string>();
|
|
56
|
+
|
|
57
|
+
for (const name of readdirSync(dir).sort()) {
|
|
58
|
+
if (!isSourceFile(name)) continue;
|
|
59
|
+
const src = readSource(join(dir, name));
|
|
60
|
+
if (!src) continue;
|
|
61
|
+
for (const cls of src.classes) {
|
|
62
|
+
const ports = useCasePorts(src, cls);
|
|
63
|
+
for (const [method, m] of cls.methods) {
|
|
64
|
+
const rpc = rpcs.get(method.toLowerCase());
|
|
65
|
+
if (!rpc || answered.has(rpc)) continue;
|
|
66
|
+
answered.add(rpc);
|
|
67
|
+
endpoints.push({
|
|
68
|
+
id: rpc,
|
|
69
|
+
line: at(src, m.node, rel),
|
|
70
|
+
source: rel(src.path),
|
|
71
|
+
useCases: useCasesRun(m.node, ports),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const [, rpc] of rpcs) {
|
|
78
|
+
if (!answered.has(rpc)) {
|
|
79
|
+
b.warn(rel(dir), rpc + " is declared by the contract and answered by no method here");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return endpoints.sort((a, c) => a.id.localeCompare(c.id));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function readTransport(httpDir: string, rel: (abs: string) => string, b: WarningSink): Transport {
|
|
88
|
+
const specPath = join(httpDir, "gen", "openapi.yaml");
|
|
89
|
+
if (!existsSync(specPath)) return { spec: undefined, endpoints: [] };
|
|
90
|
+
const spec = readSpec(specPath);
|
|
91
|
+
const wanted = new Map(spec.operations.map((op) => [op.id, op]));
|
|
92
|
+
const endpoints: Endpoint[] = [];
|
|
93
|
+
const found = new Set<string>();
|
|
94
|
+
|
|
95
|
+
for (const pkg of readdirSync(httpDir).sort()) {
|
|
96
|
+
const dir = join(httpDir, pkg);
|
|
97
|
+
if (pkg === "gen" || !statSync(dir).isDirectory()) continue;
|
|
98
|
+
for (const name of readdirSync(dir).sort()) {
|
|
99
|
+
if (!isSourceFile(name)) continue;
|
|
100
|
+
const src = readSource(join(dir, name));
|
|
101
|
+
if (!src) continue;
|
|
102
|
+
for (const cls of src.classes) {
|
|
103
|
+
const ports = useCasePorts(src, cls);
|
|
104
|
+
for (const [method, m] of cls.methods) {
|
|
105
|
+
if (!wanted.has(method) || found.has(method)) continue;
|
|
106
|
+
found.add(method);
|
|
107
|
+
endpoints.push({
|
|
108
|
+
id: method,
|
|
109
|
+
line: at(src, m.node, rel),
|
|
110
|
+
source: rel(src.path),
|
|
111
|
+
useCases: useCasesRun(m.node, ports),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const id of wanted.keys()) {
|
|
118
|
+
if (!found.has(id)) b.warn(id, `${rel(specPath)} declares ${id} but no handler under ${rel(httpDir)} is named by it`);
|
|
119
|
+
}
|
|
120
|
+
endpoints.sort((a, c) => a.id.localeCompare(c.id));
|
|
121
|
+
return { spec, endpoints };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Constructor parameters whose type is a UseCase import: field name → use case key. */
|
|
125
|
+
export function useCasePorts(src: Source, cls: ClassInfo): Map<string, string> {
|
|
126
|
+
const out = new Map<string, string>();
|
|
127
|
+
for (const p of cls.params) {
|
|
128
|
+
const local = p.type.replace(/^typeof\s+/, "").trim();
|
|
129
|
+
const imp = src.imports.find((i) => i.local === local);
|
|
130
|
+
if (!imp?.file || imp.imported !== "UseCase") continue;
|
|
131
|
+
const key = useCaseKeyOf(imp.file);
|
|
132
|
+
if (key) out.set(p.name, key);
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** `this.<port>.handle(...)` calls, in source order, as use case keys. */
|
|
138
|
+
function useCasesRun(node: Node, ports: Map<string, string>): string[] {
|
|
139
|
+
const out: string[] = [];
|
|
140
|
+
walk(node, (n) => {
|
|
141
|
+
if (!isCall(n) || !isMember(n.callee) || memberName(n.callee) !== "handle") return;
|
|
142
|
+
const port = thisMember(n.callee.object);
|
|
143
|
+
const key = port === undefined ? undefined : ports.get(port);
|
|
144
|
+
if (key) out.push(key);
|
|
145
|
+
});
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"module": "nodenext",
|
|
6
|
+
"moduleResolution": "nodenext",
|
|
7
|
+
"allowImportingTsExtensions": true,
|
|
8
|
+
"types": ["node"],
|
|
9
|
+
"jsx": "preserve"
|
|
10
|
+
},
|
|
11
|
+
"include": ["./*.ts", "../../src/catalog.ts"],
|
|
12
|
+
"exclude": ["./testdata"]
|
|
13
|
+
}
|