@moltzap/client 2026.506.0 → 2026.508.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/cli/commands/contacts.d.ts.map +1 -1
- package/dist/cli/commands/contacts.js +3 -3
- package/dist/cli/commands/contacts.js.map +1 -1
- package/dist/cli/commands/conversations.d.ts.map +1 -1
- package/dist/cli/commands/conversations.js +11 -11
- package/dist/cli/commands/conversations.js.map +1 -1
- package/dist/cli/commands/messages.d.ts.map +1 -1
- package/dist/cli/commands/messages.js +3 -3
- package/dist/cli/commands/messages.js.map +1 -1
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +3 -3
- package/dist/cli/commands/send.js.map +1 -1
- package/dist/cli/commands/test-transport.d.ts.map +1 -1
- package/dist/cli/commands/test-transport.js +11 -8
- package/dist/cli/commands/test-transport.js.map +1 -1
- package/dist/cli/http-client.d.ts +2 -2
- package/dist/cli/http-client.d.ts.map +1 -1
- package/dist/cli/socket-client.d.ts +4 -3
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +6 -4
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/cli/transport.d.ts +3 -3
- package/dist/cli/transport.d.ts.map +1 -1
- package/dist/cli/transport.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/__tests__/app-callback-test-requests.d.ts +2 -2
- package/dist/internal/__tests__/app-callback-test-requests.d.ts.map +1 -1
- package/dist/internal/__tests__/app-callback-test-requests.js +3 -2
- package/dist/internal/__tests__/app-callback-test-requests.js.map +1 -1
- package/dist/internal/app-callback-dispatcher-errors.d.ts +3 -3
- package/dist/internal/app-callback-dispatcher-errors.d.ts.map +1 -1
- package/dist/internal/app-callback-partition-key.d.ts +2 -2
- package/dist/internal/app-callback-partition-key.d.ts.map +1 -1
- package/dist/runtime/errors.d.ts +2 -12
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +4 -3
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/frame.d.ts +20 -19
- package/dist/runtime/frame.d.ts.map +1 -1
- package/dist/runtime/frame.js +49 -51
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/frame.test.js +28 -34
- package/dist/runtime/frame.test.js.map +1 -1
- package/dist/runtime/subscribers.d.ts +8 -11
- package/dist/runtime/subscribers.d.ts.map +1 -1
- package/dist/runtime/subscribers.js.map +1 -1
- package/dist/runtime/subscribers.test.js +8 -14
- package/dist/runtime/subscribers.test.js.map +1 -1
- package/dist/service.d.ts +5 -8
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +44 -65
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +9 -9
- package/dist/service.test.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +1 -1
- package/dist/test-utils/conformance-adapter.d.ts +5 -5
- package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
- package/dist/test-utils/conformance-adapter.js +26 -29
- package/dist/test-utils/conformance-adapter.js.map +1 -1
- package/dist/test-utils/fake-service.d.ts +4 -4
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +19 -10
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/test-utils/ids.d.ts +2 -6
- package/dist/test-utils/ids.d.ts.map +1 -1
- package/dist/test-utils/ids.js +1 -2
- package/dist/test-utils/ids.js.map +1 -1
- package/dist/ws-client.d.ts +11 -89
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +56 -153
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.js +35 -48
- package/dist/ws-client.test.js.map +1 -1
- package/package.json +3 -4
- package/dist/runtime/index.d.ts +0 -3
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js +0 -3
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/notification-types.types-check.d.ts +0 -2
- package/dist/runtime/notification-types.types-check.d.ts.map +0 -1
- package/dist/runtime/notification-types.types-check.js +0 -57
- package/dist/runtime/notification-types.types-check.js.map +0 -1
- package/dist/service.typed-bridge.test.d.ts +0 -2
- package/dist/service.typed-bridge.test.d.ts.map +0 -1
- package/dist/service.typed-bridge.test.js +0 -93
- package/dist/service.typed-bridge.test.js.map +0 -1
- package/dist/ws-client.typed-bridge.test.d.ts +0 -2
- package/dist/ws-client.typed-bridge.test.d.ts.map +0 -1
- package/dist/ws-client.typed-bridge.test.js +0 -105
- package/dist/ws-client.typed-bridge.test.js.map +0 -1
package/dist/ws-client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { Connect, NotConnectedError, RpcServerError, RpcTimeoutError, type AnyTaskCallbackRpcDefinition, type AnyNotificationDefinition, type
|
|
2
|
+
import { Connect, NotConnectedError, RpcServerError, RpcTimeoutError, type AnyTaskCallbackRpcDefinition, type AnyNotificationDefinition, type JsonRpcId, type ParamsOf, type ResultOf, type RpcDefinition } from "@moltzap/protocol";
|
|
3
3
|
import { DuplicateServerRpcHandlerError } from "./runtime/errors.js";
|
|
4
|
-
import { type DecodedNotification
|
|
4
|
+
import { type DecodedNotification } from "./runtime/frame.js";
|
|
5
5
|
import { type NotificationSubscription, type SubscriberHandler, type SubscriptionFilter } from "./runtime/subscribers.js";
|
|
6
6
|
import { type CloseInfo } from "./runtime/close-info.js";
|
|
7
7
|
import { type PartitionedDispatcherConfig } from "./internal/app-callback-partitioned-dispatcher.js";
|
|
@@ -14,7 +14,7 @@ export declare const RPC_TIMEOUT_MS = 30000;
|
|
|
14
14
|
export interface RpcCallOptions {
|
|
15
15
|
readonly timeoutMs?: number;
|
|
16
16
|
}
|
|
17
|
-
type ConnectResult =
|
|
17
|
+
type ConnectResult = ResultOf<typeof Connect>;
|
|
18
18
|
/**
|
|
19
19
|
* appCallback dispatcher knobs exposed on `MoltZapWsClientOptions`. All optional;
|
|
20
20
|
* defaults from `DEFAULT_PARTITIONED_DISPATCHER_CONFIG`.
|
|
@@ -34,59 +34,19 @@ export type AppCallbackDispatcherConfig = Partial<PartitionedDispatcherConfig>;
|
|
|
34
34
|
* generic InternalError reply.
|
|
35
35
|
*
|
|
36
36
|
* The `unknown`/`unknown` parameter and result types narrow generically
|
|
37
|
-
* against `
|
|
37
|
+
* against `taskCallbackMethods` at each `handleServerRpc(definition,
|
|
38
38
|
* handler)` call site via the `AnyTaskCallbackRpcDefinition` union. Phase
|
|
39
39
|
* 9b consumer-migration retired the legacy `AppCallbackRpcMap` indirection
|
|
40
40
|
* alongside the appCallback group collapse to a single member.
|
|
41
41
|
*/
|
|
42
42
|
export interface ServerRpcContext {
|
|
43
|
-
readonly requestId:
|
|
43
|
+
readonly requestId: JsonRpcId;
|
|
44
44
|
readonly definition: AnyTaskCallbackRpcDefinition;
|
|
45
45
|
readonly traceparent?: string;
|
|
46
46
|
}
|
|
47
|
-
export type ServerRpcHandler<D extends AnyTaskCallbackRpcDefinition = AnyTaskCallbackRpcDefinition> = (params:
|
|
47
|
+
export type ServerRpcHandler<D extends AnyTaskCallbackRpcDefinition = AnyTaskCallbackRpcDefinition> = (params: ParamsOf<D>, ctx: ServerRpcContext & {
|
|
48
48
|
readonly definition: D;
|
|
49
|
-
}) => Effect.Effect<
|
|
50
|
-
/**
|
|
51
|
-
* Pre-validation alias used internally by buffer/waiter machinery —
|
|
52
|
-
* the decoder is payload-opaque so frames carry `params: unknown`
|
|
53
|
-
* until a typed bridge runs `validateNotificationParams`.
|
|
54
|
-
*/
|
|
55
|
-
type AnyRawDecodedNotification = RawDecodedNotification<AnyNotificationDefinition>;
|
|
56
|
-
/**
|
|
57
|
-
* Validate `notification.params` against the schema attached by the
|
|
58
|
-
* decoder. Type predicate: a `true` result lifts the input from the
|
|
59
|
-
* raw (`params: unknown`) shape to the validated
|
|
60
|
-
* `DecodedNotification<…>` shape — the compiler-enforced lift the
|
|
61
|
-
* inbound bridges rely on. Logs a structured drift warning and
|
|
62
|
-
* returns `false` on failure. Every typed dispatcher MUST route
|
|
63
|
-
* through this guard before invoking handlers, because the wire
|
|
64
|
-
* decoder is payload-opaque (required for the
|
|
65
|
-
* `delivery/payload-opacity-client` and
|
|
66
|
-
* `boundary/schema-exhaustive-fuzz-client` conformance properties).
|
|
67
|
-
*
|
|
68
|
-
* The signature is non-generic over a single descriptor: the input
|
|
69
|
-
* accepts the broad `AnyRawDecodedNotification` union (distributed
|
|
70
|
-
* over all known descriptors), and the predicate narrows to the
|
|
71
|
-
* matching distributed validated union. Per-descriptor narrowing is
|
|
72
|
-
* the job of the caller's enclosing `is`-predicate
|
|
73
|
-
* (e.g. `acceptTypedNotification<D>`); this helper just gates "params
|
|
74
|
-
* conform to the attached schema." Current call sites:
|
|
75
|
-
* `acceptTypedNotification` below (typed-promise resolver) and
|
|
76
|
-
* `service.ts:handleNotification` (subscriber-fanout dispatch into
|
|
77
|
-
* the typed handler bucket).
|
|
78
|
-
*/
|
|
79
|
-
export declare function validateNotificationParams(notification: AnyRawDecodedNotification, logger: WsClientLogger | undefined): notification is DecodedNotification<AnyNotificationDefinition>;
|
|
80
|
-
/**
|
|
81
|
-
* Typed bridge for `waitForNotification`: matches the notification's
|
|
82
|
-
* descriptor against `definition` and then validates its params via
|
|
83
|
-
* `validateNotificationParams`. A frame whose method matches `definition`
|
|
84
|
-
* but whose params fail validation is logged and skipped — the typed
|
|
85
|
-
* promise never resolves with a malformed payload typed as if it were
|
|
86
|
-
* valid. "Skip on non-match" semantics for the bucketed waiter map are
|
|
87
|
-
* preserved; the validation guard is the lift from raw to validated.
|
|
88
|
-
*/
|
|
89
|
-
export declare function acceptTypedNotification<D extends AnyNotificationDefinition>(definition: D, notification: AnyRawDecodedNotification, logger: WsClientLogger | undefined): notification is DecodedNotification<D>;
|
|
49
|
+
}) => Effect.Effect<ResultOf<D>, RpcServerError>;
|
|
90
50
|
export interface WsClientLogger {
|
|
91
51
|
info: (...args: unknown[]) => void;
|
|
92
52
|
warn: (...args: unknown[]) => void;
|
|
@@ -118,16 +78,7 @@ export interface MoltZapWsClientOptions {
|
|
|
118
78
|
appCallbackDispatcher?: AppCallbackDispatcherConfig;
|
|
119
79
|
}
|
|
120
80
|
/**
|
|
121
|
-
*
|
|
122
|
-
* request `id` alongside the typed result without leaking the JSON-RPC wire
|
|
123
|
-
* object onto the caller surface.
|
|
124
|
-
*/
|
|
125
|
-
export interface TrackedRpcResponse<R> {
|
|
126
|
-
readonly id: JsonRpcStringId;
|
|
127
|
-
readonly result: R;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* WebSocket lifecycle: open → auth/connect → active. On disconnect,
|
|
81
|
+
* WebSocket lifecycle: open → network/connect → active. On disconnect,
|
|
131
82
|
* exponential backoff (1s base, 30s cap, jittered) retries the handshake via
|
|
132
83
|
* `Effect.sleep` + `Schedule` so TestClock can drive it. Public API is
|
|
133
84
|
* Effect-based — consumers run the returned Effects themselves (typically at
|
|
@@ -184,7 +135,7 @@ export declare class MoltZapWsClient {
|
|
|
184
135
|
*/
|
|
185
136
|
handleServerRpc<D extends AnyTaskCallbackRpcDefinition>(definition: D, handler: ServerRpcHandler<D>): Effect.Effect<void, DuplicateServerRpcHandlerError>;
|
|
186
137
|
get helloOk(): ConnectResult | null;
|
|
187
|
-
/** Open the socket, perform
|
|
138
|
+
/** Open the socket, perform network/connect, resolve with HelloOk. Fails
|
|
188
139
|
* immediately on pre-open close or error. */
|
|
189
140
|
connect(): Effect.Effect<ConnectResult, NotConnectedError | RpcTimeoutError | RpcServerError>;
|
|
190
141
|
/**
|
|
@@ -196,19 +147,7 @@ export declare class MoltZapWsClient {
|
|
|
196
147
|
* Descriptor-backed RPC call. Callers pass the protocol descriptor, and the
|
|
197
148
|
* client extracts the wire method only inside the encoder path.
|
|
198
149
|
*/
|
|
199
|
-
sendRpc<D extends RpcDefinition<string,
|
|
200
|
-
/**
|
|
201
|
-
* Send an RPC and surface the outbound request id alongside the
|
|
202
|
-
* response envelope `type` and `result`. Spec #222 §5.1–5.2:
|
|
203
|
-
* un-vacuates B4 (request-id tracking) and V5 (response-type
|
|
204
|
-
* exposure). Mirrors `sendRpc`'s descriptor-backed call shape.
|
|
205
|
-
*
|
|
206
|
-
* Invariant 3 (spec #222 §4): the returned `id` is the same identity
|
|
207
|
-
* minted inside the existing `rpc-${++counter}` site — no parallel
|
|
208
|
-
* counter, no mirror. The single-id pre-condition is what makes B4 a
|
|
209
|
-
* real check rather than a tautology.
|
|
210
|
-
*/
|
|
211
|
-
sendRpcTracked<D extends RpcDefinition<string, TSchema, TSchema>>(definition: D, params: ParamsOf<D>): Effect.Effect<TrackedRpcResponse<ResultOf<D>>, NotConnectedError | RpcTimeoutError | RpcServerError>;
|
|
150
|
+
sendRpc<D extends RpcDefinition<string, any, any>>(definition: D, params: ParamsOf<D>, opts?: RpcCallOptions): Effect.Effect<ResultOf<D>, NotConnectedError | RpcTimeoutError | RpcServerError>;
|
|
212
151
|
/**
|
|
213
152
|
* Register a per-subscription notification handler. Spec #222 §5.3 + OQ-2
|
|
214
153
|
* (A): filter grammar is the three-field `SubscriptionFilter`
|
|
@@ -236,28 +175,11 @@ export declare class MoltZapWsClient {
|
|
|
236
175
|
/** Return all buffered notifications and clear the buffer. Synchronous.
|
|
237
176
|
* Frames are pre-validation (`params: unknown`) — buffer holds the
|
|
238
177
|
* raw shape produced by the wire decoder. */
|
|
239
|
-
drainNotifications():
|
|
178
|
+
drainNotifications(): DecodedNotification<AnyNotificationDefinition>[];
|
|
240
179
|
/** Close the socket without marking as permanently closed, triggering reconnection. */
|
|
241
180
|
disconnect(): Effect.Effect<void, never>;
|
|
242
181
|
private disconnectSync;
|
|
243
182
|
private connectEffect;
|
|
244
|
-
/**
|
|
245
|
-
* Tracked variant of `sendRpcEffect`. Reuses the same Deferred /
|
|
246
|
-
* pendingRef plumbing — the only difference is the resolved value:
|
|
247
|
-
* `{id, result}` instead of bare `result`. This
|
|
248
|
-
* keeps Invariant 3 (single id source) trivially: we mint `id` once
|
|
249
|
-
* inside this body, register the Deferred under that id, and return
|
|
250
|
-
* the same id to the caller alongside the result.
|
|
251
|
-
*/
|
|
252
|
-
private sendRpcTrackedEffect;
|
|
253
|
-
/**
|
|
254
|
-
* Bare-result RPC — projects the tracked RPC envelope onto its
|
|
255
|
-
* `result` field, discarding the `{id, type}` observability surface.
|
|
256
|
-
* The write / race / timeout plumbing lives in `sendRpcTrackedEffect`
|
|
257
|
-
* so there is one shared site for Invariant 3 (single id source),
|
|
258
|
-
* Invariant 5 (typed error channel), and the `socket.writer` latch
|
|
259
|
-
* race described below.
|
|
260
|
-
*/
|
|
261
183
|
private sendRpcEffect;
|
|
262
184
|
/**
|
|
263
185
|
* Translate a typed `OfferRejected` failure from the partitioned
|
package/dist/ws-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,MAAM,EASP,MAAM,QAAQ,CAAC;AAChB,OAAO,EAEL,OAAO,
|
|
1
|
+
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"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"}
|
package/dist/ws-client.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as Socket from "@effect/platform/Socket";
|
|
2
2
|
import * as NodeSocket from "@effect/platform-node/NodeSocket";
|
|
3
3
|
import { Cause, Data, Deferred, Duration, Either, Effect, Exit, Fiber, HashMap, ManagedRuntime, Option, Ref, Schedule, Scope, } from "effect";
|
|
4
|
-
import { PROTOCOL_VERSION, Connect,
|
|
4
|
+
import { PROTOCOL_VERSION, Connect, encodeErrorResponse, NotConnectedError, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
|
|
5
5
|
import { DuplicateServerRpcHandlerError } from "./runtime/errors.js";
|
|
6
|
-
import { decodeFrames
|
|
6
|
+
import { decodeFrames } from "./runtime/frame.js";
|
|
7
7
|
import { makeSubscriberRegistry, } from "./runtime/subscribers.js";
|
|
8
8
|
import { extractCloseInfo } from "./runtime/close-info.js";
|
|
9
9
|
import { makePartitionedDispatcher, } from "./internal/app-callback-partitioned-dispatcher.js";
|
|
@@ -39,52 +39,13 @@ const UTF8_DECODER = new TextDecoder("utf-8");
|
|
|
39
39
|
const makeNotConnectedError = () => new NotConnectedError({ message: MSG_NOT_CONNECTED });
|
|
40
40
|
class ReconnectAttemptFailedError extends Data.TaggedError("ReconnectAttemptFailedError") {
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
/** Notifications are pre-validated by the wire decoder
|
|
43
|
+
* (`decodeNotification` in `@moltzap/protocol`) — fail-close on unknown
|
|
44
|
+
* methods or bad params. Buffer + waiters hold the validated shape
|
|
45
|
+
* directly; no separate Raw|Unknown lift remains. */
|
|
46
|
+
function acceptTypedNotification(definition, notification) {
|
|
43
47
|
return notification.definition === definition;
|
|
44
48
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Validate `notification.params` against the schema attached by the
|
|
47
|
-
* decoder. Type predicate: a `true` result lifts the input from the
|
|
48
|
-
* raw (`params: unknown`) shape to the validated
|
|
49
|
-
* `DecodedNotification<…>` shape — the compiler-enforced lift the
|
|
50
|
-
* inbound bridges rely on. Logs a structured drift warning and
|
|
51
|
-
* returns `false` on failure. Every typed dispatcher MUST route
|
|
52
|
-
* through this guard before invoking handlers, because the wire
|
|
53
|
-
* decoder is payload-opaque (required for the
|
|
54
|
-
* `delivery/payload-opacity-client` and
|
|
55
|
-
* `boundary/schema-exhaustive-fuzz-client` conformance properties).
|
|
56
|
-
*
|
|
57
|
-
* The signature is non-generic over a single descriptor: the input
|
|
58
|
-
* accepts the broad `AnyRawDecodedNotification` union (distributed
|
|
59
|
-
* over all known descriptors), and the predicate narrows to the
|
|
60
|
-
* matching distributed validated union. Per-descriptor narrowing is
|
|
61
|
-
* the job of the caller's enclosing `is`-predicate
|
|
62
|
-
* (e.g. `acceptTypedNotification<D>`); this helper just gates "params
|
|
63
|
-
* conform to the attached schema." Current call sites:
|
|
64
|
-
* `acceptTypedNotification` below (typed-promise resolver) and
|
|
65
|
-
* `service.ts:handleNotification` (subscriber-fanout dispatch into
|
|
66
|
-
* the typed handler bucket).
|
|
67
|
-
*/
|
|
68
|
-
export function validateNotificationParams(notification, logger) {
|
|
69
|
-
if (notification.definition.validateParams(notification.params))
|
|
70
|
-
return true;
|
|
71
|
-
logger?.warn(`Notification ${notification.definition.name} dropped: params failed schema validation (drift signal)`, notification.params);
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Typed bridge for `waitForNotification`: matches the notification's
|
|
76
|
-
* descriptor against `definition` and then validates its params via
|
|
77
|
-
* `validateNotificationParams`. A frame whose method matches `definition`
|
|
78
|
-
* but whose params fail validation is logged and skipped — the typed
|
|
79
|
-
* promise never resolves with a malformed payload typed as if it were
|
|
80
|
-
* valid. "Skip on non-match" semantics for the bucketed waiter map are
|
|
81
|
-
* preserved; the validation guard is the lift from raw to validated.
|
|
82
|
-
*/
|
|
83
|
-
export function acceptTypedNotification(definition, notification, logger) {
|
|
84
|
-
if (!notificationMatches(definition, notification))
|
|
85
|
-
return false;
|
|
86
|
-
return validateNotificationParams(notification, logger);
|
|
87
|
-
}
|
|
88
49
|
/** Drop `waiter` from its notification-definition bucket, pruning an empty bucket. */
|
|
89
50
|
function removeWaiter(m, definition, waiter) {
|
|
90
51
|
const bucket = HashMap.get(m, definition);
|
|
@@ -96,7 +57,7 @@ function removeWaiter(m, definition, waiter) {
|
|
|
96
57
|
: HashMap.set(m, definition, filtered);
|
|
97
58
|
}
|
|
98
59
|
/**
|
|
99
|
-
* WebSocket lifecycle: open →
|
|
60
|
+
* WebSocket lifecycle: open → network/connect → active. On disconnect,
|
|
100
61
|
* exponential backoff (1s base, 30s cap, jittered) retries the handshake via
|
|
101
62
|
* `Effect.sleep` + `Schedule` so TestClock can drive it. Public API is
|
|
102
63
|
* Effect-based — consumers run the returned Effects themselves (typically at
|
|
@@ -184,7 +145,7 @@ export class MoltZapWsClient {
|
|
|
184
145
|
get helloOk() {
|
|
185
146
|
return this._helloOk;
|
|
186
147
|
}
|
|
187
|
-
/** Open the socket, perform
|
|
148
|
+
/** Open the socket, perform network/connect, resolve with HelloOk. Fails
|
|
188
149
|
* immediately on pre-open close or error. */
|
|
189
150
|
connect() {
|
|
190
151
|
return Effect.suspend(() => {
|
|
@@ -216,26 +177,6 @@ export class MoltZapWsClient {
|
|
|
216
177
|
data: result,
|
|
217
178
|
}))));
|
|
218
179
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Send an RPC and surface the outbound request id alongside the
|
|
221
|
-
* response envelope `type` and `result`. Spec #222 §5.1–5.2:
|
|
222
|
-
* un-vacuates B4 (request-id tracking) and V5 (response-type
|
|
223
|
-
* exposure). Mirrors `sendRpc`'s descriptor-backed call shape.
|
|
224
|
-
*
|
|
225
|
-
* Invariant 3 (spec #222 §4): the returned `id` is the same identity
|
|
226
|
-
* minted inside the existing `rpc-${++counter}` site — no parallel
|
|
227
|
-
* counter, no mirror. The single-id pre-condition is what makes B4 a
|
|
228
|
-
* real check rather than a tautology.
|
|
229
|
-
*/
|
|
230
|
-
sendRpcTracked(definition, params) {
|
|
231
|
-
return this.sendRpcTrackedEffect(definition, params).pipe(Effect.flatMap((tracked) => definition.validateResult(tracked.result)
|
|
232
|
-
? Effect.succeed({ id: tracked.id, result: tracked.result })
|
|
233
|
-
: Effect.fail(new RpcServerError({
|
|
234
|
-
code: JSON_RPC_INTERNAL_ERROR_CODE,
|
|
235
|
-
message: `Invalid result for method: ${definition.name}`,
|
|
236
|
-
data: tracked.result,
|
|
237
|
-
}))));
|
|
238
|
-
}
|
|
239
180
|
/**
|
|
240
181
|
* Register a per-subscription notification handler. Spec #222 §5.3 + OQ-2
|
|
241
182
|
* (A): filter grammar is the three-field `SubscriptionFilter`
|
|
@@ -307,10 +248,9 @@ export class MoltZapWsClient {
|
|
|
307
248
|
* with a per-call timeout. */
|
|
308
249
|
waitForNotification(definition, timeoutMs = EVENT_WAIT_TIMEOUT_MS) {
|
|
309
250
|
return Effect.gen(this, function* () {
|
|
310
|
-
const logger = this.options.logger;
|
|
311
251
|
const buffered = yield* Ref.modify(this.notificationsBufferRef, (frames) => {
|
|
312
252
|
for (const [idx, frame] of frames.entries()) {
|
|
313
|
-
if (!acceptTypedNotification(definition, frame
|
|
253
|
+
if (!acceptTypedNotification(definition, frame))
|
|
314
254
|
continue;
|
|
315
255
|
const next = [...frames.slice(0, idx), ...frames.slice(idx + 1)];
|
|
316
256
|
return [frame, next];
|
|
@@ -322,7 +262,7 @@ export class MoltZapWsClient {
|
|
|
322
262
|
const deferred = yield* Deferred.make();
|
|
323
263
|
const waiter = {
|
|
324
264
|
definition,
|
|
325
|
-
complete: (notification) => acceptTypedNotification(definition, notification
|
|
265
|
+
complete: (notification) => acceptTypedNotification(definition, notification)
|
|
326
266
|
? Deferred.succeed(deferred, notification).pipe(Effect.asVoid)
|
|
327
267
|
: Effect.void,
|
|
328
268
|
fail: (error) => Deferred.fail(deferred, error).pipe(Effect.asVoid),
|
|
@@ -391,7 +331,7 @@ export class MoltZapWsClient {
|
|
|
391
331
|
this.runtime.runFork(Scope.close(scope, Exit.void));
|
|
392
332
|
}).pipe(Effect.zipRight(Effect.fail(makeNotConnectedError()))))));
|
|
393
333
|
const write = yield* Scope.extend(socket.writer, scope);
|
|
394
|
-
// Settled first by whichever fires: the
|
|
334
|
+
// Settled first by whichever fires: the network/connect response, or
|
|
395
335
|
// reader-fiber exit on any close/error before handshake.
|
|
396
336
|
const handshakeSettled = yield* Deferred.make();
|
|
397
337
|
// Spec #356 — partitioned task-callback dispatcher. Replaces
|
|
@@ -474,7 +414,7 @@ export class MoltZapWsClient {
|
|
|
474
414
|
// finalizes on return, interrupts the reader, and `onExit` clears
|
|
475
415
|
// `_helloOk` behind a caller that believed connect() succeeded.
|
|
476
416
|
const readerFiber = this.runtime.runFork(readerEffect);
|
|
477
|
-
// Publish state BEFORE
|
|
417
|
+
// Publish state BEFORE network/connect: the write goes through
|
|
478
418
|
// `sendRpcEffect`, which reads `stateRef`.
|
|
479
419
|
yield* Ref.set(this.stateRef, Option.some({
|
|
480
420
|
write,
|
|
@@ -489,9 +429,9 @@ export class MoltZapWsClient {
|
|
|
489
429
|
minProtocol: PROTOCOL_VERSION,
|
|
490
430
|
maxProtocol: PROTOCOL_VERSION,
|
|
491
431
|
});
|
|
492
|
-
// Race the
|
|
432
|
+
// Race the network/connect response against the handshakeSettled deferred
|
|
493
433
|
// `raceFirst` (not `race`) — `race` waits for the loser when the
|
|
494
|
-
// winner fails, so a typed
|
|
434
|
+
// winner fails, so a typed network/connect error would hang behind the
|
|
495
435
|
// still-pending handshake-watchdog Deferred.
|
|
496
436
|
const result = yield* Effect.raceFirst(authEffect, Deferred.await(handshakeSettled)).pipe(Effect.tap((value) => Effect.sync(() => {
|
|
497
437
|
this._helloOk = value;
|
|
@@ -499,35 +439,20 @@ export class MoltZapWsClient {
|
|
|
499
439
|
return result;
|
|
500
440
|
});
|
|
501
441
|
}
|
|
502
|
-
|
|
503
|
-
* Tracked variant of `sendRpcEffect`. Reuses the same Deferred /
|
|
504
|
-
* pendingRef plumbing — the only difference is the resolved value:
|
|
505
|
-
* `{id, result}` instead of bare `result`. This
|
|
506
|
-
* keeps Invariant 3 (single id source) trivially: we mint `id` once
|
|
507
|
-
* inside this body, register the Deferred under that id, and return
|
|
508
|
-
* the same id to the caller alongside the result.
|
|
509
|
-
*/
|
|
510
|
-
sendRpcTrackedEffect(definition, params, opts) {
|
|
442
|
+
sendRpcEffect(definition, params, opts) {
|
|
511
443
|
return Effect.gen(this, function* () {
|
|
512
444
|
const method = definition.name;
|
|
513
445
|
const state = yield* Ref.get(this.stateRef);
|
|
514
446
|
if (Option.isNone(state)) {
|
|
515
447
|
return yield* Effect.fail(makeNotConnectedError());
|
|
516
448
|
}
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
// Register the Deferred BEFORE writing
|
|
520
|
-
//
|
|
521
|
-
// `failAllPending` before we register — leaving us to await a
|
|
449
|
+
const frame = definition.encodeRequest(`rpc-${++this.requestCounter}`, params);
|
|
450
|
+
const id = frame.id;
|
|
451
|
+
// Register the Deferred BEFORE writing — write yields, reader could
|
|
452
|
+
// close + failAllPending before we register, leaving us awaiting a
|
|
522
453
|
// never-resolved Deferred.
|
|
523
454
|
const deferred = yield* Deferred.make();
|
|
524
455
|
yield* Ref.update(this.pendingRef, (m) => HashMap.set(m, id, deferred));
|
|
525
|
-
// `socket.writer` gates on an internal Latch that `runRaw` only
|
|
526
|
-
// opens after the WebSocket hits OPEN. If the socket never
|
|
527
|
-
// opens, `runRaw`'s `ensuring` closes the latch and `write`
|
|
528
|
-
// blocks indefinitely — so we race the write against the
|
|
529
|
-
// pending Deferred (which the reader fails on close),
|
|
530
|
-
// short-circuiting the dead write.
|
|
531
456
|
const writeAttempt = Effect.either(state.value.write(JSON.stringify(frame)));
|
|
532
457
|
const earlyFailure = Deferred.await(deferred).pipe(Effect.catchAll(() => Effect.succeed(null)), Effect.as(null));
|
|
533
458
|
const writeRace = yield* Effect.race(writeAttempt, earlyFailure);
|
|
@@ -549,25 +474,16 @@ export class MoltZapWsClient {
|
|
|
549
474
|
}), Effect.onExit((exit) => Exit.isFailure(exit)
|
|
550
475
|
? Ref.update(this.pendingRef, (m) => HashMap.remove(m, id))
|
|
551
476
|
: Effect.void));
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
477
|
+
if (!definition.validateResult(result)) {
|
|
478
|
+
return yield* Effect.fail(new RpcServerError({
|
|
479
|
+
code: JSON_RPC_INTERNAL_ERROR_CODE,
|
|
480
|
+
message: `Invalid result for method: ${definition.name}`,
|
|
481
|
+
data: result,
|
|
482
|
+
}));
|
|
483
|
+
}
|
|
484
|
+
return result;
|
|
558
485
|
});
|
|
559
486
|
}
|
|
560
|
-
/**
|
|
561
|
-
* Bare-result RPC — projects the tracked RPC envelope onto its
|
|
562
|
-
* `result` field, discarding the `{id, type}` observability surface.
|
|
563
|
-
* The write / race / timeout plumbing lives in `sendRpcTrackedEffect`
|
|
564
|
-
* so there is one shared site for Invariant 3 (single id source),
|
|
565
|
-
* Invariant 5 (typed error channel), and the `socket.writer` latch
|
|
566
|
-
* race described below.
|
|
567
|
-
*/
|
|
568
|
-
sendRpcEffect(definition, params, opts) {
|
|
569
|
-
return this.sendRpcTrackedEffect(definition, params, opts).pipe(Effect.map((tracked) => tracked.result));
|
|
570
|
-
}
|
|
571
487
|
/**
|
|
572
488
|
* Translate a typed `OfferRejected` failure from the partitioned
|
|
573
489
|
* dispatcher into a wire-level error response. Spec #356 §5: each
|
|
@@ -583,19 +499,15 @@ export class MoltZapWsClient {
|
|
|
583
499
|
}
|
|
584
500
|
offerRejectedResponse(err, requestId) {
|
|
585
501
|
if (err instanceof PartitionLimitError) {
|
|
586
|
-
return
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
message: `Server busy: partition limit reached (${err.activePartitions}/${err.maxPartitions})`,
|
|
590
|
-
},
|
|
502
|
+
return encodeErrorResponse(requestId, {
|
|
503
|
+
code: -32000,
|
|
504
|
+
message: `Server busy: partition limit reached (${err.activePartitions}/${err.maxPartitions})`,
|
|
591
505
|
});
|
|
592
506
|
}
|
|
593
507
|
if (err instanceof PartitionQueueFullError) {
|
|
594
|
-
return
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
message: `Server busy: partition queue full (capacity=${err.capacity})`,
|
|
598
|
-
},
|
|
508
|
+
return encodeErrorResponse(requestId, {
|
|
509
|
+
code: -32000,
|
|
510
|
+
message: `Server busy: partition queue full (capacity=${err.capacity})`,
|
|
599
511
|
});
|
|
600
512
|
}
|
|
601
513
|
const _exhaustive = err;
|
|
@@ -621,11 +533,9 @@ export class MoltZapWsClient {
|
|
|
621
533
|
const handlers = yield* Ref.get(this.appCallbackHandlersRef);
|
|
622
534
|
const lookup = HashMap.get(handlers, request.definition);
|
|
623
535
|
const buildReply = lookup._tag === "None"
|
|
624
|
-
? Effect.succeed(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
message: "No handler registered for app callback descriptor",
|
|
628
|
-
},
|
|
536
|
+
? Effect.succeed(encodeErrorResponse(request.id, {
|
|
537
|
+
code: -32601,
|
|
538
|
+
message: "No handler registered for app callback descriptor",
|
|
629
539
|
}))
|
|
630
540
|
: lookup
|
|
631
541
|
.value(request.params, {
|
|
@@ -633,20 +543,17 @@ export class MoltZapWsClient {
|
|
|
633
543
|
definition: request.definition,
|
|
634
544
|
})
|
|
635
545
|
.pipe(Effect.match({
|
|
636
|
-
onSuccess: (result) =>
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
code: err.code,
|
|
642
|
-
message: err.message,
|
|
643
|
-
...(err.data !== undefined ? { data: err.data } : {}),
|
|
644
|
-
},
|
|
546
|
+
onSuccess: (result) => request.definition.encodeResponse(request.id, result),
|
|
547
|
+
onFailure: (err) => encodeErrorResponse(request.id, {
|
|
548
|
+
code: err.code,
|
|
549
|
+
message: err.message,
|
|
550
|
+
...(err.data !== undefined ? { data: err.data } : {}),
|
|
645
551
|
}),
|
|
646
552
|
}), Effect.catchAllCause((cause) => Effect.sync(() => {
|
|
647
553
|
this.options.logger?.warn("appCallback handler defected", Cause.pretty(cause));
|
|
648
|
-
return
|
|
649
|
-
|
|
554
|
+
return encodeErrorResponse(request.id, {
|
|
555
|
+
code: -32603,
|
|
556
|
+
message: "Internal error",
|
|
650
557
|
});
|
|
651
558
|
})));
|
|
652
559
|
const reply = yield* buildReply;
|
|
@@ -667,8 +574,9 @@ export class MoltZapWsClient {
|
|
|
667
574
|
if (decodedFrames === null)
|
|
668
575
|
return;
|
|
669
576
|
for (const decoded of decodedFrames) {
|
|
670
|
-
if (decoded._tag === "
|
|
671
|
-
|
|
577
|
+
if (decoded._tag === "ResponseSuccess" ||
|
|
578
|
+
decoded._tag === "ResponseError") {
|
|
579
|
+
const id = decoded.id;
|
|
672
580
|
const pending = yield* Ref.modify(this.pendingRef, (m) => {
|
|
673
581
|
const entry = HashMap.get(m, id);
|
|
674
582
|
return entry._tag === "Some"
|
|
@@ -677,7 +585,8 @@ export class MoltZapWsClient {
|
|
|
677
585
|
});
|
|
678
586
|
if (pending === null)
|
|
679
587
|
continue;
|
|
680
|
-
if (
|
|
588
|
+
if (decoded._tag === "ResponseError") {
|
|
589
|
+
const { error } = decoded;
|
|
681
590
|
yield* Deferred.fail(pending, new RpcServerError({
|
|
682
591
|
code: typeof error.code === "number"
|
|
683
592
|
? error.code
|
|
@@ -687,7 +596,7 @@ export class MoltZapWsClient {
|
|
|
687
596
|
}));
|
|
688
597
|
}
|
|
689
598
|
else {
|
|
690
|
-
yield* Deferred.succeed(pending, result);
|
|
599
|
+
yield* Deferred.succeed(pending, decoded.result);
|
|
691
600
|
}
|
|
692
601
|
continue;
|
|
693
602
|
}
|
|
@@ -725,15 +634,9 @@ export class MoltZapWsClient {
|
|
|
725
634
|
continue;
|
|
726
635
|
}
|
|
727
636
|
if (decoded._tag === "Notification") {
|
|
728
|
-
// Spec #222 §5.3: subscribers see the full Raw|Unknown union
|
|
729
|
-
// (conformance fuzz exercises both); waiters require a known
|
|
730
|
-
// descriptor to key the bucket map.
|
|
731
637
|
yield* this.subscribers.dispatch(decoded);
|
|
732
|
-
if (decoded.definition === undefined)
|
|
733
|
-
continue;
|
|
734
|
-
const knownDecoded = decoded;
|
|
735
638
|
const delivered = yield* Ref.modify(this.notificationWaitersRef, (m) => {
|
|
736
|
-
const bucket = HashMap.get(m,
|
|
639
|
+
const bucket = HashMap.get(m, decoded.definition);
|
|
737
640
|
if (bucket._tag === "None" || bucket.value.length === 0) {
|
|
738
641
|
return [null, m];
|
|
739
642
|
}
|
|
@@ -741,16 +644,16 @@ export class MoltZapWsClient {
|
|
|
741
644
|
const chosen = arr[arr.length - 1];
|
|
742
645
|
const rest = arr.slice(0, -1);
|
|
743
646
|
const nextMap = rest.length === 0
|
|
744
|
-
? HashMap.remove(m,
|
|
745
|
-
: HashMap.set(m,
|
|
647
|
+
? HashMap.remove(m, decoded.definition)
|
|
648
|
+
: HashMap.set(m, decoded.definition, rest);
|
|
746
649
|
return [chosen, nextMap];
|
|
747
650
|
});
|
|
748
651
|
if (delivered !== null) {
|
|
749
|
-
yield* delivered.complete(
|
|
652
|
+
yield* delivered.complete(decoded);
|
|
750
653
|
continue;
|
|
751
654
|
}
|
|
752
655
|
yield* Ref.update(this.notificationsBufferRef, (xs) => {
|
|
753
|
-
const appended = [...xs,
|
|
656
|
+
const appended = [...xs, decoded];
|
|
754
657
|
return appended.length > MAX_EVENT_BUFFER
|
|
755
658
|
? appended.slice(-MAX_EVENT_BUFFER)
|
|
756
659
|
: appended;
|