@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,245 @@
|
|
|
1
|
+
// The join between a schema and a model, asserted.
|
|
2
|
+
//
|
|
3
|
+
// Most of what matters here is what does NOT get flagged. A type comparison
|
|
4
|
+
// that shouts about every named domain shape is worse than no comparison at
|
|
5
|
+
// all, so the negative cases below carry as much weight as the positive ones.
|
|
6
|
+
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import { rawCatalog } from "../test-catalog";
|
|
9
|
+
import { buildIndex, validateCatalog } from "../catalog";
|
|
10
|
+
import type { Catalog } from "../catalog";
|
|
11
|
+
import {
|
|
12
|
+
columnsOfBlock,
|
|
13
|
+
dbClass,
|
|
14
|
+
domainClass,
|
|
15
|
+
outboxOfService,
|
|
16
|
+
payloadColumn,
|
|
17
|
+
readersOfStore,
|
|
18
|
+
storeColumnCount,
|
|
19
|
+
storedFields,
|
|
20
|
+
storesOfService,
|
|
21
|
+
tablesPersisting,
|
|
22
|
+
typesDisagree,
|
|
23
|
+
unmappedFields,
|
|
24
|
+
} from "./data-model";
|
|
25
|
+
|
|
26
|
+
const catalog = validateCatalog(
|
|
27
|
+
JSON.parse(JSON.stringify(rawCatalog)) as unknown as Catalog,
|
|
28
|
+
);
|
|
29
|
+
const index = buildIndex(catalog);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The catalog with one table's columns pointing at fields the aggregate no
|
|
33
|
+
* longer declares - the shape a schema takes after a rename that never
|
|
34
|
+
* reached the migrations, and the thing the drift warning is about.
|
|
35
|
+
*/
|
|
36
|
+
function drifted(): { catalog: Catalog; index: ReturnType<typeof buildIndex> } {
|
|
37
|
+
const copy = JSON.parse(JSON.stringify(rawCatalog)) as unknown as Catalog;
|
|
38
|
+
const orders = (copy.stores ?? [])
|
|
39
|
+
.find((s) => s.id === "shop.oms.pg")
|
|
40
|
+
?.tables.find((t) => t.name === "orders");
|
|
41
|
+
if (!orders) throw new Error("fixture has no orders table");
|
|
42
|
+
for (const column of orders.columns) if (column.maps) column.maps = `Order.${column.name}Gone`;
|
|
43
|
+
const validated = validateCatalog(copy);
|
|
44
|
+
return { catalog: validated, index: buildIndex(validated) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("type classes", () => {
|
|
48
|
+
it("reads postgres spellings, parameters and all", () => {
|
|
49
|
+
expect(dbClass("varchar(64)")).toBe("text");
|
|
50
|
+
expect(dbClass("TIMESTAMPTZ")).toBe("time");
|
|
51
|
+
expect(dbClass("timestamp with time zone")).toBe("time");
|
|
52
|
+
expect(dbClass("bigserial")).toBe("int64");
|
|
53
|
+
expect(dbClass("jsonb")).toBe("json");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("reads go spellings, pointers and slices and all", () => {
|
|
57
|
+
expect(domainClass("*string")).toBe("text");
|
|
58
|
+
expect(domainClass("[]byte")).toBe("bytes");
|
|
59
|
+
expect(domainClass("time.Time")).toBe("time");
|
|
60
|
+
expect(domainClass("int32")).toBe("int32");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("reads java and typescript spellings", () => {
|
|
64
|
+
expect(domainClass("long")).toBe("int64");
|
|
65
|
+
expect(domainClass("Long")).toBe("int64");
|
|
66
|
+
expect(domainClass("Integer")).toBe("int32");
|
|
67
|
+
expect(domainClass("String")).toBe("text");
|
|
68
|
+
expect(domainClass("BigDecimal")).toBe("decimal");
|
|
69
|
+
expect(domainClass("Instant")).toBe("time");
|
|
70
|
+
expect(domainClass("Date")).toBe("time");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("gives a width-less int its own class", () => {
|
|
74
|
+
expect(domainClass("int")).toBe("int");
|
|
75
|
+
expect(domainClass("*int")).toBe("int");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("knows nothing about a named domain shape, and says so", () => {
|
|
79
|
+
expect(domainClass("Money")).toBe("unknown");
|
|
80
|
+
expect(domainClass("GatewayRef")).toBe("unknown");
|
|
81
|
+
expect(domainClass("number")).toBe("unknown");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("typesDisagree", () => {
|
|
86
|
+
it("flags the two disagreements the spec names", () => {
|
|
87
|
+
expect(typesDisagree("uuid", "string")).toBe(true);
|
|
88
|
+
expect(typesDisagree("bigint", "int32")).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("accepts the same shape spelled differently", () => {
|
|
92
|
+
expect(typesDisagree("text", "string")).toBe(false);
|
|
93
|
+
expect(typesDisagree("timestamptz", "time.Time")).toBe(false);
|
|
94
|
+
expect(typesDisagree("integer", "int32")).toBe(false);
|
|
95
|
+
expect(typesDisagree("bigserial", "long")).toBe(false);
|
|
96
|
+
expect(typesDisagree("timestamptz", "Instant")).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("lets a width-less int sit on any integer column, and nothing else", () => {
|
|
100
|
+
// Go's `int` is whatever the platform says; Postgres `integer` under it
|
|
101
|
+
// is a choice, not a disagreement. Only a non-integer column is.
|
|
102
|
+
expect(typesDisagree("integer", "int")).toBe(false);
|
|
103
|
+
expect(typesDisagree("bigint", "int")).toBe(false);
|
|
104
|
+
expect(typesDisagree("smallint", "int")).toBe(false);
|
|
105
|
+
expect(typesDisagree("uuid", "int")).toBe(true);
|
|
106
|
+
expect(typesDisagree("text", "int")).toBe(true);
|
|
107
|
+
expect(typesDisagree("numeric", "int")).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("stays quiet when either side is a shape it does not know", () => {
|
|
111
|
+
// Half the domain types in a catalog are named shapes; guessing at them
|
|
112
|
+
// would bury the two real disagreements above.
|
|
113
|
+
expect(typesDisagree("bigint", "Money")).toBe(false);
|
|
114
|
+
expect(typesDisagree("some_extension_type", "string")).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("storesOfService", () => {
|
|
119
|
+
it("lists owned stores first, then the ones only read", () => {
|
|
120
|
+
const stores = storesOfService(index, "delivery.core");
|
|
121
|
+
expect(stores.map((s) => [s.store.id, s.access])).toEqual([
|
|
122
|
+
["delivery.core.pg", "owns"],
|
|
123
|
+
["shop.oms.pg", "reads"],
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("does not call a service a reader of its own store", () => {
|
|
128
|
+
const stores = storesOfService(index, "shop.oms");
|
|
129
|
+
expect(stores.filter((s) => s.access === "reads")).toEqual([]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("finds the readers of a store from the other side", () => {
|
|
133
|
+
expect(
|
|
134
|
+
readersOfStore(catalog, "shop.oms.pg", "shop.oms").map((s) => s.id),
|
|
135
|
+
).toEqual(["shop.pricing", "delivery.core"]);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("tablesPersisting", () => {
|
|
140
|
+
it("lists every table holding an aggregate, with its store", () => {
|
|
141
|
+
const tables = tablesPersisting(index, "shop.oms.order");
|
|
142
|
+
expect(tables.map((t) => t.table.name)).toEqual(["orders", "order_items"]);
|
|
143
|
+
expect(tables[0]?.store.id).toBe("shop.oms.pg");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("is empty for an aggregate nothing stores", () => {
|
|
147
|
+
expect(tablesPersisting(index, "shop.pricing.price-list")).toEqual([]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("outboxOfService", () => {
|
|
152
|
+
it("finds the outbox in a service's own store", () => {
|
|
153
|
+
expect(outboxOfService(index, "shop.oms")?.table.name).toBe("outbox");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("is null for a service that publishes without one", () => {
|
|
157
|
+
expect(outboxOfService(index, "payments.ledger")).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("calls the first json column the payload", () => {
|
|
161
|
+
const outbox = outboxOfService(index, "shop.oms");
|
|
162
|
+
if (!outbox) throw new Error("fixture has no outbox");
|
|
163
|
+
expect(payloadColumn(outbox.table)?.name).toBe("payload");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("storedFields", () => {
|
|
168
|
+
const stored = storedFields(catalog, index, "shop.oms.order.order");
|
|
169
|
+
|
|
170
|
+
it("joins each column to the field it maps to", () => {
|
|
171
|
+
const status = stored.find((s) => s.owner.column.name === "status");
|
|
172
|
+
expect(status?.path).toBe("status");
|
|
173
|
+
expect(status?.field?.type).toBe("Status");
|
|
174
|
+
expect(status?.mismatch).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("marks the uuid column against a string field", () => {
|
|
178
|
+
const id = stored.find((s) => s.owner.column.name === "id");
|
|
179
|
+
expect(id?.mismatch).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("leaves a named shape unmarked", () => {
|
|
183
|
+
const total = stored.find((s) => s.owner.column.name === "total_minor");
|
|
184
|
+
expect(total?.field?.type).toBe("Money");
|
|
185
|
+
expect(total?.mismatch).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("keeps a column whose field no longer exists, with no field", () => {
|
|
189
|
+
const d = drifted();
|
|
190
|
+
const stored = storedFields(d.catalog, d.index, "shop.oms.order.order");
|
|
191
|
+
expect(stored.length).toBeGreaterThan(0);
|
|
192
|
+
expect(stored.every((s) => s.field === null)).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("resolves a nested path against the field the block declares", () => {
|
|
196
|
+
// "Order.total.amountMinor" is a fact about Money, and Money is read on
|
|
197
|
+
// its own page; here it is still Order.total.
|
|
198
|
+
const line = storedFields(catalog, index, "shop.oms.order.order-line");
|
|
199
|
+
const price = line.find(
|
|
200
|
+
(s) => s.owner.column.name === "unit_price_minor",
|
|
201
|
+
);
|
|
202
|
+
expect(price?.field?.name).toBe("unitPrice");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("is empty for a block nothing stores", () => {
|
|
206
|
+
expect(storedFields(catalog, index, "shop.oms.order.risk-decision")).toEqual(
|
|
207
|
+
[],
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("columnsOfBlock", () => {
|
|
213
|
+
it("finds the columns pointing into a child block", () => {
|
|
214
|
+
const columns = columnsOfBlock(index, "shop.oms.order.order-line");
|
|
215
|
+
expect(columns.map((c) => c.column.name)).toEqual([
|
|
216
|
+
"line_no",
|
|
217
|
+
"sku",
|
|
218
|
+
"quantity",
|
|
219
|
+
"unit_price_minor",
|
|
220
|
+
]);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("counts", () => {
|
|
225
|
+
it("counts every column in a store", () => {
|
|
226
|
+
const store = (catalog.stores ?? []).find((s) => s.id === "delivery.core.pg");
|
|
227
|
+
if (!store) throw new Error("fixture has no delivery store");
|
|
228
|
+
expect(storeColumnCount(store)).toBe(
|
|
229
|
+
store.tables.reduce((n, t) => n + t.columns.length, 0),
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("counts the fields of an aggregate no column carries", () => {
|
|
234
|
+
const order = index.aggregateById.get("shop.oms.order");
|
|
235
|
+
if (!order) throw new Error("fixture has no order");
|
|
236
|
+
const d = drifted();
|
|
237
|
+
const drift = d.index.aggregateById.get("shop.oms.order");
|
|
238
|
+
if (!drift) throw new Error("drifted fixture has no order");
|
|
239
|
+
// More once the columns point at names the aggregate no longer declares:
|
|
240
|
+
// that gap is exactly what the drift warning counts.
|
|
241
|
+
expect(unmappedFields(d.catalog, d.index, drift)).toBeGreaterThan(
|
|
242
|
+
unmappedFields(catalog, index, order),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
// The persistence axis, derived. Pure over a catalog and its index, like
|
|
2
|
+
// derive.ts and backlinks.ts: no DOM, no router, so every number a Data tab or
|
|
3
|
+
// a Persistence section shows can be asserted in a test.
|
|
4
|
+
//
|
|
5
|
+
// The interesting work here is the join between two vocabularies. A schema
|
|
6
|
+
// speaks in `uuid` and `timestamptz`; a domain model speaks in `string` and
|
|
7
|
+
// `time.Time`. Neither is authoritative and neither is wrong, so nothing is
|
|
8
|
+
// rewritten into the other — the two are put side by side and the places where
|
|
9
|
+
// they disagree are marked.
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
Aggregate,
|
|
13
|
+
Catalog,
|
|
14
|
+
CatalogIndex,
|
|
15
|
+
Column,
|
|
16
|
+
ColumnOwner,
|
|
17
|
+
Field,
|
|
18
|
+
Service,
|
|
19
|
+
Store,
|
|
20
|
+
Table,
|
|
21
|
+
View,
|
|
22
|
+
} from "../catalog";
|
|
23
|
+
import {
|
|
24
|
+
aggregateBlocks,
|
|
25
|
+
blockFields,
|
|
26
|
+
mapsFieldPath,
|
|
27
|
+
storeViews,
|
|
28
|
+
} from "../catalog";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Stores and services
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* How a service touches a store. Ownership is the store's own claim, so a
|
|
36
|
+
* service that lists a store it does not own is reading it — which is a fact
|
|
37
|
+
* the canvas draws (ghosted) rather than an error.
|
|
38
|
+
*/
|
|
39
|
+
export type StoreAccess = "owns" | "reads";
|
|
40
|
+
|
|
41
|
+
export interface ServiceStore {
|
|
42
|
+
store: Store;
|
|
43
|
+
access: StoreAccess;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Every store a service touches: the ones it owns first, in catalog order,
|
|
48
|
+
* then the ones it only reads. A store it owns but forgot to list is still
|
|
49
|
+
* owned — the list is a claim about reading, never about ownership.
|
|
50
|
+
*/
|
|
51
|
+
export function storesOfService(
|
|
52
|
+
index: CatalogIndex,
|
|
53
|
+
serviceId: string,
|
|
54
|
+
): ServiceStore[] {
|
|
55
|
+
const owned = index.storesOwnedBy.get(serviceId) ?? [];
|
|
56
|
+
const out: ServiceStore[] = owned.map((store) => ({
|
|
57
|
+
store,
|
|
58
|
+
access: "owns" as const,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
const service = index.serviceById.get(serviceId);
|
|
62
|
+
for (const storeId of service?.stores ?? []) {
|
|
63
|
+
const store = index.storeById.get(storeId);
|
|
64
|
+
if (!store || store.owner === serviceId) continue;
|
|
65
|
+
out.push({ store, access: "reads" });
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Services that list a store they do not own — its readers, in catalog order. */
|
|
71
|
+
export function readersOfStore(
|
|
72
|
+
catalog: Catalog,
|
|
73
|
+
storeId: string,
|
|
74
|
+
ownerId: string,
|
|
75
|
+
): Service[] {
|
|
76
|
+
const out: Service[] = [];
|
|
77
|
+
for (const context of catalog.contexts) {
|
|
78
|
+
for (const service of context.services) {
|
|
79
|
+
if (service.id === ownerId) continue;
|
|
80
|
+
if ((service.stores ?? []).includes(storeId)) out.push(service);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* How many columns a store holds, for the header line above an ER canvas.
|
|
88
|
+
* A view's columns are counted apart: they are not storage, and adding them to
|
|
89
|
+
* the total would inflate "how big is this schema" with rows that do not exist.
|
|
90
|
+
*/
|
|
91
|
+
export function storeColumnCount(store: Store): number {
|
|
92
|
+
return store.tables.reduce((n, t) => n + t.columns.length, 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** How many views a store declares. */
|
|
96
|
+
export function storeViewCount(store: Store): number {
|
|
97
|
+
return storeViews(store).length;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The views presenting an aggregate, alongside the tables that hold it. A
|
|
102
|
+
* report a service reads its own aggregate through belongs on that aggregate's
|
|
103
|
+
* page: it is one of the ways the model is actually queried.
|
|
104
|
+
*/
|
|
105
|
+
export interface PresentedView {
|
|
106
|
+
view: View;
|
|
107
|
+
store: Store;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function viewsPresenting(
|
|
111
|
+
index: CatalogIndex,
|
|
112
|
+
aggregateId: string,
|
|
113
|
+
): PresentedView[] {
|
|
114
|
+
const out: PresentedView[] = [];
|
|
115
|
+
for (const view of index.viewsByAggregate.get(aggregateId) ?? []) {
|
|
116
|
+
const held = index.viewById.get(view.id);
|
|
117
|
+
if (held) out.push({ view, store: held.store });
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Aggregates and blocks
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
export interface PersistedTable {
|
|
127
|
+
table: Table;
|
|
128
|
+
store: Store;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The tables that hold an aggregate — those naming it in `persists`, plus the
|
|
133
|
+
* ones naming one of its blocks. A child table pointing at a value object of
|
|
134
|
+
* the aggregate is still where that aggregate lives.
|
|
135
|
+
*/
|
|
136
|
+
export function tablesPersisting(
|
|
137
|
+
index: CatalogIndex,
|
|
138
|
+
aggregateId: string,
|
|
139
|
+
): PersistedTable[] {
|
|
140
|
+
const out: PersistedTable[] = [];
|
|
141
|
+
const seen = new Set<string>();
|
|
142
|
+
for (const table of index.tablesByAggregate.get(aggregateId) ?? []) {
|
|
143
|
+
const held = index.tableById.get(table.id);
|
|
144
|
+
if (!held || seen.has(table.id)) continue;
|
|
145
|
+
seen.add(table.id);
|
|
146
|
+
out.push({ table, store: held.store });
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Columns whose `maps` lands in a block, in table then column order. */
|
|
152
|
+
export function columnsOfBlock(
|
|
153
|
+
index: CatalogIndex,
|
|
154
|
+
blockId: string,
|
|
155
|
+
): ColumnOwner[] {
|
|
156
|
+
return index.columnsByBlock.get(blockId) ?? [];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The outbox a service publishes through, if it has one. An event's delivery
|
|
161
|
+
* is a fact about the service that announces it, not about the event, so this
|
|
162
|
+
* is asked once per publisher rather than once per event.
|
|
163
|
+
*/
|
|
164
|
+
export function outboxOfService(
|
|
165
|
+
index: CatalogIndex,
|
|
166
|
+
serviceId: string,
|
|
167
|
+
): PersistedTable | null {
|
|
168
|
+
for (const store of index.storesOwnedBy.get(serviceId) ?? []) {
|
|
169
|
+
for (const table of store.tables) {
|
|
170
|
+
if (table.role === "outbox") return { table, store };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** A table's payload column: the first `jsonb`/`json` it holds, if any. */
|
|
177
|
+
export function payloadColumn(table: Table): Column | null {
|
|
178
|
+
return table.columns.find((c) => dbClass(c.type) === "json") ?? null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Types. Two vocabularies, one comparison.
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The shape a type has, once the spelling is set aside. Widths are kept apart
|
|
187
|
+
* on purpose: `int32` in the model against `bigint` in the column is exactly
|
|
188
|
+
* the kind of quiet disagreement this whole comparison exists to surface.
|
|
189
|
+
*
|
|
190
|
+
* `int` is the one width that is no width: Go's `int` is whatever the platform
|
|
191
|
+
* says, Java's is what the schema author was not thinking about. Either agrees
|
|
192
|
+
* with any integer column - a `bigint` under a Go `int` is a choice, not a bug.
|
|
193
|
+
*/
|
|
194
|
+
export type TypeClass =
|
|
195
|
+
| "uuid"
|
|
196
|
+
| "text"
|
|
197
|
+
| "int"
|
|
198
|
+
| "int16"
|
|
199
|
+
| "int32"
|
|
200
|
+
| "int64"
|
|
201
|
+
| "float"
|
|
202
|
+
| "decimal"
|
|
203
|
+
| "bool"
|
|
204
|
+
| "time"
|
|
205
|
+
| "json"
|
|
206
|
+
| "bytes"
|
|
207
|
+
| "unknown";
|
|
208
|
+
|
|
209
|
+
/** Strips `[]`, pointers, parameters: "varchar(64)" and "*string" are still one type. */
|
|
210
|
+
function bare(type: string): string {
|
|
211
|
+
return type
|
|
212
|
+
.trim()
|
|
213
|
+
.replace(/^\*+/, "")
|
|
214
|
+
.replace(/^\[\]/, "")
|
|
215
|
+
.replace(/\(.*\)$/, "")
|
|
216
|
+
.replace(/\s+/g, " ")
|
|
217
|
+
.toLowerCase();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const DB_CLASSES: Record<string, TypeClass> = {
|
|
221
|
+
uuid: "uuid",
|
|
222
|
+
text: "text",
|
|
223
|
+
varchar: "text",
|
|
224
|
+
"character varying": "text",
|
|
225
|
+
character: "text",
|
|
226
|
+
char: "text",
|
|
227
|
+
citext: "text",
|
|
228
|
+
smallint: "int16",
|
|
229
|
+
int2: "int16",
|
|
230
|
+
smallserial: "int16",
|
|
231
|
+
integer: "int32",
|
|
232
|
+
int: "int32",
|
|
233
|
+
int4: "int32",
|
|
234
|
+
serial: "int32",
|
|
235
|
+
bigint: "int64",
|
|
236
|
+
int8: "int64",
|
|
237
|
+
bigserial: "int64",
|
|
238
|
+
numeric: "decimal",
|
|
239
|
+
decimal: "decimal",
|
|
240
|
+
money: "decimal",
|
|
241
|
+
real: "float",
|
|
242
|
+
float4: "float",
|
|
243
|
+
"double precision": "float",
|
|
244
|
+
float8: "float",
|
|
245
|
+
boolean: "bool",
|
|
246
|
+
bool: "bool",
|
|
247
|
+
timestamptz: "time",
|
|
248
|
+
"timestamp with time zone": "time",
|
|
249
|
+
timestamp: "time",
|
|
250
|
+
"timestamp without time zone": "time",
|
|
251
|
+
date: "time",
|
|
252
|
+
time: "time",
|
|
253
|
+
timetz: "time",
|
|
254
|
+
jsonb: "json",
|
|
255
|
+
json: "json",
|
|
256
|
+
bytea: "bytes",
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const DOMAIN_CLASSES: Record<string, TypeClass> = {
|
|
260
|
+
string: "text",
|
|
261
|
+
"uuid.uuid": "uuid",
|
|
262
|
+
uuid: "uuid",
|
|
263
|
+
int16: "int16",
|
|
264
|
+
int32: "int32",
|
|
265
|
+
rune: "int32",
|
|
266
|
+
int64: "int64",
|
|
267
|
+
int: "int",
|
|
268
|
+
uint64: "int64",
|
|
269
|
+
uint32: "int32",
|
|
270
|
+
float32: "float",
|
|
271
|
+
float64: "float",
|
|
272
|
+
bool: "bool",
|
|
273
|
+
"time.time": "time",
|
|
274
|
+
"time.duration": "int64",
|
|
275
|
+
"decimal.decimal": "decimal",
|
|
276
|
+
"json.rawmessage": "json",
|
|
277
|
+
byte: "bytes",
|
|
278
|
+
// Java. `bare` lowercases, so `Integer` and `Long` land here too.
|
|
279
|
+
long: "int64",
|
|
280
|
+
integer: "int32",
|
|
281
|
+
short: "int16",
|
|
282
|
+
double: "float",
|
|
283
|
+
boolean: "bool",
|
|
284
|
+
bigdecimal: "decimal",
|
|
285
|
+
instant: "time",
|
|
286
|
+
offsetdatetime: "time",
|
|
287
|
+
localdatetime: "time",
|
|
288
|
+
localdate: "time",
|
|
289
|
+
// TypeScript. `string`, `boolean` above; `number` is deliberately unknown.
|
|
290
|
+
date: "time",
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const INTEGER_CLASSES: ReadonlySet<TypeClass> = new Set([
|
|
294
|
+
"int",
|
|
295
|
+
"int16",
|
|
296
|
+
"int32",
|
|
297
|
+
"int64",
|
|
298
|
+
]);
|
|
299
|
+
|
|
300
|
+
export function dbClass(type: string): TypeClass {
|
|
301
|
+
return DB_CLASSES[bare(type)] ?? "unknown";
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function domainClass(type: string): TypeClass {
|
|
305
|
+
return DOMAIN_CLASSES[bare(type)] ?? "unknown";
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* True when a column and the field it maps to disagree about what they hold.
|
|
310
|
+
*
|
|
311
|
+
* A type either side does not recognise is NOT a mismatch. Half the domain
|
|
312
|
+
* types in a catalog are named shapes — Money, GatewayRef — and calling every
|
|
313
|
+
* one of them a disagreement with `numeric` would bury the two or three real
|
|
314
|
+
* ones under a page of amber dots.
|
|
315
|
+
*/
|
|
316
|
+
export function typesDisagree(dbType: string, domainType: string): boolean {
|
|
317
|
+
const a = dbClass(dbType);
|
|
318
|
+
const b = domainClass(domainType);
|
|
319
|
+
if (a === "unknown" || b === "unknown") return false;
|
|
320
|
+
if (a === b) return false;
|
|
321
|
+
// A width-less integer fits any integer column.
|
|
322
|
+
return !(b === "int" && INTEGER_CLASSES.has(a));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// The join: a column, the field it maps to, and whether they agree.
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
export interface StoredField {
|
|
330
|
+
owner: ColumnOwner;
|
|
331
|
+
/** The `maps` path as written, e.g. "Order.CustomerID". */
|
|
332
|
+
maps: string;
|
|
333
|
+
/** The field half of that path. */
|
|
334
|
+
path: string;
|
|
335
|
+
/** The domain field, when the block actually declares one by that name. */
|
|
336
|
+
field: Field | null;
|
|
337
|
+
/** Set when both types are recognised and they are not the same shape. */
|
|
338
|
+
mismatch: boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Every column stored against a block, joined to the field it claims to carry.
|
|
343
|
+
* A `maps` naming a field the block does not declare resolves to `field: null`
|
|
344
|
+
* — the column is still shown, because a column pointing at a field that is no
|
|
345
|
+
* longer there is precisely what a reader needs to see.
|
|
346
|
+
*/
|
|
347
|
+
export function storedFields(
|
|
348
|
+
catalog: Catalog,
|
|
349
|
+
index: CatalogIndex,
|
|
350
|
+
blockId: string,
|
|
351
|
+
): StoredField[] {
|
|
352
|
+
const owner = index.blockById.get(blockId);
|
|
353
|
+
if (!owner) return [];
|
|
354
|
+
const fields = blockFields(catalog, owner.block);
|
|
355
|
+
|
|
356
|
+
return columnsOfBlock(index, blockId).map((columnOwner) => {
|
|
357
|
+
const maps = columnOwner.column.maps ?? "";
|
|
358
|
+
const path = mapsFieldPath(maps);
|
|
359
|
+
// One level only: "Total.Amount" is matched on "Total", the field the
|
|
360
|
+
// block itself declares, and the nested shape is read on its own page.
|
|
361
|
+
const head = path.split(".")[0] ?? "";
|
|
362
|
+
const field = fields.find((f) => f.name === head) ?? null;
|
|
363
|
+
return {
|
|
364
|
+
owner: columnOwner,
|
|
365
|
+
maps,
|
|
366
|
+
path,
|
|
367
|
+
field,
|
|
368
|
+
mismatch: field
|
|
369
|
+
? typesDisagree(columnOwner.column.type, field.type)
|
|
370
|
+
: false,
|
|
371
|
+
};
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Fields of an aggregate's blocks that no column maps to. This is the evidence
|
|
377
|
+
* behind "persistence drift": a table claiming to persist an aggregate whose
|
|
378
|
+
* fields it does not carry is either mid-migration or mis-labelled, and either
|
|
379
|
+
* way the claim is worth less than it looks.
|
|
380
|
+
*/
|
|
381
|
+
export function unmappedFields(
|
|
382
|
+
catalog: Catalog,
|
|
383
|
+
index: CatalogIndex,
|
|
384
|
+
aggregate: Aggregate,
|
|
385
|
+
): number {
|
|
386
|
+
let total = 0;
|
|
387
|
+
let mapped = 0;
|
|
388
|
+
for (const { block } of aggregateBlocks(aggregate)) {
|
|
389
|
+
const fields = blockFields(catalog, block);
|
|
390
|
+
total += fields.length;
|
|
391
|
+
const paths = new Set(
|
|
392
|
+
columnsOfBlock(index, block.id).map(
|
|
393
|
+
(o) => mapsFieldPath(o.column.maps ?? "").split(".")[0] ?? "",
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
mapped += fields.filter((f) => paths.has(f.name)).length;
|
|
397
|
+
}
|
|
398
|
+
return total - mapped;
|
|
399
|
+
}
|