@reventlessdev/reventless-local 3.0.0-alpha.173 → 3.0.0-alpha.175
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.
- package/CHANGELOG.md +15 -0
- package/package.json +9 -9
- package/src/Platform.res +19 -6
- package/src/Platform.res.mjs +10 -0
- package/src/adapter/Api/LocalEvents_Server.res +324 -0
- package/src/adapter/Api/LocalEvents_Server.res.mjs +341 -0
- package/src/adapter/DomainGraphQL_Server.res +23 -0
- package/src/adapter/DomainGraphQL_Server.res.mjs +36 -14
- package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +352 -0
- package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res.mjs +362 -0
- package/src/adapter/LocalBus.res +12 -1
- package/src/adapter/LocalBus.res.mjs +36 -0
- package/tests/adapter/EventHistoryResolverTest.res +208 -0
- package/tests/adapter/EventHistoryResolverTest.res.mjs +286 -0
- package/tests/adapter/LocalEvents_ServerTest.res +200 -0
- package/tests/adapter/LocalEvents_ServerTest.res.mjs +147 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
6
|
+
import * as LocalEvents_Server$ReventlessLocal from "../../src/adapter/Api/LocalEvents_Server.res.mjs";
|
|
7
|
+
|
|
8
|
+
TestRunner$ReventlessLocal.setup();
|
|
9
|
+
|
|
10
|
+
let sentFrames = {
|
|
11
|
+
contents: []
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function makeConn() {
|
|
15
|
+
sentFrames.contents = [];
|
|
16
|
+
return LocalEvents_Server$ReventlessLocal.addConnection(f => {
|
|
17
|
+
sentFrames.contents.push(f);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseFrame(s) {
|
|
22
|
+
return Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(s)), {});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function frameField(s, field) {
|
|
26
|
+
return Stdlib_Option.flatMap(parseFrame(s)[field], Stdlib_JSON.Decode.string);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function descriptor(id) {
|
|
30
|
+
return Object.fromEntries([
|
|
31
|
+
[
|
|
32
|
+
"changeKind",
|
|
33
|
+
"Updated"
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"id",
|
|
37
|
+
id
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"sortKeyValue",
|
|
41
|
+
"2026-07-28T00:00:00Z"
|
|
42
|
+
]
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
globalThis.describe("LocalEvents_Server", () => {
|
|
47
|
+
globalThis.beforeEach(() => LocalEvents_Server$ReventlessLocal.resetConnections());
|
|
48
|
+
globalThis.describe("channelMatches", () => {
|
|
49
|
+
globalThis.test("exact match", () => {
|
|
50
|
+
globalThis.expect(LocalEvents_Server$ReventlessLocal.channelMatches("/default/Product/p-1", "/default/Product/p-1")).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
globalThis.test("wildcard prefix matches any depth", () => {
|
|
53
|
+
globalThis.expect(LocalEvents_Server$ReventlessLocal.channelMatches("/default/Product/*", "/default/Product/p-1")).toBe(true);
|
|
54
|
+
globalThis.expect(LocalEvents_Server$ReventlessLocal.channelMatches("/default/*", "/default/Product/p-1/deep")).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
globalThis.test("wildcard does not match a sibling with the same stem", () => {
|
|
57
|
+
globalThis.expect(LocalEvents_Server$ReventlessLocal.channelMatches("/default/Product/*", "/default/ProductArchive/p-1")).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
globalThis.test("non-wildcard mismatch", () => {
|
|
60
|
+
globalThis.expect(LocalEvents_Server$ReventlessLocal.channelMatches("/default/Product/p-1", "/default/Product/p-2")).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
globalThis.describe("subscribe protocol", () => {
|
|
64
|
+
globalThis.test("connection_init is answered with connection_ack", () => {
|
|
65
|
+
let conn = makeConn();
|
|
66
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"connection_init"}`);
|
|
67
|
+
globalThis.expect(sentFrames.contents.length).toBe(1);
|
|
68
|
+
globalThis.expect(frameField(sentFrames.contents[0], "type")).toEqual("connection_ack");
|
|
69
|
+
});
|
|
70
|
+
globalThis.test("subscribe → state change → data frame on the subscription id", () => {
|
|
71
|
+
let conn = makeConn();
|
|
72
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"subscribe","id":"sub-1","channel":"/default/Product/*"}`);
|
|
73
|
+
globalThis.expect(frameField(sentFrames.contents[0], "type")).toEqual("subscribe_success");
|
|
74
|
+
LocalEvents_Server$ReventlessLocal.broadcastStateChange("Product", descriptor("p-1"));
|
|
75
|
+
globalThis.expect(sentFrames.contents.length).toBe(2);
|
|
76
|
+
let data = sentFrames.contents[1];
|
|
77
|
+
globalThis.expect(frameField(data, "type")).toEqual("data");
|
|
78
|
+
globalThis.expect(frameField(data, "id")).toEqual("sub-1");
|
|
79
|
+
let event = Stdlib_JSON.Decode.object(JSON.parse(Stdlib_Option.getOr(frameField(data, "event"), "")));
|
|
80
|
+
globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(event, o => o["id"]), Stdlib_JSON.Decode.string)).toEqual("p-1");
|
|
81
|
+
});
|
|
82
|
+
globalThis.test("channel segments are normalized like the AWS publisher", () => {
|
|
83
|
+
let conn = makeConn();
|
|
84
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"subscribe","id":"s","channel":"/default/My-Model/order-1-2026"}`);
|
|
85
|
+
LocalEvents_Server$ReventlessLocal.broadcastStateChange("My.Model", descriptor("order#1@2026"));
|
|
86
|
+
globalThis.expect(sentFrames.contents.length).toBe(2);
|
|
87
|
+
});
|
|
88
|
+
globalThis.test("unsubscribe stops delivery", () => {
|
|
89
|
+
let conn = makeConn();
|
|
90
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"subscribe","id":"sub-1","channel":"/default/Product/*"}`);
|
|
91
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"unsubscribe","id":"sub-1"}`);
|
|
92
|
+
LocalEvents_Server$ReventlessLocal.broadcastStateChange("Product", descriptor("p-1"));
|
|
93
|
+
globalThis.expect(sentFrames.contents.length).toBe(2);
|
|
94
|
+
globalThis.expect(frameField(sentFrames.contents[1], "type")).toEqual("unsubscribe_success");
|
|
95
|
+
});
|
|
96
|
+
globalThis.test("malformed and unknown frames are ignored", () => {
|
|
97
|
+
let conn = makeConn();
|
|
98
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `not json`);
|
|
99
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"mystery"}`);
|
|
100
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"subscribe","id":"only-id"}`);
|
|
101
|
+
globalThis.expect(sentFrames.contents.length).toBe(0);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
globalThis.describe("handlePublish", () => {
|
|
105
|
+
globalThis.test("rejects non-client channels with 403", () => {
|
|
106
|
+
let match = LocalEvents_Server$ReventlessLocal.handlePublish(undefined, `{"channel":"/default/Product/p-1","events":["{}"]}`);
|
|
107
|
+
globalThis.expect(match[0]).toBe(403);
|
|
108
|
+
});
|
|
109
|
+
globalThis.test("rejects an unverifiable token with 401", () => {
|
|
110
|
+
let match = LocalEvents_Server$ReventlessLocal.handlePublish("garbage-token", `{"channel":"/client/x","events":["{}"]}`);
|
|
111
|
+
globalThis.expect(match[0]).toBe(401);
|
|
112
|
+
});
|
|
113
|
+
globalThis.test("rejects malformed bodies with 400", () => {
|
|
114
|
+
let match = LocalEvents_Server$ReventlessLocal.handlePublish(undefined, `nope`);
|
|
115
|
+
let match$1 = LocalEvents_Server$ReventlessLocal.handlePublish(undefined, `{"channel":"/client/x","events":[]}`);
|
|
116
|
+
globalThis.expect(match[0]).toBe(400);
|
|
117
|
+
globalThis.expect(match$1[0]).toBe(400);
|
|
118
|
+
});
|
|
119
|
+
globalThis.test("fans out to a wildcard subscriber and accounts per event", () => {
|
|
120
|
+
let conn = makeConn();
|
|
121
|
+
LocalEvents_Server$ReventlessLocal.handleFrame(conn, `{"type":"subscribe","id":"sub-p","channel":"/client/shop/presence/*"}`);
|
|
122
|
+
let match = LocalEvents_Server$ReventlessLocal.handlePublish(undefined, `{"channel":"/client/shop/presence/room-1","events":["{\\"userId\\":\\"u1\\"}","not json",42]}`);
|
|
123
|
+
globalThis.expect(match[0]).toBe(200);
|
|
124
|
+
let counts = Stdlib_Option.map(Stdlib_JSON.Decode.object(match[1]), o => [
|
|
125
|
+
Stdlib_Option.getOr(Stdlib_Option.flatMap(o["successful"], Stdlib_JSON.Decode.array), []).length,
|
|
126
|
+
Stdlib_Option.getOr(Stdlib_Option.flatMap(o["failed"], Stdlib_JSON.Decode.array), []).length
|
|
127
|
+
]);
|
|
128
|
+
globalThis.expect(counts).toEqual([
|
|
129
|
+
1,
|
|
130
|
+
2
|
|
131
|
+
]);
|
|
132
|
+
globalThis.expect(sentFrames.contents.length).toBe(2);
|
|
133
|
+
let data = sentFrames.contents[1];
|
|
134
|
+
globalThis.expect(frameField(data, "id")).toEqual("sub-p");
|
|
135
|
+
globalThis.expect(frameField(data, "event")).toEqual(`{"userId":"u1"}`);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
sentFrames,
|
|
142
|
+
makeConn,
|
|
143
|
+
parseFrame,
|
|
144
|
+
frameField,
|
|
145
|
+
descriptor,
|
|
146
|
+
}
|
|
147
|
+
/* Not a pure module */
|