@agentstage/bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/counter/store.json +8 -0
  2. package/dist/browser/createBridgeStore.d.ts +5 -0
  3. package/dist/browser/createBridgeStore.d.ts.map +1 -0
  4. package/dist/browser/createBridgeStore.js +232 -0
  5. package/dist/browser/createBridgeStore.js.map +1 -0
  6. package/dist/browser/index.d.ts +4 -0
  7. package/dist/browser/index.d.ts.map +1 -0
  8. package/dist/browser/index.js +2 -0
  9. package/dist/browser/index.js.map +1 -0
  10. package/dist/browser/types.d.ts +36 -0
  11. package/dist/browser/types.d.ts.map +1 -0
  12. package/dist/browser/types.js +2 -0
  13. package/dist/browser/types.js.map +1 -0
  14. package/dist/gateway/apiHandler.d.ts +10 -0
  15. package/dist/gateway/apiHandler.d.ts.map +1 -0
  16. package/dist/gateway/apiHandler.js +91 -0
  17. package/dist/gateway/apiHandler.js.map +1 -0
  18. package/dist/gateway/createBridgeGateway.d.ts +3 -0
  19. package/dist/gateway/createBridgeGateway.d.ts.map +1 -0
  20. package/dist/gateway/createBridgeGateway.js +689 -0
  21. package/dist/gateway/createBridgeGateway.js.map +1 -0
  22. package/dist/gateway/fileStore.d.ts +39 -0
  23. package/dist/gateway/fileStore.d.ts.map +1 -0
  24. package/dist/gateway/fileStore.js +189 -0
  25. package/dist/gateway/fileStore.js.map +1 -0
  26. package/dist/gateway/index.d.ts +6 -0
  27. package/dist/gateway/index.d.ts.map +1 -0
  28. package/dist/gateway/index.js +5 -0
  29. package/dist/gateway/index.js.map +1 -0
  30. package/dist/gateway/registry.d.ts +21 -0
  31. package/dist/gateway/registry.d.ts.map +1 -0
  32. package/dist/gateway/registry.js +136 -0
  33. package/dist/gateway/registry.js.map +1 -0
  34. package/dist/gateway/types.d.ts +50 -0
  35. package/dist/gateway/types.d.ts.map +1 -0
  36. package/dist/gateway/types.js +2 -0
  37. package/dist/gateway/types.js.map +1 -0
  38. package/dist/index.d.ts +26 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +24 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/sdk/BridgeClient.d.ts +38 -0
  43. package/dist/sdk/BridgeClient.d.ts.map +1 -0
  44. package/dist/sdk/BridgeClient.js +163 -0
  45. package/dist/sdk/BridgeClient.js.map +1 -0
  46. package/dist/sdk/index.d.ts +3 -0
  47. package/dist/sdk/index.d.ts.map +1 -0
  48. package/dist/sdk/index.js +2 -0
  49. package/dist/sdk/index.js.map +1 -0
  50. package/dist/shared/types.d.ts +154 -0
  51. package/dist/shared/types.d.ts.map +1 -0
  52. package/dist/shared/types.js +5 -0
  53. package/dist/shared/types.js.map +1 -0
  54. package/dist/utils/logger.d.ts +33 -0
  55. package/dist/utils/logger.d.ts.map +1 -0
  56. package/dist/utils/logger.js +206 -0
  57. package/dist/utils/logger.js.map +1 -0
  58. package/dist/vite/index.d.ts +6 -0
  59. package/dist/vite/index.d.ts.map +1 -0
  60. package/dist/vite/index.js +23 -0
  61. package/dist/vite/index.js.map +1 -0
  62. package/package.json +60 -0
  63. package/src/browser/createBridgeStore.ts +276 -0
  64. package/src/browser/index.ts +6 -0
  65. package/src/browser/types.ts +36 -0
  66. package/src/gateway/apiHandler.ts +107 -0
  67. package/src/gateway/createBridgeGateway.ts +854 -0
  68. package/src/gateway/fileStore.ts +244 -0
  69. package/src/gateway/index.ts +12 -0
  70. package/src/gateway/registry.ts +166 -0
  71. package/src/gateway/types.ts +65 -0
  72. package/src/index.ts +33 -0
  73. package/src/sdk/BridgeClient.ts +203 -0
  74. package/src/sdk/index.ts +2 -0
  75. package/src/shared/types.ts +117 -0
  76. package/src/utils/logger.ts +262 -0
  77. package/src/vite/index.ts +31 -0
  78. package/test/e2e/bridge.test.ts +386 -0
  79. package/test/integration/gateway.test.ts +485 -0
  80. package/test/mocks/mockWebSocket.ts +49 -0
  81. package/test/unit/browser.test.ts +267 -0
  82. package/test/unit/fileStore.test.ts +98 -0
  83. package/test/unit/registry.test.ts +345 -0
  84. package/test-page/store.json +8 -0
  85. package/tsconfig.json +20 -0
  86. package/tsconfig.tsbuildinfo +1 -0
  87. package/vitest.config.ts +17 -0
