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

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 +23 -0
  2. package/docker-compose.postgres.yml +21 -0
  3. package/package.json +7 -6
  4. package/scripts/run-pg-integration-tests.sh +58 -0
  5. package/src/Platform.res +49 -0
  6. package/src/Platform.res.mjs +48 -2
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +7 -1
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +10 -1
  9. package/src/adapter/Api/Platform_UIFragments_Lambda.res +7 -1
  10. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +10 -1
  11. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +6 -21
  12. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +2 -17
  13. package/src/adapter/DcbEventLog/DcbBackend.res +4 -0
  14. package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres.res +2 -2
  15. package/src/adapter/DcbEventLog/DcbEventLogStorage_Postgres.res.mjs +7 -1
  16. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -11
  17. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +2 -24
  18. package/src/adapter/Postgres/PgConnection.res +70 -10
  19. package/src/adapter/Postgres/PgConnection.res.mjs +48 -3
  20. package/src/adapter/Postgres/PgMigration_Builder.res +127 -0
  21. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +51 -0
  22. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +279 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +170 -0
  24. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +202 -11
  25. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +150 -9
  26. package/src/adapter/QueryDb/QueryDbBackend.res +8 -0
  27. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +4 -1
  28. package/src/adapter/QueryDb/QueryDbResolvers.res +6 -5
  29. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +6 -3
  30. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +288 -0
  31. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res.mjs +170 -0
  32. package/src/adapter/QueryDb/QueryDbStorage.res +6 -1
  33. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +2 -1
  34. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -1
  35. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +3 -3
  36. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +3 -14
  37. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +2 -25
  38. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +3 -14
  39. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +2 -25
  40. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +7 -1
  41. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +78 -15
  42. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +43 -26
  43. package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +40 -0
  44. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +88 -0
  45. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +17 -5
  46. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +10 -0
  47. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +3 -0
  48. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +9 -14
  49. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +2 -25
  50. package/src/adapter/Runtime/StateTopicPublish.mjs +204 -0
  51. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +17 -3
  52. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +86 -17
  53. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +50 -33
  54. package/src/adapter/StateTopic/StateTopic_AppSync.res +7 -1
  55. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +10 -1
  56. package/src/plugin/cloner/ClonerRunner_Fargate.res +7 -1
  57. package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +10 -1
  58. package/src/util/Util_Bundle.res +71 -1
  59. package/src/util/Util_Bundle.res.mjs +49 -1
  60. package/tests/PgChangeFeedRelay_IntegrationTest.res +3 -3
  61. package/tests/PgConnectionJsonTest.res +84 -0
  62. package/tests/PgConnectionJsonTest.res.mjs +68 -0
  63. package/tests/PgMigrationEntryPoint_IntegrationTest.res +82 -0
  64. package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +63 -0
  65. package/tests/PgPipeline_IntegrationTest.res +3 -1
  66. package/tests/PgQueryResolver_LambdaTest.res +163 -2
  67. package/tests/PgQueryResolver_LambdaTest.res.mjs +226 -14
  68. package/tests/StateTopicPublishTest.res +162 -0
  69. package/tests/StateTopicPublishTest.res.mjs +158 -0
  70. package/tests/Util_BundleEsmLoaderTest.res +57 -0
  71. package/tests/Util_BundleEsmLoaderTest.res.mjs +44 -0
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.178 (2026-07-06)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **reventless-aws:** make deployed ESM Lambdas self-contained via layer resolver hook ([3fc768c](https://github.com/ReventlessDev/reventless-core/commit/3fc768cba5ef72fca6e4a706eb855f67ac7f0b30))
11
+ ### Features
12
+
13
+ * **reventless-aws:** auth-table pipeline for group-restricted Postgres indexes (B3.2c) ([52be859](https://github.com/ReventlessDev/reventless-core/commit/52be8590ed934a2ffbc898cef381ca4ce50d8cda))
14
+ * **reventless-aws:** deploy-time Postgres schema provisioning via in-VPC migration Lambda (A3) ([44c8eee](https://github.com/ReventlessDev/reventless-core/commit/44c8eeebd351e883f2d2e21460108e2963a061ce))
15
+ * **reventless-aws:** expose DCB Postgres lock-strategy knob (C2) ([3991862](https://github.com/ReventlessDev/reventless-core/commit/3991862aa50eb939796413236a04deae80a61aa3))
16
+ * **reventless-aws:** Postgres read-model live updates via projection Lambda (B3.3) ([ff8fc43](https://github.com/ReventlessDev/reventless-core/commit/ff8fc43049a8fc7134e340a473c2a1665a139140))
17
+ * **reventless-aws:** provision the Postgres read path in plugin-stack mode (B3.2) ([719efbc](https://github.com/ReventlessDev/reventless-core/commit/719efbc20ab6e8bf5bb46499150fe07e54d9eca5))
18
+
19
+
20
+ # 3.0.0-alpha.177 (2026-07-06)
21
+
22
+ ### Features
23
+
24
+ * **reventless-aws:** AppSync read path for Postgres read models (B3.2b) ([a6dd6cb](https://github.com/ReventlessDev/reventless-core/commit/a6dd6cbddfc65410bf96da49bb2603c88670aced))
25
+ * **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))
26
+ * **reventless-postgres,reventless-aws:** items (sub-id connection) resolver for Postgres reads (B3.2c) ([2477220](https://github.com/ReventlessDev/reventless-core/commit/247722062913f5d0bcb5895b152f05033d8297d8))
27
+
28
+
6
29
  # 3.0.0-alpha.176 (2026-07-06)
7
30
 
8
31
  ### Bug Fixes
@@ -0,0 +1,21 @@
1
+ # Postgres sidecar for the PG_URL-gated integration suites (change-feed relay,
2
+ # projection pipeline, schema-migration entry point).
3
+ # Booted/torn down by scripts/run-pg-integration-tests.sh.
4
+ #
5
+ # tmpfs keeps the data dir in RAM (no on-disk state to clean up); the suites
6
+ # ensureSchema + truncateAll themselves, and run serially, so one shared DB is
7
+ # fine.
8
+ services:
9
+ postgres:
10
+ image: postgres:16
11
+ environment:
12
+ POSTGRES_PASSWORD: postgres
13
+ POSTGRES_USER: postgres
14
+ POSTGRES_DB: postgres
15
+ command: "-c fsync=off -c full_page_writes=off"
16
+ tmpfs:
17
+ - /var/lib/postgresql/data
18
+ # Non-standard host port so the sidecar never collides with a dev's own
19
+ # Postgres on 5432 (the run script exports a matching PG_URL).
20
+ ports:
21
+ - "55433:5432"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.176",
3
+ "version": "3.0.0-alpha.178",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -11,14 +11,14 @@
11
11
  "@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
12
12
  "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
13
13
  "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
14
- "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
15
14
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
15
+ "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.46",
16
16
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
17
+ "@reventlessdev/reventless-core": "3.0.0-alpha.143",
17
18
  "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
18
19
  "@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"
20
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.6",
21
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.67"
22
22
  },
23
23
  "devDependencies": {
24
24
  "rescript": "^12.3.0",
@@ -36,7 +36,8 @@
36
36
  ],
37
37
  "testPathIgnorePatterns": [
38
38
  "/node_modules/",
39
- "<rootDir>/tests/integration/"
39
+ "<rootDir>/tests/integration/",
40
+ "Pg.*IntegrationTest\\.res\\.mjs$"
40
41
  ],
41
42
  "moduleFileExtensions": [
42
43
  "js",
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # Boot a Postgres sidecar, run the PG_URL integration suites against it (serially,
3
+ # via jest.integration.pg.config.js), tear down. Safe to run anywhere: if Docker
4
+ # is unavailable the suites are skipped (the CI step is continue-on-error, and not
5
+ # every dev machine has Docker running).
6
+ #
7
+ # If PG_URL is already set (a dev's own Postgres), the sidecar is skipped and the
8
+ # suites run against that database instead.
9
+ set -euo pipefail
10
+
11
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
12
+ COMPOSE="$ROOT/reventless/reventless-aws/docker-compose.postgres.yml"
13
+
14
+ run_jest() {
15
+ cd "$ROOT"
16
+ NODE_OPTIONS='--experimental-vm-modules --no-warnings' \
17
+ npx jest --config jest.integration.pg.config.js "$@"
18
+ }
19
+
20
+ # Honour a pre-set PG_URL (dev's own database) — no sidecar needed.
21
+ if [ -n "${PG_URL:-}" ]; then
22
+ echo "▶ Using existing PG_URL — running PG integration suites"
23
+ run_jest "$@"
24
+ exit $?
25
+ fi
26
+
27
+ if ! command -v docker >/dev/null 2>&1; then
28
+ echo "⏭ Docker not available — skipping PG integration tests"
29
+ exit 0
30
+ fi
31
+ if ! docker info >/dev/null 2>&1; then
32
+ echo "⏭ Docker daemon not running — skipping PG integration tests"
33
+ exit 0
34
+ fi
35
+
36
+ cleanup() { docker compose -f "$COMPOSE" down -v >/dev/null 2>&1 || true; }
37
+ trap cleanup EXIT
38
+
39
+ echo "▶ Starting Postgres…"
40
+ docker compose -f "$COMPOSE" up -d
41
+
42
+ echo "⏳ Waiting for Postgres…"
43
+ for i in $(seq 1 30); do
44
+ if docker compose -f "$COMPOSE" exec -T postgres pg_isready -U postgres >/dev/null 2>&1; then
45
+ echo "✓ Postgres is up"
46
+ break
47
+ fi
48
+ if [ "$i" -eq 30 ]; then
49
+ echo "✗ Postgres did not start within 30s"
50
+ exit 1
51
+ fi
52
+ sleep 1
53
+ done
54
+
55
+ # Host port matches docker-compose.postgres.yml's mapping (55433 → container 5432),
56
+ # chosen to avoid colliding with a dev's own Postgres on 5432.
57
+ export PG_URL="postgres://postgres:postgres@localhost:55433/postgres"
58
+ run_jest "$@"
package/src/Platform.res CHANGED
@@ -91,6 +91,7 @@ module MakeWithConfig = (
91
91
  connectionConfig: pg.connectionConfig,
92
92
  securityGroupId: pg.securityGroupId,
93
93
  subnetIds: pg.subnetIds,
94
+ lockStrategy: pg.lockStrategy,
94
95
  })
95
96
  EventLogBackend.set({
96
97
  connectionConfig: pg.connectionConfig,
@@ -332,6 +333,25 @@ module MakeWithConfig = (
332
333
  Some(eventsApi)
333
334
  }
334
335
 
336
+ // B3.3: publish the events-API endpoint + ARN to the projection-Lambda runtime
337
+ // builders, so a subscription-enabled Postgres read model / view slice gets the
338
+ // APPSYNC_ENDPOINT env + appsync:EventPublish IAM at their `finish`. Set once
339
+ // here (before plugins build) in both monolithic and plugin-stack mode — the
340
+ // phantom events API carries apiArn + dns, so httpEndpoint resolves either way.
341
+ switch domainEventsApiOpt {
342
+ | Some(eventsApi) =>
343
+ let cfg = {
344
+ EventCollectorRuntime_Builder_Single.endpoint: AppSync_EventsApi.httpEndpoint(eventsApi),
345
+ apiArn: eventsApi.api.apiArn,
346
+ }
347
+ EventCollectorRuntime_Builder_Single.setEventsApiConfig(cfg)
348
+ StateViewSliceRuntime_Builder_Single.setEventsApiConfig({
349
+ StateViewSliceRuntime_Builder_Single.endpoint: AppSync_EventsApi.httpEndpoint(eventsApi),
350
+ apiArn: eventsApi.api.apiArn,
351
+ })
352
+ | None => ()
353
+ }
354
+
335
355
  // Returns the AppSync API/role to attach resolvers to for the current deploy target.
336
356
  // Hoisted above ApiConfig so the module can reference them as thunks.
337
357
  let resolveTargetApi = () =>
@@ -1470,6 +1490,18 @@ module MakeWithConfig = (
1470
1490
  // built above, so the DcbBackend relay registry is complete).
1471
1491
  provisionPgChangeFeedRelay()
1472
1492
 
1493
+ // B3.2b: provision the shared PgQueryResolver Lambda + AppSync data source for
1494
+ // Postgres-backed read models (monolithic mode). Runs after construct so the
1495
+ // resolver-binding registry (QueryDbResolvers_Lambda.make) and the ReadModel
1496
+ // spec-module registry are complete; it fulfils the deferred `dataSourceName`
1497
+ // the Postgres storage maker handed to the (schema-pushed) resolvers. App read
1498
+ // model resolvers attach to the Domain API. Plugin-stack mode is deferred —
1499
+ // it needs a per-stack data-source name to avoid collisions on the shared API.
1500
+ switch QueryDbBackend.get() {
1501
+ | Some(sel) => PgQueryResolver_Builder.provision(~api=domainApi, ~selection=sel, ~opts={})
1502
+ | None => ()
1503
+ }
1504
+
1473
1505
  // Admin schema push is fired by preAdminResolversSchemaHook from inside
1474
1506
  // Admin.construct (with createResolvers gated on it). Only exports below.
1475
1507
  if Config.splitApi {
@@ -2034,6 +2066,23 @@ module MakeWithConfig = (
2034
2066
  // plugin's Postgres DCB log(s) + collector queue were registered during P.make()).
2035
2067
  provisionPgChangeFeedRelay()
2036
2068
 
2069
+ // B3.2b/c: provision this plugin stack's PgQueryResolver Lambda + AppSync data
2070
+ // source for its Postgres-backed read models (registered during P.make()). App
2071
+ // read model resolvers attach to the Domain API. Per-plugin Lambda + auto-named
2072
+ // data source, so plugin stacks don't collide. The node(id) resolver is skipped
2073
+ // in plugin-stack mode — it's a single shared Query.node field only one stack
2074
+ // may own (monolithic-only; see PgQueryResolver_Builder.provision).
2075
+ switch QueryDbBackend.get() {
2076
+ | Some(sel) =>
2077
+ PgQueryResolver_Builder.provision(
2078
+ ~api=domainApi,
2079
+ ~selection=sel,
2080
+ ~opts={},
2081
+ ~createNodeResolver=false,
2082
+ )
2083
+ | None => ()
2084
+ }
2085
+
2037
2086
  // Zero-downtime handover (Part 3.1): fire ONE synthetic heartbeat for the
2038
2087
  // just-deployed plugin so its new version runs the connect handshake within
2039
2088
  // seconds instead of waiting for the CloudWatch heartbeat rule's first
@@ -61,6 +61,7 @@ import * as Task_Builder_PerBucket$ReventlessAws from "./components/Task_Builder
61
61
  import * as AutomationSlice_Builder$ReventlessAws from "./components/AutomationSlice_Builder.res.mjs";
62
62
  import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/EventTopicPublisher_SNS.res.mjs";
63
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";
64
65
  import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
65
66
  import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
66
67
  import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
@@ -88,6 +89,7 @@ import * as EventTopicPublisher_DynamoDbStream$ReventlessAws from "./adapter/Eve
88
89
  import * as InboundTranslationResolvers_AppSync$ReventlessAws from "./adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res.mjs";
89
90
  import * as PluginExtensionPointRuntime_Builder$ReventlessAws from "./plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs";
90
91
  import * as ReadModel_Builder_NoResolver_Stream$ReventlessAws from "./components/ReadModel_Builder_NoResolver_Stream.res.mjs";
92
+ import * as EventCollectorRuntime_Builder_Single$ReventlessAws from "./adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs";
91
93
  import * as Platform_ComponentDefinitions_Lambda$ReventlessAws from "./adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs";
92
94
  import * as StateViewSliceRuntime_Builder_Single$ReventlessAws from "./adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs";
93
95
  import * as AggregateRuntime_Builder_Single_Async$ReventlessAws from "./adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs";
@@ -127,7 +129,8 @@ function MakeWithConfig(Config) {
127
129
  DcbBackend$ReventlessAws.set({
128
130
  connectionConfig: pg.connectionConfig,
129
131
  securityGroupId: pg.securityGroupId,
130
- subnetIds: pg.subnetIds
132
+ subnetIds: pg.subnetIds,
133
+ lockStrategy: pg.lockStrategy
131
134
  });
132
135
  EventLogBackend$ReventlessAws.set({
133
136
  connectionConfig: pg.connectionConfig,
@@ -273,6 +276,19 @@ function MakeWithConfig(Config) {
273
276
  let awsRegion = Stdlib_Option.getOr(new Pulumi.Config("aws").get("region"), "unknown");
274
277
  domainEventsApiOpt = AppSync_EventsApi$ReventlessAws.make("DomainEventsApi", cognitoPool.poolId, awsRegion, {});
275
278
  }
279
+ if (domainEventsApiOpt !== undefined) {
280
+ let cfg_endpoint = AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt);
281
+ let cfg_apiArn = domainEventsApiOpt.api.apiArn;
282
+ let cfg = {
283
+ endpoint: cfg_endpoint,
284
+ apiArn: cfg_apiArn
285
+ };
286
+ EventCollectorRuntime_Builder_Single$ReventlessAws.setEventsApiConfig(cfg);
287
+ StateViewSliceRuntime_Builder_Single$ReventlessAws.setEventsApiConfig({
288
+ endpoint: AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt),
289
+ apiArn: domainEventsApiOpt.api.apiArn
290
+ });
291
+ }
276
292
  let resolveTargetApi = () => {
277
293
  let match = currentDeployTarget.contents;
278
294
  if (match === "Domain") {
@@ -981,6 +997,10 @@ function MakeWithConfig(Config) {
981
997
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
982
998
  }
983
999
  provisionPgChangeFeedRelay();
1000
+ let sel = QueryDbBackend$ReventlessAws.get();
1001
+ if (sel !== undefined) {
1002
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {}, undefined);
1003
+ }
984
1004
  if (Config.splitApi) {
985
1005
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
986
1006
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
@@ -1255,6 +1275,10 @@ function MakeWithConfig(Config) {
1255
1275
  let pluginOutputs = Component$ReventlessCore.outputs(pluginComponent);
1256
1276
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(pluginOutputs);
1257
1277
  provisionPgChangeFeedRelay();
1278
+ let sel = QueryDbBackend$ReventlessAws.get();
1279
+ if (sel !== undefined) {
1280
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {}, false);
1281
+ }
1258
1282
  let hbConfig = PluginRuntime_Builder$ReventlessAws.heartbeatConfigRef.contents;
1259
1283
  let match = IndexJs.isDryRun();
1260
1284
  let match$1 = hbConfig.epQueueUrl;
@@ -1337,7 +1361,8 @@ function Make($star) {
1337
1361
  DcbBackend$ReventlessAws.set({
1338
1362
  connectionConfig: pg.connectionConfig,
1339
1363
  securityGroupId: pg.securityGroupId,
1340
- subnetIds: pg.subnetIds
1364
+ subnetIds: pg.subnetIds,
1365
+ lockStrategy: pg.lockStrategy
1341
1366
  });
1342
1367
  EventLogBackend$ReventlessAws.set({
1343
1368
  connectionConfig: pg.connectionConfig,
@@ -1483,6 +1508,19 @@ function Make($star) {
1483
1508
  let awsRegion = Stdlib_Option.getOr(new Pulumi.Config("aws").get("region"), "unknown");
1484
1509
  domainEventsApiOpt = AppSync_EventsApi$ReventlessAws.make("DomainEventsApi", cognitoPool.poolId, awsRegion, {});
1485
1510
  }
1511
+ if (domainEventsApiOpt !== undefined) {
1512
+ let cfg_endpoint = AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt);
1513
+ let cfg_apiArn = domainEventsApiOpt.api.apiArn;
1514
+ let cfg = {
1515
+ endpoint: cfg_endpoint,
1516
+ apiArn: cfg_apiArn
1517
+ };
1518
+ EventCollectorRuntime_Builder_Single$ReventlessAws.setEventsApiConfig(cfg);
1519
+ StateViewSliceRuntime_Builder_Single$ReventlessAws.setEventsApiConfig({
1520
+ endpoint: AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt),
1521
+ apiArn: domainEventsApiOpt.api.apiArn
1522
+ });
1523
+ }
1486
1524
  let resolveTargetApi = () => {
1487
1525
  let match = currentDeployTarget.contents;
1488
1526
  if (match === "Domain") {
@@ -2185,6 +2223,10 @@ function Make($star) {
2185
2223
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
2186
2224
  }
2187
2225
  provisionPgChangeFeedRelay();
2226
+ let sel = QueryDbBackend$ReventlessAws.get();
2227
+ if (sel !== undefined) {
2228
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {}, undefined);
2229
+ }
2188
2230
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
2189
2231
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
2190
2232
  Pulumi$Pulumi.$$export("platformApiRoleArn", Output$Pulumi.flatMap(platformApiRole, role => role.arn));
@@ -2441,6 +2483,10 @@ function Make($star) {
2441
2483
  let pluginOutputs = Component$ReventlessCore.outputs(pluginComponent);
2442
2484
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(pluginOutputs);
2443
2485
  provisionPgChangeFeedRelay();
2486
+ let sel = QueryDbBackend$ReventlessAws.get();
2487
+ if (sel !== undefined) {
2488
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {}, false);
2489
+ }
2444
2490
  let hbConfig = PluginRuntime_Builder$ReventlessAws.heartbeatConfigRef.contents;
2445
2491
  let match = IndexJs.isDryRun();
2446
2492
  let match$1 = hbConfig.epQueueUrl;
@@ -147,8 +147,12 @@ let make = (
147
147
  "index.mjs",
148
148
  Pulumi.Asset.stringAsset(handlerCodeStub)->Pulumi.Archive.assetToAssetOrArchive,
149
149
  )
150
+ // ESM self-containment: the handler imports @aws-sdk/* bare specifiers the
151
+ // nodejs22.x runtime provides only under /var/runtime — unreachable from
152
+ // /var/task ESM without the resolver hook. Ship the loader + set its env vars.
153
+ let loaderHash = Util_Bundle.addEsmLoaderAssets(archiveContents)
150
154
  let code = Pulumi.Archive.assetArchive(archiveContents)
151
- let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub)
155
+ let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub ++ "\n---\n" ++ loaderHash)
152
156
 
153
157
  let layers =
154
158
  Lambda.reventlessLayerArn
@@ -173,6 +177,8 @@ let make = (
173
177
  Lambda.Function.variables: Dict.fromArray([
174
178
  ("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
175
179
  ("PLUGIN_RM_TABLE", pluginReadModelTableName->Pulumi.Output.asInput),
180
+ ("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
181
+ ("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
176
182
  ]),
177
183
  }: Lambda.Function.functionEnvironment
178
184
  )->Pulumi.Input.make,
@@ -119,8 +119,9 @@ function make(api, pluginReadModelTableName, opts) {
119
119
  let archiveContents = {};
120
120
  let handlerCodeStub = makeHandlerCode("", adminEntryJson);
121
121
  archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(handlerCodeStub);
122
+ let loaderHash = Util_Bundle$ReventlessAws.addEsmLoaderAssets(archiveContents);
122
123
  let code = new (Pulumi.asset.AssetArchive)(archiveContents);
123
- let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub);
124
+ let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
124
125
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
125
126
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
126
127
  handler: "index.handler",
@@ -140,6 +141,14 @@ function make(api, pluginReadModelTableName, opts) {
140
141
  [
141
142
  "PLUGIN_RM_TABLE",
142
143
  pluginReadModelTableName
144
+ ],
145
+ [
146
+ "NODE_OPTIONS",
147
+ Util_Bundle$ReventlessAws.esmLoaderNodeOptions
148
+ ],
149
+ [
150
+ "ESM_FALLBACK_DIRS",
151
+ Util_Bundle$ReventlessAws.esmFallbackDirs
143
152
  ]
144
153
  ])
145
154
  },
@@ -138,8 +138,12 @@ let make = (
138
138
  "index.mjs",
139
139
  Pulumi.Asset.stringAsset(handlerCodeStub)->Pulumi.Archive.assetToAssetOrArchive,
140
140
  )
141
+ // ESM self-containment: the handler imports @aws-sdk/* bare specifiers the
142
+ // nodejs22.x runtime provides only under /var/runtime — unreachable from
143
+ // /var/task ESM without the resolver hook. Ship the loader + set its env vars.
144
+ let loaderHash = Util_Bundle.addEsmLoaderAssets(archiveContents)
141
145
  let code = Pulumi.Archive.assetArchive(archiveContents)
142
- let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub)
146
+ let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub ++ "\n---\n" ++ loaderHash)
143
147
 
144
148
  let layers =
145
149
  Lambda.reventlessLayerArn
@@ -164,6 +168,8 @@ let make = (
164
168
  Lambda.Function.variables: Dict.fromArray([
165
169
  ("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
166
170
  ("UI_FRAGMENT_RM_TABLE", uiFragmentRegistryTableName->Pulumi.Output.asInput),
171
+ ("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
172
+ ("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
167
173
  ]),
168
174
  }: Lambda.Function.functionEnvironment
169
175
  )->Pulumi.Input.make,
@@ -185,8 +185,9 @@ export async function handler() {
185
185
  }
186
186
  `;
187
187
  archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(handlerCodeStub);
188
+ let loaderHash = Util_Bundle$ReventlessAws.addEsmLoaderAssets(archiveContents);
188
189
  let code = new (Pulumi.asset.AssetArchive)(archiveContents);
189
- let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub);
190
+ let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
190
191
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
191
192
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
192
193
  handler: "index.handler",
@@ -206,6 +207,14 @@ export async function handler() {
206
207
  [
207
208
  "UI_FRAGMENT_RM_TABLE",
208
209
  uiFragmentRegistryTableName
210
+ ],
211
+ [
212
+ "NODE_OPTIONS",
213
+ Util_Bundle$ReventlessAws.esmLoaderNodeOptions
214
+ ],
215
+ [
216
+ "ESM_FALLBACK_DIRS",
217
+ Util_Bundle$ReventlessAws.esmFallbackDirs
209
218
  ]
210
219
  ])
211
220
  },
@@ -47,27 +47,12 @@ let make: ReventlessCore.Counter_Adapter.handlerMaker = (
47
47
  packageDirs->Dict.set(specPkg, Util_Bundle.resolvePackageRoot(specPkg))
48
48
  packageDirs->Dict.set(mappingsPkg, Util_Bundle.resolvePackageRoot(mappingsPkg))
49
49
 
50
- let reExportCode = `export { handler } from "@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs";`
51
-
52
- let archiveContents: dict<Pulumi.Archive.assetOrArchive> = Dict.make()
53
- archiveContents->Dict.set(
54
- "index.mjs",
55
- Pulumi.Asset.stringAsset(reExportCode)->Pulumi.Archive.assetToAssetOrArchive,
56
- )
57
- let packageContentHashes: ref<array<string>> = ref([])
58
- packageDirs->Dict.forEachWithKey((pkgRoot, pkgName) => {
59
- let (archive, contentHash) = Util_Bundle.createFilteredPackageArchive(pkgRoot)
60
- archiveContents->Dict.set(
61
- `node_modules/${pkgName}`,
62
- archive->Pulumi.Archive.archiveToAssetOrArchive,
63
- )
64
- packageContentHashes.contents->Array.push(`${pkgName}:${contentHash}`)
65
- })
66
-
67
- let code = Pulumi.Archive.assetArchive(archiveContents)
68
- packageContentHashes.contents->Array.sort(String.compare)
69
- let sourceCodeHash = Util_Bundle.hashString(
70
- reExportCode ++ packageContentHashes.contents->Array.join(","),
50
+ // buildCodeArchive centralises the entry-point re-export, package bundling, and
51
+ // the ESM self-containment loader (register-hook.mjs / layer-resolver.mjs) that
52
+ // makeFromCodeAsset's NODE_OPTIONS=--import depends on.
53
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
54
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs",
55
+ ~packageDirs,
71
56
  )
72
57
 
73
58
  let componentOpts: Pulumi.ComponentResource.options = {parent: ?opts.parent}
@@ -1,9 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
4
3
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
4
  import * as Pulumi from "@pulumi/pulumi";
6
- import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
7
5
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
8
6
  import * as Util_DynamoDbStream$ReventlessAws from "../../util/Util_DynamoDbStream.res.mjs";
9
7
  import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
@@ -34,25 +32,12 @@ function make(name, referencesName, referencesDb, countsName, countsDb, param, s
34
32
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(mappingsModulePath);
35
33
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(specPkg);
36
34
  packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(mappingsPkg);
37
- let reExportCode = `export { handler } from "@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs";`;
38
- let archiveContents = {};
39
- archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(reExportCode);
40
- let packageContentHashes = {
41
- contents: []
42
- };
43
- Stdlib_Dict.forEachWithKey(packageDirs, (pkgRoot, pkgName) => {
44
- let match = Util_Bundle$ReventlessAws.createFilteredPackageArchive(pkgRoot);
45
- archiveContents[`node_modules/` + pkgName] = match[0];
46
- packageContentHashes.contents.push(pkgName + `:` + match[1]);
47
- });
48
- let code = new (Pulumi.asset.AssetArchive)(archiveContents);
49
- packageContentHashes.contents.sort(Primitive_string.compare);
50
- let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(reExportCode + packageContentHashes.contents.join(","));
35
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs", packageDirs, undefined);
51
36
  let componentOpts_parent = opts.parent;
52
37
  let componentOpts = {
53
38
  parent: componentOpts_parent
54
39
  };
55
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, code, sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, componentOpts);
40
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, match.code, match.sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, componentOpts);
56
41
  let lambda = runtime.parts.lambda;
57
42
  let subscribe = (sourceName, source) => Util_EventSourceMapping$ReventlessAws.subscribe(undefined, lambda, name, sourceName, source, opts);
58
43
  subscribe(referencesName, referencesStream);
@@ -19,6 +19,10 @@ type selection = {
19
19
  securityGroupId: Pulumi.Output.t<string>,
20
20
  /** Private subnets for the in-VPC DCB/relay Lambdas (PgConnection.subnetIds). */
21
21
  subnetIds: array<Pulumi.Input.t<string>>,
22
+ /** DCB append lock strategy (C2), carried from `PgConnection.lockStrategy` and
23
+ serialized into the DCB command Lambda's HANDLER_CONFIG. Default
24
+ `#AdvisoryLocks`; `#RowLocks` for RDS-Proxy-fronted deployments. */
25
+ lockStrategy: ReventlessPostgres.DcbEventLogStorage_Postgres.lockStrategy,
22
26
  }
23
27
 
24
28
  let selectionRef: ref<option<selection>> = ref(None)
@@ -26,9 +26,9 @@ let make: ReventlessCore.DcbEventLog_Adapter.storageMaker = (
26
26
  DcbBackend.registerRelayLog(~logName=name, ~partitionTag)
27
27
 
28
28
  let operations = switch DcbBackend.get() {
29
- | Some({connectionConfig}) =>
29
+ | Some({connectionConfig, lockStrategy}) =>
30
30
  connectionConfig->Pulumi.Output.apply(config =>
31
- DcbEventLogStorage_Postgres_Runtime.opsFor(config, ~logName=name)
31
+ DcbEventLogStorage_Postgres_Runtime.opsFor(config, ~logName=name, ~lockStrategy)
32
32
  )
33
33
  | None =>
34
34
  // Selectable only routes here when a selection is set; guard defensively.
@@ -8,7 +8,13 @@ function make(name, param, partitionTag, $staropt$star, param$1) {
8
8
  $staropt$star !== undefined;
9
9
  DcbBackend$ReventlessAws.registerRelayLog(name, partitionTag);
10
10
  let match = DcbBackend$ReventlessAws.get();
11
- let operations = match !== undefined ? match.connectionConfig.apply(config => DcbEventLogStorage_Postgres_Runtime$ReventlessAws.opsFor(config, name, undefined)) : Stdlib_JsError.throwWithMessage("DcbEventLogStorage_Postgres.make called without a DcbBackend selection");
11
+ let operations;
12
+ if (match !== undefined) {
13
+ let lockStrategy = match.lockStrategy;
14
+ operations = match.connectionConfig.apply(config => DcbEventLogStorage_Postgres_Runtime$ReventlessAws.opsFor(config, name, lockStrategy));
15
+ } else {
16
+ operations = Stdlib_JsError.throwWithMessage("DcbEventLogStorage_Postgres.make called without a DcbBackend selection");
17
+ }
12
18
  return {
13
19
  resources: [],
14
20
  operations: operations
@@ -58,18 +58,8 @@ let make = (
58
58
  (l.connectionConfig, l.targetQueueUrl)
59
59
  ->Pulumi.Output.all2
60
60
  ->Pulumi.Output.apply(((cc, queueUrl)) => {
61
- let pgConnectionJson =
62
- [
63
- ("host", cc.host->JSON.Encode.string),
64
- ("port", cc.port->Int.toFloat->JSON.Encode.float),
65
- ("database", cc.database->JSON.Encode.string),
66
- ("username", cc.username->JSON.Encode.string),
67
- ("secretArn", cc.secretArn->JSON.Encode.string),
68
- ]
69
- ->Dict.fromArray
70
- ->JSON.Encode.object
71
61
  let entries = [
72
- ("pgConnection", pgConnectionJson),
62
+ ("pgConnection", cc->PgConnection.connectionConfigToJson),
73
63
  ("logName", l.logName->JSON.Encode.string),
74
64
  ("subscriber", l.subscriber->JSON.Encode.string),
75
65
  ("targetQueueUrl", queueUrl->JSON.Encode.string),
@@ -12,6 +12,7 @@ import * as Adapter$ReventlessCore from "@reventlessdev/reventless-core/src/adap
12
12
  import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
13
13
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
14
14
  import * as Util_Lambda$ReventlessAws from "../../util/Util_Lambda.res.mjs";
15
+ import * as PgConnection$ReventlessAws from "./PgConnection.res.mjs";
15
16
  import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
16
17
  import * as Util_Cloudwatch$ReventlessAws from "../../util/Util_Cloudwatch.res.mjs";
17
18
  import * as Cloudwatch_EventRule$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Cloudwatch/Cloudwatch_EventRule.res.mjs";
@@ -26,33 +27,10 @@ function make(name, logs, securityGroupId, subnetIds, intervalMinutesOpt, opts)
26
27
  l.connectionConfig,
27
28
  l.targetQueueUrl
28
29
  ]).apply(param => {
29
- let cc = param[0];
30
- let pgConnectionJson = Object.fromEntries([
31
- [
32
- "host",
33
- cc.host
34
- ],
35
- [
36
- "port",
37
- cc.port
38
- ],
39
- [
40
- "database",
41
- cc.database
42
- ],
43
- [
44
- "username",
45
- cc.username
46
- ],
47
- [
48
- "secretArn",
49
- cc.secretArn
50
- ]
51
- ]);
52
30
  let entries = [
53
31
  [
54
32
  "pgConnection",
55
- pgConnectionJson
33
+ PgConnection$ReventlessAws.connectionConfigToJson(undefined, param[0])
56
34
  ],
57
35
  [
58
36
  "logName",