@reventlessdev/reventless-aws 3.0.0-alpha.263 → 3.0.0-alpha.265

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/package.json +7 -7
  3. package/src/Platform.res +37 -6
  4. package/src/Platform.res.mjs +21 -6
  5. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +6 -1
  6. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +4 -1
  7. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +5 -0
  8. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +3 -0
  9. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +6 -1
  10. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +4 -1
  11. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +6 -1
  12. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +4 -1
  13. package/src/adapter/CommandTopic/CommandTopicRegistry.res +50 -0
  14. package/src/adapter/CommandTopic/CommandTopicRegistry.res.mjs +26 -0
  15. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +21 -3
  16. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +230 -29
  17. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +124 -9
  18. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +188 -3
  19. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +107 -6
  20. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  21. package/src/components/OutboundTranslationSlice_Builder.res +20 -0
  22. package/src/components/OutboundTranslationSlice_Builder.res.mjs +12 -2
  23. package/src/plugin/runtime/PluginRuntime_Builder.res +26 -0
  24. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +26 -0
  25. package/tests/AutomationSliceEntryPoint_OpsTest.res +44 -3
  26. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +65 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
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.265 (2026-08-04)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** make a slice's TODO backlog survive, and actually sweep it ([f35dcbd](https://github.com/ReventlessDev/reventless-core/commit/f35dcbd86374124106c2d1e48d29c6f41fdbec2c))
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.264 (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
+
22
+
6
23
  # 3.0.0-alpha.263 (2026-08-03)
7
24
 
8
25
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.263",
3
+ "version": "3.0.0-alpha.265",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -16,13 +16,13 @@
16
16
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
17
17
  "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.65",
18
18
  "@reventlessdev/rescript-node": "2.0.0-alpha.1",
19
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
20
19
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
21
- "@reventlessdev/reventless-infra": "3.0.0-alpha.124",
22
- "@reventlessdev/reventless-core": "3.0.0-alpha.208",
23
- "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
24
- "@reventlessdev/reventless-spec": "3.0.0-alpha.99",
25
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.72"
20
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
21
+ "@reventlessdev/reventless-core": "3.0.0-alpha.210",
22
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.125",
23
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.74",
24
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.100",
25
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.30"
26
26
  },
27
27
  "devDependencies": {
28
28
  "rescript": "12.3.0",
package/src/Platform.res CHANGED
@@ -96,6 +96,12 @@ let getObjectStoreEndpoints = () => objectStoreEndpointsRef.contents
96
96
  the same sentinel, as `PluginRuntime_Builder.inboundSliceReg.auditTableName`. */
97
97
  let geocoderEndpointRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make(""))
98
98
 
