@shortlink-org/portolan 0.2.4 → 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 +174 -6
- package/catalog/enum_test.go +46 -0
- package/catalog/evidence_test.go +35 -0
- package/catalog/model.go +1213 -0
- package/catalog/roundtrip_test.go +210 -0
- package/catalog/via_test.go +38 -0
- package/cli/init.test.mjs +6 -1
- package/cli/portolan.mjs +14 -1
- package/cli/portolan.test.mjs +49 -0
- package/go.mod +21 -0
- package/go.sum +34 -0
- package/internal/gocall/README.md +19 -0
- package/internal/gocall/analyze.go +189 -0
- package/internal/gocall/analyze_test.go +107 -0
- package/internal/gohttp/analyze.go +2562 -0
- package/internal/gohttp/destination.go +373 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +96 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/index.go +629 -0
- package/internal/goscan/index_test.go +66 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +37 -0
- package/internal/goscan/tree.go +284 -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 +70 -0
- package/internal/wsdl/parse.go +949 -0
- package/internal/wsdl/parse_test.go +170 -0
- package/package.json +28 -15
- package/plugin/describe.go +156 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +141 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +404 -50
- package/plugins/cmd/portolan-http-clients/main.go +19 -0
- package/plugins/extract-argocd/options.schema.json +44 -0
- package/plugins/extract-celery/extract.py +0 -2
- package/plugins/extract-celery/extract_test.py +1 -1
- 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 +52 -19
- package/plugins/extract-django/choices.py +47 -0
- package/plugins/extract-django/domain.py +30 -18
- package/plugins/extract-django/extract.py +21 -7
- package/plugins/extract-django/extract_test.py +68 -2
- package/plugins/extract-django/lifecycle.py +4 -28
- package/plugins/extract-django/main.py +1 -0
- package/plugins/extract-django/operations.py +1 -1
- package/plugins/extract-django/routing_test.py +109 -1
- package/plugins/extract-django/rules.py +154 -0
- package/plugins/extract-django/rules_test.py +158 -0
- package/plugins/extract-django/store.py +1 -1
- package/plugins/extract-django/transport.py +101 -55
- package/plugins/extract-django/verbs.py +241 -0
- package/plugins/extract-go/README.md +47 -0
- package/plugins/extract-go-sqs/options.schema.json +21 -0
- package/plugins/extract-http-clients/describe.go +20 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +740 -0
- package/plugins/extract-http-clients/extract_test.go +1561 -0
- package/plugins/extract-http-clients/main.go +41 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.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-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/extract.py +0 -2
- package/plugins/extract-python-kafka/extract_test.py +1 -1
- package/plugins/extract-python-kafka/main.py +1 -0
- package/plugins/extract-terraform/options.schema.json +26 -0
- package/plugins/extract-ts/extract.test.ts +2 -2
- package/plugins/extract-ts/extract.ts +4 -5
- package/plugins/extract-ts/graphql.test.ts +1 -1
- package/plugins/extract-ts/main.ts +1 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -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/pyplugin/protocol.py +1 -5
- package/plugins/verify-otel/options.schema.json +12 -0
- package/portolan.json +132 -4
- package/rules/builtin.json +218 -0
- package/schema/portolan.schema.json +905 -4
- package/scripts/README.md +21 -13
- package/scripts/catalog-sources.mjs +6 -0
- package/scripts/delivery-presets.mjs +57 -14
- package/scripts/diff.mjs +5 -1
- package/scripts/django-aggregates.test.mjs +58 -0
- package/scripts/gen-likec4.mjs +150 -17
- package/scripts/gen-likec4.test.mjs +96 -0
- package/scripts/gen.mjs +148 -118
- package/scripts/gitops-example.test.mjs +108 -0
- package/scripts/go-discovery.test.mjs +30 -0
- package/scripts/history.mjs +186 -3
- package/scripts/history.test.mjs +1 -1
- 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 +78 -21
- package/scripts/host-plugins/fetch-git.test.mjs +62 -8
- 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 +287 -12
- package/scripts/local-api.test.mjs +150 -5
- package/scripts/local-discovery.mjs +94 -9
- package/scripts/manifest.mjs +14 -3
- package/scripts/manifest.test.mjs +24 -0
- package/scripts/output-diff.mjs +94 -0
- package/scripts/output-diff.test.mjs +36 -0
- package/scripts/package-smoke.mjs +62 -4
- package/scripts/plugin-host.mjs +45 -3
- package/scripts/plugin-host.test.mjs +9 -0
- package/scripts/plugin-wasm-worker.mjs +4 -1
- package/scripts/plugins-fresh.mjs +97 -0
- package/scripts/plugins-fresh.test.mjs +64 -0
- package/scripts/provenance.mjs +72 -0
- package/scripts/provenance.test.mjs +149 -0
- package/scripts/run-builtin.mjs +62 -7
- package/scripts/schema.mjs +167 -19
- package/scripts/trace-trials.mjs +176 -0
- package/scripts/trace-trials.test.mjs +142 -0
- package/scripts/warning-policy.mjs +167 -0
- package/scripts/warning-policy.test.mjs +93 -0
- 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/Sidebar.tsx +3 -3
- package/src/app/SidebarFooter.tsx +20 -4
- package/src/catalog-docs.test.ts +64 -0
- package/src/catalog-docs.ts +35 -0
- package/src/catalog-error.test.ts +15 -0
- package/src/catalog-index.ts +25 -0
- package/src/catalog-model.ts +293 -5
- package/src/catalog-validation.ts +113 -2
- package/src/catalog.test.ts +40 -1
- package/src/chat/Starter.tsx +5 -11
- package/src/chat/tools.test.ts +27 -0
- package/src/chat/tools.ts +5 -9
- package/src/components/CatalogStamp.tsx +10 -8
- 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.test.tsx +23 -0
- package/src/components/HTTPDestinationEvidence.tsx +31 -0
- package/src/components/Integrations.tsx +1 -1
- package/src/components/MachineDocs.tsx +6 -5
- package/src/components/MethodRows.tsx +9 -2
- package/src/components/PluginIcon.tsx +77 -0
- package/src/components/ProblemRow.tsx +173 -153
- package/src/components/RelationEvidence.test.tsx +14 -0
- package/src/components/RelationEvidence.tsx +53 -0
- package/src/components/RuleMarks.tsx +22 -0
- package/src/components/ShapeRows.tsx +24 -20
- package/src/data.ts +46 -7
- package/src/enrich.test.ts +459 -4
- package/src/enrich.ts +308 -7
- 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 +59 -0
- package/src/flow/TraceTrial.tsx +419 -0
- package/src/flow/evidence.test.ts +16 -0
- package/src/flow/evidence.ts +34 -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 +68 -0
- package/src/landing/DraggableReveal.tsx +3 -2
- package/src/landing/EvidencePipeline.tsx +105 -0
- package/src/landing/LandingPage.tsx +17 -70
- package/src/landing/ProductTour.tsx +6 -6
- package/src/lib/all-problems.ts +27 -17
- package/src/lib/catalog-diff.ts +1 -1
- 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/django-aggregates.d.mts +9 -0
- package/src/lib/django-aggregates.mjs +36 -0
- package/src/lib/django-aggregates.test.ts +29 -0
- package/src/lib/django-aggregates.ts +5 -0
- 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 +116 -4
- 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/setup-info.test.ts +17 -0
- package/src/lib/setup-info.ts +58 -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 +63 -0
- package/src/lib/warnings.ts +260 -0
- package/src/likec4/ids.test.ts +6 -2
- package/src/likec4/ids.ts +43 -0
- package/src/main.tsx +23 -0
- package/src/map/ContextMapGraph.tsx +76 -32
- package/src/merge-deployments.test.ts +127 -0
- package/src/merge.test.ts +82 -0
- package/src/merge.ts +189 -18
- package/src/pages/AggregatePage.tsx +65 -14
- package/src/pages/ContextMap.tsx +45 -3
- package/src/pages/ContextPage.tsx +8 -5
- 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 +62 -5
- package/src/pages/Settings.tsx +217 -43
- package/src/pages/settings/AboutSettings.tsx +8 -1
- package/src/pages/settings/DjangoAggregateChoices.tsx +79 -0
- 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/selection/DetailPanel.tsx +15 -0
- package/src/virtual-provenance.d.ts +11 -0
- package/vite.config.ts +5 -0
- package/scripts/vendor-lock.mjs +0 -58
- package/scripts/vendor-lock.test.mjs +0 -69
- package/src/lib/data-problems.ts +0 -314
- package/src/lib/proto-problems.ts +0 -237
- package/src/lib/wire-problems.ts +0 -342
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "portolan-extract-laravel"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
description = "A Laravel application in, a catalog fragment out: the PHP twin of extract-django, on the same protocol."
|
|
6
|
+
publish = false
|
|
7
|
+
|
|
8
|
+
[lib]
|
|
9
|
+
name = "portolan_extract_laravel"
|
|
10
|
+
path = "src/lib.rs"
|
|
11
|
+
|
|
12
|
+
[[bin]]
|
|
13
|
+
name = "portolan-extract-laravel"
|
|
14
|
+
path = "src/main.rs"
|
|
15
|
+
|
|
16
|
+
[dependencies]
|
|
17
|
+
# The PHP tree as syntax, shared with every other PHP extractor.
|
|
18
|
+
phpscan = { path = "../phpscan" }
|
|
19
|
+
serde = { version = "1", features = ["derive"] }
|
|
20
|
+
serde_json = { version = "1", features = ["preserve_order"] }
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# extract-laravel
|
|
2
|
+
|
|
3
|
+
A Laravel application in, a catalog fragment out - two, when its route files
|
|
4
|
+
prove an HTTP contract. The PHP twin of `extract-django`: like it, this reads
|
|
5
|
+
the framework rather than a layout somebody keeps by hand. A Laravel
|
|
6
|
+
application already says where the models are, what they hold, what answers a
|
|
7
|
+
request and who listens to what; nothing is annotated for the catalog, and
|
|
8
|
+
the framework's conventions are the claim.
|
|
9
|
+
|
|
10
|
+
Written in Rust and run as a process plugin, `cargo run --quiet
|
|
11
|
+
--manifest-path plugins/extract-laravel/Cargo.toml`, on [Mago](https://github.com/carthage-software/mago)'s
|
|
12
|
+
`mago-syntax`: the one maintained PHP parser in Rust, kept up with PHP 8.5
|
|
13
|
+
within weeks of each release, which is what a reader of a framework that moves
|
|
14
|
+
with the language needs. **The application is never run.** No `artisan`, no
|
|
15
|
+
container, no `composer install`: everything is read from syntax, and names
|
|
16
|
+
are resolved by namespace and by `use` line, the way `extract-ts` resolves
|
|
17
|
+
them without a type checker. A file that does not parse is read as far as it
|
|
18
|
+
parsed and reported.
|
|
19
|
+
|
|
20
|
+
## The layout it reads
|
|
21
|
+
|
|
22
|
+
Two layouts, and the manifest's `modules` option can name either or a third:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
app/ a plain Laravel application: one module, named after the root
|
|
26
|
+
Models/*.php Eloquent models
|
|
27
|
+
Events/*.php events with a payload
|
|
28
|
+
Listeners/*.php what reacts to them
|
|
29
|
+
Providers/EventServiceProvider.php
|
|
30
|
+
Http/Controllers/**/*.php what answers a route
|
|
31
|
+
routes/*.php the route files, beside app/
|
|
32
|
+
|
|
33
|
+
packages/<Vendor>/<Name>/src/ a monolith built from packages (Bagisto, Concord, nwidart/modules):
|
|
34
|
+
Models/, Events/, Listeners/, Providers/, Http/Controllers/ one module per package, named <Name>
|
|
35
|
+
Routes/*.php, Http/routes.php the package's own routes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`packages/*/*/src` is used when it matches anything, `app` otherwise. A
|
|
39
|
+
module is a source grouping: it becomes a `kind: "model-group"` aggregate,
|
|
40
|
+
`<service>.models-<module>`, the way a Django application does, because a
|
|
41
|
+
package is where the code lives and not a claim about a transactional
|
|
42
|
+
boundary. The service is the whole application; a monolith of forty packages
|
|
43
|
+
is one service with forty groups, and the events that cross between the
|
|
44
|
+
groups are the reason they are worth telling apart.
|
|
45
|
+
|
|
46
|
+
`vendor/`, tests, resources, storage and `Database/` (migrations, seeders,
|
|
47
|
+
factories) are not read.
|
|
48
|
+
|
|
49
|
+
## What becomes what
|
|
50
|
+
|
|
51
|
+
**Model.** A concrete class extending Eloquent's `Model` - or `Pivot`,
|
|
52
|
+
`MorphPivot`, `Illuminate\Foundation\Auth\User` - directly or through a base
|
|
53
|
+
class of the tree's own. Its fields are what it writes down, in this order:
|
|
54
|
+
the primary key, `id` typed `int` unless `$primaryKey` and `$keyType` say
|
|
55
|
+
otherwise; `$casts` with the type as cast (the `casts()` method in Laravel 11
|
|
56
|
+
counts); `$fillable` and `$guarded` columns with no cast, typed `mixed`
|
|
57
|
+
because the model does not say; `$dates`; `created_at` and `updated_at`
|
|
58
|
+
unless `$timestamps` is off; and one field per relation method, `items: HasMany[OrderItem]` for
|
|
59
|
+
`$this->hasMany(OrderItem::class)`, the target read off the first argument -
|
|
60
|
+
`X::class`, a string, or Concord's `XProxy::modelClass()` with the `Proxy`
|
|
61
|
+
taken off. The doc is the class docblock's first paragraph. `$hidden`,
|
|
62
|
+
accessors and scopes are not fields.
|
|
63
|
+
|
|
64
|
+
**Enum.** A backed PHP `enum` anywhere in the module, its values the backing
|
|
65
|
+
values; and on a model, two or more string constants sharing a prefix,
|
|
66
|
+
`STATUS_PENDING = 'pending'` beside `STATUS_CLOSED = 'closed'`, as one set
|
|
67
|
+
named `<Model> <Prefix>` with id `<group>.<model>-<prefix>`. A value's doc is
|
|
68
|
+
the constant's or case's docblock; `@deprecated` in it, or a `#[Deprecated]`
|
|
69
|
+
attribute, marks the value.
|
|
70
|
+
|
|
71
|
+
**Event, two ways.** A class under `Events/` - or using `Dispatchable` - is an
|
|
72
|
+
event with a payload: its promoted constructor parameters, then its public
|
|
73
|
+
properties, with the types as written; the wire name is the class's full name.
|
|
74
|
+
A string handed to `Event::dispatch('sales.order.cancel.after', $order)` or
|
|
75
|
+
`event('...')` is an event too, one with a name and no declared shape, id
|
|
76
|
+
`<group>.SalesOrderCancelAfter`, wire name the string, and no fields - which
|
|
77
|
+
is reported once per module rather than invented. Leaving those out would hide
|
|
78
|
+
most of what a package-built monolith publishes.
|
|
79
|
+
|
|
80
|
+
A named event belongs to the module its name says: `sales.order.cancel.after`
|
|
81
|
+
is Sales's whoever dispatches it, by the first segment of the name or the
|
|
82
|
+
second. When no segment names a module it goes to the module that dispatches
|
|
83
|
+
it most. A class event belongs to the module that declares it.
|
|
84
|
+
|
|
85
|
+
**Consumer.** Everything Laravel accepts as a listener: an
|
|
86
|
+
`EventServiceProvider`'s `$listen` table, in each of its shapes
|
|
87
|
+
(`Listener::class`, `'Class@method'`, `[Class::class, 'method']`); its
|
|
88
|
+
`$subscribe` classes, read through their `subscribe()` - `$events->listen(...)`
|
|
89
|
+
or the returned `['event' => 'method']` map; `Event::listen(...)` anywhere;
|
|
90
|
+
and a class under `Listeners/` whose `handle(SomeEvent $e)` names an event of
|
|
91
|
+
the tree, which Laravel discovers on its own. Each is a consumer on the event,
|
|
92
|
+
`service` the application itself and `note` the handler, and a policy flow.
|
|
93
|
+
|
|
94
|
+
**Endpoint and inferred HTTP contract.** Every route file - anything under a
|
|
95
|
+
`Routes/` or `routes/` directory, or a `routes.php` - is read the way the
|
|
96
|
+
router would read it: `Route::get|post|put|patch|delete|options|any|match`,
|
|
97
|
+
inside `prefix`, `name`, `namespace`, `controller` and `group` calls that
|
|
98
|
+
apply to everything in their closure, in both the fluent and the
|
|
99
|
+
`Route::group(['prefix' => ...], fn)` spelling. `Route::resource` and
|
|
100
|
+
`apiResource` expand to the routes they stand for, with `only` and `except`
|
|
101
|
+
honoured and the parameter named the way Laravel names it; `update` is kept
|
|
102
|
+
as PUT, PATCH being the same operation. The action is read in every shape
|
|
103
|
+
Laravel takes: `'index'` under a `controller()` group, `[C::class, 'm']`,
|
|
104
|
+
`'C@m'`, `C::class` for an invokable, a closure. A route whose path or
|
|
105
|
+
action is computed at runtime is kept as unknown and reported rather than
|
|
106
|
+
guessed. A route without its own `name()` has none, even inside a named
|
|
107
|
+
group.
|
|
108
|
+
|
|
109
|
+
The routes of one module make one interface, `<service>.<module>`, with an
|
|
110
|
+
operation per route named after the route name (`shop.checkout.cart.index` →
|
|
111
|
+
`shop_checkout_cart_index`), or the controller and method when there is no
|
|
112
|
+
name, or the verb and path when there is neither; the doc is the controller
|
|
113
|
+
method's docblock. They also make an `openapi.inferred.yaml` OpenAPI 3.1
|
|
114
|
+
document, partial on purpose and marked `x-portolan-inferred` on every
|
|
115
|
+
operation: the route file proves the verb, the path, its parameters and which
|
|
116
|
+
method answers, while request and response schemas are not in what this
|
|
117
|
+
reads and stay absent. A route answering every verb is a path item with no
|
|
118
|
+
operation. A checked-in document remains `extract-openapi`'s richer source of
|
|
119
|
+
truth. A controller the routes name that is not in the tree is reported.
|
|
120
|
+
|
|
121
|
+
**Flow.** One per endpoint: the call in, then every event the handler
|
|
122
|
+
publishes - itself, or through the classes it holds, `$this->orders->create()`
|
|
123
|
+
reaching `OrderRepository::create` when the constructor promoted `$orders`
|
|
124
|
+
with that type, up to five calls deep. And one per listener method: the
|
|
125
|
+
events it reacts to, then what it publishes in turn. A step's `ref` is the
|
|
126
|
+
event's id when the tree dispatches it, and the step is unresolved with a
|
|
127
|
+
note when it does not - a framework event, or a name nobody in the tree
|
|
128
|
+
dispatches, which is reported.
|
|
129
|
+
|
|
130
|
+
**Store.** The migrations, replayed. `extract-sql` reads DDL and a Laravel
|
|
131
|
+
schema is not DDL, so `Schema::create('orders', function (Blueprint $table)
|
|
132
|
+
{ ... })` is read here, one `$table->...` line at a time, in the order the
|
|
133
|
+
migration files sort - which is the order Laravel runs them - with
|
|
134
|
+
`Schema::table` alterations, `dropColumn`, `renameColumn`, `drop` and
|
|
135
|
+
`rename` applied on the way; only `up()` is read. Every column type Laravel's
|
|
136
|
+
blueprint has is mapped to its SQL spelling (`string` → `varchar(255)`,
|
|
137
|
+
`foreignId` → `bigint unsigned`, `decimal(12, 4)`, `enum('a','b')`,
|
|
138
|
+
`timestamps()` → two nullable timestamps), `nullable`, `unsigned`, `unique`,
|
|
139
|
+
`index`, `primary` and `comment` are honoured, and a foreign key is read
|
|
140
|
+
from `foreignId()->constrained()` and `foreign()->references()->on()`, with
|
|
141
|
+
its `onDelete`. The store is `<service>.<store>` (`db` unless the manifest
|
|
142
|
+
says), its kind read off config/database.php's default connection - mysql
|
|
143
|
+
when that cannot be read - and it goes into a second fragment, `stores.json`,
|
|
144
|
+
the way extract-django writes one. A table maps to the model whose `$table`
|
|
145
|
+
names it, or whose name Eloquent would pluralise to it (`OrderItem` →
|
|
146
|
+
`order_items`); its `persists` is that model's block and each column shared
|
|
147
|
+
with the model's fields carries `maps`. A table the code reaches that no
|
|
148
|
+
migration in the tree creates is kept with no columns, and reported.
|
|
149
|
+
|
|
150
|
+
**Table access.** `Order::create(...)`, `Order::where(...)->update(...)`,
|
|
151
|
+
`$this->model->find(...)` in a repository whose `model()` names the model -
|
|
152
|
+
or the Concord contract the model implements - and
|
|
153
|
+
`DB::table('orders')->insert(...)`: each is an access of the table, read,
|
|
154
|
+
write or delete by the strongest method on the chain, listed on the table
|
|
155
|
+
and drawn as a step to the store lane in every flow that reaches it.
|
|
156
|
+
|
|
157
|
+
**Job.** A class under `Jobs/` or implementing `ShouldQueue`. Every way one
|
|
158
|
+
is handed to the queue is read - `Job::dispatch(...)`, `dispatch(new Job)`,
|
|
159
|
+
`Bus::dispatch(new Job)`, the lists of `Bus::chain` and `Bus::batch` - and
|
|
160
|
+
the queue is the site's `onQueue('mail')`, else the job's own `$queue`, else
|
|
161
|
+
the queue its first dispatch site names, else `default`. Each queue is a
|
|
162
|
+
channel of kind `job` on the service, with a `send` message per job put on
|
|
163
|
+
it and a `receive` per job worked from it, the shape extract-celery writes
|
|
164
|
+
for a Celery queue; the transport is the connection config/queue.php
|
|
165
|
+
defaults to. A dispatch is a hop in the flow that makes it, a `call` to the
|
|
166
|
+
`Queue · mail` lane with a `job` handoff, and every job has a worker flow of
|
|
167
|
+
its own from the queue in through what `handle()` does.
|
|
168
|
+
|
|
169
|
+
## What it does not read
|
|
170
|
+
|
|
171
|
+
Lifecycles; form requests and API resources as schemas; `$hidden`,
|
|
172
|
+
accessors, scopes; broadcasting (`ShouldBroadcast`), mail and notifications,
|
|
173
|
+
which go through the queue but are not jobs; `Cache::` and `Redis::`, which
|
|
174
|
+
are a keyspace and not a table; middleware and authorization. Each is a next
|
|
175
|
+
step, not an oversight.
|
|
176
|
+
|
|
177
|
+
## Options
|
|
178
|
+
|
|
179
|
+
See `options.schema.json`. `context` and `service` default to the input
|
|
180
|
+
directory's name; `modules` picks the layout; `repo` defaults to
|
|
181
|
+
composer.json's `support.source` or `homepage` when either is a repository;
|
|
182
|
+
`store` and `storeKind` name the database; `out`, `openapiOut` and
|
|
183
|
+
`storesOut` name the three files.
|
|
184
|
+
|
|
185
|
+
## Trying it on a real one
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
git clone --depth 1 https://github.com/bagisto/bagisto
|
|
189
|
+
printf '%s' '{"input":{"root":"bagisto","output":"bagisto/portolan"},"options":{"context":"commerce","service":"bagisto"}}' \
|
|
190
|
+
| cargo run --quiet --manifest-path plugins/extract-laravel/Cargo.toml
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
On Bagisto 2.4 that is 41 packages read in well under a second: 28 model
|
|
194
|
+
groups, 125 models, 22 enums, 309 events - most of them named, dispatched
|
|
195
|
+
from admin and storefront controllers and owned by the package their name
|
|
196
|
+
says, 50 of them with a listener - ten HTTP interfaces with 520 operations,
|
|
197
|
+
a database of 138 tables and 1300 columns replayed from 189 migrations with
|
|
198
|
+
185 foreign keys and 120 places the code reads or writes them, 17 jobs on
|
|
199
|
+
one queue, 610 flows, and one controller the admin routes name that does not
|
|
200
|
+
exist.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"additionalProperties": false,
|
|
4
|
+
"properties": {
|
|
5
|
+
"context": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "Slug of the bounded context this application belongs to. A composer.json knows its name, not which context owns it. Defaults to the name of the input directory.",
|
|
8
|
+
"examples": ["shop"]
|
|
9
|
+
},
|
|
10
|
+
"contextName": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "What the context is called on the page. Defaults to the slug, titled.",
|
|
13
|
+
"examples": ["Shop"]
|
|
14
|
+
},
|
|
15
|
+
"contextSummary": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "A sentence or two on what the context is responsible for. Read on the context page and nowhere else."
|
|
18
|
+
},
|
|
19
|
+
"classification": {
|
|
20
|
+
"enum": ["core", "supporting", "generic"],
|
|
21
|
+
"description": "Whether the context is core to the business, supports it, or is generic and could be bought."
|
|
22
|
+
},
|
|
23
|
+
"service": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Slug of the service. Defaults to the name of the input directory.",
|
|
26
|
+
"examples": ["bagisto"]
|
|
27
|
+
},
|
|
28
|
+
"serviceName": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "What the service is called on the page. Defaults to the title of its README, and failing that to the slug, titled."
|
|
31
|
+
},
|
|
32
|
+
"repo": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Where the source lives. Defaults to composer.json's support.source or homepage when either names a repository.",
|
|
35
|
+
"examples": ["github.com/bagisto/bagisto"]
|
|
36
|
+
},
|
|
37
|
+
"modules": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Where the application's modules are, as a directory pattern relative to the input root with `*` for one path segment: `packages/*/*/src` for a package-per-module monolith, `app` for a plain Laravel application. Each match is one module, named after the directory the pattern's last literal segment sits in. Defaults to `packages/*/*/src` when that matches anything, and to `app` otherwise.",
|
|
40
|
+
"examples": ["packages/*/*/src", "app", "modules/*"]
|
|
41
|
+
},
|
|
42
|
+
"store": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Slug of the database the migrations describe and the models read and write. The store's id is the service id and this slug.",
|
|
45
|
+
"default": "db",
|
|
46
|
+
"examples": ["mysql"]
|
|
47
|
+
},
|
|
48
|
+
"storeKind": {
|
|
49
|
+
"enum": ["postgres", "mysql", "sqlite", "other"],
|
|
50
|
+
"description": "Which database the migrations are the schema of. Read off config/database.php's default connection when left out - `env('DB_CONNECTION', 'mysql')` names it - and mysql when that cannot be read; given here, it wins."
|
|
51
|
+
},
|
|
52
|
+
"out": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Name of the fragment file, inside the step's output directory. One extractor writes one file, so that a fragment carries the provenance of the run that produced it.",
|
|
55
|
+
"default": "domain.json"
|
|
56
|
+
},
|
|
57
|
+
"storesOut": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Name of the store fragment, inside the step's output directory. Written when the migrations declare tables or the code reaches any.",
|
|
60
|
+
"default": "stores.json"
|
|
61
|
+
},
|
|
62
|
+
"openapiOut": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Name of the inferred OpenAPI 3.1 document, inside the step's output directory. Written when the route files expose HTTP operations.",
|
|
65
|
+
"default": "openapi.inferred.yaml"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
max_width = 160
|