@reventlessdev/reventless-local 3.0.0-alpha.166 → 3.0.0-alpha.168

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 (34) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +11 -11
  3. package/src/Platform.res +11 -1
  4. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +16 -8
  5. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res.mjs +7 -7
  6. package/src/adapter/DomainGraphQL_Server.res +96 -0
  7. package/src/adapter/DomainGraphQL_Server.res.mjs +84 -0
  8. package/src/adapter/GraphQL_Server.res.mjs +18 -0
  9. package/src/adapter/LocalObjectStore.res +64 -0
  10. package/src/adapter/LocalObjectStore.res.mjs +55 -0
  11. package/src/adapter/PgProjectionCatchup.res +6 -1
  12. package/src/adapter/PgProjectionCatchup.res.mjs +4 -2
  13. package/src/adapter/ProjectionCheckpoint.res +11 -2
  14. package/src/adapter/ProjectionCheckpoint.res.mjs +9 -3
  15. package/tests/PluginEventDecodeTest.res +2 -0
  16. package/tests/PluginEventDecodeTest.res.mjs +2 -2
  17. package/tests/adapter/GraphQL_SchemaInspectorTest.res +6 -0
  18. package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +6 -0
  19. package/tests/adapter/InboundTranslationMutationTest.res +139 -0
  20. package/tests/adapter/InboundTranslationMutationTest.res.mjs +132 -0
  21. package/tests/adapter/QueryDbListResolverTest.res +2 -0
  22. package/tests/adapter/QueryDbListResolverTest.res.mjs +2 -0
  23. package/tests/adapter/ServedBucketHttpTest.res +174 -0
  24. package/tests/adapter/ServedBucketHttpTest.res.mjs +177 -0
  25. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +13 -9
  26. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +21 -13
  27. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +215 -0
  28. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res.mjs +273 -0
  29. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformTest.res +38 -0
  30. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformTest.res.mjs +28 -0
  31. package/tests/components/stateviewslice/StateViewSliceFixtures.res +1 -1
  32. package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +2 -1
  33. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +1 -1
  34. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +2 -1
