@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,53 @@
|
|
|
1
|
+
# Host
|
|
2
|
+
|
|
3
|
+
The side of portolan that runs. `npm run gen` reads the manifest, runs every
|
|
4
|
+
plugin it names, stamps and writes what comes back, merges the sources into
|
|
5
|
+
one estate and validates the union. Nothing here draws anything.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
- Refuses a `portolan.json` that does not match `schema/portolan.schema.json`,
|
|
10
|
+
the schema `npm run schema` composes out of what every declared plugin says
|
|
11
|
+
it can be told.
|
|
12
|
+
- Runs three phases in order, extract, verify, generate, and hands each
|
|
13
|
+
plugin one JSON message: the input root, output directory and stamp for an
|
|
14
|
+
extractor, the merged catalog for a verifier or a generator, the step's
|
|
15
|
+
options unread. The output path lets a fragment point at a companion file
|
|
16
|
+
returned in the same response without letting the plugin write it itself.
|
|
17
|
+
- Stamps a fragment from the last commit that touched its input, never from a
|
|
18
|
+
clock, so a committed fragment changes exactly when its subject does
|
|
19
|
+
(portolan.0002).
|
|
20
|
+
- Writes the files a plugin named, refuses a name that climbs out of the
|
|
21
|
+
step's output directory, keeps a listing per step of what it wrote, and
|
|
22
|
+
deletes what stopped being generated (portolan.0001).
|
|
23
|
+
- Merges every source the manifest's globs find and validates the union with
|
|
24
|
+
the code the site runs: `src/merge.ts`, `src/catalog.ts`, `src/enrich.ts`.
|
|
25
|
+
- In `--check` mode writes nothing and fails on the first file that differs
|
|
26
|
+
from disk. Either way it leaves `.portolan/build-report.json` for the
|
|
27
|
+
Settings page.
|
|
28
|
+
|
|
29
|
+
## What it does not do
|
|
30
|
+
|
|
31
|
+
- Know what any plugin can be told: options pass through, and the schema is
|
|
32
|
+
the plugins' answer, not the host's.
|
|
33
|
+
- Read a plugin's stdout for anything but the one message, or its stderr for
|
|
34
|
+
anything but notes; a warning is kept beside the step in the build report.
|
|
35
|
+
- Draw the C4 model: `gen-likec4.mjs` writes `likec4/` from the same merged
|
|
36
|
+
catalog, and the site draws.
|
|
37
|
+
|
|
38
|
+
## Files
|
|
39
|
+
|
|
40
|
+
- `gen.mjs`, the three phases; `plugin-host.mjs` and
|
|
41
|
+
`plugin-wasm-worker.mjs`, one plugin run as a process or a wasm module;
|
|
42
|
+
`manifest.mjs`, the manifest read and checked; `catalog-sources.mjs`, the
|
|
43
|
+
merge; `output-path.mjs`, the one refusal of an unsafe name;
|
|
44
|
+
`build-report.mjs`, what the Settings page reads.
|
|
45
|
+
- `schema.mjs`, the manifest schema composed; `gen-likec4.mjs`, the C4 model;
|
|
46
|
+
`diff.mjs`, what a branch changes about the architecture; `forge-comment.mjs`,
|
|
47
|
+
that report put on the pull request as one comment kept current;
|
|
48
|
+
`forge-release.mjs`, the same report as one section of a release's notes;
|
|
49
|
+
`forge.mjs`, what those two share - which forge the CI is, and how to talk
|
|
50
|
+
to it; `vendor-lock.mjs`,
|
|
51
|
+
the commit a fetched copy is of; `site-docs.mjs`, generated documentation
|
|
52
|
+
put into the built site; `local-api.mjs`, what the dev server answers the
|
|
53
|
+
site with.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// The last generator run, for the Settings page.
|
|
2
|
+
//
|
|
3
|
+
// Unlike catalog fragments this is deliberately not committed: elapsed time
|
|
4
|
+
// and the wall clock are observations of one run, not reproducible catalog
|
|
5
|
+
// facts. Vite reduces this file again before it reaches the browser.
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
import { writeOutputFile } from "./output-path.mjs";
|
|
11
|
+
|
|
12
|
+
export const BUILD_REPORT_DIR = ".portolan";
|
|
13
|
+
export const BUILD_REPORT_NAME = "build-report.json";
|
|
14
|
+
|
|
15
|
+
export function createBuildReport({
|
|
16
|
+
mode,
|
|
17
|
+
manifestSha256,
|
|
18
|
+
now = Date.now(),
|
|
19
|
+
}) {
|
|
20
|
+
return {
|
|
21
|
+
version: 1,
|
|
22
|
+
mode,
|
|
23
|
+
manifestSha256,
|
|
24
|
+
status: "running",
|
|
25
|
+
startedAt: new Date(now).toISOString(),
|
|
26
|
+
finishedAt: "",
|
|
27
|
+
durationMs: 0,
|
|
28
|
+
steps: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addBuildStep(report, step) {
|
|
33
|
+
report.steps.push({ ordinal: report.steps.length, ...step });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function finishBuildReport(report, status, now = Date.now()) {
|
|
37
|
+
report.status = status;
|
|
38
|
+
report.finishedAt = new Date(now).toISOString();
|
|
39
|
+
report.durationMs = Math.max(0, now - Date.parse(report.startedAt));
|
|
40
|
+
return report;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function writeBuildReport(
|
|
44
|
+
report,
|
|
45
|
+
out = BUILD_REPORT_DIR,
|
|
46
|
+
name = BUILD_REPORT_NAME,
|
|
47
|
+
) {
|
|
48
|
+
writeOutputFile(out, name, `${JSON.stringify(report, null, 2)}\n`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readBuildReport(
|
|
52
|
+
out = BUILD_REPORT_DIR,
|
|
53
|
+
name = BUILD_REPORT_NAME,
|
|
54
|
+
) {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readFileSync(join(out, name), "utf8"));
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mkdtempSync, readFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
addBuildStep,
|
|
7
|
+
createBuildReport,
|
|
8
|
+
finishBuildReport,
|
|
9
|
+
writeBuildReport,
|
|
10
|
+
} from "./build-report.mjs";
|
|
11
|
+
|
|
12
|
+
describe("build report", () => {
|
|
13
|
+
it("records one run without making it a generated catalog artifact", () => {
|
|
14
|
+
const report = createBuildReport({
|
|
15
|
+
mode: "check",
|
|
16
|
+
manifestSha256: "abc",
|
|
17
|
+
now: Date.parse("2026-09-05T00:00:00Z"),
|
|
18
|
+
});
|
|
19
|
+
addBuildStep(report, {
|
|
20
|
+
phase: "extract",
|
|
21
|
+
plugin: "domain",
|
|
22
|
+
input: "services/auth",
|
|
23
|
+
output: "services/auth/portolan",
|
|
24
|
+
status: "up-to-date",
|
|
25
|
+
durationMs: 42,
|
|
26
|
+
fileCount: 1,
|
|
27
|
+
changedCount: 0,
|
|
28
|
+
files: ["services/auth/portolan/domain.json"],
|
|
29
|
+
});
|
|
30
|
+
finishBuildReport(
|
|
31
|
+
report,
|
|
32
|
+
"ok",
|
|
33
|
+
Date.parse("2026-09-05T00:00:01Z"),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(report.durationMs).toBe(1000);
|
|
37
|
+
expect(report.steps[0]?.ordinal).toBe(0);
|
|
38
|
+
|
|
39
|
+
const dir = mkdtempSync(join(tmpdir(), "portolan-report-"));
|
|
40
|
+
writeBuildReport(report, dir);
|
|
41
|
+
expect(JSON.parse(readFileSync(join(dir, "build-report.json"), "utf8")))
|
|
42
|
+
.toEqual(report);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Resolves the plugins shipped with Portolan.
|
|
2
|
+
//
|
|
3
|
+
// A user manifest names built-ins by name and does not repeat how this npm
|
|
4
|
+
// package happens to run them. Custom plugins may still be declared in the
|
|
5
|
+
// manifest with `process` or `wasm`; those declarations always win.
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
export const INSTALL_ROOT = process.env.PORTOLAN_INSTALL_ROOT
|
|
12
|
+
? resolve(process.env.PORTOLAN_INSTALL_ROOT)
|
|
13
|
+
: resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
|
+
|
|
15
|
+
let definitions;
|
|
16
|
+
|
|
17
|
+
function shippedDefinitions() {
|
|
18
|
+
if (definitions) return definitions;
|
|
19
|
+
const manifest = JSON.parse(readFileSync(resolve(INSTALL_ROOT, "portolan.json"), "utf8"));
|
|
20
|
+
definitions = new Map((manifest.plugins ?? []).map((plugin) => [plugin.name, plugin]));
|
|
21
|
+
return definitions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function builtinPluginNames() {
|
|
25
|
+
return new Set(shippedDefinitions().keys());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Return a runnable built-in definition, or null when the package has none. */
|
|
29
|
+
export function builtinPlugin(name) {
|
|
30
|
+
const declared = shippedDefinitions().get(name);
|
|
31
|
+
if (!declared) return null;
|
|
32
|
+
|
|
33
|
+
if (declared.wasm) {
|
|
34
|
+
const value = declared.wasm.url;
|
|
35
|
+
const url = value.startsWith("file://")
|
|
36
|
+
? `file://${resolve(INSTALL_ROOT, value.slice("file://".length))}`
|
|
37
|
+
: value;
|
|
38
|
+
return { ...declared, builtin: true, wasm: { ...declared.wasm, url } };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
name,
|
|
43
|
+
builtin: true,
|
|
44
|
+
process: {
|
|
45
|
+
command: process.execPath,
|
|
46
|
+
args: [resolve(INSTALL_ROOT, "scripts/run-builtin.mjs"), name],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Reads every catalog source the manifest names, merges them, and validates
|
|
2
|
+
// the union.
|
|
3
|
+
//
|
|
4
|
+
// Shared by the generator runner and the LikeC4 generator so the two cannot
|
|
5
|
+
// disagree about what the estate is. Merging before validating is the point:
|
|
6
|
+
// referential integrity holds over the union, and a fragment naming a peer it
|
|
7
|
+
// does not own is normal rather than broken.
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { glob } from "node:fs/promises";
|
|
11
|
+
import { normalize } from "node:path";
|
|
12
|
+
|
|
13
|
+
import { validateCatalog } from "../src/catalog.ts";
|
|
14
|
+
import { filterCatalogForProfile } from "../src/catalog-profile.ts";
|
|
15
|
+
import { enrichCatalog } from "../src/enrich.ts";
|
|
16
|
+
import { mergeCatalogs } from "../src/merge.ts";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `exclude` names source files to leave out, as the manifest would spell
|
|
20
|
+
* them. It exists for the verify phase: a step that writes a fragment from
|
|
21
|
+
* what it observed must be shown the catalog WITHOUT its own last output, or
|
|
22
|
+
* what it wrote last time would count as what it saw this time.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadCatalog(manifestPath = "portolan.json", { exclude = [], profile } = {}) {
|
|
25
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
26
|
+
const excluded = new Set(exclude.map((path) => normalize(path)));
|
|
27
|
+
const selected = profile
|
|
28
|
+
? (manifest.catalogs ?? []).find((candidate) => candidate.id === profile)
|
|
29
|
+
: null;
|
|
30
|
+
if (profile && !selected) {
|
|
31
|
+
throw new Error(`${manifestPath}: unknown catalog profile ${JSON.stringify(profile)}`);
|
|
32
|
+
}
|
|
33
|
+
const patterns = selected?.sources ?? manifest.sources ?? [];
|
|
34
|
+
|
|
35
|
+
const paths = [];
|
|
36
|
+
for await (const path of glob(patterns)) {
|
|
37
|
+
if (!excluded.has(normalize(path))) paths.push(path);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (paths.length === 0) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`${manifestPath}: no catalog matched ${JSON.stringify(patterns)}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const merged = mergeCatalogs(
|
|
47
|
+
paths.map((path) => ({
|
|
48
|
+
path,
|
|
49
|
+
catalog: JSON.parse(readFileSync(path, "utf8")),
|
|
50
|
+
})),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// The edges the flows imply are added before validation, the same way the
|
|
54
|
+
// app does it, so a generator draws the same estate the reader sees.
|
|
55
|
+
const scoped = selected
|
|
56
|
+
? filterCatalogForProfile(merged.catalog, selected)
|
|
57
|
+
: merged.catalog;
|
|
58
|
+
const enriched = enrichCatalog(scoped);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
validateCatalog(enriched.catalog);
|
|
62
|
+
} catch (cause) {
|
|
63
|
+
throw new Error(`the merged catalog is not valid: ${cause.message}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { manifest, ...merged, catalog: enriched.catalog, derived: enriched.derived };
|
|
67
|
+
}
|
package/scripts/diff.mjs
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// What a branch changes about the architecture.
|
|
2
|
+
//
|
|
3
|
+
// node scripts/diff.mjs against origin/main, or main
|
|
4
|
+
// node scripts/diff.mjs <ref> against any commit, tag or branch
|
|
5
|
+
// --format markdown|json|sarif markdown is what a pull request renders
|
|
6
|
+
// --output <file> instead of stdout; the directory is made
|
|
7
|
+
// --site <url> --head <branch> markdown ends with a link into the site's
|
|
8
|
+
// Changes page for the same pair, so a
|
|
9
|
+
// reviewer can go from the list to the diff
|
|
10
|
+
//
|
|
11
|
+
// `gen:check` proves the documentation follows from the catalog; it says
|
|
12
|
+
// nothing about what a change DOES. The diff it leaves a reviewer with is a
|
|
13
|
+
// hundred markdown pages, which is a shape nobody reads and everybody
|
|
14
|
+
// approves. This prints the other thing: the events, endpoints, transitions
|
|
15
|
+
// and owners that are not what they were.
|
|
16
|
+
//
|
|
17
|
+
// The catalog at the base ref is not rebuilt. Every source a plugin writes is
|
|
18
|
+
// COMMITTED - that is the whole point of `gen:check` - so the estate as it
|
|
19
|
+
// stood at any commit is a handful of `git show`s away, and nothing here needs
|
|
20
|
+
// a checkout, a worktree, a toolchain, or the extractors to run twice. The
|
|
21
|
+
// working tree's side is read the way the app reads it.
|
|
22
|
+
|
|
23
|
+
import { execFileSync } from "node:child_process";
|
|
24
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
|
+
import { dirname } from "node:path";
|
|
26
|
+
|
|
27
|
+
import { validateCatalog } from "../src/catalog.ts";
|
|
28
|
+
import { enrichCatalog } from "../src/enrich.ts";
|
|
29
|
+
import { mergeCatalogs } from "../src/merge.ts";
|
|
30
|
+
import { diffCatalogs, SEVERITIES } from "../src/lib/catalog-diff.ts";
|
|
31
|
+
import { loadCatalog } from "./catalog-sources.mjs";
|
|
32
|
+
|
|
33
|
+
const HEADINGS = {
|
|
34
|
+
breaking: "Breaking",
|
|
35
|
+
addition: "Added",
|
|
36
|
+
change: "Changed",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
const options = parseArgs(process.argv.slice(2));
|
|
41
|
+
const ref = options.ref || defaultBase();
|
|
42
|
+
const before = catalogAt(ref);
|
|
43
|
+
const { catalog: after, sources } = await loadCatalog("portolan.json");
|
|
44
|
+
const changes = diffCatalogs(before, after);
|
|
45
|
+
const output = renderFormat(options.format, ref, changes, {
|
|
46
|
+
site: options.site,
|
|
47
|
+
head: options.head || currentBranch(),
|
|
48
|
+
files: sources.map(({ path }) => ({ path, text: readFileSync(path, "utf8") })),
|
|
49
|
+
});
|
|
50
|
+
if (options.output) {
|
|
51
|
+
mkdirSync(dirname(options.output), { recursive: true });
|
|
52
|
+
writeFileSync(options.output, output);
|
|
53
|
+
console.log(`architecture diff: ${options.format} → ${options.output}`);
|
|
54
|
+
} else {
|
|
55
|
+
process.stdout.write(output);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Zero either way. This describes a change; it does not judge one, and a
|
|
59
|
+
// non-zero exit would make every pull request that touches the estate look
|
|
60
|
+
// like a failure.
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseArgs(args) {
|
|
64
|
+
const options = { ref: "", format: "markdown", output: "", site: "", head: "" };
|
|
65
|
+
const valued = ["--format", "--output", "--site", "--head"];
|
|
66
|
+
for (let i = 0; i < args.length; i++) {
|
|
67
|
+
const arg = args[i];
|
|
68
|
+
const spelled = valued.find((name) => arg === name || arg.startsWith(name + "="));
|
|
69
|
+
if (spelled) {
|
|
70
|
+
const value = arg === spelled ? args[++i] : arg.slice(spelled.length + 1);
|
|
71
|
+
if (!value) throw new Error(`${spelled} needs a value`);
|
|
72
|
+
options[spelled.slice(2)] = value;
|
|
73
|
+
} else if (arg.startsWith("-")) {
|
|
74
|
+
throw new Error(`unknown option ${arg}`);
|
|
75
|
+
} else if (options.ref) {
|
|
76
|
+
throw new Error(`more than one base ref: ${options.ref}, ${arg}`);
|
|
77
|
+
} else {
|
|
78
|
+
options.ref = arg;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!["markdown", "json", "sarif"].includes(options.format)) {
|
|
82
|
+
throw new Error(`unknown diff format ${options.format}; use markdown, json or sarif`);
|
|
83
|
+
}
|
|
84
|
+
return options;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `extra` is what a format may use beyond the changes: `site` and `head` for
|
|
89
|
+
* the markdown's link into the Changes page, `files` (path and text of every
|
|
90
|
+
* working-tree source) for SARIF to anchor a finding to the fragment that
|
|
91
|
+
* declares the id it names.
|
|
92
|
+
*/
|
|
93
|
+
export function renderFormat(format, ref, changes, extra = {}) {
|
|
94
|
+
if (format === "json") return renderJson(ref, changes);
|
|
95
|
+
if (format === "sarif") return renderSarif(ref, changes, extra.files ?? []);
|
|
96
|
+
return render(ref, changes, extra);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The branch checked out, or "" when HEAD is detached, as a PR checkout is. */
|
|
100
|
+
function currentBranch() {
|
|
101
|
+
try {
|
|
102
|
+
const name = git(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
103
|
+
|
|
104
|
+
return name === "HEAD" ? "" : name;
|
|
105
|
+
} catch {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The Changes page for the same pair: the base as the site spells a branch,
|
|
112
|
+
* without the remote, and the head only when it is known - a detached
|
|
113
|
+
* checkout has no name, and the page can ask.
|
|
114
|
+
*/
|
|
115
|
+
export function changesHref(site, ref, head) {
|
|
116
|
+
if (!site) return "";
|
|
117
|
+
const base = ref.replace(/^origin\//, "");
|
|
118
|
+
const query = new URLSearchParams({ base });
|
|
119
|
+
if (head) query.set("head", head);
|
|
120
|
+
|
|
121
|
+
return site.replace(/\/$/, "") + "/changes?" + query.toString();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Where to compare against when nobody said: the trunk as the remote has it,
|
|
126
|
+
* because a local `main` that has not been fetched for a week would report a
|
|
127
|
+
* week of somebody else's work as this branch's.
|
|
128
|
+
*/
|
|
129
|
+
function defaultBase() {
|
|
130
|
+
for (const ref of ["origin/main", "main"]) {
|
|
131
|
+
try {
|
|
132
|
+
execFileSync("git", ["rev-parse", "--verify", "--quiet", ref], { stdio: "ignore" });
|
|
133
|
+
|
|
134
|
+
return ref;
|
|
135
|
+
} catch {
|
|
136
|
+
// Not a ref in this repository.
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throw new Error("no origin/main or main to compare against; name a ref");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** The merged, enriched, validated catalog as it stood at `ref`. */
|
|
144
|
+
function catalogAt(ref) {
|
|
145
|
+
const manifest = JSON.parse(git(["show", ref + ":portolan.json"]));
|
|
146
|
+
const patterns = (manifest.sources ?? []).map(globToRegExp);
|
|
147
|
+
|
|
148
|
+
const paths = git(["ls-tree", "-r", "--name-only", ref])
|
|
149
|
+
.split("\n")
|
|
150
|
+
.filter((path) => patterns.some((re) => re.test(path)));
|
|
151
|
+
|
|
152
|
+
if (paths.length === 0) {
|
|
153
|
+
throw new Error(ref + " holds no catalog sources matching " + JSON.stringify(manifest.sources));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const merged = mergeCatalogs(
|
|
157
|
+
paths.map((path) => ({ path, catalog: JSON.parse(git(["show", ref + ":" + path])) })),
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Enriched before it is compared, exactly as the app and the generators see
|
|
161
|
+
// it, so a derived edge is compared with a derived edge rather than with a
|
|
162
|
+
// difference in how the two sides were built.
|
|
163
|
+
const { catalog } = enrichCatalog(merged.catalog);
|
|
164
|
+
try {
|
|
165
|
+
validateCatalog(catalog);
|
|
166
|
+
} catch (cause) {
|
|
167
|
+
throw new Error("the catalog at " + ref + " is not valid: " + cause.message);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return catalog;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function git(args) {
|
|
174
|
+
return execFileSync("git", args, {
|
|
175
|
+
encoding: "utf8",
|
|
176
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
177
|
+
}).trimEnd();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A manifest glob as a regular expression.
|
|
182
|
+
*
|
|
183
|
+
* A star stops at a slash and two stars do not, which is what the manifest's
|
|
184
|
+
* patterns mean and what `fs.glob` gives the working tree's side. Anchored at
|
|
185
|
+
* both ends: a source pattern names a whole path.
|
|
186
|
+
*/
|
|
187
|
+
export function globToRegExp(pattern) {
|
|
188
|
+
let out = "";
|
|
189
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
190
|
+
const c = pattern[i];
|
|
191
|
+
if (c === "*") {
|
|
192
|
+
if (pattern[i + 1] === "*") {
|
|
193
|
+
out += ".*";
|
|
194
|
+
i++;
|
|
195
|
+
} else {
|
|
196
|
+
out += "[^/]*";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
out += /[a-zA-Z0-9/_-]/.test(c) ? c : "\\" + c;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return new RegExp("^" + out + "$");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** The report, as markdown, because that is what a pull request renders. */
|
|
208
|
+
export function render(ref, changes, { site = "", head = "" } = {}) {
|
|
209
|
+
if (changes.length === 0) {
|
|
210
|
+
return "No architectural change against `" + ref + "`.\n";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const lines = [
|
|
214
|
+
"### Architecture, against `" + ref + "`",
|
|
215
|
+
"",
|
|
216
|
+
changes.length + " change" + (changes.length === 1 ? "" : "s") + ".",
|
|
217
|
+
"",
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
for (const severity of SEVERITIES) {
|
|
221
|
+
const of = changes.filter((change) => change.severity === severity);
|
|
222
|
+
if (of.length === 0) continue;
|
|
223
|
+
|
|
224
|
+
lines.push("**" + HEADINGS[severity] + "** (" + of.length + ")", "");
|
|
225
|
+
for (const change of of) lines.push("- " + change.summary);
|
|
226
|
+
lines.push("");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// The list says what moved; the page shows it in the estate, with the
|
|
230
|
+
// diagrams and the pages around it, so the link is the reviewer's way on.
|
|
231
|
+
const href = changesHref(site, ref, head);
|
|
232
|
+
if (href) lines.push("[Open in Changes](" + href + ")", "");
|
|
233
|
+
|
|
234
|
+
return lines.join("\n");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Where a change is declared: the first working-tree source whose text holds
|
|
239
|
+
* `"id": "<where>"`, and the line it is on. A change with no such source - a
|
|
240
|
+
* removal, whose id is only on the base side; a catalog-level fact - is
|
|
241
|
+
* anchored at the manifest, because code scanning shows nothing without a
|
|
242
|
+
* location, and the manifest is the one file every estate has.
|
|
243
|
+
*/
|
|
244
|
+
export function locate(where, files) {
|
|
245
|
+
const id = where.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
246
|
+
const re = new RegExp('"id"\\s*:\\s*"' + id + '"');
|
|
247
|
+
for (const { path, text } of files) {
|
|
248
|
+
const at = text.search(re);
|
|
249
|
+
if (at === -1) continue;
|
|
250
|
+
|
|
251
|
+
return { uri: path, line: text.slice(0, at).split("\n").length };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { uri: "portolan.json", line: 1 };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function counts(changes) {
|
|
258
|
+
return Object.fromEntries(SEVERITIES.map((severity) => [severity, changes.filter((change) => change.severity === severity).length]));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Stable machine output for CI, bots and downstream reports. */
|
|
262
|
+
export function renderJson(ref, changes) {
|
|
263
|
+
return `${JSON.stringify({ version: 1, base: ref, counts: counts(changes), changes }, null, 2)}\n`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* SARIF 2.1.0: breaking changes are errors; everything else remains
|
|
268
|
+
* reviewable. Every result carries a location, because a code scanning
|
|
269
|
+
* upload shows a result without one to nobody.
|
|
270
|
+
*/
|
|
271
|
+
export function renderSarif(ref, changes, files = []) {
|
|
272
|
+
const kinds = [...new Set(changes.map((change) => change.kind))].sort();
|
|
273
|
+
const rules = kinds.map((kind) => ({
|
|
274
|
+
id: kind,
|
|
275
|
+
name: kind.replace(/[^a-zA-Z0-9]+/g, "_"),
|
|
276
|
+
shortDescription: { text: `Portolan architecture change: ${kind}` },
|
|
277
|
+
helpUri: "https://github.com/shortlink-org/portolan",
|
|
278
|
+
}));
|
|
279
|
+
const level = { breaking: "error", change: "warning", addition: "note" };
|
|
280
|
+
const sarif = {
|
|
281
|
+
version: "2.1.0",
|
|
282
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
283
|
+
runs: [{
|
|
284
|
+
tool: { driver: { name: "Portolan", informationUri: "https://github.com/shortlink-org/portolan", rules } },
|
|
285
|
+
automationDetails: { id: `architecture-diff/${ref}` },
|
|
286
|
+
results: changes.map((change) => {
|
|
287
|
+
const at = locate(change.where, files);
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
ruleId: change.kind,
|
|
291
|
+
level: level[change.severity],
|
|
292
|
+
message: { text: change.summary },
|
|
293
|
+
locations: [{
|
|
294
|
+
physicalLocation: {
|
|
295
|
+
artifactLocation: { uri: at.uri, uriBaseId: "%SRCROOT%" },
|
|
296
|
+
region: { startLine: at.line, startColumn: 1 },
|
|
297
|
+
},
|
|
298
|
+
}],
|
|
299
|
+
properties: { severity: change.severity, where: change.where, base: ref },
|
|
300
|
+
};
|
|
301
|
+
}),
|
|
302
|
+
}],
|
|
303
|
+
};
|
|
304
|
+
return `${JSON.stringify(sarif, null, 2)}\n`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Only when run, not when imported: the tests below this reach for the two
|
|
308
|
+
// pieces with logic in them, and a module that ran a git command on import
|
|
309
|
+
// would be a module they could not reach at all.
|
|
310
|
+
if (import.meta.main) {
|
|
311
|
+
main().catch((cause) => {
|
|
312
|
+
console.error("diff: " + cause.message);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
});
|
|
315
|
+
}
|