@reventlessdev/reventless-aws 3.0.0-alpha.232 → 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 +16 -0
  2. package/package.json +8 -8
  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 +11 -1
  32. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +9 -3
  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
@@ -1,21 +1,21 @@
1
1
  // EventMapper Lambda entry point (Micro mode).
2
2
  // At cold start: reads HANDLER_CONFIG, dynamically imports Target Spec and Mappings,
3
3
  // wires EventMapper_Callback.MakeCounterHandler + MakeEventCollectorHandler.
4
+ //
5
+ // Thin untyped shell ("typed core, thin shell" —
6
+ // docs/plans/minimize-lambda-entrypoint-mjs-shell.md): this file owns ONLY the
7
+ // seams that are inherently untyped — the dynamic `import()` of the
8
+ // target-spec/mappings modules named in HANDLER_CONFIG, the shape fix-ups on
9
+ // those runtime-loaded modules (patchSpecId / patchMappingsSourceIds), and the
10
+ // MakeCounterHandler functor application consuming them. HANDLER_CONFIG
11
+ // parsing, the publish/query operations, the MakeEventCollectorHandler
12
+ // application, and the per-source dispatch boundary live type-checked in
13
+ // EventMapperEntryPoint_Ops.res.
4
14
 
5
15
  import { $$String as IdString } from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
6
- import {
7
- patchSpecId,
8
- makeQueueRef,
9
- log,
10
- runEffect,
11
- setRequestId,
12
- extractMetaField,
13
- extractSentTimestamp,
14
- extractRetryCount,
15
- } from "./HandlerFactoryHelpers.mjs";
16
- import { MakeCounterHandler, MakeEventCollectorHandler } from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
17
- import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
18
- import { handleStreamEvent } from "@reventlessdev/reventless-aws/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
16
+ import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
17
+ import { MakeCounterHandler } from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
18
+ import * as Ops from "./EventMapperEntryPoint_Ops.res.mjs";
19
19
 
20
20
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
21
21
 
@@ -33,19 +33,8 @@ function patchMappingsSourceIds(mappingsModule) {
33
33
  };
34
34
  }
35
35
 
