@reventlessdev/rescript-pulumi-aws 3.0.0-alpha.13 → 3.0.0-alpha.14
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 +12 -0
- package/package.json +4 -3
- package/rescript.json +2 -1
- package/src/Lambda/Lambda.res +126 -46
- package/src/Lambda/Lambda.res.mjs +152 -23
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
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.14 (2026-09-16)
|
|
7
|
+
|
|
8
|
+
* fix(aws)!: stop a deploy that finds no Lambda layer ([62957f4](https://github.com/ReventlessDev/reventless-core/commit/62957f444ab2120f672b9ac0564f786d64090864))
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* PulumiAws.Lambda.reventlessLayerArn is now a function that
|
|
13
|
+
returns the ARN and throws when there is none. Use Lambda.reventlessLayers()
|
|
14
|
+
for a function's layers argument.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
# 3.0.0-alpha.13 (2026-09-11)
|
|
7
19
|
|
|
8
20
|
**Note:** Version bump only for package @reventlessdev/rescript-pulumi-aws
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/rescript-pulumi-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.14",
|
|
4
4
|
"description": "ReScript bindings for @pulumi/aws",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
"@pulumi/aws": "~7.19.0",
|
|
23
23
|
"@pulumi/aws-native": "^1.62.0",
|
|
24
24
|
"sury": "11.0.0-rc.2",
|
|
25
|
-
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.
|
|
26
|
-
"@reventlessdev/rescript-
|
|
25
|
+
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.19",
|
|
26
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.12",
|
|
27
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.21"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"esbuild": "^0.25.12",
|
package/rescript.json
CHANGED
package/src/Lambda/Lambda.res
CHANGED
|
@@ -46,53 +46,136 @@ type eventHandlerNoResult<'event> = eventHandler<'event, unit>
|
|
|
46
46
|
@send
|
|
47
47
|
external getRemainingTimeInMillis: context => int = "getRemainingTimeInMillis"
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
} else {
|
|
73
|
-
Some(trimmed)
|
|
74
|
-
}
|
|
75
|
-
} catch {
|
|
76
|
-
| _ => None
|
|
49
|
+
/** The outcome of looking for the Reventless layer. `region` is `None` when
|
|
50
|
+
neither the stack nor the environment names one, and the AWS CLI's default
|
|
51
|
+
region was asked. */
|
|
52
|
+
type layerLookup =
|
|
53
|
+
| Found(string)
|
|
54
|
+
| NotFound({parameter: string, region: option<string>})
|
|
55
|
+
| CouldNotLook({parameter: string, region: option<string>, reason: string})
|
|
56
|
+
|
|
57
|
+
let layerParameter = stack => `/reventless/layer-arn/${stack}`
|
|
58
|
+
|
|
59
|
+
let _nonEmpty = value =>
|
|
60
|
+
switch value->Option.map(String.trim) {
|
|
61
|
+
| Some("") | None => None
|
|
62
|
+
| trimmed => trimmed
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let _lookupRegion = () =>
|
|
66
|
+
switch Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->_nonEmpty {
|
|
67
|
+
| Some(_) as region => region
|
|
68
|
+
| None =>
|
|
69
|
+
switch NodeProcess.env->Dict.get("AWS_REGION")->_nonEmpty {
|
|
70
|
+
| Some(_) as region => region
|
|
71
|
+
| None => NodeProcess.env->Dict.get("AWS_DEFAULT_REGION")->_nonEmpty
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
75
|
+
let _firstLine = text =>
|
|
76
|
+
text
|
|
77
|
+
->String.split("\n")
|
|
78
|
+
->Array.map(String.trim)
|
|
79
|
+
->Array.find(line => line !== "")
|
|
80
|
+
->Option.getOr("the AWS CLI exited without saying why")
|
|
81
|
+
|
|
82
|
+
/** Reads the result of `aws ssm get-parameter`: only `ParameterNotFound` means
|
|
83
|
+
there is no layer; a missing CLI or a refused call means nobody could tell. */
|
|
84
|
+
let classifySsmLookup = (
|
|
85
|
+
~parameter,
|
|
86
|
+
~region,
|
|
87
|
+
result: NodeChildProcess.spawnSyncResult,
|
|
88
|
+
): layerLookup => {
|
|
89
|
+
let stderr = result.stderr->Nullable.toOption->Option.getOr("")
|
|
90
|
+
switch (result.error->Nullable.toOption, result.status->Nullable.toOption) {
|
|
91
|
+
| (Some(error), _) =>
|
|
92
|
+
let message = error->JsExn.message->Option.getOr("unknown error")
|
|
93
|
+
CouldNotLook({parameter, region, reason: `the AWS CLI could not be run (${message})`})
|
|
94
|
+
| (None, Some(0)) =>
|
|
95
|
+
switch result.stdout->Nullable.toOption->_nonEmpty {
|
|
96
|
+
| None | Some("None") => NotFound({parameter, region})
|
|
97
|
+
| Some(arn) => Found(arn)
|
|
98
|
+
}
|
|
99
|
+
| (None, _) if stderr->String.includes("ParameterNotFound") => NotFound({parameter, region})
|
|
100
|
+
| (None, _) => CouldNotLook({parameter, region, reason: _firstLine(stderr)})
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
let _lookupInSsm = (): layerLookup => {
|
|
105
|
+
let parameter = layerParameter(Pulumi.Pulumi.getStackName())
|
|
106
|
+
let region = _lookupRegion()
|
|
107
|
+
let regionArgs = region->Option.mapOr([], region => ["--region", region])
|
|
108
|
+
NodeChildProcess.spawnSync(
|
|
109
|
+
"aws",
|
|
110
|
+
[
|
|
111
|
+
"ssm",
|
|
112
|
+
"get-parameter",
|
|
113
|
+
"--name",
|
|
114
|
+
parameter,
|
|
115
|
+
"--query",
|
|
116
|
+
"Parameter.Value",
|
|
117
|
+
"--output",
|
|
118
|
+
"text",
|
|
119
|
+
]->Array.concat(regionArgs),
|
|
120
|
+
{encoding: "utf8"},
|
|
121
|
+
)->classifySsmLookup(~parameter, ~region, _)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let _lookup: ref<option<layerLookup>> = ref(None)
|
|
125
|
+
|
|
126
|
+
/** Looks once per deploy: `REVENTLESS_LAYER_ARN` when set (CI exports it),
|
|
127
|
+
otherwise the stack's SSM parameter through the AWS CLI, in the stack's
|
|
128
|
+
`aws:region` when it sets one. */
|
|
129
|
+
let lookupLayer = (): layerLookup =>
|
|
130
|
+
switch _lookup.contents {
|
|
131
|
+
| Some(lookup) => lookup
|
|
132
|
+
| None =>
|
|
133
|
+
let lookup = switch NodeProcess.env->Dict.get("REVENTLESS_LAYER_ARN")->_nonEmpty {
|
|
134
|
+
| Some(arn) => Found(arn)
|
|
135
|
+
| None => _lookupInSsm()
|
|
136
|
+
}
|
|
137
|
+
_lookup := Some(lookup)
|
|
138
|
+
lookup
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let _regionName = region =>
|
|
142
|
+
region->Option.mapOr("the AWS CLI's default region", region => `region ${region}`)
|
|
143
|
+
|
|
144
|
+
let _publishHint = `Publish the layer and store its ARN in that parameter, or set REVENTLESS_LAYER_ARN: https://docs.reventless.dev/infrastructure/aws/get-started#the-lambda-layer`
|
|
145
|
+
|
|
146
|
+
/** Why a deploy cannot go on without the layer, or `None` when it was found. */
|
|
147
|
+
let missingLayerMessage = lookup =>
|
|
148
|
+
switch lookup {
|
|
149
|
+
| Found(_) => None
|
|
150
|
+
| NotFound({parameter, region}) =>
|
|
151
|
+
Some(
|
|
152
|
+
`No Reventless Lambda layer: the SSM parameter ${parameter} does not exist in ${_regionName(
|
|
153
|
+
region,
|
|
154
|
+
)}. Without the layer every function fails with "Cannot find package" on its first run. ${_publishHint}`,
|
|
155
|
+
)
|
|
156
|
+
| CouldNotLook({parameter, region, reason}) =>
|
|
157
|
+
Some(
|
|
158
|
+
`Could not look up the Reventless Lambda layer in the SSM parameter ${parameter} (${_regionName(
|
|
159
|
+
region,
|
|
160
|
+
)}): ${reason}. ${_publishHint}`,
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The layer's ARN. Throws with `missingLayerMessage` when there is none, so a
|
|
165
|
+
function is never created without the framework code it imports. */
|
|
166
|
+
let reventlessLayerArn = (): string =>
|
|
167
|
+
switch lookupLayer() {
|
|
168
|
+
| Found(arn) => arn
|
|
169
|
+
| lookup =>
|
|
170
|
+
JsError.throwWithMessage(
|
|
171
|
+
missingLayerMessage(lookup)->Option.getOr("No Reventless Lambda layer"),
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** The `layers` argument of every framework function. */
|
|
176
|
+
let reventlessLayers = (): Pulumi.Input.t<array<Pulumi.Input.t<string>>> =>
|
|
177
|
+
[reventlessLayerArn()->Pulumi.Input.make]->Pulumi.Input.make
|
|
178
|
+
|
|
96
179
|
@val
|
|
97
180
|
external environment: option<string> = "process.env.Environment"
|
|
98
181
|
|
|
@@ -150,10 +233,7 @@ module CallbackFunction = {
|
|
|
150
233
|
~memorySize=1024->Pulumi.Input.make,
|
|
151
234
|
~timeout=180->Pulumi.Input.make,
|
|
152
235
|
~runtime=NodeJs22,
|
|
153
|
-
~layers=
|
|
154
|
-
->Option.map(arn => [arn->Pulumi.Input.make])
|
|
155
|
-
->Option.getOr([])
|
|
156
|
-
->Pulumi.Input.make,
|
|
236
|
+
~layers=reventlessLayers(),
|
|
157
237
|
~vpcConfig=?,
|
|
158
238
|
~tags=?,
|
|
159
239
|
~environment={
|
|
@@ -1,43 +1,161 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
3
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
-
import * as Child_process from "child_process";
|
|
5
5
|
import * as Pulumi from "@pulumi/pulumi";
|
|
6
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
8
|
+
import * as Nodechild_process from "node:child_process";
|
|
6
9
|
import * as PolicyDocument$PulumiAws from "../IAM/PolicyDocument.res.mjs";
|
|
7
10
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
function layerParameter(stack) {
|
|
12
|
+
return `/reventless/layer-arn/` + stack;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function _nonEmpty(value) {
|
|
16
|
+
let trimmed = Stdlib_Option.map(value, prim => prim.trim());
|
|
17
|
+
if (trimmed !== undefined && trimmed !== "") {
|
|
18
|
+
return trimmed;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function _lookupRegion() {
|
|
23
|
+
let region = _nonEmpty(new Pulumi.Config("aws").get("region"));
|
|
24
|
+
if (region !== undefined) {
|
|
25
|
+
return region;
|
|
12
26
|
}
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
let region$1 = _nonEmpty(process.env["AWS_REGION"]);
|
|
28
|
+
if (region$1 !== undefined) {
|
|
29
|
+
return region$1;
|
|
30
|
+
} else {
|
|
31
|
+
return _nonEmpty(process.env["AWS_DEFAULT_REGION"]);
|
|
15
32
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _firstLine(text) {
|
|
36
|
+
return Stdlib_Option.getOr(text.split("\n").map(prim => prim.trim()).find(line => line !== ""), "the AWS CLI exited without saying why");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function classifySsmLookup(parameter, region, result) {
|
|
40
|
+
let stderr = Stdlib_Option.getOr(Primitive_option.fromNullable(result.stderr), "");
|
|
41
|
+
let match = result.error;
|
|
42
|
+
let match$1 = result.status;
|
|
43
|
+
if (match == null) {
|
|
44
|
+
if (!(match$1 == null) && match$1 === 0) {
|
|
45
|
+
let arn = _nonEmpty(Primitive_option.fromNullable(result.stdout));
|
|
46
|
+
if (arn !== undefined && arn !== "None") {
|
|
47
|
+
return {
|
|
48
|
+
TAG: "Found",
|
|
49
|
+
_0: arn
|
|
50
|
+
};
|
|
51
|
+
} else {
|
|
52
|
+
return {
|
|
53
|
+
TAG: "NotFound",
|
|
54
|
+
parameter: parameter,
|
|
55
|
+
region: region
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (stderr.includes("ParameterNotFound")) {
|
|
60
|
+
return {
|
|
61
|
+
TAG: "NotFound",
|
|
62
|
+
parameter: parameter,
|
|
63
|
+
region: region
|
|
64
|
+
};
|
|
24
65
|
} else {
|
|
25
|
-
return
|
|
66
|
+
return {
|
|
67
|
+
TAG: "CouldNotLook",
|
|
68
|
+
parameter: parameter,
|
|
69
|
+
region: region,
|
|
70
|
+
reason: _firstLine(stderr)
|
|
71
|
+
};
|
|
26
72
|
}
|
|
27
|
-
} catch (exn) {
|
|
28
|
-
return;
|
|
29
73
|
}
|
|
74
|
+
let message = Stdlib_Option.getOr(Stdlib_JsExn.message(match), "unknown error");
|
|
75
|
+
return {
|
|
76
|
+
TAG: "CouldNotLook",
|
|
77
|
+
parameter: parameter,
|
|
78
|
+
region: region,
|
|
79
|
+
reason: `the AWS CLI could not be run (` + message + `)`
|
|
80
|
+
};
|
|
30
81
|
}
|
|
31
82
|
|
|
32
|
-
|
|
83
|
+
function _lookupInSsm() {
|
|
84
|
+
let parameter = layerParameter(Pulumi.getStack());
|
|
85
|
+
let region = _lookupRegion();
|
|
86
|
+
let regionArgs = Stdlib_Option.mapOr(region, [], region => [
|
|
87
|
+
"--region",
|
|
88
|
+
region
|
|
89
|
+
]);
|
|
90
|
+
return classifySsmLookup(parameter, region, Nodechild_process.spawnSync("aws", [
|
|
91
|
+
"ssm",
|
|
92
|
+
"get-parameter",
|
|
93
|
+
"--name",
|
|
94
|
+
parameter,
|
|
95
|
+
"--query",
|
|
96
|
+
"Parameter.Value",
|
|
97
|
+
"--output",
|
|
98
|
+
"text"
|
|
99
|
+
].concat(regionArgs), {
|
|
100
|
+
encoding: "utf8"
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
33
103
|
|
|
34
|
-
let
|
|
104
|
+
let _lookup = {
|
|
105
|
+
contents: undefined
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
function lookupLayer() {
|
|
109
|
+
let lookup = _lookup.contents;
|
|
110
|
+
if (lookup !== undefined) {
|
|
111
|
+
return lookup;
|
|
112
|
+
}
|
|
113
|
+
let arn = _nonEmpty(process.env["REVENTLESS_LAYER_ARN"]);
|
|
114
|
+
let lookup$1 = arn !== undefined ? ({
|
|
115
|
+
TAG: "Found",
|
|
116
|
+
_0: arn
|
|
117
|
+
}) : _lookupInSsm();
|
|
118
|
+
_lookup.contents = lookup$1;
|
|
119
|
+
return lookup$1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _regionName(region) {
|
|
123
|
+
return Stdlib_Option.mapOr(region, "the AWS CLI's default region", region => `region ` + region);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let _publishHint = `Publish the layer and store its ARN in that parameter, or set REVENTLESS_LAYER_ARN: https://docs.reventless.dev/infrastructure/aws/get-started#the-lambda-layer`;
|
|
127
|
+
|
|
128
|
+
function missingLayerMessage(lookup) {
|
|
129
|
+
switch (lookup.TAG) {
|
|
130
|
+
case "Found" :
|
|
131
|
+
return;
|
|
132
|
+
case "NotFound" :
|
|
133
|
+
return `No Reventless Lambda layer: the SSM parameter ` + lookup.parameter + ` does not exist in ` + _regionName(lookup.region) + `. Without the layer every function fails with "Cannot find package" on its first run. ` + _publishHint;
|
|
134
|
+
case "CouldNotLook" :
|
|
135
|
+
return `Could not look up the Reventless Lambda layer in the SSM parameter ` + lookup.parameter + ` (` + _regionName(lookup.region) + `): ` + lookup.reason + `. ` + _publishHint;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function reventlessLayerArn() {
|
|
140
|
+
let arn = lookupLayer();
|
|
141
|
+
switch (arn.TAG) {
|
|
142
|
+
case "Found" :
|
|
143
|
+
return arn._0;
|
|
144
|
+
case "NotFound" :
|
|
145
|
+
case "CouldNotLook" :
|
|
146
|
+
return Stdlib_JsError.throwWithMessage(Stdlib_Option.getOr(missingLayerMessage(arn), "No Reventless Lambda layer"));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function reventlessLayers() {
|
|
151
|
+
return [reventlessLayerArn()];
|
|
152
|
+
}
|
|
35
153
|
|
|
36
154
|
function make(callback, role, policies, deadLetterConfig, tracingConfig, memorySizeOpt, timeoutOpt, runtimeOpt, layersOpt, vpcConfig, tags, environmentOpt) {
|
|
37
155
|
let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
|
|
38
156
|
let timeout = timeoutOpt !== undefined ? timeoutOpt : 180;
|
|
39
157
|
let runtime = runtimeOpt !== undefined ? runtimeOpt : "nodejs22.x";
|
|
40
|
-
let layers = layersOpt !== undefined ? layersOpt :
|
|
158
|
+
let layers = layersOpt !== undefined ? layersOpt : [reventlessLayerArn()];
|
|
41
159
|
let environment = environmentOpt !== undefined ? environmentOpt : ({
|
|
42
160
|
variables: Object.fromEntries([[
|
|
43
161
|
"Environment",
|
|
@@ -86,12 +204,23 @@ let defaultLoggingPolicyDocument = PolicyDocument$PulumiAws.make(undefined, unde
|
|
|
86
204
|
}]);
|
|
87
205
|
|
|
88
206
|
export {
|
|
89
|
-
|
|
207
|
+
layerParameter,
|
|
208
|
+
_nonEmpty,
|
|
209
|
+
_lookupRegion,
|
|
210
|
+
_firstLine,
|
|
211
|
+
classifySsmLookup,
|
|
212
|
+
_lookupInSsm,
|
|
213
|
+
_lookup,
|
|
214
|
+
lookupLayer,
|
|
215
|
+
_regionName,
|
|
216
|
+
_publishHint,
|
|
217
|
+
missingLayerMessage,
|
|
90
218
|
reventlessLayerArn,
|
|
219
|
+
reventlessLayers,
|
|
91
220
|
CallbackFunction,
|
|
92
221
|
$$Function,
|
|
93
222
|
Permission,
|
|
94
223
|
Invocation,
|
|
95
224
|
defaultLoggingPolicyDocument,
|
|
96
225
|
}
|
|
97
|
-
/*
|
|
226
|
+
/* defaultLoggingPolicyDocument Not a pure module */
|