@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,139 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
PIN_CAP,
|
|
4
|
+
addPin,
|
|
5
|
+
isPinned,
|
|
6
|
+
movePin,
|
|
7
|
+
parsePins,
|
|
8
|
+
removePin,
|
|
9
|
+
serializePins,
|
|
10
|
+
} from "./pins";
|
|
11
|
+
import type { Pin } from "./pins";
|
|
12
|
+
|
|
13
|
+
const pin = (n: number): Pin => ({ kind: "event", id: `e${n}` });
|
|
14
|
+
const many = (n: number): Pin[] => Array.from({ length: n }, (_, i) => pin(i));
|
|
15
|
+
|
|
16
|
+
describe("what can be pinned", () => {
|
|
17
|
+
it("keeps a context and a store, which have pages of their own", () => {
|
|
18
|
+
const raw = JSON.stringify([
|
|
19
|
+
{ kind: "context", id: "shop" },
|
|
20
|
+
{ kind: "store", id: "shop.oms.pg" },
|
|
21
|
+
{ kind: "column", id: "shop.oms.pg.orders.id" },
|
|
22
|
+
]);
|
|
23
|
+
expect(parsePins(raw)).toEqual([
|
|
24
|
+
{ kind: "context", id: "shop" },
|
|
25
|
+
{ kind: "store", id: "shop.oms.pg" },
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("addPin", () => {
|
|
31
|
+
it("appends, so the newest pin is the one at the bottom", () => {
|
|
32
|
+
const { pins } = addPin([pin(0)], pin(1));
|
|
33
|
+
expect(pins.map((p) => p.id)).toEqual(["e0", "e1"]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("pins the same thing once, whatever a second click means", () => {
|
|
37
|
+
const start = [pin(0)];
|
|
38
|
+
expect(addPin(start, pin(0)).pins).toBe(start);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("tells the two kinds of the same id apart", () => {
|
|
42
|
+
const { pins } = addPin(
|
|
43
|
+
[{ kind: "event", id: "x" }],
|
|
44
|
+
{ kind: "table", id: "x" },
|
|
45
|
+
);
|
|
46
|
+
expect(pins).toHaveLength(2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("drops the oldest at the cap rather than refusing the new one", () => {
|
|
50
|
+
const full = many(PIN_CAP);
|
|
51
|
+
const { pins, evicted } = addPin(full, pin(99));
|
|
52
|
+
expect(pins).toHaveLength(PIN_CAP);
|
|
53
|
+
expect(evicted).toEqual(pin(0));
|
|
54
|
+
expect(pins[0]).toEqual(pin(1));
|
|
55
|
+
expect(pins[PIN_CAP - 1]).toEqual(pin(99));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("evicts nothing while there is still room", () => {
|
|
59
|
+
expect(addPin(many(PIN_CAP - 1), pin(99)).evicted).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("removePin", () => {
|
|
64
|
+
it("takes one out and leaves the order of the rest alone", () => {
|
|
65
|
+
expect(removePin(many(3), pin(1)).map((p) => p.id)).toEqual(["e0", "e2"]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("movePin", () => {
|
|
70
|
+
it("moves a pin down, closing the gap behind it", () => {
|
|
71
|
+
expect(movePin(many(4), 0, 2).map((p) => p.id)).toEqual([
|
|
72
|
+
"e1",
|
|
73
|
+
"e2",
|
|
74
|
+
"e0",
|
|
75
|
+
"e3",
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("moves a pin up", () => {
|
|
80
|
+
expect(movePin(many(4), 3, 1).map((p) => p.id)).toEqual([
|
|
81
|
+
"e0",
|
|
82
|
+
"e3",
|
|
83
|
+
"e1",
|
|
84
|
+
"e2",
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("leaves the list untouched for a drop that goes nowhere", () => {
|
|
89
|
+
const start = many(3);
|
|
90
|
+
expect(movePin(start, 1, 1)).toBe(start);
|
|
91
|
+
expect(movePin(start, -1, 0)).toBe(start);
|
|
92
|
+
expect(movePin(start, 0, 9)).toBe(start);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("parsePins", () => {
|
|
97
|
+
it("round-trips what it wrote", () => {
|
|
98
|
+
const pins = many(3);
|
|
99
|
+
expect(parsePins(serializePins(pins))).toEqual(pins);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("treats anything that is not a pin list as no pins at all", () => {
|
|
103
|
+
expect(parsePins(null)).toEqual([]);
|
|
104
|
+
expect(parsePins("")).toEqual([]);
|
|
105
|
+
expect(parsePins("{not json")).toEqual([]);
|
|
106
|
+
expect(parsePins('{"kind":"event"}')).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("drops entries an older build wrote and this one cannot draw", () => {
|
|
110
|
+
const raw = JSON.stringify([
|
|
111
|
+
{ kind: "event", id: "e0" },
|
|
112
|
+
{ kind: "column", id: "c1" }, // a kind that is no longer pinnable
|
|
113
|
+
{ kind: "event" }, // no id
|
|
114
|
+
{ kind: "event", id: "" }, // empty id
|
|
115
|
+
null,
|
|
116
|
+
"flow.checkout",
|
|
117
|
+
{ kind: "adr", id: "shop.oms.0007" },
|
|
118
|
+
]);
|
|
119
|
+
expect(parsePins(raw)).toEqual([
|
|
120
|
+
{ kind: "event", id: "e0" },
|
|
121
|
+
{ kind: "adr", id: "shop.oms.0007" },
|
|
122
|
+
]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("de-duplicates and holds the cap, whatever storage says", () => {
|
|
126
|
+
const raw = JSON.stringify([...many(PIN_CAP + 4), pin(0)]);
|
|
127
|
+
const parsed = parsePins(raw);
|
|
128
|
+
expect(parsed).toHaveLength(PIN_CAP);
|
|
129
|
+
expect(new Set(parsed.map((p) => `${p.kind}:${p.id}`)).size).toBe(PIN_CAP);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("isPinned", () => {
|
|
134
|
+
it("matches on kind and id together", () => {
|
|
135
|
+
const pins: Pin[] = [{ kind: "flow", id: "flow.checkout" }];
|
|
136
|
+
expect(isPinned(pins, { kind: "flow", id: "flow.checkout" })).toBe(true);
|
|
137
|
+
expect(isPinned(pins, { kind: "adr", id: "flow.checkout" })).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
});
|
package/src/lib/pins.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// What the reader keeps at the top of the tree.
|
|
2
|
+
//
|
|
3
|
+
// A pin is a reader's own answer to "where do I keep coming back to", so it is
|
|
4
|
+
// stored as nothing but a kind and a catalog id: the name, the icon and the
|
|
5
|
+
// route are all read back out of the catalog at render time. A pin taken
|
|
6
|
+
// against a build that no longer has that event therefore goes quiet rather
|
|
7
|
+
// than pointing at a page that is not there.
|
|
8
|
+
//
|
|
9
|
+
// Order is the reader's, not the catalog's - pins are dragged into the order
|
|
10
|
+
// they think in - so the list is a sequence and never re-sorted.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* What can be pinned. Not every kind: a column, a shared type or a value
|
|
14
|
+
* object is read inside the thing that holds it, and a pin that lands on a
|
|
15
|
+
* panel three levels deep is a bookmark to a scroll position.
|
|
16
|
+
*/
|
|
17
|
+
export type PinKind =
|
|
18
|
+
/** A context is a page and the top of a tree; keeping it at the top is a way in. */
|
|
19
|
+
| "context"
|
|
20
|
+
| "flow"
|
|
21
|
+
| "event"
|
|
22
|
+
| "adr"
|
|
23
|
+
| "service"
|
|
24
|
+
| "aggregate"
|
|
25
|
+
| "table"
|
|
26
|
+
/** A store has a page of its own, so it can be kept at the top like one. */
|
|
27
|
+
| "store"
|
|
28
|
+
/** A module has a page of its own, so it can be kept at the top like one. */
|
|
29
|
+
| "module";
|
|
30
|
+
|
|
31
|
+
const PIN_KINDS: readonly PinKind[] = [
|
|
32
|
+
"context",
|
|
33
|
+
"flow",
|
|
34
|
+
"event",
|
|
35
|
+
"adr",
|
|
36
|
+
"service",
|
|
37
|
+
"aggregate",
|
|
38
|
+
"table",
|
|
39
|
+
"store",
|
|
40
|
+
"module",
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
function isPinKind(value: string): value is PinKind {
|
|
44
|
+
return (PIN_KINDS as readonly string[]).includes(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Pin {
|
|
48
|
+
kind: PinKind;
|
|
49
|
+
/** The catalog id, spelled the way the catalog spells it. */
|
|
50
|
+
id: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* How many pins a reader may keep. Twelve is the last count that still reads
|
|
55
|
+
* as a shortlist above the tree rather than as a second tree; past it the
|
|
56
|
+
* pinned block would push FLOWS off the first screen, which is exactly the
|
|
57
|
+
* harm pinning was meant to undo.
|
|
58
|
+
*/
|
|
59
|
+
export const PIN_CAP = 12;
|
|
60
|
+
|
|
61
|
+
function samePin(a: Pin, b: Pin): boolean {
|
|
62
|
+
return a.kind === b.kind && a.id === b.id;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isPinned(pins: Pin[], pin: Pin): boolean {
|
|
66
|
+
return pins.some((p) => samePin(p, pin));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pin something, at the end of the list. At the cap the OLDEST pin goes, not
|
|
71
|
+
* the new one: a reader who pins a thirteenth thing has said what they want on
|
|
72
|
+
* screen, and refusing them would make the cap look like a bug. `evicted` is
|
|
73
|
+
* what they lost, so the caller can say so out loud.
|
|
74
|
+
*/
|
|
75
|
+
export function addPin(
|
|
76
|
+
pins: Pin[],
|
|
77
|
+
pin: Pin,
|
|
78
|
+
): { pins: Pin[]; evicted: Pin | null } {
|
|
79
|
+
if (isPinned(pins, pin)) return { pins, evicted: null };
|
|
80
|
+
const next = [...pins, pin];
|
|
81
|
+
if (next.length <= PIN_CAP) return { pins: next, evicted: null };
|
|
82
|
+
const evicted = next[0] ?? null;
|
|
83
|
+
return { pins: next.slice(next.length - PIN_CAP), evicted };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function removePin(pins: Pin[], pin: Pin): Pin[] {
|
|
87
|
+
return pins.filter((p) => !samePin(p, pin));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Move the pin at `from` so that it sits at `to`, closing the gap behind it. */
|
|
91
|
+
export function movePin(pins: Pin[], from: number, to: number): Pin[] {
|
|
92
|
+
if (from === to) return pins;
|
|
93
|
+
if (from < 0 || from >= pins.length) return pins;
|
|
94
|
+
if (to < 0 || to >= pins.length) return pins;
|
|
95
|
+
const next = [...pins];
|
|
96
|
+
const [moved] = next.splice(from, 1);
|
|
97
|
+
if (!moved) return pins;
|
|
98
|
+
next.splice(to, 0, moved);
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whatever is in storage, read as a pin list. Anything that is not a pin is
|
|
104
|
+
* dropped rather than repaired: what comes back was written by an older build
|
|
105
|
+
* or by hand, and a half-understood entry is worse than one that is gone.
|
|
106
|
+
*/
|
|
107
|
+
export function parsePins(raw: string | null): Pin[] {
|
|
108
|
+
if (!raw) return [];
|
|
109
|
+
let parsed: unknown;
|
|
110
|
+
try {
|
|
111
|
+
parsed = JSON.parse(raw);
|
|
112
|
+
} catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(parsed)) return [];
|
|
116
|
+
const out: Pin[] = [];
|
|
117
|
+
for (const entry of parsed) {
|
|
118
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
119
|
+
const { kind, id } = entry as { kind?: unknown; id?: unknown };
|
|
120
|
+
if (typeof kind !== "string" || typeof id !== "string") continue;
|
|
121
|
+
if (!isPinKind(kind) || id === "") continue;
|
|
122
|
+
const pin = { kind, id };
|
|
123
|
+
if (!isPinned(out, pin)) out.push(pin);
|
|
124
|
+
}
|
|
125
|
+
return out.slice(0, PIN_CAP);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function serializePins(pins: Pin[]): string {
|
|
129
|
+
return JSON.stringify(pins);
|
|
130
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { catalog } from "../data";
|
|
3
|
+
import { contextStats, edgeCount, problems } from "./derive";
|
|
4
|
+
import type { Catalog } from "../catalog";
|
|
5
|
+
|
|
6
|
+
describe("problems", () => {
|
|
7
|
+
it("finds every unresolved edge and nothing else", () => {
|
|
8
|
+
for (const problem of problems(catalog)) {
|
|
9
|
+
const context = catalog.contexts.find((c) => c.id === problem.context);
|
|
10
|
+
const service = context?.services.find((s) => s.id === problem.service);
|
|
11
|
+
expect(
|
|
12
|
+
service,
|
|
13
|
+
`${problem.id} names a service not in the catalog`,
|
|
14
|
+
).toBeDefined();
|
|
15
|
+
|
|
16
|
+
if (problem.kind === "rpc") {
|
|
17
|
+
const call = service?.consumes.find((c) => c.id === problem.id);
|
|
18
|
+
expect(call?.status).toBe("unresolved");
|
|
19
|
+
expect(call?.peer).toBe(problem.peer);
|
|
20
|
+
} else {
|
|
21
|
+
const event = service?.aggregates
|
|
22
|
+
.flatMap((a) => a.events)
|
|
23
|
+
.find((e) => e.id === problem.id);
|
|
24
|
+
const consumer = event?.consumers.find(
|
|
25
|
+
(c) => c.service === problem.peer,
|
|
26
|
+
);
|
|
27
|
+
expect(consumer?.status).toBe("unresolved");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("agrees with the per-context counts the overview prints", () => {
|
|
33
|
+
// The overview card and the problems page must never disagree about how
|
|
34
|
+
// much is broken - they are two renderings of one number.
|
|
35
|
+
const found = problems(catalog);
|
|
36
|
+
for (const context of catalog.contexts) {
|
|
37
|
+
const mine = found.filter((p) => p.context === context.id);
|
|
38
|
+
expect(mine.length, `context ${context.id}`).toBe(
|
|
39
|
+
contextStats(context).unresolved,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("says nothing at all about a clean catalog", () => {
|
|
45
|
+
const clean: Catalog = {
|
|
46
|
+
...catalog,
|
|
47
|
+
contexts: catalog.contexts.map((context) => ({
|
|
48
|
+
...context,
|
|
49
|
+
services: context.services.map((service) => ({
|
|
50
|
+
...service,
|
|
51
|
+
consumes: service.consumes.map((c) => ({
|
|
52
|
+
...c,
|
|
53
|
+
status: "verified" as const,
|
|
54
|
+
})),
|
|
55
|
+
aggregates: service.aggregates.map((aggregate) => ({
|
|
56
|
+
...aggregate,
|
|
57
|
+
events: aggregate.events.map((event) => ({
|
|
58
|
+
...event,
|
|
59
|
+
consumers: event.consumers.map((c) => ({
|
|
60
|
+
...c,
|
|
61
|
+
status: "verified" as const,
|
|
62
|
+
})),
|
|
63
|
+
})),
|
|
64
|
+
})),
|
|
65
|
+
})),
|
|
66
|
+
})),
|
|
67
|
+
};
|
|
68
|
+
expect(problems(clean)).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("edgeCount", () => {
|
|
73
|
+
it("counts every call and consumer, resolved or not", () => {
|
|
74
|
+
const byHand = catalog.contexts
|
|
75
|
+
.flatMap((c) => c.services)
|
|
76
|
+
.reduce(
|
|
77
|
+
(n, s) =>
|
|
78
|
+
n +
|
|
79
|
+
s.consumes.length +
|
|
80
|
+
s.aggregates.reduce(
|
|
81
|
+
(m, a) =>
|
|
82
|
+
m + a.events.reduce((k, e) => k + e.consumers.length, 0),
|
|
83
|
+
0,
|
|
84
|
+
),
|
|
85
|
+
0,
|
|
86
|
+
);
|
|
87
|
+
expect(edgeCount(catalog)).toBe(byHand);
|
|
88
|
+
expect(edgeCount(catalog)).toBeGreaterThanOrEqual(problems(catalog).length);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("is zero for a catalog whose services are not wired together", () => {
|
|
92
|
+
const bare: Catalog = {
|
|
93
|
+
...catalog,
|
|
94
|
+
contexts: catalog.contexts.map((context) => ({
|
|
95
|
+
...context,
|
|
96
|
+
services: context.services.map((service) => ({
|
|
97
|
+
...service,
|
|
98
|
+
consumes: [],
|
|
99
|
+
aggregates: service.aggregates.map((aggregate) => ({
|
|
100
|
+
...aggregate,
|
|
101
|
+
events: aggregate.events.map((event) => ({
|
|
102
|
+
...event,
|
|
103
|
+
consumers: [],
|
|
104
|
+
})),
|
|
105
|
+
})),
|
|
106
|
+
})),
|
|
107
|
+
})),
|
|
108
|
+
};
|
|
109
|
+
expect(edgeCount(bare)).toBe(0);
|
|
110
|
+
expect(problems(bare)).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildIndex, type Catalog, type Service } from "../catalog";
|
|
3
|
+
import { protoProblems } from "./proto-problems";
|
|
4
|
+
|
|
5
|
+
function service(id: string, overrides: Partial<Service> = {}): Service {
|
|
6
|
+
const slug = id.slice(id.indexOf(".") + 1);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
id,
|
|
10
|
+
slug,
|
|
11
|
+
name: slug,
|
|
12
|
+
repo: "",
|
|
13
|
+
path: "",
|
|
14
|
+
readme: "",
|
|
15
|
+
provides: [],
|
|
16
|
+
consumes: [],
|
|
17
|
+
aggregates: [],
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function catalogWith(services: Service[]): Catalog {
|
|
23
|
+
return {
|
|
24
|
+
generatedAt: "2024-01-01T00:00:00Z",
|
|
25
|
+
commit: "abc1234",
|
|
26
|
+
contexts: [
|
|
27
|
+
{
|
|
28
|
+
id: "shop",
|
|
29
|
+
slug: "shop",
|
|
30
|
+
name: "Shop",
|
|
31
|
+
summary: "",
|
|
32
|
+
services,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
defs: {},
|
|
36
|
+
flows: [],
|
|
37
|
+
adrs: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const pricing = () =>
|
|
42
|
+
service("shop.pricing", {
|
|
43
|
+
provides: [
|
|
44
|
+
{
|
|
45
|
+
id: "pricing.v1.Pricing",
|
|
46
|
+
methods: [{ name: "GetQuote" }],
|
|
47
|
+
source: "proto/pricing/v1/pricing.proto:7",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const calling = (id: string, status: "declared" | "unresolved" = "declared") =>
|
|
53
|
+
service("shop.oms", {
|
|
54
|
+
consumes: [
|
|
55
|
+
{
|
|
56
|
+
id,
|
|
57
|
+
peer: "shop.pricing",
|
|
58
|
+
status,
|
|
59
|
+
source: "internal/infrastructure/pricing/pricing.proto:10",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
function found(catalog: Catalog) {
|
|
65
|
+
return protoProblems(catalog, buildIndex(catalog));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("protoProblems", () => {
|
|
69
|
+
it("reports a call whose provider answers on no such method", () => {
|
|
70
|
+
const problems = found(
|
|
71
|
+
catalogWith([pricing(), calling("pricing.v1.Pricing/ListPriceLists")]),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(problems).toHaveLength(1);
|
|
75
|
+
expect(problems[0]?.kind).toBe("proto-missing");
|
|
76
|
+
expect(problems[0]?.severity).toBe("error");
|
|
77
|
+
// The near end is the CALLER: that is the copy to go and look at.
|
|
78
|
+
expect(problems[0]?.service).toBe("shop.oms");
|
|
79
|
+
expect(problems[0]?.id).toBe("pricing.v1.Pricing/ListPriceLists");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("says nothing about a call the provider does declare", () => {
|
|
83
|
+
expect(
|
|
84
|
+
found(catalogWith([pricing(), calling("pricing.v1.Pricing/GetQuote")])),
|
|
85
|
+
).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// An unresolved call already has a problem of its own from problems(), and
|
|
89
|
+
// one edge under two headings makes the page look worse than the estate is.
|
|
90
|
+
it("leaves an unresolved call to the report that already covers it", () => {
|
|
91
|
+
expect(
|
|
92
|
+
found(
|
|
93
|
+
catalogWith([
|
|
94
|
+
pricing(),
|
|
95
|
+
calling("pricing.v1.Pricing/ListPriceLists", "unresolved"),
|
|
96
|
+
]),
|
|
97
|
+
),
|
|
98
|
+
).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// A call to something outside the estate is a fact, not a defect - and it is
|
|
102
|
+
// the `rpc` problem's business, not this one's.
|
|
103
|
+
it("says nothing when the peer is not in the catalog at all", () => {
|
|
104
|
+
const outside = service("shop.oms", {
|
|
105
|
+
consumes: [
|
|
106
|
+
{
|
|
107
|
+
id: "psp.v2.Charges/Create",
|
|
108
|
+
peer: "psp",
|
|
109
|
+
status: "declared",
|
|
110
|
+
source: "internal/infrastructure/psp/psp.proto:9",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(found(catalogWith([outside]))).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("finds nothing in a catalog where nobody calls anybody", () => {
|
|
119
|
+
expect(found(catalogWith([pricing()]))).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Where a call and the interface it names disagree.
|
|
2
|
+
//
|
|
3
|
+
// docs/adr/org.0001.md promises this: producers publish their schema, consumers
|
|
4
|
+
// keep a narrowed copy, and "a field, method or enum value that differs between
|
|
5
|
+
// the two is reported against the consuming service". This is the first half of
|
|
6
|
+
// that promise - a call naming a method no provider declares.
|
|
7
|
+
//
|
|
8
|
+
// It is deliberately NOT done in the merge. src/merge.ts is documented as
|
|
9
|
+
// union-by-id plus first-non-empty and nothing else, and teaching it to compare
|
|
10
|
+
// shapes would put a semantic judgement in the one place that has none. The
|
|
11
|
+
// comparison belongs here, over the merged catalog, on the page where the rest
|
|
12
|
+
// of that judgement already lives.
|
|
13
|
+
//
|
|
14
|
+
// This is also NOT the same finding as derive.ts's `rpc` problem. That one is
|
|
15
|
+
// about the PEER: a call whose other end is nobody the catalog knows. This one
|
|
16
|
+
// is about a call whose peer is known and whose METHOD is not - the copy is
|
|
17
|
+
// stale, or the producer removed something, and the two are different repairs.
|
|
18
|
+
|
|
19
|
+
import type { Catalog, CatalogIndex } from "../catalog";
|
|
20
|
+
import type { Problem } from "./derive";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Every call that names a method no interface in the catalog declares.
|
|
24
|
+
*
|
|
25
|
+
* An unresolved call is skipped: it already has a problem of its own from
|
|
26
|
+
* `problems()`, and reporting the same edge twice under two headings would make
|
|
27
|
+
* the page look worse than the estate is.
|
|
28
|
+
*/
|
|
29
|
+
export function protoProblems(
|
|
30
|
+
catalog: Catalog,
|
|
31
|
+
index: CatalogIndex,
|
|
32
|
+
): Problem[] {
|
|
33
|
+
const out: Problem[] = [];
|
|
34
|
+
|
|
35
|
+
for (const context of catalog.contexts) {
|
|
36
|
+
for (const service of context.services) {
|
|
37
|
+
for (const call of service.consumes) {
|
|
38
|
+
if (call.status === "unresolved") continue;
|
|
39
|
+
|
|
40
|
+
// The peer has to be a service in the catalog before the method is
|
|
41
|
+
// worth asking about. A call to something outside the estate is a fact,
|
|
42
|
+
// not a defect.
|
|
43
|
+
if (!index.serviceById.has(call.peer)) continue;
|
|
44
|
+
|
|
45
|
+
if (index.rpcProviderByMethod.has(call.id)) continue;
|
|
46
|
+
|
|
47
|
+
out.push({
|
|
48
|
+
kind: "proto-missing",
|
|
49
|
+
// An error, not a staleness warning: the peer is in the catalog and
|
|
50
|
+
// does not answer on this. Either the copy is behind or the method is
|
|
51
|
+
// gone, and both are a call that will fail.
|
|
52
|
+
severity: "error",
|
|
53
|
+
context: context.id,
|
|
54
|
+
service: service.id,
|
|
55
|
+
id: call.id,
|
|
56
|
+
peer: call.peer,
|
|
57
|
+
note: call.note,
|
|
58
|
+
source: call.source,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { Catalog } from "../catalog";
|
|
3
|
+
import type { ForgeRef, ForgeRepo } from "./github-catalog";
|
|
4
|
+
import {
|
|
5
|
+
comparisonQuery,
|
|
6
|
+
comparisonSide,
|
|
7
|
+
forgeKeys,
|
|
8
|
+
forgeRefsQuery,
|
|
9
|
+
localStatusQuery,
|
|
10
|
+
sourceCodeQuery,
|
|
11
|
+
sourceKeys,
|
|
12
|
+
} from "./queries";
|
|
13
|
+
import type { RemoteSourceLocation, SourceLocation } from "./source-link";
|
|
14
|
+
|
|
15
|
+
const REPO: ForgeRepo = {
|
|
16
|
+
provider: "github",
|
|
17
|
+
owner: "acme",
|
|
18
|
+
repo: "portolan",
|
|
19
|
+
webUrl: "https://github.com/acme/portolan",
|
|
20
|
+
};
|
|
21
|
+
const OTHER: ForgeRepo = { ...REPO, repo: "shop", webUrl: "https://github.com/acme/shop" };
|
|
22
|
+
const SHA = "a".repeat(40);
|
|
23
|
+
const OTHER_SHA = "b".repeat(40);
|
|
24
|
+
const REFS: ForgeRef[] = [
|
|
25
|
+
{ name: "main", commit: SHA, protected: true, kind: "branch" },
|
|
26
|
+
{ name: "feature", commit: OTHER_SHA, protected: false, kind: "branch" },
|
|
27
|
+
];
|
|
28
|
+
const bundled = { name: "main", sha: SHA, catalog: { contexts: [] } as unknown as Catalog };
|
|
29
|
+
|
|
30
|
+
const remote: RemoteSourceLocation = {
|
|
31
|
+
kind: "remote",
|
|
32
|
+
provider: "github",
|
|
33
|
+
origin: "https://github.com",
|
|
34
|
+
repositoryUrl: "https://github.com/acme/shop",
|
|
35
|
+
ref: SHA,
|
|
36
|
+
path: "internal/cart/cart.go",
|
|
37
|
+
line: 3,
|
|
38
|
+
href: `https://github.com/acme/shop/blob/${SHA}/internal/cart/cart.go#L3`,
|
|
39
|
+
};
|
|
40
|
+
const local: SourceLocation = { kind: "local", path: "internal/cart/cart.go", line: 1, href: null };
|
|
41
|
+
|
|
42
|
+
describe("forgeRefsQuery", () => {
|
|
43
|
+
it("is disabled without a repository and keyed under the forge prefix", () => {
|
|
44
|
+
expect(forgeRefsQuery(null, "").enabled).toBe(false);
|
|
45
|
+
expect(forgeRefsQuery(REPO, "").enabled).toBe(true);
|
|
46
|
+
expect(forgeRefsQuery(REPO, "").queryKey.slice(0, forgeKeys.all.length)).toEqual(forgeKeys.all);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// A token makes the answer the token's: a different key, so a reader who
|
|
50
|
+
// connects never sees the anonymous listing, and one who disconnects never
|
|
51
|
+
// keeps the authenticated one.
|
|
52
|
+
it("keys by repository and by token", () => {
|
|
53
|
+
expect(forgeRefsQuery(REPO, "").queryKey).not.toEqual(forgeRefsQuery(OTHER, "").queryKey);
|
|
54
|
+
expect(forgeRefsQuery(REPO, "").queryKey).not.toEqual(forgeRefsQuery(REPO, "secret").queryKey);
|
|
55
|
+
expect(forgeRefsQuery(REPO, "secret").queryKey).toEqual(forgeRefsQuery(REPO, "secret").queryKey);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("comparisonSide", () => {
|
|
60
|
+
it("resolves the current branch to the bundled catalog without a lookup", () => {
|
|
61
|
+
expect(comparisonSide("main", [], bundled)).toEqual({ name: "main", sha: SHA, catalog: bundled.catalog });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("resolves other names through the forge refs, and unknown names to null", () => {
|
|
65
|
+
expect(comparisonSide("feature", REFS, bundled)).toEqual({ name: "feature", sha: OTHER_SHA, catalog: null });
|
|
66
|
+
expect(comparisonSide("gone", REFS, bundled)).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("comparisonQuery", () => {
|
|
71
|
+
const base = comparisonSide("main", REFS, bundled);
|
|
72
|
+
const head = comparisonSide("feature", REFS, bundled);
|
|
73
|
+
|
|
74
|
+
it("waits until both sides are resolved", () => {
|
|
75
|
+
expect(comparisonQuery(REPO, "", base, null).enabled).toBe(false);
|
|
76
|
+
expect(comparisonQuery(null, "", base, head).enabled).toBe(false);
|
|
77
|
+
expect(comparisonQuery(REPO, "", base, head).enabled).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Two names that point at the same commits are the same comparison; the
|
|
81
|
+
// key is the commits, so a renamed branch does not fetch twice.
|
|
82
|
+
it("keys by the resolved commits, not the names", () => {
|
|
83
|
+
const renamed = { ...head!, name: "feature-renamed" };
|
|
84
|
+
expect(comparisonQuery(REPO, "", base, head).queryKey).toEqual(comparisonQuery(REPO, "", base, renamed).queryKey);
|
|
85
|
+
expect(comparisonQuery(REPO, "", base, head).queryKey).not.toEqual(comparisonQuery(REPO, "", head, base).queryKey);
|
|
86
|
+
expect(comparisonQuery(REPO, "", base, head).queryKey.slice(0, forgeKeys.all.length)).toEqual(forgeKeys.all);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("sourceCodeQuery", () => {
|
|
91
|
+
it("keys remote files by forge, repository, commit, path and token", () => {
|
|
92
|
+
const key = sourceCodeQuery(remote, "").queryKey;
|
|
93
|
+
expect(key.slice(0, sourceKeys.all.length)).toEqual(sourceKeys.all);
|
|
94
|
+
expect(key).not.toEqual(sourceCodeQuery({ ...remote, ref: OTHER_SHA }, "").queryKey);
|
|
95
|
+
expect(key).not.toEqual(sourceCodeQuery({ ...remote, path: "go.mod" }, "").queryKey);
|
|
96
|
+
expect(key).not.toEqual(sourceCodeQuery(remote, "secret").queryKey);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("keys local files by path alone, under the same prefix", () => {
|
|
100
|
+
const key = sourceCodeQuery(local, "").queryKey;
|
|
101
|
+
expect(key.slice(0, sourceKeys.all.length)).toEqual(sourceKeys.all);
|
|
102
|
+
expect(key).toEqual(sourceCodeQuery(local, "ignored").queryKey);
|
|
103
|
+
expect(key).not.toEqual(sourceCodeQuery(remote, "").queryKey);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("localStatusQuery", () => {
|
|
108
|
+
// Settings is where a reader learns whether a local server is up, and the
|
|
109
|
+
// server may have been started since the last visit.
|
|
110
|
+
it("is stale at once so every visit asks again", () => {
|
|
111
|
+
expect(localStatusQuery().staleTime).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
});
|