@reventlessdev/reventless-aws 3.0.0-alpha.332 → 3.0.0-alpha.334
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 +21 -0
- package/package.json +8 -8
- package/src/Platform.res +71 -6
- package/src/Platform.res.mjs +63 -12
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +21 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +93 -11
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +53 -4
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +53 -11
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +30 -8
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +3 -1
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +4 -1
- package/src/capability/Capability_Messaging.res +141 -0
- package/src/capability/Capability_Messaging.res.mjs +96 -0
- package/src/capability/Capability_Messaging_Ses.res +27 -14
- package/src/capability/Capability_Messaging_Ses.res.mjs +5 -6
- package/tests/AutomationSliceEntryPoint_OpsTest.res +122 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +187 -0
- package/tests/Capability_MessagingTest.res +100 -0
- package/tests/Capability_MessagingTest.res.mjs +85 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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.334 (2026-09-02)
|
|
7
|
+
|
|
8
|
+
* feat(aws)!: the messaging sender is configuration, and a stack can choose to only log ([23b8b4b](https://github.com/ReventlessDev/reventless-core/commit/23b8b4bfe9c70555de4d74266ca686cb427485ca))
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* `Capability_Messaging_Ses.make` is replaced by
|
|
13
|
+
`Capability_Messaging.make(~name)`, which reads the transport and the address
|
|
14
|
+
from config; the SES module keeps only `emailSender`. A deployment that named its
|
|
15
|
+
sender in code must move it to `platform:messagingEmailSender` or the deploy is
|
|
16
|
+
refused.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# 3.0.0-alpha.333 (2026-09-02)
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **automation:** the slice registry rides in the archive, not the environment ([d03b42c](https://github.com/ReventlessDev/reventless-core/commit/d03b42cc074181fb6506f00e40205d617bc70ddb))
|
|
25
|
+
|
|
26
|
+
|
|
6
27
|
# 3.0.0-alpha.332 (2026-09-01)
|
|
7
28
|
|
|
8
29
|
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
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.334",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
16
16
|
"sury": "11.0.0-rc.2",
|
|
17
17
|
"uuid": "^13.0.0",
|
|
18
|
-
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.14",
|
|
19
18
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
20
19
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
21
|
-
"@reventlessdev/rescript-
|
|
20
|
+
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.14",
|
|
22
21
|
"@reventlessdev/rescript-node": "2.0.0-alpha.8",
|
|
23
|
-
"@reventlessdev/rescript-pulumi-
|
|
22
|
+
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.7",
|
|
24
23
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
25
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
26
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
24
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.254",
|
|
25
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.155",
|
|
26
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
27
27
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.34",
|
|
28
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
29
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
28
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.118",
|
|
29
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.127"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"rescript": "12.3.0",
|
package/src/Platform.res
CHANGED
|
@@ -103,6 +103,10 @@ let geocoderPlaceIndexRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make
|
|
|
103
103
|
same `""` sentinel and the same reason. */
|
|
104
104
|
let messagingEmailSenderRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make(""))
|
|
105
105
|
|
|
106
|
+
/** The email transport behind that address, carried the same way for the same case.
|
|
107
|
+
`""` means the platform named none, which the runtime reads as its default. */
|
|
108
|
+
let messagingEmailProviderRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.make(""))
|
|
109
|
+
|
|
106
110
|
module MakeWithConfig = (
|
|
107
111
|
Config: {
|
|
108
112
|
let splitApi: bool
|
|
@@ -2178,16 +2182,57 @@ module MakeWithConfig = (
|
|
|
2178
2182
|
Pulumi.Pulumi.export("messagingEmailSender", messagingEmailSenderFlat)
|
|
2179
2183
|
messagingEmailSenderRef := messagingEmailSenderFlat
|
|
2180
2184
|
|
|
2185
|
+
// Which transport is behind that sender. Exported and threaded exactly like
|
|
2186
|
+
// the address, because a plugin stack's Lambdas need both and read them the
|
|
2187
|
+
// same way — the address alone would leave a runtime guessing what to send
|
|
2188
|
+
// it with. `""` means "the platform did not say", which the runtime reads as
|
|
2189
|
+
// the default rather than as an error.
|
|
2190
|
+
let messagingEmailProviderFlat = switch cfg.messagingSender {
|
|
2191
|
+
| Some({emailProvider: ?Some(name)}) => name->Pulumi.Output.fromInput
|
|
2192
|
+
| _ => Pulumi.Output.make("")
|
|
2193
|
+
}
|
|
2194
|
+
Pulumi.Pulumi.export("messagingEmailProvider", messagingEmailProviderFlat)
|
|
2195
|
+
messagingEmailProviderRef := messagingEmailProviderFlat
|
|
2196
|
+
|
|
2197
|
+
// Exported but not handed to a Lambda: nothing reads an SMS sender yet, and
|
|
2198
|
+
// an env var no runtime consults is a setting that looks wired. The export
|
|
2199
|
+
// is how a stack can see what it configured.
|
|
2200
|
+
Pulumi.Pulumi.export(
|
|
2201
|
+
"messagingSmsSender",
|
|
2202
|
+
switch cfg.messagingSender {
|
|
2203
|
+
| Some({smsSender: ?Some(sender)}) => sender->Pulumi.Output.fromInput
|
|
2204
|
+
| _ => Pulumi.Output.make("")
|
|
2205
|
+
},
|
|
2206
|
+
)
|
|
2207
|
+
|
|
2181
2208
|
// Same gate as geocoding's, and the failure it prevents is worse: an
|
|
2182
2209
|
// unprovisioned mailer answers `Unavailable` on every send, so the messages
|
|
2183
2210
|
// queue and retry and are eventually abandoned — a notification nobody
|
|
2184
2211
|
// receives and no error names.
|
|
2185
|
-
|
|
2212
|
+
//
|
|
2213
|
+
// The test is the *sender*, not the handle. Since the address became
|
|
2214
|
+
// configuration with no default, a root that calls the helper hands over a
|
|
2215
|
+
// record either way — one carrying no sender when the stack named none — so
|
|
2216
|
+
// a gate reading `Option.isNone` would pass a deployment with no mailer.
|
|
2217
|
+
// Whether the field is present is known here, without resolving the Output
|
|
2218
|
+
// behind it, which is what keeps this refusal at graph-construction time
|
|
2219
|
+
// rather than in the `apply` further down: the same gap caught there fails
|
|
2220
|
+
// the deploy midway, and is skipped entirely by `pulumi preview`.
|
|
2221
|
+
let emailSenderProvisioned = switch cfg.messagingSender {
|
|
2222
|
+
| Some({emailSender: ?Some(_)}) => true
|
|
2223
|
+
| _ => false
|
|
2224
|
+
}
|
|
2225
|
+
if capabilities->Array.includes(Messaging) && !emailSenderProvisioned {
|
|
2186
2226
|
JsError.throwWithMessage(
|
|
2187
2227
|
`A plugin of this deployment declares the Messaging capability, but this platform ` ++
|
|
2188
|
-
`provisions no sender.\n` ++
|
|
2189
|
-
`
|
|
2190
|
-
|
|
2228
|
+
`provisions no email sender.\n` ++
|
|
2229
|
+
` The sender is configuration and has no default: set ` ++
|
|
2230
|
+
`\`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or the env var ` ++
|
|
2231
|
+
`REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES.\n` ++
|
|
2232
|
+
` A stack that should not mail anybody sets \`platform:messagingEmailProvider: log\` ` ++
|
|
2233
|
+
`instead — every message is logged and none is sent, and the address is optional.\n` ++
|
|
2234
|
+
` The root must also pass \`~messagingSender\` in \`~hostUiBundle\`, from ` ++
|
|
2235
|
+
`\`ReventlessAws.Capability_Messaging.make(~name=…)\`.\n` ++
|
|
2191
2236
|
` The declaration is generated from the plugins' capabilities.json — regenerate with ` ++
|
|
2192
2237
|
`\`pnpm run generate:platform\` if it is stale.`,
|
|
2193
2238
|
)
|
|
@@ -2516,6 +2561,23 @@ module MakeWithConfig = (
|
|
|
2516
2561
|
PluginRuntime_Builder.registerCapabilityEnv("MESSAGING_EMAIL_SENDER", messagingEmailSender)
|
|
2517
2562
|
PluginRuntime_Builder.registerMessagingSender(messagingEmailSender)
|
|
2518
2563
|
|
|
2564
|
+
// The transport, read across the same two halves. Only the env var: the IAM
|
|
2565
|
+
// grant below keys off the address, and a log-transport deployment wants the same
|
|
2566
|
+
// grant anyway — a stack that switches back to SES should not need its roles
|
|
2567
|
+
// rebuilt.
|
|
2568
|
+
let messagingEmailProvider: Pulumi.Output.t<string> = switch platformStackRef {
|
|
2569
|
+
| Some(stackRef) =>
|
|
2570
|
+
(
|
|
2571
|
+
stackRef->Pulumi.StackReference.getOutput("messagingEmailProvider"):
|
|
2572
|
+
Pulumi.Output.t<option<string>>
|
|
2573
|
+
)->Pulumi.Output.apply(o => o->Option.getOr(""))
|
|
2574
|
+
| None => messagingEmailProviderRef.contents
|
|
2575
|
+
}
|
|
2576
|
+
PluginRuntime_Builder.registerCapabilityEnv(
|
|
2577
|
+
"MESSAGING_EMAIL_PROVIDER",
|
|
2578
|
+
messagingEmailProvider,
|
|
2579
|
+
)
|
|
2580
|
+
|
|
2519
2581
|
module P = unpack(plugin)
|
|
2520
2582
|
let pluginComponent = P.make()
|
|
2521
2583
|
ReventlessCore.Plugin_Helpers.clearOffload()
|
|
@@ -2698,8 +2760,11 @@ module MakeWithConfig = (
|
|
|
2698
2760
|
`\n Geocoding is \`Capability_Geocoding_AwsLocation.make\`, passed to the ` ++
|
|
2699
2761
|
`platform as \`~geocoderPlaceIndex\`.`
|
|
2700
2762
|
| Messaging =>
|
|
2701
|
-
`\n Messaging is \`
|
|
2702
|
-
`as \`~messagingSender
|
|
2763
|
+
`\n Messaging is \`Capability_Messaging.make\`, passed to the platform ` ++
|
|
2764
|
+
`as \`~messagingSender\`. The sender itself is configuration and has no ` ++
|
|
2765
|
+
`default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or ` ++
|
|
2766
|
+
`REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or ` ++
|
|
2767
|
+
`\`platform:messagingEmailProvider: log\` to log every message and send none.`
|
|
2703
2768
|
}
|
|
2704
2769
|
)
|
|
2705
2770
|
// One line per capability, not per declaring component: two slices
|
package/src/Platform.res.mjs
CHANGED
|
@@ -144,6 +144,10 @@ let messagingEmailSenderRef = {
|
|
|
144
144
|
contents: Pulumi.output("")
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
+
let messagingEmailProviderRef = {
|
|
148
|
+
contents: Pulumi.output("")
|
|
149
|
+
};
|
|
150
|
+
|
|
147
151
|
function MakeWithConfig(Config) {
|
|
148
152
|
Stdlib_Option.forEach(Config.commandHandlerConfig.aggregates, param => {
|
|
149
153
|
Stdlib_Option.forEach(param.sync, AggregateRuntime_Builder_Single$ReventlessAws.setConfig);
|
|
@@ -1168,8 +1172,29 @@ function MakeWithConfig(Config) {
|
|
|
1168
1172
|
}
|
|
1169
1173
|
Pulumi$Pulumi.$$export("messagingEmailSender", messagingEmailSenderFlat);
|
|
1170
1174
|
messagingEmailSenderRef.contents = messagingEmailSenderFlat;
|
|
1171
|
-
|
|
1172
|
-
|
|
1175
|
+
let match$6 = hostUiBundle.messagingSender;
|
|
1176
|
+
let messagingEmailProviderFlat;
|
|
1177
|
+
if (match$6 !== undefined) {
|
|
1178
|
+
let name = match$6.emailProvider;
|
|
1179
|
+
messagingEmailProviderFlat = name !== undefined ? name : Pulumi.output("");
|
|
1180
|
+
} else {
|
|
1181
|
+
messagingEmailProviderFlat = Pulumi.output("");
|
|
1182
|
+
}
|
|
1183
|
+
Pulumi$Pulumi.$$export("messagingEmailProvider", messagingEmailProviderFlat);
|
|
1184
|
+
messagingEmailProviderRef.contents = messagingEmailProviderFlat;
|
|
1185
|
+
let match$7 = hostUiBundle.messagingSender;
|
|
1186
|
+
let tmp$2;
|
|
1187
|
+
if (match$7 !== undefined) {
|
|
1188
|
+
let sender$1 = match$7.smsSender;
|
|
1189
|
+
tmp$2 = sender$1 !== undefined ? sender$1 : Pulumi.output("");
|
|
1190
|
+
} else {
|
|
1191
|
+
tmp$2 = Pulumi.output("");
|
|
1192
|
+
}
|
|
1193
|
+
Pulumi$Pulumi.$$export("messagingSmsSender", tmp$2);
|
|
1194
|
+
let match$8 = hostUiBundle.messagingSender;
|
|
1195
|
+
let emailSenderProvisioned = match$8 !== undefined ? match$8.emailSender !== undefined : false;
|
|
1196
|
+
if (capabilities.includes("Messaging") && !emailSenderProvisioned) {
|
|
1197
|
+
Stdlib_JsError.throwWithMessage(`A plugin of this deployment declares the Messaging capability, but this platform provisions no email sender.\n The sender is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or the env var REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES.\n A stack that should not mail anybody sets \`platform:messagingEmailProvider: log\` instead — every message is logged and none is sent, and the address is optional.\n The root must also pass \`~messagingSender\` in \`~hostUiBundle\`, from \`ReventlessAws.Capability_Messaging.make(~name=…)\`.\n The declaration is generated from the plugins' capabilities.json — regenerate with \`pnpm run generate:platform\` if it is stale.`);
|
|
1173
1198
|
}
|
|
1174
1199
|
let configJsonContent = Pulumi.all([
|
|
1175
1200
|
Pulumi.all([
|
|
@@ -1240,9 +1265,9 @@ function MakeWithConfig(Config) {
|
|
|
1240
1265
|
contentType: "application/json"
|
|
1241
1266
|
});
|
|
1242
1267
|
}
|
|
1243
|
-
let match$
|
|
1244
|
-
if (componentDefinitions !== undefined && match$
|
|
1245
|
-
let manifestKeys = Platform_BakedManifest$ReventlessCore.files(match$
|
|
1268
|
+
let match$9 = hostUiBundle.bakedManifest;
|
|
1269
|
+
if (componentDefinitions !== undefined && match$9 !== undefined) {
|
|
1270
|
+
let manifestKeys = Platform_BakedManifest$ReventlessCore.files(match$9).map(param => param[0]);
|
|
1246
1271
|
let manifestKey = Stdlib_Option.getOr(manifestKeys[0], Platform_BakedManifest$ReventlessCore.defaultKey);
|
|
1247
1272
|
Pulumi.all([
|
|
1248
1273
|
componentDefinitions.roleId,
|
|
@@ -1326,6 +1351,8 @@ function MakeWithConfig(Config) {
|
|
|
1326
1351
|
let messagingEmailSender = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("messagingEmailSender").apply(o => Stdlib_Option.getOr(o, "")) : messagingEmailSenderRef.contents;
|
|
1327
1352
|
PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("MESSAGING_EMAIL_SENDER", messagingEmailSender);
|
|
1328
1353
|
PluginRuntime_Builder$ReventlessAws.registerMessagingSender(messagingEmailSender);
|
|
1354
|
+
let messagingEmailProvider = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("messagingEmailProvider").apply(o => Stdlib_Option.getOr(o, "")) : messagingEmailProviderRef.contents;
|
|
1355
|
+
PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("MESSAGING_EMAIL_PROVIDER", messagingEmailProvider);
|
|
1329
1356
|
let pluginComponent = plugin.make();
|
|
1330
1357
|
Plugin_Helpers$ReventlessCore.clearOffload();
|
|
1331
1358
|
currentDeployTarget.contents = "Domain";
|
|
@@ -1398,7 +1425,7 @@ function MakeWithConfig(Config) {
|
|
|
1398
1425
|
if (match === "Geocoding") {
|
|
1399
1426
|
return `\n Geocoding is \`Capability_Geocoding_AwsLocation.make\`, passed to the platform as \`~geocoderPlaceIndex\`.`;
|
|
1400
1427
|
} else {
|
|
1401
|
-
return `\n Messaging is \`
|
|
1428
|
+
return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
|
|
1402
1429
|
}
|
|
1403
1430
|
}))).join(""));
|
|
1404
1431
|
}
|
|
@@ -2506,8 +2533,29 @@ function Make($star) {
|
|
|
2506
2533
|
}
|
|
2507
2534
|
Pulumi$Pulumi.$$export("messagingEmailSender", messagingEmailSenderFlat);
|
|
2508
2535
|
messagingEmailSenderRef.contents = messagingEmailSenderFlat;
|
|
2509
|
-
|
|
2510
|
-
|
|
2536
|
+
let match$6 = hostUiBundle.messagingSender;
|
|
2537
|
+
let messagingEmailProviderFlat;
|
|
2538
|
+
if (match$6 !== undefined) {
|
|
2539
|
+
let name = match$6.emailProvider;
|
|
2540
|
+
messagingEmailProviderFlat = name !== undefined ? name : Pulumi.output("");
|
|
2541
|
+
} else {
|
|
2542
|
+
messagingEmailProviderFlat = Pulumi.output("");
|
|
2543
|
+
}
|
|
2544
|
+
Pulumi$Pulumi.$$export("messagingEmailProvider", messagingEmailProviderFlat);
|
|
2545
|
+
messagingEmailProviderRef.contents = messagingEmailProviderFlat;
|
|
2546
|
+
let match$7 = hostUiBundle.messagingSender;
|
|
2547
|
+
let tmp$2;
|
|
2548
|
+
if (match$7 !== undefined) {
|
|
2549
|
+
let sender$1 = match$7.smsSender;
|
|
2550
|
+
tmp$2 = sender$1 !== undefined ? sender$1 : Pulumi.output("");
|
|
2551
|
+
} else {
|
|
2552
|
+
tmp$2 = Pulumi.output("");
|
|
2553
|
+
}
|
|
2554
|
+
Pulumi$Pulumi.$$export("messagingSmsSender", tmp$2);
|
|
2555
|
+
let match$8 = hostUiBundle.messagingSender;
|
|
2556
|
+
let emailSenderProvisioned = match$8 !== undefined ? match$8.emailSender !== undefined : false;
|
|
2557
|
+
if (capabilities.includes("Messaging") && !emailSenderProvisioned) {
|
|
2558
|
+
Stdlib_JsError.throwWithMessage(`A plugin of this deployment declares the Messaging capability, but this platform provisions no email sender.\n The sender is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or the env var REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES.\n A stack that should not mail anybody sets \`platform:messagingEmailProvider: log\` instead — every message is logged and none is sent, and the address is optional.\n The root must also pass \`~messagingSender\` in \`~hostUiBundle\`, from \`ReventlessAws.Capability_Messaging.make(~name=…)\`.\n The declaration is generated from the plugins' capabilities.json — regenerate with \`pnpm run generate:platform\` if it is stale.`);
|
|
2511
2559
|
}
|
|
2512
2560
|
let configJsonContent = Pulumi.all([
|
|
2513
2561
|
Pulumi.all([
|
|
@@ -2578,9 +2626,9 @@ function Make($star) {
|
|
|
2578
2626
|
contentType: "application/json"
|
|
2579
2627
|
});
|
|
2580
2628
|
}
|
|
2581
|
-
let match$
|
|
2582
|
-
if (componentDefinitions !== undefined && match$
|
|
2583
|
-
let manifestKeys = Platform_BakedManifest$ReventlessCore.files(match$
|
|
2629
|
+
let match$9 = hostUiBundle.bakedManifest;
|
|
2630
|
+
if (componentDefinitions !== undefined && match$9 !== undefined) {
|
|
2631
|
+
let manifestKeys = Platform_BakedManifest$ReventlessCore.files(match$9).map(param => param[0]);
|
|
2584
2632
|
let manifestKey = Stdlib_Option.getOr(manifestKeys[0], Platform_BakedManifest$ReventlessCore.defaultKey);
|
|
2585
2633
|
Pulumi.all([
|
|
2586
2634
|
componentDefinitions.roleId,
|
|
@@ -2662,6 +2710,8 @@ function Make($star) {
|
|
|
2662
2710
|
let messagingEmailSender = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("messagingEmailSender").apply(o => Stdlib_Option.getOr(o, "")) : messagingEmailSenderRef.contents;
|
|
2663
2711
|
PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("MESSAGING_EMAIL_SENDER", messagingEmailSender);
|
|
2664
2712
|
PluginRuntime_Builder$ReventlessAws.registerMessagingSender(messagingEmailSender);
|
|
2713
|
+
let messagingEmailProvider = platformStackRef !== undefined ? Primitive_option.valFromOption(platformStackRef).getOutput("messagingEmailProvider").apply(o => Stdlib_Option.getOr(o, "")) : messagingEmailProviderRef.contents;
|
|
2714
|
+
PluginRuntime_Builder$ReventlessAws.registerCapabilityEnv("MESSAGING_EMAIL_PROVIDER", messagingEmailProvider);
|
|
2665
2715
|
let pluginComponent = plugin.make();
|
|
2666
2716
|
Plugin_Helpers$ReventlessCore.clearOffload();
|
|
2667
2717
|
currentDeployTarget.contents = "Domain";
|
|
@@ -2734,7 +2784,7 @@ function Make($star) {
|
|
|
2734
2784
|
if (match === "Geocoding") {
|
|
2735
2785
|
return `\n Geocoding is \`Capability_Geocoding_AwsLocation.make\`, passed to the platform as \`~geocoderPlaceIndex\`.`;
|
|
2736
2786
|
} else {
|
|
2737
|
-
return `\n Messaging is \`
|
|
2787
|
+
return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
|
|
2738
2788
|
}
|
|
2739
2789
|
}))).join(""));
|
|
2740
2790
|
}
|
|
@@ -2855,6 +2905,7 @@ export {
|
|
|
2855
2905
|
getObjectStoreEndpoints,
|
|
2856
2906
|
geocoderPlaceIndexRef,
|
|
2857
2907
|
messagingEmailSenderRef,
|
|
2908
|
+
messagingEmailProviderRef,
|
|
2858
2909
|
MakeWithConfig,
|
|
2859
2910
|
Make,
|
|
2860
2911
|
}
|
|
@@ -278,9 +278,11 @@ let finish = () =>
|
|
|
278
278
|
|
|
279
279
|
let handlerConfigOutput =
|
|
280
280
|
Pulumi.Output.all(handlerOutputs)
|
|
281
|
-
->Pulumi.Output.apply(handlers =>
|
|
282
|
-
`{"handlers":[${handlers->Array.join(",")}]}`
|
|
283
|
-
|
|
281
|
+
->Pulumi.Output.apply(handlers => {
|
|
282
|
+
let json = `{"handlers":[${handlers->Array.join(",")}]}`
|
|
283
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllAggregatesCmdHandler", ~handlerConfigJson=json)
|
|
284
|
+
json
|
|
285
|
+
})
|
|
284
286
|
|
|
285
287
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
286
288
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
@@ -14,6 +14,7 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
|
14
14
|
import * as PgConnection$ReventlessAws from "../Postgres/PgConnection.res.mjs";
|
|
15
15
|
import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
|
|
16
16
|
import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
|
|
17
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
17
18
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
18
19
|
import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
|
|
19
20
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -184,7 +185,11 @@ function finish() {
|
|
|
184
185
|
]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + param[3] + `}`);
|
|
185
186
|
handlerOutputs.push(handlerJson);
|
|
186
187
|
});
|
|
187
|
-
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers =>
|
|
188
|
+
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => {
|
|
189
|
+
let json = `{"handlers":[` + handlers.join(",") + `]}`;
|
|
190
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllAggregatesCmdHandler", json, undefined);
|
|
191
|
+
return json;
|
|
192
|
+
});
|
|
188
193
|
let envVars = {};
|
|
189
194
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
190
195
|
let tableName = pluginRmTableName.contents;
|
|
@@ -18,9 +18,21 @@ import { Make as outboundTranslationSliceCallbackMake } from "@reventlessdev/rev
|
|
|
18
18
|
import * as StreamOps from "./StreamRoutedEntryPoint_Ops.res.mjs";
|
|
19
19
|
import * as Ops from "./AutomationSliceEntryPoint_Ops.res.mjs";
|
|
20
20
|
import { runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
21
23
|
|
|
22
24
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
23
25
|
|
|
26
|
+
// Absent whenever the config is full-key (tests, and any pre-split deployment),
|
|
27
|
+
// which is why a missing file is not an error.
|
|
28
|
+
const readSliceModules = () => {
|
|
29
|
+
try {
|
|
30
|
+
return readFileSync(join(process.cwd(), "automationSliceModules.json"), "utf-8");
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
24
36
|
async function buildAllHandlers() {
|
|
25
37
|
// Runtime extension seam: any registered out-of-tree extension gets its
|
|
26
38
|
// onColdStart before a single handler is built, which is what makes the
|
|
@@ -29,7 +41,15 @@ async function buildAllHandlers() {
|
|
|
29
41
|
await runtimeExtensionsReady;
|
|
30
42
|
const handlers = {};
|
|
31
43
|
const sweeps = [];
|
|
32
|
-
|
|
44
|
+
// The slice registry rides in the archive, not the environment — two module
|
|
45
|
+
// specifiers per slice outgrow Lambda's 4KB env-var ceiling (see the asset in
|
|
46
|
+
// AutomationSliceRuntime_Builder_Single). A full-key config still wins, so a
|
|
47
|
+
// caller that supplies one — every test that drives a config directly — needs
|
|
48
|
+
// no asset on disk. process.cwd() is /var/task, where the archive unpacks.
|
|
49
|
+
const entries = Ops.parseHandlerConfigWithModules(
|
|
50
|
+
readSliceModules(),
|
|
51
|
+
process.env["HANDLER_CONFIG"] || "",
|
|
52
|
+
);
|
|
33
53
|
|
|
34
54
|
await Promise.all(entries.map(async (entry) => {
|
|
35
55
|
if (!entry.bodyModule) {
|
|
@@ -62,16 +62,83 @@ let decodeEntry = (json: JSON.t): option<handlerEntry> =>
|
|
|
62
62
|
context: h->Dict.get("context")->Option.flatMap(decodeContext),
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
// A compact entry names its slice and carries only what the deploy resolved;
|
|
66
|
+
// the module paths and context live in the archive registry, keyed by the same
|
|
67
|
+
// name (AutomationSliceRuntime_Builder_Single). Shared `queueUrl` /
|
|
68
|
+
// `commandQueueIsFifo` / `urns` are hoisted out of the per-entry keys, since a
|
|
69
|
+
// slice publishing to the DCB fallback and reading its plugin's log repeats
|
|
70
|
+
// both verbatim — which is what outgrew the 4KB env limit.
|
|
71
|
+
let decodeCompactEntry = (
|
|
72
|
+
~modules: dict<JSON.t>,
|
|
73
|
+
~sharedQueueUrl: string,
|
|
74
|
+
~sharedIsFifo: option<bool>,
|
|
75
|
+
~sharedUrns: array<string>,
|
|
76
|
+
h: dict<JSON.t>,
|
|
77
|
+
name: string,
|
|
78
|
+
): handlerEntry => {
|
|
79
|
+
let m = modules->Dict.get(name)->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
|
|
80
|
+
let urns =
|
|
81
|
+
h
|
|
82
|
+
->Dict.get("u")
|
|
83
|
+
->Option.flatMap(JSON.Decode.array)
|
|
84
|
+
->Option.map(a => a->Array.filterMap(JSON.Decode.string))
|
|
85
|
+
->Option.getOr(sharedUrns)
|
|
86
|
+
{
|
|
87
|
+
specModule: m->strOf("specModule")->Option.getOr(""),
|
|
88
|
+
bodyModule: m->strOf("bodyModule")->Option.getOr(""),
|
|
89
|
+
callbackType: m->strOf("callbackType")->Option.getOr("automation"),
|
|
90
|
+
queryDbTableName: h->strOf("q")->Option.getOr(""),
|
|
91
|
+
dcbQueueUrl: h->strOf("k")->Option.getOr(sharedQueueUrl),
|
|
92
|
+
commandQueueIsFifo: switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
|
|
93
|
+
| Some(f) => Some(f)
|
|
94
|
+
| None => sharedIsFifo
|
|
95
|
+
},
|
|
96
|
+
// Only the first, which is all the shell registers today. The remaining
|
|
97
|
+
// urns stay in the config for the multi-source registration to use.
|
|
98
|
+
sourceUrn: urns->Array.get(0)->Option.getOr(""),
|
|
99
|
+
context: m->Dict.get("context")->Option.flatMap(decodeContext),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** `modulesJson` is the archive-side slice registry (`""` when there is none);
|
|
104
|
+
an entry carrying its own `specModule` is a full-key config and needs it. */
|
|
105
|
+
let parseHandlerConfigWithModules = (modulesJson: string, rawJson: string): array<handlerEntry> =>
|
|
66
106
|
rawJson == ""
|
|
67
107
|
? []
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
: {
|
|
109
|
+
let obj = rawJson->JSON.parseOrThrow->JSON.Decode.object->Option.getOr(Dict.make())
|
|
110
|
+
let modules =
|
|
111
|
+
modulesJson == ""
|
|
112
|
+
? Dict.make()
|
|
113
|
+
: modulesJson->JSON.parseOrThrow->JSON.Decode.object->Option.getOr(Dict.make())
|
|
114
|
+
let sharedQueueUrl = obj->strOf("queueUrl")->Option.getOr("")
|
|
115
|
+
let sharedIsFifo = obj->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool)
|
|
116
|
+
let sharedUrns =
|
|
117
|
+
obj
|
|
118
|
+
->Dict.get("urns")
|
|
119
|
+
->Option.flatMap(JSON.Decode.array)
|
|
120
|
+
->Option.map(a => a->Array.filterMap(JSON.Decode.string))
|
|
121
|
+
->Option.getOr([])
|
|
122
|
+
obj
|
|
123
|
+
->Dict.get("handlers")
|
|
124
|
+
->Option.flatMap(JSON.Decode.array)
|
|
125
|
+
->Option.getOr([])
|
|
126
|
+
->Array.filterMap(item =>
|
|
127
|
+
switch item->JSON.Decode.object {
|
|
128
|
+
| None => None
|
|
129
|
+
| Some(h) =>
|
|
130
|
+
switch h->strOf("n") {
|
|
131
|
+
| Some(name) =>
|
|
132
|
+
Some(decodeCompactEntry(~modules, ~sharedQueueUrl, ~sharedIsFifo, ~sharedUrns, h, name))
|
|
133
|
+
| None => decodeEntry(item)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The full-key form, for a config that carries its own module paths. */
|
|
140
|
+
let parseHandlerConfig = (rawJson: string): array<handlerEntry> =>
|
|
141
|
+
parseHandlerConfigWithModules("", rawJson)
|
|
75
142
|
|
|
76
143
|
// A pre-`bodyModule` config entry cannot rebuild the callback (the spec module
|
|
77
144
|
// alone lacks the mappings/process). Warn and skip rather than crash the whole
|
|
@@ -230,15 +297,30 @@ outcome, not a verdict on the address. `MESSAGING_EMAIL_SENDER` is read the same
|
|
|
230
297
|
way, and empty yields a provider with no channels rather than one that claims a
|
|
231
298
|
channel it cannot send on.
|
|
232
299
|
*/
|
|
300
|
+
/** The email transport this deployment chose.
|
|
301
|
+
|
|
302
|
+
Read per call like the sender beside it, and defaulting to SES on anything it
|
|
303
|
+
does not recognise — including the empty string a platform that named nothing
|
|
304
|
+
exports. The deploy already refuses an unknown value (`Capability_Messaging`
|
|
305
|
+
parses it there, where a human is watching); defaulting again here is for the
|
|
306
|
+
case that cannot be refused, a Lambda whose variable was edited by hand. SES
|
|
307
|
+
is the safe direction: a message that should have been logged and was sent is
|
|
308
|
+
recoverable, a customer notification silently swallowed by a log line is not. */
|
|
309
|
+
let messagingEmailProvider = (): Reventless.Messaging.provider => {
|
|
310
|
+
let sender = NodeProcess.env->Dict.get("MESSAGING_EMAIL_SENDER")->Option.getOr("")
|
|
311
|
+
switch NodeProcess.env->Dict.get("MESSAGING_EMAIL_PROVIDER")->Option.getOr("") {
|
|
312
|
+
| "log" => ReventlessCore.Messaging_Log_Backend.provider(~sender)
|
|
313
|
+
| _ => Messaging_Ses_Backend.provider(~sender)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
233
317
|
let capabilities = (): Reventless.Capabilities.t => {
|
|
234
318
|
geocode: (~text) =>
|
|
235
319
|
Geocoder_AwsLocation_Backend.search(
|
|
236
320
|
~indexName=NodeProcess.env->Dict.get("PLACE_INDEX_NAME")->Option.getOr(""),
|
|
237
321
|
~text,
|
|
238
322
|
),
|
|
239
|
-
messaging:
|
|
240
|
-
~sender=NodeProcess.env->Dict.get("MESSAGING_EMAIL_SENDER")->Option.getOr(""),
|
|
241
|
-
),
|
|
323
|
+
messaging: messagingEmailProvider(),
|
|
242
324
|
}
|
|
243
325
|
|
|
244
326
|
// ── Phase-1/phase-2 pipelines ───────────────────────────────────────────────
|
|
@@ -16,6 +16,7 @@ import * as EffectLogger$ReventlessCore from "@reventlessdev/reventless-core/src
|
|
|
16
16
|
import * as DynamoDb_Error$ReventlessAws from "../../errors/DynamoDb_Error.res.mjs";
|
|
17
17
|
import * as Messaging_Ses_Backend$ReventlessAws from "../Messaging/Messaging_Ses_Backend.res.mjs";
|
|
18
18
|
import * as Util_DynamoDb_Runtime$ReventlessAws from "../../util/Util_DynamoDb_Runtime.res.mjs";
|
|
19
|
+
import * as Messaging_Log_Backend$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Messaging/Messaging_Log_Backend.res.mjs";
|
|
19
20
|
import * as AutomationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs";
|
|
20
21
|
import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
|
|
21
22
|
import * as Geocoder_AwsLocation_Backend$ReventlessAws from "../Geocoder/Geocoder_AwsLocation_Backend.res.mjs";
|
|
@@ -49,12 +50,47 @@ function decodeEntry(json) {
|
|
|
49
50
|
}));
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
function
|
|
53
|
+
function decodeCompactEntry(modules, sharedQueueUrl, sharedIsFifo, sharedUrns, h, name) {
|
|
54
|
+
let m = Stdlib_Option.getOr(Stdlib_Option.flatMap(modules[name], Stdlib_JSON.Decode.object), {});
|
|
55
|
+
let urns = Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(h["u"], Stdlib_JSON.Decode.array), a => Stdlib_Array.filterMap(a, Stdlib_JSON.Decode.string)), sharedUrns);
|
|
56
|
+
let f = Stdlib_Option.flatMap(h["f"], Stdlib_JSON.Decode.bool);
|
|
57
|
+
return {
|
|
58
|
+
specModule: Stdlib_Option.getOr(strOf(m, "specModule"), ""),
|
|
59
|
+
bodyModule: Stdlib_Option.getOr(strOf(m, "bodyModule"), ""),
|
|
60
|
+
callbackType: Stdlib_Option.getOr(strOf(m, "callbackType"), "automation"),
|
|
61
|
+
queryDbTableName: Stdlib_Option.getOr(strOf(h, "q"), ""),
|
|
62
|
+
dcbQueueUrl: Stdlib_Option.getOr(strOf(h, "k"), sharedQueueUrl),
|
|
63
|
+
commandQueueIsFifo: f !== undefined ? f : sharedIsFifo,
|
|
64
|
+
sourceUrn: Stdlib_Option.getOr(urns[0], ""),
|
|
65
|
+
context: Stdlib_Option.flatMap(m["context"], decodeContext)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseHandlerConfigWithModules(modulesJson, rawJson) {
|
|
53
70
|
if (rawJson === "") {
|
|
54
71
|
return [];
|
|
55
|
-
} else {
|
|
56
|
-
return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(rawJson)), obj => obj["handlers"]), Stdlib_JSON.Decode.array), []), decodeEntry);
|
|
57
72
|
}
|
|
73
|
+
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(rawJson)), {});
|
|
74
|
+
let modules = modulesJson === "" ? ({}) : Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(modulesJson)), {});
|
|
75
|
+
let sharedQueueUrl = Stdlib_Option.getOr(strOf(obj, "queueUrl"), "");
|
|
76
|
+
let sharedIsFifo = Stdlib_Option.flatMap(obj["commandQueueIsFifo"], Stdlib_JSON.Decode.bool);
|
|
77
|
+
let sharedUrns = Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(obj["urns"], Stdlib_JSON.Decode.array), a => Stdlib_Array.filterMap(a, Stdlib_JSON.Decode.string)), []);
|
|
78
|
+
return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["handlers"], Stdlib_JSON.Decode.array), []), item => {
|
|
79
|
+
let h = Stdlib_JSON.Decode.object(item);
|
|
80
|
+
if (h === undefined) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
let name = strOf(h, "n");
|
|
84
|
+
if (name !== undefined) {
|
|
85
|
+
return decodeCompactEntry(modules, sharedQueueUrl, sharedIsFifo, sharedUrns, h, name);
|
|
86
|
+
} else {
|
|
87
|
+
return decodeEntry(item);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseHandlerConfig(rawJson) {
|
|
93
|
+
return parseHandlerConfigWithModules("", rawJson);
|
|
58
94
|
}
|
|
59
95
|
|
|
60
96
|
function warnMissingBodyModule(entry) {
|
|
@@ -150,10 +186,20 @@ function makeLoadTodoItems(queryDbTableName, todoItems, rowSchema, comp) {
|
|
|
150
186
|
};
|
|
151
187
|
}
|
|
152
188
|
|
|
189
|
+
function messagingEmailProvider() {
|
|
190
|
+
let sender = Stdlib_Option.getOr(process.env["MESSAGING_EMAIL_SENDER"], "");
|
|
191
|
+
let match = Stdlib_Option.getOr(process.env["MESSAGING_EMAIL_PROVIDER"], "");
|
|
192
|
+
if (match === "log") {
|
|
193
|
+
return Messaging_Log_Backend$ReventlessCore.provider(sender);
|
|
194
|
+
} else {
|
|
195
|
+
return Messaging_Ses_Backend$ReventlessAws.provider(sender);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
153
199
|
function capabilities() {
|
|
154
200
|
return {
|
|
155
201
|
geocode: text => Geocoder_AwsLocation_Backend$ReventlessAws.search(Stdlib_Option.getOr(process.env["PLACE_INDEX_NAME"], ""), text, undefined),
|
|
156
|
-
messaging:
|
|
202
|
+
messaging: messagingEmailProvider()
|
|
157
203
|
};
|
|
158
204
|
}
|
|
159
205
|
|
|
@@ -247,12 +293,15 @@ export {
|
|
|
247
293
|
strOf,
|
|
248
294
|
decodeContext,
|
|
249
295
|
decodeEntry,
|
|
296
|
+
decodeCompactEntry,
|
|
297
|
+
parseHandlerConfigWithModules,
|
|
250
298
|
parseHandlerConfig,
|
|
251
299
|
warnMissingBodyModule,
|
|
252
300
|
defaultContext,
|
|
253
301
|
makePublishJsons,
|
|
254
302
|
makeSyncTodoItems,
|
|
255
303
|
makeLoadTodoItems,
|
|
304
|
+
messagingEmailProvider,
|
|
256
305
|
capabilities,
|
|
257
306
|
makeAutomationJsonEventsHandler,
|
|
258
307
|
makeOutboundJsonEventsHandler,
|