@reventlessdev/reventless-aws 3.0.0-alpha.231 → 3.0.0-alpha.233

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 (104) 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_Ops.res +16 -3
  32. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +11 -6
  33. package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
  34. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
  35. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
  36. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
  37. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
  38. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
  39. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
  40. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
  41. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
  42. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
  43. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
  44. package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
  45. package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
  46. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
  48. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
  49. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
  50. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
  51. package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
  52. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
  53. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
  54. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
  55. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
  56. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
  57. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
  58. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
  59. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
  60. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
  61. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
  62. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
  63. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
  64. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
  65. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
  66. package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
  67. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
  68. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
  69. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
  70. package/src/components/AutomationSlice_Builder.res +2 -0
  71. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  72. package/src/components/OutboundTranslationSlice_Builder.res +1 -0
  73. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  74. package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
  75. package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
  76. package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
  77. package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
  78. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
  79. package/tests/CounterEntryPoint_OpsTest.res +115 -0
  80. package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
  81. package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
  82. package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
  83. package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
  84. package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
  85. package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
  86. package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
  87. package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
  88. package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
  89. package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
  90. package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
  91. package/tests/Platform_AdminScan_OpsTest.res +80 -0
  92. package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
  93. package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
  94. package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
  95. package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
  96. package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
  97. package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
  98. package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
  99. package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
  100. package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
  101. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
  102. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
  103. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
  104. package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
