bridgething
Surfaces ↓

Net

client.net

Daemon -> webapp network surface: replies to fetch and ws.open, WebSocket frame/close/error events, and the Stream* event trio driven by net.stream.open.

Requests

you ask, the daemon answers; await the tagged result and check .ok

fetch(NetFetch): Promise<TypedRequestResult<NetFetchReply, NetFetchErrorReply>>

push form: onFetchReply (subscribe instead of awaiting)

Payload for net.fetch: a single proxied HTTP request/response round-trip through the connected companion's network stack.

const res = await client.net.fetch({ request: { /* NetFetchRequest */ } });
if (res.ok) {
  console.log(res.response.response);
} else {
  console.warn(res.kind, res.error);
}

wsOpen(NetWsOpen): Promise<TypedRequestResult<NetWsOpenReply, NetWsErrorReply>>

push form: onWsOpenReply (subscribe instead of awaiting)

const res = await client.net.wsOpen({ connectionId: '...', url: '...' });
if (res.ok) {
  console.log(res.response.acceptedProtocol);
} else {
  console.warn(res.kind, res.error);
}

Commands

fire-and-forget; the promise resolves once the daemon has taken the message

wsClose(NetWsClose): Promise<void>

await client.net.wsClose({ connectionId: '...' });

wsSend(NetWsSend): Promise<void>

await client.net.wsSend({ connectionId: '...', frame: { /* WsFrame */ } });

streamOpen(NetStreamOpen): Promise<void>

await client.net.streamOpen({ streamId: '...', request: { /* NetFetchRequest */ } });

streamCancel(NetStreamCancel): Promise<void>

await client.net.streamCancel({ streamId: '...' });

Events

the daemon pushes these unprompted; subscribing returns an unsubscribe function

onWsMessage(handler: (NetWsMessage) => void): () => void

const off = client.net.onWsMessage((netWsMessage) => {
  console.log(netWsMessage.connectionId);
});
// call off() to unsubscribe

onWsClosed(handler: (NetWsClosed) => void): () => void

const off = client.net.onWsClosed((netWsClosed) => {
  console.log(netWsClosed.connectionId);
});
// call off() to unsubscribe

onWsErrorEvent(handler: (NetWsErrorEvent) => void): () => void

const off = client.net.onWsErrorEvent((event) => {
  console.log(event.connectionId);
});
// call off() to unsubscribe

onStreamBegin(handler: (StreamBegin) => void): () => void

const off = client.net.onStreamBegin((streamBegin) => {
  console.log(streamBegin.streamId);
});
// call off() to unsubscribe

onStreamChunk(handler: (StreamChunk) => void): () => void

const off = client.net.onStreamChunk((streamChunk) => {
  console.log(streamChunk.streamId);
});
// call off() to unsubscribe

onStreamEnd(handler: (StreamEnd) => void): () => void

const off = client.net.onStreamEnd((streamEnd) => {
  console.log(streamEnd.streamId);
});
// call off() to unsubscribe

onStreamError(handler: (StreamError) => void): () => void

const off = client.net.onStreamError((streamError) => {
  console.log(streamError.streamId);
});
// call off() to unsubscribe

Types

shapes referenced above, as the SDK types them

type NetWsMessage = {
  connectionId: string;
  frame: WsFrame;
};
type NetWsClosed = {
  connectionId: string;
  code: number;
  reason: string;
};
type NetWsErrorEvent = {
  connectionId: string;
  error: WsError;
};

First event of an open stream. Carries the response status, headers, and (when known) total payload size so the consumer can preallocate or display progress. Subsequent StreamChunk and StreamEnd events for the same stream_id follow.

type StreamBegin = {
  streamId: string;
  status: number;
  headers: HttpHeader[];
  totalSize?: number;
};

One body chunk. Chunks arrive in order; offset is the byte position of bytes[0] within the full body.

type StreamChunk = {
  streamId: string;
  offset: number;
  bytes: number[];
};

Terminates a stream. After End no further chunks for stream_id are valid and the daemon clears its routing entry.

type StreamEnd = {
  streamId: string;
};

Stream failed mid-flight (or before the first byte). Terminal - the daemon clears its routing entry. The error shape is shared with fetch since the failure modes are identical.

type StreamError = {
  streamId: string;
  error: NetError;
};

Payload for net.fetch: a single proxied HTTP request/response round-trip through the connected companion's network stack.

type NetFetch = {
  request: NetFetchRequest;
};
type NetFetchReply = {
  response: NetFetchResponse;
};
type NetFetchErrorReply = {
  error: NetError;
};
type NetWsOpen = {
  connectionId: string;
  url: string;
  protocols?: string[];
  headers?: HttpHeader[];
};
type NetWsOpenReply = {
  acceptedProtocol?: string;
};
type NetWsErrorReply = {
  error: WsError;
};
type NetWsClose = {
  connectionId: string;
  code?: number;
  reason?: string;
};
type NetWsSend = {
  connectionId: string;
  frame: WsFrame;
};
type NetStreamOpen = {
  streamId: string;
  request: NetFetchRequest;
};
type NetStreamCancel = {
  streamId: string;
};
type WsFrame =
  | { type: 'text'; data: string }
  | { type: 'binary'; data: Uint8Array };
type WsError =
  | 'connectFailed'
  | 'frameTooLarge'
  | 'gatewayDisconnected'
  | 'protocolError';

One header on an HTTP request or response. Key order is preserved across serialize/deserialize.

type HttpHeader = {
  name: string;
  value: string;
};
type NetError = 'requestFailed' | 'timeout' | 'unavailable' | 'noGateway';
type NetFetchRequest = {
  url: string;
  method: HttpMethod;
  headers: HttpHeader[];
  body?: number[];
  timeoutMs?: number;
  redirect: RedirectPolicy;
};
type NetFetchResponse = {
  status: number;
  headers: HttpHeader[];
  body: number[];
};
type HttpMethod =
  | 'get'
  | 'head'
  | 'post'
  | 'put'
  | 'patch'
  | 'delete'
  | 'options';
type RedirectPolicy =
  | 'follow' // Follow up to a gateway-defined cap (typically 5).
  | 'manual' // Surface the redirect status to the caller; do not follow.
  | 'error' // Treat any 3xx as an error.;