@shortlink-org/portolan 0.1.0 → 0.2.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/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +22 -4
- package/scripts/local-api.test.mjs +84 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +6 -2
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +12 -8
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
_ "embed"
|
|
5
|
-
|
|
6
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
//go:embed options.schema.json
|
|
10
|
-
var optionsSchema []byte
|
|
11
|
-
|
|
12
|
-
func descriptor() plugin.Descriptor {
|
|
13
|
-
return plugin.Descriptor{
|
|
14
|
-
Name: "extract-commands",
|
|
15
|
-
Summary: "Reads the make targets, npm scripts, just recipes and task-runner tasks a repository declares onto its service.",
|
|
16
|
-
Phases: []string{plugin.PhaseExtract},
|
|
17
|
-
Options: optionsSchema,
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"errors"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"regexp"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
"github.com/shortlink-org/portolan/internal/commands"
|
|
11
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
var slug = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
|
|
15
|
-
|
|
16
|
-
// extract reads the runner files at the root and answers with a fragment
|
|
17
|
-
// that names the service and lists its commands - and nothing else about
|
|
18
|
-
// it, because everything else is another extractor's to say.
|
|
19
|
-
//
|
|
20
|
-
// A runner file that cannot be read comes back as a warning rather than a
|
|
21
|
-
// failure: a broken Taskfile is a fact about the checkout the page can carry,
|
|
22
|
-
// and the other runners' commands are still worth listing.
|
|
23
|
-
func extract(in plugin.Input, opts Options) (plugin.Response, error) {
|
|
24
|
-
b := &plugin.Builder{}
|
|
25
|
-
|
|
26
|
-
service := opts.Service
|
|
27
|
-
if service == "" {
|
|
28
|
-
service = filepath.Base(in.Root)
|
|
29
|
-
}
|
|
30
|
-
context := opts.Context
|
|
31
|
-
if context == "" {
|
|
32
|
-
context = filepath.Base(filepath.Dir(in.Root))
|
|
33
|
-
}
|
|
34
|
-
for _, part := range []string{context, service} {
|
|
35
|
-
if !slug.MatchString(part) {
|
|
36
|
-
return plugin.Response{}, errors.New(part + " is not a slug: a service's id is the context and the service, so both have to be one word the way the estate spells them")
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
cmds, warnings := commands.Read(in.Root)
|
|
41
|
-
for _, warning := range warnings {
|
|
42
|
-
b.Warn(in.Root, warning)
|
|
43
|
-
}
|
|
44
|
-
if len(cmds) == 0 {
|
|
45
|
-
b.Warn(in.Root, "no runner file declares a command here - no Makefile, justfile, Taskfile, package.json scripts or pyproject tasks - so the fragment lists none")
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
fragment := catalog.Catalog{
|
|
49
|
-
GeneratedAt: in.GeneratedAt,
|
|
50
|
-
Commit: in.Commit,
|
|
51
|
-
Contexts: []catalog.BoundedContext{{
|
|
52
|
-
ID: context,
|
|
53
|
-
Slug: context,
|
|
54
|
-
Services: []catalog.Service{{
|
|
55
|
-
ID: context + "." + service,
|
|
56
|
-
Slug: service,
|
|
57
|
-
Provides: []catalog.RpcService{},
|
|
58
|
-
Consumes: []catalog.RpcCall{},
|
|
59
|
-
Aggregates: []catalog.Aggregate{},
|
|
60
|
-
Commands: cmds,
|
|
61
|
-
}},
|
|
62
|
-
}},
|
|
63
|
-
Defs: map[string]catalog.TypeDef{},
|
|
64
|
-
Flows: []catalog.Flow{},
|
|
65
|
-
Adrs: []catalog.Adr{},
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
encoded, err := json.MarshalIndent(fragment, "", " ")
|
|
69
|
-
if err != nil {
|
|
70
|
-
return plugin.Response{}, err
|
|
71
|
-
}
|
|
72
|
-
out := opts.Out
|
|
73
|
-
if out == "" {
|
|
74
|
-
out = "commands.json"
|
|
75
|
-
}
|
|
76
|
-
b.File(out, string(encoded)+"\n")
|
|
77
|
-
|
|
78
|
-
return b.Response(), nil
|
|
79
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"encoding/json"
|
|
5
|
-
"os"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"testing"
|
|
8
|
-
|
|
9
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
10
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
func mustWrite(t *testing.T, path, contents string) {
|
|
14
|
-
t.Helper()
|
|
15
|
-
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
16
|
-
t.Fatal(err)
|
|
17
|
-
}
|
|
18
|
-
if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
|
|
19
|
-
t.Fatal(err)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
func fragment(t *testing.T, resp plugin.Response) catalog.Catalog {
|
|
24
|
-
t.Helper()
|
|
25
|
-
var got catalog.Catalog
|
|
26
|
-
if err := json.Unmarshal([]byte(resp.Files[0].Contents), &got); err != nil {
|
|
27
|
-
t.Fatal(err)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return got
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
func TestListsCommandsOnTheNamedService(t *testing.T) {
|
|
34
|
-
root := filepath.Join(t.TempDir(), "shop", "cart")
|
|
35
|
-
mustWrite(t, filepath.Join(root, "Makefile"), "test: ## Run the tests\n\tgo test ./...\n")
|
|
36
|
-
mustWrite(t, filepath.Join(root, "package.json"), `{"scripts":{"build":"tsc"}}`)
|
|
37
|
-
|
|
38
|
-
resp, err := extract(plugin.Input{Root: root, Commit: "abc", GeneratedAt: "2026-09-07T00:00:00Z"}, Options{})
|
|
39
|
-
if err != nil {
|
|
40
|
-
t.Fatal(err)
|
|
41
|
-
}
|
|
42
|
-
if resp.Files[0].Name != "commands.json" {
|
|
43
|
-
t.Errorf("file = %q", resp.Files[0].Name)
|
|
44
|
-
}
|
|
45
|
-
got := fragment(t, resp)
|
|
46
|
-
if got.Commit != "abc" || len(got.Contexts) != 1 || got.Contexts[0].ID != "shop" {
|
|
47
|
-
t.Fatalf("fragment = %+v", got)
|
|
48
|
-
}
|
|
49
|
-
svc := got.Contexts[0].Services[0]
|
|
50
|
-
if svc.ID != "shop.cart" || svc.Slug != "cart" || svc.Name != "" || svc.Readme != "" {
|
|
51
|
-
t.Errorf("the fragment claims only the id: %+v", svc)
|
|
52
|
-
}
|
|
53
|
-
if len(svc.Commands) != 2 || svc.Commands[0].Run != "make test" || svc.Commands[0].Doc != "Run the tests" || svc.Commands[1].Run != "npm run build" {
|
|
54
|
-
t.Errorf("commands = %+v", svc.Commands)
|
|
55
|
-
}
|
|
56
|
-
if len(resp.Warnings()) != 0 {
|
|
57
|
-
t.Errorf("warnings = %+v", resp.Warnings())
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
func TestOptionsNameTheService(t *testing.T) {
|
|
62
|
-
root := t.TempDir()
|
|
63
|
-
mustWrite(t, filepath.Join(root, "justfile"), "dev:\n cargo run\n")
|
|
64
|
-
|
|
65
|
-
resp, err := extract(plugin.Input{Root: root}, Options{Context: "shop", Service: "oms", Out: "cmds.json"})
|
|
66
|
-
if err != nil {
|
|
67
|
-
t.Fatal(err)
|
|
68
|
-
}
|
|
69
|
-
got := fragment(t, resp)
|
|
70
|
-
if resp.Files[0].Name != "cmds.json" || got.Contexts[0].Services[0].ID != "shop.oms" {
|
|
71
|
-
t.Errorf("file = %q, service = %q", resp.Files[0].Name, got.Contexts[0].Services[0].ID)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
func TestNothingDeclaredWarns(t *testing.T) {
|
|
76
|
-
resp, err := extract(plugin.Input{Root: t.TempDir()}, Options{Context: "shop", Service: "oms"})
|
|
77
|
-
if err != nil {
|
|
78
|
-
t.Fatal(err)
|
|
79
|
-
}
|
|
80
|
-
if len(resp.Warnings()) != 1 {
|
|
81
|
-
t.Errorf("warnings = %+v", resp.Warnings())
|
|
82
|
-
}
|
|
83
|
-
if got := fragment(t, resp); len(got.Contexts[0].Services[0].Commands) != 0 {
|
|
84
|
-
t.Errorf("commands = %+v", got.Contexts[0].Services[0].Commands)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
func TestRefusesANonSlug(t *testing.T) {
|
|
89
|
-
if _, err := extract(plugin.Input{Root: t.TempDir()}, Options{Context: "Shop Front", Service: "oms"}); err == nil {
|
|
90
|
-
t.Error("a context with a space in it is not an id")
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// Command extract-commands reads what a developer types against a checkout -
|
|
2
|
-
// the make targets, npm scripts, just recipes and task-runner tasks the
|
|
3
|
-
// repository declares - and answers with a catalog fragment holding them on
|
|
4
|
-
// the service they belong to.
|
|
5
|
-
//
|
|
6
|
-
// It reads the runner files and nothing else. The README that says "run make
|
|
7
|
-
// test" is a claim; the Makefile with a test target is the fact. Fragment
|
|
8
|
-
// merging puts the list on the service the domain extractor described, so
|
|
9
|
-
// this plugin does not need to know anything else about it - only which
|
|
10
|
-
// service it is, which the manifest says.
|
|
11
|
-
//
|
|
12
|
-
// extract-project reads the same files for the component it describes, so a
|
|
13
|
-
// repository read by that plugin does not need this one; this is for the
|
|
14
|
-
// services a domain extractor describes instead.
|
|
15
|
-
package main
|
|
16
|
-
|
|
17
|
-
import (
|
|
18
|
-
"fmt"
|
|
19
|
-
"io"
|
|
20
|
-
"os"
|
|
21
|
-
|
|
22
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// Options are what the manifest tells the extractor.
|
|
26
|
-
type Options struct {
|
|
27
|
-
// Context is the bounded context the service is in. Left out, the name
|
|
28
|
-
// of the input directory's parent - right for `shop/cart`, and wrong
|
|
29
|
-
// often enough that the manifest should say.
|
|
30
|
-
Context string `json:"context,omitempty"`
|
|
31
|
-
|
|
32
|
-
// Service is the service the commands belong to, as its slug. Left out,
|
|
33
|
-
// the name of the input directory.
|
|
34
|
-
Service string `json:"service,omitempty"`
|
|
35
|
-
|
|
36
|
-
// Out names the fragment file.
|
|
37
|
-
Out string `json:"out,omitempty"`
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func main() {
|
|
41
|
-
if err := run(os.Stdin, os.Stdout); err != nil {
|
|
42
|
-
fmt.Fprintln(os.Stderr, "portolan-extract-commands:", err)
|
|
43
|
-
os.Exit(1)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func run(stdin io.Reader, stdout io.Writer) error {
|
|
48
|
-
return plugin.Serve(stdin, stdout, descriptor(), func(req plugin.Request, opts Options) (plugin.Response, error) {
|
|
49
|
-
if req.Input.Root == "" {
|
|
50
|
-
return plugin.Response{}, fmt.Errorf("no input root: an extractor has nothing to read")
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return extract(req.Input, opts)
|
|
54
|
-
})
|
|
55
|
-
}
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
// Avro, read for the two things the catalog keeps: what a record is called and
|
|
4
|
-
// what fields it has.
|
|
5
|
-
//
|
|
6
|
-
// This is deliberately not an Avro implementation. It never resolves a schema
|
|
7
|
-
// against data, never checks compatibility and never validates - a registry
|
|
8
|
-
// has already done all three, and a second opinion here is only a way for the
|
|
9
|
-
// two to disagree. It reads names and shapes, and renders every type as the
|
|
10
|
-
// short label a person reads on a page.
|
|
11
|
-
|
|
12
|
-
import (
|
|
13
|
-
"encoding/json"
|
|
14
|
-
"fmt"
|
|
15
|
-
"strings"
|
|
16
|
-
|
|
17
|
-
"github.com/shortlink-org/portolan/catalog"
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
// avro reads one .avsc into the shapes it declares.
|
|
21
|
-
//
|
|
22
|
-
// The first return is the full name of the record at the top, which is the
|
|
23
|
-
// name a message on the bus goes by. The rest are every named record inside
|
|
24
|
-
// it, keyed by full name, because a nested record is a shape shared by
|
|
25
|
-
// whatever else names it and `defs` is where a shared shape lives.
|
|
26
|
-
func avro(raw []byte) (string, map[string]catalog.TypeDef, error) {
|
|
27
|
-
var node any
|
|
28
|
-
if err := json.Unmarshal(raw, &node); err != nil {
|
|
29
|
-
return "", nil, fmt.Errorf("not an Avro schema: %w", err)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
r := &avroReader{defs: map[string]catalog.TypeDef{}}
|
|
33
|
-
label, _ := r.typeOf(node, "")
|
|
34
|
-
|
|
35
|
-
// A top-level union or array is legal Avro and names no record. The label
|
|
36
|
-
// is still the honest answer to "what is this called".
|
|
37
|
-
return label, r.defs, nil
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
type avroReader struct {
|
|
41
|
-
defs map[string]catalog.TypeDef
|
|
42
|
-
|
|
43
|
-
// open guards a record that names itself inside itself - a comment thread,
|
|
44
|
-
// a tree - which is ordinary Avro and an infinite recursion here.
|
|
45
|
-
open map[string]bool
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// typeOf renders one Avro type, and returns the defs key when it is a record
|
|
49
|
-
// this reader has written down.
|
|
50
|
-
//
|
|
51
|
-
// enclosing is the namespace inherited from the schema this type sits in,
|
|
52
|
-
// which is how Avro spells a nested name: a record with no namespace of its
|
|
53
|
-
// own belongs to its parent's.
|
|
54
|
-
func (r *avroReader) typeOf(node any, enclosing string) (label, ref string) {
|
|
55
|
-
switch value := node.(type) {
|
|
56
|
-
case string:
|
|
57
|
-
// A bare name is either a primitive or a reference to a shape named
|
|
58
|
-
// earlier - possibly in another subject entirely, which is what a
|
|
59
|
-
// registry reference is for. The reference is claimed here and cleared
|
|
60
|
-
// later if nothing in the estate turns out to declare it, because only
|
|
61
|
-
// the whole step can say whether the other subject was vendored too.
|
|
62
|
-
name := qualify(value, enclosing)
|
|
63
|
-
if primitive(value) {
|
|
64
|
-
return name, ""
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return name, name
|
|
68
|
-
|
|
69
|
-
case []any:
|
|
70
|
-
return r.union(value, enclosing), ""
|
|
71
|
-
|
|
72
|
-
case map[string]any:
|
|
73
|
-
return r.object(value, enclosing)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return "", ""
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// union renders ["null", "string"] as "string?" - the shape a reader wants,
|
|
80
|
-
// rather than the shape the file has.
|
|
81
|
-
func (r *avroReader) union(members []any, enclosing string) string {
|
|
82
|
-
var (
|
|
83
|
-
labels []string
|
|
84
|
-
nullable bool
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
for _, member := range members {
|
|
88
|
-
if name, isString := member.(string); isString && name == "null" {
|
|
89
|
-
nullable = true
|
|
90
|
-
|
|
91
|
-
continue
|
|
92
|
-
}
|
|
93
|
-
label, _ := r.typeOf(member, enclosing)
|
|
94
|
-
labels = append(labels, label)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
switch {
|
|
98
|
-
case len(labels) == 0:
|
|
99
|
-
return "null"
|
|
100
|
-
case nullable:
|
|
101
|
-
return strings.Join(labels, "|") + "?"
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return strings.Join(labels, "|")
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
func (r *avroReader) object(node map[string]any, enclosing string) (label, ref string) {
|
|
108
|
-
// A logical type is what the field MEANS, and it is what belongs on a
|
|
109
|
-
// page: "timestamp-millis" says more than the "long" underneath it.
|
|
110
|
-
if logical, _ := node["logicalType"].(string); logical != "" {
|
|
111
|
-
return decorate(logical, node), ""
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
kind, _ := node["type"].(string)
|
|
115
|
-
namespace := firstNonEmpty(text(node["namespace"]), enclosing)
|
|
116
|
-
|
|
117
|
-
switch kind {
|
|
118
|
-
case "record", "error":
|
|
119
|
-
return r.record(node, namespace)
|
|
120
|
-
|
|
121
|
-
case "enum", "fixed":
|
|
122
|
-
// Named, but not a shape with fields, so there is nothing to put in
|
|
123
|
-
// defs and the label is the whole answer.
|
|
124
|
-
return qualify(text(node["name"]), namespace), ""
|
|
125
|
-
|
|
126
|
-
case "array":
|
|
127
|
-
items, _ := r.typeOf(node["items"], namespace)
|
|
128
|
-
|
|
129
|
-
return items + "[]", ""
|
|
130
|
-
|
|
131
|
-
case "map":
|
|
132
|
-
values, _ := r.typeOf(node["values"], namespace)
|
|
133
|
-
|
|
134
|
-
return "map<string, " + values + ">", ""
|
|
135
|
-
|
|
136
|
-
case "":
|
|
137
|
-
// `{"type": {...}}` with the real type one level down, which is how a
|
|
138
|
-
// field wraps a schema it wants to annotate.
|
|
139
|
-
if nested, wrapped := node["type"]; wrapped {
|
|
140
|
-
return r.typeOf(nested, namespace)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return "", ""
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return kind, ""
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
func (r *avroReader) record(node map[string]any, namespace string) (label, ref string) {
|
|
150
|
-
name := qualify(text(node["name"]), namespace)
|
|
151
|
-
if name == "" {
|
|
152
|
-
return "", ""
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if r.open[name] {
|
|
156
|
-
// Already being read further up the stack: the name is the answer, and
|
|
157
|
-
// the def it points at is written by the frame that opened it.
|
|
158
|
-
return name, name
|
|
159
|
-
}
|
|
160
|
-
if _, written := r.defs[name]; written {
|
|
161
|
-
return name, name
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if r.open == nil {
|
|
165
|
-
r.open = map[string]bool{}
|
|
166
|
-
}
|
|
167
|
-
r.open[name] = true
|
|
168
|
-
defer delete(r.open, name)
|
|
169
|
-
|
|
170
|
-
// The namespace a nested record inherits is this record's, not the one it
|
|
171
|
-
// was reached from.
|
|
172
|
-
inner := namespace
|
|
173
|
-
if own := text(node["namespace"]); own != "" {
|
|
174
|
-
inner = own
|
|
175
|
-
} else if at := strings.LastIndex(name, "."); at > 0 {
|
|
176
|
-
inner = name[:at]
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
fields := []catalog.Field{}
|
|
180
|
-
for _, entry := range list(node["fields"]) {
|
|
181
|
-
field, isObject := entry.(map[string]any)
|
|
182
|
-
if !isObject {
|
|
183
|
-
continue
|
|
184
|
-
}
|
|
185
|
-
fieldName := text(field["name"])
|
|
186
|
-
if fieldName == "" {
|
|
187
|
-
continue
|
|
188
|
-
}
|
|
189
|
-
fieldLabel, fieldRef := r.typeOf(field["type"], inner)
|
|
190
|
-
fields = append(fields, catalog.Field{
|
|
191
|
-
Name: fieldName,
|
|
192
|
-
Type: fieldLabel,
|
|
193
|
-
Doc: text(field["doc"]),
|
|
194
|
-
Ref: fieldRef,
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
r.defs[name] = catalog.TypeDef{Fields: fields}
|
|
199
|
-
|
|
200
|
-
return name, name
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// decorate spells out the parameters a logical type carries, because
|
|
204
|
-
// "decimal" without them is not a type anybody can act on.
|
|
205
|
-
func decorate(logical string, node map[string]any) string {
|
|
206
|
-
if logical != "decimal" {
|
|
207
|
-
return logical
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
precision, hasPrecision := number(node["precision"])
|
|
211
|
-
if !hasPrecision {
|
|
212
|
-
return logical
|
|
213
|
-
}
|
|
214
|
-
scale, hasScale := number(node["scale"])
|
|
215
|
-
if !hasScale {
|
|
216
|
-
scale = 0
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return fmt.Sprintf("decimal(%d,%d)", precision, scale)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// qualify gives a name its namespace, unless it already carries one or is a
|
|
223
|
-
// primitive - which is the one rule that keeps "string" from becoming
|
|
224
|
-
// "shop.oms.string".
|
|
225
|
-
func qualify(name, namespace string) string {
|
|
226
|
-
if name == "" || namespace == "" || strings.Contains(name, ".") || primitive(name) {
|
|
227
|
-
return name
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return namespace + "." + name
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
func primitive(name string) bool {
|
|
234
|
-
switch name {
|
|
235
|
-
case "null", "boolean", "int", "long", "float", "double", "bytes", "string":
|
|
236
|
-
return true
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return false
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
func text(node any) string {
|
|
243
|
-
value, _ := node.(string)
|
|
244
|
-
|
|
245
|
-
return strings.TrimSpace(value)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
func list(node any) []any {
|
|
249
|
-
values, _ := node.([]any)
|
|
250
|
-
|
|
251
|
-
return values
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
func number(node any) (int, bool) {
|
|
255
|
-
value, isNumber := node.(float64)
|
|
256
|
-
|
|
257
|
-
return int(value), isNumber
|
|
258
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
_ "embed"
|
|
5
|
-
|
|
6
|
-
"github.com/shortlink-org/portolan/plugin"
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
//go:embed options.schema.json
|
|
10
|
-
var optionsSchema []byte
|
|
11
|
-
|
|
12
|
-
func descriptor() plugin.Descriptor {
|
|
13
|
-
return plugin.Descriptor{
|
|
14
|
-
Name: "extract-csr",
|
|
15
|
-
Summary: "Reads schemas vendored out of a Confluent Schema Registry and answers with the topics they are registered against and the shapes they declare.",
|
|
16
|
-
Phases: []string{plugin.PhaseExtract},
|
|
17
|
-
Options: optionsSchema,
|
|
18
|
-
}
|
|
19
|
-
}
|