@reventlessdev/reventless-postgres 3.0.0-alpha.15 → 3.0.0-alpha.151

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 (143) hide show
  1. package/CHANGELOG.md +1093 -0
  2. package/README.md +83 -0
  3. package/lib/bs/.compiler.log +2 -2
  4. package/lib/bs/ReventlessPostgres.cmt +0 -0
  5. package/lib/bs/ReventlessPostgres.mlmap +0 -2
  6. package/lib/bs/compiler-info.json +4 -4
  7. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  8. package/lib/bs/src/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  9. package/lib/bs/src/DcbEventLogStorage_Postgres.ast +0 -0
  10. package/lib/bs/src/DcbEventLogStorage_Postgres.res +1 -1
  11. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  12. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  13. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops.ast +0 -0
  14. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops.res +43 -42
  15. package/lib/bs/src/DcbEventLogStorage_Postgres_Ops.res.mjs +4 -4
  16. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  17. package/lib/bs/src/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  18. package/lib/bs/src/EventLogChangeFeed.ast +0 -0
  19. package/lib/bs/src/EventLogChangeFeed.res +15 -6
  20. package/lib/bs/src/EventLogChangeFeed.res.mjs +3 -3
  21. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  22. package/lib/bs/src/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  23. package/lib/bs/src/EventLogStorage_Postgres.ast +0 -0
  24. package/lib/bs/src/EventLogStorage_Postgres.res +1 -1
  25. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  26. package/lib/bs/src/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  27. package/lib/bs/src/EventLogStorage_Postgres_Ops.ast +0 -0
  28. package/lib/bs/src/EventLogStorage_Postgres_Ops.res +24 -14
  29. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  30. package/lib/bs/src/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  31. package/lib/bs/src/PgChangeFeed.ast +0 -0
  32. package/lib/bs/src/PgChangeFeed.res +21 -9
  33. package/lib/bs/src/PgChangeFeed.res.mjs +6 -6
  34. package/lib/bs/src/PgDriver-ReventlessPostgres.cmi +0 -0
  35. package/lib/bs/src/PgDriver-ReventlessPostgres.cmt +0 -0
  36. package/lib/bs/src/PgDriver-ReventlessPostgres.cmti +0 -0
  37. package/lib/bs/src/PgDriver.ast +0 -0
  38. package/lib/bs/src/PgDriver.iast +0 -0
  39. package/lib/bs/src/PgDriver.res +1 -2
  40. package/lib/bs/src/PgSchema-ReventlessPostgres.cmi +0 -0
  41. package/lib/bs/src/PgSchema-ReventlessPostgres.cmt +0 -0
  42. package/lib/bs/src/PgSchema.ast +0 -0
  43. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  44. package/lib/bs/src/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  45. package/lib/bs/src/QueryDbStorage_Postgres.ast +0 -0
  46. package/lib/bs/src/QueryDbStorage_Postgres.res +7 -2
  47. package/lib/bs/src/QueryDbStorage_Postgres.res.mjs +1 -1
  48. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  49. package/lib/bs/src/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  50. package/lib/bs/src/QueryDbStorage_Postgres_Ops.ast +0 -0
  51. package/lib/bs/src/QueryDbStorage_Postgres_Ops.res +14 -14
  52. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  53. package/lib/bs/src/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  54. package/lib/bs/src/QueryEnginePostgres.ast +0 -0
  55. package/lib/bs/src/QueryEnginePostgres.res +141 -42
  56. package/lib/bs/src/QueryEnginePostgres.res.mjs +53 -30
  57. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
  58. package/lib/bs/tests/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
  59. package/lib/bs/tests/PostgresIntegrationTest.ast +0 -0
  60. package/lib/bs/tests/PostgresIntegrationTest.res +157 -104
  61. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmi +0 -0
  62. package/lib/bs/tests/QueryEnginePostgresListPageTest-ReventlessPostgres.cmt +0 -0
  63. package/lib/bs/tests/QueryEnginePostgresListPageTest.ast +0 -0
  64. package/lib/bs/tests/QueryEnginePostgresListPageTest.res +29 -6
  65. package/lib/bs/tests/QueryEnginePostgresListPageTest.res.mjs +4 -4
  66. package/lib/ocaml/.compiler.log +2 -2
  67. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  68. package/lib/ocaml/DcbEventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  69. package/lib/ocaml/DcbEventLogStorage_Postgres.ast +0 -0
  70. package/lib/ocaml/DcbEventLogStorage_Postgres.res +1 -1
  71. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  72. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  73. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops.ast +0 -0
  74. package/lib/ocaml/DcbEventLogStorage_Postgres_Ops.res +43 -42
  75. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmi +0 -0
  76. package/lib/ocaml/EventLogChangeFeed-ReventlessPostgres.cmt +0 -0
  77. package/lib/ocaml/EventLogChangeFeed.ast +0 -0
  78. package/lib/ocaml/EventLogChangeFeed.res +15 -6
  79. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmi +0 -0
  80. package/lib/ocaml/EventLogStorage_Postgres-ReventlessPostgres.cmt +0 -0
  81. package/lib/ocaml/EventLogStorage_Postgres.ast +0 -0
  82. package/lib/ocaml/EventLogStorage_Postgres.res +1 -1
  83. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  84. package/lib/ocaml/EventLogStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  85. package/lib/ocaml/EventLogStorage_Postgres_Ops.ast +0 -0
  86. package/lib/ocaml/EventLogStorage_Postgres_Ops.res +24 -14
  87. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmi +0 -0
  88. package/lib/ocaml/PgChangeFeed-ReventlessPostgres.cmt +0 -0
  89. package/lib/ocaml/PgChangeFeed.ast +0 -0
  90. package/lib/ocaml/PgChangeFeed.res +21 -9
  91. package/lib/ocaml/PgDriver-ReventlessPostgres.cmi +0 -0
  92. package/lib/ocaml/PgDriver-ReventlessPostgres.cmt +0 -0
  93. package/lib/ocaml/PgDriver-ReventlessPostgres.cmti +0 -0
  94. package/lib/ocaml/PgDriver.ast +0 -0
  95. package/lib/ocaml/PgDriver.iast +0 -0
  96. package/lib/ocaml/PgDriver.res +1 -2
  97. package/lib/ocaml/PgSchema-ReventlessPostgres.cmi +0 -0
  98. package/lib/ocaml/PgSchema-ReventlessPostgres.cmt +0 -0
  99. package/lib/ocaml/PgSchema.ast +0 -0
  100. package/lib/ocaml/PostgresIntegrationTest.res +157 -104
  101. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmi +0 -0
  102. package/lib/ocaml/QueryDbStorage_Postgres-ReventlessPostgres.cmt +0 -0
  103. package/lib/ocaml/QueryDbStorage_Postgres.ast +0 -0
  104. package/lib/ocaml/QueryDbStorage_Postgres.res +7 -2
  105. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmi +0 -0
  106. package/lib/ocaml/QueryDbStorage_Postgres_Ops-ReventlessPostgres.cmt +0 -0
  107. package/lib/ocaml/QueryDbStorage_Postgres_Ops.ast +0 -0
  108. package/lib/ocaml/QueryDbStorage_Postgres_Ops.res +14 -14
  109. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmi +0 -0
  110. package/lib/ocaml/QueryEnginePostgres-ReventlessPostgres.cmt +0 -0
  111. package/lib/ocaml/QueryEnginePostgres.ast +0 -0
  112. package/lib/ocaml/QueryEnginePostgres.res +141 -42
  113. package/lib/ocaml/QueryEnginePostgresListPageTest.res +29 -6
  114. package/lib/ocaml/ReventlessPostgres.cmt +0 -0
  115. package/package.json +13 -13
  116. package/rescript.json +2 -1
  117. package/src/DcbEventLogStorage_Postgres.res +1 -1
  118. package/src/DcbEventLogStorage_Postgres_Ops.res +43 -42
  119. package/src/DcbEventLogStorage_Postgres_Ops.res.mjs +4 -4
  120. package/src/EventLogChangeFeed.res +15 -6
  121. package/src/EventLogChangeFeed.res.mjs +3 -3
  122. package/src/EventLogStorage_Postgres.res +1 -1
  123. package/src/EventLogStorage_Postgres_Ops.res +24 -14
  124. package/src/PgChangeFeed.res +21 -9
  125. package/src/PgChangeFeed.res.mjs +6 -6
  126. package/src/PgDriver.res +1 -2
  127. package/src/QueryDbStorage_Postgres.res +7 -2
  128. package/src/QueryDbStorage_Postgres.res.mjs +1 -1
  129. package/src/QueryDbStorage_Postgres_Ops.res +14 -14
  130. package/src/QueryEnginePostgres.res +141 -42
  131. package/src/QueryEnginePostgres.res.mjs +53 -30
  132. package/tests/PostgresIntegrationTest.res +157 -104
  133. package/tests/QueryEnginePostgresListPageTest.res +29 -6
  134. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmi +0 -0
  135. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmj +0 -0
  136. package/lib/ocaml/PostgresIntegrationTest-ReventlessPostgres.cmt +0 -0
  137. package/lib/ocaml/PostgresIntegrationTest.ast +0 -0
  138. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmi +0 -0
  139. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmj +0 -0
  140. package/lib/ocaml/QueryEnginePostgresListPageTest-ReventlessPostgres.cmt +0 -0
  141. package/lib/ocaml/QueryEnginePostgresListPageTest.ast +0 -0
  142. package/tests/PostgresIntegrationTest.res.mjs +0 -377
  143. package/tests/QueryEnginePostgresListPageTest.res.mjs +0 -461
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ [![npm](https://img.shields.io/npm/v/@reventlessdev/reventless-postgres.svg?label=npm)](https://www.npmjs.com/package/@reventlessdev/reventless-postgres)
2
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
3
+ [![Docs](https://img.shields.io/badge/docs-reventless.dev-blue)](https://docs.reventless.dev)
4
+
5
+ # @reventlessdev/reventless-postgres
6
+
7
+ > ⚠️ **Alpha.** APIs and on-disk formats can change without notice between releases.
8
+ > Pin exact versions and expect breaking changes.
9
+
10
+ The **Postgres storage backend** for [Reventless](https://docs.reventless.dev) — a
11
+ spec-driven, event-sourced CQRS framework written in [ReScript](https://rescript-lang.org).
12
+ It implements the core storage adapters (classic and DCB event logs, the query
13
+ database, and change feeds) over a plain Postgres connection. It carries **no**
14
+ IaC/provider SDK dependency: it connects by connection string, so it runs against
15
+ RDS/Aurora, any managed provider, or a local container.
16
+
17
+ ## What it provides
18
+
19
+ ReScript modules, consumed by adding the package to your `rescript.json` `dependencies`:
20
+
21
+ - **`PgDriver`** — the [`node-postgres`](https://node-postgres.com) (`pg`) binding
22
+ (pool/client). Accepts a full `connectionString` or discrete fields, with SSL
23
+ and rotating-secret (password-provider) support.
24
+ - **`PgSchema`** — an idempotent schema + migration runner (`ensureSchema(pool)`),
25
+ safe to run on every startup and concurrently across processes.
26
+ - **`EventLogStorage_Postgres`** — the classic optimistic-concurrency event log
27
+ (`event_log` / `snapshot`).
28
+ - **`DcbEventLogStorage_Postgres`** — the DCB (Dynamic Consistency Boundary) event
29
+ log with a global position sequence and a concurrency-critical `dcb_append`.
30
+ - **`QueryDbStorage_Postgres`** — read-side storage over JSONB `qdb_<name>` tables.
31
+ - **`QueryEnginePostgres`** — compiles the core QueryEngine query/scan AST down to
32
+ SQL, pushing filters into the database.
33
+ - **`EventLogChangeFeed`** / **`PgChangeFeed`** — checkpointed change feeds for the
34
+ classic and DCB logs, using an `xmin`-fenced read plus `LISTEN`/`NOTIFY`
35
+ (`pg_notify`) for near-real-time wakeup. A documented public consumer surface.
36
+
37
+ The `*_Ops` modules hold the runtime-pure operations (no Pulumi import) so a
38
+ deployed handler's ESM graph stays clean; the wrapper modules add the deploy-time
39
+ adapter shape.
40
+
41
+ ## Where it fits
42
+
43
+ `reventless-postgres` is a storage **adapter** for the Reventless framework.
44
+ [`reventless-core`](https://www.npmjs.com/package/@reventlessdev/reventless-core)
45
+ is provider-agnostic; this package supplies the Postgres implementation of its
46
+ event-log, query-db, and change-feed interfaces:
47
+
48
+ - [`@reventlessdev/reventless-aws`](https://www.npmjs.com/package/@reventlessdev/reventless-aws) — AWS (DynamoDB, Lambda, SQS, SNS, S3); can delegate event logs to this package for managed Postgres
49
+ - [`@reventlessdev/reventless-postgres`](https://www.npmjs.com/package/@reventlessdev/reventless-postgres) — **this package**
50
+ - [`@reventlessdev/reventless-local`](https://www.npmjs.com/package/@reventlessdev/reventless-local) — in-memory / SQLite platform that uses this storage against a local Postgres
51
+
52
+ Because it depends only on `pg` (plus
53
+ [`reventless-core`](https://www.npmjs.com/package/@reventlessdev/reventless-core),
54
+ [`reventless-infra`](https://www.npmjs.com/package/@reventlessdev/reventless-infra),
55
+ and [`reventless-spec`](https://www.npmjs.com/package/@reventlessdev/reventless-spec)),
56
+ the platform package that embeds it owns provisioning; this package only needs a
57
+ reachable Postgres.
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pnpm add @reventlessdev/reventless-postgres
63
+ ```
64
+
65
+ Then register it as a ReScript dependency in `rescript.json`:
66
+
67
+ ```json
68
+ {
69
+ "dependencies": ["@reventlessdev/reventless-postgres"]
70
+ }
71
+ ```
72
+
73
+ Requires ReScript `^12.3.1` (peer dependency).
74
+
75
+ ## Links
76
+
77
+ - 📚 Documentation — [docs.reventless.dev](https://docs.reventless.dev)
78
+ - 📦 Repository — [ReventlessDev/reventless-core](https://github.com/ReventlessDev/reventless-core)
79
+ - 📋 [Changelog](./CHANGELOG.md)
80
+
81
+ ## License
82
+
83
+ [Apache-2.0](https://opensource.org/licenses/Apache-2.0)
@@ -1,2 +1,2 @@
1
- #Start(1783671208398)
2
- #Done(1783671210443)
1
+ #Start(1790864278382)
2
+ #Done(1790864279610)
Binary file
@@ -7,8 +7,6 @@ EventLogStorage_Postgres_Ops
7
7
  PgChangeFeed
8
8
  PgDriver
9
9
  PgSchema
10
- PostgresIntegrationTest
11
10
  QueryDbStorage_Postgres
12
11
  QueryDbStorage_Postgres_Ops
13
12
  QueryEnginePostgres
14
- QueryEnginePostgresListPageTest
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "12.3.0",
2
+ "version": "12.3.1",
3
3
  "bsc_path": "/home/runner/work/reventless-core/reventless-core/node_modules/@rescript/linux-x64/bin/bsc.exe",
4
- "bsc_hash": "75e3a59c95cc953608fd3dd6ea6ed68141bba4414a20a52f114261b8a48385fa",
5
- "rescript_config_hash": "48ea15db3d741cf15a2ff49dd0b47d86badf2b548ecb3859a896faf1e3a82a15",
4
+ "bsc_hash": "1006241435e8d5052329c523202a7fe6359a37e1e1688c023d37d6a7ebb5fb6d",
5
+ "rescript_config_hash": "af2af60a0fa33b8249a96ff2758f36482843126dc4e07b9302842d011e0ad8c6",
6
6
  "runtime_path": "/home/runner/work/reventless-core/reventless-core/node_modules/@rescript/runtime",
7
- "generated_at": "1783671210450"
7
+ "generated_at": "1790864279611"
8
8
  }
@@ -8,7 +8,7 @@
8
8
  // existing `DcbEventLogStorage_Postgres.*` reference keeps working) and adds only
9
9
  // the deploy-time `DcbEventLog_Adapter` shape, keeping the historic `makeStorage`
10
10
  // 3-tuple `(name, ops, adapter)` API unchanged.
11
- // See docs/plans/deployed-lambda-esm-self-containment.md (Rung-3 finding).
11
+ // See docs/plans/done/deployed-lambda-esm-self-containment.md (Rung-3 finding).
12
12
 
13
13
  open ReventlessCore
14
14
  include DcbEventLogStorage_Postgres_Ops
@@ -6,7 +6,7 @@
6
6
  // would leak into the Lambda import graph and fail resolution on real Lambda. The
7
7
  // thin `DcbEventLogStorage_Postgres` wrapper `include`s this and adds the
8
8
  // deploy-time `Pulumi.Output`-shaped adapter for local/deploy consumers.
9
- // See docs/plans/deployed-lambda-esm-self-containment.md (Rung-3 finding).
9
+ // See docs/plans/done/deployed-lambda-esm-self-containment.md (Rung-3 finding).
10
10
 
11
11
  open ReventlessCore
12
12
  open Reventless
@@ -67,7 +67,7 @@ let rowToEvent = (row: dict<JSON.t>): DcbEventLog_Adapter.rawSequencedEvent => {
67
67
  | _ => ""
68
68
  }
69
69
  let meta = switch row->Dict.get("meta") {
70
- | Some(metaJson) => metaJson->S.parseJsonOrThrow(Reventless.Message.metaSchema)
70
+ | Some(metaJson) => metaJson->Reventless.Util_Sury.fromJson(Reventless.Message.metaSchema)
71
71
  | None => JsError.throwWithMessage("missing meta column in dcb_event row")
72
72
  }
73
73
  {
@@ -143,7 +143,7 @@ let buildReadWhere = (
143
143
  // Strip the zero-padding a cursor carries for string-sortability, leaving a
144
144
  // plain decimal both `::xid8` and `::bigint` parse unambiguously ("0" if empty).
145
145
  let stripZeros = (s: string): string => {
146
- let t = s->String.replaceRegExp(%re("/^0+/"), "")
146
+ let t = s->String.replaceRegExp(/^0+/, "")
147
147
  t == "" ? "0" : t
148
148
  }
149
149
 
@@ -222,49 +222,45 @@ let makeOps = (
222
222
  }
223
223
  }
224
224
 
225
- let append = async (
226
- newEvents: array<DcbEventLog_Adapter.rawStoredEvent>,
227
- ~condition=?,
228
- ): result<DcbTag.sequencePosition, ReventlessInfra.DcbEventLog.appendError> => {
225
+ let append = async (newEvents: array<DcbEventLog_Adapter.rawStoredEvent>, ~condition=?): result<
226
+ DcbTag.sequencePosition,
227
+ ReventlessInfra.DcbEventLog.appendError,
228
+ > => {
229
229
  let eventsJson = JSON.Array(
230
- newEvents->Array.map(ev =>
231
- JSON.Object(
230
+ newEvents->Array.map(ev => JSON.Object(
231
+ Dict.fromArray([
232
+ ("event_type", JSON.Encode.string(ev.eventType)),
233
+ (
234
+ "tags",
235
+ JSON.Array(ev.tags->Array.map(t => JSON.Encode.string(t.key ++ "=" ++ t.value))),
236
+ ),
237
+ ("data", ev.data),
238
+ ("meta", ev.meta->Reventless.Util_Sury.toJson(Reventless.Message.metaSchema)),
239
+ ]),
240
+ )),
241
+ )
242
+ let conditionParam = switch condition {
243
+ | Some(cond: DcbTag.appendCondition) =>
244
+ let queryJson = JSON.Array(
245
+ cond.query->Array.map(clause => JSON.Object(
232
246
  Dict.fromArray([
233
- ("event_type", JSON.Encode.string(ev.eventType)),
247
+ (
248
+ "eventTypes",
249
+ switch clause.eventTypes {
250
+ | Some(types) => JSON.Array(types->Array.map(JSON.Encode.string))
251
+ | None => JSON.Encode.null
252
+ },
253
+ ),
234
254
  (
235
255
  "tags",
236
- JSON.Array(ev.tags->Array.map(t => JSON.Encode.string(t.key ++ "=" ++ t.value))),
256
+ switch clause.tags {
257
+ | Some(tags) =>
258
+ JSON.Array(tags->Array.map(t => JSON.Encode.string(t.key ++ "=" ++ t.value)))
259
+ | None => JSON.Encode.null
260
+ },
237
261
  ),
238
- ("data", ev.data),
239
- ("meta", ev.meta->S.reverseConvertToJsonOrThrow(Reventless.Message.metaSchema)),
240
262
  ]),
241
- )
242
- ),
243
- )
244
- let conditionParam = switch condition {
245
- | Some(cond: DcbTag.appendCondition) =>
246
- let queryJson = JSON.Array(
247
- cond.query->Array.map(clause =>
248
- JSON.Object(
249
- Dict.fromArray([
250
- (
251
- "eventTypes",
252
- switch clause.eventTypes {
253
- | Some(types) => JSON.Array(types->Array.map(JSON.Encode.string))
254
- | None => JSON.Encode.null
255
- },
256
- ),
257
- (
258
- "tags",
259
- switch clause.tags {
260
- | Some(tags) =>
261
- JSON.Array(tags->Array.map(t => JSON.Encode.string(t.key ++ "=" ++ t.value)))
262
- | None => JSON.Encode.null
263
- },
264
- ),
265
- ]),
266
- )
267
- ),
263
+ )),
268
264
  )
269
265
  let fields = [("query", queryJson)]
270
266
  switch cond.after {
@@ -306,7 +302,10 @@ let makeOps = (
306
302
  ? Error(ReventlessInfra.DcbEventLog.Conflict)
307
303
  : Error(
308
304
  ReventlessInfra.DcbEventLog.StorageFailure(
309
- exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("storage error"),
305
+ exn
306
+ ->JsExn.fromException
307
+ ->Option.flatMap(JsExn.message)
308
+ ->Option.getOr("storage error"),
310
309
  ),
311
310
  )
312
311
  }
@@ -324,7 +323,9 @@ let makeOps = (
324
323
  Effect.promise(() => {
325
324
  let b = mkBuilder()
326
325
  let where = buildReadWhere(b, ~name, ~query, ~after=cursor, ~applyFence=true)
327
- let sql = `SELECT ${selectColumns}, transaction_id::text AS tx_raw, position::text AS pos_raw FROM dcb_event WHERE ${where} ORDER BY transaction_id ASC, position ASC LIMIT ${Int.toString(pageSize)}`
326
+ let sql = `SELECT ${selectColumns}, transaction_id::text AS tx_raw, position::text AS pos_raw FROM dcb_event WHERE ${where} ORDER BY transaction_id ASC, position ASC LIMIT ${Int.toString(
327
+ pageSize,
328
+ )}`
328
329
  pool->PgDriver.query(sql, b.params)
329
330
  })->Effect.map(rows => {
330
331
  let events = rows->Array.map(rowToEvent)
@@ -1,6 +1,5 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
4
3
  import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
5
4
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
6
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -11,6 +10,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
11
10
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
12
11
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
13
12
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
13
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
14
14
  import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
15
15
 
16
16
  function strategyParam(s) {
@@ -68,7 +68,7 @@ function rowToEvent(row) {
68
68
  }
69
69
  };
70
70
  let metaJson = row["meta"];
71
- let meta = metaJson !== undefined ? S.parseJsonOrThrow(metaJson, Message$Reventless.metaSchema) : Stdlib_JsError.throwWithMessage("missing meta column in dcb_event row");
71
+ let meta = metaJson !== undefined ? Util_Sury$Reventless.fromJson(metaJson, Message$Reventless.metaSchema) : Stdlib_JsError.throwWithMessage("missing meta column in dcb_event row");
72
72
  return {
73
73
  position: str("cursor"),
74
74
  eventType: str("event_type"),
@@ -226,7 +226,7 @@ function makeOps(pool, name, param, param$1, param$2, param$3, lockStrategyOpt,
226
226
  ],
227
227
  [
228
228
  "meta",
229
- S.reverseConvertToJsonOrThrow(ev.meta, Message$Reventless.metaSchema)
229
+ Util_Sury$Reventless.toJson(ev.meta, Message$Reventless.metaSchema)
230
230
  ]
231
231
  ])));
232
232
  let conditionParam;
@@ -377,4 +377,4 @@ export {
377
377
  countAll,
378
378
  makeOps,
379
379
  }
380
- /* S Not a pure module */
380
+ /* Stream Not a pure module */
Binary file
@@ -15,8 +15,11 @@
15
15
  // later commit) is never skipped by a checkpointing reader. `global_seq` alone,
16
16
  // being an IDENTITY assigned at INSERT, could NOT provide that guarantee.
17
17
 
18
- // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical format.
19
- module Dcb = DcbEventLogStorage_Postgres
18
+ // Reuse the shared `<a>:<b>` cursor codec from the DCB storage — identical
19
+ // format. Point at the runtime-pure `_Ops` module (no `@pulumi/pulumi`): this
20
+ // feed is drained by the deployed change-feed relay Lambda, whose cold-start
21
+ // graph must stay Pulumi-free.
22
+ module Dcb = DcbEventLogStorage_Postgres_Ops
20
23
 
21
24
  // One classic event as the feed surfaces it. `payload` is the stored event JSON
22
25
  // verbatim; a bus/topic relay transforms it into its target shape (e.g. the AWS
@@ -87,11 +90,12 @@ let readBatch = async (
87
90
  params->Array.push(JSON.Encode.string(tx))
88
91
  params->Array.push(JSON.Encode.string(pos))
89
92
  where :=
90
- where.contents ++
91
- " AND (transaction_id > $2::xid8 OR (transaction_id = $2::xid8 AND global_seq > $3::bigint))"
93
+ where.contents ++ " AND (transaction_id > $2::xid8 OR (transaction_id = $2::xid8 AND global_seq > $3::bigint))"
92
94
  | None => ()
93
95
  }
94
- let sql = `SELECT ${selectColumns} FROM event_log WHERE ${where.contents} ORDER BY transaction_id ASC, global_seq ASC LIMIT ${Int.toString(limit)}`
96
+ let sql = `SELECT ${selectColumns} FROM event_log WHERE ${where.contents} ORDER BY transaction_id ASC, global_seq ASC LIMIT ${Int.toString(
97
+ limit,
98
+ )}`
95
99
  let rows = await pool->PgDriver.query(sql, params)
96
100
  let events = rows->Array.map(rowToEvent)
97
101
  let cursor = switch events->Array.get(events->Array.length - 1) {
@@ -163,7 +167,12 @@ let drain = async (
163
167
  let cursor = ref(await loadCheckpoint(pool, ~subscriber))
164
168
  let continue = ref(true)
165
169
  while continue.contents {
166
- let {events, cursor: newCursor} = await readBatch(pool, ~logName, ~after=?cursor.contents, ~limit)
170
+ let {events, cursor: newCursor} = await readBatch(
171
+ pool,
172
+ ~logName,
173
+ ~after=?cursor.contents,
174
+ ~limit,
175
+ )
167
176
  if events->Array.length == 0 {
168
177
  continue := false
169
178
  } else {
@@ -3,7 +3,7 @@
3
3
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
6
- import * as DcbEventLogStorage_Postgres$ReventlessPostgres from "./DcbEventLogStorage_Postgres.res.mjs";
6
+ import * as DcbEventLogStorage_Postgres_Ops$ReventlessPostgres from "./DcbEventLogStorage_Postgres_Ops.res.mjs";
7
7
 
8
8
  let selectColumns = "\n lpad(transaction_id::text, 20, '0') || ':' || lpad(global_seq::text, 20, '0') AS cursor,\n aggregate_id,\n seq_nr,\n payload,\n msg_id\n";
9
9
 
@@ -43,7 +43,7 @@ async function readBatch(pool, logName, after, limitOpt) {
43
43
  let limit = limitOpt !== undefined ? limitOpt : 500;
44
44
  let params = [logName];
45
45
  let where = "log_name = $1 AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())";
46
- let match = Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor);
46
+ let match = Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor);
47
47
  if (match !== undefined) {
48
48
  params.push(match[0]);
49
49
  params.push(match[1]);
@@ -72,7 +72,7 @@ async function loadCheckpoint(pool, subscriber) {
72
72
  }
73
73
 
74
74
  async function saveCheckpoint(pool, subscriber, cursor) {
75
- let match = DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor(cursor);
75
+ let match = DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor(cursor);
76
76
  if (match !== undefined) {
77
77
  await PgDriver$ReventlessPostgres.query(pool, "INSERT INTO event_log_subscription(subscriber, last_tx, last_global_seq)\n VALUES ($1, $2::xid8, $3::bigint)\n ON CONFLICT (subscriber)\n DO UPDATE SET last_tx = EXCLUDED.last_tx, last_global_seq = EXCLUDED.last_global_seq", [
78
78
  subscriber,
@@ -7,7 +7,7 @@
7
7
  // real Lambda). This module `include`s that pure module and adds only the
8
8
  // deploy-time `EventLog_Adapter` shape (`operations` wrapped in `Pulumi.Output`),
9
9
  // keeping the historic `makeStorage` 3-tuple `(name, ops, adapter)` API unchanged.
10
- // See docs/plans/deployed-lambda-esm-self-containment.md (Rung-3 finding).
10
+ // See docs/plans/done/deployed-lambda-esm-self-containment.md (Rung-3 finding).
11
11
 
12
12
  open ReventlessCore
13
13
  include EventLogStorage_Postgres_Ops
@@ -7,7 +7,7 @@
7
7
  // in no Lambda — not the layer, not `/var/runtime`). The thin
8
8
  // `EventLogStorage_Postgres` wrapper adds the deploy-time `Pulumi.Output`-shaped
9
9
  // adapter on top for local/deploy consumers; the runtime imports `makeOps` here.
10
- // See docs/plans/deployed-lambda-esm-self-containment.md (Rung-3 finding).
10
+ // See docs/plans/done/deployed-lambda-esm-self-containment.md (Rung-3 finding).
11
11
 
12
12
  open ReventlessCore
13
13
 
@@ -44,12 +44,10 @@ let isUniqueViolation = (exn: exn): bool => {
44
44
 
45
45
  // Builds the classic runtime op set bound to `pool`. Returns `(name, ops)`; the
46
46
  // deploy-time `EventLog_Adapter` wrapper lives in `EventLogStorage_Postgres`.
47
- let makeOps = (
48
- ~pool: PgDriver.pool,
49
- ~name: string,
50
- ~opts as _,
51
- ~onAppended=noTracking,
52
- ): (string, EventLog_Adapter.operations) => {
47
+ let makeOps = (~pool: PgDriver.pool, ~name: string, ~opts as _, ~onAppended=noTracking): (
48
+ string,
49
+ EventLog_Adapter.operations,
50
+ ) => {
53
51
  let appendTracked = async (~track, seqNr: int, id: string, jsons: array<JSON.t>) => {
54
52
  // Empty append is a legitimate no-op (at-least-once retries, idempotent
55
53
  // commands), NOT a conflict — return before the guarded insert whose 0-row
@@ -95,7 +93,10 @@ let makeOps = (
95
93
  ? Error(EventLog.Conflict)
96
94
  : Error(
97
95
  EventLog.StorageFailure(
98
- exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("storage error"),
96
+ exn
97
+ ->JsExn.fromException
98
+ ->Option.flatMap(JsExn.message)
99
+ ->Option.getOr("storage error"),
99
100
  ),
100
101
  )
101
102
  }
@@ -106,12 +107,14 @@ let makeOps = (
106
107
  appendTracked(~track=true, seqNr, id, jsons)
107
108
 
108
109
  let replayArray = async (id: string, ~fromSeq=0): array<JSON.t> =>
109
- (await pool->PgDriver.query(
110
- "SELECT payload FROM event_log
110
+ (
111
+ await pool->PgDriver.query(
112
+ "SELECT payload FROM event_log
111
113
  WHERE log_name = $1 AND aggregate_id = $2 AND seq_nr >= $3::bigint
112
114
  ORDER BY seq_nr ASC",
113
- [JSON.Encode.string(name), JSON.Encode.string(id), JSON.Encode.int(fromSeq)],
114
- ))->Array.map(decodePayload)
115
+ [JSON.Encode.string(name), JSON.Encode.string(id), JSON.Encode.int(fromSeq)],
116
+ )
117
+ )->Array.map(decodePayload)
115
118
 
116
119
  let replay: EventLog.replay<string, JSON.t> = id => replayArray(id)
117
120
 
@@ -152,14 +155,21 @@ let makeOps = (
152
155
  Ok()
153
156
  } catch {
154
157
  | exn =>
155
- Error(exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("snapshot write error"))
158
+ Error(
159
+ exn
160
+ ->JsExn.fromException
161
+ ->Option.flatMap(JsExn.message)
162
+ ->Option.getOr("snapshot write error"),
163
+ )
156
164
  }
157
165
 
158
166
  let appendStream: EventLog.appendStream<string, JSON.t> = (startingSeqNr, id, stream) =>
159
167
  stream
160
168
  ->Stream.runCollect
161
169
  ->Effect.flatMap(jsons =>
162
- Effect.promise(() => appendTracked(~track=false, startingSeqNr, id, jsons))->Effect.flatMap(result =>
170
+ Effect.promise(() =>
171
+ appendTracked(~track=false, startingSeqNr, id, jsons)
172
+ )->Effect.flatMap(result =>
163
173
  switch result {
164
174
  | Ok() => Effect.succeed()
165
175
  | Error(EventLog.Conflict) => Effect.fail("conflict")
Binary file
@@ -18,7 +18,12 @@
18
18
  open ReventlessCore
19
19
  open Reventless
20
20
 
21
- module Dcb = DcbEventLogStorage_Postgres
21
+ // Runtime-pure ops module (no `@pulumi/pulumi`) — this feed is drained by the
22
+ // deployed change-feed relay Lambda, so its cold-start graph must stay
23
+ // Pulumi-free. `_Ops` carries every helper used below (mkBuilder,
24
+ // buildReadWhere, decodeCursor, selectColumns, rowToEvent); the deploy-time
25
+ // DcbEventLogStorage_Postgres only `include`s it and adds the Pulumi adapter.
26
+ module Dcb = DcbEventLogStorage_Postgres_Ops
22
27
 
23
28
  // One page of the feed. `events` are in (transaction_id, position) order;
24
29
  // `cursor` is the checkpoint to persist and to pass as the next `after` (None
@@ -44,7 +49,9 @@ let readBatch = async (
44
49
  ~after=after->Option.flatMap(Dcb.decodeCursor),
45
50
  ~applyFence=true,
46
51
  )
47
- let sql = `SELECT ${Dcb.selectColumns} FROM dcb_event WHERE ${where} ORDER BY transaction_id ASC, position ASC LIMIT ${Int.toString(limit)}`
52
+ let sql = `SELECT ${Dcb.selectColumns} FROM dcb_event WHERE ${where} ORDER BY transaction_id ASC, position ASC LIMIT ${Int.toString(
53
+ limit,
54
+ )}`
48
55
  let rows = await pool->PgDriver.query(sql, b.params)
49
56
  let events = rows->Array.map(Dcb.rowToEvent)
50
57
  let cursor = switch events->Array.get(events->Array.length - 1) {
@@ -58,10 +65,9 @@ let readBatch = async (
58
65
 
59
66
  // The persisted checkpoint for `subscriber`, encoded as a cursor, or None if the
60
67
  // subscriber has never committed (→ replay from the beginning).
61
- let loadCheckpoint = async (
62
- pool: PgDriver.pool,
63
- ~subscriber: string,
64
- ): option<DcbTag.sequencePosition> =>
68
+ let loadCheckpoint = async (pool: PgDriver.pool, ~subscriber: string): option<
69
+ DcbTag.sequencePosition,
70
+ > =>
65
71
  switch await pool->PgDriver.queryOne(
66
72
  "SELECT lpad(last_tx::text, 20, '0') || ':' || lpad(last_position::text, 20, '0') AS cursor
67
73
  FROM dcb_subscription WHERE subscriber = $1 AND last_position > 0",
@@ -100,8 +106,9 @@ let saveCheckpoint = async (
100
106
  // dedicated client (caller owns it — release via PgDriver.unlisten). Pair with a
101
107
  // low-frequency fallback tick in the consumer loop so a missed NOTIFY only adds
102
108
  // latency, never a permanent stall.
103
- let listen = (pool: PgDriver.pool, ~logName: string, ~onWake: unit => unit): promise<PgDriver.client> =>
104
- pool->PgDriver.listen(~channel="dcb_" ++ logName, ~onNotify=_ => onWake())
109
+ let listen = (pool: PgDriver.pool, ~logName: string, ~onWake: unit => unit): promise<
110
+ PgDriver.client,
111
+ > => pool->PgDriver.listen(~channel="dcb_" ++ logName, ~onNotify=_ => onWake())
105
112
 
106
113
  let unlisten = (client: PgDriver.client, ~logName: string): promise<unit> =>
107
114
  client->PgDriver.unlisten(~channel="dcb_" ++ logName)
@@ -124,7 +131,12 @@ let drain = async (
124
131
  let cursor = ref(await loadCheckpoint(pool, ~subscriber))
125
132
  let continue = ref(true)
126
133
  while continue.contents {
127
- let {events, cursor: newCursor} = await readBatch(pool, ~logName, ~after=?cursor.contents, ~limit)
134
+ let {events, cursor: newCursor} = await readBatch(
135
+ pool,
136
+ ~logName,
137
+ ~after=?cursor.contents,
138
+ ~limit,
139
+ )
128
140
  if events->Array.length == 0 {
129
141
  continue := false
130
142
  } else {
@@ -2,15 +2,15 @@
2
2
 
3
3
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
4
  import * as PgDriver$ReventlessPostgres from "./PgDriver.res.mjs";
5
- import * as DcbEventLogStorage_Postgres$ReventlessPostgres from "./DcbEventLogStorage_Postgres.res.mjs";
5
+ import * as DcbEventLogStorage_Postgres_Ops$ReventlessPostgres from "./DcbEventLogStorage_Postgres_Ops.res.mjs";
6
6
 
7
7
  async function readBatch(pool, logName, after, limitOpt) {
8
8
  let limit = limitOpt !== undefined ? limitOpt : 500;
9
- let b = DcbEventLogStorage_Postgres$ReventlessPostgres.mkBuilder();
10
- let where = DcbEventLogStorage_Postgres$ReventlessPostgres.buildReadWhere(b, logName, [], Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor), true);
11
- let sql = `SELECT ` + DcbEventLogStorage_Postgres$ReventlessPostgres.selectColumns + ` FROM dcb_event WHERE ` + where + ` ORDER BY transaction_id ASC, position ASC LIMIT ` + limit.toString();
9
+ let b = DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.mkBuilder();
10
+ let where = DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.buildReadWhere(b, logName, [], Stdlib_Option.flatMap(after, DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor), true);
11
+ let sql = `SELECT ` + DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.selectColumns + ` FROM dcb_event WHERE ` + where + ` ORDER BY transaction_id ASC, position ASC LIMIT ` + limit.toString();
12
12
  let rows = await PgDriver$ReventlessPostgres.query(pool, sql, b.params);
13
- let events = rows.map(DcbEventLogStorage_Postgres$ReventlessPostgres.rowToEvent);
13
+ let events = rows.map(DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.rowToEvent);
14
14
  let last = events[events.length - 1 | 0];
15
15
  let cursor = last !== undefined ? last.position : undefined;
16
16
  return {
@@ -31,7 +31,7 @@ async function loadCheckpoint(pool, subscriber) {
31
31
  }
32
32
 
33
33
  async function saveCheckpoint(pool, subscriber, cursor) {
34
- let match = DcbEventLogStorage_Postgres$ReventlessPostgres.decodeCursor(cursor);
34
+ let match = DcbEventLogStorage_Postgres_Ops$ReventlessPostgres.decodeCursor(cursor);
35
35
  if (match !== undefined) {
36
36
  await PgDriver$ReventlessPostgres.query(pool, "INSERT INTO dcb_subscription(subscriber, last_tx, last_position)\n VALUES ($1, $2::xid8, $3::bigint)\n ON CONFLICT (subscriber)\n DO UPDATE SET last_tx = EXCLUDED.last_tx, last_position = EXCLUDED.last_position", [
37
37
  subscriber,
Binary file
Binary file
@@ -47,8 +47,7 @@ type queryResult = {rows: array<dict<JSON.t>>}
47
47
 
48
48
  let query = async (pool, sql, params) => (await _query(pool, sql, params)).rows
49
49
 
50
- let queryOne = async (pool, sql, params) =>
51
- (await _query(pool, sql, params)).rows->Array.get(0)
50
+ let queryOne = async (pool, sql, params) => (await _query(pool, sql, params)).rows->Array.get(0)
52
51
 
53
52
  let exec = async (pool, sql) => {
54
53
  let _ = await _queryText(pool, sql)
Binary file