@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.
Files changed (93) hide show
  1. package/dist/cli/commands/contacts.d.ts.map +1 -1
  2. package/dist/cli/commands/contacts.js +3 -3
  3. package/dist/cli/commands/contacts.js.map +1 -1
  4. package/dist/cli/commands/conversations.d.ts.map +1 -1
  5. package/dist/cli/commands/conversations.js +11 -11
  6. package/dist/cli/commands/conversations.js.map +1 -1
  7. package/dist/cli/commands/messages.d.ts.map +1 -1
  8. package/dist/cli/commands/messages.js +3 -3
  9. package/dist/cli/commands/messages.js.map +1 -1
  10. package/dist/cli/commands/send.d.ts.map +1 -1
  11. package/dist/cli/commands/send.js +3 -3
  12. package/dist/cli/commands/send.js.map +1 -1
  13. package/dist/cli/commands/test-transport.d.ts.map +1 -1
  14. package/dist/cli/commands/test-transport.js +11 -8
  15. package/dist/cli/commands/test-transport.js.map +1 -1
  16. package/dist/cli/http-client.d.ts +2 -2
  17. package/dist/cli/http-client.d.ts.map +1 -1
  18. package/dist/cli/socket-client.d.ts +4 -3
  19. package/dist/cli/socket-client.d.ts.map +1 -1
  20. package/dist/cli/socket-client.js +6 -4
  21. package/dist/cli/socket-client.js.map +1 -1
  22. package/dist/cli/transport.d.ts +3 -3
  23. package/dist/cli/transport.d.ts.map +1 -1
  24. package/dist/cli/transport.js.map +1 -1
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/__tests__/app-callback-test-requests.d.ts +2 -2
  29. package/dist/internal/__tests__/app-callback-test-requests.d.ts.map +1 -1
  30. package/dist/internal/__tests__/app-callback-test-requests.js +3 -2
  31. package/dist/internal/__tests__/app-callback-test-requests.js.map +1 -1
  32. package/dist/internal/app-callback-dispatcher-errors.d.ts +3 -3
  33. package/dist/internal/app-callback-dispatcher-errors.d.ts.map +1 -1
  34. package/dist/internal/app-callback-partition-key.d.ts +2 -2
  35. package/dist/internal/app-callback-partition-key.d.ts.map +1 -1
  36. package/dist/runtime/errors.d.ts +2 -12
  37. package/dist/runtime/errors.d.ts.map +1 -1
  38. package/dist/runtime/errors.js +4 -3
  39. package/dist/runtime/errors.js.map +1 -1
  40. package/dist/runtime/frame.d.ts +20 -19
  41. package/dist/runtime/frame.d.ts.map +1 -1
  42. package/dist/runtime/frame.js +49 -51
  43. package/dist/runtime/frame.js.map +1 -1
  44. package/dist/runtime/frame.test.js +28 -34
  45. package/dist/runtime/frame.test.js.map +1 -1
  46. package/dist/runtime/subscribers.d.ts +8 -11
  47. package/dist/runtime/subscribers.d.ts.map +1 -1
  48. package/dist/runtime/subscribers.js.map +1 -1
  49. package/dist/runtime/subscribers.test.js +8 -14
  50. package/dist/runtime/subscribers.test.js.map +1 -1
  51. package/dist/service.d.ts +5 -8
  52. package/dist/service.d.ts.map +1 -1
  53. package/dist/service.js +44 -65
  54. package/dist/service.js.map +1 -1
  55. package/dist/service.test.js +9 -9
  56. package/dist/service.test.js.map +1 -1
  57. package/dist/test/index.d.ts +1 -1
  58. package/dist/test/index.js +1 -1
  59. package/dist/test-utils/conformance-adapter.d.ts +5 -5
  60. package/dist/test-utils/conformance-adapter.d.ts.map +1 -1
  61. package/dist/test-utils/conformance-adapter.js +26 -29
  62. package/dist/test-utils/conformance-adapter.js.map +1 -1
  63. package/dist/test-utils/fake-service.d.ts +4 -4
  64. package/dist/test-utils/fake-service.d.ts.map +1 -1
  65. package/dist/test-utils/fake-service.js +19 -10
  66. package/dist/test-utils/fake-service.js.map +1 -1
  67. package/dist/test-utils/ids.d.ts +2 -6
  68. package/dist/test-utils/ids.d.ts.map +1 -1
  69. package/dist/test-utils/ids.js +1 -2
  70. package/dist/test-utils/ids.js.map +1 -1
  71. package/dist/ws-client.d.ts +11 -89
  72. package/dist/ws-client.d.ts.map +1 -1
  73. package/dist/ws-client.js +56 -153
  74. package/dist/ws-client.js.map +1 -1
  75. package/dist/ws-client.test.js +35 -48
  76. package/dist/ws-client.test.js.map +1 -1
  77. package/package.json +3 -4
  78. package/dist/runtime/index.d.ts +0 -3
  79. package/dist/runtime/index.d.ts.map +0 -1
  80. package/dist/runtime/index.js +0 -3
  81. package/dist/runtime/index.js.map +0 -1
  82. package/dist/runtime/notification-types.types-check.d.ts +0 -2
  83. package/dist/runtime/notification-types.types-check.d.ts.map +0 -1
  84. package/dist/runtime/notification-types.types-check.js +0 -57
  85. package/dist/runtime/notification-types.types-check.js.map +0 -1
  86. package/dist/service.typed-bridge.test.d.ts +0 -2
  87. package/dist/service.typed-bridge.test.d.ts.map +0 -1
  88. package/dist/service.typed-bridge.test.js +0 -93
  89. package/dist/service.typed-bridge.test.js.map +0 -1
  90. package/dist/ws-client.typed-bridge.test.d.ts +0 -2
  91. package/dist/ws-client.typed-bridge.test.d.ts.map +0 -1
  92. package/dist/ws-client.typed-bridge.test.js +0 -105
  93. package/dist/ws-client.typed-bridge.test.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
