@reventlessdev/reventless-aws 3.0.0-alpha.174 → 3.0.0-alpha.176

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 (71) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +10 -10
  3. package/src/Platform.res +115 -31
  4. package/src/Platform.res.mjs +217 -44
  5. package/src/adapter/DcbEventLog/DcbBackend.res +2 -2
  6. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +4 -1
  7. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
  8. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +14 -4
  9. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +19 -14
  10. package/src/adapter/EventLog/EventLogBackend.res +82 -0
  11. package/src/adapter/EventLog/EventLogBackend.res.mjs +53 -0
  12. package/src/adapter/EventLog/EventLogStorage.res +15 -0
  13. package/src/adapter/EventLog/EventLogStorage.res.mjs +20 -1
  14. package/src/adapter/EventLog/EventLogStorage_Postgres.res +37 -0
  15. package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +21 -0
  16. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +26 -11
  17. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +15 -6
  18. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +78 -7
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +30 -2
  20. package/src/adapter/Postgres/PgProjectionFeed.res +100 -0
  21. package/src/adapter/Postgres/PgProjectionFeed.res.mjs +54 -0
  22. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +192 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +141 -0
  24. package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
  25. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
  26. package/src/adapter/QueryDb/QueryDbResolvers.res +48 -0
  27. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +17 -1
  28. package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
  29. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
  30. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +43 -0
  31. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
  32. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
  33. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
  34. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
  35. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
  36. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
  37. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
  38. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
  39. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
  40. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
  41. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
  42. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
  43. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
  44. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
  45. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
  46. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
  47. package/src/components/Aggregate_Builder_Single.res +12 -5
  48. package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
  49. package/src/components/Aggregate_Builder_Single_Async.res +12 -5
  50. package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
  51. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
  52. package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
  53. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
  54. package/src/components/ReadModel_Builder_Single.res +14 -5
  55. package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
  56. package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
  57. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
  58. package/src/components/StateViewSlice_Builder.res +10 -4
  59. package/src/components/StateViewSlice_Builder.res.mjs +8 -10
  60. package/src/components/StateViewSlice_Builder_Stream.res +10 -4
  61. package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
  62. package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
  63. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
  64. package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
  65. package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
  66. package/tests/PgChangeFeedRelay_IntegrationTest.res +259 -0
  67. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +238 -0
  68. package/tests/PgPipeline_IntegrationTest.res +208 -0
  69. package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
  70. package/tests/PgQueryResolver_LambdaTest.res +206 -0
  71. package/tests/PgQueryResolver_LambdaTest.res.mjs +259 -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.176 (2026-07-06)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **reventless-aws:** QueryEngine deploy crash on resource-less Postgres QueryDbs (B3.1b) ([a11b5d6](https://github.com/ReventlessDev/reventless-core/commit/a11b5d6f0797f1c9324f2a4ff3ba4197c20e721b))
11
+ ### Features
12
+
13
+ * **reventless-aws:** classic EventLog Postgres deploy-time wiring + relay (B1 vertical) ([8235ba4](https://github.com/ReventlessDev/reventless-core/commit/8235ba44e506f7094d17251405c6a05c39789805))
14
+ * **reventless-aws:** PgQueryResolver Lambda dispatcher for Postgres GraphQL reads (B3.2a-2) ([140d2bd](https://github.com/ReventlessDev/reventless-core/commit/140d2bd44c2443a03de16e21fd29d5a66b5db9be))
15
+ * **reventless-aws:** Postgres projection delivery via relay feed queues (B3.0) ([80a0ffb](https://github.com/ReventlessDev/reventless-core/commit/80a0ffb1696902a1df99acca1f7915dd65e5c016))
16
+ * **reventless-aws:** Postgres QueryDb storage vertical (B3.1) ([51a7993](https://github.com/ReventlessDev/reventless-core/commit/51a79934c1d9f59bb6f61233a90651d5eadf9f4e))
17
+
18
+
19
+ # 3.0.0-alpha.175 (2026-07-05)
20
+
21
+ **Note:** Version bump only for package @reventlessdev/reventless-aws
22
+
23
+
24
+
25
+
26
+
6
27
  # 3.0.0-alpha.174 (2026-07-05)
7
28
 
8
29
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.174",
3
+ "version": "3.0.0-alpha.176",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -9,16 +9,16 @@
9
9
  "sury": "11.0.0-alpha.4",
10
10
  "uuid": "^13.0.0",
11
11
  "@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
12
- "@reventlessdev/rescript-effect": "0.1.0-alpha.24",
13
- "@reventlessdev/rescript-jest": "1.0.0-alpha.5",
12
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
13
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
14
14
  "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
15
15
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
16
- "@reventlessdev/rescript-uuid": "1.1.0-alpha.13",
17
- "@reventlessdev/reventless-infra": "3.0.0-alpha.87",
18
- "@reventlessdev/reventless-core": "3.0.0-alpha.141",
19
- "@reventlessdev/reventless-interop": "3.0.0-alpha.23",
20
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.3",
21
- "@reventlessdev/reventless-spec": "3.0.0-alpha.65"
16
+ "@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
17
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
18
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.89",
19
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.5",
20
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.67",
21
+ "@reventlessdev/reventless-core": "3.0.0-alpha.143"
22
22
  },
23
23
  "devDependencies": {
24
24
  "rescript": "^12.3.0",
@@ -61,7 +61,7 @@
61
61
  "build": "rescript build",
62
62
  "start": "rescript watch",
63
63
  "clean": "rescript clean",
64
- "rebuild": "pnpm run clean && pnpm run build -- -with-deps",
64
+ "rebuild": "pnpm run clean && pnpm run build",
65
65
  "deploy": "./scripts/deploy.sh",
66
66
  "test": "NODE_OPTIONS='--experimental-vm-modules' npx jest",
67
67
  "coverage": "nyc report --temp-directory=coverage --reporter=text-lcov | coveralls",
package/src/Platform.res CHANGED
@@ -81,17 +81,35 @@ module MakeWithConfig = (
81
81
  Config.commandHandlerConfig.stateChanges->Option.forEach(({?sync, ?async}) =>
82
82
  PluginRuntime_Builder.setStateChangesConfig(~sync?, ~async?, ())
83
83
  )
84
- // B2.3c: when a PgConnection is supplied, route ALL DCB EventLogs to Postgres
85
- // (aggregate EventLogs stay on DynamoDB until the classic feed lands — B2.5) and
86
- // record the selection for the change-feed relay wiring in makePlatform below.
87
- // Set BEFORE the Admin/plugin functors run so DcbEventLogStorage.Selectable sees it.
88
- Config.pgConnection->Option.forEach((pg: PgConnection.t) =>
84
+ // B2.3c: when a PgConnection is supplied, route ALL DCB EventLogs (DcbBackend)
85
+ // AND all classic aggregate EventLogs (EventLogBackend, Single strategies) to
86
+ // Postgres, and record the selections for the change-feed relay wiring in
87
+ // makePlatform below. Set BEFORE the Admin/plugin functors run so the storage
88
+ // Selectables see it.
89
+ Config.pgConnection->Option.forEach((pg: PgConnection.t) => {
89
90
  DcbBackend.set({
90
91
  connectionConfig: pg.connectionConfig,
91
92
  securityGroupId: pg.securityGroupId,
92
93
  subnetIds: pg.subnetIds,
93
94
  })
94
- )
95
+ EventLogBackend.set({
96
+ connectionConfig: pg.connectionConfig,
97
+ securityGroupId: pg.securityGroupId,
98
+ subnetIds: pg.subnetIds,
99
+ })
100
+ // B3.1: app read models / state view slices store to Postgres too. Admin
101
+ // read models are exempt — deploy-time consumers (schema-clobber guard's
102
+ // Plugin-RM scan, PLUGIN_RM_TABLE_NAME gates, retire hooks) query them
103
+ // during `pulumi up` from outside the VPC, and the platform lifecycle
104
+ // depends on them.
105
+ QueryDbBackend.set({
106
+ connectionConfig: pg.connectionConfig,
107
+ securityGroupId: pg.securityGroupId,
108
+ subnetIds: pg.subnetIds,
109
+ })
110
+ QueryDbBackend.exempt(ReventlessCore.PluginsReadModelSpec.name)
111
+ QueryDbBackend.exempt(ReventlessCore.UIFragmentRegistryReadModelSpec.name)
112
+ })
95
113
  type api = Types.AppSync.api
96
114
  type role = Types.AppSync.role
97
115
  type apiTarget = Domain | Platform
@@ -1230,36 +1248,101 @@ module MakeWithConfig = (
1230
1248
  type mcpSupported = | @as(true) McpSupported | @as(false) McpNotSupported
1231
1249
  let mcpSupported = McpNotSupported
1232
1250
 
1233
- // B2.3d: provision the Postgres change-feed relay when the DCB backend is Postgres.
1234
- // Called after plugins are built (makePlatform monolithic, or deployPlugin per
1235
- // stack). Each Postgres DCB log registered {logName, partitionTag} during its
1236
- // plugin build (DcbEventLogStorage_Postgres.make), and its plugin's EventCollector
1237
- // queue was attached in forPluginEventCollector — so the registry is complete here.
1238
- // One shared relay Lambda drains all logs from their per-log dcb_subscription
1239
- // checkpoints and feeds each plugin's collector queue.
1240
- let provisionPgChangeFeedRelay = () =>
1241
- switch DcbBackend.get() {
1242
- | Some({connectionConfig, securityGroupId, subnetIds}) =>
1243
- let relayLogs =
1244
- DcbBackend.getRelayLogs()->Array.filterMap(entry =>
1245
- switch (entry.collectorQueueUrl, entry.collectorQueueArn) {
1246
- | (Some(targetQueueUrl), Some(targetQueueArn)) =>
1247
- Some({
1251
+ // B2.3d: provision the Postgres change-feed relay when a Postgres backend is
1252
+ // active. Called after plugins are built (makePlatform monolithic, or deployPlugin
1253
+ // per stack). Each Postgres log registered itself during its plugin build
1254
+ // (DcbEventLogStorage_Postgres.make / EventLogStorage_Postgres.make), and its
1255
+ // plugin's EventCollector queue was attached in forPluginEventCollector — so the
1256
+ // registries are complete here. One shared relay Lambda drains all logs (DCB and
1257
+ // classic) and feeds each plugin's collector queue. The checkpoint tables key by
1258
+ // subscriber alone, so the subscriber embeds the log name — a shared subscriber
1259
+ // string across logs would clobber checkpoints and skip events.
1260
+ let provisionPgChangeFeedRelay = () => {
1261
+ // B3.0: besides each plugin's EventCollector queue, every log also fans out
1262
+ // to the registered projection feed queues (the AllReadModels /
1263
+ // AllStateViewSlices Lambdas) — Postgres logs have no DynamoDB stream, so
1264
+ // this is how RM/SVS projections are fed. One subscriber per (feed, log)
1265
+ // keeps checkpoints isolated.
1266
+ let feedTargets = (~connectionConfig, ~logName, ~feed, ~isClassic) =>
1267
+ PgProjectionFeed.getFeedQueues()->Array.filterMap(fq =>
1268
+ (isClassic ? fq.includeClassic : fq.includeDcb)
1269
+ ? Some({
1270
+ PgChangeFeedRelay_Builder.connectionConfig,
1271
+ logName,
1272
+ subscriber: `${fq.scope}:${logName}`,
1273
+ feed,
1274
+ targetQueueUrl: fq.url,
1275
+ targetQueueArn: fq.arn,
1276
+ })
1277
+ : None
1278
+ )
1279
+ let dcbLogs = switch DcbBackend.get() {
1280
+ | Some({connectionConfig}) =>
1281
+ DcbBackend.getRelayLogs()->Array.flatMap(entry => {
1282
+ let feed = PgChangeFeedRelay_Builder.Dcb({partitionTag: entry.partitionTag})
1283
+ let ecTarget = switch (entry.collectorQueueUrl, entry.collectorQueueArn) {
1284
+ | (Some(targetQueueUrl), Some(targetQueueArn)) => [
1285
+ {
1248
1286
  PgChangeFeedRelay_Builder.connectionConfig,
1249
1287
  logName: entry.logName,
1250
- subscriber: "aws-eventcollector-relay",
1251
- partitionTag: entry.partitionTag,
1288
+ subscriber: `aws-eventcollector-relay:${entry.logName}`,
1289
+ feed,
1252
1290
  targetQueueUrl,
1253
1291
  targetQueueArn,
1254
- })
1255
- | _ =>
1256
- log.warn(
1257
- ~comp="Platform",
1258
- `PgChangeFeedRelay: DCB log ${entry.logName} has no EventCollector queue — skipping (its events will not propagate)`,
1259
- )
1260
- None
1261
- }
1292
+ },
1293
+ ]
1294
+ | _ =>
1295
+ log.warn(
1296
+ ~comp="Platform",
1297
+ `PgChangeFeedRelay: DCB log ${entry.logName} has no EventCollector queue — EP/extension fan-out skipped for it`,
1298
+ )
1299
+ []
1300
+ }
1301
+ ecTarget->Array.concat(
1302
+ feedTargets(~connectionConfig, ~logName=entry.logName, ~feed, ~isClassic=false),
1303
+ )
1304
+ })
1305
+ | None => []
1306
+ }
1307
+ let classicLogs = switch EventLogBackend.get() {
1308
+ | Some({connectionConfig}) =>
1309
+ EventLogBackend.getRelayLogs()->Array.flatMap(entry => {
1310
+ let feed = PgChangeFeedRelay_Builder.Classic
1311
+ let ecTarget = switch (entry.collectorQueueUrl, entry.collectorQueueArn) {
1312
+ | (Some(targetQueueUrl), Some(targetQueueArn)) => [
1313
+ {
1314
+ PgChangeFeedRelay_Builder.connectionConfig,
1315
+ logName: entry.logName,
1316
+ subscriber: `aws-eventcollector-relay:${entry.logName}`,
1317
+ feed,
1318
+ targetQueueUrl,
1319
+ targetQueueArn,
1320
+ },
1321
+ ]
1322
+ | _ =>
1323
+ log.warn(
1324
+ ~comp="Platform",
1325
+ `PgChangeFeedRelay: classic log ${entry.logName} has no EventCollector queue — EP/extension fan-out skipped for it`,
1326
+ )
1327
+ []
1328
+ }
1329
+ ecTarget->Array.concat(
1330
+ feedTargets(~connectionConfig, ~logName=entry.logName, ~feed, ~isClassic=true),
1262
1331
  )
1332
+ })
1333
+ | None => []
1334
+ }
1335
+ let relayLogs = dcbLogs->Array.concat(classicLogs)
1336
+ let relayVpc = switch DcbBackend.get() {
1337
+ | Some(sel) => Some((sel.securityGroupId, sel.subnetIds))
1338
+ | None =>
1339
+ switch EventLogBackend.get() {
1340
+ | Some(sel) => Some((sel.securityGroupId, sel.subnetIds))
1341
+ | None => None
1342
+ }
1343
+ }
1344
+ switch relayVpc {
1345
+ | Some((securityGroupId, subnetIds)) =>
1263
1346
  if relayLogs->Array.length > 0 {
1264
1347
  let _ = PgChangeFeedRelay_Builder.make(
1265
1348
  ~name="PgChangeFeedRelay",
@@ -1270,6 +1353,7 @@ module MakeWithConfig = (
1270
1353
  }
1271
1354
  | None => ()
1272
1355
  }
1356
+ }
1273
1357
 
1274
1358
  // In split mode, create a dedicated core AppSync API and push the core schema.
1275
1359
  // In unified mode, makePlatform is a no-op (schema stitching handled by events).
@@ -31,12 +31,15 @@ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/
31
31
  import * as Util_DynamoDb$ReventlessAws from "./util/Util_DynamoDb.res.mjs";
32
32
  import * as Platform_Casts$ReventlessAws from "./Platform_Casts.res.mjs";
33
33
  import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
34
+ import * as QueryDbBackend$ReventlessAws from "./adapter/QueryDb/QueryDbBackend.res.mjs";
34
35
  import * as AppSync_Adapter$ReventlessAws from "./components/Api/AppSync_Adapter.res.mjs";
35
36
  import * as Counter_Builder$ReventlessAws from "./components/Counter_Builder.res.mjs";
37
+ import * as EventLogBackend$ReventlessAws from "./adapter/EventLog/EventLogBackend.res.mjs";
36
38
  import * as Platform_Admin$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin.res.mjs";
37
39
  import * as PluginBehavior$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginBehavior.res.mjs";
38
40
  import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
39
41
  import * as DynamoDb_DocumentClient$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/DynamoDb_DocumentClient.res.mjs";
42
+ import * as PgProjectionFeed$ReventlessAws from "./adapter/Postgres/PgProjectionFeed.res.mjs";
40
43
  import * as Util_LocalConfig$ReventlessAws from "./util/Util_LocalConfig.res.mjs";
41
44
  import * as IndexJs from "@pulumi/pulumi/runtime/index.js";
42
45
  import * as AppSync_EventsApi$ReventlessAws from "./adapter/Api/AppSync_EventsApi.res.mjs";
@@ -120,11 +123,25 @@ function MakeWithConfig(Config) {
120
123
  Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
121
124
  });
122
125
  Stdlib_Option.forEach(Config.commandHandlerConfig.stateChanges, param => PluginRuntime_Builder$ReventlessAws.setStateChangesConfig(param.sync, param.async, undefined));
123
- Stdlib_Option.forEach(Config.pgConnection, pg => DcbBackend$ReventlessAws.set({
124
- connectionConfig: pg.connectionConfig,
125
- securityGroupId: pg.securityGroupId,
126
- subnetIds: pg.subnetIds
127
- }));
126
+ Stdlib_Option.forEach(Config.pgConnection, pg => {
127
+ DcbBackend$ReventlessAws.set({
128
+ connectionConfig: pg.connectionConfig,
129
+ securityGroupId: pg.securityGroupId,
130
+ subnetIds: pg.subnetIds
131
+ });
132
+ EventLogBackend$ReventlessAws.set({
133
+ connectionConfig: pg.connectionConfig,
134
+ securityGroupId: pg.securityGroupId,
135
+ subnetIds: pg.subnetIds
136
+ });
137
+ QueryDbBackend$ReventlessAws.set({
138
+ connectionConfig: pg.connectionConfig,
139
+ securityGroupId: pg.securityGroupId,
140
+ subnetIds: pg.subnetIds
141
+ });
142
+ QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
143
+ QueryDbBackend$ReventlessAws.exempt(UIFragmentRegistryReadModelSpec$ReventlessCore.name);
144
+ });
128
145
  let currentDeployTarget = {
129
146
  contents: "Domain"
130
147
  };
@@ -816,28 +833,99 @@ function MakeWithConfig(Config) {
816
833
  return Component$ReventlessCore.operations(component);
817
834
  };
818
835
  let provisionPgChangeFeedRelay = () => {
819
- let match = DcbBackend$ReventlessAws.get();
820
- if (match === undefined) {
821
- return;
822
- }
823
- let connectionConfig = match.connectionConfig;
824
- let relayLogs = Stdlib_Array.filterMap(DcbBackend$ReventlessAws.getRelayLogs(), entry => {
825
- let match = entry.collectorQueueUrl;
826
- let match$1 = entry.collectorQueueArn;
827
- if (match !== undefined && match$1 !== undefined) {
836
+ let feedTargets = (connectionConfig, logName, feed, isClassic) => Stdlib_Array.filterMap(PgProjectionFeed$ReventlessAws.getFeedQueues(), fq => {
837
+ if (isClassic ? fq.includeClassic : fq.includeDcb) {
828
838
  return {
829
839
  connectionConfig: connectionConfig,
830
- logName: entry.logName,
831
- subscriber: "aws-eventcollector-relay",
832
- partitionTag: entry.partitionTag,
833
- targetQueueUrl: match,
834
- targetQueueArn: match$1
840
+ logName: logName,
841
+ subscriber: fq.scope + `:` + logName,
842
+ feed: feed,
843
+ targetQueueUrl: fq.url,
844
+ targetQueueArn: fq.arn
835
845
  };
836
846
  }
837
- log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — skipping (its events will not propagate)`);
838
847
  });
839
- if (relayLogs.length !== 0) {
840
- PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, match.securityGroupId, match.subnetIds, undefined, undefined);
848
+ let match = DcbBackend$ReventlessAws.get();
849
+ let dcbLogs;
850
+ if (match !== undefined) {
851
+ let connectionConfig = match.connectionConfig;
852
+ dcbLogs = DcbBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
853
+ let feed = {
854
+ TAG: "Dcb",
855
+ partitionTag: entry.partitionTag
856
+ };
857
+ let match = entry.collectorQueueUrl;
858
+ let match$1 = entry.collectorQueueArn;
859
+ let ecTarget;
860
+ let exit = 0;
861
+ if (match !== undefined && match$1 !== undefined) {
862
+ ecTarget = [{
863
+ connectionConfig: connectionConfig,
864
+ logName: entry.logName,
865
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
866
+ feed: feed,
867
+ targetQueueUrl: match,
868
+ targetQueueArn: match$1
869
+ }];
870
+ } else {
871
+ exit = 1;
872
+ }
873
+ if (exit === 1) {
874
+ log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
875
+ ecTarget = [];
876
+ }
877
+ return ecTarget.concat(feedTargets(connectionConfig, entry.logName, feed, false));
878
+ });
879
+ } else {
880
+ dcbLogs = [];
881
+ }
882
+ let match$1 = EventLogBackend$ReventlessAws.get();
883
+ let classicLogs;
884
+ if (match$1 !== undefined) {
885
+ let connectionConfig$1 = match$1.connectionConfig;
886
+ classicLogs = EventLogBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
887
+ let match = entry.collectorQueueUrl;
888
+ let match$1 = entry.collectorQueueArn;
889
+ let ecTarget;
890
+ let exit = 0;
891
+ if (match !== undefined && match$1 !== undefined) {
892
+ ecTarget = [{
893
+ connectionConfig: connectionConfig$1,
894
+ logName: entry.logName,
895
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
896
+ feed: "Classic",
897
+ targetQueueUrl: match,
898
+ targetQueueArn: match$1
899
+ }];
900
+ } else {
901
+ exit = 1;
902
+ }
903
+ if (exit === 1) {
904
+ log.warn("Platform", undefined, `PgChangeFeedRelay: classic log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
905
+ ecTarget = [];
906
+ }
907
+ return ecTarget.concat(feedTargets(connectionConfig$1, entry.logName, "Classic", true));
908
+ });
909
+ } else {
910
+ classicLogs = [];
911
+ }
912
+ let relayLogs = dcbLogs.concat(classicLogs);
913
+ let sel = DcbBackend$ReventlessAws.get();
914
+ let relayVpc;
915
+ if (sel !== undefined) {
916
+ relayVpc = [
917
+ sel.securityGroupId,
918
+ sel.subnetIds
919
+ ];
920
+ } else {
921
+ let sel$1 = EventLogBackend$ReventlessAws.get();
922
+ relayVpc = sel$1 !== undefined ? [
923
+ sel$1.securityGroupId,
924
+ sel$1.subnetIds
925
+ ] : undefined;
926
+ }
927
+ if (relayVpc !== undefined && relayLogs.length !== 0) {
928
+ PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, relayVpc[0], relayVpc[1], undefined, undefined);
841
929
  return;
842
930
  }
843
931
  };
@@ -1245,11 +1333,25 @@ function Make($star) {
1245
1333
  Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
1246
1334
  });
1247
1335
  Stdlib_Option.forEach(commandHandlerConfig.stateChanges, param => PluginRuntime_Builder$ReventlessAws.setStateChangesConfig(param.sync, param.async, undefined));
1248
- Stdlib_Option.forEach(undefined, pg => DcbBackend$ReventlessAws.set({
1249
- connectionConfig: pg.connectionConfig,
1250
- securityGroupId: pg.securityGroupId,
1251
- subnetIds: pg.subnetIds
1252
- }));
1336
+ Stdlib_Option.forEach(undefined, pg => {
1337
+ DcbBackend$ReventlessAws.set({
1338
+ connectionConfig: pg.connectionConfig,
1339
+ securityGroupId: pg.securityGroupId,
1340
+ subnetIds: pg.subnetIds
1341
+ });
1342
+ EventLogBackend$ReventlessAws.set({
1343
+ connectionConfig: pg.connectionConfig,
1344
+ securityGroupId: pg.securityGroupId,
1345
+ subnetIds: pg.subnetIds
1346
+ });
1347
+ QueryDbBackend$ReventlessAws.set({
1348
+ connectionConfig: pg.connectionConfig,
1349
+ securityGroupId: pg.securityGroupId,
1350
+ subnetIds: pg.subnetIds
1351
+ });
1352
+ QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
1353
+ QueryDbBackend$ReventlessAws.exempt(UIFragmentRegistryReadModelSpec$ReventlessCore.name);
1354
+ });
1253
1355
  let currentDeployTarget = {
1254
1356
  contents: "Domain"
1255
1357
  };
@@ -1940,28 +2042,99 @@ function Make($star) {
1940
2042
  return Component$ReventlessCore.operations(component);
1941
2043
  };
1942
2044
  let provisionPgChangeFeedRelay = () => {
1943
- let match = DcbBackend$ReventlessAws.get();
1944
- if (match === undefined) {
1945
- return;
1946
- }
1947
- let connectionConfig = match.connectionConfig;
1948
- let relayLogs = Stdlib_Array.filterMap(DcbBackend$ReventlessAws.getRelayLogs(), entry => {
1949
- let match = entry.collectorQueueUrl;
1950
- let match$1 = entry.collectorQueueArn;
1951
- if (match !== undefined && match$1 !== undefined) {
2045
+ let feedTargets = (connectionConfig, logName, feed, isClassic) => Stdlib_Array.filterMap(PgProjectionFeed$ReventlessAws.getFeedQueues(), fq => {
2046
+ if (isClassic ? fq.includeClassic : fq.includeDcb) {
1952
2047
  return {
1953
2048
  connectionConfig: connectionConfig,
1954
- logName: entry.logName,
1955
- subscriber: "aws-eventcollector-relay",
1956
- partitionTag: entry.partitionTag,
1957
- targetQueueUrl: match,
1958
- targetQueueArn: match$1
2049
+ logName: logName,
2050
+ subscriber: fq.scope + `:` + logName,
2051
+ feed: feed,
2052
+ targetQueueUrl: fq.url,
2053
+ targetQueueArn: fq.arn
1959
2054
  };
1960
2055
  }
1961
- log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — skipping (its events will not propagate)`);
1962
2056
  });
1963
- if (relayLogs.length !== 0) {
1964
- PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, match.securityGroupId, match.subnetIds, undefined, undefined);
2057
+ let match = DcbBackend$ReventlessAws.get();
2058
+ let dcbLogs;
2059
+ if (match !== undefined) {
2060
+ let connectionConfig = match.connectionConfig;
2061
+ dcbLogs = DcbBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
2062
+ let feed = {
2063
+ TAG: "Dcb",
2064
+ partitionTag: entry.partitionTag
2065
+ };
2066
+ let match = entry.collectorQueueUrl;
2067
+ let match$1 = entry.collectorQueueArn;
2068
+ let ecTarget;
2069
+ let exit = 0;
2070
+ if (match !== undefined && match$1 !== undefined) {
2071
+ ecTarget = [{
2072
+ connectionConfig: connectionConfig,
2073
+ logName: entry.logName,
2074
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
2075
+ feed: feed,
2076
+ targetQueueUrl: match,
2077
+ targetQueueArn: match$1
2078
+ }];
2079
+ } else {
2080
+ exit = 1;
2081
+ }
2082
+ if (exit === 1) {
2083
+ log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
2084
+ ecTarget = [];
2085
+ }
2086
+ return ecTarget.concat(feedTargets(connectionConfig, entry.logName, feed, false));
2087
+ });
2088
+ } else {
2089
+ dcbLogs = [];
2090
+ }
2091
+ let match$1 = EventLogBackend$ReventlessAws.get();
2092
+ let classicLogs;
2093
+ if (match$1 !== undefined) {
2094
+ let connectionConfig$1 = match$1.connectionConfig;
2095
+ classicLogs = EventLogBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
2096
+ let match = entry.collectorQueueUrl;
2097
+ let match$1 = entry.collectorQueueArn;
2098
+ let ecTarget;
2099
+ let exit = 0;
2100
+ if (match !== undefined && match$1 !== undefined) {
2101
+ ecTarget = [{
2102
+ connectionConfig: connectionConfig$1,
2103
+ logName: entry.logName,
2104
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
2105
+ feed: "Classic",
2106
+ targetQueueUrl: match,
2107
+ targetQueueArn: match$1
2108
+ }];
2109
+ } else {
2110
+ exit = 1;
2111
+ }
2112
+ if (exit === 1) {
2113
+ log.warn("Platform", undefined, `PgChangeFeedRelay: classic log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
2114
+ ecTarget = [];
2115
+ }
2116
+ return ecTarget.concat(feedTargets(connectionConfig$1, entry.logName, "Classic", true));
2117
+ });
2118
+ } else {
2119
+ classicLogs = [];
2120
+ }
2121
+ let relayLogs = dcbLogs.concat(classicLogs);
2122
+ let sel = DcbBackend$ReventlessAws.get();
2123
+ let relayVpc;
2124
+ if (sel !== undefined) {
2125
+ relayVpc = [
2126
+ sel.securityGroupId,
2127
+ sel.subnetIds
2128
+ ];
2129
+ } else {
2130
+ let sel$1 = EventLogBackend$ReventlessAws.get();
2131
+ relayVpc = sel$1 !== undefined ? [
2132
+ sel$1.securityGroupId,
2133
+ sel$1.subnetIds
2134
+ ] : undefined;
2135
+ }
2136
+ if (relayVpc !== undefined && relayLogs.length !== 0) {
2137
+ PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, relayVpc[0], relayVpc[1], undefined, undefined);
1965
2138
  return;
1966
2139
  }
1967
2140
  };
@@ -10,8 +10,8 @@
10
10
  // EventCollector SQS queue.
11
11
  //
12
12
  // All DCB logs in the platform share one selection (the confirmed B2.3c toggle);
13
- // aggregate EventLogs are unaffected they stay on DynamoDB until the classic
14
- // `event_log` change feed lands (B2.5). See docs/plans/aws-postgres-change-feed-bridge.md.
13
+ // classic aggregate EventLogs follow the same platform toggle via the sibling
14
+ // `EventLogBackend` registry. See docs/plans/done/aws-postgres-change-feed-bridge.md.
15
15
 
16
16
  type selection = {
17
17
  connectionConfig: Pulumi.Output.t<PgConnection.connectionConfig>,
@@ -32,10 +32,13 @@ let make: ReventlessCore.EventCollector_Adapter.channelMaker<callbackEvent, 'con
32
32
  ~eventTopics,
33
33
  ~opts as _,
34
34
  ) => {
35
+ // Postgres-backed event logs have NO storage resources (no table, no stream) —
36
+ // skip them instead of producing undefined entries; their events arrive via the
37
+ // PgProjectionFeed SQS queue (B3.0), not a stream ESM.
35
38
  let eventTopicResources =
36
39
  eventTopics
37
40
  ->Dict.valuesToArray
38
- ->Array.map(outputs => outputs.resources->Array.getUnsafe(0)) // FIXME
41
+ ->Array.filterMap(outputs => outputs.resources->Array.get(0))
39
42
 
40
43
  let enqueueEventNotSupported = (delay, id, messageBody) => {
41
44
  // TODO: can we check this at deploy time ?
@@ -21,7 +21,7 @@ function connect(name, channelSpecs, runtime, opts) {
21
21
  }
22
22
 
23
23
  function make(param, eventTopics, param$1) {
24
- let eventTopicResources = Object.values(eventTopics).map(outputs => outputs.resources[0]);
24
+ let eventTopicResources = Stdlib_Array.filterMap(Object.values(eventTopics), outputs => outputs.resources[0]);
25
25
  let enqueueEventNotSupported = (delay, id, messageBody) => {
26
26
  log.debug("EventCollectorChannel_DynamoDbStream", Object.fromEntries([
27
27
  [
@@ -1,12 +1,22 @@
1
+ // Shared record decoder for the ReadModel / StateViewSlice entry points.
2
+ //
3
+ // DynamoDB path: the Lambda is invoked by DynamoDB-stream ESMs — decode each
4
+ // NEW_IMAGE. Postgres path (B3.0): the Lambda is invoked by its SQS feed queue,
5
+ // fed by the PgChangeFeedRelay with ready-made `{id, meta, event}` bodies —
6
+ // parse each body straight back to JSON. Both record kinds can appear in one
7
+ // deployment (mixed backends), so the branch is per record, not per event.
1
8
  let handleStreamEvent: (
2
9
  ReventlessCore.EventCollector.jsonEventsHandler,
3
- PulumiAws.DynamoDb.Stream.event,
10
+ PulumiAws.Lambda.CallbackFunction.event,
4
11
  _,
5
- ) => Effect.t<unit, string, unit> = (handleEvents, streamEvent, _) => {
6
- let jsons = streamEvent.records->Array.filterMap(record =>
12
+ ) => Effect.t<unit, string, unit> = (handleEvents, event, _) => {
13
+ let jsons = event.records->Array.filterMap(record =>
7
14
  switch record.eventSource {
15
+ | "aws:sqs" => record->PulumiAws.SQS.Queue.asRecord->Util.SQS_Runtime.parseSqsRecord
8
16
  | "aws:dynamodb" =>
9
- switch record->Util.DynamoDbStream_Runtime.parseDynamoDbStreamRecordEvent {
17
+ switch record
18
+ ->PulumiAws.DynamoDb.Stream.asRecord
19
+ ->Util.DynamoDbStream_Runtime.parseDynamoDbStreamRecordEvent {
10
20
  | NewImage(_, newImage)
11
21
  | NewAndOldImage(_, newImage, _) =>
12
22
  Some(newImage)