@reventlessdev/reventless-aws 3.0.0-alpha.262 → 3.0.0-alpha.263
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 +7 -0
- package/package.json +6 -6
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +27 -16
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +7 -2
- package/tests/AutomationSliceEntryPoint_OpsTest.res +12 -2
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.263 (2026-08-03)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** make the slice callback protocol one declaration, not three ([d49e543](https://github.com/ReventlessDev/reventless-core/commit/d49e543f4715c955c75cec8bd691ee0927b7e2f4))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.262 (2026-08-03)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.263",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"sury": "11.0.0-alpha.4",
|
|
13
13
|
"uuid": "^13.0.0",
|
|
14
14
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.3",
|
|
15
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
15
16
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
16
17
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.65",
|
|
17
18
|
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
18
|
-
"@reventlessdev/rescript-
|
|
19
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
19
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
20
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.207",
|
|
21
21
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.124",
|
|
22
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.208",
|
|
22
23
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
23
|
-
"@reventlessdev/reventless-
|
|
24
|
-
"@reventlessdev/
|
|
25
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.99"
|
|
24
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.99",
|
|
25
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.72"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"rescript": "12.3.0",
|
|
@@ -129,16 +129,21 @@ let makeSyncTodoItems = (
|
|
|
129
129
|
// The callback shapes the shell's functor applications produce
|
|
130
130
|
// (AutomationSlice_Callback.T / OutboundTranslationSlice_Callback.T), typed at
|
|
131
131
|
// the seam.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
// The entry point builds callbacks dynamically per HANDLER_CONFIG entry, so it
|
|
133
|
+
// cannot name `AutomationSlice_Callback.T` / `OutboundTranslationSlice_Callback.T`
|
|
134
|
+
// — the Spec is not known statically — and has to ascribe a structural type where
|
|
135
|
+
// the value crosses from the dynamically-imported JS module into ReScript.
|
|
136
|
+
//
|
|
137
|
+
// These are aliases of the protocol types each callback module owns, never
|
|
138
|
+
// restatements of them. That distinction is the whole point: a hand-written copy
|
|
139
|
+
// is a second source of truth the compiler cannot reconcile with the first, and
|
|
140
|
+
// it drifts silently. It did — when `phase1` gained its `sourceId` the copy here
|
|
141
|
+
// kept saying `array<'event>`, stayed internally consistent, compiled, and then
|
|
142
|
+
// destructured a bare event as a tuple at runtime, so every outbound slice on AWS
|
|
143
|
+
// died reading `.TAG` of `undefined` on its first record. As aliases, the same
|
|
144
|
+
// change is a compile error here instead.
|
|
145
|
+
type automationCallback = ReventlessCore.AutomationSlice_Callback.runtime
|
|
146
|
+
type outboundCallback<'event> = ReventlessCore.OutboundTranslationSlice_Callback.runtime<'event>
|
|
142
147
|
|
|
143
148
|
// Automation: phase 1 takes the RAW envelope JSONs — the callback routes by
|
|
144
149
|
// `meta.service` and unwraps `{id, meta, event}` itself — plus the context.
|
|
@@ -175,14 +180,20 @@ let makeOutboundJsonEventsHandler = (
|
|
|
175
180
|
stream
|
|
176
181
|
->Stream.mapEffect(json =>
|
|
177
182
|
Effect.sync(() => {
|
|
178
|
-
let
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
let envelope = json->JSON.Decode.object
|
|
184
|
+
let rawEvent = envelope->Option.flatMap(d => d->Dict.get("event"))->Option.getOr(json)
|
|
185
|
+
// The entity the event was published for, mirroring the in-process
|
|
186
|
+
// builder. An Aggregate's event payload does not repeat its own id, so
|
|
187
|
+
// without lifting this out of the envelope `collect` cannot name the
|
|
188
|
+
// subject of the outbound item it creates.
|
|
189
|
+
let sourceId =
|
|
190
|
+
envelope
|
|
191
|
+
->Option.flatMap(d => d->Dict.get("id"))
|
|
192
|
+
->Option.flatMap(JSON.Decode.string)
|
|
193
|
+
->Option.getOr("")
|
|
183
194
|
let (eventType, dataDict) = rawEvent->ReventlessCore.Message.splitMessage
|
|
184
195
|
switch decoder.decode(~eventType, ~data=dataDict) {
|
|
185
|
-
| Some(event) => [event]
|
|
196
|
+
| Some(event) => [(sourceId, event)]
|
|
186
197
|
| None => []
|
|
187
198
|
}
|
|
188
199
|
})
|
|
@@ -98,11 +98,16 @@ function makeAutomationJsonEventsHandler(context, callback, publishJsons, syncTo
|
|
|
98
98
|
function makeOutboundJsonEventsHandler(consumedEventSchema, callback, publishJsons, syncTodoItems) {
|
|
99
99
|
let decoder = DcbDecode$Reventless.makeDecoder(consumedEventSchema);
|
|
100
100
|
return stream => Effect.flatMap(Stream.runCollect(Stream$1.flatMap(Stream$1.mapEffect(stream, json => Effect.sync(() => {
|
|
101
|
-
let
|
|
101
|
+
let envelope = Stdlib_JSON.Decode.object(json);
|
|
102
|
+
let rawEvent = Stdlib_Option.getOr(Stdlib_Option.flatMap(envelope, d => d["event"]), json);
|
|
103
|
+
let sourceId = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(envelope, d => d["id"]), Stdlib_JSON.Decode.string), "");
|
|
102
104
|
let match = Message$ReventlessCore.splitMessage(rawEvent);
|
|
103
105
|
let event = decoder.decode(match[0], match[1]);
|
|
104
106
|
if (event !== undefined) {
|
|
105
|
-
return [
|
|
107
|
+
return [[
|
|
108
|
+
sourceId,
|
|
109
|
+
Primitive_option.valFromOption(event)
|
|
110
|
+
]];
|
|
106
111
|
} else {
|
|
107
112
|
return [];
|
|
108
113
|
}
|
|
@@ -150,16 +150,26 @@ describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler", () => {
|
|
|
150
150
|
~publishJsons=noopPublish,
|
|
151
151
|
~syncTodoItems=async () => steps->Array.push("sync"),
|
|
152
152
|
)
|
|
153
|
+
// `id` is the envelope's subject — the entity the event was published for.
|
|
154
|
+
// An Aggregate's event payload does not repeat it, so this is the only place
|
|
155
|
+
// `collect` can learn which customer/order an event belongs to.
|
|
153
156
|
let placed = obj([
|
|
157
|
+
("id", str("ord-1")),
|
|
154
158
|
("meta", obj([("service", str("Order"))])),
|
|
155
159
|
("event", obj([("TAG", str("OrderPlaced")), ("orderId", str("ord-1"))])),
|
|
156
160
|
])
|
|
157
161
|
// Not consumed by this slice — dropped without an error.
|
|
158
162
|
let ignored = obj([("event", obj([("TAG", str("OrderShipped")), ("orderId", str("ord-1"))]))])
|
|
159
|
-
// Bare payload-less variant, no envelope.
|
|
163
|
+
// Bare payload-less variant, no envelope and therefore no id.
|
|
160
164
|
let archived = str("OrderArchived")
|
|
161
165
|
await Stream.fromIterable([placed, ignored, archived])->handler->Effect.runPromise
|
|
162
|
-
|
|
166
|
+
// Pairs, not bare events. Asserting the bare shape is what let the AWS entry
|
|
167
|
+
// point ship a phase1 that destructured `undefined` out of a non-tuple: the
|
|
168
|
+
// test agreed with the drifted local type instead of with the callback.
|
|
169
|
+
expect(received.contents)->toEqual([
|
|
170
|
+
("ord-1", OrderPlaced({orderId: "ord-1"})),
|
|
171
|
+
("", OrderArchived),
|
|
172
|
+
])
|
|
163
173
|
expect(steps)->toEqual(["phase1", "sync", "phase2", "sync"])
|
|
164
174
|
})
|
|
165
175
|
})
|
|
@@ -216,6 +216,10 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler
|
|
|
216
216
|
steps.push("sync");
|
|
217
217
|
});
|
|
218
218
|
let placed = Object.fromEntries([
|
|
219
|
+
[
|
|
220
|
+
"id",
|
|
221
|
+
"ord-1"
|
|
222
|
+
],
|
|
219
223
|
[
|
|
220
224
|
"meta",
|
|
221
225
|
Object.fromEntries([[
|
|
@@ -256,11 +260,17 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler
|
|
|
256
260
|
"OrderArchived"
|
|
257
261
|
])));
|
|
258
262
|
globalThis.expect(received.contents).toEqual([
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
[
|
|
264
|
+
"ord-1",
|
|
265
|
+
{
|
|
266
|
+
TAG: "OrderPlaced",
|
|
267
|
+
orderId: "ord-1"
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
[
|
|
271
|
+
"",
|
|
272
|
+
"OrderArchived"
|
|
273
|
+
]
|
|
264
274
|
]);
|
|
265
275
|
globalThis.expect(steps).toEqual([
|
|
266
276
|
"phase1",
|