@reventlessdev/reventless-core 3.0.0-alpha.208 → 3.0.0-alpha.210
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 +20 -0
- package/package.json +5 -5
- package/src/admin/Platform_Admin.res.mjs +1 -1
- package/src/components/Aggregate/Aggregate_Builder.res +12 -6
- package/src/components/Aggregate/Aggregate_Builder.res.mjs +17 -15
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +6 -2
- package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +1 -1
- package/src/components/Dcb/Dcb_Builder.res +21 -1
- package/src/components/Dcb/Dcb_Builder.res.mjs +4 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +3 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +14 -2
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +4 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res +28 -5
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs +3 -3
- package/src/plugin/component/Plugin_Builder.res +3 -0
- package/src/plugin/component/Plugin_Builder.res.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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.210 (2026-08-04)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **core:** tag a slice's published command with its target, not itself ([bb2b063](https://github.com/ReventlessDev/reventless-core/commit/bb2b063f914e45fb8efcba28e0b691713cf93d8a))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **outbound:** hand translate its geocoder instead of making it fetch one ([fb18312](https://github.com/ReventlessDev/reventless-core/commit/fb1831216b37c9562868c46e1a09054e69418c67))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# 3.0.0-alpha.209 (2026-08-03)
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **aws:** give slice command publishes a queue they can actually reach ([42fdb55](https://github.com/ReventlessDev/reventless-core/commit/42fdb55b751a197d586c49e3615ce12ffea42046))
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **dcb:** route an outbound slice's command to the aggregate it targets ([897b37b](https://github.com/ReventlessDev/reventless-core/commit/897b37b46a4e856567646dfdc2f0652e81cf7388))
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
# 3.0.0-alpha.208 (2026-08-03)
|
|
7
27
|
|
|
8
28
|
### Bug Fixes
|
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.210",
|
|
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.32",
|
|
32
|
-
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.1",
|
|
33
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
34
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
|
|
33
|
+
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.1",
|
|
35
34
|
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
35
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.1",
|
|
38
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.124",
|
|
39
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
40
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
41
|
-
"@reventlessdev/reventless-
|
|
40
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.125",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.100"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.0",
|
|
@@ -78,7 +78,7 @@ function Make(RuntimeEnvironment) {
|
|
|
78
78
|
A.Spec.eventSchema
|
|
79
79
|
]);
|
|
80
80
|
Plugin_Helpers$ReventlessCore.registerAdminAggregateMutations(aggregates, Config.hooks);
|
|
81
|
-
let dcbResult = DcbBuilder.construct(Platform_Admin_Structure$ReventlessCore.pluginId, Platform_Admin_Structure$ReventlessCore.pluginId, "Platform", true, undefined, undefined, aggregateEventTopics, aggregateProducedEvents, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, undefined, undefined, undefined, opts);
|
|
81
|
+
let dcbResult = DcbBuilder.construct(Platform_Admin_Structure$ReventlessCore.pluginId, Platform_Admin_Structure$ReventlessCore.pluginId, "Platform", true, undefined, undefined, aggregateEventTopics, aggregateProducedEvents, undefined, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, undefined, undefined, undefined, opts);
|
|
82
82
|
let slicePublishJsons = dcbResult.dcbPublishJsons;
|
|
83
83
|
if (slicePublishJsons !== undefined) {
|
|
84
84
|
stateChangeSlices.forEach(Sc => {
|
|
@@ -66,7 +66,17 @@ module Make = (
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
let createCommandTopic = (eventLog: SpecificEventLog.component, name, opts, ~memorySize, ~timeout) =>
|
|
69
|
+
let createCommandTopic = (eventLog: SpecificEventLog.component, name, opts, ~memorySize, ~timeout) => {
|
|
70
|
+
// Created outside the apply below, so the topic — and with it the channel's
|
|
71
|
+
// queue — exists while plugin construction is still synchronous. An outbound
|
|
72
|
+
// slice targeting this aggregate resolves its publish queue in a finalizer
|
|
73
|
+
// that runs before any Output settles; a queue created inside the apply is
|
|
74
|
+
// invisible there. Only the handler needs the event log's operations.
|
|
75
|
+
let commandTopic = SpecificCommandTopic.make(
|
|
76
|
+
~name,
|
|
77
|
+
~owner={kind: ComponentType.Aggregate, name: Spec.name},
|
|
78
|
+
~opts,
|
|
79
|
+
)
|
|
70
80
|
eventLog
|
|
71
81
|
->Component.operations
|
|
72
82
|
->Pulumi.Output.apply(eventLogOps => {
|
|
@@ -79,11 +89,6 @@ module Make = (
|
|
|
79
89
|
let eventLog = eventLogOps
|
|
80
90
|
},
|
|
81
91
|
)
|
|
82
|
-
let commandTopic = SpecificCommandTopic.make(
|
|
83
|
-
~name,
|
|
84
|
-
~owner={kind: ComponentType.Aggregate, name: Spec.name},
|
|
85
|
-
~opts,
|
|
86
|
-
)
|
|
87
92
|
let handler = SpecificCommandTopic.makeHandler(
|
|
88
93
|
~commandTopic,
|
|
89
94
|
~commandsHandler=AggregateCallback.handleCommands,
|
|
@@ -98,6 +103,7 @@ module Make = (
|
|
|
98
103
|
)
|
|
99
104
|
commandTopic
|
|
100
105
|
})
|
|
106
|
+
}
|
|
101
107
|
|
|
102
108
|
let createCommandGenerator = (
|
|
103
109
|
commandTopic: Pulumi.Output.t<SpecificCommandTopic.component>,
|
|
@@ -30,25 +30,27 @@ function Make(Spec) {
|
|
|
30
30
|
Id: Spec.Id,
|
|
31
31
|
commandSchema: Spec.commandSchema
|
|
32
32
|
})(SpecificEventCollector)(EventMappings)(AggregateRuntimeBuilder);
|
|
33
|
-
let createCommandTopic = (eventLog, name, opts, memorySize, timeout) =>
|
|
34
|
-
let AggregateCallback = Aggregate_Callback$ReventlessCore.Make(Spec)(Behavior)({
|
|
35
|
-
Spec: Spec,
|
|
36
|
-
EventLog: SpecificEventLog,
|
|
37
|
-
eventLog: eventLogOps
|
|
38
|
-
});
|
|
33
|
+
let createCommandTopic = (eventLog, name, opts, memorySize, timeout) => {
|
|
39
34
|
let commandTopic = SpecificCommandTopic.make(name, {
|
|
40
35
|
kind: "Aggregate",
|
|
41
36
|
name: Spec.name
|
|
42
37
|
}, opts);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
return Component$ReventlessCore.operations(eventLog).apply(eventLogOps => {
|
|
39
|
+
let AggregateCallback = Aggregate_Callback$ReventlessCore.Make(Spec)(Behavior)({
|
|
40
|
+
Spec: Spec,
|
|
41
|
+
EventLog: SpecificEventLog,
|
|
42
|
+
eventLog: eventLogOps
|
|
43
|
+
});
|
|
44
|
+
let handler = SpecificCommandTopic.makeHandler(commandTopic, AggregateCallback.handleCommands);
|
|
45
|
+
let eventLog$1 = Component$ReventlessCore.outputs(eventLog);
|
|
46
|
+
let resources = [
|
|
47
|
+
eventLog$1.resources,
|
|
48
|
+
eventLog$1.eventTopic.resources
|
|
49
|
+
].flat();
|
|
50
|
+
AggregateRuntimeBuilder.forCommandTopic(handler, none => SpecificCommandTopic.connect(none, resources, commandTopic), memorySize, timeout, commandTopic);
|
|
51
|
+
return commandTopic;
|
|
52
|
+
});
|
|
53
|
+
};
|
|
52
54
|
let createCommandGenerator = (commandTopic, api, name, opts, memorySize, timeout) => Output$Pulumi.flatMap(commandTopic, commandTopic => Component$ReventlessCore.operations(commandTopic).apply(param => {
|
|
53
55
|
let publishJsonsAndWait = param.publishJsonsAndWait;
|
|
54
56
|
let publishJsons = param.publishJsons;
|
|
@@ -86,8 +86,12 @@ module Make = (
|
|
|
86
86
|
// only carries `item` data), so causation across the automation hop is
|
|
87
87
|
// lost — emitted commands are roots of a fresh correlation chain. If we
|
|
88
88
|
// later thread source meta through todoItems, switch to deriveMeta.
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// `service` names the command's TARGET, matching the API path
|
|
90
|
+
// (`CommandGenerator_Callback` passes `~serviceName=AggregateSpec.name`). An
|
|
91
|
+
// aggregate derives its event meta from the command's, and ReadModel /
|
|
92
|
+
// AutomationSlice callbacks dispatch mappings on `meta.service` — so naming
|
|
93
|
+
// the slice here makes the target's own events unmatchable, silently.
|
|
94
|
+
let makeMeta = (): Reventless.Message.meta => Message.generateMeta(~service=Spec.targetName)
|
|
91
95
|
|
|
92
96
|
// Per-source erased dispatch — pre-compile decoders once at module init.
|
|
93
97
|
type dispatch = {
|
|
@@ -33,7 +33,7 @@ let todoRowSchema = S.schema(s => ({
|
|
|
33
33
|
function Make(Spec) {
|
|
34
34
|
return Automation => {
|
|
35
35
|
let todoItems = {};
|
|
36
|
-
let makeMeta = () => Message$ReventlessCore.generateMeta(
|
|
36
|
+
let makeMeta = () => Message$ReventlessCore.generateMeta(Spec.targetName, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
|
37
37
|
let dispatches = Automation.mappings.map(M => {
|
|
38
38
|
let decoder = DcbDecode$Reventless.makeDecoder(M.sourceEventSchema);
|
|
39
39
|
let handle = (json, ctx) => {
|
|
@@ -94,6 +94,17 @@ module Make = (
|
|
|
94
94
|
// also drives DCB GSI creation: an aggregate's events live on its own
|
|
95
95
|
// EventLog and must not mint indexes on the DCB table.
|
|
96
96
|
~aggregateProducedEvents: array<(string, S.t<unknown>)>=[],
|
|
97
|
+
// Per-aggregate command publishers, keyed by Aggregate `Spec.name`. An
|
|
98
|
+
// OutboundTranslationSlice naming one in `targetName` publishes its command
|
|
99
|
+
// through that aggregate's CommandTopic instead of this plugin's DCB topic —
|
|
100
|
+
// the two are handled by different Lambdas, so sending an aggregate command
|
|
101
|
+
// to the DCB queue delivers it somewhere nothing handles it.
|
|
102
|
+
//
|
|
103
|
+
// Populated before this runs (`createAggregatesWithoutEventMappers`), so the
|
|
104
|
+
// publisher is in hand by the time a slice is built. DCB StateChangeSlice
|
|
105
|
+
// publishers are registered *after* and are deliberately absent: a slice
|
|
106
|
+
// targeting one wants the DCB topic, which is the fallback.
|
|
107
|
+
~publishToAggregates: dict<Pulumi.Output.t<CommandTopic.publishJsons>>=Dict.make(),
|
|
97
108
|
~stateChangeSlices: array<module(StateChangeSlice.T)>,
|
|
98
109
|
~stateViewSlices: array<module(StateViewSlice.T)>,
|
|
99
110
|
~automationSlices: array<module(AutomationSlice.T)>,
|
|
@@ -629,12 +640,21 @@ module Make = (
|
|
|
629
640
|
let outboundTranslationSlicesOutputs =
|
|
630
641
|
outboundTranslationSlices
|
|
631
642
|
->Array.map((module(OTS: OutboundTranslationSlice.T)) => {
|
|
643
|
+
// Where this slice's inbound command goes. `targetName` naming an
|
|
644
|
+
// Aggregate routes to that aggregate's CommandTopic; anything else —
|
|
645
|
+
// a DCB slice, or no target at all — keeps the plugin's DCB topic,
|
|
646
|
+
// which is what every slice wanted before aggregates were reachable.
|
|
647
|
+
let targetPublishJsons =
|
|
648
|
+
OTS.Spec.targetName
|
|
649
|
+
->Option.flatMap(target => publishToAggregates->Dict.get(target))
|
|
650
|
+
->Option.getOr(publishJsons)
|
|
651
|
+
|
|
632
652
|
let ots = OTS.make(
|
|
633
653
|
~dcbEventLog,
|
|
634
654
|
// Same dict the AutomationSlices get, so an outbound slice can
|
|
635
655
|
// name an Aggregate source by its Spec.name.
|
|
636
656
|
~allEventTopics,
|
|
637
|
-
~publishJsons,
|
|
657
|
+
~publishJsons=targetPublishJsons,
|
|
638
658
|
~runtime=?componentRuntime->Dict.get(OTS.Spec.name),
|
|
639
659
|
~opts,
|
|
640
660
|
)
|
|
@@ -83,13 +83,14 @@ let emptyResult = {
|
|
|
83
83
|
|
|
84
84
|
function Make(DcbEventLogStorage) {
|
|
85
85
|
return DcbEventTopicPublisher => (DcbCommandTopicChannel => (DcbCommandTopicChannelAsync => (RuntimeBuilder => (HooksConfig => {
|
|
86
|
-
let construct = (name, childName, apiNamePrefixOpt, onAdminApiOpt, environmentOpt, platformNameOpt, aggregateEventTopicsOpt, aggregateProducedEventsOpt, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponentsOpt, componentRuntimeOpt, pluginStructure, opts) => {
|
|
86
|
+
let construct = (name, childName, apiNamePrefixOpt, onAdminApiOpt, environmentOpt, platformNameOpt, aggregateEventTopicsOpt, aggregateProducedEventsOpt, publishToAggregatesOpt, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponentsOpt, componentRuntimeOpt, pluginStructure, opts) => {
|
|
87
87
|
let apiNamePrefix = apiNamePrefixOpt !== undefined ? apiNamePrefixOpt : name;
|
|
88
88
|
let onAdminApi = onAdminApiOpt !== undefined ? onAdminApiOpt : false;
|
|
89
89
|
let environment = environmentOpt !== undefined ? environmentOpt : "";
|
|
90
90
|
let platformName = platformNameOpt !== undefined ? platformNameOpt : "";
|
|
91
91
|
let aggregateEventTopics = aggregateEventTopicsOpt !== undefined ? aggregateEventTopicsOpt : ({});
|
|
92
92
|
let aggregateProducedEvents = aggregateProducedEventsOpt !== undefined ? aggregateProducedEventsOpt : [];
|
|
93
|
+
let publishToAggregates = publishToAggregatesOpt !== undefined ? publishToAggregatesOpt : ({});
|
|
93
94
|
let systemCallableComponents = systemCallableComponentsOpt !== undefined ? systemCallableComponentsOpt : [];
|
|
94
95
|
let componentRuntime = componentRuntimeOpt !== undefined ? componentRuntimeOpt : ({});
|
|
95
96
|
let hasDcb = stateChangeSlices.length !== 0;
|
|
@@ -330,7 +331,8 @@ function Make(DcbEventLogStorage) {
|
|
|
330
331
|
];
|
|
331
332
|
}));
|
|
332
333
|
let outboundTranslationSlicesOutputs = Object.fromEntries(outboundTranslationSlices.map(OTS => {
|
|
333
|
-
let
|
|
334
|
+
let targetPublishJsons = Stdlib_Option.getOr(Stdlib_Option.flatMap(OTS.Spec.targetName, target => publishToAggregates[target]), publishJsons);
|
|
335
|
+
let ots = OTS.make(dcbEventLog, allEventTopics, targetPublishJsons, componentRuntime[OTS.Spec.name], opts);
|
|
334
336
|
return [
|
|
335
337
|
OTS.Spec.name,
|
|
336
338
|
Component$ReventlessCore.outputs(ots)
|
|
@@ -102,8 +102,9 @@ module Make = (
|
|
|
102
102
|
// no upstream Reventless meta to derive from, so emitted commands are roots
|
|
103
103
|
// of a fresh correlation chain. `traceparent` populated from an inbound HTTP
|
|
104
104
|
// header would need to be threaded in here by the API/ingress adapter.
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
// `service` names the command's TARGET, as on the API path — see the note in
|
|
106
|
+
// `AutomationSlice_Callback`. The target's events are dispatched on it.
|
|
107
|
+
let makeMeta = (): Reventless.Message.meta => Message.generateMeta(~service=Spec.targetName)
|
|
107
108
|
|
|
108
109
|
let receive = async (
|
|
109
110
|
publishJsons: ReventlessInfra.CommandTopic.publishJsons,
|
|
@@ -69,7 +69,7 @@ function takeAuditRow(auditLog, requestId) {
|
|
|
69
69
|
function Make(Spec) {
|
|
70
70
|
return Translation => {
|
|
71
71
|
let auditLog = {};
|
|
72
|
-
let makeMeta = () => Message$ReventlessCore.generateMeta(
|
|
72
|
+
let makeMeta = () => Message$ReventlessCore.generateMeta(Spec.targetName, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
|
73
73
|
let receive = async (publishJsons, inputJson) => {
|
|
74
74
|
let requestId = Uuid.v4();
|
|
75
75
|
let input;
|
|
@@ -17,6 +17,17 @@ module Make = (
|
|
|
17
17
|
let api: unit => QueryDbStorage.api
|
|
18
18
|
let apiRole: unit => QueryDbStorage.role
|
|
19
19
|
},
|
|
20
|
+
// What the platform provisioned, for `translate` to reach. A thunk like
|
|
21
|
+
// `Api.api` rather than a value, so a platform can resolve it when the runtime
|
|
22
|
+
// exists rather than when this functor is applied.
|
|
23
|
+
//
|
|
24
|
+
// A functor parameter, not a mutable slot: the platform that omits it does not
|
|
25
|
+
// compile, which is the property D9 chose injection for. This supplies the
|
|
26
|
+
// in-process path; the Lambda entry point supplies its own, because there the
|
|
27
|
+
// callback is driven by a bundled shell rather than by this builder.
|
|
28
|
+
Capabilities: {
|
|
29
|
+
let capabilities: unit => Reventless.Capabilities.t
|
|
30
|
+
},
|
|
20
31
|
) => {
|
|
21
32
|
let finish = EventCollectorRuntimeBuilder.finish
|
|
22
33
|
module Make = (
|
|
@@ -187,7 +198,7 @@ module Make = (
|
|
|
187
198
|
// same topic, awaiting would self-deadlock the bus — same
|
|
188
199
|
// shape as AutomationSlice. Detach it; errors are logged.
|
|
189
200
|
let _ =
|
|
190
|
-
Callback.phase2(publishJsonsFn)
|
|
201
|
+
Callback.phase2(publishJsonsFn, ~capabilities=Capabilities.capabilities())
|
|
191
202
|
->Promise.then(() => syncToQueryDb(queryDbOps))
|
|
192
203
|
->Promise.catch(exn => {
|
|
193
204
|
let errMsg =
|
|
@@ -223,7 +234,8 @@ module Make = (
|
|
|
223
234
|
->Pulumi.Output.apply(((ecOps, publishJsonsFn)) => {
|
|
224
235
|
let ops: OutboundTranslationSlice.operations = {
|
|
225
236
|
enqueueEvent: ecOps.enqueueEvent,
|
|
226
|
-
translatePending: async () =>
|
|
237
|
+
translatePending: async () =>
|
|
238
|
+
await Callback.phase2(publishJsonsFn, ~capabilities=Capabilities.capabilities()),
|
|
227
239
|
}
|
|
228
240
|
ops
|
|
229
241
|
}),
|
|
@@ -29,7 +29,7 @@ import * as OutboundTranslationSlice$ReventlessCore from "./OutboundTranslationS
|
|
|
29
29
|
import * as OutboundTranslationSlice_Callback$ReventlessCore from "./OutboundTranslationSlice_Callback.res.mjs";
|
|
30
30
|
|
|
31
31
|
function Make(RuntimeEnvironment) {
|
|
32
|
-
return QueryDbStorage => (QueryDbResolvers => (EventCollectorChannel => (EventCollectorRuntimeBuilder => (Api => {
|
|
32
|
+
return QueryDbStorage => (QueryDbResolvers => (EventCollectorChannel => (EventCollectorRuntimeBuilder => (Api => (Capabilities => {
|
|
33
33
|
let Make = Spec => (Translation => {
|
|
34
34
|
let Callback = OutboundTranslationSlice_Callback$ReventlessCore.Make(Spec)(Translation);
|
|
35
35
|
let queryDbName = Spec.name + "Todo";
|
|
@@ -108,7 +108,7 @@ function Make(RuntimeEnvironment) {
|
|
|
108
108
|
})), events => Stream$1.fromIterable(events))), eventsArr => Effect.promise(async () => {
|
|
109
109
|
Callback.phase1(eventsArr);
|
|
110
110
|
await syncToQueryDb(queryDbOps);
|
|
111
|
-
Stdlib_Promise.$$catch(Callback.phase2(publishJsonsFn).then(() => syncToQueryDb(queryDbOps)), exn => {
|
|
111
|
+
Stdlib_Promise.$$catch(Callback.phase2(publishJsonsFn, Capabilities.capabilities()).then(() => syncToQueryDb(queryDbOps)), exn => {
|
|
112
112
|
let errMsg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown");
|
|
113
113
|
Effect.runSync(EffectLogger$ReventlessCore.logError(`OutboundTranslationSlice(` + Spec.name + `)`, undefined, `detached phase 2 error: ` + errMsg));
|
|
114
114
|
return Promise.resolve();
|
|
@@ -126,7 +126,7 @@ function Make(RuntimeEnvironment) {
|
|
|
126
126
|
let publishJsonsFn = param[1];
|
|
127
127
|
return {
|
|
128
128
|
enqueueEvent: param[0].enqueueEvent,
|
|
129
|
-
translatePending: async () => await Callback.phase2(publishJsonsFn)
|
|
129
|
+
translatePending: async () => await Callback.phase2(publishJsonsFn, Capabilities.capabilities())
|
|
130
130
|
};
|
|
131
131
|
}));
|
|
132
132
|
let aggregatedResources = Object.values(eventTopics).flatMap(t => t.resources);
|
|
@@ -149,7 +149,7 @@ function Make(RuntimeEnvironment) {
|
|
|
149
149
|
finish: EventCollectorRuntimeBuilder.finish,
|
|
150
150
|
Make: Make
|
|
151
151
|
};
|
|
152
|
-
}))));
|
|
152
|
+
})))));
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
export {
|
|
@@ -39,8 +39,16 @@ runtime, as `undefined` destructured from a tuple that was never a tuple.
|
|
|
39
39
|
*/
|
|
40
40
|
type phase1<'event> = array<(string, 'event)> => unit
|
|
41
41
|
|
|
42
|
-
/** Phase 2: translate all pending items, optionally publishing commands.
|
|
43
|
-
|
|
42
|
+
/** Phase 2: translate all pending items, optionally publishing commands.
|
|
43
|
+
|
|
44
|
+
`~capabilities` is supplied by whoever drives the callback — the in-process
|
|
45
|
+
builder or the Lambda entry point — because that is the layer that knows what
|
|
46
|
+
the platform provisioned. It arrives here rather than at `Make` so the
|
|
47
|
+
functor stays a pure function of the Spec, appliable before a runtime exists. */
|
|
48
|
+
type phase2 = (
|
|
49
|
+
ReventlessInfra.CommandTopic.publishJsons,
|
|
50
|
+
~capabilities: Reventless.Capabilities.t,
|
|
51
|
+
) => promise<unit>
|
|
44
52
|
|
|
45
53
|
/** The whole protocol as a value — what a driver holding a built callback needs,
|
|
46
54
|
with no knowledge of the Spec it was built from. */
|
|
@@ -81,8 +89,20 @@ module Make = (
|
|
|
81
89
|
// meta is not threaded from phase1 → phase2 today, so causation across the
|
|
82
90
|
// translation hop is not preserved. If sources later carry meta into todoItems,
|
|
83
91
|
// switch to Message.deriveMeta.
|
|
92
|
+
//
|
|
93
|
+
// `service` names the command's TARGET, matching what the API path does
|
|
94
|
+
// (`CommandGenerator_Callback` passes `~serviceName=AggregateSpec.name`). It is
|
|
95
|
+
// load-bearing rather than descriptive: an aggregate derives its event's meta
|
|
96
|
+
// from the command's, and ReadModel/AutomationSlice callbacks dispatch mappings
|
|
97
|
+
// on `meta.service`. Naming the slice here instead makes the aggregate emit an
|
|
98
|
+
// event no mapping for that aggregate matches — projected as zero actions, with
|
|
99
|
+
// no error. Falls back to the slice's own name only when there is no target, in
|
|
100
|
+
// which case the command goes to the plugin's DCB topic and nothing dispatches
|
|
101
|
+
// on it.
|
|
84
102
|
let makeMeta = (): Reventless.Message.meta =>
|
|
85
|
-
Message.generateMeta(
|
|
103
|
+
Message.generateMeta(
|
|
104
|
+
~service=Spec.targetName->Option.getOr(`OutboundTranslationSlice:${Spec.name}`),
|
|
105
|
+
)
|
|
86
106
|
|
|
87
107
|
let phase1 = (events: array<(string, Spec.consumedEvent)>) => {
|
|
88
108
|
events->Array.forEach(((sourceId, event)) => {
|
|
@@ -103,7 +123,10 @@ module Make = (
|
|
|
103
123
|
})
|
|
104
124
|
}
|
|
105
125
|
|
|
106
|
-
let phase2 = async (
|
|
126
|
+
let phase2 = async (
|
|
127
|
+
publishJsons: ReventlessInfra.CommandTopic.publishJsons,
|
|
128
|
+
~capabilities: Reventless.Capabilities.t,
|
|
129
|
+
) => {
|
|
107
130
|
let pending =
|
|
108
131
|
todoItems
|
|
109
132
|
->Dict.toArray
|
|
@@ -131,7 +154,7 @@ module Make = (
|
|
|
131
154
|
| Some(item) =>
|
|
132
155
|
todoItems->Dict.set(id, {...row, status: Processing, processedAt: now()})
|
|
133
156
|
|
|
134
|
-
let result = try await Translation.translate(id, item) catch {
|
|
157
|
+
let result = try await Translation.translate(id, item, ~capabilities) catch {
|
|
135
158
|
| exn =>
|
|
136
159
|
let msg =
|
|
137
160
|
exn
|
|
@@ -32,7 +32,7 @@ let todoRowSchema = S.schema(s => ({
|
|
|
32
32
|
function Make(Spec) {
|
|
33
33
|
return Translation => {
|
|
34
34
|
let todoItems = {};
|
|
35
|
-
let makeMeta = () => Message$ReventlessCore.generateMeta(`OutboundTranslationSlice:` + Spec.name, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
|
35
|
+
let makeMeta = () => Message$ReventlessCore.generateMeta(Stdlib_Option.getOr(Spec.targetName, `OutboundTranslationSlice:` + Spec.name), undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
|
36
36
|
let phase1 = events => {
|
|
37
37
|
events.forEach(param => {
|
|
38
38
|
Translation.collect(param[1], param[0]).forEach(param => {
|
|
@@ -53,7 +53,7 @@ function Make(Spec) {
|
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
55
|
};
|
|
56
|
-
let phase2 = async publishJsons => {
|
|
56
|
+
let phase2 = async (publishJsons, capabilities) => {
|
|
57
57
|
let pending = Object.entries(todoItems).filter(param => {
|
|
58
58
|
let row = param[1];
|
|
59
59
|
if (row.status === "Pending") {
|
|
@@ -86,7 +86,7 @@ function Make(Spec) {
|
|
|
86
86
|
todoItems[id] = newrecord;
|
|
87
87
|
let result;
|
|
88
88
|
try {
|
|
89
|
-
result = await Translation.translate(id, Primitive_option.valFromOption(item));
|
|
89
|
+
result = await Translation.translate(id, Primitive_option.valFromOption(item), capabilities);
|
|
90
90
|
} catch (raw_exn$1) {
|
|
91
91
|
let exn$1 = Primitive_exceptions.internalToException(raw_exn$1);
|
|
92
92
|
let msg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn$1), Stdlib_JsExn.message), "unknown error");
|
|
@@ -168,6 +168,9 @@ module Make = (
|
|
|
168
168
|
~platformName=Spec.platformName,
|
|
169
169
|
~aggregateEventTopics,
|
|
170
170
|
~aggregateProducedEvents,
|
|
171
|
+
// Carries the aggregate publishers registered just above, so an outbound
|
|
172
|
+
// slice can route its command to the aggregate it names.
|
|
173
|
+
~publishToAggregates,
|
|
171
174
|
~stateChangeSlices,
|
|
172
175
|
~stateViewSlices,
|
|
173
176
|
~automationSlices,
|
|
@@ -156,7 +156,7 @@ function Make(Spec) {
|
|
|
156
156
|
let DcbBuilder = Dcb_Builder$ReventlessCore.Make(DcbEventLogStorage)(DcbEventTopicPublisher)(DcbCommandTopicChannel)(DcbCommandTopicChannelAsync)(PluginRuntimeBuilder)({
|
|
157
157
|
hooks: Spec.hooks
|
|
158
158
|
});
|
|
159
|
-
let dcbResult = DcbBuilder.construct(extra$1, childName, undefined, undefined, Spec.environment, Spec.platformName, aggregateEventTopics, aggregateProducedEvents, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponents, componentRuntime, pluginStructure, opts);
|
|
159
|
+
let dcbResult = DcbBuilder.construct(extra$1, childName, undefined, undefined, Spec.environment, Spec.platformName, aggregateEventTopics, aggregateProducedEvents, Plugin_Helpers$ReventlessCore.publishToAggregates, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponents, componentRuntime, pluginStructure, opts);
|
|
160
160
|
let mutationEntriesFromAggregates = aggregates.flatMap(M => {
|
|
161
161
|
let commandSchema = M.Spec.commandSchema;
|
|
162
162
|
if (ApiNoApiHelpers$ReventlessCore.isNoApi(commandSchema)) {
|