@proj-airi/server-sdk 0.6.0 → 0.6.1
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.mts +31 -29
- package/dist/index.mjs +145 -161
- package/dist/index.mjs.map +1 -0
- package/dist/utils/node/index.d.mts +3 -1
- package/dist/utils/node/index.mjs +12 -10
- package/dist/utils/node/index.mjs.map +1 -0
- package/package.json +11 -11
- package/dist/index.cjs +0 -173
- package/dist/index.d.cts +0 -34
- package/dist/index.d.ts +0 -34
- package/dist/utils/node/index.cjs +0 -30
- package/dist/utils/node/index.d.cts +0 -3
- package/dist/utils/node/index.d.ts +0 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WebSocketBaseEvent, WebSocketEvent, WebSocketEvents } from "@proj-airi/server-shared/types";
|
|
2
2
|
|
|
3
|
+
//#region src/client.d.ts
|
|
3
4
|
interface ClientOptions<C = undefined> {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
url?: string;
|
|
6
|
+
name: string;
|
|
7
|
+
possibleEvents?: Array<(keyof WebSocketEvents<C>)>;
|
|
8
|
+
token?: string;
|
|
9
|
+
onError?: (error: unknown) => void;
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
autoConnect?: boolean;
|
|
12
|
+
autoReconnect?: boolean;
|
|
12
13
|
}
|
|
13
14
|
declare class Client<C = undefined> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
private connected;
|
|
16
|
+
private opts;
|
|
17
|
+
private websocket;
|
|
18
|
+
private eventListeners;
|
|
19
|
+
private reconnectAttempts;
|
|
20
|
+
private shouldClose;
|
|
21
|
+
constructor(options: ClientOptions<C>);
|
|
22
|
+
retryWithExponentialBackoff(fn: () => void | Promise<void>, attempts?: number, maxAttempts?: number): Promise<void>;
|
|
23
|
+
tryReconnectWithExponentialBackoff(): Promise<void>;
|
|
24
|
+
private _connect;
|
|
25
|
+
connect(): Promise<void>;
|
|
26
|
+
private tryAnnounce;
|
|
27
|
+
private tryAuthenticate;
|
|
28
|
+
private handleMessage;
|
|
29
|
+
onEvent<E extends keyof WebSocketEvents<C>>(event: E, callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>): void;
|
|
30
|
+
send(data: WebSocketEvent<C>): void;
|
|
31
|
+
sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void;
|
|
32
|
+
close(): void;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
export { Client,
|
|
34
|
+
//#endregion
|
|
35
|
+
export { Client, ClientOptions };
|
|
36
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,167 +1,151 @@
|
|
|
1
|
-
import WebSocket from
|
|
2
|
-
import { defu } from
|
|
1
|
+
import WebSocket from "crossws/websocket";
|
|
2
|
+
import { defu } from "defu";
|
|
3
3
|
|
|
4
|
+
//#region src/utils/concurrency.ts
|
|
4
5
|
function sleep(ms) {
|
|
5
|
-
|
|
6
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
listeners.push(callback);
|
|
150
|
-
this.eventListeners.set(event, listeners);
|
|
151
|
-
}
|
|
152
|
-
send(data) {
|
|
153
|
-
this.websocket?.send(JSON.stringify(data));
|
|
154
|
-
}
|
|
155
|
-
sendRaw(data) {
|
|
156
|
-
this.websocket?.send(data);
|
|
157
|
-
}
|
|
158
|
-
close() {
|
|
159
|
-
this.shouldClose = true;
|
|
160
|
-
if (this.connected && this.websocket) {
|
|
161
|
-
this.websocket.close();
|
|
162
|
-
this.connected = false;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/client.ts
|
|
11
|
+
var Client = class {
|
|
12
|
+
connected = false;
|
|
13
|
+
opts;
|
|
14
|
+
websocket;
|
|
15
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
16
|
+
reconnectAttempts = 0;
|
|
17
|
+
shouldClose = false;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.opts = defu(options, {
|
|
20
|
+
url: "ws://localhost:6121/ws",
|
|
21
|
+
possibleEvents: [],
|
|
22
|
+
onError: () => {},
|
|
23
|
+
onClose: () => {},
|
|
24
|
+
autoConnect: true,
|
|
25
|
+
autoReconnect: true
|
|
26
|
+
});
|
|
27
|
+
if (this.opts.autoConnect) try {
|
|
28
|
+
this.connect();
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(err);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async retryWithExponentialBackoff(fn, attempts = 0, maxAttempts = -1) {
|
|
34
|
+
if (maxAttempts !== -1 && attempts >= maxAttempts) {
|
|
35
|
+
console.error(`Maximum retry attempts (${maxAttempts}) reached`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await fn();
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error("Encountered an error when retrying", err);
|
|
42
|
+
await sleep(2 ** attempts * 1e3);
|
|
43
|
+
await this.retryWithExponentialBackoff(fn, attempts++, maxAttempts);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async tryReconnectWithExponentialBackoff() {
|
|
47
|
+
await this.retryWithExponentialBackoff(() => this._connect(), this.reconnectAttempts);
|
|
48
|
+
}
|
|
49
|
+
_connect() {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
if (this.shouldClose) {
|
|
52
|
+
resolve();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.connected) {
|
|
56
|
+
resolve();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.websocket = new WebSocket(this.opts.url);
|
|
60
|
+
this.onEvent("module:authenticated", async (event) => {
|
|
61
|
+
const auth = event.data.authenticated;
|
|
62
|
+
if (!auth) this.retryWithExponentialBackoff(() => this.tryAuthenticate());
|
|
63
|
+
else this.tryAnnounce();
|
|
64
|
+
});
|
|
65
|
+
this.websocket.onerror = (event) => {
|
|
66
|
+
this.opts.onError?.(event);
|
|
67
|
+
if ("error" in event && event.error instanceof Error) {
|
|
68
|
+
if (event.error.message === "Received network error or non-101 status code.") {
|
|
69
|
+
this.connected = false;
|
|
70
|
+
if (!this.opts.autoReconnect) {
|
|
71
|
+
this.opts.onError?.(event);
|
|
72
|
+
this.opts.onClose?.();
|
|
73
|
+
reject(event.error);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
reject(event.error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
this.websocket.onclose = () => {
|
|
81
|
+
this.opts.onClose?.();
|
|
82
|
+
this.connected = false;
|
|
83
|
+
if (!this.opts.autoReconnect) this.opts.onClose?.();
|
|
84
|
+
else this.tryReconnectWithExponentialBackoff();
|
|
85
|
+
};
|
|
86
|
+
this.websocket.onmessage = (event) => {
|
|
87
|
+
this.handleMessage(event);
|
|
88
|
+
};
|
|
89
|
+
this.websocket.onopen = () => {
|
|
90
|
+
this.reconnectAttempts = 0;
|
|
91
|
+
if (this.opts.token) this.tryAuthenticate();
|
|
92
|
+
else this.tryAnnounce();
|
|
93
|
+
this.connected = true;
|
|
94
|
+
resolve();
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async connect() {
|
|
99
|
+
await this.tryReconnectWithExponentialBackoff();
|
|
100
|
+
}
|
|
101
|
+
tryAnnounce() {
|
|
102
|
+
this.send({
|
|
103
|
+
type: "module:announce",
|
|
104
|
+
data: {
|
|
105
|
+
name: this.opts.name,
|
|
106
|
+
possibleEvents: this.opts.possibleEvents
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
tryAuthenticate() {
|
|
111
|
+
if (this.opts.token) this.send({
|
|
112
|
+
type: "module:authenticate",
|
|
113
|
+
data: { token: this.opts.token || "" }
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async handleMessage(event) {
|
|
117
|
+
try {
|
|
118
|
+
const data = JSON.parse(event.data);
|
|
119
|
+
const listeners = this.eventListeners.get(data.type);
|
|
120
|
+
if (!listeners) return;
|
|
121
|
+
for (const listener of listeners) await listener(data);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.error("Failed to parse message:", err);
|
|
124
|
+
this.opts.onError?.(err);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
onEvent(event, callback) {
|
|
128
|
+
if (!this.eventListeners.get(event)) this.eventListeners.set(event, []);
|
|
129
|
+
const listeners = this.eventListeners.get(event);
|
|
130
|
+
if (!listeners) return;
|
|
131
|
+
listeners.push(callback);
|
|
132
|
+
this.eventListeners.set(event, listeners);
|
|
133
|
+
}
|
|
134
|
+
send(data) {
|
|
135
|
+
this.websocket?.send(JSON.stringify(data));
|
|
136
|
+
}
|
|
137
|
+
sendRaw(data) {
|
|
138
|
+
this.websocket?.send(data);
|
|
139
|
+
}
|
|
140
|
+
close() {
|
|
141
|
+
this.shouldClose = true;
|
|
142
|
+
if (this.connected && this.websocket) {
|
|
143
|
+
this.websocket.close();
|
|
144
|
+
this.connected = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
166
148
|
|
|
149
|
+
//#endregion
|
|
167
150
|
export { Client };
|
|
151
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["ms: number","options: ClientOptions<C>","fn: () => void | Promise<void>","event: any","event: E","callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>","data: WebSocketEvent<C>","data: string | ArrayBufferLike | ArrayBufferView"],"sources":["../src/utils/concurrency.ts","../src/client.ts"],"sourcesContent":["export function sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n","import type { WebSocketBaseEvent, WebSocketEvent, WebSocketEvents } from '@proj-airi/server-shared/types'\n\nimport WebSocket from 'crossws/websocket'\nimport { defu } from 'defu'\n\nimport { sleep } from './utils'\n\nexport interface ClientOptions<C = undefined> {\n url?: string\n name: string\n possibleEvents?: Array<(keyof WebSocketEvents<C>)>\n token?: string\n onError?: (error: unknown) => void\n onClose?: () => void\n autoConnect?: boolean\n autoReconnect?: boolean\n}\n\nexport class Client<C = undefined> {\n private connected = false\n private opts: Required<ClientOptions<C>>\n private websocket: WebSocket | undefined\n private eventListeners: Map<keyof WebSocketEvents<C>, Array<(data: WebSocketBaseEvent<any, any>) => void | Promise<void>>> = new Map()\n\n private reconnectAttempts = 0\n private shouldClose = false\n\n constructor(options: ClientOptions<C>) {\n this.opts = defu<Required<ClientOptions<C>>, Required<Omit<ClientOptions<C>, 'name' | 'token'>>[]>(\n options,\n {\n url: 'ws://localhost:6121/ws',\n possibleEvents: [],\n onError: () => { },\n onClose: () => { },\n autoConnect: true,\n autoReconnect: true,\n },\n )\n\n if (this.opts.autoConnect) {\n try {\n this.connect()\n }\n catch (err) {\n console.error(err)\n }\n }\n }\n\n async retryWithExponentialBackoff(fn: () => void | Promise<void>, attempts = 0, maxAttempts = -1) {\n if (maxAttempts !== -1 && attempts >= maxAttempts) {\n console.error(`Maximum retry attempts (${maxAttempts}) reached`)\n return\n }\n\n try {\n await fn()\n }\n catch (err) {\n console.error('Encountered an error when retrying', err)\n await sleep(2 ** attempts * 1000)\n await this.retryWithExponentialBackoff(fn, attempts++, maxAttempts)\n }\n }\n\n async tryReconnectWithExponentialBackoff() {\n await this.retryWithExponentialBackoff(() => this._connect(), this.reconnectAttempts)\n }\n\n private _connect() {\n return new Promise<void>((resolve, reject) => {\n if (this.shouldClose) {\n resolve()\n return\n }\n\n if (this.connected) {\n resolve()\n return\n }\n\n this.websocket = new WebSocket(this.opts.url)\n\n this.onEvent('module:authenticated', async (event) => {\n const auth = event.data.authenticated\n if (!auth) {\n this.retryWithExponentialBackoff(() => this.tryAuthenticate())\n }\n else {\n this.tryAnnounce()\n }\n })\n\n this.websocket.onerror = (event) => {\n this.opts.onError?.(event)\n\n if ('error' in event && event.error instanceof Error) {\n if (event.error.message === 'Received network error or non-101 status code.') {\n this.connected = false\n\n if (!this.opts.autoReconnect) {\n this.opts.onError?.(event)\n this.opts.onClose?.()\n reject(event.error)\n return\n }\n\n reject(event.error)\n }\n }\n }\n\n this.websocket.onclose = () => {\n this.opts.onClose?.()\n this.connected = false\n\n if (!this.opts.autoReconnect) {\n this.opts.onClose?.()\n }\n else {\n this.tryReconnectWithExponentialBackoff()\n }\n }\n\n this.websocket.onmessage = (event) => {\n this.handleMessage(event)\n }\n\n this.websocket.onopen = () => {\n this.reconnectAttempts = 0\n\n if (this.opts.token) {\n this.tryAuthenticate()\n }\n else {\n this.tryAnnounce()\n }\n\n this.connected = true\n\n resolve()\n }\n })\n }\n\n async connect() {\n await this.tryReconnectWithExponentialBackoff()\n }\n\n private tryAnnounce() {\n this.send({\n type: 'module:announce',\n data: {\n name: this.opts.name,\n possibleEvents: this.opts.possibleEvents,\n },\n })\n }\n\n private tryAuthenticate() {\n if (this.opts.token) {\n this.send({ type: 'module:authenticate', data: { token: this.opts.token || '' } })\n }\n }\n\n private async handleMessage(event: any) {\n try {\n const data = JSON.parse(event.data) as WebSocketEvent<C>\n const listeners = this.eventListeners.get(data.type)\n if (!listeners)\n return\n\n for (const listener of listeners)\n await listener(data)\n }\n catch (err) {\n console.error('Failed to parse message:', err)\n this.opts.onError?.(err)\n }\n }\n\n onEvent<E extends keyof WebSocketEvents<C>>(\n event: E,\n callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>,\n ): void {\n if (!this.eventListeners.get(event)) {\n this.eventListeners.set(event, [])\n }\n\n const listeners = this.eventListeners.get(event)\n if (!listeners) {\n return\n }\n\n listeners.push(callback as unknown as (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>)\n this.eventListeners.set(event, listeners)\n }\n\n send(data: WebSocketEvent<C>): void {\n this.websocket?.send(JSON.stringify(data))\n }\n\n sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void {\n this.websocket?.send(data)\n }\n\n close(): void {\n this.shouldClose = true\n\n if (this.connected && this.websocket) {\n this.websocket.close()\n this.connected = false\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import process from
|
|
1
|
+
import process from "node:process";
|
|
2
2
|
|
|
3
|
+
//#region src/utils/node/process.ts
|
|
3
4
|
let running = true;
|
|
4
5
|
function killProcess() {
|
|
5
|
-
|
|
6
|
+
running = false;
|
|
6
7
|
}
|
|
7
8
|
process.on("SIGTERM", () => {
|
|
8
|
-
|
|
9
|
+
killProcess();
|
|
9
10
|
});
|
|
10
11
|
process.on("SIGINT", () => {
|
|
11
|
-
|
|
12
|
+
killProcess();
|
|
12
13
|
});
|
|
13
14
|
process.on("uncaughtException", (e) => {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
console.error(e);
|
|
16
|
+
killProcess();
|
|
16
17
|
});
|
|
17
18
|
function runUntilSignal() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, 10);
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
if (running) runUntilSignal();
|
|
21
|
+
}, 10);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
//#endregion
|
|
24
25
|
export { runUntilSignal };
|
|
26
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/node/process.ts"],"sourcesContent":["import process from 'node:process'\n\nlet running = true\n\nfunction killProcess() {\n running = false\n}\n\nprocess.on('SIGTERM', () => {\n killProcess()\n})\nprocess.on('SIGINT', () => {\n killProcess()\n})\nprocess.on('uncaughtException', (e) => {\n console.error(e)\n killProcess()\n})\n\nexport function runUntilSignal() {\n setTimeout(() => {\n if (running)\n runUntilSignal()\n }, 10)\n}\n"],"mappings":";;;AAEA,IAAI,UAAU;AAEd,SAAS,cAAc;AACrB,WAAU;AACX;AAED,QAAQ,GAAG,WAAW,MAAM;AAC1B,cAAa;AACd,EAAC;AACF,QAAQ,GAAG,UAAU,MAAM;AACzB,cAAa;AACd,EAAC;AACF,QAAQ,GAAG,qBAAqB,CAAC,MAAM;AACrC,SAAQ,MAAM,EAAE;AAChB,cAAa;AACd,EAAC;AAEF,SAAgB,iBAAiB;AAC/B,YAAW,MAAM;AACf,MAAI,QACF,iBAAgB;CACnB,GAAE,GAAG;AACP"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proj-airi/server-sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"description": "Client-side SDK implementation for connecting to AIRI server components and runtimes",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Moeru AI Project AIRI Team",
|
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
19
|
-
"types": "./dist/index.d.
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
20
|
"import": "./dist/index.mjs",
|
|
21
|
-
"require": "./dist/index.
|
|
21
|
+
"require": "./dist/index.mjs"
|
|
22
22
|
},
|
|
23
23
|
"./utils/node": {
|
|
24
|
-
"types": "./dist/utils/node/index.d.
|
|
24
|
+
"types": "./dist/utils/node/index.d.mts",
|
|
25
25
|
"import": "./dist/utils/node/index.mjs",
|
|
26
|
-
"require": "./dist/utils/node/index.
|
|
26
|
+
"require": "./dist/utils/node/index.mjs"
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
|
-
"main": "./dist/index.
|
|
29
|
+
"main": "./dist/index.mjs",
|
|
30
30
|
"module": "./dist/index.mjs",
|
|
31
|
-
"types": "./dist/index.d.
|
|
31
|
+
"types": "./dist/index.d.mts",
|
|
32
32
|
"files": [
|
|
33
33
|
"README.md",
|
|
34
34
|
"dist",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"crossws": "^0.4.1",
|
|
39
39
|
"defu": "^6.1.4",
|
|
40
|
-
"@proj-airi/server-shared": "^0.6.
|
|
40
|
+
"@proj-airi/server-shared": "^0.6.1"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
-
"dev": "pnpm run
|
|
44
|
-
"stub": "
|
|
45
|
-
"build": "
|
|
43
|
+
"dev": "pnpm run build",
|
|
44
|
+
"stub": "pnpm run build",
|
|
45
|
+
"build": "tsdown",
|
|
46
46
|
"typecheck": "tsc --noEmit"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const WebSocket = require('crossws/websocket');
|
|
4
|
-
const defu = require('defu');
|
|
5
|
-
|
|
6
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
7
|
-
|
|
8
|
-
const WebSocket__default = /*#__PURE__*/_interopDefaultCompat(WebSocket);
|
|
9
|
-
|
|
10
|
-
function sleep(ms) {
|
|
11
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class Client {
|
|
15
|
-
connected = false;
|
|
16
|
-
opts;
|
|
17
|
-
websocket;
|
|
18
|
-
eventListeners = /* @__PURE__ */ new Map();
|
|
19
|
-
reconnectAttempts = 0;
|
|
20
|
-
shouldClose = false;
|
|
21
|
-
constructor(options) {
|
|
22
|
-
this.opts = defu.defu(
|
|
23
|
-
options,
|
|
24
|
-
{
|
|
25
|
-
url: "ws://localhost:6121/ws",
|
|
26
|
-
possibleEvents: [],
|
|
27
|
-
onError: () => {
|
|
28
|
-
},
|
|
29
|
-
onClose: () => {
|
|
30
|
-
},
|
|
31
|
-
autoConnect: true,
|
|
32
|
-
autoReconnect: true
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
|
-
if (this.opts.autoConnect) {
|
|
36
|
-
try {
|
|
37
|
-
this.connect();
|
|
38
|
-
} catch (err) {
|
|
39
|
-
console.error(err);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
async retryWithExponentialBackoff(fn, attempts = 0, maxAttempts = -1) {
|
|
44
|
-
if (maxAttempts !== -1 && attempts >= maxAttempts) {
|
|
45
|
-
console.error(`Maximum retry attempts (${maxAttempts}) reached`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
await fn();
|
|
50
|
-
} catch (err) {
|
|
51
|
-
console.error("Encountered an error when retrying", err);
|
|
52
|
-
await sleep(2 ** attempts * 1e3);
|
|
53
|
-
await this.retryWithExponentialBackoff(fn, attempts++, maxAttempts);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async tryReconnectWithExponentialBackoff() {
|
|
57
|
-
await this.retryWithExponentialBackoff(() => this._connect(), this.reconnectAttempts);
|
|
58
|
-
}
|
|
59
|
-
_connect() {
|
|
60
|
-
return new Promise((resolve, reject) => {
|
|
61
|
-
if (this.shouldClose) {
|
|
62
|
-
resolve();
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (this.connected) {
|
|
66
|
-
resolve();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
this.websocket = new WebSocket__default(this.opts.url);
|
|
70
|
-
this.onEvent("module:authenticated", async (event) => {
|
|
71
|
-
const auth = event.data.authenticated;
|
|
72
|
-
if (!auth) {
|
|
73
|
-
this.retryWithExponentialBackoff(() => this.tryAuthenticate());
|
|
74
|
-
} else {
|
|
75
|
-
this.tryAnnounce();
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
this.websocket.onerror = (event) => {
|
|
79
|
-
this.opts.onError?.(event);
|
|
80
|
-
if ("error" in event && event.error instanceof Error) {
|
|
81
|
-
if (event.error.message === "Received network error or non-101 status code.") {
|
|
82
|
-
this.connected = false;
|
|
83
|
-
if (!this.opts.autoReconnect) {
|
|
84
|
-
this.opts.onError?.(event);
|
|
85
|
-
this.opts.onClose?.();
|
|
86
|
-
reject(event.error);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
reject(event.error);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
this.websocket.onclose = () => {
|
|
94
|
-
this.opts.onClose?.();
|
|
95
|
-
this.connected = false;
|
|
96
|
-
if (!this.opts.autoReconnect) {
|
|
97
|
-
this.opts.onClose?.();
|
|
98
|
-
} else {
|
|
99
|
-
this.tryReconnectWithExponentialBackoff();
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
this.websocket.onmessage = (event) => {
|
|
103
|
-
this.handleMessage(event);
|
|
104
|
-
};
|
|
105
|
-
this.websocket.onopen = () => {
|
|
106
|
-
this.reconnectAttempts = 0;
|
|
107
|
-
if (this.opts.token) {
|
|
108
|
-
this.tryAuthenticate();
|
|
109
|
-
} else {
|
|
110
|
-
this.tryAnnounce();
|
|
111
|
-
}
|
|
112
|
-
this.connected = true;
|
|
113
|
-
resolve();
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
async connect() {
|
|
118
|
-
await this.tryReconnectWithExponentialBackoff();
|
|
119
|
-
}
|
|
120
|
-
tryAnnounce() {
|
|
121
|
-
this.send({
|
|
122
|
-
type: "module:announce",
|
|
123
|
-
data: {
|
|
124
|
-
name: this.opts.name,
|
|
125
|
-
possibleEvents: this.opts.possibleEvents
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
tryAuthenticate() {
|
|
130
|
-
if (this.opts.token) {
|
|
131
|
-
this.send({ type: "module:authenticate", data: { token: this.opts.token || "" } });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async handleMessage(event) {
|
|
135
|
-
try {
|
|
136
|
-
const data = JSON.parse(event.data);
|
|
137
|
-
const listeners = this.eventListeners.get(data.type);
|
|
138
|
-
if (!listeners)
|
|
139
|
-
return;
|
|
140
|
-
for (const listener of listeners)
|
|
141
|
-
await listener(data);
|
|
142
|
-
} catch (err) {
|
|
143
|
-
console.error("Failed to parse message:", err);
|
|
144
|
-
this.opts.onError?.(err);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
onEvent(event, callback) {
|
|
148
|
-
if (!this.eventListeners.get(event)) {
|
|
149
|
-
this.eventListeners.set(event, []);
|
|
150
|
-
}
|
|
151
|
-
const listeners = this.eventListeners.get(event);
|
|
152
|
-
if (!listeners) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
listeners.push(callback);
|
|
156
|
-
this.eventListeners.set(event, listeners);
|
|
157
|
-
}
|
|
158
|
-
send(data) {
|
|
159
|
-
this.websocket?.send(JSON.stringify(data));
|
|
160
|
-
}
|
|
161
|
-
sendRaw(data) {
|
|
162
|
-
this.websocket?.send(data);
|
|
163
|
-
}
|
|
164
|
-
close() {
|
|
165
|
-
this.shouldClose = true;
|
|
166
|
-
if (this.connected && this.websocket) {
|
|
167
|
-
this.websocket.close();
|
|
168
|
-
this.connected = false;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
exports.Client = Client;
|
package/dist/index.d.cts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { WebSocketEvents, WebSocketBaseEvent, WebSocketEvent } from '@proj-airi/server-shared/types';
|
|
2
|
-
|
|
3
|
-
interface ClientOptions<C = undefined> {
|
|
4
|
-
url?: string;
|
|
5
|
-
name: string;
|
|
6
|
-
possibleEvents?: Array<(keyof WebSocketEvents<C>)>;
|
|
7
|
-
token?: string;
|
|
8
|
-
onError?: (error: unknown) => void;
|
|
9
|
-
onClose?: () => void;
|
|
10
|
-
autoConnect?: boolean;
|
|
11
|
-
autoReconnect?: boolean;
|
|
12
|
-
}
|
|
13
|
-
declare class Client<C = undefined> {
|
|
14
|
-
private connected;
|
|
15
|
-
private opts;
|
|
16
|
-
private websocket;
|
|
17
|
-
private eventListeners;
|
|
18
|
-
private reconnectAttempts;
|
|
19
|
-
private shouldClose;
|
|
20
|
-
constructor(options: ClientOptions<C>);
|
|
21
|
-
retryWithExponentialBackoff(fn: () => void | Promise<void>, attempts?: number, maxAttempts?: number): Promise<void>;
|
|
22
|
-
tryReconnectWithExponentialBackoff(): Promise<void>;
|
|
23
|
-
private _connect;
|
|
24
|
-
connect(): Promise<void>;
|
|
25
|
-
private tryAnnounce;
|
|
26
|
-
private tryAuthenticate;
|
|
27
|
-
private handleMessage;
|
|
28
|
-
onEvent<E extends keyof WebSocketEvents<C>>(event: E, callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>): void;
|
|
29
|
-
send(data: WebSocketEvent<C>): void;
|
|
30
|
-
sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void;
|
|
31
|
-
close(): void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export { Client, type ClientOptions };
|
package/dist/index.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { WebSocketEvents, WebSocketBaseEvent, WebSocketEvent } from '@proj-airi/server-shared/types';
|
|
2
|
-
|
|
3
|
-
interface ClientOptions<C = undefined> {
|
|
4
|
-
url?: string;
|
|
5
|
-
name: string;
|
|
6
|
-
possibleEvents?: Array<(keyof WebSocketEvents<C>)>;
|
|
7
|
-
token?: string;
|
|
8
|
-
onError?: (error: unknown) => void;
|
|
9
|
-
onClose?: () => void;
|
|
10
|
-
autoConnect?: boolean;
|
|
11
|
-
autoReconnect?: boolean;
|
|
12
|
-
}
|
|
13
|
-
declare class Client<C = undefined> {
|
|
14
|
-
private connected;
|
|
15
|
-
private opts;
|
|
16
|
-
private websocket;
|
|
17
|
-
private eventListeners;
|
|
18
|
-
private reconnectAttempts;
|
|
19
|
-
private shouldClose;
|
|
20
|
-
constructor(options: ClientOptions<C>);
|
|
21
|
-
retryWithExponentialBackoff(fn: () => void | Promise<void>, attempts?: number, maxAttempts?: number): Promise<void>;
|
|
22
|
-
tryReconnectWithExponentialBackoff(): Promise<void>;
|
|
23
|
-
private _connect;
|
|
24
|
-
connect(): Promise<void>;
|
|
25
|
-
private tryAnnounce;
|
|
26
|
-
private tryAuthenticate;
|
|
27
|
-
private handleMessage;
|
|
28
|
-
onEvent<E extends keyof WebSocketEvents<C>>(event: E, callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>): void;
|
|
29
|
-
send(data: WebSocketEvent<C>): void;
|
|
30
|
-
sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void;
|
|
31
|
-
close(): void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export { Client, type ClientOptions };
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const process = require('node:process');
|
|
4
|
-
|
|
5
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
6
|
-
|
|
7
|
-
const process__default = /*#__PURE__*/_interopDefaultCompat(process);
|
|
8
|
-
|
|
9
|
-
let running = true;
|
|
10
|
-
function killProcess() {
|
|
11
|
-
running = false;
|
|
12
|
-
}
|
|
13
|
-
process__default.on("SIGTERM", () => {
|
|
14
|
-
killProcess();
|
|
15
|
-
});
|
|
16
|
-
process__default.on("SIGINT", () => {
|
|
17
|
-
killProcess();
|
|
18
|
-
});
|
|
19
|
-
process__default.on("uncaughtException", (e) => {
|
|
20
|
-
console.error(e);
|
|
21
|
-
killProcess();
|
|
22
|
-
});
|
|
23
|
-
function runUntilSignal() {
|
|
24
|
-
setTimeout(() => {
|
|
25
|
-
if (running)
|
|
26
|
-
runUntilSignal();
|
|
27
|
-
}, 10);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
exports.runUntilSignal = runUntilSignal;
|