@reventlessdev/reventless-aws 3.0.0-alpha.417 → 3.0.0-alpha.419

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 CHANGED
@@ -3,6 +3,21 @@
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.419 (2026-11-01)
7
+
8
+ ### Features
9
+
10
+ * **core:** a command's outcome, the flow through automations, and each view change are told ([dcaeed2](https://github.com/ReventlessDev/reventless-core/commit/dcaeed2fabe2047a21797b459f2931618474dec3))
11
+
12
+
13
+ # 3.0.0-alpha.418 (2026-11-01)
14
+
15
+ **Note:** Version bump only for package @reventlessdev/reventless-aws
16
+
17
+
18
+
19
+
20
+
6
21
  # 3.0.0-alpha.417 (2026-10-30)
7
22
 
8
23
  **Note:** Version bump only for package @reventlessdev/reventless-aws
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.417",
3
+ "version": "3.0.0-alpha.419",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -22,19 +22,19 @@
22
22
  "sury": "11.0.0",
23
23
  "uuid": "^13.0.0",
24
24
  "yaml": "^2.8.3",
25
+ "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.38",
25
26
  "@reventlessdev/rescript-effect": "1.0.0-alpha.1",
26
- "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.37",
27
27
  "@reventlessdev/rescript-jest": "1.0.0-alpha.13",
28
- "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.37",
29
- "@reventlessdev/rescript-node": "2.0.0-alpha.24",
30
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
28
+ "@reventlessdev/rescript-node": "2.0.0-alpha.25",
29
+ "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.38",
31
30
  "@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
32
31
  "@reventlessdev/rescript-web": "1.0.0-alpha.8",
33
- "@reventlessdev/reventless-infra": "3.0.0-alpha.220",
32
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
33
+ "@reventlessdev/reventless-core": "3.0.0-alpha.326",
34
34
  "@reventlessdev/reventless-interop": "3.0.0-alpha.42",
35
- "@reventlessdev/reventless-core": "3.0.0-alpha.325",
36
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.189",
37
- "@reventlessdev/reventless-spec": "3.0.0-alpha.191"
35
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.190",
36
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.221",
37
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.192"
38
38
  },
