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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +10 -10
  3. package/src/Platform.res +127 -31
  4. package/src/Platform.res.mjs +226 -44
  5. package/src/adapter/DcbEventLog/DcbBackend.res +2 -2
  6. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +4 -1
  7. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
  8. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +14 -4
  9. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +19 -14
  10. package/src/adapter/EventLog/EventLogBackend.res +82 -0
  11. package/src/adapter/EventLog/EventLogBackend.res.mjs +53 -0
  12. package/src/adapter/EventLog/EventLogStorage.res +15 -0
  13. package/src/adapter/EventLog/EventLogStorage.res.mjs +20 -1
  14. package/src/adapter/EventLog/EventLogStorage_Postgres.res +37 -0
  15. package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +21 -0
  16. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +26 -11
  17. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +15 -6
  18. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +54 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +23 -0
  20. package/src/adapter/Postgres/PgProjectionFeed.res +100 -0
  21. package/src/adapter/Postgres/PgProjectionFeed.res.mjs +54 -0
  22. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +281 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +189 -0
  24. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +335 -0
  25. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +252 -0
  26. package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
  27. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
  28. package/src/adapter/QueryDb/QueryDbResolvers.res +49 -0
  29. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +20 -1
  30. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +264 -0
  31. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res.mjs +164 -0
  32. package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
  33. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
  34. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +47 -0
  35. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
  36. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
  37. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
  38. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
  39. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
  40. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
  41. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
  42. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
  43. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
  44. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
  45. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
  46. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +88 -0
  48. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
  49. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
  50. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
  51. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
  52. package/src/components/Aggregate_Builder_Single.res +12 -5
  53. package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
  54. package/src/components/Aggregate_Builder_Single_Async.res +12 -5
  55. package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
  56. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
  57. package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
  58. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
  59. package/src/components/ReadModel_Builder_Single.res +14 -5
  60. package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
  61. package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
  62. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
  63. package/src/components/StateViewSlice_Builder.res +10 -4
  64. package/src/components/StateViewSlice_Builder.res.mjs +8 -10
  65. package/src/components/StateViewSlice_Builder_Stream.res +10 -4
  66. package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
  67. package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
  68. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
  69. package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
  70. package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
  71. package/tests/PgChangeFeedRelay_IntegrationTest.res +115 -4
  72. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +84 -0
  73. package/tests/PgPipeline_IntegrationTest.res +208 -0
  74. package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
  75. package/tests/PgQueryResolver_LambdaTest.res +308 -0
  76. package/tests/PgQueryResolver_LambdaTest.res.mjs +392 -0
