@reventlessdev/reventless-aws 3.0.0-alpha.400 → 3.0.0-alpha.402
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 +33 -0
- package/package.json +9 -9
- package/src/Platform.res.mjs +2 -2
- package/src/adapter/Mcp/MCP_Lambda.res +1 -1
- package/src/adapter/Mcp/MCP_Lambda.res.mjs +0 -1
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +6 -14
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +13 -123
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +8 -74
- package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
- package/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs +1 -1
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +5 -5
- package/src/adapter/Upload/Upload_Presign_S3_Ops.res +13 -3
- package/src/adapter/Upload/Upload_Presign_S3_Ops.res.mjs +7 -2
- package/src/plugin/cloner/ClonerRunner_Fargate.res +1 -2
- package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +1 -2
- package/src/plugin/runtime/PluginRuntime_Builder.res +13 -49
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -15
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res.mjs +1 -1
- package/tests/EventCollectorEntryPoint_OpsTest.res +27 -32
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +28 -46
- package/tests/EventMapperEntryPoint_OpsTest.res +1 -1
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +1 -1
- package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +3 -3
- package/tests/PgPipeline_IntegrationTest.res.mjs +1 -1
- package/tests/Upload_ReleaseRuleTest.res +24 -0
- package/tests/Upload_ReleaseRuleTest.res.mjs +15 -0
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res.mjs +1 -1
- package/src/components/ForeignReadModel_Builder.res +0 -19
- package/src/components/ForeignReadModel_Builder.res.mjs +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,39 @@
|
|
|
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.402 (2026-10-29)
|
|
7
|
+
|
|
8
|
+
* feat(core)!: the caller's IP address leaves the messages a request causes ([aa9e8c7](https://github.com/ReventlessDev/reventless-core/commit/aa9e8c79c0360528a5cfeda2bb88d1da52f28674))
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **upload:** an upload's path carries nothing a person typed ([456c88c](https://github.com/ReventlessDev/reventless-core/commit/456c88c4ccee6a50943e3a71011a2e78aef0d770))
|
|
12
|
+
|
|
13
|
+
### BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* `Message.generateMeta` no longer takes `~ip`. Drop the argument; a
|
|
16
|
+
message that must carry the address declares it as a field of its own payload.
|
|
17
|
+
|
|
18
|
+
Step PD3 of docs/plans/a-personal-field-is-marked-redacted-and-encrypted-at-the-seam.md.
|
|
19
|
+
Claude-Session: https://claude.ai/code/session_01HiQfdxVEsHwLHRoHSUcus1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# 3.0.0-alpha.401 (2026-10-29)
|
|
24
|
+
|
|
25
|
+
* refactor!: drop `dcbSources`, the cross-plugin DCB subscription nobody declared ([cf3b66d](https://github.com/ReventlessDev/reventless-core/commit/cf3b66d8c1b5d87673197b64eaee174598bb8253))
|
|
26
|
+
* refactor!: a read model hears another plugin only through an Extension and a slice ([15c7cae](https://github.com/ReventlessDev/reventless-core/commit/15c7cae214d5aa04404779fcd87a8429aad1965c))
|
|
27
|
+
|
|
28
|
+
### BREAKING CHANGES
|
|
29
|
+
|
|
30
|
+
* `Reventless.Plugin.extensionDefinition` has no `dcbSources`
|
|
31
|
+
field, and `Platform_PluginExtensions.dcbSources` is gone from the GraphQL
|
|
32
|
+
schema. A query that selects it fails as a whole; drop the selection.
|
|
33
|
+
* `ReventlessInfra.Extension.T.make` and the core
|
|
34
|
+
`Extension_Builder.make` no longer take `~readModelNamesForSourceName` and
|
|
35
|
+
`~publishToReadModels`. Both arguments did nothing; drop them at the call site.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
6
39
|
# 3.0.0-alpha.400 (2026-10-25)
|
|
7
40
|
|
|
8
41
|
**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.402",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -22,19 +22,19 @@
|
|
|
22
22
|
"sury": "11.0.0",
|
|
23
23
|
"uuid": "^13.0.0",
|
|
24
24
|
"yaml": "^2.8.3",
|
|
25
|
+
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.36",
|
|
25
26
|
"@reventlessdev/rescript-effect": "1.0.0-alpha.1",
|
|
26
27
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.13",
|
|
27
28
|
"@reventlessdev/rescript-node": "2.0.0-alpha.23",
|
|
28
|
-
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.
|
|
29
|
-
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.36",
|
|
30
|
-
"@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
|
|
31
|
-
"@reventlessdev/rescript-web": "1.0.0-alpha.8",
|
|
29
|
+
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.35",
|
|
32
30
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
|
|
33
|
-
"@reventlessdev/
|
|
34
|
-
"@reventlessdev/reventless-
|
|
31
|
+
"@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
|
|
32
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.312",
|
|
33
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.209",
|
|
35
34
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.42",
|
|
36
|
-
"@reventlessdev/reventless-
|
|
37
|
-
"@reventlessdev/
|
|
35
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.180",
|
|
36
|
+
"@reventlessdev/rescript-web": "1.0.0-alpha.8",
|
|
37
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.176"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"rescript": "12.3.1",
|
package/src/Platform.res.mjs
CHANGED
|
@@ -1552,7 +1552,7 @@ function MakeWithConfig(Config) {
|
|
|
1552
1552
|
};
|
|
1553
1553
|
let publishHeartbeat = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO");
|
|
1554
1554
|
let message_id = hbConfig.pluginId;
|
|
1555
|
-
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name,
|
|
1555
|
+
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name, "DeployHeartbeat", undefined, undefined, undefined, undefined, undefined);
|
|
1556
1556
|
let message_commandJson = Util_Sury$Reventless.toJson({
|
|
1557
1557
|
TAG: "RedetectPlugin",
|
|
1558
1558
|
_0: hbConfig.heartbeatTimeout
|
|
@@ -2962,7 +2962,7 @@ function Make($star) {
|
|
|
2962
2962
|
};
|
|
2963
2963
|
let publishHeartbeat = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO");
|
|
2964
2964
|
let message_id = hbConfig.pluginId;
|
|
2965
|
-
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name,
|
|
2965
|
+
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name, "DeployHeartbeat", undefined, undefined, undefined, undefined, undefined);
|
|
2966
2966
|
let message_commandJson = Util_Sury$Reventless.toJson({
|
|
2967
2967
|
TAG: "RedetectPlugin",
|
|
2968
2968
|
_0: hbConfig.heartbeatTimeout
|
|
@@ -383,7 +383,7 @@ let dispatchTool = async (tool: mcpToolEntry, args: JSON.t, identity: Reventless
|
|
|
383
383
|
let payload: ReventlessCore.CommandGenerator.payload = {
|
|
384
384
|
command: tool.name,
|
|
385
385
|
arguments: args->Obj.magic,
|
|
386
|
-
meta: {
|
|
386
|
+
meta: {user: identity.userId, info: `mcp/tools/${tool.name}`},
|
|
387
387
|
identity,
|
|
388
388
|
}
|
|
389
389
|
// Route through the shared dispatch helper so this tool call gets the same
|
|
@@ -254,7 +254,6 @@ async function dispatchTool(tool, args, identity) {
|
|
|
254
254
|
let generateCommand = CommandGenerator_CallbackResMjs.makeGenerateCommand(publishJsons, tool.name, Sury.json, tool.componentKind, false);
|
|
255
255
|
let payload_command = tool.name;
|
|
256
256
|
let payload_meta = {
|
|
257
|
-
ip: [],
|
|
258
257
|
user: identity.userId,
|
|
259
258
|
info: `mcp/tools/` + tool.name
|
|
260
259
|
};
|
|
@@ -42,16 +42,12 @@
|
|
|
42
42
|
// "mappingsModule": string, // dynamic import for the user extension file (`module Mapping`)
|
|
43
43
|
// "delegateModule": string, // dynamic import for the Delegate spec (aggregate / slice)
|
|
44
44
|
// "extensionPointName": string, // service key for incomingExtensionEventHandlers (e.g. "Ordering.Orders")
|
|
45
|
-
// "aggregateNames": string[]
|
|
46
|
-
// "readModelNames": string[] // RMs this extension may enqueue events into (filter into readModelQueueUrls)
|
|
45
|
+
// "aggregateNames": string[] // outgoing service keys + filter into top-level publishToAggregates
|
|
47
46
|
// }
|
|
48
|
-
// ]
|
|
49
|
-
// (publishToAggregates
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
// "readModelNamesForSourceName": { // deploy-side inversion of RM.sourceNames; sourceServiceName → [rmName]
|
|
53
|
-
// "Ordering.Orders": ["ProductDemands"]
|
|
54
|
-
// }
|
|
47
|
+
// ]
|
|
48
|
+
// (publishToAggregates moved to the queueUrls.json asset — one entry per
|
|
49
|
+
// target, so it outgrew the 4KB environment. It maps a target name to the
|
|
50
|
+
// PTA_ env var holding its SQS URL.)
|
|
55
51
|
// }
|
|
56
52
|
//
|
|
57
53
|
// Why this shape and not separate entry points per Lambda: at deploy time both the
|
|
@@ -187,8 +183,6 @@ async function buildHandler() {
|
|
|
187
183
|
const ops = extensionOperationsMake(specMod)(extBuilder.ConnectPluginMappings)({
|
|
188
184
|
publishToAggregates,
|
|
189
185
|
publishToPluginExtensionPoint,
|
|
190
|
-
readModelNamesForSourceName: {},
|
|
191
|
-
publishToReadModels: {},
|
|
192
186
|
queryEngine,
|
|
193
187
|
});
|
|
194
188
|
connectExtensionHandler = {
|
|
@@ -229,12 +223,10 @@ async function buildHandler() {
|
|
|
229
223
|
mappings: [transformedMapping],
|
|
230
224
|
};
|
|
231
225
|
|
|
232
|
-
const dicts = Ops.extensionPublishDicts(
|
|
226
|
+
const dicts = Ops.extensionPublishDicts(queueUrlNames, ext);
|
|
233
227
|
const ops = extensionOperationsMake(epSpec)(mappingsModule)({
|
|
234
228
|
publishToAggregates: dicts.publishToAggregates,
|
|
235
229
|
publishToPluginExtensionPoint,
|
|
236
|
-
readModelNamesForSourceName: dicts.readModelNamesForSourceName,
|
|
237
|
-
publishToReadModels: dicts.publishToReadModels,
|
|
238
230
|
queryEngine,
|
|
239
231
|
});
|
|
240
232
|
|
|
@@ -61,15 +61,6 @@ external scanByTableName: (
|
|
|
61
61
|
let exnMessage = (exn: exn): string =>
|
|
62
62
|
exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("unknown error")
|
|
63
63
|
|
|
64
|
-
// ── Absent-field normalization ──────────────────────────────────────────────
|
|
65
|
-
// pluginDefinition's optionals omit the key, so a plain JSON.parse (not a sury
|
|
66
|
-
// parse) yields `undefined` and ReScript reads it as None unaided. Kept because
|
|
67
|
-
// the same records also arrive from stores and peers written when an absent
|
|
68
|
-
// field was `null`, which ReScript's option would misread as Some.
|
|
69
|
-
|
|
70
|
-
external optionAsNullable: option<'a> => Nullable.t<'a> = "%identity"
|
|
71
|
-
let jsOption = (v: option<'a>): option<'a> => v->optionAsNullable->Nullable.toOption
|
|
72
|
-
|
|
73
64
|
// ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
|
|
74
65
|
// Written by PluginRuntime_Builder.forPluginEventCollector; the field-by-field
|
|
75
66
|
// schema is documented in EventCollectorEntryPoint.mjs. Explicitly decoded (no
|
|
@@ -94,7 +85,6 @@ type extensionConfig = {
|
|
|
94
85
|
delegateModule: string,
|
|
95
86
|
extensionPointName: string,
|
|
96
87
|
aggregateNames: array<string>,
|
|
97
|
-
readModelNames: array<string>,
|
|
98
88
|
}
|
|
99
89
|
type handlerConfig = {
|
|
100
90
|
queueUrl: string,
|
|
@@ -108,17 +98,13 @@ type handlerConfig = {
|
|
|
108
98
|
extensionPoints: array<extensionPointConfig>,
|
|
109
99
|
connectExtension?: connectExtensionConfig,
|
|
110
100
|
extensions: array<extensionConfig>,
|
|
111
|
-
readModelNamesForSourceName: dict<array<string>>,
|
|
112
101
|
}
|
|
113
102
|
|
|
114
|
-
// The target→env-var
|
|
103
|
+
// The target→env-var map that used to ride HANDLER_CONFIG, now read from
|
|
115
104
|
// queueUrls.json (PluginRuntime_Builder). Values are env var NAMES, not URLs —
|
|
116
105
|
// the URLs stay in the environment because the builder cannot resolve those
|
|
117
106
|
// Outputs in bulk.
|
|
118
|
-
type queueUrlNames = {
|
|
119
|
-
publishToAggregates: dict<string>,
|
|
120
|
-
readModelQueueUrls: dict<string>,
|
|
121
|
-
}
|
|
107
|
+
type queueUrlNames = {publishToAggregates: dict<string>}
|
|
122
108
|
|
|
123
109
|
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
124
110
|
obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
@@ -196,7 +182,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
196
182
|
delegateModule: ext->strOf("delegateModule")->Option.getOr(""),
|
|
197
183
|
extensionPointName,
|
|
198
184
|
aggregateNames: ext->strArrOf("aggregateNames"),
|
|
199
|
-
readModelNames: ext->strArrOf("readModelNames"),
|
|
200
185
|
}
|
|
201
186
|
})
|
|
202
187
|
)
|
|
@@ -211,17 +196,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
211
196
|
mappingsModule: ce->strOf("mappingsModule")->Option.getOr(""),
|
|
212
197
|
extensionPointName: ce->strOf("extensionPointName")->Option.getOr(""),
|
|
213
198
|
})
|
|
214
|
-
let readModelNamesForSourceName =
|
|
215
|
-
obj
|
|
216
|
-
->Dict.get("readModelNamesForSourceName")
|
|
217
|
-
->Option.flatMap(JSON.Decode.object)
|
|
218
|
-
->Option.getOr(Dict.make())
|
|
219
|
-
->Dict.toArray
|
|
220
|
-
->Array.map(((k, v)) => (
|
|
221
|
-
k,
|
|
222
|
-
v->JSON.Decode.array->Option.getOr([])->Array.filterMap(JSON.Decode.string),
|
|
223
|
-
))
|
|
224
|
-
->Dict.fromArray
|
|
225
199
|
{
|
|
226
200
|
queueUrl: obj->strOf("queueUrl")->Option.getOr(""),
|
|
227
201
|
pluginExtensionPointCmdTopicUrl: obj
|
|
@@ -236,7 +210,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
236
210
|
extensionPoints,
|
|
237
211
|
?connectExtension,
|
|
238
212
|
extensions,
|
|
239
|
-
readModelNamesForSourceName,
|
|
240
213
|
}
|
|
241
214
|
}
|
|
242
215
|
|
|
@@ -253,7 +226,6 @@ type pluginProjection = {
|
|
|
253
226
|
eventCollector: string,
|
|
254
227
|
extensions: array<Reventless.Plugin.extensionDefinition>,
|
|
255
228
|
extensionPoints: array<Reventless.Plugin.extensionPointDefinition>,
|
|
256
|
-
dcbEventLog?: Reventless.Plugin.dcbEventLogDefinition,
|
|
257
229
|
}
|
|
258
230
|
|
|
259
231
|
let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
@@ -262,17 +234,6 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
262
234
|
| Some(obj) =>
|
|
263
235
|
switch (obj->strOf("id"), obj->strOf("status")) {
|
|
264
236
|
| (Some(id), Some(status)) =>
|
|
265
|
-
let dcbEventLog =
|
|
266
|
-
obj
|
|
267
|
-
->Dict.get("dcbEventLog")
|
|
268
|
-
->Option.flatMap(JSON.Decode.object)
|
|
269
|
-
->Option.flatMap(d =>
|
|
270
|
-
switch (d->strOf("name"), d->strOf("eventTopicArn")) {
|
|
271
|
-
| (Some(name), Some(eventTopicArn)) =>
|
|
272
|
-
Some(({name, eventTopicArn}: Reventless.Plugin.dcbEventLogDefinition))
|
|
273
|
-
| _ => None
|
|
274
|
-
}
|
|
275
|
-
)
|
|
276
237
|
let extensions =
|
|
277
238
|
obj
|
|
278
239
|
->Dict.get("extensions")
|
|
@@ -288,7 +249,6 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
288
249
|
(extensionPointName): Reventless.Plugin.extensionDefinition => {
|
|
289
250
|
name: eo->strOf("name")->Option.getOr(""),
|
|
290
251
|
extensionPointName,
|
|
291
|
-
dcbSources: eo->strArrOf("dcbSources"),
|
|
292
252
|
},
|
|
293
253
|
)
|
|
294
254
|
)
|
|
@@ -323,30 +283,26 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
323
283
|
eventCollector: obj->strOf("eventCollector")->Option.getOr(""),
|
|
324
284
|
extensions,
|
|
325
285
|
extensionPoints,
|
|
326
|
-
?dcbEventLog,
|
|
327
286
|
})
|
|
328
287
|
| _ => None
|
|
329
288
|
}
|
|
330
289
|
}
|
|
331
290
|
|
|
332
291
|
// A pluginDefinition viewed as a projection (the manage logic reads only the
|
|
333
|
-
// shared subset; `status` is never read for the acting plugin).
|
|
334
|
-
// fields stay normalized: a definition arriving over the wire, rather than from
|
|
335
|
-
// loadPluginDefinition's decode, may still carry null for an absent field.
|
|
292
|
+
// shared subset; `status` is never read for the acting plugin).
|
|
336
293
|
let projectionOfDefinition = (d: Reventless.Plugin.pluginDefinition): pluginProjection => {
|
|
337
294
|
id: d.id,
|
|
338
295
|
status: "",
|
|
339
296
|
eventCollector: d.eventCollector,
|
|
340
297
|
extensions: d.extensions,
|
|
341
298
|
extensionPoints: d.extensionPoints,
|
|
342
|
-
dcbEventLog: ?(d.dcbEventLog->jsOption),
|
|
343
299
|
}
|
|
344
300
|
|
|
345
301
|
// ── Cross-plugin SNS subscription manager ───────────────────────────────────
|
|
346
302
|
// When DoConnectPlugin / DoDisconnectPlugin fires on the admin EP mapping, scan
|
|
347
303
|
// the Plugin RM for currently-Connected peers and wire (or tear down) SNS
|
|
348
304
|
// subscriptions between this plugin's EventCollector queue and peer plugins'
|
|
349
|
-
// EP
|
|
305
|
+
// EP event topics — in both directions. subscribeQueueToTopic is idempotent
|
|
350
306
|
// (lists existing subscriptions first); unsubscribeQueueFromTopic is
|
|
351
307
|
// best-effort. Failures are logged but do not throw — admin's RM projection
|
|
352
308
|
// still proceeds and cold-start reconciliation re-attempts missed work.
|
|
@@ -453,50 +409,6 @@ let makeManageSubscriptions = (tableName: string): option<
|
|
|
453
409
|
}
|
|
454
410
|
}
|
|
455
411
|
}
|
|
456
|
-
// DCB sources this plugin's extensions consume: owning peer's DCB
|
|
457
|
-
// EventTopic → this EC queue.
|
|
458
|
-
for i in 0 to pluginDef.extensions->Array.length - 1 {
|
|
459
|
-
let ext = pluginDef.extensions->Array.getUnsafe(i)
|
|
460
|
-
for j in 0 to ext.dcbSources->Array.length - 1 {
|
|
461
|
-
let dcbSourceName = ext.dcbSources->Array.getUnsafe(j)
|
|
462
|
-
switch peers->Array.find(p =>
|
|
463
|
-
switch p.dcbEventLog {
|
|
464
|
-
| Some(l) => l.name == dcbSourceName
|
|
465
|
-
| None => false
|
|
466
|
-
}
|
|
467
|
-
) {
|
|
468
|
-
| Some(peer) =>
|
|
469
|
-
switch peer.dcbEventLog {
|
|
470
|
-
| Some(l) =>
|
|
471
|
-
await op(
|
|
472
|
-
pluginDef.eventCollector,
|
|
473
|
-
l.eventTopicArn,
|
|
474
|
-
`dcb ${dcbSourceName} -> ${pluginDef.id}`,
|
|
475
|
-
)
|
|
476
|
-
| None => ()
|
|
477
|
-
}
|
|
478
|
-
| None => ()
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
// If THIS plugin has a DCB EventLog: its topic → each consuming
|
|
483
|
-
// peer's EC queue.
|
|
484
|
-
switch pluginDef.dcbEventLog {
|
|
485
|
-
| Some(myDcb) =>
|
|
486
|
-
for j in 0 to peers->Array.length - 1 {
|
|
487
|
-
let peer = peers->Array.getUnsafe(j)
|
|
488
|
-
let consuming =
|
|
489
|
-
peer.extensions->Array.some(e => e.dcbSources->Array.includes(myDcb.name))
|
|
490
|
-
if consuming {
|
|
491
|
-
await op(
|
|
492
|
-
peer.eventCollector,
|
|
493
|
-
myDcb.eventTopicArn,
|
|
494
|
-
`dcb ${myDcb.name} -> ${peer.id}`,
|
|
495
|
-
)
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
| None => ()
|
|
499
|
-
}
|
|
500
412
|
}
|
|
501
413
|
},
|
|
502
414
|
)
|
|
@@ -630,20 +542,12 @@ let buildPublishToAggregates = (map: dict<string>): dict<
|
|
|
630
542
|
))
|
|
631
543
|
->Dict.fromArray
|
|
632
544
|
|
|
633
|
-
// Per-extension publish
|
|
634
|
-
//
|
|
635
|
-
//
|
|
636
|
-
type extensionDicts = {
|
|
637
|
-
publishToAggregates: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
638
|
-
publishToReadModels: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
639
|
-
readModelNamesForSourceName: dict<array<string>>,
|
|
640
|
-
}
|
|
545
|
+
// Per-extension publish dict, filtered to the extension's declared aggregate
|
|
546
|
+
// names; entries with no env-var mapping or an empty URL are skipped (same as
|
|
547
|
+
// the former JS `continue`s).
|
|
548
|
+
type extensionDicts = {publishToAggregates: dict<ReventlessCore.CommandTopic.publishJsons>}
|
|
641
549
|
|
|
642
|
-
let extensionPublishDicts = (
|
|
643
|
-
config: handlerConfig,
|
|
644
|
-
names: queueUrlNames,
|
|
645
|
-
ext: extensionConfig,
|
|
646
|
-
): extensionDicts => {
|
|
550
|
+
let extensionPublishDicts = (names: queueUrlNames, ext: extensionConfig): extensionDicts => {
|
|
647
551
|
let resolve = (names: array<string>, envVarNames: dict<string>) =>
|
|
648
552
|
names
|
|
649
553
|
->Array.filterMap(name =>
|
|
@@ -654,18 +558,7 @@ let extensionPublishDicts = (
|
|
|
654
558
|
->Option.map(queueUrl => (name, makePublishJsons(queueUrl)))
|
|
655
559
|
)
|
|
656
560
|
->Dict.fromArray
|
|
657
|
-
{
|
|
658
|
-
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates),
|
|
659
|
-
publishToReadModels: resolve(ext.readModelNames, names.readModelQueueUrls),
|
|
660
|
-
// Scoped to this extension's EP — Extension_Operations uses this dict to
|
|
661
|
-
// decide which RMs receive each incoming event.
|
|
662
|
-
readModelNamesForSourceName: Dict.fromArray([
|
|
663
|
-
(
|
|
664
|
-
ext.extensionPointName,
|
|
665
|
-
config.readModelNamesForSourceName->Dict.get(ext.extensionPointName)->Option.getOr([]),
|
|
666
|
-
),
|
|
667
|
-
]),
|
|
668
|
-
}
|
|
561
|
+
{publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)}
|
|
669
562
|
}
|
|
670
563
|
|
|
671
564
|
let warnSkippedExtension = (ext: extensionConfig) =>
|
|
@@ -702,8 +595,8 @@ let loadPluginDefinition = (): Reventless.Plugin.pluginDefinition =>
|
|
|
702
595
|
)
|
|
703
596
|
}
|
|
704
597
|
|
|
705
|
-
// The target→env-var
|
|
706
|
-
// nowhere writes empty
|
|
598
|
+
// The target→env-var map, read once at cold start. A plugin that publishes
|
|
599
|
+
// nowhere writes an empty map rather than omitting the file, so a read failure is
|
|
707
600
|
// a genuinely broken archive and not an empty-map case worth guessing at.
|
|
708
601
|
let loadQueueUrlNames = (): queueUrlNames =>
|
|
709
602
|
try {
|
|
@@ -713,10 +606,7 @@ let loadQueueUrlNames = (): queueUrlNames =>
|
|
|
713
606
|
->JSON.parseOrThrow
|
|
714
607
|
->JSON.Decode.object
|
|
715
608
|
->Option.getOr(Dict.make())
|
|
716
|
-
{
|
|
717
|
-
publishToAggregates: obj->strDictOf("publishToAggregates"),
|
|
718
|
-
readModelQueueUrls: obj->strDictOf("readModelQueueUrls"),
|
|
719
|
-
}
|
|
609
|
+
{publishToAggregates: obj->strDictOf("publishToAggregates")}
|
|
720
610
|
} catch {
|
|
721
611
|
| exn =>
|
|
722
612
|
JsError.throwWithMessage("Failed to load queueUrls.json from asset zip: " ++ exnMessage(exn))
|
|
@@ -7,7 +7,6 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
7
7
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
8
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
11
10
|
import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
|
|
12
11
|
import * as SNS_Helpers$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/SNS_Helpers.res.mjs";
|
|
13
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
@@ -64,14 +63,6 @@ function exnMessage(exn) {
|
|
|
64
63
|
return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown error");
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
function jsOption(v) {
|
|
68
|
-
if (v == null) {
|
|
69
|
-
return;
|
|
70
|
-
} else {
|
|
71
|
-
return Primitive_option.some(v);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
66
|
function strOf(obj, key) {
|
|
76
67
|
return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
|
|
77
68
|
}
|
|
@@ -135,8 +126,7 @@ function parseHandlerConfig(rawJson) {
|
|
|
135
126
|
mappingsModule: Stdlib_Option.getOr(strOf(ext, "mappingsModule"), ""),
|
|
136
127
|
delegateModule: Stdlib_Option.getOr(strOf(ext, "delegateModule"), ""),
|
|
137
128
|
extensionPointName: extensionPointName,
|
|
138
|
-
aggregateNames: strArrOf(ext, "aggregateNames")
|
|
139
|
-
readModelNames: strArrOf(ext, "readModelNames")
|
|
129
|
+
aggregateNames: strArrOf(ext, "aggregateNames")
|
|
140
130
|
};
|
|
141
131
|
})) : Stdlib_JsError.throwWithMessage("HANDLER_CONFIG.extensions must be an array");
|
|
142
132
|
let connectExtension = Stdlib_Option.map(Stdlib_Option.flatMap(obj["connectExtension"], Stdlib_JSON.Decode.object), ce => ({
|
|
@@ -144,10 +134,6 @@ function parseHandlerConfig(rawJson) {
|
|
|
144
134
|
mappingsModule: Stdlib_Option.getOr(strOf(ce, "mappingsModule"), ""),
|
|
145
135
|
extensionPointName: Stdlib_Option.getOr(strOf(ce, "extensionPointName"), "")
|
|
146
136
|
}));
|
|
147
|
-
let readModelNamesForSourceName = Object.fromEntries(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["readModelNamesForSourceName"], Stdlib_JSON.Decode.object), {})).map(param => [
|
|
148
|
-
param[0],
|
|
149
|
-
Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(param[1]), []), Stdlib_JSON.Decode.string)
|
|
150
|
-
]));
|
|
151
137
|
return {
|
|
152
138
|
queueUrl: Stdlib_Option.getOr(strOf(obj, "queueUrl"), ""),
|
|
153
139
|
pluginExtensionPointCmdTopicUrl: Stdlib_Option.getOr(strOf(obj, "pluginExtensionPointCmdTopicUrl"), ""),
|
|
@@ -159,8 +145,7 @@ function parseHandlerConfig(rawJson) {
|
|
|
159
145
|
schedulerQueueName: Stdlib_Option.getOr(strOf(obj, "schedulerQueueName"), ""),
|
|
160
146
|
extensionPoints: extensionPoints,
|
|
161
147
|
connectExtension: connectExtension,
|
|
162
|
-
extensions: extensions
|
|
163
|
-
readModelNamesForSourceName: readModelNamesForSourceName
|
|
148
|
+
extensions: extensions
|
|
164
149
|
};
|
|
165
150
|
}
|
|
166
151
|
|
|
@@ -177,20 +162,9 @@ function projectPluginRow(row) {
|
|
|
177
162
|
if (match$1 === undefined) {
|
|
178
163
|
return;
|
|
179
164
|
}
|
|
180
|
-
let dcbEventLog = Stdlib_Option.flatMap(Stdlib_Option.flatMap(obj["dcbEventLog"], Stdlib_JSON.Decode.object), d => {
|
|
181
|
-
let match = strOf(d, "name");
|
|
182
|
-
let match$1 = strOf(d, "eventTopicArn");
|
|
183
|
-
if (match !== undefined && match$1 !== undefined) {
|
|
184
|
-
return {
|
|
185
|
-
name: match,
|
|
186
|
-
eventTopicArn: match$1
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
165
|
let extensions = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["extensions"], Stdlib_JSON.Decode.array), []), e => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(e), eo => Stdlib_Option.map(strOf(eo, "extensionPointName"), extensionPointName => ({
|
|
191
166
|
name: Stdlib_Option.getOr(strOf(eo, "name"), ""),
|
|
192
|
-
extensionPointName: extensionPointName
|
|
193
|
-
dcbSources: strArrOf(eo, "dcbSources")
|
|
167
|
+
extensionPointName: extensionPointName
|
|
194
168
|
}))));
|
|
195
169
|
let extensionPoints = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["extensionPoints"], Stdlib_JSON.Decode.array), []), ep => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(ep), epo => {
|
|
196
170
|
let match = strOf(epo, "name");
|
|
@@ -208,8 +182,7 @@ function projectPluginRow(row) {
|
|
|
208
182
|
status: match$1,
|
|
209
183
|
eventCollector: Stdlib_Option.getOr(strOf(obj, "eventCollector"), ""),
|
|
210
184
|
extensions: extensions,
|
|
211
|
-
extensionPoints: extensionPoints
|
|
212
|
-
dcbEventLog: dcbEventLog
|
|
185
|
+
extensionPoints: extensionPoints
|
|
213
186
|
};
|
|
214
187
|
}
|
|
215
188
|
|
|
@@ -219,8 +192,7 @@ function projectionOfDefinition(d) {
|
|
|
219
192
|
status: "",
|
|
220
193
|
eventCollector: d.eventCollector,
|
|
221
194
|
extensions: d.extensions,
|
|
222
|
-
extensionPoints: d.extensionPoints
|
|
223
|
-
dcbEventLog: Primitive_option.fromNullable(d.dcbEventLog)
|
|
195
|
+
extensionPoints: d.extensionPoints
|
|
224
196
|
};
|
|
225
197
|
}
|
|
226
198
|
|
|
@@ -334,37 +306,6 @@ function makeManageSubscriptions(tableName) {
|
|
|
334
306
|
}
|
|
335
307
|
}
|
|
336
308
|
}
|
|
337
|
-
for (let i$2 = 0, i_finish$2 = pluginDef.extensions.length; i$2 < i_finish$2; ++i$2) {
|
|
338
|
-
let ext$1 = pluginDef.extensions[i$2];
|
|
339
|
-
for (let j$2 = 0, j_finish$2 = ext$1.dcbSources.length; j$2 < j_finish$2; ++j$2) {
|
|
340
|
-
let dcbSourceName = ext$1.dcbSources[j$2];
|
|
341
|
-
let peer$2 = peers.find(p => {
|
|
342
|
-
let l = p.dcbEventLog;
|
|
343
|
-
if (l !== undefined) {
|
|
344
|
-
return l.name === dcbSourceName;
|
|
345
|
-
} else {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
if (peer$2 !== undefined) {
|
|
350
|
-
let l = peer$2.dcbEventLog;
|
|
351
|
-
if (l !== undefined) {
|
|
352
|
-
await op(pluginDef.eventCollector, l.eventTopicArn, `dcb ` + dcbSourceName + ` -> ` + pluginDef.id);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
let myDcb = pluginDef.dcbEventLog;
|
|
358
|
-
if (myDcb === undefined) {
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
for (let j$3 = 0, j_finish$3 = peers.length; j$3 < j_finish$3; ++j$3) {
|
|
362
|
-
let peer$3 = peers[j$3];
|
|
363
|
-
let consuming = peer$3.extensions.some(e => e.dcbSources.includes(myDcb.name));
|
|
364
|
-
if (consuming) {
|
|
365
|
-
await op(peer$3.eventCollector, myDcb.eventTopicArn, `dcb ` + myDcb.name + ` -> ` + peer$3.id);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
309
|
};
|
|
369
310
|
}
|
|
370
311
|
}
|
|
@@ -494,18 +435,13 @@ function buildPublishToAggregates(map) {
|
|
|
494
435
|
]));
|
|
495
436
|
}
|
|
496
437
|
|
|
497
|
-
function extensionPublishDicts(
|
|
438
|
+
function extensionPublishDicts(names, ext) {
|
|
498
439
|
let resolve = (names, envVarNames) => Object.fromEntries(Stdlib_Array.filterMap(names, name => Stdlib_Option.map(Stdlib_Option.filter(Stdlib_Option.flatMap(envVarNames[name], envVarName => process.env[envVarName]), queueUrl => queueUrl !== ""), queueUrl => [
|
|
499
440
|
name,
|
|
500
441
|
makePublishJsons(queueUrl)
|
|
501
442
|
])));
|
|
502
443
|
return {
|
|
503
|
-
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)
|
|
504
|
-
publishToReadModels: resolve(ext.readModelNames, names.readModelQueueUrls),
|
|
505
|
-
readModelNamesForSourceName: Object.fromEntries([[
|
|
506
|
-
ext.extensionPointName,
|
|
507
|
-
Stdlib_Option.getOr(config.readModelNamesForSourceName[ext.extensionPointName], [])
|
|
508
|
-
]])
|
|
444
|
+
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)
|
|
509
445
|
};
|
|
510
446
|
}
|
|
511
447
|
|
|
@@ -532,8 +468,7 @@ function loadQueueUrlNames() {
|
|
|
532
468
|
try {
|
|
533
469
|
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(Nodefs.readFileSync(assetPath("queueUrls.json"), "utf8"))), {});
|
|
534
470
|
return {
|
|
535
|
-
publishToAggregates: strDictOf(obj, "publishToAggregates")
|
|
536
|
-
readModelQueueUrls: strDictOf(obj, "readModelQueueUrls")
|
|
471
|
+
publishToAggregates: strDictOf(obj, "publishToAggregates")
|
|
537
472
|
};
|
|
538
473
|
} catch (raw_exn) {
|
|
539
474
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -632,7 +567,6 @@ export {
|
|
|
632
567
|
logError,
|
|
633
568
|
scanByTableName,
|
|
634
569
|
exnMessage,
|
|
635
|
-
jsOption,
|
|
636
570
|
strOf,
|
|
637
571
|
strArrOf,
|
|
638
572
|
strDictOf,
|
|
@@ -39,7 +39,6 @@ let handler = async (_event: JSON.t, _context: PulumiAws.Lambda.context) => {
|
|
|
39
39
|
id: pluginId,
|
|
40
40
|
meta: ReventlessCore.Message.generateMeta(
|
|
41
41
|
~service=ReventlessInfra.PluginExtensionPointSpec.name,
|
|
42
|
-
~ip="",
|
|
43
42
|
~user="Heartbeat",
|
|
44
43
|
),
|
|
45
44
|
commandJson: ReventlessInfra.PluginExtensionPointSpec.Heartbeat(
|
|
@@ -26,7 +26,7 @@ let runtimeExtensionsReady = HandlerFactoryHelpersMjs.runtimeExtensionsReady;
|
|
|
26
26
|
|
|
27
27
|
async function handler(_event, _context) {
|
|
28
28
|
await runtimeExtensionsReady;
|
|
29
|
-
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name, "
|
|
29
|
+
let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name, "Heartbeat", undefined, undefined, undefined, undefined, undefined);
|
|
30
30
|
let message_commandJson = Util_Sury$Reventless.toJson({
|
|
31
31
|
TAG: "Heartbeat",
|
|
32
32
|
_0: timeout
|
|
@@ -15,11 +15,11 @@ let additionalIamPolicies: array<iamPolicy> = []
|
|
|
15
15
|
|
|
16
16
|
// CallbackFunction path — the last serialized-closure Lambda builder on AWS.
|
|
17
17
|
//
|
|
18
|
-
// REACHABLE, despite most deployments going through makeFromCodeAsset.
|
|
18
|
+
// REACHABLE, despite most deployments going through makeFromCodeAsset. Two
|
|
19
19
|
// component builders instantiate ReventlessCore.EventCollectorRuntime_Builder_
|
|
20
|
-
// PerEventCollector.Make over this module —
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// PerEventCollector.Make over this module — SideEffectHandler_PerSideEffectHandler
|
|
21
|
+
// and Task_Builder_PerBucket — and that functor's forEventCollector calls `make`
|
|
22
|
+
// below. The live route is a Task whose
|
|
23
23
|
// spec returns `Task.sideEffects`: Task_Builder then constructs a
|
|
24
24
|
// SideEffectHandler, which provisions its collector Lambda here.
|
|
25
25
|
//
|
|
@@ -29,7 +29,7 @@ let additionalIamPolicies: array<iamPolicy> = []
|
|
|
29
29
|
// where an ordering-aws deploy came up four Lambdas short). Where it does
|
|
30
30
|
// serialize, it then resolves @aws-sdk against whatever the layer and the runtime
|
|
31
31
|
// each supply, carrying the cold-start SDK-skew failure compiled entry points
|
|
32
|
-
// were introduced to avoid. Converting these
|
|
32
|
+
// were introduced to avoid. Converting these two builders to a compiled entry
|
|
33
33
|
// point is the fix; until then a side-effect-bearing Task is the exposed surface.
|
|
34
34
|
//
|
|
35
35
|
// `make` cannot simply be dropped: Runtime.Environment requires environmentMaker
|