@grest-ts/websocket 0.0.23 → 0.0.25

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 (67) hide show
  1. package/README.md +150 -40
  2. package/dist/src/adapter/NodeSocketAdapter.d.ts +2 -0
  3. package/dist/src/adapter/NodeSocketAdapter.d.ts.map +1 -1
  4. package/dist/src/adapter/NodeSocketAdapter.js +6 -0
  5. package/dist/src/adapter/NodeSocketAdapter.js.map +1 -1
  6. package/dist/src/client/GGSocketPool.d.ts +21 -0
  7. package/dist/src/client/GGSocketPool.d.ts.map +1 -1
  8. package/dist/src/client/GGSocketPool.js +82 -63
  9. package/dist/src/client/GGSocketPool.js.map +1 -1
  10. package/dist/src/client/GGWebSocketSchema.createClient.d.ts +154 -0
  11. package/dist/src/client/GGWebSocketSchema.createClient.d.ts.map +1 -0
  12. package/dist/src/client/GGWebSocketSchema.createClient.js +345 -0
  13. package/dist/src/client/GGWebSocketSchema.createClient.js.map +1 -0
  14. package/dist/src/index-browser.d.ts +2 -1
  15. package/dist/src/index-browser.d.ts.map +1 -1
  16. package/dist/src/index-browser.js +3 -1
  17. package/dist/src/index-browser.js.map +1 -1
  18. package/dist/src/index-node.d.ts +2 -1
  19. package/dist/src/index-node.d.ts.map +1 -1
  20. package/dist/src/index-node.js +2 -1
  21. package/dist/src/index-node.js.map +1 -1
  22. package/dist/src/schema/GGWebSocketMiddleware.d.ts +12 -0
  23. package/dist/src/schema/GGWebSocketMiddleware.d.ts.map +1 -1
  24. package/dist/src/schema/GGWebSocketMiddleware.js +0 -4
  25. package/dist/src/schema/GGWebSocketMiddleware.js.map +1 -1
  26. package/dist/src/schema/GGWebSocketSchema.d.ts +4 -3
  27. package/dist/src/schema/GGWebSocketSchema.d.ts.map +1 -1
  28. package/dist/src/schema/GGWebSocketSchema.js +3 -1
  29. package/dist/src/schema/GGWebSocketSchema.js.map +1 -1
  30. package/dist/src/schema/webSocketSchema.d.ts +12 -6
  31. package/dist/src/schema/webSocketSchema.d.ts.map +1 -1
  32. package/dist/src/schema/webSocketSchema.js +9 -2
  33. package/dist/src/schema/webSocketSchema.js.map +1 -1
  34. package/dist/src/server/GGSocketServer.d.ts.map +1 -1
  35. package/dist/src/server/GGSocketServer.js +36 -2
  36. package/dist/src/server/GGSocketServer.js.map +1 -1
  37. package/dist/src/server/GGWebSocketSchema.startServer.d.ts +5 -3
  38. package/dist/src/server/GGWebSocketSchema.startServer.d.ts.map +1 -1
  39. package/dist/src/server/GGWebSocketSchema.startServer.js +7 -5
  40. package/dist/src/server/GGWebSocketSchema.startServer.js.map +1 -1
  41. package/dist/src/socket/GGSocket.d.ts +13 -1
  42. package/dist/src/socket/GGSocket.d.ts.map +1 -1
  43. package/dist/src/socket/GGSocket.js +52 -2
  44. package/dist/src/socket/GGSocket.js.map +1 -1
  45. package/dist/src/socket/SocketAdapter.d.ts +11 -0
  46. package/dist/src/socket/SocketAdapter.d.ts.map +1 -1
  47. package/dist/testkit/client/GGWebSocketSchema.callOn.d.ts +1 -1
  48. package/dist/testkit/client/GGWebSocketSchema.callOn.d.ts.map +1 -1
  49. package/dist/tsconfig.publish.tsbuildinfo +1 -1
  50. package/package.json +11 -11
  51. package/src/adapter/NodeSocketAdapter.ts +8 -0
  52. package/src/client/GGSocketPool.ts +90 -73
  53. package/src/client/GGWebSocketSchema.createClient.ts +534 -0
  54. package/src/index-browser.ts +5 -2
  55. package/src/index-node.ts +2 -1
  56. package/src/schema/GGWebSocketMiddleware.ts +14 -0
  57. package/src/schema/GGWebSocketSchema.ts +7 -3
  58. package/src/schema/webSocketSchema.ts +18 -8
  59. package/src/server/GGSocketServer.ts +51 -2
  60. package/src/server/GGWebSocketSchema.startServer.ts +14 -10
  61. package/src/socket/GGSocket.ts +56 -2
  62. package/src/socket/SocketAdapter.ts +13 -0
  63. package/dist/src/client/GGSocketClient.d.ts +0 -10
  64. package/dist/src/client/GGSocketClient.d.ts.map +0 -1
  65. package/dist/src/client/GGSocketClient.js +0 -17
  66. package/dist/src/client/GGSocketClient.js.map +0 -1
  67. package/src/client/GGSocketClient.ts +0 -25
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Client extension for GGWebSocketSchema - adds createClient method.
3
+ *
4
+ * Mirrors the server's onConnection handler exactly:
5
+ *
6
+ * server: ChatApi.register((incoming, outgoing) => {
7
+ * incoming.on({ ... })
8
+ * })
9
+ *
10
+ * client: const client = ChatApi.createClient({ url })
11
+ * await client.connect(({incoming, outgoing}) => {
12
+ * incoming.on({ ... })
13
+ * })
14
+ * await client.outgoing.xxx(...)
15
+ *
16
+ * The setup callback is the single place that wires handlers. It is re-run on
17
+ * every successful (re)connection, so auto-reconnect produces a fully-rewired
18
+ * client without any persistent-handler state to go stale.
19
+ *
20
+ * Works in both browser and Node.js contexts.
21
+ */
22
+ import { GGWebSocketMiddleware } from "../schema/GGWebSocketMiddleware";
23
+ export interface GGHeartbeatConfig {
24
+ /** How often to send a PING. Default 30 000 ms. */
25
+ intervalMs?: number;
26
+ /** Grace period for a PONG after each PING. Default 10 000 ms. */
27
+ timeoutMs?: number;
28
+ }
29
+ export interface GGReconnectConfig {
30
+ /** First retry delay. Default 500 ms. */
31
+ initialDelayMs?: number;
32
+ /** Delay cap for exponential backoff. Default 30 000 ms. */
33
+ maxDelayMs?: number;
34
+ /** Backoff multiplier. Default 2. */
35
+ multiplier?: number;
36
+ /** Give up after this many consecutive failures. Default Infinity. */
37
+ maxAttempts?: number;
38
+ /**
39
+ * Predicate deciding whether an error during a reconnect attempt should
40
+ * trigger another retry. Default: retry on any error EXCEPT NOT_AUTHORIZED
41
+ * and FORBIDDEN — those are treated as permanent and fire a final onClose
42
+ * with reason "unrecoverable". Return true to retry, false to give up.
43
+ */
44
+ shouldRetry?: (error: Error) => boolean;
45
+ /**
46
+ * Dead-connection detection via protocol PING/PONG. Off by default.
47
+ * Only supported on Node (browsers cannot initiate pings). Browser clients
48
+ * silently ignore this config.
49
+ */
50
+ heartbeat?: GGHeartbeatConfig;
51
+ }
52
+ export interface GGWebSocketClientConfig<TQuery = undefined> {
53
+ /**
54
+ * WebSocket server URL, e.g. "ws://localhost:3000".
55
+ * If omitted, uses service discovery (requires @grest-ts/discovery).
56
+ * In browsers, pass an explicit URL (or "" for same-origin).
57
+ */
58
+ url?: string;
59
+ /**
60
+ * Query parameters to include on connect. Typed from `queryOnConnect<T>()` if used.
61
+ * If the schema declares a query validator, it's applied here before connecting.
62
+ */
63
+ query?: TQuery;
64
+ /**
65
+ * Extra middlewares merged on top of the schema's middlewares, in order.
66
+ * Use this to attach per-client concerns (e.g. a static auth token) without
67
+ * requiring callers to set up a GGContext around connect(). Manual
68
+ * header manipulation is intentionally not exposed — middleware is the API.
69
+ */
70
+ middlewares?: GGWebSocketMiddleware[];
71
+ /**
72
+ * Default timeout in ms for request/response outgoing calls. Defaults to 30 000.
73
+ * Fire-and-forget methods ignore this.
74
+ */
75
+ timeout?: number;
76
+ /**
77
+ * Auto-reconnect on unexpected drops. Default off.
78
+ * `true` enables with defaults; pass an object to tune.
79
+ * Manual `disconnect()` / `close()` always wins over reconnect.
80
+ */
81
+ reconnect?: boolean | GGReconnectConfig;
82
+ }
83
+ export interface GGWebSocketSetupTools<TServerToClientImpl, TClientToServer> {
84
+ incoming: {
85
+ /**
86
+ * Register handlers for serverToClient messages.
87
+ * Partial — register only methods you care about.
88
+ */
89
+ on(handlers: Partial<TServerToClientImpl>): void;
90
+ };
91
+ outgoing: TClientToServer;
92
+ }
93
+ /**
94
+ * Setup callback — receives handler registration tools + outgoing methods.
95
+ * Re-invoked on every successful (re)connection; keep it pure wrt setup actions.
96
+ *
97
+ * For correctness: register incoming handlers synchronously at the top of the
98
+ * callback (before any `await`), so no pushed message can slip through before
99
+ * handlers exist.
100
+ */
101
+ export type GGWebSocketSetup<TServerToClientImpl, TClientToServer> = (tools: GGWebSocketSetupTools<TServerToClientImpl, TClientToServer>) => void | Promise<void>;
102
+ /**
103
+ * Reason the client was finally closed (no further reconnects will happen).
104
+ *
105
+ * - "manual" — user called disconnect()/close()
106
+ * - "drop" — socket dropped and reconnect is disabled
107
+ * - "retries-exhausted" — reconnect enabled, hit maxAttempts
108
+ * - "unrecoverable" — reconnect skipped due to shouldRetry returning false
109
+ * (default: NOT_AUTHORIZED / FORBIDDEN)
110
+ */
111
+ export type GGWebSocketCloseReason = "manual" | "drop" | "retries-exhausted" | "unrecoverable";
112
+ export interface GGWebSocketClient<TClientToServer, TServerToClientImpl> {
113
+ /**
114
+ * Methods to call on the server (clientToServer).
115
+ * Throws `SERVER_ERROR` synchronously if called before connect().
116
+ */
117
+ readonly outgoing: TClientToServer;
118
+ /** True when a socket is connected and the handshake has completed. */
119
+ readonly isConnected: boolean;
120
+ /**
121
+ * Establish the connection and run the setup callback.
122
+ * If `reconnect` is enabled, the callback is re-invoked on every successful
123
+ * reconnection, so handlers + initial outgoing calls rebuild the full state.
124
+ */
125
+ connect(setup?: GGWebSocketSetup<TServerToClientImpl, TClientToServer>): Promise<void>;
126
+ /**
127
+ * Gracefully close. Disables further auto-reconnect and drains pending calls.
128
+ */
129
+ disconnect(): Promise<void>;
130
+ /**
131
+ * Immediately close. Disables further auto-reconnect.
132
+ */
133
+ close(): void;
134
+ /**
135
+ * Fires once, when the client has stopped reconnecting.
136
+ * The `reason` identifies why; for terminal/error cases `error` carries the cause.
137
+ */
138
+ onClose(cb: (reason: GGWebSocketCloseReason, error?: Error) => void): this;
139
+ /**
140
+ * Fires on every socket drop (before any reconnect attempt).
141
+ * `"manual"` = user called disconnect/close; `"drop"` = unexpected.
142
+ */
143
+ onDisconnect(cb: (reason: "manual" | "drop") => void): this;
144
+ /**
145
+ * Fires on socket errors. Multiple events possible per connection lifetime.
146
+ */
147
+ onError(cb: (error: Error) => void): this;
148
+ }
149
+ declare module "../schema/GGWebSocketSchema" {
150
+ interface GGWebSocketSchema<TClientToServer, TServerToClient, TContext = {}, TQuery = undefined, TClientToServerImpl = TClientToServer, TServerToClientImpl = TServerToClient> {
151
+ createClient(config?: GGWebSocketClientConfig<TQuery>): GGWebSocketClient<TClientToServer, TServerToClientImpl>;
152
+ }
153
+ }
154
+ //# sourceMappingURL=GGWebSocketSchema.createClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGWebSocketSchema.createClient.d.ts","sourceRoot":"","sources":["../../../src/client/GGWebSocketSchema.createClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Client extension for GGWebSocketSchema - adds createClient method.
3
+ *
4
+ * Mirrors the server's onConnection handler exactly:
5
+ *
6
+ * server: ChatApi.register((incoming, outgoing) => {
7
+ * incoming.on({ ... })
8
+ * })
9
+ *
10
+ * client: const client = ChatApi.createClient({ url })
11
+ * await client.connect(({incoming, outgoing}) => {
12
+ * incoming.on({ ... })
13
+ * })
14
+ * await client.outgoing.xxx(...)
15
+ *
16
+ * The setup callback is the single place that wires handlers. It is re-run on
17
+ * every successful (re)connection, so auto-reconnect produces a fully-rewired
18
+ * client without any persistent-handler state to go stale.
19
+ *
20
+ * Works in both browser and Node.js contexts.
21
+ */
22
+ import { FORBIDDEN, GGContractExecutor, GGPromise, NOT_AUTHORIZED, SERVER_ERROR, VALIDATION_ERROR, } from "@grest-ts/schema";
23
+ import { GGWebSocketSchema } from "../schema/GGWebSocketSchema.js";
24
+ import { GGSocketPool } from "./GGSocketPool.js";
25
+ /** Default: don't retry if the server said "auth" — re-trying won't help. */
26
+ const defaultShouldRetry = (err) => {
27
+ if (err instanceof NOT_AUTHORIZED)
28
+ return false;
29
+ if (err instanceof FORBIDDEN)
30
+ return false;
31
+ return true;
32
+ };
33
+ function normalizeReconnect(r) {
34
+ if (!r)
35
+ return null;
36
+ const cfg = r === true ? {} : r;
37
+ return {
38
+ initialDelayMs: cfg.initialDelayMs ?? 500,
39
+ maxDelayMs: cfg.maxDelayMs ?? 30_000,
40
+ multiplier: cfg.multiplier ?? 2,
41
+ maxAttempts: cfg.maxAttempts ?? Infinity,
42
+ shouldRetry: cfg.shouldRetry ?? defaultShouldRetry,
43
+ heartbeat: cfg.heartbeat
44
+ ? {
45
+ intervalMs: cfg.heartbeat.intervalMs ?? 30_000,
46
+ timeoutMs: cfg.heartbeat.timeoutMs ?? 10_000,
47
+ }
48
+ : undefined,
49
+ };
50
+ }
51
+ GGWebSocketSchema.prototype.createClient = function (config) {
52
+ const contract = this.contract;
53
+ if (!contract) {
54
+ throw new Error(`WebSocketSchema "${this.name}" has no contract.`);
55
+ }
56
+ const schemaName = this.name;
57
+ const normalizedPath = this.path.startsWith("/") ? this.path : "/" + this.path;
58
+ const schemaMiddlewares = this.middlewares || [];
59
+ const queryValidator = this.queryValidator;
60
+ const clientToServerContract = contract.clientToServer;
61
+ const serverToClientContract = contract.serverToClient;
62
+ const timeout = config?.timeout ?? 30_000;
63
+ const reconnectConfig = normalizeReconnect(config?.reconnect);
64
+ let socket;
65
+ let savedSetup;
66
+ let reconnectAttempt = 0;
67
+ let reconnectTimer;
68
+ let finallyClosed = false;
69
+ let finalCloseFired = false;
70
+ const onCloseCallbacks = [];
71
+ const onDisconnectCallbacks = [];
72
+ const onErrorCallbacks = [];
73
+ // --------------------------------------------------------------------
74
+ // URL resolution + query validation
75
+ // --------------------------------------------------------------------
76
+ const resolveDomain = async () => {
77
+ if (config?.url !== undefined) {
78
+ return config.url;
79
+ }
80
+ try {
81
+ const { GG_DISCOVERY } = await import(/* @vite-ignore */ "@grest-ts/discovery");
82
+ return await GG_DISCOVERY.get().discoverApi(schemaName);
83
+ }
84
+ catch (err) {
85
+ throw new SERVER_ERROR({
86
+ displayMessage: "Service discovery failed for WebSocket API " + schemaName,
87
+ originalError: err,
88
+ });
89
+ }
90
+ };
91
+ const validateQuery = () => {
92
+ if (!queryValidator)
93
+ return config?.query;
94
+ if (config?.query === undefined)
95
+ return undefined;
96
+ const parsed = queryValidator.safeParse(config.query, true);
97
+ if (parsed.success === false) {
98
+ throw new VALIDATION_ERROR(parsed.issues.toJSON(), { displayMessage: "Invalid query parameters" });
99
+ }
100
+ return parsed.value;
101
+ };
102
+ // --------------------------------------------------------------------
103
+ // Outgoing — stable object; methods throw if called before/after connect
104
+ // --------------------------------------------------------------------
105
+ const outgoingImpl = {};
106
+ for (const methodName of Object.keys(clientToServerContract.methods)) {
107
+ const contractFn = clientToServerContract.methods[methodName];
108
+ const hasResponse = contractFn.success !== undefined;
109
+ outgoingImpl[methodName] = async (data) => {
110
+ if (!socket) {
111
+ throw new SERVER_ERROR({
112
+ displayMessage: "WebSocket client is not connected. Call connect() first.",
113
+ });
114
+ }
115
+ return socket.send(`${schemaName}.${methodName}`, data, hasResponse, timeout);
116
+ };
117
+ }
118
+ const outgoing = clientToServerContract.implement(outgoingImpl, { skipLocatorRegistration: true });
119
+ // --------------------------------------------------------------------
120
+ // Setup tools factory — fresh per connect attempt
121
+ // --------------------------------------------------------------------
122
+ const buildSetupTools = (s) => ({
123
+ incoming: {
124
+ on(handlers) {
125
+ for (const methodName of Object.keys(handlers)) {
126
+ const userHandler = handlers[methodName];
127
+ if (!userHandler)
128
+ continue;
129
+ const contractFn = serverToClientContract.methods[methodName];
130
+ if (!contractFn) {
131
+ throw new Error(`Method "${methodName}" is not defined in serverToClient of "${schemaName}"`);
132
+ }
133
+ const wrapped = (data) => new GGPromise(GGContractExecutor.call(contractFn, data, undefined, async (validated) => userHandler(validated)));
134
+ s.registerHandler({ path: `${schemaName}.${methodName}`, handler: wrapped });
135
+ }
136
+ },
137
+ },
138
+ outgoing,
139
+ });
140
+ // --------------------------------------------------------------------
141
+ // Connection flow
142
+ // --------------------------------------------------------------------
143
+ /**
144
+ * Open one socket. Runs setup, wires close/error handlers, starts heartbeat.
145
+ * Throws on any failure — caller (initial connect or reconnect loop) decides
146
+ * retry policy. Never leaves a socket dangling on error.
147
+ */
148
+ const openOnce = async () => {
149
+ const domain = await resolveDomain();
150
+ const validatedQuery = validateQuery();
151
+ const merged = [...schemaMiddlewares, ...(config?.middlewares ?? [])];
152
+ const newSocket = await GGSocketPool.connect({
153
+ domain,
154
+ path: normalizedPath,
155
+ query: validatedQuery,
156
+ middlewares: merged,
157
+ });
158
+ // #1: If user called disconnect() while we were awaiting the handshake,
159
+ // close the freshly-opened socket immediately — don't leak it.
160
+ if (finallyClosed) {
161
+ newSocket.close();
162
+ return;
163
+ }
164
+ socket = newSocket;
165
+ // setupPhase guards the onClose listener: while setup is in flight, a drop
166
+ // (whether from setup's own catch closing the socket, or an external close)
167
+ // only fires onDisconnect. Reconnect scheduling / finalClose are the job of
168
+ // the catch chain in this openOnce call or its caller (scheduleReconnect).
169
+ // After setup completes, setupPhase is cleared and onClose becomes the
170
+ // authoritative reconnect dispatcher.
171
+ let setupPhase = true;
172
+ newSocket.onClose(() => {
173
+ if (socket === newSocket) {
174
+ socket = undefined;
175
+ }
176
+ fireOnDisconnect(finallyClosed ? "manual" : "drop");
177
+ if (finallyClosed) {
178
+ fireFinalClose("manual");
179
+ return;
180
+ }
181
+ if (setupPhase) {
182
+ // Catch chain owns retry decisions for setup-phase failures.
183
+ return;
184
+ }
185
+ if (!reconnectConfig) {
186
+ fireFinalClose("drop");
187
+ return;
188
+ }
189
+ if (reconnectAttempt >= reconnectConfig.maxAttempts) {
190
+ fireFinalClose("retries-exhausted");
191
+ return;
192
+ }
193
+ scheduleReconnect();
194
+ });
195
+ for (const cb of onErrorCallbacks)
196
+ newSocket.onError(cb);
197
+ // Heartbeat (if configured and adapter supports it — browsers no-op).
198
+ if (reconnectConfig?.heartbeat) {
199
+ newSocket.startHeartbeat(reconnectConfig.heartbeat);
200
+ }
201
+ // #2: Run setup with explicit cleanup on throw so we never leak a socket.
202
+ if (savedSetup) {
203
+ try {
204
+ await savedSetup(buildSetupTools(newSocket));
205
+ }
206
+ catch (setupErr) {
207
+ if (socket === newSocket) {
208
+ socket = undefined;
209
+ }
210
+ try {
211
+ newSocket.close();
212
+ }
213
+ catch (_) { }
214
+ throw setupErr;
215
+ }
216
+ }
217
+ setupPhase = false;
218
+ reconnectAttempt = 0;
219
+ };
220
+ /**
221
+ * Schedule a retry per the reconnect config. Called from the onClose handler
222
+ * (socket dropped) — never from initial connect().
223
+ */
224
+ const scheduleReconnect = () => {
225
+ if (!reconnectConfig)
226
+ return;
227
+ const attempt = reconnectAttempt++;
228
+ const delay = Math.min(reconnectConfig.initialDelayMs * Math.pow(reconnectConfig.multiplier, attempt), reconnectConfig.maxDelayMs);
229
+ reconnectTimer = setTimeout(async () => {
230
+ reconnectTimer = undefined;
231
+ if (finallyClosed)
232
+ return;
233
+ try {
234
+ await openOnce();
235
+ }
236
+ catch (err) {
237
+ const error = err;
238
+ for (const cb of onErrorCallbacks) {
239
+ try {
240
+ cb(error);
241
+ }
242
+ catch (_) { }
243
+ }
244
+ // #4: terminal errors skip further retries.
245
+ if (!reconnectConfig.shouldRetry(error)) {
246
+ fireFinalClose("unrecoverable", error);
247
+ return;
248
+ }
249
+ if (reconnectAttempt < reconnectConfig.maxAttempts) {
250
+ scheduleReconnect();
251
+ }
252
+ else {
253
+ fireFinalClose("retries-exhausted", error);
254
+ }
255
+ }
256
+ }, delay);
257
+ };
258
+ const fireOnDisconnect = (reason) => {
259
+ for (const cb of onDisconnectCallbacks) {
260
+ try {
261
+ cb(reason);
262
+ }
263
+ catch (_) { }
264
+ }
265
+ };
266
+ const fireFinalClose = (reason, error) => {
267
+ if (finalCloseFired)
268
+ return;
269
+ finalCloseFired = true;
270
+ for (const cb of onCloseCallbacks) {
271
+ try {
272
+ cb(reason, error);
273
+ }
274
+ catch (_) { }
275
+ }
276
+ };
277
+ // --------------------------------------------------------------------
278
+ // Public client surface
279
+ // --------------------------------------------------------------------
280
+ const client = {
281
+ outgoing,
282
+ get isConnected() {
283
+ return socket !== undefined;
284
+ },
285
+ async connect(setup) {
286
+ if (finallyClosed) {
287
+ throw new SERVER_ERROR({
288
+ displayMessage: "WebSocket client has been closed and cannot be reconnected. Create a new client.",
289
+ });
290
+ }
291
+ if (socket)
292
+ return;
293
+ savedSetup = setup;
294
+ await openOnce();
295
+ },
296
+ async disconnect() {
297
+ finallyClosed = true;
298
+ if (reconnectTimer) {
299
+ clearTimeout(reconnectTimer);
300
+ reconnectTimer = undefined;
301
+ }
302
+ const s = socket;
303
+ socket = undefined;
304
+ if (s) {
305
+ await s.teardown();
306
+ }
307
+ else {
308
+ fireOnDisconnect("manual");
309
+ fireFinalClose("manual");
310
+ }
311
+ },
312
+ close() {
313
+ finallyClosed = true;
314
+ if (reconnectTimer) {
315
+ clearTimeout(reconnectTimer);
316
+ reconnectTimer = undefined;
317
+ }
318
+ const s = socket;
319
+ socket = undefined;
320
+ if (s) {
321
+ s.close();
322
+ }
323
+ else {
324
+ fireOnDisconnect("manual");
325
+ fireFinalClose("manual");
326
+ }
327
+ },
328
+ onClose(cb) {
329
+ onCloseCallbacks.push(cb);
330
+ return this;
331
+ },
332
+ onDisconnect(cb) {
333
+ onDisconnectCallbacks.push(cb);
334
+ return this;
335
+ },
336
+ onError(cb) {
337
+ onErrorCallbacks.push(cb);
338
+ if (socket)
339
+ socket.onError(cb);
340
+ return this;
341
+ },
342
+ };
343
+ return client;
344
+ };
345
+ //# sourceMappingURL=GGWebSocketSchema.createClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GGWebSocketSchema.createClient.js","sourceRoot":"","sources":["../../../src/client/GGWebSocketSchema.createClient.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ export * from "./socket/GGSocket";
5
5
  export * from "./schema/webSocketSchema";
