@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.
- package/CHANGELOG.md +16 -0
- package/package.json +8 -8
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
- package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +11 -1
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +9 -3
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
- package/src/components/AutomationSlice_Builder.res +2 -0
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +1 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
- package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
- package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
- package/tests/CounterEntryPoint_OpsTest.res +115 -0
- package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
- package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
- package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
- package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
- package/tests/Platform_AdminScan_OpsTest.res +80 -0
- package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
- package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
- package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
- package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
- package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
- package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.233 (2026-07-27)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** grant DCB command Lambda write access to InboundTranslationSlice audit tables ([2c445fa](https://github.com/ReventlessDev/reventless-core/commit/2c445faf9692573d0eab2f0d4a456e8c5d723b41))
|
|
11
|
+
* refactor(aws)!: convert remaining Group-1 entry points to typed-core/thin-shell; fix Counter publish + EP stub wiring ([d7f1aeb](https://github.com/ReventlessDev/reventless-core/commit/d7f1aeb90cfc122106ccc3ffa522911fba5db2de))
|
|
12
|
+
|
|
13
|
+
### BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* reventless-postgres QueryEnginePostgres.Make no longer
|
|
16
|
+
exposes the dead deploy-time `make` (queryEngineMaker); its Pulumi.Output
|
|
17
|
+
wrap was the last @pulumi/pulumi import in the PgQueryResolver Lambda's
|
|
18
|
+
cold-start graph. Pure helpers now alias QueryDbStorage_Postgres_Ops.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# 3.0.0-alpha.232 (2026-07-27)
|
|
7
23
|
|
|
8
24
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.233",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
12
12
|
"sury": "11.0.0-alpha.4",
|
|
13
13
|
"uuid": "^13.0.0",
|
|
14
|
+
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.25",
|
|
14
15
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.31",
|
|
15
|
-
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.24",
|
|
16
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
17
|
-
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.60",
|
|
18
16
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
19
|
-
"@reventlessdev/rescript-
|
|
20
|
-
"@reventlessdev/
|
|
17
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
18
|
+
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.61",
|
|
19
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.184",
|
|
21
20
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.29",
|
|
22
21
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.105",
|
|
23
|
-
"@reventlessdev/
|
|
24
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.81"
|
|
22
|
+
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
|
|
23
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.81",
|
|
24
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.48"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"rescript": "12.3.0",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Shared SigV4 signer + AppSync Events publisher for the two stream-relay
|
|
2
|
+
// Lambdas (EventLogSubscription, StateTopic). Both sign a `POST /event` to the
|
|
3
|
+
// AppSync Events HTTP API with the ambient Lambda execution-role credentials and
|
|
4
|
+
// publish onto a channel.
|
|
5
|
+
//
|
|
6
|
+
// Runtime-pure and dependency-free by design: the signing is hand-rolled on
|
|
7
|
+
// `node:crypto` (always present, no SDK) rather than @smithy/signature-v4 — so
|
|
8
|
+
// the Lambda cold-start graph carries NO AWS SDK and cannot hit the SDK-version
|
|
9
|
+
// skew ("Frankenstein SDK") class of failure. This is a faithful port of the
|
|
10
|
+
// former inline JS `signedHeaders` the two handler strings duplicated verbatim;
|
|
11
|
+
// AppSyncEventsSigner_ParityFixture.mjs pins that original as a golden reference
|
|
12
|
+
// and AppSyncEventsSigner_OpsTest asserts byte-for-byte equality.
|
|
13
|
+
|
|
14
|
+
// ── node:crypto (string- and buffer-keyed HMAC; the signing key chain feeds a
|
|
15
|
+
// Buffer digest back in as the next key) ──────────────────────────────────
|
|
16
|
+
|
|
17
|
+
type buffer
|
|
18
|
+
|
|
19
|
+
type hmac
|
|
20
|
+
@module("node:crypto") external createHmacStr: (string, string) => hmac = "createHmac"
|
|
21
|
+
@module("node:crypto") external createHmacBuf: (string, buffer) => hmac = "createHmac"
|
|
22
|
+
@send external hmacUpdate: (hmac, string) => hmac = "update"
|
|
23
|
+
@send external hmacDigestBuf: hmac => buffer = "digest"
|
|
24
|
+
@send external hmacDigestHex: (hmac, string) => string = "digest"
|
|
25
|
+
|
|
26
|
+
type hash
|
|
27
|
+
@module("node:crypto") external createHash: string => hash = "createHash"
|
|
28
|
+
@send external hashUpdate: (hash, string) => hash = "update"
|
|
29
|
+
@send external hashDigestHex: (hash, string) => string = "digest"
|
|
30
|
+
|
|
31
|
+
let sha256hex = (data: string): string => createHash("sha256")->hashUpdate(data)->hashDigestHex("hex")
|
|
32
|
+
|
|
33
|
+
let hmacFromStr = (key: string, data: string): buffer =>
|
|
34
|
+
createHmacStr("sha256", key)->hmacUpdate(data)->hmacDigestBuf
|
|
35
|
+
let hmacFromBuf = (key: buffer, data: string): buffer =>
|
|
36
|
+
createHmacBuf("sha256", key)->hmacUpdate(data)->hmacDigestBuf
|
|
37
|
+
|
|
38
|
+
// ── SigV4 ───────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
type creds = {accessKeyId: string, secretAccessKey: string, sessionToken: option<string>}
|
|
41
|
+
|
|
42
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
43
|
+
|
|
44
|
+
// Ambient Lambda execution-role credentials (the managed runtime injects these).
|
|
45
|
+
let envCreds = (): creds => {
|
|
46
|
+
accessKeyId: processEnv->Dict.get("AWS_ACCESS_KEY_ID")->Option.getOr(""),
|
|
47
|
+
secretAccessKey: processEnv->Dict.get("AWS_SECRET_ACCESS_KEY")->Option.getOr(""),
|
|
48
|
+
sessionToken: processEnv->Dict.get("AWS_SESSION_TOKEN"),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let region = (): string => processEnv->Dict.get("AWS_REGION")->Option.getOr("eu-west-1")
|
|
52
|
+
|
|
53
|
+
// Sign a `POST <path>` for service `appsync`. `isoNow` is a Date().toISOString()
|
|
54
|
+
// string, taken as an argument so the signer is pure and parity-testable. The
|
|
55
|
+
// amzDate / dateStamp are derived by fixed slicing of the ISO-8601 layout
|
|
56
|
+
// ("YYYY-MM-DDTHH:MM:SS.sssZ") — equivalent to the former regex strip, without
|
|
57
|
+
// the regex. Returns the headers (host, x-amz-date, [security-token],
|
|
58
|
+
// Authorization) to merge into the request.
|
|
59
|
+
let signedHeaders = (
|
|
60
|
+
~host: string,
|
|
61
|
+
~path: string,
|
|
62
|
+
~body: string,
|
|
63
|
+
~region: string,
|
|
64
|
+
~isoNow: string,
|
|
65
|
+
~creds: creds,
|
|
66
|
+
): dict<string> => {
|
|
67
|
+
let sl = (a, b) => isoNow->String.slice(~start=a, ~end=b)
|
|
68
|
+
let dateStamp = sl(0, 4) ++ sl(5, 7) ++ sl(8, 10)
|
|
69
|
+
let amzDate = dateStamp ++ "T" ++ sl(11, 13) ++ sl(14, 16) ++ sl(17, 19) ++ "Z"
|
|
70
|
+
|
|
71
|
+
let baseHeaders = [("host", host), ("x-amz-date", amzDate)]
|
|
72
|
+
let headers = switch creds.sessionToken {
|
|
73
|
+
| Some(token) if token != "" => baseHeaders->Array.concat([("x-amz-security-token", token)])
|
|
74
|
+
| _ => baseHeaders
|
|
75
|
+
}
|
|
76
|
+
// Codepoint sort matches both the JS `localeCompare` (canonical headers) and
|
|
77
|
+
// the default `.sort()` (signed-headers list) for these lowercase-ASCII keys.
|
|
78
|
+
let sorted = headers->Array.toSorted(((a, _), (b, _)) => String.compare(a, b))
|
|
79
|
+
let canonicalHeaders = sorted->Array.map(((k, v)) => `${k}:${v}\n`)->Array.join("")
|
|
80
|
+
let signedHeaderList = sorted->Array.map(((k, _)) => k)->Array.join(";")
|
|
81
|
+
|
|
82
|
+
let canonicalRequest =
|
|
83
|
+
["POST", path, "", canonicalHeaders, signedHeaderList, sha256hex(body)]->Array.join("\n")
|
|
84
|
+
let scope = `${dateStamp}/${region}/appsync/aws4_request`
|
|
85
|
+
let stringToSign =
|
|
86
|
+
["AWS4-HMAC-SHA256", amzDate, scope, sha256hex(canonicalRequest)]->Array.join("\n")
|
|
87
|
+
|
|
88
|
+
let kDate = hmacFromStr("AWS4" ++ creds.secretAccessKey, dateStamp)
|
|
89
|
+
let kRegion = hmacFromBuf(kDate, region)
|
|
90
|
+
let kService = hmacFromBuf(kRegion, "appsync")
|
|
91
|
+
let kSigning = hmacFromBuf(kService, "aws4_request")
|
|
92
|
+
let signature = createHmacBuf("sha256", kSigning)->hmacUpdate(stringToSign)->hmacDigestHex("hex")
|
|
93
|
+
|
|
94
|
+
let authorization = `AWS4-HMAC-SHA256 Credential=${creds.accessKeyId}/${scope}, SignedHeaders=${signedHeaderList}, Signature=${signature}`
|
|
95
|
+
let out = Dict.fromArray(sorted)
|
|
96
|
+
out->Dict.set("Authorization", authorization)
|
|
97
|
+
out
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── AppSync Events publish (Node 18+ global fetch) ──────────────────────────
|
|
101
|
+
|
|
102
|
+
type url
|
|
103
|
+
@new external parseUrl: string => url = "URL"
|
|
104
|
+
@get external urlHostname: url => string = "hostname"
|
|
105
|
+
|
|
106
|
+
type fetchInit = {method: string, headers: dict<string>, body: string}
|
|
107
|
+
type fetchResponse
|
|
108
|
+
@val external fetch: (string, fetchInit) => promise<fetchResponse> = "fetch"
|
|
109
|
+
@get external responseOk: fetchResponse => bool = "ok"
|
|
110
|
+
@get external responseStatus: fetchResponse => int = "status"
|
|
111
|
+
@send external responseText: fetchResponse => promise<string> = "text"
|
|
112
|
+
|
|
113
|
+
// Sign and POST one AppSync Events request body to `<endpoint>/event`. The
|
|
114
|
+
// caller builds `body` (channel + events) and inspects the response; error
|
|
115
|
+
// handling differs per handler (log-and-continue vs. transient/permanent split).
|
|
116
|
+
let postEvent = async (
|
|
117
|
+
~endpoint: string,
|
|
118
|
+
~region: string,
|
|
119
|
+
~isoNow: string,
|
|
120
|
+
~creds: creds,
|
|
121
|
+
~body: string,
|
|
122
|
+
): fetchResponse => {
|
|
123
|
+
let host = parseUrl(endpoint)->urlHostname
|
|
124
|
+
let auth = signedHeaders(~host, ~path="/event", ~body, ~region, ~isoNow, ~creds)
|
|
125
|
+
let headers = Dict.fromArray([
|
|
126
|
+
("accept", "application/json, text/javascript"),
|
|
127
|
+
("content-encoding", "amz-1.0"),
|
|
128
|
+
("content-type", "application/json; charset=UTF-8"),
|
|
129
|
+
])
|
|
130
|
+
auth->Dict.forEachWithKey((v, k) => headers->Dict.set(k, v))
|
|
131
|
+
await fetch(`${endpoint}/event`, {method: "POST", headers, body})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// AppSync Events channel segments allow only [A-Za-z0-9-]; anything else
|
|
135
|
+
// collapses to `-`. Mirrors the UI's AutoLive.normalizeSegment.
|
|
136
|
+
let pathSegment = (value: string): string =>
|
|
137
|
+
value->String.replaceRegExp(%re("/[^A-Za-z0-9-]/g"), "-")
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
4
|
+
import * as Nodecrypto from "node:crypto";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
7
|
+
|
|
8
|
+
function sha256hex(data) {
|
|
9
|
+
return Nodecrypto.createHash("sha256").update(data).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hmacFromStr(key, data) {
|
|
13
|
+
return Nodecrypto.createHmac("sha256", key).update(data).digest();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hmacFromBuf(key, data) {
|
|
17
|
+
return Nodecrypto.createHmac("sha256", key).update(data).digest();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function envCreds() {
|
|
21
|
+
return {
|
|
22
|
+
accessKeyId: Stdlib_Option.getOr(process.env["AWS_ACCESS_KEY_ID"], ""),
|
|
23
|
+
secretAccessKey: Stdlib_Option.getOr(process.env["AWS_SECRET_ACCESS_KEY"], ""),
|
|
24
|
+
sessionToken: process.env["AWS_SESSION_TOKEN"]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function region() {
|
|
29
|
+
return Stdlib_Option.getOr(process.env["AWS_REGION"], "eu-west-1");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function signedHeaders(host, path, body, region, isoNow, creds) {
|
|
33
|
+
let sl = (a, b) => isoNow.slice(a, b);
|
|
34
|
+
let dateStamp = sl(0, 4) + sl(5, 7) + sl(8, 10);
|
|
35
|
+
let amzDate = dateStamp + "T" + sl(11, 13) + sl(14, 16) + sl(17, 19) + "Z";
|
|
36
|
+
let baseHeaders = [
|
|
37
|
+
[
|
|
38
|
+
"host",
|
|
39
|
+
host
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
"x-amz-date",
|
|
43
|
+
amzDate
|
|
44
|
+
]
|
|
45
|
+
];
|
|
46
|
+
let token = creds.sessionToken;
|
|
47
|
+
let headers = token !== undefined && token !== "" ? baseHeaders.concat([[
|
|
48
|
+
"x-amz-security-token",
|
|
49
|
+
token
|
|
50
|
+
]]) : baseHeaders;
|
|
51
|
+
let sorted = headers.toSorted((param, param$1) => Primitive_string.compare(param[0], param$1[0]));
|
|
52
|
+
let canonicalHeaders = sorted.map(param => param[0] + `:` + param[1] + `\n`).join("");
|
|
53
|
+
let signedHeaderList = sorted.map(param => param[0]).join(";");
|
|
54
|
+
let canonicalRequest = [
|
|
55
|
+
"POST",
|
|
56
|
+
path,
|
|
57
|
+
"",
|
|
58
|
+
canonicalHeaders,
|
|
59
|
+
signedHeaderList,
|
|
60
|
+
sha256hex(body)
|
|
61
|
+
].join("\n");
|
|
62
|
+
let scope = dateStamp + `/` + region + `/appsync/aws4_request`;
|
|
63
|
+
let stringToSign = [
|
|
64
|
+
"AWS4-HMAC-SHA256",
|
|
65
|
+
amzDate,
|
|
66
|
+
scope,
|
|
67
|
+
sha256hex(canonicalRequest)
|
|
68
|
+
].join("\n");
|
|
69
|
+
let kDate = hmacFromStr("AWS4" + creds.secretAccessKey, dateStamp);
|
|
70
|
+
let kRegion = hmacFromBuf(kDate, region);
|
|
71
|
+
let kService = hmacFromBuf(kRegion, "appsync");
|
|
72
|
+
let kSigning = hmacFromBuf(kService, "aws4_request");
|
|
73
|
+
let signature = Nodecrypto.createHmac("sha256", kSigning).update(stringToSign).digest("hex");
|
|
74
|
+
let authorization = `AWS4-HMAC-SHA256 Credential=` + creds.accessKeyId + `/` + scope + `, SignedHeaders=` + signedHeaderList + `, Signature=` + signature;
|
|
75
|
+
let out = Object.fromEntries(sorted);
|
|
76
|
+
out["Authorization"] = authorization;
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function postEvent(endpoint, region, isoNow, creds, body) {
|
|
81
|
+
let host = new URL(endpoint).hostname;
|
|
82
|
+
let auth = signedHeaders(host, "/event", body, region, isoNow, creds);
|
|
83
|
+
let headers = Object.fromEntries([
|
|
84
|
+
[
|
|
85
|
+
"accept",
|
|
86
|
+
"application/json, text/javascript"
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"content-encoding",
|
|
90
|
+
"amz-1.0"
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
"content-type",
|
|
94
|
+
"application/json; charset=UTF-8"
|
|
95
|
+
]
|
|
96
|
+
]);
|
|
97
|
+
Stdlib_Dict.forEachWithKey(auth, (v, k) => {
|
|
98
|
+
headers[k] = v;
|
|
99
|
+
});
|
|
100
|
+
return await fetch(endpoint + `/event`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: headers,
|
|
103
|
+
body: body
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pathSegment(value) {
|
|
108
|
+
return value.replace(/[^A-Za-z0-9-]/g, "-");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
sha256hex,
|
|
113
|
+
hmacFromStr,
|
|
114
|
+
hmacFromBuf,
|
|
115
|
+
envCreds,
|
|
116
|
+
region,
|
|
117
|
+
signedHeaders,
|
|
118
|
+
postEvent,
|
|
119
|
+
pathSegment,
|
|
120
|
+
}
|
|
121
|
+
/* node:crypto Not a pure module */
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Shared runtime helpers for the two admin-query resolver Lambdas
|
|
2
|
+
// (Platform_UIFragments, Platform_ComponentDefinitions). Both scan a read-model
|
|
3
|
+
// table and collapse the rows to the highest version per bare plugin name.
|
|
4
|
+
//
|
|
5
|
+
// Runtime-pure and self-contained: bare `@aws-sdk/*` specifiers (resolved from
|
|
6
|
+
// the managed runtime via the ESM resolve-hook), no `PulumiAws` — so it ships
|
|
7
|
+
// inside an EntryPoint code archive with no serialized closure and no Pulumi in
|
|
8
|
+
// the cold-start graph. Extracted from the former inline JS handler strings,
|
|
9
|
+
// which duplicated the version comparator verbatim across both files.
|
|
10
|
+
|
|
11
|
+
// ── @aws-sdk DynamoDB DocumentClient (bare specifiers) ──────────────────────
|
|
12
|
+
|
|
13
|
+
type ddbClient
|
|
14
|
+
type docClient
|
|
15
|
+
@module("@aws-sdk/client-dynamodb") @new external makeDdbClient: unit => ddbClient = "DynamoDBClient"
|
|
16
|
+
@module("@aws-sdk/lib-dynamodb") @scope("DynamoDBDocumentClient")
|
|
17
|
+
external docFrom: ddbClient => docClient = "from"
|
|
18
|
+
|
|
19
|
+
type scanInput = {
|
|
20
|
+
@as("TableName") tableName: string,
|
|
21
|
+
@as("Limit") limit?: int,
|
|
22
|
+
@as("ExclusiveStartKey") exclusiveStartKey?: JSON.t,
|
|
23
|
+
@as("FilterExpression") filterExpression?: string,
|
|
24
|
+
@as("ExpressionAttributeNames") expressionAttributeNames?: dict<string>,
|
|
25
|
+
@as("ExpressionAttributeValues") expressionAttributeValues?: dict<JSON.t>,
|
|
26
|
+
}
|
|
27
|
+
type scanCommand
|
|
28
|
+
@module("@aws-sdk/lib-dynamodb") @new external makeScanCommand: scanInput => scanCommand = "ScanCommand"
|
|
29
|
+
type scanOutput = {
|
|
30
|
+
@as("Items") items?: array<dict<JSON.t>>,
|
|
31
|
+
@as("LastEvaluatedKey") lastEvaluatedKey?: JSON.t,
|
|
32
|
+
}
|
|
33
|
+
@send external send: (docClient, scanCommand) => promise<scanOutput> = "send"
|
|
34
|
+
|
|
35
|
+
// One document client per container (cold start).
|
|
36
|
+
let doc = makeDdbClient()->docFrom
|
|
37
|
+
|
|
38
|
+
// Drain every page of a table scan (1000/page), following LastEvaluatedKey.
|
|
39
|
+
let scanAll = async (
|
|
40
|
+
~tableName: string,
|
|
41
|
+
~filterExpression: option<string>=?,
|
|
42
|
+
~expressionAttributeNames: option<dict<string>>=?,
|
|
43
|
+
~expressionAttributeValues: option<dict<JSON.t>>=?,
|
|
44
|
+
): array<dict<JSON.t>> => {
|
|
45
|
+
let items = []
|
|
46
|
+
let startKey = ref(None)
|
|
47
|
+
let more = ref(true)
|
|
48
|
+
while more.contents {
|
|
49
|
+
let out =
|
|
50
|
+
await doc->send(
|
|
51
|
+
makeScanCommand({
|
|
52
|
+
tableName,
|
|
53
|
+
limit: 1000,
|
|
54
|
+
exclusiveStartKey: ?startKey.contents,
|
|
55
|
+
filterExpression: ?filterExpression,
|
|
56
|
+
expressionAttributeNames: ?expressionAttributeNames,
|
|
57
|
+
expressionAttributeValues: ?expressionAttributeValues,
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
out.items->Option.forEach(is => is->Array.forEach(i => items->Array.push(i)))
|
|
61
|
+
switch out.lastEvaluatedKey {
|
|
62
|
+
| Some(k) => startKey := Some(k)
|
|
63
|
+
| None => more := false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
items
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Version comparison ──────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
// `Number(...)` binding so the numeric-segment comparison is byte-identical to
|
|
72
|
+
// the former JS `cmpVer` (empty string is guarded separately, exactly as before).
|
|
73
|
+
@val external jsNumber: string => float = "Number"
|
|
74
|
+
|
|
75
|
+
// Compare two version strings (`-`/`+` normalised to `.`, then segment-wise:
|
|
76
|
+
// numeric segments compared as numbers, others lexically). Mirrors the platform
|
|
77
|
+
// invariant "one version per plugin at a time" ordering. Returns 1/-1/0.
|
|
78
|
+
let compareVersions = (a: string, b: string): int => {
|
|
79
|
+
let norm = s => s->String.replaceRegExp(%re("/[-+]/g"), ".")->String.split(".")
|
|
80
|
+
let pa = norm(a)
|
|
81
|
+
let pb = norm(b)
|
|
82
|
+
let len = Math.Int.max(pa->Array.length, pb->Array.length)
|
|
83
|
+
let res = ref(0)
|
|
84
|
+
let i = ref(0)
|
|
85
|
+
while res.contents == 0 && i.contents < len {
|
|
86
|
+
let sa = pa->Array.get(i.contents)->Option.getOr("")
|
|
87
|
+
let sb = pb->Array.get(i.contents)->Option.getOr("")
|
|
88
|
+
let na = jsNumber(sa)
|
|
89
|
+
let nb = jsNumber(sb)
|
|
90
|
+
let bothNum = sa != "" && sb != "" && !Float.isNaN(na) && !Float.isNaN(nb)
|
|
91
|
+
if bothNum {
|
|
92
|
+
if na > nb {
|
|
93
|
+
res := 1
|
|
94
|
+
} else if na < nb {
|
|
95
|
+
res := -1
|
|
96
|
+
}
|
|
97
|
+
} else if sa > sb {
|
|
98
|
+
res := 1
|
|
99
|
+
} else if sa < sb {
|
|
100
|
+
res := -1
|
|
101
|
+
}
|
|
102
|
+
i := i.contents + 1
|
|
103
|
+
}
|
|
104
|
+
res.contents
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Collapse scanned rows to the highest-version entry per bare plugin name.
|
|
108
|
+
// `nameVersionOf` reads the row's `name@version` string; `toEntry` builds the
|
|
109
|
+
// output entry for a surviving row (returning None drops a malformed row). The
|
|
110
|
+
// name/version split mirrors ReventlessCore.Plugin.name / compareVersions.
|
|
111
|
+
let latestByName = (
|
|
112
|
+
items: array<dict<JSON.t>>,
|
|
113
|
+
~nameVersionOf: dict<JSON.t> => option<string>,
|
|
114
|
+
~toEntry: (dict<JSON.t>, ~name: string) => option<JSON.t>,
|
|
115
|
+
): array<JSON.t> => {
|
|
116
|
+
// bare plugin name -> (version, entry); highest version wins.
|
|
117
|
+
let latest: dict<(string, JSON.t)> = Dict.make()
|
|
118
|
+
items->Array.forEach(item =>
|
|
119
|
+
switch item->nameVersionOf {
|
|
120
|
+
| None => ()
|
|
121
|
+
| Some(nameVersion) =>
|
|
122
|
+
let segments = nameVersion->String.split("@")
|
|
123
|
+
let name = segments->Array.get(0)->Option.getOr("")
|
|
124
|
+
let version = segments->Array.get(1)->Option.getOr("")
|
|
125
|
+
switch item->toEntry(~name) {
|
|
126
|
+
| None => ()
|
|
127
|
+
| Some(entry) =>
|
|
128
|
+
switch latest->Dict.get(name) {
|
|
129
|
+
| Some((prevVersion, _)) if compareVersions(version, prevVersion) <= 0 => ()
|
|
130
|
+
| _ => latest->Dict.set(name, (version, entry))
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
latest->Dict.valuesToArray->Array.map(((_, entry)) => entry)
|
|
136
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
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 LibDynamodb from "@aws-sdk/lib-dynamodb";
|
|
5
|
+
import * as ClientDynamodb from "@aws-sdk/client-dynamodb";
|
|
6
|
+
|
|
7
|
+
let doc = LibDynamodb.DynamoDBDocumentClient.from(new ClientDynamodb.DynamoDBClient());
|
|
8
|
+
|
|
9
|
+
async function scanAll(tableName, filterExpression, expressionAttributeNames, expressionAttributeValues) {
|
|
10
|
+
let items = [];
|
|
11
|
+
let startKey;
|
|
12
|
+
let more = true;
|
|
13
|
+
while (more) {
|
|
14
|
+
let out = await doc.send(new LibDynamodb.ScanCommand({
|
|
15
|
+
TableName: tableName,
|
|
16
|
+
Limit: 1000,
|
|
17
|
+
ExclusiveStartKey: startKey,
|
|
18
|
+
FilterExpression: filterExpression,
|
|
19
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
20
|
+
ExpressionAttributeValues: expressionAttributeValues
|
|
21
|
+
}));
|
|
22
|
+
Stdlib_Option.forEach(out.Items, is => {
|
|
23
|
+
is.forEach(i => {
|
|
24
|
+
items.push(i);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
let k = out.LastEvaluatedKey;
|
|
28
|
+
if (k !== undefined) {
|
|
29
|
+
startKey = k;
|
|
30
|
+
} else {
|
|
31
|
+
more = false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return items;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function compareVersions(a, b) {
|
|
38
|
+
let norm = s => s.replace(/[-+]/g, ".").split(".");
|
|
39
|
+
let pa = norm(a);
|
|
40
|
+
let pb = norm(b);
|
|
41
|
+
let len = Math.max(pa.length, pb.length);
|
|
42
|
+
let res = 0;
|
|
43
|
+
let i = 0;
|
|
44
|
+
while (res === 0 && i < len) {
|
|
45
|
+
let sa = Stdlib_Option.getOr(pa[i], "");
|
|
46
|
+
let sb = Stdlib_Option.getOr(pb[i], "");
|
|
47
|
+
let na = Number(sa);
|
|
48
|
+
let nb = Number(sb);
|
|
49
|
+
let bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
50
|
+
if (bothNum) {
|
|
51
|
+
if (na > nb) {
|
|
52
|
+
res = 1;
|
|
53
|
+
} else if (na < nb) {
|
|
54
|
+
res = -1;
|
|
55
|
+
}
|
|
56
|
+
} else if (sa > sb) {
|
|
57
|
+
res = 1;
|
|
58
|
+
} else if (sa < sb) {
|
|
59
|
+
res = -1;
|
|
60
|
+
}
|
|
61
|
+
i = i + 1 | 0;
|
|
62
|
+
};
|
|
63
|
+
return res;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function latestByName(items, nameVersionOf, toEntry) {
|
|
67
|
+
let latest = {};
|
|
68
|
+
items.forEach(item => {
|
|
69
|
+
let nameVersion = nameVersionOf(item);
|
|
70
|
+
if (nameVersion === undefined) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let segments = nameVersion.split("@");
|
|
74
|
+
let name = Stdlib_Option.getOr(segments[0], "");
|
|
75
|
+
let version = Stdlib_Option.getOr(segments[1], "");
|
|
76
|
+
let entry = toEntry(item, name);
|
|
77
|
+
if (entry === undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let match = latest[name];
|
|
81
|
+
if (match !== undefined && compareVersions(version, match[0]) <= 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
latest[name] = [
|
|
85
|
+
version,
|
|
86
|
+
entry
|
|
87
|
+
];
|
|
88
|
+
});
|
|
89
|
+
return Object.values(latest).map(param => param[1]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
doc,
|
|
94
|
+
scanAll,
|
|
95
|
+
compareVersions,
|
|
96
|
+
latestByName,
|
|
97
|
+
}
|
|
98
|
+
/* doc Not a pure module */
|
|
@@ -13,85 +13,6 @@
|
|
|
13
13
|
|
|
14
14
|
open PulumiAws
|
|
15
15
|
|
|
16
|
-
// `adminEntryJson` is the JSON-stringified entry for the built-in Platform_Admin
|
|
17
|
-
// plugin (Plugin aggregate with Activate/Deactivate, Plugin read model).
|
|
18
|
-
// The admin never `Connect`s to itself, so its structure never
|
|
19
|
-
// enters the Plugin read model — we inject it at deploy time so the host shell
|
|
20
|
-
// renders Auto UI for it alongside user plugins.
|
|
21
|
-
let makeHandlerCode = (~tableName as _: string, ~adminEntryJson: string): string =>
|
|
22
|
-
`
|
|
23
|
-
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
24
|
-
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
25
|
-
|
|
26
|
-
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
27
|
-
const TABLE = process.env.PLUGIN_RM_TABLE;
|
|
28
|
-
const ADMIN_ENTRY = ${adminEntryJson};
|
|
29
|
-
|
|
30
|
-
export async function handler() {
|
|
31
|
-
if (!TABLE) {
|
|
32
|
-
console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
|
|
33
|
-
return [ADMIN_ENTRY];
|
|
34
|
-
}
|
|
35
|
-
const items = [];
|
|
36
|
-
let exclusiveStartKey;
|
|
37
|
-
do {
|
|
38
|
-
const out = await client.send(new ScanCommand({
|
|
39
|
-
TableName: TABLE,
|
|
40
|
-
FilterExpression: "contains(#status, :connected)",
|
|
41
|
-
ExpressionAttributeNames: { "#status": "status" },
|
|
42
|
-
ExpressionAttributeValues: { ":connected": "Connected" },
|
|
43
|
-
Limit: 1000,
|
|
44
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
45
|
-
}));
|
|
46
|
-
if (out.Items) items.push(...out.Items);
|
|
47
|
-
exclusiveStartKey = out.LastEvaluatedKey;
|
|
48
|
-
} while (exclusiveStartKey);
|
|
49
|
-
|
|
50
|
-
// Platform invariant: one version per plugin at a time, so the UI sees just
|
|
51
|
-
// the bare plugin name (mirrors ReventlessCore.Plugin.name). The Connected
|
|
52
|
-
// filter above does not enforce single-version: during a rolling deploy or a
|
|
53
|
-
// missed retire, several versions of the same plugin sit in Connected at once
|
|
54
|
-
// and each would otherwise emit a full duplicate set of AutoUI menu entries.
|
|
55
|
-
// Collapse to the highest version per plugin name (mirrors
|
|
56
|
-
// ReventlessCore.Plugin.compareVersions).
|
|
57
|
-
const cmpVer = (a, b) => {
|
|
58
|
-
const pa = String(a).replace(/[-+]/g, ".").split(".");
|
|
59
|
-
const pb = String(b).replace(/[-+]/g, ".").split(".");
|
|
60
|
-
const len = Math.max(pa.length, pb.length);
|
|
61
|
-
for (let i = 0; i < len; i++) {
|
|
62
|
-
const sa = pa[i] ?? "", sb = pb[i] ?? "";
|
|
63
|
-
const na = Number(sa), nb = Number(sb);
|
|
64
|
-
const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
65
|
-
if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
|
|
66
|
-
else if (sa !== sb) return sa > sb ? 1 : -1;
|
|
67
|
-
}
|
|
68
|
-
return 0;
|
|
69
|
-
};
|
|
70
|
-
// The persisted structure carries Internal ReadModels / StateViewSlices for
|
|
71
|
-
// developer tooling; they must stay out of the deployed AutoUI. Mirror
|
|
72
|
-
// ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable here on the
|
|
73
|
-
// read path (the persisted structure is pre-filter — it is NOT re-encoded).
|
|
74
|
-
const isPublicQueryable = (q) => q?.visibility !== "Internal";
|
|
75
|
-
const filterStructure = (s) => ({
|
|
76
|
-
...s,
|
|
77
|
-
readModels: (s.readModels ?? []).filter(isPublicQueryable),
|
|
78
|
-
stateViewSlices: (s.stateViewSlices ?? []).filter(isPublicQueryable),
|
|
79
|
-
});
|
|
80
|
-
const latestByName = new Map();
|
|
81
|
-
for (const item of items) {
|
|
82
|
-
if (!item || !item.structure) continue;
|
|
83
|
-
const name = String(item.name).split("@")[0];
|
|
84
|
-
const version = String(item.name).split("@")[1] ?? "";
|
|
85
|
-
const prev = latestByName.get(name);
|
|
86
|
-
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
87
|
-
latestByName.set(name, { version, entry: { pluginId: name, ...filterStructure(item.structure) } });
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const userEntries = [...latestByName.values()].map(v => v.entry);
|
|
91
|
-
return [ADMIN_ENTRY, ...userEntries];
|
|
92
|
-
}
|
|
93
|
-
`
|
|
94
|
-
|
|
95
16
|
let resolverCode = `
|
|
96
17
|
import { util } from '@aws-appsync/utils';
|
|
97
18
|
export function request(ctx) {
|
|
@@ -160,18 +81,18 @@ let make = (
|
|
|
160
81
|
ReventlessCore.Platform_Admin_Structure.structure,
|
|
161
82
|
)->JSON.stringify
|
|
162
83
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
84
|
+
// Bundle reventless-aws (the compiled `_Ops` handler lives inside it) and
|
|
85
|
+
// re-export its `handler`; buildCodeArchive also ships the ESM resolve-hook so
|
|
86
|
+
// the handler's bare @aws-sdk/* specifiers resolve from the managed runtime.
|
|
87
|
+
// The admin entry is passed as an env var (ADMIN_ENTRY_JSON) rather than
|
|
88
|
+
// interpolated into the source, so the archive hash is stable across deploys.
|
|
89
|
+
let packageDirs = Dict.fromArray([
|
|
90
|
+
("@reventlessdev/reventless-aws", Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws")),
|
|
91
|
+
])
|
|
92
|
+
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
93
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs",
|
|
94
|
+
~packageDirs,
|
|
168
95
|
)
|
|
169
|
-
// ESM self-containment: the handler imports @aws-sdk/* bare specifiers the
|
|
170
|
-
// nodejs22.x runtime provides only under /var/runtime — unreachable from
|
|
171
|
-
// /var/task ESM without the resolver hook. Ship the loader + set its env vars.
|
|
172
|
-
let loaderHash = Util_Bundle.addEsmLoaderAssets(archiveContents)
|
|
173
|
-
let code = Pulumi.Archive.assetArchive(archiveContents)
|
|
174
|
-
let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub ++ "\n---\n" ++ loaderHash)
|
|
175
96
|
|
|
176
97
|
let layers =
|
|
177
98
|
Lambda.reventlessLayerArn
|
|
@@ -196,6 +117,7 @@ let make = (
|
|
|
196
117
|
Lambda.Function.variables: Dict.fromArray([
|
|
197
118
|
("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
|
|
198
119
|
("PLUGIN_RM_TABLE", pluginReadModelTableName->Pulumi.Output.asInput),
|
|
120
|
+
("ADMIN_ENTRY_JSON", adminEntryJson->Pulumi.Input.make),
|
|
199
121
|
("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
|
|
200
122
|
("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
|
|
201
123
|
]),
|