@reventlessdev/reventless-infra 3.0.0-alpha.151 → 3.0.0-alpha.152
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 +3 -3
- package/src/types/ExtensionMapping.res +64 -83
- package/src/types/ExtensionMapping.res.mjs +5 -0
- package/src/types/ExtensionPointMapping.res +47 -86
- package/src/types/PluginExtensionPointSpec.res +3 -1
- package/src/types/PluginExtensionPointSpec.res.mjs +24 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,31 @@
|
|
|
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.152 (2026-08-27)
|
|
7
|
+
|
|
8
|
+
* feat(spec)!: reflect the command direction across a port, both halves ([f2fe258](https://github.com/ReventlessDev/reventless-core/commit/f2fe258d195b74f4a61488edee305665341020ea))
|
|
9
|
+
* feat(spec)!: drop the plugin protocol's reconnect event, which nothing published ([7549db6](https://github.com/ReventlessDev/reventless-core/commit/7549db6aac6ad3f94dbb8d72dac4b4e783756ad1))
|
|
10
|
+
* feat(spec)!: read the port's translation table off the mapping's arms ([956348a](https://github.com/ReventlessDev/reventless-core/commit/956348a9fcb256cfff9db51809bdc27d73360e6c))
|
|
11
|
+
|
|
12
|
+
### BREAKING CHANGES
|
|
13
|
+
|
|
14
|
+
* `ExtensionPointMapping.Mapping` gains `acceptedCommands` and
|
|
15
|
+
`ExtensionMapping.Mapping` gains `issuedCommands`; both are derived, so a
|
|
16
|
+
mapping the ppx can read needs no source change, and one it cannot names itself
|
|
17
|
+
at compile time. Definitions persisted before the two new def fields must be
|
|
18
|
+
re-emitted before a consumer can read them as present — and until then they
|
|
19
|
+
decode as None, which means unknown, not "issues nothing".
|
|
20
|
+
* `PluginExtensionPointSpec.event` no longer declares
|
|
21
|
+
`PluginReconnected`. An extension matching on it was matching an event it could
|
|
22
|
+
never receive; drop the arm.
|
|
23
|
+
* `ExtensionPointMapping.Mapping` requires `publishedEvents` and
|
|
24
|
+
`ExtensionMapping.Mapping` requires `handledEvents`; the ppx injects both for
|
|
25
|
+
mappings it can read, so app code must build against the matching ppx. Plugin
|
|
26
|
+
definitions persisted before the new fields must be re-emitted before a consumer
|
|
27
|
+
reads them as present.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
6
31
|
# 3.0.0-alpha.151 (2026-08-23)
|
|
7
32
|
|
|
8
33
|
**Note:** Version bump only for package @reventlessdev/reventless-infra
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-infra",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.152",
|
|
4
4
|
"description": "Infrastructure types for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"uuid": "^13.0.0",
|
|
19
19
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
20
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
21
|
-
"@reventlessdev/
|
|
22
|
-
"@reventlessdev/
|
|
21
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.124"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"rescript": "12.3.0",
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
Used when an extension needs to dispatch to an extension point it does not
|
|
4
|
-
own — the command JSON is forwarded opaquely without re-encoding.
|
|
5
|
-
*/
|
|
1
|
+
/** A command routed to an extension point this extension does not own —
|
|
2
|
+
forwarded opaquely, without re-encoding. */
|
|
6
3
|
type forwardCommand = {
|
|
7
4
|
extensionPointName: string,
|
|
8
5
|
id: string,
|
|
@@ -11,18 +8,9 @@ type forwardCommand = {
|
|
|
11
8
|
|
|
12
9
|
type id = string
|
|
13
10
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
When an extension point emits an event, the extension's mapping can:
|
|
18
|
-
- publish a command to the aggregate it wraps (`PublishAggregateCommand`)
|
|
19
|
-
- publish a command to the StateChangeSlice it wraps (`PublishStateChangeSliceCommand`)
|
|
20
|
-
— no id argument; the framework derives the FIFO grouping id from the command's
|
|
21
|
-
`@partitionTag` (or `@compositePartitionTag`) field.
|
|
22
|
-
- publish a command back to the extension point (`PublishExtensionPointCommand`)
|
|
23
|
-
- forward a command to another extension point opaquely (`ForwardCommand`)
|
|
24
|
-
- handle an extension-point-defined directive via an async handler (`HandleDirective`)
|
|
25
|
-
*/
|
|
11
|
+
/** What `mapIncomingEvent` can do with a published event: command the wrapped
|
|
12
|
+
aggregate or slice, command the extension point, forward, or run a directive.
|
|
13
|
+
The slice forms take no id — the FIFO group comes from `@partitionTag`. */
|
|
26
14
|
type incomingCommandAction<'aggregateCommand, 'extensionPointCommand, 'directive> =
|
|
27
15
|
| PublishAggregateCommand(id, 'aggregateCommand)
|
|
28
16
|
| PublishAggregateCommandAsync(promise<(id, 'aggregateCommand)>)
|
|
@@ -34,26 +22,14 @@ type incomingCommandAction<'aggregateCommand, 'extensionPointCommand, 'directive
|
|
|
34
22
|
| ForwardCommand(forwardCommand)
|
|
35
23
|
| HandleDirective(Reventless.Handler.handler<'directive>, 'directive)
|
|
36
24
|
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
When the wrapped aggregate emits an event, the extension can:
|
|
41
|
-
- publish a command to the extension point (`PublishExtensionPointCommand`)
|
|
42
|
-
- forward a command to another extension point opaquely (`ForwardCommand`)
|
|
43
|
-
- handle an extension-point-defined directive via an async handler (`HandleDirective`)
|
|
44
|
-
*/
|
|
25
|
+
/** What `mapOutgoingEvent` can do with a delegate event: command the extension
|
|
26
|
+
point, forward, or run a directive. */
|
|
45
27
|
type outgoingCommandAction<'extensionPointCommand, 'directive> =
|
|
46
28
|
| PublishExtensionPointCommand(id, 'extensionPointCommand)
|
|
47
29
|
| ForwardCommand(forwardCommand)
|
|
48
30
|
| HandleDirective(Reventless.Handler.handler<'directive>, 'directive)
|
|
49
31
|
|
|
50
|
-
/**
|
|
51
|
-
Maps an extension point event to actions on the wrapped aggregate or extension point.
|
|
52
|
-
|
|
53
|
-
Called when the extension point emits an event that this extension handles.
|
|
54
|
-
Receives the entity ID, the event, message metadata, the plugin definition,
|
|
55
|
-
and the query engine.
|
|
56
|
-
*/
|
|
32
|
+
/** Maps a published event to actions on the wrapped delegate or the extension point. */
|
|
57
33
|
type mapIncomingEvent<
|
|
58
34
|
'extensionPointEvent,
|
|
59
35
|
'aggregateCommand,
|
|
@@ -69,12 +45,8 @@ type mapIncomingEvent<
|
|
|
69
45
|
incomingCommandAction<'aggregateCommand, 'extensionPointCommand, 'extensionPointDirective>,
|
|
70
46
|
>
|
|
71
47
|
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Optionally defined — return `None` if the aggregate's outgoing events do not
|
|
76
|
-
need to be reflected back through the extension point.
|
|
77
|
-
*/
|
|
48
|
+
/** Maps a delegate event back to actions on the extension point. `None` when
|
|
49
|
+
nothing flows back. */
|
|
78
50
|
type mapOutgoingEvent<'aggregateEvent, 'extensionPointCommand, 'extensionPointDirective> = (
|
|
79
51
|
string,
|
|
80
52
|
'aggregateEvent,
|
|
@@ -82,11 +54,8 @@ type mapOutgoingEvent<'aggregateEvent, 'extensionPointCommand, 'extensionPointDi
|
|
|
82
54
|
Reventless.Plugin.pluginDefinition,
|
|
83
55
|
) => array<outgoingCommandAction<'extensionPointCommand, 'extensionPointDirective>>
|
|
84
56
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
Mirrors `ExtensionPoint.Spec` so the extension can be type-checked
|
|
88
|
-
against the extension point's command / event / directive types.
|
|
89
|
-
*/
|
|
57
|
+
/** The extension point protocol this `Extension` connects to — mirrors
|
|
58
|
+
`ExtensionPoint.Spec` so the mapping type-checks against it. */
|
|
90
59
|
module type Spec = {
|
|
91
60
|
let name: string
|
|
92
61
|
let moduleUrl: string
|
|
@@ -99,30 +68,35 @@ module type Spec = {
|
|
|
99
68
|
type directive
|
|
100
69
|
}
|
|
101
70
|
|
|
102
|
-
/**
|
|
103
|
-
|
|
71
|
+
/** One published event this extension handles and the commands it routes to —
|
|
72
|
+
the subscriber's half of `ExtensionPointMapping.publishedEvent`. Bare names,
|
|
73
|
+
resolved against the `Delegate`'s commands first, the EP's second. */
|
|
74
|
+
type handledEvent = {
|
|
75
|
+
name: string,
|
|
76
|
+
toCommandTypes: array<string>,
|
|
77
|
+
}
|
|
104
78
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
79
|
+
/** One command this extension sends back to the port and the `Delegate` events
|
|
80
|
+
producing it — the subscriber's half of `ExtensionPointMapping.acceptedCommand`.
|
|
81
|
+
Read off `mapOutgoingEvent` only: an EP command published from an arm of
|
|
82
|
+
`mapIncomingEvent` is an event→command edge and stays in `handledEvent`. */
|
|
83
|
+
type issuedCommand = {
|
|
84
|
+
name: string,
|
|
85
|
+
fromEventTypes: array<string>,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** An extension's bidirectional mapping between one extension point and one
|
|
89
|
+
delegate (aggregate or DCB slice). */
|
|
110
90
|
module type Mapping = {
|
|
111
91
|
module ExtensionPoint: Spec
|
|
112
92
|
module Delegate: Reventless.Aggregate.Spec
|
|
113
93
|
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
// point to dynamic-import the user mapping for runtime reconstruction of
|
|
117
|
-
// mapIncomingEvent / mapOutgoingEvent. PPX-injected on @@reventless.extension
|
|
118
|
-
// files as the same specifier as the file-level moduleUrl.
|
|
94
|
+
// The extension file's own url — dynamic-imported at runtime to reconstruct the
|
|
95
|
+
// two mapping functions. Injected by `@@reventless.extension`.
|
|
119
96
|
let moduleUrl: string
|
|
120
97
|
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// cold start (Mapping.ExtensionPoint and Mapping.Delegate are erased in the
|
|
124
|
-
// compiled .res.mjs export). PPX-injected on @@reventless.extension files
|
|
125
|
-
// as `Delegate.moduleUrl`.
|
|
98
|
+
// The Delegate's url — the EventCollector imports its spec at cold start, since
|
|
99
|
+
// the compiled export erases `Mapping.Delegate`.
|
|
126
100
|
let delegateModuleUrl: string
|
|
127
101
|
|
|
128
102
|
let mapIncomingEvent: mapIncomingEvent<
|
|
@@ -135,12 +109,18 @@ module type Mapping = {
|
|
|
135
109
|
let mapOutgoingEvent: option<
|
|
136
110
|
mapOutgoingEvent<Delegate.event, ExtensionPoint.command, ExtensionPoint.directive>,
|
|
137
111
|
>
|
|
112
|
+
|
|
113
|
+
/** Which published event routes to which commands — see `handledEvent`. Derived
|
|
114
|
+
by the PPX from `mapIncomingEvent`'s arms; hand-written only where it says
|
|
115
|
+
it cannot read them. */
|
|
116
|
+
let handledEvents: array<handledEvent>
|
|
117
|
+
|
|
118
|
+
/** Which commands travel back to the port — see `issuedCommand`. Derived from
|
|
119
|
+
`mapOutgoingEvent`'s arms, under the same rule. */
|
|
120
|
+
let issuedCommands: array<issuedCommand>
|
|
138
121
|
}
|
|
139
122
|
|
|
140
|
-
/**
|
|
141
|
-
A dummy target used when an extension does not wrap a real aggregate or slice.
|
|
142
|
-
Satisfies `Aggregate.Spec` with unit command / event / error types.
|
|
143
|
-
*/
|
|
123
|
+
/** A stand-in for an extension that wraps no aggregate or slice. */
|
|
144
124
|
module NoDelegate = {
|
|
145
125
|
let name = "NoDelegate"
|
|
146
126
|
|
|
@@ -193,6 +173,14 @@ module type T = {
|
|
|
193
173
|
|
|
194
174
|
let delegateName: string
|
|
195
175
|
|
|
176
|
+
// All carried through the functor: `Plugin_Structure` sees an extension only
|
|
177
|
+
// compiled, and `T` erases the Delegate's types, so the checks against the two
|
|
178
|
+
// tables need its command and event names rather than its schemas.
|
|
179
|
+
let handledEvents: array<handledEvent>
|
|
180
|
+
let issuedCommands: array<issuedCommand>
|
|
181
|
+
let delegateCommandNames: array<string>
|
|
182
|
+
let delegateEventNames: array<string>
|
|
183
|
+
|
|
196
184
|
let mapIncomingEvent: (
|
|
197
185
|
Reventless.Message.event'<string, ExtensionPoint.event>,
|
|
198
186
|
pluginDefinition,
|
|
@@ -217,21 +205,18 @@ module Make = (MappingImpl: Mapping): (
|
|
|
217
205
|
module Delegate = MappingImpl.Delegate
|
|
218
206
|
let delegateName = Delegate.name
|
|
219
207
|
let extensionPointName = Spec.name
|
|
208
|
+
let handledEvents = MappingImpl.handledEvents
|
|
209
|
+
let issuedCommands = MappingImpl.issuedCommands
|
|
210
|
+
let delegateCommandNames = Reventless.DcbTag.extractAllVariantNames(Delegate.commandSchema)
|
|
220
211
|
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
// are silently skipped without any decode attempt.
|
|
225
|
-
// extractAllVariantNames keeps payload-less variants (sury-compiled bare
|
|
226
|
-
// `S.literal("Name")` strings) — the JSON envelope's `event` TAG still
|
|
227
|
-
// carries the literal name, so the filter would otherwise drop them.
|
|
212
|
+
// Pre-filter for incoming envelopes: sibling variants the Delegate did not
|
|
213
|
+
// declare are skipped undecoded. Payload-less variants included — the
|
|
214
|
+
// envelope's `event` TAG still carries their name.
|
|
228
215
|
let acceptedTags = Reventless.DcbTag.extractAllVariantNames(Delegate.eventSchema)
|
|
216
|
+
let delegateEventNames = acceptedTags
|
|
229
217
|
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
// `@compositePartitionTag` annotation exists (i.e. the Delegate is an Aggregate,
|
|
233
|
-
// not a StateChangeSlice — in which case the user should use
|
|
234
|
-
// `PublishAggregateCommand` instead).
|
|
218
|
+
// Partition tag for `PublishStateChangeSliceCommand*`. Throws when the Delegate
|
|
219
|
+
// declares none — an Aggregate, where `PublishAggregateCommand` is the right form.
|
|
235
220
|
let derivedPartitionTagLazy = ref(None)
|
|
236
221
|
let getDerivedPartitionTag = () =>
|
|
237
222
|
switch derivedPartitionTagLazy.contents {
|
|
@@ -253,10 +238,8 @@ module Make = (MappingImpl: Mapping): (
|
|
|
253
238
|
Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec)
|
|
254
239
|
}
|
|
255
240
|
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
// JSON `comp` field. Without the unified logger installed, Effect's default
|
|
259
|
-
// logger still renders the annotation, just less prettily.
|
|
241
|
+
// `comp` as an Effect log annotation; EffectLogger.install lifts it to the
|
|
242
|
+
// top-level JSON field.
|
|
260
243
|
let compLog = (comp, msg) =>
|
|
261
244
|
Effect.logInfo(msg)->Effect.annotateLogs("comp", comp)->Effect.runSync
|
|
262
245
|
|
|
@@ -378,9 +361,7 @@ module Make = (MappingImpl: Mapping): (
|
|
|
378
361
|
|
|
379
362
|
let doMapOutgoingEvent = (mapOutgoingEventImpl, targetEvent'Json, pluginDef) => {
|
|
380
363
|
let tag = variantTagOfEnvelope(targetEvent'Json)
|
|
381
|
-
//
|
|
382
|
-
// did not declare are not this mapping's concern — skip silently with no
|
|
383
|
-
// decode attempt.
|
|
364
|
+
// Not this mapping's concern — skip without decoding.
|
|
384
365
|
if !(acceptedTags->Array.includes(tag)) {
|
|
385
366
|
[]
|
|
386
367
|
} else {
|
|
@@ -47,6 +47,7 @@ function Make(MappingImpl) {
|
|
|
47
47
|
let Delegate = MappingImpl.Delegate;
|
|
48
48
|
let delegateName = Delegate.name;
|
|
49
49
|
let extensionPointName = Spec.name;
|
|
50
|
+
let delegateCommandNames = DcbTag$Reventless.extractAllVariantNames(Delegate.commandSchema);
|
|
50
51
|
let acceptedTags = DcbTag$Reventless.extractAllVariantNames(Delegate.eventSchema);
|
|
51
52
|
let derivedPartitionTagLazy = {
|
|
52
53
|
contents: undefined
|
|
@@ -248,6 +249,10 @@ function Make(MappingImpl) {
|
|
|
248
249
|
}));
|
|
249
250
|
return {
|
|
250
251
|
delegateName: delegateName,
|
|
252
|
+
handledEvents: MappingImpl.handledEvents,
|
|
253
|
+
issuedCommands: MappingImpl.issuedCommands,
|
|
254
|
+
delegateCommandNames: delegateCommandNames,
|
|
255
|
+
delegateEventNames: acceptedTags,
|
|
251
256
|
mapIncomingEvent: mapIncomingEvent,
|
|
252
257
|
mapOutgoingEvent: mapOutgoingEvent
|
|
253
258
|
};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
type extensionPointName = string
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
or query engine as a side effect. Used as the handler argument in `HandleDirective`
|
|
6
|
-
on both `commandAction` and `eventAction`.
|
|
7
|
-
*/
|
|
3
|
+
/** An async handler for a typed directive, used by `HandleDirective` on both
|
|
4
|
+
`commandAction` and `eventAction`. */
|
|
8
5
|
type directiveHandler<'directive> = (
|
|
9
6
|
Reventless.Schedule.create,
|
|
10
7
|
Reventless.Schedule.delete,
|
|
@@ -12,37 +9,20 @@ type directiveHandler<'directive> = (
|
|
|
12
9
|
'directive,
|
|
13
10
|
) => promise<unit>
|
|
14
11
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
receives a command from an extension.
|
|
18
|
-
|
|
19
|
-
- `PublishCommand(id, cmd)` — publish a command to the wrapped aggregate
|
|
20
|
-
- `HandleDirective(handler, directive)` — invoke an async handler for an
|
|
21
|
-
extension-point-defined directive
|
|
22
|
-
*/
|
|
23
|
-
/* these actions are needed for Mapping */
|
|
12
|
+
/** What `mapIncomingCommand` can do with a command an extension published:
|
|
13
|
+
publish one to the wrapped aggregate, or run a directive handler. */
|
|
24
14
|
type commandAction<'command, 'directive> =
|
|
25
15
|
| PublishCommand(string, 'command)
|
|
26
16
|
| HandleDirective(directiveHandler<'directive>, 'directive)
|
|
27
17
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
emits an event that should be reflected through the extension point.
|
|
31
|
-
|
|
32
|
-
- `PublishEvent(id, event)` — synchronously emit an extension point event
|
|
33
|
-
- `PublishEventAsync(promise)` — resolve a promise and emit the resulting event
|
|
34
|
-
- `HandleDirective(handler, directive)` — invoke an async handler for an
|
|
35
|
-
extension-point-defined directive
|
|
36
|
-
*/
|
|
18
|
+
/** What `mapOutgoingEvent` can do with a Delegate event: publish an EP event,
|
|
19
|
+
publish one behind a promise, or run a directive handler. */
|
|
37
20
|
type eventAction<'event, 'directive> =
|
|
38
21
|
| PublishEvent(string, 'event)
|
|
39
22
|
| PublishEventAsync(promise<(string, 'event)>)
|
|
40
23
|
| HandleDirective(directiveHandler<'directive>, 'directive)
|
|
41
24
|
|
|
42
|
-
/**
|
|
43
|
-
The extension point protocol — defines the command, event, and directive types
|
|
44
|
-
that extensions and aggregates exchange through this extension point.
|
|
45
|
-
*/
|
|
25
|
+
/** The extension point protocol: the command, event and directive types crossing it. */
|
|
46
26
|
module type Spec = {
|
|
47
27
|
let name: string
|
|
48
28
|
let moduleUrl: string
|
|
@@ -55,26 +35,14 @@ module type Spec = {
|
|
|
55
35
|
type directive
|
|
56
36
|
}
|
|
57
37
|
|
|
58
|
-
/**
|
|
59
|
-
Maps an incoming extension point command to zero or more aggregate commands
|
|
60
|
-
(or side-effect calls).
|
|
61
|
-
|
|
62
|
-
Called when an extension publishes a command to this extension point.
|
|
63
|
-
Receives the entity ID, the command, and the message metadata.
|
|
64
|
-
*/
|
|
38
|
+
/** Maps a command an extension published to zero or more aggregate commands. */
|
|
65
39
|
type mapIncomingCommand<'extensionPointCommand, 'aggregateCommand, 'extensionPointDirective> = (
|
|
66
40
|
string,
|
|
67
41
|
'extensionPointCommand,
|
|
68
42
|
Reventless.Message.meta,
|
|
69
43
|
) => array<commandAction<'aggregateCommand, 'extensionPointDirective>>
|
|
70
44
|
|
|
71
|
-
/**
|
|
72
|
-
Maps an aggregate outgoing event to zero or more extension point events
|
|
73
|
-
(or side-effect calls).
|
|
74
|
-
|
|
75
|
-
Called when the wrapped aggregate emits an event. Receives the entity ID,
|
|
76
|
-
the event, message metadata, and the query engine.
|
|
77
|
-
*/
|
|
45
|
+
/** Maps an aggregate event to zero or more extension point events. */
|
|
78
46
|
type mapOutgoingEvent<'aggregateEvent, 'extensionPointEvent, 'extensionPointDirective> = (
|
|
79
47
|
string,
|
|
80
48
|
'aggregateEvent,
|
|
@@ -82,22 +50,30 @@ type mapOutgoingEvent<'aggregateEvent, 'extensionPointEvent, 'extensionPointDire
|
|
|
82
50
|
Reventless.QueryEngine.operations,
|
|
83
51
|
) => array<eventAction<'extensionPointEvent, 'extensionPointDirective>>
|
|
84
52
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
53
|
+
/** One published event of this port and the `Delegate` events producing it. Bare
|
|
54
|
+
constructor names, qualified downstream by `Plugin_Structure`. Per published
|
|
55
|
+
event, so many-to-one — the case a port exists for — reads naturally. */
|
|
56
|
+
type publishedEvent = {
|
|
57
|
+
name: string,
|
|
58
|
+
fromEventTypes: array<string>,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The port's inbound half: one command this port takes and the `Delegate`
|
|
62
|
+
commands it routes to. Keyed by the arriving command, mirroring
|
|
63
|
+
`ExtensionMapping.handledEvent`. */
|
|
64
|
+
type acceptedCommand = {
|
|
65
|
+
name: string,
|
|
66
|
+
toCommandTypes: array<string>,
|
|
67
|
+
}
|
|
88
68
|
|
|
89
|
-
|
|
90
|
-
`ExtensionPointMapping.
|
|
91
|
-
*/
|
|
69
|
+
/** One aggregate's mapping to an extension point. Pass to
|
|
70
|
+
`ExtensionPointMapping.Make` for the compiled `T`. */
|
|
92
71
|
module type Mapping = {
|
|
93
72
|
module ExtensionPoint: Spec
|
|
94
73
|
module Delegate: Reventless.Aggregate.Spec
|
|
95
74
|
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
// (the spec, in catalog-spec). Auto-injected by `@@reventless.spec` on
|
|
99
|
-
// ExtensionPointMapping files. The EventCollector runtime needs THIS url
|
|
100
|
-
// (not the spec's) to dynamic-import the mapping file's mapOutgoingEvent.
|
|
75
|
+
// The mapping file's own url, not the spec's — the EventCollector runtime
|
|
76
|
+
// dynamic-imports mapOutgoingEvent from it. Injected by `@@reventless.spec`.
|
|
101
77
|
let moduleUrl: string
|
|
102
78
|
|
|
103
79
|
let mapIncomingCommand: mapIncomingCommand<
|
|
@@ -109,10 +85,17 @@ module type Mapping = {
|
|
|
109
85
|
let mapOutgoingEvent: option<
|
|
110
86
|
mapOutgoingEvent<Delegate.event, ExtensionPoint.event, ExtensionPoint.directive>,
|
|
111
87
|
>
|
|
88
|
+
|
|
89
|
+
/** The port's translation table — see `publishedEvent`. Derived by the PPX from
|
|
90
|
+
`mapOutgoingEvent`'s own arms; write it by hand only where it says it cannot. */
|
|
91
|
+
let publishedEvents: array<publishedEvent>
|
|
92
|
+
|
|
93
|
+
/** The same for the command direction — see `acceptedCommand`. Derived from
|
|
94
|
+
`mapIncomingCommand`'s arms, under the same rule. */
|
|
95
|
+
let acceptedCommands: array<acceptedCommand>
|
|
112
96
|
}
|
|
113
97
|
|
|
114
|
-
//
|
|
115
|
-
// Created by ExtensionPointMapping.Make (in reventless); consumed by ExtensionPoint_Callback
|
|
98
|
+
// Pre-compiled action types: made by Make, consumed by ExtensionPoint_Callback
|
|
116
99
|
// and ExtensionPoint_Operations.
|
|
117
100
|
|
|
118
101
|
/** Internal runtime action produced after pre-encoding a `commandAction`. Not for direct use. */
|
|
@@ -126,25 +109,15 @@ type abstractEventAction<'extensionPointEvent> =
|
|
|
126
109
|
| AbstractPublishEventAsync(promise<(string, Reventless.Message.meta, JSON.t)>)
|
|
127
110
|
| AbstractHandleDirective(unit => promise<unit>)
|
|
128
111
|
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
The runtime uses `mapIncomingCommands` and `mapOutgoingEvent` to dispatch commands
|
|
133
|
-
and events without knowing the concrete extension point or aggregate types.
|
|
134
|
-
Application developers call `Make` themselves; the result satisfies this type.
|
|
135
|
-
*/
|
|
136
|
-
// Pre-compiled mapping module type. Created by ExtensionPointMapping.Make(Spec, Mapping).
|
|
137
|
-
// App developers call Make themselves; the result satisfies this type.
|
|
112
|
+
/** A compiled mapping, produced by `Make`. Lets the runtime dispatch without
|
|
113
|
+
knowing the concrete extension point or aggregate types. */
|
|
138
114
|
module type T = {
|
|
139
115
|
module ExtensionPoint: Spec
|
|
140
116
|
|
|
141
117
|
/** Name of the target this mapping connects to the extension point. */
|
|
142
118
|
let delegateName: string
|
|
143
119
|
|
|
144
|
-
/**
|
|
145
|
-
Converts a batch of typed extension point commands into pre-encoded abstract actions.
|
|
146
|
-
Called by the extension point runtime for each incoming command batch.
|
|
147
|
-
*/
|
|
120
|
+
/** Pre-encodes a batch of typed EP commands for the runtime. */
|
|
148
121
|
let mapIncomingCommands: (
|
|
149
122
|
array<CommandTopic.topicItem<Reventless.Message.command'<Reventless.Id.String.t, ExtensionPoint.command>>>,
|
|
150
123
|
Reventless.Schedule.create,
|
|
@@ -152,10 +125,7 @@ module type T = {
|
|
|
152
125
|
Reventless.QueryEngine.operations,
|
|
153
126
|
) => array<abstractCommandAction>
|
|
154
127
|
|
|
155
|
-
/**
|
|
156
|
-
Converts a raw aggregate event JSON into pre-encoded abstract event actions.
|
|
157
|
-
`None` if this mapping does not produce outgoing extension point events.
|
|
158
|
-
*/
|
|
128
|
+
/** Pre-encodes a raw aggregate event JSON. `None` if nothing is published out. */
|
|
159
129
|
let mapOutgoingEvent: option<
|
|
160
130
|
(
|
|
161
131
|
JSON.t,
|
|
@@ -174,20 +144,13 @@ module Make = (MappingImpl: Mapping): (
|
|
|
174
144
|
let delegateName = Delegate.name
|
|
175
145
|
let extensionPointName = Spec.name
|
|
176
146
|
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// are silently skipped without any decode attempt.
|
|
181
|
-
// Pre-filter incoming envelopes against the Delegate's full constructor
|
|
182
|
-
// set — includes payload-less variants (e.g. UnknownPluginDetected
|
|
183
|
-
// compiled to `S.literal("…")`) since the JSON envelope's `event` TAG
|
|
184
|
-
// still carries the literal name even when the variant has no payload.
|
|
147
|
+
// Pre-filter for incoming envelopes: sibling variants the Delegate did not
|
|
148
|
+
// declare are skipped undecoded. Payload-less variants included — the
|
|
149
|
+
// envelope's `event` TAG still carries their name.
|
|
185
150
|
let acceptedTags = Reventless.DcbTag.extractAllVariantNames(Delegate.eventSchema)
|
|
186
151
|
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
// JSON `comp` field. Without the unified logger installed, Effect's default
|
|
190
|
-
// logger still renders the annotation, just less prettily.
|
|
152
|
+
// `comp` as an Effect log annotation; EffectLogger.install lifts it to the
|
|
153
|
+
// top-level JSON field.
|
|
191
154
|
let compLog = (comp, msg) =>
|
|
192
155
|
Effect.logInfo(msg)->Effect.annotateLogs("comp", comp)->Effect.runSync
|
|
193
156
|
|
|
@@ -248,9 +211,7 @@ module Make = (MappingImpl: Mapping): (
|
|
|
248
211
|
queryEngine,
|
|
249
212
|
) => {
|
|
250
213
|
let tag = variantTagOfEnvelope(targetEventJson')
|
|
251
|
-
//
|
|
252
|
-
// did not declare are not this mapping's concern — skip silently with no
|
|
253
|
-
// decode attempt.
|
|
214
|
+
// Not this mapping's concern — skip without decoding.
|
|
254
215
|
if !(acceptedTags->Array.includes(tag)) {
|
|
255
216
|
[]
|
|
256
217
|
} else {
|
|
@@ -37,8 +37,10 @@ type event =
|
|
|
37
37
|
// Emitted when a connecting plugin declared incompatible protocol versions.
|
|
38
38
|
// The plugin is still connected; this event gives operators visibility.
|
|
39
39
|
| IncompatiblePlugin(pluginDefinition)
|
|
40
|
+
// One fact for both arrivals, because the Plugin aggregate has one: per-version
|
|
41
|
+
// tracking made `VersionConnected` replace Connected/Reconnected, and a
|
|
42
|
+
// subscriber does the same work either way.
|
|
40
43
|
| PluginConnected(pluginDefinition)
|
|
41
|
-
| PluginReconnected(pluginDefinition)
|
|
42
44
|
| PluginDisconnected(pluginDefinition)
|
|
43
45
|
| PluginDeactivated(pluginDefinition)
|
|
44
46
|
| PluginActivated(pluginDefinition)
|
|
@@ -45,10 +45,6 @@ let eventSchema = Sury.union([
|
|
|
45
45
|
TAG: "PluginConnected",
|
|
46
46
|
_0: s.m(Plugin$Reventless.pluginDefinitionSchema)
|
|
47
47
|
})),
|
|
48
|
-
Sury.$schema(s => ({
|
|
49
|
-
TAG: "PluginReconnected",
|
|
50
|
-
_0: s.m(Plugin$Reventless.pluginDefinitionSchema)
|
|
51
|
-
})),
|
|
52
48
|
Sury.$schema(s => ({
|
|
53
49
|
TAG: "PluginDisconnected",
|
|
54
50
|
_0: s.m(Plugin$Reventless.pluginDefinitionSchema)
|
|
@@ -153,6 +149,22 @@ let outboundTranslationSliceDefSchema = Plugin$Reventless.outboundTranslationSli
|
|
|
153
149
|
|
|
154
150
|
let inboundTranslationSliceDefSchema = Plugin$Reventless.inboundTranslationSliceDefSchema;
|
|
155
151
|
|
|
152
|
+
let publishedEventDefSchema = Plugin$Reventless.publishedEventDefSchema;
|
|
153
|
+
|
|
154
|
+
let publishedEventDefArrayOptionSchema = Plugin$Reventless.publishedEventDefArrayOptionSchema;
|
|
155
|
+
|
|
156
|
+
let acceptedCommandDefSchema = Plugin$Reventless.acceptedCommandDefSchema;
|
|
157
|
+
|
|
158
|
+
let acceptedCommandDefArrayOptionSchema = Plugin$Reventless.acceptedCommandDefArrayOptionSchema;
|
|
159
|
+
|
|
160
|
+
let handledEventDefSchema = Plugin$Reventless.handledEventDefSchema;
|
|
161
|
+
|
|
162
|
+
let handledEventDefArrayOptionSchema = Plugin$Reventless.handledEventDefArrayOptionSchema;
|
|
163
|
+
|
|
164
|
+
let issuedCommandDefSchema = Plugin$Reventless.issuedCommandDefSchema;
|
|
165
|
+
|
|
166
|
+
let issuedCommandDefArrayOptionSchema = Plugin$Reventless.issuedCommandDefArrayOptionSchema;
|
|
167
|
+
|
|
156
168
|
let extensionDefSchema = Plugin$Reventless.extensionDefSchema;
|
|
157
169
|
|
|
158
170
|
let extensionPointDefSchema = Plugin$Reventless.extensionPointDefSchema;
|
|
@@ -200,6 +212,14 @@ export {
|
|
|
200
212
|
automationSliceDefSchema,
|
|
201
213
|
outboundTranslationSliceDefSchema,
|
|
202
214
|
inboundTranslationSliceDefSchema,
|
|
215
|
+
publishedEventDefSchema,
|
|
216
|
+
publishedEventDefArrayOptionSchema,
|
|
217
|
+
acceptedCommandDefSchema,
|
|
218
|
+
acceptedCommandDefArrayOptionSchema,
|
|
219
|
+
handledEventDefSchema,
|
|
220
|
+
handledEventDefArrayOptionSchema,
|
|
221
|
+
issuedCommandDefSchema,
|
|
222
|
+
issuedCommandDefArrayOptionSchema,
|
|
203
223
|
extensionDefSchema,
|
|
204
224
|
extensionPointDefSchema,
|
|
205
225
|
extensionPointDefArrayOptionSchema,
|