@@ -0,0 +1,105 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Effect from "effect/Effect";
8
+ import * as Stream from "effect/Stream";
9
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
10
+ import * as Projection$ReventlessCore from "@reventlessdev/reventless-core/src/Projection.res.mjs";
11
+ import * as ProjectionEntryPoint_Ops$ReventlessAws from "./ProjectionEntryPoint_Ops.res.mjs";
12
+ import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
13
+
14
+ function strOf(obj, key) {
15
+ return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
16
+ }
17
+
18
+ function pgConnectionOf(obj) {
19
+ return Stdlib_Option.map(Stdlib_Option.filter(obj["pgConnection"], j => j !== null), prim => prim);
20
+ }
21
+
22
+ function parseHandlerConfig(rawJson) {
23
+ if (rawJson === "") {
24
+ return [];
25
+ }
26
+ let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(rawJson)), {});
27
+ let base = Stdlib_Option.getOr(strOf(obj, "base"), "");
28
+ let sharedUrn = Stdlib_Option.getOr(strOf(obj, "sourceUrn"), "");
29
+ let sharedPgConnection = pgConnectionOf(obj);
30
+ return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["handlers"], Stdlib_JSON.Decode.array), []), item => Stdlib_Option.map(Stdlib_JSON.Decode.object(item), h => {
31
+ let specModule = strOf(h, "specModule");
32
+ if (specModule !== undefined) {
33
+ return {
34
+ specModule: specModule,
35
+ projectionModule: Stdlib_Option.getOr(strOf(h, "projectionModule"), ""),
36
+ queryDbTableName: Stdlib_Option.getOr(strOf(h, "queryDbTableName"), ""),
37
+ sourceUrn: Stdlib_Option.getOr(strOf(h, "sourceUrn"), ""),
38
+ stateTopicName: strOf(h, "stateTopicName"),
39
+ pgConnection: pgConnectionOf(h)
40
+ };
41
+ } else {
42
+ return {
43
+ specModule: base + Stdlib_Option.getOr(strOf(h, "s"), ""),
44
+ projectionModule: base + Stdlib_Option.getOr(strOf(h, "p"), ""),
45
+ queryDbTableName: Stdlib_Option.getOr(strOf(h, "q"), ""),
46
+ sourceUrn: Stdlib_Option.getOr(strOf(h, "u"), sharedUrn),
47
+ stateTopicName: strOf(h, "t"),
48
+ pgConnection: sharedPgConnection
49
+ };
50
+ }
51
+ }));
52
+ }
53
+
54
+ function decodeEnvelope(json) {
55
+ let fields = Stdlib_JSON.Decode.object(json);
56
+ let event = Stdlib_Option.flatMap(fields, f => f["event"]);
57
+ let tmp;
58
+ tmp = event !== undefined && event !== null ? event : json;
59
+ return {
60
+ event: tmp,
61
+ meta: Stdlib_Option.flatMap(fields, f => f["meta"]),
62
+ recordedAt: Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(fields, f => f["recordedAt"]), Stdlib_JSON.Decode.string), "")
63
+ };
64
+ }
65
+
66
+ function makeJsonEventsHandler(sliceName, eventSchema, project, queryDbOps, subIdConfig) {
67
+ return stream => Stream.runForEach(Stream.flatMap(Stream.mapEffect(stream, json => Effect.sync(() => {
68
+ try {
69
+ let match = decodeEnvelope(json);
70
+ return project({
71
+ event: S.parseJsonOrThrow(match.event, eventSchema),
72
+ meta: match.meta,
73
+ recordedAt: match.recordedAt
74
+ });
75
+ } catch (raw_exn) {
76
+ let exn = Primitive_exceptions.internalToException(raw_exn);
77
+ StreamRoutedEntryPoint_Ops$ReventlessAws.logError("failed to decode event", {
78
+ comp: "StateViewSliceRuntime",
79
+ detail: StreamRoutedEntryPoint_Ops$ReventlessAws.exnMessage(exn)
80
+ });
81
+ return [];
82
+ }
83
+ })), actions => Stream.fromIterable(actions)), action => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(sliceName, action, queryDbOps, subIdConfig)), param => {}));
84
+ }
85
+
86
+ function makeRegisteredHandler(entry, modules) {
87
+ let queryDbOps = ProjectionEntryPoint_Ops$ReventlessAws.makeQueryDbOps(entry.queryDbTableName, entry.pgConnection, entry.stateTopicName, ProjectionEntryPoint_Ops$ReventlessAws.indexesOf(modules.config), ProjectionEntryPoint_Ops$ReventlessAws.subIdFieldOf(modules.subIdConfig));
88
+ return {
89
+ handler: StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(makeJsonEventsHandler(modules.name, modules.consumedEventSchema, modules.project, queryDbOps, modules.subIdConfig)),
90
+ comp: `StateViewSlice(` + modules.name + `)`
91
+ };
92
+ }
93
+
94
+ let exnMessage = StreamRoutedEntryPoint_Ops$ReventlessAws.exnMessage;
95
+
96
+ export {
97
+ strOf,
98
+ pgConnectionOf,
99
+ parseHandlerConfig,
100
+ decodeEnvelope,
101
+ exnMessage,
102
+ makeJsonEventsHandler,
103
+ makeRegisteredHandler,
104
+ }
105
+ /* S Not a pure module */
@@ -0,0 +1,156 @@
1
+ // Typed routed dispatch boundary shared by the stream-routed Lambda entry
2
+ // points (ReadModelEntryPoint.mjs, StateViewSliceEntryPoint.mjs,
3
+ // AutomationSliceEntryPoint.mjs).
4
+ //
5
+ // The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
6
+ // all three shells route DynamoDB-stream / SQS-feed records to per-source-URN
7
+ // handler lists with per-handler log attribution — machinery the `.mjs` files
8
+ // previously duplicated verbatim. It lives here so every co-hosted component
9
+ // dispatches identically; the shells keep only their dynamic-import seams.
10
+ //
11
+ // Kept free of storage-backend imports (in particular the Postgres runtime,
12
+ // which lives in ProjectionEntryPoint_Ops): the AutomationSlice Lambda routes
13
+ // through this module but must not carry `pg`/PgRuntime in its cold-start
14
+ // graph.
15
+ //
16
+ // Runtime-pure: no `open PulumiAws` values — Pulumi appears in type positions
17
+ // only (erased).
18
+
19
+ // ── Shim bindings (HandlerFactoryHelpers.mjs) ───────────────────────────────
20
+ // The structured-log + Effect dispatch boundary shared by every deployed entry
21
+ // point.
22
+
23
+ type dispatchOpts = {
24
+ correlationId?: string,
25
+ causationId?: string,
26
+ comp?: string,
27
+ plugin?: string,
28
+ timestamp?: float,
29
+ retryCount?: int,
30
+ detail?: string,
31
+ }
32
+
33
+ @module("./HandlerFactoryHelpers.mjs")
34
+ external setRequestId: string => unit = "setRequestId"
35
+ @module("./HandlerFactoryHelpers.mjs")
36
+ external runEffect: (Effect.t<'a, 'e, 'r>, dispatchOpts) => promise<unit> = "runEffect"
37
+ @module("./HandlerFactoryHelpers.mjs")
38
+ external extractMetaField: (
39
+ array<PulumiAws.Lambda.CallbackFunction.record>,
40
+ string,
41
+ ) => option<string> = "extractMetaField"
42
+ @module("./HandlerFactoryHelpers.mjs")
43
+ external extractSentTimestamp: array<PulumiAws.Lambda.CallbackFunction.record> => option<float> =
44
+ "extractSentTimestamp"
45
+ @module("./HandlerFactoryHelpers.mjs")
46
+ external extractRetryCount: array<PulumiAws.Lambda.CallbackFunction.record> => int =
47
+ "extractRetryCount"
48
+ @module("./HandlerFactoryHelpers.mjs") @scope("log")
49
+ external logDebug: (string, dispatchOpts) => unit = "debug"
50
+ @module("./HandlerFactoryHelpers.mjs") @scope("log")
51
+ external logWarn: (string, dispatchOpts) => unit = "warn"
52
+ @module("./HandlerFactoryHelpers.mjs") @scope("log")
53
+ external logError: (string, dispatchOpts) => unit = "error"
54
+
55
+ let exnMessage = (exn: exn): string =>
56
+ exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("unknown error")
57
+
58
+ // ── Handler registry + routed dispatch boundary ─────────────────────────────
59
+
60
+ type streamHandler = (
61
+ PulumiAws.Lambda.CallbackFunction.event,
62
+ PulumiAws.Lambda.context,
63
+ ) => Effect.t<unit, string, unit>
64
+
65
+ // One Lambda hosts every component of its kind, so each handler carries the
66
+ // comp of the component it runs (and, for read models, the owning plugin —
67
+ // the shared Lambda's own name carries no single plugin identity).
68
+ type registeredHandler = {
69
+ handler: streamHandler,
70
+ comp?: string,
71
+ plugin?: string,
72
+ }
73
+
74
+ let toStreamHandler = (
75
+ handleJsonEvents: ReventlessCore.EventCollector.jsonEventsHandler,
76
+ ): streamHandler =>
77
+ (event, context) =>
78
+ EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(handleJsonEvents, event, context)
79
+
80
+ // Multiple components can share one source stream — e.g. every admin read
81
+ // model (Plugins, PluginHistory, PlatformEventGraph, UIFragmentRegistry)
82
+ // projects the Plugin aggregate's EventLog stream, and several automation
83
+ // slices can react to the same DcbEventLog. Accumulate ALL handlers per source
84
+ // URN; a plain `registry[urn] = handler` collapses them to one (whichever
85
+ // async builder wins the Promise.all race), silently dropping the rest.
86
+ let addToRegistry = (
87
+ registry: dict<array<registeredHandler>>,
88
+ sourceUrn: string,
89
+ handler: registeredHandler,
90
+ ) =>
91
+ switch registry->Dict.get(sourceUrn) {
92
+ | Some(handlers) => handlers->Array.push(handler)
93
+ | None => registry->Dict.set(sourceUrn, [handler])
94
+ }
95
+
96
+ let groupBySource = (
97
+ records: array<PulumiAws.Lambda.CallbackFunction.record>,
98
+ ): dict<array<PulumiAws.Lambda.CallbackFunction.record>> => {
99
+ let grouped = Dict.make()
100
+ records->Array.forEach(record =>
101
+ switch grouped->Dict.get(record.eventSourceARN) {
102
+ | Some(existing) => existing->Array.push(record)
103
+ | None => grouped->Dict.set(record.eventSourceARN, [record])
104
+ }
105
+ )
106
+ grouped
107
+ }
108
+
109
+ // The exported Lambda handler: group the batch by source ARN and run every
110
+ // handler registered for each source (independent storage, so concurrent is
111
+ // safe). Each handler carries its own comp so the shared Lambda's log lines
112
+ // stay separable per component.
113
+ let makeRoutedHandler = (
114
+ ~comp: string,
115
+ registryPromise: promise<dict<array<registeredHandler>>>,
116
+ ) =>
117
+ async (event: PulumiAws.Lambda.CallbackFunction.event, context: PulumiAws.Lambda.context) => {
118
+ setRequestId(context.awsRequestId)
119
+ let registry = await registryPromise
120
+ let _ =
121
+ await groupBySource(event.records)
122
+ ->Dict.toArray
123
+ ->Array.map(async ((arn, subRecords)) =>
124
+ switch registry->Dict.get(arn)->Option.filter(hs => hs->Array.length > 0) {
125
+ | Some(streamHandlers) =>
126
+ logDebug(
127
+ `found ${streamHandlers->Array.length->Int.toString} handler(s) for ${arn}`,
128
+ {comp: comp},
129
+ )
130
+ let correlationId = extractMetaField(subRecords, "correlationId")
131
+ let causationId = extractMetaField(subRecords, "causationId")
132
+ let timestamp = extractSentTimestamp(subRecords)
133
+ let retryCount = extractRetryCount(subRecords)
134
+ let subEvent: PulumiAws.Lambda.CallbackFunction.event = {records: subRecords}
135
+ let _ =
136
+ await streamHandlers
137
+ ->Array.map(registered =>
138
+ runEffect(
139
+ registered.handler(subEvent, context),
140
+ {
141
+ correlationId: ?correlationId,
142
+ causationId: ?causationId,
143
+ comp: ?registered.comp,
144
+ plugin: ?registered.plugin,
145
+ timestamp: ?timestamp,
146
+ retryCount,
147
+ },
148
+ )
149
+ )
150
+ ->Promise.all
151
+ | None => logWarn("no handler found: " ++ arn, {comp: comp})
152
+ }
153
+ )
154
+ ->Promise.all
155
+ ""
156
+ }
@@ -0,0 +1,123 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
6
+ import * as EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
7
+
8
+ function setRequestId(prim) {
9
+ HandlerFactoryHelpersMjs.setRequestId(prim);
10
+ }
11
+
12
+ function runEffect(prim0, prim1) {
13
+ return HandlerFactoryHelpersMjs.runEffect(prim0, prim1);
14
+ }
15
+
16
+ function extractMetaField(prim0, prim1) {
17
+ return HandlerFactoryHelpersMjs.extractMetaField(prim0, prim1);
18
+ }
19
+
20
+ function extractSentTimestamp(prim) {
21
+ return HandlerFactoryHelpersMjs.extractSentTimestamp(prim);
22
+ }
23
+
24
+ function extractRetryCount(prim) {
25
+ return HandlerFactoryHelpersMjs.extractRetryCount(prim);
26
+ }
27
+
28
+ function logDebug(prim0, prim1) {
29
+ HandlerFactoryHelpersMjs.log.debug(prim0, prim1);
30
+ }
31
+
32
+ function logWarn(prim0, prim1) {
33
+ HandlerFactoryHelpersMjs.log.warn(prim0, prim1);
34
+ }
35
+
36
+ function logError(prim0, prim1) {
37
+ HandlerFactoryHelpersMjs.log.error(prim0, prim1);
38
+ }
39
+
40
+ function exnMessage(exn) {
41
+ return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown error");
42
+ }
43
+
44
+ function toStreamHandler(handleJsonEvents) {
45
+ return (event, context) => EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws.handleStreamEvent(handleJsonEvents, event, context);
46
+ }
47
+
48
+ function addToRegistry(registry, sourceUrn, handler) {
49
+ let handlers = registry[sourceUrn];
50
+ if (handlers !== undefined) {
51
+ handlers.push(handler);
52
+ } else {
53
+ registry[sourceUrn] = [handler];
54
+ }
55
+ }
56
+
57
+ function groupBySource(records) {
58
+ let grouped = {};
59
+ records.forEach(record => {
60
+ let existing = grouped[record.eventSourceARN];
61
+ if (existing !== undefined) {
62
+ existing.push(record);
63
+ } else {
64
+ grouped[record.eventSourceARN] = [record];
65
+ }
66
+ });
67
+ return grouped;
68
+ }
69
+
70
+ function makeRoutedHandler(comp, registryPromise) {
71
+ return async (event, context) => {
72
+ HandlerFactoryHelpersMjs.setRequestId(context.awsRequestId);
73
+ let registry = await registryPromise;
74
+ await Promise.all(Object.entries(groupBySource(event.Records)).map(async param => {
75
+ let subRecords = param[1];
76
+ let arn = param[0];
77
+ let streamHandlers = Stdlib_Option.filter(registry[arn], hs => hs.length !== 0);
78
+ if (streamHandlers !== undefined) {
79
+ let prim0 = `found ` + streamHandlers.length.toString() + ` handler(s) for ` + arn;
80
+ HandlerFactoryHelpersMjs.log.debug(prim0, {
81
+ comp: comp
82
+ });
83
+ let correlationId = HandlerFactoryHelpersMjs.extractMetaField(subRecords, "correlationId");
84
+ let causationId = HandlerFactoryHelpersMjs.extractMetaField(subRecords, "causationId");
85
+ let timestamp = HandlerFactoryHelpersMjs.extractSentTimestamp(subRecords);
86
+ let retryCount = HandlerFactoryHelpersMjs.extractRetryCount(subRecords);
87
+ let subEvent = {
88
+ Records: subRecords
89
+ };
90
+ await Promise.all(streamHandlers.map(registered => HandlerFactoryHelpersMjs.runEffect(registered.handler(subEvent, context), {
91
+ correlationId: correlationId,
92
+ causationId: causationId,
93
+ comp: registered.comp,
94
+ plugin: registered.plugin,
95
+ timestamp: timestamp,
96
+ retryCount: retryCount
97
+ })));
98
+ return;
99
+ }
100
+ HandlerFactoryHelpersMjs.log.warn("no handler found: " + arn, {
101
+ comp: comp
102
+ });
103
+ }));
104
+ return "";
105
+ };
106
+ }
107
+
108
+ export {
109
+ setRequestId,
110
+ runEffect,
111
+ extractMetaField,
112
+ extractSentTimestamp,
113
+ extractRetryCount,
114
+ logDebug,
115
+ logWarn,
116
+ logError,
117
+ exnMessage,
118
+ toStreamHandler,
119
+ addToRegistry,
120
+ groupBySource,
121
+ makeRoutedHandler,
122
+ }
123
+ /* Stdlib_JsExn Not a pure module */
@@ -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
  }