@reventlessdev/reventless-core 3.0.0-alpha.152 → 3.0.0-alpha.154
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 +18 -0
- package/package.json +6 -6
- package/src/Message.res +8 -2
- package/src/Message.res.mjs +8 -2
- package/src/adapter/Monitoring/Monitoring.res +96 -0
- package/src/adapter/Monitoring/Monitoring.res.mjs +31 -0
- package/src/admin/Platform_Admin_Structure.res +2 -0
- package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +11 -0
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +28 -0
- package/src/plugin/component/Plugin.res +1 -0
- package/src/plugin/component/Plugin_Builder.res +40 -1
- package/src/plugin/component/Plugin_Builder.res.mjs +37 -9
- package/src/plugin/component/Plugin_BuiltHook.res +11 -0
- package/src/plugin/component/Plugin_Structure.res +15 -0
- package/src/plugin/component/Plugin_Structure.res.mjs +16 -8
- package/tests/adapter/MonitoringTest.res +53 -0
- package/tests/adapter/MonitoringTest.res.mjs +82 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +69 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +64 -8
- package/tests/message/MessageTest.res +146 -0
- package/tests/message/MessageTest.res.mjs +127 -0
- package/tests/plugin/ManifestVisibilityTest.res.mjs +1 -1
- package/tests/plugin/PluginStructureTest.res +34 -0
- package/tests/plugin/PluginStructureTest.res.mjs +32 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.154 (2026-07-11)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **plugin-lifecycle:** heal message decode of definitions persisted before a schema field existed ([6bb3e72](https://github.com/ReventlessDev/reventless-core/commit/6bb3e7259ad606a0f77fb670bcfc680256592003))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **monitoring:** deploy-time Monitoring hook seam for provisioned execution units ([30f1c23](https://github.com/ReventlessDev/reventless-core/commit/30f1c23ba118805dae83af9115341f4aff6db92b))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# 3.0.0-alpha.153 (2026-07-10)
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **plugin-structure:** capture per-component chapter grouping for the deployed graph ([f9c88a9](https://github.com/ReventlessDev/reventless-core/commit/f9c88a9a48d8c032ffe23f9e5277caf12c29e85c))
|
|
21
|
+
* **plugin-structure:** carry translation-slice externalSystem into the deployed structure ([80c0cda](https://github.com/ReventlessDev/reventless-core/commit/80c0cda3b6d5fc6625d4b285f41505028f1dd06f))
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
# 3.0.0-alpha.152 (2026-07-10)
|
|
7
25
|
|
|
8
26
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.154",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.25",
|
|
32
|
-
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.11",
|
|
33
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.10",
|
|
34
33
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.6",
|
|
35
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
36
34
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.10",
|
|
35
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
36
|
+
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.11",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.11",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
|
|
39
|
-
"@reventlessdev/reventless-
|
|
40
|
-
"@reventlessdev/reventless-
|
|
41
|
-
"@reventlessdev/reventless-
|
|
39
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.24",
|
|
40
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.95",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.73"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "^12.3.0",
|
package/src/Message.res
CHANGED
|
@@ -64,10 +64,16 @@ let toCommandSchema' = (idSchema, commandSchema) =>
|
|
|
64
64
|
})
|
|
65
65
|
)
|
|
66
66
|
|
|
67
|
+
// Schema-migration-on-read: reuse the tolerant parse from `Reventless.Message`
|
|
68
|
+
// (`parseJsonTolerant` / `fillMissingDefaults`, included above) so a message persisted
|
|
69
|
+
// before a nested `@schema` field existed still decodes instead of bricking the
|
|
70
|
+
// aggregate's log replay. These two entry points thread the memoized envelope schema;
|
|
71
|
+
// `Reventless.Message.decode` (the other central decoder) is tolerant at the source.
|
|
72
|
+
// See docs/plans/platform-infrastructure-in-plugin-list.md (durable fix option 2).
|
|
67
73
|
let decodeEvent' = (json, idSchema, eventSchema) =>
|
|
68
|
-
json->
|
|
74
|
+
json->parseJsonTolerant(toEventSchema'(idSchema, eventSchema))
|
|
69
75
|
let decodeCommand' = (json, idSchema, commandSchema) =>
|
|
70
|
-
json->
|
|
76
|
+
json->parseJsonTolerant(toCommandSchema'(idSchema, commandSchema))
|
|
71
77
|
|
|
72
78
|
let encodeEvent' = (event', idSchema, eventSchema) =>
|
|
73
79
|
event'->S.reverseConvertToJsonOrThrow(toEventSchema'(idSchema, eventSchema))
|
package/src/Message.res.mjs
CHANGED
|
@@ -44,11 +44,11 @@ function toCommandSchema$p(idSchema, commandSchema) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function decodeEvent$p(json, idSchema, eventSchema) {
|
|
47
|
-
return
|
|
47
|
+
return Message$Reventless.parseJsonTolerant(json, toEventSchema$p(idSchema, eventSchema));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function decodeCommand$p(json, idSchema, commandSchema) {
|
|
51
|
-
return
|
|
51
|
+
return Message$Reventless.parseJsonTolerant(json, toCommandSchema$p(idSchema, commandSchema));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function encodeEvent$p(event$p, idSchema, eventSchema) {
|
|
@@ -384,6 +384,10 @@ let statusChangeSchema = Message$Reventless.statusChangeSchema;
|
|
|
384
384
|
|
|
385
385
|
let commandJsonSchema = Message$Reventless.commandJsonSchema;
|
|
386
386
|
|
|
387
|
+
let fillMissingDefaults = Message$Reventless.fillMissingDefaults;
|
|
388
|
+
|
|
389
|
+
let parseJsonTolerant = Message$Reventless.parseJsonTolerant;
|
|
390
|
+
|
|
387
391
|
let decode = Message$Reventless.decode;
|
|
388
392
|
|
|
389
393
|
let encode = Message$Reventless.encode;
|
|
@@ -397,6 +401,8 @@ export {
|
|
|
397
401
|
contextSchema,
|
|
398
402
|
statusChangeSchema,
|
|
399
403
|
commandJsonSchema,
|
|
404
|
+
fillMissingDefaults,
|
|
405
|
+
parseJsonTolerant,
|
|
400
406
|
decode,
|
|
401
407
|
encode,
|
|
402
408
|
InvalidEvent,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Deploy-time inventory hook: every runtime backend announces the execution units
|
|
3
|
+
it provisions so an extension can attach provider-native monitoring to each.
|
|
4
|
+
|
|
5
|
+
No-op by default. Zero behavioral change unless an extension registers a backend
|
|
6
|
+
via `use` before the platform/plugin build. The seam fires once per unit at
|
|
7
|
+
provisioning time (inside the deploy program) — never at invocation, and it runs
|
|
8
|
+
no code in production. Monitoring/alerting itself is deliberately NOT a framework
|
|
9
|
+
concern; this only exposes the choke point through which the framework says
|
|
10
|
+
"I just provisioned an execution unit" and leaves what to do about it to the
|
|
11
|
+
listener.
|
|
12
|
+
|
|
13
|
+
See `docs/plans/monitoring-hook-seam.md`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
The ROLE of a provisioned execution unit, not its mechanism. What "failure"
|
|
18
|
+
means for a unit is provider-specific (a Lambda `Errors` metric vs. container
|
|
19
|
+
restarts vs. NATS redeliveries) and belongs in the extension's per-provider
|
|
20
|
+
`Backend`; core only says *that* a unit exists and *what role* it plays, which is
|
|
21
|
+
what keeps the contract stable across runtimes. `Other(string)` is the escape
|
|
22
|
+
hatch for support units and future roles (same pattern as the protocol's
|
|
23
|
+
`OtherKind`).
|
|
24
|
+
*/
|
|
25
|
+
type unitKind =
|
|
26
|
+
| /** aggregates, state-change slices, extension points — failure = writes
|
|
27
|
+
rejected/dropped (the silent-freeze class) */
|
|
28
|
+
CommandHandler
|
|
29
|
+
| /** state-view slices, read models — failure = silently stale reads */
|
|
30
|
+
Projection
|
|
31
|
+
| /** automation slices, side-effect handlers, translations — failure = missed
|
|
32
|
+
side effects at boundaries */
|
|
33
|
+
Reactor
|
|
34
|
+
| /** cross-plugin/admin event ingestion — failure = event flow between plugins
|
|
35
|
+
stops */
|
|
36
|
+
EventCollector
|
|
37
|
+
| /** scheduled task runners */
|
|
38
|
+
Task
|
|
39
|
+
| /** heartbeat/keep-alive — failure = lifecycle detection AND any staleness
|
|
40
|
+
watchdog go blind (monitoring's own pulse) */
|
|
41
|
+
Scheduler
|
|
42
|
+
| /** receives messages that exhausted processing */
|
|
43
|
+
DeadLetterSink
|
|
44
|
+
| /** support units (counters, change-feed relays, migration runners, query
|
|
45
|
+
resolvers, …) — providers pass their name */
|
|
46
|
+
Other(string)
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
A monitoring backend, registered by an extension (a deploy program) before the
|
|
50
|
+
platform builds. `onProvisioned` is called once per execution unit.
|
|
51
|
+
|
|
52
|
+
`~name` is the unit's STATIC logical name — the string the builder passed to its
|
|
53
|
+
provisioning call. A Pulumi-based backend derives logical resource names from it
|
|
54
|
+
(alarm/rule resource names must be stable and known at graph-construction time).
|
|
55
|
+
`~component` is the deploy-time `Adapter.resource`; its fields are `Output`s
|
|
56
|
+
(physical, suffix-bearing — right for a metric dimension like a CloudWatch
|
|
57
|
+
FunctionName or a PrometheusRule deployment selector, but unusable as a logical
|
|
58
|
+
resource name). Because `onProvisioned` fires during resource creation inside the
|
|
59
|
+
deploy program, a Pulumi-based backend can create monitoring resources in the
|
|
60
|
+
same stack, parented naturally.
|
|
61
|
+
*/
|
|
62
|
+
module type Backend = {
|
|
63
|
+
let onProvisioned: (
|
|
64
|
+
~kind: unitKind,
|
|
65
|
+
~name: string,
|
|
66
|
+
~component: ReventlessInfra.Adapter.resource,
|
|
67
|
+
) => unit
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Default backend: does nothing. Active unless an extension calls `use`. */
|
|
71
|
+
module Noop: Backend = {
|
|
72
|
+
let onProvisioned = (~kind as _, ~name as _, ~component as _) => ()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let backend: ref<module(Backend)> = ref(module(Noop: Backend))
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
Register the monitoring backend. Must run before the platform/plugin build in the
|
|
79
|
+
deploy program (plain statement order — the registry is consulted lazily at each
|
|
80
|
+
provisioning site). Called once, by the extension, first.
|
|
81
|
+
*/
|
|
82
|
+
let use = (b: module(Backend)) => backend := b
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
Announce a provisioned execution unit to the registered backend. Called by
|
|
86
|
+
runtime backends at their provisioning sites (once per unit). No-op until an
|
|
87
|
+
extension registers a backend.
|
|
88
|
+
*/
|
|
89
|
+
let notify = (
|
|
90
|
+
~kind: unitKind,
|
|
91
|
+
~name: string,
|
|
92
|
+
~component: ReventlessInfra.Adapter.resource,
|
|
93
|
+
) => {
|
|
94
|
+
module B = unpack(backend.contents)
|
|
95
|
+
B.onProvisioned(~kind, ~name, ~component)
|
|
96
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function onProvisioned(param, param$1, param$2) {
|
|
5
|
+
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let Noop = {
|
|
9
|
+
onProvisioned: onProvisioned
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let backend = {
|
|
13
|
+
contents: Noop
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function use(b) {
|
|
17
|
+
backend.contents = b;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function notify(kind, name, component) {
|
|
21
|
+
let B = backend.contents;
|
|
22
|
+
B.onProvisioned(kind, name, component);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
Noop,
|
|
27
|
+
backend,
|
|
28
|
+
use,
|
|
29
|
+
notify,
|
|
30
|
+
}
|
|
31
|
+
/* No side effect */
|
|
@@ -96,6 +96,7 @@ let pluginAggregate: writableDef = {
|
|
|
96
96
|
linkedViews: ["Plugins"],
|
|
97
97
|
consistencyRead: None,
|
|
98
98
|
events: [],
|
|
99
|
+
chapter: None,
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
let pluginReadModel: queryableDef = {
|
|
@@ -110,6 +111,7 @@ let pluginReadModel: queryableDef = {
|
|
|
110
111
|
searchableFields: ["name"],
|
|
111
112
|
statusField: Some("status"),
|
|
112
113
|
visibility: None,
|
|
114
|
+
chapter: None,
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
let structure: pluginStructure = {
|
|
@@ -110,7 +110,8 @@ let pluginAggregate = {
|
|
|
110
110
|
consumedEventTypes: pluginAggregate_consumedEventTypes,
|
|
111
111
|
linkedViews: pluginAggregate_linkedViews,
|
|
112
112
|
consistencyRead: undefined,
|
|
113
|
-
events: pluginAggregate_events
|
|
113
|
+
events: pluginAggregate_events,
|
|
114
|
+
chapter: undefined
|
|
114
115
|
};
|
|
115
116
|
|
|
116
117
|
let pluginReadModel_queryField = Api_Naming$ReventlessCore.adminField("Plugins");
|
|
@@ -134,7 +135,8 @@ let pluginReadModel = {
|
|
|
134
135
|
labelField: "name",
|
|
135
136
|
searchableFields: pluginReadModel_searchableFields,
|
|
136
137
|
statusField: pluginReadModel_statusField,
|
|
137
|
-
visibility: undefined
|
|
138
|
+
visibility: undefined,
|
|
139
|
+
chapter: undefined
|
|
138
140
|
};
|
|
139
141
|
|
|
140
142
|
let structure_readModels = [pluginReadModel];
|
|
@@ -72,6 +72,7 @@ let encodeQueryableDef = (r: queryableDef): JSON.t =>
|
|
|
72
72
|
("labelField", JSON.Encode.string(r.labelField)),
|
|
73
73
|
("searchableFields", encodeStrings(r.searchableFields)),
|
|
74
74
|
("statusField", r.statusField->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
75
|
+
("chapter", r.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
75
76
|
])->JSON.Encode.object
|
|
76
77
|
|
|
77
78
|
let encodeEventDef = (e: eventDef): JSON.t =>
|
|
@@ -94,6 +95,7 @@ let encodeWritableDef = (w: writableDef): JSON.t =>
|
|
|
94
95
|
("consumedEventTypes", encodeStrings(w.consumedEventTypes)),
|
|
95
96
|
// Phase 6.3: emitted-event field schemas (None → [] on the wire).
|
|
96
97
|
("events", w.events->Array.map(encodeEventDef)->JSON.Encode.array),
|
|
98
|
+
("chapter", w.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
97
99
|
])->JSON.Encode.object
|
|
98
100
|
|
|
99
101
|
let encodeAutomationSliceDef = (a: automationSliceDef): JSON.t =>
|
|
@@ -101,6 +103,7 @@ let encodeAutomationSliceDef = (a: automationSliceDef): JSON.t =>
|
|
|
101
103
|
("name", JSON.Encode.string(a.name)),
|
|
102
104
|
("consumedEventTypes", encodeStrings(a.consumedEventTypes)),
|
|
103
105
|
("producedCommandTypes", encodeStrings(a.producedCommandTypes)),
|
|
106
|
+
("chapter", a.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
104
107
|
])->JSON.Encode.object
|
|
105
108
|
|
|
106
109
|
let encodeOutboundTranslationSliceDef = (o: outboundTranslationSliceDef): JSON.t =>
|
|
@@ -108,12 +111,20 @@ let encodeOutboundTranslationSliceDef = (o: outboundTranslationSliceDef): JSON.t
|
|
|
108
111
|
("name", JSON.Encode.string(o.name)),
|
|
109
112
|
("consumedEventTypes", encodeStrings(o.consumedEventTypes)),
|
|
110
113
|
("inboundCommandTypes", encodeStrings(o.inboundCommandTypes)),
|
|
114
|
+
// Foreign system this slice publishes to — lets a deployed-graph consumer draw
|
|
115
|
+
// the external-system boundary box without workspace access. None → null.
|
|
116
|
+
("externalSystem", o.externalSystem->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
117
|
+
("chapter", o.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
111
118
|
])->JSON.Encode.object
|
|
112
119
|
|
|
113
120
|
let encodeInboundTranslationSliceDef = (i: inboundTranslationSliceDef): JSON.t =>
|
|
114
121
|
Dict.fromArray([
|
|
115
122
|
("name", JSON.Encode.string(i.name)),
|
|
116
123
|
("commandTypes", encodeStrings(i.commandTypes)),
|
|
124
|
+
// Foreign system this slice receives from — lets a deployed-graph consumer draw
|
|
125
|
+
// the external-system boundary box without workspace access. None → null.
|
|
126
|
+
("externalSystem", i.externalSystem->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
127
|
+
("chapter", i.chapter->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
117
128
|
])->JSON.Encode.object
|
|
118
129
|
|
|
119
130
|
let encodeExtensionDef = (e: extensionDef): JSON.t =>
|
|
@@ -115,6 +115,10 @@ function encodeQueryableDef(r) {
|
|
|
115
115
|
[
|
|
116
116
|
"statusField",
|
|
117
117
|
Stdlib_Option.mapOr(r.statusField, null, prim => prim)
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"chapter",
|
|
121
|
+
Stdlib_Option.mapOr(r.chapter, null, prim => prim)
|
|
118
122
|
]
|
|
119
123
|
]);
|
|
120
124
|
}
|
|
@@ -165,6 +169,10 @@ function encodeWritableDef(w) {
|
|
|
165
169
|
[
|
|
166
170
|
"events",
|
|
167
171
|
w.events.map(encodeEventDef)
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
"chapter",
|
|
175
|
+
Stdlib_Option.mapOr(w.chapter, null, prim => prim)
|
|
168
176
|
]
|
|
169
177
|
]);
|
|
170
178
|
}
|
|
@@ -182,6 +190,10 @@ function encodeAutomationSliceDef(a) {
|
|
|
182
190
|
[
|
|
183
191
|
"producedCommandTypes",
|
|
184
192
|
a.producedCommandTypes.map(prim => prim)
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
"chapter",
|
|
196
|
+
Stdlib_Option.mapOr(a.chapter, null, prim => prim)
|
|
185
197
|
]
|
|
186
198
|
]);
|
|
187
199
|
}
|
|
@@ -199,6 +211,14 @@ function encodeOutboundTranslationSliceDef(o) {
|
|
|
199
211
|
[
|
|
200
212
|
"inboundCommandTypes",
|
|
201
213
|
o.inboundCommandTypes.map(prim => prim)
|
|
214
|
+
],
|
|
215
|
+
[
|
|
216
|
+
"externalSystem",
|
|
217
|
+
Stdlib_Option.mapOr(o.externalSystem, null, prim => prim)
|
|
218
|
+
],
|
|
219
|
+
[
|
|
220
|
+
"chapter",
|
|
221
|
+
Stdlib_Option.mapOr(o.chapter, null, prim => prim)
|
|
202
222
|
]
|
|
203
223
|
]);
|
|
204
224
|
}
|
|
@@ -212,6 +232,14 @@ function encodeInboundTranslationSliceDef(i) {
|
|
|
212
232
|
[
|
|
213
233
|
"commandTypes",
|
|
214
234
|
i.commandTypes.map(prim => prim)
|
|
235
|
+
],
|
|
236
|
+
[
|
|
237
|
+
"externalSystem",
|
|
238
|
+
Stdlib_Option.mapOr(i.externalSystem, null, prim => prim)
|
|
239
|
+
],
|
|
240
|
+
[
|
|
241
|
+
"chapter",
|
|
242
|
+
Stdlib_Option.mapOr(i.chapter, null, prim => prim)
|
|
215
243
|
]
|
|
216
244
|
]);
|
|
217
245
|
}
|
|
@@ -46,6 +46,7 @@ module type T = {
|
|
|
46
46
|
~inboundTranslationSlices: array<module(ReventlessInfra.InboundTranslationSlice.T)>=?,
|
|
47
47
|
~extensions: array<module(ReventlessInfra.Extension.Blueprint)>=?,
|
|
48
48
|
~extensionPoints: array<module(ReventlessInfra.ExtensionPointMapping.Mapping)>=?,
|
|
49
|
+
~componentChapters: dict<string>=?,
|
|
49
50
|
) => Reventless.Plugin.pluginStructure
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -368,6 +368,30 @@ module Make = (
|
|
|
368
368
|
// so only aggregates, read models, and DCB slice names are included.
|
|
369
369
|
|
|
370
370
|
let extractTypes = schema => Reventless.DcbTag.extractAllVariantNames(schema)
|
|
371
|
+
|
|
372
|
+
// Chapter (intra-plugin grouping band) per component name, mirrored from the
|
|
373
|
+
// static pluginStructure so the hook surface reaches parity with it. The
|
|
374
|
+
// structure is the single source — it was populated by the generator's
|
|
375
|
+
// componentChapters map at makePluginDefinition time.
|
|
376
|
+
let chapterByName: Dict.t<string> = Dict.make()
|
|
377
|
+
switch pluginStructure {
|
|
378
|
+
| Some(ps) =>
|
|
379
|
+
let put = (n, c) =>
|
|
380
|
+
switch c {
|
|
381
|
+
| Some(ch) => chapterByName->Dict.set(n, ch)
|
|
382
|
+
| None => ()
|
|
383
|
+
}
|
|
384
|
+
ps.aggregates->Array.forEach(d => put(d.name, d.chapter))
|
|
385
|
+
ps.readModels->Array.forEach(d => put(d.name, d.chapter))
|
|
386
|
+
ps.stateChangeSlices->Array.forEach(d => put(d.name, d.chapter))
|
|
387
|
+
ps.stateViewSlices->Array.forEach(d => put(d.name, d.chapter))
|
|
388
|
+
ps.automationSlices->Array.forEach(d => put(d.name, d.chapter))
|
|
389
|
+
ps.outboundTranslationSlices->Array.forEach(d => put(d.name, d.chapter))
|
|
390
|
+
ps.inboundTranslationSlices->Array.forEach(d => put(d.name, d.chapter))
|
|
391
|
+
| None => ()
|
|
392
|
+
}
|
|
393
|
+
let chapterFor = (n: string): option<string> => chapterByName->Dict.get(n)
|
|
394
|
+
|
|
371
395
|
// Build per-component schema data and register it for the deployed hook.
|
|
372
396
|
let aggregateComponents = aggregates->Array.map((
|
|
373
397
|
module(M: ReventlessInfra.Aggregate.T with type api = api),
|
|
@@ -382,6 +406,7 @@ module Make = (
|
|
|
382
406
|
eventSchemas: [
|
|
383
407
|
SchemaWalker.walk(M.Spec.name ++ ".event", M.Spec.eventSchema),
|
|
384
408
|
],
|
|
409
|
+
chapter: ?chapterFor(M.Spec.name),
|
|
385
410
|
}
|
|
386
411
|
Plugin_Helpers.componentSchemaRegistry->Dict.set(M.Spec.name, schema)
|
|
387
412
|
({name: M.Spec.name, kind: "Aggregate", schema}: Plugin_Helpers.pluginBuiltComponent)
|
|
@@ -393,6 +418,7 @@ module Make = (
|
|
|
393
418
|
let qn = Api_Naming.queryFieldNamesForReadModel(~plugin=name, ~name=R.Spec.name)
|
|
394
419
|
let schema: Plugin_Helpers.pluginDeployedSchema = {
|
|
395
420
|
queryFields: [qn.singleFieldName, qn.listFieldName],
|
|
421
|
+
chapter: ?chapterFor(R.Spec.name),
|
|
396
422
|
}
|
|
397
423
|
Plugin_Helpers.componentSchemaRegistry->Dict.set(R.Spec.name, schema)
|
|
398
424
|
({name: R.Spec.name, kind: "ReadModel", schema}: Plugin_Helpers.pluginBuiltComponent)
|
|
@@ -421,6 +447,7 @@ module Make = (
|
|
|
421
447
|
consumedEventTypes,
|
|
422
448
|
producedCommandTypes: extractTypes(As.Spec.commandSchema),
|
|
423
449
|
targetName: As.Spec.targetName,
|
|
450
|
+
chapter: ?chapterFor(As.Spec.name),
|
|
424
451
|
},
|
|
425
452
|
)
|
|
426
453
|
})
|
|
@@ -434,6 +461,9 @@ module Make = (
|
|
|
434
461
|
commandTypes: extractTypes(Ots.Spec.inboundCommandSchema),
|
|
435
462
|
// Outbound slices may have no target (option); pass it through.
|
|
436
463
|
targetName: ?Ots.Spec.targetName,
|
|
464
|
+
// Foreign system this slice publishes to (option); drives the external box.
|
|
465
|
+
externalSystem: ?Ots.Spec.externalSystem,
|
|
466
|
+
chapter: ?chapterFor(Ots.Spec.name),
|
|
437
467
|
},
|
|
438
468
|
)
|
|
439
469
|
)
|
|
@@ -445,6 +475,9 @@ module Make = (
|
|
|
445
475
|
{
|
|
446
476
|
commandTypes: extractTypes(Its.Spec.commandSchema),
|
|
447
477
|
targetName: Its.Spec.targetName,
|
|
478
|
+
// Foreign system this slice receives from (option); drives the external box.
|
|
479
|
+
externalSystem: ?Its.Spec.externalSystem,
|
|
480
|
+
chapter: ?chapterFor(Its.Spec.name),
|
|
448
481
|
},
|
|
449
482
|
)
|
|
450
483
|
)
|
|
@@ -464,13 +497,17 @@ module Make = (
|
|
|
464
497
|
commandTypes: extractTypes(Scs.Spec.commandSchema),
|
|
465
498
|
eventTypes: extractEventTypes(Scs.Spec.eventSchema->S.castToUnknown),
|
|
466
499
|
consumedEventTypes: extractEventTypes(Scs.Spec.consumedEventSchema->S.castToUnknown),
|
|
500
|
+
chapter: ?chapterFor(Scs.Spec.name),
|
|
467
501
|
},
|
|
468
502
|
)
|
|
469
503
|
)
|
|
470
504
|
stateViewSlices->Array.forEach((module(Svs: ReventlessInfra.StateViewSlice.T)) =>
|
|
471
505
|
stateSchemas->Dict.set(
|
|
472
506
|
Svs.Spec.name,
|
|
473
|
-
{
|
|
507
|
+
{
|
|
508
|
+
consumedEventTypes: extractEventTypes(Svs.Spec.consumedEventSchema->S.castToUnknown),
|
|
509
|
+
chapter: ?chapterFor(Svs.Spec.name),
|
|
510
|
+
},
|
|
474
511
|
)
|
|
475
512
|
)
|
|
476
513
|
|
|
@@ -993,6 +1030,7 @@ module Make = (
|
|
|
993
1030
|
~inboundTranslationSlices: array<module(ReventlessInfra.InboundTranslationSlice.T)>=[],
|
|
994
1031
|
~extensions: array<module(ReventlessInfra.Extension.Blueprint)>=[],
|
|
995
1032
|
~extensionPoints: array<module(ReventlessInfra.ExtensionPointMapping.Mapping)>=[],
|
|
1033
|
+
~componentChapters: dict<string>=Dict.make(),
|
|
996
1034
|
): Reventless.Plugin.pluginStructure =>
|
|
997
1035
|
Plugin_Structure.make(
|
|
998
1036
|
~name,
|
|
@@ -1005,6 +1043,7 @@ module Make = (
|
|
|
1005
1043
|
~inboundTranslationSlices,
|
|
1006
1044
|
~extensions,
|
|
1007
1045
|
~extensionPoints,
|
|
1046
|
+
~componentChapters,
|
|
1008
1047
|
)
|
|
1009
1048
|
|
|
1010
1049
|
let make = (
|
|
@@ -89,7 +89,7 @@ function Make(Spec) {
|
|
|
89
89
|
pages: pages
|
|
90
90
|
};
|
|
91
91
|
};
|
|
92
|
-
let makePluginDefinition = (name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt) => {
|
|
92
|
+
let makePluginDefinition = (name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt) => {
|
|
93
93
|
let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
|
|
94
94
|
let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
|
|
95
95
|
let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
|
|
@@ -99,7 +99,8 @@ function Make(Spec) {
|
|
|
99
99
|
let inboundTranslationSlices = inboundTranslationSlicesOpt !== undefined ? inboundTranslationSlicesOpt : [];
|
|
100
100
|
let extensions = extensionsOpt !== undefined ? extensionsOpt : [];
|
|
101
101
|
let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
|
|
102
|
-
|
|
102
|
+
let componentChapters = componentChaptersOpt !== undefined ? componentChaptersOpt : ({});
|
|
103
|
+
return Plugin_Structure$ReventlessCore.make(name, aggregates, readModels, stateViewSlices, stateChangeSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, extensions, extensionPoints, componentChapters);
|
|
103
104
|
};
|
|
104
105
|
let make = (name, heartbeatInterval, extensionPointsOpt, extensionsOpt, aggregatesOpt, readModelsOpt, tasksOpt, stateChangeSlicesOpt, stateViewSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, systemCallableComponentsOpt, uiFragments, pluginStructure, opts) => {
|
|
105
106
|
let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
|
|
@@ -264,18 +265,36 @@ function Make(Spec) {
|
|
|
264
265
|
allQueryDbs[param[0] + "Todo"] = param[1].queryDb;
|
|
265
266
|
});
|
|
266
267
|
let queryEngine = QueryEngineAdapter.make(allQueryDbs);
|
|
268
|
+
let chapterByName = {};
|
|
269
|
+
if (pluginStructure !== undefined) {
|
|
270
|
+
let put = (n, c) => {
|
|
271
|
+
if (c !== undefined) {
|
|
272
|
+
chapterByName[n] = c;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
pluginStructure.aggregates.forEach(d => put(d.name, d.chapter));
|
|
277
|
+
pluginStructure.readModels.forEach(d => put(d.name, d.chapter));
|
|
278
|
+
pluginStructure.stateChangeSlices.forEach(d => put(d.name, d.chapter));
|
|
279
|
+
pluginStructure.stateViewSlices.forEach(d => put(d.name, d.chapter));
|
|
280
|
+
pluginStructure.automationSlices.forEach(d => put(d.name, d.chapter));
|
|
281
|
+
pluginStructure.outboundTranslationSlices.forEach(d => put(d.name, d.chapter));
|
|
282
|
+
pluginStructure.inboundTranslationSlices.forEach(d => put(d.name, d.chapter));
|
|
283
|
+
}
|
|
267
284
|
let aggregateComponents = aggregates.map(M => {
|
|
268
285
|
let schema_commandTypes = DcbTag$Reventless.extractAllVariantNames(M.Spec.commandSchema);
|
|
269
286
|
let schema_eventTypes = DcbTag$Reventless.extractAllVariantNames(M.Spec.eventSchema);
|
|
270
287
|
let schema_errorTypes = DcbTag$Reventless.extractAllVariantNames(M.Spec.errorSchema);
|
|
271
288
|
let schema_commandSchemas = [SchemaWalker$ReventlessCore.walk(M.Spec.name + ".command", M.Spec.commandSchema)];
|
|
272
289
|
let schema_eventSchemas = [SchemaWalker$ReventlessCore.walk(M.Spec.name + ".event", M.Spec.eventSchema)];
|
|
290
|
+
let schema_chapter = chapterByName[M.Spec.name];
|
|
273
291
|
let schema = {
|
|
274
292
|
commandTypes: schema_commandTypes,
|
|
275
293
|
eventTypes: schema_eventTypes,
|
|
276
294
|
errorTypes: schema_errorTypes,
|
|
277
295
|
commandSchemas: schema_commandSchemas,
|
|
278
|
-
eventSchemas: schema_eventSchemas
|
|
296
|
+
eventSchemas: schema_eventSchemas,
|
|
297
|
+
chapter: schema_chapter
|
|
279
298
|
};
|
|
280
299
|
Plugin_Helpers$ReventlessCore.componentSchemaRegistry[M.Spec.name] = schema;
|
|
281
300
|
return {
|
|
@@ -290,8 +309,10 @@ function Make(Spec) {
|
|
|
290
309
|
qn.singleFieldName,
|
|
291
310
|
qn.listFieldName
|
|
292
311
|
];
|
|
312
|
+
let schema_chapter = chapterByName[R.Spec.name];
|
|
293
313
|
let schema = {
|
|
294
|
-
queryFields: schema_queryFields
|
|
314
|
+
queryFields: schema_queryFields,
|
|
315
|
+
chapter: schema_chapter
|
|
295
316
|
};
|
|
296
317
|
Plugin_Helpers$ReventlessCore.componentSchemaRegistry[R.Spec.name] = schema;
|
|
297
318
|
return {
|
|
@@ -306,20 +327,25 @@ function Make(Spec) {
|
|
|
306
327
|
routingSchemas[As.Spec.name] = {
|
|
307
328
|
consumedEventTypes: consumedEventTypes,
|
|
308
329
|
producedCommandTypes: DcbTag$Reventless.extractAllVariantNames(As.Spec.commandSchema),
|
|
309
|
-
targetName: As.Spec.targetName
|
|
330
|
+
targetName: As.Spec.targetName,
|
|
331
|
+
chapter: chapterByName[As.Spec.name]
|
|
310
332
|
};
|
|
311
333
|
});
|
|
312
334
|
outboundTranslationSlices.forEach(Ots => {
|
|
313
335
|
routingSchemas[Ots.Spec.name] = {
|
|
314
336
|
commandTypes: DcbTag$Reventless.extractAllVariantNames(Ots.Spec.inboundCommandSchema),
|
|
315
337
|
consumedEventTypes: DcbTag$Reventless.extractVariantNames(Ots.Spec.consumedEventSchema),
|
|
316
|
-
targetName: Ots.Spec.targetName
|
|
338
|
+
targetName: Ots.Spec.targetName,
|
|
339
|
+
externalSystem: Ots.Spec.externalSystem,
|
|
340
|
+
chapter: chapterByName[Ots.Spec.name]
|
|
317
341
|
};
|
|
318
342
|
});
|
|
319
343
|
inboundTranslationSlices.forEach(Its => {
|
|
320
344
|
routingSchemas[Its.Spec.name] = {
|
|
321
345
|
commandTypes: DcbTag$Reventless.extractAllVariantNames(Its.Spec.commandSchema),
|
|
322
|
-
targetName: Its.Spec.targetName
|
|
346
|
+
targetName: Its.Spec.targetName,
|
|
347
|
+
externalSystem: Its.Spec.externalSystem,
|
|
348
|
+
chapter: chapterByName[Its.Spec.name]
|
|
323
349
|
};
|
|
324
350
|
});
|
|
325
351
|
let stateSchemas = {};
|
|
@@ -327,12 +353,14 @@ function Make(Spec) {
|
|
|
327
353
|
stateSchemas[Scs.Spec.name] = {
|
|
328
354
|
commandTypes: DcbTag$Reventless.extractAllVariantNames(Scs.Spec.commandSchema),
|
|
329
355
|
eventTypes: DcbTag$Reventless.extractVariantNames(Scs.Spec.eventSchema),
|
|
330
|
-
consumedEventTypes: DcbTag$Reventless.extractVariantNames(Scs.Spec.consumedEventSchema)
|
|
356
|
+
consumedEventTypes: DcbTag$Reventless.extractVariantNames(Scs.Spec.consumedEventSchema),
|
|
357
|
+
chapter: chapterByName[Scs.Spec.name]
|
|
331
358
|
};
|
|
332
359
|
});
|
|
333
360
|
stateViewSlices.forEach(Svs => {
|
|
334
361
|
stateSchemas[Svs.Spec.name] = {
|
|
335
|
-
consumedEventTypes: DcbTag$Reventless.extractVariantNames(Svs.Spec.consumedEventSchema)
|
|
362
|
+
consumedEventTypes: DcbTag$Reventless.extractVariantNames(Svs.Spec.consumedEventSchema),
|
|
363
|
+
chapter: chapterByName[Svs.Spec.name]
|
|
336
364
|
};
|
|
337
365
|
});
|
|
338
366
|
let enrichedComponents = (d, kind, schemas) => Object.keys(d).map(name => {
|
|
@@ -78,6 +78,12 @@ type pluginDeployedSchema = {
|
|
|
78
78
|
/** For routing slices (automation / inbound + outbound translation): the name
|
|
79
79
|
of the writable component this slice routes its commands to. */
|
|
80
80
|
targetName?: string,
|
|
81
|
+
/** For inbound / outbound translation slices: the foreign system this slice
|
|
82
|
+
integrates with, mirrored from the static `pluginStructure` for hook parity.
|
|
83
|
+
Drives the deployed graph's external-system boundary box. Absent when the slice
|
|
84
|
+
names no external system. See the translation-slice defs on
|
|
85
|
+
`Reventless.Plugin.pluginStructure`. */
|
|
86
|
+
externalSystem?: string,
|
|
81
87
|
sharedBy?: array<string>,
|
|
82
88
|
extensionPointName?: string,
|
|
83
89
|
providerPlugin?: string,
|
|
@@ -88,6 +94,11 @@ type pluginDeployedSchema = {
|
|
|
88
94
|
eventSchemas?: array<typeSchema>,
|
|
89
95
|
/** Field-level schema for the state type. */
|
|
90
96
|
stateSchema?: typeSchema,
|
|
97
|
+
/** Intra-plugin grouping band (the "chapter") this component belongs to, mirrored
|
|
98
|
+
from the static `pluginStructure` for parity on the hook surface. Absent when the
|
|
99
|
+
component carries no chapter (lives directly under a kind-folder). See
|
|
100
|
+
`Reventless.Plugin.queryableDef.chapter`. */
|
|
101
|
+
chapter?: string,
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
// ---------------------------------------------------------------------------
|