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

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.
@@ -0,0 +1,120 @@
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 Nodecrypto from "node:crypto";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as ClientS3 from "@aws-sdk/client-s3";
7
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
8
+ import * as S3RequestPresigner from "@aws-sdk/s3-request-presigner";
9
+
10
+ function getEnv(k) {
11
+ let v = process.env[k];
12
+ if (v !== undefined && v !== "") {
13
+ return v;
14
+ }
15
+ }
16
+
17
+ function corsHeaders() {
18
+ return Object.fromEntries([
19
+ [
20
+ "content-type",
21
+ "application/json"
22
+ ],
23
+ [
24
+ "access-control-allow-origin",
25
+ "*"
26
+ ],
27
+ [
28
+ "access-control-allow-methods",
29
+ "POST,OPTIONS"
30
+ ],
31
+ [
32
+ "access-control-allow-headers",
33
+ "*"
34
+ ]
35
+ ]);
36
+ }
37
+
38
+ function decodeJwtSub(header) {
39
+ try {
40
+ let token = header.startsWith("Bearer ") ? header.slice(7, header.length) : header;
41
+ let payload = token.split(".")[1];
42
+ if (payload === undefined) {
43
+ return;
44
+ }
45
+ let base64 = payload.replaceAll("-", "+").replaceAll("_", "/");
46
+ let obj = JSON.parse(Buffer.from(base64, "base64").toString("utf8"));
47
+ if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
48
+ return Stdlib_Option.flatMap(obj["sub"], Stdlib_JSON.Decode.string);
49
+ } else {
50
+ return;
51
+ }
52
+ } catch (exn) {
53
+ return;
54
+ }
55
+ }
56
+
57
+ function identityPrefix(event) {
58
+ let authHeader = Stdlib_Option.flatMap(event.headers, h => Stdlib_Option.orElse(h["authorization"], h["Authorization"]));
59
+ let sub = Stdlib_Option.flatMap(authHeader, decodeJwtSub);
60
+ if (sub !== undefined) {
61
+ return sub + `/`;
62
+ } else {
63
+ return "";
64
+ }
65
+ }
66
+
67
+ async function handler(event) {
68
+ try {
69
+ let bucket = Stdlib_Option.getOr(getEnv("UPLOAD_BUCKET"), "");
70
+ let parsed = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(Stdlib_Option.getOr(event.body, "{}"))), {});
71
+ let fileName = Stdlib_Option.getOr(Stdlib_Option.flatMap(parsed["fileName"], Stdlib_JSON.Decode.string), "upload");
72
+ let contentType = Stdlib_Option.flatMap(parsed["contentType"], Stdlib_JSON.Decode.string);
73
+ let servedPrefix = Stdlib_Option.getOr(getEnv("SERVED_PREFIX"), "uploads");
74
+ let key = servedPrefix + `/` + identityPrefix(event) + Nodecrypto.randomUUID() + `/` + fileName;
75
+ let client = new ClientS3.S3Client();
76
+ let command = new ClientS3.PutObjectCommand({
77
+ Bucket: bucket,
78
+ Key: key,
79
+ ContentType: contentType
80
+ });
81
+ let uploadUrl = await S3RequestPresigner.getSignedUrl(client, command, {
82
+ expiresIn: 300
83
+ });
84
+ let storageRef = `/` + key;
85
+ return {
86
+ statusCode: 200,
87
+ headers: corsHeaders(),
88
+ body: JSON.stringify(Object.fromEntries([
89
+ [
90
+ "uploadUrl",
91
+ uploadUrl
92
+ ],
93
+ [
94
+ "storageRef",
95
+ storageRef
96
+ ]
97
+ ]))
98
+ };
99
+ } catch (raw_exn) {
100
+ let exn = Primitive_exceptions.internalToException(raw_exn);
101
+ console.error("UploadPresign: presign failed", exn);
102
+ return {
103
+ statusCode: 400,
104
+ headers: corsHeaders(),
105
+ body: JSON.stringify(Object.fromEntries([[
106
+ "error",
107
+ "presign_failed"
108
+ ]]))
109
+ };
110
+ }
111
+ }
112
+
113
+ export {
114
+ getEnv,
115
+ corsHeaders,
116
+ decodeJwtSub,
117
+ identityPrefix,
118
+ handler,
119
+ }
120
+ /* node:crypto Not a pure module */
@@ -96,7 +96,7 @@ let forCommandTopic = (
96
96
 
97
97
  // No user packages — all framework imports are in the Layer
98
98
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
99
- ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs",
99
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs",
100
100
  ~packageDirs=Dict.make(),
101
101
  )
102
102
 
@@ -65,7 +65,7 @@ function forCommandTopic(param, connect, memorySizeOpt, timeoutOpt, param$1, par
65
65
  return `{"queueUrl":"` + queueUrl + `","pluginReadModelTableName":"` + rmTable + `","schedulerRoleArn":"` + schedRoleArn + `","schedulerQueueArn":"` + schedQueueArn + `","schedulerQueueName":"` + schedQueueName + `","publishToAggregates":{` + publishToAggregatesJson + `}}`;
66
66
  });
67
67
  envVars["HANDLER_CONFIG"] = handlerConfigJson;
68
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs", {}, undefined);
68
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs", {}, undefined);
69
69
  return connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", "ExtensionPoint", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
70
70
  }
71
71
 
@@ -731,7 +731,7 @@ module Make = (
731
731
 
732
732
  // Static re-export from Layer entry point — no esbuild, no user packages
733
733
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
734
- ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs",
734
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs",
735
735
  ~packageDirs=Dict.make(),
736
736
  )
737
737
 
@@ -422,7 +422,7 @@ function Make(EventCollectorChannel) {
422
422
  envVars["EP_QUEUE_URL"] = epQueueUrl;
423
423
  envVars["PLUGIN_ID"] = Pulumi.output(hbConfig.pluginId);
424
424
  envVars["HEARTBEAT_TIMEOUT"] = Pulumi.output(hbConfig.heartbeatTimeout.toString());
425
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs", {}, undefined);
425
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs", {}, undefined);
426
426
  connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "Scheduler", "Scheduler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
427
427
  };
428
428
  let forDcbCommandTopic = (param, connect, memorySizeOpt, timeoutOpt, dcbCommandTopic) => {
@@ -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
- }