39
39
  "devDependencies": {
40
40
  "rescript": "12.3.1",
@@ -137,12 +137,22 @@ async function checkPluginStatus(event) {
137
137
  // Postgres EventLog runtime; absence keeps the DynamoDB path byte-identical.
138
138
  // `eventLogTableName` doubles as the Postgres `event_log.log_name` discriminator
139
139
  // (all aggregates share one Postgres table, unlike DynamoDB's table-per-aggregate).
140
- function buildAggregateParts(specModule, decisionModule, eventLogTableName, queueUrl, pgConnection) {
140
+ // Exported for tests, which pass `makeStorage` to stand an in-memory storage in for
141
+ // DynamoDB and drive the functor wiring below exactly as a deployed handler does
142
+ // (tests/AggregateEntryPoint_WiringTest.res).
143
+ export function buildAggregateParts(
144
+ specModule,
145
+ decisionModule,
146
+ eventLogTableName,
147
+ queueUrl,
148
+ pgConnection,
149
+ makeStorage = makeStorageOps,
150
+ ) {
141
151
  const patchedSpec = patchSpecId(specModule);
142
152
  // Typed core: backend-specific EventLog storage ops (Postgres when pgConnection
143
153
  // is present, else DynamoDB). Returns the full 6-field EventLog_Adapter.operations
144
154
  // — the shell previously dropped latestSnapshot/writeSnapshot.
145
- const rawStorageOps = makeStorageOps(eventLogTableName, pgConnection);
155
+ const rawStorageOps = makeStorage(eventLogTableName, pgConnection);
146
156
  const eventLogOps = eventLogOperationsMake(patchedSpec)({
147
157
  Spec: patchedSpec,
148
158
  EventTopic: { Spec: patchedSpec },
@@ -539,7 +539,7 @@ let makeOutboundJsonEventsHandler = (
539
539
  ->Option.getOr("")
540
540
  let (eventType, dataDict) = rawEvent->ReventlessCore.Message.splitMessage
541
541
  switch decoder.decode(~eventType, ~data=dataDict) {
542
- | Some(event) => [(sourceId, event)]
542
+ | Some(event) => [(sourceId, event, ReventlessCore.Message.metaOfEnvelope(envelope))]
543
543
  | None => []
544
544
  }
545
545
  })
@@ -549,7 +549,11 @@ let makeOutboundJsonEventsHandler = (
549
549
  ->Effect.flatMap(events =>
550
550
  Effect.promise(async () => {
551
551
  await loadTodoItems()
552
- await callback.phase1(events, ~capabilities=capabilities())
552
+ await callback.phase1(
553
+ events->Array.map(((sourceId, event, _)) => (sourceId, event)),
554
+ ~metas=events->Array.map(((_, _, meta)) => meta),
555
+ ~capabilities=capabilities(),
556
+ )
553
557
  await syncTodoItems()
554
558
  await callback.phase2(publishJsons, ~capabilities=capabilities())
555
559
  await syncTodoItems()
@@ -323,14 +323,18 @@ function makeOutboundJsonEventsHandler(consumedEventSchema, callback, publishJso
323
323
  if (event !== undefined) {
324
324
  return [[
325
325
  sourceId,
326
- Primitive_option.valFromOption(event)
326
+ Primitive_option.valFromOption(event),
327
+ Message$ReventlessCore.metaOfEnvelope(envelope)
327
328
  ]];
328
329
  } else {
329
330
  return [];
330
331
  }
331
332
  })), events => Stream$1.fromIterable(events))), events => Effect.promise(async () => {
332
333
  await loadTodoItems();
333
- await callback.phase1(events, capabilities());
334
+ await callback.phase1(events.map(param => [
335
+ param[0],
336
+ param[1]
337
+ ]), events.map(param => param[2]), capabilities());
334
338
  await syncTodoItems();
335
339
  await callback.phase2(publishJsons, capabilities());
336
340
  return await syncTodoItems();
@@ -126,6 +126,20 @@ let decodeEnvelope = (json: JSON.t): envelope<'event> => {
126
126
 
127
127
  let exnMessage = StreamRoutedEntryPoint_Ops.exnMessage
128
128
 
129
+ // The event an envelope carries, as the source of the rows its actions change.
130
+ // Read off the JSON, like `producerTime`: an envelope without a meta yields none.
131
+ let sourcesOf = (json: JSON.t): array<ReventlessCore.Projection.changeSource> => {
132
+ let fields = json->JSON.Decode.object->Option.getOr(Dict.make())
133
+ let meta = fields->Dict.get("meta")->Option.flatMap(JSON.Decode.object)
134
+ let string = (d, k) => d->Option.flatMap(d => d->Dict.get(k))->Option.flatMap(JSON.Decode.string)
135
+ switch string(meta, "msgId") {
136
+ | Some(msgId) =>
137
+ let (entityId, entityType) = ReventlessCore.ViewChange.entityOfEnvelope(fields)
138
+ [{msgId, correlationId: ?string(meta, "correlationId"), ?entityId, ?entityType}]
139
+ | None => []
140
+ }
141
+ }
142
+
129
143
  // The typed core of buildJsonEventsHandler: decode each envelope, parse the
130
144
  // event against the slice's consumed-event schema, project, and run every
131
145
  // resulting action against the view table. A decode failure logs and yields no
@@ -140,7 +154,13 @@ let makeJsonEventsHandler = (
140
154
  ~queryDbOps: ReventlessCore.QueryDb_Adapter.operations,
141
155
  ~subIdConfig: option<Reventless.ReadModel.subIdConfig<JSON.t>>,
142
156
  ~stateSchema: option<S.t<unknown>>,
143
- ): ReventlessCore.EventCollector.jsonEventsHandler =>
157
+ ): ReventlessCore.EventCollector.jsonEventsHandler => {
158
+ // Each row the events change is logged; the states are already JSON.
159
+ let onChange = ReventlessCore.ViewChange.reporter(
160
+ ~comp=sliceName,
161
+ ~view=sliceName,
162
+ ~stateToJson=state => state,
163
+ )
144
164
  stream =>
145
165
  stream
146
166
  ->Stream.mapEffect(json =>
@@ -155,11 +175,14 @@ let makeJsonEventsHandler = (
155
175
  // The deployed slice assembles JSON-level operations rather than going
156
176
  // through the typed rewrite, so the trail is appended here — with this
157
177
  // envelope's own time, which the apply below no longer has.
158
- producerTime === ""
159
- ? actions
160
- : actions->Array.map(
161
- ReventlessCore.Projection.rewriteJsonAction(_, ~at=producerTime, ~stateSchema),
162
- )
178
+ let sources = sourcesOf(json)
179
+ let actions =
180
+ producerTime === ""
181
+ ? actions
182
+ : actions->Array.map(
183
+ ReventlessCore.Projection.rewriteJsonAction(_, ~at=producerTime, ~stateSchema),
184
+ )
185
+ actions->Array.map(action => (action, sources))
163
186
  } catch {
164
187
  | exn =>
165
188
  StreamRoutedEntryPoint_Ops.logError(
@@ -171,7 +194,7 @@ let makeJsonEventsHandler = (
171
194
  )
172
195
  )
173
196
  ->Stream.flatMap(actions => Stream.fromIterable(actions))
174
- ->Stream.runForEach(action =>
197
+ ->Stream.runForEach(((action, sources)) =>
175
198
  // Thread the slice's `subIdConfig` (generated from `@subId`) so
176
199
  // sub-id-dependent actions like `UpdateMultiState` can resolve the sort
177
200
  // key. Dropping it here made every such action hit the
@@ -181,9 +204,17 @@ let makeJsonEventsHandler = (
181
204
  // actions. `~comp` is the slice's spec name — attribution for
182
205
  // Projection.handleAction's debug-lazy action log.
183
206
  Effect.promise(() =>
184
- ReventlessCore.Projection.handleAction(~comp=sliceName, action, queryDbOps, subIdConfig)
207
+ ReventlessCore.Projection.handleAction(
208
+ ~comp=sliceName,
209
+ ~sources,
210
+ ~onChange,
211
+ action,
212
+ queryDbOps,
213
+ subIdConfig,
214
+ )
185
215
  )->Effect.map(_ => ())
186
216
  )
217
+ }
187
218
 
188
219
  // ── Handler assembly (called by the shell per HANDLER_CONFIG entry) ─────────
189
220
  // `sliceModules` types the shell's reads off the two dynamically imported
@@ -8,6 +8,7 @@ import * as Stream from "effect/Stream";
8
8
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
9
9
  import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
10
10
  import * as Projection$ReventlessCore from "@reventlessdev/reventless-core/src/Projection.res.mjs";
11
+ import * as ViewChange$ReventlessCore from "@reventlessdev/reventless-core/src/ViewChange.res.mjs";
11
12
  import * as ProjectionEntryPoint_Ops$ReventlessAws from "./ProjectionEntryPoint_Ops.res.mjs";
12
13
  import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
13
14
 
@@ -64,7 +65,25 @@ function decodeEnvelope(json) {
64
65
  };
65
66
  }
66
67
 
68
+ function sourcesOf(json) {
69
+ let fields = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(json), {});
70
+ let meta = Stdlib_Option.flatMap(fields["meta"], Stdlib_JSON.Decode.object);
71
+ let string = (d, k) => Stdlib_Option.flatMap(Stdlib_Option.flatMap(d, d => d[k]), Stdlib_JSON.Decode.string);
72
+ let msgId = string(meta, "msgId");
73
+ if (msgId === undefined) {
74
+ return [];
75
+ }
76
+ let match = ViewChange$ReventlessCore.entityOfEnvelope(fields);
77
+ return [{
78
+ msgId: msgId,
79
+ correlationId: string(meta, "correlationId"),
80
+ entityId: match[0],
81
+ entityType: match[1]
82
+ }];
83
+ }
84
+
67
85
  function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subIdConfig, stateSchema) {
86
+ let onChange = ViewChange$ReventlessCore.reporter(sliceName, sliceName, state => state);
68
87
  return stream => Stream.runForEach(Stream.flatMap(Stream.mapEffect(stream, json => Effect.sync(() => {
69
88
  try {
70
89
  let match = decodeEnvelope(json);
@@ -74,11 +93,12 @@ function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subI
74
93
  meta: match.meta,
75
94
  recordedAt: match.recordedAt
76
95
  });
77
- if (producerTime === "") {
78
- return actions;
79
- } else {
80
- return actions.map(__x => Projection$ReventlessCore.rewriteJsonAction(__x, producerTime, stateSchema));
81
- }
96
+ let sources = sourcesOf(json);
97
+ let actions$1 = producerTime === "" ? actions : actions.map(__x => Projection$ReventlessCore.rewriteJsonAction(__x, producerTime, stateSchema));
98
+ return actions$1.map(action => [
99
+ action,
100
+ sources
101
+ ]);
82
102
  } catch (raw_exn) {
83
103
  let exn = Primitive_exceptions.internalToException(raw_exn);
84
104
  StreamRoutedEntryPoint_Ops$ReventlessAws.logError("failed to decode event", {
@@ -87,7 +107,11 @@ function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subI
87
107
  });
88
108
  return [];
89
109
  }
90
- })), actions => Stream.fromIterable(actions)), action => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(sliceName, action, queryDbOps, subIdConfig)), param => {}));
110
+ })), actions => Stream.fromIterable(actions)), param => {
111
+ let sources = param[1];
112
+ let action = param[0];
113
+ return Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(sliceName, sources, onChange, action, queryDbOps, subIdConfig)), param => {});
114
+ });
91
115
  }
