@minion-stack/shared 0.1.0 → 0.2.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/dist/gateway/client.d.ts +73 -0
- package/dist/gateway/client.d.ts.map +1 -0
- package/dist/gateway/client.js +244 -0
- package/dist/gateway/client.js.map +1 -0
- package/dist/gateway/client.test.d.ts +2 -0
- package/dist/gateway/client.test.d.ts.map +1 -0
- package/dist/gateway/client.test.js +235 -0
- package/dist/gateway/client.test.js.map +1 -0
- package/dist/gateway/index.d.ts +1 -0
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +1 -0
- package/dist/gateway/index.js.map +1 -1
- package/dist/node/index.d.ts +20 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +28 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/index.test.d.ts +2 -0
- package/dist/node/index.test.d.ts.map +1 -0
- package/dist/node/index.test.js +92 -0
- package/dist/node/index.test.js.map +1 -0
- package/package.json +20 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { EventFrame } from './types.js';
|
|
2
|
+
export declare const PROTOCOL_VERSION = 3;
|
|
3
|
+
export interface GatewayClientOptions {
|
|
4
|
+
/** WebSocket URL to connect to. */
|
|
5
|
+
url: string;
|
|
6
|
+
/**
|
|
7
|
+
* Optional WebSocket constructor injection (defaults to globalThis.WebSocket).
|
|
8
|
+
* Pass ws's WebSocket class when running in Node.
|
|
9
|
+
*/
|
|
10
|
+
WebSocketImpl?: any;
|
|
11
|
+
/** Second arg to WebSocket constructor (Node ws accepts headers/maxPayload record). */
|
|
12
|
+
wsConstructorArgs?: [] | [Record<string, unknown>];
|
|
13
|
+
/**
|
|
14
|
+
* Called when server sends connect.challenge.
|
|
15
|
+
* Must return the params object to send with the 'connect' request.
|
|
16
|
+
*/
|
|
17
|
+
onChallenge: (nonce: string) => Promise<Record<string, unknown>>;
|
|
18
|
+
/** Called for every inbound event frame except connect.challenge. */
|
|
19
|
+
onEvent?: (frame: EventFrame) => void | Promise<void>;
|
|
20
|
+
/** Called when the socket opens (before challenge handshake completes). */
|
|
21
|
+
onOpen?: () => void;
|
|
22
|
+
/** Called when the socket closes. */
|
|
23
|
+
onClose?: (code: number, reason: string) => void;
|
|
24
|
+
/** Called just before a reconnect delay starts (useful for UI toasts). */
|
|
25
|
+
onReconnectScheduled?: (delayMs: number) => void;
|
|
26
|
+
/**
|
|
27
|
+
* true = exponential backoff auto-reconnect (browser UX).
|
|
28
|
+
* false = single-shot; close() is final (Node adapter default).
|
|
29
|
+
* Default: false.
|
|
30
|
+
*/
|
|
31
|
+
autoReconnect?: boolean;
|
|
32
|
+
/** Timeout for the connect() promise (ms). Default: 10000. */
|
|
33
|
+
connectTimeoutMs?: number;
|
|
34
|
+
/** Default timeout for request<T>() (ms). Default: 15000. */
|
|
35
|
+
requestTimeoutMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class GatewayClient {
|
|
38
|
+
private readonly opts;
|
|
39
|
+
private ws;
|
|
40
|
+
/** Increments per connect() call to fence stale socket event handlers. */
|
|
41
|
+
private generation;
|
|
42
|
+
private pending;
|
|
43
|
+
private connectNonce;
|
|
44
|
+
private connectSent;
|
|
45
|
+
private reconnectTimer;
|
|
46
|
+
private backoffMs;
|
|
47
|
+
private closed;
|
|
48
|
+
private helloResolve;
|
|
49
|
+
private helloReject;
|
|
50
|
+
constructor(opts: GatewayClientOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Open the WebSocket and complete the connect.challenge handshake.
|
|
53
|
+
* Resolves with the HelloOk payload from the server.
|
|
54
|
+
*/
|
|
55
|
+
connect(): Promise<unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* Send a gateway request and resolve with the response payload.
|
|
58
|
+
* Rejects if not connected or if the request times out.
|
|
59
|
+
*/
|
|
60
|
+
request<T>(method: string, params?: unknown, opts?: {
|
|
61
|
+
timeoutMs?: number;
|
|
62
|
+
}): Promise<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Gracefully close the connection and cancel any pending reconnect timers.
|
|
65
|
+
* All pending requests are rejected.
|
|
66
|
+
*/
|
|
67
|
+
close(code?: number, reason?: string): void;
|
|
68
|
+
private wireEvents;
|
|
69
|
+
private handleMessage;
|
|
70
|
+
private sendConnect;
|
|
71
|
+
private scheduleReconnect;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/gateway/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IAEH,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD;;;OAGG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACjE,qEAAqE;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,aAAa;IAcZ,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC,OAAO,CAAC,EAAE,CAAa;IACvB,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,WAAW,CAAuC;gBAE7B,IAAI,EAAE,oBAAoB;IAEvD;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAgDjC;;;OAGG;IACG,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IAmB7F;;;OAGG;IACH,KAAK,CAAC,IAAI,SAAO,EAAE,MAAM,SAAiB,GAAG,IAAI;IAkBjD,OAAO,CAAC,UAAU;IAwElB,OAAO,CAAC,aAAa;YAyBP,WAAW;IAkBzB,OAAO,CAAC,iBAAiB;CAW1B"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// packages/shared/src/gateway/client.ts
|
|
2
|
+
// Runtime-agnostic GatewayClient — browser uses globalThis.WebSocket, Node uses ws via ./node subpath.
|
|
3
|
+
import { flushPending, handleResponseFrame } from './protocol.js';
|
|
4
|
+
import { uuid } from '../utils/uuid.js';
|
|
5
|
+
export const PROTOCOL_VERSION = 3;
|
|
6
|
+
export class GatewayClient {
|
|
7
|
+
opts;
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
ws = null;
|
|
10
|
+
/** Increments per connect() call to fence stale socket event handlers. */
|
|
11
|
+
generation = 0;
|
|
12
|
+
pending = new Map();
|
|
13
|
+
connectNonce = null;
|
|
14
|
+
connectSent = false;
|
|
15
|
+
reconnectTimer = null;
|
|
16
|
+
backoffMs = 800;
|
|
17
|
+
closed = false;
|
|
18
|
+
helloResolve = null;
|
|
19
|
+
helloReject = null;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.opts = opts;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Open the WebSocket and complete the connect.challenge handshake.
|
|
25
|
+
* Resolves with the HelloOk payload from the server.
|
|
26
|
+
*/
|
|
27
|
+
async connect() {
|
|
28
|
+
this.closed = false;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const Impl = this.opts.WebSocketImpl ?? globalThis.WebSocket;
|
|
31
|
+
if (!Impl)
|
|
32
|
+
throw new Error('No WebSocket implementation available. Pass WebSocketImpl or run in a browser.');
|
|
33
|
+
const args = this.opts.wsConstructorArgs ?? [];
|
|
34
|
+
const gen = ++this.generation;
|
|
35
|
+
this.connectSent = false;
|
|
36
|
+
this.connectNonce = null;
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
this.helloResolve = resolve;
|
|
39
|
+
this.helloReject = reject;
|
|
40
|
+
let connectTimer = null;
|
|
41
|
+
const connectTimeoutMs = this.opts.connectTimeoutMs ?? 10000;
|
|
42
|
+
connectTimer = setTimeout(() => {
|
|
43
|
+
connectTimer = null;
|
|
44
|
+
if (this.generation === gen) {
|
|
45
|
+
this.helloReject?.(new Error(`connect timed out after ${connectTimeoutMs}ms`));
|
|
46
|
+
this.helloResolve = this.helloReject = null;
|
|
47
|
+
this.ws?.close();
|
|
48
|
+
}
|
|
49
|
+
}, connectTimeoutMs);
|
|
50
|
+
const origReject = reject;
|
|
51
|
+
this.helloReject = (err) => {
|
|
52
|
+
if (connectTimer) {
|
|
53
|
+
clearTimeout(connectTimer);
|
|
54
|
+
connectTimer = null;
|
|
55
|
+
}
|
|
56
|
+
origReject(err);
|
|
57
|
+
};
|
|
58
|
+
this.helloResolve = (value) => {
|
|
59
|
+
if (connectTimer) {
|
|
60
|
+
clearTimeout(connectTimer);
|
|
61
|
+
connectTimer = null;
|
|
62
|
+
}
|
|
63
|
+
resolve(value);
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
this.ws = new Impl(this.opts.url, ...args);
|
|
68
|
+
this.wireEvents(gen);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (connectTimer) {
|
|
72
|
+
clearTimeout(connectTimer);
|
|
73
|
+
connectTimer = null;
|
|
74
|
+
}
|
|
75
|
+
this.helloResolve = this.helloReject = null;
|
|
76
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Send a gateway request and resolve with the response payload.
|
|
82
|
+
* Rejects if not connected or if the request times out.
|
|
83
|
+
*/
|
|
84
|
+
async request(method, params, opts) {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
const ws = this.ws;
|
|
87
|
+
if (!ws || ws.readyState !== 1 /* OPEN */)
|
|
88
|
+
throw new Error('not connected');
|
|
89
|
+
const id = uuid();
|
|
90
|
+
const timeoutMs = opts?.timeoutMs ?? this.opts.requestTimeoutMs ?? 15000;
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
this.pending.delete(id);
|
|
94
|
+
reject(new Error(`request '${method}' timed out after ${timeoutMs}ms`));
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
this.pending.set(id, {
|
|
97
|
+
resolve: (v) => { clearTimeout(timer); resolve(v); },
|
|
98
|
+
reject: (e) => { clearTimeout(timer); reject(e); },
|
|
99
|
+
});
|
|
100
|
+
ws.send(JSON.stringify({ type: 'req', id, method, params }));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Gracefully close the connection and cancel any pending reconnect timers.
|
|
105
|
+
* All pending requests are rejected.
|
|
106
|
+
*/
|
|
107
|
+
close(code = 1000, reason = 'client close') {
|
|
108
|
+
this.closed = true;
|
|
109
|
+
if (this.reconnectTimer) {
|
|
110
|
+
clearTimeout(this.reconnectTimer);
|
|
111
|
+
this.reconnectTimer = null;
|
|
112
|
+
}
|
|
113
|
+
if (this.ws) {
|
|
114
|
+
this.ws.close(code, reason);
|
|
115
|
+
this.ws = null;
|
|
116
|
+
}
|
|
117
|
+
flushPending(this.pending, new Error('disconnected'));
|
|
118
|
+
this.backoffMs = 800;
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Private helpers
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
wireEvents(gen) {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
const ws = this.ws;
|
|
126
|
+
// Normalize Node ws (.on) vs browser WebSocket (addEventListener).
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
+
const on = (ev, fn) => {
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
+
if (typeof ws.on === 'function') {
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
132
|
+
ws.on(ev, fn);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
ws.addEventListener(ev, (e) => fn(e));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
on('open', () => {
|
|
139
|
+
if (this.generation !== gen)
|
|
140
|
+
return;
|
|
141
|
+
this.opts.onOpen?.();
|
|
142
|
+
});
|
|
143
|
+
on('message', (evOrData) => {
|
|
144
|
+
if (this.generation !== gen)
|
|
145
|
+
return;
|
|
146
|
+
// Node ws fires message(data, isBinary); browser fires MessageEvent.
|
|
147
|
+
let raw;
|
|
148
|
+
if (typeof evOrData === 'object' &&
|
|
149
|
+
evOrData !== null &&
|
|
150
|
+
'data' in evOrData) {
|
|
151
|
+
raw = String(evOrData.data ?? '');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
raw = String(evOrData ?? '');
|
|
155
|
+
}
|
|
156
|
+
this.handleMessage(raw);
|
|
157
|
+
});
|
|
158
|
+
on('close', (evOrCode, reasonBuf) => {
|
|
159
|
+
if (this.generation !== gen)
|
|
160
|
+
return;
|
|
161
|
+
// Node ws close(code, reason: Buffer); browser fires CloseEvent.
|
|
162
|
+
let code;
|
|
163
|
+
let reason;
|
|
164
|
+
if (typeof evOrCode === 'object' && evOrCode !== null && 'code' in evOrCode) {
|
|
165
|
+
const ev = evOrCode;
|
|
166
|
+
code = Number(ev.code);
|
|
167
|
+
reason = ev.reason != null ? String(ev.reason) : '';
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
code = Number(evOrCode ?? 1006);
|
|
171
|
+
reason = reasonBuf != null ? String(reasonBuf) : '';
|
|
172
|
+
}
|
|
173
|
+
this.ws = null;
|
|
174
|
+
flushPending(this.pending, new Error(`closed (${code}): ${reason}`));
|
|
175
|
+
this.opts.onClose?.(code, reason);
|
|
176
|
+
// If connect() promise is still pending (closed before hello completed):
|
|
177
|
+
if (this.helloReject) {
|
|
178
|
+
this.helloReject(new Error(`closed before hello (${code})`));
|
|
179
|
+
this.helloResolve = this.helloReject = null;
|
|
180
|
+
}
|
|
181
|
+
if (this.opts.autoReconnect && !this.closed) {
|
|
182
|
+
this.scheduleReconnect();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
on('error', () => {
|
|
186
|
+
// close handler fires next — no action needed here.
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
handleMessage(raw) {
|
|
190
|
+
let frame;
|
|
191
|
+
try {
|
|
192
|
+
frame = JSON.parse(raw);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Malformed JSON — silently discard (T-07-02 mitigation).
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (frame['type'] === 'event') {
|
|
199
|
+
if (frame['event'] === 'connect.challenge') {
|
|
200
|
+
const payload = frame['payload'];
|
|
201
|
+
const nonce = payload && typeof payload.nonce === 'string' ? payload.nonce : null;
|
|
202
|
+
if (nonce) {
|
|
203
|
+
void this.sendConnect(nonce);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
void Promise.resolve(this.opts.onEvent?.(frame)).catch(() => { });
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
handleResponseFrame(frame, this.pending);
|
|
211
|
+
}
|
|
212
|
+
async sendConnect(nonce) {
|
|
213
|
+
if (this.connectSent)
|
|
214
|
+
return;
|
|
215
|
+
this.connectSent = true;
|
|
216
|
+
try {
|
|
217
|
+
const params = await this.opts.onChallenge(nonce);
|
|
218
|
+
const hello = await this.request('connect', params);
|
|
219
|
+
// Successful connect — reset backoff.
|
|
220
|
+
this.backoffMs = 800;
|
|
221
|
+
this.helloResolve?.(hello);
|
|
222
|
+
this.helloResolve = this.helloReject = null;
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
this.helloReject?.(err instanceof Error ? err : new Error(String(err)));
|
|
226
|
+
this.helloResolve = this.helloReject = null;
|
|
227
|
+
// Close the socket to trigger reconnect logic if autoReconnect is true.
|
|
228
|
+
this.ws?.close(4008, 'connect failed');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
scheduleReconnect() {
|
|
232
|
+
if (this.closed)
|
|
233
|
+
return;
|
|
234
|
+
const delay = this.backoffMs;
|
|
235
|
+
// Exponential backoff capped at 15000ms (T-07-04 mitigation).
|
|
236
|
+
this.backoffMs = Math.min(this.backoffMs * 1.7, 15000);
|
|
237
|
+
this.opts.onReconnectScheduled?.(delay);
|
|
238
|
+
this.reconnectTimer = setTimeout(() => {
|
|
239
|
+
this.reconnectTimer = null;
|
|
240
|
+
void this.connect().catch(() => { });
|
|
241
|
+
}, delay);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/gateway/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/gateway/client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// packages/shared/src/gateway/client.test.ts
|
|
2
|
+
// Unit tests for GatewayClient using a hand-rolled mock WebSocket.
|
|
3
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
|
+
import { GatewayClient, PROTOCOL_VERSION } from './client.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Mock WebSocket
|
|
7
|
+
// Supports Node ws style (.on()) — that's what the client uses for the mock.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
class MockWebSocket {
|
|
10
|
+
static CONNECTING = 0;
|
|
11
|
+
static OPEN = 1;
|
|
12
|
+
static CLOSING = 2;
|
|
13
|
+
static CLOSED = 3;
|
|
14
|
+
readyState = MockWebSocket.OPEN; // Start as OPEN so send() works immediately after construction
|
|
15
|
+
sentMessages = [];
|
|
16
|
+
listeners = {};
|
|
17
|
+
// Node ws API
|
|
18
|
+
on(event, fn) {
|
|
19
|
+
(this.listeners[event] ??= []).push(fn);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
send(data) {
|
|
23
|
+
this.sentMessages.push(data);
|
|
24
|
+
}
|
|
25
|
+
close(code, reason) {
|
|
26
|
+
this.readyState = MockWebSocket.CLOSED;
|
|
27
|
+
this.__emit('close', code ?? 1000, reason ?? '');
|
|
28
|
+
}
|
|
29
|
+
__emit(event, ...args) {
|
|
30
|
+
for (const fn of this.listeners[event] ?? [])
|
|
31
|
+
fn(...args);
|
|
32
|
+
}
|
|
33
|
+
// Simulation helpers
|
|
34
|
+
__simulateOpen() {
|
|
35
|
+
this.readyState = MockWebSocket.OPEN;
|
|
36
|
+
this.__emit('open');
|
|
37
|
+
}
|
|
38
|
+
__simulateMessage(data) {
|
|
39
|
+
this.__emit('message', data);
|
|
40
|
+
}
|
|
41
|
+
__simulateClose(code, reason) {
|
|
42
|
+
this.readyState = MockWebSocket.CLOSED;
|
|
43
|
+
this.__emit('close', code, reason);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Factory: build a GatewayClient with injected MockWebSocket
|
|
48
|
+
// connectTimeoutMs set very high so fake timers don't fire the connect timeout.
|
|
49
|
+
// requestTimeoutMs set high for the connect sub-request too.
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
function makeMockImpl(instance) {
|
|
52
|
+
return function MockImpl(_url, ..._args) {
|
|
53
|
+
return instance;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function makeClient(mockWs, opts = {}) {
|
|
57
|
+
return new GatewayClient({
|
|
58
|
+
url: 'ws://mock-host/gateway',
|
|
59
|
+
onChallenge: async (_nonce) => ({ token: 'test-token', minProtocol: 3, maxProtocol: 3 }),
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
WebSocketImpl: makeMockImpl(mockWs),
|
|
62
|
+
connectTimeoutMs: 999_999,
|
|
63
|
+
requestTimeoutMs: 999_999,
|
|
64
|
+
...opts,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Helper: perform the full connect handshake synchronously with fake timers
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
async function performConnect(client, mockWs) {
|
|
71
|
+
const connectPromise = client.connect();
|
|
72
|
+
// Simulate open + challenge
|
|
73
|
+
mockWs.__simulateOpen();
|
|
74
|
+
mockWs.__simulateMessage(JSON.stringify({ type: 'event', event: 'connect.challenge', payload: { nonce: 'test-nonce' } }));
|
|
75
|
+
// Let the onChallenge async callback resolve
|
|
76
|
+
await Promise.resolve();
|
|
77
|
+
await Promise.resolve();
|
|
78
|
+
// Find the connect request id from sent messages
|
|
79
|
+
const connectMsg = mockWs.sentMessages.find((m) => {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(m).method === 'connect';
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
if (!connectMsg)
|
|
88
|
+
throw new Error('connect request not sent');
|
|
89
|
+
const connectReq = JSON.parse(connectMsg);
|
|
90
|
+
const helloPayload = { type: 'hello-ok', protocol: 3, server: { connId: 'conn-1' } };
|
|
91
|
+
mockWs.__simulateMessage(JSON.stringify({ type: 'res', id: connectReq.id, ok: true, payload: helloPayload }));
|
|
92
|
+
return connectPromise;
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Tests
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
describe('GatewayClient', () => {
|
|
98
|
+
let mockWs;
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
mockWs = new MockWebSocket();
|
|
101
|
+
vi.useFakeTimers();
|
|
102
|
+
});
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
vi.useRealTimers();
|
|
105
|
+
vi.restoreAllMocks();
|
|
106
|
+
});
|
|
107
|
+
it('exports PROTOCOL_VERSION = 3', () => {
|
|
108
|
+
expect(PROTOCOL_VERSION).toBe(3);
|
|
109
|
+
});
|
|
110
|
+
it('resolves connect() with hello payload after challenge handshake', async () => {
|
|
111
|
+
const client = makeClient(mockWs);
|
|
112
|
+
const connectPromise = client.connect();
|
|
113
|
+
// Socket opens → server sends connect.challenge
|
|
114
|
+
mockWs.__simulateOpen();
|
|
115
|
+
mockWs.__simulateMessage(JSON.stringify({ type: 'event', event: 'connect.challenge', payload: { nonce: 'abc-nonce' } }));
|
|
116
|
+
// Let async onChallenge resolve
|
|
117
|
+
await Promise.resolve();
|
|
118
|
+
await Promise.resolve();
|
|
119
|
+
// The client should have sent the 'connect' request
|
|
120
|
+
const connectMsg = mockWs.sentMessages.find((m) => {
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(m).method === 'connect';
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
expect(connectMsg).toBeDefined();
|
|
129
|
+
const connectReq = JSON.parse(connectMsg);
|
|
130
|
+
const helloPayload = { type: 'hello-ok', protocol: 3, server: { connId: 'conn-1' } };
|
|
131
|
+
mockWs.__simulateMessage(JSON.stringify({ type: 'res', id: connectReq.id, ok: true, payload: helloPayload }));
|
|
132
|
+
const result = await connectPromise;
|
|
133
|
+
expect(result).toEqual(helloPayload);
|
|
134
|
+
});
|
|
135
|
+
it('request<T>() matches response by id and resolves', async () => {
|
|
136
|
+
const client = makeClient(mockWs);
|
|
137
|
+
await performConnect(client, mockWs);
|
|
138
|
+
// Now send a real request
|
|
139
|
+
const reqPromise = client.request('agents.list', { page: 1 });
|
|
140
|
+
const lastMsg = mockWs.sentMessages.at(-1);
|
|
141
|
+
const sentReq = JSON.parse(lastMsg);
|
|
142
|
+
expect(sentReq.method).toBe('agents.list');
|
|
143
|
+
// Simulate matching response
|
|
144
|
+
mockWs.__simulateMessage(JSON.stringify({ type: 'res', id: sentReq.id, ok: true, payload: { value: 42 } }));
|
|
145
|
+
const result = await reqPromise;
|
|
146
|
+
expect(result).toEqual({ value: 42 });
|
|
147
|
+
});
|
|
148
|
+
it('request<T>() rejects after requestTimeoutMs', async () => {
|
|
149
|
+
// Use a short requestTimeoutMs for this specific test
|
|
150
|
+
const client = makeClient(mockWs, { requestTimeoutMs: 500 });
|
|
151
|
+
await performConnect(client, mockWs);
|
|
152
|
+
const reqPromise = client.request('slow.method');
|
|
153
|
+
// Advance timers past the request timeout (not connect timeout — that's 999999)
|
|
154
|
+
vi.advanceTimersByTime(600);
|
|
155
|
+
await expect(reqPromise).rejects.toThrow("request 'slow.method' timed out after 500ms");
|
|
156
|
+
});
|
|
157
|
+
it('close() flushes pending requests with disconnect error', async () => {
|
|
158
|
+
const client = makeClient(mockWs);
|
|
159
|
+
await performConnect(client, mockWs);
|
|
160
|
+
// Queue a request (no response will come)
|
|
161
|
+
const reqPromise = client.request('pending.method');
|
|
162
|
+
// close() should flush it immediately
|
|
163
|
+
client.close();
|
|
164
|
+
// Pending requests are flushed by the close event handler with the close code/reason.
|
|
165
|
+
// (MockWebSocket.close() fires synchronously, so the close handler runs before our explicit flush.)
|
|
166
|
+
await expect(reqPromise).rejects.toThrow(/closed|disconnected/);
|
|
167
|
+
});
|
|
168
|
+
it('does not reconnect when autoReconnect is false (default)', async () => {
|
|
169
|
+
const client = makeClient(mockWs); // autoReconnect not set → defaults to false
|
|
170
|
+
await performConnect(client, mockWs);
|
|
171
|
+
const sentCountBefore = mockWs.sentMessages.length;
|
|
172
|
+
// Simulate unexpected server close
|
|
173
|
+
mockWs.__simulateClose(1006, 'network gone');
|
|
174
|
+
// Advance timers — no reconnect should fire
|
|
175
|
+
vi.advanceTimersByTime(5000);
|
|
176
|
+
// No new messages on the same socket (reconnect would make a new socket)
|
|
177
|
+
expect(mockWs.sentMessages.length).toBe(sentCountBefore);
|
|
178
|
+
});
|
|
179
|
+
it('schedules reconnect with exponential backoff when autoReconnect is true', async () => {
|
|
180
|
+
const instances = [];
|
|
181
|
+
let instanceIdx = 0;
|
|
182
|
+
const ws1 = new MockWebSocket();
|
|
183
|
+
const ws2 = new MockWebSocket();
|
|
184
|
+
instances.push(ws1, ws2);
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
186
|
+
const MultiImpl = function (_url, ..._args) {
|
|
187
|
+
return instances[instanceIdx++];
|
|
188
|
+
};
|
|
189
|
+
const reconnectDelays = [];
|
|
190
|
+
const client = new GatewayClient({
|
|
191
|
+
url: 'ws://mock-host/gateway',
|
|
192
|
+
onChallenge: async (_nonce) => ({ token: 'x' }),
|
|
193
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
194
|
+
WebSocketImpl: MultiImpl,
|
|
195
|
+
autoReconnect: true,
|
|
196
|
+
connectTimeoutMs: 999_999,
|
|
197
|
+
requestTimeoutMs: 999_999,
|
|
198
|
+
onReconnectScheduled: (delay) => { reconnectDelays.push(delay); },
|
|
199
|
+
});
|
|
200
|
+
// --- First connect ---
|
|
201
|
+
await performConnect(client, ws1);
|
|
202
|
+
// --- Trigger close (unexpected) → should schedule reconnect at 800ms ---
|
|
203
|
+
ws1.__simulateClose(1006, 'gone');
|
|
204
|
+
expect(reconnectDelays).toEqual([800]);
|
|
205
|
+
// Advance 800ms → second connect() fires on ws2
|
|
206
|
+
vi.advanceTimersByTime(800);
|
|
207
|
+
// Let the reconnect's connect() promise start and its async onChallenge run
|
|
208
|
+
await Promise.resolve();
|
|
209
|
+
await Promise.resolve();
|
|
210
|
+
// ws2 should have been opened and gotten the challenge
|
|
211
|
+
ws2.__simulateOpen();
|
|
212
|
+
ws2.__simulateMessage(JSON.stringify({ type: 'event', event: 'connect.challenge', payload: { nonce: 'n2' } }));
|
|
213
|
+
await Promise.resolve();
|
|
214
|
+
await Promise.resolve();
|
|
215
|
+
const connectMsg2 = ws2.sentMessages.find((m) => {
|
|
216
|
+
try {
|
|
217
|
+
return JSON.parse(m).method === 'connect';
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
if (connectMsg2) {
|
|
224
|
+
const req2 = JSON.parse(connectMsg2);
|
|
225
|
+
ws2.__simulateMessage(JSON.stringify({ type: 'res', id: req2.id, ok: true, payload: { type: 'hello-ok' } }));
|
|
226
|
+
await Promise.resolve();
|
|
227
|
+
}
|
|
228
|
+
// --- Trigger close again → backoff should be ~1360ms (800 * 1.7) ---
|
|
229
|
+
ws2.__simulateClose(1006, 'gone again');
|
|
230
|
+
// The second reconnect delay should be 1360 (800 * 1.7)
|
|
231
|
+
expect(reconnectDelays.length).toBeGreaterThanOrEqual(2);
|
|
232
|
+
expect(reconnectDelays[1]).toBeCloseTo(1360, -1);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../src/gateway/client.test.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
package/dist/gateway/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GatewayClient, type GatewayClientOptions } from '../gateway/client.js';
|
|
2
|
+
export interface NodeGatewayClientOptions extends Omit<GatewayClientOptions, 'WebSocketImpl' | 'wsConstructorArgs'> {
|
|
3
|
+
/** Additional HTTP headers to send during the WebSocket upgrade (Node ws feature). */
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
/** Max message payload in bytes. Default: 25 MiB. */
|
|
6
|
+
maxPayload?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create a GatewayClient instance backed by the `ws` npm package.
|
|
10
|
+
*
|
|
11
|
+
* Use this in Node.js environments (paperclip adapter, scripts).
|
|
12
|
+
* Hub and site use `new GatewayClient(opts)` directly (browser globalThis.WebSocket).
|
|
13
|
+
*/
|
|
14
|
+
export declare function createNodeGatewayClient(options: NodeGatewayClientOptions): GatewayClient;
|
|
15
|
+
export { GatewayClient, PROTOCOL_VERSION } from '../gateway/client.js';
|
|
16
|
+
export type { GatewayClientOptions } from '../gateway/client.js';
|
|
17
|
+
export * from '../gateway/types.js';
|
|
18
|
+
export * from '../gateway/protocol.js';
|
|
19
|
+
export * from '../utils/index.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAoB,MAAM,sBAAsB,CAAC;AAElG,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,oBAAoB,EAAE,eAAe,GAAG,mBAAmB,CAAC;IACzE,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CAQxF;AAGD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// packages/shared/src/node/index.ts
|
|
2
|
+
// Node-runtime entry point for the gateway client.
|
|
3
|
+
// Consumers: paperclip-minion adapter (pnpm, Node 22+).
|
|
4
|
+
// Browser consumers (hub, site) import from '@minion-stack/shared' (main entry) — NOT this file.
|
|
5
|
+
// This file is the ONLY entry point in this package that imports 'ws'.
|
|
6
|
+
import { WebSocket } from 'ws';
|
|
7
|
+
import { GatewayClient } from '../gateway/client.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create a GatewayClient instance backed by the `ws` npm package.
|
|
10
|
+
*
|
|
11
|
+
* Use this in Node.js environments (paperclip adapter, scripts).
|
|
12
|
+
* Hub and site use `new GatewayClient(opts)` directly (browser globalThis.WebSocket).
|
|
13
|
+
*/
|
|
14
|
+
export function createNodeGatewayClient(options) {
|
|
15
|
+
const { headers, maxPayload, ...rest } = options;
|
|
16
|
+
return new GatewayClient({
|
|
17
|
+
...rest,
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
WebSocketImpl: WebSocket,
|
|
20
|
+
wsConstructorArgs: [{ headers: headers ?? {}, maxPayload: maxPayload ?? 25 * 1024 * 1024 }],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
// Re-export shared surface so Node consumers can import everything from one subpath.
|
|
24
|
+
export { GatewayClient, PROTOCOL_VERSION } from '../gateway/client.js';
|
|
25
|
+
export * from '../gateway/types.js';
|
|
26
|
+
export * from '../gateway/protocol.js';
|
|
27
|
+
export * from '../utils/index.js';
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,mDAAmD;AACnD,wDAAwD;AACxD,iGAAiG;AACjG,uEAAuE;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,aAAa,EAA+C,MAAM,sBAAsB,CAAC;AAUlG;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAiC;IACvE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACjD,OAAO,IAAI,aAAa,CAAC;QACvB,GAAG,IAAI;QACP,8DAA8D;QAC9D,aAAa,EAAE,SAA2B;QAC1C,iBAAiB,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,UAAU,EAAE,UAAU,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;KAC5F,CAAC,CAAC;AACL,CAAC;AAED,qFAAqF;AACrF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEvE,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/node/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// packages/shared/src/node/index.test.ts
|
|
2
|
+
// Integration smoke test: spin up a real ws WebSocketServer, connect via createNodeGatewayClient,
|
|
3
|
+
// assert connect() resolves with canned helloOk payload.
|
|
4
|
+
import { describe, it, expect, afterAll } from 'vitest';
|
|
5
|
+
import { WebSocketServer } from 'ws';
|
|
6
|
+
import { createNodeGatewayClient, PROTOCOL_VERSION, GatewayClient } from './index.js';
|
|
7
|
+
let server;
|
|
8
|
+
let port;
|
|
9
|
+
// Spin up a minimal gateway mock server before tests.
|
|
10
|
+
function startMockServer() {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
server = new WebSocketServer({ port: 0 }, () => {
|
|
13
|
+
const addr = server.address();
|
|
14
|
+
port = typeof addr === 'object' && addr !== null ? addr.port : 0;
|
|
15
|
+
resolve();
|
|
16
|
+
});
|
|
17
|
+
server.on('connection', (ws) => {
|
|
18
|
+
// 1. Send connect.challenge immediately after connection.
|
|
19
|
+
ws.send(JSON.stringify({
|
|
20
|
+
type: 'event',
|
|
21
|
+
event: 'connect.challenge',
|
|
22
|
+
payload: { nonce: 'server-nonce-123' },
|
|
23
|
+
}));
|
|
24
|
+
// 2. Listen for client's 'connect' request and respond with helloOk.
|
|
25
|
+
ws.on('message', (data) => {
|
|
26
|
+
let frame;
|
|
27
|
+
try {
|
|
28
|
+
frame = JSON.parse(data.toString());
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (frame['method'] === 'connect') {
|
|
34
|
+
ws.send(JSON.stringify({
|
|
35
|
+
type: 'res',
|
|
36
|
+
id: frame['id'],
|
|
37
|
+
ok: true,
|
|
38
|
+
payload: {
|
|
39
|
+
type: 'hello-ok',
|
|
40
|
+
protocol: 3,
|
|
41
|
+
server: { version: '1.0.0', connId: 'smoke-conn-1' },
|
|
42
|
+
features: { methods: ['connect'], events: ['connect.challenge'] },
|
|
43
|
+
snapshot: { presence: [], health: {}, stateVersion: { presence: 0, health: 0 }, uptimeMs: 0 },
|
|
44
|
+
policy: { maxPayload: 1048576, maxBufferedBytes: 1048576, tickIntervalMs: 1000 },
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Start server once for all tests in this file.
|
|
53
|
+
await startMockServer();
|
|
54
|
+
afterAll(() => {
|
|
55
|
+
server?.close();
|
|
56
|
+
});
|
|
57
|
+
describe('createNodeGatewayClient (integration smoke)', () => {
|
|
58
|
+
it('exports createNodeGatewayClient as a function', () => {
|
|
59
|
+
expect(typeof createNodeGatewayClient).toBe('function');
|
|
60
|
+
});
|
|
61
|
+
it('exports GatewayClient class', () => {
|
|
62
|
+
expect(typeof GatewayClient).toBe('function');
|
|
63
|
+
});
|
|
64
|
+
it('re-exports PROTOCOL_VERSION = 3', () => {
|
|
65
|
+
expect(PROTOCOL_VERSION).toBe(3);
|
|
66
|
+
});
|
|
67
|
+
it('connect() resolves with hello-ok payload from a real WebSocketServer', async () => {
|
|
68
|
+
const client = createNodeGatewayClient({
|
|
69
|
+
url: `ws://127.0.0.1:${port}`,
|
|
70
|
+
onChallenge: async (_nonce) => ({
|
|
71
|
+
token: 'smoke-token',
|
|
72
|
+
minProtocol: 3,
|
|
73
|
+
maxProtocol: 3,
|
|
74
|
+
client: { id: 'smoke-test', mode: 'backend' },
|
|
75
|
+
}),
|
|
76
|
+
autoReconnect: false,
|
|
77
|
+
});
|
|
78
|
+
const result = await client.connect();
|
|
79
|
+
expect(result).toMatchObject({ type: 'hello-ok', protocol: 3 });
|
|
80
|
+
client.close();
|
|
81
|
+
});
|
|
82
|
+
it('createNodeGatewayClient injects ws as WebSocketImpl (returns GatewayClient instance)', () => {
|
|
83
|
+
const client = createNodeGatewayClient({
|
|
84
|
+
url: `ws://127.0.0.1:${port}`,
|
|
85
|
+
onChallenge: async (_nonce) => ({}),
|
|
86
|
+
autoReconnect: false,
|
|
87
|
+
});
|
|
88
|
+
expect(client).toBeInstanceOf(GatewayClient);
|
|
89
|
+
client.close();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/node/index.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minion-stack/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Shared gateway protocol types and utilities for the Minion platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,6 +23,18 @@
|
|
|
23
23
|
"./utils": {
|
|
24
24
|
"types": "./dist/utils/index.d.ts",
|
|
25
25
|
"import": "./dist/utils/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./node": {
|
|
28
|
+
"types": "./dist/node/index.d.ts",
|
|
29
|
+
"import": "./dist/node/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"ws": "^8.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"ws": {
|
|
37
|
+
"optional": true
|
|
26
38
|
}
|
|
27
39
|
},
|
|
28
40
|
"publishConfig": {
|
|
@@ -34,10 +46,15 @@
|
|
|
34
46
|
],
|
|
35
47
|
"scripts": {
|
|
36
48
|
"build": "tsc",
|
|
37
|
-
"prepublishOnly": "tsc"
|
|
49
|
+
"prepublishOnly": "tsc",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest"
|
|
38
52
|
},
|
|
39
53
|
"devDependencies": {
|
|
40
54
|
"@minion-stack/tsconfig": "workspace:*",
|
|
41
|
-
"
|
|
55
|
+
"@types/ws": "^8.18.1",
|
|
56
|
+
"typescript": "^5.7.0",
|
|
57
|
+
"vitest": "^2.1.9",
|
|
58
|
+
"ws": "^8.19.0"
|
|
42
59
|
}
|
|
43
60
|
}
|