6
6
  export * from "./schema/GGWebSocketSchema";
7
7
  export * from "./schema/GGWebSocketMiddleware";
8
- export * from "./client/GGSocketClient";
9
8
  export * from "./client/GGSocketPool";
9
+ export * from "./client/GGWebSocketSchema.createClient";
10
+ import "./client/GGWebSocketSchema.createClient";
10
11
  //# sourceMappingURL=index-browser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-browser.d.ts","sourceRoot":"","sources":["../../src/index-browser.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAC;AAGxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index-browser.d.ts","sourceRoot":"","sources":["../../src/index-browser.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAC;AAGxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yCAAyC,CAAC;AAGxD,OAAO,yCAAyC,CAAC"}
@@ -10,6 +10,8 @@ export * from "./schema/webSocketSchema.js";
10
10
  export * from "./schema/GGWebSocketSchema.js";
11
11
  export * from "./schema/GGWebSocketMiddleware.js";
12
12
  // Client
13
- export * from "./client/GGSocketClient.js";
14
13
  export * from "./client/GGSocketPool.js";
14
+ export * from "./client/GGWebSocketSchema.createClient.js";
15
+ // Extensions
16
+ import "./client/GGWebSocketSchema.createClient.js";
15
17
  //# sourceMappingURL=index-browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-browser.js","sourceRoot":"","sources":["../../src/index-browser.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,yBAAyB,CAAC;AAExC,UAAU;AACV,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,OAAO;AACP,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAE/C,SAAS;AACT,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index-browser.js","sourceRoot":"","sources":["../../src/index-browser.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,yBAAyB,CAAC;AAExC,UAAU;AACV,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,OAAO;AACP,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAE/C,SAAS;AACT,cAAc,uBAAuB,CAAC;AACtC,cAAc,yCAAyC,CAAC;AAExD,aAAa;AACb,OAAO,yCAAyC,CAAC"}
