@firtoz/socka 2.0.0 → 3.0.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.
- package/README.md +195 -42
- package/dist/SockaWebSocketSession-B1w7RAid.d.ts +209 -0
- package/dist/bun/index.d.ts +30 -5
- package/dist/bun/index.js +28 -5
- package/dist/bun/index.js.map +1 -1
- package/dist/{chunk-MZCQHJXY.js → chunk-IFIGKR3W.js} +45 -8
- package/dist/chunk-IFIGKR3W.js.map +1 -0
- package/dist/{chunk-45D4T232.js → chunk-LVVCHLNW.js} +74 -9
- package/dist/chunk-LVVCHLNW.js.map +1 -0
- package/dist/{chunk-AM7PB26G.js → chunk-P3JEEOJL.js} +192 -10
- package/dist/chunk-P3JEEOJL.js.map +1 -0
- package/dist/chunk-QGURL3DJ.js +8 -0
- package/dist/chunk-QGURL3DJ.js.map +1 -0
- package/dist/client/index.d.ts +59 -3
- package/dist/client/index.js +2 -2
- package/dist/core/index.d.ts +2 -21
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/do/index.d.ts +20 -2
- package/dist/do/index.js +36 -2
- package/dist/do/index.js.map +1 -1
- package/dist/hono/cloudflare-workers.d.ts +4 -4
- package/dist/hono/cloudflare-workers.js +4 -3
- package/dist/hono/cloudflare-workers.js.map +1 -1
- package/dist/hono/index.d.ts +22 -6
- package/dist/hono/index.js +5 -3
- package/dist/hono/index.js.map +1 -1
- package/dist/react/index.d.ts +43 -4
- package/dist/react/index.js +103 -9
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.d.ts +18 -5
- package/dist/server/index.js +24 -4
- package/dist/server/index.js.map +1 -1
- package/dist/{socka-report-error-DzFI2Tr7.d.ts → socka-report-error-CXwpAUgl.d.ts} +80 -8
- package/dist/test/index.d.ts +11 -0
- package/dist/test/index.js +84 -0
- package/dist/test/index.js.map +1 -0
- package/docs/README.md +16 -7
- package/docs/auth.md +27 -0
- package/docs/backpressure.md +16 -0
- package/docs/client.md +48 -3
- package/docs/comparison.md +2 -2
- package/docs/durable-objects.md +3 -3
- package/docs/getting-started.md +143 -84
- package/docs/history.md +26 -0
- package/docs/internals.md +56 -0
- package/docs/lifecycle.md +3 -3
- package/docs/multi-room.md +10 -8
- package/docs/peers.md +11 -7
- package/docs/presence.md +43 -0
- package/docs/{events.md → pushes.md} +1 -1
- package/docs/recipes.md +77 -0
- package/docs/reconnection.md +44 -0
- package/docs/reference.md +27 -32
- package/docs/server.md +19 -3
- package/docs/testing.md +20 -0
- package/docs/wire-format.md +29 -0
- package/examples/minimal-socka.ts +56 -3
- package/package.json +14 -10
- package/roadmap.md +2 -2
- package/skills/socka/core-rpc/SKILL.md +2 -2
- package/skills/socka/do-session/SKILL.md +2 -2
- package/skills/socka/standard-schema/SKILL.md +1 -1
- package/dist/SockaWebSocketSession-Bru8yFcK.d.ts +0 -107
- package/dist/chunk-45D4T232.js.map +0 -1
- package/dist/chunk-AM7PB26G.js.map +0 -1
- package/dist/chunk-MZCQHJXY.js.map +0 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { RESERVED_SOCKA_PROCEDURE_NAMES } from './chunk-YMT4HAH7.js';
|
|
2
|
-
import { parseWirePayload, decodeSockaWire, SockaWireError, encodeClientRequest, encodeSockaWire, SockaError,
|
|
2
|
+
import { parseWirePayload, decodeSockaWire, SockaWireError, encodeClientRequest, encodeSockaWire, SockaError, reportSockaError, parseStandardSchema } from './chunk-IFIGKR3W.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() {
|
|
@@ -329,6 +443,15 @@ var SockaSessionBase = class {
|
|
|
329
443
|
}
|
|
330
444
|
const id = this.nextId(callName);
|
|
331
445
|
const body = input !== void 0 && input !== null ? input : {};
|
|
446
|
+
const proc = this.client.contract.calls[callName];
|
|
447
|
+
if (proc !== void 0 && proc.output === void 0) {
|
|
448
|
+
try {
|
|
449
|
+
this.client.sendRequest(id, callName, body);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
452
|
+
}
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
332
455
|
return new Promise((resolve, reject) => {
|
|
333
456
|
this.pending.set(id, { rpc: callName, resolve, reject });
|
|
334
457
|
try {
|
|
@@ -341,14 +464,42 @@ var SockaSessionBase = class {
|
|
|
341
464
|
})();
|
|
342
465
|
}
|
|
343
466
|
handleResponse(frame) {
|
|
344
|
-
const entry = this.pending.get(frame.id);
|
|
345
|
-
if (!entry) return;
|
|
346
|
-
this.pending.delete(frame.id);
|
|
347
467
|
const proc = this.client.contract.calls[frame.rpc];
|
|
348
468
|
if (!proc) {
|
|
349
|
-
|
|
469
|
+
const entry2 = this.pending.get(frame.id);
|
|
470
|
+
if (entry2) {
|
|
471
|
+
this.pending.delete(frame.id);
|
|
472
|
+
entry2.reject(new SockaError(`Unknown call: ${frame.rpc}`));
|
|
473
|
+
}
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (proc.output === void 0) {
|
|
477
|
+
const entry2 = this.pending.get(frame.id);
|
|
478
|
+
if (entry2) {
|
|
479
|
+
this.pending.delete(frame.id);
|
|
480
|
+
reportSockaError(this.reportError, {
|
|
481
|
+
kind: "clientUnexpectedServerResponse",
|
|
482
|
+
rpc: frame.rpc,
|
|
483
|
+
requestId: frame.id
|
|
484
|
+
});
|
|
485
|
+
entry2.reject(
|
|
486
|
+
new SockaError(
|
|
487
|
+
"socka: unexpected serverResponse for fire-and-forget call",
|
|
488
|
+
{ requestId: frame.id, rpc: frame.rpc }
|
|
489
|
+
)
|
|
490
|
+
);
|
|
491
|
+
} else {
|
|
492
|
+
reportSockaError(this.reportError, {
|
|
493
|
+
kind: "clientUnexpectedServerResponse",
|
|
494
|
+
rpc: frame.rpc,
|
|
495
|
+
requestId: frame.id
|
|
496
|
+
});
|
|
497
|
+
}
|
|
350
498
|
return;
|
|
351
499
|
}
|
|
500
|
+
const entry = this.pending.get(frame.id);
|
|
501
|
+
if (!entry) return;
|
|
502
|
+
this.pending.delete(frame.id);
|
|
352
503
|
void parseStandardSchema(proc.output, frame.body).then(
|
|
353
504
|
(validated) => entry.resolve(validated),
|
|
354
505
|
(err) => entry.reject(err instanceof Error ? err : new Error(String(err)))
|
|
@@ -356,9 +507,32 @@ var SockaSessionBase = class {
|
|
|
356
507
|
}
|
|
357
508
|
handleServerError(frame) {
|
|
358
509
|
const entry = this.pending.get(frame.id);
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
510
|
+
if (entry) {
|
|
511
|
+
this.pending.delete(frame.id);
|
|
512
|
+
entry.reject(SockaError.fromWire(frame));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const err = SockaError.fromWire(frame);
|
|
516
|
+
const rpcName = frame.rpc;
|
|
517
|
+
if (rpcName === void 0) {
|
|
518
|
+
reportSockaError(this.reportError, {
|
|
519
|
+
kind: "clientFireAndForgetRpcError",
|
|
520
|
+
error: err
|
|
521
|
+
});
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const proc = this.client.contract.calls[rpcName];
|
|
525
|
+
if (proc !== void 0 && proc.output === void 0) {
|
|
526
|
+
reportSockaError(this.reportError, {
|
|
527
|
+
kind: "clientFireAndForgetRpcError",
|
|
528
|
+
error: err
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
reportSockaError(this.reportError, {
|
|
533
|
+
kind: "clientOrphanServerError",
|
|
534
|
+
error: err
|
|
535
|
+
});
|
|
362
536
|
}
|
|
363
537
|
handleEvent(frame) {
|
|
364
538
|
const schema = this.client.contract.pushes[frame.event];
|
|
@@ -413,9 +587,17 @@ var SockaSessionBase = class {
|
|
|
413
587
|
connect() {
|
|
414
588
|
return this.client.connect();
|
|
415
589
|
}
|
|
590
|
+
/** Same as {@link SockaWebSocketClient.status}. */
|
|
591
|
+
get status() {
|
|
592
|
+
return this.client.status;
|
|
593
|
+
}
|
|
594
|
+
/** Same as {@link SockaWebSocketClient.onStatusChange}. */
|
|
595
|
+
onStatusChange(listener) {
|
|
596
|
+
return this.client.onStatusChange(listener);
|
|
597
|
+
}
|
|
416
598
|
};
|
|
417
599
|
var SockaSession = SockaSessionBase;
|
|
418
600
|
|
|
419
601
|
export { SockaSession, SockaWebSocketClient };
|
|
420
|
-
//# sourceMappingURL=chunk-
|
|
421
|
-
//# sourceMappingURL=chunk-
|
|
602
|
+
//# sourceMappingURL=chunk-P3JEEOJL.js.map
|
|
603
|
+
//# sourceMappingURL=chunk-P3JEEOJL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/SockaWebSocketClient.ts","../src/client/SockaSession.ts"],"names":["entry"],"mappings":"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","file":"chunk-P3JEEOJL.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\tconst proc = this.client.contract.calls[callName];\n\t\t\tif (proc !== undefined && proc.output === undefined) {\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\tthrow err instanceof Error ? err : new Error(String(err));\n\t\t\t\t}\n\t\t\t\treturn;\n\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 proc = this.client.contract.calls[frame.rpc];\n\t\tif (!proc) {\n\t\t\tconst entry = this.pending.get(frame.id);\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(frame.id);\n\t\t\t\tentry.reject(new SockaError(`Unknown call: ${frame.rpc}`));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (proc.output === undefined) {\n\t\t\tconst entry = this.pending.get(frame.id);\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(frame.id);\n\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\tkind: \"clientUnexpectedServerResponse\",\n\t\t\t\t\trpc: frame.rpc,\n\t\t\t\t\trequestId: frame.id,\n\t\t\t\t});\n\t\t\t\tentry.reject(\n\t\t\t\t\tnew SockaError(\n\t\t\t\t\t\t\"socka: unexpected serverResponse for fire-and-forget call\",\n\t\t\t\t\t\t{ requestId: frame.id, rpc: frame.rpc },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\treportSockaError(this.reportError, {\n\t\t\t\t\tkind: \"clientUnexpectedServerResponse\",\n\t\t\t\t\trpc: frame.rpc,\n\t\t\t\t\trequestId: frame.id,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst entry = this.pending.get(frame.id);\n\t\tif (!entry) return;\n\t\tthis.pending.delete(frame.id);\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) {\n\t\t\tthis.pending.delete(frame.id);\n\t\t\tentry.reject(SockaError.fromWire(frame));\n\t\t\treturn;\n\t\t}\n\n\t\tconst err = SockaError.fromWire(frame);\n\t\tconst rpcName = frame.rpc;\n\t\tif (rpcName === undefined) {\n\t\t\treportSockaError(this.reportError, {\n\t\t\t\tkind: \"clientFireAndForgetRpcError\",\n\t\t\t\terror: err,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst proc = this.client.contract.calls[rpcName];\n\t\tif (proc !== undefined && proc.output === undefined) {\n\t\t\treportSockaError(this.reportError, {\n\t\t\t\tkind: \"clientFireAndForgetRpcError\",\n\t\t\t\terror: err,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\treportSockaError(this.reportError, {\n\t\t\tkind: \"clientOrphanServerError\",\n\t\t\terror: err,\n\t\t});\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"]}
|
|
@@ -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-QGURL3DJ.js.map
|
|
8
|
+
//# sourceMappingURL=chunk-QGURL3DJ.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-QGURL3DJ.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 * strict upgrade is the default: **`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"]}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SockaContract, a as SockaContractConfig,
|
|
2
|
-
export {
|
|
1
|
+
import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat, n as SockaServerResponseFrame, l as SockaServerErrorFrame, m as SockaServerEventFrame, I as InferSockaSend, f as InferSockaPushPayload, e as InferSockaPushHandlers, z as SockaReportError } from '../socka-report-error-CXwpAUgl.js';
|
|
2
|
+
export { y as reportSockaError } from '../socka-report-error-CXwpAUgl.js';
|
|
3
3
|
import '@standard-schema/spec';
|
|
4
4
|
|
|
5
5
|
interface SockaWebSocketClientOptions<TContract extends SockaContract<SockaContractConfig>> {
|
|
@@ -23,7 +23,41 @@ interface SockaWebSocketClientOptions<TContract extends SockaContract<SockaContr
|
|
|
23
23
|
onServerError?: (frame: SockaServerErrorFrame) => void;
|
|
24
24
|
onEvent?: (frame: SockaServerEventFrame) => void;
|
|
25
25
|
onValidationError?: (error: Error, rawMessage: unknown) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Automatic reconnect after an abnormal close. **Disabled by default** when
|
|
28
|
+
* {@link webSocket} is injected (tests); enabled when using {@link url}.
|
|
29
|
+
* Pass `false` to disable.
|
|
30
|
+
*/
|
|
31
|
+
reconnect?: false | SockaReconnectConfig;
|
|
32
|
+
/** Fired before a reconnect attempt is scheduled (after delay is chosen). */
|
|
33
|
+
onReconnecting?: (info: SockaReconnectingInfo) => void;
|
|
34
|
+
/** Fired after a new socket reaches `open` following a reconnect. */
|
|
35
|
+
onReconnected?: (info: SockaReconnectedInfo) => void;
|
|
26
36
|
}
|
|
37
|
+
/** Options for {@link SockaWebSocketClientOptions.reconnect}. */
|
|
38
|
+
type SockaReconnectConfig = {
|
|
39
|
+
/** Default `1000`. */
|
|
40
|
+
initialDelayMs?: number;
|
|
41
|
+
/** Default `30000`. */
|
|
42
|
+
maxDelayMs?: number;
|
|
43
|
+
/** 0–1 fraction of delay to randomize. Default `0.2`. */
|
|
44
|
+
jitter?: number;
|
|
45
|
+
/** Omit for infinite attempts. */
|
|
46
|
+
maxAttempts?: number;
|
|
47
|
+
/**
|
|
48
|
+
* When `true` (default), delay reconnect until `document` is visible again.
|
|
49
|
+
*/
|
|
50
|
+
pauseWhenHidden?: boolean;
|
|
51
|
+
};
|
|
52
|
+
type SockaReconnectingInfo = {
|
|
53
|
+
attempt: number;
|
|
54
|
+
delayMs: number;
|
|
55
|
+
};
|
|
56
|
+
type SockaReconnectedInfo = {
|
|
57
|
+
attempt: number;
|
|
58
|
+
};
|
|
59
|
+
/** High-level connection lifecycle for UI and telemetry. */
|
|
60
|
+
type SockaConnectionStatus = "idle" | "connecting" | "open" | "reconnecting" | "closed";
|
|
27
61
|
/**
|
|
28
62
|
* Browser WebSocket client driven by a socka contract. Sends client request
|
|
29
63
|
* frames and dispatches decoded server frames to callbacks.
|
|
@@ -39,9 +73,27 @@ declare class SockaWebSocketClient<TContract extends SockaContract<SockaContract
|
|
|
39
73
|
private readonly onEventCb?;
|
|
40
74
|
private readonly onValidationError?;
|
|
41
75
|
readonly contract: TContract;
|
|
76
|
+
private manualClose;
|
|
77
|
+
private reconnectAttempt;
|
|
78
|
+
private reconnectTimer;
|
|
79
|
+
private _status;
|
|
80
|
+
private readonly statusListeners;
|
|
42
81
|
constructor(options: SockaWebSocketClientOptions<TContract>);
|
|
82
|
+
private setStatus;
|
|
83
|
+
/** Current connection lifecycle state. */
|
|
84
|
+
get status(): SockaConnectionStatus;
|
|
85
|
+
/**
|
|
86
|
+
* Subscribe to {@link status} changes. The listener is called immediately with the
|
|
87
|
+
* current status, then on every transition. Returns an unsubscribe function.
|
|
88
|
+
*/
|
|
89
|
+
onStatusChange(listener: (status: SockaConnectionStatus) => void): () => void;
|
|
43
90
|
private createSocket;
|
|
44
91
|
private attachSocket;
|
|
92
|
+
private getReconnectEnabled;
|
|
93
|
+
private resolveReconnectConfig;
|
|
94
|
+
private computeReconnectDelayMs;
|
|
95
|
+
private maybeScheduleReconnect;
|
|
96
|
+
private openReplacementSocket;
|
|
45
97
|
private handleMessageEvent;
|
|
46
98
|
/**
|
|
47
99
|
* Creates the WebSocket (when {@link SockaWebSocketClientOptions.autoConnect}
|
|
@@ -106,6 +158,10 @@ declare class SockaSessionBase<TContract extends SockaContract<SockaContractConf
|
|
|
106
158
|
close(code?: number, reason?: string): void;
|
|
107
159
|
/** Opens the WebSocket when using {@link SockaWebSocketClientOptions.autoConnect} `false`. */
|
|
108
160
|
connect(): Promise<void>;
|
|
161
|
+
/** Same as {@link SockaWebSocketClient.status}. */
|
|
162
|
+
get status(): SockaConnectionStatus;
|
|
163
|
+
/** Same as {@link SockaWebSocketClient.onStatusChange}. */
|
|
164
|
+
onStatusChange(listener: (status: SockaConnectionStatus) => void): () => void;
|
|
109
165
|
}
|
|
110
166
|
interface SockaSessionConstructor {
|
|
111
167
|
new <TContract extends SockaContract<SockaContractConfig>>(options: SockaSessionOptions<TContract>): SockaSession<TContract>;
|
|
@@ -116,4 +172,4 @@ type SockaSession<TContract extends SockaContract<SockaContractConfig>> = SockaS
|
|
|
116
172
|
*/
|
|
117
173
|
declare const SockaSession: SockaSessionConstructor;
|
|
118
174
|
|
|
119
|
-
export { SockaReportError, SockaSession, type SockaSessionConstructor, type SockaSessionOptions, type SockaSessionPushWaitOptions, type SockaSessionSubscribeApi, SockaWebSocketClient, type SockaWebSocketClientOptions };
|
|
175
|
+
export { type SockaConnectionStatus, type SockaReconnectConfig, type SockaReconnectedInfo, type SockaReconnectingInfo, SockaReportError, SockaSession, type SockaSessionConstructor, type SockaSessionOptions, type SockaSessionPushWaitOptions, type SockaSessionSubscribeApi, SockaWebSocketClient, type SockaWebSocketClientOptions };
|
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { SockaSession, SockaWebSocketClient } from '../chunk-
|
|
1
|
+
export { SockaSession, SockaWebSocketClient } from '../chunk-P3JEEOJL.js';
|
|
2
2
|
import '../chunk-YMT4HAH7.js';
|
|
3
|
-
export { reportSockaError } from '../chunk-
|
|
3
|
+
export { reportSockaError } from '../chunk-IFIGKR3W.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { D as DecodedSockaWire,
|
|
1
|
+
export { D as DecodedSockaWire, c as InferSockaHandlers, e as InferSockaPushHandlers, f as InferSockaPushPayload, I as InferSockaSend, R as RESERVED_SOCKA_PROCEDURE_NAMES, g as ReservedSockaProcedureName, i as SOCKA_WIRE_VERSION, k as SockaClientRequestFrame, S as SockaContract, a as SockaContractConfig, h as SockaError, b as SockaProcedureDef, z as SockaReportError, l as SockaServerErrorFrame, m as SockaServerEventFrame, n as SockaServerResponseFrame, j as SockaWireError, w as SockaWireFormat, o as SockaWireFrame, V as ValidateSockaCallKeys, p as decodeSockaWire, x as defaultReportError, d as defineSocka, q as encodeClientRequest, s as encodeServerError, t as encodeServerEvent, r as encodeServerResponse, u as encodeSockaWire, v as parseWirePayload, y as reportSockaError } from '../socka-report-error-CXwpAUgl.js';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,23 +7,4 @@ import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
|
7
7
|
*/
|
|
8
8
|
declare function parseStandardSchema<T>(schema: StandardSchemaV1<unknown, T>, value: unknown): Promise<T>;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
* Thrown on the server and surfaced on the client when the wire uses a shared
|
|
12
|
-
* `{ type: "error", id, error }` envelope for correlated RPC failures.
|
|
13
|
-
*/
|
|
14
|
-
declare class SockaError extends Error {
|
|
15
|
-
readonly requestId?: string;
|
|
16
|
-
readonly code?: string;
|
|
17
|
-
constructor(message: string, options?: {
|
|
18
|
-
requestId?: string;
|
|
19
|
-
code?: string;
|
|
20
|
-
cause?: unknown;
|
|
21
|
-
});
|
|
22
|
-
/** Builds a {@link SockaError} from a standard RPC error envelope. */
|
|
23
|
-
static fromWire(msg: {
|
|
24
|
-
id: string;
|
|
25
|
-
error: string;
|
|
26
|
-
}): SockaError;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export { SockaError, parseStandardSchema };
|
|
10
|
+
export { parseStandardSchema };
|
package/dist/core/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { RESERVED_SOCKA_PROCEDURE_NAMES } from '../chunk-YMT4HAH7.js';
|
|
2
|
-
export { SOCKA_WIRE_VERSION, SockaError, SockaWireError, decodeSockaWire, defaultReportError, encodeClientRequest, encodeServerError, encodeServerEvent, encodeServerResponse, encodeSockaWire, parseStandardSchema, parseWirePayload, reportSockaError } from '../chunk-
|
|
2
|
+
export { SOCKA_WIRE_VERSION, SockaError, SockaWireError, decodeSockaWire, defaultReportError, encodeClientRequest, encodeServerError, encodeServerEvent, encodeServerResponse, encodeSockaWire, parseStandardSchema, parseWirePayload, reportSockaError } from '../chunk-IFIGKR3W.js';
|
|
3
3
|
|
|
4
4
|
// src/core/contract.ts
|
|
5
5
|
function defineSocka(config) {
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/contract.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../src/core/contract.ts"],"names":[],"mappings":";;;;AAkIO,SAAS,YACf,MAAA,EACmB;AACnB,EAAA,OAAO;AAAA,IACN,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,MAAA,EAAS,MAAA,CAAO,MAAA,IAAU;AAAC,GAC5B;AACD","file":"index.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ReservedSockaProcedureName } from \"./reserved-procedure-names\";\n\n/**\n * Defines one client-initiated call: optional `input` and optional `output` schemas\n * (Standard Schema v1: Zod v4, Valibot, ArkType, etc.).\n *\n * - **`output` present** (including `z.void()`): request/response RPC; the server sends\n * a validated `serverResponse` on success.\n * - **`output` omitted**: fire-and-forget on success (no `serverResponse`); the client\n * `send` method resolves after the request is sent. Use `output: z.void()` when you\n * still want a correlated ack. See the package README and {@link defineSocka}.\n */\nexport type SockaProcedureDef = {\n\treadonly input?: StandardSchemaV1;\n\treadonly output?: StandardSchemaV1;\n};\n\n/** Configuration object accepted by {@link defineSocka}. */\nexport type SockaContractConfig = {\n\treadonly calls: Record<string, SockaProcedureDef>;\n\treadonly pushes?: Record<string, StandardSchemaV1>;\n};\n\n/**\n * When call keys are a **narrow** object type, rejects keys in\n * {@link ReservedSockaProcedureName} (thenable / `Object.prototype` hazards on\n * `session.send`). Wide `Record<string, SockaProcedureDef>` is unchanged so\n * dynamic maps still typecheck; use runtime validation (see {@link SockaSession}\n * `send`).\n */\nexport type ValidateSockaCallKeys<P extends Record<string, SockaProcedureDef>> =\n\tstring extends keyof P\n\t\t? P\n\t\t: keyof P & ReservedSockaProcedureName extends never\n\t\t\t? P\n\t\t\t: never;\n\n/** Runtime contract returned by {@link defineSocka}, preserving full generic types. */\nexport type SockaContract<T extends SockaContractConfig = SockaContractConfig> =\n\t{\n\t\treadonly calls: T[\"calls\"];\n\t\treadonly pushes: T extends { pushes: Record<string, StandardSchemaV1> }\n\t\t\t? T[\"pushes\"]\n\t\t\t: Record<string, never>;\n\t};\n\n/** Inferred client return type for a call: payload type or `void` when `output` is omitted. */\ntype InferSockaCallReturn<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t? StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t: // biome-ignore lint/suspicious/noConfusingVoidType: This is correct\n\t\t\tvoid;\n\ntype CallFn<P extends SockaProcedureDef> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>) => Promise<InferSockaCallReturn<P>>\n\t: () => Promise<InferSockaCallReturn<P>>;\n\n/**\n * Infers the typed `session.send.*` method map for a contract.\n */\nexport type InferSockaSend<C extends SockaContract> = {\n\t[K in keyof C[\"calls\"]]: CallFn<C[\"calls\"][K]>;\n};\n\ntype HandlerOut<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t?\n\t\t\t\t| StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t\t\t| Promise<StandardSchemaV1.InferOutput<P[\"output\"]>>\n\t\t: void | Promise<void>;\n\ntype HandlerFn<P extends SockaProcedureDef, TSession> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>, session: TSession) => HandlerOut<P>\n\t: (session: TSession) => HandlerOut<P>;\n\n/**\n * Infers the typed server handler map for a contract. Handlers with an input\n * schema take `(input, session)`; calls without input take `(session)` only.\n * When `output` is present, the return value is validated and sent as `serverResponse`.\n * When `output` is omitted (fire-and-forget), the handler should return `void`; the\n * server does not send a success response.\n */\nexport type InferSockaHandlers<C extends SockaContract, TSession> = {\n\t[K in keyof C[\"calls\"]]: HandlerFn<C[\"calls\"][K], TSession>;\n};\n\ntype InferPushPayload<S extends StandardSchemaV1> =\n\tStandardSchemaV1.InferOutput<S>;\n\n/**\n * Payload type for a contract push (output of the push's Standard Schema).\n */\nexport type InferSockaPushPayload<\n\tC extends SockaContract<SockaContractConfig>,\n\tK extends keyof C[\"pushes\"],\n> = C[\"pushes\"][K] extends StandardSchemaV1\n\t? InferPushPayload<C[\"pushes\"][K]>\n\t: never;\n\n/**\n * Infers the typed push subscription handler map for a contract's `pushes`.\n */\nexport type InferSockaPushHandlers<C extends SockaContract> = {\n\t[K in keyof C[\"pushes\"]]: C[\"pushes\"][K] extends StandardSchemaV1\n\t\t? (payload: InferPushPayload<C[\"pushes\"][K]>) => void | Promise<void>\n\t\t: never;\n};\n\n/**\n * Creates a socka contract from call and push definitions. Pass Zod, Valibot,\n * ArkType, or any Standard Schema v1 schemas directly — no adapters needed.\n *\n * ```ts\n * export const myContract = defineSocka({\n * calls: {\n * list: { output: z.array(itemSchema) },\n * insert: { input: z.object({ item: itemSchema }), output: z.void() },\n * notify: { input: z.object({ text: z.string() }) },\n * },\n * });\n * ```\n *\n * Call names must not be {@link ReservedSockaProcedureName} — they would make\n * `session.send` thenable or unsafe as a plain method bag.\n */\nexport function defineSocka<const T extends SockaContractConfig>(\n\tconfig: T & { calls: ValidateSockaCallKeys<T[\"calls\"]> },\n): SockaContract<T> {\n\treturn {\n\t\tcalls: config.calls,\n\t\tpushes: (config.pushes ?? {}) as SockaContract<T>[\"pushes\"],\n\t};\n}\n"]}
|
package/dist/do/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context } from 'hono';
|
|
2
2
|
import { BaseSession, SessionEnv, BaseWebSocketDO } from '@firtoz/websocket-do';
|
|
3
|
-
import { S as SockaContract, a as SockaContractConfig,
|
|
4
|
-
import {
|
|
3
|
+
import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat, c as InferSockaHandlers, z as SockaReportError, f as InferSockaPushPayload } from '../socka-report-error-CXwpAUgl.js';
|
|
4
|
+
import { d as SockaPushSession } from '../SockaWebSocketSession-B1w7RAid.js';
|
|
5
5
|
import '@standard-schema/spec';
|
|
6
6
|
|
|
7
7
|
/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
|
|
@@ -52,6 +52,24 @@ declare class SockaDoSession<TContract extends SockaContract<SockaContractConfig
|
|
|
52
52
|
emitWireEvent(event: string, body: unknown): void;
|
|
53
53
|
emitPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>): Promise<void>;
|
|
54
54
|
broadcastPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>, excludeSelf?: boolean): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* {@link SockaWebSocketSession.listPeers} for this Durable Object room.
|
|
57
|
+
*/
|
|
58
|
+
listPeers(options?: {
|
|
59
|
+
excludeSelf?: boolean;
|
|
60
|
+
}): TData[];
|
|
61
|
+
/**
|
|
62
|
+
* Like {@link listPeers} but maps each peer {@link SockaDoSession}.
|
|
63
|
+
*/
|
|
64
|
+
listPeersWith<R>(map: (session: SockaDoSession<TContract, TData, TEnv>) => R, options?: {
|
|
65
|
+
excludeSelf?: boolean;
|
|
66
|
+
}): R[];
|
|
67
|
+
peerCount(options?: {
|
|
68
|
+
excludeSelf?: boolean;
|
|
69
|
+
}): number;
|
|
70
|
+
hasPeers(options?: {
|
|
71
|
+
excludeSelf?: boolean;
|
|
72
|
+
}): boolean;
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
type SockaWebSocketDOOptions<TEnv extends object, TSession extends SockaDoSession<any, any, TEnv>> = {
|