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

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 (33) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +5 -5
  3. package/src/adapter/Geocoder/Geocoder_AwsLocation.res +60 -141
  4. package/src/adapter/Geocoder/Geocoder_AwsLocation.res.mjs +52 -118
  5. package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res +137 -0
  6. package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res.mjs +124 -0
  7. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +51 -6
  8. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +60 -0
  9. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +29 -1
  10. package/src/adapter/Runtime/HeartbeatEntryPoint.res +44 -0
  11. package/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs +47 -0
  12. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +234 -0
  13. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs +226 -0
  14. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +49 -4
  15. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +22 -4
  16. package/src/adapter/Upload/Upload_Presign_S3.res +65 -164
  17. package/src/adapter/Upload/Upload_Presign_S3.res.mjs +56 -119
  18. package/src/adapter/Upload/Upload_Presign_S3_Ops.res +157 -0
  19. package/src/adapter/Upload/Upload_Presign_S3_Ops.res.mjs +120 -0
  20. package/src/components/InboundTranslationSlice_Builder.res +27 -1
  21. package/src/components/InboundTranslationSlice_Builder.res.mjs +15 -2
  22. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +1 -1
  23. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
  24. package/src/plugin/runtime/PluginRuntime_Builder.res +59 -1
  25. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +49 -2
  26. package/tests/DcbInboundTranslationRoutingTest.res +73 -0
  27. package/tests/DcbInboundTranslationRoutingTest.res.mjs +71 -0
  28. package/tests/EpInboundTestSlice.res +19 -0
  29. package/tests/EpInboundTestSlice.res.mjs +31 -0
  30. package/tests/EpInboundTestSliceTranslation.res +13 -0
  31. package/tests/EpInboundTestSliceTranslation.res.mjs +33 -0
  32. package/src/adapter/Runtime/HeartbeatEntryPoint.mjs +0 -38
  33. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +0 -127
@@ -1,6 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Aws from "@pulumi/aws";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
6
  import * as Pulumi from "@pulumi/pulumi";
6
7
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
@@ -50,6 +51,38 @@ function registerStateChangeSliceSpec(specPath, behaviorPath) {
50
51
  });
51
52
  }
52
53
 
