@firtoz/socka 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +191 -40
  2. package/dist/{SockaWebSocketSession-Bru8yFcK.d.ts → SockaWebSocketSession-Cza7Fti-.d.ts} +87 -5
  3. package/dist/bun/index.d.ts +28 -3
  4. package/dist/bun/index.js +28 -5
  5. package/dist/bun/index.js.map +1 -1
  6. package/dist/{chunk-MZCQHJXY.js → chunk-2FNWVCP3.js} +27 -8
  7. package/dist/chunk-2FNWVCP3.js.map +1 -0
  8. package/dist/{chunk-AM7PB26G.js → chunk-H3S3435J.js} +125 -3
  9. package/dist/chunk-H3S3435J.js.map +1 -0
  10. package/dist/{chunk-45D4T232.js → chunk-JVLUA3Q5.js} +64 -6
  11. package/dist/chunk-JVLUA3Q5.js.map +1 -0
  12. package/dist/chunk-KQO5AVKA.js +8 -0
  13. package/dist/chunk-KQO5AVKA.js.map +1 -0
  14. package/dist/client/index.d.ts +59 -3
  15. package/dist/client/index.js +2 -2
  16. package/dist/core/index.d.ts +5 -1
  17. package/dist/core/index.js +1 -1
  18. package/dist/do/index.d.ts +20 -2
  19. package/dist/do/index.js +35 -2
  20. package/dist/do/index.js.map +1 -1
  21. package/dist/hono/cloudflare-workers.d.ts +2 -2
  22. package/dist/hono/cloudflare-workers.js +4 -3
  23. package/dist/hono/cloudflare-workers.js.map +1 -1
  24. package/dist/hono/index.d.ts +20 -4
  25. package/dist/hono/index.js +5 -3
  26. package/dist/hono/index.js.map +1 -1
  27. package/dist/react/index.d.ts +43 -4
  28. package/dist/react/index.js +103 -9
  29. package/dist/react/index.js.map +1 -1
  30. package/dist/server/index.d.ts +17 -4
  31. package/dist/server/index.js +24 -4
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/{socka-report-error-DzFI2Tr7.d.ts → socka-report-error-ixTynx4w.d.ts} +8 -1
  34. package/dist/test/index.d.ts +11 -0
  35. package/dist/test/index.js +84 -0
  36. package/dist/test/index.js.map +1 -0
  37. package/docs/README.md +15 -6
  38. package/docs/auth.md +27 -0
  39. package/docs/backpressure.md +16 -0
  40. package/docs/client.md +44 -3
  41. package/docs/comparison.md +1 -1
  42. package/docs/durable-objects.md +2 -2
  43. package/docs/getting-started.md +143 -84
  44. package/docs/history.md +26 -0
  45. package/docs/internals.md +56 -0
  46. package/docs/lifecycle.md +3 -3
  47. package/docs/multi-room.md +10 -8
  48. package/docs/peers.md +11 -7
  49. package/docs/presence.md +43 -0
  50. package/docs/{events.md → pushes.md} +1 -1
  51. package/docs/recipes.md +78 -0
  52. package/docs/reconnection.md +44 -0
  53. package/docs/reference.md +21 -30
  54. package/docs/server.md +14 -1
  55. package/docs/testing.md +20 -0
  56. package/docs/wire-format.md +25 -0
  57. package/examples/minimal-socka.ts +56 -3
  58. package/package.json +14 -10
  59. package/dist/chunk-45D4T232.js.map +0 -1
  60. package/dist/chunk-AM7PB26G.js.map +0 -1
  61. package/dist/chunk-MZCQHJXY.js.map +0 -1
