@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,281 @@
|
|
|
1
|
+
// Where two services meet on one channel, and where a service's document and
|
|
2
|
+
// its code do not tell the same story about the bus.
|
|
3
|
+
//
|
|
4
|
+
// A channel belongs to the service that publishes on it, the way a store
|
|
5
|
+
// belongs to the service that writes it: a subscriber to `shop.cart.basket`
|
|
6
|
+
// is told, by the name, whose events it is reading and whose schema to vendor.
|
|
7
|
+
// A second publisher on the same channel breaks that promise silently - the
|
|
8
|
+
// subscriber dispatches on the event's name and never notices the message
|
|
9
|
+
// came from somewhere else, until one arrives that it cannot parse.
|
|
10
|
+
//
|
|
11
|
+
// Two sources say what a service puts on the bus, and they are read out of
|
|
12
|
+
// different files by different extractors. The domain says an aggregate raises
|
|
13
|
+
// an event and how it leaves, in `wire`. An AsyncAPI document says the service
|
|
14
|
+
// declares a channel and what travels on it, in `channels`. Neither is the
|
|
15
|
+
// truth on its own - a document is a promise and a wire is what the code does -
|
|
16
|
+
// and where they disagree is a fact worth a row, because one of them is stale.
|
|
17
|
+
//
|
|
18
|
+
// This is the messaging half of `shared-store`. It is done here, over the
|
|
19
|
+
// merged catalog, for the same reason the store rule is: each extractor sees
|
|
20
|
+
// one repository and cannot know what another declares.
|
|
21
|
+
|
|
22
|
+
import type { Catalog, CatalogIndex, Event, Service } from "../catalog";
|
|
23
|
+
import type { Problem } from "./derive";
|
|
24
|
+
|
|
25
|
+
interface Publisher {
|
|
26
|
+
service: Service;
|
|
27
|
+
context: string;
|
|
28
|
+
/** The events of this service whose wire names the channel. */
|
|
29
|
+
events: Event[];
|
|
30
|
+
/** Whether the service's own document declares it sends here. */
|
|
31
|
+
declared: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function wireProblems(
|
|
35
|
+
catalog: Catalog,
|
|
36
|
+
index: CatalogIndex,
|
|
37
|
+
): Problem[] {
|
|
38
|
+
return [
|
|
39
|
+
...sharedChannels(catalog),
|
|
40
|
+
...documentAgainstCode(catalog),
|
|
41
|
+
...unresolvedSubscriptions(catalog, index),
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Every service that publishes on a channel another service publishes on
|
|
47
|
+
* too: one row per service per such channel, pointing at the other side.
|
|
48
|
+
*
|
|
49
|
+
* Publishing is either of the two claims - an event whose wire names the
|
|
50
|
+
* channel, or a document declaring a send on it - because both are the service
|
|
51
|
+
* saying it owns the channel, and a document that says so while the code has
|
|
52
|
+
* not caught up is the same collision one release earlier.
|
|
53
|
+
*
|
|
54
|
+
* A service with several aggregates on one channel is not this: the channel
|
|
55
|
+
* is still one service's. An event without a wire is skipped, because a
|
|
56
|
+
* channel nobody named cannot be shared.
|
|
57
|
+
*/
|
|
58
|
+
function sharedChannels(catalog: Catalog): Problem[] {
|
|
59
|
+
const byChannel = new Map<string, Map<string, Publisher>>();
|
|
60
|
+
|
|
61
|
+
const publisher = (
|
|
62
|
+
channel: string,
|
|
63
|
+
service: Service,
|
|
64
|
+
context: string,
|
|
65
|
+
): Publisher => {
|
|
66
|
+
const publishers = byChannel.get(channel) ?? new Map<string, Publisher>();
|
|
67
|
+
byChannel.set(channel, publishers);
|
|
68
|
+
const mine = publishers.get(service.id) ?? {
|
|
69
|
+
service,
|
|
70
|
+
context,
|
|
71
|
+
events: [],
|
|
72
|
+
declared: false,
|
|
73
|
+
};
|
|
74
|
+
publishers.set(service.id, mine);
|
|
75
|
+
|
|
76
|
+
return mine;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const context of catalog.contexts) {
|
|
80
|
+
for (const service of context.services) {
|
|
81
|
+
for (const aggregate of service.aggregates) {
|
|
82
|
+
for (const event of aggregate.events) {
|
|
83
|
+
const channel = event.wire?.channel;
|
|
84
|
+
if (!channel) continue;
|
|
85
|
+
publisher(channel, service, context.id).events.push(event);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const channel of service.channels ?? []) {
|
|
90
|
+
if (channel.kind && channel.kind !== "event") continue;
|
|
91
|
+
if (!sends(channel.messages)) continue;
|
|
92
|
+
publisher(channel.address, service, context.id).declared = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const out: Problem[] = [];
|
|
98
|
+
for (const [channel, publishers] of byChannel) {
|
|
99
|
+
if (publishers.size < 2) continue;
|
|
100
|
+
for (const mine of publishers.values()) {
|
|
101
|
+
for (const theirs of publishers.values()) {
|
|
102
|
+
if (theirs === mine) continue;
|
|
103
|
+
const [first] = mine.events;
|
|
104
|
+
out.push({
|
|
105
|
+
kind: "shared-channel",
|
|
106
|
+
// An error, like a second writer in a database: a subscriber vendors
|
|
107
|
+
// one publisher's schema, and a message from the other is one it
|
|
108
|
+
// will fail on, not one it will merely misfile.
|
|
109
|
+
severity: "error",
|
|
110
|
+
context: mine.context,
|
|
111
|
+
service: mine.service.id,
|
|
112
|
+
// The near end is one event when this side has one, so the row leads
|
|
113
|
+
// to a page; a side known only from its document leads to the
|
|
114
|
+
// service, which is the only page there is.
|
|
115
|
+
id: first?.id ?? mine.service.id,
|
|
116
|
+
peer: theirs.service.id,
|
|
117
|
+
note: `${mine.service.id} publishes on ${channel}, and so does ${theirs.service.id}: ${claim(theirs, channel)}. A subscriber reads both without being told.`,
|
|
118
|
+
source: first?.versions[0]?.source ?? sourceOf(mine.service, channel),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** What a publisher is putting on a channel, as the catalog knows it. */
|
|
128
|
+
function claim(publisher: Publisher, channel: string): string {
|
|
129
|
+
if (publisher.events.length > 0) {
|
|
130
|
+
return publisher.events.map((e) => e.name).join(", ");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const declared = publisher.service.channels?.find(
|
|
134
|
+
(c) => c.address === channel,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
declared?.messages
|
|
139
|
+
.filter((m) => m.direction === "send")
|
|
140
|
+
.map((m) => m.name)
|
|
141
|
+
.join(", ") ?? "nothing the catalog can name"
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Where a service's document and its own code disagree about the bus.
|
|
147
|
+
*
|
|
148
|
+
* Both directions are a warning rather than an error, and for the same reason:
|
|
149
|
+
* neither claim is wrong on its own. A channel the code publishes on and the
|
|
150
|
+
* document does not declare is a document that has fallen behind - or an event
|
|
151
|
+
* going out where nobody agreed it would. A channel the document declares and
|
|
152
|
+
* no event names is a promise nothing keeps yet.
|
|
153
|
+
*
|
|
154
|
+
* A service with no document says nothing here. Silence is not a disagreement,
|
|
155
|
+
* and a rule that fired on every service without an AsyncAPI file would be a
|
|
156
|
+
* page of rows about work nobody has started.
|
|
157
|
+
*/
|
|
158
|
+
function documentAgainstCode(catalog: Catalog): Problem[] {
|
|
159
|
+
const out: Problem[] = [];
|
|
160
|
+
|
|
161
|
+
for (const context of catalog.contexts) {
|
|
162
|
+
for (const service of context.services) {
|
|
163
|
+
const channels = service.channels ?? [];
|
|
164
|
+
const declared = new Set(
|
|
165
|
+
channels
|
|
166
|
+
.filter((c) => (!c.kind || c.kind === "event") && sends(c.messages))
|
|
167
|
+
.map((c) => c.address),
|
|
168
|
+
);
|
|
169
|
+
// Channels that only receive say nothing about what the service
|
|
170
|
+
// sends: a subscription read out of the code is not a document of
|
|
171
|
+
// the service's publications, and holding the events against it
|
|
172
|
+
// would report every one of them.
|
|
173
|
+
if (declared.size === 0) continue;
|
|
174
|
+
const published = new Map<string, Event>();
|
|
175
|
+
|
|
176
|
+
for (const aggregate of service.aggregates) {
|
|
177
|
+
for (const event of aggregate.events) {
|
|
178
|
+
const channel = event.wire?.channel;
|
|
179
|
+
if (!channel) continue;
|
|
180
|
+
if (!published.has(channel)) published.set(channel, event);
|
|
181
|
+
if (declared.has(channel)) continue;
|
|
182
|
+
|
|
183
|
+
out.push({
|
|
184
|
+
kind: "channel-undeclared",
|
|
185
|
+
severity: "warning",
|
|
186
|
+
context: context.id,
|
|
187
|
+
service: service.id,
|
|
188
|
+
id: event.id,
|
|
189
|
+
peer: channel,
|
|
190
|
+
note: `${event.name} goes out on ${channel}, which ${service.id} does not declare it sends on. A subscriber reading the document does not know this message exists.`,
|
|
191
|
+
source: event.versions[0]?.source,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const channel of channels) {
|
|
197
|
+
if (channel.kind && channel.kind !== "event") continue;
|
|
198
|
+
if (!sends(channel.messages)) continue;
|
|
199
|
+
if (published.has(channel.address)) continue;
|
|
200
|
+
|
|
201
|
+
out.push({
|
|
202
|
+
kind: "channel-unpublished",
|
|
203
|
+
severity: "warning",
|
|
204
|
+
context: context.id,
|
|
205
|
+
service: service.id,
|
|
206
|
+
id: service.id,
|
|
207
|
+
peer: channel.address,
|
|
208
|
+
note: `${service.id} declares it sends on ${channel.address}, and no event of it names that channel: ${channel.messages
|
|
209
|
+
.filter((m) => m.direction === "send")
|
|
210
|
+
.map((m) => m.name)
|
|
211
|
+
.join(", ")}.`,
|
|
212
|
+
source: channel.source,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* A message a service listens for that nothing in the estate publishes.
|
|
223
|
+
*
|
|
224
|
+
* This is the only edge in the catalog that runs the other way. Everywhere else
|
|
225
|
+
* the publisher names its consumers and a name that resolves to nobody is the
|
|
226
|
+
* problem; here the subscriber names a message, and the estate is searched for
|
|
227
|
+
* whoever puts it on the wire. A subscription that resolves is how two
|
|
228
|
+
* repositories that never mention each other are found to be joined.
|
|
229
|
+
*/
|
|
230
|
+
function unresolvedSubscriptions(
|
|
231
|
+
catalog: Catalog,
|
|
232
|
+
index: CatalogIndex,
|
|
233
|
+
): Problem[] {
|
|
234
|
+
// Every name anything in the estate puts on the wire. The index knows the
|
|
235
|
+
// events; a document declaring a send is the other half, and it counts for
|
|
236
|
+
// the same reason it counts as publishing above - the code may simply not
|
|
237
|
+
// have caught up with it yet.
|
|
238
|
+
const published = new Set(index.eventByWireName.keys());
|
|
239
|
+
for (const context of catalog.contexts) {
|
|
240
|
+
for (const service of context.services) {
|
|
241
|
+
for (const channel of service.channels ?? []) {
|
|
242
|
+
for (const message of channel.messages) {
|
|
243
|
+
if (message.direction === "send") published.add(message.name);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const out: Problem[] = [];
|
|
250
|
+
for (const context of catalog.contexts) {
|
|
251
|
+
for (const service of context.services) {
|
|
252
|
+
for (const channel of service.channels ?? []) {
|
|
253
|
+
for (const message of channel.messages) {
|
|
254
|
+
if (message.direction !== "receive") continue;
|
|
255
|
+
if (published.has(message.name)) continue;
|
|
256
|
+
|
|
257
|
+
out.push({
|
|
258
|
+
kind: "subscription-unresolved",
|
|
259
|
+
severity: "warning",
|
|
260
|
+
context: context.id,
|
|
261
|
+
service: service.id,
|
|
262
|
+
id: service.id,
|
|
263
|
+
peer: message.name,
|
|
264
|
+
note: `${service.id} listens on ${channel.address} for ${message.name}, and nothing in the catalog publishes it. Either the publisher is outside the estate, or the name has drifted.`,
|
|
265
|
+
source: channel.source,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function sends(messages: { direction: string }[]): boolean {
|
|
276
|
+
return messages.some((m) => m.direction === "send");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function sourceOf(service: Service, channel: string): string | undefined {
|
|
280
|
+
return service.channels?.find((c) => c.address === channel)?.source;
|
|
281
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { InteractiveView } from "./InteractiveView";
|
|
3
|
+
import { catalogIdOf } from "./mapping";
|
|
4
|
+
import { fqn } from "./ids";
|
|
5
|
+
import { viewHasNode } from "./view-index";
|
|
6
|
+
import { useSelectionStore } from "../selection/store";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A declared C4 element view. Same renderer as the flows, same generated
|
|
10
|
+
* sources — portolan never draws these itself. Clicking a node selects what it
|
|
11
|
+
* stands for; clicking the canvas clears.
|
|
12
|
+
*/
|
|
13
|
+
export function C4View({
|
|
14
|
+
viewId,
|
|
15
|
+
height = 320,
|
|
16
|
+
controls = false,
|
|
17
|
+
}: {
|
|
18
|
+
viewId: string;
|
|
19
|
+
height?: number | string;
|
|
20
|
+
/** Zoom controls, useful when a whole estate has to fit on a phone. */
|
|
21
|
+
controls?: boolean;
|
|
22
|
+
}) {
|
|
23
|
+
const selection = useSelectionStore((s) => s.selection);
|
|
24
|
+
const source = useSelectionStore((s) => s.source);
|
|
25
|
+
const select = useSelectionStore((s) => s.select);
|
|
26
|
+
const clear = useSelectionStore((s) => s.clear);
|
|
27
|
+
|
|
28
|
+
const onNode = useCallback(
|
|
29
|
+
(likec4Id: string) => select(catalogIdOf(likec4Id), "diagram"),
|
|
30
|
+
[select],
|
|
31
|
+
);
|
|
32
|
+
const onCanvas = useCallback(() => clear("diagram"), [clear]);
|
|
33
|
+
|
|
34
|
+
// A selection made here is already marked by LikeC4 itself; one made
|
|
35
|
+
// elsewhere is only marked when this view actually draws it.
|
|
36
|
+
const marked =
|
|
37
|
+
selection && source !== "diagram" && viewHasNode(viewId, fqn(selection.id))
|
|
38
|
+
? [fqn(selection.id)]
|
|
39
|
+
: [];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
className="w-full overflow-hidden rounded-card border border-line bg-canvas shadow-xs"
|
|
44
|
+
style={{ height }}
|
|
45
|
+
>
|
|
46
|
+
<InteractiveView
|
|
47
|
+
viewId={viewId}
|
|
48
|
+
controls={controls}
|
|
49
|
+
highlightNodes={marked}
|
|
50
|
+
onNode={onNode}
|
|
51
|
+
onCanvas={onCanvas}
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// The one wire between the page and the picture.
|
|
2
|
+
//
|
|
3
|
+
// LikeC4's diagram API is only reachable from inside its own provider, so this
|
|
4
|
+
// component renders as a child of the canvas and does nothing else: it draws
|
|
5
|
+
// no markup, owns no state of its own, and exists so that the rail outside can
|
|
6
|
+
// move the viewport and the toolbar outside can drive the walkthrough without
|
|
7
|
+
// either of them knowing that a state machine is involved.
|
|
8
|
+
//
|
|
9
|
+
// Two directions, and they are deliberately not symmetrical:
|
|
10
|
+
//
|
|
11
|
+
// page -> canvas selecting a step brings its arrow into view. During
|
|
12
|
+
// playback that becomes a walkthrough step instead, because
|
|
13
|
+
// there must only ever be one animator and it is LikeC4's.
|
|
14
|
+
// canvas -> page the walkthrough says which step it is on, and the rail
|
|
15
|
+
// follows: it highlights, it expands the chapter, it
|
|
16
|
+
// scrolls. The rail never leads the walkthrough.
|
|
17
|
+
|
|
18
|
+
import { useEffect, useImperativeHandle, useRef } from "react";
|
|
19
|
+
import type { RefObject } from "react";
|
|
20
|
+
import { useDiagram, useDiagramContext } from "likec4/react";
|
|
21
|
+
import type { DiagramApi } from "likec4/react";
|
|
22
|
+
import { boundsOf, focusViewport } from "./canvas-viewport";
|
|
23
|
+
import type { Size } from "./canvas-viewport";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* LikeC4 brands its ids so that one kind cannot be passed where another is
|
|
27
|
+
* wanted. Ours are read straight back out of its own view model, so widening a
|
|
28
|
+
* string into the brand is a formality rather than a claim — and taking the
|
|
29
|
+
* type off the method keeps the formality honest if the brand ever changes.
|
|
30
|
+
*/
|
|
31
|
+
type EdgeIdArg = Parameters<DiagramApi["centerViewportOnEdge"]>[0];
|
|
32
|
+
type StepPathArg = Parameters<DiagramApi["startWalkthrough"]>[0] & string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* How big the canvas is right now, measured from the DOM rather than read off
|
|
36
|
+
* React Flow's store.
|
|
37
|
+
*
|
|
38
|
+
* The store's width is kept by a ResizeObserver, which fires a frame late —
|
|
39
|
+
* and selecting a step opens the detail rail, which takes a hundred-odd pixels
|
|
40
|
+
* off the canvas in the very same commit. Aiming a viewport with the width the
|
|
41
|
+
* canvas had a moment ago overshoots by exactly that much, which is how "bring
|
|
42
|
+
* this step into view" ends up putting it a hundred pixels past the edge.
|
|
43
|
+
*/
|
|
44
|
+
function canvasNode(diagram: DiagramApi): HTMLElement | null {
|
|
45
|
+
const node: unknown = diagram.getContext().xystore.getState().domNode;
|
|
46
|
+
return node instanceof HTMLElement ? node : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function canvasSize(node: HTMLElement): Size | null {
|
|
50
|
+
const box = node.getBoundingClientRect();
|
|
51
|
+
return box.width > 0 && box.height > 0
|
|
52
|
+
? { width: box.width, height: box.height }
|
|
53
|
+
: null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CanvasHandle {
|
|
57
|
+
/** Begin playback at the first step. */
|
|
58
|
+
start: () => void;
|
|
59
|
+
step: (delta: 1 | -1) => void;
|
|
60
|
+
stop: () => void;
|
|
61
|
+
/** The escape hatch from readable zoom: show the whole shape. */
|
|
62
|
+
fit: () => void;
|
|
63
|
+
/** The canvas element, for whoever wants to draw it to a file. */
|
|
64
|
+
node: () => HTMLElement | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function CanvasBridge({
|
|
68
|
+
handle,
|
|
69
|
+
activeEdgeId,
|
|
70
|
+
focusEdgeIds,
|
|
71
|
+
onWalkthroughEdge,
|
|
72
|
+
}: {
|
|
73
|
+
handle: RefObject<CanvasHandle | null>;
|
|
74
|
+
/** The edge the page wants in view, or null when it wants nothing. */
|
|
75
|
+
activeEdgeId: string | null;
|
|
76
|
+
/** The selected executable path, fitted after its siblings leave. */
|
|
77
|
+
focusEdgeIds?: readonly string[] | null;
|
|
78
|
+
/** The edge playback is on, or null when playback is not running. */
|
|
79
|
+
onWalkthroughEdge: (edgeId: string | null) => void;
|
|
80
|
+
}) {
|
|
81
|
+
const diagram = useDiagram();
|
|
82
|
+
/** The last step the page asked the canvas to show, acted on once. */
|
|
83
|
+
const asked = useRef<string | null>(null);
|
|
84
|
+
const walkthroughStep = useDiagramContext(
|
|
85
|
+
(context) => context.activeWalkthrough?.stepId ?? null,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
useImperativeHandle(
|
|
89
|
+
handle,
|
|
90
|
+
() => ({
|
|
91
|
+
start: () => diagram.startWalkthrough(),
|
|
92
|
+
step: (delta) => diagram.walkthroughStep(delta > 0 ? "next" : "prev"),
|
|
93
|
+
stop: () => diagram.stopWalkthrough(),
|
|
94
|
+
fit: () => diagram.fitDiagram(),
|
|
95
|
+
node: () => canvasNode(diagram),
|
|
96
|
+
}),
|
|
97
|
+
[diagram],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
onWalkthroughEdge(walkthroughStep ? String(walkthroughStep) : null);
|
|
102
|
+
}, [walkthroughStep, onWalkthroughEdge]);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!activeEdgeId) return;
|
|
106
|
+
|
|
107
|
+
if (walkthroughStep) {
|
|
108
|
+
// Playback is running, so the only thing worth acting on is a step the
|
|
109
|
+
// reader has just ASKED for — and only once.
|
|
110
|
+
//
|
|
111
|
+
// Acting on the value itself would deadlock the two of them: playback
|
|
112
|
+
// advances to step 3 while the page is still pointing at step 2, the
|
|
113
|
+
// page scrubs it back to 2, playback reports 2, and neither ever gets
|
|
114
|
+
// to finish. The ref is what tells "the reader picked this" apart from
|
|
115
|
+
// "this is what the page was already pointing at".
|
|
116
|
+
if (activeEdgeId === asked.current) return;
|
|
117
|
+
asked.current = activeEdgeId;
|
|
118
|
+
if (String(walkthroughStep) !== activeEdgeId) {
|
|
119
|
+
// A scrub, not a pan: it moves the playhead rather than the camera.
|
|
120
|
+
diagram.walkthroughStep({ step: activeEdgeId as StepPathArg });
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
asked.current = activeEdgeId;
|
|
125
|
+
|
|
126
|
+
// Computed here rather than handed to `centerViewportOnEdge`, which caps
|
|
127
|
+
// the zoom at whatever the viewport already had: an arrow wider than the
|
|
128
|
+
// pane then ends up centred and still half off the screen, which is not
|
|
129
|
+
// what "bring step 12 into view" was asked for. The API is still the one
|
|
130
|
+
// moving the viewport; only the destination is ours.
|
|
131
|
+
const bounds = boundsOf(diagram.findDiagramEdge(activeEdgeId)?.points ?? []);
|
|
132
|
+
const node = canvasNode(diagram);
|
|
133
|
+
if (!bounds || !node) {
|
|
134
|
+
diagram.centerViewportOnEdge(activeEdgeId as EdgeIdArg);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const aim = (): void => {
|
|
139
|
+
const canvas = canvasSize(node);
|
|
140
|
+
if (!canvas) return;
|
|
141
|
+
diagram.send({
|
|
142
|
+
type: "xyflow.setViewport",
|
|
143
|
+
viewport: focusViewport(bounds, canvas),
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
aim();
|
|
148
|
+
|
|
149
|
+
// And again whenever the canvas changes size under it. Selecting a step is
|
|
150
|
+
// also what opens the detail rail, which takes its width off the canvas a
|
|
151
|
+
// commit later — so the first aim is at a canvas that is about to stop
|
|
152
|
+
// existing, and without this correction the step lands just off the edge.
|
|
153
|
+
// It keeps the step framed through a divider drag too, which is what the
|
|
154
|
+
// reader dragging the divider while reading a step wants.
|
|
155
|
+
const observer = new ResizeObserver(aim);
|
|
156
|
+
observer.observe(node);
|
|
157
|
+
return () => observer.disconnect();
|
|
158
|
+
}, [activeEdgeId, walkthroughStep, diagram]);
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (activeEdgeId || walkthroughStep || !focusEdgeIds?.length) return;
|
|
162
|
+
const points = focusEdgeIds.flatMap(
|
|
163
|
+
(edgeId) => diagram.findDiagramEdge(edgeId)?.points ?? [],
|
|
164
|
+
);
|
|
165
|
+
const bounds = boundsOf(points);
|
|
166
|
+
const node = canvasNode(diagram);
|
|
167
|
+
if (!bounds || !node) return;
|
|
168
|
+
|
|
169
|
+
const aim = (): void => {
|
|
170
|
+
const canvas = canvasSize(node);
|
|
171
|
+
if (!canvas) return;
|
|
172
|
+
diagram.send({
|
|
173
|
+
type: "xyflow.setViewport",
|
|
174
|
+
viewport: focusViewport(bounds, canvas),
|
|
175
|
+
duration: 180,
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Let the stylesheet that hides the other branches land before moving the
|
|
180
|
+
// camera; the geometry is precomputed, but the transition should describe
|
|
181
|
+
// the picture the reader can already see.
|
|
182
|
+
const frame = requestAnimationFrame(aim);
|
|
183
|
+
return () => cancelAnimationFrame(frame);
|
|
184
|
+
}, [activeEdgeId, walkthroughStep, focusEdgeIds, diagram]);
|
|
185
|
+
|
|
186
|
+
return null;
|
|
187
|
+
}
|