@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,185 @@
|
|
|
1
|
+
// A call to another service, read off the generated client the adapter holds.
|
|
2
|
+
//
|
|
3
|
+
// An HTTP peer: the adapter's file imports `createClient` from openapi-fetch
|
|
4
|
+
// and the `paths` type from its gen/, and a call `client.GET("/v1/x")` names
|
|
5
|
+
// the verb and the route in the code; the document vendored beside gen/ says
|
|
6
|
+
// which operation answers there. A gRPC peer: the adapter imports a service
|
|
7
|
+
// descriptor from a `*_pb` module and creates a Connect client from it; the
|
|
8
|
+
// descriptor's name is the service in the vendored .proto, whose package is
|
|
9
|
+
// the proto package, and a call `client.getQuote()` is the rpc in the proto's
|
|
10
|
+
// own case. Either way the id is the one the callee's extractor would give.
|
|
11
|
+
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
13
|
+
import { basename, dirname, join } from "node:path";
|
|
14
|
+
import { callID, findOperation, readSpec, type Spec } from "./openapi.ts";
|
|
15
|
+
import type { Source } from "./source.ts";
|
|
16
|
+
import { isCall, isMember, isTemplate, memberName, stringOf, templateShape, walk, type CallExpression, type Node } from "./ast.ts";
|
|
17
|
+
import type { WarningSink } from "./domain.ts";
|
|
18
|
+
|
|
19
|
+
/** One call the adapter makes, in the catalog's terms. */
|
|
20
|
+
export interface RpcHop {
|
|
21
|
+
/** "auth.v1.Sessions/validateSession" or "shop.v1.Pricing/GetQuote" */
|
|
22
|
+
id: string;
|
|
23
|
+
/** What the manifest's peers map is keyed by: the api id or the proto package. */
|
|
24
|
+
pkg: string;
|
|
25
|
+
/** The document or the proto the call was named from. */
|
|
26
|
+
source: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const HTTP_VERBS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
30
|
+
|
|
31
|
+
export interface ProtoService {
|
|
32
|
+
pkg: string;
|
|
33
|
+
name: string;
|
|
34
|
+
rpcs: string[];
|
|
35
|
+
source: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** What one adapter file can reach: its HTTP document and its proto services. */
|
|
39
|
+
class Peer {
|
|
40
|
+
readonly spec: Spec | undefined;
|
|
41
|
+
readonly protos: ProtoService[];
|
|
42
|
+
constructor(spec: Spec | undefined, protos: ProtoService[]) {
|
|
43
|
+
this.spec = spec;
|
|
44
|
+
this.protos = protos;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const peers = new Map<string, Peer>();
|
|
49
|
+
|
|
50
|
+
function peerOf(src: Source, rel: (abs: string) => string, b: WarningSink): Peer {
|
|
51
|
+
const hit = peers.get(src.path);
|
|
52
|
+
if (hit) return hit;
|
|
53
|
+
|
|
54
|
+
let spec: Spec | undefined;
|
|
55
|
+
const protos: ProtoService[] = [];
|
|
56
|
+
for (const imp of src.imports) {
|
|
57
|
+
if (!imp.file) continue;
|
|
58
|
+
// gen/types.ts (or types.d.ts, in a JavaScript tree) of openapi-typescript → gen/openapi.yaml beside it
|
|
59
|
+
if (/[\\/]gen[\\/]/.test(imp.file) && /\.(d\.)?ts$/.test(imp.file) && !/_pb\.[jt]s$/.test(imp.file)) {
|
|
60
|
+
const found = specBeside(dirname(imp.file));
|
|
61
|
+
if (found) spec = readSpec(found);
|
|
62
|
+
}
|
|
63
|
+
// gen/**/<x>_pb.ts, or _pb.js, of Connect-ES → proto/**/*.proto beside gen/
|
|
64
|
+
if (/_pb\.[jt]s$/.test(imp.file)) {
|
|
65
|
+
const gen = genDirAbove(imp.file);
|
|
66
|
+
if (gen) protos.push(...readProtos(join(dirname(gen), "proto"), rel));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!spec && src.imports.some((i) => i.specifier === "openapi-fetch")) {
|
|
70
|
+
b.warn(rel(src.path), "creates an openapi-fetch client but imports no gen/types beside an openapi document, so its calls cannot be named");
|
|
71
|
+
}
|
|
72
|
+
if (protos.length === 0 && src.imports.some((i) => i.specifier.startsWith("@connectrpc/"))) {
|
|
73
|
+
b.warn(rel(src.path), "creates a Connect client but imports no *_pb descriptor with a proto beside it, so its calls cannot be named");
|
|
74
|
+
}
|
|
75
|
+
const peer = new Peer(spec, protos);
|
|
76
|
+
peers.set(src.path, peer);
|
|
77
|
+
return peer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The gen/ a generated file sits under, however deep its package path put it. */
|
|
81
|
+
function genDirAbove(file: string): string | undefined {
|
|
82
|
+
let dir = dirname(file);
|
|
83
|
+
for (let i = 0; i < 8 && dir !== dirname(dir); i++) {
|
|
84
|
+
if (basename(dir) === "gen") return dir;
|
|
85
|
+
dir = dirname(dir);
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function specBeside(dir: string): string | undefined {
|
|
91
|
+
for (const name of ["openapi.yaml", "openapi.yml", "swagger.yaml", "swagger.yml"]) {
|
|
92
|
+
const path = join(dir, name);
|
|
93
|
+
if (existsSync(path)) return path;
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** A minimal reading of .proto files: package, services, rpcs. */
|
|
99
|
+
export function readProtos(dir: string, rel: (abs: string) => string): ProtoService[] {
|
|
100
|
+
if (!existsSync(dir)) return [];
|
|
101
|
+
const out: ProtoService[] = [];
|
|
102
|
+
const walk = (d: string): void => {
|
|
103
|
+
for (const name of readdirSync(d).sort()) {
|
|
104
|
+
const p = join(d, name);
|
|
105
|
+
if (statSync(p).isDirectory()) walk(p);
|
|
106
|
+
else if (name.endsWith(".proto")) out.push(...protoServices(readFileSync(p, "utf8"), rel(p)));
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
walk(dir);
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function protoServices(text: string, source: string): ProtoService[] {
|
|
114
|
+
const stripped = text.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
115
|
+
const pkg = /^\s*package\s+([\w.]+)\s*;/m.exec(stripped)?.[1] ?? "";
|
|
116
|
+
const out: ProtoService[] = [];
|
|
117
|
+
const service = /\bservice\s+(\w+)\s*\{([^}]*)\}/g;
|
|
118
|
+
for (let m = service.exec(stripped); m; m = service.exec(stripped)) {
|
|
119
|
+
const rpcs = [...m[2]!.matchAll(/\brpc\s+(\w+)\s*\(/g)].map((r) => r[1]!);
|
|
120
|
+
out.push({ pkg, name: m[1]!, rpcs, source });
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The calls one method of an adapter makes to its peer, in order. Read off
|
|
127
|
+
* the method's body: `x.GET("/route")` for HTTP, `x.getQuote(...)` for gRPC.
|
|
128
|
+
*/
|
|
129
|
+
export function adapterCalls(src: Source, cls: string, method: string, rel: (abs: string) => string, b: WarningSink): RpcHop[] {
|
|
130
|
+
const c = src.classes.find((k) => k.name === cls);
|
|
131
|
+
const m = c?.methods.get(method);
|
|
132
|
+
if (!m?.body) return [];
|
|
133
|
+
const peer = peerOf(src, rel, b);
|
|
134
|
+
const out: RpcHop[] = [];
|
|
135
|
+
const seen = new Set<string>();
|
|
136
|
+
walk(m.body, (node) => {
|
|
137
|
+
if (!isCall(node) || !isMember(node.callee)) return;
|
|
138
|
+
const name = memberName(node.callee);
|
|
139
|
+
const hop = name === undefined ? undefined : hopOf(peer, name, node, rel, b);
|
|
140
|
+
if (hop && !seen.has(hop.id)) {
|
|
141
|
+
seen.add(hop.id);
|
|
142
|
+
out.push(hop);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Calls from any body that reaches a generated client directly, by the same reading. */
|
|
149
|
+
export function callsIn(src: Source, node: Node, rel: (abs: string) => string, b: WarningSink): RpcHop[] {
|
|
150
|
+
const peer = peerOf(src, rel, b);
|
|
151
|
+
if (!peer.spec && peer.protos.length === 0) return [];
|
|
152
|
+
const out: RpcHop[] = [];
|
|
153
|
+
walk(node, (n) => {
|
|
154
|
+
if (!isCall(n) || !isMember(n.callee)) return;
|
|
155
|
+
const name = memberName(n.callee);
|
|
156
|
+
const hop = name === undefined ? undefined : hopOf(peer, name, n, rel, b);
|
|
157
|
+
if (hop) out.push(hop);
|
|
158
|
+
});
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hopOf(peer: Peer, name: string, call: CallExpression, rel: (abs: string) => string, b: WarningSink): RpcHop | undefined {
|
|
163
|
+
if (HTTP_VERBS.has(name)) {
|
|
164
|
+
if (!peer.spec) return undefined;
|
|
165
|
+
const first = call.arguments[0];
|
|
166
|
+
if (!first) return undefined;
|
|
167
|
+
const route = stringOf(first) ?? (isTemplate(first) ? templateShape(first) : undefined);
|
|
168
|
+
if (!route) return undefined;
|
|
169
|
+
const op = findOperation(peer.spec, name, route);
|
|
170
|
+
if (!op) {
|
|
171
|
+
b.warn(rel(peer.spec.source), `calls ${name} ${route}, which the document does not declare; the call is left out`);
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
return { id: callID(peer.spec, op), pkg: peer.spec.api, source: rel(peer.spec.source) };
|
|
175
|
+
}
|
|
176
|
+
for (const svc of peer.protos) {
|
|
177
|
+
const rpc = svc.rpcs.find((r) => r.toLowerCase() === name.toLowerCase());
|
|
178
|
+
if (rpc) return { id: `${svc.pkg}.${svc.name}/${rpc}`, pkg: svc.pkg, source: svc.source };
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function isOpenapiFetchClientType(type: string): boolean {
|
|
184
|
+
return /^Client<\s*paths\s*>$/.test(type.trim());
|
|
185
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Aggregates, read off src/domain: the root by the directory's name, the
|
|
2
|
+
// entities beside it, the value objects under vo/, the events under events/.
|
|
3
|
+
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { basename, join } from "node:path";
|
|
6
|
+
import type { Aggregate, Block, Event, Field as CatalogField } from "../../src/catalog.ts";
|
|
7
|
+
import { aggregateID, blockID, eventID, pascal, slug, title } from "./ids.ts";
|
|
8
|
+
import { isExportNamed, isIdent, isString, isVarDecl } from "./ast.ts";
|
|
9
|
+
import { readLifecycle } from "./lifecycle.ts";
|
|
10
|
+
import { docWithDeprecation, readSource, sourceFiles, sourceNamed, type ClassInfo, type Documented, type Source } from "./source.ts";
|
|
11
|
+
|
|
12
|
+
export interface WarningSink {
|
|
13
|
+
warn(ref: string, message: string): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AggregateRead {
|
|
17
|
+
aggregate: Aggregate;
|
|
18
|
+
/** The directory it was read from, absolute. */
|
|
19
|
+
dir: string;
|
|
20
|
+
/** Event class name → event id, for the flows. */
|
|
21
|
+
events: Map<string, string>;
|
|
22
|
+
/** Class names declared in the directory's own files: the root and the entities. */
|
|
23
|
+
own: Set<string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function readAggregates(domainDir: string, svcID: string, rel: (abs: string) => string, b: WarningSink): AggregateRead[] {
|
|
27
|
+
if (!existsSync(domainDir)) return [];
|
|
28
|
+
const out: AggregateRead[] = [];
|
|
29
|
+
for (const name of readdirSync(domainDir).sort()) {
|
|
30
|
+
const dir = join(domainDir, name);
|
|
31
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
32
|
+
const read = readAggregate(dir, name, svcID, rel, b);
|
|
33
|
+
if (read) out.push(read);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readAggregate(dir: string, name: string, svcID: string, rel: (abs: string) => string, b: WarningSink): AggregateRead | null {
|
|
39
|
+
const id = aggregateID(svcID, name);
|
|
40
|
+
const rootName = pascal(name);
|
|
41
|
+
const own = new Set<string>();
|
|
42
|
+
const entities: Block[] = [];
|
|
43
|
+
let root: ClassInfo | undefined;
|
|
44
|
+
let rootFile = "";
|
|
45
|
+
|
|
46
|
+
for (const file of sourceFiles(dir)) {
|
|
47
|
+
const src = readSource(file);
|
|
48
|
+
if (!src) continue;
|
|
49
|
+
for (const c of src.classes) {
|
|
50
|
+
if (!c.exported) continue;
|
|
51
|
+
own.add(c.name);
|
|
52
|
+
if (c.name === rootName) {
|
|
53
|
+
root = c;
|
|
54
|
+
rootFile = file;
|
|
55
|
+
}
|
|
56
|
+
entities.push(block(id, c));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!root) {
|
|
60
|
+
b.warn(id, `${rel(dir)} has no class ${rootName}; a domain directory is named after its root, and this one is skipped`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
// The root first, then the entities in file order, as extract-go lists them.
|
|
64
|
+
entities.sort((a, c) => (a.name === rootName ? -1 : c.name === rootName ? 1 : 0));
|
|
65
|
+
|
|
66
|
+
const valueObjects: Block[] = [];
|
|
67
|
+
for (const file of sourceFiles(join(dir, "vo"))) {
|
|
68
|
+
const src = readSource(file);
|
|
69
|
+
for (const c of src?.classes ?? []) if (c.exported) valueObjects.push(block(id, c));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const events: Event[] = [];
|
|
73
|
+
const eventIds = new Map<string, string>();
|
|
74
|
+
const channel = channelOf(dir, name);
|
|
75
|
+
for (const file of sourceFiles(join(dir, "events"))) {
|
|
76
|
+
const src = readSource(file);
|
|
77
|
+
for (const c of src?.classes ?? []) {
|
|
78
|
+
if (!c.exported) continue;
|
|
79
|
+
if (c.nameLiteral === undefined) {
|
|
80
|
+
b.warn(id, `${rel(file)}: class ${c.name} has no \`readonly name = "…"\`, so it is not read as an event`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const evID = eventID(id, c.name);
|
|
84
|
+
eventIds.set(c.name, evID);
|
|
85
|
+
events.push({
|
|
86
|
+
id: evID,
|
|
87
|
+
slug: slug(c.name),
|
|
88
|
+
name: c.name,
|
|
89
|
+
versions: [{ version: "v1", doc: docWithDeprecation(c), ...deprecated(c), source: rel(file), fields: fields(c) }],
|
|
90
|
+
consumers: [],
|
|
91
|
+
wire: { name: c.nameLiteral, ...(channel ? { channel } : {}) },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const readmePath = join(dir, "README.md");
|
|
97
|
+
// README.md when the author wrote one; else the root class own doc, examples and all.
|
|
98
|
+
const readme = existsSync(readmePath) ? readFileSync(readmePath, "utf8").trim() : readmeOf(root);
|
|
99
|
+
const lifecycle = readLifecycle(dir, root, rootFile, eventIds, id, rel, b);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
aggregate: {
|
|
103
|
+
id,
|
|
104
|
+
slug: name,
|
|
105
|
+
name: title(name),
|
|
106
|
+
readme,
|
|
107
|
+
root: rootName,
|
|
108
|
+
entities,
|
|
109
|
+
valueObjects,
|
|
110
|
+
operations: [],
|
|
111
|
+
events,
|
|
112
|
+
...(lifecycle ? { lifecycle } : {}),
|
|
113
|
+
},
|
|
114
|
+
dir,
|
|
115
|
+
events: eventIds,
|
|
116
|
+
own,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Where the aggregate's events go: the `TOPIC` constant of
|
|
122
|
+
* src/infrastructure/repository/<aggregate>/dto.ts, the module that turns a
|
|
123
|
+
* domain event into a message. The domain names the event and the adapter
|
|
124
|
+
* names the channel, because the channel is a fact about the transport, not
|
|
125
|
+
* about what happened. Empty when the module or the constant is missing.
|
|
126
|
+
*/
|
|
127
|
+
function channelOf(domainDir: string, aggregate: string): string {
|
|
128
|
+
const src = readSource(sourceNamed(join(domainDir, "..", "..", "infrastructure", "repository", aggregate), "dto"));
|
|
129
|
+
if (!src) return "";
|
|
130
|
+
for (const stmt of src.parsed.program.body) {
|
|
131
|
+
const decl = isExportNamed(stmt) ? stmt.declaration : stmt;
|
|
132
|
+
if (!isVarDecl(decl)) continue;
|
|
133
|
+
for (const d of decl.declarations) {
|
|
134
|
+
if (isIdent(d.id) && d.id.name === "TOPIC" && isString(d.init)) return d.init.value;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function block(aggregate: string, c: ClassInfo): Block {
|
|
141
|
+
return { id: blockID(aggregate, slug(c.name)), slug: slug(c.name), name: c.name, doc: docWithDeprecation(c), ...deprecated(c), fields: fields(c) };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function fields(c: ClassInfo): CatalogField[] {
|
|
145
|
+
return c.fields.map((f) => ({ name: f.name, type: f.type, doc: docWithDeprecation(f), ...deprecated(f) }));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The flag, only when the tag was there: an absent key is what an older fragment looks like too. */
|
|
149
|
+
function deprecated(d: Documented): { deprecated?: true } {
|
|
150
|
+
return d.deprecated === undefined ? {} : { deprecated: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The root's doc comment as the aggregate's page, when there is no README:
|
|
155
|
+
* the prose, then each `@example` as a fenced block, since an example in a
|
|
156
|
+
* comment is code and the page renders markdown.
|
|
157
|
+
*/
|
|
158
|
+
function readmeOf(root: ClassInfo): string {
|
|
159
|
+
const parts = [docWithDeprecation(root)];
|
|
160
|
+
for (const example of root.examples ?? []) parts.push(example.startsWith("```") ? example : `\`\`\`ts\n${example}\n\`\`\``);
|
|
161
|
+
return parts.filter(Boolean).join("\n\n");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export { basename as _basename, type Source as _Source };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import type { Alt, Step } from "../../src/catalog.ts";
|
|
4
|
+
import { extract } from "./extract.ts";
|
|
5
|
+
import { serve } from "./main.ts";
|
|
6
|
+
|
|
7
|
+
const ROOT = "plugins/extract-ts/testdata/cart";
|
|
8
|
+
const options = { context: "shop", service: "cart", store: "pg", peers: { "auth.v1": "auth.auth", "shop.v1": "shop.pricing" } };
|
|
9
|
+
|
|
10
|
+
function run() {
|
|
11
|
+
return extract({ root: ROOT, commit: "abc1234", generatedAt: "2026-09-04T00:00:00Z" }, options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function fragment() {
|
|
15
|
+
return JSON.parse(run().files[0]!.contents) as ReturnType<typeof JSON.parse>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The fixture is the layout the contract describes, each shape once, and
|
|
19
|
+
// expected.json is what it reads as. A change to the reader shows up here as
|
|
20
|
+
// a diff, which is the review worth having.
|
|
21
|
+
describe("the fixture, as a whole", () => {
|
|
22
|
+
it("reads to the golden fragment", () => {
|
|
23
|
+
const golden = JSON.parse(readFileSync(`${ROOT}/expected.json`, "utf8"));
|
|
24
|
+
expect(fragment()).toEqual(golden);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("reports the operation with no handler, and nothing else", () => {
|
|
28
|
+
const { warnings } = run();
|
|
29
|
+
expect(warnings.map((d) => d.ref)).toEqual(["getBasket"]);
|
|
30
|
+
expect(warnings[0]!.message).toContain("no handler");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("the service", () => {
|
|
35
|
+
const svc = fragment().contexts[0].services[0];
|
|
36
|
+
|
|
37
|
+
it("is named by its readme and placed by its package", () => {
|
|
38
|
+
expect(svc.id).toBe("shop.cart");
|
|
39
|
+
expect(svc.name).toBe("Shopping Cart");
|
|
40
|
+
expect(svc.repo).toBe("github.com/acme/shop");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("has one aggregate: the root by the directory's name, entities beside it, value objects and events", () => {
|
|
44
|
+
const [basket] = svc.aggregates;
|
|
45
|
+
expect(basket.id).toBe("shop.cart.basket");
|
|
46
|
+
expect(basket.root).toBe("Basket");
|
|
47
|
+
expect(basket.entities.map((e: { name: string }) => e.name)).toEqual(["Basket", "BasketItem"]);
|
|
48
|
+
expect(basket.valueObjects.map((v: { name: string }) => v.name)).toEqual(["Money"]);
|
|
49
|
+
expect(basket.events.map((e: { id: string }) => e.id)).toEqual([
|
|
50
|
+
"shop.cart.basket.BasketAbandoned",
|
|
51
|
+
"shop.cart.basket.BasketCheckedOut",
|
|
52
|
+
"shop.cart.basket.BasketCreated",
|
|
53
|
+
"shop.cart.basket.BasketItemAdded",
|
|
54
|
+
]);
|
|
55
|
+
const added = basket.events[3].versions[0];
|
|
56
|
+
expect(added.fields.map((f: { name: string; type: string }) => `${f.name}:${f.type}`)).toEqual(["basketId:string", "sku:string", "quantity:number", "unitPrice:Money"]);
|
|
57
|
+
expect(added.doc).not.toContain("Published on the bus");
|
|
58
|
+
// A tag says what the prose cannot: the flag, and the reason folded into the doc a reader sees.
|
|
59
|
+
const abandoned = basket.events[0].versions[0];
|
|
60
|
+
expect(abandoned.deprecated).toBe(true);
|
|
61
|
+
expect(abandoned.doc).toContain("Deprecated: expiry is a policy now");
|
|
62
|
+
expect(added.deprecated).toBeUndefined();
|
|
63
|
+
expect(basket.events[3].wire).toEqual({ name: "cart.BasketItemAdded", channel: "shop.cart.basket" });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("reads fields off properties and constructor parameter properties alike", () => {
|
|
67
|
+
const root = svc.aggregates[0].entities[0];
|
|
68
|
+
expect(root.fields.map((f: { name: string }) => f.name)).toEqual(["items", "status", "id", "token", "currency", "version"]);
|
|
69
|
+
// The root doc: the summary, then @remarks with its {@link}s flattened; the readme adds the @example as code.
|
|
70
|
+
expect(root.doc).toBe("A visitor's or a customer's lines, under one lock.\n\nEach line is a BasketItem; the price on it is a money value.");
|
|
71
|
+
expect(svc.aggregates[0].readme).toContain("```ts\nconst [basket] = Basket.create(id, token);");
|
|
72
|
+
const item = svc.aggregates[0].entities[1];
|
|
73
|
+
expect(item.fields.find((f: { name: string }) => f.name === "priceMinor")).toMatchObject({ deprecated: true, doc: expect.stringContaining("Deprecated: read `unitPrice` instead.") });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("reads each use case as an operation, command or query by what it does to a port, exposed by its handler", () => {
|
|
77
|
+
const ops = svc.aggregates[0].operations;
|
|
78
|
+
expect(ops.map((o: { id: string; kind: string; exposedBy?: string[] }) => `${o.id}:${o.kind}:${(o.exposedBy ?? []).join(",")}`)).toEqual([
|
|
79
|
+
"AddItem:command:addItem",
|
|
80
|
+
"Checkout:command:checkout",
|
|
81
|
+
"Expire:command:expireIdleBaskets",
|
|
82
|
+
"Merge:command:mergeBaskets",
|
|
83
|
+
]);
|
|
84
|
+
expect(ops[1].doc).toContain("Freezes the basket");
|
|
85
|
+
// `@deprecated` above the class is the flag; the README, when there is one, is still the doc.
|
|
86
|
+
expect(ops.map((o: { deprecated?: boolean }) => o.deprecated ?? false)).toEqual([false, false, false, true]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("follows a value through a helper, a loop, a list and a port declared elsewhere", () => {
|
|
90
|
+
const flows = fragment().flows as { slug: string; steps: (Step | Alt)[] }[];
|
|
91
|
+
const line = (s: Step | Alt) => (s.type === "step" ? `${s.from}->${s.to} ${s.kind} ${s.label}` : `alt ${s.id}`);
|
|
92
|
+
// The port `Sessions` is checkout's, bound twice in assembly - to the
|
|
93
|
+
// adapter over auth when auth is named, to a stand-in when it is not -
|
|
94
|
+
// and the flow shows the binding that reaches a peer. `holderOf` hands
|
|
95
|
+
// back a Basket; `events.push(into.addItem(...))` inside a loop collects
|
|
96
|
+
// what `...events` then hands to the store.
|
|
97
|
+
expect(flows.find((f) => f.slug === "cart-merge-baskets")!.steps.map(line)).toEqual([
|
|
98
|
+
"client->shop.cart rpc mergeBaskets",
|
|
99
|
+
"shop.cart->auth.auth rpc validateSession",
|
|
100
|
+
"shop.cart->cart-pg call openFor",
|
|
101
|
+
"shop.cart->cart-pg call save",
|
|
102
|
+
"shop.cart->bus event BasketCreated",
|
|
103
|
+
"shop.cart->bus event BasketItemAdded",
|
|
104
|
+
"shop.cart->cart-pg call save",
|
|
105
|
+
]);
|
|
106
|
+
// `for (const basket of idle)` holds one of what `idleSince` handed back.
|
|
107
|
+
expect(flows.find((f) => f.slug === "cart-expire-idle-baskets")!.steps.map(line)).toEqual([
|
|
108
|
+
"client->shop.cart rpc expireIdleBaskets",
|
|
109
|
+
"shop.cart->cart-pg call idleSince",
|
|
110
|
+
"shop.cart->cart-pg call save",
|
|
111
|
+
"shop.cart->bus event BasketAbandoned",
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("records what it calls, named the way the callee names the method", () => {
|
|
116
|
+
expect(svc.consumes.map((c: { id: string; peer: string; status: string }) => `${c.id}→${c.peer}:${c.status}`)).toEqual([
|
|
117
|
+
"auth.v1.Sessions/validateSession→auth.auth:declared",
|
|
118
|
+
"shop.v1.Pricing/GetQuote→shop.pricing:declared",
|
|
119
|
+
]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("the flows", () => {
|
|
124
|
+
const flows = fragment().flows as { slug: string; steps: (Step | Alt)[]; participants: { id: string; kind: string }[] }[];
|
|
125
|
+
const bySlug = (slug: string) => flows.find((f) => f.slug === slug)!;
|
|
126
|
+
const line = (s: Step | Alt) => (s.type === "step" ? `${s.from}->${s.to} ${s.kind} ${s.label}` : `alt ${s.id}`);
|
|
127
|
+
|
|
128
|
+
it("opens on the endpoint and follows the use case through its ports", () => {
|
|
129
|
+
expect(bySlug("cart-add-item").steps.map(line)).toEqual([
|
|
130
|
+
"client->shop.cart rpc addItem",
|
|
131
|
+
"shop.cart->cart-pg call byId",
|
|
132
|
+
"shop.cart->cart-pg call save",
|
|
133
|
+
"shop.cart->bus event BasketItemAdded",
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("reads a call to a peer through the adapter the provider binds the port to", () => {
|
|
138
|
+
const checkout = bySlug("cart-checkout");
|
|
139
|
+
expect(checkout.steps.map(line)).toEqual([
|
|
140
|
+
"client->shop.cart rpc checkout",
|
|
141
|
+
"shop.cart->auth.auth rpc validateSession",
|
|
142
|
+
"shop.cart->cart-pg call byId",
|
|
143
|
+
"shop.cart->shop.pricing rpc GetQuote",
|
|
144
|
+
"shop.cart->cart-pg call save",
|
|
145
|
+
"shop.cart->bus event BasketCheckedOut",
|
|
146
|
+
]);
|
|
147
|
+
expect(checkout.participants.map((p) => `${p.id}/${p.kind}`)).toContain("auth.auth/service");
|
|
148
|
+
const rpc = checkout.steps[1] as Step;
|
|
149
|
+
expect(rpc.ref).toBe("auth.v1.Sessions/validateSession");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("opens a policy on the bus and draws its choices", () => {
|
|
153
|
+
const policy = bySlug("cart-touch-on-checkout");
|
|
154
|
+
expect(policy.steps[0]).toMatchObject({ from: "bus", to: "shop.cart", kind: "event", ref: "shop.cart.basket.BasketCheckedOut" });
|
|
155
|
+
const alts = policy.steps.filter((s): s is Alt => s.type === "alt");
|
|
156
|
+
expect(alts.map((a) => a.branches.map((b) => `${b.title}${b.terminal ? "!" : ""}:${b.steps.length}`))).toEqual([
|
|
157
|
+
['basket.status === "open"!:1', "otherwise:0"],
|
|
158
|
+
['basket.currency is "EUR":1', "otherwise!:0"],
|
|
159
|
+
]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("does not read a field off an event as the event leaving", () => {
|
|
163
|
+
// event.basketId is handed to byId; the event itself is not published again.
|
|
164
|
+
const policy = bySlug("cart-touch-on-checkout");
|
|
165
|
+
const events = policy.steps.filter((s): s is Step => s.type === "step" && s.kind === "event");
|
|
166
|
+
expect(events).toHaveLength(1);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("the protocol", () => {
|
|
171
|
+
it("answers a describe with what it is and what it can be told", () => {
|
|
172
|
+
const resp = JSON.parse(serve(JSON.stringify({ kind: "describe" })));
|
|
173
|
+
expect(resp.describe.name).toBe("extract-ts");
|
|
174
|
+
expect(resp.describe.phases).toEqual(["extract"]);
|
|
175
|
+
expect(resp.describe.options.additionalProperties).toBe(false);
|
|
176
|
+
expect(Object.keys(resp.describe.options.properties).sort()).toEqual(
|
|
177
|
+
["classification", "context", "contextName", "contextSummary", "events", "graphql", "out", "peers", "repo", "service", "serviceName", "source", "store"],
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("refuses to run with no root", () => {
|
|
182
|
+
expect(() => serve(JSON.stringify({}))).toThrow(/no input root/);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// The same service in JavaScript: the types in doc comments, a port a
|
|
187
|
+
// typedef, a field an assignment to `this`. What it reads to is what the
|
|
188
|
+
// TypeScript tree reads to, and the golden is the proof - compared with the
|
|
189
|
+
// two things that can only differ set aside: the root and the line numbers.
|
|
190
|
+
describe("the JavaScript twin", () => {
|
|
191
|
+
const JS = "plugins/extract-ts/testdata/cart-js";
|
|
192
|
+
const sameShape = (v: unknown): unknown =>
|
|
193
|
+
JSON.parse(JSON.stringify(v), (_k, x) =>
|
|
194
|
+
typeof x === "string"
|
|
195
|
+
? x
|
|
196
|
+
.replace(/testdata\/cart-js(\/|$)/g, "testdata/cart$1")
|
|
197
|
+
.replace(/\.(d\.ts|js)(:\d+)?$/, ".ts")
|
|
198
|
+
.replace(/\.ts:\d+$/, ".ts")
|
|
199
|
+
: x,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
it("reads to the fragment the TypeScript tree reads to", () => {
|
|
203
|
+
const twin = extract({ root: JS, commit: "abc1234", generatedAt: "2026-09-04T00:00:00Z" }, options);
|
|
204
|
+
const golden = JSON.parse(readFileSync(`${ROOT}/expected.json`, "utf8"));
|
|
205
|
+
expect(sameShape(JSON.parse(twin.files[0]!.contents))).toEqual(sameShape(golden));
|
|
206
|
+
expect(twin.warnings.map((d) => d.ref)).toEqual(["getBasket"]);
|
|
207
|
+
});
|
|
208
|
+
});
|