@reventlessdev/reventless-aws 3.0.0-alpha.232 → 3.0.0-alpha.234

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 (110) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +7 -7
  3. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
  4. package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
  5. package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
  6. package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
  9. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
  10. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
  11. package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
  12. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
  13. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
  14. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
  15. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
  16. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
  17. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
  18. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
  20. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
  21. package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
  22. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
  23. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
  24. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
  25. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
  26. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
  27. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
  28. package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
  29. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
  30. package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
  31. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +14 -1
  32. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +22 -2
  33. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +15 -4
  34. package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
  35. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
  36. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
  37. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
  38. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
  39. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
  40. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
  41. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
  42. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
  43. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
  44. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
  45. package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
  46. package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
  48. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
  49. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
  50. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
  51. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
  52. package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
  53. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
  54. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
  55. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
  56. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +26 -6
  57. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
  58. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
  59. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
  60. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +5 -2
  61. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
  62. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
  63. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
  64. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
  65. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
  66. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
  67. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
  68. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
  69. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
  70. package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
  71. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
  72. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
  73. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
  74. package/src/components/AutomationSlice_Builder.res +2 -0
  75. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  76. package/src/components/OutboundTranslationSlice_Builder.res +1 -0
  77. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  78. package/src/plugin/runtime/PluginRuntime_Builder.res +13 -4
  79. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +1 -1
  80. package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
  81. package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
  82. package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
  83. package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
  84. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
  85. package/tests/CounterEntryPoint_OpsTest.res +115 -0
  86. package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
  87. package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
  88. package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
  89. package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
  90. package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
  91. package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
  92. package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
  93. package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
  94. package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
  95. package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
  96. package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
  97. package/tests/Platform_AdminScan_OpsTest.res +80 -0
  98. package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
  99. package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
  100. package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
  101. package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
  102. package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
  103. package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
  104. package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
  105. package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
  106. package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
  107. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
  108. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
  109. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
  110. package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
@@ -1,150 +1,30 @@
1
1
  // TaskBucket Lambda entry point.
2
- // At cold start: reads HANDLER_CONFIG, dynamically imports callback module,
3
- // wires TaskBucket_S3_Runtime.handleBucketEvent, dispatches task actions to SQS.
4
-
5
- import { makeQueueRef, log } from "./HandlerFactoryHelpers.mjs";
6
- import { handleBucketEvent } from "@reventlessdev/reventless-aws/src/adapter/Task/TaskBucket_S3_Runtime.res.mjs";
7
- import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
8
- import {
9
- CloudWatchEventsClient,
10
- PutRuleCommand,
11
- PutTargetsCommand,
12
- RemoveTargetsCommand,
13
- DeleteRuleCommand,
14
- } from "@aws-sdk/client-cloudwatch-events";
2
+ // At cold start: reads HANDLER_CONFIG, dynamically imports the task callback
3
+ // module, and dispatches the resulting task actions (command publishing, CW
4
+ // Events schedules).
5
+ //
6
+ // Thin untyped shell ("typed core, thin shell"
7
+ // docs/plans/minimize-lambda-entrypoint-mjs-shell.md): this file owns ONLY the
8
+ // inherently-untyped seam — the dynamic `import()` of the callback module named
9
+ // in HANDLER_CONFIG and the read of its `callback` export. Everything else
10
+ // (HANDLER_CONFIG parsing, S3-event handling via TaskBucket_S3_Runtime, the
11
+ // exhaustive task-action dispatch, per-aggregate command publishing, the
12
+ // CloudWatch Events scheduler ops) lives type-checked in
13
+ // TaskBucketEntryPoint_Ops.res.
14
+
15
+ import * as Ops from "./TaskBucketEntryPoint_Ops.res.mjs";
15
16
 
16
17
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
17
18
 