92
116
 
93
117
  function makeRegisteredHandler(entry, modules) {
@@ -106,6 +130,7 @@ export {
106
130
  parseHandlerConfig,
107
131
  decodeEnvelope,
108
132
  exnMessage,
133
+ sourcesOf,
109
134
  makeJsonEventsHandler,
110
135
  makeRegisteredHandler,
111
136
  }
@@ -0,0 +1,124 @@
1
+ // The deployed aggregate handler, wired exactly as `AggregateEntryPoint.mjs` wires it,
2
+ // over an in-memory storage in place of DynamoDB.
3
+ //
4
+ // The shell is hand-written JS that passes the compiled EventLog operations MODULE
5
+ // where the aggregate callback expects the `EventLog.operations` RECORD, so a field
6
+ // the record has and the module does not export under the same name type-checks
7
+ // everywhere and fails only in a Lambda. That is how a primed field
8
+ // (`replayStream'`, exported as `replayStream$p`) took down every command of the
9
+ // platform's Plugin aggregate on AWS while every in-process test passed. The
10
+ // command that broke was a derived one — it carries the stable-id header, which
11
+ // sends the handler down the cause-reading replay — so that is the command sent
12
+ // here, its redelivery, and a plain command, which between them reach every
13
+ // operation the aggregate callback reads.
14
+
15
+ open JestGlobals
16
+
17
+ module Spec = AggTestAggregate
18
+
19
+ // ── An in-memory storage, typed by the contract the shell's storage satisfies ──
20
+
21
+ let stored: dict<array<JSON.t>> = Dict.make()
22
+
23
+ let storage: ReventlessCore.EventLog_Adapter.operations = {
24
+ append: async (seq, id, events) => {
25
+ let current = stored->Dict.get(id)->Option.getOr([])
26
+ if current->Array.length != seq {
27
+ Error(ReventlessCore.EventLog.Conflict)
28
+ } else {
29
+ stored->Dict.set(id, current->Array.concat(events))
30
+ Ok()
31
+ }
32
+ },
33
+ replay: async id => stored->Dict.get(id)->Option.getOr([]),
34
+ replayStream: (id, ~fromSeq=0) =>
35
+ Stream.fromIterable(stored->Dict.get(id)->Option.getOr([])->Array.slice(~start=fromSeq)),
36
+ appendStream: (_, _, _) => Effect.succeed(),
37
+ latestSnapshot: async _ => Ok(None),
38
+ writeSnapshot: async (_, _) => Ok(),
39
+ }
40
+
41
+ // ── The shell's own wiring ────────────────────────────────────────────────────
42
+
43
+ type parts = {handleJsonCommands: ReventlessCore.CommandTopic_Helpers.jsonCommandsHandler}
44
+
45
+ let buildParts: ReventlessCore.EventLog_Adapter.operations => promise<parts> = %raw(`
46
+ async (storage) => {
47
+ const { buildAggregateParts } = await import(
48
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
49
+ );
50
+ const spec = await import("./integration/AggTestAggregate.res.mjs");
51
+ const decision = await import("./integration/AggTestAggregateDecision.res.mjs");
52
+ return buildAggregateParts(
53
+ spec,
54
+ decision,
55
+ "agg-wiring-test",
56
+ "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-wiring-test",
57
+ undefined,
58
+ () => storage,
59
+ );
60
+ }
61
+ `)
62
+
63
+ /** A command a mapping derived: its id is stable across redeliveries, and the
64
+ header says so. */
65
+ let derived = (~id, ~msgId, name): JSON.t => {
66
+ let meta: Reventless.Message.meta = {
67
+ service: "AggWiringTest",
68
+ time: "2026-11-01T00:00:00.000Z",
69
+ msgId,
70
+ correlationId: msgId,
71
+ headers: Dict.fromArray([(ReventlessInfra.Message.stableIdHeader, "true")]),
72
+ }
73
+ ReventlessCore.Message.encodeCommand'(
74
+ {
75
+ Reventless.Message.id: Reventless.Id.String.makeFromString(id),
76
+ meta,
77
+ command: Spec.Add({name: name}),
78
+ },
79
+ Reventless.Id.String.schema,
80
+ Spec.commandSchema,
81
+ )
82
+ }
83
+
84
+ let send = async (parts: parts, json) =>
85
+ await parts.handleJsonCommands(
86
+ Stream.fromIterable([{ReventlessInfra.CommandTopic.reference: "r", command: json}]),
87
+ )->Effect.runPromise
88
+
89
+ describe("the deployed aggregate handler's wiring", () => {
90
+ beforeEach(() => stored->Dict.keysToArray->Array.forEach(k => stored->Dict.delete(k)))
91
+
92
+ testPromise("handles a derived command, which replays its stream with causes", async () => {
93
+ let parts = await buildParts(storage)
94
+ let results = await send(parts, derived(~id="a-1", ~msgId="m-1", "first"))
95
+ expect(results->Array.every(Result.isOk))->toBe(true)
96
+ expect(stored->Dict.get("a-1")->Option.map(Array.length))->toEqual(Some(1))
97
+ })
98
+
99
+ testPromise("appends nothing for a redelivery of a derived command", async () => {
100
+ let parts = await buildParts(storage)
101
+ let _ = await send(parts, derived(~id="a-2", ~msgId="m-2", "first"))
102
+ let again = await send(parts, derived(~id="a-2", ~msgId="m-2", "first"))
103
+ expect(again->Array.every(Result.isOk))->toBe(true)
104
+ expect(stored->Dict.get("a-2")->Option.map(Array.length))->toEqual(Some(1))
105
+ })
106
+
107
+ // An API command: no stable id, so the handler seeds from the snapshot read and
108
+ // replays events alone — the other operations the shell's module must carry.
109
+ testPromise("handles a command with a fresh id after a derived one", async () => {
110
+ let parts = await buildParts(storage)
111
+ let _ = await send(parts, derived(~id="a-3", ~msgId="m-3", "first"))
112
+ let fresh = derived(~id="a-3", ~msgId="m-4", "second")
113
+ switch fresh {
114
+ | Object(d) =>
115
+ d
116
+ ->Dict.get("meta")
117
+ ->Option.flatMap(JSON.Decode.object)
118
+ ->Option.forEach(meta => meta->Dict.delete("headers"))
119
+ | _ => ()
120
+ }
121
+ let results = await send(parts, fresh)
122
+ expect(results->Array.every(Result.isOk))->toBe(true)
123
+ })
124
+ })
@@ -0,0 +1,152 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Stream from "effect/Stream";
10
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
11
+ import * as Message$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/Message.res.mjs";
12
+ import * as AggTestAggregate$ReventlessAws from "./integration/AggTestAggregate.res.mjs";
13
+
14
+ let stored = {};
15
+
16
+ async function storage_append(seq, id, events) {
17
+ let current = Stdlib_Option.getOr(stored[id], []);
18
+ if (current.length !== seq) {
19
+ return {
20
+ TAG: "Error",
21
+ _0: "Conflict"
22
+ };
23
+ } else {
24
+ stored[id] = current.concat(events);
25
+ return {
26
+ TAG: "Ok",
27
+ _0: undefined
28
+ };
29
+ }
30
+ }
31
+
32
+ async function storage_replay(id) {
33
+ return Stdlib_Option.getOr(stored[id], []);
34
+ }
35
+
36
+ function storage_replayStream(id, fromSeqOpt) {
37
+ let fromSeq = fromSeqOpt !== undefined ? fromSeqOpt : 0;
38
+ return Stream.fromIterable(Stdlib_Option.getOr(stored[id], []).slice(fromSeq));
39
+ }
40
+
41
+ function storage_appendStream(param, param$1, param$2) {
42
+ return Effect.succeed();
43
+ }
44
+
45
+ async function storage_latestSnapshot(param) {
46
+ return {
47
+ TAG: "Ok",
48
+ _0: undefined
49
+ };
50
+ }
51
+
52
+ async function storage_writeSnapshot(param, param$1) {
53
+ return {
54
+ TAG: "Ok",
55
+ _0: undefined
56
+ };
57
+ }
58
+
59
+ let storage = {
60
+ append: storage_append,
61
+ replay: storage_replay,
62
+ replayStream: storage_replayStream,
63
+ appendStream: storage_appendStream,
64
+ latestSnapshot: storage_latestSnapshot,
65
+ writeSnapshot: storage_writeSnapshot
66
+ };
67
+
68
+ let buildParts = (async (storage) => {
69
+ const { buildAggregateParts } = await import(
70
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
71
+ );
72
+ const spec = await import("./integration/AggTestAggregate.res.mjs");
73
+ const decision = await import("./integration/AggTestAggregateDecision.res.mjs");
74
+ return buildAggregateParts(
75
+ spec,
76
+ decision,
77
+ "agg-wiring-test",
78
+ "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-wiring-test",
79
+ undefined,
80
+ () => storage,
81
+ );
82
+ });
83
+
84
+ function derived(id, msgId, name) {
85
+ let meta_headers = Object.fromEntries([[
86
+ Message$ReventlessInfra.stableIdHeader,
87
+ "true"
88
+ ]]);
89
+ let meta = {
90
+ service: "AggWiringTest",
91
+ time: "2026-11-01T00:00:00.000Z",
92
+ msgId: msgId,
93
+ correlationId: msgId,
94
+ headers: meta_headers
95
+ };
96
+ return Message$ReventlessCore.encodeCommand$p({
97
+ id: Id$Reventless.$$String.makeFromString(id),
98
+ meta: meta,
99
+ command: {
100
+ TAG: "Add",
101
+ name: name
102
+ }
103
+ }, Id$Reventless.$$String.schema, AggTestAggregate$ReventlessAws.commandSchema);
104
+ }
105
+
106
+ async function send(parts, json) {
107
+ return await Effect.runPromise(parts.handleJsonCommands(Stream.fromIterable([{
108
+ command: json,
109
+ reference: "r"
110
+ }])));
111
+ }
112
+
113
+ globalThis.describe("the deployed aggregate handler's wiring", () => {
114
+ globalThis.beforeEach(() => {
115
+ Object.keys(stored).forEach(k => Stdlib_Dict.$$delete(stored, k));
116
+ });
117
+ globalThis.test("handles a derived command, which replays its stream with causes", async () => {
118
+ let parts = await buildParts(storage);
119
+ let results = await send(parts, derived("a-1", "m-1", "first"));
120
+ globalThis.expect(results.every(Stdlib_Result.isOk)).toBe(true);
121
+ globalThis.expect(Stdlib_Option.map(stored["a-1"], prim => prim.length)).toEqual(1);
122
+ });
123
+ globalThis.test("appends nothing for a redelivery of a derived command", async () => {
124
+ let parts = await buildParts(storage);
125
+ await send(parts, derived("a-2", "m-2", "first"));
126
+ let again = await send(parts, derived("a-2", "m-2", "first"));
127
+ globalThis.expect(again.every(Stdlib_Result.isOk)).toBe(true);
128
+ globalThis.expect(Stdlib_Option.map(stored["a-2"], prim => prim.length)).toEqual(1);
129
+ });
130
+ globalThis.test("handles a command with a fresh id after a derived one", async () => {
131
+ let parts = await buildParts(storage);
132
+ await send(parts, derived("a-3", "m-3", "first"));
133
+ let fresh = derived("a-3", "m-4", "second");
134
+ if (typeof fresh === "object" && fresh !== null && !Array.isArray(fresh)) {
135
+ Stdlib_Option.forEach(Stdlib_Option.flatMap(fresh["meta"], Stdlib_JSON.Decode.object), meta => Stdlib_Dict.$$delete(meta, "headers"));
136
+ }
137
+ let results = await send(parts, fresh);
138
+ globalThis.expect(results.every(Stdlib_Result.isOk)).toBe(true);
139
+ });
140
+ });
141
+
142
+ let Spec;
143
+
144
+ export {
145
+ Spec,
146
+ stored,
147
+ storage,
148
+ buildParts,
149
+ derived,
150
+ send,
151
+ }
152
+ /* buildParts Not a pure module */
@@ -388,7 +388,7 @@ describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler", () => {
388
388
  let received = ref([])
389
389
  let callback: AutomationSliceEntryPoint_Ops.outboundCallback<outboundEvent> = {
390
390
  todoItems: Dict.make(),
391
- phase1: async (events, ~capabilities as _) => {
391
+ phase1: async (events, ~metas as _=?, ~capabilities as _) => {
392
392
  received := events
393
393
  steps->Array.push("phase1")
394
394
  },
@@ -581,7 +581,7 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler
581
581
  contents: []
582
582
  };
583
583
  let callback_todoItems = {};
584
- let callback_phase1 = async (events, param) => {
584
+ let callback_phase1 = async (events, param, param$1) => {
585
585
  received.contents = events;
586
586
  steps.push("phase1");
587
587
  };