@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,40 @@
|
|
|
1
|
+
// What the glossary calls the thing this page is about.
|
|
2
|
+
//
|
|
3
|
+
// One line, under the header, before the model: a reader who does not know
|
|
4
|
+
// what the word means in this context cannot read the page below it, and the
|
|
5
|
+
// sentence that says so is already written - in a file next to the code, by
|
|
6
|
+
// somebody who knows.
|
|
7
|
+
//
|
|
8
|
+
// It appears only where the glossary and the code spell the word the same
|
|
9
|
+
// way. That is most of the words worth knowing and nowhere near all of them,
|
|
10
|
+
// and the page says nothing at all when there is no pairing: a strip reading
|
|
11
|
+
// "no definition" on two thirds of the estate would be a lint nobody asked
|
|
12
|
+
// for, printed on every page.
|
|
13
|
+
|
|
14
|
+
import { Link } from "react-router";
|
|
15
|
+
import { catalog } from "../data";
|
|
16
|
+
import { paths } from "../routes";
|
|
17
|
+
import { bindTerms } from "../lib/terms";
|
|
18
|
+
|
|
19
|
+
// Built once, from the catalog the app ships, exactly as the palette builds
|
|
20
|
+
// its index: the pairing is a property of the estate, not of a render.
|
|
21
|
+
const BOUND = bindTerms(catalog);
|
|
22
|
+
|
|
23
|
+
export function InLanguage({ id }: { id: string }) {
|
|
24
|
+
const term = BOUND.byTarget.get(id);
|
|
25
|
+
if (!term) return null;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<p className="mb-3 max-w-prose text-muted">
|
|
29
|
+
<Link
|
|
30
|
+
to={paths.term(term.id)}
|
|
31
|
+
className="rounded-control font-medium text-ink hover:underline"
|
|
32
|
+
title={`${term.id} — in the glossary of ${term.context}`}
|
|
33
|
+
>
|
|
34
|
+
{term.name}
|
|
35
|
+
</Link>
|
|
36
|
+
{" — "}
|
|
37
|
+
{term.definition}
|
|
38
|
+
</p>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// One entry of a glossary, as a card: the word, and what it means.
|
|
2
|
+
//
|
|
3
|
+
// The slug is not drawn. `Bus` and `bus` beside each other is the same word
|
|
4
|
+
// printed twice, and the id it would have carried is on the copy button that
|
|
5
|
+
// every row in this app has.
|
|
6
|
+
|
|
7
|
+
import { Fragment } from "react";
|
|
8
|
+
import { Link } from "react-router";
|
|
9
|
+
import type { Term } from "../catalog";
|
|
10
|
+
import { KindIcon } from "../components/kind";
|
|
11
|
+
import type { TermTarget } from "../lib/terms";
|
|
12
|
+
import { RowActions } from "../components/RowActions";
|
|
13
|
+
import { ContextPill } from "../components/primitives";
|
|
14
|
+
import { ctxStyle } from "../lib/context-color";
|
|
15
|
+
import { sourceHref } from "../lib/source-link";
|
|
16
|
+
import { staggerStyle } from "../lib/motion";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Backticks, and nothing else.
|
|
20
|
+
*
|
|
21
|
+
* A glossary is prose with the occasional identifier in it - `logout`, the
|
|
22
|
+
* name of a service - and every other mark the format allows is already gone
|
|
23
|
+
* by the time a term reaches the catalog. So this is a split on backticks and
|
|
24
|
+
* not a markdown renderer: one react-markdown per card, thirty-eight of them
|
|
25
|
+
* on a page, to italicise nothing.
|
|
26
|
+
*/
|
|
27
|
+
function inline(text: string) {
|
|
28
|
+
return text.split("`").map((part, i) =>
|
|
29
|
+
i % 2 === 1 ? (
|
|
30
|
+
<code key={i} className="mono">
|
|
31
|
+
{part}
|
|
32
|
+
</code>
|
|
33
|
+
) : (
|
|
34
|
+
<Fragment key={i}>{part}</Fragment>
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function TermCard({
|
|
40
|
+
term,
|
|
41
|
+
targets = [],
|
|
42
|
+
index = 0,
|
|
43
|
+
showContext = false,
|
|
44
|
+
}: {
|
|
45
|
+
term: Term;
|
|
46
|
+
/**
|
|
47
|
+
* What the word names in the model, when the two spell it the same way.
|
|
48
|
+
* Empty is the ordinary case for the words worth defining - Bus, Refusal,
|
|
49
|
+
* Conflict name no type - so the row simply does not appear, and nothing on
|
|
50
|
+
* the card says anything is missing.
|
|
51
|
+
*/
|
|
52
|
+
targets?: readonly TermTarget[];
|
|
53
|
+
/** Position in the list, for the staggered entrance. */
|
|
54
|
+
index?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Names the context on the card. Off inside a vocabulary, whose heading has
|
|
57
|
+
* already said it; on where cards from several contexts stand together, and
|
|
58
|
+
* there the context is the only thing telling two of them apart.
|
|
59
|
+
*/
|
|
60
|
+
showContext?: boolean;
|
|
61
|
+
}) {
|
|
62
|
+
const href = sourceHref(term.source, null);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
id={`term-${term.id}`}
|
|
67
|
+
/* A column, so the row of cards can be one height and the line under
|
|
68
|
+
the definition still sits on the floor of each of them. Stretched to
|
|
69
|
+
its neighbours and left alone, a short entry grows a pocket of empty
|
|
70
|
+
card under its last sentence. */
|
|
71
|
+
className="card card-static stagger-in flex flex-col scroll-mt-4"
|
|
72
|
+
style={{ ...staggerStyle(index), ...ctxStyle(term.context), borderLeftWidth: 3 }}
|
|
73
|
+
>
|
|
74
|
+
<div className="flex items-baseline gap-2">
|
|
75
|
+
<h3 className="font-semibold">{term.name}</h3>
|
|
76
|
+
{showContext ? (
|
|
77
|
+
<span className="ml-auto shrink-0">
|
|
78
|
+
<ContextPill id={term.context} />
|
|
79
|
+
</span>
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<p className="mt-1.5">{inline(term.definition)}</p>
|
|
84
|
+
|
|
85
|
+
{targets.length > 0 ? (
|
|
86
|
+
<div className="mt-2 flex flex-wrap items-center gap-1.5">
|
|
87
|
+
{targets.map((target) => (
|
|
88
|
+
<Link
|
|
89
|
+
key={target.id}
|
|
90
|
+
to={target.path}
|
|
91
|
+
className="chip hover:border-line-strong hover:text-ink"
|
|
92
|
+
title={`${target.kind} ${target.id}`}
|
|
93
|
+
>
|
|
94
|
+
{/* A state is not one of the app's kinds - it has no page and no
|
|
95
|
+
mark of its own - so it wears a dot rather than borrowing an
|
|
96
|
+
icon that would say it was something it is not. */}
|
|
97
|
+
{target.kind === "state" ? (
|
|
98
|
+
<span aria-hidden className="dot" />
|
|
99
|
+
) : (
|
|
100
|
+
<KindIcon kind={target.kind} />
|
|
101
|
+
)}
|
|
102
|
+
{target.name}
|
|
103
|
+
</Link>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
) : null}
|
|
107
|
+
|
|
108
|
+
{/* Where the entry was written, and the button that copies its id, on one
|
|
109
|
+
line under the definition. The copy sat in the heading before, which
|
|
110
|
+
put a button between the word and the context that means it - two
|
|
111
|
+
things a reader compares across cards, and a control that appears
|
|
112
|
+
between them on hover is a control that moves them apart. */}
|
|
113
|
+
<div className="mt-auto flex items-baseline gap-2 pt-2 t-micro">
|
|
114
|
+
{href ? (
|
|
115
|
+
<a
|
|
116
|
+
href={href}
|
|
117
|
+
target="_blank"
|
|
118
|
+
rel="noreferrer"
|
|
119
|
+
className="mono text-faint hover:text-accent"
|
|
120
|
+
>
|
|
121
|
+
{term.source}
|
|
122
|
+
</a>
|
|
123
|
+
) : (
|
|
124
|
+
/* Not a link, but still the answer to "where do I change this?" -
|
|
125
|
+
the same way every unlinkable path in this app stays text. */
|
|
126
|
+
<span className="mono text-faint">{term.source}</span>
|
|
127
|
+
)}
|
|
128
|
+
<RowActions copy={term.id} label={term.name} />
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { catalog } from "../testing/estate";
|
|
3
|
+
import type { Catalog, Term } from "../catalog";
|
|
4
|
+
import { homonyms, matchTerms, vocabularies } from "./cards";
|
|
5
|
+
|
|
6
|
+
const term = (id: string, context: string, slug: string, name = slug): Term => ({
|
|
7
|
+
id,
|
|
8
|
+
slug,
|
|
9
|
+
context,
|
|
10
|
+
name,
|
|
11
|
+
definition: "one sentence.",
|
|
12
|
+
source: "GLOSSARY.md:1",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** A catalog with only what these functions read. */
|
|
16
|
+
const of = (terms: Term[], contexts: string[] = ["auth", "shop"]): Catalog =>
|
|
17
|
+
({
|
|
18
|
+
contexts: contexts.map((id) => ({ id, slug: id, name: id, services: [] })),
|
|
19
|
+
terms,
|
|
20
|
+
}) as unknown as Catalog;
|
|
21
|
+
|
|
22
|
+
describe("vocabularies", () => {
|
|
23
|
+
it("groups by context, in catalog order, alphabetical inside", () => {
|
|
24
|
+
const arranged = vocabularies(
|
|
25
|
+
of([
|
|
26
|
+
term("shop.order", "shop", "order", "Order"),
|
|
27
|
+
term("auth.session", "auth", "session", "Session"),
|
|
28
|
+
term("auth.attempt", "auth", "attempt", "Attempt"),
|
|
29
|
+
]),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(arranged.map((v) => v.contextId)).toEqual(["auth", "shop"]);
|
|
33
|
+
expect(arranged[0]?.terms.map((t) => t.name)).toEqual([
|
|
34
|
+
"Attempt",
|
|
35
|
+
"Session",
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("leaves out a context that has written no glossary", () => {
|
|
40
|
+
const arranged = vocabularies(of([term("auth.session", "auth", "session")]));
|
|
41
|
+
|
|
42
|
+
expect(arranged).toHaveLength(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// The validator refuses this catalog, so nothing should ever render it. The
|
|
46
|
+
// arrangement still shows the terms rather than dropping them: a list that
|
|
47
|
+
// silently loses rows is the harder bug of the two.
|
|
48
|
+
it("still shows terms whose context the catalog does not declare", () => {
|
|
49
|
+
const arranged = vocabularies(of([term("oms.order", "oms", "order")]));
|
|
50
|
+
|
|
51
|
+
expect(arranged).toHaveLength(1);
|
|
52
|
+
expect(arranged[0]?.context).toBeNull();
|
|
53
|
+
expect(arranged[0]?.contextId).toBe("oms");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("homonyms", () => {
|
|
58
|
+
it("finds one word meant in two contexts", () => {
|
|
59
|
+
const found = homonyms(
|
|
60
|
+
of([
|
|
61
|
+
term("auth.bus", "auth", "bus", "Bus"),
|
|
62
|
+
term("shop.bus", "shop", "bus", "Bus"),
|
|
63
|
+
term("auth.session", "auth", "session", "Session"),
|
|
64
|
+
]),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(found).toHaveLength(1);
|
|
68
|
+
expect(found[0]?.word).toBe("Bus");
|
|
69
|
+
expect(found[0]?.terms.map((t) => t.context)).toEqual(["auth", "shop"]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("says nothing about a word only one context uses", () => {
|
|
73
|
+
expect(homonyms(of([term("auth.session", "auth", "session")]))).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// What the estate actually has, and the reason the section exists: two
|
|
77
|
+
// services describe the same piece of plumbing in their own words.
|
|
78
|
+
it("finds the words the example estate means twice", () => {
|
|
79
|
+
expect(homonyms(catalog).map((h) => h.slug)).toEqual(["bus", "outbox"]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("matchTerms", () => {
|
|
84
|
+
const terms = [
|
|
85
|
+
{
|
|
86
|
+
...term("auth.session", "auth", "session", "Session"),
|
|
87
|
+
definition:
|
|
88
|
+
"Proof that a user logged in, and the token it is presented with.",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
...term("auth.lockout", "auth", "lockout", "Lockout"),
|
|
92
|
+
definition: "An account refusing passwords for a while.",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
it("matches on the name", () => {
|
|
97
|
+
expect(matchTerms(terms, "lock").map((t) => t.name)).toEqual(["Lockout"]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("matches on the definition, not only the word", () => {
|
|
101
|
+
expect(matchTerms(terms, "token").map((t) => t.name)).toEqual(["Session"]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("takes every word, in any order", () => {
|
|
105
|
+
expect(matchTerms(terms, "passwords account").map((t) => t.name)).toEqual([
|
|
106
|
+
"Lockout",
|
|
107
|
+
]);
|
|
108
|
+
expect(matchTerms(terms, "passwords token")).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("answers an empty query with everything", () => {
|
|
112
|
+
expect(matchTerms(terms, " ")).toHaveLength(2);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// The glossary, arranged for reading. Pure: a catalog in, plain values out,
|
|
2
|
+
// so what the page shows can be asserted in a test rather than eyeballed.
|
|
3
|
+
//
|
|
4
|
+
// Two arrangements, and the second is the one that earns the page. A context's
|
|
5
|
+
// vocabulary is a list anyone could have written by hand; the words that mean
|
|
6
|
+
// two things in two contexts are only visible once every glossary in the
|
|
7
|
+
// estate is in one place, and they are exactly what a reader crossing a
|
|
8
|
+
// boundary gets wrong.
|
|
9
|
+
|
|
10
|
+
import type { BoundedContext, Catalog, Term } from "../catalog";
|
|
11
|
+
import { allTerms } from "../catalog";
|
|
12
|
+
|
|
13
|
+
/** One context's words, alphabetical, however the sources were ordered. */
|
|
14
|
+
export interface Vocabulary {
|
|
15
|
+
contextId: string;
|
|
16
|
+
/** The context itself, when the catalog declares one. */
|
|
17
|
+
context: BoundedContext | null;
|
|
18
|
+
terms: Term[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function byName(a: Term, b: Term): number {
|
|
22
|
+
return a.name.localeCompare(b.name, undefined, { sensitivity: "base" });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Every vocabulary, in catalog order - which is the order the contexts are
|
|
27
|
+
* drawn in everywhere else, so the page reads in the same direction as the
|
|
28
|
+
* sidebar and the map.
|
|
29
|
+
*/
|
|
30
|
+
export function vocabularies(catalog: Catalog): Vocabulary[] {
|
|
31
|
+
const byContext = new Map<string, Term[]>();
|
|
32
|
+
for (const term of allTerms(catalog)) {
|
|
33
|
+
const list = byContext.get(term.context) ?? [];
|
|
34
|
+
list.push(term);
|
|
35
|
+
byContext.set(term.context, list);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const out: Vocabulary[] = [];
|
|
39
|
+
for (const context of catalog.contexts) {
|
|
40
|
+
const terms = byContext.get(context.id);
|
|
41
|
+
if (!terms) continue;
|
|
42
|
+
out.push({ contextId: context.id, context, terms: [...terms].sort(byName) });
|
|
43
|
+
byContext.delete(context.id);
|
|
44
|
+
}
|
|
45
|
+
// A glossary whose context is not in the catalog cannot happen - the
|
|
46
|
+
// validator refuses it - but the arrangement does not depend on that being
|
|
47
|
+
// true, because a list that silently drops rows is worse than one that
|
|
48
|
+
// shows them somewhere plain.
|
|
49
|
+
for (const [contextId, terms] of byContext) {
|
|
50
|
+
out.push({ contextId, context: null, terms: [...terms].sort(byName) });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One word, and the contexts that mean different things by it. */
|
|
57
|
+
export interface Homonym {
|
|
58
|
+
/** The shared slug - what makes two spellings one word. */
|
|
59
|
+
slug: string;
|
|
60
|
+
/** As the glossaries spell it, taking the first if they disagree in case. */
|
|
61
|
+
word: string;
|
|
62
|
+
terms: Term[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The words that appear in more than one context.
|
|
67
|
+
*
|
|
68
|
+
* Not a problem to fix. `User` in auth is an id, an email and a hash; `User`
|
|
69
|
+
* in shop is a name and an address - two entries is the correct answer, and
|
|
70
|
+
* the layout rules say so. What is worth seeing is that a reader crossing the
|
|
71
|
+
* boundary is now holding two meanings of one word, which no single glossary
|
|
72
|
+
* can tell them.
|
|
73
|
+
*/
|
|
74
|
+
export function homonyms(catalog: Catalog): Homonym[] {
|
|
75
|
+
const bySlug = new Map<string, Term[]>();
|
|
76
|
+
for (const term of allTerms(catalog)) {
|
|
77
|
+
const list = bySlug.get(term.slug) ?? [];
|
|
78
|
+
list.push(term);
|
|
79
|
+
bySlug.set(term.slug, list);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const out: Homonym[] = [];
|
|
83
|
+
for (const [slug, terms] of bySlug) {
|
|
84
|
+
// Two entries of one context cannot happen either - the merge reports it -
|
|
85
|
+
// so counting contexts rather than terms is the honest test of "the same
|
|
86
|
+
// word, twice".
|
|
87
|
+
if (new Set(terms.map((t) => t.context)).size < 2) continue;
|
|
88
|
+
out.push({
|
|
89
|
+
slug,
|
|
90
|
+
word: terms[0]?.name ?? slug,
|
|
91
|
+
terms: [...terms].sort((a, b) => a.context.localeCompare(b.context)),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return out.sort((a, b) => a.word.localeCompare(b.word));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The filter box: every word of the query has to appear somewhere in the
|
|
100
|
+
* entry, in any order.
|
|
101
|
+
*/
|
|
102
|
+
export function matchTerms(terms: Term[], query: string): Term[] {
|
|
103
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
104
|
+
if (words.length === 0) return terms;
|
|
105
|
+
|
|
106
|
+
return terms.filter((term) => {
|
|
107
|
+
const haystack =
|
|
108
|
+
`${term.name} ${term.slug} ${term.definition}`.toLowerCase();
|
|
109
|
+
return words.every((word) => haystack.includes(word));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
ADMONITION_KINDS,
|
|
4
|
+
ADMONITION_LABEL,
|
|
5
|
+
parseAdmonition,
|
|
6
|
+
} from "./admonition";
|
|
7
|
+
|
|
8
|
+
describe("GitHub admonitions", () => {
|
|
9
|
+
it("labels every kind it claims to know", () => {
|
|
10
|
+
for (const kind of ADMONITION_KINDS) {
|
|
11
|
+
expect(ADMONITION_LABEL[kind], kind).toBeTruthy();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("reads the marker and hands back the body", () => {
|
|
16
|
+
for (const kind of ADMONITION_KINDS) {
|
|
17
|
+
const marker = `[!${kind.toUpperCase()}]`;
|
|
18
|
+
expect(parseAdmonition(`${marker}\nMind this.`)).toEqual({
|
|
19
|
+
kind,
|
|
20
|
+
rest: "Mind this.",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("accepts a marker that stands alone", () => {
|
|
26
|
+
expect(parseAdmonition("[!NOTE]")).toEqual({ kind: "note", rest: "" });
|
|
27
|
+
expect(parseAdmonition("[!NOTE]\n")).toEqual({ kind: "note", rest: "" });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("tolerates the case and the trailing spaces a writer leaves behind", () => {
|
|
31
|
+
expect(parseAdmonition("[!Warning] \nCareful.")).toEqual({
|
|
32
|
+
kind: "warning",
|
|
33
|
+
rest: "Careful.",
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("keeps the rest of a multi-line body intact", () => {
|
|
38
|
+
expect(parseAdmonition("[!TIP]\nfirst\nsecond")?.rest).toBe(
|
|
39
|
+
"first\nsecond",
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("refuses anything that is not a bare marker line", () => {
|
|
44
|
+
expect(parseAdmonition("[!NOTE] on the same line")).toBeNull();
|
|
45
|
+
expect(parseAdmonition("[!HINT]\nnot a GitHub kind")).toBeNull();
|
|
46
|
+
expect(parseAdmonition("A quotation.")).toBeNull();
|
|
47
|
+
expect(parseAdmonition("text before\n[!NOTE]\nafter")).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// GitHub's alert syntax: a blockquote whose first line is a bare `[!NOTE]`
|
|
2
|
+
// marker. It turns up in any README written on GitHub, so Portolan renders the
|
|
3
|
+
// five kinds GitHub knows and leaves everything else a plain quotation - the
|
|
4
|
+
// same rule GitHub itself applies to `[!HINT]`.
|
|
5
|
+
|
|
6
|
+
export const ADMONITION_KINDS = [
|
|
7
|
+
"note",
|
|
8
|
+
"tip",
|
|
9
|
+
"important",
|
|
10
|
+
"warning",
|
|
11
|
+
"caution",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
export type AdmonitionKind = (typeof ADMONITION_KINDS)[number];
|
|
15
|
+
|
|
16
|
+
export const ADMONITION_LABEL: Record<AdmonitionKind, string> = {
|
|
17
|
+
note: "Note",
|
|
18
|
+
tip: "Tip",
|
|
19
|
+
important: "Important",
|
|
20
|
+
warning: "Warning",
|
|
21
|
+
caution: "Caution",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The marker owns its whole line: `[!NOTE] see below` is prose that happens to
|
|
26
|
+
* start with brackets, not an alert. Matching is case-insensitive, as it is in
|
|
27
|
+
* cmark-gfm, but the label always comes back canonical.
|
|
28
|
+
*/
|
|
29
|
+
const MARKER = /^\[!([a-z]+)\][^\S\n]*(?:\r?\n|$)/i;
|
|
30
|
+
|
|
31
|
+
function isKind(word: string): word is AdmonitionKind {
|
|
32
|
+
return (ADMONITION_KINDS as readonly string[]).includes(word);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads the marker off the first text of a blockquote. `rest` is what is left
|
|
37
|
+
* of that text once the marker line is gone - usually the alert's first
|
|
38
|
+
* sentence, empty when the marker stands alone.
|
|
39
|
+
*/
|
|
40
|
+
export function parseAdmonition(
|
|
41
|
+
text: string,
|
|
42
|
+
): { kind: AdmonitionKind; rest: string } | null {
|
|
43
|
+
const match = MARKER.exec(text);
|
|
44
|
+
if (!match) return null;
|
|
45
|
+
const kind = match[1]?.toLowerCase() ?? "";
|
|
46
|
+
if (!isKind(kind)) return null;
|
|
47
|
+
return { kind, rest: text.slice(match[0].length) };
|
|
48
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { catalog } from "../testing/estate";
|
|
4
|
+
import {
|
|
5
|
+
adrCoversService,
|
|
6
|
+
adrNumber,
|
|
7
|
+
adrsForService,
|
|
8
|
+
isCurrent,
|
|
9
|
+
newestAccepted,
|
|
10
|
+
sortAdrs,
|
|
11
|
+
} from "./adr";
|
|
12
|
+
import type { Adr } from "../catalog";
|
|
13
|
+
|
|
14
|
+
function byId(id: string): Adr {
|
|
15
|
+
const adr = catalog.adrs.find((a) => a.id === id);
|
|
16
|
+
if (!adr) throw new Error(`no adr ${id}`);
|
|
17
|
+
return adr;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("adrNumber", () => {
|
|
21
|
+
it("pads to the four digits people write in commits", () => {
|
|
22
|
+
expect(adrNumber(byId("org.0001"))).toBe("ADR-0001");
|
|
23
|
+
expect(adrNumber(byId("shop.oms.0007"))).toBe("ADR-0007");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("sortAdrs", () => {
|
|
28
|
+
it("puts the newest decision first", () => {
|
|
29
|
+
expect(sortAdrs(catalog.adrs).map((a) => a.id)).toEqual([
|
|
30
|
+
"shop.oms.0007",
|
|
31
|
+
"payments.0004",
|
|
32
|
+
"shop.oms.0003",
|
|
33
|
+
"org.0002",
|
|
34
|
+
"org.0001",
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("does not mutate its input", () => {
|
|
39
|
+
const before = catalog.adrs.map((a) => a.id);
|
|
40
|
+
sortAdrs(catalog.adrs);
|
|
41
|
+
expect(catalog.adrs.map((a) => a.id)).toEqual(before);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("isCurrent", () => {
|
|
46
|
+
it("is false for records that no longer hold", () => {
|
|
47
|
+
expect(isCurrent(byId("shop.oms.0003"))).toBe(false);
|
|
48
|
+
expect(isCurrent(byId("shop.oms.0007"))).toBe(true);
|
|
49
|
+
expect(isCurrent(byId("payments.0004"))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("adrCoversService", () => {
|
|
54
|
+
const org = byId("org.0002");
|
|
55
|
+
const context = byId("payments.0004");
|
|
56
|
+
const service = byId("shop.oms.0007");
|
|
57
|
+
const relates = byId("org.0001");
|
|
58
|
+
|
|
59
|
+
it("lets an org decision govern every service", () => {
|
|
60
|
+
expect(adrCoversService(org, "shop.pricing", "shop")).toBe(true);
|
|
61
|
+
expect(adrCoversService(org, "delivery.core", "delivery")).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("scopes a context decision to that context only", () => {
|
|
65
|
+
expect(adrCoversService(context, "payments.ledger", "payments")).toBe(true);
|
|
66
|
+
expect(adrCoversService(context, "shop.oms", "shop")).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("scopes a service decision to that service only", () => {
|
|
70
|
+
expect(adrCoversService(service, "shop.oms", "shop")).toBe(true);
|
|
71
|
+
expect(adrCoversService(service, "shop.pricing", "shop")).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("also covers services named in relates", () => {
|
|
75
|
+
expect(relates.scope.kind).toBe("org");
|
|
76
|
+
const narrowed: Adr = {
|
|
77
|
+
...relates,
|
|
78
|
+
scope: { kind: "context", context: "shop" },
|
|
79
|
+
};
|
|
80
|
+
expect(adrCoversService(narrowed, "delivery.core", "delivery")).toBe(true);
|
|
81
|
+
expect(adrCoversService(narrowed, "shop.pricing", "shop")).toBe(true);
|
|
82
|
+
expect(adrCoversService(narrowed, "payments.ledger", "payments")).toBe(
|
|
83
|
+
false,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("adrsForService", () => {
|
|
89
|
+
it("lists everything governing shop.oms, newest first", () => {
|
|
90
|
+
expect(
|
|
91
|
+
adrsForService(catalog, "shop.oms", "shop").map((a) => a.id),
|
|
92
|
+
).toEqual(["shop.oms.0007", "shop.oms.0003", "org.0002", "org.0001"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("gives shop.pricing only the org-wide decisions", () => {
|
|
96
|
+
expect(
|
|
97
|
+
adrsForService(catalog, "shop.pricing", "shop").map((a) => a.id),
|
|
98
|
+
).toEqual(["org.0002", "org.0001"]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("gives payments.ledger its own context's decision too", () => {
|
|
102
|
+
expect(
|
|
103
|
+
adrsForService(catalog, "payments.ledger", "payments").map((a) => a.id),
|
|
104
|
+
).toEqual(["payments.0004", "org.0002", "org.0001"]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("newestAccepted", () => {
|
|
109
|
+
it("skips proposed and superseded records", () => {
|
|
110
|
+
expect(newestAccepted(catalog, 5).map((a) => a.id)).toEqual([
|
|
111
|
+
"shop.oms.0007",
|
|
112
|
+
"org.0002",
|
|
113
|
+
"org.0001",
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("honours the limit", () => {
|
|
118
|
+
expect(newestAccepted(catalog, 1).map((a) => a.id)).toEqual([
|
|
119
|
+
"shop.oms.0007",
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("decision pages draw nothing from the current model", () => {
|
|
125
|
+
it("never reaches for a LikeC4 view", () => {
|
|
126
|
+
// A record is frozen history. Rendering a live view inside one would make
|
|
127
|
+
// it re-draw from a model that has moved on since the decision was taken.
|
|
128
|
+
const source = readFileSync("src/pages/AdrDetail.tsx", "utf8");
|
|
129
|
+
expect(source).not.toMatch(/LikeC4View|C4View|FlowView|likec4/);
|
|
130
|
+
});
|
|
131
|
+
});
|