@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.
Files changed (25) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +8 -8
  3. package/src/Platform.res +71 -6
  4. package/src/Platform.res.mjs +63 -12
  5. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +5 -3
  6. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +6 -1
  7. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +21 -1
  8. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +93 -11
  9. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +53 -4
  10. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +53 -11
  11. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +30 -8
  12. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +5 -3
  13. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +6 -1
  14. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +5 -3
  15. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +6 -1
  16. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +3 -1
  17. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +4 -1
  18. package/src/capability/Capability_Messaging.res +141 -0
  19. package/src/capability/Capability_Messaging.res.mjs +96 -0
  20. package/src/capability/Capability_Messaging_Ses.res +27 -14
  21. package/src/capability/Capability_Messaging_Ses.res.mjs +5 -6
  22. package/tests/AutomationSliceEntryPoint_OpsTest.res +122 -0
  23. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +187 -0
  24. package/tests/Capability_MessagingTest.res +100 -0
  25. package/tests/Capability_MessagingTest.res.mjs +85 -0
@@ -83,6 +83,128 @@ describe("AutomationSliceEntryPoint_Ops.parseHandlerConfig", () => {
83
83
  })
84
84
  })
85
85
 
86
+ // The compact form the builder emits: module paths and context ride in the
87
+ // archive registry, and the DCB queue + source urn are hoisted out of every
88
+ // entry that does not override them. Together these are what keep the Lambda's
89
+ // environment under AWS's 4KB ceiling as a plugin gains slices.
90
+ describe("AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules", () => {
91
+ let modules =
92
+ obj([
93
+ (
94
+ "Restock",
95
+ obj([
96
+ ("specModule", str("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs")),
97
+ ("bodyModule", str("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs")),
98
+ ("callbackType", str("automation")),
99
+ (
100
+ "context",
101
+ obj([
102
+ ("environment", str("alpha")),
103
+ ("platformName", str("OnlineShop")),
104
+ ("pluginName", str("Ordering")),
105
+ ("sliceName", str("Restock")),
106
+ ]),
107
+ ),
108
+ ]),
109
+ ),
110
+ (
111
+ "Ship",
112
+ obj([
113
+ ("specModule", str("@x/plugin/src/Order/OutboundTranslationSlice/Ship.res.mjs")),
114
+ ("bodyModule", str("@x/plugin/src/Order/OutboundTranslationSlice/Ship_Translation.res.mjs")),
115
+ ("callbackType", str("outbound")),
116
+ ]),
117
+ ),
118
+ ])->JSON.stringify
119
+
120
+ let compactConfig = handlers =>
121
+ obj([
122
+ ("queueUrl", str("https://sqs/dcb")),
123
+ ("commandQueueIsFifo", JSON.Encode.bool(false)),
124
+ ("urns", JSON.Encode.array([str("arn:dcb:log")])),
125
+ ("handlers", JSON.Encode.array(handlers)),
126
+ ])->JSON.stringify
127
+
128
+ testSync("a compact entry takes its modules from the registry and the shared defaults", () => {
129
+ let config = compactConfig([obj([("n", str("Restock")), ("q", str("RestockTodo-abc"))])])
130
+ let e =
131
+ AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
132
+ modules,
133
+ config,
134
+ )->Array.getUnsafe(0)
135
+ expect(e.specModule)->toBe("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs")
136
+ expect(e.bodyModule)->toBe("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs")
137
+ expect(e.callbackType)->toBe("automation")
138
+ expect(e.queryDbTableName)->toBe("RestockTodo-abc")
139
+ expect(e.dcbQueueUrl)->toBe("https://sqs/dcb")
140
+ expect(e.commandQueueIsFifo)->toEqual(Some(false))
141
+ expect(e.sourceUrn)->toBe("arn:dcb:log")
142
+ switch e.context {
143
+ | Some(ctx) => expect(ctx.sliceName)->toBe("Restock")
144
+ | None => JsError.throwWithMessage("expected Some(context)")
145
+ }
146
+ })
147
+
148
+ testSync("per-entry queue and urn override the shared defaults", () => {
149
+ let config = compactConfig([
150
+ obj([
151
+ ("n", str("Ship")),
152
+ ("q", str("ShipTodo-abc")),
153
+ ("u", JSON.Encode.array([str("arn:aggregate:orders"), str("arn:dcb:log")])),
154
+ ("k", str("https://sqs/orders.fifo")),
155
+ ("f", JSON.Encode.bool(true)),
156
+ ]),
157
+ ])
158
+ let e =
159
+ AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
160
+ modules,
161
+ config,
162
+ )->Array.getUnsafe(0)
163
+ expect(e.callbackType)->toBe("outbound")
164
+ expect(e.dcbQueueUrl)->toBe("https://sqs/orders.fifo")
165
+ expect(e.commandQueueIsFifo)->toEqual(Some(true))
166
+ // The first of its own urns, not the shared one.
167
+ expect(e.sourceUrn)->toBe("arn:aggregate:orders")
168
+ expect(e.context->Option.isNone)->toBe(true)
169
+ })
170
+
171
+ testSync("a compact entry with no registry match yields no bodyModule to skip on", () => {
172
+ let config = compactConfig([obj([("n", str("Gone")), ("q", str("GoneTodo-abc"))])])
173
+ let e =
174
+ AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
175
+ modules,
176
+ config,
177
+ )->Array.getUnsafe(0)
178
+ expect(e.specModule)->toBe("")
179
+ expect(e.bodyModule)->toBe("")
180
+ })
181
+
182
+ testSync("full-key entries still decode when a registry is present", () => {
183
+ let config = obj([
184
+ (
185
+ "handlers",
186
+ JSON.Encode.array([
187
+ obj([
188
+ ("specModule", str("a.res.mjs")),
189
+ ("bodyModule", str("b.res.mjs")),
190
+ ("callbackType", str("outbound")),
191
+ ("queryDbTableName", str("t")),
192
+ ("dcbQueueUrl", str("u")),
193
+ ("sourceUrn", str("arn")),
194
+ ]),
195
+ ]),
196
+ ),
197
+ ])->JSON.stringify
198
+ let e =
199
+ AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
200
+ modules,
201
+ config,
202
+ )->Array.getUnsafe(0)
203
+ expect(e.specModule)->toBe("a.res.mjs")
204
+ expect(e.dcbQueueUrl)->toBe("u")
205
+ })
206
+ })
207
+
86
208
  // Shared stubs — the pipelines only touch phase1/phase2 plus the injected
