@reventlessdev/reventless-aws 3.0.0-alpha.235 → 3.0.0-alpha.236

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,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.236 (2026-07-27)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** deploy AllAutomationSlices via DCB-eventlog finalizer ([d88ff1a](https://github.com/ReventlessDev/reventless-core/commit/d88ff1aac3a67881f5653c48bfd441878e822945))
11
+
12
+
6
13
  # 3.0.0-alpha.235 (2026-07-27)
7
14
 
8
15
  **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.235",
3
+ "version": "3.0.0-alpha.236",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -11,17 +11,17 @@
11
11
  "@aws-sdk/s3-request-presigner": "3.970.0",
12
12
  "sury": "11.0.0-alpha.4",
13
13
  "uuid": "^13.0.0",
14
- "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
15
14
  "@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.25",
16
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
17
- "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.61",
18
15
  "@reventlessdev/rescript-effect": "0.1.0-alpha.31",
16
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
17
+ "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.61",
18
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
19
19
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
20
- "@reventlessdev/reventless-core": "3.0.0-alpha.185",
21
- "@reventlessdev/reventless-interop": "3.0.0-alpha.29",
22
20
  "@reventlessdev/reventless-infra": "3.0.0-alpha.106",
23
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.49",
24
- "@reventlessdev/reventless-spec": "3.0.0-alpha.81"
21
+ "@reventlessdev/reventless-core": "3.0.0-alpha.186",
22
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.50",
23
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.81",
24
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.29"
25
25
  },
26
26
  "devDependencies": {
27
27
  "rescript": "12.3.0",
package/src/Platform.res CHANGED
@@ -695,7 +695,7 @@ module MakeWithConfig = (
695
695
  onDcbSlicesCreated: dcbEventLogUnknown => {
696
696
  let dcbEventLog = dcbEventLogUnknown->asDcbEventLog
697
697
  StateViewSliceRuntime_Builder_Single.finishWithDcbEventLog(dcbEventLog)
698
- AutomationSliceRuntime_Builder_Single.finish()
698
+ AutomationSliceRuntime_Builder_Single.finishWithDcbEventLog(dcbEventLog)
699
699
  },
700
700
  // Heartbeat EP channel hook — extracts SQS queue URL for heartbeat Lambda handler
701
701
  // and records the calling plugin's id so the Lambda runtime can emit Connect commands
@@ -496,7 +496,7 @@ function MakeWithConfig(Config) {
496
496
  };
497
497
  let hooks_onDcbSlicesCreated = dcbEventLogUnknown => {
498
498
  StateViewSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
499
- AutomationSliceRuntime_Builder_Single$ReventlessAws.finish();
499
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
500
500
  };
501
501
  let hooks_onHeartbeatEpChannelAvailable = (remoteChannelUnknown, pluginId, heartbeatInterval) => {
502
502
  let resource = remoteChannelUnknown.resources[0];
@@ -1502,7 +1502,7 @@ function Make($star) {
1502
1502
  };
1503
1503
  let hooks_onDcbSlicesCreated = dcbEventLogUnknown => {
1504
1504
  StateViewSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
1505
- AutomationSliceRuntime_Builder_Single$ReventlessAws.finish();
1505
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
1506
1506
  };
1507
1507
  let hooks_onHeartbeatEpChannelAvailable = (remoteChannelUnknown, pluginId, heartbeatInterval) => {
1508
1508
  let resource = remoteChannelUnknown.resources[0];
@@ -16,6 +16,11 @@ type sliceInfo = {
16
16
  bodyModulePath: string,
17
17
  callbackType: string,
18
18
  queryDbTableName: Pulumi.Output.t<string>,
19
+ // The slice's TODO view table resource(s). Passed as the channel `resources`
20
+ // so `connect` grants the Lambda write access to sync todo rows. Captured
21
+ // synchronously at `registerAutomationSlice` time — the finalizer must not
22
+ // depend on the apply-deferred `storedSpecs` (see `finishWithDcbEventLog`).
23
+ queryDbResources: array<ReventlessInfra.Adapter.resource>,
19
24
  // Threaded to phase 1 collect/resolve on the automation path (mirrors the
20
25
  // in-process builder's `~context`); outbound slices take no context.
21
26
  context: option<Reventless.AutomationSlice.context>,
@@ -36,9 +41,13 @@ let registerAutomationSlice = (
36
41
  ~bodyModulePath,
37
42
  ~callbackType="automation",
38
43
  ~queryDbTableName,
44
+ ~queryDbResources=[],
39
45
  ~context=?,
40
46
  ) =>
41
- bundledInfos->Dict.set(name, {specModulePath, bodyModulePath, callbackType, queryDbTableName, context})
47
+ bundledInfos->Dict.set(
48
+ name,
49
+ {specModulePath, bodyModulePath, callbackType, queryDbTableName, queryDbResources, context},
50
+ )
42
51
 
43
52
  type storedSpec = {
44
53
  componentName: string,
@@ -112,6 +121,13 @@ let forEventCollector: ReventlessCore.Runtime.forEventCollector<
112
121
 
113
122
  let finished = ref(false)
114
123
 
124
+ // Legacy finalizer, kept only to satisfy `EventCollectorRuntime_Builder.T`.
125
+ // It is gated on `storedSpecs`, which the core `AutomationSlice_Builder`
126
+ // populates via `forEventCollector` *inside a `Pulumi.Output.apply`* — i.e.
127
+ // asynchronously. The platform's `onDcbSlicesCreated` hook runs synchronously,
128
+ // so at call time `storedSpecs` is still empty and no Lambda is ever built.
129
+ // The DCB platform path uses `finishWithDcbEventLog` instead, which drives off
130
+ // the synchronously-registered `bundledInfos`. Mirrors StateViewSlice's split.
115
131
  let finish = () =>
116
132
  if !finished.contents {
117
133
  if storedSpecs->Array.length > 0 {
@@ -216,3 +232,114 @@ let finish = () =>
216
232
  }
217
233
  finished := true
218
234
  }
235
+
236
+ // The finalizer the platform actually calls (Platform.res `onDcbSlicesCreated`).
237
+ // Mirrors `StateViewSliceRuntime_Builder_Single.finishWithDcbEventLog`: it drives
238
+ // off the synchronously-populated `bundledInfos` (never the apply-deferred
239
+ // `storedSpecs`) and builds a single channel from the plugin's DCB EventLog
240
+ // stream — the sole source for automation/outbound slices, which dispatch by
241
+ // `meta.service` inside the callback. `dcbEventLog` is passed so the parent and
242
+ // the stream URN are available synchronously here.
243
+ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component) =>
244
+ if !finished.contents {
245
+ if bundledInfos->Dict.keysToArray->Array.length > 0 {
246
+ let dcbResource = dcbEventLog->ReventlessCore.Component.toPulumiResource
247
+ switch dcbResource.parent {
248
+ | Some(parent) =>
249
+ let opts = {Pulumi.ComponentResource.parent: parent}
250
+ let dcbOutputs: ReventlessCore.DcbEventLog.outputs =
251
+ dcbEventLog->ReventlessCore.Component.outputs
252
+ let eventTopics: ReventlessCore.EventTopic.allOutputs = Dict.fromArray([
253
+ ("DcbEventLog", dcbOutputs.eventTopic),
254
+ ])
255
+ let channel = EventCollectorChannel.make(
256
+ ~name="AllAutomationSlices",
257
+ ~eventTopics,
258
+ ~owner=None,
259
+ ~opts,
260
+ )
261
+
262
+ // One DCB stream URN, identical for every slice on this plugin log — the
263
+ // dispatch key the entry point maps event-source records back to.
264
+ let sourceUrn = switch dcbOutputs.eventTopic.resources->Array.get(0) {
265
+ | Some(resource) => resource.urn
266
+ | None => Pulumi.Output.make("")
267
+ }
268
+
269
+ let dcbQueueUrl = switch dcbQueueUrlRef.contents {
270
+ | Some(url) => url
271
+ | None => Pulumi.Output.make("NOT_AVAILABLE")
272
+ }
273
+
274
+ let handlerOutputs: array<Pulumi.Output.t<string>> = []
275
+ let packageDirs: dict<string> = Dict.make()
276
+ let allQueryDbResources: array<ReventlessInfra.Adapter.resource> = []
277
+
278
+ bundledInfos->Dict.forEachWithKey((info, _name) => {
279
+ info.queryDbResources->Array.forEach(r => allQueryDbResources->Array.push(r)->ignore)
280
+ let pkg = Util_Bundle.extractPackageName(info.specModulePath)
281
+ packageDirs->Dict.set(pkg, Util_Bundle.resolvePackageRoot(pkg))
282
+ // Usually the same package as the spec, but bundle it explicitly —
283
+ // the entry point imports the body module by specifier too.
284
+ let bodyPkg = Util_Bundle.extractPackageName(info.bodyModulePath)
285
+ packageDirs->Dict.set(bodyPkg, Util_Bundle.resolvePackageRoot(bodyPkg))
286
+
287
+ let specModule = info.specModulePath->JSON.stringifyAny->Option.getOr(`""`)
288
+ let bodyModule = info.bodyModulePath->JSON.stringifyAny->Option.getOr(`""`)
289
+ let callbackType = info.callbackType->JSON.stringifyAny->Option.getOr(`""`)
290
+ let contextFragment = switch info.context {
291
+ | Some(context) =>
292
+ context
293
+ ->JSON.stringifyAny
294
+ ->Option.map(json => `,"context":${json}`)
295
+ ->Option.getOr("")
296
+ | None => ""
297
+ }
298
+
299
+ let handlerJson =
300
+ Pulumi.Output.all3((info.queryDbTableName, dcbQueueUrl, sourceUrn))
301
+ ->Pulumi.Output.apply(((tableName, queueUrl, urn)) =>
302
+ `{"specModule":${specModule},"bodyModule":${bodyModule},"callbackType":${callbackType},"queryDbTableName":"${tableName}","dcbQueueUrl":"${queueUrl}","sourceUrn":"${urn}"${contextFragment}}`
303
+ )
304
+ let _ = handlerOutputs->Array.push(handlerJson)
305
+ })
306
+
307
+ let handlerConfigOutput =
308
+ Pulumi.Output.all(handlerOutputs)
309
+ ->Pulumi.Output.apply(handlers => `{"handlers":[${handlers->Array.join(",")}]}`)
310
+
311
+ let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
312
+ envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
313
+
314
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
315
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs",
316
+ ~packageDirs,
317
+ )
318
+
319
+ let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
320
+ ~name="AllAutomationSlices",
321
+ ~unitKind=ReventlessCore.Monitoring.Reactor,
322
+ ~componentKind=ReventlessCore.ComponentType.AutomationSlice,
323
+ ~code,
324
+ ~sourceCodeHash,
325
+ ~envVars,
326
+ ~memorySize=1024,
327
+ ~timeout=30,
328
+ ~opts,
329
+ )
330
+
331
+ let _connectResources = EventCollectorChannel.connect(
332
+ ~name="AllAutomationSlices",
333
+ ~channelSpecs=[{channel, eventTopics, resources: allQueryDbResources}],
334
+ ~runtime,
335
+ ~opts,
336
+ )
337
+ | None =>
338
+ log.warn(
339
+ ~comp="AutomationSliceRuntime_Builder_Single",
340
+ "finishWithDcbEventLog: DCB EventLog has no parent",
341
+ )
342
+ }
343
+ }
344
+ finished := true
345
+ }
@@ -1,5 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
3
4
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
6
  import * as Pulumi from "@pulumi/pulumi";
@@ -26,13 +27,15 @@ function getDcbQueueUrl() {
26
27
  return dcbQueueUrlRef.contents;
27
28
  }
28
29
 
29
- function registerAutomationSlice(name, specModulePath, bodyModulePath, callbackTypeOpt, queryDbTableName, context) {
30
+ function registerAutomationSlice(name, specModulePath, bodyModulePath, callbackTypeOpt, queryDbTableName, queryDbResourcesOpt, context) {
30
31
  let callbackType = callbackTypeOpt !== undefined ? callbackTypeOpt : "automation";
32
+ let queryDbResources = queryDbResourcesOpt !== undefined ? queryDbResourcesOpt : [];
31
33
  bundledInfos[name] = {
32
34
  specModulePath: specModulePath,
33
35
  bodyModulePath: bodyModulePath,
34
36
  callbackType: callbackType,
35
37
  queryDbTableName: queryDbTableName,
38
+ queryDbResources: queryDbResources,
36
39
  context: context
37
40
  };
38
41
  }
@@ -137,6 +140,68 @@ function finish() {
137
140
  finished.contents = true;
138
141
  }
139
142
 
143
+ function finishWithDcbEventLog(dcbEventLog) {
144
+ if (finished.contents) {
145
+ return;
146
+ }
147
+ if (Object.keys(bundledInfos).length !== 0) {
148
+ let dcbResource = Component$ReventlessCore.toPulumiResource(dcbEventLog);
149
+ let parent = dcbResource.__parentResource;
150
+ if (parent !== undefined) {
151
+ let opts_parent = parent;
152
+ let opts = {
153
+ parent: opts_parent
154
+ };
155
+ let dcbOutputs = Component$ReventlessCore.outputs(dcbEventLog);
156
+ let eventTopics = Object.fromEntries([[
157
+ "DcbEventLog",
158
+ dcbOutputs.eventTopic
159
+ ]]);
160
+ let channel = EventCollectorChannel_DynamoDbStream$ReventlessAws.make("AllAutomationSlices", eventTopics, undefined, opts);
161
+ let resource = dcbOutputs.eventTopic.resources[0];
162
+ let sourceUrn = resource !== undefined ? resource.urn : Pulumi.output("");
163
+ let url = dcbQueueUrlRef.contents;
164
+ let dcbQueueUrl = url !== undefined ? url : Pulumi.output("NOT_AVAILABLE");
165
+ let handlerOutputs = [];
166
+ let packageDirs = {};
167
+ let allQueryDbResources = [];
168
+ Stdlib_Dict.forEachWithKey(bundledInfos, (info, _name) => {
169
+ info.queryDbResources.forEach(r => {
170
+ allQueryDbResources.push(r);
171
+ });
172
+ let pkg = Util_Bundle$ReventlessAws.extractPackageName(info.specModulePath);
173
+ packageDirs[pkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(pkg);
174
+ let bodyPkg = Util_Bundle$ReventlessAws.extractPackageName(info.bodyModulePath);
175
+ packageDirs[bodyPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(bodyPkg);
176
+ let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
177
+ let bodyModule = Stdlib_Option.getOr(JSON.stringify(info.bodyModulePath), `""`);
178
+ let callbackType = Stdlib_Option.getOr(JSON.stringify(info.callbackType), `""`);
179
+ let context = info.context;
180
+ let contextFragment = context !== undefined ? Stdlib_Option.getOr(Stdlib_Option.map(JSON.stringify(context), json => `,"context":` + json), "") : "";
181
+ let handlerJson = Pulumi.all([
182
+ info.queryDbTableName,
183
+ dcbQueueUrl,
184
+ sourceUrn
185
+ ]).apply(param => `{"specModule":` + specModule + `,"bodyModule":` + bodyModule + `,"callbackType":` + callbackType + `,"queryDbTableName":"` + param[0] + `","dcbQueueUrl":"` + param[1] + `","sourceUrn":"` + param[2] + `"` + contextFragment + `}`);
186
+ handlerOutputs.push(handlerJson);
187
+ });
188
+ let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
189
+ let envVars = {};
190
+ envVars["HANDLER_CONFIG"] = handlerConfigOutput;
191
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
192
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match.code, match.sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, opts);
193
+ EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", [{
194
+ channel: channel,
195
+ eventTopics: eventTopics,
196
+ resources: allQueryDbResources
197
+ }], runtime, opts);
198
+ } else {
199
+ log.warn("AutomationSliceRuntime_Builder_Single", undefined, "finishWithDcbEventLog: DCB EventLog has no parent");
200
+ }
201
+ }
202
+ finished.contents = true;
203
+ }
204
+
140
205
  let EventCollectorChannel;
141
206
 
142
207
  let RuntimeEnvironment;
@@ -155,5 +220,6 @@ export {
155
220
  forEventCollector,
156
221
  finished,
157
222
  finish,
223
+ finishWithDcbEventLog,
158
224
  }
159
225
  /* log Not a pure module */
@@ -43,6 +43,7 @@ module Make = (Api: {
43
43
  ~bodyModulePath=Util_Bundle.getModuleSpecifier(Automation.moduleUrl),
44
44
  ~callbackType="automation",
45
45
  ~queryDbTableName,
46
+ ~queryDbResources=queryDbOutputs.resources,
46
47
  ~context,
47
48
  )
48
49
 
@@ -41,7 +41,7 @@ function Make(Api) {
41
41
  let queryDbOutputs = Component$ReventlessCore.outputs(as_).queryDb;
42
42
  let tableResource = queryDbOutputs.resources[0];
43
43
  let queryDbTableName = tableResource.name;
44
- AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Automation.moduleUrl), "automation", queryDbTableName, context);
44
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Automation.moduleUrl), "automation", queryDbTableName, queryDbOutputs.resources, context);
45
45
  return as_;
46
46
  };
47
47
  return {
@@ -51,6 +51,7 @@ module Make = (Api: {
51
51
  ~bodyModulePath=Util_Bundle.getModuleSpecifier(Translation.moduleUrl),
52
52
  ~callbackType="outbound",
53
53
  ~queryDbTableName,
54
+ ~queryDbResources=queryDbOutputs.resources,
54
55
  )
55
56
 
56
57
  ots
@@ -48,7 +48,7 @@ function Make(Api) {
48
48
  let queryDbOutputs = Component$ReventlessCore.outputs(ots).queryDb;
49
49
  let tableResource = queryDbOutputs.resources[0];
50
50
  let queryDbTableName = tableResource.name;
51
- AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Translation.moduleUrl), "outbound", queryDbTableName, undefined);
51
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Translation.moduleUrl), "outbound", queryDbTableName, queryDbOutputs.resources, undefined);
52
52
  return ots;
53
53
  };
54
54
  return {