actions/peer_ping.ts

Shared peer/ping action — the protocol action that exercises the server→client request/response direction of ActionPeer.

peer/ping is initiator: 'both': the client→server direction overlaps with heartbeat, but the new direction this action drives is the reverse — a connected client invokes peer/ping over its socket, and the handler turns around and *initiates* a peer/ping request back to that same socket (ctx.request_client), awaits the client's echo, validates it against PingResponse, and returns the validated shape. So one client RPC drives the whole round-trip and every outcome surfaces as that RPC's wire response.

Public auth. A liveness echo is non-sensitive; the WebSocket upgrade itself still authenticates, so only admitted sockets reach the handler.

No return transport (HTTP RPC). Over HTTP there is no socket to ping — ctx.request_client is absent — so the handler refuses with peer_no_transport rather than method_not_found. That requires the action to be mounted on the HTTP RPC endpoint as well as the WS endpoint (it is, via the spine's full mount); the manifest excludes it as a protocol action so the cross-impl diff stays apples-to-apples.

The data.reason discriminators and the PingResponse shape are the cross-impl wire contract — twins of the Rust spine's REASON_PEER_* constants and PingResponse (parity by test, not codegen).

Frontend-safe. Like heartbeat.ts / cancel.ts, this module names the dispatcher types (ActionContext, RpcAction) type-only and builds peer_ping_action as a plain literal — it never imports the runtime action_rpc.ts (which would drag the HTTP dispatch core + Hono into any frontend that pulls protocol_action_specs). The frontend responder (peer_ping_responder) lives here too so a real frontend can answer the server's probe.

view source

Declarations
#

15 declarations

peer_ping_action
#

actions/peer_ping.ts view source

RpcAction import {peer_ping_action} from '@fuzdev/fuz_app/actions/peer_ping.js';

Protocol-action tuple — spread into the server's actions array (via protocol_actions from actions/protocol.ts) so the dispatcher resolves the peer/ping handler on both the WS endpoint and (for the no-transport refusal) the HTTP RPC endpoint. A plain RpcAction literal (not rpc_action(...)) so this module stays free of the runtime action_rpc.ts import — the same frontend-safety discipline heartbeat_action / cancel_action follow; the handler's input/output are already pinned by peer_ping_handler's signature. Usable directly in the spine's RpcAction[] full mount and the Action[] protocol bundle alike.

peer_ping_action_spec
#

actions/peer_ping.ts view source

{ method: string; kind: "request_response"; initiator: "both"; auth: { account: "none"; actor: "none"; }; side_effects: false; input: ZodDefault<ZodObject<{ nonce: ZodOptional<ZodNumber>; timeout_ms: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; async: true; description: string; } import {peer_ping_action_spec} from '@fuzdev/fuz_app/actions/peer_ping.js';

ActionSpec for the shared peer/ping. initiator: 'both' (the client→server invocation drives a server→client request); auth: public (liveness is non-sensitive; the upgrade authenticated the socket); side_effects: false (no state change — the handler only round-trips a ping).

peer_ping_handler
#

actions/peer_ping.ts view source

(input: { nonce?: number | undefined; timeout_ms?: number | undefined; }, ctx: ActionContext): Promise<{ nonce: number; protocol_version: number; }> import {peer_ping_handler} from '@fuzdev/fuz_app/actions/peer_ping.js';

Handler — initiates a peer/ping request back to the originating client, awaits the reply, validates it against PingResponse, and returns it.

input

type { nonce?: number | undefined; timeout_ms?: number | undefined; }

ctx

returns

Promise<{ nonce: number; protocol_version: number; }>

throws

  • ThrownJsonrpcError - with `data.reason` of `peer_no_transport` (HTTP),

PEER_PING_METHOD
#

peer_ping_responder
#

actions/peer_ping.ts view source

(params: unknown): { nonce: number; protocol_version: number; } import {peer_ping_responder} from '@fuzdev/fuz_app/actions/peer_ping.js';

Reusable responder for an inbound *server→client* peer/ping request — the mirror of peer_ping_handler, run by the client (frontend). Validates the request params and echoes a PingResponse carrying the issued nonce. Pure + transport-agnostic (the production twin of the cross-process test transport's echo_responder); FrontendWebsocketTransport wires it so a real frontend answers the server's liveness probe with zero consumer plumbing. Falls back to nonce: 0 on a malformed request — the server then surfaces a nonce mismatch, the correct outcome for a bad probe.

params

the inbound request's params (PingRequestParams shape)

type unknown

returns

{ nonce: number; protocol_version: number; }

the PingResponse echo to send back as the reply's result

PEER_PROTOCOL_VERSION
#

actions/peer_ping.ts view source

1 import {PEER_PROTOCOL_VERSION} from '@fuzdev/fuz_app/actions/peer_ping.js';

Wire-protocol version reported in peer/ping replies (PingResponse.protocol_version). Twin of the Rust spine's value — bump in lockstep with a breaking peer-protocol change. The far side validates the reply's *shape*, not this value, so it's informational telemetry today, but it's the hook a future protocol negotiation reads.

PingActionInput
#

actions/peer_ping.ts view source

ZodDefault<ZodObject<{ nonce: ZodOptional<ZodNumber>; timeout_ms: ZodOptional<ZodNumber>; }, $strict>> import type {PingActionInput} from '@fuzdev/fuz_app/actions/peer_ping.js';

Input to the client→server peer/ping invocation. Both fields optional (mirrors the Rust PingActionInput): nonce defaults to a server-issued value, timeout_ms to DEFAULT_PEER_REQUEST_TIMEOUT (clamped shorten-only).

PingRequestParams
#

actions/peer_ping.ts view source

ZodObject<{ nonce: ZodNumber; }, $strict> import type {PingRequestParams} from '@fuzdev/fuz_app/actions/peer_ping.js';

Params of the server→client peer/ping request frame. Twin of the Rust PingRequest.

PingResponse
#

actions/peer_ping.ts view source

ZodObject<{ nonce: ZodNumber; protocol_version: ZodNumber; }, $strict> import type {PingResponse} from '@fuzdev/fuz_app/actions/peer_ping.js';

The client's reply shape — also the action's output (the handler returns the validated echo). Twin of the Rust PingResponse.

REASON_PEER_CONNECTION_GONE
#

actions/peer_ping.ts view source

"peer_connection_gone" import {REASON_PEER_CONNECTION_GONE} from '@fuzdev/fuz_app/actions/peer_ping.js';

The socket closed before the peer replied.

REASON_PEER_NO_TRANSPORT
#

actions/peer_ping.ts view source

"peer_no_transport" import {REASON_PEER_NO_TRANSPORT} from '@fuzdev/fuz_app/actions/peer_ping.js';

No return socket — the action ran over a transport that can't initiate a server→client request (HTTP RPC).

REASON_PEER_PING_INVALID_REPLY
#

REASON_PEER_PING_NONCE_MISMATCH
#

actions/peer_ping.ts view source

"peer_ping_nonce_mismatch" import {REASON_PEER_PING_NONCE_MISMATCH} from '@fuzdev/fuz_app/actions/peer_ping.js';

The peer replied with a PingResponse whose nonce didn't echo the issued one.

REASON_PEER_TIMEOUT
#

actions/peer_ping.ts view source

"peer_timeout" import {REASON_PEER_TIMEOUT} from '@fuzdev/fuz_app/actions/peer_ping.js';

The peer did not reply within the deadline.

REASON_PEER_TOO_MANY_IN_FLIGHT
#

actions/peer_ping.ts view source

"peer_too_many_in_flight" import {REASON_PEER_TOO_MANY_IN_FLIGHT} from '@fuzdev/fuz_app/actions/peer_ping.js';

The per-connection in-flight server→client request cap was hit.

Depends on
#

Imported by
#