UNPKG

@firtoz/socka

Version:

Standard Schema–first WebSocket RPC for TypeScript — Bun, Hono, Node ws, Cloudflare Workers, Durable Objects

195 lines (191 loc) • 6.34 kB
import { pack, unpack } from 'msgpackr'; import { exhaustiveGuard } from '@firtoz/maybe-error'; // src/core/validate.ts async function parseStandardSchema(schema, value) { const result = await schema["~standard"].validate(value); if (result.issues) { const messages = result.issues.map((issue) => issue.message).join("; "); throw new Error(messages || "Validation failed"); } return result.value; } // src/core/socka-error.ts var SockaError = class _SockaError extends Error { constructor(message, options) { super(message); this.name = "SockaError"; this.requestId = options?.requestId; this.rpc = options?.rpc; this.code = options?.code; this.data = options?.data; if (options?.cause !== void 0) { Object.defineProperty(this, "cause", { value: options.cause, configurable: true, enumerable: false, writable: true }); } Object.setPrototypeOf(this, _SockaError.prototype); } /** Builds a {@link SockaError} from a standard RPC error envelope. */ static fromWire(msg) { return new _SockaError(msg.error, { requestId: msg.id, rpc: msg.rpc, code: msg.code, data: msg.data }); } }; // src/core/envelope.ts var SOCKA_WIRE_VERSION = 1; var SockaWireError = class extends Error { constructor() { super(...arguments); this.name = "SockaWireError"; } }; function isRecord(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function decodeSockaWire(parsed) { if (!isRecord(parsed)) { throw new SockaWireError("socka: expected a JSON object"); } if (parsed.socka === void 0) { throw new SockaWireError('socka: missing "socka" discriminator'); } if (parsed.v !== SOCKA_WIRE_VERSION) { throw new SockaWireError("socka: unsupported wire version"); } const socka = parsed.socka; if (socka === "clientRequest" && typeof parsed.id === "string" && typeof parsed.rpc === "string" && isRecord(parsed.body)) { return { kind: "clientRequest", frame: parsed }; } if (socka === "serverResponse" && typeof parsed.id === "string" && typeof parsed.rpc === "string") { return { kind: "serverResponse", frame: parsed }; } if (socka === "serverError" && typeof parsed.id === "string" && typeof parsed.error === "string") { const code = "code" in parsed && typeof parsed.code === "string" ? parsed.code : void 0; const data = "data" in parsed ? parsed.data : void 0; const rpc = "rpc" in parsed && typeof parsed.rpc === "string" ? parsed.rpc : void 0; const frame = { socka: "serverError", v: SOCKA_WIRE_VERSION, id: parsed.id, error: parsed.error, ...rpc !== void 0 ? { rpc } : {}, ...code !== void 0 ? { code } : {}, ...data !== void 0 ? { data } : {} }; return { kind: "serverError", frame }; } if (socka === "serverEvent" && typeof parsed.event === "string") { return { kind: "serverEvent", frame: parsed }; } throw new SockaWireError( `socka: unknown or invalid frame kind ${String(socka)}` ); } function encodeClientRequest(id, rpc, body) { return { socka: "clientRequest", v: SOCKA_WIRE_VERSION, id, rpc, body }; } function encodeServerResponse(id, rpc, body) { return { socka: "serverResponse", v: SOCKA_WIRE_VERSION, id, rpc, body }; } function encodeServerError(id, error, extra) { return { socka: "serverError", v: SOCKA_WIRE_VERSION, id, error, ...extra?.rpc !== void 0 ? { rpc: extra.rpc } : {}, ...extra?.code !== void 0 ? { code: extra.code } : {}, ...extra?.data !== void 0 ? { data: extra.data } : {} }; } function encodeServerEvent(event, body) { return { socka: "serverEvent", v: SOCKA_WIRE_VERSION, event, body }; } function encodeSockaWire(frame, format, serializeJson = JSON.stringify) { if (format === "json") { return serializeJson(frame); } return pack(frame); } function parseWirePayload(data, format, deserializeJson = JSON.parse) { if (format === "json") { if (typeof data !== "string") { throw new Error("socka: expected a JSON text frame"); } return deserializeJson(data); } if (data instanceof Uint8Array) { return unpack(data); } if (data instanceof ArrayBuffer) { return unpack(new Uint8Array(data)); } throw new Error("socka: expected an ArrayBuffer or Uint8Array msgpack frame"); } function defaultReportError(event) { switch (event.kind) { case "clientEventListener": console.error("socka: event listener error", event.error); return; case "clientEventValidation": console.error("socka: event validation error", event.error); return; case "clientFireAndForgetRpcError": console.error("socka: fire-and-forget RPC error", event.error); return; case "clientOrphanServerError": console.error("socka: orphan serverError (no pending RPC)", event.error); return; case "clientUnexpectedServerResponse": console.error( "socka: unexpected serverResponse for fire-and-forget RPC", event.rpc, event.requestId ); return; case "serverOnAttached": console.error("socka: onAttached error:", event.error); return; case "serverInboundMessage": if (event.adapter === "hono") { console.error("socka: onMessage error:", event.error); } else { console.error("socka: message handler error:", event.error); } return; case "serverHandleClose": console.error("socka: handleClose error:", event.error); return; case "serverShutdown": if (event.adapter === "hono") { console.error("socka: onClose error:", event.error); } else { console.error("socka: shutdown error:", event.error); } return; default: exhaustiveGuard(event); } } function reportSockaError(reportError, event) { (reportError ?? defaultReportError)(event); } export { SOCKA_WIRE_VERSION, SockaError, SockaWireError, decodeSockaWire, defaultReportError, encodeClientRequest, encodeServerError, encodeServerEvent, encodeServerResponse, encodeSockaWire, parseStandardSchema, parseWirePayload, reportSockaError }; //# sourceMappingURL=chunk-IFIGKR3W.js.map //# sourceMappingURL=chunk-IFIGKR3W.js.map