@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,295 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// Reading a view.
|
|
4
|
+
//
|
|
5
|
+
// A view is kept apart from a table rather than folded in behind a flag: it has
|
|
6
|
+
// no key, no constraints and no rows of its own, and what it has instead is
|
|
7
|
+
// what it reads. So this reads the select: which relations it draws from, what
|
|
8
|
+
// each output column is called, and - the part a table cannot say - which
|
|
9
|
+
// column each one was computed from.
|
|
10
|
+
//
|
|
11
|
+
// The type of a column is not in the statement, because a view does not declare
|
|
12
|
+
// one. It is taken from the column it is computed from when there is exactly
|
|
13
|
+
// one; a column summed, coalesced or joined out of several says nothing and is
|
|
14
|
+
// left blank rather than guessed at.
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"strings"
|
|
18
|
+
|
|
19
|
+
"github.com/pgplex/pgparser/nodes"
|
|
20
|
+
|
|
21
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
// view is what one CREATE VIEW says, before the store turns names into ids.
|
|
25
|
+
type view struct {
|
|
26
|
+
name string
|
|
27
|
+
doc string
|
|
28
|
+
// materialized: the database keeps the rows rather than recomputing them,
|
|
29
|
+
// which means they can be stale. A reader has to know that before believing
|
|
30
|
+
// one, so it is carried rather than inferred from the name.
|
|
31
|
+
materialized bool
|
|
32
|
+
// reads names the relations of the FROM clause, in the order written.
|
|
33
|
+
reads []string
|
|
34
|
+
// columns, in the order the select lists them.
|
|
35
|
+
columns []viewColumn
|
|
36
|
+
// definition is the statement as it was written.
|
|
37
|
+
definition string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type viewColumn struct {
|
|
41
|
+
name string
|
|
42
|
+
// from is "<relation>.<column>" for every column this one is computed from.
|
|
43
|
+
from []string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func readView(stmt *nodes.ViewStmt, sql string) view {
|
|
47
|
+
return selectInto(relationName(stmt.View), stmt.Query, false, sql)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// readMaterializedView reads the other spelling. The grammar calls it a CREATE
|
|
51
|
+
// TABLE AS whose object type is a matview, because that is what it is: a query
|
|
52
|
+
// whose rows are kept.
|
|
53
|
+
func readMaterializedView(stmt *nodes.CreateTableAsStmt, sql string) (view, bool) {
|
|
54
|
+
if stmt.Objtype != nodes.OBJECT_MATVIEW || stmt.Into == nil || stmt.Into.Rel == nil {
|
|
55
|
+
return view{}, false
|
|
56
|
+
}
|
|
57
|
+
query := stmt.Query
|
|
58
|
+
if query == nil {
|
|
59
|
+
query = stmt.Into.ViewQuery
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return selectInto(relationName(stmt.Into.Rel), query, true, sql), true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func selectInto(name string, node nodes.Node, materialized bool, sql string) view {
|
|
66
|
+
out := view{name: name, materialized: materialized}
|
|
67
|
+
|
|
68
|
+
query, ok := node.(*nodes.SelectStmt)
|
|
69
|
+
if !ok {
|
|
70
|
+
return out
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// alias -> relation, so `j.id` can be read as a column of journal_entries.
|
|
74
|
+
relations := map[string]string{}
|
|
75
|
+
readFrom(query.FromClause, relations, &out.reads)
|
|
76
|
+
|
|
77
|
+
for _, item := range items(query.TargetList) {
|
|
78
|
+
target, ok := item.(*nodes.ResTarget)
|
|
79
|
+
if !ok {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var sources []string
|
|
84
|
+
columnRefs(target.Val, relations, &sources)
|
|
85
|
+
|
|
86
|
+
name := target.Name
|
|
87
|
+
if name == "" && len(sources) == 1 {
|
|
88
|
+
// `SELECT j.state` names its column after the column it takes.
|
|
89
|
+
name = sources[0][strings.LastIndex(sources[0], ".")+1:]
|
|
90
|
+
}
|
|
91
|
+
if name == "" {
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
out.columns = append(out.columns, viewColumn{name: name, from: sources})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
out.definition = definitionOf(sql, out.name, materialized)
|
|
98
|
+
|
|
99
|
+
return out
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// readFrom walks the FROM clause, which is a tree once anything is joined.
|
|
103
|
+
func readFrom(list *nodes.List, relations map[string]string, reads *[]string) {
|
|
104
|
+
for _, item := range items(list) {
|
|
105
|
+
readFromItem(item, relations, reads)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func readFromItem(item nodes.Node, relations map[string]string, reads *[]string) {
|
|
110
|
+
switch node := item.(type) {
|
|
111
|
+
case *nodes.RangeVar:
|
|
112
|
+
name := relationName(node)
|
|
113
|
+
relations[node.Relname] = name
|
|
114
|
+
relations[name] = name
|
|
115
|
+
if node.Alias != nil && node.Alias.Aliasname != "" {
|
|
116
|
+
relations[node.Alias.Aliasname] = name
|
|
117
|
+
}
|
|
118
|
+
*reads = append(*reads, name)
|
|
119
|
+
|
|
120
|
+
case *nodes.JoinExpr:
|
|
121
|
+
readFromItem(node.Larg, relations, reads)
|
|
122
|
+
readFromItem(node.Rarg, relations, reads)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// columnRefs collects every column an expression reaches, however deep: a sum
|
|
127
|
+
// of a coalesce of a column is still that column's value arriving here.
|
|
128
|
+
func columnRefs(node nodes.Node, relations map[string]string, into *[]string) {
|
|
129
|
+
switch expression := node.(type) {
|
|
130
|
+
case *nodes.ColumnRef:
|
|
131
|
+
fields := stringList(expression.Fields)
|
|
132
|
+
if len(fields) == 0 {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
column := fields[len(fields)-1]
|
|
136
|
+
relation := ""
|
|
137
|
+
if len(fields) > 1 {
|
|
138
|
+
relation = relations[fields[len(fields)-2]]
|
|
139
|
+
} else {
|
|
140
|
+
// A qualified relation has two lookup keys (base name and full
|
|
141
|
+
// name), but it is still only one FROM source.
|
|
142
|
+
for _, candidate := range relations {
|
|
143
|
+
if relation == "" {
|
|
144
|
+
relation = candidate
|
|
145
|
+
} else if relation != candidate {
|
|
146
|
+
relation = ""
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if relation == "" || column == "*" {
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
*into = append(*into, relation+"."+column)
|
|
155
|
+
|
|
156
|
+
case *nodes.FuncCall:
|
|
157
|
+
for _, argument := range items(expression.Args) {
|
|
158
|
+
columnRefs(argument, relations, into)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
case *nodes.CoalesceExpr:
|
|
162
|
+
for _, argument := range items(expression.Args) {
|
|
163
|
+
columnRefs(argument, relations, into)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case *nodes.A_Expr:
|
|
167
|
+
columnRefs(expression.Lexpr, relations, into)
|
|
168
|
+
columnRefs(expression.Rexpr, relations, into)
|
|
169
|
+
|
|
170
|
+
case *nodes.TypeCast:
|
|
171
|
+
columnRefs(expression.Arg, relations, into)
|
|
172
|
+
|
|
173
|
+
case *nodes.CaseExpr:
|
|
174
|
+
columnRefs(expression.Arg, relations, into)
|
|
175
|
+
for _, when := range items(expression.Args) {
|
|
176
|
+
if clause, ok := when.(*nodes.CaseWhen); ok {
|
|
177
|
+
columnRefs(clause.Expr, relations, into)
|
|
178
|
+
columnRefs(clause.Result, relations, into)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
columnRefs(expression.Defresult, relations, into)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// definitionOf cuts the statement out of the file it was read from.
|
|
186
|
+
//
|
|
187
|
+
// The parse tree is not printed back: a reader comparing the page to the
|
|
188
|
+
// migration wants the SQL somebody wrote, down to the line breaks they chose.
|
|
189
|
+
func definitionOf(sql, name string, materialized bool) string {
|
|
190
|
+
lower := strings.ToLower(sql)
|
|
191
|
+
opening := "create view "
|
|
192
|
+
if materialized {
|
|
193
|
+
opening = "create materialized view "
|
|
194
|
+
}
|
|
195
|
+
at := strings.Index(lower, opening+strings.ToLower(name))
|
|
196
|
+
if at < 0 {
|
|
197
|
+
at = strings.Index(lower, "create or replace view "+strings.ToLower(name))
|
|
198
|
+
}
|
|
199
|
+
if at < 0 {
|
|
200
|
+
return ""
|
|
201
|
+
}
|
|
202
|
+
end := strings.Index(sql[at:], ";")
|
|
203
|
+
if end < 0 {
|
|
204
|
+
return strings.TrimSpace(sql[at:])
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return strings.TrimSpace(sql[at : at+end+1])
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// asCatalog turns the names into ids, and takes what the statement does not
|
|
211
|
+
// say - a column's type, and whether it can be null - from the column it is
|
|
212
|
+
// computed from.
|
|
213
|
+
func (v view) asCatalog(storeID, source string, tables []catalog.Table) catalog.View {
|
|
214
|
+
out := catalog.View{
|
|
215
|
+
ID: storeID + "." + v.name,
|
|
216
|
+
Name: v.name,
|
|
217
|
+
Doc: v.doc,
|
|
218
|
+
Materialized: v.materialized,
|
|
219
|
+
Definition: v.definition,
|
|
220
|
+
Source: source,
|
|
221
|
+
}
|
|
222
|
+
for _, relation := range v.reads {
|
|
223
|
+
out.Reads = append(out.Reads, storeID+"."+relation)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for _, column := range v.columns {
|
|
227
|
+
held := catalog.Column{Name: column.name, Nullable: true}
|
|
228
|
+
for _, from := range column.from {
|
|
229
|
+
held.From = append(held.From, storeID+"."+from)
|
|
230
|
+
}
|
|
231
|
+
if len(column.from) == 1 {
|
|
232
|
+
at := strings.LastIndex(column.from[0], ".")
|
|
233
|
+
if at < 0 {
|
|
234
|
+
continue
|
|
235
|
+
}
|
|
236
|
+
relation, name := column.from[0][:at], column.from[0][at+1:]
|
|
237
|
+
if source := columnOf(tables, storeID+"."+relation, name); source != nil {
|
|
238
|
+
held.Type = source.Type
|
|
239
|
+
held.Nullable = source.Nullable
|
|
240
|
+
held.Maps = source.Maps
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
out.Columns = append(out.Columns, held)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// What it holds is what the relation it is built on holds: a view over one
|
|
247
|
+
// aggregate's tables is another way of reading that aggregate, and a view
|
|
248
|
+
// that draws on several says nothing here rather than picking one.
|
|
249
|
+
persists := ""
|
|
250
|
+
for _, relation := range v.reads {
|
|
251
|
+
table := tableOf(tables, storeID+"."+relation)
|
|
252
|
+
if table == nil || table.Persists == nil {
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
if persists == "" {
|
|
256
|
+
persists = table.Persists.Aggregate
|
|
257
|
+
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
if persists != table.Persists.Aggregate {
|
|
261
|
+
persists = ""
|
|
262
|
+
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if persists != "" {
|
|
267
|
+
out.Persists = &catalog.Persists{Aggregate: persists}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return out
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
func tableOf(tables []catalog.Table, id string) *catalog.Table {
|
|
274
|
+
for i := range tables {
|
|
275
|
+
if tables[i].ID == id {
|
|
276
|
+
return &tables[i]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return nil
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
func columnOf(tables []catalog.Table, tableID, name string) *catalog.Column {
|
|
284
|
+
table := tableOf(tables, tableID)
|
|
285
|
+
if table == nil {
|
|
286
|
+
return nil
|
|
287
|
+
}
|
|
288
|
+
for i := range table.Columns {
|
|
289
|
+
if table.Columns[i].Name == name {
|
|
290
|
+
return &table.Columns[i]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return nil
|
|
295
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# extract-ts
|
|
2
|
+
|
|
3
|
+
A TypeScript or JavaScript service in, a catalog fragment out. The TypeScript twin of
|
|
4
|
+
`extract-go`: it describes one service - its aggregates, what they publish,
|
|
5
|
+
what runs when somebody calls in, what the service calls - by reading the
|
|
6
|
+
tree, and it reads the tree by its layout. Nothing is annotated for the
|
|
7
|
+
catalog; the layout is the claim.
|
|
8
|
+
|
|
9
|
+
Written in TypeScript and run as a process plugin, `node plugins/extract-ts/main.ts`,
|
|
10
|
+
because the catalog's own types in `src/catalog.ts` are the fragment's types.
|
|
11
|
+
The parser is oxc-parser: native, with no TypeScript inside it, handing back
|
|
12
|
+
an ESTree with the TypeScript nodes typescript-eslint would give. The
|
|
13
|
+
project's TypeScript is the 7 series, a compiler with no syntax tree to offer,
|
|
14
|
+
and holding a second copy of 5 beside it for the parser alone was a workaround
|
|
15
|
+
this replaces. The tree is read through `ast.ts`, a hand-written, minimal set
|
|
16
|
+
of node shapes and predicates, so the reading does not move when the parser's
|
|
17
|
+
own typings do. There is no type checker: everything is resolved by name and by import - the
|
|
18
|
+
resolver is `oxc-resolver`, so a tsconfig `paths` alias and a workspace package
|
|
19
|
+
resolve as they would for Node and TypeScript - which is all a layout that is
|
|
20
|
+
the claim needs.
|
|
21
|
+
|
|
22
|
+
## The layout it reads
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
src/
|
|
26
|
+
domain/<aggregate>/
|
|
27
|
+
<aggregate>.ts the root: a class named like the directory, in PascalCase
|
|
28
|
+
*.ts entities: other classes exported from the directory's own files
|
|
29
|
+
vo/*.ts value objects: one class per file
|
|
30
|
+
rules/*.ts specifications; skipped, they are not shapes
|
|
31
|
+
events/*.ts one class per event, `readonly name = "<service>.<Name>"`
|
|
32
|
+
port.ts interfaces a use case holds: repositories, publishers
|
|
33
|
+
status.ts optional: `export const TRANSITIONS = { open: ["checked-out"], … }`, the root's lifecycle
|
|
34
|
+
README.md the aggregate's page, or the doc comment above the root class
|
|
35
|
+
application/<aggregate>/usecases/<name>/
|
|
36
|
+
usecase.ts `export class UseCase` with a `handle(...)` method
|
|
37
|
+
README.md the operation's doc, or the doc comment above the class
|
|
38
|
+
application/policy/*.ts one class per policy, `handle(event)` reacting to one event
|
|
39
|
+
infrastructure/transport/http/
|
|
40
|
+
gen/openapi.yaml the document; operationIds name the handlers
|
|
41
|
+
<pkg>/*.ts handlers: `async <operationId>(...)` methods on a class holding use cases
|
|
42
|
+
schema/<module>/
|
|
43
|
+
schema.graphql the contract, read by extract-graphql rather than here
|
|
44
|
+
resolvers/<Root>/<field>.ts
|
|
45
|
+
one file per field of Query, Mutation or Subscription,
|
|
46
|
+
exporting the field's own name; the third parameter is
|
|
47
|
+
the context, and `ctx.<port>.<method>(...)` is a hop
|
|
48
|
+
<anywhere>/context.ts `export interface GraphQLContext` - the ports every
|
|
49
|
+
resolver may reach, which is what a use case says in
|
|
50
|
+
its constructor
|
|
51
|
+
ports/*.ts ports a service with no use cases declares on their own
|
|
52
|
+
infrastructure/<peer>/
|
|
53
|
+
gen/openapi.yaml + gen/types.ts an HTTP peer: the vendored document and `openapi-typescript` output
|
|
54
|
+
proto/**/*.proto + gen/*_pb.ts a gRPC peer: the vendored contract and Connect-ES output
|
|
55
|
+
client.ts the adapter over the generated client, implementing a use case's port
|
|
56
|
+
infrastructure/repository/<aggregate>/
|
|
57
|
+
migrations/*.sql read by extract-sql, with `repositories` pointed at the directory above
|
|
58
|
+
di/**/*.ts which adapter or use case fills a port: `export function provideX(deps): Port`,
|
|
59
|
+
or an Inversify container's `bind<Port>(TOKENS.X).to(Impl)` or
|
|
60
|
+
`.toConstantValue(new Impl(...))`; a port bound more than once - the adapter
|
|
61
|
+
behind a setting, a stand-in without - shows as the binding that reaches a
|
|
62
|
+
peer; a use case's constructor may carry `@inject(TOKENS.X)`, the type
|
|
63
|
+
beside it is still the port
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## A service whose way in is a graph
|
|
67
|
+
|
|
68
|
+
A BFF has no aggregates, no store and no use cases: it composes what other
|
|
69
|
+
services decided, and there is nothing left for it to decide. Told where the
|
|
70
|
+
schema is - the `graphql` option, `src/schema` by convention - this reader
|
|
71
|
+
opens a flow on each resolver instead of on an HTTP or gRPC endpoint, and does
|
|
72
|
+
not report the missing domain.
|
|
73
|
+
|
|
74
|
+
A resolver's ports do not arrive in a constructor. The signature comes from
|
|
75
|
+
the generated resolver type, so the context parameter is typed by where it
|
|
76
|
+
sits rather than by anything written in the file, and the ports are read once
|
|
77
|
+
from the one exported `GraphQLContext` interface - the name codegen's own
|
|
78
|
+
`contextType` conventionally points at. From there everything is the same as
|
|
79
|
+
inside a use case: `ctx.baskets.byId(...)` is a port call, assembly says what
|
|
80
|
+
fills the port, and the adapter behind it names the peer.
|
|
81
|
+
|
|
82
|
+
The field is called what `extract-graphql` calls it - `Query.basket`,
|
|
83
|
+
`Mutation.checkout` - because the directory a resolver sits in is the root
|
|
84
|
+
type and the file is the field. That is why nothing here parses a schema: the
|
|
85
|
+
layout already says what the SDL would.
|
|
86
|
+
|
|
87
|
+
An adapter over the bus is the one port call that draws no step. It calls
|
|
88
|
+
nobody - it waits - and what it hears is said by the channel the service
|
|
89
|
+
declares in its AsyncAPI document, not by a step in a flow.
|
|
90
|
+
|
|
91
|
+
## A service written in JavaScript
|
|
92
|
+
|
|
93
|
+
The same layout in `.js` (or `.mjs`, `.jsx`) reads the same way. What the
|
|
94
|
+
syntax cannot say, the doc comments do, in the form `tsc --checkJs` reads:
|
|
95
|
+
|
|
96
|
+
| in TypeScript | in JavaScript |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `readonly items: BasketItem[] = []` | `/** @type {BasketItem[]} */ items = [];` |
|
|
99
|
+
| `constructor(readonly id: string)` | `/** @param {string} id */ constructor(id) { this.id = id; }` - a field is what the constructor assigns to `this`, typed by the parameter it takes or by what it is given |
|
|
100
|
+
| `handle(input: Input): Promise<void>` | `/** @param {Input} input @returns {Promise<void>} */ handle(input)` |
|
|
101
|
+
| `export interface Port { save(b: Basket): Promise<void> }` | `/** @typedef {{ save(b: Basket): Promise<void> }} Port */`, or `@typedef {Object} Port` with one `@property {type} name` per member |
|
|
102
|
+
| `import type { Port } from "./port.ts"` | `/** @typedef {import("./port.js").Port} Port */`, or `import("./port.js").Port` written inline in any type |
|
|
103
|
+
| `bind<Port>(TOKENS.Port)` | `bind(TOKENS.Port)` - the token's name is the port's |
|
|
104
|
+
| `function provideX(deps): Port` | `/** @returns {Port} */ function provideX(deps)` |
|
|
105
|
+
| `gen/types.ts`, `gen/**/x_pb.ts` | `gen/types.d.ts`, `gen/**/x_pb.js` |
|
|
106
|
+
|
|
107
|
+
A type expression in a comment is TypeScript's own syntax, so it is parsed
|
|
108
|
+
as one - `let _: <expr>` - and read by the same code that reads an
|
|
109
|
+
annotation. Closure's `?T` and `Array.<T>` are not, and read as nothing.
|
|
110
|
+
`@param`, `@returns` and `@type` are read only where the syntax has no
|
|
111
|
+
annotation, so a TypeScript file that leaves a field untyped and documents it
|
|
112
|
+
instead reads that too. Decorators are not JavaScript; assembly in a
|
|
113
|
+
JavaScript tree is the container's `bind(...)` calls or the `provideX`
|
|
114
|
+
functions, which need none.
|
|
115
|
+
|
|
116
|
+
`testdata/cart-js` is `testdata/cart` written in JavaScript, file for file,
|
|
117
|
+
and the test is that both read to one fragment.
|
|
118
|
+
|
|
119
|
+
## What a doc comment says
|
|
120
|
+
|
|
121
|
+
The `/** … */` above a class, a field or a constructor parameter property is
|
|
122
|
+
its doc, read the way [JSDoc](https://jsdoc.app/) and TSDoc say to: the prose
|
|
123
|
+
above the first tag, then the tags. Most tags are for a type checker or a site
|
|
124
|
+
generator and are dropped - `@param`, `@returns`, `@see`, `@fires` - because
|
|
125
|
+
the layout and the signature already say what they would, and a tag that
|
|
126
|
+
restates the code is one more place for it to go stale. Three are kept:
|
|
127
|
+
|
|
128
|
+
| tag | becomes |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `@remarks` | the paragraphs after the summary, in the same doc: TSDoc's long description is still the description |
|
|
131
|
+
| `@example` | a fenced block in the aggregate's readme when the root has no `README.md`; elsewhere an example is code where a sentence should be, and is left out |
|
|
132
|
+
| `@deprecated` | `deprecated: true` on the entity, value object, field, event version or operation, which the page strikes through and badges; the reason, when one follows the tag, is appended to the doc as "Deprecated: …" - except for an operation, whose doc stays the one paragraph its README or summary gives |
|
|
133
|
+
|
|
134
|
+
An inline `{@link X}` is flattened to `X`, or to its label when it has one
|
|
135
|
+
(`{@link X|label}`, `[label]{@link X}`): the catalog has no way to say from
|
|
136
|
+
inside a sentence that a name is a page, so the name is kept as a word rather
|
|
137
|
+
than left as a tag nobody renders.
|
|
138
|
+
|
|
139
|
+
## What becomes what
|
|
140
|
+
|
|
141
|
+
**Aggregate.** Every directory under `src/domain/` whose name has a root class
|
|
142
|
+
in it. The root is the class whose name is the directory's in PascalCase
|
|
143
|
+
(`basket` → `Basket`); a directory without one is reported and skipped. Its
|
|
144
|
+
fields are the class's property declarations and constructor parameter
|
|
145
|
+
properties, `readonly id: string` and `constructor(readonly id: string)` alike,
|
|
146
|
+
with the type as written. Other classes exported from the directory's top-level
|
|
147
|
+
files are entities; classes in `vo/` are value objects; `rules/` is skipped.
|
|
148
|
+
The readme is `README.md` in the directory, or the doc comment above the root,
|
|
149
|
+
its `@example`s fenced as code.
|
|
150
|
+
|
|
151
|
+
**Event.** Each class in `events/` with a `name` property initialised to a
|
|
152
|
+
string literal - `readonly name = "cart.BasketCheckedOut"` or `static readonly
|
|
153
|
+
name` - is an event of the aggregate, its id `<service>.<aggregate>.<Class>`.
|
|
154
|
+
Fields as for a root; doc from JSDoc; one version, `v1`; `consumers` empty,
|
|
155
|
+
because a service does not know who listens - the merge is where the other end
|
|
156
|
+
arrives, and the flows imply the rest.
|
|
157
|
+
|
|
158
|
+
**Operation.** Each `application/<aggregate>/usecases/<name>/usecase.ts`
|
|
159
|
+
exporting a class `UseCase` with a method `handle`. The id is the directory's
|
|
160
|
+
name in PascalCase (`add_item` → `AddItem`). It is a command when `handle` -
|
|
161
|
+
or a private method it calls - calls `save`, `delete`, `create`, `update` or
|
|
162
|
+
`publish` on a port, else a query. The doc is the directory's `README.md`
|
|
163
|
+
first paragraph, or the JSDoc above the class. `exposedBy` names the HTTP
|
|
164
|
+
operations that run it, read from the handlers.
|
|
165
|
+
|
|
166
|
+
**Port.** A constructor parameter of `UseCase` typed with an interface or a
|
|
167
|
+
class. What it is decides what a call on it becomes:
|
|
168
|
+
|
|
169
|
+
| the type is | a call on it is |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| an interface from `domain/<aggregate>/port.ts` | a `call` into the store lane, named by the method; an argument that holds an event is the event leaving for the bus |
|
|
172
|
+
| another `UseCase` | a `call` to the service itself, and that use case's own steps, inlined two deep at most |
|
|
173
|
+
| an interface bound in `di/` to a use case | the same |
|
|
174
|
+
| an interface bound in `di/` to an adapter over a generated client | an `rpc` to the peer, read through the adapter's method to the client's call |
|
|
175
|
+
| an interface another use case declares, imported from its `usecase.ts` | whatever that use case's binding says: the port is looked up under the use case that declares it |
|
|
176
|
+
| the generated client itself | an `rpc` to the peer |
|
|
177
|
+
| a function type, `() => Date` | nothing: a clock is a port with nobody at the other end |
|
|
178
|
+
|
|
179
|
+
**Lifecycle.** An aggregate whose directory exports a `TRANSITIONS` table -
|
|
180
|
+
an object literal, each state to the states it may become - gets a
|
|
181
|
+
`lifecycle`: the states in the table's order, the first being where a new
|
|
182
|
+
root starts, and one move per edge. The method of the root that assigns
|
|
183
|
+
`this.status` is the mover; every public method that calls it with a literal
|
|
184
|
+
(`this.moveTo("checked-out", now)`) makes the edges into that state, and
|
|
185
|
+
`emits` is the event its return type names. A status assigned anywhere else,
|
|
186
|
+
a move into a state the table lacks, and an edge in the table no method makes
|
|
187
|
+
are each reported. A state nothing leads out of is terminal; that is derived
|
|
188
|
+
on the page, never written down.
|
|
189
|
+
|
|
190
|
+
**Flow, from an endpoint.** Each handler method named by an `operationId` in
|
|
191
|
+
`transport/http/gen/openapi.yaml` opens a flow: `client → service : rpc
|
|
192
|
+
<operationId>`, then the steps of every use case the handler runs, in the order
|
|
193
|
+
it runs them. **Flow, from a policy.** Each class in `application/policy/` with
|
|
194
|
+
a `handle` opens a flow on the bus: `bus → service : event <ref>`, where the
|
|
195
|
+
event is what the body tests for - `event instanceof BasketCheckedOut`, `event
|
|
196
|
+
.name === "cart.BasketCheckedOut"`, or a `switch` on `event.name`. An event
|
|
197
|
+
imported from outside `src/domain` is another service's; with a line in the
|
|
198
|
+
`events` option mapping the import to an aggregate id it resolves, without one
|
|
199
|
+
the step names the type and is unresolved.
|
|
200
|
+
|
|
201
|
+
**Inside a body.** Statements are read in source order. A call on a port is a
|
|
202
|
+
hop; a call on `this.<method>` is followed into the method; `if` and `switch`
|
|
203
|
+
become an `alt` when some arm holds a hop, an arm that `return`s or `throw`s
|
|
204
|
+
is terminal; `for`, `for … of`, `while` become a note on the steps inside;
|
|
205
|
+
`await` is transparent; a `.then(...)` chain is followed as a call. Every step
|
|
206
|
+
is `declared`: this reads code, and code is a claim about behaviour, not a
|
|
207
|
+
record of it. A call whose peer the manifest does not name is `unresolved`.
|
|
208
|
+
|
|
209
|
+
**What a name holds.** A value is followed back to its type by name, without a
|
|
210
|
+
checker: what a domain constructor, a domain method or a port method hands
|
|
211
|
+
back, as its return type is written - `Promise<[Basket, BasketCreated]>` binds
|
|
212
|
+
both by position - and what a helper function anywhere under `src/` returns
|
|
213
|
+
when its return type names something of an aggregate (`holderOf(repo, id,
|
|
214
|
+
token): Promise<Basket>`). `for (const basket of idle)` holds one of what
|
|
215
|
+
`idle` holds; `into = created` holds what `created` held; `events.push(x)`
|
|
216
|
+
collects what `x` holds, and `...events` handed to a port is each event
|
|
217
|
+
collected, once. An event handed to a domain port is the event leaving for the
|
|
218
|
+
bus; a field read off one is not.
|
|
219
|
+
|
|
220
|
+
**HTTP peer.** A generated `openapi-fetch` client is called with the verb and
|
|
221
|
+
the path in the code - `client.GET("/v1/sessions/current", …)` - and the
|
|
222
|
+
document vendored beside `gen/types.ts` says which operation answers on that
|
|
223
|
+
route and which interface it belongs to, spelled by `plugins/openapi` so the
|
|
224
|
+
call and the method on the other side share one id: `auth.v1.Sessions/validateSession`.
|
|
225
|
+
**gRPC peer.** A Connect-ES client is created from a service descriptor whose
|
|
226
|
+
`typeName` is the proto service, `shop.v1.Pricing`, and a call on it is the
|
|
227
|
+
method in the descriptor's own case: `client.getQuote(…)` is
|
|
228
|
+
`shop.v1.Pricing/GetQuote`. The descriptor is imported from under `gen/`,
|
|
229
|
+
however deep its package path put it, and the service is looked up in the
|
|
230
|
+
`.proto` files vendored under `proto/` beside that `gen/`. Either way the peer is the manifest's `peers`
|
|
231
|
+
entry for the api id or the proto package; without one the lane is `unknown`
|
|
232
|
+
and the step unresolved, and the call is recorded on the service's `consumes`
|
|
233
|
+
with the vendored document as its source.
|
|
234
|
+
|
|
235
|
+
## Options
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"context": "shop",
|
|
240
|
+
"contextName": "Shop",
|
|
241
|
+
"service": "cart",
|
|
242
|
+
"store": "pg",
|
|
243
|
+
"peers": { "auth.v1": "auth.auth", "shop.v1": "shop.pricing" },
|
|
244
|
+
"events": { "@acme/payments-events": "payments.ledger.payment" },
|
|
245
|
+
"source": "src",
|
|
246
|
+
"out": "domain.json"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`source` is the directory the layout starts in, `src` unless said otherwise.
|
|
251
|
+
Everything else means what it means for `extract-go`.
|
|
252
|
+
|
|
253
|
+
## Extraction limits
|
|
254
|
+
|
|
255
|
+
These cases do not become facts in the fragment:
|
|
256
|
+
|
|
257
|
+
- a domain directory with no root class of its name;
|
|
258
|
+
- a use case directory with no `UseCase`, or a `UseCase` with no `handle`;
|
|
259
|
+
- a port whose type is neither a domain port, a use case, nor a client, whose
|
|
260
|
+
calls are therefore left out;
|
|
261
|
+
- a handler named by no `operationId`, and an `operationId` with no handler;
|
|
262
|
+
- a policy whose `handle` tests for no event;
|
|
263
|
+
- a peer package with no document beside it, or a route the document does not
|
|
264
|
+
declare;
|
|
265
|
+
- an event no flow reaches;
|
|
266
|
+
- a proto package or api id the manifest names no peer for.
|
|
267
|
+
- an import of a dependency: what lands in `node_modules` is not read, so a
|
|
268
|
+
port declared by a published package is a name with nothing behind it. A
|
|
269
|
+
tsconfig `paths` alias and a workspace package are followed, because they
|
|
270
|
+
resolve to the repository's own files.
|
|
271
|
+
- a file with a syntax error, past the error: the tree up to it is read, the
|
|
272
|
+
rest is not, and the warning says where.
|
|
273
|
+
|
|
274
|
+
## Manifest
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"plugins": [{ "name": "ts-domain", "process": { "command": "node", "args": ["plugins/extract-ts/main.ts"] } }],
|
|
279
|
+
"extract": [
|
|
280
|
+
{ "plugin": "ts-domain", "in": "examples/shop/cart", "out": "examples/shop/cart/portolan",
|
|
281
|
+
"options": { "context": "shop", "service": "cart", "store": "pg",
|
|
282
|
+
"peers": { "auth.v1": "auth.auth", "shop.v1": "shop.pricing" }, "out": "domain.json" } },
|
|
283
|
+
{ "plugin": "openapi", "in": "examples/shop/cart", "out": "examples/shop/cart/portolan",
|
|
284
|
+
"options": { "context": "shop", "service": "cart",
|
|
285
|
+
"spec": "src/infrastructure/transport/http/gen/openapi.yaml", "out": "api.json" } },
|
|
286
|
+
{ "plugin": "sql", "in": "examples/shop/cart", "out": "examples/shop/cart/portolan",
|
|
287
|
+
"options": { "context": "shop", "service": "cart", "store": "pg", "name": "Cart database",
|
|
288
|
+
"repositories": "src/infrastructure/repository", "out": "stores.json" } }
|
|
289
|
+
],
|
|
290
|
+
"verify": [
|
|
291
|
+
{ "plugin": "otel", "in": "examples/shop/cart", "out": "examples/shop/cart/portolan",
|
|
292
|
+
"options": { "traces": ["telemetry/traces.jsonl"], "out": "observed.json" } }
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`extract-sql`'s `repositories` points it at the adapters: its default is the
|
|
298
|
+
Go layout, `internal/infrastructure/repository`, and a TypeScript service
|
|
299
|
+
keeps the same shape one directory over, `<aggregate>/migrations` under it.
|
|
300
|
+
|
|
301
|
+
## Tests
|
|
302
|
+
|
|
303
|
+
The reader is held to fixtures under `testdata/`: a small service in the
|
|
304
|
+
layout above, each shape it claims to read present once, and a golden
|
|
305
|
+
fragment. `examples/shop/cart/portolan/domain.json` is the second golden, held
|
|
306
|
+
by `gen:check` like every other fragment.
|