@reventlessdev/reventless-aws 3.0.0-alpha.175 → 3.0.0-alpha.177

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 (76) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +10 -10
  3. package/src/Platform.res +127 -31
  4. package/src/Platform.res.mjs +226 -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 +54 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +23 -0
  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_Builder.res +281 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +189 -0
  24. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +335 -0
  25. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +252 -0
  26. package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
  27. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
  28. package/src/adapter/QueryDb/QueryDbResolvers.res +49 -0
  29. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +20 -1
  30. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +264 -0
  31. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res.mjs +164 -0
  32. package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
  33. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
  34. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +47 -0
  35. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
  36. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
  37. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
  38. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
  39. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
  40. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
  41. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
  42. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
  43. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
  44. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
  45. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
  46. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +88 -0
  48. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
  49. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
  50. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
  51. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
  52. package/src/components/Aggregate_Builder_Single.res +12 -5
  53. package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
  54. package/src/components/Aggregate_Builder_Single_Async.res +12 -5
  55. package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
  56. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
  57. package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
  58. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
  59. package/src/components/ReadModel_Builder_Single.res +14 -5
  60. package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
  61. package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
  62. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
  63. package/src/components/StateViewSlice_Builder.res +10 -4
  64. package/src/components/StateViewSlice_Builder.res.mjs +8 -10
  65. package/src/components/StateViewSlice_Builder_Stream.res +10 -4
  66. package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
  67. package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
  68. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
  69. package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
  70. package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
  71. package/tests/PgChangeFeedRelay_IntegrationTest.res +115 -4
  72. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +84 -0
  73. package/tests/PgPipeline_IntegrationTest.res +208 -0
  74. package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
  75. package/tests/PgQueryResolver_LambdaTest.res +308 -0
  76. package/tests/PgQueryResolver_LambdaTest.res.mjs +392 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
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.177 (2026-07-06)
7
+
8
+ ### Features
9
+
10
+ * **reventless-aws:** AppSync read path for Postgres read models (B3.2b) ([a6dd6cb](https://github.com/ReventlessDev/reventless-core/commit/a6dd6cbddfc65410bf96da49bb2603c88670aced))
11
+ * **reventless-aws:** cross-table @resolves/[@resolves](https://github.com/resolves)Many + node resolvers for Postgres reads (B3.2c) ([713b6ab](https://github.com/ReventlessDev/reventless-core/commit/713b6ab736715209e7198b2b4ab609c5447adef5))
12
+ * **reventless-postgres,reventless-aws:** items (sub-id connection) resolver for Postgres reads (B3.2c) ([2477220](https://github.com/ReventlessDev/reventless-core/commit/247722062913f5d0bcb5895b152f05033d8297d8))
13
+
14
+
15
+ # 3.0.0-alpha.176 (2026-07-06)
16
+
17
+ ### Bug Fixes
18
+
19
+ * **reventless-aws:** QueryEngine deploy crash on resource-less Postgres QueryDbs (B3.1b) ([a11b5d6](https://github.com/ReventlessDev/reventless-core/commit/a11b5d6f0797f1c9324f2a4ff3ba4197c20e721b))
20
+ ### Features
21
+
22
+ * **reventless-aws:** classic EventLog Postgres deploy-time wiring + relay (B1 vertical) ([8235ba4](https://github.com/ReventlessDev/reventless-core/commit/8235ba44e506f7094d17251405c6a05c39789805))
23
+ * **reventless-aws:** PgQueryResolver Lambda dispatcher for Postgres GraphQL reads (B3.2a-2) ([140d2bd](https://github.com/ReventlessDev/reventless-core/commit/140d2bd44c2443a03de16e21fd29d5a66b5db9be))
24
+ * **reventless-aws:** Postgres projection delivery via relay feed queues (B3.0) ([80a0ffb](https://github.com/ReventlessDev/reventless-core/commit/80a0ffb1696902a1df99acca1f7915dd65e5c016))
25
+ * **reventless-aws:** Postgres QueryDb storage vertical (B3.1) ([51a7993](https://github.com/ReventlessDev/reventless-core/commit/51a79934c1d9f59bb6f61233a90651d5eadf9f4e))
26
+
27
+
6
28
  # 3.0.0-alpha.175 (2026-07-05)
7
29
 
8
30
  **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.175",
3
+ "version": "3.0.0-alpha.177",
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",
12
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
13
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
13
14
  "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
14
- "@reventlessdev/rescript-jest": "1.0.0-alpha.5",
15
15
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
16
- "@reventlessdev/rescript-uuid": "1.1.0-alpha.13",
17
- "@reventlessdev/reventless-core": "3.0.0-alpha.142",
18
- "@reventlessdev/reventless-interop": "3.0.0-alpha.23",
19
- "@reventlessdev/reventless-infra": "3.0.0-alpha.88",
20
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.4",
21
- "@reventlessdev/reventless-spec": "3.0.0-alpha.66"
16
+ "@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
17
+ "@reventlessdev/reventless-core": "3.0.0-alpha.143",
18
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.89",
19
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.6",
20
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
21
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.67"
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),
1262
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),
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).
@@ -1386,6 +1470,18 @@ module MakeWithConfig = (
1386
1470
  // built above, so the DcbBackend relay registry is complete).
1387
1471
  provisionPgChangeFeedRelay()
1388
1472
 
1473
+ // B3.2b: provision the shared PgQueryResolver Lambda + AppSync data source for
1474
+ // Postgres-backed read models (monolithic mode). Runs after construct so the
1475
+ // resolver-binding registry (QueryDbResolvers_Lambda.make) and the ReadModel
1476
+ // spec-module registry are complete; it fulfils the deferred `dataSourceName`
1477
+ // the Postgres storage maker handed to the (schema-pushed) resolvers. App read
1478
+ // model resolvers attach to the Domain API. Plugin-stack mode is deferred —
1479
+ // it needs a per-stack data-source name to avoid collisions on the shared API.
1480
+ switch QueryDbBackend.get() {
1481
+ | Some(sel) => PgQueryResolver_Builder.provision(~api=domainApi, ~selection=sel, ~opts={})
1482
+ | None => ()
1483
+ }
1484
+
1389
1485
  // Admin schema push is fired by preAdminResolversSchemaHook from inside
1390
1486
  // Admin.construct (with createResolvers gated on it). Only exports below.
1391
1487
  if Config.splitApi {
@@ -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";
@@ -58,6 +61,7 @@ import * as Task_Builder_PerBucket$ReventlessAws from "./components/Task_Builder
58
61
  import * as AutomationSlice_Builder$ReventlessAws from "./components/AutomationSlice_Builder.res.mjs";
59
62
  import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/EventTopicPublisher_SNS.res.mjs";
60
63
  import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
64
+ import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
61
65
  import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
62
66
  import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
63
67
  import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
@@ -120,11 +124,25 @@ function MakeWithConfig(Config) {
120
124
  Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
121
125
  });
122
126
  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
- }));
127
+ Stdlib_Option.forEach(Config.pgConnection, pg => {
128
+ DcbBackend$ReventlessAws.set({
129
+ connectionConfig: pg.connectionConfig,
130
+ securityGroupId: pg.securityGroupId,
131
+ subnetIds: pg.subnetIds
132
+ });
133
+ EventLogBackend$ReventlessAws.set({
134
+ connectionConfig: pg.connectionConfig,
135
+ securityGroupId: pg.securityGroupId,
136
+ subnetIds: pg.subnetIds
137
+ });
138
+ QueryDbBackend$ReventlessAws.set({
139
+ connectionConfig: pg.connectionConfig,
140
+ securityGroupId: pg.securityGroupId,
141
+ subnetIds: pg.subnetIds
142
+ });
143
+ QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
144
+ QueryDbBackend$ReventlessAws.exempt(UIFragmentRegistryReadModelSpec$ReventlessCore.name);
145
+ });
128
146
  let currentDeployTarget = {
129
147
  contents: "Domain"
130
148
  };
@@ -816,28 +834,99 @@ function MakeWithConfig(Config) {
816
834
  return Component$ReventlessCore.operations(component);
817
835
  };
818
836
  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) {
837
+ let feedTargets = (connectionConfig, logName, feed, isClassic) => Stdlib_Array.filterMap(PgProjectionFeed$ReventlessAws.getFeedQueues(), fq => {
838
+ if (isClassic ? fq.includeClassic : fq.includeDcb) {
828
839
  return {
829
840
  connectionConfig: connectionConfig,
830
- logName: entry.logName,
831
- subscriber: "aws-eventcollector-relay",
832
- partitionTag: entry.partitionTag,
833
- targetQueueUrl: match,
834
- targetQueueArn: match$1
841
+ logName: logName,
842
+ subscriber: fq.scope + `:` + logName,
843
+ feed: feed,
844
+ targetQueueUrl: fq.url,
845
+ targetQueueArn: fq.arn
835
846
  };
836
847
  }
837
- log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — skipping (its events will not propagate)`);
838
848
  });
839
- if (relayLogs.length !== 0) {
840
- PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, match.securityGroupId, match.subnetIds, undefined, undefined);
849
+ let match = DcbBackend$ReventlessAws.get();
850
+ let dcbLogs;
851
+ if (match !== undefined) {
852
+ let connectionConfig = match.connectionConfig;
853
+ dcbLogs = DcbBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
854
+ let feed = {
855
+ TAG: "Dcb",
856
+ partitionTag: entry.partitionTag
857
+ };
858
+ let match = entry.collectorQueueUrl;
859
+ let match$1 = entry.collectorQueueArn;
860
+ let ecTarget;
861
+ let exit = 0;
862
+ if (match !== undefined && match$1 !== undefined) {
863
+ ecTarget = [{
864
+ connectionConfig: connectionConfig,
865
+ logName: entry.logName,
866
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
867
+ feed: feed,
868
+ targetQueueUrl: match,
869
+ targetQueueArn: match$1
870
+ }];
871
+ } else {
872
+ exit = 1;
873
+ }
874
+ if (exit === 1) {
875
+ log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
876
+ ecTarget = [];
877
+ }
878
+ return ecTarget.concat(feedTargets(connectionConfig, entry.logName, feed, false));
879
+ });
880
+ } else {
881
+ dcbLogs = [];
882
+ }
883
+ let match$1 = EventLogBackend$ReventlessAws.get();
884
+ let classicLogs;
885
+ if (match$1 !== undefined) {
886
+ let connectionConfig$1 = match$1.connectionConfig;
887
+ classicLogs = EventLogBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
888
+ let match = entry.collectorQueueUrl;
889
+ let match$1 = entry.collectorQueueArn;
890
+ let ecTarget;
891
+ let exit = 0;
892
+ if (match !== undefined && match$1 !== undefined) {
893
+ ecTarget = [{
894
+ connectionConfig: connectionConfig$1,
895
+ logName: entry.logName,
896
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
897
+ feed: "Classic",
898
+ targetQueueUrl: match,
899
+ targetQueueArn: match$1
900
+ }];
901
+ } else {
902
+ exit = 1;
903
+ }
904
+ if (exit === 1) {
905
+ log.warn("Platform", undefined, `PgChangeFeedRelay: classic log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
906
+ ecTarget = [];
907
+ }
908
+ return ecTarget.concat(feedTargets(connectionConfig$1, entry.logName, "Classic", true));
909
+ });
910
+ } else {
911
+ classicLogs = [];
912
+ }
913
+ let relayLogs = dcbLogs.concat(classicLogs);
914
+ let sel = DcbBackend$ReventlessAws.get();
915
+ let relayVpc;
916
+ if (sel !== undefined) {
917
+ relayVpc = [
918
+ sel.securityGroupId,
919
+ sel.subnetIds
920
+ ];
921
+ } else {
922
+ let sel$1 = EventLogBackend$ReventlessAws.get();
923
+ relayVpc = sel$1 !== undefined ? [
924
+ sel$1.securityGroupId,
925
+ sel$1.subnetIds
926
+ ] : undefined;
927
+ }
928
+ if (relayVpc !== undefined && relayLogs.length !== 0) {
929
+ PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, relayVpc[0], relayVpc[1], undefined, undefined);
841
930
  return;
842
931
  }
843
932
  };
@@ -893,6 +982,10 @@ function MakeWithConfig(Config) {
893
982
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
894
983
  }
895
984
  provisionPgChangeFeedRelay();
985
+ let sel = QueryDbBackend$ReventlessAws.get();
986
+ if (sel !== undefined) {
987
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {});
988
+ }
896
989
  if (Config.splitApi) {
897
990
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
898
991
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
@@ -1245,11 +1338,25 @@ function Make($star) {
1245
1338
  Stdlib_Option.forEach(param.async, AggregateRuntime_Builder_Single_Async$ReventlessAws.setConfig);
1246
1339
  });
1247
1340
  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
- }));
1341
+ Stdlib_Option.forEach(undefined, pg => {
1342
+ DcbBackend$ReventlessAws.set({
1343
+ connectionConfig: pg.connectionConfig,
1344
+ securityGroupId: pg.securityGroupId,
1345
+ subnetIds: pg.subnetIds
1346
+ });
1347
+ EventLogBackend$ReventlessAws.set({
1348
+ connectionConfig: pg.connectionConfig,
1349
+ securityGroupId: pg.securityGroupId,
1350
+ subnetIds: pg.subnetIds
1351
+ });
1352
+ QueryDbBackend$ReventlessAws.set({
1353
+ connectionConfig: pg.connectionConfig,
1354
+ securityGroupId: pg.securityGroupId,
1355
+ subnetIds: pg.subnetIds
1356
+ });
1357
+ QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
1358
+ QueryDbBackend$ReventlessAws.exempt(UIFragmentRegistryReadModelSpec$ReventlessCore.name);
1359
+ });
1253
1360
  let currentDeployTarget = {
1254
1361
  contents: "Domain"
1255
1362
  };
@@ -1940,28 +2047,99 @@ function Make($star) {
1940
2047
  return Component$ReventlessCore.operations(component);
1941
2048
  };
1942
2049
  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) {
2050
+ let feedTargets = (connectionConfig, logName, feed, isClassic) => Stdlib_Array.filterMap(PgProjectionFeed$ReventlessAws.getFeedQueues(), fq => {
2051
+ if (isClassic ? fq.includeClassic : fq.includeDcb) {
1952
2052
  return {
1953
2053
  connectionConfig: connectionConfig,
1954
- logName: entry.logName,
1955
- subscriber: "aws-eventcollector-relay",
1956
- partitionTag: entry.partitionTag,
1957
- targetQueueUrl: match,
1958
- targetQueueArn: match$1
2054
+ logName: logName,
2055
+ subscriber: fq.scope + `:` + logName,
2056
+ feed: feed,
2057
+ targetQueueUrl: fq.url,
2058
+ targetQueueArn: fq.arn
1959
2059
  };
1960
2060
  }
1961
- log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — skipping (its events will not propagate)`);
1962
2061
  });
1963
- if (relayLogs.length !== 0) {
1964
- PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, match.securityGroupId, match.subnetIds, undefined, undefined);
2062
+ let match = DcbBackend$ReventlessAws.get();
2063
+ let dcbLogs;
2064
+ if (match !== undefined) {
2065
+ let connectionConfig = match.connectionConfig;
2066
+ dcbLogs = DcbBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
2067
+ let feed = {
2068
+ TAG: "Dcb",
2069
+ partitionTag: entry.partitionTag
2070
+ };
2071
+ let match = entry.collectorQueueUrl;
2072
+ let match$1 = entry.collectorQueueArn;
2073
+ let ecTarget;
2074
+ let exit = 0;
2075
+ if (match !== undefined && match$1 !== undefined) {
2076
+ ecTarget = [{
2077
+ connectionConfig: connectionConfig,
2078
+ logName: entry.logName,
2079
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
2080
+ feed: feed,
2081
+ targetQueueUrl: match,
2082
+ targetQueueArn: match$1
2083
+ }];
2084
+ } else {
2085
+ exit = 1;
2086
+ }
2087
+ if (exit === 1) {
2088
+ log.warn("Platform", undefined, `PgChangeFeedRelay: DCB log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
2089
+ ecTarget = [];
2090
+ }
2091
+ return ecTarget.concat(feedTargets(connectionConfig, entry.logName, feed, false));
2092
+ });
2093
+ } else {
2094
+ dcbLogs = [];
2095
+ }
2096
+ let match$1 = EventLogBackend$ReventlessAws.get();
2097
+ let classicLogs;
2098
+ if (match$1 !== undefined) {
2099
+ let connectionConfig$1 = match$1.connectionConfig;
2100
+ classicLogs = EventLogBackend$ReventlessAws.getRelayLogs().flatMap(entry => {
2101
+ let match = entry.collectorQueueUrl;
2102
+ let match$1 = entry.collectorQueueArn;
2103
+ let ecTarget;
2104
+ let exit = 0;
2105
+ if (match !== undefined && match$1 !== undefined) {
2106
+ ecTarget = [{
2107
+ connectionConfig: connectionConfig$1,
2108
+ logName: entry.logName,
2109
+ subscriber: `aws-eventcollector-relay:` + entry.logName,
2110
+ feed: "Classic",
2111
+ targetQueueUrl: match,
2112
+ targetQueueArn: match$1
2113
+ }];
2114
+ } else {
2115
+ exit = 1;
2116
+ }
2117
+ if (exit === 1) {
2118
+ log.warn("Platform", undefined, `PgChangeFeedRelay: classic log ` + entry.logName + ` has no EventCollector queue — EP/extension fan-out skipped for it`);
2119
+ ecTarget = [];
2120
+ }
2121
+ return ecTarget.concat(feedTargets(connectionConfig$1, entry.logName, "Classic", true));
2122
+ });
2123
+ } else {
2124
+ classicLogs = [];
2125
+ }
2126
+ let relayLogs = dcbLogs.concat(classicLogs);
2127
+ let sel = DcbBackend$ReventlessAws.get();
2128
+ let relayVpc;
2129
+ if (sel !== undefined) {
2130
+ relayVpc = [
2131
+ sel.securityGroupId,
2132
+ sel.subnetIds
2133
+ ];
2134
+ } else {
2135
+ let sel$1 = EventLogBackend$ReventlessAws.get();
2136
+ relayVpc = sel$1 !== undefined ? [
2137
+ sel$1.securityGroupId,
2138
+ sel$1.subnetIds
2139
+ ] : undefined;
2140
+ }
2141
+ if (relayVpc !== undefined && relayLogs.length !== 0) {
2142
+ PgChangeFeedRelay_Builder$ReventlessAws.make("PgChangeFeedRelay", relayLogs, relayVpc[0], relayVpc[1], undefined, undefined);
1965
2143
  return;
1966
2144
  }
1967
2145
  };
@@ -2012,6 +2190,10 @@ function Make($star) {
2012
2190
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
2013
2191
  }
2014
2192
  provisionPgChangeFeedRelay();
2193
+ let sel = QueryDbBackend$ReventlessAws.get();
2194
+ if (sel !== undefined) {
2195
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {});
2196
+ }
2015
2197
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
2016
2198
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
2017
2199
  Pulumi$Pulumi.$$export("platformApiRoleArn", Output$Pulumi.flatMap(platformApiRole, role => role.arn));
@@ -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
  [