18
- // Mirrors ScheduledPublisher_CloudWatchEvents_Runtime.toScheduleExpression — kept
19
- // in sync manually because that runtime helper isn't bundled in this Lambda.
20
- function toScheduleExpression(rate) {
21
- // rate is a serialized variant from Reventless.Schedule.rate
22
- switch (rate.TAG) {
23
- case "Single": {
24
- const [year, month, day, hour, minute] = [rate._0, rate._1, rate._2, rate._3, rate._4];
25
- return `cron(${minute} ${hour} ${day} ${month} ? ${year})`;
26
- }
27
- case "Minutes": {
28
- const n = rate._0;
29
- return `rate(${n} minute${n === 1 ? "" : "s"})`;
30
- }
31
- case "Hours": {
32
- const n = rate._0;
33
- return `rate(${n} hour${n === 1 ? "" : "s"})`;
34
- }
35
- case "Days": {
36
- const n = rate._0;
37
- return `rate(${n} day${n === 1 ? "" : "s"})`;
38
- }
39
- case "Daily":
40
- return `cron(${rate._1} ${rate._0} * * * *)`;
41
- case "Weekdays":
42
- return `cron(${rate._1} ${rate._0} ? * MON-FRI *)`;
43
- case "WeekdaysAndSaturday":
44
- return `cron(${rate._1} ${rate._0} ? * MON-SAT *)`;
45
- default:
46
- throw new Error(`TaskBucketEntryPoint: unknown schedule rate ${JSON.stringify(rate)}`);
47
- }
48
- }
49
-
50
- let cachedScheduler = null;
51
- function makeSchedulerOps(schedulerCfg) {
52
- if (!schedulerCfg) return null;
53
- if (cachedScheduler) return cachedScheduler;
54
- const roleArn = process.env[schedulerCfg.roleArnEnv];
55
- const targetArn = process.env[schedulerCfg.targetArnEnv];
56
- const targetName = process.env[schedulerCfg.targetNameEnv];
57
- if (!roleArn || !targetArn || !targetName) {
58
- log.warn("scheduler env vars missing — schedules will no-op", { comp: "TaskBucketRuntime" });
59
- return null;
60
- }
61
- const client = new CloudWatchEventsClient({});
62
- cachedScheduler = {
63
- createSchedule: async (schedule) => {
64
- await client.send(new PutRuleCommand({
65
- Name: schedule.name,
66
- ScheduleExpression: toScheduleExpression(schedule.rate),
67
- RoleArn: roleArn,
68
- State: "ENABLED",
69
- }));
70
- await client.send(new PutTargetsCommand({
71
- Rule: schedule.name,
72
- Targets: [{ Arn: targetArn, Id: targetName, Input: schedule.payload }],
73
- }));
74
- },
75
- deleteSchedule: async (name) => {
76
- await client.send(new RemoveTargetsCommand({ Rule: name, Ids: [targetName] }));
77
- await client.send(new DeleteRuleCommand({ Name: name }));
78
- },
79
- };
80
- return cachedScheduler;
81
- }
82
-
83
19
  async function buildHandler() {
84
- const configStr = process.env["HANDLER_CONFIG"] || "{}";
85
- const config = JSON.parse(configStr);
86
-
20
+ const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
87
21
  const callbackModule = await dynamicImport(config.callbackModule);
88
- const bucketCallback = callbackModule.callback;
89
- const handleEvents = handleBucketEvent(bucketCallback);
90
-
91
- // Build publishCommands dict from env var queue URLs. The deploy-side builder
92
- // writes HANDLER_CONFIG.publishToAggregates as { aggregateName: envVarName }
93
- // (see TaskRuntime_Builder_PerBucket.res), so iterate accordingly.
94
- const publishCommandsFns = {};
95
- for (const [aggName, envVarName] of Object.entries(config.publishToAggregates || {})) {
96
- const queueUrl = process.env[envVarName] || "";
97
- if (queueUrl !== "") {
98
- publishCommandsFns[aggName] = sqsPublishJsons(makeQueueRef(queueUrl), "SQS_FIFO");
99
- }
100
- }
101
-
102
- return async (event, _context) => {
103
- const taskActions = await handleEvents(event, _context);
104
-
105
- await Promise.all(taskActions.map(async action => {
106
- switch (action.TAG) {
107
- case "PublishCommands": {
108
- const aggregateName = action._0;
109
- const cmdJsons = action._1;
110
- const pub = publishCommandsFns[aggregateName];
111
- if (pub !== undefined) {
112
- await pub(cmdJsons);
113
- } else {
114
- log.warn(`no publish function for aggregate "${aggregateName}"`, { comp: "TaskBucketRuntime" });
115
- }
116
- break;
117
- }
118
- case "CreateSchedule": {
119
- const ops = makeSchedulerOps(config.scheduler);
120
- if (ops) {
121
- await ops.createSchedule(action._0);
122
- } else {
123
- log.warn("CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", { comp: "TaskBucketRuntime" });
124
- }
125
- break;
126
- }
127
- case "DeleteSchedule": {
128
- const ops = makeSchedulerOps(config.scheduler);
129
- if (ops) {
130
- await ops.deleteSchedule(action._0);
131
- } else {
132
- log.warn("DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", { comp: "TaskBucketRuntime" });
133
- }
134
- break;
135
- }
136
- default:
137
- log.warn(`unknown task action TAG ${action.TAG}`, { comp: "TaskBucketRuntime" });
138
- }
139
- }));
140
-
141
- return "";
142
- };
22
+ return Ops.makeHandler(callbackModule.callback, config);
143
23
  }