@@ -8,7 +8,8 @@ export * from "./schema/GGWebSocketSchema";
8
8
  export * from "./schema/GGWebSocketMiddleware";
9
9
  export * from "./server/GGSocketServer";
10
10
  export * from "./server/GGWebSocketSchema.startServer";
11
- export * from "./client/GGSocketClient";
12
11
  export * from "./client/GGSocketPool";
12
+ export * from "./client/GGWebSocketSchema.createClient";
13
13
  import "./server/GGWebSocketSchema.startServer";
14
+ import "./client/GGWebSocketSchema.createClient";
14
15
  //# sourceMappingURL=index-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAC;AAGxC,cAAc,6BAA6B,CAAC;AAG5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AAGvD,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAC;AAGxC,cAAc,6BAA6B,CAAC;AAG5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AAGvD,cAAc,uBAAuB,CAAC;AACtC,cAAc,yCAAyC,CAAC;AAGxD,OAAO,wCAAwC,CAAC;AAChD,OAAO,yCAAyC,CAAC"}
@@ -15,8 +15,9 @@ export * from "./schema/GGWebSocketMiddleware.js";
15
15
  export * from "./server/GGSocketServer.js";
16
16
  export * from "./server/GGWebSocketSchema.startServer.js";
17
17
  // Client
