@moltzap/client 2026.511.0 → 2026.518.0
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/dist/auth.d.ts +12 -6
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +67 -29
- package/dist/auth.js.map +1 -1
- package/dist/auth.test.js +113 -76
- package/dist/auth.test.js.map +1 -1
- package/dist/channel-core-context.test.d.ts +2 -0
- package/dist/channel-core-context.test.d.ts.map +1 -0
- package/dist/channel-core-context.test.js +334 -0
- package/dist/channel-core-context.test.js.map +1 -0
- package/dist/channel-core-dispatch.test.d.ts +2 -0
- package/dist/channel-core-dispatch.test.d.ts.map +1 -0
- package/dist/channel-core-dispatch.test.js +550 -0
- package/dist/channel-core-dispatch.test.js.map +1 -0
- package/dist/channel-core-enrichment.d.ts +8 -0
- package/dist/channel-core-enrichment.d.ts.map +1 -0
- package/dist/channel-core-enrichment.js +134 -0
- package/dist/channel-core-enrichment.js.map +1 -0
- package/dist/channel-core-errors.d.ts +20 -0
- package/dist/channel-core-errors.d.ts.map +1 -0
- package/dist/channel-core-errors.js +12 -0
- package/dist/channel-core-errors.js.map +1 -0
- package/dist/channel-core-test-support.d.ts +48 -0
- package/dist/channel-core-test-support.d.ts.map +1 -0
- package/dist/channel-core-test-support.js +111 -0
- package/dist/channel-core-test-support.js.map +1 -0
- package/dist/channel-core.d.ts +30 -7
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +214 -237
- package/dist/channel-core.js.map +1 -1
- package/dist/channel-core.test.js +216 -1082
- package/dist/channel-core.test.js.map +1 -1
- package/dist/cli/__tests__/cli-multi-agent.int.test.js +129 -23
- package/dist/cli/__tests__/cli-multi-agent.int.test.js.map +1 -1
- package/dist/cli/commands/conversations-v2.test.js +64 -54
- package/dist/cli/commands/conversations-v2.test.js.map +1 -1
- package/dist/cli/commands/conversations.d.ts +5 -5
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +43 -36
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/messages.d.ts +4 -4
- package/dist/cli/commands/messages.d.ts.map +1 -1
- package/dist/cli/commands/messages.js +4 -4
- package/dist/cli/commands/messages.js.map +1 -1
- package/dist/cli/commands/messages.test.js +61 -49
- package/dist/cli/commands/messages.test.js.map +1 -1
- package/dist/cli/commands/ping.d.ts +0 -9
- package/dist/cli/commands/ping.d.ts.map +1 -1
- package/dist/cli/commands/ping.js +1 -1
- package/dist/cli/commands/ping.js.map +1 -1
- package/dist/cli/commands/presence.d.ts +1 -1
- package/dist/cli/commands/presence.js +1 -1
- package/dist/cli/commands/register.d.ts +2 -2
- package/dist/cli/commands/register.d.ts.map +1 -1
- package/dist/cli/commands/register.js +98 -77
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/register.test.js +63 -39
- package/dist/cli/commands/register.test.js.map +1 -1
- package/dist/cli/commands/send.d.ts +6 -6
- package/dist/cli/commands/send.js +6 -6
- package/dist/cli/commands/send.test.js +32 -28
- package/dist/cli/commands/send.test.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +3 -4
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/config.d.ts +0 -2
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +86 -76
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +80 -42
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/index.d.ts +1 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +50 -113
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/process-argv.d.ts +2 -0
- package/dist/cli/process-argv.d.ts.map +1 -0
- package/dist/cli/process-argv.js +2 -0
- package/dist/cli/process-argv.js.map +1 -0
- package/dist/cli/profile.d.ts +3 -10
- package/dist/cli/profile.d.ts.map +1 -1
- package/dist/cli/profile.js +105 -104
- package/dist/cli/profile.js.map +1 -1
- package/dist/cli/profile.test.js +338 -232
- package/dist/cli/profile.test.js.map +1 -1
- package/dist/cli/runtime.d.ts +5 -17
- package/dist/cli/runtime.d.ts.map +1 -1
- package/dist/cli/runtime.js +6 -59
- package/dist/cli/runtime.js.map +1 -1
- package/dist/cli/socket-client.d.ts +10 -15
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +74 -89
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +9 -5
- package/dist/cli/transport.d.ts.map +1 -1
- package/dist/cli/transport.js +45 -42
- package/dist/cli/transport.js.map +1 -1
- package/dist/cli/transport.test.js +227 -133
- package/dist/cli/transport.test.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/local-daemon-rpc.d.ts +13 -0
- package/dist/local-daemon-rpc.d.ts.map +1 -0
- package/dist/local-daemon-rpc.js +17 -0
- package/dist/local-daemon-rpc.js.map +1 -0
- package/dist/local-paths.d.ts +7 -0
- package/dist/local-paths.d.ts.map +1 -0
- package/dist/local-paths.js +20 -0
- package/dist/local-paths.js.map +1 -0
- package/dist/runtime/close-info.d.ts +3 -12
- package/dist/runtime/close-info.d.ts.map +1 -1
- package/dist/runtime/close-info.js +28 -39
- package/dist/runtime/close-info.js.map +1 -1
- package/dist/runtime/close-info.test.js +77 -70
- package/dist/runtime/close-info.test.js.map +1 -1
- package/dist/runtime/errors.test.js +42 -28
- package/dist/runtime/errors.test.js.map +1 -1
- package/dist/runtime/frame.d.ts +4 -31
- package/dist/runtime/frame.d.ts.map +1 -1
- package/dist/runtime/frame.js +7 -116
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/frame.test.js +41 -54
- package/dist/runtime/frame.test.js.map +1 -1
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +7 -11
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/local-history.d.ts +67 -0
- package/dist/runtime/local-history.d.ts.map +1 -0
- package/dist/runtime/local-history.js +77 -0
- package/dist/runtime/local-history.js.map +1 -0
- package/dist/runtime/local-service-commands.d.ts +19 -0
- package/dist/runtime/local-service-commands.d.ts.map +1 -1
- package/dist/runtime/local-service-commands.js +23 -0
- package/dist/runtime/local-service-commands.js.map +1 -1
- package/dist/runtime/local-socket-server.d.ts +20 -0
- package/dist/runtime/local-socket-server.d.ts.map +1 -0
- package/dist/runtime/local-socket-server.js +94 -0
- package/dist/runtime/local-socket-server.js.map +1 -0
- package/dist/runtime/refs.d.ts +1 -1
- package/dist/runtime/refs.js +1 -1
- package/dist/runtime/runtime.test.js +29 -21
- package/dist/runtime/runtime.test.js.map +1 -1
- package/dist/runtime/service-helpers.d.ts +32 -0
- package/dist/runtime/service-helpers.d.ts.map +1 -0
- package/dist/runtime/service-helpers.js +85 -0
- package/dist/runtime/service-helpers.js.map +1 -0
- package/dist/runtime/subscribers.d.ts +13 -17
- package/dist/runtime/subscribers.d.ts.map +1 -1
- package/dist/runtime/subscribers.js +37 -32
- package/dist/runtime/subscribers.js.map +1 -1
- package/dist/runtime/subscribers.test.js +139 -131
- package/dist/runtime/subscribers.test.js.map +1 -1
- package/dist/service-socket-path.test.d.ts +2 -0
- package/dist/service-socket-path.test.d.ts.map +1 -0
- package/dist/service-socket-path.test.js +87 -0
- package/dist/service-socket-path.test.js.map +1 -0
- package/dist/service.d.ts +46 -20
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +243 -340
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +650 -571
- package/dist/service.test.js.map +1 -1
- package/dist/test/index.d.ts +30 -14
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +25 -21
- package/dist/test/index.js.map +1 -1
- package/dist/test-utils/channel-service-fixture.d.ts +1 -0
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +139 -98
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
- package/dist/test-utils/conformance-adapter.js +145 -124
- package/dist/test-utils/conformance-adapter.js.map +1 -1
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +3 -3
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/test-utils/index.d.ts +25 -0
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +25 -8
- package/dist/test-utils/index.js.map +1 -1
- package/dist/ws-client-test-support.d.ts +180 -0
- package/dist/ws-client-test-support.d.ts.map +1 -0
- package/dist/ws-client-test-support.js +399 -0
- package/dist/ws-client-test-support.js.map +1 -0
- package/dist/ws-client.d.ts +59 -32
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +272 -321
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.js +388 -800
- package/dist/ws-client.test.js.map +1 -1
- package/package.json +14 -10
package/dist/cli/transport.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* the wire. Decides between the singleton daemon (Unix socket) and a
|
|
4
4
|
* direct WebSocket per invocation.
|
|
5
5
|
*
|
|
6
|
-
* This file is the `--as
|
|
6
|
+
* This file is the `--as <apiKey>` branch point (spec sbd#177 rev 3 §5.1,
|
|
7
7
|
* Invariant §4.2). Command handlers pull `Transport` from Effect context;
|
|
8
8
|
* they do NOT open sockets or construct clients themselves. The kind of
|
|
9
9
|
* transport in effect is decided once at CLI boot by {@link makeTransportLayer}
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* that provides a recording `Transport`; unit tests provide `Transport`
|
|
14
14
|
* directly via `Effect.provideService`.
|
|
15
15
|
*/
|
|
16
|
-
import * as
|
|
17
|
-
import { Config, Context, Data, Effect, Layer, Option, Scope } from "effect";
|
|
16
|
+
import * as NodeSocket from "@effect/platform-node/NodeSocket";
|
|
17
|
+
import { Config, Context, Data, Effect, Layer, Option, Redacted, Scope, } from "effect";
|
|
18
18
|
import { MoltZapService } from "../service.js";
|
|
19
19
|
import { NotConnectedError, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
|
|
20
20
|
import { MoltZapWsClient } from "../ws-client.js";
|
|
@@ -44,16 +44,33 @@ export class TransportDecodeError extends Data.TaggedError("TransportDecodeError
|
|
|
44
44
|
*/
|
|
45
45
|
export class TransportConfigError extends Data.TaggedError("TransportConfigError") {
|
|
46
46
|
}
|
|
47
|
+
// ─── Transport surface ─────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Kind of transport currently in use. Observable for logs and tests.
|
|
50
|
+
* Commands never branch on kind.
|
|
51
|
+
*/
|
|
52
|
+
export const DIRECT_TRANSPORT_KIND = "direct";
|
|
47
53
|
export const Transport = Context.GenericTag("moltzap/cli/Transport");
|
|
48
54
|
const DEFAULT_SERVER_URL = "wss://api.moltzap.xyz";
|
|
49
55
|
const DAEMON_TIMEOUT_MS = 10_000;
|
|
50
56
|
const JSON_RPC_SERVER_ERROR_CODE = -32000;
|
|
51
57
|
const PROBE_DAEMON_TIMEOUT_MS = 250;
|
|
52
58
|
const EnvServerUrl = Config.option(Config.string("MOLTZAP_SERVER_URL"));
|
|
53
|
-
const EnvApiKey = Config.option(Config.
|
|
59
|
+
const EnvApiKey = Config.option(Config.redacted("MOLTZAP_API_KEY"));
|
|
54
60
|
const loadEnvServerUrl = EnvServerUrl.pipe(Effect.map(Option.getOrUndefined), Effect.orElseSucceed(() => undefined));
|
|
55
61
|
const loadEnvServerUrlWithDefault = loadEnvServerUrl.pipe(Effect.map((serverUrl) => serverUrl ?? DEFAULT_SERVER_URL));
|
|
56
|
-
const loadEnvApiKey = EnvApiKey.pipe(Effect.map(
|
|
62
|
+
const loadEnvApiKey = EnvApiKey.pipe(Effect.map((value) => Option.match(value, {
|
|
63
|
+
onNone: () => undefined,
|
|
64
|
+
onSome: Redacted.value,
|
|
65
|
+
})), Effect.orElseSucceed(() => undefined));
|
|
66
|
+
const selectDirectTransportKey = (decision, options) => {
|
|
67
|
+
const keyByReason = {
|
|
68
|
+
"as-flag": options.impersonateKey,
|
|
69
|
+
"env-fallback": options.envFallbackKey,
|
|
70
|
+
profile: options.profileKey,
|
|
71
|
+
};
|
|
72
|
+
return keyByReason[decision.reason];
|
|
73
|
+
};
|
|
57
74
|
/**
|
|
58
75
|
* Decision function — Effect-returning because the env-fallback branch
|
|
59
76
|
* may invoke the `probeDaemon` thunk. The as-flag branch short-circuits
|
|
@@ -79,7 +96,7 @@ export const decideTransport = (options) => Effect.gen(function* () {
|
|
|
79
96
|
// Default: daemon branch.
|
|
80
97
|
const socketPath = options.socketPath ?? MoltZapService.SOCKET_PATH;
|
|
81
98
|
return { _tag: "UseDaemon", socketPath };
|
|
82
|
-
});
|
|
99
|
+
}).pipe(Effect.withSpan("decideTransport"));
|
|
83
100
|
// Map daemon-branch Error to TransportError tags. The daemon socket client
|
|
84
101
|
// surfaces a generic Error; we re-tag at the boundary so command handlers
|
|
85
102
|
// can discriminate.
|
|
@@ -112,7 +129,10 @@ const makeDaemonTransport = (socketPath) => ({
|
|
|
112
129
|
});
|
|
113
130
|
// Map ws-client errors (NotConnectedError | RpcTimeoutError | RpcServerError)
|
|
114
131
|
// to TransportError tags.
|
|
115
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Exported for decoder-fixture tests only (sbd#198).
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
116
136
|
export const tagWsError = (method, err) => {
|
|
117
137
|
if (err instanceof NotConnectedError) {
|
|
118
138
|
return new ServiceUnreachableError({
|
|
@@ -136,6 +156,14 @@ export const tagWsError = (method, err) => {
|
|
|
136
156
|
}
|
|
137
157
|
return new TransportDecodeError({ method, cause: err });
|
|
138
158
|
};
|
|
159
|
+
function mapDirectRpcError(method) {
|
|
160
|
+
return (err) => tagWsError(method, err);
|
|
161
|
+
}
|
|
162
|
+
function sendDirectRpc(connectOnce, definition, params) {
|
|
163
|
+
return connectOnce.pipe(Effect.flatMap((client) => client
|
|
164
|
+
.sendRpc(definition, params)
|
|
165
|
+
.pipe(Effect.mapError(mapDirectRpcError(definition.name)))));
|
|
166
|
+
}
|
|
139
167
|
/**
|
|
140
168
|
* Build a direct-WebSocket transport. Requires `Scope` so `Layer.scoped`
|
|
141
169
|
* can install a finalizer via `Effect.addFinalizer`. The finalizer fires on
|
|
@@ -180,10 +208,8 @@ const makeDirectTransport = (serverUrl, agentKey) => Effect.gen(function* () {
|
|
|
180
208
|
return c;
|
|
181
209
|
}));
|
|
182
210
|
return {
|
|
183
|
-
kind:
|
|
184
|
-
rpc: (definition, params) => connectOnce
|
|
185
|
-
.sendRpc(definition, params)
|
|
186
|
-
.pipe(Effect.mapError((e) => tagWsError(definition.name, e))))),
|
|
211
|
+
kind: DIRECT_TRANSPORT_KIND,
|
|
212
|
+
rpc: (definition, params) => sendDirectRpc(connectOnce, definition, params),
|
|
187
213
|
};
|
|
188
214
|
});
|
|
189
215
|
/**
|
|
@@ -206,18 +232,7 @@ export const makeTransportLayer = (options) => Layer.scoped(Transport, Effect.ge
|
|
|
206
232
|
case "UseDaemon":
|
|
207
233
|
return makeDaemonTransport(decision.socketPath);
|
|
208
234
|
case "UseDirect": {
|
|
209
|
-
|
|
210
|
-
switch (decision.reason) {
|
|
211
|
-
case "as-flag":
|
|
212
|
-
key = options.impersonateKey;
|
|
213
|
-
break;
|
|
214
|
-
case "profile":
|
|
215
|
-
key = options.profileKey;
|
|
216
|
-
break;
|
|
217
|
-
case "env-fallback":
|
|
218
|
-
key = options.envFallbackKey;
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
235
|
+
const key = selectDirectTransportKey(decision, options);
|
|
221
236
|
if (key === undefined) {
|
|
222
237
|
return yield* Effect.fail(new TransportConfigError({
|
|
223
238
|
reason: `direct transport (${decision.reason}) requires an apiKey`,
|
|
@@ -232,7 +247,7 @@ export const makeTransportLayer = (options) => Layer.scoped(Transport, Effect.ge
|
|
|
232
247
|
default:
|
|
233
248
|
return absurd(decision);
|
|
234
249
|
}
|
|
235
|
-
}));
|
|
250
|
+
}).pipe(Effect.withSpan("makeTransportLayer")));
|
|
236
251
|
/**
|
|
237
252
|
* Convenience for command handlers: pull the Transport tag and call rpc.
|
|
238
253
|
* Every new subcommand routes through this helper; raw `socket-client.request`
|
|
@@ -241,7 +256,7 @@ export const makeTransportLayer = (options) => Layer.scoped(Transport, Effect.ge
|
|
|
241
256
|
export const rpc = (definition, params) => Effect.flatMap(Transport, (t) => t.rpc(definition, params));
|
|
242
257
|
/**
|
|
243
258
|
* Uniform error-to-exit adapter for subcommand handlers. Catches every error
|
|
244
|
-
* channel, prints `Failed:
|
|
259
|
+
* channel, prints `Failed: <msg>` to stderr, and exits non-zero. Uses the
|
|
245
260
|
* tagged-error `message` field if present, otherwise the `_tag`, otherwise
|
|
246
261
|
* a generic fallback. Shared across every v2 subcommand wrapper so the
|
|
247
262
|
* exit-code contract (Invariant §4.6) has a single implementation.
|
|
@@ -303,7 +318,7 @@ export const resolveTransportInputs = (parsed) => Effect.gen(function* () {
|
|
|
303
318
|
socketPath: MoltZapService.SOCKET_PATH,
|
|
304
319
|
probeDaemon: probeDaemonDefault,
|
|
305
320
|
};
|
|
306
|
-
});
|
|
321
|
+
}).pipe(Effect.withSpan("resolveTransportInputs"));
|
|
307
322
|
/**
|
|
308
323
|
* Default daemon reachability probe: attempt a real connect to the daemon
|
|
309
324
|
* socket and resolve on `connect`. A bare `fs.existsSync` would mis-report
|
|
@@ -312,22 +327,10 @@ export const resolveTransportInputs = (parsed) => Effect.gen(function* () {
|
|
|
312
327
|
* check. 250ms cap keeps boot latency invisible on the common fast-local
|
|
313
328
|
* path and still fails fast when the socket refuses or hangs.
|
|
314
329
|
*/
|
|
315
|
-
const probeDaemonDefault = () => Effect.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return;
|
|
320
|
-
settled = true;
|
|
321
|
-
clearTimeout(timer);
|
|
322
|
-
conn.removeAllListeners();
|
|
323
|
-
conn.destroy();
|
|
324
|
-
resume(Effect.succeed(reachable));
|
|
325
|
-
};
|
|
326
|
-
const conn = net.createConnection(MoltZapService.SOCKET_PATH);
|
|
327
|
-
const timer = setTimeout(() => done(false), PROBE_DAEMON_TIMEOUT_MS);
|
|
328
|
-
conn.once("connect", () => done(true));
|
|
329
|
-
conn.once("error", () => done(false));
|
|
330
|
-
});
|
|
330
|
+
const probeDaemonDefault = () => Effect.scoped(NodeSocket.makeNet({
|
|
331
|
+
path: MoltZapService.SOCKET_PATH,
|
|
332
|
+
openTimeout: `${PROBE_DAEMON_TIMEOUT_MS} millis`,
|
|
333
|
+
}).pipe(Effect.as(true), Effect.catchAll(() => Effect.succeed(false))));
|
|
331
334
|
function absurd(x) {
|
|
332
335
|
throw new Error(`unreachable: ${String(x)}`);
|
|
333
336
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/cli/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/cli/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,168 +4,277 @@
|
|
|
4
4
|
* the E2E fixture (`__tests__/cli-multi-agent.int.test.ts`).
|
|
5
5
|
*/
|
|
6
6
|
import { Cause, Effect, Exit, Option } from "effect";
|
|
7
|
-
import
|
|
7
|
+
import * as fc from "fast-check";
|
|
8
|
+
import { it as effectIt } from "@effect/vitest";
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, vi } from "vitest";
|
|
8
10
|
import { NotConnectedError, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
|
|
9
11
|
import { decideTransport, makeTransportLayer, resolveTransportInputs, tagWsError, Transport, TransportDecodeError, TransportRpcError, ServiceUnreachableError, TransportTimeoutError, } from "./transport.js";
|
|
10
12
|
import { TasksList } from "@moltzap/protocol";
|
|
13
|
+
const it = effectIt.effect;
|
|
14
|
+
const SESSION_NOT_FOUND_CODE = -32001;
|
|
15
|
+
const RPC_TIMEOUT_MS = 15_000;
|
|
16
|
+
const SERVER_URL = "wss://example.test";
|
|
17
|
+
const TEST_SOCKET_PATH = "/var/run/moltzap-test.sock";
|
|
18
|
+
const USE_DIRECT_TAG = "UseDirect";
|
|
19
|
+
const USE_DAEMON_TAG = "UseDaemon";
|
|
20
|
+
const AS_FLAG_REASON = "as-flag";
|
|
21
|
+
const PROFILE_REASON = "profile";
|
|
22
|
+
const ENV_FALLBACK_REASON = "env-fallback";
|
|
23
|
+
const TRANSPORT_RPC_ERROR_TAG = "TransportRpcError";
|
|
24
|
+
const SERVICE_UNREACHABLE_ERROR_TAG = "ServiceUnreachableError";
|
|
25
|
+
const TRANSPORT_DECODE_ERROR_TAG = "TransportDecodeError";
|
|
26
|
+
const SESSION_NOT_FOUND_MESSAGE = "session not found";
|
|
27
|
+
const ITEM_NOT_FOUND_MESSAGE = "item not found";
|
|
28
|
+
const NOT_CONNECTED_MESSAGE = "not connected";
|
|
29
|
+
const UNKNOWN_ERROR_MESSAGE = "some unknown error";
|
|
30
|
+
const IMPERSONATE_KEY = "key-1";
|
|
31
|
+
const PROFILE_KEY = "pk-1";
|
|
32
|
+
const ENV_KEY = "env-key";
|
|
33
|
+
const LEAKED_ENV_KEY = "leaked-key";
|
|
34
|
+
const EXPLICIT_KEY = "explicit-key";
|
|
35
|
+
const OVERRIDE_SERVER_URL = "wss://override.test";
|
|
36
|
+
const DIRECT_TEST_KEY = "test-key";
|
|
37
|
+
const DIRECT_TEST_SERVER_URL = "wss://test.example";
|
|
38
|
+
function makeMockWsClient() {
|
|
39
|
+
return {
|
|
40
|
+
connect: () => Effect.void,
|
|
41
|
+
sendRpc: () => Effect.fail(new RpcServerError({
|
|
42
|
+
code: SESSION_NOT_FOUND_CODE,
|
|
43
|
+
message: ITEM_NOT_FOUND_MESSAGE,
|
|
44
|
+
})),
|
|
45
|
+
close: () => Effect.void,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
11
48
|
/**
|
|
12
49
|
* Module-level mock so transport.ts's `new MoltZapWsClient(...)` call is
|
|
13
50
|
* intercepted for the composed-rpc test below. Existing tests (decideTransport,
|
|
14
51
|
* tagWsError, resolveTransportInputs) do not exercise the ws-client path, so
|
|
15
52
|
* the mock is a no-op for them.
|
|
16
53
|
*/
|
|
17
|
-
vi.mock("../ws-client.js",
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
MoltZapWsClient: vi.fn().mockImplementation(() => ({
|
|
22
|
-
connect: () => effect.Effect.void,
|
|
23
|
-
sendRpc: (_definition, _params) => effect.Effect.fail(new protocol.RpcServerError({
|
|
24
|
-
code: -32001,
|
|
25
|
-
message: "item not found",
|
|
26
|
-
})),
|
|
27
|
-
close: () => effect.Effect.void,
|
|
28
|
-
})),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
54
|
+
vi.mock("../ws-client.js", () => ({
|
|
55
|
+
MoltZapWsClient: vi.fn().mockImplementation(makeMockWsClient),
|
|
56
|
+
}));
|
|
31
57
|
const makeOpts = (over = {}) => ({
|
|
32
|
-
serverUrl:
|
|
58
|
+
serverUrl: SERVER_URL,
|
|
33
59
|
...over,
|
|
34
60
|
});
|
|
61
|
+
const unreachableDaemon = () => Effect.succeed(false);
|
|
62
|
+
const reachableDaemon = () => Effect.succeed(true);
|
|
63
|
+
function countedProbe(counter) {
|
|
64
|
+
return () => Effect.sync(() => {
|
|
65
|
+
counter.count++;
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function useDirectAsFlag() {
|
|
70
|
+
return Effect.gen(function* () {
|
|
71
|
+
const probe = { count: 0 };
|
|
72
|
+
const decision = yield* decideTransport(makeOpts({
|
|
73
|
+
impersonateKey: IMPERSONATE_KEY,
|
|
74
|
+
probeDaemon: countedProbe(probe),
|
|
75
|
+
}));
|
|
76
|
+
expect(decision).toEqual({ _tag: USE_DIRECT_TAG, reason: AS_FLAG_REASON });
|
|
77
|
+
expect(probe.count).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function useDirectProfile() {
|
|
81
|
+
return Effect.gen(function* () {
|
|
82
|
+
const decision = yield* decideTransport(makeOpts({ profileKey: PROFILE_KEY }));
|
|
83
|
+
expect(decision).toEqual({ _tag: USE_DIRECT_TAG, reason: PROFILE_REASON });
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function useDirectEnvFallback() {
|
|
87
|
+
return Effect.gen(function* () {
|
|
88
|
+
const decision = yield* decideTransport(makeOpts({ envFallbackKey: ENV_KEY, probeDaemon: unreachableDaemon }));
|
|
89
|
+
expect(decision).toEqual({
|
|
90
|
+
_tag: USE_DIRECT_TAG,
|
|
91
|
+
reason: ENV_FALLBACK_REASON,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function useDaemonWhenReachable() {
|
|
96
|
+
return Effect.gen(function* () {
|
|
97
|
+
const decision = yield* decideTransport(makeOpts({
|
|
98
|
+
envFallbackKey: ENV_KEY,
|
|
99
|
+
socketPath: TEST_SOCKET_PATH,
|
|
100
|
+
probeDaemon: reachableDaemon,
|
|
101
|
+
}));
|
|
102
|
+
expect(decision._tag).toBe(USE_DAEMON_TAG);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function useDaemonByDefault() {
|
|
106
|
+
return Effect.gen(function* () {
|
|
107
|
+
const decision = yield* decideTransport(makeOpts({ socketPath: TEST_SOCKET_PATH }));
|
|
108
|
+
expect(decision).toEqual({
|
|
109
|
+
_tag: USE_DAEMON_TAG,
|
|
110
|
+
socketPath: TEST_SOCKET_PATH,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function impersonateBypassesProbe() {
|
|
115
|
+
return Effect.gen(function* () {
|
|
116
|
+
const probe = { count: 0 };
|
|
117
|
+
yield* decideTransport(makeOpts({
|
|
118
|
+
impersonateKey: IMPERSONATE_KEY,
|
|
119
|
+
probeDaemon: countedProbe(probe),
|
|
120
|
+
}));
|
|
121
|
+
expect(probe.count).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function expectBypassForKey(key) {
|
|
125
|
+
const probe = { count: 0 };
|
|
126
|
+
const decision = Effect.runSync(decideTransport(makeOpts({
|
|
127
|
+
impersonateKey: key,
|
|
128
|
+
probeDaemon: countedProbe(probe),
|
|
129
|
+
})));
|
|
130
|
+
expect(decision._tag).toBe(USE_DIRECT_TAG);
|
|
131
|
+
expect(probe.count).toBe(0);
|
|
132
|
+
}
|
|
133
|
+
function impersonateBypassesProbeForAnyKey() {
|
|
134
|
+
return Effect.sync(() => {
|
|
135
|
+
fc.assert(fc.property(fc.string({ minLength: 1 }), expectBypassForKey));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
35
138
|
describe("decideTransport", () => {
|
|
36
|
-
const originalEnv = process.env;
|
|
37
139
|
beforeEach(() => {
|
|
38
|
-
|
|
39
|
-
delete process.env.MOLTZAP_API_KEY;
|
|
140
|
+
vi.unstubAllEnvs();
|
|
40
141
|
});
|
|
41
142
|
afterEach(() => {
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
it("returns UseDirect{as-flag} when impersonateKey is set", async () => {
|
|
45
|
-
const probe = { invoked: 0 };
|
|
46
|
-
const decision = await Effect.runPromise(decideTransport(makeOpts({
|
|
47
|
-
impersonateKey: "key-1",
|
|
48
|
-
probeDaemon: () => Effect.sync(() => {
|
|
49
|
-
probe.invoked++;
|
|
50
|
-
return true;
|
|
51
|
-
}),
|
|
52
|
-
})));
|
|
53
|
-
expect(decision).toEqual({ _tag: "UseDirect", reason: "as-flag" });
|
|
54
|
-
expect(probe.invoked).toBe(0);
|
|
55
|
-
});
|
|
56
|
-
it("returns UseDirect{profile} when profileKey set and no --as", async () => {
|
|
57
|
-
const decision = await Effect.runPromise(decideTransport(makeOpts({ profileKey: "pk-1" })));
|
|
58
|
-
expect(decision).toEqual({ _tag: "UseDirect", reason: "profile" });
|
|
59
|
-
});
|
|
60
|
-
it("returns UseDirect{env-fallback} when envFallbackKey + daemonReachable=false", async () => {
|
|
61
|
-
const decision = await Effect.runPromise(decideTransport(makeOpts({
|
|
62
|
-
envFallbackKey: "env-key",
|
|
63
|
-
probeDaemon: () => Effect.succeed(false),
|
|
64
|
-
})));
|
|
65
|
-
expect(decision).toEqual({ _tag: "UseDirect", reason: "env-fallback" });
|
|
66
|
-
});
|
|
67
|
-
it("returns UseDaemon when envFallbackKey + daemonReachable=true", async () => {
|
|
68
|
-
const decision = await Effect.runPromise(decideTransport(makeOpts({
|
|
69
|
-
envFallbackKey: "env-key",
|
|
70
|
-
socketPath: "/tmp/sock",
|
|
71
|
-
probeDaemon: () => Effect.succeed(true),
|
|
72
|
-
})));
|
|
73
|
-
expect(decision._tag).toBe("UseDaemon");
|
|
74
|
-
});
|
|
75
|
-
it("returns UseDaemon when neither as-flag nor env-fallback nor profile", async () => {
|
|
76
|
-
const decision = await Effect.runPromise(decideTransport(makeOpts({ socketPath: "/tmp/sock" })));
|
|
77
|
-
expect(decision).toEqual({ _tag: "UseDaemon", socketPath: "/tmp/sock" });
|
|
78
|
-
});
|
|
79
|
-
it("never invokes probeDaemon when impersonateKey is set (Invariant §4.2)", async () => {
|
|
80
|
-
let invocations = 0;
|
|
81
|
-
await Effect.runPromise(decideTransport(makeOpts({
|
|
82
|
-
impersonateKey: "key-1",
|
|
83
|
-
probeDaemon: () => Effect.sync(() => {
|
|
84
|
-
invocations++;
|
|
85
|
-
return true;
|
|
86
|
-
}),
|
|
87
|
-
})));
|
|
88
|
-
expect(invocations).toBe(0);
|
|
143
|
+
vi.unstubAllEnvs();
|
|
89
144
|
});
|
|
145
|
+
it("impersonate key bypasses daemon probe for any non-empty key", impersonateBypassesProbeForAnyKey);
|
|
146
|
+
it("returns UseDirect{as-flag} when impersonateKey is set", useDirectAsFlag);
|
|
147
|
+
it("returns UseDirect{profile} when profileKey set and no --as", useDirectProfile);
|
|
148
|
+
it("returns UseDirect{env-fallback} when envFallbackKey + daemonReachable=false", useDirectEnvFallback);
|
|
149
|
+
it("returns UseDaemon when envFallbackKey + daemonReachable=true", useDaemonWhenReachable);
|
|
150
|
+
it("returns UseDaemon when neither as-flag nor env-fallback nor profile", useDaemonByDefault);
|
|
151
|
+
it("never invokes probeDaemon when impersonateKey is set (Invariant §4.2)", impersonateBypassesProbe);
|
|
90
152
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
153
|
+
function expectTimeoutForwarded(timeoutMs) {
|
|
154
|
+
const err = tagWsError(TasksList.name, new RpcTimeoutError({ method: TasksList.name, timeoutMs }));
|
|
155
|
+
expect(err).toBeInstanceOf(TransportTimeoutError);
|
|
156
|
+
if (err instanceof TransportTimeoutError) {
|
|
157
|
+
expect(err.timeoutMs).toBe(timeoutMs);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function timeoutErrorForwardsGeneratedTimeouts() {
|
|
161
|
+
return Effect.sync(() => {
|
|
162
|
+
fc.assert(fc.property(fc.integer({ min: 1, max: 60_000 }), expectTimeoutForwarded));
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function rpcServerErrorMapsToTransportRpcError() {
|
|
166
|
+
return Effect.sync(() => {
|
|
104
167
|
const err = tagWsError(TasksList.name, new RpcServerError({
|
|
105
|
-
code:
|
|
106
|
-
message:
|
|
168
|
+
code: SESSION_NOT_FOUND_CODE,
|
|
169
|
+
message: SESSION_NOT_FOUND_MESSAGE,
|
|
107
170
|
}));
|
|
108
171
|
expect(err).toBeInstanceOf(TransportRpcError);
|
|
109
|
-
expect(err._tag).toBe(
|
|
172
|
+
expect(err._tag).toBe(TRANSPORT_RPC_ERROR_TAG);
|
|
110
173
|
if (err instanceof TransportRpcError) {
|
|
111
|
-
expect(err.code).toBe(
|
|
112
|
-
expect(err.message).toBe(
|
|
174
|
+
expect(err.code).toBe(SESSION_NOT_FOUND_CODE);
|
|
175
|
+
expect(err.message).toBe(SESSION_NOT_FOUND_MESSAGE);
|
|
113
176
|
}
|
|
114
177
|
});
|
|
115
|
-
|
|
116
|
-
|
|
178
|
+
}
|
|
179
|
+
function notConnectedMapsToServiceUnreachable() {
|
|
180
|
+
return Effect.sync(() => {
|
|
181
|
+
const err = tagWsError(TasksList.name, new NotConnectedError({ message: NOT_CONNECTED_MESSAGE }));
|
|
117
182
|
expect(err).toBeInstanceOf(ServiceUnreachableError);
|
|
118
|
-
expect(err._tag).toBe(
|
|
183
|
+
expect(err._tag).toBe(SERVICE_UNREACHABLE_ERROR_TAG);
|
|
119
184
|
});
|
|
120
|
-
|
|
185
|
+
}
|
|
186
|
+
function rpcTimeoutMapsToTransportTimeout() {
|
|
187
|
+
return Effect.sync(() => {
|
|
121
188
|
const err = tagWsError(TasksList.name, new RpcTimeoutError({
|
|
122
189
|
method: TasksList.name,
|
|
123
|
-
timeoutMs:
|
|
190
|
+
timeoutMs: RPC_TIMEOUT_MS,
|
|
124
191
|
}));
|
|
125
192
|
expect(err).toBeInstanceOf(TransportTimeoutError);
|
|
126
193
|
if (err instanceof TransportTimeoutError) {
|
|
127
|
-
expect(err.timeoutMs).toBe(
|
|
194
|
+
expect(err.timeoutMs).toBe(RPC_TIMEOUT_MS);
|
|
128
195
|
}
|
|
129
196
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// TransportDecodeError so the error is observable, not silently swallowed.
|
|
197
|
+
}
|
|
198
|
+
function unknownErrorMapsToTransportDecode() {
|
|
199
|
+
return Effect.sync(() => {
|
|
134
200
|
const err = tagWsError(TasksList.name, {
|
|
135
|
-
message:
|
|
201
|
+
message: UNKNOWN_ERROR_MESSAGE,
|
|
136
202
|
});
|
|
137
203
|
expect(err).toBeInstanceOf(TransportDecodeError);
|
|
138
|
-
expect(err._tag).toBe(
|
|
204
|
+
expect(err._tag).toBe(TRANSPORT_DECODE_ERROR_TAG);
|
|
139
205
|
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Regression guard for sbd#198: the original v2 implementation at 069135d
|
|
209
|
+
* used `Effect.runPromise(sendRpc)` inside `Effect.tryPromise`. In Effect 3.21,
|
|
210
|
+
* `runPromise` wraps typed failures in `FiberFailureImpl` (no `_tag`), so
|
|
211
|
+
* `tagWsError`'s switch hit the default branch and emitted `TransportDecodeError`
|
|
212
|
+
* for every ws error. Fixed by code-guard commit ff2de0d; these tests guard
|
|
213
|
+
* against regression to that pattern.
|
|
214
|
+
*
|
|
215
|
+
* `tagWsError` is `@internal`-exported so this suite can reach it directly
|
|
216
|
+
* without a mock WS server.
|
|
217
|
+
*/
|
|
218
|
+
describe("tagWsError — maps ws-client error tags to TransportError variants", () => {
|
|
219
|
+
it("RpcTimeoutError forwards generated timeoutMs values", timeoutErrorForwardsGeneratedTimeouts);
|
|
220
|
+
it("RpcServerError maps to TransportRpcError (not TransportDecodeError)", rpcServerErrorMapsToTransportRpcError);
|
|
221
|
+
it("NotConnectedError maps to ServiceUnreachableError", notConnectedMapsToServiceUnreachable);
|
|
222
|
+
it("RpcTimeoutError maps to TransportTimeoutError with timeoutMs forwarded", rpcTimeoutMapsToTransportTimeout);
|
|
223
|
+
it("FiberFailureImpl-shaped error (no _tag) maps to TransportDecodeError — not to TransportRpcError", unknownErrorMapsToTransportDecode);
|
|
140
224
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
afterEach(() => {
|
|
149
|
-
process.env = originalEnv;
|
|
150
|
-
});
|
|
151
|
-
it("impersonateKey branch does NOT read MOLTZAP_API_KEY env", async () => {
|
|
152
|
-
process.env.MOLTZAP_API_KEY = "leaked-key";
|
|
153
|
-
const opts = await Effect.runPromise(resolveTransportInputs({ impersonateKey: "explicit-key" }));
|
|
154
|
-
expect(opts.impersonateKey).toBe("explicit-key");
|
|
155
|
-
// The returned TransportOptions does NOT expose MOLTZAP_API_KEY as profileKey.
|
|
225
|
+
function explicitKeyIgnoresEnvKey() {
|
|
226
|
+
return Effect.gen(function* () {
|
|
227
|
+
vi.stubEnv("MOLTZAP_API_KEY", LEAKED_ENV_KEY);
|
|
228
|
+
const opts = yield* resolveTransportInputs({
|
|
229
|
+
impersonateKey: EXPLICIT_KEY,
|
|
230
|
+
});
|
|
231
|
+
expect(opts.impersonateKey).toBe(EXPLICIT_KEY);
|
|
156
232
|
expect(opts.profileKey).toBeUndefined();
|
|
157
233
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
234
|
+
}
|
|
235
|
+
function explicitKeyUsesServerUrlEnv() {
|
|
236
|
+
return Effect.gen(function* () {
|
|
237
|
+
vi.stubEnv("MOLTZAP_SERVER_URL", OVERRIDE_SERVER_URL);
|
|
238
|
+
const opts = yield* resolveTransportInputs({ impersonateKey: "k" });
|
|
239
|
+
expect(opts.serverUrl).toBe(OVERRIDE_SERVER_URL);
|
|
162
240
|
});
|
|
163
|
-
|
|
164
|
-
|
|
241
|
+
}
|
|
242
|
+
function emptyInputUsesDaemon() {
|
|
243
|
+
return Effect.gen(function* () {
|
|
244
|
+
const opts = yield* resolveTransportInputs({});
|
|
165
245
|
expect(opts.impersonateKey).toBeUndefined();
|
|
166
246
|
expect(opts.profileKey).toBeUndefined();
|
|
167
247
|
expect(opts.socketPath).toBeDefined();
|
|
168
248
|
});
|
|
249
|
+
}
|
|
250
|
+
function directRpcFailurePropagates() {
|
|
251
|
+
return Effect.gen(function* () {
|
|
252
|
+
const opts = {
|
|
253
|
+
impersonateKey: DIRECT_TEST_KEY,
|
|
254
|
+
serverUrl: DIRECT_TEST_SERVER_URL,
|
|
255
|
+
};
|
|
256
|
+
const exit = yield* Transport.pipe(Effect.flatMap((transport) => transport.rpc(TasksList, {})), Effect.exit, Effect.provide(makeTransportLayer(opts)));
|
|
257
|
+
expect(Exit.isFailure(exit)).toBe(true);
|
|
258
|
+
if (Exit.isFailure(exit)) {
|
|
259
|
+
const failure = Cause.failureOption(exit.cause);
|
|
260
|
+
expect(Option.isSome(failure)).toBe(true);
|
|
261
|
+
if (Option.isSome(failure)) {
|
|
262
|
+
expect(failure.value).toBeInstanceOf(TransportRpcError);
|
|
263
|
+
expect(failure.value._tag).toBe(TRANSPORT_RPC_ERROR_TAG);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
describe("resolveTransportInputs (composition-boundary gate)", () => {
|
|
269
|
+
beforeEach(() => {
|
|
270
|
+
vi.unstubAllEnvs();
|
|
271
|
+
});
|
|
272
|
+
afterEach(() => {
|
|
273
|
+
vi.unstubAllEnvs();
|
|
274
|
+
});
|
|
275
|
+
it("impersonateKey branch does NOT read MOLTZAP_API_KEY env", explicitKeyIgnoresEnvKey);
|
|
276
|
+
it("impersonateKey branch uses MOLTZAP_SERVER_URL if present, else default", explicitKeyUsesServerUrlEnv);
|
|
277
|
+
it("empty input falls through to legacy daemon path (no impersonate, no profile)", emptyInputUsesDaemon);
|
|
169
278
|
});
|
|
170
279
|
/**
|
|
171
280
|
* Composed rpc() failure path — pins the full call chain so a future
|
|
@@ -179,21 +288,6 @@ describe("resolveTransportInputs (composition-boundary gate)", () => {
|
|
|
179
288
|
* FiberFailureImpl (no _tag) and the result would be TransportDecodeError.
|
|
180
289
|
*/
|
|
181
290
|
describe("makeDirectTransport — composed rpc() failure path", () => {
|
|
182
|
-
it("RpcServerError from sendRpc propagates as TransportRpcError through tagWsError",
|
|
183
|
-
const opts = {
|
|
184
|
-
impersonateKey: "test-key",
|
|
185
|
-
serverUrl: "wss://test.example",
|
|
186
|
-
};
|
|
187
|
-
const exit = await Effect.runPromise(Transport.pipe(Effect.flatMap((t) => t.rpc(TasksList, {})), Effect.exit, Effect.provide(makeTransportLayer(opts))));
|
|
188
|
-
expect(Exit.isFailure(exit)).toBe(true);
|
|
189
|
-
if (Exit.isFailure(exit)) {
|
|
190
|
-
const failure = Cause.failureOption(exit.cause);
|
|
191
|
-
expect(Option.isSome(failure)).toBe(true);
|
|
192
|
-
if (Option.isSome(failure)) {
|
|
193
|
-
expect(failure.value).toBeInstanceOf(TransportRpcError);
|
|
194
|
-
expect(failure.value._tag).toBe("TransportRpcError");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
291
|
+
it("RpcServerError from sendRpc propagates as TransportRpcError through tagWsError", directRpcFailurePropagates);
|
|
198
292
|
});
|
|
199
293
|
//# sourceMappingURL=transport.test.js.map
|