144
24
 
145
25
  const initPromise = buildHandler();
146
26
 
147
27
  export async function handler(event, context) {
148
- const s3Handler = await initPromise;
149
- return await s3Handler(event, context);
28
+ const bucketHandler = await initPromise;
29
+ return await bucketHandler(event, context);
150
30
  }
@@ -0,0 +1,198 @@
1
+ // Typed cold-start core for the TaskBucket Lambda entry point.
2
+ //
3
+ // The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
4
+ // TaskBucketEntryPoint.mjs keeps only the inherently-untyped seam — the
5
+ // dynamic `import()` of the task callback module named in HANDLER_CONFIG and
6
+ // the read of its `callback` export. HANDLER_CONFIG parsing, the S3-event →
7
+ // task-action dispatch (an exhaustive match over Reventless.Task.taskAction —
8
+ // the former JS `switch (action.TAG)` with its unreachable "unknown TAG" arm), the
9
+ // per-aggregate command publishing, and the CloudWatch Events scheduler ops
10
+ // live here, fully type-checked.
11
+ //
12
+ // The former `.mjs` carried a hand-maintained mirror of
13
+ // `toScheduleExpression` ("kept in sync manually"); typed code calls
14
+ // ScheduledPublisher_CloudWatchEvents_Runtime directly — the layer ships it —
15
+ // so the mirror is gone.
16
+
17
+ @val @scope("process") external processEnv: dict<string> = "env"
18
+ @module("./HandlerFactoryHelpers.mjs") @scope("log")
19
+ external logWarn: (string, StreamRoutedEntryPoint_Ops.dispatchOpts) => unit = "warn"
20
+
21
+ // ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
22
+ // Written by TaskRuntime_Builder_PerBucket.
23
+
24
+ type schedulerEnvConfig = {
25
+ roleArnEnv: string,
26
+ targetArnEnv: string,
27
+ targetNameEnv: string,
28
+ }
29
+ type handlerConfig = {
30
+ callbackModule: string,
31
+ // aggregateName → env-var name holding the aggregate's cmd-topic SQS URL.
32
+ publishToAggregates: dict<string>,
33
+ scheduler: option<schedulerEnvConfig>,
34
+ }
35
+
36
+ let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
37
+ obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
38
+
39
+ let parseHandlerConfig = (rawJson: string): handlerConfig => {
40
+ let obj =
41
+ (rawJson == "" ? "{}" : rawJson)
42
+ ->JSON.parseOrThrow
43
+ ->JSON.Decode.object
44
+ ->Option.getOr(Dict.make())
45
+ {
46
+ callbackModule: obj->strOf("callbackModule")->Option.getOr(""),
47
+ publishToAggregates: obj
48
+ ->Dict.get("publishToAggregates")
49
+ ->Option.flatMap(JSON.Decode.object)
50
+ ->Option.getOr(Dict.make())
51
+ ->Dict.toArray
52
+ ->Array.filterMap(((k, v)) => v->JSON.Decode.string->Option.map(s => (k, s)))
53
+ ->Dict.fromArray,
54
+ scheduler: obj
55
+ ->Dict.get("scheduler")
56
+ ->Option.flatMap(JSON.Decode.object)
57
+ ->Option.map(s => {
58
+ roleArnEnv: s->strOf("roleArnEnv")->Option.getOr(""),
59
+ targetArnEnv: s->strOf("targetArnEnv")->Option.getOr(""),
60
+ targetNameEnv: s->strOf("targetNameEnv")->Option.getOr(""),
61
+ }),
62
+ }
63
+ }
64
+
65
+ // ── Command publishing ──────────────────────────────────────────────────────
66
+ // aggregateName → publishJsons, resolving each queue URL via the env-var name
67
+ // carried in HANDLER_CONFIG; entries with an unset or empty URL are skipped
68
+ // (same as the former JS `if (queueUrl !== "")`).
69
+
70
+ let buildPublishCommands = (map: dict<string>): dict<ReventlessCore.CommandTopic.publishJsons> =>
71
+ map
72
+ ->Dict.toArray
73
+ ->Array.filterMap(((aggName, envVarName)) =>
74
+ processEnv
75
+ ->Dict.get(envVarName)
76
+ ->Option.filter(queueUrl => queueUrl != "")
77
+ ->Option.map(queueUrl => {
78
+ let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
79
+ (aggName, queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO))
80
+ })
81
+ )
82
+ ->Dict.fromArray
83
+
84
+ // ── Scheduler ops (CloudWatch Events) ───────────────────────────────────────
85
+ // Resolved lazily on the first schedule action (tasks without schedules never
86
+ // touch the CloudWatch client) and cached for the container lifetime.
87
+
88
+ type schedulerOps = {
89
+ createSchedule: Reventless.Schedule.schedule => promise<unit>,
90
+ deleteSchedule: string => promise<unit>,
91
+ }
92
+
93
+ let schedulerOpsCache: ref<option<schedulerOps>> = ref(None)
94
+
95
+ let makeSchedulerOps = (config: option<schedulerEnvConfig>): option<schedulerOps> =>
96
+ switch (schedulerOpsCache.contents, config) {
97
+ | (Some(ops), _) => Some(ops)
98
+ | (None, None) => None
99
+ | (None, Some(cfg)) =>
100
+ switch (
101
+ processEnv->Dict.get(cfg.roleArnEnv),
102
+ processEnv->Dict.get(cfg.targetArnEnv),
103
+ processEnv->Dict.get(cfg.targetNameEnv),
104
+ ) {
105
+ | (Some(roleArn), Some(targetArn), Some(targetName))
106
+ if roleArn != "" && targetArn != "" && targetName != "" =>
107
+ // The single resolved resource the shared CloudWatch Events runtime
108
+ // targets: `urn` is the PutTargets ARN, `name` the target id.
109
+ let resources: array<ReventlessInfra.Adapter.resolvedResource> = [
110
+ {
111
+ name: targetName,
112
+ id: targetName,
113
+ urn: targetArn,
114
+ service: "unknown",
115
+ resourceInfo: ReventlessInfra.Adapter.NoInfo,
116
+ role: "",
117
+ region: "",
118
+ resourceType: "",
119
+ configuration: Dict.make(),
120
+ tags: Dict.make(),
121
+ },
122
+ ]
123
+ let ops = {
124
+ createSchedule: schedule =>
125
+ ScheduledPublisher_CloudWatchEvents_Runtime.createSchedule(~roleArn)(resources, schedule),
126
+ deleteSchedule: name =>
127
+ ScheduledPublisher_CloudWatchEvents_Runtime.deleteSchedule(resources, name),
128
+ }
129
+ schedulerOpsCache := Some(ops)
130
+ Some(ops)
131
+ | _ =>
132
+ logWarn("scheduler env vars missing — schedules will no-op", {comp: "TaskBucketRuntime"})
133
+ None
134
+ }
135
+ }
136
+
137
+ // ── Task-action dispatch ────────────────────────────────────────────────────
138
+
139
+ let dispatchTaskActions = async (
140
+ ~actions: array<Reventless.Task.taskAction>,
141
+ ~publishCommands: dict<ReventlessCore.CommandTopic.publishJsons>,
142
+ ~schedulerOps: unit => option<schedulerOps>,
143
+ ): unit => {
144
+ let _ =
145
+ await actions
146
+ ->Array.map(async action =>
147
+ switch action {
148
+ | Reventless.Task.PublishCommands(aggregateName, cmdJsons) =>
149
+ switch publishCommands->Dict.get(aggregateName) {
150
+ | Some(publish) => await publish(cmdJsons)
151
+ | None =>
152
+ logWarn(
153
+ `no publish function for aggregate "${aggregateName}"`,
154
+ {comp: "TaskBucketRuntime"},
155
+ )
156
+ }
157
+ | CreateSchedule(schedule) =>
158
+ switch schedulerOps() {
159
+ | Some(ops) => await ops.createSchedule(schedule)
160
+ | None =>
161
+ logWarn(
162
+ "CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
163
+ {comp: "TaskBucketRuntime"},
164
+ )
165
+ }
166
+ | DeleteSchedule(name) =>
167
+ switch schedulerOps() {
168
+ | Some(ops) => await ops.deleteSchedule(name)
169
+ | None =>
170
+ logWarn(
171
+ "DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
172
+ {comp: "TaskBucketRuntime"},
173
+ )
174
+ }
175
+ }
176
+ )
177
+ ->Promise.all
178
+ }
179
+
180
+ // ── Exported Lambda handler ─────────────────────────────────────────────────
181
+ // The shell reads `callback` off the dynamically imported task module and
182
+ // hands it here; from this point everything is typed
183
+ // (TaskBucket_S3_Runtime.handleBucketEvent decodes the S3 records and invokes
184
+ // the callback per object).
185
+
186
+ let makeHandler = (~callback: ReventlessCore.Task.bucketCallback, ~config: handlerConfig) => {
187
+ let handleEvents = TaskBucket_S3_Runtime.handleBucketEvent(callback)
188
+ let publishCommands = buildPublishCommands(config.publishToAggregates)
189
+ async (event: PulumiAws.S3.Bucket.event, context: PulumiAws.Lambda.context) => {
190
+ let actions = await handleEvents(event, context)
191
+ await dispatchTaskActions(
192
+ ~actions,
193
+ ~publishCommands,
194
+ ~schedulerOps=() => makeSchedulerOps(config.scheduler),
195
+ )
196
+ ""
197
+ }
198
+ }
@@ -0,0 +1,155 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
7
+ import * as TaskBucket_S3_Runtime$ReventlessAws from "../Task/TaskBucket_S3_Runtime.res.mjs";
8
+ import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
9
+ import * as ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws from "../ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
10
+
11
+ function logWarn(prim0, prim1) {
12
+ HandlerFactoryHelpersMjs.log.warn(prim0, prim1);
13
+ }
14
+
15
+ function strOf(obj, key) {
16
+ return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
17
+ }
18
+
19
+ function parseHandlerConfig(rawJson) {
20
+ let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(rawJson === "" ? "{}" : rawJson)), {});
21
+ return {
22
+ callbackModule: Stdlib_Option.getOr(strOf(obj, "callbackModule"), ""),
23
+ publishToAggregates: Object.fromEntries(Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["publishToAggregates"], Stdlib_JSON.Decode.object), {})), param => {
24
+ let k = param[0];
25
+ return Stdlib_Option.map(Stdlib_JSON.Decode.string(param[1]), s => [
26
+ k,
27
+ s
28
+ ]);
29
+ })),
30
+ scheduler: Stdlib_Option.map(Stdlib_Option.flatMap(obj["scheduler"], Stdlib_JSON.Decode.object), s => ({
31
+ roleArnEnv: Stdlib_Option.getOr(strOf(s, "roleArnEnv"), ""),
32
+ targetArnEnv: Stdlib_Option.getOr(strOf(s, "targetArnEnv"), ""),
33
+ targetNameEnv: Stdlib_Option.getOr(strOf(s, "targetNameEnv"), "")
34
+ }))
35
+ };
36
+ }
37
+
38
+ function buildPublishCommands(map) {
39
+ return Object.fromEntries(Stdlib_Array.filterMap(Object.entries(map), param => {
40
+ let aggName = param[0];
41
+ return Stdlib_Option.map(Stdlib_Option.filter(process.env[param[1]], queueUrl => queueUrl !== ""), queueUrl => {
42
+ let queue = {
43
+ id: queueUrl,
44
+ name: queueUrl,
45
+ arn: ""
46
+ };
47
+ return [
48
+ aggName,
49
+ CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO")
50
+ ];
51
+ });
52
+ }));
53
+ }
54
+
55
+ let schedulerOpsCache = {
56
+ contents: undefined
57
+ };
58
+
59
+ function makeSchedulerOps(config) {
60
+ let match = schedulerOpsCache.contents;
61
+ if (match !== undefined) {
62
+ return match;
63
+ }
64
+ if (config === undefined) {
65
+ return;
66
+ }
67
+ let match$1 = process.env[config.roleArnEnv];
68
+ let match$2 = process.env[config.targetArnEnv];
69
+ let match$3 = process.env[config.targetNameEnv];
70
+ if (match$1 !== undefined && match$2 !== undefined && match$3 !== undefined && match$1 !== "" && match$2 !== "" && match$3 !== "") {
71
+ let resources = [{
72
+ name: match$3,
73
+ id: match$3,
74
+ urn: match$2,
75
+ resourceInfo: "NoInfo",
76
+ service: "unknown",
77
+ role: "",
78
+ region: "",
79
+ resourceType: "",
80
+ configuration: {},
81
+ tags: {}
82
+ }];
83
+ let ops_createSchedule = schedule => ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.createSchedule(match$1)(resources, schedule);
84
+ let ops_deleteSchedule = name => ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.deleteSchedule(resources, name);
85
+ let ops = {
86
+ createSchedule: ops_createSchedule,
87
+ deleteSchedule: ops_deleteSchedule
88
+ };
89
+ schedulerOpsCache.contents = ops;
90
+ return ops;
91
+ }
92
+ HandlerFactoryHelpersMjs.log.warn("scheduler env vars missing — schedules will no-op", {
93
+ comp: "TaskBucketRuntime"
94
+ });
95
+ }
96
+
97
+ async function dispatchTaskActions(actions, publishCommands, schedulerOps) {
98
+ await Promise.all(actions.map(async action => {
99
+ switch (action.TAG) {
100
+ case "PublishCommands" :
101
+ let aggregateName = action._0;
102
+ let publish = publishCommands[aggregateName];
103
+ if (publish !== undefined) {
104
+ return await publish(action._1);
105
+ } else {
106
+ HandlerFactoryHelpersMjs.log.warn(`no publish function for aggregate "` + aggregateName + `"`, {
107
+ comp: "TaskBucketRuntime"
108
+ });
109
+ return;
110
+ }
111
+ case "CreateSchedule" :
112
+ let ops = schedulerOps();
113
+ if (ops !== undefined) {
114
+ return await ops.createSchedule(action._0);
115
+ } else {
116
+ HandlerFactoryHelpersMjs.log.warn("CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", {
117
+ comp: "TaskBucketRuntime"
118
+ });
119
+ return;
120
+ }
121
+ case "DeleteSchedule" :
122
+ let ops$1 = schedulerOps();
123
+ if (ops$1 !== undefined) {
124
+ return await ops$1.deleteSchedule(action._0);
125
+ } else {
126
+ HandlerFactoryHelpersMjs.log.warn("DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", {
127
+ comp: "TaskBucketRuntime"
128
+ });
129
+ return;
130
+ }
131
+ }
132
+ }));
133
+ }
134
+
135
+ function makeHandler(callback, config) {
136
+ let handleEvents = TaskBucket_S3_Runtime$ReventlessAws.handleBucketEvent(callback);
137
+ let publishCommands = buildPublishCommands(config.publishToAggregates);
138
+ return async (event, context) => {
139
+ let actions = await handleEvents(event, context);
140
+ await dispatchTaskActions(actions, publishCommands, () => makeSchedulerOps(config.scheduler));
141
+ return "";
142
+ };
143
+ }
144
+
145
+ export {
146
+ logWarn,
147
+ strOf,
148
+ parseHandlerConfig,
149
+ buildPublishCommands,
150
+ schedulerOpsCache,
151
+ makeSchedulerOps,
152
+ dispatchTaskActions,
153
+ makeHandler,
154
+ }
155
+ /* ./HandlerFactoryHelpers.mjs Not a pure module */