99
+ /** The geocoding place index, for the monolithic case — the SDK-side half of the
100
+ same capability, carried for exactly the reasons above and with the same `""`
101
+ sentinel. Slice Lambdas call Amazon Location directly with this; only the
102
+ browser needs the Function URL. */
103
+ let geocoderPlaceIndexRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make(""))
104
+
99
105
  module MakeWithConfig = (
100
106
  Config: {
101
107
  let splitApi: bool
@@ -794,12 +800,17 @@ module MakeWithConfig = (
794
800
  let tableResource = outputs.resources->Array.getUnsafe(0)
795
801
  PluginRuntime_Builder.registerDcbTableName(tableResource.name)
796
802
  },
797
- // DCB CommandTopic created hook — extracts SQS queue URL for slice builders.
803
+ // DCB CommandTopic created hook — extracts SQS queue URL for slice builders,
804
+ // and the queue resource so the slice Lambda's role is granted
805
+ // `sqs:SendMessage` on it.
798
806
  onDcbCommandTopicCreated: dcbCommandTopicUnknown => {
799
807
  let commandTopic = dcbCommandTopicUnknown->asDcbCommandTopicComponent
800
808
  let channel = commandTopic->ReventlessCore.CommandTopic_Adapter.channel
801
809
  let channelParts = channel.parts->asSqsChannelParts
802
- AutomationSliceRuntime_Builder_Single.setDcbQueueUrl(channelParts.queue.id)
810
+ AutomationSliceRuntime_Builder_Single.setDcbQueueUrl(
811
+ ~resource=?channel.resources->Array.get(0),
812
+ channelParts.queue.id,
813
+ )
803
814
  },
804
815
  // DCB slices created hook — finalize slice Lambdas.
805
816
  onDcbSlicesCreated: dcbEventLogUnknown => {
@@ -2078,6 +2089,19 @@ module MakeWithConfig = (
2078
2089
  Pulumi.Pulumi.export("geocoderEndpoint", geocoderEndpointFlat)
2079
2090
  geocoderEndpointRef := geocoderEndpointFlat
2080
2091
 
2092
+ // The place index itself, exported beside the Function URL because the two
2093
+ // callers need different things from the same capability. A browser cannot
2094
+ // sign an SDK call and gets the URL; a slice's Lambda can, and gets the
2095
+ // index name — no proxy hop, and no dependence on a public unauthenticated
2096
+ // endpoint for an unattended path. `""` when unset, for the same
2097
+ // one-shape-to-handle reason as the endpoint above.
2098
+ let geocoderPlaceIndexFlat = switch cfg.geocoderPlaceIndex {
2099
+ | Some(index) => index.indexName->Pulumi.Output.fromInput
2100
+ | None => Pulumi.Output.make("")
2101
+ }
2102
+ Pulumi.Pulumi.export("geocoderPlaceIndex", geocoderPlaceIndexFlat)
2103
+ geocoderPlaceIndexRef := geocoderPlaceIndexFlat
2104
+
2081
2105
  // Presign endpoints are no longer written to config.json: under route B the
2082
2106
  // client calls the platform API's `Upload_Presign` mutation (reachable from
2083
2107
  // `platformApiEndpoint`, already present) with the store it declares, so there
@@ -2246,15 +2270,22 @@ module MakeWithConfig = (
2246
2270
  // is a modelled outcome, which is the point of the empty string: a plugin
2247
2271
  // deployed against a platform without the capability degrades rather than
2248
2272
  // failing to deploy.
2249
- let geocoderEndpoint: Pulumi.Output.t<string> = switch platformStackRef {
2273
+ // The place index, not the Function URL. A slice's Lambda reaches Amazon
2274
+ // Location through the SDK now, so what it needs is the index's name — and
2275
+ // the grant that goes with it, which is why this is also handed to
2276
+ // `registerGeocoderPlaceIndex` rather than only to the environment. The
2277
+ // browser keeps using the Function URL until the platform API carries a
2278
+ // geocode field; the two doors are independent.
2279
+ let geocoderPlaceIndex: Pulumi.Output.t<string> = switch platformStackRef {
2250
2280
  | Some(stackRef) =>
2251
2281
  (
2252
- stackRef->Pulumi.StackReference.getOutput("geocoderEndpoint"):
2282
+ stackRef->Pulumi.StackReference.getOutput("geocoderPlaceIndex"):
2253
2283
  Pulumi.Output.t<option<string>>
2254
2284
  )->Pulumi.Output.apply(o => o->Option.getOr(""))
2255
- | None => geocoderEndpointRef.contents
2285
+ | None => geocoderPlaceIndexRef.contents
2256
2286
  }
2257
- PluginRuntime_Builder.registerCapabilityEnv("GEOCODER_ENDPOINT", geocoderEndpoint)
2287
+ PluginRuntime_Builder.registerCapabilityEnv("PLACE_INDEX_NAME", geocoderPlaceIndex)
2288
+ PluginRuntime_Builder.registerGeocoderPlaceIndex(geocoderPlaceIndex)
2258
2289
 
2259
2290
  module P = unpack(plugin)
2260
2291
  let pluginComponent = P.make()
@@ -134,6 +134,10 @@ let geocoderEndpointRef = {
134
134
  contents: Pulumi.output("")
135
135
  };
136
136
 
137
+ let geocoderPlaceIndexRef = {
138
+ contents: Pulumi.output("")
139
+ };
140
+
137
141
  function MakeWithConfig(Config) {
138
142
  Stdlib_Option.forEach(Config.commandHandlerConfig.aggregates, param => {
139
143
  Stdlib_Option.forEach(param.sync, AggregateRuntime_Builder_Single$ReventlessAws.setConfig);
@@ -542,7 +546,7 @@ function MakeWithConfig(Config) {
542
546
  let hooks_onDcbCommandTopicCreated = dcbCommandTopicUnknown => {
543
547
  let channel = dcbCommandTopicUnknown.channel;
544
548
  let channelParts = channel.parts;
545
- AutomationSliceRuntime_Builder_Single$ReventlessAws.setDcbQueueUrl(channelParts.queue.id);
549
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.setDcbQueueUrl(undefined, channel.resources[0], channelParts.queue.id);
546
550
  };
547
551
  let hooks_onDcbSlicesCreated = dcbEventLogUnknown => {
548
552
  StateViewSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
@@ -1131,6 +1135,10 @@ function MakeWithConfig(Config) {
1131
1135
  let geocoderEndpointFlat = geocoderEndpointOutput.apply(o => Stdlib_Option.getOr(o, ""));
1132
1136
  Pulumi$Pulumi.$$export("geocoderEndpoint", geocoderEndpointFlat);
1133
1137
  geocoderEndpointRef.contents = geocoderEndpointFlat;
1138
+ let index$1 = hostUiBundle.geocoderPlaceIndex;
1139
+ let geocoderPlaceIndexFlat = index$1 !== undefined ? index$1.indexName : Pulumi.output("");
1140
+ Pulumi$Pulumi.$$export("geocoderPlaceIndex", geocoderPlaceIndexFlat);
1141
+ geocoderPlaceIndexRef.contents = geocoderPlaceIndexFlat;
1134
1142
  let configJsonContent = Pulumi.all([
1135
1143
  Pulumi.all([
1136
1144
  resolvedDomainApiEndpoint,
@@ -1247,8 +1255,9 @@ function MakeWithConfig(Config) {
1247
1255
  bytes: bytes.length
1248
1256
  };
1249
1257
  });
1250
- let geocoderEndpoint = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("geocoderEndpoint").apply(o => Stdlib_Option.getOr(o, "")) : geocoderEndpointRef.contents;
1251
- PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("GEOCODER_ENDPOINT", geocoderEndpoint);
1258
+ let geocoderPlaceIndex = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("geocoderPlaceIndex").apply(o => Stdlib_Option.getOr(o, "")) : geocoderPlaceIndexRef.contents;
1259
+ PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("PLACE_INDEX_NAME", geocoderPlaceIndex);
1260
+ PluginRuntime_Builder$ReventlessAws.registerGeocoderPlaceIndex(geocoderPlaceIndex);
1252
1261
  let pluginComponent = plugin.make();
1253
1262
  Plugin_Helpers$ReventlessCore.clearOffload();
1254
1263
  currentDeployTarget.contents = "Domain";
@@ -1796,7 +1805,7 @@ function Make($star) {
1796
1805
  let hooks_onDcbCommandTopicCreated = dcbCommandTopicUnknown => {
1797
1806
  let channel = dcbCommandTopicUnknown.channel;
1798
1807
  let channelParts = channel.parts;
1799
- AutomationSliceRuntime_Builder_Single$ReventlessAws.setDcbQueueUrl(channelParts.queue.id);
1808
+ AutomationSliceRuntime_Builder_Single$ReventlessAws.setDcbQueueUrl(undefined, channel.resources[0], channelParts.queue.id);
1800
1809
  };
1801
1810
  let hooks_onDcbSlicesCreated = dcbEventLogUnknown => {
1802
1811
  StateViewSliceRuntime_Builder_Single$ReventlessAws.finishWithDcbEventLog(dcbEventLogUnknown);
@@ -2362,6 +2371,10 @@ function Make($star) {
2362
2371
  let geocoderEndpointFlat = geocoderEndpointOutput.apply(o => Stdlib_Option.getOr(o, ""));
2363
2372
  Pulumi$Pulumi.$$export("geocoderEndpoint", geocoderEndpointFlat);
2364
2373
  geocoderEndpointRef.contents = geocoderEndpointFlat;
2374
+ let index$1 = hostUiBundle.geocoderPlaceIndex;
2375
+ let geocoderPlaceIndexFlat = index$1 !== undefined ? index$1.indexName : Pulumi.output("");
2376
+ Pulumi$Pulumi.$$export("geocoderPlaceIndex", geocoderPlaceIndexFlat);
2377
+ geocoderPlaceIndexRef.contents = geocoderPlaceIndexFlat;
2365
2378
  let configJsonContent = Pulumi.all([
2366
2379
  Pulumi.all([
2367
2380
  resolvedDomainApiEndpoint,
@@ -2478,8 +2491,9 @@ function Make($star) {
2478
2491
  bytes: bytes.length
2479
2492
  };
2480
2493
  });
2481
- let geocoderEndpoint = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("geocoderEndpoint").apply(o => Stdlib_Option.getOr(o, "")) : geocoderEndpointRef.contents;
2482
- PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("GEOCODER_ENDPOINT", geocoderEndpoint);
2494
+ let geocoderPlaceIndex = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("geocoderPlaceIndex").apply(o => Stdlib_Option.getOr(o, "")) : geocoderPlaceIndexRef.contents;
2495
+ PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("PLACE_INDEX_NAME", geocoderPlaceIndex);
2496
+ PluginRuntime_Builder$ReventlessAws.registerGeocoderPlaceIndex(geocoderPlaceIndex);
2483
2497
  let pluginComponent = plugin.make();
2484
2498
  Plugin_Helpers$ReventlessCore.clearOffload();
2485
2499
  currentDeployTarget.contents = "Domain";
@@ -2632,6 +2646,7 @@ export {
2632
2646
  objectStoreEndpointsRef,
2633
2647
  getObjectStoreEndpoints,
2634
2648
  geocoderEndpointRef,
2649
+ geocoderPlaceIndexRef,
2635
2650
  MakeWithConfig,
2636
2651
  Make,
2637
2652
  }
@@ -65,10 +65,15 @@ let make: ReventlessCore.CommandTopic_Adapter.channelMaker<
65
65
  )
66
66
 
67
67
  let resolvedQueueOutput = queue->Util_SQS.toResolvedQueueOutput
68
+ let queueResource = queue->Util_SQS.toResource(~tags=tags->Pulumi.Output.fromInput)
69
+
70
+ // Recorded while the queue is still concrete, so a component publishing to
71
+ // this owner from a Lambda can be given the URL and flavor as configuration.
72
+ CommandTopicRegistry.register(~owner, ~queueUrl=queue.id, ~resource=queueResource, ~isFifo=false)
68
73
 
69
74
  {
70
75
  ReventlessCore.CommandTopic_Adapter.parts: {queue: queue},
71
- resources: [queue->Util_SQS.toResource(~tags=tags->Pulumi.Output.fromInput)],
76
+ resources: [queueResource],
72
77
  publishJsons: resolvedQueueOutput->Pulumi.Output.apply(resolvedQueue =>
73
78
  resolvedQueue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS, ...)
74
79
  ),
@@ -12,6 +12,7 @@ import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
12
12
  import * as Util_SQS$ReventlessAws from "../../util/Util_SQS.res.mjs";
13
13
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
14
14
  import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
15
+ import * as CommandTopicRegistry$ReventlessAws from "./CommandTopicRegistry.res.mjs";
15
16
  import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
16
17
  import * as CommandTopicChannel_Helpers$ReventlessAws from "./CommandTopicChannel_Helpers.res.mjs";
17
18
  import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "./CommandTopicChannel_SQS_Runtime.res.mjs";
@@ -48,11 +49,13 @@ function make(name, owner, opts) {
48
49
  sqsManagedSseEnabled: false
49
50
  }, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
50
51
  let resolvedQueueOutput = Util_SQS$ReventlessAws.toResolvedQueueOutput(queue);
52
+ let queueResource = Util_SQS$ReventlessAws.toResource(tags, queue);
53
+ CommandTopicRegistry$ReventlessAws.register(owner, queue.id, queueResource, false);
51
54
  return {
52
55
  parts: {
53
56
  queue: queue
54
57
  },
55
- resources: [Util_SQS$ReventlessAws.toResource(tags, queue)],
58
+ resources: [queueResource],
56
59
  publishJsons: resolvedQueueOutput.apply(resolvedQueue => CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS")),
57
60
  publishJsonsStream: resolvedQueueOutput.apply(resolvedQueue => {
58
61
  let publishJsons = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS");
@@ -44,6 +44,11 @@ let make: ReventlessCore.CommandTopic_Adapter.channelMaker<
44
44
  )
45
45
 
46
46
  let resolvedQueueOutput = queue->Util_SQS.toResolvedQueueOutput
47
+ let queueResource = queue->Util_SQS.toResource(~tags=tags->Pulumi.Output.fromInput)
48
+
49
+ // Async aggregates and StateChangeSlices land here; same capture so a slice
50
+ // targeting one is routed to it rather than to the plugin's DCB topic.
51
+ CommandTopicRegistry.register(~owner, ~queueUrl=queue.id, ~resource=queueResource, ~isFifo=false)
47
52
 
48
53
  {
49
54
  parts: {queue: queue},
@@ -13,6 +13,7 @@ import * as Util_SQS$ReventlessAws from "../../util/Util_SQS.res.mjs";
13
13
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
14
14
  import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
15
15
  import * as Util_SQS_FIFO$ReventlessAws from "../../util/Util_SQS_FIFO.res.mjs";
16
+ import * as CommandTopicRegistry$ReventlessAws from "./CommandTopicRegistry.res.mjs";
16
17
  import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
17
18
  import * as CommandTopicChannel_SQS$ReventlessAws from "./CommandTopicChannel_SQS.res.mjs";
18
19
  import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "./CommandTopicChannel_SQS_Runtime.res.mjs";
@@ -31,6 +32,8 @@ function make(name, owner, opts) {
31
32
  sqsManagedSseEnabled: false
32
33
  }, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
33
34
  let resolvedQueueOutput = Util_SQS$ReventlessAws.toResolvedQueueOutput(queue);
35
+ let queueResource = Util_SQS$ReventlessAws.toResource(tags, queue);
36
+ CommandTopicRegistry$ReventlessAws.register(owner, queue.id, queueResource, false);
34
37
  return {
35
38
  parts: {
36
39
  queue: queue
@@ -33,10 +33,15 @@ let make: ReventlessCore.CommandTopic_Adapter.channelMaker<
33
33
  )
34
34
 
35
35
  let resolvedQueueOutput = queue->Util_SQS.toResolvedQueueOutput
36
+ let queueResource = queue->Util_SQS_FIFO.toResource(~tags=tags->Pulumi.Output.fromInput)
37
+
38
+ // Same capture as the standard variant, and the reason the flag exists: only
39
+ // the creating module knows this queue needs a MessageGroupId.
40
+ CommandTopicRegistry.register(~owner, ~queueUrl=queue.id, ~resource=queueResource, ~isFifo=true)
36
41
 
37
42
  {
38
43
  parts: {queue: queue},
39
- resources: [queue->Util_SQS_FIFO.toResource(~tags=tags->Pulumi.Output.fromInput)],
44
+ resources: [queueResource],
40
45
  publishJsons: resolvedQueueOutput->Pulumi.Output.apply(resolvedQueue =>
41
46
  resolvedQueue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO, ...)
42
47
  ),
@@ -13,6 +13,7 @@ import * as Util_SQS$ReventlessAws from "../../util/Util_SQS.res.mjs";
13
13
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
14
14
  import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
15
15
  import * as Util_SQS_FIFO$ReventlessAws from "../../util/Util_SQS_FIFO.res.mjs";
16
+ import * as CommandTopicRegistry$ReventlessAws from "./CommandTopicRegistry.res.mjs";
16
17
  import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
17
18
  import * as CommandTopicChannel_SQS$ReventlessAws from "./CommandTopicChannel_SQS.res.mjs";
18
19
  import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "./CommandTopicChannel_SQS_Runtime.res.mjs";
@@ -31,11 +32,13 @@ function make(name, owner, opts) {
31
32
  sqsManagedSseEnabled: false
32
33
  }, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
33
34
  let resolvedQueueOutput = Util_SQS$ReventlessAws.toResolvedQueueOutput(queue);
35
+ let queueResource = Util_SQS_FIFO$ReventlessAws.toResource(tags, queue);
36
+ CommandTopicRegistry$ReventlessAws.register(owner, queue.id, queueResource, true);
34
37
  return {
35
38
  parts: {
36
39
  queue: queue
37
40
  },
38
- resources: [Util_SQS_FIFO$ReventlessAws.toResource(tags, queue)],
41
+ resources: [queueResource],
39
42
  publishJsons: resolvedQueueOutput.apply(resolvedQueue => CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS_FIFO")),
40
43
  publishJsonsStream: resolvedQueueOutput.apply(resolvedQueue => {
41
44
  let publishJsons = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS_FIFO");
@@ -45,10 +45,15 @@ let make: ReventlessCore.CommandTopic_Adapter.channelMaker<
45
45
  )
46
46
 
47
47
  let resolvedQueueOutput = queue->Util_SQS.toResolvedQueueOutput
48
+ let queueResource = queue->Util_SQS.toResource(~tags=tags->Pulumi.Output.fromInput)
49
+
50
+ // This is the maker Aggregates use, so it is the one an OutboundTranslationSlice
51
+ // naming an Aggregate in `targetName` needs to find.
52
+ CommandTopicRegistry.register(~owner, ~queueUrl=queue.id, ~resource=queueResource, ~isFifo=false)
48
53
 
49
54
  {
50
55
  ReventlessCore.CommandTopic_Adapter.parts: {queue: queue},
51
- resources: [queue->Util_SQS.toResource(~tags=tags->Pulumi.Output.fromInput)],
56
+ resources: [queueResource],
52
57
  publishJsons: resolvedQueueOutput->Pulumi.Output.apply(resolvedQueue =>
53
58
  resolvedQueue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS, ...)
54
59
  ),
@@ -11,6 +11,7 @@ import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
11
11
  import * as Util_SQS$ReventlessAws from "../../util/Util_SQS.res.mjs";
12
12
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
13
13
  import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
14
+ import * as CommandTopicRegistry$ReventlessAws from "./CommandTopicRegistry.res.mjs";
14
15
  import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
15
16
  import * as CommandTopicChannel_SQS$ReventlessAws from "./CommandTopicChannel_SQS.res.mjs";
16
17
  import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "./CommandTopicChannel_SQS_Runtime.res.mjs";
@@ -28,11 +29,13 @@ function make(name, owner, opts) {
28
29
  sqsManagedSseEnabled: false
29
30
  }, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
30
31
  let resolvedQueueOutput = Util_SQS$ReventlessAws.toResolvedQueueOutput(queue);
32
+ let queueResource = Util_SQS$ReventlessAws.toResource(tags, queue);
33
+ CommandTopicRegistry$ReventlessAws.register(owner, queue.id, queueResource, false);
31
34
  return {
32
35
  parts: {
33
36
  queue: queue
34
37
  },
35
- resources: [Util_SQS$ReventlessAws.toResource(tags, queue)],
38
+ resources: [queueResource],
36
39
  publishJsons: resolvedQueueOutput.apply(resolvedQueue => CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS")),
37
40
  publishJsonsStream: resolvedQueueOutput.apply(resolvedQueue => {
38
41
  let publishJsons = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(resolvedQueue, "SQS");
@@ -0,0 +1,50 @@
1
+ // Where each component's CommandTopic queue lives, recorded as the queue is
2
+ // created.
3
+ //
4
+ // Runtime code cannot be handed a publisher. A slice Lambda is a compiled
5
+ // EntryPoint configured through `HANDLER_CONFIG`, not a serialized closure —
6
+ // that separation is what fixed the cold-start crashes from closures mixing
7
+ // layer and runtime SDK versions — so a `publishJsons` function cannot cross
8
+ // into it. The same choice has to travel as *data*: which queue, and whether it
9
+ // is FIFO.
10
+ //
11
+ // Captured here rather than recovered later, because recovering it does not
12
+ // work. A component's `resources` array reaches callers inside a
13
+ // `Pulumi.Output.t`, and although every `resource` field is typed
14
+ // `Pulumi.Output.t<string>`, Pulumi flattens nested Outputs at resolution — so
15
+ // within the enclosing apply those fields are already plain strings and calling
16
+ // `.apply` on one throws. At creation the queue is still concrete: `.id` is a
17
+ // flat Output, and the creating module's identity already settles the flavor.
18
+ //
19
+ // Keyed by owner *name* — an Aggregate's `Spec.name` — which is the same key an
20
+ // OutboundTranslationSlice names in `targetName`.
21
+
22
+ type entry = {
23
+ /** The queue URL, for a runtime publisher rebuilt from configuration. */
24
+ queueUrl: Pulumi.Output.t<string>,
25
+ /** The queue as a deploy-time resource, so a caller can add it to a channel
26
+ spec and pick up `connectLambda`'s existing `sqs:SendMessage` grant. */
27
+ resource: ReventlessInfra.Adapter.resource,
28
+ /** FIFO queues need a `MessageGroupId` and standard queues reject one, so this
29
+ cannot be assumed by the caller — it is a property of the queue that only
30
+ the module which created it knows. */
31
+ isFifo: bool,
32
+ }
33
+
34
+ let byOwner: dict<entry> = Dict.make()
35
+
36
+ let register = (
37
+ ~owner: option<ReventlessCore.ResourceAttribution.owner>,
38
+ ~queueUrl: Pulumi.Output.t<string>,
39
+ ~resource: ReventlessInfra.Adapter.resource,
40
+ ~isFifo: bool,
41
+ ) =>
42
+ owner->Option.forEach(({name}) =>
43
+ byOwner->Dict.set(name, {queueUrl, resource, isFifo})
44
+ )
45
+
46
+ /** The CommandTopic of the component with this name, if one has been created.
47
+ `None` for a target that is not an Aggregate in this plugin — a DCB
48
+ StateChangeSlice, or a name that matches nothing — where the caller keeps
49
+ the plugin-wide DCB command topic. */
50
+ let get = (name: string) => byOwner->Dict.get(name)
@@ -0,0 +1,26 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+
5
+ let byOwner = {};
6
+
7
+ function register(owner, queueUrl, resource, isFifo) {
8
+ Stdlib_Option.forEach(owner, param => {
9
+ byOwner[param.name] = {
10
+ queueUrl: queueUrl,
11
+ resource: resource,
12
+ isFifo: isFifo
13
+ };
14
+ });
15
+ }
16
+
17
+ function get(name) {
18
+ return byOwner[name];
19
+ }
20
+
21
+ export {
22
+ byOwner,
23
+ register,
24
+ get,
25
+ }
26
+ /* No side effect */
@@ -22,6 +22,7 @@ const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifie
22
22
 
23
23
  async function buildAllHandlers() {
24
24
  const handlers = {};
25
+ const sweeps = [];
25
26
  const entries = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
26
27
 
27
28
  await Promise.all(entries.map(async (entry) => {
@@ -56,11 +57,28 @@ async function buildAllHandlers() {
56
57
  ? [entry.sourceUrn]
57
58
  : [];
58
59
  for (const sourceUrn of sourceUrns) {
59
- StreamOps.addToRegistry(handlers, sourceUrn, registered);
60
+ StreamOps.addToRegistry(handlers, sourceUrn, registered.registered);
60
61
  }
62
+ // Every slice can also be swept without an event — see the scheduled branch
63
+ // below. Collected regardless of how many streams it listens on, so a
64
+ // multi-source slice is swept once rather than once per stream.
65
+ sweeps.push(registered);
61
66
  }));
62
67
 
63
- return handlers;
68
+ return { handlers, sweeps };
64
69
  }
65
70
 
66
- export const handler = StreamOps.makeRoutedHandler("AutomationSliceRuntime", buildAllHandlers());
71
+ const builtPromise = buildAllHandlers();
72
+ const routed = StreamOps.makeRoutedHandler(
73
+ "AutomationSliceRuntime",
74
+ builtPromise.then((b) => b.handlers),
75
+ );
76
+
77
+ // The scheduled sweep arrives as the EventBridge rule's constant Input, which
78
+ // has no `records` — so this branch is the one place that must probe an untyped
79
+ // Lambda payload, which is what this shell exists for. Everything it dispatches
80
+ // to is type-checked in AutomationSliceEntryPoint_Ops.
81
+ export const handler = async (event, context) =>
82
+ event?.reventlessSweep
83
+ ? Ops.runSweeps((await builtPromise).sweeps)
84
+ : routed(event, context);