@reventlessdev/reventless-spec 3.0.0-alpha.124 → 3.0.0-alpha.125
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/CHANGELOG.md +25 -0
- package/package.json +5 -2
- package/run-certify-trait.mjs +2 -0
- package/run-graft-trait.mjs +2 -0
- package/run-trait-manifest.mjs +2 -0
- package/schema/platform-api.graphql +17 -3
- package/src/components/Aggregate.res +22 -0
- package/src/components/AutomationSlice.res +29 -3
- package/src/components/CapabilityManifest.res +52 -24
- package/src/components/CapabilityManifest.res.mjs +35 -11
- package/src/components/InboundTranslationSlice.res +14 -0
- package/src/components/OutboundTranslationSlice.res +24 -0
- package/src/components/Plugin.res +53 -0
- package/src/components/Plugin.res.mjs +23 -1
- package/src/components/StateChangeSlice.res +22 -0
- package/src/components/TraitCertificate.res +105 -0
- package/src/components/TraitCertificate.res.mjs +65 -0
- package/src/components/TraitManifest.res +90 -0
- package/src/components/TraitManifest.res.mjs +48 -0
- package/src/generator/CertifyTrait.res +190 -0
- package/src/generator/CertifyTrait.res.mjs +154 -0
- package/src/generator/GraftTrait.res +230 -0
- package/src/generator/GraftTrait.res.mjs +193 -0
- package/src/generator/PlatformCodegen.res +44 -30
- package/src/generator/PlatformCodegen.res.mjs +36 -17
- package/src/generator/TraitManifestCli.res +138 -0
- package/src/generator/TraitManifestCli.res.mjs +105 -0
- package/src/semantic/Capabilities.res +19 -3
- package/src/semantic/Capabilities.res.mjs +18 -2
- package/src/semantic/CapabilityNeed.res +81 -0
- package/src/semantic/CapabilityNeed.res.mjs +46 -0
- package/src/semantic/Messaging.res +127 -0
- package/src/semantic/Messaging.res.mjs +57 -0
- package/src/types/Trait.res +62 -0
- package/src/types/Trait.res.mjs +18 -0
- package/src/types/Transition.res +71 -0
- package/src/types/Transition.res.mjs +36 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// What a domain trait says about itself, so that a graft leaves a trace.
|
|
2
|
+
//
|
|
3
|
+
// A graft becomes ordinary host source — that is the design, and it is why every
|
|
4
|
+
// other signal a trait leaves is source-side: the package dependency, the variant
|
|
5
|
+
// spread, the `module X = Trait_Rules` alias, the conformance binding under
|
|
6
|
+
// `tests/`. None of them survives into a deployed plugin, so a running estate
|
|
7
|
+
// cannot answer "which of my components came from a trait" at all.
|
|
8
|
+
//
|
|
9
|
+
// This is the one fact that does survive, because it travels the way
|
|
10
|
+
// `capabilityNeeds` does: a value the trait exports and the host names, collected
|
|
11
|
+
// into `pluginStructure` while it is assembled and re-emitted whole on every
|
|
12
|
+
// registration.
|
|
13
|
+
//
|
|
14
|
+
// **Nothing here is typed by a human.** The trait exports its own identity, so a
|
|
15
|
+
// renamed or removed trait is a build error rather than a stale row; the version
|
|
16
|
+
// is read from the trait's own package rather than restated; and the component is
|
|
17
|
+
// filled in by the structure, which is the only party that knows which component
|
|
18
|
+
// declared it. That is the whole reason this is a value rather than an
|
|
19
|
+
// annotation — an attribute's fields would all be strings the compiler never
|
|
20
|
+
// checks, which is the failure this program exists to remove.
|
|
21
|
+
//
|
|
22
|
+
// **A declaration is a claim about origin, never about behaviour.** After a graft
|
|
23
|
+
// the developer owns the files and may edit them freely. So this says "grafted
|
|
24
|
+
// from trait X", and it does NOT say "still behaves like trait X" — the thing that
|
|
25
|
+
// answers the second question is the trait's conformance suite, which runs in the
|
|
26
|
+
// consumer's build and reports separately. A reader that paints a declared graft
|
|
27
|
+
// as verified is drawing a conclusion this field cannot support.
|
|
28
|
+
|
|
29
|
+
/** Whether the graft reports back into the host it is grafted onto.
|
|
30
|
+
|
|
31
|
+
`WritesBack` publishes commands to its host — the geocoding shape, where the
|
|
32
|
+
slice reports its answer back onto the aggregate. `Observes` reads host events
|
|
33
|
+
and writes nothing back. `SelfContained` brings its own components and grafts
|
|
34
|
+
only by reading — the notification shape, which is what broke the
|
|
35
|
+
write-back assumption the first two specimens shared. */
|
|
36
|
+
type posture =
|
|
37
|
+
| WritesBack
|
|
38
|
+
| Observes
|
|
39
|
+
| SelfContained
|
|
40
|
+
|
|
41
|
+
let postureToString = (p: posture): string =>
|
|
42
|
+
switch p {
|
|
43
|
+
| WritesBack => "WritesBack"
|
|
44
|
+
| Observes => "Observes"
|
|
45
|
+
| SelfContained => "SelfContained"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
One trait's own account of itself, exported by the trait package.
|
|
50
|
+
|
|
51
|
+
`version` is read from the trait's package at load time rather than restated in
|
|
52
|
+
source — see `PackageVersion.fromModuleUrl`, which the certification CLI already
|
|
53
|
+
resolves trait versions with. A trait whose package.json cannot be found reports
|
|
54
|
+
`"0.0.0"`, which is visibly wrong rather than quietly stale.
|
|
55
|
+
*/
|
|
56
|
+
type t = {
|
|
57
|
+
/** The package, as it is depended on: `"@reventlessdev/trait-attachments"`. */
|
|
58
|
+
trait: string,
|
|
59
|
+
/** Resolved from the trait's own package, never written by hand. */
|
|
60
|
+
version: string,
|
|
61
|
+
posture: posture,
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function postureToString(p) {
|
|
5
|
+
switch (p) {
|
|
6
|
+
case "WritesBack" :
|
|
7
|
+
return "WritesBack";
|
|
8
|
+
case "Observes" :
|
|
9
|
+
return "Observes";
|
|
10
|
+
case "SelfContained" :
|
|
11
|
+
return "SelfContained";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
postureToString,
|
|
17
|
+
}
|
|
18
|
+
/* No side effect */
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The lifecycle edge a command owns, declared as a value rather than as an
|
|
2
|
+
// attribute on the constructor.
|
|
3
|
+
//
|
|
4
|
+
// `@transition([Orders.Placed] => Orders.Shipped)` says the same thing and is
|
|
5
|
+
// still the shorter spelling for a command a host declares itself. It cannot say
|
|
6
|
+
// it for a command a host did NOT declare: a variant spread splices members,
|
|
7
|
+
// while the annotation lowers to a dict on the parent union, so a spliced
|
|
8
|
+
// command arrives carrying no edge at all. Nor can the annotation be checked —
|
|
9
|
+
// the PPX extracts leaf identifiers as strings, and the states belong to another
|
|
10
|
+
// component's enum, so a misspelling survives to the plugin structure.
|
|
11
|
+
//
|
|
12
|
+
// A `command => t<'state>` switch answers both. It is exhaustive, so a spliced
|
|
13
|
+
// constructor is a compile error until the host says what it does; and `'state`
|
|
14
|
+
// is the view's own lifecycle enum, so `Customers.Active` is a constructor the
|
|
15
|
+
// compiler resolves rather than a string nobody reads.
|
|
16
|
+
//
|
|
17
|
+
// `'state` is one type across the whole switch, which is a third thing the
|
|
18
|
+
// annotation cannot do: every arm of one component's edges must name the same
|
|
19
|
+
// lifecycle, and a from-set drawn from one enum with a target from another does
|
|
20
|
+
// not compile.
|
|
21
|
+
//
|
|
22
|
+
// The type stays parameterised all the way down rather than storing names,
|
|
23
|
+
// because erasing a constructor to its own name means asserting its runtime
|
|
24
|
+
// representation — and this is the module that exists so nothing has to be
|
|
25
|
+
// asserted. The erasure happens once, at the framework's type-erasure boundary
|
|
26
|
+
// (`Plugin_Structure`), which already reads every spec member that way.
|
|
27
|
+
//
|
|
28
|
+
// The reference costs nothing at run time. A lifecycle enum's arms are
|
|
29
|
+
// payload-less, so `[Customers.Active]` compiles to `["Active"]` and the
|
|
30
|
+
// generated module imports nothing from the view — which is also why it cannot
|
|
31
|
+
// cycle: a view spec holds no reference back to the aggregate it projects.
|
|
32
|
+
//
|
|
33
|
+
// Read the same way `commandAuthorization` is: `Plugin_Structure.toCommandDef`
|
|
34
|
+
// evaluates it against a synthetic value per constructor.
|
|
35
|
+
//
|
|
36
|
+
// No `@schema`: nothing serialises a transition. It is read once, while the
|
|
37
|
+
// plugin structure is assembled, and what leaves is the pair of names the
|
|
38
|
+
// structure already carried.
|
|
39
|
+
type t<'state> =
|
|
40
|
+
/** No edge declared: legal in every state, moves the row nowhere. The honest
|
|
41
|
+
answer for a report a slice publishes, which must not be refused because
|
|
42
|
+
the row moved on while the report was in flight. */
|
|
43
|
+
| Unrestricted
|
|
44
|
+
/** Brings the row into existence, so there is no state it could come from.
|
|
45
|
+
Distinct from `Unrestricted`, which draws no edge at all. */
|
|
46
|
+
| Creates('state)
|
|
47
|
+
/** Legal in these states, and moves the row nowhere. A positive claim rather
|
|
48
|
+
than an omission. */
|
|
49
|
+
| Guards(array<'state>)
|
|
50
|
+
/** Legal in these states, and lands the row in that one. */
|
|
51
|
+
| Moves(array<'state>, 'state)
|
|
52
|
+
|
|
53
|
+
/** The from-set, or `None` for a command that names no states to come from —
|
|
54
|
+
which `Creates` and `Unrestricted` both do, for different reasons the target
|
|
55
|
+
tells apart. */
|
|
56
|
+
let allowedStates = (transition: t<'state>): option<array<'state>> =>
|
|
57
|
+
switch transition {
|
|
58
|
+
| Unrestricted
|
|
59
|
+
| Creates(_) => None
|
|
60
|
+
| Guards(states)
|
|
61
|
+
| Moves(states, _) => Some(states)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The state the command's handler writes, or `None` for one that moves nothing. */
|
|
65
|
+
let targetState = (transition: t<'state>): option<'state> =>
|
|
66
|
+
switch transition {
|
|
67
|
+
| Unrestricted
|
|
68
|
+
| Guards(_) => None
|
|
69
|
+
| Creates(state)
|
|
70
|
+
| Moves(_, state) => Some(state)
|
|
71
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
4
|
+
|
|
5
|
+
function allowedStates(transition) {
|
|
6
|
+
if (typeof transition !== "object") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
switch (transition.TAG) {
|
|
10
|
+
case "Creates" :
|
|
11
|
+
return;
|
|
12
|
+
case "Guards" :
|
|
13
|
+
case "Moves" :
|
|
14
|
+
return transition._0;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function targetState(transition) {
|
|
19
|
+
if (typeof transition !== "object") {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
switch (transition.TAG) {
|
|
23
|
+
case "Creates" :
|
|
24
|
+
return Primitive_option.some(transition._0);
|
|
25
|
+
case "Guards" :
|
|
26
|
+
return;
|
|
27
|
+
case "Moves" :
|
|
28
|
+
return Primitive_option.some(transition._1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
allowedStates,
|
|
34
|
+
targetState,
|
|
35
|
+
}
|
|
36
|
+
/* No side effect */
|