36
- function groupBySource(records) {
37
- const dict = {};
38
- records.forEach(record => {
39
- const arn = record.eventSourceARN;
40
- const existing = dict[arn] || [];
41
- dict[arn] = existing.concat([record]);
42
- });
43
- return dict;
44
- }
45
-
46
- async function buildHandler() {
47
- const configStr = process.env["HANDLER_CONFIG"] || "{}";
48
- const config = JSON.parse(configStr);
36
+ async function build() {
37
+ const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "{}");
49
38
 
50
39
  const targetSpecModule = await dynamicImport(config.targetSpecModule);
51
40
  const mappingsModule = await dynamicImport(config.mappingsModule);
@@ -53,47 +42,14 @@ async function buildHandler() {
53
42
  const patchedTarget = patchSpecId(targetSpecModule);
54
43
  const patchedMappings = patchMappingsSourceIds(mappingsModule);
55
44
 
56
- const publishJsons = sqsPublishJsons(makeQueueRef(config.queueUrl), "SQS_FIFO");
57
- const queryEngine = {
58
- query: async () => { log.error("queryEngine not available in bundled mode", { comp: "EventMapperEntryPoint" }); return []; },
59
- get: async () => { log.error("queryEngine not available in bundled mode", { comp: "EventMapperEntryPoint" }); return undefined; },
60
- };
45
+ const counterOps = Ops.makeCounterOps(config);
46
+ const counterHandler = MakeCounterHandler(patchedTarget)(patchedMappings)(counterOps);
61
47
 
62
- const counterHandler = MakeCounterHandler(patchedTarget)(patchedMappings)({ publishJsons, queryEngine });
63
- const noopAsync = async (_) => {};
64
- const eventCollectorHandler = MakeEventCollectorHandler({
65
- publishJsons,
66
- count: noopAsync,
67
- addToCounterTarget: noopAsync,
68
- commonEventsHandler: counterHandler.commonEventsHandler,
69
- });
70
-
71
- return {
72
- streamHandler: (event, context) =>
73
- handleStreamEvent(eventCollectorHandler.handleJsonEvents, event, context),
74
- // The mapper is named after the target it maps into.
75
- comp: `EventMapper(${patchedTarget.name})`,
76
- };
48
+ // The mapper is named after the target it maps into.
49
+ return [
50
+ Ops.makeStreamHandler(counterOps, counterHandler.commonEventsHandler),
51
+ `EventMapper(${patchedTarget.name})`,
52
+ ];
77
53
  }
78
54
 
79
- const initPromise = buildHandler();
80
-
81
- export async function handler(event, context) {
82
- setRequestId(context?.awsRequestId);
83
- const { streamHandler, comp } = await initPromise;
84
- const records = event.Records || [];
85
- const grouped = groupBySource(records);
86
-
87
- await Promise.all(Object.entries(grouped).map(async ([arn, subRecords]) => {
88
- log.debug("processing " + arn, { comp: "EventMapperRuntime" });
89
- await runEffect(streamHandler({ Records: subRecords }, context), {
90
- correlationId: extractMetaField(subRecords, "correlationId"),
91
- causationId: extractMetaField(subRecords, "causationId"),
92
- comp,
93
- timestamp: extractSentTimestamp(subRecords),
94
- retryCount: extractRetryCount(subRecords),
95
- });
96
- }));
97
-
98
- return "";
99
- }
55
+ export const handler = Ops.makeHandler(build());
@@ -0,0 +1,126 @@
1
+ // Typed cold-start core for the EventMapper Lambda entry point (Micro mode).
2
+ //
3
+ // The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
4
+ // EventMapperEntryPoint.mjs keeps only the untyped seam — the dynamic
5
+ // `import()` of the target-spec/mappings modules named in HANDLER_CONFIG, the
6
+ // `patchSpecId` / mapping-Source-Id fix-ups, and the
7
+ // EventMapper_Callback.MakeCounterHandler functor application consuming them.
8
+ // HANDLER_CONFIG parsing, the publish/query operations, the
9
+ // MakeEventCollectorHandler application (a functor over an inline module
10
+ // capturing runtime values), and the per-source dispatch boundary live here,
11
+ // fully type-checked.
12
+ //
13
+ // The former shell's queryEngine stub carried wrong field names
14
+ // (`query`/`get` for QueryEngine.operations' `scan`/`query`) — a mapping
15
+ // calling `scan` crashed with "not a function" instead of the intended
16
+ // logged-error fallback. The typed record fixes the shape; both operations
17
+ // still log an error and return [] (not available in the bundled handler).
18
+
19
+ // ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
20
+ // Written by AggregateRuntime_Builder_Micro / _Micro_Async.
21
+
22
+ type handlerConfig = {
23
+ targetSpecModule?: string,
24
+ mappingsModule?: string,
25
+ queueUrl?: string,
26
+ }
27
+ @val @scope("JSON") external jsonParse: string => handlerConfig = "parse"
28
+ // A real binding (not a bare external) so the shell can import it.
29
+ let parseHandlerConfig = (raw: string): handlerConfig => jsonParse(raw)
30
+
31
+ // ── Counter-handler operations (third argument of MakeCounterHandler) ───────
32
+
33
+ type counterOps = {
34
+ publishJsons: ReventlessCore.CommandTopic.publishJsons,
35
+ queryEngine: Reventless.QueryEngine.operations,
36
+ }
37
+
38
+ let makeCounterOps = (config: handlerConfig): counterOps => {
39
+ let queueUrl = config.queueUrl->Option.getOr("")
40
+ let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
41
+ {
42
+ publishJsons: queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO),
43
+ queryEngine: {
44
+ scan: async (~readModelName as _, ~filterConfigs as _, ~limit as _) => {
45
+ StreamRoutedEntryPoint_Ops.logError(
46
+ "queryEngine not available in bundled mode",
47
+ {comp: "EventMapperEntryPoint"},
48
+ )
49
+ []
50
+ },
51
+ query: async (
52
+ ~readModelName as _,
53
+ ~key as _=?,
54
+ ~id as _,
55
+ ~subIdConfig as _=?,
56
+ ~filterConfigs as _=?,
57
+ ~ascending as _=?,
58
+ ~limit as _=?,
59
+ ) => {
60
+ StreamRoutedEntryPoint_Ops.logError(
61
+ "queryEngine not available in bundled mode",
62
+ {comp: "EventMapperEntryPoint"},
63
+ )
64
+ []
65
+ },
66
+ },
67
+ }
68
+ }
69
+
70
+ // ── Stream handler ──────────────────────────────────────────────────────────
71
+ // `commonEventsHandler` comes off the shell's MakeCounterHandler application
72
+ // (it consumes the runtime-loaded target/mappings modules); everything it
73
+ // feeds — counter no-ops, publishing, the stream wrap — is typed.
74
+
75
+ type commonEventsHandler = array<JSON.t> => promise<(
76
+ promise<array<ReventlessCore.Message.commandJson>>,
77
+ array<ReventlessCore.Counter.action>,
78
+ )>
79
+
80
+ let makeStreamHandler = (
81
+ ops: counterOps,
82
+ commonEventsHandler: commonEventsHandler,
83
+ ): StreamRoutedEntryPoint_Ops.streamHandler => {
84
+ module Handler = ReventlessCore.EventMapper_Callback.MakeEventCollectorHandler({
85
+ let publishJsons = ops.publishJsons
86
+ let count: ReventlessCore.Counter.count = async _ => ()
87
+ let addToCounterTarget: ReventlessCore.Counter.addToCounterTarget = async _ => ()
88
+ let commonEventsHandler = commonEventsHandler
89
+ })
90
+ StreamRoutedEntryPoint_Ops.toStreamHandler(Handler.handleJsonEvents)
91
+ }
92
+
93
+ // ── Dispatch boundary ───────────────────────────────────────────────────────
94
+ // Unlike the registry-routed entry points (one Lambda, many components), the
95
+ // mapper Lambda serves a single target: every source group runs the same
96
+ // handler, attributed `EventMapper(<Target>)`.
97
+
98
+ let makeHandler = (
99
+ buildPromise: promise<(StreamRoutedEntryPoint_Ops.streamHandler, string)>,
100
+ ) =>
101
+ async (event: PulumiAws.Lambda.CallbackFunction.event, context: PulumiAws.Lambda.context) => {
102
+ StreamRoutedEntryPoint_Ops.setRequestId(context.awsRequestId)
103
+ let (streamHandler, comp) = await buildPromise
104
+ let _ =
105
+ await StreamRoutedEntryPoint_Ops.groupBySource(event.records)
106
+ ->Dict.toArray
107
+ ->Array.map(async ((arn, subRecords)) => {
108
+ StreamRoutedEntryPoint_Ops.logDebug("processing " ++ arn, {comp: "EventMapperRuntime"})
109
+ let subEvent: PulumiAws.Lambda.CallbackFunction.event = {records: subRecords}
110
+ await StreamRoutedEntryPoint_Ops.runEffect(
111
+ streamHandler(subEvent, context),
112
+ {
113
+ correlationId: ?StreamRoutedEntryPoint_Ops.extractMetaField(
114
+ subRecords,
115
+ "correlationId",
116
+ ),
117
+ causationId: ?StreamRoutedEntryPoint_Ops.extractMetaField(subRecords, "causationId"),
118
+ comp,
119
+ timestamp: ?StreamRoutedEntryPoint_Ops.extractSentTimestamp(subRecords),
120
+ retryCount: StreamRoutedEntryPoint_Ops.extractRetryCount(subRecords),
121
+ },
122
+ )
123
+ })
124
+ ->Promise.all
125
+ ""
126
+ }
@@ -0,0 +1,83 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as EventMapper_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
5
+ import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
6
+ import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
7
+
8
+ function parseHandlerConfig(raw) {
9
+ return JSON.parse(raw);
10
+ }
11
+
12
+ function makeCounterOps(config) {
13
+ let queueUrl = Stdlib_Option.getOr(config.queueUrl, "");
14
+ let queue = {
15
+ id: queueUrl,
16
+ name: queueUrl,
17
+ arn: ""
18
+ };
19
+ return {
20
+ publishJsons: CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO"),
21
+ queryEngine: {
22
+ scan: async (param, param$1, param$2) => {
23
+ StreamRoutedEntryPoint_Ops$ReventlessAws.logError("queryEngine not available in bundled mode", {
24
+ comp: "EventMapperEntryPoint"
25
+ });
26
+ return [];
27
+ },
28
+ query: async (param, param$1, param$2, param$3, param$4, param$5, param$6) => {
29
+ StreamRoutedEntryPoint_Ops$ReventlessAws.logError("queryEngine not available in bundled mode", {
30
+ comp: "EventMapperEntryPoint"
31
+ });
32
+ return [];
33
+ }
34
+ }
35
+ };
36
+ }
37
+
38
+ function makeStreamHandler(ops, commonEventsHandler) {
39
+ let publishJsons = ops.publishJsons;
40
+ let count = async param => {};
41
+ let addToCounterTarget = async param => {};
42
+ let Handler = EventMapper_Callback$ReventlessCore.MakeEventCollectorHandler({
43
+ publishJsons: publishJsons,
44
+ count: count,
45
+ addToCounterTarget: addToCounterTarget,
46
+ commonEventsHandler: commonEventsHandler
47
+ });
48
+ return StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(Handler.handleJsonEvents);
49
+ }
50
+
51
+ function makeHandler(buildPromise) {
52
+ return async (event, context) => {
53
+ StreamRoutedEntryPoint_Ops$ReventlessAws.setRequestId(context.awsRequestId);
54
+ let match = await buildPromise;
55
+ let comp = match[1];
56
+ let streamHandler = match[0];
57
+ await Promise.all(Object.entries(StreamRoutedEntryPoint_Ops$ReventlessAws.groupBySource(event.Records)).map(async param => {
58
+ let subRecords = param[1];
59
+ StreamRoutedEntryPoint_Ops$ReventlessAws.logDebug("processing " + param[0], {
60
+ comp: "EventMapperRuntime"
61
+ });
62
+ let subEvent = {
63
+ Records: subRecords
64
+ };
65
+ return await StreamRoutedEntryPoint_Ops$ReventlessAws.runEffect(streamHandler(subEvent, context), {
66
+ correlationId: StreamRoutedEntryPoint_Ops$ReventlessAws.extractMetaField(subRecords, "correlationId"),
67
+ causationId: StreamRoutedEntryPoint_Ops$ReventlessAws.extractMetaField(subRecords, "causationId"),
68
+ comp: comp,
69
+ timestamp: StreamRoutedEntryPoint_Ops$ReventlessAws.extractSentTimestamp(subRecords),
70
+ retryCount: StreamRoutedEntryPoint_Ops$ReventlessAws.extractRetryCount(subRecords)
71
+ });
72
+ }));
73
+ return "";
74
+ };
75
+ }
76
+
77
+ export {
78
+ parseHandlerConfig,
79
+ makeCounterOps,
80
+ makeStreamHandler,
81
+ makeHandler,
82
+ }
83
+ /* EventMapper_Callback-ReventlessCore Not a pure module */
@@ -2,84 +2,37 @@
2
2
  // At cold start: reads HANDLER_CONFIG, dynamically imports Spec/Mappings,