87
209
  // sync/publish, so the callback records carry empty TODO dicts.
88
210
  let noopPublish: ReventlessCore.CommandTopic.publishJsons = async _ => ()
@@ -119,6 +119,193 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.parseHandlerConfig", () => {
119
119
  });
120
120
  });
121
121
 
122
+ globalThis.describe("AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules", () => {
123
+ let modules = JSON.stringify(Object.fromEntries([
124
+ [
125
+ "Restock",
126
+ Object.fromEntries([
127
+ [
128
+ "specModule",
129
+ "@x/plugin/src/Order/AutomationSlice/Restock.res.mjs"
130
+ ],
131
+ [
132
+ "bodyModule",
133
+ "@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs"
134
+ ],
135
+ [
136
+ "callbackType",
137
+ "automation"
138
+ ],
139
+ [
140
+ "context",
141
+ Object.fromEntries([
142
+ [
143
+ "environment",
144
+ "alpha"
145
+ ],
146
+ [
147
+ "platformName",
148
+ "OnlineShop"
149
+ ],
150
+ [
151
+ "pluginName",
152
+ "Ordering"
153
+ ],
154
+ [
155
+ "sliceName",
156
+ "Restock"
157
+ ]
158
+ ])
159
+ ]
160
+ ])
161
+ ],
162
+ [
163
+ "Ship",
164
+ Object.fromEntries([
165
+ [
166
+ "specModule",
167
+ "@x/plugin/src/Order/OutboundTranslationSlice/Ship.res.mjs"
168
+ ],
169
+ [
170
+ "bodyModule",
171
+ "@x/plugin/src/Order/OutboundTranslationSlice/Ship_Translation.res.mjs"
172
+ ],
173
+ [
174
+ "callbackType",
175
+ "outbound"
176
+ ]
177
+ ])
178
+ ]
179
+ ]));
180
+ let compactConfig = handlers => JSON.stringify(Object.fromEntries([
181
+ [
182
+ "queueUrl",
183
+ "https://sqs/dcb"
184
+ ],
185
+ [
186
+ "commandQueueIsFifo",
187
+ false
188
+ ],
189
+ [
190
+ "urns",
191
+ ["arn:dcb:log"]
192
+ ],
193
+ [
194
+ "handlers",
195
+ handlers
196
+ ]
197
+ ]));
198
+ globalThis.test("a compact entry takes its modules from the registry and the shared defaults", () => {
199
+ let config = compactConfig([Object.fromEntries([
200
+ [
201
+ "n",
202
+ "Restock"
203
+ ],
204
+ [
205
+ "q",
206
+ "RestockTodo-abc"
207
+ ]
208
+ ])]);
209
+ let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
210
+ globalThis.expect(e.specModule).toBe("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs");
211
+ globalThis.expect(e.bodyModule).toBe("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs");
212
+ globalThis.expect(e.callbackType).toBe("automation");
213
+ globalThis.expect(e.queryDbTableName).toBe("RestockTodo-abc");
214
+ globalThis.expect(e.dcbQueueUrl).toBe("https://sqs/dcb");
215
+ globalThis.expect(e.commandQueueIsFifo).toEqual(false);
216
+ globalThis.expect(e.sourceUrn).toBe("arn:dcb:log");
217
+ let ctx = e.context;
218
+ if (ctx !== undefined) {
219
+ globalThis.expect(ctx.sliceName).toBe("Restock");
220
+ return;
221
+ } else {
222
+ return Stdlib_JsError.throwWithMessage("expected Some(context)");
223
+ }
224
+ });
225
+ globalThis.test("per-entry queue and urn override the shared defaults", () => {
226
+ let config = compactConfig([Object.fromEntries([
227
+ [
228
+ "n",
229
+ "Ship"
230
+ ],
231
+ [
232
+ "q",
233
+ "ShipTodo-abc"
234
+ ],
235
+ [
236
+ "u",
237
+ [
238
+ "arn:aggregate:orders",
239
+ "arn:dcb:log"
240
+ ]
241
+ ],
242
+ [
243
+ "k",
244
+ "https://sqs/orders.fifo"
245
+ ],
246
+ [
247
+ "f",
248
+ true
249
+ ]
250
+ ])]);
251
+ let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
252
+ globalThis.expect(e.callbackType).toBe("outbound");
253
+ globalThis.expect(e.dcbQueueUrl).toBe("https://sqs/orders.fifo");
254
+ globalThis.expect(e.commandQueueIsFifo).toEqual(true);
255
+ globalThis.expect(e.sourceUrn).toBe("arn:aggregate:orders");
256
+ globalThis.expect(Stdlib_Option.isNone(e.context)).toBe(true);
257
+ });
258
+ globalThis.test("a compact entry with no registry match yields no bodyModule to skip on", () => {
259
+ let config = compactConfig([Object.fromEntries([
260
+ [
261
+ "n",
262
+ "Gone"
263
+ ],
264
+ [
265
+ "q",
266
+ "GoneTodo-abc"
267
+ ]
268
+ ])]);
269
+ let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
270
+ globalThis.expect(e.specModule).toBe("");
271
+ globalThis.expect(e.bodyModule).toBe("");
272
+ });
273
+ globalThis.test("full-key entries still decode when a registry is present", () => {
274
+ let config = JSON.stringify(Object.fromEntries([[
275
+ "handlers",
276
+ [Object.fromEntries([
277
+ [
278
+ "specModule",
279
+ "a.res.mjs"
280
+ ],
281
+ [
282
+ "bodyModule",
283
+ "b.res.mjs"
284
+ ],
285
+ [
286
+ "callbackType",
287
+ "outbound"
288
+ ],
289
+ [
290
+ "queryDbTableName",
291
+ "t"
292
+ ],
293
+ [
294
+ "dcbQueueUrl",
295
+ "u"
296
+ ],
297
+ [
298
+ "sourceUrn",
299
+ "arn"
300
+ ]
301
+ ])]
302
+ ]]));
303
+ let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
304
+ globalThis.expect(e.specModule).toBe("a.res.mjs");
305
+ globalThis.expect(e.dcbQueueUrl).toBe("u");
306
+ });
307
+ });
308
+
122
309
  async function noopPublish(param) {
123
310
 
124
311
  }
