testing/transports/ws_client.ts

Shared WebSocket client surface for cross-backend tests.

WsClient is the interface every in-process or cross-process WS test driver implements — send / request / close / messages / wait_for. Two impls today:

Wire-frame types + predicates also live here so both impls (and every shared assertion helper) reference one source.

view source

Declarations
#

12 declarations

deliver_inbound
#

testing/transports/ws_client.ts view source

(parsed: unknown, received: unknown[], waiters: WsWaiter[], on_request: WsRequestResponder | undefined, reply: (message: unknown) => void): void import {deliver_inbound} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Deliver one parsed inbound frame to a client's receive sink — the shared parse→(respond|push)→resolve core.

A server-initiated request ({method, id}) is answered by on_request when supplied (replying via reply) and is not surfaced as a normal message; everything else — notifications, replies to the client's own requests, and non-JSON frames — is pushed onto received and resolves any matching waiters, exactly as before. With no on_request, every frame (including a server-initiated request) flows to the sink unchanged, so the seam is purely additive.

parsed

type unknown

received

type unknown[]

waiters

type WsWaiter[]

on_request

type WsRequestResponder | undefined

reply

type (message: unknown) => void

returns

void

is_notification
#

testing/transports/ws_client.ts view source

(method: string): (msg: unknown) => boolean import {is_notification} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Predicate matching a JSON-RPC notification with the given method name.

method

type string

returns

(msg: unknown) => boolean

is_notification_with
#

testing/transports/ws_client.ts view source

<P>(method: string, match: (params: P) => boolean): (msg: unknown) => msg is JsonrpcNotificationFrame<P> import {is_notification_with} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Type-guard combinator: match a notification whose typed params satisfies match. Collapses the common test pattern of casting msg to JsonrpcNotificationFrame<P> in every predicate body.

const match_roster_for = (id: Uuid) => is_notification_with<RosterChangedParams>( WORLD_METHODS.roster_changed, (params) => params.character_id === id && !params.removed, ); const roster = await client.wait_for(match_roster_for(char_id));

method

type string

match

type (params: P) => boolean

returns

(msg: unknown) => msg is JsonrpcNotificationFrame<P>

generics

is_notification_with<P>
P

is_response_for
#

testing/transports/ws_client.ts view source

(id: string | number): (msg: unknown) => boolean import {is_response_for} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Predicate matching a JSON-RPC response frame (success or error) for the given request id.

id

type string | number

returns

(msg: unknown) => boolean

JsonrpcErrorResponseFrame
#

testing/transports/ws_client.ts view source

JsonrpcErrorResponseFrame<D> import type {JsonrpcErrorResponseFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcErrorResponseFrame<D = unknown>
D
default unknown

jsonrpc

type typeof JSONRPC_VERSION

id

type number | string

error

type {code: number; message: string; data?: D}

JsonrpcNotificationFrame
#

testing/transports/ws_client.ts view source

JsonrpcNotificationFrame<P> import type {JsonrpcNotificationFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcNotificationFrame<P = unknown>
P
default unknown

jsonrpc

type typeof JSONRPC_VERSION

method

type string

params

type P

JsonrpcSuccessResponseFrame
#

testing/transports/ws_client.ts view source

JsonrpcSuccessResponseFrame<R> import type {JsonrpcSuccessResponseFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcSuccessResponseFrame<R = unknown>
R
default unknown

jsonrpc

type typeof JSONRPC_VERSION

id

type number | string

result

type R

WS_CLIENT_DEFAULT_TIMEOUT_MS
#

testing/transports/ws_client.ts view source

1000 import {WS_CLIENT_DEFAULT_TIMEOUT_MS} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Default wait-for timeout shared across in-process + cross-process impls. Tunable per-call via the timeout_ms parameter.

WsClient
#

testing/transports/ws_client.ts view source

WsClient import type {WsClient} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

A test WS client: send requests, inspect / await incoming messages.

send

Send a JSON-RPC message (request or notification) to the server.

type (message: unknown) => Promise<void>

throws

  • Error - if called after `close()` resolves — every impl

request

Send a JSON-RPC request and await its response. Resolves with the result; throws with a useful message (code, text, and any data payload) on an error frame — without this, asserting on result.foo for a failed request throws Cannot read property 'foo' of undefined, hiding the real cause. Use send + wait_for(is_response_for(id)) directly when the test needs to assert on the error frame itself.

type <R = unknown>( id: number | string, method: string, params: unknown, timeout_ms?: number, ) => Promise<R>

throws

  • Error - if the server returns a JSON-RPC error frame for `id`,

close

Close the connection. Returns a promise that resolves once the transport's own cleanup (and any on_socket_close for the in-process driver) has completed — tests that assert on post-close state should await.

type (code?: number, reason?: string) => Promise<void>

wait_for_close

Wait for the server to close the connection. Resolves true if the socket closed within timeout_ms, false on timeout. The signal for server-initiated close — used by close-on-revoke tests that fire a revocation over a side channel and assert the live socket drops.

Resolves true immediately when the socket is already closed. Distinct from close() (client-initiated): this awaits a close the test did not request. Mirrors wait_for_close on the SSE frame reader in testing/sse_round_trip.ts.

type (timeout_ms?: number) => Promise<boolean>

messages

Every message the server has sent, in arrival order.

type ReadonlyArray<unknown>

readonly

wait_for

Wait until a message satisfies predicate. Matches are checked against already-received messages first, then new arrivals until the timeout (defaults to WS_CLIENT_DEFAULT_TIMEOUT_MS).

When predicate is a type guard (e.g. is_notification_with<P>), the result is narrowed automatically and callers don't need to spell <JsonrpcNotificationFrame<P>> on the call site.

type { <T>(predicate: (msg: unknown) => msg is T, timeout_ms?: number): Promise<T>; // eslint-disable-next-line @typescript-eslint/unified-signatures <T = unknown>(predicate: (msg: unknown) => boolean, timeout_ms?: number): Promise<T>; }

throws

  • Error - if `timeout_ms` elapses before a matching message

WsRequestResponder
#

testing/transports/ws_client.ts view source

WsRequestResponder import type {WsRequestResponder} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Answers a server-initiated request (the server→client direction ActionPeer adds). Passed at client construction so it's attached before the upgrade completes — otherwise it races a connect-time ping. Receives the parsed request frame and returns the reply outcome (or a promise of it).

(call)

type (request: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }): WsResponderOutcome | Promise<WsResponderOutcome>

request

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }
returns WsResponderOutcome | Promise<WsResponderOutcome>

WsResponderOutcome
#

testing/transports/ws_client.ts view source

WsResponderOutcome import type {WsResponderOutcome} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

What a WsRequestResponder returns for a server-initiated request:

  • {result} → reply with a JSON-RPC success
  • {error} → reply with a JSON-RPC error envelope
  • undefined → send nothing (models a never-replying peer — exercises the server-side Timeout)

WsWaiter
#

testing/transports/ws_client.ts view source

WsWaiter import type {WsWaiter} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

A pending wait_for entry — predicate + its resolver.

predicate

type (msg: unknown) => boolean

resolve

type (msg: unknown) => void

Depends on
#

Imported by
#