@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
JavaScript
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