sockress-client 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.
- package/dist/index.d.ts +107 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +422 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -0
- package/src/index.ts +585 -0
- package/tsconfig.json +10 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
2
|
+
export interface SockressClientOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
socketPath?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
reconnectInterval?: number;
|
|
8
|
+
maxReconnectInterval?: number;
|
|
9
|
+
autoConnect?: boolean;
|
|
10
|
+
preferSocket?: boolean;
|
|
11
|
+
fetchImpl?: typeof fetch;
|
|
12
|
+
wsFactory?: WebSocketFactory;
|
|
13
|
+
credentials?: RequestCredentials;
|
|
14
|
+
}
|
|
15
|
+
export interface SockressClientRequest {
|
|
16
|
+
path: string;
|
|
17
|
+
method?: HTTPMethod;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
query?: Record<string, string | number | boolean | Array<string | number | boolean>>;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
timeout?: number;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
disableHttpFallback?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface SockressClientResponse<T = unknown> {
|
|
26
|
+
status: number;
|
|
27
|
+
ok: boolean;
|
|
28
|
+
headers: Record<string, string>;
|
|
29
|
+
body: T;
|
|
30
|
+
json: <R = T>() => R;
|
|
31
|
+
text: () => string;
|
|
32
|
+
raw: () => T;
|
|
33
|
+
}
|
|
34
|
+
export type EventMap = {
|
|
35
|
+
open: void;
|
|
36
|
+
close: {
|
|
37
|
+
code?: number;
|
|
38
|
+
reason?: string;
|
|
39
|
+
};
|
|
40
|
+
error: unknown;
|
|
41
|
+
reconnect: {
|
|
42
|
+
attempt: number;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type Listener<K extends keyof EventMap> = (payload: EventMap[K]) => void;
|
|
46
|
+
type WebSocketLike = {
|
|
47
|
+
readyState: number;
|
|
48
|
+
send(data: string): void;
|
|
49
|
+
close(code?: number, reason?: string): void;
|
|
50
|
+
onopen?: ((event: any) => void) | null;
|
|
51
|
+
onmessage?: ((event: any) => void) | null;
|
|
52
|
+
onclose?: ((event: any) => void) | null;
|
|
53
|
+
onerror?: ((event: any) => void) | null;
|
|
54
|
+
addEventListener?(type: string, listener: (event: any) => void): void;
|
|
55
|
+
removeEventListener?(type: string, listener: (event: any) => void): void;
|
|
56
|
+
on?(type: string, listener: (...args: unknown[]) => void): void;
|
|
57
|
+
off?(type: string, listener: (...args: unknown[]) => void): void;
|
|
58
|
+
};
|
|
59
|
+
export type WebSocketFactory = (url: string) => WebSocketLike;
|
|
60
|
+
interface NormalizedOptions {
|
|
61
|
+
baseUrl: string;
|
|
62
|
+
socketPath: string;
|
|
63
|
+
headers: Record<string, string>;
|
|
64
|
+
timeout: number;
|
|
65
|
+
reconnectInterval: number;
|
|
66
|
+
maxReconnectInterval: number;
|
|
67
|
+
autoConnect: boolean;
|
|
68
|
+
preferSocket: boolean;
|
|
69
|
+
fetchImpl: typeof fetch;
|
|
70
|
+
wsFactory?: WebSocketFactory;
|
|
71
|
+
credentials: RequestCredentials;
|
|
72
|
+
}
|
|
73
|
+
export declare class SockressClient {
|
|
74
|
+
private readonly options;
|
|
75
|
+
private ws?;
|
|
76
|
+
private reconnectAttempts;
|
|
77
|
+
private pending;
|
|
78
|
+
private queue;
|
|
79
|
+
private listeners;
|
|
80
|
+
private socketEnabled;
|
|
81
|
+
constructor(options: NormalizedOptions);
|
|
82
|
+
static create(options: SockressClientOptions): SockressClient;
|
|
83
|
+
on<K extends keyof EventMap>(event: K, listener: Listener<K>): () => void;
|
|
84
|
+
off<K extends keyof EventMap>(event: K, listener: Listener<K>): void;
|
|
85
|
+
emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void;
|
|
86
|
+
connect(): Promise<void>;
|
|
87
|
+
private attachSocketHandlers;
|
|
88
|
+
private flushQueue;
|
|
89
|
+
private scheduleReconnect;
|
|
90
|
+
private handleSocketMessage;
|
|
91
|
+
private applyCookies;
|
|
92
|
+
private clearPending;
|
|
93
|
+
private rejectAllPending;
|
|
94
|
+
private canUseSocket;
|
|
95
|
+
request<T = unknown>(options: SockressClientRequest): Promise<SockressClientResponse<T>>;
|
|
96
|
+
get<T = unknown>(path: string, options?: Omit<SockressClientRequest, 'path' | 'method'>): Promise<SockressClientResponse<T>>;
|
|
97
|
+
post<T = unknown>(path: string, options?: Omit<SockressClientRequest, 'path' | 'method'>): Promise<SockressClientResponse<T>>;
|
|
98
|
+
put<T = unknown>(path: string, options?: Omit<SockressClientRequest, 'path' | 'method'>): Promise<SockressClientResponse<T>>;
|
|
99
|
+
patch<T = unknown>(path: string, options?: Omit<SockressClientRequest, 'path' | 'method'>): Promise<SockressClientResponse<T>>;
|
|
100
|
+
delete<T = unknown>(path: string, options?: Omit<SockressClientRequest, 'path' | 'method'>): Promise<SockressClientResponse<T>>;
|
|
101
|
+
private sendViaSocket;
|
|
102
|
+
private sendViaHttp;
|
|
103
|
+
close(): void;
|
|
104
|
+
}
|
|
105
|
+
export declare function createSockressClient(options: SockressClientOptions): SockressClient;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AA4BnF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IACrF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC,GAAG,OAAO;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC;IACR,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC,CAAC;CACd;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC,CAAC;AAEF,KAAK,QAAQ,CAAC,CAAC,SAAS,MAAM,QAAQ,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAEzE,KAAK,aAAa,GAAG;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,gBAAgB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IACtE,mBAAmB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAChE,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;AAQ9D,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,WAAW,EAAE,kBAAkB,CAAC;CACjC;AAED,qBAAa,cAAc;IAab,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZpC,OAAO,CAAC,EAAE,CAAC,CAAgB;IAC3B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAKf;IACF,OAAO,CAAC,aAAa,CAAQ;gBAEA,OAAO,EAAE,iBAAiB;IAQvD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc;IAI7D,EAAE,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKzE,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAIpE,IAAI,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAM9D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B,OAAO,CAAC,oBAAoB;IA6C5B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAId,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAmB9F,GAAG,CAAC,CAAC,GAAG,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACvD,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAIrC,IAAI,CAAC,CAAC,GAAG,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACvD,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAIrC,GAAG,CAAC,CAAC,GAAG,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACvD,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAIrC,KAAK,CAAC,CAAC,GAAG,OAAO,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACvD,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAIrC,MAAM,CAAC,CAAC,GAAG,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACvD,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;YAIvB,aAAa;YAuCb,WAAW;IAyDzB,KAAK,IAAI,IAAI;CASd;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAEnF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SockressClient = void 0;
|
|
4
|
+
exports.createSockressClient = createSockressClient;
|
|
5
|
+
const nanoid_1 = require("nanoid");
|
|
6
|
+
class SockressClient {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.reconnectAttempts = 0;
|
|
10
|
+
this.pending = new Map();
|
|
11
|
+
this.queue = [];
|
|
12
|
+
this.listeners = {
|
|
13
|
+
open: new Set(),
|
|
14
|
+
close: new Set(),
|
|
15
|
+
error: new Set(),
|
|
16
|
+
reconnect: new Set()
|
|
17
|
+
};
|
|
18
|
+
this.socketEnabled = true;
|
|
19
|
+
if (options.autoConnect) {
|
|
20
|
+
this.connect().catch(() => {
|
|
21
|
+
// Ignore initial connection failures, HTTP fallback will handle requests.
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
static create(options) {
|
|
26
|
+
return new SockressClient(normalizeOptions(options));
|
|
27
|
+
}
|
|
28
|
+
on(event, listener) {
|
|
29
|
+
this.listeners[event].add(listener);
|
|
30
|
+
return () => this.off(event, listener);
|
|
31
|
+
}
|
|
32
|
+
off(event, listener) {
|
|
33
|
+
this.listeners[event].delete(listener);
|
|
34
|
+
}
|
|
35
|
+
emit(event, payload) {
|
|
36
|
+
for (const listener of this.listeners[event]) {
|
|
37
|
+
listener(payload);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async connect() {
|
|
41
|
+
if (!this.options.wsFactory) {
|
|
42
|
+
this.socketEnabled = false;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const socketUrl = buildSocketUrl(this.options.baseUrl, this.options.socketPath);
|
|
49
|
+
this.ws = this.options.wsFactory(socketUrl);
|
|
50
|
+
this.attachSocketHandlers(this.ws);
|
|
51
|
+
}
|
|
52
|
+
attachSocketHandlers(socket) {
|
|
53
|
+
const handleOpen = () => {
|
|
54
|
+
this.reconnectAttempts = 0;
|
|
55
|
+
this.emit('open', undefined);
|
|
56
|
+
this.flushQueue();
|
|
57
|
+
};
|
|
58
|
+
const handleMessage = (event) => {
|
|
59
|
+
const data = resolveEventData(event);
|
|
60
|
+
if (data) {
|
|
61
|
+
this.handleSocketMessage(data);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const handleError = (event) => {
|
|
65
|
+
this.emit('error', event);
|
|
66
|
+
this.rejectAllPending(new Error('Socket error'));
|
|
67
|
+
};
|
|
68
|
+
const handleClose = (details) => {
|
|
69
|
+
this.emit('close', { code: details === null || details === void 0 ? void 0 : details.code, reason: details === null || details === void 0 ? void 0 : details.reason });
|
|
70
|
+
this.rejectAllPending(new Error('Socket closed'));
|
|
71
|
+
this.scheduleReconnect();
|
|
72
|
+
};
|
|
73
|
+
if (typeof socket.addEventListener === 'function') {
|
|
74
|
+
socket.addEventListener('open', () => handleOpen());
|
|
75
|
+
socket.addEventListener('message', (event) => handleMessage(event));
|
|
76
|
+
socket.addEventListener('error', (event) => handleError(event));
|
|
77
|
+
socket.addEventListener('close', (event) => handleClose(extractCloseDetails(event)));
|
|
78
|
+
}
|
|
79
|
+
else if (typeof socket.on === 'function') {
|
|
80
|
+
socket.on('open', handleOpen);
|
|
81
|
+
socket.on('message', (data) => handleMessage({ data }));
|
|
82
|
+
socket.on('error', handleError);
|
|
83
|
+
socket.on('close', (...args) => {
|
|
84
|
+
const [code, reason] = args;
|
|
85
|
+
handleClose({
|
|
86
|
+
code: typeof code === 'number' ? code : undefined,
|
|
87
|
+
reason: typeof reason === 'string' ? reason : typeof reason === 'object' && reason ? `${reason}` : undefined
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
socket.onopen = () => handleOpen();
|
|
93
|
+
socket.onmessage = (event) => handleMessage(event);
|
|
94
|
+
socket.onerror = (event) => handleError(event);
|
|
95
|
+
socket.onclose = (event) => handleClose(extractCloseDetails(event));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
flushQueue() {
|
|
99
|
+
if (!this.ws || this.ws.readyState !== 1)
|
|
100
|
+
return;
|
|
101
|
+
while (this.queue.length) {
|
|
102
|
+
const payload = this.queue.shift();
|
|
103
|
+
if (!payload)
|
|
104
|
+
continue;
|
|
105
|
+
this.ws.send(JSON.stringify(payload));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
scheduleReconnect() {
|
|
109
|
+
if (!this.socketEnabled || !this.options.wsFactory) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.reconnectAttempts += 1;
|
|
113
|
+
const delay = Math.min(this.options.reconnectInterval * this.reconnectAttempts, this.options.maxReconnectInterval);
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
this.emit('reconnect', { attempt: this.reconnectAttempts });
|
|
116
|
+
this.connect().catch(() => {
|
|
117
|
+
// keep trying silently
|
|
118
|
+
});
|
|
119
|
+
}, delay);
|
|
120
|
+
}
|
|
121
|
+
handleSocketMessage(raw) {
|
|
122
|
+
try {
|
|
123
|
+
const payload = JSON.parse(raw);
|
|
124
|
+
if (payload.type === 'error') {
|
|
125
|
+
const pending = payload.id ? this.pending.get(payload.id) : null;
|
|
126
|
+
const error = new Error(payload.message);
|
|
127
|
+
if (pending) {
|
|
128
|
+
this.clearPending(payload.id);
|
|
129
|
+
pending.reject(error);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.emit('error', error);
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const pending = this.pending.get(payload.id);
|
|
137
|
+
if (!pending) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.clearPending(payload.id);
|
|
141
|
+
const response = createClientResponse(payload);
|
|
142
|
+
this.applyCookies(payload.cookies);
|
|
143
|
+
pending.resolve(response);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
this.emit('error', error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
applyCookies(cookies) {
|
|
150
|
+
if (!cookies || typeof document === 'undefined')
|
|
151
|
+
return;
|
|
152
|
+
for (const cookie of cookies) {
|
|
153
|
+
document.cookie = cookie;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
clearPending(id) {
|
|
157
|
+
const pending = this.pending.get(id);
|
|
158
|
+
if (pending === null || pending === void 0 ? void 0 : pending.timeout) {
|
|
159
|
+
clearTimeout(pending.timeout);
|
|
160
|
+
}
|
|
161
|
+
this.pending.delete(id);
|
|
162
|
+
}
|
|
163
|
+
rejectAllPending(error) {
|
|
164
|
+
for (const [id, pending] of this.pending.entries()) {
|
|
165
|
+
this.clearPending(id);
|
|
166
|
+
pending.reject(error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
canUseSocket() {
|
|
170
|
+
return Boolean(this.options.wsFactory && this.socketEnabled && this.ws && this.ws.readyState === 1);
|
|
171
|
+
}
|
|
172
|
+
async request(options) {
|
|
173
|
+
var _a, _b;
|
|
174
|
+
const method = (options.method || 'GET').toUpperCase();
|
|
175
|
+
const path = normalizePath(options.path);
|
|
176
|
+
const headers = { ...this.options.headers, ...((_a = options.headers) !== null && _a !== void 0 ? _a : {}) };
|
|
177
|
+
const query = options.query ? normalizeQuery(options.query) : undefined;
|
|
178
|
+
const timeout = (_b = options.timeout) !== null && _b !== void 0 ? _b : this.options.timeout;
|
|
179
|
+
if (this.options.preferSocket && this.socketEnabled) {
|
|
180
|
+
try {
|
|
181
|
+
return await this.sendViaSocket({ method, path, headers, query, body: options.body, timeout });
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (options.disableHttpFallback) {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return this.sendViaHttp({ method, path, headers, query, body: options.body, signal: options.signal });
|
|
190
|
+
}
|
|
191
|
+
get(path, options) {
|
|
192
|
+
return this.request({ ...(options !== null && options !== void 0 ? options : {}), path, method: 'GET' });
|
|
193
|
+
}
|
|
194
|
+
post(path, options) {
|
|
195
|
+
return this.request({ ...(options !== null && options !== void 0 ? options : {}), path, method: 'POST' });
|
|
196
|
+
}
|
|
197
|
+
put(path, options) {
|
|
198
|
+
return this.request({ ...(options !== null && options !== void 0 ? options : {}), path, method: 'PUT' });
|
|
199
|
+
}
|
|
200
|
+
patch(path, options) {
|
|
201
|
+
return this.request({ ...(options !== null && options !== void 0 ? options : {}), path, method: 'PATCH' });
|
|
202
|
+
}
|
|
203
|
+
delete(path, options) {
|
|
204
|
+
return this.request({ ...(options !== null && options !== void 0 ? options : {}), path, method: 'DELETE' });
|
|
205
|
+
}
|
|
206
|
+
async sendViaSocket(input) {
|
|
207
|
+
if (!this.options.wsFactory) {
|
|
208
|
+
this.socketEnabled = false;
|
|
209
|
+
throw new Error('Socket transport is unavailable');
|
|
210
|
+
}
|
|
211
|
+
await this.connect();
|
|
212
|
+
const id = (0, nanoid_1.nanoid)();
|
|
213
|
+
const payload = {
|
|
214
|
+
type: 'request',
|
|
215
|
+
id,
|
|
216
|
+
method: input.method,
|
|
217
|
+
path: input.path,
|
|
218
|
+
headers: input.headers,
|
|
219
|
+
query: input.query,
|
|
220
|
+
body: input.body
|
|
221
|
+
};
|
|
222
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
223
|
+
const timeout = setTimeout(() => {
|
|
224
|
+
this.clearPending(id);
|
|
225
|
+
reject(new Error('Socket request timed out'));
|
|
226
|
+
}, input.timeout);
|
|
227
|
+
this.pending.set(id, { resolve, reject, timeout });
|
|
228
|
+
});
|
|
229
|
+
const serialized = JSON.stringify(payload);
|
|
230
|
+
if (this.canUseSocket()) {
|
|
231
|
+
this.ws.send(serialized);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.queue.push(payload);
|
|
235
|
+
}
|
|
236
|
+
return responsePromise;
|
|
237
|
+
}
|
|
238
|
+
async sendViaHttp(input) {
|
|
239
|
+
var _a;
|
|
240
|
+
const url = buildHttpUrl(this.options.baseUrl, input.path, input.query);
|
|
241
|
+
const headers = new Headers(input.headers);
|
|
242
|
+
const init = {
|
|
243
|
+
method: input.method,
|
|
244
|
+
headers,
|
|
245
|
+
credentials: this.options.credentials,
|
|
246
|
+
signal: input.signal
|
|
247
|
+
};
|
|
248
|
+
if (input.body !== undefined && input.method !== 'GET' && input.method !== 'HEAD') {
|
|
249
|
+
if (typeof input.body === 'string' ||
|
|
250
|
+
input.body instanceof URLSearchParams ||
|
|
251
|
+
isBlob(input.body) ||
|
|
252
|
+
isFormData(input.body)) {
|
|
253
|
+
init.body = input.body;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
init.body = JSON.stringify(input.body);
|
|
257
|
+
if (!headers.has('content-type')) {
|
|
258
|
+
headers.set('content-type', 'application/json');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const response = await this.options.fetchImpl(url, init);
|
|
263
|
+
const text = await response.text();
|
|
264
|
+
let parsed = text;
|
|
265
|
+
const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : '';
|
|
266
|
+
if (contentType.includes('application/json') && text) {
|
|
267
|
+
try {
|
|
268
|
+
parsed = JSON.parse(text);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
parsed = text;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const headersObj = {};
|
|
275
|
+
response.headers.forEach((value, key) => {
|
|
276
|
+
headersObj[key.toLowerCase()] = value;
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
status: response.status,
|
|
280
|
+
ok: response.ok,
|
|
281
|
+
headers: headersObj,
|
|
282
|
+
body: parsed,
|
|
283
|
+
json: () => parsed,
|
|
284
|
+
text: () => text,
|
|
285
|
+
raw: () => parsed
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
close() {
|
|
289
|
+
this.socketEnabled = false;
|
|
290
|
+
if (this.ws) {
|
|
291
|
+
this.ws.close();
|
|
292
|
+
this.ws = undefined;
|
|
293
|
+
}
|
|
294
|
+
this.queue = [];
|
|
295
|
+
this.rejectAllPending(new Error('Client closed'));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.SockressClient = SockressClient;
|
|
299
|
+
function createSockressClient(options) {
|
|
300
|
+
return SockressClient.create(options);
|
|
301
|
+
}
|
|
302
|
+
function normalizeOptions(options) {
|
|
303
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
304
|
+
if (!options.baseUrl) {
|
|
305
|
+
throw new Error('baseUrl is required');
|
|
306
|
+
}
|
|
307
|
+
const fetchImpl = (_a = options.fetchImpl) !== null && _a !== void 0 ? _a : globalThis.fetch;
|
|
308
|
+
if (!fetchImpl) {
|
|
309
|
+
throw new Error('Fetch implementation is not available in this environment');
|
|
310
|
+
}
|
|
311
|
+
const wsFactory = (_b = options.wsFactory) !== null && _b !== void 0 ? _b : (typeof WebSocket !== 'undefined' ? (url) => new WebSocket(url) : undefined);
|
|
312
|
+
return {
|
|
313
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ''),
|
|
314
|
+
socketPath: (_c = options.socketPath) !== null && _c !== void 0 ? _c : '/sockress',
|
|
315
|
+
headers: { ...((_d = options.headers) !== null && _d !== void 0 ? _d : {}) },
|
|
316
|
+
timeout: (_e = options.timeout) !== null && _e !== void 0 ? _e : 15000,
|
|
317
|
+
reconnectInterval: (_f = options.reconnectInterval) !== null && _f !== void 0 ? _f : 1000,
|
|
318
|
+
maxReconnectInterval: (_g = options.maxReconnectInterval) !== null && _g !== void 0 ? _g : 15000,
|
|
319
|
+
autoConnect: (_h = options.autoConnect) !== null && _h !== void 0 ? _h : true,
|
|
320
|
+
preferSocket: (_j = options.preferSocket) !== null && _j !== void 0 ? _j : true,
|
|
321
|
+
fetchImpl,
|
|
322
|
+
wsFactory,
|
|
323
|
+
credentials: (_k = options.credentials) !== null && _k !== void 0 ? _k : 'include'
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function buildSocketUrl(baseUrl, socketPath) {
|
|
327
|
+
const url = new URL(socketPath, baseUrl);
|
|
328
|
+
url.protocol = url.protocol.replace('http', 'ws');
|
|
329
|
+
return url.toString();
|
|
330
|
+
}
|
|
331
|
+
function buildHttpUrl(baseUrl, path, query) {
|
|
332
|
+
const url = new URL(path, baseUrl);
|
|
333
|
+
if (query) {
|
|
334
|
+
for (const [key, value] of Object.entries(query)) {
|
|
335
|
+
if (Array.isArray(value)) {
|
|
336
|
+
value.forEach((entry) => url.searchParams.append(key, entry));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
url.searchParams.append(key, value);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return url.toString();
|
|
344
|
+
}
|
|
345
|
+
function normalizePath(path) {
|
|
346
|
+
if (!path.startsWith('/')) {
|
|
347
|
+
return `/${path}`;
|
|
348
|
+
}
|
|
349
|
+
return path;
|
|
350
|
+
}
|
|
351
|
+
function normalizeQuery(query) {
|
|
352
|
+
const result = {};
|
|
353
|
+
for (const [key, value] of Object.entries(query)) {
|
|
354
|
+
if (Array.isArray(value)) {
|
|
355
|
+
result[key] = value.map((item) => String(item));
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
result[key] = String(value);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
function createClientResponse(payload) {
|
|
364
|
+
var _a;
|
|
365
|
+
const headers = (_a = payload.headers) !== null && _a !== void 0 ? _a : {};
|
|
366
|
+
const status = payload.status;
|
|
367
|
+
const body = payload.body;
|
|
368
|
+
return {
|
|
369
|
+
status,
|
|
370
|
+
ok: status >= 200 && status < 300,
|
|
371
|
+
headers,
|
|
372
|
+
body,
|
|
373
|
+
json: () => body,
|
|
374
|
+
text: () => (typeof body === 'string' ? body : JSON.stringify(body)),
|
|
375
|
+
raw: () => body
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function extractCloseDetails(event) {
|
|
379
|
+
if (!event || typeof event !== 'object')
|
|
380
|
+
return {};
|
|
381
|
+
const closeEvent = event;
|
|
382
|
+
return {
|
|
383
|
+
code: closeEvent.code,
|
|
384
|
+
reason: closeEvent.reason
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function resolveEventData(event) {
|
|
388
|
+
if (!event)
|
|
389
|
+
return '';
|
|
390
|
+
if (typeof event === 'string')
|
|
391
|
+
return event;
|
|
392
|
+
if (typeof event === 'object' && 'data' in event) {
|
|
393
|
+
return toText(event.data);
|
|
394
|
+
}
|
|
395
|
+
return toText(event);
|
|
396
|
+
}
|
|
397
|
+
const textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
398
|
+
function toText(value) {
|
|
399
|
+
if (typeof value === 'string') {
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
if (value instanceof ArrayBuffer) {
|
|
403
|
+
return textDecoder ? textDecoder.decode(value) : '';
|
|
404
|
+
}
|
|
405
|
+
if (ArrayBuffer.isView(value)) {
|
|
406
|
+
const view = value;
|
|
407
|
+
if (textDecoder) {
|
|
408
|
+
return textDecoder.decode(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (value && typeof value.toString === 'function') {
|
|
412
|
+
return value.toString();
|
|
413
|
+
}
|
|
414
|
+
return '';
|
|
415
|
+
}
|
|
416
|
+
function isBlob(value) {
|
|
417
|
+
return typeof Blob !== 'undefined' && value instanceof Blob;
|
|
418
|
+
}
|
|
419
|
+
function isFormData(value) {
|
|
420
|
+
return typeof FormData !== 'undefined' && value instanceof FormData;
|
|
421
|
+
}
|
|
422
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sockress-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Client SDK for Sockress with socket-first transport and HTTP fallback",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"nanoid": "^5.0.7"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.14.10"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
2
|
+
|
|
3
|
+
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
4
|
+
|
|
5
|
+
interface SocketRequestPayload {
|
|
6
|
+
type: 'request';
|
|
7
|
+
id: string;
|
|
8
|
+
method: HTTPMethod;
|
|
9
|
+
path: string;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
query?: Record<string, string | string[]>;
|
|
12
|
+
body?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SocketResponsePayload {
|
|
16
|
+
type: 'response';
|
|
17
|
+
id: string;
|
|
18
|
+
status: number;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
cookies?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface SocketErrorPayload {
|
|
25
|
+
type: 'error';
|
|
26
|
+
id?: string;
|
|
27
|
+
message: string;
|
|
28
|
+
code?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SockressClientOptions {
|
|
32
|
+
baseUrl: string;
|
|
33
|
+
socketPath?: string;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
reconnectInterval?: number;
|
|
37
|
+
maxReconnectInterval?: number;
|
|
38
|
+
autoConnect?: boolean;
|
|
39
|
+
preferSocket?: boolean;
|
|
40
|
+
fetchImpl?: typeof fetch;
|
|
41
|
+
wsFactory?: WebSocketFactory;
|
|
42
|
+
credentials?: RequestCredentials;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SockressClientRequest {
|
|
46
|
+
path: string;
|
|
47
|
+
method?: HTTPMethod;
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
query?: Record<string, string | number | boolean | Array<string | number | boolean>>;
|
|
50
|
+
body?: unknown;
|
|
51
|
+
timeout?: number;
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
disableHttpFallback?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface SockressClientResponse<T = unknown> {
|
|
57
|
+
status: number;
|
|
58
|
+
ok: boolean;
|
|
59
|
+
headers: Record<string, string>;
|
|
60
|
+
body: T;
|
|
61
|
+
json: <R = T>() => R;
|
|
62
|
+
text: () => string;
|
|
63
|
+
raw: () => T;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type EventMap = {
|
|
67
|
+
open: void;
|
|
68
|
+
close: { code?: number; reason?: string };
|
|
69
|
+
error: unknown;
|
|
70
|
+
reconnect: { attempt: number };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type Listener<K extends keyof EventMap> = (payload: EventMap[K]) => void;
|
|
74
|
+
|
|
75
|
+
type WebSocketLike = {
|
|
76
|
+
readyState: number;
|
|
77
|
+
send(data: string): void;
|
|
78
|
+
close(code?: number, reason?: string): void;
|
|
79
|
+
onopen?: ((event: any) => void) | null;
|
|
80
|
+
onmessage?: ((event: any) => void) | null;
|
|
81
|
+
onclose?: ((event: any) => void) | null;
|
|
82
|
+
onerror?: ((event: any) => void) | null;
|
|
83
|
+
addEventListener?(type: string, listener: (event: any) => void): void;
|
|
84
|
+
removeEventListener?(type: string, listener: (event: any) => void): void;
|
|
85
|
+
on?(type: string, listener: (...args: unknown[]) => void): void;
|
|
86
|
+
off?(type: string, listener: (...args: unknown[]) => void): void;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type WebSocketFactory = (url: string) => WebSocketLike;
|
|
90
|
+
|
|
91
|
+
interface PendingRequest {
|
|
92
|
+
resolve: (value: SockressClientResponse<any>) => void;
|
|
93
|
+
reject: (reason?: unknown) => void;
|
|
94
|
+
timeout?: ReturnType<typeof setTimeout>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface NormalizedOptions {
|
|
98
|
+
baseUrl: string;
|
|
99
|
+
socketPath: string;
|
|
100
|
+
headers: Record<string, string>;
|
|
101
|
+
timeout: number;
|
|
102
|
+
reconnectInterval: number;
|
|
103
|
+
maxReconnectInterval: number;
|
|
104
|
+
autoConnect: boolean;
|
|
105
|
+
preferSocket: boolean;
|
|
106
|
+
fetchImpl: typeof fetch;
|
|
107
|
+
wsFactory?: WebSocketFactory;
|
|
108
|
+
credentials: RequestCredentials;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class SockressClient {
|
|
112
|
+
private ws?: WebSocketLike;
|
|
113
|
+
private reconnectAttempts = 0;
|
|
114
|
+
private pending = new Map<string, PendingRequest>();
|
|
115
|
+
private queue: SocketRequestPayload[] = [];
|
|
116
|
+
private listeners: { [K in keyof EventMap]: Set<Listener<K>> } = {
|
|
117
|
+
open: new Set(),
|
|
118
|
+
close: new Set(),
|
|
119
|
+
error: new Set(),
|
|
120
|
+
reconnect: new Set()
|
|
121
|
+
};
|
|
122
|
+
private socketEnabled = true;
|
|
123
|
+
|
|
124
|
+
constructor(private readonly options: NormalizedOptions) {
|
|
125
|
+
if (options.autoConnect) {
|
|
126
|
+
this.connect().catch(() => {
|
|
127
|
+
// Ignore initial connection failures, HTTP fallback will handle requests.
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static create(options: SockressClientOptions): SockressClient {
|
|
133
|
+
return new SockressClient(normalizeOptions(options));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
on<K extends keyof EventMap>(event: K, listener: Listener<K>): () => void {
|
|
137
|
+
this.listeners[event].add(listener as Listener<K>);
|
|
138
|
+
return () => this.off(event, listener);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
off<K extends keyof EventMap>(event: K, listener: Listener<K>): void {
|
|
142
|
+
this.listeners[event].delete(listener as Listener<K>);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {
|
|
146
|
+
for (const listener of this.listeners[event]) {
|
|
147
|
+
listener(payload);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async connect(): Promise<void> {
|
|
152
|
+
if (!this.options.wsFactory) {
|
|
153
|
+
this.socketEnabled = false;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const socketUrl = buildSocketUrl(this.options.baseUrl, this.options.socketPath);
|
|
160
|
+
this.ws = this.options.wsFactory(socketUrl);
|
|
161
|
+
this.attachSocketHandlers(this.ws);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private attachSocketHandlers(socket: WebSocketLike): void {
|
|
165
|
+
const handleOpen = () => {
|
|
166
|
+
this.reconnectAttempts = 0;
|
|
167
|
+
this.emit('open', undefined);
|
|
168
|
+
this.flushQueue();
|
|
169
|
+
};
|
|
170
|
+
const handleMessage = (event: unknown) => {
|
|
171
|
+
const data = resolveEventData(event);
|
|
172
|
+
if (data) {
|
|
173
|
+
this.handleSocketMessage(data);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const handleError = (event: unknown) => {
|
|
177
|
+
this.emit('error', event);
|
|
178
|
+
this.rejectAllPending(new Error('Socket error'));
|
|
179
|
+
};
|
|
180
|
+
const handleClose = (details?: { code?: number; reason?: string }) => {
|
|
181
|
+
this.emit('close', { code: details?.code, reason: details?.reason });
|
|
182
|
+
this.rejectAllPending(new Error('Socket closed'));
|
|
183
|
+
this.scheduleReconnect();
|
|
184
|
+
};
|
|
185
|
+
if (typeof socket.addEventListener === 'function') {
|
|
186
|
+
socket.addEventListener('open', () => handleOpen());
|
|
187
|
+
socket.addEventListener('message', (event) => handleMessage(event));
|
|
188
|
+
socket.addEventListener('error', (event) => handleError(event));
|
|
189
|
+
socket.addEventListener('close', (event) => handleClose(extractCloseDetails(event)));
|
|
190
|
+
} else if (typeof socket.on === 'function') {
|
|
191
|
+
socket.on('open', handleOpen);
|
|
192
|
+
socket.on('message', (data: unknown) => handleMessage({ data }));
|
|
193
|
+
socket.on('error', handleError);
|
|
194
|
+
socket.on('close', (...args: unknown[]) => {
|
|
195
|
+
const [code, reason] = args;
|
|
196
|
+
handleClose({
|
|
197
|
+
code: typeof code === 'number' ? code : undefined,
|
|
198
|
+
reason: typeof reason === 'string' ? reason : typeof reason === 'object' && reason ? `${reason}` : undefined
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
socket.onopen = () => handleOpen();
|
|
203
|
+
socket.onmessage = (event) => handleMessage(event);
|
|
204
|
+
socket.onerror = (event) => handleError(event);
|
|
205
|
+
socket.onclose = (event) => handleClose(extractCloseDetails(event));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private flushQueue(): void {
|
|
210
|
+
if (!this.ws || this.ws.readyState !== 1) return;
|
|
211
|
+
while (this.queue.length) {
|
|
212
|
+
const payload = this.queue.shift();
|
|
213
|
+
if (!payload) continue;
|
|
214
|
+
this.ws.send(JSON.stringify(payload));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private scheduleReconnect(): void {
|
|
219
|
+
if (!this.socketEnabled || !this.options.wsFactory) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this.reconnectAttempts += 1;
|
|
223
|
+
const delay = Math.min(
|
|
224
|
+
this.options.reconnectInterval * this.reconnectAttempts,
|
|
225
|
+
this.options.maxReconnectInterval
|
|
226
|
+
);
|
|
227
|
+
setTimeout(() => {
|
|
228
|
+
this.emit('reconnect', { attempt: this.reconnectAttempts });
|
|
229
|
+
this.connect().catch(() => {
|
|
230
|
+
// keep trying silently
|
|
231
|
+
});
|
|
232
|
+
}, delay);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private handleSocketMessage(raw: string): void {
|
|
236
|
+
try {
|
|
237
|
+
const payload = JSON.parse(raw) as SocketResponsePayload | SocketErrorPayload;
|
|
238
|
+
if (payload.type === 'error') {
|
|
239
|
+
const pending = payload.id ? this.pending.get(payload.id) : null;
|
|
240
|
+
const error = new Error(payload.message);
|
|
241
|
+
if (pending) {
|
|
242
|
+
this.clearPending(payload.id!);
|
|
243
|
+
pending.reject(error);
|
|
244
|
+
} else {
|
|
245
|
+
this.emit('error', error);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const pending = this.pending.get(payload.id);
|
|
250
|
+
if (!pending) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
this.clearPending(payload.id);
|
|
254
|
+
const response = createClientResponse(payload);
|
|
255
|
+
this.applyCookies(payload.cookies);
|
|
256
|
+
pending.resolve(response);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
this.emit('error', error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private applyCookies(cookies?: string[]): void {
|
|
263
|
+
if (!cookies || typeof document === 'undefined') return;
|
|
264
|
+
for (const cookie of cookies) {
|
|
265
|
+
document.cookie = cookie;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private clearPending(id: string): void {
|
|
270
|
+
const pending = this.pending.get(id);
|
|
271
|
+
if (pending?.timeout) {
|
|
272
|
+
clearTimeout(pending.timeout);
|
|
273
|
+
}
|
|
274
|
+
this.pending.delete(id);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private rejectAllPending(error: Error): void {
|
|
278
|
+
for (const [id, pending] of this.pending.entries()) {
|
|
279
|
+
this.clearPending(id);
|
|
280
|
+
pending.reject(error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private canUseSocket(): boolean {
|
|
285
|
+
return Boolean(this.options.wsFactory && this.socketEnabled && this.ws && this.ws.readyState === 1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async request<T = unknown>(options: SockressClientRequest): Promise<SockressClientResponse<T>> {
|
|
289
|
+
const method = (options.method || 'GET').toUpperCase() as HTTPMethod;
|
|
290
|
+
const path = normalizePath(options.path);
|
|
291
|
+
const headers = { ...this.options.headers, ...(options.headers ?? {}) };
|
|
292
|
+
const query = options.query ? normalizeQuery(options.query) : undefined;
|
|
293
|
+
const timeout = options.timeout ?? this.options.timeout;
|
|
294
|
+
|
|
295
|
+
if (this.options.preferSocket && this.socketEnabled) {
|
|
296
|
+
try {
|
|
297
|
+
return await this.sendViaSocket<T>({ method, path, headers, query, body: options.body, timeout });
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (options.disableHttpFallback) {
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return this.sendViaHttp<T>({ method, path, headers, query, body: options.body, signal: options.signal });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
get<T = unknown>(
|
|
308
|
+
path: string,
|
|
309
|
+
options?: Omit<SockressClientRequest, 'path' | 'method'>
|
|
310
|
+
): Promise<SockressClientResponse<T>> {
|
|
311
|
+
return this.request<T>({ ...(options ?? {}), path, method: 'GET' });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
post<T = unknown>(
|
|
315
|
+
path: string,
|
|
316
|
+
options?: Omit<SockressClientRequest, 'path' | 'method'>
|
|
317
|
+
): Promise<SockressClientResponse<T>> {
|
|
318
|
+
return this.request<T>({ ...(options ?? {}), path, method: 'POST' });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
put<T = unknown>(
|
|
322
|
+
path: string,
|
|
323
|
+
options?: Omit<SockressClientRequest, 'path' | 'method'>
|
|
324
|
+
): Promise<SockressClientResponse<T>> {
|
|
325
|
+
return this.request<T>({ ...(options ?? {}), path, method: 'PUT' });
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
patch<T = unknown>(
|
|
329
|
+
path: string,
|
|
330
|
+
options?: Omit<SockressClientRequest, 'path' | 'method'>
|
|
331
|
+
): Promise<SockressClientResponse<T>> {
|
|
332
|
+
return this.request<T>({ ...(options ?? {}), path, method: 'PATCH' });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
delete<T = unknown>(
|
|
336
|
+
path: string,
|
|
337
|
+
options?: Omit<SockressClientRequest, 'path' | 'method'>
|
|
338
|
+
): Promise<SockressClientResponse<T>> {
|
|
339
|
+
return this.request<T>({ ...(options ?? {}), path, method: 'DELETE' });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private async sendViaSocket<T>(input: {
|
|
343
|
+
method: HTTPMethod;
|
|
344
|
+
path: string;
|
|
345
|
+
headers: Record<string, string>;
|
|
346
|
+
query?: Record<string, string | string[]>;
|
|
347
|
+
body?: unknown;
|
|
348
|
+
timeout: number;
|
|
349
|
+
}): Promise<SockressClientResponse<T>> {
|
|
350
|
+
if (!this.options.wsFactory) {
|
|
351
|
+
this.socketEnabled = false;
|
|
352
|
+
throw new Error('Socket transport is unavailable');
|
|
353
|
+
}
|
|
354
|
+
await this.connect();
|
|
355
|
+
const id = nanoid();
|
|
356
|
+
const payload: SocketRequestPayload = {
|
|
357
|
+
type: 'request',
|
|
358
|
+
id,
|
|
359
|
+
method: input.method,
|
|
360
|
+
path: input.path,
|
|
361
|
+
headers: input.headers,
|
|
362
|
+
query: input.query,
|
|
363
|
+
body: input.body
|
|
364
|
+
};
|
|
365
|
+
const responsePromise = new Promise<SockressClientResponse<T>>((resolve, reject) => {
|
|
366
|
+
const timeout = setTimeout(() => {
|
|
367
|
+
this.clearPending(id);
|
|
368
|
+
reject(new Error('Socket request timed out'));
|
|
369
|
+
}, input.timeout);
|
|
370
|
+
this.pending.set(id, { resolve, reject, timeout });
|
|
371
|
+
});
|
|
372
|
+
const serialized = JSON.stringify(payload);
|
|
373
|
+
if (this.canUseSocket()) {
|
|
374
|
+
this.ws!.send(serialized);
|
|
375
|
+
} else {
|
|
376
|
+
this.queue.push(payload);
|
|
377
|
+
}
|
|
378
|
+
return responsePromise;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private async sendViaHttp<T>(input: {
|
|
382
|
+
method: HTTPMethod;
|
|
383
|
+
path: string;
|
|
384
|
+
headers: Record<string, string>;
|
|
385
|
+
query?: Record<string, string | string[]>;
|
|
386
|
+
body?: unknown;
|
|
387
|
+
signal?: AbortSignal;
|
|
388
|
+
}): Promise<SockressClientResponse<T>> {
|
|
389
|
+
const url = buildHttpUrl(this.options.baseUrl, input.path, input.query);
|
|
390
|
+
const headers = new Headers(input.headers);
|
|
391
|
+
const init: RequestInit = {
|
|
392
|
+
method: input.method,
|
|
393
|
+
headers,
|
|
394
|
+
credentials: this.options.credentials,
|
|
395
|
+
signal: input.signal
|
|
396
|
+
};
|
|
397
|
+
if (input.body !== undefined && input.method !== 'GET' && input.method !== 'HEAD') {
|
|
398
|
+
if (
|
|
399
|
+
typeof input.body === 'string' ||
|
|
400
|
+
input.body instanceof URLSearchParams ||
|
|
401
|
+
isBlob(input.body) ||
|
|
402
|
+
isFormData(input.body)
|
|
403
|
+
) {
|
|
404
|
+
init.body = input.body as BodyInit;
|
|
405
|
+
} else {
|
|
406
|
+
init.body = JSON.stringify(input.body);
|
|
407
|
+
if (!headers.has('content-type')) {
|
|
408
|
+
headers.set('content-type', 'application/json');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const response = await this.options.fetchImpl(url, init);
|
|
413
|
+
const text = await response.text();
|
|
414
|
+
let parsed: unknown = text;
|
|
415
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
416
|
+
if (contentType.includes('application/json') && text) {
|
|
417
|
+
try {
|
|
418
|
+
parsed = JSON.parse(text);
|
|
419
|
+
} catch {
|
|
420
|
+
parsed = text;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const headersObj: Record<string, string> = {};
|
|
424
|
+
response.headers.forEach((value, key) => {
|
|
425
|
+
headersObj[key.toLowerCase()] = value;
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
status: response.status,
|
|
429
|
+
ok: response.ok,
|
|
430
|
+
headers: headersObj,
|
|
431
|
+
body: parsed as T,
|
|
432
|
+
json: <R>() => parsed as R,
|
|
433
|
+
text: () => text,
|
|
434
|
+
raw: () => parsed as T
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
close(): void {
|
|
439
|
+
this.socketEnabled = false;
|
|
440
|
+
if (this.ws) {
|
|
441
|
+
this.ws.close();
|
|
442
|
+
this.ws = undefined;
|
|
443
|
+
}
|
|
444
|
+
this.queue = [];
|
|
445
|
+
this.rejectAllPending(new Error('Client closed'));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function createSockressClient(options: SockressClientOptions): SockressClient {
|
|
450
|
+
return SockressClient.create(options);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function normalizeOptions(options: SockressClientOptions): NormalizedOptions {
|
|
454
|
+
if (!options.baseUrl) {
|
|
455
|
+
throw new Error('baseUrl is required');
|
|
456
|
+
}
|
|
457
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
458
|
+
if (!fetchImpl) {
|
|
459
|
+
throw new Error('Fetch implementation is not available in this environment');
|
|
460
|
+
}
|
|
461
|
+
const wsFactory =
|
|
462
|
+
options.wsFactory ??
|
|
463
|
+
(typeof WebSocket !== 'undefined' ? (url: string) => new WebSocket(url) as WebSocketLike : undefined);
|
|
464
|
+
return {
|
|
465
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ''),
|
|
466
|
+
socketPath: options.socketPath ?? '/sockress',
|
|
467
|
+
headers: { ...(options.headers ?? {}) },
|
|
468
|
+
timeout: options.timeout ?? 15_000,
|
|
469
|
+
reconnectInterval: options.reconnectInterval ?? 1_000,
|
|
470
|
+
maxReconnectInterval: options.maxReconnectInterval ?? 15_000,
|
|
471
|
+
autoConnect: options.autoConnect ?? true,
|
|
472
|
+
preferSocket: options.preferSocket ?? true,
|
|
473
|
+
fetchImpl,
|
|
474
|
+
wsFactory,
|
|
475
|
+
credentials: options.credentials ?? 'include'
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function buildSocketUrl(baseUrl: string, socketPath: string): string {
|
|
480
|
+
const url = new URL(socketPath, baseUrl);
|
|
481
|
+
url.protocol = url.protocol.replace('http', 'ws');
|
|
482
|
+
return url.toString();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function buildHttpUrl(
|
|
486
|
+
baseUrl: string,
|
|
487
|
+
path: string,
|
|
488
|
+
query?: Record<string, string | string[]>
|
|
489
|
+
): string {
|
|
490
|
+
const url = new URL(path, baseUrl);
|
|
491
|
+
if (query) {
|
|
492
|
+
for (const [key, value] of Object.entries(query)) {
|
|
493
|
+
if (Array.isArray(value)) {
|
|
494
|
+
value.forEach((entry) => url.searchParams.append(key, entry));
|
|
495
|
+
} else {
|
|
496
|
+
url.searchParams.append(key, value);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return url.toString();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function normalizePath(path: string): string {
|
|
504
|
+
if (!path.startsWith('/')) {
|
|
505
|
+
return `/${path}`;
|
|
506
|
+
}
|
|
507
|
+
return path;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function normalizeQuery(
|
|
511
|
+
query: Record<string, string | number | boolean | Array<string | number | boolean>>
|
|
512
|
+
): Record<string, string | string[]> {
|
|
513
|
+
const result: Record<string, string | string[]> = {};
|
|
514
|
+
for (const [key, value] of Object.entries(query)) {
|
|
515
|
+
if (Array.isArray(value)) {
|
|
516
|
+
result[key] = value.map((item) => String(item));
|
|
517
|
+
} else {
|
|
518
|
+
result[key] = String(value);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function createClientResponse<T = unknown>(payload: SocketResponsePayload): SockressClientResponse<T> {
|
|
525
|
+
const headers = payload.headers ?? {};
|
|
526
|
+
const status = payload.status;
|
|
527
|
+
const body = payload.body as T;
|
|
528
|
+
return {
|
|
529
|
+
status,
|
|
530
|
+
ok: status >= 200 && status < 300,
|
|
531
|
+
headers,
|
|
532
|
+
body,
|
|
533
|
+
json: <R = T>() => body as unknown as R,
|
|
534
|
+
text: () => (typeof body === 'string' ? body : JSON.stringify(body)),
|
|
535
|
+
raw: () => body
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function extractCloseDetails(event: unknown): { code?: number; reason?: string } {
|
|
540
|
+
if (!event || typeof event !== 'object') return {};
|
|
541
|
+
const closeEvent = event as Partial<CloseEvent>;
|
|
542
|
+
return {
|
|
543
|
+
code: closeEvent.code,
|
|
544
|
+
reason: closeEvent.reason
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function resolveEventData(event: unknown): string {
|
|
549
|
+
if (!event) return '';
|
|
550
|
+
if (typeof event === 'string') return event;
|
|
551
|
+
if (typeof event === 'object' && 'data' in (event as Record<string, unknown>)) {
|
|
552
|
+
return toText((event as { data: unknown }).data);
|
|
553
|
+
}
|
|
554
|
+
return toText(event);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
558
|
+
|
|
559
|
+
function toText(value: unknown): string {
|
|
560
|
+
if (typeof value === 'string') {
|
|
561
|
+
return value;
|
|
562
|
+
}
|
|
563
|
+
if (value instanceof ArrayBuffer) {
|
|
564
|
+
return textDecoder ? textDecoder.decode(value) : '';
|
|
565
|
+
}
|
|
566
|
+
if (ArrayBuffer.isView(value)) {
|
|
567
|
+
const view = value as ArrayBufferView;
|
|
568
|
+
if (textDecoder) {
|
|
569
|
+
return textDecoder.decode(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (value && typeof (value as { toString: () => string }).toString === 'function') {
|
|
573
|
+
return (value as { toString: () => string }).toString();
|
|
574
|
+
}
|
|
575
|
+
return '';
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function isBlob(value: unknown): value is Blob {
|
|
579
|
+
return typeof Blob !== 'undefined' && value instanceof Blob;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function isFormData(value: unknown): value is FormData {
|
|
583
|
+
return typeof FormData !== 'undefined' && value instanceof FormData;
|
|
584
|
+
}
|
|
585
|
+
|