@@ -18,6 +18,7 @@ var SockaError = class _SockaError extends Error {
18
18
  this.name = "SockaError";
19
19
  this.requestId = options?.requestId;
20
20
  this.code = options?.code;
21
+ this.data = options?.data;
21
22
  if (options?.cause !== void 0) {
22
23
  Object.defineProperty(this, "cause", {
23
24
  value: options.cause,
@@ -30,7 +31,11 @@ var SockaError = class _SockaError extends Error {
30
31
  }
31
32
  /** Builds a {@link SockaError} from a standard RPC error envelope. */
32
33
  static fromWire(msg) {
33
- return new _SockaError(msg.error, { requestId: msg.id });
34
+ return new _SockaError(msg.error, {
35
+ requestId: msg.id,
36
+ code: msg.code,
37
+ data: msg.data
38
+ });
34
39
  }
35
40
  };
36
41
 
@@ -69,10 +74,17 @@ function decodeSockaWire(parsed) {
69
74
  };
70
75
  }
71
76
  if (socka === "serverError" && typeof parsed.id === "string" && typeof parsed.error === "string") {
72
- return {
73
- kind: "serverError",
74
- frame: parsed
77
+ const code = "code" in parsed && typeof parsed.code === "string" ? parsed.code : void 0;
78
+ const data = "data" in parsed ? parsed.data : void 0;
79
+ const frame = {
80
+ socka: "serverError",
81
+ v: SOCKA_WIRE_VERSION,
82
+ id: parsed.id,
83
+ error: parsed.error,
84
+ ...code !== void 0 ? { code } : {},
85
+ ...data !== void 0 ? { data } : {}
75
86
  };
87
+ return { kind: "serverError", frame };
76
88
  }
77
89
  if (socka === "serverEvent" && typeof parsed.event === "string") {
78
90
  return {
@@ -90,8 +102,15 @@ function encodeClientRequest(id, rpc, body) {
90
102
  function encodeServerResponse(id, rpc, body) {
91
103
  return { socka: "serverResponse", v: SOCKA_WIRE_VERSION, id, rpc, body };
92
104
  }
93
- function encodeServerError(id, error) {
94
- return { socka: "serverError", v: SOCKA_WIRE_VERSION, id, error };
105
+ function encodeServerError(id, error, extra) {
106
+ return {
107
+ socka: "serverError",
108
+ v: SOCKA_WIRE_VERSION,
109
+ id,
110
+ error,
111
+ ...extra?.code !== void 0 ? { code: extra.code } : {},
112
+ ...extra?.data !== void 0 ? { data: extra.data } : {}
113
+ };
95
114
  }
96
115
  function encodeServerEvent(event, body) {
97
116
  return { socka: "serverEvent", v: SOCKA_WIRE_VERSION, event, body };
@@ -154,5 +173,5 @@ function reportSockaError(reportError, event) {
154
173
  }
155
174
 
156
175
  export { SOCKA_WIRE_VERSION, SockaError, SockaWireError, decodeSockaWire, defaultReportError, encodeClientRequest, encodeServerError, encodeServerEvent, encodeServerResponse, encodeSockaWire, parseStandardSchema, parseWirePayload, reportSockaError };
157
- //# sourceMappingURL=chunk-MZCQHJXY.js.map
158
- //# sourceMappingURL=chunk-MZCQHJXY.js.map
176
+ //# sourceMappingURL=chunk-2FNWVCP3.js.map
177
+ //# sourceMappingURL=chunk-2FNWVCP3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/validate.ts","../src/core/socka-error.ts","../src/core/envelope.ts","../src/core/wire-codec.ts","../src/core/socka-report-error.ts"],"names":[],"mappings":"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","file":"chunk-2FNWVCP3.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Validates {@link value} with a Standard Schema v1 schema and returns the output,\n * or throws an {@link Error} whose message aggregates issue messages.\n */\nexport async function parseStandardSchema<T>(\n\tschema: StandardSchemaV1<unknown, T>,\n\tvalue: unknown,\n): Promise<T> {\n\tconst result = await schema[\"~standard\"].validate(value);\n\tif (result.issues) {\n\t\tconst messages = result.issues.map((issue) => issue.message).join(\"; \");\n\t\tthrow new Error(messages || \"Validation failed\");\n\t}\n\treturn result.value;\n}\n","/**\n * Thrown on the server and surfaced on the client when the wire uses a shared\n * `{ type: \"error\", id, error }` envelope for correlated RPC failures.\n */\nexport class SockaError extends Error {\n\treadonly requestId?: string;\n\treadonly code?: string;\n\treadonly data?: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: {\n\t\t\trequestId?: string;\n\t\t\tcode?: string;\n\t\t\tdata?: unknown;\n\t\t\tcause?: unknown;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SockaError\";\n\t\tthis.requestId = options?.requestId;\n\t\tthis.code = options?.code;\n\t\tthis.data = options?.data;\n\t\tif (options?.cause !== undefined) {\n\t\t\tObject.defineProperty(this, \"cause\", {\n\t\t\t\tvalue: options.cause,\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: false,\n\t\t\t\twritable: true,\n\t\t\t});\n\t\t}\n\t\tObject.setPrototypeOf(this, SockaError.prototype);\n\t}\n\n\t/** Builds a {@link SockaError} from a standard RPC error envelope. */\n\tstatic fromWire(msg: {\n\t\tid: string;\n\t\terror: string;\n\t\tcode?: string;\n\t\tdata?: unknown;\n\t}): SockaError {\n\t\treturn new SockaError(msg.error, {\n\t\t\trequestId: msg.id,\n\t\t\tcode: msg.code,\n\t\t\tdata: msg.data,\n\t\t});\n\t}\n}\n","/**\n * Versioned socka wire framing. After JSON parse or msgpack unpack, every frame\n * must satisfy {@link decodeSockaWire}; procedure bodies are validated with Standard Schema on each side.\n */\n\nexport const SOCKA_WIRE_VERSION = 1 as const;\n\nexport class SockaWireError extends Error {\n\toverride readonly name = \"SockaWireError\";\n}\n\nexport type SockaClientRequestFrame = {\n\treadonly socka: \"clientRequest\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly rpc: string;\n\treadonly body: Record<string, unknown>;\n};\n\nexport type SockaServerResponseFrame = {\n\treadonly socka: \"serverResponse\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly rpc: string;\n\treadonly body: unknown;\n};\n\nexport type SockaServerErrorFrame = {\n\treadonly socka: \"serverError\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly error: string;\n\t/** Optional machine-readable code (e.g. `FORBIDDEN`). */\n\treadonly code?: string;\n\t/** Optional structured detail for clients; keep small and JSON-serializable. */\n\treadonly data?: unknown;\n};\n\nexport type SockaServerEventFrame = {\n\treadonly socka: \"serverEvent\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly event: string;\n\treadonly body: unknown;\n};\n\nexport type SockaWireFrame =\n\t| SockaClientRequestFrame\n\t| SockaServerResponseFrame\n\t| SockaServerErrorFrame\n\t| SockaServerEventFrame;\n\nexport type DecodedSockaWire =\n\t| { readonly kind: \"clientRequest\"; readonly frame: SockaClientRequestFrame }\n\t| {\n\t\t\treadonly kind: \"serverResponse\";\n\t\t\treadonly frame: SockaServerResponseFrame;\n\t }\n\t| { readonly kind: \"serverError\"; readonly frame: SockaServerErrorFrame }\n\t| { readonly kind: \"serverEvent\"; readonly frame: SockaServerEventFrame };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Decodes a parsed wire object (from JSON or msgpack). Throws {@link SockaWireError}\n * if the payload is not a valid socka v1 frame.\n */\nexport function decodeSockaWire(parsed: unknown): DecodedSockaWire {\n\tif (!isRecord(parsed)) {\n\t\tthrow new SockaWireError(\"socka: expected a JSON object\");\n\t}\n\tif (parsed.socka === undefined) {\n\t\tthrow new SockaWireError('socka: missing \"socka\" discriminator');\n\t}\n\tif (parsed.v !== SOCKA_WIRE_VERSION) {\n\t\tthrow new SockaWireError(\"socka: unsupported wire version\");\n\t}\n\tconst socka = parsed.socka;\n\tif (\n\t\tsocka === \"clientRequest\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.rpc === \"string\" &&\n\t\tisRecord(parsed.body)\n\t) {\n\t\treturn {\n\t\t\tkind: \"clientRequest\",\n\t\t\tframe: parsed as SockaClientRequestFrame,\n\t\t};\n\t}\n\tif (\n\t\tsocka === \"serverResponse\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.rpc === \"string\"\n\t) {\n\t\treturn {\n\t\t\tkind: \"serverResponse\",\n\t\t\tframe: parsed as SockaServerResponseFrame,\n\t\t};\n\t}\n\tif (\n\t\tsocka === \"serverError\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.error === \"string\"\n\t) {\n\t\tconst code =\n\t\t\t\"code\" in parsed && typeof parsed.code === \"string\"\n\t\t\t\t? parsed.code\n\t\t\t\t: undefined;\n\t\tconst data = \"data\" in parsed ? parsed.data : undefined;\n\t\tconst frame: SockaServerErrorFrame = {\n\t\t\tsocka: \"serverError\",\n\t\t\tv: SOCKA_WIRE_VERSION,\n\t\t\tid: parsed.id,\n\t\t\terror: parsed.error,\n\t\t\t...(code !== undefined ? { code } : {}),\n\t\t\t...(data !== undefined ? { data } : {}),\n\t\t};\n\t\treturn { kind: \"serverError\", frame };\n\t}\n\tif (socka === \"serverEvent\" && typeof parsed.event === \"string\") {\n\t\treturn {\n\t\t\tkind: \"serverEvent\",\n\t\t\tframe: parsed as SockaServerEventFrame,\n\t\t};\n\t}\n\tthrow new SockaWireError(\n\t\t`socka: unknown or invalid frame kind ${String(socka)}`,\n\t);\n}\n\n/** Builds a socka v1 client request frame. */\nexport function encodeClientRequest(\n\tid: string,\n\trpc: string,\n\tbody: Record<string, unknown>,\n): SockaClientRequestFrame {\n\treturn { socka: \"clientRequest\", v: SOCKA_WIRE_VERSION, id, rpc, body };\n}\n\n/** Builds a socka v1 server response frame. */\nexport function encodeServerResponse(\n\tid: string,\n\trpc: string,\n\tbody: unknown,\n): SockaServerResponseFrame {\n\treturn { socka: \"serverResponse\", v: SOCKA_WIRE_VERSION, id, rpc, body };\n}\n\n/** Builds a socka v1 server error frame. */\nexport function encodeServerError(\n\tid: string,\n\terror: string,\n\textra?: { readonly code?: string; readonly data?: unknown },\n): SockaServerErrorFrame {\n\treturn {\n\t\tsocka: \"serverError\",\n\t\tv: SOCKA_WIRE_VERSION,\n\t\tid,\n\t\terror,\n\t\t...(extra?.code !== undefined ? { code: extra.code } : {}),\n\t\t...(extra?.data !== undefined ? { data: extra.data } : {}),\n\t};\n}\n\n/** Builds a socka v1 server event frame. */\nexport function encodeServerEvent(\n\tevent: string,\n\tbody: unknown,\n): SockaServerEventFrame {\n\treturn { socka: \"serverEvent\", v: SOCKA_WIRE_VERSION, event, body };\n}\n","/**\n * JSON text frames vs msgpack binary frames for the same socka v1 object graph.\n * Matches {@link decodeSockaWire} after parse/unpack.\n */\n\nimport { pack, unpack } from \"msgpackr\";\nimport type { SockaWireFrame } from \"./envelope\";\n\n/** Wire encoding: UTF-8 JSON strings (default) or msgpack `ArrayBuffer` frames. */\nexport type SockaWireFormat = \"json\" | \"msgpack\";\n\n/**\n * Encodes a socka frame for the wire. JSON returns a string; msgpack returns bytes\n * suitable for `WebSocket.send`.\n */\nexport function encodeSockaWire(\n\tframe: SockaWireFrame,\n\tformat: SockaWireFormat,\n\tserializeJson: (value: unknown) => string = JSON.stringify,\n): string | Uint8Array {\n\tif (format === \"json\") {\n\t\treturn serializeJson(frame);\n\t}\n\treturn pack(frame) as Uint8Array;\n}\n\n/**\n * Decodes a wire payload to a plain object before {@link decodeSockaWire}.\n * Msgpack mode accepts `ArrayBuffer` or `Uint8Array` (e.g. from `msgpackr` / `WebSocket`).\n */\nexport function parseWirePayload(\n\tdata: string | ArrayBuffer | Uint8Array,\n\tformat: SockaWireFormat,\n\tdeserializeJson: (raw: string) => unknown = JSON.parse,\n): unknown {\n\tif (format === \"json\") {\n\t\tif (typeof data !== \"string\") {\n\t\t\tthrow new Error(\"socka: expected a JSON text frame\");\n\t\t}\n\t\treturn deserializeJson(data);\n\t}\n\tif (data instanceof Uint8Array) {\n\t\treturn unpack(data);\n\t}\n\tif (data instanceof ArrayBuffer) {\n\t\treturn unpack(new Uint8Array(data));\n\t}\n\tthrow new Error(\"socka: expected an ArrayBuffer or Uint8Array msgpack frame\");\n}\n","import { exhaustiveGuard } from \"@firtoz/maybe-error\";\n\n/**\n * Single discriminated union for optional `reportError` on session config and\n * `SockaSession` options: `kind` narrows context; `error` is what was thrown or rejected.\n */\nexport type SockaReportError =\n\t| { kind: \"clientEventListener\"; eventName: string; error: unknown }\n\t| { kind: \"clientEventValidation\"; eventName: string; error: unknown }\n\t| { kind: \"serverOnAttached\"; error: unknown }\n\t| {\n\t\t\tkind: \"serverInboundMessage\";\n\t\t\t/** `hono` uses the same log line as the Hono adapters; others use attach-style. */\n\t\t\tadapter: \"attach\" | \"hono\" | \"bun\";\n\t\t\terror: unknown;\n\t }\n\t| { kind: \"serverHandleClose\"; error: unknown }\n\t| {\n\t\t\tkind: \"serverShutdown\";\n\t\t\tadapter: \"attach\" | \"hono\";\n\t\t\terror: unknown;\n\t };\n\n/** Default `console.error` behavior; same messages as pre–`reportError` socka. */\nexport function defaultReportError(event: SockaReportError): void {\n\tswitch (event.kind) {\n\t\tcase \"clientEventListener\":\n\t\t\tconsole.error(\"socka: event listener error\", event.error);\n\t\t\treturn;\n\t\tcase \"clientEventValidation\":\n\t\t\tconsole.error(\"socka: event validation error\", event.error);\n\t\t\treturn;\n\t\tcase \"serverOnAttached\":\n\t\t\tconsole.error(\"socka: onAttached error:\", event.error);\n\t\t\treturn;\n\t\tcase \"serverInboundMessage\":\n\t\t\tif (event.adapter === \"hono\") {\n\t\t\t\tconsole.error(\"socka: onMessage error:\", event.error);\n\t\t\t} else {\n\t\t\t\tconsole.error(\"socka: message handler error:\", event.error);\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"serverHandleClose\":\n\t\t\tconsole.error(\"socka: handleClose error:\", event.error);\n\t\t\treturn;\n\t\tcase \"serverShutdown\":\n\t\t\tif (event.adapter === \"hono\") {\n\t\t\t\tconsole.error(\"socka: onClose error:\", event.error);\n\t\t\t} else {\n\t\t\t\tconsole.error(\"socka: shutdown error:\", event.error);\n\t\t\t}\n\t\t\treturn;\n\t\tdefault:\n\t\t\texhaustiveGuard(event);\n\t}\n}\n\n/** Invokes the optional `reportError` callback when provided, otherwise `defaultReportError`. */\nexport function reportSockaError(\n\treportError: ((event: SockaReportError) => void) | undefined,\n\tevent: SockaReportError,\n): void {\n\t(reportError ?? defaultReportError)(event);\n}\n"]}
@@ -1,9 +1,12 @@
1
1
  import { RESERVED_SOCKA_PROCEDURE_NAMES } from './chunk-YMT4HAH7.js';
2
- import { parseWirePayload, decodeSockaWire, SockaWireError, encodeClientRequest, encodeSockaWire, SockaError, parseStandardSchema, reportSockaError } from './chunk-MZCQHJXY.js';
2
+ import { parseWirePayload, decodeSockaWire, SockaWireError, encodeClientRequest, encodeSockaWire, SockaError, parseStandardSchema, reportSockaError } from './chunk-2FNWVCP3.js';
3
3
 
4
4
  // src/client/SockaWebSocketClient.ts
5
5
  var SockaWebSocketClient = class {
6
6
  constructor(options) {
7
+ this.manualClose = false;
8
+ this.reconnectAttempt = 0;
9
+ this.statusListeners = /* @__PURE__ */ new Set();
7
10
  this.opts = options;
8
11
  this.contract = options.contract;
9
12
  this.wireFormat = options.wireFormat ?? "json";
@@ -14,9 +17,34 @@ var SockaWebSocketClient = class {
14
17
  this.onEventCb = options.onEvent;
15
18
  this.onValidationError = options.onValidationError;
16
19
  if (options.autoConnect !== false) {
20
+ this._status = "connecting";
17
21
  this.attachSocket(this.createSocket());
22
+ } else {
23
+ this._status = "idle";
24
+ }
25
+ }
26
+ setStatus(next) {
27
+ if (this._status === next) return;
28
+ this._status = next;
29
+ for (const fn of this.statusListeners) {
30
+ fn(next);
18
31
  }
19
32
  }
33
+ /** Current connection lifecycle state. */
34
+ get status() {
35
+ return this._status;
36
+ }
37
+ /**
38
+ * Subscribe to {@link status} changes. The listener is called immediately with the
39
+ * current status, then on every transition. Returns an unsubscribe function.
40
+ */
41
+ onStatusChange(listener) {
42
+ this.statusListeners.add(listener);
43
+ listener(this._status);
44
+ return () => {
45
+ this.statusListeners.delete(listener);
46
+ };
47
+ }
20
48
  createSocket() {
21
49
  if (this.opts.webSocket) {
22
50
  return this.opts.webSocket;
@@ -27,11 +55,18 @@ var SockaWebSocketClient = class {
27
55
  throw new Error("Either 'url' or 'webSocket' must be provided");
28
56
  }
29
57
  attachSocket(ws) {
58
+ this.setStatus("connecting");
30
59
  this.ws = ws;
31
60
  if (this.wireFormat === "msgpack") {
32
61
  ws.binaryType = "arraybuffer";
33
62
  }
34
63
  ws.addEventListener("open", (event) => {
64
+ const prev = this.reconnectAttempt;
65
+ this.reconnectAttempt = 0;
66
+ if (prev > 0) {
67
+ this.opts.onReconnected?.({ attempt: prev });
68
+ }
69
+ this.setStatus("open");
35
70
  this.opts.onOpen?.(event);
36
71
  });
37
72
  ws.addEventListener("message", (event) => {
@@ -39,11 +74,84 @@ var SockaWebSocketClient = class {
39
74
  });
40
75
  ws.addEventListener("close", (event) => {
41
76
  this.opts.onClose?.(event);
77
+ if (this.manualClose) {
78
+ this.setStatus("closed");
79
+ return;
80
+ }
81
+ if (!this.getReconnectEnabled()) {
82
+ this.setStatus("closed");
83
+ return;
84
+ }
85
+ const cfg = this.resolveReconnectConfig();
86
+ const maxAttempts = cfg.maxAttempts;
87
+ if (maxAttempts !== void 0 && this.reconnectAttempt >= maxAttempts) {
88
+ this.setStatus("closed");
89
+ return;
90
+ }
91
+ this.maybeScheduleReconnect();
42
92
  });
43
93
  ws.addEventListener("error", (event) => {
44
94
  this.opts.onError?.(event);
45
95
  });
46
96
  }
97
+ getReconnectEnabled() {
98
+ if (this.opts.reconnect === false) return false;
99
+ if (this.opts.webSocket !== void 0 && this.opts.reconnect === void 0) {
100
+ return false;
101
+ }
102
+ return this.opts.url !== void 0;
103
+ }
104
+ resolveReconnectConfig() {
105
+ const r = this.opts.reconnect;
106
+ if (r === false) return {};
107
+ return r ?? {};
108
+ }
109
+ computeReconnectDelayMs(attempt, cfg) {
110
+ const initial = cfg.initialDelayMs ?? 1e3;
111
+ const max = cfg.maxDelayMs ?? 3e4;
112
+ const jitterRatio = cfg.jitter ?? 0.2;
113
+ const base = Math.min(max, initial * 2 ** Math.max(0, attempt - 1));
114
+ const spread = base * jitterRatio;
115
+ return Math.max(0, base + (Math.random() * 2 - 1) * spread);
116
+ }
117
+ maybeScheduleReconnect() {
118
+ if (this.manualClose) return;
119
+ if (!this.getReconnectEnabled()) return;
120
+ const cfg = this.resolveReconnectConfig();
121
+ const maxAttempts = cfg.maxAttempts;
122
+ if (maxAttempts !== void 0 && this.reconnectAttempt >= maxAttempts) {
123
+ return;
124
+ }
125
+ if (cfg.pauseWhenHidden !== false && typeof document !== "undefined" && document.hidden) {
126
+ this.setStatus("reconnecting");
127
+ const onVis = () => {
128
+ if (!document.hidden) {
129
+ document.removeEventListener("visibilitychange", onVis);
130
+ this.maybeScheduleReconnect();
131
+ }
132
+ };
133
+ document.addEventListener("visibilitychange", onVis);
134
+ return;
135
+ }
136
+ this.reconnectAttempt += 1;
137
+ this.setStatus("reconnecting");
138
+ const delayMs = this.computeReconnectDelayMs(this.reconnectAttempt, cfg);
139
+ this.opts.onReconnecting?.({ attempt: this.reconnectAttempt, delayMs });
140
+ if (this.reconnectTimer !== void 0) {
141
+ clearTimeout(this.reconnectTimer);
142
+ }
143
+ this.reconnectTimer = setTimeout(() => {
144
+ this.reconnectTimer = void 0;
145
+ if (this.manualClose) return;
146
+ this.openReplacementSocket();
147
+ }, delayMs);
148
+ }
149
+ openReplacementSocket() {
150
+ if (this.manualClose) return;
151
+ if (!this.opts.url) return;
152
+ this.ws = void 0;
153
+ this.attachSocket(this.createSocket());
154
+ }
47
155
  handleMessageEvent(event) {
48
156
  try {
49
157
  const fmt = this.wireFormat;
@@ -140,6 +248,12 @@ var SockaWebSocketClient = class {
140
248
  this.ws.send(copy.buffer);
141
249
  }
142
250
  close(code, reason) {
251
+ this.manualClose = true;
252
+ if (this.reconnectTimer !== void 0) {
253
+ clearTimeout(this.reconnectTimer);
254
+ this.reconnectTimer = void 0;
255
+ }
256
+ this.setStatus("closed");
143
257
  this.ws?.close(code, reason);
144
258
  }
145
259
  get readyState() {
@@ -413,9 +527,17 @@ var SockaSessionBase = class {
413
527
  connect() {
414
528
  return this.client.connect();
415
529
  }
530
+ /** Same as {@link SockaWebSocketClient.status}. */
531
+ get status() {
532
+ return this.client.status;
533
+ }
534
+ /** Same as {@link SockaWebSocketClient.onStatusChange}. */
535
+ onStatusChange(listener) {
536
+ return this.client.onStatusChange(listener);
537
+ }
416
538
  };
417
539
  var SockaSession = SockaSessionBase;
418
540
 
419
541
  export { SockaSession, SockaWebSocketClient };
420
- //# sourceMappingURL=chunk-AM7PB26G.js.map
421
- //# sourceMappingURL=chunk-AM7PB26G.js.map
542
+ //# sourceMappingURL=chunk-H3S3435J.js.map
543
+ //# sourceMappingURL=chunk-H3S3435J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/SockaWebSocketClient.ts","../src/client/SockaSession.ts"],"names":[],"mappings":"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","file":"chunk-H3S3435J.js","sourcesContent":["import type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport {\n\tSockaWireError,\n\tdecodeSockaWire,\n\tencodeClientRequest,\n} from \"../core/envelope\";\nimport type {\n\tSockaServerResponseFrame,\n\tSockaServerErrorFrame,\n\tSockaServerEventFrame,\n} from \"../core/envelope\";\nimport {\n\tencodeSockaWire,\n\tparseWirePayload,\n\ttype SockaWireFormat,\n} from \"../core/wire-codec\";\n\nexport interface SockaWebSocketClientOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n> {\n\tcontract: TContract;\n\t/** Default `\"json\"` (text frames). Use `\"msgpack\"` for binary `ArrayBuffer` frames. */\n\twireFormat?: SockaWireFormat;\n\turl?: string;\n\twebSocket?: WebSocket;\n\t/**\n\t * When `false`, the socket is not created until {@link SockaWebSocketClient.connect}\n\t * (or the first operation that implicitly opens, e.g. {@link SockaSession} `send`).\n\t * Default `true`.\n\t */\n\tautoConnect?: boolean;\n\tserializeJson?: (value: unknown) => string;\n\tdeserializeJson?: (raw: string) => unknown;\n\tonOpen?: (event: Event) => void;\n\tonClose?: (event: CloseEvent) => void;\n\tonError?: (event: Event) => void;\n\tonResponse?: (frame: SockaServerResponseFrame) => void;\n\tonServerError?: (frame: SockaServerErrorFrame) => void;\n\tonEvent?: (frame: SockaServerEventFrame) => void;\n\tonValidationError?: (error: Error, rawMessage: unknown) => void;\n\t/**\n\t * Automatic reconnect after an abnormal close. **Disabled by default** when\n\t * {@link webSocket} is injected (tests); enabled when using {@link url}.\n\t * Pass `false` to disable.\n\t */\n\treconnect?: false | SockaReconnectConfig;\n\t/** Fired before a reconnect attempt is scheduled (after delay is chosen). */\n\tonReconnecting?: (info: SockaReconnectingInfo) => void;\n\t/** Fired after a new socket reaches `open` following a reconnect. */\n\tonReconnected?: (info: SockaReconnectedInfo) => void;\n}\n\n/** Options for {@link SockaWebSocketClientOptions.reconnect}. */\nexport type SockaReconnectConfig = {\n\t/** Default `1000`. */\n\tinitialDelayMs?: number;\n\t/** Default `30000`. */\n\tmaxDelayMs?: number;\n\t/** 0–1 fraction of delay to randomize. Default `0.2`. */\n\tjitter?: number;\n\t/** Omit for infinite attempts. */\n\tmaxAttempts?: number;\n\t/**\n\t * When `true` (default), delay reconnect until `document` is visible again.\n\t */\n\tpauseWhenHidden?: boolean;\n};\n\nexport type SockaReconnectingInfo = {\n\tattempt: number;\n\tdelayMs: number;\n};\n\nexport type SockaReconnectedInfo = {\n\tattempt: number;\n};\n\n/** High-level connection lifecycle for UI and telemetry. */\nexport type SockaConnectionStatus =\n\t| \"idle\"\n\t| \"connecting\"\n\t| \"open\"\n\t| \"reconnecting\"\n\t| \"closed\";\n\n/**\n * Browser WebSocket client driven by a socka contract. Sends client request\n * frames and dispatches decoded server frames to callbacks.\n */\nexport class SockaWebSocketClient<\n\tTContract extends SockaContract<SockaContractConfig>,\n> {\n\tprivate ws: WebSocket | undefined;\n\tprivate readonly opts: SockaWebSocketClientOptions<TContract>;\n\tprivate readonly wireFormat: SockaWireFormat;\n\tprivate readonly serializeJson: (value: unknown) => string;\n\tprivate readonly deserializeJson: (raw: string) => unknown;\n\tprivate readonly onResponseCb?: (frame: SockaServerResponseFrame) => void;\n\tprivate readonly onServerErrorCb?: (frame: SockaServerErrorFrame) => void;\n\tprivate readonly onEventCb?: (frame: SockaServerEventFrame) => void;\n\tprivate readonly onValidationError?: (\n\t\terror: Error,\n\t\trawMessage: unknown,\n\t) => void;\n\n\treadonly contract: TContract;\n\n\tprivate manualClose = false;\n\tprivate reconnectAttempt = 0;\n\tprivate reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\n\tprivate _status: SockaConnectionStatus;\n\tprivate readonly statusListeners = new Set<\n\t\t(status: SockaConnectionStatus) => void\n\t>();\n\n\tconstructor(options: SockaWebSocketClientOptions<TContract>) {\n\t\tthis.opts = options;\n\t\tthis.contract = options.contract;\n\t\tthis.wireFormat = options.wireFormat ?? \"json\";\n\t\tthis.serializeJson = options.serializeJson ?? JSON.stringify;\n\t\tthis.deserializeJson = options.deserializeJson ?? JSON.parse;\n\t\tthis.onResponseCb = options.onResponse;\n\t\tthis.onServerErrorCb = options.onServerError;\n\t\tthis.onEventCb = options.onEvent;\n\t\tthis.onValidationError = options.onValidationError;\n\n\t\tif (options.autoConnect !== false) {\n\t\t\tthis._status = \"connecting\";\n\t\t\tthis.attachSocket(this.createSocket());\n\t\t} else {\n\t\t\tthis._status = \"idle\";\n\t\t}\n\t}\n\n\tprivate setStatus(next: SockaConnectionStatus): void {\n\t\tif (this._status === next) return;\n\t\tthis._status = next;\n\t\tfor (const fn of this.statusListeners) {\n\t\t\tfn(next);\n\t\t}\n\t}\n\n\t/** Current connection lifecycle state. */\n\tget status(): SockaConnectionStatus {\n\t\treturn this._status;\n\t}\n\n\t/**\n\t * Subscribe to {@link status} changes. The listener is called immediately with the\n\t * current status, then on every transition. Returns an unsubscribe function.\n\t */\n\tonStatusChange(\n\t\tlistener: (status: SockaConnectionStatus) => void,\n\t): () => void {\n\t\tthis.statusListeners.add(listener);\n\t\tlistener(this._status);\n\t\treturn () => {\n\t\t\tthis.statusListeners.delete(listener);\n\t\t};\n\t}\n\n\tprivate createSocket(): WebSocket {\n\t\tif (this.opts.webSocket) {\n\t\t\treturn this.opts.webSocket;\n\t\t}\n\t\tif (this.opts.url) {\n\t\t\treturn new WebSocket(this.opts.url);\n\t\t}\n\t\tthrow new Error(\"Either 'url' or 'webSocket' must be provided\");\n\t}\n\n\tprivate attachSocket(ws: WebSocket): void {\n\t\tthis.setStatus(\"connecting\");\n\t\tthis.ws = ws;\n\t\tif (this.wireFormat === \"msgpack\") {\n\t\t\tws.binaryType = \"arraybuffer\";\n\t\t}\n\n\t\tws.addEventListener(\"open\", (event) => {\n\t\t\tconst prev = this.reconnectAttempt;\n\t\t\tthis.reconnectAttempt = 0;\n\t\t\tif (prev > 0) {\n\t\t\t\tthis.opts.onReconnected?.({ attempt: prev });\n\t\t\t}\n\t\t\tthis.setStatus(\"open\");\n\t\t\tthis.opts.onOpen?.(event);\n\t\t});\n\n\t\tws.addEventListener(\"message\", (event) => {\n\t\t\tthis.handleMessageEvent(event);\n\t\t});\n\n\t\tws.addEventListener(\"close\", (event) => {\n\t\t\tthis.opts.onClose?.(event);\n\t\t\tif (this.manualClose) {\n\t\t\t\tthis.setStatus(\"closed\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!this.getReconnectEnabled()) {\n\t\t\t\tthis.setStatus(\"closed\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst cfg = this.resolveReconnectConfig();\n\t\t\tconst maxAttempts = cfg.maxAttempts;\n\t\t\tif (maxAttempts !== undefined && this.reconnectAttempt >= maxAttempts) {\n\t\t\t\tthis.setStatus(\"closed\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.maybeScheduleReconnect();\n\t\t});\n\n\t\tws.addEventListener(\"error\", (event) => {\n\t\t\tthis.opts.onError?.(event);\n\t\t});\n\t}\n\n\tprivate getReconnectEnabled(): boolean {\n\t\tif (this.opts.reconnect === false) return false;\n\t\tif (\n\t\t\tthis.opts.webSocket !== undefined &&\n\t\t\tthis.opts.reconnect === undefined\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.opts.url !== undefined;\n\t}\n\n\tprivate resolveReconnectConfig(): SockaReconnectConfig {\n\t\tconst r = this.opts.reconnect;\n\t\tif (r === false) return {};\n\t\treturn r ?? {};\n\t}\n\n\tprivate computeReconnectDelayMs(\n\t\tattempt: number,\n\t\tcfg: SockaReconnectConfig,\n\t): number {\n\t\tconst initial = cfg.initialDelayMs ?? 1000;\n\t\tconst max = cfg.maxDelayMs ?? 30000;\n\t\tconst jitterRatio = cfg.jitter ?? 0.2;\n\t\tconst base = Math.min(max, initial * 2 ** Math.max(0, attempt - 1));\n\t\tconst spread = base * jitterRatio;\n\t\treturn Math.max(0, base + (Math.random() * 2 - 1) * spread);\n\t}\n\n\tprivate maybeScheduleReconnect(): void {\n\t\tif (this.manualClose) return;\n\t\tif (!this.getReconnectEnabled()) return;\n\t\tconst cfg = this.resolveReconnectConfig();\n\t\tconst maxAttempts = cfg.maxAttempts;\n\t\tif (maxAttempts !== undefined && this.reconnectAttempt >= maxAttempts) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tcfg.pauseWhenHidden !== false &&\n\t\t\ttypeof document !== \"undefined\" &&\n\t\t\tdocument.hidden\n\t\t) {\n\t\t\tthis.setStatus(\"reconnecting\");\n\t\t\tconst onVis = (): void => {\n\t\t\t\tif (!document.hidden) {\n\t\t\t\t\tdocument.removeEventListener(\"visibilitychange\", onVis);\n\t\t\t\t\tthis.maybeScheduleReconnect();\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener(\"visibilitychange\", onVis);\n\t\t\treturn;\n\t\t}\n\t\tthis.reconnectAttempt += 1;\n\t\tthis.setStatus(\"reconnecting\");\n\t\tconst delayMs = this.computeReconnectDelayMs(this.reconnectAttempt, cfg);\n\t\tthis.opts.onReconnecting?.({ attempt: this.reconnectAttempt, delayMs });\n\t\tif (this.reconnectTimer !== undefined) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t}\n\t\tthis.reconnectTimer = setTimeout(() => {\n\t\t\tthis.reconnectTimer = undefined;\n\t\t\tif (this.manualClose) return;\n\t\t\tthis.openReplacementSocket();\n\t\t}, delayMs);\n\t}\n\n\tprivate openReplacementSocket(): void {\n\t\tif (this.manualClose) return;\n\t\tif (!this.opts.url) return;\n\t\tthis.ws = undefined;\n\t\tthis.attachSocket(this.createSocket());\n\t}\n\n\tprivate handleMessageEvent(event: MessageEvent): void {\n\t\ttry {\n\t\t\tconst fmt = this.wireFormat;\n\t\t\tlet payload: string | ArrayBuffer;\n\t\t\tif (fmt === \"json\") {\n\t\t\t\tif (typeof event.data !== \"string\") {\n\t\t\t\t\tthis.onValidationError?.(\n\t\t\t\t\t\tnew Error(\"socka: expected JSON text frame\"),\n\t\t\t\t\t\tevent.data,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpayload = event.data;\n\t\t\t} else {\n\t\t\t\tif (!(event.data instanceof ArrayBuffer)) {\n\t\t\t\t\tthis.onValidationError?.(\n\t\t\t\t\t\tnew Error(\"socka: expected ArrayBuffer msgpack frame\"),\n\t\t\t\t\t\tevent.data,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpayload = event.data;\n\t\t\t}\n\n\t\t\tlet parsed: unknown;\n\t\t\ttry {\n\t\t\t\tparsed = parseWirePayload(payload, fmt, this.deserializeJson);\n\t\t\t} catch (err) {\n\t\t\t\tthis.onValidationError?.(\n\t\t\t\t\terr instanceof Error ? err : new Error(String(err)),\n\t\t\t\t\tevent.data,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet decoded: ReturnType<typeof decodeSockaWire>;\n\t\t\ttry {\n\t\t\t\tdecoded = decodeSockaWire(parsed);\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof SockaWireError) {\n\t\t\t\t\tthis.onValidationError?.(err, parsed);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tswitch (decoded.kind) {\n\t\t\t\tcase \"serverResponse\":\n\t\t\t\t\tthis.onResponseCb?.(decoded.frame);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"serverError\":\n\t\t\t\t\tthis.onServerErrorCb?.(decoded.frame);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"serverEvent\":\n\t\t\t\t\tthis.onEventCb?.(decoded.frame);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"clientRequest\":\n\t\t\t\t\tthis.onValidationError?.(\n\t\t\t\t\t\tnew Error(\"socka: unexpected clientRequest frame from server\"),\n\t\t\t\t\t\tparsed,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tconst _exhaustive: never = decoded;\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`socka: unexpected wire decode branch ${JSON.stringify(_exhaustive)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\tthis.onValidationError?.(err, event.data);\n\t\t}\n\t}\n\n\t/**\n\t * Creates the WebSocket (when {@link SockaWebSocketClientOptions.autoConnect}\n\t * was `false`) and waits until the connection is open.\n\t */\n\tasync connect(): Promise<void> {\n\t\tif (!this.ws) {\n\t\t\tthis.attachSocket(this.createSocket());\n\t\t}\n\t\tawait this.waitForOpen();\n\t}\n\n\tsendRequest(id: string, rpc: string, body: Record<string, unknown>): void {\n\t\tif (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n\t\t\tthrow new Error(\"WebSocket is not open\");\n\t\t}\n\t\tconst frame = encodeClientRequest(id, rpc, body);\n\t\tconst encoded = encodeSockaWire(frame, this.wireFormat, this.serializeJson);\n\t\tif (typeof encoded === \"string\") {\n\t\t\tthis.ws.send(encoded);\n\t\t\treturn;\n\t\t}\n\t\tconst copy = new Uint8Array(encoded.byteLength);\n\t\tcopy.set(encoded);\n\t\tthis.ws.send(copy.buffer);\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.manualClose = true;\n\t\tif (this.reconnectTimer !== undefined) {\n\t\t\tclearTimeout(this.reconnectTimer);\n\t\t\tthis.reconnectTimer = undefined;\n\t\t}\n\t\tthis.setStatus(\"closed\");\n\t\tthis.ws?.close(code, reason);\n\t}\n\n\tget readyState(): number {\n\t\treturn this.ws?.readyState ?? WebSocket.CONNECTING;\n\t}\n\n\tget socket(): WebSocket {\n\t\tif (!this.ws) {\n\t\t\tthrow new Error(\n\t\t\t\t\"socka: WebSocket not created yet; call connect() first or use autoConnect: true\",\n\t\t\t);\n\t\t}\n\t\treturn this.ws;\n\t}\n\n\tasync waitForOpen(): Promise<void> {\n\t\tconst ws = this.ws;\n\t\tif (!ws) {\n\t\t\tthrow new Error(\n\t\t\t\t\"socka: WebSocket not created yet; call connect() first or use autoConnect: true\",\n\t\t\t);\n\t\t}\n\t\tif (ws.readyState === WebSocket.OPEN) {\n\t\t\treturn;\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst abortController = new AbortController();\n\t\t\tconst { signal } = abortController;\n\t\t\tconst cleanup = () => {\n\t\t\t\tabortController.abort();\n\t\t\t};\n\t\t\tws.addEventListener(\n\t\t\t\t\"open\",\n\t\t\t\t() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t\tws.addEventListener(\n\t\t\t\t\"error\",\n\t\t\t\t() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new Error(\"WebSocket connection failed\"));\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t});\n\t}\n}\n","import type {\n\tSockaContract,\n\tSockaContractConfig,\n\tInferSockaSend,\n\tInferSockaPushHandlers,\n\tInferSockaPushPayload,\n} from \"../core/contract\";\nimport {\n\treportSockaError,\n\ttype SockaReportError,\n} from \"../core/socka-report-error\";\nimport { parseStandardSchema } from \"../core/validate\";\nimport { SockaError } from \"../core/socka-error\";\nimport type {\n\tSockaServerResponseFrame,\n\tSockaServerErrorFrame,\n\tSockaServerEventFrame,\n} from \"../core/envelope\";\nimport {\n\tSockaWebSocketClient,\n\ttype SockaConnectionStatus,\n\ttype SockaWebSocketClientOptions,\n} from \"./SockaWebSocketClient\";\nimport { RESERVED_SOCKA_PROCEDURE_NAMES } from \"../core/reserved-procedure-names\";\n\ntype PendingEntry = {\n\trpc: string;\n\tresolve: (value: unknown) => void;\n\treject: (error: Error) => void;\n};\n\ntype PushListenerFn = (payload: unknown) => void | Promise<void>;\n\n/** Same strings as `RESERVED_SOCKA_PROCEDURE_NAMES` (core) for O(1) lookup on `send`. */\nconst RESERVED_CALL_NAMES = new Set<string>(RESERVED_SOCKA_PROCEDURE_NAMES);\n\nexport type SockaSessionPushWaitOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tK extends keyof TContract[\"pushes\"] & string,\n> = {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n\tpredicate?: (payload: InferSockaPushPayload<TContract, K>) => boolean;\n};\n\nexport type SockaSessionSubscribeApi<\n\tTContract extends SockaContract<SockaContractConfig>,\n> = {\n\ton<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\thandler: (\n\t\t\tpayload: InferSockaPushPayload<TContract, K>,\n\t\t) => void | Promise<void>,\n\t): void;\n\toff<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\thandler: (\n\t\t\tpayload: InferSockaPushPayload<TContract, K>,\n\t\t) => void | Promise<void>,\n\t): void;\n\tonce<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\thandler: (\n\t\t\tpayload: InferSockaPushPayload<TContract, K>,\n\t\t) => void | Promise<void>,\n\t): void;\n\twaitForPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\toptions?: SockaSessionPushWaitOptions<TContract, K>,\n\t): Promise<InferSockaPushPayload<TContract, K>>;\n};\n\nfunction waitForPushAbortError(): Error {\n\tif (typeof DOMException !== \"undefined\") {\n\t\treturn new DOMException(\"waitForPush aborted\", \"AbortError\");\n\t}\n\treturn new Error(\"socka: waitForPush aborted\");\n}\n\nexport type SockaSessionOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n> = Omit<\n\tSockaWebSocketClientOptions<TContract>,\n\t\"onResponse\" | \"onServerError\" | \"onEvent\"\n> & {\n\tonOpen?: (event: Event) => void;\n\tonClose?: (event: CloseEvent) => void;\n\tonError?: (event: Event) => void;\n\tpushHandlers?: Partial<InferSockaPushHandlers<TContract>>;\n\t/**\n\t * Optional sink for client-side push pipeline failures (listener throws,\n\t * push payload validation). Defaults to `console.error`; see\n\t * `SockaReportError` in `@firtoz/socka/core`.\n\t */\n\treportError?: (event: SockaReportError) => void;\n};\n\n/**\n * Browser WebSocket session: **`session.send`** for contract calls, **`session.subscribe`**\n * for server pushes, **`session.client`** for low-level wire access.\n */\nclass SockaSessionBase<TContract extends SockaContract<SockaContractConfig>> {\n\treadonly client: SockaWebSocketClient<TContract>;\n\treadonly send: InferSockaSend<TContract>;\n\treadonly subscribe: SockaSessionSubscribeApi<TContract>;\n\n\tprivate readonly pending = new Map<string, PendingEntry>();\n\tprivate idSeq = 0;\n\tprivate readonly pushListeners = new Map<string, Set<PushListenerFn>>();\n\tprivate readonly reportError?: (event: SockaReportError) => void;\n\n\tconstructor(options: SockaSessionOptions<TContract>) {\n\t\tconst { pushHandlers, reportError, ...clientOpts } = options;\n\t\tthis.reportError = reportError;\n\n\t\tthis.client = new SockaWebSocketClient({\n\t\t\t...clientOpts,\n\t\t\tonResponse: (frame) => this.handleResponse(frame),\n\t\t\tonServerError: (frame) => this.handleServerError(frame),\n\t\t\tonEvent: (frame) => this.handleEvent(frame),\n\t\t});\n\n\t\tthis.subscribe = this.createSubscribeApi();\n\n\t\tconst sendBag = this.buildSendMethods();\n\t\tfor (const name of Object.keys(sendBag)) {\n\t\t\tif (RESERVED_CALL_NAMES.has(name)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`socka: call name \"${name}\" is reserved on SockaSession.send; rename it in defineSocka`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.send = sendBag;\n\n\t\tif (pushHandlers) {\n\t\t\tfor (const key of Object.keys(pushHandlers) as Array<\n\t\t\t\tkeyof InferSockaPushHandlers<TContract> & string\n\t\t\t>) {\n\t\t\t\tconst fn = pushHandlers[key];\n\t\t\t\tif (fn) {\n\t\t\t\t\tthis.subscribe.on(key, fn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate createSubscribeApi(): SockaSessionSubscribeApi<TContract> {\n\t\treturn {\n\t\t\ton: (name, handler) => {\n\t\t\t\tthis.addPushListener(name, handler as PushListenerFn);\n\t\t\t},\n\t\t\toff: (name, handler) => {\n\t\t\t\tthis.removePushListener(name, handler as PushListenerFn);\n\t\t\t},\n\t\t\tonce: (name, handler) => {\n\t\t\t\tconst wrapped: PushListenerFn = (payload: unknown) => {\n\t\t\t\t\tthis.removePushListener(name, wrapped);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = (handler as (p: unknown) => void | Promise<void>)(\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\t\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\t\t\t\tkind: \"clientEventListener\",\n\t\t\t\t\t\t\t\teventName: String(name),\n\t\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\t\t\tkind: \"clientEventListener\",\n\t\t\t\t\t\t\teventName: String(name),\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tthis.addPushListener(name, wrapped);\n\t\t\t},\n\t\t\twaitForPush: (name, options) => this.waitForPushImpl(name, options),\n\t\t};\n\t}\n\n\tprivate addPushListener(name: string, handler: PushListenerFn): void {\n\t\tlet set = this.pushListeners.get(name);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.pushListeners.set(name, set);\n\t\t}\n\t\tset.add(handler);\n\t}\n\n\tprivate removePushListener(name: string, handler: PushListenerFn): void {\n\t\tthis.pushListeners.get(name)?.delete(handler);\n\t}\n\n\tprivate waitForPushImpl<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\toptions?: SockaSessionPushWaitOptions<TContract, K>,\n\t): Promise<InferSockaPushPayload<TContract, K>> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst signal = options?.signal;\n\t\t\tif (signal?.aborted) {\n\t\t\t\treject(waitForPushAbortError());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tcleanup();\n\t\t\t\treject(waitForPushAbortError());\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\t\tif (options?.timeoutMs != null) {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\treject(new Error(\"socka: waitForPush timed out\"));\n\t\t\t\t}, options.timeoutMs);\n\t\t\t}\n\n\t\t\tconst listener: PushListenerFn = (payload: unknown) => {\n\t\t\t\tif (\n\t\t\t\t\toptions?.predicate &&\n\t\t\t\t\t!options.predicate(payload as InferSockaPushPayload<TContract, K>)\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcleanup();\n\t\t\t\tresolve(payload as InferSockaPushPayload<TContract, K>);\n\t\t\t};\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (timeoutId !== undefined) clearTimeout(timeoutId);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tthis.removePushListener(name, listener);\n\t\t\t};\n\n\t\t\tthis.addPushListener(name, listener);\n\t\t});\n\t}\n\n\tprivate buildSendMethods(): InferSockaSend<TContract> {\n\t\tconst methods: Record<string, (input?: unknown) => Promise<unknown>> = {};\n\n\t\tfor (const name of Object.keys(this.client.contract.calls)) {\n\t\t\tconst proc = this.client.contract.calls[name];\n\t\t\tif (proc.input) {\n\t\t\t\tmethods[name] = (input: unknown) => this.call(name, input);\n\t\t\t} else {\n\t\t\t\tmethods[name] = () => this.call(name, undefined);\n\t\t\t}\n\t\t}\n\n\t\treturn methods as InferSockaSend<TContract>;\n\t}\n\n\tprivate call(callName: string, input: unknown): Promise<unknown> {\n\t\treturn (async () => {\n\t\t\tawait this.client.connect();\n\t\t\tif (this.client.readyState !== WebSocket.OPEN) {\n\t\t\t\tthrow new Error(\"WebSocket not connected\");\n\t\t\t}\n\t\t\tconst id = this.nextId(callName);\n\t\t\tconst body =\n\t\t\t\tinput !== undefined && input !== null\n\t\t\t\t\t? (input as Record<string, unknown>)\n\t\t\t\t\t: {};\n\t\t\treturn new Promise<unknown>((resolve, reject) => {\n\t\t\t\tthis.pending.set(id, { rpc: callName, resolve, reject });\n\t\t\t\ttry {\n\t\t\t\t\tthis.client.sendRequest(id, callName, body);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.pending.delete(id);\n\t\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t\t}\n\t\t\t});\n\t\t})();\n\t}\n\n\tprivate handleResponse(frame: SockaServerResponseFrame): void {\n\t\tconst entry = this.pending.get(frame.id);\n\t\tif (!entry) return;\n\t\tthis.pending.delete(frame.id);\n\n\t\tconst proc = this.client.contract.calls[frame.rpc];\n\t\tif (!proc) {\n\t\t\tentry.reject(new SockaError(`Unknown call: ${frame.rpc}`));\n\t\t\treturn;\n\t\t}\n\n\t\tvoid parseStandardSchema(proc.output, frame.body).then(\n\t\t\t(validated) => entry.resolve(validated),\n\t\t\t(err) =>\n\t\t\t\tentry.reject(err instanceof Error ? err : new Error(String(err))),\n\t\t);\n\t}\n\n\tprivate handleServerError(frame: SockaServerErrorFrame): void {\n\t\tconst entry = this.pending.get(frame.id);\n\t\tif (!entry) return;\n\t\tthis.pending.delete(frame.id);\n\t\tentry.reject(SockaError.fromWire(frame));\n\t}\n\n\tprivate handleEvent(frame: SockaServerEventFrame): void {\n\t\tconst schema = (\n\t\t\tthis.client.contract.pushes as Record<\n\t\t\t\tstring,\n\t\t\t\tParameters<typeof parseStandardSchema>[0] | undefined\n\t\t\t>\n\t\t)[frame.event];\n\t\tif (schema) {\n\t\t\tvoid parseStandardSchema(schema, frame.body).then(\n\t\t\t\t(validated) => this.dispatchValidatedPush(frame.event, validated),\n\t\t\t\t(error: unknown) =>\n\t\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\t\tkind: \"clientEventValidation\",\n\t\t\t\t\t\teventName: frame.event,\n\t\t\t\t\t\terror,\n\t\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.dispatchValidatedPush(frame.event, frame.body);\n\t\t}\n\t}\n\n\tprivate dispatchValidatedPush(pushName: string, payload: unknown): void {\n\t\tconst set = this.pushListeners.get(pushName);\n\t\tif (!set || set.size === 0) return;\n\t\tfor (const fn of [...set]) {\n\t\t\ttry {\n\t\t\t\tconst result = fn(payload);\n\t\t\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\t\tkind: \"clientEventListener\",\n\t\t\t\t\t\teventName: pushName,\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\tkind: \"clientEventListener\",\n\t\t\t\t\teventName: pushName,\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate nextId(prefix: string): string {\n\t\treturn `${prefix}-${++this.idSeq}`;\n\t}\n\n\trejectAllPending(reason: Error): void {\n\t\tfor (const [, entry] of this.pending) {\n\t\t\tentry.reject(reason);\n\t\t}\n\t\tthis.pending.clear();\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tthis.client.close(code, reason);\n\t}\n\n\t/** Opens the WebSocket when using {@link SockaWebSocketClientOptions.autoConnect} `false`. */\n\tconnect(): Promise<void> {\n\t\treturn this.client.connect();\n\t}\n\n\t/** Same as {@link SockaWebSocketClient.status}. */\n\tget status(): SockaConnectionStatus {\n\t\treturn this.client.status;\n\t}\n\n\t/** Same as {@link SockaWebSocketClient.onStatusChange}. */\n\tonStatusChange(\n\t\tlistener: (status: SockaConnectionStatus) => void,\n\t): () => void {\n\t\treturn this.client.onStatusChange(listener);\n\t}\n}\n\nexport type SockaSession<TContract extends SockaContract<SockaContractConfig>> =\n\tSockaSessionBase<TContract>;\n\nexport interface SockaSessionConstructor {\n\tnew <TContract extends SockaContract<SockaContractConfig>>(\n\t\toptions: SockaSessionOptions<TContract>,\n\t): SockaSession<TContract>;\n}\n\n/**\n * WebSocket session: **`session.send`** for contract calls, **`session.subscribe`** for server pushes.\n */\nexport const SockaSession: SockaSessionConstructor =\n\tSockaSessionBase as SockaSessionConstructor;\n"]}
@@ -1,6 +1,9 @@
1
- import { parseWirePayload, decodeSockaWire, SockaWireError, encodeServerError, parseStandardSchema, SockaError, encodeServerResponse, encodeSockaWire, encodeServerEvent, reportSockaError } from './chunk-MZCQHJXY.js';
1
+ import { parseWirePayload, decodeSockaWire, SockaWireError, encodeServerError, parseStandardSchema, SockaError, encodeServerResponse, encodeSockaWire, encodeServerEvent, reportSockaError } from './chunk-2FNWVCP3.js';
2
2
  import { exhaustiveGuard } from '@firtoz/maybe-error';
3
3
 
4
+ function isStrictUpgradeConfig(config) {
5
+ return config.strictUpgradeRequest === true;
6
+ }
4
7
  function broadcastSockaEventToPeers(sessions, self, event, body, excludeSelf = false) {
5
8
  for (const [ws, session] of sessions) {
6
9
  if (excludeSelf && ws === self.websocket) continue;
@@ -13,12 +16,64 @@ var SockaWebSocketSession = class {
13
16
  this.sessions = sessions;
14
17
  this.config = config;
15
18
  this.wireFormat = config.wireFormat ?? "json";
16
- const create = config.createData ?? ((_i) => ({}));
17
- this._data = create(init ?? {});
19
+ if (isStrictUpgradeConfig(config)) {
20
+ if (!init?.request) {
21
+ throw new Error(
22
+ "socka: strictUpgradeRequest requires a Request on the upgrade init (e.g. Bun upgrade with `data: { \u2026, request: req }`, or Hono default sockaInit)"
23
+ );
24
+ }
25
+ const strictInit = { request: init.request };
26
+ if (config.createData) {
27
+ this._data = config.createData(strictInit);
28
+ } else {
29
+ this._data = {};
30
+ }
31
+ } else {
32
+ const createData = config.createData;
33
+ const create = createData ?? ((_i) => ({}));
34
+ this._data = create(init ?? {});
35
+ }
18
36
  }
19
37
  get data() {
20
38
  return this._data;
21
39
  }
40
+ /**
41
+ * Session data for every connection in the same {@link sessions} map (same room),
42
+ * optionally excluding this socket.
43
+ */
44
+ listPeers(options) {
45
+ const out = [];
46
+ for (const [ws, s] of this.sessions) {
47
+ if (options?.excludeSelf && ws === this.websocket) continue;
48
+ out.push(s.data);
49
+ }
50
+ return out;
51
+ }
52
+ /**
53
+ * Like {@link listPeers} but maps each peer {@link SockaWebSocketSession}
54
+ * (e.g. when you need more than {@link #data}).
55
+ */
56
+ listPeersWith(map, options) {
57
+ const out = [];
58
+ for (const [ws, s] of this.sessions) {
59
+ if (options?.excludeSelf && ws === this.websocket) continue;
60
+ out.push(map(s));
61
+ }
62
+ return out;
63
+ }
64
+ /** Count of sessions in this room (same {@link sessions} map), optionally excluding self. */
65
+ peerCount(options) {
66
+ let n = 0;
67
+ for (const [ws] of this.sessions) {
68
+ if (options?.excludeSelf && ws === this.websocket) continue;
69
+ n += 1;
70
+ }
71
+ return n;
72
+ }
73
+ /** Whether any peer sessions exist (optionally excluding self). */
74
+ hasPeers(options) {
75
+ return this.peerCount(options) > 0;
76
+ }
22
77
  /**
23
78
  * Invokes the user {@link typeof SockaWebSocketSessionConfig.handleClose} callback.
24
79
  * Server adapters should call this when the WebSocket closes, **before** deleting
@@ -131,7 +186,10 @@ var SockaWebSocketSession = class {
131
186
  const sockaErr = err instanceof SockaError ? err : new SockaError(
132
187
  err instanceof Error ? err.message : "Handler failed"
133
188
  );
134
- const errorFrame = encodeServerError(frame.id, sockaErr.message);
189
+ const errorFrame = encodeServerError(frame.id, sockaErr.message, {
190
+ code: sockaErr.code,
191
+ data: sockaErr.data
192
+ });
135
193
  this.sendWireFrame(errorFrame);
136
194
  return;
137
195
  }
@@ -232,5 +290,5 @@ function runSockaSessionOnAttached(config, session) {
232
290
  }
233
291
 
234
292
  export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached };
235
- //# sourceMappingURL=chunk-45D4T232.js.map
236
- //# sourceMappingURL=chunk-45D4T232.js.map
293
+ //# sourceMappingURL=chunk-JVLUA3Q5.js.map
294
+ //# sourceMappingURL=chunk-JVLUA3Q5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/SockaWebSocketSession.ts"],"names":[],"mappings":";;;AAuCA,SAAS,sBAIR,MAAA,EAGC;AACD,EAAA,OAAO,OAAO,oBAAA,KAAyB,IAAA;AACxC;AAgCO,SAAS,2BACf,QAAA,EACA,IAAA,EACA,KAAA,EACA,IAAA,EACA,cAAc,KAAA,EACP;AACP,EAAA,KAAA,MAAW,CAAC,EAAA,EAAI,OAAO,CAAA,IAAK,QAAA,EAAU;AACrC,IAAA,IAAI,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AAC1C,IAAA,OAAA,CAAQ,aAAA,CAAc,OAAO,IAAI,CAAA;AAAA,EAClC;AACD;AAMO,IAAM,wBAAN,MAIP;AAAA,EAKQ,WAAA,CACU,SAAA,EACG,QAAA,EAInB,MAAA,EACA,IAAA,EACC;AAPe,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACG,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAOnB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,UAAA,GAAa,OAAO,UAAA,IAAc,MAAA;AACvC,IAAA,IAAI,qBAAA,CAAsB,MAAM,CAAA,EAAG;AAClC,MAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACT;AAAA,SACD;AAAA,MACD;AACA,MAAA,MAAM,UAAA,GAAuC,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAQ;AACrE,MAAA,IAAI,OAAO,UAAA,EAAY;AACtB,QAAA,IAAA,CAAK,KAAA,GAAQ,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AAAA,MAC1C,CAAA,MAAO;AACN,QAAA,IAAA,CAAK,QAAQ,EAAC;AAAA,MACf;AAAA,IACD,CAAA,MAAO;AACN,MAAA,MAAM,aAAa,MAAA,CAAO,UAAA;AAG1B,MAAA,MAAM,MAAA,GAAS,UAAA,KAAe,CAAC,EAAA,MAA4B,EAAC,CAAA,CAAA;AAC5D,MAAA,IAAA,CAAK,KAAA,GAAQ,MAAA,CAAO,IAAA,IAAQ,EAAE,CAAA;AAAA,IAC/B;AAAA,EACD;AAAA,EAEA,IAAW,IAAA,GAAc;AACxB,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU,OAAA,EAA8C;AAC9D,IAAA,MAAM,MAAe,EAAC;AACtB,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,CAAA,IAAK,KAAK,QAAA,EAAU;AACpC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,IAAI,CAAA;AAAA,IAChB;AACA,IAAA,OAAO,GAAA;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAA,CACN,KACA,OAAA,EACM;AACN,IAAA,MAAM,MAAW,EAAC;AAClB,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,CAAA,IAAK,KAAK,QAAA,EAAU;AACpC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,IAChB;AACA,IAAA,OAAO,GAAA;AAAA,EACR;AAAA;AAAA,EAGO,UAAU,OAAA,EAA6C;AAC7D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,CAAC,EAAE,CAAA,IAAK,IAAA,CAAK,QAAA,EAAU;AACjC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,CAAA,IAAK,CAAA;AAAA,IACN;AACA,IAAA,OAAO,CAAA;AAAA,EACR;AAAA;AAAA,EAGO,SAAS,OAAA,EAA8C;AAC7D,IAAA,OAAO,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,GAAI,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,iBAAA,GAAmC;AAC/C,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,IAAI,CAAA;AAAA,EACnC;AAAA,EAEA,MAAa,iBAAiB,UAAA,EAAmC;AAChE,IAAA,IAAI,IAAA,CAAK,eAAe,MAAA,EAAQ;AAC/B,MAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,QACV,IAAI,MAAM,8CAA8C,CAAA;AAAA,QACxD;AAAA,OACD;AACA,MAAA;AAAA,IACD;AACA,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,MAAA,CAAO,eAAA,IAAmB,IAAA,CAAK,KAAA;AACxD,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACH,MAAA,MAAA,GAAS,YAAY,UAAU,CAAA;AAAA,IAChC,CAAA,CAAA,MAAQ;AACP,MAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,QACV,IAAI,MAAM,qBAAqB,CAAA;AAAA,QAC/B;AAAA,OACD;AACA,MAAA;AAAA,IACD;AACA,IAAA,MAAM,IAAA,CAAK,mBAAA,CAAoB,MAAA,EAAQ,UAAU,CAAA;AAAA,EAClD;AAAA,EAEA,MAAa,oBAAoB,MAAA,EAAoC;AACpE,IAAA,IAAI,IAAA,CAAK,eAAe,SAAA,EAAW;AAClC,MAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,QACV,IAAI,MAAM,6CAA6C,CAAA;AAAA,QACvD;AAAA,OACD;AACA,MAAA;AAAA,IACD;AACA,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACH,MAAA,MAAA,GAAS,gBAAA,CAAiB,QAAQ,SAAS,CAAA;AAAA,IAC5C,SAAS,GAAA,EAAK;AACb,MAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,QACV,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,QACrE;AAAA,OACD;AACA,MAAA;AAAA,IACD;AACA,IAAA,MAAM,IAAA,CAAK,mBAAA,CAAoB,MAAA,EAAQ,MAAM,CAAA;AAAA,EAC9C;AAAA,EAEA,MAAc,mBAAA,CACb,MAAA,EACA,YAAA,EACgB;AAChB,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACH,MAAA,OAAA,GAAU,gBAAgB,MAAM,CAAA;AAAA,IACjC,SAAS,GAAA,EAAK;AACb,MAAA,IAAI,eAAe,cAAA,EAAgB;AAClC,QAAA,MAAM,IAAA,CAAK,qBAAA,CAAsB,GAAA,EAAK,YAAY,CAAA;AAClD,QAAA;AAAA,MACD;AACA,MAAA,MAAM,GAAA;AAAA,IACP;AAEA,IAAA,QAAQ,QAAQ,IAAA;AAAM,MACrB,KAAK,eAAA;AACJ,QAAA,MAAM,IAAA,CAAK,qBAAA,CAAsB,OAAA,CAAQ,KAAA,EAAO,YAAY,CAAA;AAC5D,QAAA;AAAA,MACD,KAAK,gBAAA;AAAA,MACL,KAAK,aAAA;AAAA,MACL,KAAK,aAAA;AACJ,QAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,UACV,IAAI,MAAM,uDAAuD,CAAA;AAAA,UACjE;AAAA,SACD;AACA,QAAA;AAAA,MACD;AACC,QAAA,eAAA,CAAgB,OAAO,CAAA;AAAA;AACzB,EACD;AAAA,EAEA,MAAc,qBAAA,CACb,KAAA,EACA,aAAA,EACgB;AAChB,IAAA,MAAM,UAAU,KAAA,CAAM,GAAA;AACtB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,MAAM,OAAO,CAAA;AAEpD,IAAA,IAAI,CAAC,SAAA,EAAW;AACf,MAAA,MAAM,UAAA,GAAa,iBAAA;AAAA,QAClB,KAAA,CAAM,EAAA;AAAA,QACN,iBAAiB,OAAO,CAAA;AAAA,OACzB;AACA,MAAA,IAAA,CAAK,cAAc,UAAU,CAAA;AAC7B,MAAA;AAAA,IACD;AAEA,IAAA,IAAI,cAAA;AACJ,IAAA,IAAI,UAAU,KAAA,EAAO;AACpB,MAAA,IAAI;AACH,QAAA,cAAA,GAAiB,MAAM,mBAAA,CAAoB,SAAA,CAAU,KAAA,EAAO,MAAM,IAAI,CAAA;AAAA,MACvE,SAAS,GAAA,EAAK;AACb,QAAA,MAAM,GAAA,GACL,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,yBAAA;AACtC,QAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,KAAA,CAAM,EAAA,EAAI,GAAG,CAAA;AAClD,QAAA,IAAA,CAAK,cAAc,UAAU,CAAA;AAC7B,QAAA;AAAA,MACD;AAAA,IACD;AAEA,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACH,MAAA,IAAI,UAAU,KAAA,EAAO;AACpB,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAI5C,QAAA,MAAA,GAAS,MAAM,OAAA,CAAQ,cAAA,EAAgB,IAAI,CAAA;AAAA,MAC5C,CAAA,MAAO;AACN,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAG5C,QAAA,MAAA,GAAS,MAAM,QAAQ,IAAI,CAAA;AAAA,MAC5B;AAAA,IACD,SAAS,GAAA,EAAK;AACb,MAAA,IAAA,CAAK,MAAA,CAAO,cAAA,GAAiB,GAAA,EAAK,OAAA,EAAS,gBAAgB,IAAI,CAAA;AAC/D,MAAA,MAAM,QAAA,GACL,GAAA,YAAe,UAAA,GACZ,GAAA,GACA,IAAI,UAAA;AAAA,QACJ,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU;AAAA,OACtC;AACH,MAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,KAAA,CAAM,EAAA,EAAI,SAAS,OAAA,EAAS;AAAA,QAChE,MAAM,QAAA,CAAS,IAAA;AAAA,QACf,MAAM,QAAA,CAAS;AAAA,OACf,CAAA;AACD,MAAA,IAAA,CAAK,cAAc,UAAU,CAAA;AAC7B,MAAA;AAAA,IACD;AAEA,IAAA,IAAI,eAAA;AACJ,IAAA,IAAI;AACH,MAAA,eAAA,GAAkB,MAAM,mBAAA,CAAoB,SAAA,CAAU,MAAA,EAAQ,MAAM,CAAA;AAAA,IACrE,SAAS,GAAA,EAAK;AACb,MAAA,MAAM,GAAA,GACL,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,0BAAA;AACtC,MAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,KAAA,CAAM,EAAA,EAAI,GAAG,CAAA;AAClD,MAAA,IAAA,CAAK,cAAc,UAAU,CAAA;AAC7B,MAAA;AAAA,IACD;AAEA,IAAA,MAAM,aAAA,GAAgB,oBAAA;AAAA,MACrB,KAAA,CAAM,EAAA;AAAA,MACN,OAAA;AAAA,MACA;AAAA,KACD;AACA,IAAA,IAAA,CAAK,cAAc,aAAa,CAAA;AAAA,EACjC;AAAA,EAEQ,eAAe,KAAA,EAA4C;AAClE,IAAA,OAAO,eAAA;AAAA,MACN,KAAA;AAAA,MACA,IAAA,CAAK,UAAA;AAAA,MACL,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,IAAA,CAAK;AAAA,KACnC;AAAA,EACD;AAAA,EAEQ,cAAc,KAAA,EAA6B;AAClD,IAAA,IAAI,IAAA,CAAK,SAAA,CAAU,UAAA,KAAe,SAAA,CAAU,IAAA,EAAM;AACjD,MAAA;AAAA,IACD;AACA,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,cAAA,CAAe,KAAK,CAAA;AACzC,IAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAChC,MAAA,IAAA,CAAK,SAAA,CAAU,KAAK,OAAO,CAAA;AAC3B,MAAA;AAAA,IACD;AACA,IAAA,MAAM,IAAA,GAAO,IAAI,UAAA,CAAW,OAAA,CAAQ,UAAU,CAAA;AAC9C,IAAA,IAAA,CAAK,IAAI,OAAO,CAAA;AAChB,IAAA,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAA,CAAc,OAAe,IAAA,EAAqB;AACxD,IAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,KAAA,EAAO,IAAI,CAAA;AAC3C,IAAA,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,EACzB;AAAA,EAEA,MAAa,QAAA,CACZ,IAAA,EACA,IAAA,EACgB;AAChB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,OAAO,IAAI,CAAA;AAC/C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,MAAA,CAAO,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IACtD;AACA,IAAA,MAAM,YAAY,MAAM,mBAAA;AAAA,MACvB,MAAA;AAAA,MACA;AAAA,KACD;AACA,IAAA,IAAA,CAAK,aAAA,CAAc,MAAM,SAAS,CAAA;AAAA,EACnC;AAAA,EAEA,MAAa,aAAA,CACZ,IAAA,EACA,IAAA,EACA,cAAc,KAAA,EACE;AAChB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,OAAO,IAAI,CAAA;AAC/C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,MAAA,CAAO,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IACtD;AACA,IAAA,MAAM,YAAY,MAAM,mBAAA;AAAA,MACvB,MAAA;AAAA,MACA;AAAA,KACD;AACA,IAAA,0BAAA;AAAA,MACC,IAAA,CAAK,QAAA;AAAA,MACL,IAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACD;AAAA,EACD;AAAA,EAEA,MAAc,qBAAA,CACb,KAAA,EACA,eAAA,EACgB;AAChB,IAAA,IAAI,IAAA,CAAK,OAAO,iBAAA,EAAmB;AAClC,MAAA,MAAM,IAAA,CAAK,MAAA,CAAO,iBAAA,CAAkB,KAAA,EAAO,eAAe,CAAA;AAAA,IAC3D,CAAA,MAAO;AACN,MAAA,OAAA,CAAQ,KAAA,CAAM,0BAAA,EAA4B,KAAA,EAAO,eAAe,CAAA;AAAA,IACjE;AAAA,EACD;AACD;AAMO,SAAS,yBAAA,CAIf,QACA,OAAA,EACO;AACP,EAAA,MAAM,KAAK,MAAA,CAAO,UAAA;AAClB,EAAA,IAAI,CAAC,EAAA,EAAI;AACT,EAAA,IAAI;AACH,IAAA,MAAM,MAAA,GAAS,GAAG,OAAO,CAAA;AACzB,IAAA,KAAK,QAAQ,OAAA,CAAQ,MAAM,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AACtD,MAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa;AAAA,QACpC,IAAA,EAAM,kBAAA;AAAA,QACN;AAAA,OACA,CAAA;AAAA,IACF,CAAC,CAAA;AAAA,EACF,SAAS,KAAA,EAAO;AACf,IAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,CAAA;AAAA,EACzE;AACD","file":"chunk-JVLUA3Q5.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport type {\n\tInferSockaPushPayload,\n\tSockaContract,\n\tSockaContractConfig,\n} from \"../core/contract\";\nimport {\n\tSockaWireError,\n\tdecodeSockaWire,\n\tencodeServerResponse,\n\tencodeServerError,\n\tencodeServerEvent,\n\ttype SockaClientRequestFrame,\n\ttype SockaWireFrame,\n} from \"../core/envelope\";\nimport {\n\tencodeSockaWire,\n\tparseWirePayload,\n\ttype SockaWireFormat,\n} from \"../core/wire-codec\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport { parseStandardSchema } from \"../core/validate\";\nimport { SockaError } from \"../core/socka-error\";\nimport type {\n\tSockaStrictWebSocketInit,\n\tSockaWebSocketInit,\n\tSockaWebSocketSessionConfig,\n} from \"./SockaWebSocketSessionConfig\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\nexport type {\n\tSockaStrictWebSocketInit,\n\tSockaWebSocketInit,\n\tSockaWebSocketSessionConfig,\n};\n\nfunction isStrictUpgradeConfig<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n): config is SockaWebSocketSessionConfig<TContract, TData> & {\n\tstrictUpgradeRequest: true;\n} {\n\treturn config.strictUpgradeRequest === true;\n}\n\n/** Session that can send a wire-level server event (already validated). */\nexport type SockaEmitCapable = {\n\temitWireEvent(event: string, body: unknown): void;\n};\n\n/**\n * Contract-typed session surface for handlers that push to clients.\n */\nexport interface SockaPushSession<\n\tTContract extends SockaContract<SockaContractConfig>,\n> {\n\temitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void>;\n\tbroadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf?: boolean,\n\t): Promise<void>;\n}\n\n/**\n * Broadcast a socka server event to every session in the map (optionally\n * excluding the caller). Payload must already be contract-validated.\n *\n * Exclusion uses the **WebSocket** identity (`self.websocket`), not the session\n * object reference, so the same `sessions` map can hold `SockaDoSession` while\n * `broadcastPush` runs on `this.socka` (inner {@link SockaWebSocketSession}).\n */\nexport function broadcastSockaEventToPeers(\n\tsessions: Map<WebSocket, SockaEmitCapable>,\n\tself: SockaEmitCapable & { readonly websocket: WebSocket },\n\tevent: string,\n\tbody: unknown,\n\texcludeSelf = false,\n): void {\n\tfor (const [ws, session] of sessions) {\n\t\tif (excludeSelf && ws === self.websocket) continue;\n\t\tsession.emitWireEvent(event, body);\n\t}\n}\n\n/**\n * Runtime-agnostic socka server session: standard {@link WebSocket} wire\n * dispatch without Cloudflare Durable Object APIs.\n */\nexport class SockaWebSocketSession<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData = EmptySockaSessionData,\n> implements SockaPushSession<TContract>\n{\n\tprivate readonly config: SockaWebSocketSessionConfig<TContract, TData>;\n\tprivate readonly wireFormat: SockaWireFormat;\n\tprivate _data!: TData;\n\n\tpublic constructor(\n\t\tpublic readonly websocket: WebSocket,\n\t\tprotected readonly sessions: Map<\n\t\t\tWebSocket,\n\t\t\tSockaWebSocketSession<TContract, TData>\n\t\t>,\n\t\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n\t\tinit?: SockaWebSocketInit,\n\t) {\n\t\tthis.config = config;\n\t\tthis.wireFormat = config.wireFormat ?? \"json\";\n\t\tif (isStrictUpgradeConfig(config)) {\n\t\t\tif (!init?.request) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"socka: strictUpgradeRequest requires a Request on the upgrade init (e.g. Bun upgrade with `data: { …, request: req }`, or Hono default sockaInit)\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst strictInit: SockaStrictWebSocketInit = { request: init.request };\n\t\t\tif (config.createData) {\n\t\t\t\tthis._data = config.createData(strictInit);\n\t\t\t} else {\n\t\t\t\tthis._data = {} as TData;\n\t\t\t}\n\t\t} else {\n\t\t\tconst createData = config.createData as\n\t\t\t\t| ((init: SockaWebSocketInit) => TData)\n\t\t\t\t| undefined;\n\t\t\tconst create = createData ?? ((_i: SockaWebSocketInit) => ({}) as TData);\n\t\t\tthis._data = create(init ?? {});\n\t\t}\n\t}\n\n\tpublic get data(): TData {\n\t\treturn this._data;\n\t}\n\n\t/**\n\t * Session data for every connection in the same {@link sessions} map (same room),\n\t * optionally excluding this socket.\n\t */\n\tpublic listPeers(options?: { excludeSelf?: boolean }): TData[] {\n\t\tconst out: TData[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(s.data);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * Like {@link listPeers} but maps each peer {@link SockaWebSocketSession}\n\t * (e.g. when you need more than {@link #data}).\n\t */\n\tpublic listPeersWith<R>(\n\t\tmap: (session: SockaWebSocketSession<TContract, TData>) => R,\n\t\toptions?: { excludeSelf?: boolean },\n\t): R[] {\n\t\tconst out: R[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(map(s));\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Count of sessions in this room (same {@link sessions} map), optionally excluding self. */\n\tpublic peerCount(options?: { excludeSelf?: boolean }): number {\n\t\tlet n = 0;\n\t\tfor (const [ws] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tn += 1;\n\t\t}\n\t\treturn n;\n\t}\n\n\t/** Whether any peer sessions exist (optionally excluding self). */\n\tpublic hasPeers(options?: { excludeSelf?: boolean }): boolean {\n\t\treturn this.peerCount(options) > 0;\n\t}\n\n\t/**\n\t * Invokes the user {@link typeof SockaWebSocketSessionConfig.handleClose} callback.\n\t * Server adapters should call this when the WebSocket closes, **before** deleting\n\t * this session from the shared `sessions` map.\n\t */\n\tpublic async invokeHandleClose(): Promise<void> {\n\t\tawait this.config.handleClose(this);\n\t}\n\n\tpublic async handleRawMessage(rawMessage: string): Promise<void> {\n\t\tif (this.wireFormat !== \"json\") {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: unexpected JSON frame in msgpack mode\"),\n\t\t\t\trawMessage,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst deserialize = this.config.deserializeJson ?? JSON.parse;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = deserialize(rawMessage);\n\t\t} catch {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: invalid JSON\"),\n\t\t\t\trawMessage,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tawait this.dispatchAfterParsed(parsed, rawMessage);\n\t}\n\n\tpublic async handleBinaryMessage(buffer: ArrayBuffer): Promise<void> {\n\t\tif (this.wireFormat !== \"msgpack\") {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: unexpected binary frame in JSON mode\"),\n\t\t\t\tbuffer,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = parseWirePayload(buffer, \"msgpack\");\n\t\t} catch (err) {\n\t\t\tawait this.reportValidationError(\n\t\t\t\terr instanceof Error ? err : new Error(\"socka: msgpack decode failed\"),\n\t\t\t\tbuffer,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tawait this.dispatchAfterParsed(parsed, buffer);\n\t}\n\n\tprivate async dispatchAfterParsed(\n\t\tparsed: unknown,\n\t\toriginalWire: unknown,\n\t): Promise<void> {\n\t\tlet decoded: ReturnType<typeof decodeSockaWire>;\n\t\ttry {\n\t\t\tdecoded = decodeSockaWire(parsed);\n\t\t} catch (err) {\n\t\t\tif (err instanceof SockaWireError) {\n\t\t\t\tawait this.reportValidationError(err, originalWire);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\n\t\tswitch (decoded.kind) {\n\t\t\tcase \"clientRequest\":\n\t\t\t\tawait this.dispatchClientRequest(decoded.frame, originalWire);\n\t\t\t\treturn;\n\t\t\tcase \"serverResponse\":\n\t\t\tcase \"serverError\":\n\t\t\tcase \"serverEvent\":\n\t\t\t\tawait this.reportValidationError(\n\t\t\t\t\tnew Error(\"socka: unexpected server-originated frame from client\"),\n\t\t\t\t\tparsed,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tdefault:\n\t\t\t\texhaustiveGuard(decoded);\n\t\t}\n\t}\n\n\tprivate async dispatchClientRequest(\n\t\tframe: SockaClientRequestFrame,\n\t\t_originalWire: unknown,\n\t): Promise<void> {\n\t\tconst rpcName = frame.rpc;\n\t\tconst procedure = this.config.contract.calls[rpcName];\n\n\t\tif (!procedure) {\n\t\t\tconst errorFrame = encodeServerError(\n\t\t\t\tframe.id,\n\t\t\t\t`Unknown call: ${rpcName}`,\n\t\t\t);\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tlet validatedInput: unknown;\n\t\tif (procedure.input) {\n\t\t\ttry {\n\t\t\t\tvalidatedInput = await parseStandardSchema(procedure.input, frame.body);\n\t\t\t} catch (err) {\n\t\t\t\tconst msg =\n\t\t\t\t\terr instanceof Error ? err.message : \"Input validation failed\";\n\t\t\t\tconst errorFrame = encodeServerError(frame.id, msg);\n\t\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet result: unknown;\n\t\ttry {\n\t\t\tif (procedure.input) {\n\t\t\t\tconst handler = this.config.handlers[rpcName] as (\n\t\t\t\t\tinput: unknown,\n\t\t\t\t\ts: SockaWebSocketSession<TContract, TData>,\n\t\t\t\t) => unknown | Promise<unknown>;\n\t\t\t\tresult = await handler(validatedInput, this);\n\t\t\t} else {\n\t\t\t\tconst handler = this.config.handlers[rpcName] as (\n\t\t\t\t\ts: SockaWebSocketSession<TContract, TData>,\n\t\t\t\t) => unknown | Promise<unknown>;\n\t\t\t\tresult = await handler(this);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.config.onHandlerError?.(err, rpcName, validatedInput, this);\n\t\t\tconst sockaErr =\n\t\t\t\terr instanceof SockaError\n\t\t\t\t\t? err\n\t\t\t\t\t: new SockaError(\n\t\t\t\t\t\t\terr instanceof Error ? err.message : \"Handler failed\",\n\t\t\t\t\t\t);\n\t\t\tconst errorFrame = encodeServerError(frame.id, sockaErr.message, {\n\t\t\t\tcode: sockaErr.code,\n\t\t\t\tdata: sockaErr.data,\n\t\t\t});\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tlet validatedOutput: unknown;\n\t\ttry {\n\t\t\tvalidatedOutput = await parseStandardSchema(procedure.output, result);\n\t\t} catch (err) {\n\t\t\tconst msg =\n\t\t\t\terr instanceof Error ? err.message : \"Output validation failed\";\n\t\t\tconst errorFrame = encodeServerError(frame.id, msg);\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tconst responseFrame = encodeServerResponse(\n\t\t\tframe.id,\n\t\t\trpcName,\n\t\t\tvalidatedOutput,\n\t\t);\n\t\tthis.sendWireFrame(responseFrame);\n\t}\n\n\tprivate encodeOutgoing(frame: SockaWireFrame): string | Uint8Array {\n\t\treturn encodeSockaWire(\n\t\t\tframe,\n\t\t\tthis.wireFormat,\n\t\t\tthis.config.serializeJson ?? JSON.stringify,\n\t\t);\n\t}\n\n\tprivate sendWireFrame(frame: SockaWireFrame): void {\n\t\tif (this.websocket.readyState !== WebSocket.OPEN) {\n\t\t\treturn;\n\t\t}\n\t\tconst encoded = this.encodeOutgoing(frame);\n\t\tif (typeof encoded === \"string\") {\n\t\t\tthis.websocket.send(encoded);\n\t\t\treturn;\n\t\t}\n\t\tconst copy = new Uint8Array(encoded.byteLength);\n\t\tcopy.set(encoded);\n\t\tthis.websocket.send(copy.buffer);\n\t}\n\n\t/**\n\t * Send a server event frame (wire). Prefer {@link emitPush} so\n\t * payloads are validated against the contract.\n\t */\n\tpublic emitWireEvent(event: string, body: unknown): void {\n\t\tconst frame = encodeServerEvent(event, body);\n\t\tthis.sendWireFrame(frame);\n\t}\n\n\tpublic async emitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void> {\n\t\tconst schema = this.config.contract.pushes[name];\n\t\tif (!schema) {\n\t\t\tthrow new Error(`socka: unknown push ${String(name)}`);\n\t\t}\n\t\tconst validated = await parseStandardSchema(\n\t\t\tschema as StandardSchemaV1<unknown, InferSockaPushPayload<TContract, K>>,\n\t\t\tbody,\n\t\t);\n\t\tthis.emitWireEvent(name, validated);\n\t}\n\n\tpublic async broadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf = false,\n\t): Promise<void> {\n\t\tconst schema = this.config.contract.pushes[name];\n\t\tif (!schema) {\n\t\t\tthrow new Error(`socka: unknown push ${String(name)}`);\n\t\t}\n\t\tconst validated = await parseStandardSchema(\n\t\t\tschema as StandardSchemaV1<unknown, InferSockaPushPayload<TContract, K>>,\n\t\t\tbody,\n\t\t);\n\t\tbroadcastSockaEventToPeers(\n\t\t\tthis.sessions,\n\t\t\tthis,\n\t\t\tname,\n\t\t\tvalidated,\n\t\t\texcludeSelf,\n\t\t);\n\t}\n\n\tprivate async reportValidationError(\n\t\terror: unknown,\n\t\toriginalMessage: unknown,\n\t): Promise<void> {\n\t\tif (this.config.onValidationError) {\n\t\t\tawait this.config.onValidationError(error, originalMessage);\n\t\t} else {\n\t\t\tconsole.error(\"socka: validation error:\", error, originalMessage);\n\t\t}\n\t}\n}\n\n/**\n * Invoke {@link SockaWebSocketSessionConfig.onAttached} after the session is\n * registered in the shared map.\n */\nexport function runSockaSessionOnAttached<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n\tsession: SockaWebSocketSession<TContract, TData>,\n): void {\n\tconst cb = config.onAttached;\n\tif (!cb) return;\n\ttry {\n\t\tconst result = cb(session);\n\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverOnAttached\",\n\t\t\t\terror,\n\t\t\t});\n\t\t});\n\t} catch (error) {\n\t\treportSockaError(config.reportError, { kind: \"serverOnAttached\", error });\n\t}\n}\n"]}
@@ -0,0 +1,8 @@
1
+ // src/hono/strict-init-context.ts
2
+ function sockaHonoStrictInitFromContext(c) {
3
+ return { request: new Request(c.req.url) };
4
+ }
5
+
6
+ export { sockaHonoStrictInitFromContext };
7
+ //# sourceMappingURL=chunk-KQO5AVKA.js.map
8
+ //# sourceMappingURL=chunk-KQO5AVKA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hono/strict-init-context.ts"],"names":[],"mappings":";AAcO,SAAS,+BACf,CAAA,EAC2B;AAC3B,EAAA,OAAO,EAAE,OAAA,EAAS,IAAI,QAAQ,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA,EAAE;AAC1C","file":"chunk-KQO5AVKA.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { SockaStrictWebSocketInit } from \"../server/SockaWebSocketSession\";\n\n/**\n * Build {@link SockaStrictWebSocketInit} from a Hono {@link Context} by synthesizing a\n * **`Request`** from **`c.req.url`** (method GET; URL matches the incoming upgrade).\n *\n * Used when **`sockaHonoNodeWs` / `sockaHonoCloudflare`** omit a custom **`sockaInit`** and\n * **`strictUpgradeRequest: true`** is set: **`createData`** then always receives\n * **`init.request`** without you writing **`sockaInit: (ctx) => ({ request: new Request(ctx.req.url) })`** by hand.\n *\n * For full fidelity to the original upgrade (headers, method), pass your own **`sockaInit`**\n * that forwards the real **`Request`** from your runtime instead of this helper.\n */\nexport function sockaHonoStrictInitFromContext(\n\tc: Context,\n): SockaStrictWebSocketInit {\n\treturn { request: new Request(c.req.url) };\n}\n"]}