54
+ let registeredInboundSlices = {};
55
+
56
+ function registerInboundTranslationSliceSpec(specName, specPath, translationPath) {
57
+ let reg = registeredInboundSlices[specName];
58
+ if (reg !== undefined) {
59
+ registeredInboundSlices[specName] = {
60
+ specPath: specPath,
61
+ translationPath: translationPath,
62
+ auditTableName: reg.auditTableName
63
+ };
64
+ } else {
65
+ registeredInboundSlices[specName] = {
66
+ specPath: specPath,
67
+ translationPath: translationPath,
68
+ auditTableName: undefined
69
+ };
70
+ }
71
+ }
72
+
73
+ function registerInboundAuditTableName(specName, tableName) {
74
+ let reg = registeredInboundSlices[specName];
75
+ if (reg !== undefined) {
76
+ reg.auditTableName = tableName;
77
+ } else {
78
+ registeredInboundSlices[specName] = {
79
+ specPath: "",
80
+ translationPath: "",
81
+ auditTableName: tableName
82
+ };
83
+ }
84
+ }
85
+
53
86
  function registerDcbConfig(pluginName, dcbTableName, stateChangeSliceModulePathsOpt, param) {
54
87
  let stateChangeSliceModulePaths = stateChangeSliceModulePathsOpt !== undefined ? stateChangeSliceModulePathsOpt : [];
55
88
  dcbConfigRef.contents = {
@@ -422,7 +455,7 @@ function Make(EventCollectorChannel) {
422
455
  envVars["EP_QUEUE_URL"] = epQueueUrl;
423
456
  envVars["PLUGIN_ID"] = Pulumi.output(hbConfig.pluginId);
424
457
  envVars["HEARTBEAT_TIMEOUT"] = Pulumi.output(hbConfig.heartbeatTimeout.toString());
425
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs", {}, undefined);
458
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs", {}, undefined);
426
459
  connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "Scheduler", "Scheduler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
427
460
  };
428
461
  let forDcbCommandTopic = (param, connect, memorySizeOpt, timeoutOpt, dcbCommandTopic) => {
@@ -433,7 +466,18 @@ function Make(EventCollectorChannel) {
433
466
  param.specPath,
434
467
  param.behaviorPath
435
468
  ]);
436
- StateChangeSliceRuntime_Builder_Single$ReventlessAws.forDcbCommandTopic(slicePaths, dcbConfig.dcbTableName, dcbConfig.pluginName, syncStateChangesConfigRef.contents, asyncStateChangesConfigRef.contents, memorySize, timeout, connect, dcbCommandTopic);
469
+ let inboundSlices = Stdlib_Array.filterMap(Object.values(registeredInboundSlices), reg => {
470
+ if (reg.specPath === "") {
471
+ return;
472
+ } else {
473
+ return {
474
+ specPath: reg.specPath,
475
+ translationPath: reg.translationPath,
476
+ auditTableName: reg.auditTableName
477
+ };
478
+ }
479
+ });
480
+ StateChangeSliceRuntime_Builder_Single$ReventlessAws.forDcbCommandTopic(slicePaths, inboundSlices, dcbConfig.dcbTableName, dcbConfig.pluginName, syncStateChangesConfigRef.contents, asyncStateChangesConfigRef.contents, memorySize, timeout, connect, dcbCommandTopic);
437
481
  };
438
482
  let finish = () => {};
439
483
  return {
@@ -452,6 +496,9 @@ export {
452
496
  dcbConfigRef,
453
497
  registeredSliceModulePaths,
454
498
  registerStateChangeSliceSpec,
499
+ registeredInboundSlices,
500
+ registerInboundTranslationSliceSpec,
501
+ registerInboundAuditTableName,
455
502
  registerDcbConfig,
456
503
  registerDcbTableName,
457
504
  heartbeatConfigRef,
@@ -0,0 +1,73 @@
1
+ // CI routing guard for the DCB CommandTopic Lambda's Route 0 (InboundTranslation).
2
+ //
3
+ // The AppSync resolver for an InboundTranslationSlice mutation invokes the shared
4
+ // DCB command Lambda with `{__inboundTranslation, fieldName, arguments}` — no
5
+ // `command`, no `Records`. Before Route 0 existed the payload fell through to the
6
+ // SQS route and crashed on `event.records` being undefined ("Cannot read
7
+ // properties of undefined (reading 'length')"), so every inbound mutation 500'd
8
+ // on a deployed stack (docs/plans/done/aws-inbound-translation-lambda-routing.md).
9
+ // The gap that let this ship: `__inboundTranslation` was only ever asserted on the
10
+ // sending side (the resolver-template test). This pins the receiving end.
11
+ //
12
+ // Drives the real `buildHandlersForConfig` with an inbound slice module and a stub
13
+ // loader, then dispatches the payload exactly as `handler`'s Route 0 does. The
14
+ // fixture translation rejects its input, so the receiver returns CommandRejected
15
+ // inline without touching SQS or DynamoDB — no Docker needed, runs in CI.
16
+
17
+ open JestGlobals
18
+
19
+ let routeInbound: JSON.t => promise<JSON.t> = %raw(`
20
+ async (event) => {
21
+ const { buildHandlersForConfig } = await import(
22
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs"
23
+ );
24
+ const loadModule = async (specifier) => {
25
+ if (specifier === "ep-inbound-test://spec") return await import("./EpInboundTestSlice.res.mjs");
26
+ if (specifier === "ep-inbound-test://translation") return await import("./EpInboundTestSliceTranslation.res.mjs");
27
+ throw new Error("unknown test specifier: " + specifier);
28
+ };
29
+ const config = {
30
+ pluginName: "EpInboundTestPlugin",
31
+ dcbEventLogTableName: "ep-inbound-test-table",
32
+ stateChangeSliceModules: [],
33
+ queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/ep-inbound-test-queue",
34
+ inboundTranslationSliceModules: [
35
+ { spec: "ep-inbound-test://spec", translation: "ep-inbound-test://translation", auditTableName: null },
36
+ ],
37
+ };
38
+ const [,,,, inboundReceivers] = await buildHandlersForConfig(config, { loadModule });
39
+ // Mirror handler's Route 0 dispatch.
40
+ const receiver = (inboundReceivers || {})[event.fieldName];
41
+ if (receiver === undefined) throw new Error("no inbound receiver for " + event.fieldName);
42
+ return await receiver(event.arguments);
43
+ }
44
+ `)
45
+
46
+ let inboundEvent = (~fieldName, ~currency): JSON.t => {
47
+ let arguments = Dict.fromArray([
48
+ ("sku", "SKU-1"->JSON.Encode.string),
49
+ ("currency", currency->JSON.Encode.string),
50
+ ])
51
+ Dict.fromArray([
52
+ ("__inboundTranslation", true->JSON.Encode.bool),
53
+ ("fieldName", fieldName->JSON.Encode.string),
54
+ ("arguments", arguments->JSON.Encode.object),
55
+ ])->JSON.Encode.object
56
+ }
57
+
58
+ describe("DcbCommandTopicEntryPoint Route 0 (InboundTranslation)", () => {
59
+ test(
60
+ "routes an __inboundTranslation payload to the slice's receive and encodes the outcome",
61
+ async () => {
62
+ // fieldName is `${pluginName}_${specName}` — the same string
63
+ // Api_Naming.sliceMutationField produces at deploy time.
64
+ let event = inboundEvent(~fieldName="EpInboundTestPlugin_EpInboundTest", ~currency="EUR")
65
+ let outcome = await routeInbound(event)
66
+ let s = outcome->JSON.stringifyAny->Option.getOr("<unserializable>")
67
+ // A serializable outcome at all proves the payload no longer crashes on the
68
+ // SQS route; the rejection proves it ran the slice's translate.
69
+ expect(s->String.includes("CommandRejected"))->toBe(true)
70
+ expect(s->String.includes("Unsupported currency"))->toBe(true)
71
+ },
72
+ )
73
+ })
@@ -0,0 +1,71 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+
5
+ let routeInbound = (async (event) => {
6
+ const { buildHandlersForConfig } = await import(
7
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs"
8
+ );
9
+ const loadModule = async (specifier) => {
10
+ if (specifier === "ep-inbound-test://spec") return await import("./EpInboundTestSlice.res.mjs");
11
+ if (specifier === "ep-inbound-test://translation") return await import("./EpInboundTestSliceTranslation.res.mjs");
12
+ throw new Error("unknown test specifier: " + specifier);
13
+ };
14
+ const config = {
15
+ pluginName: "EpInboundTestPlugin",
16
+ dcbEventLogTableName: "ep-inbound-test-table",
17
+ stateChangeSliceModules: [],
18
+ queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/ep-inbound-test-queue",
19
+ inboundTranslationSliceModules: [
20
+ { spec: "ep-inbound-test://spec", translation: "ep-inbound-test://translation", auditTableName: null },
21
+ ],
22
+ };
23
+ const [,,,, inboundReceivers] = await buildHandlersForConfig(config, { loadModule });
24
+ // Mirror handler's Route 0 dispatch.
25
+ const receiver = (inboundReceivers || {})[event.fieldName];
26
+ if (receiver === undefined) throw new Error("no inbound receiver for " + event.fieldName);
27
+ return await receiver(event.arguments);
28
+ });
29
+
30
+ function inboundEvent(fieldName, currency) {
31
+ let $$arguments = Object.fromEntries([
32
+ [
33
+ "sku",
34
+ "SKU-1"
35
+ ],
36
+ [
37
+ "currency",
38
+ currency
39
+ ]
40
+ ]);
41
+ return Object.fromEntries([
42
+ [
43
+ "__inboundTranslation",
44
+ true
45
+ ],
46
+ [
47
+ "fieldName",
48
+ fieldName
49
+ ],
50
+ [
51
+ "arguments",
52
+ $$arguments
53
+ ]
54
+ ]);
55
+ }
56
+
57
+ globalThis.describe("DcbCommandTopicEntryPoint Route 0 (InboundTranslation)", () => {
58
+ globalThis.test("routes an __inboundTranslation payload to the slice's receive and encodes the outcome", async () => {
59
+ let event = inboundEvent("EpInboundTestPlugin_EpInboundTest", "EUR");
60
+ let outcome = await routeInbound(event);
61
+ let s = Stdlib_Option.getOr(JSON.stringify(outcome), "<unserializable>");
62
+ globalThis.expect(s.includes("CommandRejected")).toBe(true);
63
+ globalThis.expect(s.includes("Unsupported currency")).toBe(true);
64
+ });
65
+ });
66
+
67
+ export {
68
+ routeInbound,
69
+ inboundEvent,
70
+ }
71
+ /* routeInbound Not a pure module */
@@ -0,0 +1,19 @@
1
+ // Minimal InboundTranslationSlice spec fixture for the DCB entry-point Route 0
2
+ // routing test (DcbInboundTranslationRoutingTest). Hand-written — not a plugin
3
+ // component, so no folder-based ppx; only the fields
4
+ // InboundTranslationSlice_Callback.Make reads at runtime are provided.
5
+
6
+ let name = "EpInboundTest"
7
+ let moduleUrl = "ep-inbound-test://spec"
8
+
9
+ @schema
10
+ type externalInput = {
11
+ sku: string,
12
+ currency: string,
13
+ }
14
+
15
+ @schema
16
+ type command = AddThing({thingId: string})
17
+
18
+ let targetName = "AddThing"
19
+ let externalSystem: option<string> = Some("TestFeed")
@@ -0,0 +1,31 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+
5
+ let externalInputSchema = S.schema(s => ({
6
+ sku: s.m(S.string),
7
+ currency: s.m(S.string)
8
+ }));
9
+
10
+ let commandSchema = S.schema(s => ({
11
+ TAG: "AddThing",
12
+ thingId: s.m(S.string)
13
+ }));
14
+
15
+ let name = "EpInboundTest";
16
+
17
+ let moduleUrl = "ep-inbound-test://spec";
18
+
19
+ let targetName = "AddThing";
20
+
21
+ let externalSystem = "TestFeed";
22
+
23
+ export {
24
+ name,
25
+ moduleUrl,
26
+ externalInputSchema,
27
+ commandSchema,
28
+ targetName,
29
+ externalSystem,
30
+ }
31
+ /* externalInputSchema Not a pure module */
@@ -0,0 +1,13 @@
1
+ // Translation fixture paired with EpInboundTestSlice. Rejects non-USD input so the
2
+ // routing test exercises the receive reject path — no command is published, so the
3
+ // test needs neither SQS nor DynamoDB.
4
+
5
+ module Spec = EpInboundTestSlice
6
+ open Spec
7
+
8
+ let moduleUrl = "ep-inbound-test://translation"
9
+
10
+ let translate = (input: externalInput): result<array<(string, command)>, string> =>
11
+ input.currency !== "USD"
12
+ ? Error("Unsupported currency: " ++ input.currency)
13
+ : Ok([(input.sku, AddThing({thingId: input.sku}))])
@@ -0,0 +1,33 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function translate(input) {
5
+ if (input.currency !== "USD") {
6
+ return {
7
+ TAG: "Error",
8
+ _0: "Unsupported currency: " + input.currency
9
+ };
10
+ } else {
11
+ return {
12
+ TAG: "Ok",
13
+ _0: [[
14
+ input.sku,
15
+ {
16
+ TAG: "AddThing",
17
+ thingId: input.sku
18
+ }
19
+ ]]
20
+ };
21
+ }
22
+ }
23
+
24
+ let Spec;
25
+
26
+ let moduleUrl = "ep-inbound-test://translation";
27
+
28
+ export {
29
+ Spec,
30
+ moduleUrl,
31
+ translate,
32
+ }
33
+ /* No side effect */
@@ -1,38 +0,0 @@
1
- // Heartbeat Lambda handler.
2
- // Publishes a Heartbeat(timeout) command to the PluginExtensionPoint CommandTopic.
3
- // Triggered by CloudWatch Events on a schedule.
4
-
5
- import { reverseConvertToJsonOrThrow } from "sury/src/S.res.mjs";
6
- import { makeQueueRef } from "./HandlerFactoryHelpers.mjs";
7
- import { uuid } from "@reventlessdev/reventless-core/src/Message.res.mjs";
8
- import { name as pluginExtensionPointName, commandSchema } from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
9
- import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
10
-
11
- // === Initialize eagerly at module load (Lambda cold start) ===
12
-
13
- const epQueueUrl = process.env["EP_QUEUE_URL"] || "";
14
- const pluginId = process.env["PLUGIN_ID"] || "";
15
- const timeout = parseInt(process.env["HEARTBEAT_TIMEOUT"], 10) || 10;
16
-
17
- const publishJsons = sqsPublishJsons(makeQueueRef(epQueueUrl), "SQS_FIFO");
18
-
19
- // === Exported handler ===
20
-
21
- export async function handler(_event, _context) {
22
- const msgId = uuid();
23
- const commandJson = reverseConvertToJsonOrThrow({ TAG: "Heartbeat", _0: timeout }, commandSchema);
24
- const message = {
25
- id: pluginId,
26
- meta: {
27
- service: pluginExtensionPointName,
28
- time: new Date().toISOString(),
29
- ip: "",
30
- user: "Heartbeat",
31
- msgId,
32
- correlationId: msgId,
33
- },
34
- commandJson,
35
- };
36
- await publishJsons([message]);
37
- return "";
38
- }
@@ -1,127 +0,0 @@
1
- // PluginExtensionPoint Lambda entry point.
2
- // All imports are framework packages — no user modules needed.
3
- // Handles Heartbeat, Cloner, and other plugin-level extension point commands.
4
-
5
- import {
6
- patchSpecId,
7
- makeQueueRef,
8
- scanByTableName,
9
- log,
10
- runEffect,
11
- setRequestId,
12
- extractMetaField,
13
- extractSentTimestamp,
14
- extractRetryCount,
15
- } from "./HandlerFactoryHelpers.mjs";
16
- import { sendMessage } from "@reventlessdev/reventless-aws/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs";
17
- import * as PluginExtensionPointSpec from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
18
- import { Make as pluginEPPluginMake } from "@reventlessdev/reventless-core/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs";
19
- import { Make as extensionPointCallbackMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs";
20
- import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Callback.res.mjs";
21
- import { handleQueueEvent, publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
22
- import { createSchedule as cwCreateSchedule, deleteSchedule as cwDeleteSchedule } from "@reventlessdev/reventless-aws/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
23
-
24
- function buildHandler() {
25
- const configStr = process.env["HANDLER_CONFIG"] || "{}";
26
- const config = JSON.parse(configStr);
27
-
28
- const patchedSpec = patchSpecId(PluginExtensionPointSpec);
29
-
30
- // Reconstruct runtimeOps — only sendMessageToChannel is used by this
31
- // Lambda's incoming-command path (ForwardCommand). Cross-plugin
32
- // subscribe / unsubscribe directives were retired in Phase 3 Step 3.
33
- const runtimeOps = {
34
- messagePublish: { sendMessageToChannel: sendMessage },
35
- };
36
-
37
- // Instantiate Plugin EP mapping. updateApiSchema and manageSubscriptions are
38
- // admin-only hooks; this Lambda only handles incoming commands (Heartbeat,
39
- // ForwardCommand) so they stay undefined here.
40
- //
41
- // `environment` prefixes the disconnect schedule's EventBridge rule name, so it
42
- // must be stable across deploys and unique per stack. The stack name is both;
43
- // AWS_LAMBDA_FUNCTION_NAME is neither — it carries a content hash, so replacing
44
- // this Lambda would orphan every outstanding rule the previous generation
45
- // created. EventCollectorEntryPoint instantiates the same EP module and must
46
- // agree, or one Lambda creates rules the other cannot delete.
47
- const environment = process.env["Environment"] || "unknown";
48
- const pluginModule = pluginEPPluginMake({
49
- runtimeOps,
50
- environment,
51
- updateApiSchema: undefined,
52
- manageSubscriptions: undefined,
53
- });
54
- const mappingsModule = { mappings: [pluginModule.Mapping] };
55
-
56
- // Reconstruct publishToAggregates. The deploy-side builder writes
57
- // HANDLER_CONFIG.publishToAggregates as { aggregateName: envVarName }
58
- // (see PluginExtensionPointRuntime_Builder.res), so iterate accordingly.
59
- const publishToAggregates = {};
60
- for (const [aggName, envVarName] of Object.entries(config.publishToAggregates || {})) {
61
- const queueUrl = process.env[envVarName] || "";
62
- publishToAggregates[aggName] = sqsPublishJsons(makeQueueRef(queueUrl), "SQS_FIFO");
63
- }
64
-
65
- // Reconstruct queryEngine
66
- const queryEngine = {
67
- scan: (readModelName, filterConfigs, limit) => scanByTableName(config.pluginReadModelTableName, filterConfigs, limit),
68
- query: async () => { throw new Error("QueryEngine.query not available in bundled Plugin EP handler"); },
69
- };
70
-
71
- // Reconstruct scheduler
72
- const scheduler = {
73
- createSchedule: cwCreateSchedule(config.schedulerRoleArn),
74
- deleteSchedule: cwDeleteSchedule,
75
- };
76
-
77
- // CommandTopic resources for scheduler targets
78
- const commandTopicResources = config.schedulerQueueArn !== ""
79
- ? [{
80
- name: config.schedulerQueueName,
81
- id: config.schedulerQueueName,
82
- urn: config.schedulerQueueArn,
83
- service: "unknown",
84
- resourceInfo: "NoInfo",
85
- role: "",
86
- region: "",
87
- resourceType: "",
88
- configuration: {},
89
- tags: {},
90
- }]
91
- : [];
92
-
93
- const invalidNameChars = /[^.\-_a-zA-Z0-9]/g;
94
- const resourceNaming = {
95
- validateName: (n) => n.replace(invalidNameChars, "_"),
96
- urnName: (arn) => { const parts = arn.split(":"); return parts[5] || "unknown"; },
97
- };
98
-
99
- const callbackConfig = { publishToAggregates, commandTopicResources, scheduler, queryEngine, resourceNaming };
100
- const callback = extensionPointCallbackMake(callbackConfig)(patchedSpec)(mappingsModule);
101
- const commandTopicCallback = commandTopicCallbackMake(patchedSpec)({
102
- Spec: patchedSpec,
103
- commandsHandler: callback.handleIncomingCommands,
104
- });
105
-
106
- return {
107
- sqsHandler: handleQueueEvent(makeQueueRef(config.queueUrl), commandTopicCallback.handleJsonCommands),
108
- comp: `ExtensionPoint(${patchedSpec.name})`,
109
- };
110
- }
111
-
112
- const { sqsHandler, comp } = buildHandler();
113
-
114
- export async function handler(event, context) {
115
- setRequestId(context?.awsRequestId);
116
- const records = event.Records || [];
117
-
118
- log.debug("processing " + records.length.toString() + " record(s)", { comp: "PluginExtensionPointRuntime" });
119
- await runEffect(sqsHandler(event, context), {
120
- correlationId: extractMetaField(records, "correlationId"),
121
- causationId: extractMetaField(records, "causationId"),
122
- comp,
123
- timestamp: extractSentTimestamp(records),
124
- retryCount: extractRetryCount(records),
125
- });
126
- return "";
127
- }