@@ -0,0 +1,100 @@
1
+ open JestGlobals
2
+
3
+ // Which transport a stack mails through, and who it sends as. There is no
4
+ // default address: a placeholder would provision an identity nobody can verify,
5
+ // so "unset" has to survive the whole way to the deploy gate as absence rather
6
+ // than as an address.
7
+
8
+ module Messaging = Capability_Messaging
9
+
10
+ let withEnv = (key, value, f) => {
11
+ Dict.set(NodeProcess.env, key, value)
12
+ let result = f()
13
+ Dict.delete(NodeProcess.env, key)
14
+ result
15
+ }
16
+
17
+ describe("Capability_Messaging — reading a configured value", () => {
18
+ // Only the env-var rung is reachable here: the rungs below it end at
19
+ // `Pulumi.Config`, which needs a deploy-time runtime this suite does not have.
20
+ // `configured` returns before touching it whenever the variable is set.
21
+ testSync("an address set in the environment is the sender", () => {
22
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", "mail@shop.test", () =>
23
+ Messaging.configured(Messaging.emailSenderKey)
24
+ )
25
+ expect(sender)->toEqual(Some("mail@shop.test"))
26
+ })
27
+
28
+ // A stray `REVENTLESS_MESSAGING_EMAIL_SENDER=` in CI, or a blank line in the
29
+ // sidecar. Reading it as an address asks SES for an identity with none.
30
+ testSync("an empty value is not an address", () => {
31
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", "", () =>
32
+ Messaging.configured(Messaging.emailSenderKey)
33
+ )
34
+ expect(sender)->toEqual(None)
35
+ })
36
+
37
+ // The one that is not obviously empty: a key left with a space after the colon.
38
+ // It reached SES as an identity request for " " before this was trimmed.
39
+ testSync("a whitespace-only value is not an address either", () => {
40
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", " ", () =>
41
+ Messaging.configured(Messaging.emailSenderKey)
42
+ )
43
+ expect(sender)->toEqual(None)
44
+ })
45
+
46
+ testSync("a surviving value is the trimmed one", () => {
47
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", " mail@shop.test ", () =>
48
+ Messaging.configured(Messaging.emailSenderKey)
49
+ )
50
+ expect(sender)->toEqual(Some("mail@shop.test"))
51
+ })
52
+
53
+ testSync("the sms sender reads off its own key", () => {
54
+ let sender = withEnv("REVENTLESS_MESSAGING_SMS_SENDER", "+15550100", () =>
55
+ Messaging.configured(Messaging.smsSenderKey)
56
+ )
57
+ expect(sender)->toEqual(Some("+15550100"))
58
+ })
59
+ })
60
+
61
+ describe("Capability_Messaging — choosing a transport", () => {
62
+ testSync("ses and log are the two a stack can name", () => {
63
+ let pair: (Messaging.emailProvider, Messaging.emailProvider) = (Ses, Log)
64
+ expect((Messaging.parseEmailProvider("ses"), Messaging.parseEmailProvider("log")))->toEqual(pair)
65
+ })
66
+
67
+ // Config files are written by hand; a capitalised or padded value means what it
68
+ // says, and refusing it would be pedantry rather than safety.
69
+ testSync("the value is read case- and whitespace-insensitively", () => {
70
+ expect(Messaging.parseEmailProvider(" LOG "))->toEqual(Messaging.Log)
71
+ })
72
+
73
+ // The one that matters: defaulting a typo to SES would provision a real
74
+ // identity for a stack that asked to only log.
75
+ testSync("an unrecognised transport is refused, not defaulted", () => {
76
+ let outcome = try {
77
+ let _ = Messaging.parseEmailProvider("smtp")
78
+ None
79
+ } catch {
80
+ | JsExn(e) => e->JsExn.message
81
+ }
82
+ expect(outcome->Option.getOr("")->String.includes("is not an email provider"))->toBe(true)
83
+ })
84
+
85
+ // SES is the default so that a deployment saying nothing still mails: a stack
86
+ // that means not to send says so, rather than silence meaning it.
87
+ testSync("a named transport wins over the default", () => {
88
+ let chosen = withEnv("REVENTLESS_MESSAGING_EMAIL_PROVIDER", "log", () => Messaging.emailProvider())
89
+ let expected: Messaging.emailProvider = Log
90
+ expect(chosen)->toEqual(expected)
91
+ })
92
+ })
93
+
94
+ describe("Capability_Messaging — the log transport's default sender", () => {
95
+ // Hard-coded where the SES address is required, because a logged message
96
+ // reaches nobody. `.test` can never resolve, so it cannot be a real inbox.
97
+ testSync("is a reserved address", () => {
98
+ expect(Messaging.logDefaultAddress->String.endsWith(".test"))->toBe(true)
99
+ })
100
+ })
@@ -0,0 +1,85 @@
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 Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
7
+ import * as Capability_Messaging$ReventlessAws from "../src/capability/Capability_Messaging.res.mjs";
8
+
9
+ function withEnv(key, value, f) {
10
+ process.env[key] = value;
11
+ let result = f();
12
+ Stdlib_Dict.$$delete(process.env, key);
13
+ return result;
14
+ }
15
+
16
+ globalThis.describe("Capability_Messaging — reading a configured value", () => {
17
+ globalThis.test("an address set in the environment is the sender", () => {
18
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", "mail@shop.test", () => Capability_Messaging$ReventlessAws.configured(Capability_Messaging$ReventlessAws.emailSenderKey));
19
+ globalThis.expect(sender).toEqual("mail@shop.test");
20
+ });
21
+ globalThis.test("an empty value is not an address", () => {
22
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", "", () => Capability_Messaging$ReventlessAws.configured(Capability_Messaging$ReventlessAws.emailSenderKey));
23
+ globalThis.expect(sender).toEqual(undefined);
24
+ });
25
+ globalThis.test("a whitespace-only value is not an address either", () => {
26
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", " ", () => Capability_Messaging$ReventlessAws.configured(Capability_Messaging$ReventlessAws.emailSenderKey));
27
+ globalThis.expect(sender).toEqual(undefined);
28
+ });
29
+ globalThis.test("a surviving value is the trimmed one", () => {
30
+ let sender = withEnv("REVENTLESS_MESSAGING_EMAIL_SENDER", " mail@shop.test ", () => Capability_Messaging$ReventlessAws.configured(Capability_Messaging$ReventlessAws.emailSenderKey));
31
+ globalThis.expect(sender).toEqual("mail@shop.test");
32
+ });
33
+ globalThis.test("the sms sender reads off its own key", () => {
34
+ let sender = withEnv("REVENTLESS_MESSAGING_SMS_SENDER", "+15550100", () => Capability_Messaging$ReventlessAws.configured(Capability_Messaging$ReventlessAws.smsSenderKey));
35
+ globalThis.expect(sender).toEqual("+15550100");
36
+ });
37
+ });
38
+
39
+ globalThis.describe("Capability_Messaging — choosing a transport", () => {
40
+ globalThis.test("ses and log are the two a stack can name", () => {
41
+ globalThis.expect([
42
+ Capability_Messaging$ReventlessAws.parseEmailProvider("ses"),
43
+ Capability_Messaging$ReventlessAws.parseEmailProvider("log")
44
+ ]).toEqual([
45
+ "Ses",
46
+ "Log"
47
+ ]);
48
+ });
49
+ globalThis.test("the value is read case- and whitespace-insensitively", () => {
50
+ globalThis.expect(Capability_Messaging$ReventlessAws.parseEmailProvider(" LOG ")).toEqual("Log");
51
+ });
52
+ globalThis.test("an unrecognised transport is refused, not defaulted", () => {
53
+ let outcome;
54
+ try {
55
+ Capability_Messaging$ReventlessAws.parseEmailProvider("smtp");
56
+ outcome = undefined;
57
+ } catch (raw_e) {
58
+ let e = Primitive_exceptions.internalToException(raw_e);
59
+ if (e.RE_EXN_ID === "JsExn") {
60
+ outcome = Stdlib_JsExn.message(e._1);
61
+ } else {
62
+ throw e;
63
+ }
64
+ }
65
+ globalThis.expect(Stdlib_Option.getOr(outcome, "").includes("is not an email provider")).toBe(true);
66
+ });
67
+ globalThis.test("a named transport wins over the default", () => {
68
+ let chosen = withEnv("REVENTLESS_MESSAGING_EMAIL_PROVIDER", "log", () => Capability_Messaging$ReventlessAws.emailProvider());
69
+ globalThis.expect(chosen).toEqual("Log");
70
+ });
71
+ });
72
+
73
+ globalThis.describe("Capability_Messaging — the log transport's default sender", () => {
74
+ globalThis.test("is a reserved address", () => {
75
+ globalThis.expect(Capability_Messaging$ReventlessAws.logDefaultAddress.endsWith(".test")).toBe(true);
76
+ });
77
+ });
78
+
79
+ let Messaging;
80
+
81
+ export {
82
+ Messaging,
83
+ withEnv,
84
+ }
85
+ /* Not a pure module */