18
- export * from "./client/GGSocketClient.js";
19
18
  export * from "./client/GGSocketPool.js";
19
+ export * from "./client/GGWebSocketSchema.createClient.js";
20
20
  // Extensions
21
21
  import "./server/GGWebSocketSchema.startServer.js";
22
+ import "./client/GGWebSocketSchema.createClient.js";
22
23
  //# sourceMappingURL=index-node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-node.js","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,yBAAyB,CAAC;AAExC,UAAU;AACV,cAAc,6BAA6B,CAAC;AAE5C,UAAU;AACV,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,OAAO;AACP,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAE/C,SAAS;AACT,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AAEvD,SAAS;AACT,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AAEtC,aAAa;AACb,OAAO,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index-node.js","sourceRoot":"","sources":["../../src/index-node.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,yBAAyB,CAAC;AAExC,UAAU;AACV,cAAc,6BAA6B,CAAC;AAE5C,UAAU;AACV,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,OAAO;AACP,cAAc,mBAAmB,CAAC;AAElC,aAAa;AACb,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAE/C,SAAS;AACT,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AAEvD,SAAS;AACT,cAAc,uBAAuB,CAAC;AACtC,cAAc,yCAAyC,CAAC;AAExD,aAAa;AACb,OAAO,wCAAwC,CAAC;AAChD,OAAO,yCAAyC,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { GGSchema } from "@grest-ts/schema";
