@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,357 @@
|
|
|
1
|
+
// Composes the JSON Schema for portolan.json out of what the plugins say about
|
|
2
|
+
// themselves.
|
|
3
|
+
//
|
|
4
|
+
// node scripts/schema.mjs write schema/portolan.schema.json
|
|
5
|
+
// node scripts/schema.mjs --check fail if the schema on disk is not what
|
|
6
|
+
// the plugins describe now
|
|
7
|
+
//
|
|
8
|
+
// The manifest tells each plugin the things its source cannot carry - which
|
|
9
|
+
// bounded context a Go module belongs to, whose proto package a client calls.
|
|
10
|
+
// Only the plugin knows what it can be told, so it is asked, and the answers
|
|
11
|
+
// are composed into one document keyed by the name the manifest declares each
|
|
12
|
+
// plugin under. An editor reads it while the file is being written; `gen` reads
|
|
13
|
+
// it before running anything.
|
|
14
|
+
//
|
|
15
|
+
// The result is committed, which is what keeps `gen` from having to ask four
|
|
16
|
+
// plugins the same question on every run. `--check` in CI is what keeps the
|
|
17
|
+
// committed copy honest.
|
|
18
|
+
|
|
19
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
|
|
21
|
+
import { describePlugin } from "./plugin-host.mjs";
|
|
22
|
+
|
|
23
|
+
const OUT = "schema/portolan.schema.json";
|
|
24
|
+
|
|
25
|
+
const check = process.argv.includes("--check");
|
|
26
|
+
const manifest = JSON.parse(readFileSync("portolan.json", "utf8"));
|
|
27
|
+
|
|
28
|
+
const described = new Map();
|
|
29
|
+
for (const plugin of manifest.plugins ?? []) {
|
|
30
|
+
let descriptor;
|
|
31
|
+
try {
|
|
32
|
+
descriptor = await describePlugin(plugin);
|
|
33
|
+
} catch (cause) {
|
|
34
|
+
fail(`plugin ${plugin.name} could not be asked what it is: ${cause.message}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!descriptor) {
|
|
38
|
+
// A plugin from before this part of the protocol existed. Naming it in the
|
|
39
|
+
// manifest is not an error; it just means its options are the one thing
|
|
40
|
+
// here nothing can check.
|
|
41
|
+
console.warn(
|
|
42
|
+
` ${plugin.name} does not describe itself, so its options are not checked`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
described.set(plugin.name, descriptor);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const composed = `${JSON.stringify(compose(), null, 2)}\n`;
|
|
50
|
+
|
|
51
|
+
let current = null;
|
|
52
|
+
try {
|
|
53
|
+
current = readFileSync(OUT, "utf8");
|
|
54
|
+
} catch {
|
|
55
|
+
// Absent, which is the same as out of date.
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (current === composed) {
|
|
59
|
+
console.log(`${OUT}: up to date`);
|
|
60
|
+
} else if (check) {
|
|
61
|
+
console.error(
|
|
62
|
+
`${OUT} is ${current === null ? "missing" : "out of date"}. Run \`npm run schema\`.`,
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
} else {
|
|
66
|
+
mkdirSync("schema", { recursive: true });
|
|
67
|
+
writeFileSync(OUT, composed);
|
|
68
|
+
console.log(`${OUT}: written from ${described.size} plugin${described.size === 1 ? "" : "s"}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function compose() {
|
|
72
|
+
const defs = {};
|
|
73
|
+
for (const [name, descriptor] of described) {
|
|
74
|
+
if (descriptor?.options) defs[optionsKey(name)] = descriptor.options;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
79
|
+
$id: "https://github.com/shortlink-org/portolan/schema/portolan.schema.json",
|
|
80
|
+
title: "portolan manifest",
|
|
81
|
+
description:
|
|
82
|
+
"Where the facts come from, what produces them, and what turns them into something to read. Generated by scripts/schema.mjs from the plugins this manifest declares; run `npm run schema` after changing them.",
|
|
83
|
+
type: "object",
|
|
84
|
+
additionalProperties: false,
|
|
85
|
+
required: ["sources"],
|
|
86
|
+
properties: {
|
|
87
|
+
$schema: { type: "string" },
|
|
88
|
+
$comment: { type: "string" },
|
|
89
|
+
sources: {
|
|
90
|
+
type: "array",
|
|
91
|
+
minItems: 1,
|
|
92
|
+
items: { type: "string" },
|
|
93
|
+
description:
|
|
94
|
+
"Globs of the catalog fragments that make up the estate. They are merged, then validated as a union, so a fragment naming a peer it does not own is normal rather than broken.",
|
|
95
|
+
},
|
|
96
|
+
defaultCatalog: {
|
|
97
|
+
type: "string",
|
|
98
|
+
minLength: 1,
|
|
99
|
+
pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
100
|
+
description: "Catalog profile selected when the URL does not name one.",
|
|
101
|
+
},
|
|
102
|
+
catalogs: {
|
|
103
|
+
type: "array",
|
|
104
|
+
minItems: 1,
|
|
105
|
+
items: { $ref: "#/$defs/catalogProfile" },
|
|
106
|
+
description:
|
|
107
|
+
"Independent presentations of the available fragments. Each profile is merged, enriched and validated on its own.",
|
|
108
|
+
},
|
|
109
|
+
projects: {
|
|
110
|
+
type: "array",
|
|
111
|
+
items: { $ref: "#/$defs/project" },
|
|
112
|
+
description:
|
|
113
|
+
"The source projects that make up the estate. A project gives repeated pipeline inputs one name for the generated site's Settings page; estate-wide inputs such as flows need no project.",
|
|
114
|
+
},
|
|
115
|
+
plugins: {
|
|
116
|
+
type: "array",
|
|
117
|
+
items: { $ref: "#/$defs/plugin" },
|
|
118
|
+
description: "How to run each plugin. A step names one by the name declared here.",
|
|
119
|
+
},
|
|
120
|
+
extract: {
|
|
121
|
+
type: "array",
|
|
122
|
+
items: step("extract"),
|
|
123
|
+
description: "Steps that read source and write catalog fragments. They run first, before there is a catalog.",
|
|
124
|
+
},
|
|
125
|
+
verify: {
|
|
126
|
+
type: "array",
|
|
127
|
+
items: step("verify"),
|
|
128
|
+
description:
|
|
129
|
+
"Steps that read something observed - traces, a test's record - and answer with a fragment about the catalog somebody else declared.",
|
|
130
|
+
},
|
|
131
|
+
generate: {
|
|
132
|
+
type: "array",
|
|
133
|
+
items: step("generate"),
|
|
134
|
+
description: "Steps that read the merged catalog and write something to read.",
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
$defs: {
|
|
138
|
+
catalogProfile: {
|
|
139
|
+
type: "object",
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
required: ["id", "title", "sources", "contexts", "projects"],
|
|
142
|
+
properties: {
|
|
143
|
+
id: {
|
|
144
|
+
type: "string",
|
|
145
|
+
minLength: 1,
|
|
146
|
+
pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
147
|
+
description: "Stable profile id used in URLs and generator steps.",
|
|
148
|
+
},
|
|
149
|
+
title: { type: "string", minLength: 1 },
|
|
150
|
+
sources: {
|
|
151
|
+
type: "array",
|
|
152
|
+
minItems: 1,
|
|
153
|
+
items: { type: "string" },
|
|
154
|
+
description: "Catalog fragment globs merged for this profile.",
|
|
155
|
+
},
|
|
156
|
+
contexts: {
|
|
157
|
+
type: "array",
|
|
158
|
+
minItems: 1,
|
|
159
|
+
items: { type: "string", minLength: 1 },
|
|
160
|
+
description: "Top-level catalog groups visible in this profile.",
|
|
161
|
+
},
|
|
162
|
+
projects: {
|
|
163
|
+
type: "array",
|
|
164
|
+
items: { type: "string", minLength: 1 },
|
|
165
|
+
description: "Project ids belonging to this profile.",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
project: {
|
|
170
|
+
type: "object",
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
required: ["id", "name", "root"],
|
|
173
|
+
properties: {
|
|
174
|
+
id: {
|
|
175
|
+
type: "string",
|
|
176
|
+
minLength: 1,
|
|
177
|
+
pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
178
|
+
description: "Stable id used to associate pipeline inputs with this project.",
|
|
179
|
+
},
|
|
180
|
+
name: {
|
|
181
|
+
type: "string",
|
|
182
|
+
minLength: 1,
|
|
183
|
+
description: "Human-readable project name shown in the generated site.",
|
|
184
|
+
},
|
|
185
|
+
root: {
|
|
186
|
+
type: "string",
|
|
187
|
+
minLength: 1,
|
|
188
|
+
description: "Repository-relative root. A pipeline input at or below this path belongs to the project.",
|
|
189
|
+
},
|
|
190
|
+
context: {
|
|
191
|
+
type: "string",
|
|
192
|
+
minLength: 1,
|
|
193
|
+
description: "Legacy alias for group. Kept so existing manifests remain valid.",
|
|
194
|
+
},
|
|
195
|
+
service: {
|
|
196
|
+
type: "string",
|
|
197
|
+
minLength: 1,
|
|
198
|
+
description: "Legacy alias for component. Kept so existing manifests remain valid.",
|
|
199
|
+
},
|
|
200
|
+
group: {
|
|
201
|
+
type: "string",
|
|
202
|
+
minLength: 1,
|
|
203
|
+
description: "Neutral top-level group this project contributes to.",
|
|
204
|
+
},
|
|
205
|
+
component: {
|
|
206
|
+
type: "string",
|
|
207
|
+
minLength: 1,
|
|
208
|
+
description: "Component slug inside the group.",
|
|
209
|
+
},
|
|
210
|
+
groupKind: {
|
|
211
|
+
enum: ["bounded-context", "system", "product", "team", "namespace"],
|
|
212
|
+
description: "Semantic role of the project's top-level group.",
|
|
213
|
+
},
|
|
214
|
+
componentKind: {
|
|
215
|
+
enum: ["service", "application", "webapp", "worker", "job", "function", "cli", "library", "data-pipeline"],
|
|
216
|
+
description: "Runtime or code role of the project component.",
|
|
217
|
+
},
|
|
218
|
+
repository: {
|
|
219
|
+
type: "string",
|
|
220
|
+
minLength: 1,
|
|
221
|
+
description: "Repository page or clone URL, when the project comes from another repository.",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
plugin: {
|
|
226
|
+
type: "object",
|
|
227
|
+
additionalProperties: false,
|
|
228
|
+
required: ["name"],
|
|
229
|
+
oneOf: [{ required: ["wasm"] }, { required: ["process"] }],
|
|
230
|
+
properties: {
|
|
231
|
+
name: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "What the steps below call this plugin. It need not be the plugin's own name.",
|
|
234
|
+
},
|
|
235
|
+
wasm: {
|
|
236
|
+
type: "object",
|
|
237
|
+
additionalProperties: false,
|
|
238
|
+
required: ["url"],
|
|
239
|
+
description:
|
|
240
|
+
"The default. The module gets no filesystem, no network and no environment, so a plugin from somebody else's repository can be run over your source tree without reading it.",
|
|
241
|
+
properties: {
|
|
242
|
+
url: {
|
|
243
|
+
type: "string",
|
|
244
|
+
pattern: "^(https://|file://)",
|
|
245
|
+
description:
|
|
246
|
+
"https:// to download, or file:// for a path beside the manifest, as in file://plugins/gen-markdown.wasm.",
|
|
247
|
+
},
|
|
248
|
+
sha256: {
|
|
249
|
+
type: "string",
|
|
250
|
+
pattern: "^[0-9a-f]{64}$",
|
|
251
|
+
description:
|
|
252
|
+
"Checksum of the module. Required for a download; optional for a local file, which it does not protect.",
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
process: {
|
|
257
|
+
type: "object",
|
|
258
|
+
additionalProperties: false,
|
|
259
|
+
required: ["command"],
|
|
260
|
+
description:
|
|
261
|
+
"The escape hatch for a plugin that needs a toolchain. Same protocol, none of the sandbox, which is the trade being made.",
|
|
262
|
+
properties: {
|
|
263
|
+
command: {
|
|
264
|
+
type: "string",
|
|
265
|
+
minLength: 1,
|
|
266
|
+
description: "The executable to run. It is passed directly to the OS, never through a shell.",
|
|
267
|
+
},
|
|
268
|
+
args: {
|
|
269
|
+
type: "array",
|
|
270
|
+
items: { type: "string" },
|
|
271
|
+
description: "Arguments passed to the executable without shell parsing.",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
...defs,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** The schema for one step of a phase, with the options of each plugin that belongs in it. */
|
|
283
|
+
function step(phase) {
|
|
284
|
+
const names = [...described]
|
|
285
|
+
.filter(([, descriptor]) => !descriptor || (descriptor.phases ?? []).includes(phase))
|
|
286
|
+
.map(([name]) => name);
|
|
287
|
+
|
|
288
|
+
const reads = phase !== "generate";
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
type: "object",
|
|
292
|
+
additionalProperties: false,
|
|
293
|
+
required: reads ? ["plugin", "in", "out"] : ["plugin", "out"],
|
|
294
|
+
properties: {
|
|
295
|
+
plugin: {
|
|
296
|
+
// The enum is the declared names, not the plugins' own, and it leaves
|
|
297
|
+
// out the ones that do not belong in this phase: an extractor offered
|
|
298
|
+
// where a renderer goes is a step that would fail with nothing to read.
|
|
299
|
+
enum: names,
|
|
300
|
+
description: "A plugin declared above, one that belongs in this phase.",
|
|
301
|
+
},
|
|
302
|
+
...(!reads
|
|
303
|
+
? {
|
|
304
|
+
catalog: {
|
|
305
|
+
type: "string",
|
|
306
|
+
minLength: 1,
|
|
307
|
+
description: "Catalog profile supplied to this generator.",
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
: {}),
|
|
311
|
+
...(reads
|
|
312
|
+
? {
|
|
313
|
+
in: {
|
|
314
|
+
type: "string",
|
|
315
|
+
description: "The directory to read, relative to the repository.",
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
: {}),
|
|
319
|
+
out: {
|
|
320
|
+
type: "string",
|
|
321
|
+
description:
|
|
322
|
+
"Where what comes back is written. A plugin names files; nothing it names is allowed to land outside this directory.",
|
|
323
|
+
},
|
|
324
|
+
options: {
|
|
325
|
+
type: "object",
|
|
326
|
+
description: "Passed to the plugin unread. What it accepts is described below.",
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
// One branch per plugin, so the options offered are the ones that plugin
|
|
330
|
+
// takes. A plugin that does not describe itself has no branch, and its
|
|
331
|
+
// options go unchecked rather than being refused.
|
|
332
|
+
allOf: names
|
|
333
|
+
.filter((name) => described.get(name)?.options)
|
|
334
|
+
.map((name) => ({
|
|
335
|
+
if: { required: ["plugin"], properties: { plugin: { const: name } } },
|
|
336
|
+
then: {
|
|
337
|
+
properties: {
|
|
338
|
+
options: { $ref: `#/$defs/${pointer(optionsKey(name))}` },
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
})),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function optionsKey(name) {
|
|
346
|
+
return `options.${name}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Escapes a $defs key for the JSON pointer that refers to it. */
|
|
350
|
+
function pointer(key) {
|
|
351
|
+
return encodeURIComponent(key.replaceAll("~", "~0").replaceAll("/", "~1"));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function fail(message) {
|
|
355
|
+
console.error(`portolan schema: ${message}`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// Puts the generated documentation into the built site.
|
|
2
|
+
//
|
|
3
|
+
// node scripts/site-docs.mjs after `vite build`
|
|
4
|
+
//
|
|
5
|
+
// The markdown generator writes docs/ with relative links and knows nothing of
|
|
6
|
+
// where the site is served from, which is right for a directory that is read
|
|
7
|
+
// on a forge or in an editor. llmstxt.org, though, asks for /llms.txt at the
|
|
8
|
+
// root of the site, and a link from there has to reach the page it names. So
|
|
9
|
+
// the built site gets three things:
|
|
10
|
+
//
|
|
11
|
+
// dist/docs/** the generated directory, as it is
|
|
12
|
+
// dist/llms.txt the index, its relative links pointed into docs/
|
|
13
|
+
// dist/llms-full.txt every page in one file, copied as it is: its links
|
|
14
|
+
// are relative to each page's own directory already,
|
|
15
|
+
// which the file itself says
|
|
16
|
+
//
|
|
17
|
+
// The docs are mounted under /docs/ and not at the root because the root is
|
|
18
|
+
// the app's: /flows, /adrs and every context id are routes, and a markdown
|
|
19
|
+
// tree beside them would shadow whichever it shares a name with.
|
|
20
|
+
//
|
|
21
|
+
// The dev server places nothing, so siteDocsPlugin answers the same three
|
|
22
|
+
// paths from the generated directory as it is on disk, and a page edited or
|
|
23
|
+
// regenerated shows on the next request.
|
|
24
|
+
|
|
25
|
+
import { cpSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { extname, isAbsolute, join, normalize, posix, resolve, sep } from "node:path";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
|
|
29
|
+
import { loadManifest } from "./manifest.mjs";
|
|
30
|
+
|
|
31
|
+
const LLMS = "llms.txt";
|
|
32
|
+
const LLMS_FULL = "llms-full.txt";
|
|
33
|
+
|
|
34
|
+
/** Where the generated directory lives inside the site, with its slash. */
|
|
35
|
+
export const DOCS_MOUNT = "docs/";
|
|
36
|
+
|
|
37
|
+
/** The file list the generator keeps for itself; not a page. */
|
|
38
|
+
const GENERATOR_MANIFEST = ".portolan-manifest";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Prefix every relative markdown link so the text still resolves once the
|
|
42
|
+
* file is moved up out of its directory. Absolute URLs, root-relative paths
|
|
43
|
+
* and same-page anchors are left as they are: none of them was relative to
|
|
44
|
+
* the file's directory to begin with.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} text
|
|
47
|
+
* @param {string} prefix
|
|
48
|
+
*/
|
|
49
|
+
export function mountLinks(text, prefix) {
|
|
50
|
+
return text.replace(
|
|
51
|
+
/\]\(([^)\s]+)((?:\s+"[^"]*")?)\)/g,
|
|
52
|
+
(whole, target, title) =>
|
|
53
|
+
isRelative(target) ? `](${prefix}${target}${title})` : whole,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isRelative(target) {
|
|
58
|
+
if (target.startsWith("#") || target.startsWith("/")) return false;
|
|
59
|
+
// A scheme: http:, https:, mailto:. A Windows drive letter is not a link a
|
|
60
|
+
// generated page would carry, so one letter is treated as a scheme too.
|
|
61
|
+
return !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(target);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The generated directory named by the manifest's markdown step, or null when
|
|
66
|
+
* the manifest declares no such step.
|
|
67
|
+
*
|
|
68
|
+
* @param {{generate?: Array<{plugin: string, out: string}>}} manifest
|
|
69
|
+
*/
|
|
70
|
+
export function docsDirectory(manifest) {
|
|
71
|
+
const step = (manifest.generate ?? []).find((item) => item.plugin === "markdown");
|
|
72
|
+
return step?.out ?? null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Copy the docs and place the llms files. Returns the paths written, relative
|
|
77
|
+
* to dist, so the caller can say what happened.
|
|
78
|
+
*
|
|
79
|
+
* @param {{manifest: object, dist: string}} input
|
|
80
|
+
* @returns {string[]}
|
|
81
|
+
*/
|
|
82
|
+
export function siteDocs({ manifest, dist }) {
|
|
83
|
+
const docs = docsDirectory(manifest);
|
|
84
|
+
const written = [];
|
|
85
|
+
|
|
86
|
+
if (docs !== null && existsSync(docs)) {
|
|
87
|
+
const mount = DOCS_MOUNT.replace(/\/$/, "");
|
|
88
|
+
cpSync(docs, join(dist, mount), {
|
|
89
|
+
recursive: true,
|
|
90
|
+
filter: (source) => !source.endsWith(`/${GENERATOR_MANIFEST}`),
|
|
91
|
+
});
|
|
92
|
+
written.push(`${mount}/`);
|
|
93
|
+
|
|
94
|
+
const index = join(docs, LLMS);
|
|
95
|
+
if (existsSync(index)) {
|
|
96
|
+
writeFileSync(join(dist, LLMS), mountLinks(readFileSync(index, "utf8"), DOCS_MOUNT));
|
|
97
|
+
written.push(LLMS);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const full = join(docs, LLMS_FULL);
|
|
101
|
+
if (existsSync(full)) {
|
|
102
|
+
cpSync(full, join(dist, LLMS_FULL));
|
|
103
|
+
written.push(LLMS_FULL);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const step of manifest.generate ?? []) {
|
|
108
|
+
const mount = generatedMount(step);
|
|
109
|
+
if (mount === null || !existsSync(mount)) continue;
|
|
110
|
+
cpSync(mount, join(dist, mount), {
|
|
111
|
+
recursive: true,
|
|
112
|
+
filter: (source) => !source.endsWith(`/${GENERATOR_MANIFEST}`),
|
|
113
|
+
});
|
|
114
|
+
written.push(`${mount}/`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return written;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A generated output that can safely be mounted below the built site's root. */
|
|
121
|
+
export function generatedMount(step) {
|
|
122
|
+
if (
|
|
123
|
+
step.plugin === "markdown" ||
|
|
124
|
+
typeof step.out !== "string" ||
|
|
125
|
+
step.out === "" ||
|
|
126
|
+
isAbsolute(step.out) ||
|
|
127
|
+
step.out.includes("\\")
|
|
128
|
+
) return null;
|
|
129
|
+
const mount = posix.normalize(step.out).replace(/^\.\//, "").replace(/\/$/, "");
|
|
130
|
+
if (mount === "." || mount === ".." || mount.startsWith("../")) return null;
|
|
131
|
+
return mount;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* What one request under the site's base asks for, or null when it is not
|
|
136
|
+
* one of the files the build would place. Pure, so it can be tested without a
|
|
137
|
+
* server: the answer says which file to read and how, not what is in it.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} pathname the request path, base included
|
|
140
|
+
* @param {string} base the site's base, "/" or "/portolan/"
|
|
141
|
+
* @returns {{kind: "index"} | {kind: "full"} | {kind: "redirect", to: string}
|
|
142
|
+
* | {kind: "page", path: string} | null}
|
|
143
|
+
*/
|
|
144
|
+
export function docsRequest(pathname, base = "/") {
|
|
145
|
+
const root = base.endsWith("/") ? base : `${base}/`;
|
|
146
|
+
if (!pathname.startsWith(root)) return null;
|
|
147
|
+
const rest = pathname.slice(root.length);
|
|
148
|
+
|
|
149
|
+
if (rest === LLMS) return { kind: "index" };
|
|
150
|
+
if (rest === LLMS_FULL) return { kind: "full" };
|
|
151
|
+
if (rest !== DOCS_MOUNT.slice(0, -1) && !rest.startsWith(DOCS_MOUNT)) return null;
|
|
152
|
+
|
|
153
|
+
// "docs" without its slash: send the browser to the slash so the page's own
|
|
154
|
+
// relative links resolve against the directory, as they do on the forge.
|
|
155
|
+
if (rest === DOCS_MOUNT.slice(0, -1)) return { kind: "redirect", to: `${root}${DOCS_MOUNT}` };
|
|
156
|
+
|
|
157
|
+
let inside;
|
|
158
|
+
try {
|
|
159
|
+
inside = decodeURIComponent(rest.slice(DOCS_MOUNT.length));
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const segments = inside.split("/");
|
|
164
|
+
if (segments.some((segment) => segment === ".." || segment.startsWith(".") && segment !== "")) return null;
|
|
165
|
+
if (inside.includes("\\")) return null;
|
|
166
|
+
if (inside === "" || inside.endsWith("/")) inside += "README.md";
|
|
167
|
+
|
|
168
|
+
return { kind: "page", path: posix.normalize(inside) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const TYPES = {
|
|
172
|
+
".md": "text/markdown; charset=utf-8",
|
|
173
|
+
".txt": "text/plain; charset=utf-8",
|
|
174
|
+
".json": "application/json; charset=utf-8",
|
|
175
|
+
".yaml": "application/yaml; charset=utf-8",
|
|
176
|
+
".yml": "application/yaml; charset=utf-8",
|
|
177
|
+
".mmd": "text/plain; charset=utf-8",
|
|
178
|
+
".svg": "image/svg+xml",
|
|
179
|
+
".png": "image/png",
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Read one answer of docsRequest from the generated directory, or null when
|
|
184
|
+
* the file is not there. Returned with its content type so the server needs
|
|
185
|
+
* to know nothing about markdown.
|
|
186
|
+
*
|
|
187
|
+
* @param {string} docs the generated directory
|
|
188
|
+
* @param {ReturnType<typeof docsRequest>} request
|
|
189
|
+
* @returns {{type: string, body: Buffer | string} | null}
|
|
190
|
+
*/
|
|
191
|
+
export function readDocsRequest(docs, request) {
|
|
192
|
+
if (request === null || request.kind === "redirect") return null;
|
|
193
|
+
const root = resolve(docs);
|
|
194
|
+
|
|
195
|
+
if (request.kind === "index") {
|
|
196
|
+
const file = join(root, LLMS);
|
|
197
|
+
if (!isFile(file)) return null;
|
|
198
|
+
return { type: TYPES[".txt"], body: mountLinks(readFileSync(file, "utf8"), DOCS_MOUNT) };
|
|
199
|
+
}
|
|
200
|
+
if (request.kind === "full") {
|
|
201
|
+
const file = join(root, LLMS_FULL);
|
|
202
|
+
if (!isFile(file)) return null;
|
|
203
|
+
return { type: TYPES[".txt"], body: readFileSync(file) };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const file = resolve(root, normalize(request.path));
|
|
207
|
+
if (!file.startsWith(root + sep) || !isFile(file)) return null;
|
|
208
|
+
let target = file;
|
|
209
|
+
// "docs/auth" resolving to a directory is answered by its index too, since a
|
|
210
|
+
// link written as a bare directory is a link somebody expects to work.
|
|
211
|
+
if (statSync(file).isDirectory()) {
|
|
212
|
+
target = join(file, "README.md");
|
|
213
|
+
if (!isFile(target)) return null;
|
|
214
|
+
}
|
|
215
|
+
return { type: TYPES[extname(target)] ?? "application/octet-stream", body: readFileSync(target) };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function isFile(path) {
|
|
219
|
+
return existsSync(path) && !path.endsWith(`${sep}${GENERATOR_MANIFEST}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** A safe request for a non-markdown generator output in development. */
|
|
223
|
+
export function generatedRequest(pathname, manifest, base = "/") {
|
|
224
|
+
const root = base.endsWith("/") ? base : `${base}/`;
|
|
225
|
+
if (!pathname.startsWith(root)) return null;
|
|
226
|
+
const rest = pathname.slice(root.length);
|
|
227
|
+
for (const step of manifest.generate ?? []) {
|
|
228
|
+
const mount = generatedMount(step);
|
|
229
|
+
if (mount === null) continue;
|
|
230
|
+
if (rest !== mount && !rest.startsWith(`${mount}/`)) continue;
|
|
231
|
+
if (rest === mount) return { kind: "redirect", to: `${root}${mount}/` };
|
|
232
|
+
let inside;
|
|
233
|
+
try { inside = decodeURIComponent(rest.slice(mount.length + 1)); } catch { return null; }
|
|
234
|
+
if (inside.includes("\\") || inside.split("/").some((part) => part === ".." || part.startsWith("."))) return null;
|
|
235
|
+
if (inside === "" || inside.endsWith("/")) inside += "README.md";
|
|
236
|
+
return { kind: "artifact", path: posix.join(mount, inside) };
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function readGeneratedRequest(workspace, request) {
|
|
242
|
+
if (request === null || request.kind !== "artifact") return null;
|
|
243
|
+
const root = resolve(workspace);
|
|
244
|
+
const file = resolve(root, normalize(request.path));
|
|
245
|
+
if (!file.startsWith(root + sep) || !isFile(file)) return null;
|
|
246
|
+
return { type: TYPES[extname(file)] ?? "application/octet-stream", body: readFileSync(file) };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The dev server's side of the build step: llms.txt, llms-full.txt and docs/
|
|
251
|
+
* under the site's base, answered from the workspace as it is now. Vite's
|
|
252
|
+
* own fallback would otherwise hand every one of them the app's index.html.
|
|
253
|
+
*/
|
|
254
|
+
export function siteDocsPlugin(workspace = process.cwd()) {
|
|
255
|
+
return {
|
|
256
|
+
name: "portolan-site-docs",
|
|
257
|
+
apply: "serve",
|
|
258
|
+
configureServer(server) {
|
|
259
|
+
server.middlewares.use((req, res, next) => {
|
|
260
|
+
if (req.method !== "GET" && req.method !== "HEAD") return next();
|
|
261
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
262
|
+
let manifest;
|
|
263
|
+
try {
|
|
264
|
+
manifest = JSON.parse(readFileSync(join(workspace, "portolan.json"), "utf8"));
|
|
265
|
+
} catch {
|
|
266
|
+
return next();
|
|
267
|
+
}
|
|
268
|
+
const request = docsRequest(url.pathname, server.config.base) ?? generatedRequest(url.pathname, manifest, server.config.base);
|
|
269
|
+
if (request === null) return next();
|
|
270
|
+
if (request.kind === "redirect") {
|
|
271
|
+
res.writeHead(301, { Location: request.to });
|
|
272
|
+
return res.end();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const docs = docsDirectory(manifest);
|
|
276
|
+
const answer = request.kind === "artifact"
|
|
277
|
+
? readGeneratedRequest(workspace, request)
|
|
278
|
+
: docs === null ? null : readDocsRequest(join(workspace, docs), request);
|
|
279
|
+
if (answer === null) return next();
|
|
280
|
+
res.writeHead(200, { "Content-Type": answer.type, "Cache-Control": "no-cache" });
|
|
281
|
+
res.end(req.method === "HEAD" ? undefined : answer.body);
|
|
282
|
+
});
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
288
|
+
const { manifest } = loadManifest();
|
|
289
|
+
const written = siteDocs({ manifest, dist: "dist" });
|
|
290
|
+
if (written.length === 0) {
|
|
291
|
+
console.log("site docs: nothing to place (no markdown step, or its output is not generated yet)");
|
|
292
|
+
} else {
|
|
293
|
+
console.log(`site docs: ${written.join(", ")} → dist/`);
|
|
294
|
+
}
|
|
295
|
+
}
|