@@ -0,0 +1,208 @@
1
+ // End-to-end Postgres projection pipeline (B1 + B3.0 + B3.1 composed), minus AWS.
2
+ //
3
+ // Skipped unless PG_URL is set. Exercises the full deployed classic path against
4
+ // a real database:
5
+ // classic append (`event_log`) → change-feed relay (`relayClassicWithPool`,
6
+ // emitting the EventCollector `{id, meta, event}` bodies) → feed-queue SQS
7
+ // records → `handleStreamEvent` decode → a real `ReadModel_Callback` projection
8
+ // (meta.service dispatch) → `QueryDbStorage_Postgres` write → `qdb_` row.
9
+ //
10
+ // This is the composition the first live deploy will exercise; the only pieces
11
+ // it can't cover are the AWS boundary itself (SQS delivery, ESMs, VPC).
12
+
13
+ open JestGlobals
14
+ open ReventlessCore
15
+
16
+ @val external processEnv: dict<string> = "process.env"
17
+
18
+ // --- Fixtures: one classic source, one read model, one projection mapping ---
19
+
20
+ module OrderSource = {
21
+ module Id = Reventless.Id.StringPure
22
+ let name = "Order"
23
+
24
+ @schema
25
+ type event = OrderPlaced({product: string})
26
+ }
27
+
28
+ module OrdersSpec = {
29
+ module Id = Reventless.Id.StringPure
30
+ let name = "PipelineOrders"
31
+ let moduleUrl = ""
32
+
33
+ @schema
34
+ type state = {id: string, product: string}
35
+
36
+ let config = Reventless.ReadModel.config()
37
+ let subIdConfig = None
38
+ let authorization: Reventless.Authorization.permission = AllowAuthenticated
39
+ let visibility: Reventless.Visibility.t = Public
40
+ }
41
+
42
+ module OrderMapping = Reventless.Projection.Mapping.Make(
43
+ OrderSource,
44
+ OrdersSpec,
45
+ {
46
+ let project = ({id, event, _}: Reventless.Message.event'<string, OrderSource.event>) =>
47
+ switch event {
48
+ | OrderPlaced({product}) => Reventless.Projection.Set(id, ({id, product}: OrdersSpec.state))
49
+ }
50
+ },
51
+ )
52
+
53
+ module OrdersMappings = {
54
+ module Target = OrdersSpec
55
+ module M = Reventless.Projection.Mappings.Make(OrdersSpec)
56
+ module type Mapping = M.Mapping
57
+ let moduleUrl = ""
58
+ let mappings: array<module(Mapping)> = [module(OrderMapping)]
59
+ }
60
+
61
+ // --- Helpers ---
62
+
63
+ // The flat on-disk item the classic append stores verbatim — same shape the
64
+ // DynamoDB path puts (id, position, event, data, decomposed meta). `service`
65
+ // must be the SOURCE name: ReadModel_Callback dispatches mappings by
66
+ // meta.service (the repeatedly-bitten projection dispatch key).
67
+ let flatItem = (~id, ~seq, ~service, ~event: OrderSource.event) => {
68
+ let (eventType, data) =
69
+ event
70
+ ->S.reverseConvertToJsonOrThrow(OrderSource.eventSchema)
71
+ ->Message.splitMessage
72
+ [
73
+ ("id", JSON.Encode.string(id)),
74
+ ("position", JSON.Encode.string(seq->Int.toString->String.padStart(9, "0"))),
75
+ ("event", JSON.Encode.string(eventType)),
76
+ ("data", JSON.Encode.object(data)),
77
+ ]
78
+ ->Array.concat(Message.generateMeta(~service)->Message.decomposeMeta)
79
+ ->Dict.fromArray
80
+ ->JSON.Encode.object
81
+ }
82
+
83
+ // Wrap relay bodies the way the deployed feed queue delivers them.
84
+ let asSqsEvent = (bodies: array<JSON.t>): PulumiAws.Lambda.CallbackFunction.event =>
85
+ Obj.magic({
86
+ "Records": bodies->Array.map(body =>
87
+ {
88
+ "eventSource": "aws:sqs",
89
+ "eventSourceARN": "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
90
+ "body": JSON.stringify(body),
91
+ }
92
+ ),
93
+ })
94
+
95
+ switch processEnv->Dict.get("PG_URL") {
96
+ | None =>
97
+ testSync("Postgres pipeline integration (skipped — set PG_URL to run)", () =>
98
+ expect(true)->toBe(true)
99
+ )
100
+ | Some(url) =>
101
+ let pool = ReventlessPostgres.PgDriver.makePool({connectionString: url})
102
+
103
+ let qdbOps = ReventlessPostgres.QueryDbStorage_Postgres.makeOperations(
104
+ ~pool,
105
+ ~name=OrdersSpec.name,
106
+ ~indexes=[],
107
+ ~subIdField=None,
108
+ )
109
+
110
+ module Callback = ReadModel_Callback.Make(
111
+ OrdersSpec,
112
+ OrdersMappings,
113
+ {
114
+ module ReadModelSpec = OrdersSpec
115
+ // Production passes the raw JSON op set (ReadModelEntryPoint) — actions are
116
+ // rewritten to JSON via stateSchema before handleAction, so the JSON-level
117
+ // ops satisfy the typed slot exactly as deployed.
118
+ let operations: QueryDb.operations<string, OrdersSpec.state> = Obj.magic(qdbOps)
119
+ },
120
+ )
121
+
122
+ beforeAllAsync(async () => {
123
+ await ReventlessPostgres.PgSchema.ensureSchema(pool)
124
+ await ReventlessPostgres.PgSchema.truncateAll(pool)
125
+ let _ = await pool->ReventlessPostgres.PgDriver.query(
126
+ `DROP TABLE IF EXISTS qdb_${OrdersSpec.name}`,
127
+ [],
128
+ )
129
+ })
130
+ afterAll(() => {
131
+ let _ = pool->ReventlessPostgres.PgDriver.endPool
132
+ })
133
+
134
+ describe("Postgres pipeline (append → relay → feed decode → projection → qdb row)", () => {
135
+ testPromise("projects a classic event end-to-end into the Postgres QueryDb", async () => {
136
+ let (_n, elOps, _s) =
137
+ ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
138
+ ~pool,
139
+ ~name="PipelineOrderEventLog",
140
+ ~opts=(),
141
+ )
142
+ let _ = await elOps.append(
143
+ 0,
144
+ "o-1",
145
+ [flatItem(~id="o-1", ~seq=0, ~service=OrderSource.name, ~event=OrderPlaced({product: "boat"}))],
146
+ )
147
+
148
+ // Relay drains event_log and emits the EventCollector bodies.
149
+ let sink = ref([])
150
+ let relayed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
151
+ ~pool,
152
+ ~logName="PipelineOrderEventLog",
153
+ ~subscriber="aws-rm-feed:PipelineOrderEventLog",
154
+ ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
155
+ )
156
+ expect(relayed)->toBe(1)
157
+
158
+ // Feed-queue delivery → entry-point decode → real projection callback.
159
+ let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
160
+ Callback.handleJsonEvents,
161
+ asSqsEvent(sink.contents),
162
+ (),
163
+ )->Effect.runPromise
164
+
165
+ // The projected row is in the Postgres QueryDb.
166
+ switch await qdbOps.load("o-1") {
167
+ | Ok(rows) =>
168
+ expect(rows->Array.length)->toBe(1)
169
+ let row = rows->Array.getUnsafe(0)->JSON.Decode.object->Option.getOrThrow
170
+ expect(row->Dict.get("product"))->toEqual(Some(JSON.Encode.string("boat")))
171
+ expect(row->Dict.get("id"))->toEqual(Some(JSON.Encode.string("o-1")))
172
+ | Error(_) => expect("load failed")->toBe("Ok")
173
+ }
174
+ })
175
+
176
+ testPromise("meta.service mismatch is a silent no-op (dispatch filter)", async () => {
177
+ let (_n, elOps, _s) =
178
+ ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
179
+ ~pool,
180
+ ~name="PipelineWrongServiceEventLog",
181
+ ~opts=(),
182
+ )
183
+ let _ = await elOps.append(
184
+ 0,
185
+ "o-2",
186
+ [flatItem(~id="o-2", ~seq=0, ~service="NotTheSource", ~event=OrderPlaced({product: "x"}))],
187
+ )
188
+
189
+ let sink = ref([])
190
+ let _ = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
191
+ ~pool,
192
+ ~logName="PipelineWrongServiceEventLog",
193
+ ~subscriber="aws-rm-feed:PipelineWrongServiceEventLog",
194
+ ~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
195
+ )
196
+ let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
197
+ Callback.handleJsonEvents,
198
+ asSqsEvent(sink.contents),
199
+ (),
200
+ )->Effect.runPromise
201
+
202
+ switch await qdbOps.load("o-2") {
203
+ | Ok(rows) => expect(rows->Array.length)->toBe(0)
204
+ | Error(_) => expect("load failed")->toBe("Ok")
205
+ }
206
+ })
207
+ })
208
+ }
@@ -0,0 +1,260 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
6
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
+ import * as Effect from "effect/Effect";
8
+ import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
9
+ import * as Projection$Reventless from "@reventlessdev/reventless-spec/src/types/Projection.res.mjs";
10
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
11
+ import * as PgDriver$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgDriver.res.mjs";
12
+ import * as PgSchema$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgSchema.res.mjs";
13
+ import * as ReadModel_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Callback.res.mjs";
14
+ import * as PgChangeFeedRelay_Runtime$ReventlessAws from "../src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs";
15
+ import * as QueryDbStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/QueryDbStorage_Postgres.res.mjs";
16
+ import * as EventLogStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/EventLogStorage_Postgres.res.mjs";
17
+ import * as EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws from "../src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
18
+
19
+ let name = "Order";
20
+
21
+ let eventSchema = S.schema(s => ({
22
+ TAG: "OrderPlaced",
23
+ product: s.m(S.string)
24
+ }));
25
+
26
+ let OrderSource = {
27
+ Id: undefined,
28
+ name: name,
29
+ eventSchema: eventSchema
30
+ };
31
+
32
+ let name$1 = "PipelineOrders";
33
+
34
+ let moduleUrl = "";
35
+
36
+ let stateSchema = S.schema(s => ({
37
+ id: s.m(S.string),
38
+ product: s.m(S.string)
39
+ }));
40
+
41
+ let config = ReadModel$Reventless.config(undefined, undefined, undefined);
42
+
43
+ let OrdersSpec = {
44
+ Id: undefined,
45
+ name: name$1,
46
+ moduleUrl: moduleUrl,
47
+ stateSchema: stateSchema,
48
+ config: config,
49
+ subIdConfig: undefined,
50
+ authorization: "AllowAuthenticated",
51
+ visibility: "Public"
52
+ };
53
+
54
+ function project(param) {
55
+ let id = param.id;
56
+ return {
57
+ TAG: "Set",
58
+ _0: id,
59
+ _1: {
60
+ id: id,
61
+ product: param.event.product
62
+ }
63
+ };
64
+ }
65
+
66
+ let OrderMapping = Projection$Reventless.Mapping.Make({
67
+ Id: {
68
+ schema: Id$Reventless.StringPure.schema,
69
+ make: prim => prim,
70
+ makeFromString: prim => prim,
71
+ toString: prim => prim,
72
+ cmp: Id$Reventless.StringPure.cmp
73
+ },
74
+ name: name,
75
+ eventSchema: eventSchema
76
+ })({
77
+ Id: {
78
+ schema: Id$Reventless.StringPure.schema,
79
+ make: prim => prim,
80
+ makeFromString: prim => prim,
81
+ toString: prim => prim,
82
+ cmp: Id$Reventless.StringPure.cmp
83
+ },
84
+ name: name$1,
85
+ stateSchema: stateSchema,
86
+ subIdConfig: undefined
87
+ })({
88
+ project: project
89
+ });
90
+
91
+ let M = Projection$Reventless.Mappings.Make({
92
+ Id: {
93
+ schema: Id$Reventless.StringPure.schema,
94
+ make: prim => prim,
95
+ makeFromString: prim => prim,
96
+ toString: prim => prim,
97
+ cmp: Id$Reventless.StringPure.cmp
98
+ },
99
+ name: name$1,
100
+ stateSchema: stateSchema,
101
+ subIdConfig: undefined
102
+ });
103
+
104
+ let moduleUrl$1 = "";
105
+
106
+ let mappings = [OrderMapping];
107
+
108
+ let OrdersMappings = {
109
+ Target: undefined,
110
+ M: M,
111
+ moduleUrl: moduleUrl$1,
112
+ mappings: mappings
113
+ };
114
+
115
+ function flatItem(id, seq, service, event) {
116
+ let match = Message$ReventlessCore.splitMessage(S.reverseConvertToJsonOrThrow(event, eventSchema));
117
+ return Object.fromEntries([
118
+ [
119
+ "id",
120
+ id
121
+ ],
122
+ [
123
+ "position",
124
+ seq.toString().padStart(9, "0")
125
+ ],
126
+ [
127
+ "event",
128
+ match[0]
129
+ ],
130
+ [
131
+ "data",
132
+ match[1]
133
+ ]
134
+ ].concat(Message$ReventlessCore.decomposeMeta(Message$ReventlessCore.generateMeta(service, undefined, undefined, undefined, undefined, undefined, undefined, undefined))));
135
+ }
136
+
137
+ function asSqsEvent(bodies) {
138
+ return {
139
+ Records: bodies.map(body => ({
140
+ eventSource: "aws:sqs",
141
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
142
+ body: JSON.stringify(body)
143
+ }))
144
+ };
145
+ }
146
+
147
+ let url = process.env["PG_URL"];
148
+
149
+ if (url !== undefined) {
150
+ let pool = PgDriver$ReventlessPostgres.makePool({
151
+ connectionString: url
152
+ });
153
+ let qdbOps = QueryDbStorage_Postgres$ReventlessPostgres.makeOperations(pool, name$1, [], undefined);
154
+ let Callback = ReadModel_Callback$ReventlessCore.Make({
155
+ Id: {
156
+ schema: Id$Reventless.StringPure.schema,
157
+ make: prim => prim,
158
+ makeFromString: prim => prim,
159
+ toString: prim => prim,
160
+ cmp: Id$Reventless.StringPure.cmp
161
+ },
162
+ name: name$1,
163
+ moduleUrl: moduleUrl,
164
+ stateSchema: stateSchema,
165
+ config: config,
166
+ subIdConfig: undefined,
167
+ authorization: "AllowAuthenticated",
168
+ visibility: "Public"
169
+ })({
170
+ moduleUrl: moduleUrl$1,
171
+ mappings: mappings
172
+ })({
173
+ ReadModelSpec: {
174
+ Id: {
175
+ schema: Id$Reventless.StringPure.schema,
176
+ make: prim => prim,
177
+ makeFromString: prim => prim,
178
+ toString: prim => prim,
179
+ cmp: Id$Reventless.StringPure.cmp
180
+ },
181
+ name: name$1,
182
+ moduleUrl: moduleUrl,
183
+ stateSchema: stateSchema,
184
+ config: config,
185
+ subIdConfig: undefined,
186
+ authorization: "AllowAuthenticated",
187
+ visibility: "Public"
188
+ },
189
+ operations: qdbOps
190
+ });
191
+ globalThis.beforeAll(async () => {
192
+ await PgSchema$ReventlessPostgres.ensureSchema(pool);
193
+ await PgSchema$ReventlessPostgres.truncateAll(pool);
194
+ await PgDriver$ReventlessPostgres.query(pool, `DROP TABLE IF EXISTS qdb_` + name$1, []);
195
+ });
196
+ globalThis.afterAll(() => {
197
+ PgDriver$ReventlessPostgres.endPool(pool);
198
+ });
199
+ globalThis.describe("Postgres pipeline (append → relay → feed decode → projection → qdb row)", () => {
200
+ globalThis.test("projects a classic event end-to-end into the Postgres QueryDb", async () => {
201
+ let match = EventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, "PipelineOrderEventLog", undefined, undefined);
202
+ await match[1].append(0, "o-1", [flatItem("o-1", 0, name, {
203
+ TAG: "OrderPlaced",
204
+ product: "boat"
205
+ })]);
206
+ let sink = {
207
+ contents: []
208
+ };
209
+ let relayed = await PgChangeFeedRelay_Runtime$ReventlessAws.relayClassicWithPool(pool, "PipelineOrderEventLog", "aws-rm-feed:PipelineOrderEventLog", async jsons => {
210
+ sink.contents = sink.contents.concat(jsons);
211
+ });
212
+ globalThis.expect(relayed).toBe(1);
213
+ await Effect.runPromise(EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws.handleStreamEvent(Callback.handleJsonEvents, asSqsEvent(sink.contents), undefined));
214
+ let rows = await qdbOps.load("o-1");
215
+ if (rows.TAG === "Ok") {
216
+ let rows$1 = rows._0;
217
+ globalThis.expect(rows$1.length).toBe(1);
218
+ let row = Stdlib_Option.getOrThrow(Stdlib_JSON.Decode.object(rows$1[0]), undefined);
219
+ globalThis.expect(row["product"]).toEqual("boat");
220
+ globalThis.expect(row["id"]).toEqual("o-1");
221
+ return;
222
+ }
223
+ globalThis.expect("load failed").toBe("Ok");
224
+ });
225
+ globalThis.test("meta.service mismatch is a silent no-op (dispatch filter)", async () => {
226
+ let match = EventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, "PipelineWrongServiceEventLog", undefined, undefined);
227
+ await match[1].append(0, "o-2", [flatItem("o-2", 0, "NotTheSource", {
228
+ TAG: "OrderPlaced",
229
+ product: "x"
230
+ })]);
231
+ let sink = {
232
+ contents: []
233
+ };
234
+ await PgChangeFeedRelay_Runtime$ReventlessAws.relayClassicWithPool(pool, "PipelineWrongServiceEventLog", "aws-rm-feed:PipelineWrongServiceEventLog", async jsons => {
235
+ sink.contents = sink.contents.concat(jsons);
236
+ });
237
+ await Effect.runPromise(EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws.handleStreamEvent(Callback.handleJsonEvents, asSqsEvent(sink.contents), undefined));
238
+ let rows = await qdbOps.load("o-2");
239
+ if (rows.TAG === "Ok") {
240
+ globalThis.expect(rows._0.length).toBe(0);
241
+ return;
242
+ }
243
+ globalThis.expect("load failed").toBe("Ok");
244
+ });
245
+ });
246
+ } else {
247
+ globalThis.test("Postgres pipeline integration (skipped — set PG_URL to run)", () => {
248
+ globalThis.expect(true).toBe(true);
249
+ });
250
+ }
251
+
252
+ export {
253
+ OrderSource,
254
+ OrdersSpec,
255
+ OrderMapping,
256
+ OrdersMappings,
257
+ flatItem,
258
+ asSqsEvent,
259
+ }
260
+ /* eventSchema Not a pure module */