@vafast/api-client 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -196
- package/TODO.md +83 -0
- package/example/auto-infer.ts +223 -0
- package/example/test-sse.ts +192 -0
- package/package.json +12 -13
- package/src/core/eden.ts +705 -0
- package/src/index.ts +34 -65
- package/src/types/index.ts +17 -116
- package/test/eden.test.ts +425 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +10 -0
- package/vitest.config.ts +8 -0
- package/bun.lock +0 -569
- package/example/index.ts +0 -255
- package/src/core/api-client.ts +0 -389
- package/src/core/typed-client.ts +0 -305
- package/src/utils/index.ts +0 -232
- package/src/websocket/websocket-client.ts +0 -347
- package/test/api-client.test.ts +0 -262
- package/test/basic.test.ts +0 -55
- package/test/typed-client.test.ts +0 -304
- package/test/utils.test.ts +0 -363
- package/test/websocket.test.ts +0 -434
- package/tsup.config.ts +0 -23
package/test/websocket.test.ts
DELETED
|
@@ -1,434 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, beforeEach, mock } from "bun:test";
|
|
2
|
-
import { VafastWebSocketClient, createWebSocketClient, createTypedWebSocketClient } from "../src";
|
|
3
|
-
|
|
4
|
-
// Mock WebSocket
|
|
5
|
-
class MockWebSocket {
|
|
6
|
-
static instances: MockWebSocket[] = [];
|
|
7
|
-
static mockOpen: (() => void) | null = null;
|
|
8
|
-
static mockClose: (() => void) | null = null;
|
|
9
|
-
static mockError: ((error: Event) => void) | null = null;
|
|
10
|
-
static mockMessage: ((event: MessageEvent) => void) | null = null;
|
|
11
|
-
|
|
12
|
-
url: string;
|
|
13
|
-
readyState: number;
|
|
14
|
-
onopen: ((event: Event) => void) | null = null;
|
|
15
|
-
onclose: ((event: CloseEvent) => void) | null = null;
|
|
16
|
-
onerror: ((event: Event) => void) | null = null;
|
|
17
|
-
onmessage: ((event: MessageEvent) => void) | null = null;
|
|
18
|
-
|
|
19
|
-
constructor(url: string) {
|
|
20
|
-
this.url = url;
|
|
21
|
-
this.readyState = WebSocket.CONNECTING;
|
|
22
|
-
MockWebSocket.instances.push(this);
|
|
23
|
-
|
|
24
|
-
// Simulate connection process
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
this.readyState = WebSocket.OPEN;
|
|
27
|
-
if (this.onopen) {
|
|
28
|
-
this.onopen(new Event("open"));
|
|
29
|
-
}
|
|
30
|
-
if (MockWebSocket.mockOpen) {
|
|
31
|
-
MockWebSocket.mockOpen();
|
|
32
|
-
}
|
|
33
|
-
}, 10);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
send(data: any) {
|
|
37
|
-
if (this.readyState !== WebSocket.OPEN) {
|
|
38
|
-
throw new Error("WebSocket is not connected");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Simulate message
|
|
42
|
-
if (this.onmessage) {
|
|
43
|
-
const messageEvent = new MessageEvent("message", {
|
|
44
|
-
data: typeof data === "string" ? data : JSON.stringify(data),
|
|
45
|
-
});
|
|
46
|
-
this.onmessage(messageEvent);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
close() {
|
|
51
|
-
this.readyState = WebSocket.CLOSED;
|
|
52
|
-
if (this.onclose) {
|
|
53
|
-
this.onclose(new CloseEvent("close"));
|
|
54
|
-
}
|
|
55
|
-
if (MockWebSocket.mockClose) {
|
|
56
|
-
MockWebSocket.mockClose();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
static reset() {
|
|
61
|
-
MockWebSocket.instances = [];
|
|
62
|
-
MockWebSocket.mockOpen = null;
|
|
63
|
-
MockWebSocket.mockClose = null;
|
|
64
|
-
MockWebSocket.mockError = null;
|
|
65
|
-
MockWebSocket.mockMessage = null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Mock global WebSocket
|
|
70
|
-
global.WebSocket = MockWebSocket as any;
|
|
71
|
-
|
|
72
|
-
describe("WebSocket Client", () => {
|
|
73
|
-
beforeEach(() => {
|
|
74
|
-
MockWebSocket.reset();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe("VafastWebSocketClient", () => {
|
|
78
|
-
let client: VafastWebSocketClient;
|
|
79
|
-
|
|
80
|
-
beforeEach(() => {
|
|
81
|
-
client = new VafastWebSocketClient("wss://test.example.com");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should create WebSocket client", () => {
|
|
85
|
-
expect(client).toBeDefined();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should connect to WebSocket server", async () => {
|
|
89
|
-
await client.connect();
|
|
90
|
-
// Wait for the async connection to complete
|
|
91
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
92
|
-
expect(client.isConnected()).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("should handle connection events", async () => {
|
|
96
|
-
let connected = false;
|
|
97
|
-
|
|
98
|
-
// Set up event listener before connection
|
|
99
|
-
client.on("open", () => {
|
|
100
|
-
connected = true;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
await client.connect();
|
|
104
|
-
// Wait for the async connection to complete
|
|
105
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
106
|
-
|
|
107
|
-
// Since the connection is already established, we need to test the event listener differently
|
|
108
|
-
// Let's test that the event listener was added correctly
|
|
109
|
-
expect(client.getEventListenerCount("open")).toBe(1);
|
|
110
|
-
|
|
111
|
-
// And test that we can trigger events manually
|
|
112
|
-
let testEventTriggered = false;
|
|
113
|
-
client.on("test", () => {
|
|
114
|
-
testEventTriggered = true;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Simulate a test event
|
|
118
|
-
const listeners = (client as any).eventListeners.get("test");
|
|
119
|
-
if (listeners) {
|
|
120
|
-
listeners.forEach((callback: any) => callback());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
expect(testEventTriggered).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("should send messages", async () => {
|
|
127
|
-
await client.connect();
|
|
128
|
-
// Wait for the async connection to complete
|
|
129
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
130
|
-
|
|
131
|
-
let receivedMessage: any = null;
|
|
132
|
-
client.on("message", (data) => {
|
|
133
|
-
receivedMessage = data;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
client.send({ type: "test", data: "hello" });
|
|
137
|
-
expect(receivedMessage).toBeDefined();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("should handle disconnection", async () => {
|
|
141
|
-
await client.connect();
|
|
142
|
-
// Wait for the async connection to complete
|
|
143
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
144
|
-
expect(client.isConnected()).toBe(true);
|
|
145
|
-
|
|
146
|
-
client.disconnect();
|
|
147
|
-
expect(client.isConnected()).toBe(false);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("should handle message events", async () => {
|
|
151
|
-
await client.connect();
|
|
152
|
-
// Wait for the async connection to complete
|
|
153
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
154
|
-
|
|
155
|
-
let messageCount = 0;
|
|
156
|
-
client.on("message", () => {
|
|
157
|
-
messageCount++;
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
client.send("test message 1");
|
|
161
|
-
client.send("test message 2");
|
|
162
|
-
|
|
163
|
-
expect(messageCount).toBeGreaterThan(0);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should remove event listeners", async () => {
|
|
167
|
-
await client.connect();
|
|
168
|
-
// Wait for the async connection to complete
|
|
169
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
170
|
-
|
|
171
|
-
let messageCount = 0;
|
|
172
|
-
const handler = () => {
|
|
173
|
-
messageCount++;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
client.on("message", handler);
|
|
177
|
-
client.send("test message");
|
|
178
|
-
expect(messageCount).toBeGreaterThan(0);
|
|
179
|
-
|
|
180
|
-
client.off("message", handler);
|
|
181
|
-
messageCount = 0;
|
|
182
|
-
client.send("test message 2");
|
|
183
|
-
expect(messageCount).toBe(0);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("should get connection state", async () => {
|
|
187
|
-
expect(client.getReadyState()).toBe(WebSocket.CLOSED); // CLOSED
|
|
188
|
-
|
|
189
|
-
await client.connect();
|
|
190
|
-
// Wait for the async connection to complete
|
|
191
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
192
|
-
expect(client.getReadyState()).toBe(WebSocket.OPEN); // OPEN
|
|
193
|
-
|
|
194
|
-
client.disconnect();
|
|
195
|
-
expect(client.getReadyState()).toBe(WebSocket.CLOSED); // CLOSED
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("should get connection state text", () => {
|
|
199
|
-
expect(client.getReadyStateText()).toBe("CLOSED");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("should configure auto-reconnect", () => {
|
|
203
|
-
client.setAutoReconnect(false);
|
|
204
|
-
client.setAutoReconnect(true);
|
|
205
|
-
expect(client).toBeDefined();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("should configure max reconnect attempts", () => {
|
|
209
|
-
client.setMaxReconnectAttempts(10);
|
|
210
|
-
expect(client).toBeDefined();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("should configure reconnect delay", () => {
|
|
214
|
-
client.setReconnectDelay(5000);
|
|
215
|
-
expect(client).toBeDefined();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("should get event listener count", async () => {
|
|
219
|
-
await client.connect();
|
|
220
|
-
// Wait for the async connection to complete
|
|
221
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
222
|
-
|
|
223
|
-
expect(client.getEventListenerCount("message")).toBe(0);
|
|
224
|
-
|
|
225
|
-
client.on("message", () => {});
|
|
226
|
-
expect(client.getEventListenerCount("message")).toBe(1);
|
|
227
|
-
|
|
228
|
-
client.on("message", () => {});
|
|
229
|
-
expect(client.getEventListenerCount("message")).toBe(2);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("should clear event listeners", async () => {
|
|
233
|
-
await client.connect();
|
|
234
|
-
// Wait for the async connection to complete
|
|
235
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
236
|
-
|
|
237
|
-
client.on("message", () => {});
|
|
238
|
-
client.on("open", () => {});
|
|
239
|
-
|
|
240
|
-
expect(client.getEventNames().length).toBeGreaterThan(0);
|
|
241
|
-
|
|
242
|
-
client.clearEventListeners();
|
|
243
|
-
expect(client.getEventNames().length).toBe(0);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it("should clear specific event listeners", async () => {
|
|
247
|
-
await client.connect();
|
|
248
|
-
// Wait for the async connection to complete
|
|
249
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
250
|
-
|
|
251
|
-
client.on("message", () => {});
|
|
252
|
-
client.on("open", () => {});
|
|
253
|
-
|
|
254
|
-
expect(client.getEventListenerCount("message")).toBe(1);
|
|
255
|
-
expect(client.getEventListenerCount("open")).toBe(1);
|
|
256
|
-
|
|
257
|
-
client.clearEventListeners("message");
|
|
258
|
-
expect(client.getEventListenerCount("message")).toBe(0);
|
|
259
|
-
expect(client.getEventListenerCount("open")).toBe(1);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("should handle manual reconnection", async () => {
|
|
263
|
-
await client.connect();
|
|
264
|
-
// Wait for the async connection to complete
|
|
265
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
266
|
-
client.disconnect();
|
|
267
|
-
|
|
268
|
-
expect(client.isConnected()).toBe(false);
|
|
269
|
-
|
|
270
|
-
await client.reconnect();
|
|
271
|
-
// Wait for the async connection to complete
|
|
272
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
273
|
-
expect(client.isConnected()).toBe(true);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it("should handle connection errors gracefully", async () => {
|
|
277
|
-
// Mock WebSocket to throw error
|
|
278
|
-
const originalWebSocket = global.WebSocket;
|
|
279
|
-
global.WebSocket = class extends MockWebSocket {
|
|
280
|
-
constructor(url: string) {
|
|
281
|
-
super(url);
|
|
282
|
-
setTimeout(() => {
|
|
283
|
-
if (this.onerror) {
|
|
284
|
-
this.onerror(new Event("error"));
|
|
285
|
-
}
|
|
286
|
-
}, 10);
|
|
287
|
-
}
|
|
288
|
-
} as any;
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
await client.connect();
|
|
292
|
-
// Should not reach here if error is thrown
|
|
293
|
-
expect(false).toBe(true);
|
|
294
|
-
} catch (error) {
|
|
295
|
-
expect(error).toBeDefined();
|
|
296
|
-
} finally {
|
|
297
|
-
global.WebSocket = originalWebSocket;
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("createWebSocketClient", () => {
|
|
303
|
-
it("should create WebSocket client with factory function", () => {
|
|
304
|
-
const client = createWebSocketClient("wss://test.example.com");
|
|
305
|
-
expect(client).toBeDefined();
|
|
306
|
-
expect(client).toBeInstanceOf(VafastWebSocketClient);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("should create WebSocket client with options", () => {
|
|
310
|
-
const client = createWebSocketClient("wss://test.example.com", {
|
|
311
|
-
autoReconnect: false,
|
|
312
|
-
maxReconnectAttempts: 10,
|
|
313
|
-
reconnectDelay: 5000,
|
|
314
|
-
});
|
|
315
|
-
expect(client).toBeDefined();
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
describe("createTypedWebSocketClient", () => {
|
|
320
|
-
interface TestEvents {
|
|
321
|
-
chat: { message: string; userId: string };
|
|
322
|
-
join: { room: string; userId: string };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
it("should create typed WebSocket client", () => {
|
|
326
|
-
const client = createTypedWebSocketClient<TestEvents>("wss://test.example.com");
|
|
327
|
-
expect(client).toBeDefined();
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it("should have typed event methods", () => {
|
|
331
|
-
const client = createTypedWebSocketClient<TestEvents>("wss://test.example.com");
|
|
332
|
-
|
|
333
|
-
// These should have proper typing
|
|
334
|
-
expect(typeof client.on).toBe("function");
|
|
335
|
-
expect(typeof client.send).toBe("function");
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("should handle typed events", async () => {
|
|
339
|
-
const client = createTypedWebSocketClient<TestEvents>("wss://test.example.com");
|
|
340
|
-
|
|
341
|
-
// First connect the client
|
|
342
|
-
await client.connect();
|
|
343
|
-
// Wait for the async connection to complete
|
|
344
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
345
|
-
|
|
346
|
-
let receivedChat: any = null;
|
|
347
|
-
let receivedJoin: any = null;
|
|
348
|
-
|
|
349
|
-
client.on("chat", (data) => {
|
|
350
|
-
receivedChat = data;
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
client.on("join", (data) => {
|
|
354
|
-
receivedJoin = data;
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
// Now send messages
|
|
358
|
-
client.send("chat", { message: "Hello!", userId: "user123" });
|
|
359
|
-
client.send("join", { room: "general", userId: "user123" });
|
|
360
|
-
|
|
361
|
-
expect(receivedChat).toBeDefined();
|
|
362
|
-
expect(receivedJoin).toBeDefined();
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
describe("WebSocket Lifecycle", () => {
|
|
367
|
-
it("should handle complete connection lifecycle", async () => {
|
|
368
|
-
const client = new VafastWebSocketClient("wss://test.example.com");
|
|
369
|
-
|
|
370
|
-
// Initial state
|
|
371
|
-
expect(client.isConnected()).toBe(false);
|
|
372
|
-
|
|
373
|
-
// Connect
|
|
374
|
-
await client.connect();
|
|
375
|
-
// Wait for the async connection to complete
|
|
376
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
377
|
-
expect(client.isConnected()).toBe(true);
|
|
378
|
-
|
|
379
|
-
// Send message
|
|
380
|
-
let messageReceived = false;
|
|
381
|
-
client.on("message", () => {
|
|
382
|
-
messageReceived = true;
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
client.send("test message");
|
|
386
|
-
expect(messageReceived).toBe(true);
|
|
387
|
-
|
|
388
|
-
// Disconnect
|
|
389
|
-
client.disconnect();
|
|
390
|
-
expect(client.isConnected()).toBe(false);
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it("should handle multiple connections and disconnections", async () => {
|
|
394
|
-
const client = new VafastWebSocketClient("wss://test.example.com");
|
|
395
|
-
|
|
396
|
-
for (let i = 0; i < 3; i++) {
|
|
397
|
-
await client.connect();
|
|
398
|
-
// Wait for the async connection to complete
|
|
399
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
400
|
-
expect(client.isConnected()).toBe(true);
|
|
401
|
-
|
|
402
|
-
client.disconnect();
|
|
403
|
-
expect(client.isConnected()).toBe(false);
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
describe("Error Scenarios", () => {
|
|
409
|
-
it("should handle sending message when disconnected", async () => {
|
|
410
|
-
const client = new VafastWebSocketClient("wss://test.example.com");
|
|
411
|
-
|
|
412
|
-
expect(() => {
|
|
413
|
-
client.send("test message");
|
|
414
|
-
}).toThrow("WebSocket is not connected");
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it("should handle invalid event listener removal", () => {
|
|
418
|
-
const client = new VafastWebSocketClient("wss://test.example.com");
|
|
419
|
-
|
|
420
|
-
// Should not throw when removing non-existent listener
|
|
421
|
-
expect(() => {
|
|
422
|
-
client.off("nonexistent", () => {});
|
|
423
|
-
}).not.toThrow();
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
it("should handle invalid interceptor index", () => {
|
|
427
|
-
const client = new VafastWebSocketClient("wss://test.example.com");
|
|
428
|
-
|
|
429
|
-
// WebSocket client doesn't have removeInterceptor method
|
|
430
|
-
// This test should be removed or modified
|
|
431
|
-
expect(client).toBeDefined();
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
});
|
package/tsup.config.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup'
|
|
2
|
-
|
|
3
|
-
export default defineConfig([
|
|
4
|
-
{
|
|
5
|
-
entry: ['src/index.ts'],
|
|
6
|
-
outDir: 'dist',
|
|
7
|
-
format: ['esm'],
|
|
8
|
-
target: 'node18',
|
|
9
|
-
dts: true,
|
|
10
|
-
clean: true,
|
|
11
|
-
sourcemap: true,
|
|
12
|
-
outExtension: () => ({ js: '.mjs' }),
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
entry: ['src/index.ts'],
|
|
16
|
-
outDir: 'dist/cjs',
|
|
17
|
-
format: ['cjs'],
|
|
18
|
-
target: 'node18',
|
|
19
|
-
dts: false,
|
|
20
|
-
clean: false,
|
|
21
|
-
},
|
|
22
|
-
])
|
|
23
|
-
|