@@ -0,0 +1,485 @@
1
+ import { describe, it, expect, beforeEach, afterAll, beforeAll } from 'vitest';
2
+ import { createServer } from 'http';
3
+ import { WebSocket, WebSocketServer } from 'ws';
4
+ import { mkdtempSync, readdirSync, rmSync } from 'fs';
5
+ import { tmpdir } from 'os';
6
+ import { join } from 'path';
7
+ import { createBridgeGateway } from '../../src/gateway/createBridgeGateway.js';
8
+ import type { Gateway } from '../../src/gateway/types.js';
9
+ import getPort from 'get-port';
10
+
11
+ function attachAutoStateAppliedAck(ws: WebSocket): void {
12
+ ws.on('message', (data) => {
13
+ const msg = JSON.parse(data.toString());
14
+ if (msg.type !== 'client.setState' || !msg.payload?.requestId || !msg.payload?.storeId) {
15
+ return;
16
+ }
17
+
18
+ ws.send(
19
+ JSON.stringify({
20
+ type: 'store.stateApplied',
21
+ payload: {
22
+ storeId: msg.payload.storeId,
23
+ requestId: msg.payload.requestId,
24
+ status: 'applied',
25
+ version: typeof msg.payload.version === 'number' ? msg.payload.version : 0,
26
+ },
27
+ })
28
+ );
29
+ });
30
+ }
31
+
32
+ describe('Gateway + Registry Integration', () => {
33
+ let gateway: Gateway & { attach: (server: any) => WebSocketServer; destroy: () => void };
34
+ let server: ReturnType<typeof createServer>;
35
+ let wss: WebSocketServer;
36
+ let port: number;
37
+ let tempDir: string;
38
+
39
+ beforeAll(async () => {
40
+ tempDir = mkdtempSync(join(tmpdir(), 'bridge-integration-'));
41
+ gateway = createBridgeGateway({ pagesDir: tempDir }) as any;
42
+ server = createServer();
43
+ wss = gateway.attach(server);
44
+ port = await getPort();
45
+ await new Promise<void>((resolve) => server.listen(port, resolve));
46
+ });
47
+
48
+ afterAll(() => {
49
+ gateway.destroy();
50
+ wss.close();
51
+ server.close();
52
+ // Clean up temp directory
53
+ try {
54
+ rmSync(tempDir, { recursive: true, force: true });
55
+ } catch {}
56
+ });
57
+
58
+ beforeEach(async () => {
59
+ // Clear all stores between tests by disconnecting all connected stores
60
+ for (const store of gateway.listStores()) {
61
+ const ws = gateway.getStore(store.id)?.ws;
62
+ if (ws && ws.readyState === WebSocket.OPEN) {
63
+ ws.close();
64
+ }
65
+ }
66
+ // Wait longer for all async cleanup to complete
67
+ await new Promise(r => setTimeout(r, 100));
68
+
69
+ // Clear any remaining stores from registry
70
+ for (const store of gateway.listStores()) {
71
+ const ws = gateway.getStore(store.id)?.ws;
72
+ if (ws) {
73
+ ws.terminate?.();
74
+ }
75
+ }
76
+ await new Promise(r => setTimeout(r, 50));
77
+
78
+ // Clean up files in temp directory between tests
79
+ try {
80
+ for (const file of readdirSync(tempDir)) {
81
+ rmSync(join(tempDir, file), { recursive: true, force: true });
82
+ }
83
+ } catch {}
84
+ });
85
+
86
+ describe('full lifecycle', () => {
87
+ it('should handle complete register -> stateChange -> disconnect flow', async () => {
88
+ // 1. Browser connects and registers
89
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
90
+ attachAutoStateAppliedAck(browserWs);
91
+
92
+ await new Promise<void>((resolve, reject) => {
93
+ browserWs.on('open', resolve);
94
+ browserWs.on('error', reject);
95
+ });
96
+
97
+ // Register store
98
+ browserWs.send(JSON.stringify({
99
+ type: 'store.register',
100
+ payload: {
101
+ storeId: 'page#test123',
102
+ pageId: 'test-page',
103
+ storeKey: 'main',
104
+ description: {
105
+ pageId: 'test-page',
106
+ storeKey: 'main',
107
+ schema: { type: 'object' },
108
+ actions: {},
109
+ },
110
+ initialState: { count: 0 },
111
+ },
112
+ }));
113
+
114
+ // Wait a bit for registration
115
+ await new Promise(r => setTimeout(r, 50));
116
+
117
+ // Verify store is registered
118
+ const store = gateway.getStore('page#test123');
119
+ expect(store).toBeDefined();
120
+ expect(store?.pageId).toBe('test-page');
121
+ expect(store?.currentState).toEqual({ count: 0 });
122
+
123
+ // 2. Browser sends state change
124
+ browserWs.send(JSON.stringify({
125
+ type: 'store.stateChanged',
126
+ payload: {
127
+ storeId: 'page#test123',
128
+ state: { count: 5 },
129
+ version: 1,
130
+ source: 'browser',
131
+ },
132
+ }));
133
+
134
+ await new Promise(r => setTimeout(r, 50));
135
+
136
+ // Verify state is updated
137
+ const updatedStore = gateway.getStore('page#test123');
138
+ expect(updatedStore?.currentState).toEqual({ count: 5 });
139
+ expect(updatedStore?.version).toBe(1);
140
+
141
+ // 3. Browser disconnects
142
+ browserWs.close();
143
+
144
+ await new Promise(r => setTimeout(r, 50));
145
+
146
+ // Verify store is removed
147
+ expect(gateway.getStore('page#test123')).toBeUndefined();
148
+ });
149
+
150
+ it('should clean up all stores on the same ws when connection closes', async () => {
151
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
152
+ attachAutoStateAppliedAck(browserWs);
153
+
154
+ await new Promise<void>((resolve, reject) => {
155
+ browserWs.on('open', resolve);
156
+ browserWs.on('error', reject);
157
+ });
158
+
159
+ browserWs.send(
160
+ JSON.stringify({
161
+ type: 'store.register',
162
+ payload: {
163
+ storeId: 'page#multi-a',
164
+ pageId: 'lifecycle-a',
165
+ storeKey: 'main',
166
+ description: {
167
+ pageId: 'lifecycle-a',
168
+ storeKey: 'main',
169
+ schema: { type: 'object' },
170
+ actions: {},
171
+ },
172
+ initialState: { count: 1 },
173
+ },
174
+ })
175
+ );
176
+
177
+ browserWs.send(
178
+ JSON.stringify({
179
+ type: 'store.register',
180
+ payload: {
181
+ storeId: 'page#multi-b',
182
+ pageId: 'lifecycle-b',
183
+ storeKey: 'main',
184
+ description: {
185
+ pageId: 'lifecycle-b',
186
+ storeKey: 'main',
187
+ schema: { type: 'object' },
188
+ actions: {},
189
+ },
190
+ initialState: { count: 2 },
191
+ },
192
+ })
193
+ );
194
+
195
+ await new Promise((r) => setTimeout(r, 80));
196
+ expect(gateway.getStore('page#multi-a')).toBeDefined();
197
+ expect(gateway.getStore('page#multi-b')).toBeDefined();
198
+
199
+ browserWs.close();
200
+ await new Promise((r) => setTimeout(r, 80));
201
+
202
+ expect(gateway.getStore('page#multi-a')).toBeUndefined();
203
+ expect(gateway.getStore('page#multi-b')).toBeUndefined();
204
+ });
205
+
206
+ it('should broadcast state changes to subscribers', async () => {
207
+ // 1. Browser registers store
208
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
209
+ attachAutoStateAppliedAck(browserWs);
210
+ await new Promise<void>((resolve) => browserWs.on('open', resolve));
211
+
212
+ browserWs.send(JSON.stringify({
213
+ type: 'store.register',
214
+ payload: {
215
+ storeId: 'page#test123',
216
+ pageId: 'test-page',
217
+ storeKey: 'main',
218
+ description: {
219
+ pageId: 'test-page',
220
+ storeKey: 'main',
221
+ schema: { type: 'object' },
222
+ actions: {},
223
+ },
224
+ initialState: { count: 0 },
225
+ },
226
+ }));
227
+
228
+ await new Promise(r => setTimeout(r, 50));
229
+
230
+ // 2. Client subscribes to store
231
+ const clientWs = new WebSocket(`ws://localhost:${port}/_bridge?type=client`);
232
+ const receivedMessages: unknown[] = [];
233
+
234
+ // Set up message handler BEFORE waiting for open
235
+ clientWs.on('message', (data) => {
236
+ receivedMessages.push(JSON.parse(data.toString()));
237
+ });
238
+
239
+ await new Promise<void>((resolve) => clientWs.on('open', resolve));
240
+
241
+ clientWs.send(JSON.stringify({
242
+ type: 'subscribe',
243
+ payload: { storeId: 'page#test123' },
244
+ }));
245
+
246
+ await new Promise(r => setTimeout(r, 50));
247
+
248
+ // Should receive snapshot immediately
249
+ expect(receivedMessages.length).toBeGreaterThanOrEqual(1);
250
+ const snapshot = receivedMessages.find(m => (m as any).type === 'store.stateChanged');
251
+ expect(snapshot).toBeDefined();
252
+ expect((snapshot as any).payload.source).toBe('snapshot');
253
+
254
+ // 3. Browser sends state change
255
+ browserWs.send(JSON.stringify({
256
+ type: 'store.stateChanged',
257
+ payload: {
258
+ storeId: 'page#test123',
259
+ state: { count: 10 },
260
+ version: 1,
261
+ source: 'browser',
262
+ },
263
+ }));
264
+
265
+ await new Promise(r => setTimeout(r, 50));
266
+
267
+ // 4. Client should receive broadcast
268
+ const stateChanged = receivedMessages.find(
269
+ m => (m as any).type === 'store.stateChanged' && (m as any).payload.source === 'browser'
270
+ );
271
+ expect(stateChanged).toBeDefined();
272
+ expect((stateChanged as any).payload.state).toEqual({ count: 10 });
273
+
274
+ browserWs.close();
275
+ clientWs.close();
276
+ });
277
+
278
+ it('should handle page refresh scenario (new store replaces old)', async () => {
279
+ // 1. First browser tab connects
280
+ const browser1 = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
281
+ attachAutoStateAppliedAck(browser1);
282
+ await new Promise<void>((resolve) => browser1.on('open', resolve));
283
+
284
+ browser1.send(JSON.stringify({
285
+ type: 'store.register',
286
+ payload: {
287
+ storeId: 'page#old',
288
+ pageId: 'test-page',
289
+ storeKey: 'main',
290
+ description: {
291
+ pageId: 'test-page',
292
+ storeKey: 'main',
293
+ schema: { type: 'object' },
294
+ actions: {},
295
+ },
296
+ initialState: { count: 0 },
297
+ },
298
+ }));
299
+
300
+ await new Promise(r => setTimeout(r, 50));
301
+
302
+ expect(gateway.getStore('page#old')).toBeDefined();
303
+ expect(gateway.findStore('test-page', 'main')?.id).toBe('page#old');
304
+
305
+ // 2. Page refreshes (new tab connects with same pageId+storeKey)
306
+ const browser2 = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
307
+ attachAutoStateAppliedAck(browser2);
308
+ await new Promise<void>((resolve) => browser2.on('open', resolve));
309
+
310
+ browser2.send(JSON.stringify({
311
+ type: 'store.register',
312
+ payload: {
313
+ storeId: 'page#new',
314
+ pageId: 'test-page',
315
+ storeKey: 'main',
316
+ description: {
317
+ pageId: 'test-page',
318
+ storeKey: 'main',
319
+ schema: { type: 'object' },
320
+ actions: {},
321
+ },
322
+ initialState: { count: 5 },
323
+ },
324
+ }));
325
+
326
+ await new Promise(r => setTimeout(r, 50));
327
+
328
+ // Old store should be replaced
329
+ expect(gateway.getStore('page#old')).toBeUndefined();
330
+ expect(gateway.getStore('page#new')).toBeDefined();
331
+ expect(gateway.findStore('test-page', 'main')?.id).toBe('page#new');
332
+
333
+ browser1.close();
334
+ browser2.close();
335
+ });
336
+
337
+ it('should handle setState from client to browser', async () => {
338
+ // 1. Browser registers
339
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
340
+ attachAutoStateAppliedAck(browserWs);
341
+ const browserMessages: unknown[] = [];
342
+
343
+ await new Promise<void>((resolve) => browserWs.on('open', resolve));
344
+
345
+ browserWs.on('message', (data) => {
346
+ browserMessages.push(JSON.parse(data.toString()));
347
+ });
348
+
349
+ // Use unique IDs to avoid file storage pollution from other tests
350
+ browserWs.send(JSON.stringify({
351
+ type: 'store.register',
352
+ payload: {
353
+ storeId: 'page#setstate-test',
354
+ pageId: 'setstate-test-page',
355
+ storeKey: 'main',
356
+ description: {
357
+ pageId: 'setstate-test-page',
358
+ storeKey: 'main',
359
+ schema: { type: 'object' },
360
+ actions: {},
361
+ },
362
+ initialState: { count: 0 },
363
+ },
364
+ }));
365
+
366
+ await new Promise(r => setTimeout(r, 50));
367
+
368
+ // 2. Call gateway.setState
369
+ await gateway.setState('page#setstate-test', { count: 42 });
370
+
371
+ await new Promise(r => setTimeout(r, 50));
372
+
373
+ // 3. Browser should receive setState message (filter out the initial file-based setState)
374
+ const setStateMsgs = browserMessages.filter(
375
+ m => (m as any).type === 'client.setState'
376
+ );
377
+ expect(setStateMsgs.length).toBeGreaterThanOrEqual(1);
378
+ // The last setState should be from gateway.setState
379
+ expect((setStateMsgs[setStateMsgs.length - 1] as any).payload.state).toEqual({ count: 42 });
380
+
381
+ browserWs.close();
382
+ });
383
+
384
+ it('should handle dispatch from client to browser', async () => {
385
+ // 1. Browser registers
386
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
387
+ attachAutoStateAppliedAck(browserWs);
388
+ const browserMessages: unknown[] = [];
389
+
390
+ await new Promise<void>((resolve) => browserWs.on('open', resolve));
391
+
392
+ browserWs.on('message', (data) => {
393
+ browserMessages.push(JSON.parse(data.toString()));
394
+ });
395
+
396
+ browserWs.send(JSON.stringify({
397
+ type: 'store.register',
398
+ payload: {
399
+ storeId: 'page#test123',
400
+ pageId: 'test-page',
401
+ storeKey: 'main',
402
+ description: {
403
+ pageId: 'test-page',
404
+ storeKey: 'main',
405
+ schema: { type: 'object' },
406
+ actions: {},
407
+ },
408
+ initialState: { count: 0 },
409
+ },
410
+ }));
411
+
412
+ await new Promise(r => setTimeout(r, 50));
413
+
414
+ // 2. Call gateway.dispatch
415
+ await gateway.dispatch('page#test123', { type: 'increment', payload: { by: 5 } });
416
+
417
+ await new Promise(r => setTimeout(r, 50));
418
+
419
+ // 3. Browser should receive dispatch message
420
+ const dispatchMsg = browserMessages.find(
421
+ m => (m as any).type === 'client.dispatch'
422
+ );
423
+ expect(dispatchMsg).toBeDefined();
424
+ expect((dispatchMsg as any).payload.action).toEqual({ type: 'increment', payload: { by: 5 } });
425
+
426
+ browserWs.close();
427
+ });
428
+
429
+ it('should notify subscribers when store disconnects', async () => {
430
+ // 1. Browser registers
431
+ const browserWs = new WebSocket(`ws://localhost:${port}/_bridge?type=browser`);
432
+ attachAutoStateAppliedAck(browserWs);
433
+ await new Promise<void>((resolve) => browserWs.on('open', resolve));
434
+
435
+ browserWs.send(JSON.stringify({
436
+ type: 'store.register',
437
+ payload: {
438
+ storeId: 'page#test123',
439
+ pageId: 'test-page',
440
+ storeKey: 'main',
441
+ description: {
442
+ pageId: 'test-page',
443
+ storeKey: 'main',
444
+ schema: { type: 'object' },
445
+ actions: {},
446
+ },
447
+ initialState: { count: 0 },
448
+ },
449
+ }));
450
+
451
+ await new Promise(r => setTimeout(r, 50));
452
+
453
+ // 2. Client subscribes
454
+ const clientWs = new WebSocket(`ws://localhost:${port}/_bridge?type=client`);
455
+ const receivedMessages: unknown[] = [];
456
+
457
+ await new Promise<void>((resolve) => clientWs.on('open', resolve));
458
+
459
+ clientWs.on('message', (data) => {
460
+ receivedMessages.push(JSON.parse(data.toString()));
461
+ });
462
+
463
+ clientWs.send(JSON.stringify({
464
+ type: 'subscribe',
465
+ payload: { storeId: 'page#test123' },
466
+ }));
467
+
468
+ await new Promise(r => setTimeout(r, 50));
469
+
470
+ // 3. Browser disconnects
471
+ browserWs.close();
472
+
473
+ await new Promise(r => setTimeout(r, 100));
474
+
475
+ // 4. Client should receive disconnected message
476
+ const disconnectedMsg = receivedMessages.find(
477
+ m => (m as any).type === 'store.disconnected'
478
+ );
479
+ expect(disconnectedMsg).toBeDefined();
480
+ expect((disconnectedMsg as any).payload.storeId).toBe('page#test123');
481
+
482
+ clientWs.close();
483
+ });
484
+ });
485
+ });
@@ -0,0 +1,49 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { WebSocket as WebSocketType } from 'ws';
3
+
4
+ /**
5
+ * Mock WebSocket for unit testing
6
+ */
7
+ export class MockWebSocket extends EventEmitter implements Partial<WebSocketType> {
8
+ public readyState: number = 1; // OPEN
9
+ public static readonly CONNECTING = 0;
10
+ public static readonly OPEN = 1;
11
+ public static readonly CLOSING = 2;
12
+ public static readonly CLOSED = 3;
13
+
14
+ sentMessages: unknown[] = [];
15
+ url: string;
16
+
17
+ constructor(url: string) {
18
+ super();
19
+ this.url = url;
20
+ // Simulate async connection
21
+ setTimeout(() => this.emit('open'), 0);
22
+ }
23
+
24
+ send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void {
25
+ this.sentMessages.push(data);
26
+ }
27
+
28
+ close(): void {
29
+ this.readyState = 3; // CLOSED
30
+ this.emit('close');
31
+ }
32
+
33
+ // Helper to simulate receiving a message
34
+ simulateMessage(data: string): void {
35
+ this.emit('message', Buffer.from(data));
36
+ }
37
+
38
+ // Helper to simulate error
39
+ simulateError(error: Error): void {
40
+ this.emit('error', error);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Create a mock WebSocket constructor
46
+ */
47
+ export function createMockWebSocket() {
48
+ return MockWebSocket as unknown as typeof WebSocketType;
49
+ }