@reventlessdev/reventless-local 3.0.0-alpha.318 → 3.0.0-alpha.319
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 +8 -0
- package/package.json +8 -8
- package/src/LocalPlatformRegistry.res +7 -1
- package/src/LocalPlatformRegistry.res.mjs +7 -5
- package/src/Platform.res +3 -0
- package/src/Platform.res.mjs +2 -2
- package/src/adapter/LocalTapLines.res +2 -0
- package/src/adapter/LocalTapLines.res.mjs +4 -1
- package/src/adapter/ProjectionCheckpoint.res.mjs +3 -3
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/LocalAutomationSchedules.res.mjs +1 -1
- package/src/components/LocalAutomationSweep.res.mjs +1 -1
- package/tests/LocalPlatformRegistryTest.res +17 -0
- package/tests/LocalPlatformRegistryTest.res.mjs +14 -2
- package/tests/LocalPlatformStartTest.res.mjs +1 -1
- package/tests/adapter/LocalTapLinesTest.res +25 -0
- package/tests/adapter/LocalTapLinesTest.res.mjs +35 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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.319 (2026-11-01)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **core:** a component's log line carries the ids of the message it is about ([9c26267](https://github.com/ReventlessDev/reventless-core/commit/9c26267ca6d03ff66b8406caa32243e205e29b5a))
|
|
11
|
+
* **local:** the registry entry names the platform API, and a command's outcome names its cause ([51717c4](https://github.com/ReventlessDev/reventless-core/commit/51717c441820c19dc59e595559b037b808ca716a))
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
# 3.0.0-alpha.318 (2026-11-01)
|
|
7
15
|
|
|
8
16
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.319",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
"graphql-yoga": "^5.21.0",
|
|
35
35
|
"sury": "11.0.0",
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
|
-
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.30",
|
|
38
37
|
"@reventlessdev/rescript-effect": "1.0.0-alpha.1",
|
|
38
|
+
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.30",
|
|
39
39
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.13",
|
|
40
|
-
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.24",
|
|
41
40
|
"@reventlessdev/rescript-node": "2.0.0-alpha.25",
|
|
41
|
+
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.24",
|
|
42
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.327",
|
|
43
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.175",
|
|
42
44
|
"@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
|
|
43
|
-
"@reventlessdev/reventless-
|
|
44
|
-
"@reventlessdev/reventless-gwt": "1.0.0-alpha.275",
|
|
45
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.276",
|
|
45
46
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.221",
|
|
46
|
-
"@reventlessdev/reventless-
|
|
47
|
-
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.174",
|
|
47
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.191",
|
|
48
48
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.192",
|
|
49
|
-
"@reventlessdev/reventless-
|
|
49
|
+
"@reventlessdev/reventless-seed": "1.0.0-alpha.40"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.1",
|
|
@@ -30,6 +30,9 @@ type entry = {
|
|
|
30
30
|
// Where the log feed is served, when it is: the platform's recent and live log
|
|
31
31
|
// records. Optional for the same reason as `tapPort`.
|
|
32
32
|
logPort?: int,
|
|
33
|
+
// Where the platform API is served: its own port when the platform serves two
|
|
34
|
+
// APIs, else the same endpoint as `endpoint`. Optional for the same reason.
|
|
35
|
+
platformEndpoint?: string,
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
// Threaded rather than read at each use, so tests need not move the process.
|
|
@@ -115,6 +118,7 @@ let write = (
|
|
|
115
118
|
~loginEndpoint: string,
|
|
116
119
|
~store: store,
|
|
117
120
|
~tapPort: option<int>=?,
|
|
121
|
+
~platformEndpoint: option<string>=?,
|
|
118
122
|
~pid: int=NodeProcess.pid,
|
|
119
123
|
~cwd: string=NodeProcess.cwd(),
|
|
120
124
|
): string => {
|
|
@@ -128,6 +132,7 @@ let write = (
|
|
|
128
132
|
store,
|
|
129
133
|
startedAt: Date.make()->Date.toISOString,
|
|
130
134
|
?tapPort,
|
|
135
|
+
?platformEndpoint,
|
|
131
136
|
}
|
|
132
137
|
try {
|
|
133
138
|
NodeFs.mkdirSync(runningDir(~cwd, ()), {recursive: true})
|
|
@@ -187,8 +192,9 @@ let register = (
|
|
|
187
192
|
~loginEndpoint: string,
|
|
188
193
|
~store: store,
|
|
189
194
|
~tapPort: option<int>=?,
|
|
195
|
+
~platformEndpoint: option<string>=?,
|
|
190
196
|
): unit => {
|
|
191
|
-
let path = write(~port, ~endpoint, ~loginEndpoint, ~store, ~tapPort?)
|
|
197
|
+
let path = write(~port, ~endpoint, ~loginEndpoint, ~store, ~tapPort?, ~platformEndpoint?)
|
|
192
198
|
NodeProcess.onExit(_ => removeQuietly(path))
|
|
193
199
|
NodeProcess.onSignal(SIGINT, () => {
|
|
194
200
|
removeQuietly(path)
|
|
@@ -23,7 +23,8 @@ let entrySchema = Sury.$schema(s => ({
|
|
|
23
23
|
store: s.m(storeSchema),
|
|
24
24
|
startedAt: s.m(Sury.string),
|
|
25
25
|
tapPort: s.m(Sury.$option(Sury.int32)),
|
|
26
|
-
logPort: s.m(Sury.$option(Sury.int32))
|
|
26
|
+
logPort: s.m(Sury.$option(Sury.int32)),
|
|
27
|
+
platformEndpoint: s.m(Sury.$option(Sury.string))
|
|
27
28
|
}));
|
|
28
29
|
|
|
29
30
|
function runningDir(cwdOpt, param) {
|
|
@@ -110,7 +111,7 @@ function list(cwdOpt, param) {
|
|
|
110
111
|
}).toSorted((a, b) => Primitive_int.compare(a.port, b.port));
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
function write(port, endpoint, loginEndpoint, store, tapPort, pidOpt, cwdOpt) {
|
|
114
|
+
function write(port, endpoint, loginEndpoint, store, tapPort, platformEndpoint, pidOpt, cwdOpt) {
|
|
114
115
|
let pid = pidOpt !== undefined ? pidOpt : process.pid;
|
|
115
116
|
let cwd = cwdOpt !== undefined ? cwdOpt : process.cwd();
|
|
116
117
|
let path = entryPath(port, cwd);
|
|
@@ -124,7 +125,8 @@ function write(port, endpoint, loginEndpoint, store, tapPort, pidOpt, cwdOpt) {
|
|
|
124
125
|
loginEndpoint: loginEndpoint,
|
|
125
126
|
store: store,
|
|
126
127
|
startedAt: entry_startedAt,
|
|
127
|
-
tapPort: tapPort
|
|
128
|
+
tapPort: tapPort,
|
|
129
|
+
platformEndpoint: platformEndpoint
|
|
128
130
|
};
|
|
129
131
|
try {
|
|
130
132
|
Nodefs.mkdirSync(runningDir(cwd, undefined), {
|
|
@@ -169,8 +171,8 @@ function publishLogPort(port, logPort, cwdOpt) {
|
|
|
169
171
|
}
|
|
170
172
|
}
|
|
171
173
|
|
|
172
|
-
function register(port, endpoint, loginEndpoint, store, tapPort) {
|
|
173
|
-
let path = write(port, endpoint, loginEndpoint, store, tapPort, undefined, undefined);
|
|
174
|
+
function register(port, endpoint, loginEndpoint, store, tapPort, platformEndpoint) {
|
|
175
|
+
let path = write(port, endpoint, loginEndpoint, store, tapPort, platformEndpoint, undefined, undefined);
|
|
174
176
|
process.on("exit", param => removeQuietly(path));
|
|
175
177
|
process.on("SIGINT", () => {
|
|
176
178
|
removeQuietly(path);
|
package/src/Platform.res
CHANGED
|
@@ -1422,6 +1422,9 @@ module MakeWithConfig = (
|
|
|
1422
1422
|
~endpoint=`http://localhost:${domainPort->Int.toString}/graphql`,
|
|
1423
1423
|
~loginEndpoint=`http://localhost:${domainPort->Int.toString}/__inmemory/login`,
|
|
1424
1424
|
~store={kind, ?path},
|
|
1425
|
+
~platformEndpoint=Config.splitApi
|
|
1426
|
+
? `http://localhost:${platformPort->Int.toString}/graphql`
|
|
1427
|
+
: `http://localhost:${domainPort->Int.toString}/graphql`,
|
|
1425
1428
|
)
|
|
1426
1429
|
// After the register, and it fills the tap port in once the socket is bound:
|
|
1427
1430
|
// the port is ephemeral by default, so it is not known until `listen` calls
|
package/src/Platform.res.mjs
CHANGED
|
@@ -1176,7 +1176,7 @@ function MakeWithConfig(Config) {
|
|
|
1176
1176
|
LocalPlatformRegistry$ReventlessLocal.register(domainPort, `http://localhost:` + domainPort.toString() + `/graphql`, `http://localhost:` + domainPort.toString() + `/__inmemory/login`, {
|
|
1177
1177
|
kind: match[0],
|
|
1178
1178
|
path: match[1]
|
|
1179
|
-
}, undefined);
|
|
1179
|
+
}, undefined, Config.splitApi ? `http://localhost:` + platformPort.toString() + `/graphql` : `http://localhost:` + domainPort.toString() + `/graphql`);
|
|
1180
1180
|
LocalEventTap$ReventlessLocal.start(undefined, tapPort => LocalPlatformRegistry$ReventlessLocal.publishTapPort(domainPort, tapPort, undefined), undefined);
|
|
1181
1181
|
LocalLogFeed$ReventlessLocal.start(undefined, logPort => LocalPlatformRegistry$ReventlessLocal.publishLogPort(domainPort, logPort, undefined), undefined);
|
|
1182
1182
|
Plugin_Helpers$ReventlessCore.firePlatformDeployedHook({
|
|
@@ -2973,7 +2973,7 @@ function Make($star) {
|
|
|
2973
2973
|
LocalPlatformRegistry$ReventlessLocal.register(domainPort, `http://localhost:` + domainPort.toString() + `/graphql`, `http://localhost:` + domainPort.toString() + `/__inmemory/login`, {
|
|
2974
2974
|
kind: match[0],
|
|
2975
2975
|
path: match[1]
|
|
2976
|
-
}, undefined);
|
|
2976
|
+
}, undefined, `http://localhost:` + platformPort.toString() + `/graphql`);
|
|
2977
2977
|
LocalEventTap$ReventlessLocal.start(undefined, tapPort => LocalPlatformRegistry$ReventlessLocal.publishTapPort(domainPort, tapPort, undefined), undefined);
|
|
2978
2978
|
LocalLogFeed$ReventlessLocal.start(undefined, logPort => LocalPlatformRegistry$ReventlessLocal.publishLogPort(domainPort, logPort, undefined), undefined);
|
|
2979
2979
|
Plugin_Helpers$ReventlessCore.firePlatformDeployedHook({
|
|
@@ -34,6 +34,7 @@ let outcomeLine = (r: Outcome.commandOutcomeReport): string => {
|
|
|
34
34
|
->Array.concat(optional("plugin", plugin))
|
|
35
35
|
->Array.concat([("msgId", str(r.reference))])
|
|
36
36
|
->Array.concat(optional("correlationId", r.correlationId))
|
|
37
|
+
->Array.concat(optional("causationId", r.causationId))
|
|
37
38
|
let body = switch r.outcome {
|
|
38
39
|
| OutcomeAccepted({entityId, eventCount}) =>
|
|
39
40
|
[("outcome", str("accepted"))]
|
|
@@ -69,6 +70,7 @@ let rejectionRecord = (r: Outcome.commandOutcomeReport): option<Logger.record> =
|
|
|
69
70
|
}
|
|
70
71
|
let annotations = Dict.fromArray([("msgId", r.reference)])
|
|
71
72
|
r.correlationId->Option.forEach(id => annotations->Dict.set("correlationId", id))
|
|
73
|
+
r.causationId->Option.forEach(id => annotations->Dict.set("causationId", id))
|
|
72
74
|
Some({
|
|
73
75
|
time: now(),
|
|
74
76
|
level,
|
|
@@ -50,7 +50,7 @@ function outcomeLine(r) {
|
|
|
50
50
|
].concat(optional("plugin", plugin)).concat([[
|
|
51
51
|
"msgId",
|
|
52
52
|
r.reference
|
|
53
|
-
]]).concat(optional("correlationId", r.correlationId));
|
|
53
|
+
]]).concat(optional("correlationId", r.correlationId)).concat(optional("causationId", r.causationId));
|
|
54
54
|
let match = r.outcome;
|
|
55
55
|
let body;
|
|
56
56
|
body = match.TAG === "OutcomeAccepted" ? [[
|
|
@@ -118,6 +118,9 @@ function rejectionRecord(r) {
|
|
|
118
118
|
Stdlib_Option.forEach(r.correlationId, id => {
|
|
119
119
|
annotations["correlationId"] = id;
|
|
120
120
|
});
|
|
121
|
+
Stdlib_Option.forEach(r.causationId, id => {
|
|
122
|
+
annotations["causationId"] = id;
|
|
123
|
+
});
|
|
121
124
|
return {
|
|
122
125
|
time: new Date().toISOString(),
|
|
123
126
|
level: level,
|
|
@@ -273,7 +273,7 @@ async function runCatchup(db, upperBound, dcbUpperBound, handlers) {
|
|
|
273
273
|
if (checkpoint < bound) {
|
|
274
274
|
let rows = missedRows(db, axis, checkpoint, bound);
|
|
275
275
|
if (rows.length !== 0) {
|
|
276
|
-
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, undefined, `catch-up: delivering ` + rows.length.toString() + ` missed ` + axisLabel(axis) + ` event(s) (positions ` + checkpoint.toString() + `..` + bound.toString() + `] to ` + name));
|
|
276
|
+
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, undefined, undefined, `catch-up: delivering ` + rows.length.toString() + ` missed ` + axisLabel(axis) + ` event(s) (positions ` + checkpoint.toString() + `..` + bound.toString() + `] to ` + name));
|
|
277
277
|
}
|
|
278
278
|
for (let j = 0, j_finish = rows.length; j < j_finish; ++j) {
|
|
279
279
|
let match$2 = rows[j];
|
|
@@ -283,10 +283,10 @@ async function runCatchup(db, upperBound, dcbUpperBound, handlers) {
|
|
|
283
283
|
try {
|
|
284
284
|
await handler(envelope, undefined);
|
|
285
285
|
} catch (exn) {
|
|
286
|
-
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `catch-up: ` + name + ` failed on stored ` + axisLabel(axis) + ` event at position ` + pos.toString() + ` — skipped`));
|
|
286
|
+
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, undefined, `catch-up: ` + name + ` failed on stored ` + axisLabel(axis) + ` event at position ` + pos.toString() + ` — skipped`));
|
|
287
287
|
}
|
|
288
288
|
} else {
|
|
289
|
-
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `catch-up: unreadable stored ` + axisLabel(axis) + ` event at position ` + pos.toString() + ` — skipped`));
|
|
289
|
+
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, undefined, `catch-up: unreadable stored ` + axisLabel(axis) + ` event at position ` + pos.toString() + ` — skipped`));
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
}
|
|
@@ -59,7 +59,7 @@ function Make(Bus) {
|
|
|
59
59
|
return await ops.tick(key, at);
|
|
60
60
|
});
|
|
61
61
|
Stdlib_Promise.$$catch(TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(component)).then(ops => ops.reconcileSchedules()), exn => {
|
|
62
|
-
Effect.runSync(EffectLogger$ReventlessCore.logError(`AutomationSlice(` + Spec.name + `)`, undefined, `the schedules could not be reconciled: ` + Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown")));
|
|
62
|
+
Effect.runSync(EffectLogger$ReventlessCore.logError(`AutomationSlice(` + Spec.name + `)`, undefined, undefined, `the schedules could not be reconciled: ` + Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown")));
|
|
63
63
|
return Promise.resolve();
|
|
64
64
|
});
|
|
65
65
|
return component;
|
|
@@ -18,7 +18,7 @@ function timerName(slice, key) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function tickFailed(slice, key, message) {
|
|
21
|
-
Effect.runSync(EffectLogger$ReventlessCore.logError("LocalAutomationSchedules", undefined, `tick ` + key + ` of ` + slice + ` failed: ` + message));
|
|
21
|
+
Effect.runSync(EffectLogger$ReventlessCore.logError("LocalAutomationSchedules", undefined, undefined, `tick ` + key + ` of ` + slice + ` failed: ` + message));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function register(slice, tick) {
|
|
@@ -40,7 +40,7 @@ async function sweepOnce() {
|
|
|
40
40
|
} catch (raw_exn) {
|
|
41
41
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
42
42
|
let message = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown");
|
|
43
|
-
return Effect.runSync(EffectLogger$ReventlessCore.logError("LocalAutomationSweep", undefined, `sweep of ` + param[0] + ` failed: ` + message));
|
|
43
|
+
return Effect.runSync(EffectLogger$ReventlessCore.logError("LocalAutomationSweep", undefined, undefined, `sweep of ` + param[0] + ` failed: ` + message));
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
running.contents = false;
|
|
@@ -199,4 +199,21 @@ describe("LocalPlatformRegistry.tapPort", () => {
|
|
|
199
199
|
|
|
200
200
|
expect(LocalPlatformRegistry.list(~cwd, ())->Array.map(e => e.tapPort))->toEqual([Some(4100)])
|
|
201
201
|
})
|
|
202
|
+
|
|
203
|
+
testSync("names where the platform API is served, and keeps it when a port is published", () => {
|
|
204
|
+
let cwd = tempRoot()
|
|
205
|
+
let _ = LocalPlatformRegistry.write(
|
|
206
|
+
~port=4000,
|
|
207
|
+
~endpoint="http://localhost:4000/graphql",
|
|
208
|
+
~loginEndpoint="http://localhost:4000/__inmemory/login",
|
|
209
|
+
~store=memoryStore,
|
|
210
|
+
~platformEndpoint="http://localhost:4001/graphql",
|
|
211
|
+
~cwd,
|
|
212
|
+
)
|
|
213
|
+
LocalPlatformRegistry.publishTapPort(~port=4000, ~tapPort=4100, ~cwd)
|
|
214
|
+
|
|
215
|
+
expect(
|
|
216
|
+
LocalPlatformRegistry.list(~cwd, ())->Array.map(e => (e.platformEndpoint, e.tapPort)),
|
|
217
|
+
)->toEqual([(Some("http://localhost:4001/graphql"), Some(4100))])
|
|
218
|
+
})
|
|
202
219
|
})
|
|
@@ -24,7 +24,7 @@ let memoryStore = {
|
|
|
24
24
|
|
|
25
25
|
function writeAt(cwd, port, store, pidOpt) {
|
|
26
26
|
let pid = pidOpt !== undefined ? pidOpt : process.pid;
|
|
27
|
-
return LocalPlatformRegistry$ReventlessLocal.write(port, `http://localhost:` + port.toString() + `/graphql`, `http://localhost:` + port.toString() + `/__inmemory/login`, store, undefined, pid, cwd);
|
|
27
|
+
return LocalPlatformRegistry$ReventlessLocal.write(port, `http://localhost:` + port.toString() + `/graphql`, `http://localhost:` + port.toString() + `/__inmemory/login`, store, undefined, undefined, pid, cwd);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
globalThis.describe("LocalPlatformRegistry", () => {
|
|
@@ -173,9 +173,21 @@ globalThis.describe("LocalPlatformRegistry.tapPort", () => {
|
|
|
173
173
|
globalThis.test("ignores a platform that never published an entry", () => LocalPlatformRegistry$ReventlessLocal.publishTapPort(4000, 1, tempRoot()));
|
|
174
174
|
globalThis.test("round-trips the port a platform serving a tap publishes", () => {
|
|
175
175
|
let cwd = tempRoot();
|
|
176
|
-
LocalPlatformRegistry$ReventlessLocal.write(4000, "http://localhost:4000/graphql", "http://localhost:4000/__inmemory/login", memoryStore, 4100, undefined, cwd);
|
|
176
|
+
LocalPlatformRegistry$ReventlessLocal.write(4000, "http://localhost:4000/graphql", "http://localhost:4000/__inmemory/login", memoryStore, 4100, undefined, undefined, cwd);
|
|
177
177
|
globalThis.expect(LocalPlatformRegistry$ReventlessLocal.list(cwd, undefined).map(e => e.tapPort)).toEqual([4100]);
|
|
178
178
|
});
|
|
179
|
+
globalThis.test("names where the platform API is served, and keeps it when a port is published", () => {
|
|
180
|
+
let cwd = tempRoot();
|
|
181
|
+
LocalPlatformRegistry$ReventlessLocal.write(4000, "http://localhost:4000/graphql", "http://localhost:4000/__inmemory/login", memoryStore, undefined, "http://localhost:4001/graphql", undefined, cwd);
|
|
182
|
+
LocalPlatformRegistry$ReventlessLocal.publishTapPort(4000, 4100, cwd);
|
|
183
|
+
globalThis.expect(LocalPlatformRegistry$ReventlessLocal.list(cwd, undefined).map(e => [
|
|
184
|
+
e.platformEndpoint,
|
|
185
|
+
e.tapPort
|
|
186
|
+
])).toEqual([[
|
|
187
|
+
"http://localhost:4001/graphql",
|
|
188
|
+
4100
|
|
189
|
+
]]);
|
|
190
|
+
});
|
|
179
191
|
});
|
|
180
192
|
|
|
181
193
|
let deadPid = 2147483646;
|
|
@@ -28,7 +28,7 @@ let memoryStore = {
|
|
|
28
28
|
|
|
29
29
|
function writeAt(cwd, port, store, pidOpt) {
|
|
30
30
|
let pid = pidOpt !== undefined ? pidOpt : process.pid;
|
|
31
|
-
return LocalPlatformRegistry$ReventlessLocal.write(port, `http://localhost:` + port.toString() + `/graphql`, `http://localhost:` + port.toString() + `/__inmemory/login`, store, undefined, pid, cwd);
|
|
31
|
+
return LocalPlatformRegistry$ReventlessLocal.write(port, `http://localhost:` + port.toString() + `/graphql`, `http://localhost:` + port.toString() + `/__inmemory/login`, store, undefined, undefined, pid, cwd);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function withoutBypass(f) {
|
|
@@ -180,3 +180,28 @@ describe("LocalTapLines", () => {
|
|
|
180
180
|
await disconnect(tap)
|
|
181
181
|
})
|
|
182
182
|
})
|
|
183
|
+
|
|
184
|
+
describe("the outcome line names its cause", () => {
|
|
185
|
+
testSync("a command an automation sent says which event it acted on", () => {
|
|
186
|
+
let line = LocalTapLines.outcomeLine({
|
|
187
|
+
component: "Notice",
|
|
188
|
+
reference: "cmd-2",
|
|
189
|
+
correlationId: "flow-1",
|
|
190
|
+
causationId: "evt-7",
|
|
191
|
+
outcome: OutcomeAccepted({entityId: Some("n-1"), eventCount: 1}),
|
|
192
|
+
})
|
|
193
|
+
let d = line->JSON.parseOrThrow->JSON.Decode.object->Option.getOrThrow
|
|
194
|
+
expect(d->Dict.get("causationId"))->toEqual(Some(JSON.String("evt-7")))
|
|
195
|
+
expect(d->Dict.get("msgId"))->toEqual(Some(JSON.String("cmd-2")))
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
testSync("a command sent from outside has none", () => {
|
|
199
|
+
let line = LocalTapLines.outcomeLine({
|
|
200
|
+
component: "Order",
|
|
201
|
+
reference: "cmd-1",
|
|
202
|
+
outcome: OutcomeAccepted({entityId: None, eventCount: 1}),
|
|
203
|
+
})
|
|
204
|
+
let d = line->JSON.parseOrThrow->JSON.Decode.object->Option.getOrThrow
|
|
205
|
+
expect(d->Dict.get("causationId"))->toEqual(None)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
@@ -93,11 +93,11 @@ async function disconnect(tap) {
|
|
|
93
93
|
globalThis.describe("LocalTapLines", () => {
|
|
94
94
|
globalThis.test("an accepted and a rejected command each appear once on the tap", async () => {
|
|
95
95
|
let tap = await connectTap();
|
|
96
|
-
CommandTopic_Helpers$ReventlessCore.reportAccepted("Order", "flow-1", "m-1", {
|
|
96
|
+
CommandTopic_Helpers$ReventlessCore.reportAccepted("Order", "flow-1", undefined, "m-1", {
|
|
97
97
|
entityId: "o-1",
|
|
98
98
|
eventCount: 2
|
|
99
99
|
});
|
|
100
|
-
CommandTopic_Helpers$ReventlessCore.reportRejected("Order", "DomainRejection", "flow-1", "m-2", {
|
|
100
|
+
CommandTopic_Helpers$ReventlessCore.reportRejected("Order", "DomainRejection", "flow-1", undefined, "m-2", {
|
|
101
101
|
errorCode: "AlreadyExists",
|
|
102
102
|
errorDetail: `{"name":"Widget"}`
|
|
103
103
|
});
|
|
@@ -181,7 +181,7 @@ globalThis.describe("LocalTapLines", () => {
|
|
|
181
181
|
});
|
|
182
182
|
globalThis.test("an infrastructure failure is kept as an error", async () => {
|
|
183
183
|
let tap = await connectTap();
|
|
184
|
-
CommandTopic_Helpers$ReventlessCore.reportRejected("Order", "InfrastructureFailure", undefined, "m-3", {
|
|
184
|
+
CommandTopic_Helpers$ReventlessCore.reportRejected("Order", "InfrastructureFailure", undefined, undefined, "m-3", {
|
|
185
185
|
errorCode: "AppendFailed",
|
|
186
186
|
errorDetail: "store down"
|
|
187
187
|
});
|
|
@@ -224,6 +224,38 @@ globalThis.describe("LocalTapLines", () => {
|
|
|
224
224
|
});
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
+
globalThis.describe("the outcome line names its cause", () => {
|
|
228
|
+
globalThis.test("a command an automation sent says which event it acted on", () => {
|
|
229
|
+
let line = LocalTapLines$ReventlessLocal.outcomeLine({
|
|
230
|
+
component: "Notice",
|
|
231
|
+
reference: "cmd-2",
|
|
232
|
+
correlationId: "flow-1",
|
|
233
|
+
causationId: "evt-7",
|
|
234
|
+
outcome: {
|
|
235
|
+
TAG: "OutcomeAccepted",
|
|
236
|
+
entityId: "n-1",
|
|
237
|
+
eventCount: 1
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
let d = Stdlib_Option.getOrThrow(Stdlib_JSON.Decode.object(JSON.parse(line)), undefined);
|
|
241
|
+
globalThis.expect(d["causationId"]).toEqual("evt-7");
|
|
242
|
+
globalThis.expect(d["msgId"]).toEqual("cmd-2");
|
|
243
|
+
});
|
|
244
|
+
globalThis.test("a command sent from outside has none", () => {
|
|
245
|
+
let line = LocalTapLines$ReventlessLocal.outcomeLine({
|
|
246
|
+
component: "Order",
|
|
247
|
+
reference: "cmd-1",
|
|
248
|
+
outcome: {
|
|
249
|
+
TAG: "OutcomeAccepted",
|
|
250
|
+
entityId: undefined,
|
|
251
|
+
eventCount: 1
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
let d = Stdlib_Option.getOrThrow(Stdlib_JSON.Decode.object(JSON.parse(line)), undefined);
|
|
255
|
+
globalThis.expect(d["causationId"]).toEqual(undefined);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
227
259
|
let Outcome;
|
|
228
260
|
|
|
229
261
|
let ViewChange;
|