- import { Connect, NotConnectedError, RpcServerError, RpcTimeoutError, type AnyTaskCallbackRpcDefinition, type AnyNotificationDefinition, type JsonRpcStringId, type ParamsOf, type ResultOf, type RpcDefinition, type TSchema, type Static } from "@moltzap/protocol";
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, type RawDecodedNotification } from "./runtime/frame.js";
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 = Static<typeof Connect.resultSchema>;
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 `taskCallbackRpcMethods` at each `handleServerRpc(definition,
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: JsonRpcStringId;
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: Static<D["paramsSchema"]>, ctx: ServerRpcContext & {
47
+ export type ServerRpcHandler<D extends AnyTaskCallbackRpcDefinition = AnyTaskCallbackRpcDefinition> = (params: ParamsOf<D>, ctx: ServerRpcContext & {
48
48
  readonly definition: D;
49
- }) => Effect.Effect<Static<D["resultSchema"]>, RpcServerError>;
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
- * Return shape of `MoltZapWsClient.sendRpcTracked`. Surfaces the outbound
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 auth/connect, resolve with HelloOk. Fails
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, TSchema, TSchema>>(definition: D, params: ParamsOf<D>, opts?: RpcCallOptions): Effect.Effect<ResultOf<D>, NotConnectedError | RpcTimeoutError | RpcServerError>;
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(): AnyRawDecodedNotification[];
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
@@ -1 +1 @@
1
- {"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"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"}
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, decodeRpcResult, jsonRpcStringId, NotConnectedError, requestFrame, responseFrame, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
4
+ import { PROTOCOL_VERSION, Connect, encodeErrorResponse, NotConnectedError, RpcServerError, RpcTimeoutError, } from "@moltzap/protocol";
5
5
  import { DuplicateServerRpcHandlerError } from "./runtime/errors.js";
6
- import { decodeFrames, } from "./runtime/frame.js";
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
- function notificationMatches(definition, notification) {
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 → auth/connect → active. On disconnect,
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 auth/connect, resolve with HelloOk. Fails
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, logger))
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, logger)
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 auth/connect response, or
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 auth/connect: the write goes through
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 auth/connect response against the handshakeSettled deferred
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 auth/connect error would hang behind the
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 id = jsonRpcStringId(`rpc-${++this.requestCounter}`);
518
- const frame = requestFrame(id, definition, params);
519
- // Register the Deferred BEFORE writing. `write` yields to the
520
- // scheduler; the reader could interleave, see a close, and
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
- const decodedResult = yield* decodeRpcResult(definition, result).pipe(Effect.mapError(() => new RpcServerError({
553
- code: JSON_RPC_INTERNAL_ERROR_CODE,
554
- message: `Invalid result for method: ${definition.name}`,
555
- data: result,
556
- })));
557
- return { id, result: decodedResult };
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 responseFrame(requestId, {
587
- error: {
588
- code: -32000,
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 responseFrame(requestId, {
595
- error: {
596
- code: -32000,
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(responseFrame(request.id, {
625
- error: {
626
- code: -32601,
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) => responseFrame(request.id, {
637
- result,
638
- }),
639
- onFailure: (err) => responseFrame(request.id, {
640
- error: {
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 responseFrame(request.id, {
649
- error: { code: -32603, message: "Internal error" },
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 === "Response") {
671
- const { id, result, error } = decoded;
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 (error) {
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, knownDecoded.definition);
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, knownDecoded.definition)
745
- : HashMap.set(m, knownDecoded.definition, rest);
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(knownDecoded);
652
+ yield* delivered.complete(decoded);
750
653
  continue;
751
654
  }
752
655
  yield* Ref.update(this.notificationsBufferRef, (xs) => {
753
- const appended = [...xs, knownDecoded];
656
+ const appended = [...xs, decoded];
754
657
  return appended.length > MAX_EVENT_BUFFER
755
658
  ? appended.slice(-MAX_EVENT_BUFFER)
756
659
  : appended;