@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,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"properties": {
|
|
5
|
+
"files": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": { "type": "string" },
|
|
8
|
+
"description": "The record files to read, as globs relative to the input root. A README.md among them is an index rather than a record and is skipped. Left out, every docs/adr/*.md.",
|
|
9
|
+
"examples": [["docs/adr/*.md"], ["adr/*.md"]]
|
|
10
|
+
},
|
|
11
|
+
"scope": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "What a record is about when it does not say so itself: \"org\", a context slug, or \"<context>.<service>\". A record written by adr-tools has no scope of its own and takes its id's prefix from here; a MADR record with a Scope bullet keeps its own.",
|
|
14
|
+
"examples": ["avia.aviasupp", "payments", "org"]
|
|
15
|
+
},
|
|
16
|
+
"history": {
|
|
17
|
+
"enum": ["git", "none"],
|
|
18
|
+
"description": "Where a record's author and dates come from. \"git\", the default, reads the file's commits: who first added it and who last touched it. \"none\" reads nothing, for a tree that is not a checkout or a fragment that must not change when the history does.",
|
|
19
|
+
"default": "git"
|
|
20
|
+
},
|
|
21
|
+
"out": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Name of the fragment file, inside the step's output directory. Left out, adr.json."
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"path"
|
|
6
|
+
"regexp"
|
|
7
|
+
"strconv"
|
|
8
|
+
"strings"
|
|
9
|
+
"time"
|
|
10
|
+
|
|
11
|
+
"github.com/shortlink-org/portolan/catalog"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// The format, in one place.
|
|
15
|
+
//
|
|
16
|
+
// # auth.0003 — Session expiry publishes no event
|
|
17
|
+
//
|
|
18
|
+
// - **Status:** accepted
|
|
19
|
+
// - **Date:** 2026-08-22
|
|
20
|
+
// - **Scope:** auth.auth
|
|
21
|
+
// - **Superseded by:** auth.0007 optional
|
|
22
|
+
// - **Supersedes:** auth.0001, auth.0002 optional
|
|
23
|
+
// - **Relates:** auth.auth.session.SessionEnded, shop.cart, checkout
|
|
24
|
+
// - **Note:** prose the catalog has no other field for
|
|
25
|
+
//
|
|
26
|
+
// ## Context and Problem Statement
|
|
27
|
+
// …
|
|
28
|
+
//
|
|
29
|
+
// The title carries the id and, after an em dash, the title itself. The id's
|
|
30
|
+
// prefix is whatever the record is about - a service (`auth`), a context
|
|
31
|
+
// (`payments`) or the organisation (`org`) - and the four digits after it are
|
|
32
|
+
// the record's number, which the file's own name repeats.
|
|
33
|
+
//
|
|
34
|
+
// The bullets are everything the markdown knows that the prose does not say
|
|
35
|
+
// in a form anything can read. Status, Date and Scope are required; the rest
|
|
36
|
+
// are written when there is something to write. `Relates` names events,
|
|
37
|
+
// services and flows in one list and they are told apart by their shape,
|
|
38
|
+
// because a record's author should not have to remember which of three lists
|
|
39
|
+
// a name belongs in.
|
|
40
|
+
//
|
|
41
|
+
// Everything from the first `##` onward is the record. It is frozen history:
|
|
42
|
+
// it goes into the catalog exactly as written and comes out onto the page the
|
|
43
|
+
// same way, and nothing in it is ever regenerated from the model as it stands
|
|
44
|
+
// now.
|
|
45
|
+
//
|
|
46
|
+
// The other format read here is the one adr-tools writes, because a tree of
|
|
47
|
+
// records kept that way for years should not have to be retyped to be read:
|
|
48
|
+
//
|
|
49
|
+
// # 2. Integration with external suppliers
|
|
50
|
+
//
|
|
51
|
+
// Date: 2024-09-04
|
|
52
|
+
//
|
|
53
|
+
// ## Status
|
|
54
|
+
//
|
|
55
|
+
// Superseded by [5. Use schemas](0005-use-schemas.md)
|
|
56
|
+
//
|
|
57
|
+
// ## Context
|
|
58
|
+
// …
|
|
59
|
+
//
|
|
60
|
+
// The title carries the number and the title; the id's prefix, which the
|
|
61
|
+
// format has no place for, comes from the step's scope option, as does the
|
|
62
|
+
// scope itself. The date is a line of its own above the record, and the status
|
|
63
|
+
// is the first section of the record, where adr-tools writes it - a word, or
|
|
64
|
+
// "Superseded by" and a link to the record that replaced this one, and
|
|
65
|
+
// "Supersedes" and a link the other way. The record is the same as above:
|
|
66
|
+
// everything from the first `##`, the status section included.
|
|
67
|
+
|
|
68
|
+
var (
|
|
69
|
+
titleLine = regexp.MustCompile(`^#\s+(\S+)\s+—\s+(.+?)\s*$`)
|
|
70
|
+
toolsTitle = regexp.MustCompile(`^#\s+(\d+)\.\s+(.+?)\s*$`)
|
|
71
|
+
plainTitle = regexp.MustCompile(`^#\s+(.+?)\s*$`)
|
|
72
|
+
toolsDate = regexp.MustCompile(`^Date:\s*(.+?)\s*$`)
|
|
73
|
+
toolsLink = regexp.MustCompile(`\[\s*(\d+)\.[^\]]*\]\([^)]*\)`)
|
|
74
|
+
toolsBare = regexp.MustCompile(`(?i)^(?:adr[-\s]?)?0*(\d+)$`)
|
|
75
|
+
statusHead = regexp.MustCompile(`^##\s+Status\s*$`)
|
|
76
|
+
sectionAny = regexp.MustCompile(`^#{1,2}\s`)
|
|
77
|
+
bulletLine = regexp.MustCompile(`^-\s+\*\*([^*:]+):\*\*\s*(.*?)\s*$`)
|
|
78
|
+
bodyStart = regexp.MustCompile(`^##\s`)
|
|
79
|
+
fileName = regexp.MustCompile(`^(\d{4})-([a-z0-9]+(?:-[a-z0-9]+)*)$`)
|
|
80
|
+
adrID = regexp.MustCompile(`^([a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*)\.(\d+)$`)
|
|
81
|
+
scopeValue = regexp.MustCompile(`^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)?$`)
|
|
82
|
+
flowSlug = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
|
|
83
|
+
eventID = regexp.MustCompile(`^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[A-Za-z][A-Za-z0-9]*$`)
|
|
84
|
+
serviceID = regexp.MustCompile(`^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$`)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
var statuses = map[string]catalog.AdrStatus{
|
|
88
|
+
"proposed": catalog.AdrProposed,
|
|
89
|
+
"accepted": catalog.AdrAccepted,
|
|
90
|
+
"superseded": catalog.AdrSuperseded,
|
|
91
|
+
"deprecated": catalog.AdrDeprecated,
|
|
92
|
+
"rejected": catalog.AdrRejected,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// defaults are what the manifest says about a tree of records, for the
|
|
96
|
+
// records that do not say it themselves.
|
|
97
|
+
type defaults struct {
|
|
98
|
+
// Scope is what a record is about when it has no Scope of its own: "org",
|
|
99
|
+
// a context, or "<context>.<service>". It also lends an adr-tools record
|
|
100
|
+
// the prefix of its id, which is the last segment: the service, the
|
|
101
|
+
// context, or "org".
|
|
102
|
+
Scope string
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type parser struct {
|
|
106
|
+
file string
|
|
107
|
+
lines []string
|
|
108
|
+
errs []string
|
|
109
|
+
d defaults
|
|
110
|
+
|
|
111
|
+
// tools is set when the title is the one adr-tools writes, and picks the
|
|
112
|
+
// way the rest of the file is read.
|
|
113
|
+
tools bool
|
|
114
|
+
|
|
115
|
+
adr catalog.Adr
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// parseAdr reads one record. Every mistake it can find is collected rather
|
|
119
|
+
// than returned at the first one, because a file with two typos in its header
|
|
120
|
+
// should be fixed once.
|
|
121
|
+
func parseAdr(file, src string, d defaults) (catalog.Adr, []string) {
|
|
122
|
+
p := &parser{
|
|
123
|
+
file: file,
|
|
124
|
+
lines: strings.Split(strings.ReplaceAll(src, "\r\n", "\n"), "\n"),
|
|
125
|
+
d: d,
|
|
126
|
+
}
|
|
127
|
+
p.read()
|
|
128
|
+
if len(p.errs) > 0 {
|
|
129
|
+
return catalog.Adr{}, p.errs
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return p.adr, nil
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func (p *parser) fail(line int, msg string) {
|
|
136
|
+
p.errs = append(p.errs, p.file+":"+strconv.Itoa(line+1)+": "+msg)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func (p *parser) read() {
|
|
140
|
+
p.adr.Source = p.file
|
|
141
|
+
p.adr.Relates = catalog.AdrRelates{}
|
|
142
|
+
|
|
143
|
+
head := p.title()
|
|
144
|
+
if head < 0 {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
p.checkFileName()
|
|
148
|
+
|
|
149
|
+
body := -1
|
|
150
|
+
if p.tools {
|
|
151
|
+
body = p.toolsMeta(head + 1)
|
|
152
|
+
} else {
|
|
153
|
+
body = p.meta(head + 1)
|
|
154
|
+
}
|
|
155
|
+
if body < 0 {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
p.adr.Body = strings.TrimSpace(strings.Join(p.lines[body:], "\n")) + "\n"
|
|
159
|
+
if p.tools {
|
|
160
|
+
p.toolsStatus(body)
|
|
161
|
+
p.checkSupersession(head)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// title reads the one line the record has to open with and answers with its
|
|
166
|
+
// index, or -1 when there is nothing to go on.
|
|
167
|
+
func (p *parser) title() int {
|
|
168
|
+
for i, line := range p.lines {
|
|
169
|
+
if strings.TrimSpace(line) == "" {
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
match := titleLine.FindStringSubmatch(line)
|
|
174
|
+
if match == nil {
|
|
175
|
+
if tools := toolsTitle.FindStringSubmatch(line); tools != nil {
|
|
176
|
+
return p.toolsTitleLine(i, tools)
|
|
177
|
+
}
|
|
178
|
+
// A title with no number at all is numbered by its file, unless
|
|
179
|
+
// it opens with something shaped like an id: that is a MADR
|
|
180
|
+
// title with the wrong dash, and reading it as a plain one would
|
|
181
|
+
// turn a typo into a record under another name.
|
|
182
|
+
if plain := plainTitle.FindStringSubmatch(line); plain != nil && !adrID.MatchString(strings.Fields(plain[1])[0]) {
|
|
183
|
+
return p.plainTitleLine(i, plain[1])
|
|
184
|
+
}
|
|
185
|
+
p.fail(i, `a record opens with "# <id> — <title>", an em dash between the two, or with "# <n>. <title>" as adr-tools writes it`)
|
|
186
|
+
|
|
187
|
+
return -1
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
id := adrID.FindStringSubmatch(match[1])
|
|
191
|
+
if id == nil {
|
|
192
|
+
p.fail(i, "the id "+strconv.Quote(match[1])+` is not a prefix and a number, as in "auth.0003"`)
|
|
193
|
+
|
|
194
|
+
return -1
|
|
195
|
+
}
|
|
196
|
+
number, err := strconv.Atoi(id[2])
|
|
197
|
+
if err != nil || fmt.Sprintf("%04d", number) != id[2] {
|
|
198
|
+
// The app fails the whole catalog on an id that does not end with
|
|
199
|
+
// its own number, so the digits are the number and are written
|
|
200
|
+
// the one way that round-trips.
|
|
201
|
+
p.fail(i, "the number in "+strconv.Quote(match[1])+" is not four padded digits")
|
|
202
|
+
|
|
203
|
+
return -1
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
p.adr.ID = match[1]
|
|
207
|
+
p.adr.Number = number
|
|
208
|
+
p.adr.Title = strings.TrimSpace(match[2])
|
|
209
|
+
|
|
210
|
+
return i
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
p.fail(0, "the file is empty")
|
|
214
|
+
|
|
215
|
+
return -1
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// toolsTitleLine reads the title adr-tools writes: the record's number, a
|
|
219
|
+
// full stop, the title. The id is built from the number and the prefix the
|
|
220
|
+
// scope lends, because the format keeps no id of its own.
|
|
221
|
+
func (p *parser) toolsTitleLine(i int, match []string) int {
|
|
222
|
+
number, err := strconv.Atoi(match[1])
|
|
223
|
+
if err != nil || number <= 0 {
|
|
224
|
+
p.fail(i, "the number "+strconv.Quote(match[1])+" is not a record's number")
|
|
225
|
+
|
|
226
|
+
return -1
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
p.tools = true
|
|
230
|
+
p.adr.ID = p.toolsID(number)
|
|
231
|
+
p.adr.Number = number
|
|
232
|
+
p.adr.Title = strings.TrimSpace(match[2])
|
|
233
|
+
|
|
234
|
+
return i
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// plainTitleLine reads a title that carries no number, which adr-tools trees
|
|
238
|
+
// collect when a record is written by hand: the file's own name numbers it,
|
|
239
|
+
// and the heading is the title.
|
|
240
|
+
func (p *parser) plainTitleLine(i int, title string) int {
|
|
241
|
+
match := fileName.FindStringSubmatch(strings.TrimSuffix(path.Base(p.file), ".md"))
|
|
242
|
+
if match == nil {
|
|
243
|
+
p.fail(i, `the title carries no number and the file is not named "NNNN-kebab-slug.md", so nothing numbers the record`)
|
|
244
|
+
|
|
245
|
+
return -1
|
|
246
|
+
}
|
|
247
|
+
number, _ := strconv.Atoi(match[1])
|
|
248
|
+
if number <= 0 {
|
|
249
|
+
p.fail(i, "the file is numbered "+match[1]+", which is not a record's number")
|
|
250
|
+
|
|
251
|
+
return -1
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
p.tools = true
|
|
255
|
+
p.adr.ID = p.toolsID(number)
|
|
256
|
+
p.adr.Number = number
|
|
257
|
+
p.adr.Title = strings.TrimSpace(title)
|
|
258
|
+
|
|
259
|
+
return i
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// toolsID is the id an adr-tools record gets: the prefix the scope lends and
|
|
263
|
+
// the number, padded the one way that round-trips.
|
|
264
|
+
func (p *parser) toolsID(number int) string {
|
|
265
|
+
return p.prefix() + "." + fmt.Sprintf("%04d", number)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// prefix is what an id opens with, taken from the scope the way the MADR
|
|
269
|
+
// records of the estate do it: the service for a service, the context for a
|
|
270
|
+
// context, and "org" for the organisation.
|
|
271
|
+
func (p *parser) prefix() string {
|
|
272
|
+
scope := strings.TrimSpace(p.d.Scope)
|
|
273
|
+
if scope == "" || scope == "org" {
|
|
274
|
+
return "org"
|
|
275
|
+
}
|
|
276
|
+
if _, service, ok := strings.Cut(scope, "."); ok {
|
|
277
|
+
return service
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return scope
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// toolsMeta reads what adr-tools keeps above the record - the date, on a line
|
|
284
|
+
// of its own - and answers with the index the record starts at, or -1 when
|
|
285
|
+
// there is none.
|
|
286
|
+
func (p *parser) toolsMeta(from int) int {
|
|
287
|
+
for i := from; i < len(p.lines); i++ {
|
|
288
|
+
line := p.lines[i]
|
|
289
|
+
switch {
|
|
290
|
+
case bodyStart.MatchString(line):
|
|
291
|
+
if p.adr.Date == "" {
|
|
292
|
+
p.fail(from-1, `the record says no "Date:"`)
|
|
293
|
+
}
|
|
294
|
+
p.scope(from-1, p.d.Scope)
|
|
295
|
+
|
|
296
|
+
return i
|
|
297
|
+
|
|
298
|
+
case strings.TrimSpace(line) == "":
|
|
299
|
+
|
|
300
|
+
case toolsDate.MatchString(line):
|
|
301
|
+
value := toolsDate.FindStringSubmatch(line)[1]
|
|
302
|
+
if _, err := time.Parse("2006-01-02", value); err != nil {
|
|
303
|
+
p.fail(i, strconv.Quote(value)+" is not a date, as in 2026-08-22")
|
|
304
|
+
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
p.adr.Date = value
|
|
308
|
+
|
|
309
|
+
default:
|
|
310
|
+
p.fail(i, "only \"Date:\" belongs between the title and the first `##`")
|
|
311
|
+
|
|
312
|
+
return -1
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
p.fail(len(p.lines)-1, "the record has no body: nothing here is under a `##`")
|
|
317
|
+
|
|
318
|
+
return -1
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// toolsStatus reads the status from the record's own Status section, which is
|
|
322
|
+
// where adr-tools keeps it. Each non-empty line there is one statement: a
|
|
323
|
+
// status word, or "Superseded by" or "Supersedes" and the record it means. A
|
|
324
|
+
// later statement wins over an earlier one, because a record that was accepted
|
|
325
|
+
// and then superseded has both written down, in that order.
|
|
326
|
+
func (p *parser) toolsStatus(body int) {
|
|
327
|
+
head := -1
|
|
328
|
+
for i := body; i < len(p.lines); i++ {
|
|
329
|
+
if statusHead.MatchString(p.lines[i]) {
|
|
330
|
+
head = i
|
|
331
|
+
|
|
332
|
+
break
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if head < 0 {
|
|
336
|
+
p.fail(body, "the record has no `## Status` section")
|
|
337
|
+
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
statements := 0
|
|
342
|
+
for i := head + 1; i < len(p.lines) && !sectionAny.MatchString(p.lines[i]); i++ {
|
|
343
|
+
statement := strings.TrimSpace(p.lines[i])
|
|
344
|
+
if statement == "" {
|
|
345
|
+
continue
|
|
346
|
+
}
|
|
347
|
+
statements++
|
|
348
|
+
p.toolsStatement(i, statement)
|
|
349
|
+
}
|
|
350
|
+
if statements == 0 {
|
|
351
|
+
p.fail(head, "the Status section says nothing")
|
|
352
|
+
}
|
|
353
|
+
if p.adr.Status == "" && len(p.errs) == 0 {
|
|
354
|
+
p.fail(head, "the Status section names no status: proposed, accepted, superseded, deprecated or rejected")
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
func (p *parser) toolsStatement(at int, statement string) {
|
|
359
|
+
lower := strings.ToLower(strings.TrimRight(statement, ". "))
|
|
360
|
+
if status, ok := statuses[lower]; ok {
|
|
361
|
+
p.adr.Status = status
|
|
362
|
+
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
switch {
|
|
367
|
+
case strings.HasPrefix(lower, "superseded by"):
|
|
368
|
+
ids := p.toolsRecords(statement[len("superseded by"):])
|
|
369
|
+
if len(ids) != 1 {
|
|
370
|
+
p.fail(at, "the record is superseded and says by which one record")
|
|
371
|
+
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
p.adr.Status = catalog.AdrSuperseded
|
|
375
|
+
p.adr.SupersededBy = ids[0]
|
|
376
|
+
|
|
377
|
+
case strings.HasPrefix(lower, "supersedes"):
|
|
378
|
+
ids := p.toolsRecords(statement[len("supersedes"):])
|
|
379
|
+
if len(ids) == 0 {
|
|
380
|
+
p.fail(at, "the record supersedes something and says which record")
|
|
381
|
+
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
p.adr.Supersedes = append(p.adr.Supersedes, ids...)
|
|
385
|
+
|
|
386
|
+
case strings.HasPrefix(lower, "amends"), strings.HasPrefix(lower, "amended by"):
|
|
387
|
+
// An amendment is a fact the catalog has no field for. It stays in the
|
|
388
|
+
// body, which is on the page as written.
|
|
389
|
+
|
|
390
|
+
default:
|
|
391
|
+
p.fail(at, strconv.Quote(statement)+` is not a status: proposed, accepted, superseded, deprecated or rejected, or "Superseded by" a record`)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// toolsRecords finds the records a statement names, as the links adr-tools
|
|
396
|
+
// writes - "[3. Use DTO](0003-use-dto.md)" - or as bare numbers.
|
|
397
|
+
func (p *parser) toolsRecords(rest string) []string {
|
|
398
|
+
var ids []string
|
|
399
|
+
for _, match := range toolsLink.FindAllStringSubmatch(rest, -1) {
|
|
400
|
+
number, err := strconv.Atoi(match[1])
|
|
401
|
+
if err != nil {
|
|
402
|
+
continue
|
|
403
|
+
}
|
|
404
|
+
ids = append(ids, p.toolsID(number))
|
|
405
|
+
}
|
|
406
|
+
if len(ids) > 0 {
|
|
407
|
+
return ids
|
|
408
|
+
}
|
|
409
|
+
for _, word := range strings.Fields(strings.NewReplacer(",", " ", "and", " ").Replace(rest)) {
|
|
410
|
+
if match := toolsBare.FindStringSubmatch(word); match != nil {
|
|
411
|
+
number, _ := strconv.Atoi(match[1])
|
|
412
|
+
ids = append(ids, p.toolsID(number))
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return ids
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// checkFileName ties the record to the file it is in. The slug is built from
|
|
420
|
+
// both - the id says which record this is, the file's name says what it was
|
|
421
|
+
// about - so a file renamed away from its record would silently change the
|
|
422
|
+
// URL of a decision somebody linked to.
|
|
423
|
+
func (p *parser) checkFileName() {
|
|
424
|
+
base := strings.TrimSuffix(path.Base(p.file), ".md")
|
|
425
|
+
|
|
426
|
+
match := fileName.FindStringSubmatch(base)
|
|
427
|
+
if match == nil {
|
|
428
|
+
p.fail(0, "the file is named "+strconv.Quote(base+".md")+`, not "NNNN-kebab-slug.md"`)
|
|
429
|
+
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
if number, _ := strconv.Atoi(match[1]); number != p.adr.Number {
|
|
433
|
+
p.fail(0, "the file is numbered "+match[1]+" and the record inside it is "+p.adr.ID)
|
|
434
|
+
|
|
435
|
+
return
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
p.adr.Slug = strings.ReplaceAll(p.adr.ID, ".", "-") + "-" + match[2]
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// meta reads the bullets between the title and the record, and answers with
|
|
442
|
+
// the index the record starts at, or -1 when there is none.
|
|
443
|
+
func (p *parser) meta(from int) int {
|
|
444
|
+
seen := map[string]bool{}
|
|
445
|
+
key, value, at := "", "", 0
|
|
446
|
+
flush := func() {
|
|
447
|
+
if key != "" {
|
|
448
|
+
p.bullet(at, key, value)
|
|
449
|
+
}
|
|
450
|
+
key, value = "", ""
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
for i := from; i < len(p.lines); i++ {
|
|
454
|
+
line := p.lines[i]
|
|
455
|
+
switch {
|
|
456
|
+
case bodyStart.MatchString(line):
|
|
457
|
+
flush()
|
|
458
|
+
p.require(from-1, seen)
|
|
459
|
+
|
|
460
|
+
return i
|
|
461
|
+
|
|
462
|
+
case strings.TrimSpace(line) == "":
|
|
463
|
+
flush()
|
|
464
|
+
|
|
465
|
+
case bulletLine.MatchString(line):
|
|
466
|
+
flush()
|
|
467
|
+
match := bulletLine.FindStringSubmatch(line)
|
|
468
|
+
key, value, at = strings.TrimSpace(match[1]), match[2], i
|
|
469
|
+
if seen[key] {
|
|
470
|
+
p.fail(i, strconv.Quote(key)+" is written twice")
|
|
471
|
+
}
|
|
472
|
+
seen[key] = true
|
|
473
|
+
|
|
474
|
+
case key != "" && (strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t")):
|
|
475
|
+
// A bullet wraps onto the next line, indented under itself. The
|
|
476
|
+
// break is the author's line width and means nothing, so it
|
|
477
|
+
// closes up into a space.
|
|
478
|
+
value = strings.TrimSpace(value + " " + strings.TrimSpace(line))
|
|
479
|
+
|
|
480
|
+
default:
|
|
481
|
+
p.fail(i, "only meta bullets belong between the title and the first `##`")
|
|
482
|
+
|
|
483
|
+
return -1
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
flush()
|
|
488
|
+
p.fail(len(p.lines)-1, "the record has no body: nothing here is under a `##`")
|
|
489
|
+
|
|
490
|
+
return -1
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// require reports the three bullets a record cannot be read without. They are
|
|
494
|
+
// reported against the title rather than against the missing line, because
|
|
495
|
+
// there is no missing line to point at.
|
|
496
|
+
func (p *parser) require(at int, seen map[string]bool) {
|
|
497
|
+
for _, key := range []string{"Status", "Date"} {
|
|
498
|
+
if !seen[key] {
|
|
499
|
+
p.fail(at, "the record says no "+strconv.Quote(key))
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// A record that does not say what it is about is about what the step says
|
|
503
|
+
// its tree is about; only when the step says nothing either is that a
|
|
504
|
+
// mistake.
|
|
505
|
+
switch {
|
|
506
|
+
case seen["Scope"]:
|
|
507
|
+
case strings.TrimSpace(p.d.Scope) != "":
|
|
508
|
+
p.scope(at, p.d.Scope)
|
|
509
|
+
default:
|
|
510
|
+
p.fail(at, `the record says no "Scope"`)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
p.checkSupersession(at)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// checkSupersession holds the two things a record says about being replaced
|
|
517
|
+
// against each other. Supersession is a two-way fact and half of it recorded
|
|
518
|
+
// is a bug. The other half - that the record named actually names this one
|
|
519
|
+
// back - can only be checked once every record is read, so it is checked
|
|
520
|
+
// there.
|
|
521
|
+
func (p *parser) checkSupersession(at int) {
|
|
522
|
+
if p.adr.Status == catalog.AdrSuperseded && p.adr.SupersededBy == "" {
|
|
523
|
+
p.fail(at, "the record is superseded and says by what")
|
|
524
|
+
}
|
|
525
|
+
if p.adr.SupersededBy != "" && p.adr.Status != catalog.AdrSuperseded {
|
|
526
|
+
p.fail(at, "the record is superseded by "+p.adr.SupersededBy+` but its status is "`+string(p.adr.Status)+`"`)
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
func (p *parser) bullet(at int, key, value string) {
|
|
531
|
+
switch key {
|
|
532
|
+
case "Status":
|
|
533
|
+
status, ok := statuses[value]
|
|
534
|
+
if !ok {
|
|
535
|
+
p.fail(at, strconv.Quote(value)+" is not a status: proposed, accepted, superseded, deprecated or rejected")
|
|
536
|
+
|
|
537
|
+
return
|
|
538
|
+
}
|
|
539
|
+
p.adr.Status = status
|
|
540
|
+
|
|
541
|
+
case "Date":
|
|
542
|
+
if _, err := time.Parse("2006-01-02", value); err != nil {
|
|
543
|
+
p.fail(at, strconv.Quote(value)+" is not a date, as in 2026-08-22")
|
|
544
|
+
|
|
545
|
+
return
|
|
546
|
+
}
|
|
547
|
+
p.adr.Date = value
|
|
548
|
+
|
|
549
|
+
case "Scope":
|
|
550
|
+
p.scope(at, value)
|
|
551
|
+
|
|
552
|
+
case "Superseded by":
|
|
553
|
+
if ids := p.ids(at, key, value); len(ids) > 1 {
|
|
554
|
+
p.fail(at, "a record is superseded by one record, not "+strconv.Itoa(len(ids)))
|
|
555
|
+
} else if len(ids) == 1 {
|
|
556
|
+
p.adr.SupersededBy = ids[0]
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
case "Supersedes":
|
|
560
|
+
p.adr.Supersedes = p.ids(at, key, value)
|
|
561
|
+
|
|
562
|
+
case "Relates":
|
|
563
|
+
p.relates(at, value)
|
|
564
|
+
|
|
565
|
+
case "Note":
|
|
566
|
+
p.adr.Note = value
|
|
567
|
+
|
|
568
|
+
default:
|
|
569
|
+
p.fail(at, strconv.Quote(key)+" is not one of the bullets a record carries")
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// scope says what the record is about, and the number of segments says which
|
|
574
|
+
// of the three kinds it is: none for the organisation, one for a context, two
|
|
575
|
+
// for a service. Whether the thing named exists is not a question this side
|
|
576
|
+
// can answer - an extractor sees one service's tree - so it is left to the
|
|
577
|
+
// validator, which sees the merged catalog.
|
|
578
|
+
func (p *parser) scope(at int, value string) {
|
|
579
|
+
value = strings.TrimSpace(value)
|
|
580
|
+
if value == "" || value == "org" {
|
|
581
|
+
p.adr.Scope = catalog.AdrScope{Kind: "org"}
|
|
582
|
+
|
|
583
|
+
return
|
|
584
|
+
}
|
|
585
|
+
if !scopeValue.MatchString(value) {
|
|
586
|
+
p.fail(at, strconv.Quote(value)+` is not a scope: "org", a context, or "<context>.<service>"`)
|
|
587
|
+
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
if strings.Contains(value, ".") {
|
|
591
|
+
p.adr.Scope = catalog.AdrScope{Kind: "service", Service: value}
|
|
592
|
+
|
|
593
|
+
return
|
|
594
|
+
}
|
|
595
|
+
p.adr.Scope = catalog.AdrScope{Kind: "context", Context: value}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// relates names events, services and flows in one comma-separated list and
|
|
599
|
+
// they are told apart by their shape: a flow by its slug, which has no dots; a
|
|
600
|
+
// service by `<context>.<service>`; an event by the aggregate and Name after
|
|
601
|
+
// that. A name matching none of the three is a typo, and reported here rather
|
|
602
|
+
// than left to come back from the validator as a reference to something that
|
|
603
|
+
// does not exist.
|
|
604
|
+
func (p *parser) relates(at int, value string) {
|
|
605
|
+
for _, id := range split(value) {
|
|
606
|
+
switch {
|
|
607
|
+
case eventID.MatchString(id):
|
|
608
|
+
p.adr.Relates.Events = append(p.adr.Relates.Events, id)
|
|
609
|
+
case serviceID.MatchString(id):
|
|
610
|
+
p.adr.Relates.Services = append(p.adr.Relates.Services, id)
|
|
611
|
+
case flowSlug.MatchString(id):
|
|
612
|
+
p.adr.Relates.Flows = append(p.adr.Relates.Flows, id)
|
|
613
|
+
default:
|
|
614
|
+
p.fail(at, strconv.Quote(id)+" is not the shape of an event, a service or a flow")
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
func (p *parser) ids(at int, key, value string) []string {
|
|
620
|
+
var ids []string
|
|
621
|
+
for _, id := range split(value) {
|
|
622
|
+
if !adrID.MatchString(id) {
|
|
623
|
+
p.fail(at, strconv.Quote(id)+" in "+strconv.Quote(key)+" is not a record's id")
|
|
624
|
+
|
|
625
|
+
continue
|
|
626
|
+
}
|
|
627
|
+
ids = append(ids, id)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return ids
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
func split(value string) []string {
|
|
634
|
+
var out []string
|
|
635
|
+
for _, part := range strings.Split(value, ",") {
|
|
636
|
+
if part = strings.TrimSpace(part); part != "" {
|
|
637
|
+
out = append(out, part)
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return out
|
|
642
|
+
}
|