1
2
  /**
2
3
  * WebSocket-specific middleware interface.
3
4
  * Independent from @grest-ts/http - WebSocket has its own middleware system.
@@ -20,6 +21,17 @@ export interface GGWebSocketHandshakeContext {
20
21
  * Unlike HTTP middleware, WebSocket middleware works with handshake data.
21
22
  */
22
23
  export interface GGWebSocketMiddleware {
24
+ /**
25
+ * Handshake headers this middleware reads or writes, mapped to their value schemas.
26
+ * Keys are header names; values describe the header value format for docs and OpenAPI/AsyncAPI.
27
+ * Use {} if the middleware touches no custom handshake headers.
28
+ *
29
+ * @example
30
+ * headers: {
31
+ * "authorization": IsBearerToken.docs({description: "JWT access token"})
32
+ * }
33
+ */
34
+ readonly headers?: Record<string, GGSchema<string | undefined>>;
23
35
  /**
24
36
  * Client-side: Add headers to the handshake message before sending.
25
37
  * Called by GGSocketPool when establishing a connection.
@@ -1 +1 @@
1
- {"version":3,"file":"GGWebSocketMiddleware.d.ts","sourceRoot":"","sources":["../../../src/schema/GGWebSocketMiddleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,eAAe,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAE7D;;;OAGG;IACH,cAAc,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAE5D;;;;OAIG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
1
+ {"version":3,"file":"GGWebSocketMiddleware.d.ts","sourceRoot":"","sources":["../../../src/schema/GGWebSocketMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IACxC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAEhE;;;OAGG;IACH,eAAe,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAE7D;;;OAGG;IACH,cAAc,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAE5D;;;;OAIG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
@@ -1,6 +1,2 @@
1
- /**
2
- * WebSocket-specific middleware interface.
3
- * Independent from @grest-ts/http - WebSocket has its own middleware system.
4
- */
5
1
  export {};
6
2
  //# sourceMappingURL=GGWebSocketMiddleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GGWebSocketMiddleware.js","sourceRoot":"","sources":["../../../src/schema/GGWebSocketMiddleware.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
1
+ {"version":3,"file":"GGWebSocketMiddleware.js","sourceRoot":"","sources":["../../../src/schema/GGWebSocketMiddleware.ts"],"names":[],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  import { GGWebSocketMiddleware } from "./GGWebSocketMiddleware";
2
- import { GGContractApiDefinition, GGContractClass } from "@grest-ts/schema";
2
+ import { GGContractApiDefinition, GGContractClass, GGValidator } from "@grest-ts/schema";
3
3
  /**
4
4
  * WebSocket API Schema - pure data definition with typed context
5
5
  *
@@ -10,13 +10,14 @@ import { GGContractApiDefinition, GGContractClass } from "@grest-ts/schema";
10
10
  * - TQuery: Query parameters on connect
11
11
  * - TClientToServerImpl: Server implementation type for clientToServer handlers (returns Promise)
12
12
  */
13
- export declare class GGWebSocketSchema<TClientToServer, TServerToClient, TContext = {}, TQuery = undefined, TClientToServerImpl = TClientToServer> {
13
+ export declare class GGWebSocketSchema<TClientToServer, TServerToClient, TContext = {}, TQuery = undefined, TClientToServerImpl = TClientToServer, TServerToClientImpl = TServerToClient> {
14
14
  readonly name: string;
15
15
  readonly path: string;
16
16
  readonly middlewares: readonly GGWebSocketMiddleware[];
17
+ readonly queryValidator?: GGValidator<TQuery>;
17
18
  private readonly contractFactory;
18
19
  private contractCache;
19
- constructor(name: string, path: string, contractFactory: () => GGWebSocketContractRuntime, middlewares?: readonly GGWebSocketMiddleware[]);
20
+ constructor(name: string, path: string, contractFactory: () => GGWebSocketContractRuntime, middlewares?: readonly GGWebSocketMiddleware[], queryValidator?: GGValidator<TQuery>);
20
21
  get contract(): GGWebSocketContractRuntime;
21
22
  }
22
23
  /**