3
3
  // wires ExtensionPoint_Callback.Make + CommandTopic_Callback.Make,
4
4
  // routes SQS events through handleQueueEvent.
5
-
6
- import {
7
- patchSpecId,
8
- makeQueueRef,
9
- log,
10
- runEffect,
11
- setRequestId,
12
- extractMetaField,
13
- extractSentTimestamp,
14
- extractRetryCount,
15
- } from "./HandlerFactoryHelpers.mjs";
5
+ //
6
+ // Thin untyped shell ("typed core, thin shell" —
7
+ // docs/plans/minimize-lambda-entrypoint-mjs-shell.md): this file owns ONLY the
8
+ // seams that are inherently untyped — the dynamic `import()` of the
9
+ // spec/mappings modules named in HANDLER_CONFIG, the `patchSpecId` fix-up, and
10
+ // the two functor applications consuming them. The callback config,
11
+ // HANDLER_CONFIG parsing, and the SQS dispatch boundary live type-checked in
12
+ // ExtensionPointEntryPoint_Ops.res.
13
+
14
+ import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
16
15
  import { Make as extensionPointCallbackMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs";
17
16
  import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Callback.res.mjs";
18
- import { handleQueueEvent, publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
17
+ import * as Ops from "./ExtensionPointEntryPoint_Ops.res.mjs";
19
18
 
20
19
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
21
20
 
22
- async function buildHandler() {
23
- const configStr = process.env["HANDLER_CONFIG"] || "{}";
24
- const config = JSON.parse(configStr);
21
+ async function build() {
22
+ const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "{}");
25
23
 
26
24
  const specModule = await dynamicImport(config.specModule);
27
25
  const mappingsModule = await dynamicImport(config.mappingsModule);
28
26
 
29
27
  const patchedSpec = patchSpecId(specModule);
30
28
 
31
- // Build publishToAggregates dict from env var queue URLs. The deploy-side
32
- // builder writes HANDLER_CONFIG.publishToAggregates as { aggregateName: envVarName }
33
- // (see ExtensionPointRuntime_Builder_PerExtensionPoint.res), so iterate accordingly.
34
- const publishToAggregates = {};
35
- for (const [aggName, envVarName] of Object.entries(config.publishToAggregates || {})) {
36
- const queueUrl = process.env[envVarName] || "";
37
- publishToAggregates[aggName] = sqsPublishJsons(makeQueueRef(queueUrl), "SQS_FIFO");
38
- }
39
-
40
- const callbackConfig = {
41
- publishToAggregates,
42
- commandTopicResources: [],
43
- scheduler: {
44
- create: async () => { throw new Error("Scheduler not available in bundled ExtensionPoint handler"); },
45
- delete: async () => { throw new Error("Scheduler not available in bundled ExtensionPoint handler"); },
46
- },
47
- queryEngine: {
48
- scan: async () => { throw new Error("QueryEngine not available in bundled ExtensionPoint handler"); },
49
- query: async () => { throw new Error("QueryEngine not available in bundled ExtensionPoint handler"); },
50
- },
51
- resourceNaming: { name: (n) => n, resolve: (n) => n },
52
- };
53
-
54
- const callback = extensionPointCallbackMake(callbackConfig)(patchedSpec)(mappingsModule);
29
+ const callback = extensionPointCallbackMake(Ops.makeCallbackSpec(config))(patchedSpec)(mappingsModule);
55
30
  const commandTopicCallback = commandTopicCallbackMake(patchedSpec)({
56
31
  Spec: patchedSpec,
57
32
  commandsHandler: callback.handleIncomingCommands,
58
33
  });
59
34
 
60
- const resolvedQueue = makeQueueRef(config.queueUrl);
61
- // Pair the handler with the extension point it serves so the invocation's log
62
- // lines name the component, not just this Lambda.
63
- return {
64
- sqsHandler: handleQueueEvent(resolvedQueue, commandTopicCallback.handleJsonCommands),
65
- comp: `ExtensionPoint(${patchedSpec.name})`,
66
- };
35
+ return Ops.makeBuilt(config, patchedSpec.name, commandTopicCallback.handleJsonCommands);
67
36
  }
68
37
 
69
- const initPromise = buildHandler();
70
-
71
- export async function handler(event, context) {
72
- setRequestId(context?.awsRequestId);
73
- const { sqsHandler, comp } = await initPromise;
74
- const records = event.Records || [];
75
-
76
- log.debug("processing " + records.length.toString() + " record(s)", { comp: "ExtensionPointRuntime" });
77
- await runEffect(sqsHandler(event, context), {
78
- correlationId: extractMetaField(records, "correlationId"),
79
- causationId: extractMetaField(records, "causationId"),
80
- comp,
81
- timestamp: extractSentTimestamp(records),
82
- retryCount: extractRetryCount(records),
83
- });
84
- return "";
85
- }
38
+ export const handler = Ops.makeHandler(build());
@@ -0,0 +1,150 @@
1
+ // Typed cold-start core for the (per-extension-point) ExtensionPoint Lambda
2
+ // entry point.
3
+ //
4
+ // The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
5
+ // ExtensionPointEntryPoint.mjs keeps only the untyped seam — the dynamic
6
+ // `import()` of the spec/mappings modules named in HANDLER_CONFIG, the
7
+ // `patchSpecId` fix-up, and the ExtensionPoint_Callback.Make /
8
+ // CommandTopic_Callback.Make functor applications consuming them. The callback
9
+ // config (publish dict, scheduler/query stubs, resource naming), HANDLER_CONFIG
10
+ // parsing, and the SQS dispatch boundary live here, fully type-checked.
11
+ //
12
+ // The former shell's stub records carried wrong field names (`create`/`delete`
13
+ // for Scheduler.operations' `createSchedule`/`deleteSchedule`, `name`/`resolve`
14
+ // for ResourceNaming.operations' `validateName`/`urnName`) — a mapping touching
15
+ // them crashed with "not a function" instead of the intended error. The typed
16
+ // records fix the shapes; scheduler/queryEngine still throw by design (not
17
+ // available in the bundled handler).
18
+
19
+ @val @scope("process") external processEnv: dict<string> = "env"
20
+
21
+ // ── Shim bindings (HandlerFactoryHelpers.mjs), typed for SQS records ────────
22
+ @module("./HandlerFactoryHelpers.mjs")
23
+ external setRequestId: string => unit = "setRequestId"
24
+ @module("./HandlerFactoryHelpers.mjs")
25
+ external runEffect: (
26
+ Effect.t<'a, 'e, 'r>,
27
+ StreamRoutedEntryPoint_Ops.dispatchOpts,
28
+ ) => promise<unit> = "runEffect"
29
+ @module("./HandlerFactoryHelpers.mjs")
30
+ external extractMetaField: (array<PulumiAws.SQS.Queue.record>, string) => option<string> =
31
+ "extractMetaField"
32
+ @module("./HandlerFactoryHelpers.mjs")
33
+ external extractSentTimestamp: array<PulumiAws.SQS.Queue.record> => option<float> =
34
+ "extractSentTimestamp"
35
+ @module("./HandlerFactoryHelpers.mjs")
36
+ external extractRetryCount: array<PulumiAws.SQS.Queue.record> => int = "extractRetryCount"
37
+ @module("./HandlerFactoryHelpers.mjs") @scope("log")
38
+ external logDebug: (string, StreamRoutedEntryPoint_Ops.dispatchOpts) => unit = "debug"
39
+
40
+ // ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
41
+ // Written by ExtensionPointRuntime_Builder_PerExtensionPoint; all fields
42
+ // optional so a partial config degrades like the former shell's `|| ""` reads.
43
+
44
+ type handlerConfig = {
45
+ specModule?: string,
46
+ mappingsModule?: string,
47
+ queueUrl?: string,
48
+ // aggregateName → env-var name holding the aggregate's cmd-topic SQS URL.
49
+ publishToAggregates?: dict<string>,
50
+ }
51
+ @val @scope("JSON") external jsonParse: string => handlerConfig = "parse"
52
+ // A real binding (not a bare external) so the shell can import it.
53
+ let parseHandlerConfig = (raw: string): handlerConfig => jsonParse(raw)
54
+
55
+ // ── Callback config (first argument of ExtensionPoint_Callback.Make) ────────
56
+ // The record's field names/types mirror ExtensionPoint_Callback's `Spec` module
57
+ // type — the compiled functor consumes it as a plain object.
58
+
59
+ type callbackSpec = {
60
+ publishToAggregates: dict<ReventlessCore.CommandTopic.publishJsons>,
61
+ commandTopicResources: array<ReventlessInfra.Adapter.resolvedResource>,
62
+ scheduler: ReventlessCore.Scheduler.operations,
63
+ queryEngine: Reventless.QueryEngine.operations,
64
+ resourceNaming: ReventlessInfra.ResourceNaming.operations,
65
+ }
66
+
67
+ let makeCallbackSpec = (config: handlerConfig): callbackSpec => {
68
+ publishToAggregates: config.publishToAggregates
69
+ ->Option.getOr(Dict.make())
70
+ ->Dict.toArray
71
+ ->Array.map(((aggName, envVarName)) => {
72
+ let queueUrl = processEnv->Dict.get(envVarName)->Option.getOr("")
73
+ let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
74
+ (aggName, queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO))
75
+ })
76
+ ->Dict.fromArray,
77
+ commandTopicResources: [],
78
+ scheduler: {
79
+ createSchedule: (_, _) =>
80
+ JsError.throwWithMessage("Scheduler not available in bundled ExtensionPoint handler"),
81
+ deleteSchedule: (_, _) =>
82
+ JsError.throwWithMessage("Scheduler not available in bundled ExtensionPoint handler"),
83
+ },
84
+ queryEngine: {
85
+ scan: (~readModelName as _, ~filterConfigs as _, ~limit as _) =>
86
+ JsError.throwWithMessage("QueryEngine not available in bundled ExtensionPoint handler"),
87
+ query: (
88
+ ~readModelName as _,
89
+ ~key as _=?,
90
+ ~id as _,
91
+ ~subIdConfig as _=?,
92
+ ~filterConfigs as _=?,
93
+ ~ascending as _=?,
94
+ ~limit as _=?,
95
+ ) => JsError.throwWithMessage("QueryEngine not available in bundled ExtensionPoint handler"),
96
+ },
97
+ resourceNaming: {
98
+ validateName: n => n,
99
+ urnName: n => n,
100
+ },
101
+ }
102
+
103
+ // ── SQS dispatch boundary ───────────────────────────────────────────────────
104
+ // The shell resolves the spec module, applies the functors, and hands the
105
+ // typed jsonCommandsHandler back here; `comp` pairs the invocation's log lines
106
+ // with the extension point the Lambda serves, not just the Lambda itself.
107
+
108
+ type built = {
109
+ comp: string,
110
+ sqsHandler: (
111
+ PulumiAws.SQS.Queue.event,
112
+ PulumiAws.Lambda.context,
113
+ ) => Effect.t<unit, string, unit>,
114
+ }
115
+
116
+ let makeBuilt = (
117
+ config: handlerConfig,
118
+ specName: string,
119
+ handleJsonCommands: ReventlessCore.CommandTopic.jsonCommandsHandler,
120
+ ): built => {
121
+ let queueUrl = config.queueUrl->Option.getOr("")
122
+ let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
123
+ {
124
+ comp: `ExtensionPoint(${specName})`,
125
+ sqsHandler: CommandTopicChannel_SQS_Runtime.handleQueueEvent(queue, handleJsonCommands),
126
+ }
127
+ }
128
+
129
+ let makeHandler = (buildPromise: promise<built>) =>
130
+ async (event: PulumiAws.SQS.Queue.event, context: PulumiAws.Lambda.context) => {
131
+ setRequestId(context.awsRequestId)
132
+ let {comp, sqsHandler} = await buildPromise
133
+ let records = event.records
134
+
135
+ logDebug(
136
+ `processing ${records->Array.length->Int.toString} record(s)`,
137
+ {comp: "ExtensionPointRuntime"},
138
+ )
139
+ await runEffect(
140
+ sqsHandler(event, context),
141
+ {
142
+ correlationId: ?extractMetaField(records, "correlationId"),
143
+ causationId: ?extractMetaField(records, "causationId"),
144
+ comp,
145
+ timestamp: ?extractSentTimestamp(records),
146
+ retryCount: extractRetryCount(records),
147
+ },
148
+ )
149
+ ""
150
+ }