@reventlessdev/reventless-aws 3.0.0-alpha.171 → 3.0.0-alpha.173

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 (52) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +6 -5
  3. package/rescript.json +2 -1
  4. package/src/Platform.res.mjs +5 -5
  5. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +1 -1
  6. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +12 -6
  7. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +17 -5
  8. package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres_Runtime.res +26 -0
  9. package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres_Runtime.res.mjs +15 -0
  10. package/src/adapter/EventLog/EventLogStorage_Postgres_Runtime.res +23 -0
  11. package/src/adapter/EventLog/EventLogStorage_Postgres_Runtime.res.mjs +14 -0
  12. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +181 -0
  13. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +135 -0
  14. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +64 -0
  15. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +36 -0
  16. package/src/adapter/Postgres/PgConnection.res +169 -0
  17. package/src/adapter/Postgres/PgConnection.res.mjs +112 -0
  18. package/src/adapter/Postgres/PgRuntime.res +71 -0
  19. package/src/adapter/Postgres/PgRuntime.res.mjs +71 -0
  20. package/src/adapter/Runtime/AggregateEntryPoint.mjs +27 -9
  21. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +3 -3
  22. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +3 -3
  23. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +1 -1
  24. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +1 -1
  25. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +1 -1
  26. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +1 -1
  27. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +1 -1
  28. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +20 -6
  29. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +1 -1
  30. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +1 -1
  31. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +1 -1
  32. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +52 -0
  33. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +38 -0
  34. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +20 -2
  35. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +1 -1
  36. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
  37. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +1 -1
  38. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +1 -1
  39. package/src/components/Api/AppSync_Adapter.res +72 -8
  40. package/src/components/Api/AppSync_Adapter.res.mjs +45 -6
  41. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
  42. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -2
  43. package/src/util/Util_AppSync_Caller.res +24 -4
  44. package/src/util/Util_AppSync_Caller.res.mjs +7 -4
  45. package/tests/AppSync_AdapterTest.res +140 -0
  46. package/tests/AppSync_AdapterTest.res.mjs +182 -6
  47. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +7 -3
  48. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res.mjs +18 -3
  49. package/tests/PgChangeFeedRelay_RuntimeTest.res +44 -0
  50. package/tests/PgChangeFeedRelay_RuntimeTest.res.mjs +70 -0
  51. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +2 -1
  52. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res.mjs +2 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
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.173 (2026-07-05)
7
+
8
+ ### Features
9
+
10
+ * **reventless-aws:** aggregate EventLog Postgres runtime + entry-point branch ([fd81ece](https://github.com/ReventlessDev/reventless-core/commit/fd81ece65c2bf6aebf227d74ec2d0f002833d351))
11
+ * **reventless-aws:** DCB EventLog Postgres runtime + entry-point branch (B2.1) ([a6b4f8d](https://github.com/ReventlessDev/reventless-core/commit/a6b4f8d34509ff7213273a1f71f31705b625ff40)), closes [#4](https://github.com/ReventlessDev/reventless-core/issues/4)
12
+ * **reventless-aws:** opt-in [@aws](https://github.com/aws)_iam dual-auth for deploy-time AppSync callers ([94037f1](https://github.com/ReventlessDev/reventless-core/commit/94037f17ae48a33415b02e8e7b178906be8b59a4))
13
+ * **reventless-aws:** Postgres change-feed relay deploy builder + Lambda VPC (B2.3a/b, C1) ([64d67f3](https://github.com/ReventlessDev/reventless-core/commit/64d67f3ecd2c42d59ba814d6d821683ae8c81612))
14
+ * **reventless-aws:** Postgres change-feed relay runtime (B2.2) ([9ccb601](https://github.com/ReventlessDev/reventless-core/commit/9ccb60192457e9da21869e0d1c98fe8dbfb42178))
15
+
16
+
17
+ # 3.0.0-alpha.172 (2026-07-05)
18
+
19
+ ### Bug Fixes
20
+
21
+ * **reventless-aws:** sub-select CommandResult in AppSync mutation caller ([24cf37c](https://github.com/ReventlessDev/reventless-core/commit/24cf37cc537215b9523d1eac4a0f8ac6c610257d))
22
+ ### Features
23
+
24
+ * **postgres:** add reventless-postgres backend + local-platform integration ([6913200](https://github.com/ReventlessDev/reventless-core/commit/69132001f9271e832a5af33416acd5b645feaf47))
25
+ * **postgres:** cold-start pool foundation for AWS Postgres adapters ([b393449](https://github.com/ReventlessDev/reventless-core/commit/b393449769b6cd92abd03d2d5e7f564fe092938e))
26
+ * **reventless-aws:** add PgConnection component + wire reventless-postgres dep ([a403a62](https://github.com/ReventlessDev/reventless-core/commit/a403a6292381513cfe679a2f7a967fda0ab00c0e))
27
+
28
+
6
29
  # 3.0.0-alpha.171 (2026-07-04)
7
30
 
8
31
  **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.171",
3
+ "version": "3.0.0-alpha.173",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -11,13 +11,14 @@
11
11
  "@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
12
12
  "@reventlessdev/rescript-jest": "1.0.0-alpha.5",
13
13
  "@reventlessdev/rescript-effect": "0.1.0-alpha.24",
14
- "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.44",
14
+ "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
15
15
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
16
16
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.13",
17
- "@reventlessdev/reventless-core": "3.0.0-alpha.138",
17
+ "@reventlessdev/reventless-core": "3.0.0-alpha.140",
18
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.86",
18
19
  "@reventlessdev/reventless-interop": "3.0.0-alpha.23",
19
- "@reventlessdev/reventless-spec": "3.0.0-alpha.64",
20
- "@reventlessdev/reventless-infra": "3.0.0-alpha.84"
20
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.2",
21
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.64"
21
22
  },
22
23
  "devDependencies": {
23
24
  "rescript": "^12.3.0",
package/rescript.json CHANGED
@@ -32,7 +32,8 @@
32
32
  "@reventlessdev/reventless-infra",
33
33
  "@reventlessdev/reventless-interop",
34
34
  "@reventlessdev/reventless-spec",
35
- "@reventlessdev/reventless-core"
35
+ "@reventlessdev/reventless-core",
36
+ "@reventlessdev/reventless-postgres"
36
37
  ],
37
38
  "compiler-flags": [],
38
39
  "suffix": ".res.mjs"
@@ -598,7 +598,7 @@ function MakeWithConfig(Config) {
598
598
  return Output$Pulumi.flatMap(targetApi, api => Output$Pulumi.flatMap(api.id, apiId => {
599
599
  let runSchemaPush = () => writeAndScanFragments().then(async allPluginFragments => {
600
600
  let baseFragment;
601
- baseFragment = capturedDeployTarget === "Domain" && Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin");
601
+ baseFragment = capturedDeployTarget === "Domain" && Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
602
602
  let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments);
603
603
  let currentHash = AppSync_Adapter$ReventlessAws.sha256Hex(sdl);
604
604
  let storedHash = tableNameOpt !== undefined ? await readSchemaHash(tableNameOpt, apiId) : undefined;
@@ -653,7 +653,7 @@ function MakeWithConfig(Config) {
653
653
  let hooks_preAdminResolversSchemaHook = adminBarrier => {
654
654
  let match = splitApiOutputsRef.contents;
655
655
  let targetApi = match !== undefined ? match.platformApi : domainApi;
656
- let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin");
656
+ let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
657
657
  let sdl = GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []);
658
658
  return Output$Pulumi.flatMap(Pulumi.all([
659
659
  targetApi,
@@ -920,7 +920,7 @@ function MakeWithConfig(Config) {
920
920
  return;
921
921
  }
922
922
  });
923
- let baseFragment = Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin");
923
+ let baseFragment = Config.splitApi ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(Config.cloner), "Admin", undefined);
924
924
  let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, fragments);
925
925
  return await AppSync_Adapter$ReventlessAws.startSchemaCreationRetrying(AppSync_Adapter$ReventlessAws.getClient(), {
926
926
  apiId: apiId,
@@ -1686,7 +1686,7 @@ function Make($star) {
1686
1686
  return Output$Pulumi.flatMap(targetApi, api => Output$Pulumi.flatMap(api.id, apiId => {
1687
1687
  let runSchemaPush = () => writeAndScanFragments().then(async allPluginFragments => {
1688
1688
  let baseFragment;
1689
- baseFragment = capturedDeployTarget === "Domain" ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin");
1689
+ baseFragment = capturedDeployTarget === "Domain" ? emptyBaseFragment : AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin", undefined);
1690
1690
  let sdl = GraphQL_Stitcher$ReventlessCore.stitch(baseFragment, allPluginFragments);
1691
1691
  let currentHash = AppSync_Adapter$ReventlessAws.sha256Hex(sdl);
1692
1692
  let storedHash = tableNameOpt !== undefined ? await readSchemaHash(tableNameOpt, apiId) : undefined;
@@ -1741,7 +1741,7 @@ function Make($star) {
1741
1741
  let hooks_preAdminResolversSchemaHook = adminBarrier => {
1742
1742
  let match = splitApiOutputsRef.contents;
1743
1743
  let targetApi = match !== undefined ? match.platformApi : domainApi;
1744
- let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin");
1744
+ let adminBaseFragment = AppSync_Adapter$ReventlessAws.injectAwsAuthAll(AdminApi$ReventlessCore.baseFragment(false), "Admin", undefined);
1745
1745
  let sdl = GraphQL_Stitcher$ReventlessCore.stitch(adminBaseFragment, []);
1746
1746
  return Output$Pulumi.flatMap(Pulumi.all([
1747
1747
  targetApi,
@@ -52,7 +52,7 @@ function make(name, referencesName, referencesDb, countsName, countsDb, param, s
52
52
  let componentOpts = {
53
53
  parent: componentOpts_parent
54
54
  };
55
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, code, sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, componentOpts);
55
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, code, sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, componentOpts);
56
56
  let lambda = runtime.parts.lambda;
57
57
  let subscribe = (sourceName, source) => Util_EventSourceMapping$ReventlessAws.subscribe(undefined, lambda, name, sourceName, source, opts);
58
58
  subscribe(referencesName, referencesStream);
@@ -595,7 +595,7 @@ let read = (table: resolvedTable, ~crossPartitionTagKeys: array<string>=[]) =>
595
595
  //
596
596
  // All Puts and Updates ride a single `TransactWriteItems` call — atomic by
597
597
  // DynamoDB. Conflicts surface as `TransactionCanceledException` →
598
- // `DynamoDb_Error.StaleState` → `Error("Conflict: …")`.
598
+ // `DynamoDb_Error.StaleState` → `Error(DcbEventLog.Conflict)`.
599
599
 
600
600
  let fenceSortKey = "FENCE"
601
601
 
@@ -861,9 +861,9 @@ let runTransactWrite = async (
861
861
  ->Effect.map(_ => Ok(basePosition))
862
862
  ->Effect.catchAll(err =>
863
863
  switch err {
864
- | DynamoDb_Error.StaleState(msg) => Effect.succeed(Error(`Conflict: ${msg}`))
864
+ | DynamoDb_Error.StaleState(_msg) => Effect.succeed(Error(ReventlessInfra.DcbEventLog.Conflict))
865
865
  | Transient(msg) | Permanent(msg) =>
866
- Effect.succeed(Error(`${errorPrefix}: ${msg}`))
866
+ Effect.succeed(Error(ReventlessInfra.DcbEventLog.StorageFailure(`${errorPrefix}: ${msg}`)))
867
867
  }
868
868
  )
869
869
  ->Effect.runPromise
@@ -886,7 +886,9 @@ let appendUnconditional = async (
886
886
  Ok(generatePosition())
887
887
  } else if totalItems > transactWriteItemsLimit {
888
888
  Error(
889
- `DCB append: TransactWriteItems limit exceeded (${totalItems->Int.toString} > ${transactWriteItemsLimit->Int.toString}); reduce events or distinct tag values per command`,
889
+ ReventlessInfra.DcbEventLog.StorageFailure(
890
+ `DCB append: TransactWriteItems limit exceeded (${totalItems->Int.toString} > ${transactWriteItemsLimit->Int.toString}); reduce events or distinct tag values per command`,
891
+ ),
890
892
  )
891
893
  } else {
892
894
  let basePosition = generatePosition()
@@ -1211,7 +1213,9 @@ let appendConditional = async (
1211
1213
 
1212
1214
  if queryTags->Array.length == 0 {
1213
1215
  Error(
1214
- "DCB append: tagless conditions are not supported on DynamoDB — every queryItem must have at least one tag",
1216
+ ReventlessInfra.DcbEventLog.StorageFailure(
1217
+ "DCB append: tagless conditions are not supported on DynamoDB — every queryItem must have at least one tag",
1218
+ ),
1215
1219
  )
1216
1220
  } else {
1217
1221
  let basePosition = generatePosition()
@@ -1219,7 +1223,9 @@ let appendConditional = async (
1219
1223
  let totalItems = transactItems->Array.length
1220
1224
  if totalItems > transactWriteItemsLimit {
1221
1225
  Error(
1222
- `DCB append: TransactWriteItems limit exceeded (${totalItems->Int.toString} > ${transactWriteItemsLimit->Int.toString}); reduce events or distinct tag values per command`,
1226
+ ReventlessInfra.DcbEventLog.StorageFailure(
1227
+ `DCB append: TransactWriteItems limit exceeded (${totalItems->Int.toString} > ${transactWriteItemsLimit->Int.toString}); reduce events or distinct tag values per command`,
1228
+ ),
1223
1229
  )
1224
1230
  } else {
1225
1231
  let input: TransactWriteCommand.input = {transactItems: transactItems}
@@ -641,7 +641,7 @@ async function runTransactWrite(input, basePosition, errorPrefix) {
641
641
  case "StaleState" :
642
642
  return Effect$1.succeed({
643
643
  TAG: "Error",
644
- _0: `Conflict: ` + err._0
644
+ _0: "Conflict"
645
645
  });
646
646
  case "Transient" :
647
647
  case "Permanent" :
@@ -649,7 +649,10 @@ async function runTransactWrite(input, basePosition, errorPrefix) {
649
649
  }
650
650
  return Effect$1.succeed({
651
651
  TAG: "Error",
652
- _0: errorPrefix + `: ` + err._0
652
+ _0: {
653
+ TAG: "StorageFailure",
654
+ _0: errorPrefix + `: ` + err._0
655
+ }
653
656
  });
654
657
  }));
655
658
  }
@@ -666,7 +669,10 @@ async function appendUnconditional(table, events, partitionTag) {
666
669
  if (totalItems > 100) {
667
670
  return {
668
671
  TAG: "Error",
669
- _0: `DCB append: TransactWriteItems limit exceeded (` + totalItems.toString() + ` > ` + (100).toString() + `); reduce events or distinct tag values per command`
672
+ _0: {
673
+ TAG: "StorageFailure",
674
+ _0: `DCB append: TransactWriteItems limit exceeded (` + totalItems.toString() + ` > ` + (100).toString() + `); reduce events or distinct tag values per command`
675
+ }
670
676
  };
671
677
  }
672
678
  let basePosition = generatePosition();
@@ -908,7 +914,10 @@ async function appendConditional(table, events, cond, partitionTag, crossPartiti
908
914
  if (queryTags.length === 0) {
909
915
  return {
910
916
  TAG: "Error",
911
- _0: "DCB append: tagless conditions are not supported on DynamoDB — every queryItem must have at least one tag"
917
+ _0: {
918
+ TAG: "StorageFailure",
919
+ _0: "DCB append: tagless conditions are not supported on DynamoDB — every queryItem must have at least one tag"
920
+ }
912
921
  };
913
922
  }
914
923
  let basePosition = generatePosition();
@@ -917,7 +926,10 @@ async function appendConditional(table, events, cond, partitionTag, crossPartiti
917
926
  if (totalItems > 100) {
918
927
  return {
919
928
  TAG: "Error",
920
- _0: `DCB append: TransactWriteItems limit exceeded (` + totalItems.toString() + ` > ` + (100).toString() + `); reduce events or distinct tag values per command`
929
+ _0: {
930
+ TAG: "StorageFailure",
931
+ _0: `DCB append: TransactWriteItems limit exceeded (` + totalItems.toString() + ` > ` + (100).toString() + `); reduce events or distinct tag values per command`
932
+ }
921
933
  };
922
934
  }
923
935
  let input = {
@@ -0,0 +1,26 @@
1
+ // AWS-side runtime DcbEventLog operations for the Postgres backend. The DCB
2
+ // analogue of EventLogStorage_Postgres_Runtime: turn a resolved connection config
3
+ // into the DCB operation set (`read`/`append`/`readStream`), bound to the
4
+ // container-lifetime pool from PgRuntime. Consumed by DcbCommandTopicEntryPoint.mjs
5
+ // (Postgres branch, selected when a PG_CONNECTION is present in HANDLER_CONFIG).
6
+ //
7
+ // The pg maker ignores `~indexes` / `~partitionTag` / `~crossPartitionTagKeys`
8
+ // (Postgres evaluates the real `DcbTag.query` atomically — no GSI routing), so
9
+ // they are passed empty here. `logName` is the `dcb_event.log_name` discriminator.
10
+
11
+ let opsFor = (
12
+ config: PgConnection.connectionConfig,
13
+ ~logName: string,
14
+ ~lockStrategy: ReventlessPostgres.DcbEventLogStorage_Postgres.lockStrategy=#AdvisoryLocks,
15
+ ): ReventlessCore.DcbEventLog_Adapter.operations => {
16
+ let pool = PgRuntime.poolFor(config)
17
+ let (_name, ops, _storage) = ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
18
+ ~pool,
19
+ ~name=logName,
20
+ ~indexes=[],
21
+ ~partitionTag=(),
22
+ ~opts=(),
23
+ ~lockStrategy,
24
+ )
25
+ ops
26
+ }
@@ -0,0 +1,15 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
4
+ import * as DcbEventLogStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/DcbEventLogStorage_Postgres.res.mjs";
5
+
6
+ function opsFor(config, logName, lockStrategyOpt) {
7
+ let lockStrategy = lockStrategyOpt !== undefined ? lockStrategyOpt : "AdvisoryLocks";
8
+ let pool = PgRuntime$ReventlessAws.poolFor(config);
9
+ return DcbEventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, logName, [], undefined, undefined, undefined, lockStrategy, undefined)[1];
10
+ }
11
+
12
+ export {
13
+ opsFor,
14
+ }
15
+ /* PgRuntime-ReventlessAws Not a pure module */
@@ -0,0 +1,23 @@
1
+ // AWS-side runtime EventLog operations for the Postgres backend. Mirrors the
2
+ // role of EventLogStorage_DynamoDb_Runtime: turn a resolved connection config
3
+ // into the classic EventLog operation set, bound to the container-lifetime pool
4
+ // from PgRuntime. Consumed by AggregateEntryPoint.mjs (Postgres branch, selected
5
+ // when a PG_CONNECTION is present in HANDLER_CONFIG).
6
+ //
7
+ // Unlike DynamoDB (table-per-aggregate), all aggregates share the single
8
+ // Postgres `event_log` table, discriminated by `log_name`. `logName` is that
9
+ // per-aggregate discriminator — the same stable identifier the builder would
10
+ // otherwise use as the DynamoDB table name.
11
+
12
+ let opsFor = (
13
+ config: PgConnection.connectionConfig,
14
+ ~logName: string,
15
+ ): ReventlessCore.EventLog_Adapter.operations => {
16
+ let pool = PgRuntime.poolFor(config)
17
+ let (_name, ops, _storage) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
18
+ ~pool,
19
+ ~name=logName,
20
+ ~opts=(),
21
+ )
22
+ ops
23
+ }
@@ -0,0 +1,14 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
4
+ import * as EventLogStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/EventLogStorage_Postgres.res.mjs";
5
+
6
+ function opsFor(config, logName) {
7
+ let pool = PgRuntime$ReventlessAws.poolFor(config);
8
+ return EventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, logName, undefined, undefined)[1];
9
+ }
10
+
11
+ export {
12
+ opsFor,
13
+ }
14
+ /* PgRuntime-ReventlessAws Not a pure module */
@@ -0,0 +1,181 @@
1
+ // Deploy-time builder for the Postgres change-feed relay (B2.3b).
2
+ //
3
+ // Provisions a scheduled, in-VPC Lambda that drains each Postgres DCB log via
4
+ // PgChangeFeed and relays events to the plugin EventCollector SQS queue — which
5
+ // fans out to read-model projections, aggregate command topics, and the
6
+ // cross-plugin SNS EventTopic. The platform (D1, B2.3c) calls this with the set of
7
+ // Postgres-backed DCB logs and points the EventCollector's event source at the
8
+ // relay-fed queue.
9
+ //
10
+ // NOTE: EventBridge rate rules have a 1-minute floor, so v1 poll latency is
11
+ // >= 1 minute. Sub-minute latency needs a self-invoking loop / EventBridge
12
+ // Scheduler (follow-up) or the Fargate-LISTEN upgrade.
13
+
14
+ // One Postgres-backed DCB log to relay.
15
+ type relayLog = {
16
+ connectionConfig: Pulumi.Output.t<PgConnection.connectionConfig>,
17
+ /** dcb_event.log_name — also the DCB `event_log` discriminator. */
18
+ logName: string,
19
+ /** dcb_subscription checkpoint key for this relay. */
20
+ subscriber: string,
21
+ /** Plugin EventCollector SQS queue this log's events are relayed to. */
22
+ targetQueueUrl: Pulumi.Output.t<string>,
23
+ targetQueueArn: Pulumi.Output.t<string>,
24
+ }
25
+
26
+ // ~securityGroupId: DB-access SG the relay Lambda attaches (PgConnection.securityGroupId).
27
+ // ~subnetIds: private subnets for the relay Lambda (PgConnection.subnetIds).
28
+ let make = (
29
+ ~name: string,
30
+ ~logs: array<relayLog>,
31
+ ~securityGroupId: Pulumi.Output.t<string>,
32
+ ~subnetIds: array<Pulumi.Input.t<string>>,
33
+ ~intervalMinutes: int=1,
34
+ ~opts=?,
35
+ ): array<ReventlessInfra.Adapter.resource> => {
36
+ open PulumiAws
37
+ let customOpts =
38
+ opts->Option.map(ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions)
39
+
40
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
41
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs",
42
+ ~packageDirs=Dict.make(),
43
+ )
44
+
45
+ // HANDLER_CONFIG.logs[], serialized from the deploy-time-resolved Outputs.
46
+ let logJsonOutputs = logs->Array.map(l =>
47
+ (l.connectionConfig, l.targetQueueUrl)
48
+ ->Pulumi.Output.all2
49
+ ->Pulumi.Output.apply(((cc, queueUrl)) => {
50
+ let pgConnectionJson =
51
+ [
52
+ ("host", cc.host->JSON.Encode.string),
53
+ ("port", cc.port->Int.toFloat->JSON.Encode.float),
54
+ ("database", cc.database->JSON.Encode.string),
55
+ ("username", cc.username->JSON.Encode.string),
56
+ ("secretArn", cc.secretArn->JSON.Encode.string),
57
+ ]
58
+ ->Dict.fromArray
59
+ ->JSON.Encode.object
60
+ [
61
+ ("pgConnection", pgConnectionJson),
62
+ ("logName", l.logName->JSON.Encode.string),
63
+ ("subscriber", l.subscriber->JSON.Encode.string),
64
+ ("targetQueueUrl", queueUrl->JSON.Encode.string),
65
+ ]
66
+ ->Dict.fromArray
67
+ ->JSON.Encode.object
68
+ ->JSON.stringify
69
+ })
70
+ )
71
+ let handlerConfigOutput =
72
+ Pulumi.Output.all(logJsonOutputs)->Pulumi.Output.apply(items =>
73
+ `{"logs":[${items->Array.join(",")}]}`
74
+ )
75
+
76
+ let envVars = Dict.fromArray([("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)])
77
+
78
+ let vpcConfig =
79
+ securityGroupId
80
+ ->Pulumi.Output.apply(sgId =>
81
+ (
82
+ {
83
+ Lambda.Function.subnetIds: subnetIds->Pulumi.Input.make,
84
+ securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
85
+ }: Lambda.Function.vpcConfig
86
+ )
87
+ )
88
+ ->Pulumi.Output.asInput
89
+
90
+ let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
91
+ ~name,
92
+ ~code,
93
+ ~sourceCodeHash,
94
+ ~envVars,
95
+ ~vpcConfig,
96
+ ~opts?,
97
+ )
98
+
99
+ // IAM: GetSecretValue on each log's DB secret + SendMessage on each target queue.
100
+ let secretArns =
101
+ logs->Array.map(l => l.connectionConfig->Pulumi.Output.apply(cc => cc.secretArn))
102
+ let queueArns = logs->Array.map(l => l.targetQueueArn)
103
+ let _iam =
104
+ (Pulumi.Output.all(secretArns), Pulumi.Output.all(queueArns), runtime.parts.lambdaRole.id)
105
+ ->Pulumi.Output.all3
106
+ ->Pulumi.Output.apply(((secretArns, queueArns, roleId)) => {
107
+ open PulumiAws.PolicyDocument
108
+ let _ = PulumiAws.IAM.RolePolicy.make(
109
+ ~name=`${name}RelayAccess`,
110
+ ~args={
111
+ policy: PulumiAws.PolicyDocument.make(
112
+ ~id=`${name}RelayAccessPolicy`,
113
+ ~statements=[
114
+ {
115
+ sid: "AllowGetSecret",
116
+ effect: Allow,
117
+ actions: Action("secretsmanager:GetSecretValue"),
118
+ resources: Resources(secretArns),
119
+ },
120
+ {
121
+ sid: "AllowSendSqs",
122
+ effect: Allow,
123
+ actions: Action("sqs:SendMessage"),
124
+ resources: Resources(queueArns),
125
+ },
126
+ ],
127
+ )
128
+ ->PulumiAws.PolicyDocument.toJsonString
129
+ ->Pulumi.Input.make,
130
+ role: roleId->Pulumi.Input.make,
131
+ },
132
+ )
133
+ })
134
+
135
+ // Scheduled poll: EventRule (rate) → Permission → EventTarget.
136
+ let rule = {
137
+ open PulumiAws.Cloudwatch
138
+ EventRule.make(
139
+ ~name=`${Pulumi.Pulumi.getStackName()}-${name}`,
140
+ ~args={
141
+ description: "Poll Postgres DCB change feed and relay to EventCollector"->Pulumi.Input.make,
142
+ scheduleExpression: EventRule.ScheduleExpression.every(Minutes(intervalMinutes)),
143
+ },
144
+ ~opts=?customOpts,
145
+ )
146
+ }
147
+
148
+ let lambda = runtime.parts.lambda
149
+ let _wire =
150
+ (lambda->Pulumi.Output.flatMap(l => l.arn), lambda->Pulumi.Output.flatMap(l => l.name))
151
+ ->Pulumi.Output.all2
152
+ ->Pulumi.Output.apply(((lambdaArn, lambdaName)) => {
153
+ let _permission = PulumiAws.Lambda.Permission.make(
154
+ ~name=`${name}SchedulePermission`,
155
+ ~args={
156
+ action: "lambda:InvokeFunction",
157
+ function: lambdaName->Pulumi.Input.make,
158
+ principal: AWS.CloudwatchEventRule.principal,
159
+ },
160
+ ~opts=?customOpts,
161
+ )
162
+ let _target = {
163
+ open PulumiAws.Cloudwatch
164
+ EventTarget.make(
165
+ ~name,
166
+ ~args={
167
+ rule: EventTarget.Rule.ofEventRule(rule),
168
+ arn: lambdaArn->Pulumi.Input.make,
169
+ },
170
+ ~opts=?customOpts,
171
+ )
172
+ }
173
+ })
174
+
175
+ [
176
+ lambda
177
+ ->Pulumi.Output.apply(l => l->Util_Lambda.toResource)
178
+ ->ReventlessCore.Adapter.outputToResource,
179
+ rule->Util_Cloudwatch.EventRule.toResource,
180
+ ]
181
+ }
@@ -0,0 +1,135 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Aws from "@pulumi/aws";
4
+ import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Pulumi from "@pulumi/pulumi";
7
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
8
+ import * as AWS$ReventlessAws from "../AWS.res.mjs";
9
+ import * as Adapter$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Adapter.res.mjs";
10
+ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
11
+ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
12
+ import * as Util_Lambda$ReventlessAws from "../../util/Util_Lambda.res.mjs";
13
+ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
14
+ import * as Util_Cloudwatch$ReventlessAws from "../../util/Util_Cloudwatch.res.mjs";
15
+ import * as Cloudwatch_EventRule$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Cloudwatch/Cloudwatch_EventRule.res.mjs";
16
+ import * as Cloudwatch_EventTarget$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Cloudwatch/Cloudwatch_EventTarget.res.mjs";
17
+ import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvironment_Lambda.res.mjs";
18
+
19
+ function make(name, logs, securityGroupId, subnetIds, intervalMinutesOpt, opts) {
20
+ let intervalMinutes = intervalMinutesOpt !== undefined ? intervalMinutesOpt : 1;
21
+ let customOpts = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
22
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs", {}, undefined);
23
+ let logJsonOutputs = logs.map(l => Pulumi.all([
24
+ l.connectionConfig,
25
+ l.targetQueueUrl
26
+ ]).apply(param => {
27
+ let cc = param[0];
28
+ let pgConnectionJson = Object.fromEntries([
29
+ [
30
+ "host",
31
+ cc.host
32
+ ],
33
+ [
34
+ "port",
35
+ cc.port
36
+ ],
37
+ [
38
+ "database",
39
+ cc.database
40
+ ],
41
+ [
42
+ "username",
43
+ cc.username
44
+ ],
45
+ [
46
+ "secretArn",
47
+ cc.secretArn
48
+ ]
49
+ ]);
50
+ return JSON.stringify(Object.fromEntries([
51
+ [
52
+ "pgConnection",
53
+ pgConnectionJson
54
+ ],
55
+ [
56
+ "logName",
57
+ l.logName
58
+ ],
59
+ [
60
+ "subscriber",
61
+ l.subscriber
62
+ ],
63
+ [
64
+ "targetQueueUrl",
65
+ param[1]
66
+ ]
67
+ ]));
68
+ }));
69
+ let handlerConfigOutput = Pulumi.all(logJsonOutputs).apply(items => `{"logs":[` + items.join(",") + `]}`);
70
+ let envVars = Object.fromEntries([[
71
+ "HANDLER_CONFIG",
72
+ handlerConfigOutput
73
+ ]]);
74
+ let vpcConfig = securityGroupId.apply(sgId => ({
75
+ subnetIds: subnetIds,
76
+ securityGroupIds: [sgId]
77
+ }));
78
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, match.code, match.sourceCodeHash, envVars, undefined, undefined, undefined, undefined, undefined, undefined, vpcConfig, opts);
79
+ let secretArns = logs.map(l => l.connectionConfig.apply(cc => cc.secretArn));
80
+ let queueArns = logs.map(l => l.targetQueueArn);
81
+ Pulumi.all([
82
+ Pulumi.all(secretArns),
83
+ Pulumi.all(queueArns),
84
+ runtime.parts.lambdaRole.id
85
+ ]).apply(param => {
86
+ new (Aws.iam.RolePolicy)(name + `RelayAccess`, {
87
+ policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `RelayAccessPolicy`, [
88
+ {
89
+ Sid: "AllowGetSecret",
90
+ Effect: "Allow",
91
+ Action: "secretsmanager:GetSecretValue",
92
+ Resource: param[0]
93
+ },
94
+ {
95
+ Sid: "AllowSendSqs",
96
+ Effect: "Allow",
97
+ Action: "sqs:SendMessage",
98
+ Resource: param[1]
99
+ }
100
+ ])),
101
+ role: param[2]
102
+ });
103
+ });
104
+ let rule = new (Aws.cloudwatch.EventRule)(Pulumi.getStack() + `-` + name, {
105
+ description: "Poll Postgres DCB change feed and relay to EventCollector",
106
+ scheduleExpression: Primitive_option.some(Cloudwatch_EventRule$PulumiAws.ScheduleExpression.every({
107
+ TAG: "Minutes",
108
+ _0: intervalMinutes
109
+ }))
110
+ }, customOpts !== undefined ? Primitive_option.valFromOption(customOpts) : undefined);
111
+ let lambda = runtime.parts.lambda;
112
+ Pulumi.all([
113
+ Output$Pulumi.flatMap(lambda, l => l.arn),
114
+ Output$Pulumi.flatMap(lambda, l => l.name)
115
+ ]).apply(param => {
116
+ new (Aws.lambda.Permission)(name + `SchedulePermission`, {
117
+ action: "lambda:InvokeFunction",
118
+ function: param[1],
119
+ principal: AWS$ReventlessAws.CloudwatchEventRule.principal
120
+ }, customOpts !== undefined ? Primitive_option.valFromOption(customOpts) : undefined);
121
+ new (Aws.cloudwatch.EventTarget)(name, {
122
+ rule: Cloudwatch_EventTarget$PulumiAws.Rule.ofEventRule(rule),
123
+ arn: param[0]
124
+ }, customOpts !== undefined ? Primitive_option.valFromOption(customOpts) : undefined);
125
+ });
126
+ return [
127
+ Adapter$ReventlessCore.outputToResource(lambda.apply(l => Util_Lambda$ReventlessAws.toResource(undefined, l))),
128
+ Util_Cloudwatch$ReventlessAws.EventRule.toResource(rule)
129
+ ];
130
+ }
131
+
132
+ export {
133
+ make,
134
+ }
135
+ /* @pulumi/aws Not a pure module */