@shortlink-org/portolan 0.3.0 → 0.4.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 +151 -6
- package/catalog/model.go +147 -0
- package/catalog/roundtrip_test.go +7 -0
- package/cli/portolan.mjs +6 -1
- package/go.mod +7 -0
- package/go.sum +20 -6
- package/package.json +10 -9
- package/plugin/describe.go +38 -0
- package/plugins/README.md +296 -5
- package/plugins/extract-argocd/options.schema.json +44 -0
- package/plugins/extract-celery/main.py +1 -0
- package/plugins/extract-csharp-ddd/README.md +213 -0
- package/plugins/extract-csharp-ddd/options.schema.json +55 -0
- package/plugins/extract-django/README.md +13 -2
- package/plugins/extract-django/choices.py +47 -0
- package/plugins/extract-django/domain.py +2 -1
- package/plugins/extract-django/extract_test.py +13 -0
- package/plugins/extract-django/lifecycle.py +2 -28
- package/plugins/extract-django/main.py +1 -0
- package/plugins/extract-django/rules.py +154 -0
- package/plugins/extract-django/rules_test.py +158 -0
- package/plugins/extract-go-sqs/options.schema.json +21 -0
- package/plugins/extract-http-clients/describe.go +5 -4
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-k8s/options.schema.json +32 -0
- package/plugins/extract-laravel/Cargo.lock +962 -0
- package/plugins/extract-laravel/Cargo.toml +20 -0
- package/plugins/extract-laravel/README.md +200 -0
- package/plugins/extract-laravel/options.schema.json +68 -0
- package/plugins/extract-laravel/rustfmt.toml +1 -0
- package/plugins/extract-php-ddd/Cargo.lock +972 -0
- package/plugins/extract-php-ddd/Cargo.toml +22 -0
- package/plugins/extract-php-ddd/README.md +141 -0
- package/plugins/extract-php-ddd/options.schema.json +50 -0
- package/plugins/extract-php-ddd/rustfmt.toml +1 -0
- package/plugins/extract-python-kafka/main.py +1 -0
- package/plugins/extract-terraform/options.schema.json +26 -0
- package/plugins/extract-ts/main.ts +1 -0
- package/plugins/phpscan/Cargo.lock +953 -0
- package/plugins/phpscan/Cargo.toml +19 -0
- package/plugins/phpscan/rustfmt.toml +1 -0
- package/plugins/portolan-go.wasm +0 -0
- package/plugins/pyplugin/catalog.py +24 -1
- package/plugins/verify-otel/options.schema.json +12 -0
- package/portolan.json +130 -3
- package/rules/builtin.json +218 -0
- package/schema/portolan.schema.json +880 -13
- package/scripts/README.md +3 -1
- package/scripts/delivery-presets.mjs +36 -3
- package/scripts/gen-likec4.mjs +149 -16
- package/scripts/gen-likec4.test.mjs +96 -0
- package/scripts/gen.mjs +30 -3
- package/scripts/gitops-example.test.mjs +108 -0
- package/scripts/host-plugins/fetch-argocd.mjs +338 -0
- package/scripts/host-plugins/fetch-argocd.options.json +49 -0
- package/scripts/host-plugins/fetch-argocd.test.mjs +274 -0
- package/scripts/host-plugins/fetch-bsr.mjs +1 -0
- package/scripts/host-plugins/fetch-csr.mjs +1 -0
- package/scripts/host-plugins/fetch-git.mjs +1 -0
- package/scripts/host-plugins/fetch-k8s.mjs +263 -0
- package/scripts/host-plugins/fetch-k8s.options.json +50 -0
- package/scripts/host-plugins/fetch-k8s.test.mjs +259 -0
- package/scripts/host-plugins/k8s-topology.mjs +183 -0
- package/scripts/json-format.mjs +192 -0
- package/scripts/json-format.test.mjs +97 -0
- package/scripts/local-api.mjs +216 -8
- package/scripts/local-api.test.mjs +88 -2
- package/scripts/local-discovery.mjs +12 -0
- package/scripts/manifest.mjs +10 -1
- package/scripts/plugin-host.mjs +23 -1
- package/scripts/plugins-fresh.mjs +97 -0
- package/scripts/plugins-fresh.test.mjs +64 -0
- package/scripts/run-builtin.mjs +26 -5
- package/scripts/schema.mjs +138 -19
- package/scripts/trace-trials.mjs +176 -0
- package/scripts/trace-trials.test.mjs +142 -0
- package/scripts/warning-policy.mjs +12 -6
- package/scripts/warning-policy.test.mjs +40 -3
- package/src/app/Breadcrumbs.test.ts +3 -0
- package/src/app/Breadcrumbs.tsx +3 -0
- package/src/app/CatalogApp.tsx +2 -0
- package/src/app/SidebarFooter.tsx +20 -4
- package/src/catalog-index.ts +25 -0
- package/src/catalog-model.ts +246 -1
- package/src/catalog-validation.ts +104 -2
- package/src/catalog.test.ts +40 -1
- package/src/components/ChannelRows.test.tsx +76 -1
- package/src/components/ChannelRows.tsx +223 -60
- package/src/components/DeploymentRows.tsx +131 -0
- package/src/components/DocsLinks.test.tsx +27 -0
- package/src/components/DocsLinks.tsx +56 -0
- package/src/components/FieldTree.tsx +5 -2
- package/src/components/HTTPDestinationEvidence.tsx +1 -1
- package/src/components/PluginIcon.tsx +77 -0
- package/src/components/ProblemRow.tsx +173 -153
- package/src/components/RuleMarks.tsx +22 -0
- package/src/components/ShapeRows.tsx +24 -20
- package/src/data.ts +21 -0
- package/src/enrich.test.ts +127 -3
- package/src/enrich.ts +104 -6
- package/src/er/ErCanvas.tsx +218 -12
- package/src/er/GroupNode.tsx +57 -0
- package/src/er/StoreHeader.tsx +1 -0
- package/src/er/layout.test.ts +85 -2
- package/src/er/layout.ts +140 -5
- package/src/er/spec.test.ts +17 -0
- package/src/er/spec.ts +23 -10
- package/src/flow/Recordings.test.tsx +52 -0
- package/src/flow/Recordings.tsx +236 -0
- package/src/flow/StepDetail.tsx +53 -0
- package/src/flow/TraceTrial.tsx +419 -0
- package/src/flow/examples.test.ts +33 -0
- package/src/flow/examples.ts +37 -0
- package/src/flow/outline.test.ts +28 -0
- package/src/flow/outline.ts +7 -2
- package/src/flow/trace-trial-resume.test.ts +42 -0
- package/src/flow/trace-trial-resume.ts +74 -0
- package/src/graph/elk.ts +78 -0
- package/src/index.css +24 -0
- package/src/landing/LandingPage.tsx +15 -11
- package/src/landing/ProductTour.tsx +6 -6
- package/src/lib/all-problems.ts +27 -17
- package/src/lib/centrality.test.ts +251 -0
- package/src/lib/centrality.ts +232 -0
- package/src/lib/confluence.test.ts +37 -0
- package/src/lib/confluence.ts +41 -0
- package/src/lib/context-color.ts +1 -1
- package/src/lib/deployment-drift.ts +22 -0
- package/src/lib/derive.ts +43 -69
- package/src/lib/environments.test.ts +83 -0
- package/src/lib/environments.ts +57 -0
- package/src/lib/integration-url.test.ts +30 -0
- package/src/lib/integration-url.ts +63 -0
- package/src/lib/kafka-ui.ts +3 -45
- package/src/lib/local-api.ts +96 -2
- package/src/lib/notion.ts +13 -0
- package/src/lib/plugin-index.json +2765 -0
- package/src/lib/plugins.test.ts +68 -0
- package/src/lib/plugins.ts +264 -0
- package/src/lib/problem-flows.test.ts +61 -0
- package/src/lib/problem-flows.ts +78 -0
- package/src/lib/problem-rules-cel.d.mts +43 -0
- package/src/lib/problem-rules-cel.mjs +407 -0
- package/src/lib/problem-rules.test.ts +287 -0
- package/src/lib/problem-rules.ts +271 -0
- package/src/lib/problem-subjects.ts +737 -0
- package/src/lib/rule-entries.ts +39 -0
- package/src/lib/{data-problems.test.ts → rules-data.test.ts} +18 -16
- package/src/lib/rules-deploy.test.ts +133 -0
- package/src/lib/{problems.test.ts → rules-edges.test.ts} +8 -3
- package/src/lib/{proto-problems.test.ts → rules-proto.test.ts} +9 -25
- package/src/lib/{wire-problems.test.ts → rules-wire.test.ts} +13 -11
- package/src/lib/rules.test.ts +51 -0
- package/src/lib/rules.ts +86 -0
- package/src/lib/shape.test.ts +32 -0
- package/src/lib/shape.ts +30 -6
- package/src/lib/tech.ts +16 -0
- package/src/lib/trace-project.test.ts +34 -0
- package/src/lib/trace-project.ts +50 -0
- package/src/lib/use-problems.ts +23 -0
- package/src/lib/warnings.test.ts +9 -0
- package/src/lib/warnings.ts +10 -10
- package/src/likec4/ids.test.ts +6 -2
- package/src/likec4/ids.ts +43 -0
- package/src/main.tsx +23 -0
- package/src/merge-deployments.test.ts +127 -0
- package/src/merge.test.ts +82 -0
- package/src/merge.ts +173 -9
- package/src/pages/AggregatePage.tsx +57 -7
- package/src/pages/ContextMap.tsx +45 -3
- package/src/pages/ContextPage.tsx +2 -0
- package/src/pages/EventPage.tsx +15 -5
- package/src/pages/FlowDetail.tsx +23 -2
- package/src/pages/GraphPage.tsx +40 -3
- package/src/pages/Overview.tsx +152 -12
- package/src/pages/PluginIndex.tsx +190 -0
- package/src/pages/Problems.tsx +396 -128
- package/src/pages/ServicePage.tsx +58 -2
- package/src/pages/Settings.tsx +28 -2
- package/src/pages/settings/AboutSettings.tsx +8 -1
- package/src/pages/settings/IntegrationsSettings.tsx +63 -17
- package/src/pages/settings/RecordingSettings.tsx +138 -0
- package/src/pages/settings/RulesSettings.tsx +825 -0
- package/src/routes.test.ts +9 -0
- package/src/routes.ts +22 -1
- package/src/lib/data-problems.ts +0 -314
- package/src/lib/proto-problems.ts +0 -237
- package/src/lib/wire-problems.ts +0 -342
package/README.md
CHANGED
|
@@ -87,8 +87,9 @@ reference).
|
|
|
87
87
|
- **Diagrams** — LikeC4 C4 views (estate landscape, every container in the
|
|
88
88
|
estate with its technology and the protocol on each edge, one per context,
|
|
89
89
|
two per service, one dynamic view per flow), an ELK-routed dependency graph,
|
|
90
|
-
and a context map. The app never draws these itself; `npm run
|
|
91
|
-
|
|
90
|
+
and a context map. The app never draws these itself; `npm run gen` writes
|
|
91
|
+
the model from the catalog as its last step, and holds it to the catalog in
|
|
92
|
+
`gen:check`, and `npm run likec4:gen` writes it on its own before `dev`.
|
|
92
93
|
- **ER canvases** — per store: tables, views, keys and crow's feet, plus column
|
|
93
94
|
lineage (`from`) drawn dashed; hovering a column lights the whole chain back
|
|
94
95
|
to where the value came from.
|
|
@@ -116,7 +117,43 @@ first:
|
|
|
116
117
|
with a second writer, a table that no longer holds the aggregate it claims,
|
|
117
118
|
a column whose type has drifted from its field's, an outbox with no payload;
|
|
118
119
|
- a channel with a second publisher, an event on a channel its service does not
|
|
119
|
-
declare, a declared channel no event names, a subscription nothing publishes
|
|
120
|
+
declare, a declared channel no event names, a subscription nothing publishes;
|
|
121
|
+
- what the deployer runs from a place no service lives at, and where the GitOps
|
|
122
|
+
tree and the deployer disagree.
|
|
123
|
+
|
|
124
|
+
Each check is a rule in `rules/builtin.json`: a passport - id, severity, what
|
|
125
|
+
it looks for and what to do about a row - and the rule itself, in CEL over one
|
|
126
|
+
subject. A subject is one row with every fact a rule would otherwise have to
|
|
127
|
+
look up already on it - a table knows who writes it, a channel knows who else
|
|
128
|
+
publishes there, a call knows whether its peer is in the estate - so a rule is
|
|
129
|
+
one line a reader can read, copy and tighten. **Settings → Rules** lists them
|
|
130
|
+
with the rows each produces now. A shipped rule can be switched off or
|
|
131
|
+
re-graded in `portolan.json`, with a reason, and a rule of your own is written
|
|
132
|
+
there the same way - over a service, a call, a copy, an event, a consumer, a
|
|
133
|
+
channel, a subscription, a table, a column, a deployment, a flow or an
|
|
134
|
+
aggregate - and runs in the page the moment it is saved:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"problemRules": [
|
|
139
|
+
{ "id": "shared-store", "enabled": false, "reason": "the estate shares one database by design" },
|
|
140
|
+
{
|
|
141
|
+
"id": "team.quiet-event",
|
|
142
|
+
"over": "event",
|
|
143
|
+
"severity": "warning",
|
|
144
|
+
"title": "Event nobody consumes",
|
|
145
|
+
"when": "size(event.consumers) == 0 && !event.name.endsWith('Audit')",
|
|
146
|
+
"message": "'nothing consumes ' + event.id",
|
|
147
|
+
"peer": "event.service"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Expressions are type-checked against the subject's fields when the manifest is
|
|
154
|
+
read - `event.nme` is refused, not shown as an empty page - and again in the
|
|
155
|
+
page, by the same module. The Rules page writes the entry after a preview of
|
|
156
|
+
the rows it would add (portolan.0016, portolan.0017).
|
|
120
157
|
|
|
121
158
|
## Where the facts come from
|
|
122
159
|
|
|
@@ -125,17 +162,24 @@ Plugins, one JSON message in and one out (`plugins/README.md`), declared in
|
|
|
125
162
|
|
|
126
163
|
| phase | plugins |
|
|
127
164
|
| --- | --- |
|
|
128
|
-
| extract | `extract-project`, `extract-go`, `extract-ts`, `extract-rust`, `extract-java`, `extract-django`, `extract-celery`, `extract-python-kafka`, `extract-openapi`, `extract-wsdl`, `extract-http-clients`, `extract-redis`, `extract-asyncapi`, `extract-graphql`, `extract-proto`, `extract-river`, `extract-watermill`, `extract-go-nats`, `extract-csr`, `extract-sql`, `extract-flows`, `extract-adr`, `extract-glossary`, `extract-commands` |
|
|
165
|
+
| extract | `extract-project`, `extract-go`, `extract-ts`, `extract-rust`, `extract-java`, `extract-django`, `extract-laravel`, `extract-php-ddd`, `extract-csharp-ddd`, `extract-celery`, `extract-python-kafka`, `extract-openapi`, `extract-wsdl`, `extract-http-clients`, `extract-redis`, `extract-asyncapi`, `extract-graphql`, `extract-proto`, `extract-river`, `extract-watermill`, `extract-go-nats`, `extract-go-sqs`, `extract-csr`, `extract-sql`, `extract-flows`, `extract-adr`, `extract-glossary`, `extract-commands`, `extract-k8s`, `extract-argocd` |
|
|
129
166
|
| verify | `verify-otel` — reads traces, marks the hops they show as `verified`; `verify-codeowners` — reads CODEOWNERS, says who to ask about each service |
|
|
130
167
|
| generate | `gen-markdown` — `docs/`, `gen-mermaid` — standalone flow diagrams, `gen-backstage` — Backstage entities |
|
|
131
168
|
|
|
132
169
|
`fetch-git`, `fetch-bsr` and `fetch-csr` bring in sources from other
|
|
133
170
|
repositories, the Buf Schema Registry and a Confluent Schema Registry, against a
|
|
134
|
-
lock, so a later build can reproduce them without a socket.
|
|
171
|
+
lock, so a later build can reproduce them without a socket. `fetch-k8s` reads a
|
|
172
|
+
live cluster through `kubectl` into the names each service answers on and
|
|
173
|
+
dials, and replays the committed fragment when there is no cluster to ask.
|
|
174
|
+
`fetch-argocd` reads the applications an Argo CD server manages into a snapshot
|
|
175
|
+
of where each service runs - environment, cluster, namespace, revision, images -
|
|
176
|
+
against the same kind of lock; the service page shows it under "Where it runs".
|
|
135
177
|
|
|
136
178
|
Each plugin describes its own options; `npm run schema` asks all of them and
|
|
137
179
|
composes `schema/portolan.schema.json`, which editors complete against and `gen`
|
|
138
|
-
checks before running anything.
|
|
180
|
+
checks before running anything. The same answers are rendered as the site's
|
|
181
|
+
plugin reference - `/plugins` on any generated site, grouped by what each reads
|
|
182
|
+
or makes, with the options it takes - so the list above is the short form.
|
|
139
183
|
|
|
140
184
|
Non-fatal extractor diagnostics remain attached to their pipeline step. The
|
|
141
185
|
Settings page groups repetitions by plugin, stable rule and severity, and shows
|
|
@@ -257,6 +301,107 @@ live in a dedicated architecture repository: `fetch-git` pins the service
|
|
|
257
301
|
repositories at immutable commits and the normal merge, check, diff, and build
|
|
258
302
|
commands operate on the combined estate.
|
|
259
303
|
|
|
304
|
+
### A real one: Bagisto
|
|
305
|
+
|
|
306
|
+
The services under `examples/` are small on purpose. For what the catalog
|
|
307
|
+
looks like on a real application, the site ships a third profile: pick
|
|
308
|
+
**Bagisto** in the catalog selector. [Bagisto](https://github.com/bagisto/bagisto)
|
|
309
|
+
is an open-source e-commerce platform on Laravel - 41 packages, one per
|
|
310
|
+
module, talking to each other through named events - read by
|
|
311
|
+
`extract-laravel` alone, with nothing annotated for the catalog.
|
|
312
|
+
|
|
313
|
+
What comes out of Bagisto 2.4, pinned in `vendor/repos/bagisto/bagisto`:
|
|
314
|
+
|
|
315
|
+
| | |
|
|
316
|
+
| --- | --- |
|
|
317
|
+
| model groups | 28, one per package with models, 125 Eloquent models and 22 enums between them |
|
|
318
|
+
| events | 309, most of them named - `Event::dispatch('sales.order.cancel.after', …)` - owned by the package their name says, 50 with a listener |
|
|
319
|
+
| HTTP | 520 operations in 10 interfaces, one per route file, with an inferred OpenAPI document |
|
|
320
|
+
| database | 138 tables and 1300 columns replayed from 189 migrations, 185 foreign keys, 120 places the code reads or writes them |
|
|
321
|
+
| queues | 17 jobs on one queue, each a hop in the flow that dispatches it and a worker flow of its own |
|
|
322
|
+
| flows | 610 |
|
|
323
|
+
|
|
324
|
+
Go to the service and read it the way the tabs are ordered: **bus** for the
|
|
325
|
+
queue, **data** for the schema laid out by model group - the picker beside
|
|
326
|
+
the arrangement toggle switches groups off, the search brings a table up
|
|
327
|
+
close - and **flows** for `shop-checkout-onepage-orders-store`, the checkout
|
|
328
|
+
followed from the controller through the repository it holds to the events
|
|
329
|
+
and the job it ends in. Every "view source" link opens the file on GitHub at
|
|
330
|
+
the pinned commit.
|
|
331
|
+
|
|
332
|
+
The warnings the extractor left are findings about Bagisto, not about the
|
|
333
|
+
tool: 218 events under the admin package are dispatched by name and declare
|
|
334
|
+
no payload; `Settings\CurrencyController` answers admin routes and does not
|
|
335
|
+
exist; the sitemap job reads a `pages` table no migration creates; a
|
|
336
|
+
foreign key points at a `theme_customizations` table no migration creates;
|
|
337
|
+
one listener waits for `bagisto.shop.products.price.after`, which nothing
|
|
338
|
+
dispatches, and one for the repository events of a vendored package. How the
|
|
339
|
+
fragments were made, and how to refresh them, is in
|
|
340
|
+
`vendor/repos/bagisto/bagisto/README.md`; the reading rules are in
|
|
341
|
+
`plugins/extract-laravel/README.md`.
|
|
342
|
+
|
|
343
|
+
### The small one, by the book: CodelyTV php-ddd-example
|
|
344
|
+
|
|
345
|
+
The fourth profile, **CodelyTV php-ddd-example**, is the opposite kind of
|
|
346
|
+
real: [php-ddd-example](https://github.com/CodelyTV/php-ddd-example) is
|
|
347
|
+
CodelyTV's reference for DDD, hexagonal architecture and CQRS in PHP, a
|
|
348
|
+
Symfony monorepository laid out as `src/<Context>/<Module>/{Domain,Application,Infrastructure}`
|
|
349
|
+
with the deployables under `apps/`, read by `extract-php-ddd` alone - the
|
|
350
|
+
layout is the claim, and nothing is annotated. What comes out, pinned in
|
|
351
|
+
`vendor/repos/CodelyTV/php-ddd-example`: four contexts (Mooc, Backoffice,
|
|
352
|
+
Analytics, Retention), six services, five aggregates with their value
|
|
353
|
+
objects and three domain events on one RabbitMQ exchange, one queue per
|
|
354
|
+
subscriber, the MySQL tables from the Doctrine mappings including a JOINED
|
|
355
|
+
`steps` family, and sixteen flows. The one to read is
|
|
356
|
+
`backoffice-frontend-courses-post`: the back office's form dispatches
|
|
357
|
+
Mooc's `CreateCourseCommand` over an in-memory bus, so the flow crosses to
|
|
358
|
+
`mooc.backend` with a `call` step, records `CourseCreated`, saves the
|
|
359
|
+
course, and the subscribers in Mooc, Backoffice and Analytics pick the event
|
|
360
|
+
up from their own queues.
|
|
361
|
+
|
|
362
|
+
The warnings are findings about the project: Analytics and Retention have no
|
|
363
|
+
application under `apps/`, so their code deploys nowhere and Analytics'
|
|
364
|
+
subscriber to every event runs nowhere; `TrimVideoCommandHandler` is named
|
|
365
|
+
like a handler and implements neither bus interface; Retention's four modules
|
|
366
|
+
and Mooc's Notifications are empty scaffolding; the front end routes
|
|
367
|
+
`/api/courses` to a controller that does not exist. How the fragments were
|
|
368
|
+
made is in `vendor/repos/CodelyTV/php-ddd-example/README.md`; the reading
|
|
369
|
+
rules are in `plugins/extract-php-ddd/README.md`.
|
|
370
|
+
|
|
371
|
+
### The same lesson in C#: Modular Monolith with DDD
|
|
372
|
+
|
|
373
|
+
The fifth profile, **Modular Monolith with DDD**, is Kamil Grzybek's
|
|
374
|
+
[modular-monolith-with-ddd](https://github.com/kgrzybek/modular-monolith-with-ddd),
|
|
375
|
+
the reference for the pattern in .NET: five modules laid out as
|
|
376
|
+
`src/Modules/<Module>/{Domain,Application,Infrastructure,IntegrationEvents}`,
|
|
377
|
+
one API host, one SQL Server database with a schema per module, read by
|
|
378
|
+
`extract-csharp-ddd` alone - through Roslyn, the compiler's own parser, over
|
|
379
|
+
the tree as one compilation and without restoring a single package. What
|
|
380
|
+
comes out, pinned in `vendor/repos/kgrzybek/modular-monolith-with-ddd`: five
|
|
381
|
+
contexts with one service each, nineteen aggregates and five model groups,
|
|
382
|
+
sixty domain events dispatched in process and seven integration events on
|
|
383
|
+
the in-memory bus, sixty-one commands and twenty-nine queries, an internal command queue
|
|
384
|
+
per module, the forty-three tables and seventeen views of the database
|
|
385
|
+
project with the root tables mapped to their aggregates through the EF
|
|
386
|
+
configurations, fifty-one HTTP operations across four documents, the
|
|
387
|
+
project's seventeen decision records, and a hundred and two flows. The one
|
|
388
|
+
to read is `register-new-user`: the User Access controller hands
|
|
389
|
+
`RegisterNewUserCommand` to the Registrations module's facade in process,
|
|
390
|
+
the flow crosses with a `call` step, `NewUserRegisteredDomainEvent` is
|
|
391
|
+
raised and saved, and from there the outbox republishes it to the bus as an
|
|
392
|
+
integration event that Meetings, Administration and Payments each hear and
|
|
393
|
+
turn into a job on their own internal queue.
|
|
394
|
+
|
|
395
|
+
The warnings are findings about the project: `MemberCreatedIntegrationEvent`
|
|
396
|
+
is declared and published nowhere while User Access subscribes to it;
|
|
397
|
+
Payments subscribes to `MeetingGroupProposalAcceptedIntegrationEvent` and
|
|
398
|
+
has no handler; the `app` schema belongs to no module; the view is created
|
|
399
|
+
as `v_Countriess` and the query reads `v_Countries`; and two modules keep a
|
|
400
|
+
`Domain/Users` directory with a typed id and nothing to hold it. How the
|
|
401
|
+
fragments were made is in
|
|
402
|
+
`vendor/repos/kgrzybek/modular-monolith-with-ddd/README.md`; the reading
|
|
403
|
+
rules are in `plugins/extract-csharp-ddd/README.md`.
|
|
404
|
+
|
|
260
405
|
## Develop Portolan itself
|
|
261
406
|
|
|
262
407
|
```bash
|
package/catalog/model.go
CHANGED
|
@@ -44,9 +44,59 @@ type Catalog struct {
|
|
|
44
44
|
Modules []ProtoModule `json:"modules,omitempty"`
|
|
45
45
|
Terms []Term `json:"terms,omitempty"`
|
|
46
46
|
Repos []RepoPin `json:"repos,omitempty"`
|
|
47
|
+
Deployments []Deployment `json:"deployments,omitempty"`
|
|
47
48
|
Externals []External `json:"externals,omitempty"`
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
// Deployment is one place a service runs: an Argo CD Application as the
|
|
52
|
+
// deployer listed it, reduced to what a deploy changes. A list on the catalog
|
|
53
|
+
// rather than a field on Service: the fetcher that writes it reads a control
|
|
54
|
+
// plane and does not know which service an Application is, and one service
|
|
55
|
+
// stands in several places.
|
|
56
|
+
type Deployment struct {
|
|
57
|
+
// ID is "<argocd namespace>/<application name>".
|
|
58
|
+
ID string `json:"id"`
|
|
59
|
+
Name string `json:"name"`
|
|
60
|
+
Project string `json:"project"`
|
|
61
|
+
Environment string `json:"environment"`
|
|
62
|
+
Cluster string `json:"cluster"`
|
|
63
|
+
Namespace string `json:"namespace"`
|
|
64
|
+
// Repo is spelled the way Service.Repo spells it; empty for a chart
|
|
65
|
+
// from a registry.
|
|
66
|
+
Repo string `json:"repo"`
|
|
67
|
+
Path string `json:"path"`
|
|
68
|
+
// Chart is the Helm chart name when the source is a registry.
|
|
69
|
+
Chart string `json:"chart,omitempty"`
|
|
70
|
+
TargetRevision string `json:"targetRevision"`
|
|
71
|
+
Revision string `json:"revision"`
|
|
72
|
+
// Tool is helm, kustomize, directory or plugin, as Argo CD says it.
|
|
73
|
+
Tool string `json:"tool"`
|
|
74
|
+
URL string `json:"url"`
|
|
75
|
+
// Service is the id of the service the Application's labels name;
|
|
76
|
+
// empty when they name none, and the path decides.
|
|
77
|
+
Service string `json:"service,omitempty"`
|
|
78
|
+
Images []string `json:"images,omitempty"`
|
|
79
|
+
// Basis says who said so: "manifest" for a row read out of a GitOps
|
|
80
|
+
// tree, "api" for one read off the deployer, "both" for one the merge
|
|
81
|
+
// folded from the two. Empty reads as "api", which is what every row
|
|
82
|
+
// was before there were trees to read.
|
|
83
|
+
Basis string `json:"basis,omitempty"`
|
|
84
|
+
// Drift is what the tree says where it and the deployer disagree, set
|
|
85
|
+
// by the merge when both spoke (portolan.0013).
|
|
86
|
+
Drift *DeploymentDrift `json:"drift,omitempty"`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// DeploymentDrift is the GitOps tree's word on the fields where the
|
|
90
|
+
// deployer says otherwise. A field is set only when the two differ.
|
|
91
|
+
type DeploymentDrift struct {
|
|
92
|
+
Project string `json:"project,omitempty"`
|
|
93
|
+
Cluster string `json:"cluster,omitempty"`
|
|
94
|
+
Namespace string `json:"namespace,omitempty"`
|
|
95
|
+
Path string `json:"path,omitempty"`
|
|
96
|
+
TargetRevision string `json:"targetRevision,omitempty"`
|
|
97
|
+
Images []string `json:"images,omitempty"`
|
|
98
|
+
}
|
|
99
|
+
|
|
50
100
|
// External is a system outside the estate with a contract: what it answers on,
|
|
51
101
|
// read from the copy of its document vendored beside the adapter that calls it,
|
|
52
102
|
// and nothing else. It sits at the root beside the contexts, so its id is its
|
|
@@ -160,6 +210,23 @@ type Service struct {
|
|
|
160
210
|
// list is the one the runner would accept. Absent when nothing declares
|
|
161
211
|
// any, which is not the same as a service that cannot be built.
|
|
162
212
|
Commands []Command `json:"commands,omitempty"`
|
|
213
|
+
|
|
214
|
+
// Hosts are the names this service answers on, read from what deploys
|
|
215
|
+
// it: a Kubernetes Service's name in its short, namespaced, `svc` and
|
|
216
|
+
// fully qualified forms, and the hosts of the Ingress or HTTPRoute in
|
|
217
|
+
// front of it. Written so that a call another service is configured to
|
|
218
|
+
// make to `pricing.shop.svc` can find the service that answers. Absent
|
|
219
|
+
// when nothing in the tree says where the service is reachable.
|
|
220
|
+
Hosts []string `json:"hosts,omitempty"`
|
|
221
|
+
|
|
222
|
+
// Dials are the in-cluster names this service's workload is configured
|
|
223
|
+
// to reach, read out of its environment and config maps and reduced to
|
|
224
|
+
// the host alone. A value is never kept: not the variable it came from,
|
|
225
|
+
// not the scheme, port, path or credentials around the name. Only a name
|
|
226
|
+
// the cluster resolves qualifies - a Service in the same tree, or a
|
|
227
|
+
// `<name>.<namespace>.svc` form - so a password in an environment
|
|
228
|
+
// variable is not something this list can hold by shape.
|
|
229
|
+
Dials []string `json:"dials,omitempty"`
|
|
163
230
|
}
|
|
164
231
|
|
|
165
232
|
// Command is one entry of a task runner's file: a make target, an npm script,
|
|
@@ -477,6 +544,12 @@ type Operation struct {
|
|
|
477
544
|
// appear in RpcService.Methods. Empty means nothing outside the service can
|
|
478
545
|
// reach it, which is a fact worth having rather than a gap.
|
|
479
546
|
ExposedBy []string `json:"exposedBy,omitempty"`
|
|
547
|
+
// Fields is what the caller hands in: the command's or query's own shape
|
|
548
|
+
// as the message class declares it. Nil when the extractor does not read
|
|
549
|
+
// messages; an empty list is a message that carries nothing.
|
|
550
|
+
Fields []Field `json:"fields,omitempty"`
|
|
551
|
+
// Source is where the handler is, "path:line".
|
|
552
|
+
Source string `json:"source,omitempty"`
|
|
480
553
|
}
|
|
481
554
|
|
|
482
555
|
// Block is an entity or a value object. The two are told apart by the list
|
|
@@ -603,6 +676,30 @@ type Field struct {
|
|
|
603
676
|
// Number is the protobuf field number when the source carries one. Other
|
|
604
677
|
// schema and domain extractors leave it absent.
|
|
605
678
|
Number int `json:"number,omitempty"`
|
|
679
|
+
// Required is set when the source says the field must be sent: a
|
|
680
|
+
// Protovalidate `required`, a name in a JSON Schema `required` list.
|
|
681
|
+
// Absent means the source does not say, which in proto3 and OpenAPI alike
|
|
682
|
+
// means it may be left out.
|
|
683
|
+
Required bool `json:"required,omitempty"`
|
|
684
|
+
// Rules is what the source says a value must satisfy, in the order it
|
|
685
|
+
// said it.
|
|
686
|
+
Rules []FieldRule `json:"rules,omitempty"`
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// FieldRule is one constraint on a field's value, in the catalog's own
|
|
690
|
+
// vocabulary so a Protovalidate `min_len` and a JSON Schema `minLength` are
|
|
691
|
+
// one rule: `min_len`, `max_len`, `len`, `pattern`, `prefix`, `suffix`,
|
|
692
|
+
// `contains`, `not_contains`, `format`, `gt`, `gte`, `lt`, `lte`, `const`,
|
|
693
|
+
// `in`, `not_in`, `multiple_of`, `min_items`, `max_items`, `unique`,
|
|
694
|
+
// `min_pairs`, `max_pairs`, `defined_only`, `lt_now`, `gt_now`, `cel`. A rule
|
|
695
|
+
// on what a list holds is prefixed `items.`; on a map's keys or values,
|
|
696
|
+
// `keys.` or `values.`. A custom option the catalog has no word for keeps the
|
|
697
|
+
// name the source gave it, `(acme.pii)`, so it is shown rather than lost.
|
|
698
|
+
type FieldRule struct {
|
|
699
|
+
Name string `json:"name"`
|
|
700
|
+
// Value is the bound as the source wrote it, text so a 64-bit number
|
|
701
|
+
// survives; absent for a bare flag such as `unique`.
|
|
702
|
+
Value string `json:"value,omitempty"`
|
|
606
703
|
}
|
|
607
704
|
|
|
608
705
|
type TypeDef struct {
|
|
@@ -619,6 +716,7 @@ const (
|
|
|
619
716
|
StoreKindMongoDB StoreKind = "mongodb"
|
|
620
717
|
StoreKindClickHouse StoreKind = "clickhouse"
|
|
621
718
|
StoreKindS3 StoreKind = "s3"
|
|
719
|
+
StoreKindDynamoDB StoreKind = "dynamodb"
|
|
622
720
|
StoreKindOther StoreKind = "other"
|
|
623
721
|
)
|
|
624
722
|
|
|
@@ -788,6 +886,48 @@ type Flow struct {
|
|
|
788
886
|
// Participants order is significant: it is the lane order.
|
|
789
887
|
Participants []Participant `json:"participants"`
|
|
790
888
|
Steps FlowNodes `json:"steps"`
|
|
889
|
+
// Examples are recordings of this flow running: one per trace a verifier
|
|
890
|
+
// kept, each naming the steps it showed with what the spans said about
|
|
891
|
+
// them. They are examples of data, not evidence - the evidence is the
|
|
892
|
+
// status on the step.
|
|
893
|
+
Examples []FlowExample `json:"examples,omitempty"`
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// FlowExample is one recorded run of a flow, read from one trace.
|
|
897
|
+
type FlowExample struct {
|
|
898
|
+
// ID is "<recording>#<trace id>": unique within the flow, stable across
|
|
899
|
+
// runs of the same files.
|
|
900
|
+
ID string `json:"id"`
|
|
901
|
+
// Recording is the file the trace was read from, relative to the step's
|
|
902
|
+
// input root - where the verifier was pointed, which is where an uploaded
|
|
903
|
+
// recording is kept.
|
|
904
|
+
Recording string `json:"recording"`
|
|
905
|
+
TraceID string `json:"traceId"`
|
|
906
|
+
// RecordedAt is when the root span started, RFC 3339 in UTC; absent when
|
|
907
|
+
// the recording carries no clock.
|
|
908
|
+
RecordedAt string `json:"recordedAt,omitempty"`
|
|
909
|
+
// DurationMs is root start to the last end the trace shows.
|
|
910
|
+
DurationMs float64 `json:"durationMs"`
|
|
911
|
+
// Steps are the steps this trace showed, in the order it showed them,
|
|
912
|
+
// with what an allowlist of span attributes said about each.
|
|
913
|
+
Steps []ExampleStep `json:"steps"`
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// ExampleStep is what one span said about one step: how long it took and
|
|
917
|
+
// the names it carried. Values that could be somebody's data - a query text,
|
|
918
|
+
// a header, a path with an id in it - are not carried; see verify-otel.
|
|
919
|
+
type ExampleStep struct {
|
|
920
|
+
Step string `json:"step"`
|
|
921
|
+
Label string `json:"label,omitempty"`
|
|
922
|
+
DurationMs float64 `json:"durationMs"`
|
|
923
|
+
Attributes map[string]string `json:"attributes,omitempty"`
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// StepSeen says how many recordings showed a step. On a declared step it
|
|
927
|
+
// accompanies the raised status; on a step the code does not declare it is
|
|
928
|
+
// the reason the step is in the flow at all.
|
|
929
|
+
type StepSeen struct {
|
|
930
|
+
Traces int `json:"traces"`
|
|
791
931
|
}
|
|
792
932
|
|
|
793
933
|
type FlowTrigger struct {
|
|
@@ -926,6 +1066,9 @@ type Step struct {
|
|
|
926
1066
|
// enriched with its Redis operation and key family after store fragments
|
|
927
1067
|
// have been merged into the catalog.
|
|
928
1068
|
StoreAccess *FlowStoreAccess `json:"storeAccess,omitempty"`
|
|
1069
|
+
// Seen counts the recordings that showed this hop. A verifier sets it;
|
|
1070
|
+
// on a step no source declares, it is what put the step here.
|
|
1071
|
+
Seen *StepSeen `json:"seen,omitempty"`
|
|
929
1072
|
}
|
|
930
1073
|
|
|
931
1074
|
func (*Step) NodeType() string { return "step" }
|
|
@@ -985,6 +1128,10 @@ type AltBranch struct {
|
|
|
985
1128
|
// Without it, the steps drawn after the alt read as if they follow this
|
|
986
1129
|
// branch too.
|
|
987
1130
|
Terminal bool `json:"terminal,omitempty"`
|
|
1131
|
+
// Seen counts the recordings that went this way, on a frame a verifier
|
|
1132
|
+
// wrote where recordings parted. A branch with no steps and a count is
|
|
1133
|
+
// the recordings that went no further.
|
|
1134
|
+
Seen *StepSeen `json:"seen,omitempty"`
|
|
988
1135
|
}
|
|
989
1136
|
|
|
990
1137
|
type Loop struct {
|
|
@@ -27,6 +27,13 @@ func TestRoundTrip(t *testing.T) {
|
|
|
27
27
|
if len(files) == 0 {
|
|
28
28
|
t.Fatal("no catalogs under ../data - this test proves nothing without one")
|
|
29
29
|
}
|
|
30
|
+
// The snapshot a fetcher wrote is a catalog too, and the one place the
|
|
31
|
+
// deployments field is exercised by a real file.
|
|
32
|
+
snapshots, err := filepath.Glob("../examples/argocd/argocd.apps.json")
|
|
33
|
+
if err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
files = append(files, snapshots...)
|
|
30
37
|
|
|
31
38
|
for _, path := range files {
|
|
32
39
|
t.Run(filepath.Base(path), func(t *testing.T) {
|
package/cli/portolan.mjs
CHANGED
|
@@ -213,7 +213,9 @@ export async function prepareSite(workspace) {
|
|
|
213
213
|
rmSync(stage, { recursive: true, force: true });
|
|
214
214
|
mkdirSync(stage, { recursive: true });
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
// rules/ rides along: scripts/manifest.mjs and src/lib/problem-rules.ts read
|
|
217
|
+
// ../rules/builtin.json beside themselves.
|
|
218
|
+
for (const directory of ["src", "public", "scripts", "rules"]) {
|
|
217
219
|
cpSync(resolve(installRoot, directory), resolve(stage, directory), { recursive: true });
|
|
218
220
|
}
|
|
219
221
|
for (const file of ["index.html", "vite.config.ts", "tsconfig.json"]) {
|
|
@@ -382,6 +384,9 @@ function prepareHost(workspace) {
|
|
|
382
384
|
mkdirSync(host, { recursive: true });
|
|
383
385
|
cpSync(resolve(installRoot, "scripts"), resolve(host, "scripts"), { recursive: true });
|
|
384
386
|
cpSync(resolve(installRoot, "src"), resolve(host, "src"), { recursive: true });
|
|
387
|
+
// scripts/manifest.mjs and src/lib/problem-rules.ts read ../rules/builtin.json
|
|
388
|
+
// beside themselves; the host is the package with the same shape.
|
|
389
|
+
cpSync(resolve(installRoot, "rules"), resolve(host, "rules"), { recursive: true });
|
|
385
390
|
writeFileSync(resolve(host, "package.json"), `${JSON.stringify({ private: true, type: "module", version: VERSION })}\n`);
|
|
386
391
|
symlinkSync(dependencyRoot(), resolve(host, "node_modules"), process.platform === "win32" ? "junction" : "dir");
|
|
387
392
|
return host;
|
package/go.mod
CHANGED
|
@@ -3,12 +3,19 @@ module github.com/shortlink-org/portolan
|
|
|
3
3
|
go 1.27.0
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
|
+
github.com/hashicorp/hcl/v2 v2.24.0
|
|
6
7
|
github.com/pgplex/pgparser v0.2.0
|
|
8
|
+
github.com/zclconf/go-cty v1.19.0
|
|
7
9
|
golang.org/x/tools v0.50.0
|
|
8
10
|
gopkg.in/yaml.v3 v3.0.1
|
|
9
11
|
)
|
|
10
12
|
|
|
11
13
|
require (
|
|
14
|
+
github.com/agext/levenshtein v1.2.1 // indirect
|
|
15
|
+
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
|
16
|
+
github.com/apparentlymart/go-textseg/v17 v17.0.1 // indirect
|
|
17
|
+
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
|
12
18
|
golang.org/x/mod v0.41.0 // indirect
|
|
13
19
|
golang.org/x/sync v0.23.0 // indirect
|
|
20
|
+
golang.org/x/text v0.25.0 // indirect
|
|
14
21
|
)
|
package/go.sum
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
|
+
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
|
2
|
+
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
|
3
|
+
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
|
4
|
+
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
|
5
|
+
github.com/apparentlymart/go-textseg/v17 v17.0.1 h1:bpMXRgQ5cEoRNuQke1a80/Nl6w3G5eoIbWo9f3gXkAs=
|
|
6
|
+
github.com/apparentlymart/go-textseg/v17 v17.0.1/go.mod h1:fa8X4jgGeevslICIY6LcdjkSecWnXmYd9Lk34z/VxZs=
|
|
7
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
8
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
9
|
+
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
|
10
|
+
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
|
1
11
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
2
12
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
13
|
+
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
|
14
|
+
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
|
15
|
+
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
|
16
|
+
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
|
3
17
|
github.com/pgplex/pgparser v0.2.0 h1:BB7I/Mrwetiw07PTq/AlXcKfajlCwoJfwB7KZ7V9irI=
|
|
4
18
|
github.com/pgplex/pgparser v0.2.0/go.mod h1:DWhK7oZJn/16KTFeOQ+mk78+sRnEYya1XPghIoP9NFU=
|
|
5
|
-
|
|
6
|
-
|
|
19
|
+
github.com/zclconf/go-cty v1.19.0 h1:IV8WdqYZc2c5rLX9bEoLNXKojBAp0MZPBHMIrCoa/s4=
|
|
20
|
+
github.com/zclconf/go-cty v1.19.0/go.mod h1:12W89jGn3JCOIQi7infWr9m80rOkb5RNYJqXMZcN4c8=
|
|
21
|
+
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
|
22
|
+
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
|
7
23
|
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
|
8
24
|
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
|
9
|
-
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
|
10
|
-
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
|
11
25
|
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
|
12
26
|
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
|
13
|
-
golang.org/x/
|
|
14
|
-
golang.org/x/
|
|
27
|
+
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
|
28
|
+
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
|
15
29
|
golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU=
|
|
16
30
|
golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0=
|
|
17
31
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@shortlink-org/portolan",
|
|
3
3
|
"description": "Generate a navigable architecture catalog from code and specifications.",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://github.com/shortlink-org/portolan#readme",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"cli",
|
|
24
|
+
"rules",
|
|
24
25
|
"scripts",
|
|
25
26
|
"src",
|
|
26
27
|
"public",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"build": "node cli/portolan.mjs build",
|
|
62
63
|
"preview": "vite preview",
|
|
63
64
|
"test": "vitest run",
|
|
64
|
-
"test:go": "go test ./...",
|
|
65
|
+
"test:go": "GOFLAGS=-mod=mod go test ./...",
|
|
65
66
|
"pretest:package": "npm run plugins:build",
|
|
66
67
|
"test:package": "node scripts/package-smoke.mjs",
|
|
67
68
|
"test:watch": "vitest",
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
"likec4:gen": "node scripts/gen-likec4.mjs && likec4 gen react likec4 -o src/likec4/generated.jsx --no-use-dot",
|
|
70
71
|
"likec4:validate": "likec4 validate likec4",
|
|
71
72
|
"predev": "npm run likec4:gen",
|
|
72
|
-
"plugins:build": "GOOS=wasip1 GOARCH=wasm go build -trimpath -ldflags=\"-s -w\" -o plugins/portolan-go.wasm ./plugins/cmd/portolan-go && javac --release 21 -d plugins/extract-java/build plugins/extract-java/src/org/portolan/extract/*.java",
|
|
73
|
+
"plugins:build": "GOFLAGS=-mod=mod GOOS=wasip1 GOARCH=wasm go build -trimpath -ldflags=\"-s -w\" -o plugins/portolan-go.wasm ./plugins/cmd/portolan-go && javac --release 21 -d plugins/extract-java/build plugins/extract-java/src/org/portolan/extract/*.java && dotnet build plugins/extract-csharp-ddd -c Release --nologo -v q",
|
|
73
74
|
"schema": "npm run plugins:build && node scripts/schema.mjs",
|
|
74
75
|
"schema:check": "npm run plugins:build && node scripts/schema.mjs --check",
|
|
75
76
|
"gen": "npm run plugins:build && node scripts/gen.mjs",
|
|
@@ -78,8 +79,8 @@
|
|
|
78
79
|
"prepack": "npm run plugins:build"
|
|
79
80
|
},
|
|
80
81
|
"dependencies": {
|
|
81
|
-
"@ai-sdk/openai-compatible": "3.0.
|
|
82
|
-
"@ai-sdk/react": "4.0.
|
|
82
|
+
"@ai-sdk/openai-compatible": "3.0.48",
|
|
83
|
+
"@ai-sdk/react": "4.0.102",
|
|
83
84
|
"@asyncapi/react-component": "^3.1.8",
|
|
84
85
|
"@clack/prompts": "^1.8.0",
|
|
85
86
|
"@floating-ui/react": "^0.27.20",
|
|
@@ -89,16 +90,16 @@
|
|
|
89
90
|
"@tailwindcss/vite": "^4.0.0",
|
|
90
91
|
"@tanstack/react-query": "^5.102.8",
|
|
91
92
|
"@tanstack/react-table": "^9.2.4",
|
|
92
|
-
"@tanstack/react-virtual": "^3.14.
|
|
93
|
+
"@tanstack/react-virtual": "^3.14.12",
|
|
93
94
|
"@vitejs/plugin-react": "^6.1.1",
|
|
94
95
|
"@xyflow/react": "^12.11.5",
|
|
95
|
-
"ai": "7.0.
|
|
96
|
+
"ai": "7.0.99",
|
|
96
97
|
"ajv": "^8.20.0",
|
|
97
98
|
"elkjs": "^0.12.0",
|
|
98
99
|
"html-to-image": "^1.11.13",
|
|
99
100
|
"likec4": "^1.59.2",
|
|
100
101
|
"lowlight": "^3.3.0",
|
|
101
|
-
"lucide-react": "^1.
|
|
102
|
+
"lucide-react": "^1.45.0",
|
|
102
103
|
"mermaid": "^12.0.0",
|
|
103
104
|
"motion": "^13.2.0",
|
|
104
105
|
"oxc-parser": "^0.149.0",
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
"remark-gfm": "^4.0.0",
|
|
112
113
|
"simple-icons": "^16.30.0",
|
|
113
114
|
"tailwindcss": "^4.0.0",
|
|
114
|
-
"vite": "^8.
|
|
115
|
+
"vite": "^8.3.0",
|
|
115
116
|
"vite-plugin-node-polyfills": "^0.28.0",
|
|
116
117
|
"zustand": "^5.0.15"
|
|
117
118
|
},
|
package/plugin/describe.go
CHANGED
|
@@ -32,6 +32,40 @@ const (
|
|
|
32
32
|
PhaseGenerate = "generate"
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
+
// Categories a plugin sorts itself under on the plugin index: what it reads,
|
|
36
|
+
// or what it makes. A phase says where in a run a plugin goes; a category
|
|
37
|
+
// says what kind of fact it is after, which is the question a reader choosing
|
|
38
|
+
// extractors for an estate is asking.
|
|
39
|
+
const (
|
|
40
|
+
// CategoryCode reads a language: the aggregates, events and use cases a
|
|
41
|
+
// service declares in its source.
|
|
42
|
+
CategoryCode = "code"
|
|
43
|
+
// CategoryContracts reads an interface description - OpenAPI, protobuf,
|
|
44
|
+
// GraphQL, WSDL, AsyncAPI, a registry's schemas.
|
|
45
|
+
CategoryContracts = "contracts"
|
|
46
|
+
// CategoryMessaging reads queues, subjects, topics and the jobs on them.
|
|
47
|
+
CategoryMessaging = "messaging"
|
|
48
|
+
// CategoryData reads the stores a service keeps and their shape.
|
|
49
|
+
CategoryData = "data"
|
|
50
|
+
// CategoryInfrastructure reads what the service is deployed on - the
|
|
51
|
+
// queues, topics, functions and stores a Terraform module declares, and
|
|
52
|
+
// the wiring between them that only the infrastructure knows.
|
|
53
|
+
CategoryInfrastructure = "infrastructure"
|
|
54
|
+
// CategoryRepository reads what the repository says about itself: its
|
|
55
|
+
// metadata, its task runners.
|
|
56
|
+
CategoryRepository = "repository"
|
|
57
|
+
// CategoryDocuments reads what people wrote by hand: decisions, glossaries,
|
|
58
|
+
// flows.
|
|
59
|
+
CategoryDocuments = "documents"
|
|
60
|
+
// CategoryEvidence checks the catalog against something outside the code:
|
|
61
|
+
// traces, ownership rules.
|
|
62
|
+
CategoryEvidence = "evidence"
|
|
63
|
+
// CategorySources brings in trees from elsewhere for the extractors to read.
|
|
64
|
+
CategorySources = "sources"
|
|
65
|
+
// CategoryExports turns the catalog into something else.
|
|
66
|
+
CategoryExports = "exports"
|
|
67
|
+
)
|
|
68
|
+
|
|
35
69
|
// Descriptor is a plugin's answer to KindDescribe.
|
|
36
70
|
type Descriptor struct {
|
|
37
71
|
// Name is what the plugin calls itself. The manifest is free to declare it
|
|
@@ -42,6 +76,10 @@ type Descriptor struct {
|
|
|
42
76
|
// Summary is one line, and it is the line an editor shows beside the step.
|
|
43
77
|
Summary string `json:"summary,omitempty"`
|
|
44
78
|
|
|
79
|
+
// Category is one of the Category constants: what the plugin reads or
|
|
80
|
+
// makes, which is how the plugin index groups it.
|
|
81
|
+
Category string `json:"category"`
|
|
82
|
+
|
|
45
83
|
// Phases are the manifest sections this plugin belongs in.
|
|
46
84
|
Phases []string `json:"phases"`
|
|
47
85
|
|