@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
package/src/graph/elk.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// elkjs layout for everything React Flow renders. Async by nature, so callers
|
|
2
|
+
// hold the result in state; nothing here touches React.
|
|
3
|
+
//
|
|
4
|
+
// elk does not just place the boxes. It also ROUTES the lines, and that
|
|
5
|
+
// routing is the reason elk is in the stack at all: a bezier drawn between two
|
|
6
|
+
// node centres knows nothing about the third node it passes through, and on a
|
|
7
|
+
// layered graph it will pass through one. So the sections elk returns come
|
|
8
|
+
// back with the positions, and the canvas draws those rather than a curve of
|
|
9
|
+
// its own invention.
|
|
10
|
+
|
|
11
|
+
import ELK from "elkjs/lib/elk.bundled.js";
|
|
12
|
+
import type { ElkNode, ElkExtendedEdge } from "elkjs/lib/elk-api";
|
|
13
|
+
|
|
14
|
+
const elk = new ELK();
|
|
15
|
+
|
|
16
|
+
export interface Point {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LayoutInput {
|
|
22
|
+
nodes: { id: string; width: number; height: number }[];
|
|
23
|
+
edges: { id: string; source: string; target: string; labelWidth?: number }[];
|
|
24
|
+
direction?: "RIGHT" | "DOWN";
|
|
25
|
+
/** extra space between layers; the focused graph wants more than the big one */
|
|
26
|
+
layerSpacing?: number;
|
|
27
|
+
nodeSpacing?: number;
|
|
28
|
+
/**
|
|
29
|
+
* "layered" is a flow: it reads left to right and every edge is a step. The
|
|
30
|
+
* context map is not a flow - it is a map, its edges run both ways, and a
|
|
31
|
+
* layered pass would put a third domain in the middle of the line between
|
|
32
|
+
* the other two. "stress" spreads the nodes in two dimensions instead, and
|
|
33
|
+
* `edgeLength` is how far apart it tries to hold them.
|
|
34
|
+
*/
|
|
35
|
+
algorithm?: "layered" | "stress";
|
|
36
|
+
edgeLength?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Give every node one WEST input port and one EAST output port, pinned to
|
|
39
|
+
* those sides. Without it elk is free to attach a line anywhere on the
|
|
40
|
+
* boundary, and a line leaving the top-left corner of a box that is laid out
|
|
41
|
+
* left-to-right reads as a mistake even when the routing is optimal.
|
|
42
|
+
*/
|
|
43
|
+
ports?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* How the lines are drawn. ORTHOGONAL is right angles, which is what a
|
|
46
|
+
* bundled service-to-service edge wants; SPLINES is smooth, which is what a
|
|
47
|
+
* graph with a node between every pair of services wants.
|
|
48
|
+
*/
|
|
49
|
+
edgeRouting?: "ORTHOGONAL" | "SPLINES" | "POLYLINE";
|
|
50
|
+
/**
|
|
51
|
+
* How the layers are packed vertically.
|
|
52
|
+
*
|
|
53
|
+
* NETWORK_SIMPLEX is the best answer and by far the most expensive one: it
|
|
54
|
+
* solves for straight edges rather than approximating, and past roughly a
|
|
55
|
+
* hundred nodes-and-edges it goes from milliseconds to seconds. Callers big
|
|
56
|
+
* enough to feel that ask for BRANDES_KOEPF instead, which is the same
|
|
57
|
+
* family of answer computed by heuristic.
|
|
58
|
+
*/
|
|
59
|
+
nodePlacement?: "NETWORK_SIMPLEX" | "BRANDES_KOEPF";
|
|
60
|
+
/**
|
|
61
|
+
* How hard elk works at crossing minimisation. elk's own default is 7; the
|
|
62
|
+
* only reason to lower it is a graph too big to afford the default.
|
|
63
|
+
*/
|
|
64
|
+
thoroughness?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Break layer ties by the order the caller listed the nodes in. It is what
|
|
67
|
+
* puts an event pill at the height of the service that emits it: the events
|
|
68
|
+
* are listed in publisher order, so within their layer they end up in
|
|
69
|
+
* publisher order too.
|
|
70
|
+
*/
|
|
71
|
+
considerModelOrder?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface LayoutResult {
|
|
75
|
+
positions: Record<string, Point>;
|
|
76
|
+
/**
|
|
77
|
+
* Edge id -> the points elk routed it through, start first and end last.
|
|
78
|
+
* Empty for an edge elk chose not to route (it does that for edges it hides
|
|
79
|
+
* inside a self-loop, which this app never asks it to draw).
|
|
80
|
+
*/
|
|
81
|
+
routes: Record<string, Point[]>;
|
|
82
|
+
width: number;
|
|
83
|
+
height: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const IN = (id: string): string => `${id}::in`;
|
|
87
|
+
const OUT = (id: string): string => `${id}::out`;
|
|
88
|
+
|
|
89
|
+
export async function layoutWithElk(input: LayoutInput): Promise<LayoutResult> {
|
|
90
|
+
const children: ElkNode[] = input.nodes.map((n) => ({
|
|
91
|
+
id: n.id,
|
|
92
|
+
width: n.width,
|
|
93
|
+
height: n.height,
|
|
94
|
+
...(input.ports
|
|
95
|
+
? {
|
|
96
|
+
layoutOptions: { "elk.portConstraints": "FIXED_SIDE" },
|
|
97
|
+
ports: [
|
|
98
|
+
{
|
|
99
|
+
id: IN(n.id),
|
|
100
|
+
width: 1,
|
|
101
|
+
height: 1,
|
|
102
|
+
layoutOptions: { "elk.port.side": "WEST" },
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: OUT(n.id),
|
|
106
|
+
width: 1,
|
|
107
|
+
height: 1,
|
|
108
|
+
layoutOptions: { "elk.port.side": "EAST" },
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
}
|
|
112
|
+
: {}),
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
const edges: ElkExtendedEdge[] = input.edges.map((e) => ({
|
|
116
|
+
id: e.id,
|
|
117
|
+
sources: [input.ports ? OUT(e.source) : e.source],
|
|
118
|
+
targets: [input.ports ? IN(e.target) : e.target],
|
|
119
|
+
...(e.labelWidth
|
|
120
|
+
? { labels: [{ text: "", width: e.labelWidth, height: 14 }] }
|
|
121
|
+
: {}),
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
const stress = input.algorithm === "stress";
|
|
125
|
+
const graph: ElkNode = {
|
|
126
|
+
id: "root",
|
|
127
|
+
layoutOptions: stress
|
|
128
|
+
? {
|
|
129
|
+
"elk.algorithm": "stress",
|
|
130
|
+
"elk.stress.desiredEdgeLength": String(input.edgeLength ?? 320),
|
|
131
|
+
"elk.spacing.nodeNode": String(input.nodeSpacing ?? 60),
|
|
132
|
+
"elk.padding": "[top=16,left=16,bottom=16,right=16]",
|
|
133
|
+
}
|
|
134
|
+
: {
|
|
135
|
+
"elk.algorithm": "layered",
|
|
136
|
+
"elk.direction": input.direction ?? "RIGHT",
|
|
137
|
+
// Every node is a peer; nothing here nests, so there are no children
|
|
138
|
+
// to lay out inside a parent.
|
|
139
|
+
"elk.hierarchyHandling": "SEPARATE_CHILDREN",
|
|
140
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": String(
|
|
141
|
+
input.layerSpacing ?? 130,
|
|
142
|
+
),
|
|
143
|
+
"elk.spacing.nodeNode": String(input.nodeSpacing ?? 34),
|
|
144
|
+
"elk.layered.spacing.edgeNodeBetweenLayers": "24",
|
|
145
|
+
"elk.spacing.edgeLabel": "6",
|
|
146
|
+
"elk.layered.nodePlacement.strategy":
|
|
147
|
+
input.nodePlacement ?? "NETWORK_SIMPLEX",
|
|
148
|
+
...(input.thoroughness
|
|
149
|
+
? { "elk.layered.thoroughness": String(input.thoroughness) }
|
|
150
|
+
: {}),
|
|
151
|
+
"elk.layered.crossingMinimization.semiInteractive": "true",
|
|
152
|
+
...(input.considerModelOrder
|
|
153
|
+
? {
|
|
154
|
+
"elk.layered.considerModelOrder.strategy": "NODES_AND_EDGES",
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
157
|
+
...(input.edgeRouting
|
|
158
|
+
? { "elk.edgeRouting": input.edgeRouting }
|
|
159
|
+
: {}),
|
|
160
|
+
"elk.padding": "[top=16,left=16,bottom=16,right=16]",
|
|
161
|
+
},
|
|
162
|
+
children,
|
|
163
|
+
edges,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const laid = await elk.layout(graph);
|
|
167
|
+
const positions: Record<string, Point> = {};
|
|
168
|
+
for (const child of laid.children ?? []) {
|
|
169
|
+
positions[child.id] = { x: child.x ?? 0, y: child.y ?? 0 };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const routes: Record<string, Point[]> = {};
|
|
173
|
+
for (const edge of (laid.edges ?? []) as ElkExtendedEdge[]) {
|
|
174
|
+
const points: Point[] = [];
|
|
175
|
+
for (const section of edge.sections ?? []) {
|
|
176
|
+
// Sections chain: the end of one is the start of the next, so the shared
|
|
177
|
+
// point is written once.
|
|
178
|
+
if (points.length === 0) points.push(section.startPoint);
|
|
179
|
+
for (const bend of section.bendPoints ?? []) points.push(bend);
|
|
180
|
+
points.push(section.endPoint);
|
|
181
|
+
}
|
|
182
|
+
if (points.length > 0) routes[edge.id] = points;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
positions,
|
|
187
|
+
routes,
|
|
188
|
+
width: laid.width ?? 0,
|
|
189
|
+
height: laid.height ?? 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Drawing what elk answered
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A polyline through `points` with its corners rounded off.
|
|
199
|
+
*
|
|
200
|
+
* The radius shrinks to fit: a corner between two short segments gets a small
|
|
201
|
+
* arc rather than one that overshoots into the segment after it, which is what
|
|
202
|
+
* turns a tidy orthogonal route into a knot at exactly the places where the
|
|
203
|
+
* route is densest.
|
|
204
|
+
*/
|
|
205
|
+
export function roundedPath(points: readonly Point[], radius = 10): string {
|
|
206
|
+
if (points.length === 0) return "";
|
|
207
|
+
const first = points[0];
|
|
208
|
+
if (!first) return "";
|
|
209
|
+
if (points.length === 1) return `M ${first.x},${first.y}`;
|
|
210
|
+
|
|
211
|
+
let d = `M ${first.x},${first.y}`;
|
|
212
|
+
for (let i = 1; i < points.length - 1; i += 1) {
|
|
213
|
+
const prev = points[i - 1];
|
|
214
|
+
const corner = points[i];
|
|
215
|
+
const next = points[i + 1];
|
|
216
|
+
if (!prev || !corner || !next) continue;
|
|
217
|
+
|
|
218
|
+
const inLen = Math.hypot(corner.x - prev.x, corner.y - prev.y);
|
|
219
|
+
const outLen = Math.hypot(next.x - corner.x, next.y - corner.y);
|
|
220
|
+
const r = Math.min(radius, inLen / 2, outLen / 2);
|
|
221
|
+
if (r < 0.5) {
|
|
222
|
+
d += ` L ${corner.x},${corner.y}`;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const start = lerp(corner, prev, r / inLen);
|
|
226
|
+
const end = lerp(corner, next, r / outLen);
|
|
227
|
+
d += ` L ${start.x},${start.y} Q ${corner.x},${corner.y} ${end.x},${end.y}`;
|
|
228
|
+
}
|
|
229
|
+
const last = points[points.length - 1];
|
|
230
|
+
if (last) d += ` L ${last.x},${last.y}`;
|
|
231
|
+
return d;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function lerp(from: Point, to: Point, t: number): Point {
|
|
235
|
+
return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** The point half way along a polyline, where a chip or a label goes. */
|
|
239
|
+
export function midpoint(points: readonly Point[]): Point {
|
|
240
|
+
if (points.length === 0) return { x: 0, y: 0 };
|
|
241
|
+
const head = points[0];
|
|
242
|
+
if (!head) return { x: 0, y: 0 };
|
|
243
|
+
if (points.length === 1) return head;
|
|
244
|
+
|
|
245
|
+
let total = 0;
|
|
246
|
+
for (let i = 1; i < points.length; i += 1) {
|
|
247
|
+
const a = points[i - 1];
|
|
248
|
+
const b = points[i];
|
|
249
|
+
if (a && b) total += Math.hypot(b.x - a.x, b.y - a.y);
|
|
250
|
+
}
|
|
251
|
+
let walked = 0;
|
|
252
|
+
for (let i = 1; i < points.length; i += 1) {
|
|
253
|
+
const a = points[i - 1];
|
|
254
|
+
const b = points[i];
|
|
255
|
+
if (!a || !b) continue;
|
|
256
|
+
const len = Math.hypot(b.x - a.x, b.y - a.y);
|
|
257
|
+
if (walked + len >= total / 2) {
|
|
258
|
+
const t = len === 0 ? 0 : (total / 2 - walked) / len;
|
|
259
|
+
return lerp(a, b, t);
|
|
260
|
+
}
|
|
261
|
+
walked += len;
|
|
262
|
+
}
|
|
263
|
+
return points[points.length - 1] ?? head;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Moves the interior of a route sideways, leaving its ends on their ports.
|
|
268
|
+
*
|
|
269
|
+
* Two services that publish to each other get two lines through the same
|
|
270
|
+
* corridor. elk routes them apart when it can and on top of each other when it
|
|
271
|
+
* cannot; nudging one up and the other down guarantees the reader sees two
|
|
272
|
+
* lines whichever elk chose, and keeping the endpoints pinned means neither
|
|
273
|
+
* line detaches from the box it belongs to.
|
|
274
|
+
*/
|
|
275
|
+
export function offsetInterior(points: readonly Point[], dy: number): Point[] {
|
|
276
|
+
if (points.length <= 2) {
|
|
277
|
+
// A straight run has no interior to move, so it grows one: a single
|
|
278
|
+
// waypoint at the middle, offset, which bows the line clear of its twin.
|
|
279
|
+
const a = points[0];
|
|
280
|
+
const b = points[1];
|
|
281
|
+
if (!a || !b) return [...points];
|
|
282
|
+
return [a, { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 + dy }, b];
|
|
283
|
+
}
|
|
284
|
+
return points.map((p, i) =>
|
|
285
|
+
i === 0 || i === points.length - 1 ? p : { x: p.x, y: p.y + dy },
|
|
286
|
+
);
|
|
287
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Handle, Position } from "@xyflow/react";
|
|
2
|
+
import type { NodeProps, Node } from "@xyflow/react";
|
|
3
|
+
import { Server } from "lucide-react";
|
|
4
|
+
import { EventIcon } from "../components/ddd-icons";
|
|
5
|
+
import { contextVar } from "../lib/context-color";
|
|
6
|
+
|
|
7
|
+
export type ServiceNodeData = {
|
|
8
|
+
label: string;
|
|
9
|
+
context: string | null;
|
|
10
|
+
ghost: boolean;
|
|
11
|
+
kind: "service" | "event" | "producer" | "context";
|
|
12
|
+
/**
|
|
13
|
+
* What this node is to the graph it is in - "publisher", "consumer". The
|
|
14
|
+
* kind says what an entity IS; the role says why it is on this canvas, and
|
|
15
|
+
* on a picture of one event that is the more useful of the two.
|
|
16
|
+
*/
|
|
17
|
+
role?: string;
|
|
18
|
+
/** Second line, on the nodes big enough to hold one. */
|
|
19
|
+
sub?: string;
|
|
20
|
+
/** Right-aligned tag - a context's classification, and nothing else so far. */
|
|
21
|
+
tag?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type PortolanNode = Node<ServiceNodeData, "portolan">;
|
|
25
|
+
|
|
26
|
+
/** Fallback when a graph does not name the role itself. */
|
|
27
|
+
const ROLE: Record<ServiceNodeData["kind"], string> = {
|
|
28
|
+
producer: "publisher",
|
|
29
|
+
event: "event",
|
|
30
|
+
service: "service",
|
|
31
|
+
context: "context",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The node the focused event graph draws. (The dependency graph and the
|
|
36
|
+
* context map register their own components under the same `portolan` key,
|
|
37
|
+
* so this one is not shared - it used to be, and the comment saying so
|
|
38
|
+
* outlived the split.)
|
|
39
|
+
*
|
|
40
|
+
* A card: an icon in a tinted tile, and beside it two lines. The eyebrow says
|
|
41
|
+
* outright what the box is - publisher, event, consumer - so a reader does
|
|
42
|
+
* not have to work it out from position, and it takes the colour, which
|
|
43
|
+
* leaves the name under it plain and readable. The tile is where the card
|
|
44
|
+
* keeps the colour of the context that owns it; it used to be a bar down the
|
|
45
|
+
* left edge and a wash across the box, and on a canvas of cards a wash is
|
|
46
|
+
* the one thing that stops a card being a card.
|
|
47
|
+
*/
|
|
48
|
+
export function PortolanNode({ data }: NodeProps<PortolanNode>) {
|
|
49
|
+
const accent = data.ghost
|
|
50
|
+
? "var(--status-unresolved)"
|
|
51
|
+
: data.kind === "event"
|
|
52
|
+
? // An event is amber everywhere else in the app; it was blue here only
|
|
53
|
+
// because blue is what --accent happened to be.
|
|
54
|
+
"var(--kind-event)"
|
|
55
|
+
: contextVar(data.context);
|
|
56
|
+
const Icon = data.kind === "event" ? EventIcon : Server;
|
|
57
|
+
// A node the catalog does not know is not a publisher or a consumer - it is
|
|
58
|
+
// a name someone wrote down, and the eyebrow is where that is said.
|
|
59
|
+
const eyebrow = data.ghost
|
|
60
|
+
? "not in catalog"
|
|
61
|
+
: (data.role ?? ROLE[data.kind]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
className={`flex h-full w-full items-center gap-2 px-2.5 ${
|
|
66
|
+
data.ghost ? "flow-card-ghost" : "flow-card"
|
|
67
|
+
}`}
|
|
68
|
+
style={{ color: data.ghost ? "var(--status-unresolved)" : "var(--fg)" }}
|
|
69
|
+
title={data.ghost ? `${data.label} — not in the catalog` : data.label}
|
|
70
|
+
>
|
|
71
|
+
<span className="flow-tile" style={{ color: accent }}>
|
|
72
|
+
<Icon size={12} aria-hidden />
|
|
73
|
+
</span>
|
|
74
|
+
<div className="flex min-w-0 flex-1 flex-col justify-center gap-px">
|
|
75
|
+
<span
|
|
76
|
+
className="truncate uppercase"
|
|
77
|
+
style={{
|
|
78
|
+
color: accent,
|
|
79
|
+
fontSize: 9,
|
|
80
|
+
lineHeight: 1.3,
|
|
81
|
+
letterSpacing: "0.06em",
|
|
82
|
+
fontWeight: 600,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{eyebrow}
|
|
86
|
+
</span>
|
|
87
|
+
<span className="mono truncate" style={{ fontSize: 12, lineHeight: 1.3 }}>
|
|
88
|
+
{data.label}
|
|
89
|
+
</span>
|
|
90
|
+
</div>
|
|
91
|
+
<Handle type="target" position={Position.Left} style={{ opacity: 0 }} />
|
|
92
|
+
<Handle type="source" position={Position.Right} style={{ opacity: 0 }} />
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const nodeTypes = { portolan: PortolanNode };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// The shared visual language. Both renderers speak it: these are the same
|
|
2
|
+
// tokens LikeC4 gets baked into its generated spec, so the seam is invisible.
|
|
3
|
+
|
|
4
|
+
import type { Status } from "../catalog";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Edge weight. One width for a line at rest, one for a line the selection has
|
|
8
|
+
* lit, on every canvas - a reader who has learned that a thick line means
|
|
9
|
+
* "this one" on the dependency graph should not have to relearn it on the
|
|
10
|
+
* context map.
|
|
11
|
+
*/
|
|
12
|
+
export const EDGE_W = 1.25;
|
|
13
|
+
export const EDGE_W_LIT = 2;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The focused event graph's boxes - the only nodes elk sizes from these
|
|
17
|
+
* defaults. Two lines tall: an eyebrow naming the role and the name under it.
|
|
18
|
+
*/
|
|
19
|
+
export const NODE_W = 176;
|
|
20
|
+
export const NODE_H = 48;
|
|
21
|
+
export const EVENT_W = 204;
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// The dependency graph's two node kinds.
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A service box: icon tile, name, and the two traffic counters under it. Wide
|
|
29
|
+
* enough for the counters to sit beside the tile without a truncated word.
|
|
30
|
+
*/
|
|
31
|
+
export const SERVICE_W = 212;
|
|
32
|
+
export const SERVICE_H = 52;
|
|
33
|
+
|
|
34
|
+
/** An event pill. 28px is the height of a chip, which is what it is. */
|
|
35
|
+
export const EVENT_H = 28;
|
|
36
|
+
export const EVENT_MIN_W = 104;
|
|
37
|
+
export const EVENT_MAX_W = 260;
|
|
38
|
+
|
|
39
|
+
/** Width of a pill collapsed to its icon, at fit zooms too small to read. */
|
|
40
|
+
export const EVENT_ICON_W = 28;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Below this zoom a pill's 11px mono renders under 6px on screen, which is a
|
|
44
|
+
* smudge with a shape rather than a word. Pills collapse to their icon there
|
|
45
|
+
* and give their name back on hover.
|
|
46
|
+
*
|
|
47
|
+
* The threshold is a judgement, and it is set by what the graphs actually fit
|
|
48
|
+
* at. The sample estate fits a full-width pane at about 0.58, so it keeps its
|
|
49
|
+
* names - small, but names; the forty-service estate fits at a third of that,
|
|
50
|
+
* where a row of clean icons beats a row of grey marks. Anything in between
|
|
51
|
+
* lands on the side its own fit zoom puts it.
|
|
52
|
+
*/
|
|
53
|
+
export const LEGIBLE_ZOOM = 0.55;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How wide a pill has to be to hold a name.
|
|
57
|
+
*
|
|
58
|
+
* Measured rather than guessed would mean a canvas pass per label before elk
|
|
59
|
+
* can start; 6.35px is the advance of the app's mono at 11px, and the box is
|
|
60
|
+
* padded to the point where being a pixel out is invisible.
|
|
61
|
+
*/
|
|
62
|
+
export function eventWidth(name: string, extras = 0): number {
|
|
63
|
+
const text = name.length * 6.35;
|
|
64
|
+
return Math.min(EVENT_MAX_W, Math.max(EVENT_MIN_W, text + 42 + extras));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function statusColor(status: Status): string {
|
|
68
|
+
return `var(--status-${status})`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Same three markers as the LikeC4 spec: solid / dashed / dotted. */
|
|
72
|
+
export function statusDash(status: Status): string | undefined {
|
|
73
|
+
switch (status) {
|
|
74
|
+
case "verified":
|
|
75
|
+
return undefined;
|
|
76
|
+
case "declared":
|
|
77
|
+
return "5 3";
|
|
78
|
+
case "unresolved":
|
|
79
|
+
return "2 3";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import type { Edge } from "@xyflow/react";
|
|
3
|
+
import { MarkerType } from "@xyflow/react";
|
|
4
|
+
import type { Status } from "../catalog";
|
|
5
|
+
import { layoutWithElk } from "./elk";
|
|
6
|
+
import type { PortolanNode, ServiceNodeData } from "./nodes";
|
|
7
|
+
import { EDGE_W, NODE_H, NODE_W, statusColor, statusDash } from "./theme";
|
|
8
|
+
|
|
9
|
+
export interface FlowSpec {
|
|
10
|
+
nodes: {
|
|
11
|
+
id: string;
|
|
12
|
+
data: ServiceNodeData;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
}[];
|
|
16
|
+
edges: {
|
|
17
|
+
id: string;
|
|
18
|
+
source: string;
|
|
19
|
+
target: string;
|
|
20
|
+
label: string;
|
|
21
|
+
status: Status;
|
|
22
|
+
eventId?: string;
|
|
23
|
+
}[];
|
|
24
|
+
direction?: "RIGHT" | "DOWN";
|
|
25
|
+
layerSpacing?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FlowState {
|
|
29
|
+
nodes: PortolanNode[];
|
|
30
|
+
edges: Edge[];
|
|
31
|
+
ready: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Runs elk whenever the spec changes and returns React Flow's nodes and edges. */
|
|
35
|
+
export function useElkFlow(spec: FlowSpec): FlowState {
|
|
36
|
+
const [state, setState] = useState<FlowState>({
|
|
37
|
+
nodes: [],
|
|
38
|
+
edges: [],
|
|
39
|
+
ready: false,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
let cancelled = false;
|
|
44
|
+
const run = async (): Promise<void> => {
|
|
45
|
+
const { positions } = await layoutWithElk({
|
|
46
|
+
nodes: spec.nodes.map((n) => ({
|
|
47
|
+
id: n.id,
|
|
48
|
+
width: n.width ?? NODE_W,
|
|
49
|
+
height: n.height ?? NODE_H,
|
|
50
|
+
})),
|
|
51
|
+
edges: spec.edges.map((e) => ({
|
|
52
|
+
id: e.id,
|
|
53
|
+
source: e.source,
|
|
54
|
+
target: e.target,
|
|
55
|
+
labelWidth: Math.max(40, e.label.length * 6),
|
|
56
|
+
})),
|
|
57
|
+
...(spec.direction ? { direction: spec.direction } : {}),
|
|
58
|
+
...(spec.layerSpacing ? { layerSpacing: spec.layerSpacing } : {}),
|
|
59
|
+
});
|
|
60
|
+
if (cancelled) return;
|
|
61
|
+
|
|
62
|
+
const nodes: PortolanNode[] = spec.nodes.map((n) => ({
|
|
63
|
+
id: n.id,
|
|
64
|
+
type: "portolan",
|
|
65
|
+
position: positions[n.id] ?? { x: 0, y: 0 },
|
|
66
|
+
data: n.data,
|
|
67
|
+
initialWidth: n.width ?? NODE_W,
|
|
68
|
+
initialHeight: n.height ?? NODE_H,
|
|
69
|
+
width: n.width ?? NODE_W,
|
|
70
|
+
height: n.height ?? NODE_H,
|
|
71
|
+
draggable: false,
|
|
72
|
+
connectable: false,
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
const edges: Edge[] = spec.edges.map((e) => {
|
|
76
|
+
const color = statusColor(e.status);
|
|
77
|
+
const dash = statusDash(e.status);
|
|
78
|
+
return {
|
|
79
|
+
id: e.id,
|
|
80
|
+
source: e.source,
|
|
81
|
+
target: e.target,
|
|
82
|
+
label: e.label,
|
|
83
|
+
type: "bezier",
|
|
84
|
+
animated: false,
|
|
85
|
+
style: {
|
|
86
|
+
stroke: color,
|
|
87
|
+
strokeWidth: EDGE_W,
|
|
88
|
+
...(dash ? { strokeDasharray: dash } : {}),
|
|
89
|
+
},
|
|
90
|
+
labelStyle: {
|
|
91
|
+
fill: "var(--fg-muted)",
|
|
92
|
+
fontSize: 10,
|
|
93
|
+
fontFamily: "var(--font-mono)",
|
|
94
|
+
},
|
|
95
|
+
// A word printed straight onto a line is a word with a line through
|
|
96
|
+
// it. The label carries the canvas background and a hairline of its
|
|
97
|
+
// own, which is the same pill the bundled edges wear.
|
|
98
|
+
labelBgStyle: {
|
|
99
|
+
fill: "var(--flow-card)",
|
|
100
|
+
stroke: "var(--border)",
|
|
101
|
+
strokeWidth: 1,
|
|
102
|
+
},
|
|
103
|
+
labelBgPadding: [6, 3] as [number, number],
|
|
104
|
+
labelBgBorderRadius: 8,
|
|
105
|
+
labelShowBg: true,
|
|
106
|
+
markerEnd: {
|
|
107
|
+
type: MarkerType.ArrowClosed,
|
|
108
|
+
color,
|
|
109
|
+
width: 14,
|
|
110
|
+
height: 14,
|
|
111
|
+
},
|
|
112
|
+
data: e.eventId ? { eventId: e.eventId } : {},
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
setState({ nodes, edges, ready: true });
|
|
117
|
+
};
|
|
118
|
+
void run();
|
|
119
|
+
return () => {
|
|
120
|
+
cancelled = true;
|
|
121
|
+
};
|
|
122
|
+
}, [spec]);
|
|
123
|
+
|
|
124
|
+
return state;
|
|
125
|
+
}
|