@@ -0,0 +1,273 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Stream from "effect/Stream";
8
+ import * as Pulumi from "@pulumi/pulumi";
9
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
10
+ import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
11
+ import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
12
+ import * as Component$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Component.res.mjs";
13
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
14
+ import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
15
+ import * as TestFixtures$ReventlessLocal from "../../TestFixtures.res.mjs";
16
+ import * as DcbEventLog_Builder$ReventlessLocal from "../../../src/components/DcbEventLog_Builder.res.mjs";
17
+ import * as StateChangeSlice_Builder$ReventlessLocal from "../../../src/components/StateChangeSlice_Builder.res.mjs";
18
+ import * as OutboundTranslationSlice_Builder$ReventlessLocal from "../../../src/components/OutboundTranslationSlice_Builder.res.mjs";
19
+
20
+ let name = "Place";
21
+
22
+ let moduleUrl = import.meta.url;
23
+
24
+ let eventSchema = S.schema(s => ({
25
+ TAG: "Placed",
26
+ orderId: s.m(DcbTag$Reventless.string),
27
+ customerId: s.m(S.string)
28
+ }));
29
+
30
+ let consumedEventSchema = S.literal("Placed");
31
+
32
+ let commandSchema = S.schema(s => ({
33
+ TAG: "Place",
34
+ orderId: s.m(DcbTag$Reventless.string),
35
+ customerId: s.m(S.string)
36
+ }));
37
+
38
+ let errorSchema = S.literal("AlreadyPlaced");
39
+
40
+ function commandAuthorization(param) {
41
+ return "AllowAuthenticated";
42
+ }
43
+
44
+ let PlaceSpec = {
45
+ name: name,
46
+ Id: undefined,
47
+ moduleUrl: moduleUrl,
48
+ eventSchema: eventSchema,
49
+ consumedEventSchema: consumedEventSchema,
50
+ errorSchema: errorSchema,
51
+ commandSchema: commandSchema,
52
+ commandAuthorization: commandAuthorization,
53
+ readConsistency: "EscalateOnRetry"
54
+ };
55
+
56
+ let moduleUrl$1 = import.meta.url;
57
+
58
+ function evolve(_state, _event) {
59
+ return true;
60
+ }
61
+
62
+ function decide(state, command) {
63
+ if (state) {
64
+ return {
65
+ TAG: "Error",
66
+ _0: "AlreadyPlaced"
67
+ };
68
+ } else {
69
+ return {
70
+ TAG: "Ok",
71
+ _0: [{
72
+ TAG: "Placed",
73
+ orderId: command.orderId,
74
+ customerId: command.customerId
75
+ }]
76
+ };
77
+ }
78
+ }
79
+
80
+ let PlaceBehavior = {
81
+ Spec: undefined,
82
+ moduleUrl: moduleUrl$1,
83
+ initialState: false,
84
+ evolve: evolve,
85
+ decide: decide
86
+ };
87
+
88
+ let moduleUrl$2 = import.meta.url;
89
+
90
+ let consumedEventSchema$1 = S.schema(s => ({
91
+ TAG: "Placed",
92
+ orderId: s.m(S.string)
93
+ }));
94
+
95
+ let outboundItemSchema = S.schema(s => ({
96
+ orderId: s.m(S.string)
97
+ }));
98
+
99
+ let SendConfirmSpec_externalSystem = "EmailService";
100
+
101
+ let SendConfirmSpec = {
102
+ name: "SendConfirm",
103
+ moduleUrl: moduleUrl$2,
104
+ consumedEventSchema: consumedEventSchema$1,
105
+ outboundItemSchema: outboundItemSchema,
106
+ inboundCommandSchema: S.unit,
107
+ maxRetries: 3,
108
+ heartbeatInterval: 60,
109
+ targetName: undefined,
110
+ externalSystem: SendConfirmSpec_externalSystem
111
+ };
112
+
113
+ let collectCalls = [];
114
+
115
+ let externalCalls = [];
116
+
117
+ let moduleUrl$3 = import.meta.url;
118
+
119
+ function collect(event) {
120
+ let orderId = event.orderId;
121
+ collectCalls.push(orderId);
122
+ return [[
123
+ orderId,
124
+ {
125
+ orderId: orderId
126
+ }
127
+ ]];
128
+ }
129
+
130
+ async function translate(_id, item) {
131
+ externalCalls.push(item.orderId);
132
+ return {
133
+ TAG: "Ok",
134
+ _0: undefined
135
+ };
136
+ }
137
+
138
+ let SendConfirmTranslation = {
139
+ collect: collect,
140
+ translate: translate,
141
+ moduleUrl: moduleUrl$3
142
+ };
143
+
144
+ let Bus = LocalBus$ReventlessLocal.Make({});
145
+
146
+ TestRunner$ReventlessLocal.setup();
147
+
148
+ let DcbLogMaker = DcbEventLog_Builder$ReventlessLocal.Make(Bus);
149
+
150
+ let dcbEventLog = DcbLogMaker.make("TestLog", undefined, {
151
+ TAG: "Simple",
152
+ _0: {
153
+ key: "orderId"
154
+ }
155
+ }, undefined);
156
+
157
+ async function publishJsons(cmdJsons) {
158
+ await Promise.all(cmdJsons.map(async cmdJson => {
159
+ let dict = cmdJson.commandJson;
160
+ let typeName;
161
+ typeName = typeof dict === "object" && dict !== null && !Array.isArray(dict) ? Stdlib_Option.getOr(Stdlib_Option.flatMap(dict["TAG"], j => {
162
+ if (typeof j === "string") {
163
+ return j;
164
+ }
165
+ }), "") : "";
166
+ let fullBody = Object.fromEntries([
167
+ [
168
+ "id",
169
+ cmdJson.id
170
+ ],
171
+ [
172
+ "meta",
173
+ S.reverseConvertToJsonOrThrow(cmdJson.meta, Message$Reventless.metaSchema)
174
+ ],
175
+ [
176
+ "command",
177
+ cmdJson.commandJson
178
+ ]
179
+ ]);
180
+ let handlers = CommandTopic$ReventlessCore.getHandlers(typeName);
181
+ await Promise.all(handlers.map(async entry => {
182
+ let item_reference = cmdJson.id;
183
+ let item = {
184
+ command: fullBody,
185
+ reference: item_reference
186
+ };
187
+ await Effect.runPromise(entry.handler(Stream.fromIterable([item])));
188
+ }));
189
+ }));
190
+ }
191
+
192
+ let publishJsonsOutput = Pulumi.output(publishJsons);
193
+
194
+ let PlaceMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
195
+ name: name,
196
+ moduleUrl: moduleUrl,
197
+ Id: Id$Reventless.$$String,
198
+ consumedEventSchema: consumedEventSchema,
199
+ errorSchema: errorSchema,
200
+ eventSchema: eventSchema,
201
+ commandSchema: commandSchema,
202
+ commandAuthorization: commandAuthorization,
203
+ readConsistency: "EscalateOnRetry"
204
+ })({
205
+ initialState: false,
206
+ evolve: evolve,
207
+ decide: decide,
208
+ moduleUrl: moduleUrl$1
209
+ });
210
+
211
+ let _placeSlice = PlaceMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
212
+
213
+ let dcbTopicOutputs = Component$ReventlessInfra.outputs(dcbEventLog).eventTopic;
214
+
215
+ let OutboundMaker = OutboundTranslationSlice_Builder$ReventlessLocal.Make(Bus);
216
+
217
+ let SendConfirm = OutboundMaker.Make(SendConfirmSpec)(SendConfirmTranslation);
218
+
219
+ let sendConfirmSlice = SendConfirm.make(dcbEventLog, publishJsonsOutput, undefined, undefined);
220
+
221
+ function placeCmdJson(orderId) {
222
+ return {
223
+ id: orderId,
224
+ meta: TestFixtures$ReventlessLocal.testMeta,
225
+ commandJson: S.reverseConvertToJsonOrThrow({
226
+ TAG: "Place",
227
+ orderId: orderId,
228
+ customerId: "cust-" + orderId
229
+ }, commandSchema)
230
+ };
231
+ }
232
+
233
+ async function readEventTypes(orderId) {
234
+ let logOps = await TestRunner$ReventlessLocal.resolve(DcbLogMaker.operations(dcbEventLog));
235
+ let result = await logOps.read([{
236
+ tags: [{
237
+ key: "orderId",
238
+ value: orderId
239
+ }]
240
+ }], undefined);
241
+ return result.events.map(e => e.eventType);
242
+ }
243
+
244
+ async function flush() {
245
+ await Promise.resolve();
246
+ await Promise.resolve();
247
+ await Promise.resolve();
248
+ await Promise.resolve();
249
+ }
250
+
251
+ export {
252
+ PlaceSpec,
253
+ PlaceBehavior,
254
+ SendConfirmSpec,
255
+ collectCalls,
256
+ externalCalls,
257
+ SendConfirmTranslation,
258
+ Bus,
259
+ DcbLogMaker,
260
+ dcbEventLog,
261
+ publishJsons,
262
+ publishJsonsOutput,
263
+ PlaceMaker,
264
+ _placeSlice,
265
+ dcbTopicOutputs,
266
+ OutboundMaker,
267
+ SendConfirm,
268
+ sendConfirmSlice,
269
+ placeCmdJson,
270
+ readEventTypes,
271
+ flush,
272
+ }
273
+ /* moduleUrl Not a pure module */
@@ -0,0 +1,38 @@
1
+ // Platform-level regression test for OutboundTranslationSlice wiring.
2
+ //
3
+ // The callback test next door exercises phase1/phase2 directly and stays green
4
+ // while the component never receives an event. This one publishes a command
5
+ // through the real chain — StateChangeSlice → DcbEventLog → event topic →
6
+ // EventCollector → phase 1 → phase 2 — and asserts the external call lands.
7
+
8
+ open JestGlobals
9
+ open OutboundTranslationSlicePlatformFixtures
10
+
11
+ describe("OutboundTranslationSlice platform wiring:", () => {
12
+ let _ = beforeAllAsync(async () => {
13
+ let _ = await dcbEventLog->DcbLogMaker.operations->TestRunner.resolve
14
+ let _ = await sendConfirmSlice->SendConfirm.operations->TestRunner.resolve
15
+ // LocalEventCollectorChannel.connect registers the bus subscription inside a
16
+ // fire-and-forget Effect.runPromise; flush so it is in place before publishing.
17
+ let _ = await flush()
18
+ let resource = dcbTopicOutputs.resources->Array.getUnsafe(0)
19
+ let _ = await resource.name->TestRunner.resolve
20
+ })
21
+
22
+ testPromise("Place → Placed reaches phase 1 collect", async () => {
23
+ let _ = await publishJsons([placeCmdJson("order-1")])
24
+ let _ = await flush()
25
+
26
+ let eventTypes = await readEventTypes("order-1")
27
+ expect(eventTypes->Array.includes("Placed"))->toBe(true)
28
+
29
+ expect(collectCalls->Array.includes("order-1"))->toBe(true)
30
+ })
31
+
32
+ testPromise("phase 2 translates the collected item without the heartbeat", async () => {
33
+ let _ = await publishJsons([placeCmdJson("order-2")])
34
+ let _ = await flush()
35
+
36
+ expect(externalCalls->Array.includes("order-2"))->toBe(true)
37
+ })
38
+ })
@@ -0,0 +1,28 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
4
+ import * as OutboundTranslationSlicePlatformFixtures$ReventlessLocal from "./OutboundTranslationSlicePlatformFixtures.res.mjs";
5
+
6
+ globalThis.describe("OutboundTranslationSlice platform wiring:", () => {
7
+ globalThis.beforeAll(async () => {
8
+ await TestRunner$ReventlessLocal.resolve(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.DcbLogMaker.operations(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.dcbEventLog));
9
+ await TestRunner$ReventlessLocal.resolve(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.SendConfirm.operations(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.sendConfirmSlice));
10
+ await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.flush();
11
+ let resource = OutboundTranslationSlicePlatformFixtures$ReventlessLocal.dcbTopicOutputs.resources[0];
12
+ await TestRunner$ReventlessLocal.resolve(resource.name);
13
+ });
14
+ globalThis.test("Place → Placed reaches phase 1 collect", async () => {
15
+ await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.publishJsons([OutboundTranslationSlicePlatformFixtures$ReventlessLocal.placeCmdJson("order-1")]);
16
+ await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.flush();
17
+ let eventTypes = await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.readEventTypes("order-1");
18
+ globalThis.expect(eventTypes.includes("Placed")).toBe(true);
19
+ globalThis.expect(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.collectCalls.includes("order-1")).toBe(true);
20
+ });
21
+ globalThis.test("phase 2 translates the collected item without the heartbeat", async () => {
22
+ await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.publishJsons([OutboundTranslationSlicePlatformFixtures$ReventlessLocal.placeCmdJson("order-2")]);
23
+ await OutboundTranslationSlicePlatformFixtures$ReventlessLocal.flush();
24
+ globalThis.expect(OutboundTranslationSlicePlatformFixtures$ReventlessLocal.externalCalls.includes("order-2")).toBe(true);
25
+ });
26
+ });
27
+
28
+ /* Not a pure module */
@@ -42,7 +42,7 @@ module ItemsViewProjection = {
42
42
 
43
43
  let moduleUrl: string = %raw(`import.meta.url`)
44
44
 
45
- let project = (event: consumedEvent) =>
45
+ let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
46
46
  switch event {
47
47
  | ItemAdded({id, name}) => [Set(id, {id, name})]
48
48
  | ItemRenamed({id, name}) => [Update(id, s => {...s, name})]
@@ -74,7 +74,8 @@ let ItemsViewSpec = {
74
74
 
75
75
  let moduleUrl$1 = import.meta.url;
76
76
 
77
- function project(event) {
77
+ function project(param) {
78
+ let event = param.event;
78
79
  switch (event.TAG) {
79
80
  case "ItemAdded" :
80
81
  let id = event.id;
@@ -44,7 +44,7 @@ module ScoresViewProjection = {
44
44
 
45
45
  let moduleUrl: string = %raw(`import.meta.url`)
46
46
 
47
- let project = (event: consumedEvent) =>
47
+ let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
48
48
  switch event {
49
49
  | ScoreRecorded({id, category, date, score}) =>
50
50
  [Set(id, {id, category, date, score})]
@@ -79,7 +79,8 @@ let ScoresViewSpec = {
79
79
 
80
80
  let moduleUrl$1 = import.meta.url;
81
81
 
82
- function project(event) {
82
+ function project(param) {
83
+ let event = param.event;
83
84
  if (event.TAG !== "ScoreRecorded") {
84
85
  return [{
85
86
  TAG: "Delete",