@proj-airi/server-sdk 0.8.1-beta.1 → 0.8.1-beta.10
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 +15 -1
- package/dist/index.mjs +91 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContextUpdateStrategy, WebSocketBaseEvent, WebSocketEventOptionalSource, WebSocketEventSource, WebSocketEvents } from "@proj-airi/server-shared/types";
|
|
1
|
+
import { ContextUpdateStrategy, MessageHeartbeat, MetadataEventSource, WebSocketBaseEvent, WebSocketEvent, WebSocketEventOptionalSource, WebSocketEventSource, WebSocketEvents } from "@proj-airi/server-shared/types";
|
|
2
2
|
export * from "@proj-airi/server-shared/types";
|
|
3
3
|
|
|
4
4
|
//#region src/client.d.ts
|
|
@@ -7,11 +7,18 @@ interface ClientOptions<C = undefined> {
|
|
|
7
7
|
name: string;
|
|
8
8
|
possibleEvents?: Array<keyof WebSocketEvents<C>>;
|
|
9
9
|
token?: string;
|
|
10
|
+
identity?: MetadataEventSource;
|
|
11
|
+
heartbeat?: {
|
|
12
|
+
readTimeout?: number;
|
|
13
|
+
message?: MessageHeartbeat | string;
|
|
14
|
+
};
|
|
10
15
|
onError?: (error: unknown) => void;
|
|
11
16
|
onClose?: () => void;
|
|
12
17
|
autoConnect?: boolean;
|
|
13
18
|
autoReconnect?: boolean;
|
|
14
19
|
maxReconnectAttempts?: number;
|
|
20
|
+
onAnyMessage?: (data: WebSocketEvent<C>) => void;
|
|
21
|
+
onAnySend?: (data: WebSocketEvent<C>) => void;
|
|
15
22
|
}
|
|
16
23
|
declare class Client<C = undefined> {
|
|
17
24
|
private connected;
|
|
@@ -20,6 +27,8 @@ declare class Client<C = undefined> {
|
|
|
20
27
|
private shouldClose;
|
|
21
28
|
private connectAttempt?;
|
|
22
29
|
private connectTask?;
|
|
30
|
+
private heartbeatTimer?;
|
|
31
|
+
private readonly identity;
|
|
23
32
|
private readonly opts;
|
|
24
33
|
private readonly eventListeners;
|
|
25
34
|
constructor(options: ClientOptions<C>);
|
|
@@ -36,6 +45,11 @@ declare class Client<C = undefined> {
|
|
|
36
45
|
send(data: WebSocketEventOptionalSource<C>): void;
|
|
37
46
|
sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void;
|
|
38
47
|
close(): void;
|
|
48
|
+
private startHeartbeat;
|
|
49
|
+
private stopHeartbeat;
|
|
50
|
+
private sendNativeHeartbeat;
|
|
51
|
+
private sendHeartbeatPing;
|
|
52
|
+
private sendHeartbeatPong;
|
|
39
53
|
private _reconnectDueToUnauthorized;
|
|
40
54
|
}
|
|
41
55
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import WebSocket from "crossws/websocket";
|
|
2
|
-
import
|
|
2
|
+
import superjson from "superjson";
|
|
3
|
+
import { ContextUpdateStrategy, MessageHeartbeat, MessageHeartbeatKind, WebSocketEventSource } from "@proj-airi/server-shared/types";
|
|
3
4
|
|
|
4
5
|
//#region ../../node_modules/.pnpm/@moeru+std@0.1.0-beta.14/node_modules/@moeru/std/dist/sleep/index.js
|
|
5
6
|
const sleep = async (delay) => new Promise((resolve) => setTimeout(resolve, delay));
|
|
6
7
|
|
|
7
8
|
//#endregion
|
|
8
9
|
//#region src/client.ts
|
|
10
|
+
function createInstanceId() {
|
|
11
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
12
|
+
}
|
|
13
|
+
function createEventId() {
|
|
14
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
15
|
+
}
|
|
9
16
|
var Client = class {
|
|
10
17
|
connected = false;
|
|
11
18
|
connecting = false;
|
|
@@ -13,19 +20,33 @@ var Client = class {
|
|
|
13
20
|
shouldClose = false;
|
|
14
21
|
connectAttempt;
|
|
15
22
|
connectTask;
|
|
23
|
+
heartbeatTimer;
|
|
24
|
+
identity;
|
|
16
25
|
opts;
|
|
17
26
|
eventListeners = /* @__PURE__ */ new Map();
|
|
18
27
|
constructor(options) {
|
|
28
|
+
const identity = options.identity ?? {
|
|
29
|
+
plugin: options.name,
|
|
30
|
+
instanceId: createInstanceId()
|
|
31
|
+
};
|
|
19
32
|
this.opts = {
|
|
20
33
|
url: "ws://localhost:6121/ws",
|
|
34
|
+
onAnyMessage: () => {},
|
|
35
|
+
onAnySend: () => {},
|
|
21
36
|
possibleEvents: [],
|
|
22
37
|
onError: () => {},
|
|
23
38
|
onClose: () => {},
|
|
24
39
|
autoConnect: true,
|
|
25
40
|
autoReconnect: true,
|
|
26
41
|
maxReconnectAttempts: -1,
|
|
27
|
-
|
|
42
|
+
heartbeat: {
|
|
43
|
+
readTimeout: 3e4,
|
|
44
|
+
message: MessageHeartbeat.Ping
|
|
45
|
+
},
|
|
46
|
+
...options,
|
|
47
|
+
identity
|
|
28
48
|
};
|
|
49
|
+
this.identity = identity;
|
|
29
50
|
this.onEvent("module:authenticated", async (event) => {
|
|
30
51
|
if (event.data.authenticated) this.tryAnnounce();
|
|
31
52
|
else await this.retryWithExponentialBackoff(() => this.tryAuthenticate());
|
|
@@ -33,6 +54,9 @@ var Client = class {
|
|
|
33
54
|
this.onEvent("error", async (event) => {
|
|
34
55
|
if (event.data.message === "not authenticated") await this._reconnectDueToUnauthorized();
|
|
35
56
|
});
|
|
57
|
+
this.onEvent("transport:connection:heartbeat", (event) => {
|
|
58
|
+
if (event.data.kind === MessageHeartbeatKind.Ping) this.sendHeartbeatPong();
|
|
59
|
+
});
|
|
36
60
|
if (this.opts.autoConnect) this.connect();
|
|
37
61
|
}
|
|
38
62
|
async retryWithExponentialBackoff(fn) {
|
|
@@ -89,6 +113,7 @@ var Client = class {
|
|
|
89
113
|
}
|
|
90
114
|
if (this.connected) {
|
|
91
115
|
this.connected = false;
|
|
116
|
+
this.stopHeartbeat();
|
|
92
117
|
this.opts.onClose?.();
|
|
93
118
|
}
|
|
94
119
|
if (this.opts.autoReconnect && !this.shouldClose) this.tryReconnectWithExponentialBackoff();
|
|
@@ -96,6 +121,7 @@ var Client = class {
|
|
|
96
121
|
ws.onopen = () => {
|
|
97
122
|
settle(() => {
|
|
98
123
|
this.connected = true;
|
|
124
|
+
this.startHeartbeat();
|
|
99
125
|
if (this.opts.token) this.tryAuthenticate();
|
|
100
126
|
else this.tryAnnounce();
|
|
101
127
|
resolve();
|
|
@@ -115,6 +141,7 @@ var Client = class {
|
|
|
115
141
|
type: "module:announce",
|
|
116
142
|
data: {
|
|
117
143
|
name: this.opts.name,
|
|
144
|
+
identity: this.identity,
|
|
118
145
|
possibleEvents: this.opts.possibleEvents
|
|
119
146
|
}
|
|
120
147
|
});
|
|
@@ -130,7 +157,12 @@ var Client = class {
|
|
|
130
157
|
};
|
|
131
158
|
async handleMessage(event) {
|
|
132
159
|
try {
|
|
133
|
-
const data =
|
|
160
|
+
const data = superjson.parse(event.data);
|
|
161
|
+
if (!data) {
|
|
162
|
+
console.warn("Received empty message");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.opts.onAnyMessage?.(data);
|
|
134
166
|
const listeners = this.eventListeners.get(data.type);
|
|
135
167
|
if (!listeners?.size) return;
|
|
136
168
|
const executions = [];
|
|
@@ -158,21 +190,73 @@ var Client = class {
|
|
|
158
190
|
} else this.eventListeners.delete(event);
|
|
159
191
|
}
|
|
160
192
|
send(data) {
|
|
161
|
-
if (this.websocket && this.connected)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
193
|
+
if (this.websocket && this.connected) {
|
|
194
|
+
const payload = {
|
|
195
|
+
...data,
|
|
196
|
+
metadata: {
|
|
197
|
+
...data?.metadata,
|
|
198
|
+
source: data?.metadata?.source ?? this.identity,
|
|
199
|
+
event: {
|
|
200
|
+
id: data?.metadata?.event?.id ?? createEventId(),
|
|
201
|
+
...data?.metadata?.event
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
this.opts.onAnySend?.(payload);
|
|
206
|
+
this.websocket.send(superjson.stringify(payload));
|
|
207
|
+
}
|
|
165
208
|
}
|
|
166
209
|
sendRaw(data) {
|
|
167
210
|
if (this.websocket && this.connected) this.websocket.send(data);
|
|
168
211
|
}
|
|
169
212
|
close() {
|
|
170
213
|
this.shouldClose = true;
|
|
214
|
+
this.stopHeartbeat();
|
|
171
215
|
if (this.websocket) {
|
|
172
216
|
this.websocket.close();
|
|
173
217
|
this.connected = false;
|
|
174
218
|
}
|
|
175
219
|
}
|
|
220
|
+
startHeartbeat() {
|
|
221
|
+
if (!this.opts.heartbeat?.readTimeout) return;
|
|
222
|
+
this.stopHeartbeat();
|
|
223
|
+
const ping = () => this.sendHeartbeatPing();
|
|
224
|
+
ping();
|
|
225
|
+
this.heartbeatTimer = setInterval(ping, this.opts.heartbeat.readTimeout);
|
|
226
|
+
}
|
|
227
|
+
stopHeartbeat() {
|
|
228
|
+
if (this.heartbeatTimer) {
|
|
229
|
+
clearInterval(this.heartbeatTimer);
|
|
230
|
+
this.heartbeatTimer = void 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
sendNativeHeartbeat(kind) {
|
|
234
|
+
const websocket = this.websocket;
|
|
235
|
+
if (kind === "ping") websocket.ping?.();
|
|
236
|
+
else websocket.pong?.();
|
|
237
|
+
}
|
|
238
|
+
sendHeartbeatPing() {
|
|
239
|
+
this.send({
|
|
240
|
+
type: "transport:connection:heartbeat",
|
|
241
|
+
data: {
|
|
242
|
+
kind: MessageHeartbeatKind.Ping,
|
|
243
|
+
message: this.opts.heartbeat?.message ?? MessageHeartbeat.Ping,
|
|
244
|
+
at: Date.now()
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
this.sendNativeHeartbeat("ping");
|
|
248
|
+
}
|
|
249
|
+
sendHeartbeatPong() {
|
|
250
|
+
this.send({
|
|
251
|
+
type: "transport:connection:heartbeat",
|
|
252
|
+
data: {
|
|
253
|
+
kind: MessageHeartbeatKind.Pong,
|
|
254
|
+
message: MessageHeartbeat.Pong,
|
|
255
|
+
at: Date.now()
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
this.sendNativeHeartbeat("pong");
|
|
259
|
+
}
|
|
176
260
|
async _reconnectDueToUnauthorized() {
|
|
177
261
|
if (this.shouldClose) return;
|
|
178
262
|
const ws = this.websocket;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["executions: Promise<void>[]"],"sources":["../../../node_modules/.pnpm/@moeru+std@0.1.0-beta.14/node_modules/@moeru/std/dist/sleep/index.js","../src/client.ts"],"sourcesContent":["const sleep = async (delay) => new Promise((resolve) => setTimeout(resolve, delay));\n\nexport { sleep };\n","import type {\n WebSocketBaseEvent,\n WebSocketEvent,\n WebSocketEventOptionalSource,\n WebSocketEvents,\n WebSocketEventSource,\n} from '@proj-airi/server-shared/types'\n\nimport WebSocket from 'crossws/websocket'\n\nimport { sleep } from '@moeru/std'\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 maxReconnectAttempts?: number\n}\n\nexport class Client<C = undefined> {\n private connected = false\n private connecting = false\n private websocket?: WebSocket\n private shouldClose = false\n private connectAttempt?: Promise<void>\n private connectTask?: Promise<void>\n\n private readonly opts: Required<Omit<ClientOptions<C>, 'token'>> & Pick<ClientOptions<C>, 'token'>\n private readonly eventListeners = new Map<\n keyof WebSocketEvents<C>,\n Set<(data: WebSocketBaseEvent<any, any>) => void | Promise<void>>\n >()\n\n constructor(options: ClientOptions<C>) {\n this.opts = {\n url: 'ws://localhost:6121/ws',\n possibleEvents: [],\n onError: () => {},\n onClose: () => {},\n autoConnect: true,\n autoReconnect: true,\n maxReconnectAttempts: -1,\n ...options,\n }\n\n // Authentication listener is registered once only\n this.onEvent('module:authenticated', async (event) => {\n if (event.data.authenticated) {\n this.tryAnnounce()\n }\n else {\n await this.retryWithExponentialBackoff(() => this.tryAuthenticate())\n }\n })\n\n this.onEvent('error', async (event) => {\n if (event.data.message === 'not authenticated') {\n await this._reconnectDueToUnauthorized()\n }\n })\n\n if (this.opts.autoConnect) {\n void this.connect()\n }\n }\n\n private async retryWithExponentialBackoff(fn: () => void | Promise<void>) {\n const { maxReconnectAttempts } = this.opts\n let attempts = 0\n\n // Loop until attempts exceed maxReconnectAttempts, or unlimited if -1\n while (true) {\n if (maxReconnectAttempts !== -1 && attempts >= maxReconnectAttempts) {\n console.error(`Maximum retry attempts (${maxReconnectAttempts}) reached`)\n return\n }\n\n try {\n await fn()\n return\n }\n catch (err) {\n this.opts.onError?.(err)\n const delay = Math.min(2 ** attempts * 1000, 30_000) // capped exponential backoff\n await sleep(delay)\n attempts++\n }\n }\n }\n\n private async tryReconnectWithExponentialBackoff() {\n if (this.shouldClose) {\n throw new Error('Client is closed')\n }\n\n await this.retryWithExponentialBackoff(() => this._connect())\n }\n\n private _connect(): Promise<void> {\n if (this.shouldClose || this.connected) {\n return Promise.resolve()\n }\n if (this.connecting) {\n return this.connectAttempt ?? Promise.resolve()\n }\n\n this.connectAttempt = new Promise((resolve, reject) => {\n this.connecting = true\n let settled = false\n\n const settle = (fn: () => void) => {\n if (settled)\n return\n\n settled = true\n this.connecting = false\n this.connectAttempt = undefined\n fn()\n }\n\n const ws = new WebSocket(this.opts.url)\n this.websocket = ws\n\n ws.onmessage = this.handleMessageBound\n ws.onerror = (event: any) => {\n settle(() => {\n this.connected = false\n\n this.opts.onError?.(event)\n reject(event?.error ?? new Error('WebSocket error'))\n })\n }\n ws.onclose = () => {\n if (!settled && !this.connected) {\n settle(() => {\n reject(new Error('WebSocket closed before open'))\n })\n return\n }\n\n if (this.connected) {\n this.connected = false\n this.opts.onClose?.()\n }\n if (this.opts.autoReconnect && !this.shouldClose) {\n void this.tryReconnectWithExponentialBackoff()\n }\n }\n ws.onopen = () => {\n settle(() => {\n this.connected = true\n\n if (this.opts.token)\n this.tryAuthenticate()\n else\n this.tryAnnounce()\n\n resolve()\n })\n }\n })\n\n return this.connectAttempt\n }\n\n async connect() {\n if (this.connected) {\n return\n }\n if (this.connectTask) {\n return this.connectTask\n }\n\n this.connectTask = this.tryReconnectWithExponentialBackoff().finally(() => (this.connectTask = undefined))\n\n return this.connectTask\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({\n type: 'module:authenticate',\n data: { token: this.opts.token },\n })\n }\n }\n\n // bound reference avoids new closure allocation on every connect\n private readonly handleMessageBound = (event: MessageEvent) => {\n void this.handleMessage(event)\n }\n\n private async handleMessage(event: MessageEvent) {\n try {\n const data = JSON.parse(event.data as string) as WebSocketEvent<C>\n const listeners = this.eventListeners.get(data.type)\n if (!listeners?.size) {\n return\n }\n\n // Execute all listeners concurrently\n const executions: Promise<void>[] = []\n for (const listener of listeners) {\n executions.push(Promise.resolve(listener(data as any)))\n }\n\n await Promise.allSettled(executions)\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 let listeners = this.eventListeners.get(event)\n if (!listeners) {\n listeners = new Set()\n this.eventListeners.set(event, listeners)\n }\n listeners.add(callback as any)\n }\n\n offEvent<E extends keyof WebSocketEvents<C>>(\n event: E,\n callback?: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void,\n ): void {\n const listeners = this.eventListeners.get(event)\n if (!listeners) {\n return\n }\n\n if (callback) {\n listeners.delete(callback as any)\n if (!listeners.size) {\n this.eventListeners.delete(event)\n }\n }\n else {\n this.eventListeners.delete(event)\n }\n }\n\n send(data: WebSocketEventOptionalSource<C>): void {\n if (this.websocket && this.connected) {\n this.websocket.send(JSON.stringify({ source: this.opts.name as WebSocketEventSource | string, ...data } as WebSocketEvent<C>))\n }\n }\n\n sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void {\n if (this.websocket && this.connected) {\n this.websocket.send(data)\n }\n }\n\n close(): void {\n this.shouldClose = true\n if (this.websocket) {\n this.websocket.close()\n this.connected = false\n }\n }\n\n private async _reconnectDueToUnauthorized() {\n if (this.shouldClose)\n return\n\n const ws = this.websocket\n this.connected = false\n if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {\n ws.close()\n }\n\n await this.connect()\n }\n}\n"],"x_google_ignoreList":[0],"mappings":";;;;AAAA,MAAM,QAAQ,OAAO,UAAU,IAAI,SAAS,YAAY,WAAW,SAAS,MAAM,CAAC;;;;ACwBnF,IAAa,SAAb,MAAmC;CACjC,AAAQ,YAAY;CACpB,AAAQ,aAAa;CACrB,AAAQ;CACR,AAAQ,cAAc;CACtB,AAAQ;CACR,AAAQ;CAER,AAAiB;CACjB,AAAiB,iCAAiB,IAAI,KAGnC;CAEH,YAAY,SAA2B;AACrC,OAAK,OAAO;GACV,KAAK;GACL,gBAAgB,EAAE;GAClB,eAAe;GACf,eAAe;GACf,aAAa;GACb,eAAe;GACf,sBAAsB;GACtB,GAAG;GACJ;AAGD,OAAK,QAAQ,wBAAwB,OAAO,UAAU;AACpD,OAAI,MAAM,KAAK,cACb,MAAK,aAAa;OAGlB,OAAM,KAAK,kCAAkC,KAAK,iBAAiB,CAAC;IAEtE;AAEF,OAAK,QAAQ,SAAS,OAAO,UAAU;AACrC,OAAI,MAAM,KAAK,YAAY,oBACzB,OAAM,KAAK,6BAA6B;IAE1C;AAEF,MAAI,KAAK,KAAK,YACZ,CAAK,KAAK,SAAS;;CAIvB,MAAc,4BAA4B,IAAgC;EACxE,MAAM,EAAE,yBAAyB,KAAK;EACtC,IAAI,WAAW;AAGf,SAAO,MAAM;AACX,OAAI,yBAAyB,MAAM,YAAY,sBAAsB;AACnE,YAAQ,MAAM,2BAA2B,qBAAqB,WAAW;AACzE;;AAGF,OAAI;AACF,UAAM,IAAI;AACV;YAEK,KAAK;AACV,SAAK,KAAK,UAAU,IAAI;AAExB,UAAM,MADQ,KAAK,IAAI,KAAK,WAAW,KAAM,IAAO,CAClC;AAClB;;;;CAKN,MAAc,qCAAqC;AACjD,MAAI,KAAK,YACP,OAAM,IAAI,MAAM,mBAAmB;AAGrC,QAAM,KAAK,kCAAkC,KAAK,UAAU,CAAC;;CAG/D,AAAQ,WAA0B;AAChC,MAAI,KAAK,eAAe,KAAK,UAC3B,QAAO,QAAQ,SAAS;AAE1B,MAAI,KAAK,WACP,QAAO,KAAK,kBAAkB,QAAQ,SAAS;AAGjD,OAAK,iBAAiB,IAAI,SAAS,SAAS,WAAW;AACrD,QAAK,aAAa;GAClB,IAAI,UAAU;GAEd,MAAM,UAAU,OAAmB;AACjC,QAAI,QACF;AAEF,cAAU;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,QAAI;;GAGN,MAAM,KAAK,IAAI,UAAU,KAAK,KAAK,IAAI;AACvC,QAAK,YAAY;AAEjB,MAAG,YAAY,KAAK;AACpB,MAAG,WAAW,UAAe;AAC3B,iBAAa;AACX,UAAK,YAAY;AAEjB,UAAK,KAAK,UAAU,MAAM;AAC1B,YAAO,OAAO,yBAAS,IAAI,MAAM,kBAAkB,CAAC;MACpD;;AAEJ,MAAG,gBAAgB;AACjB,QAAI,CAAC,WAAW,CAAC,KAAK,WAAW;AAC/B,kBAAa;AACX,6BAAO,IAAI,MAAM,+BAA+B,CAAC;OACjD;AACF;;AAGF,QAAI,KAAK,WAAW;AAClB,UAAK,YAAY;AACjB,UAAK,KAAK,WAAW;;AAEvB,QAAI,KAAK,KAAK,iBAAiB,CAAC,KAAK,YACnC,CAAK,KAAK,oCAAoC;;AAGlD,MAAG,eAAe;AAChB,iBAAa;AACX,UAAK,YAAY;AAEjB,SAAI,KAAK,KAAK,MACZ,MAAK,iBAAiB;SAEtB,MAAK,aAAa;AAEpB,cAAS;MACT;;IAEJ;AAEF,SAAO,KAAK;;CAGd,MAAM,UAAU;AACd,MAAI,KAAK,UACP;AAEF,MAAI,KAAK,YACP,QAAO,KAAK;AAGd,OAAK,cAAc,KAAK,oCAAoC,CAAC,cAAe,KAAK,cAAc,OAAW;AAE1G,SAAO,KAAK;;CAGd,AAAQ,cAAc;AACpB,OAAK,KAAK;GACR,MAAM;GACN,MAAM;IACJ,MAAM,KAAK,KAAK;IAChB,gBAAgB,KAAK,KAAK;IAC3B;GACF,CAAC;;CAGJ,AAAQ,kBAAkB;AACxB,MAAI,KAAK,KAAK,MACZ,MAAK,KAAK;GACR,MAAM;GACN,MAAM,EAAE,OAAO,KAAK,KAAK,OAAO;GACjC,CAAC;;CAKN,AAAiB,sBAAsB,UAAwB;AAC7D,EAAK,KAAK,cAAc,MAAM;;CAGhC,MAAc,cAAc,OAAqB;AAC/C,MAAI;GACF,MAAM,OAAO,KAAK,MAAM,MAAM,KAAe;GAC7C,MAAM,YAAY,KAAK,eAAe,IAAI,KAAK,KAAK;AACpD,OAAI,CAAC,WAAW,KACd;GAIF,MAAMA,aAA8B,EAAE;AACtC,QAAK,MAAM,YAAY,UACrB,YAAW,KAAK,QAAQ,QAAQ,SAAS,KAAY,CAAC,CAAC;AAGzD,SAAM,QAAQ,WAAW,WAAW;WAE/B,KAAK;AACV,WAAQ,MAAM,4BAA4B,IAAI;AAC9C,QAAK,KAAK,UAAU,IAAI;;;CAI5B,QACE,OACA,UACM;EACN,IAAI,YAAY,KAAK,eAAe,IAAI,MAAM;AAC9C,MAAI,CAAC,WAAW;AACd,+BAAY,IAAI,KAAK;AACrB,QAAK,eAAe,IAAI,OAAO,UAAU;;AAE3C,YAAU,IAAI,SAAgB;;CAGhC,SACE,OACA,UACM;EACN,MAAM,YAAY,KAAK,eAAe,IAAI,MAAM;AAChD,MAAI,CAAC,UACH;AAGF,MAAI,UAAU;AACZ,aAAU,OAAO,SAAgB;AACjC,OAAI,CAAC,UAAU,KACb,MAAK,eAAe,OAAO,MAAM;QAInC,MAAK,eAAe,OAAO,MAAM;;CAIrC,KAAK,MAA6C;AAChD,MAAI,KAAK,aAAa,KAAK,UACzB,MAAK,UAAU,KAAK,KAAK,UAAU;GAAE,QAAQ,KAAK,KAAK;GAAuC,GAAG;GAAM,CAAsB,CAAC;;CAIlI,QAAQ,MAAwD;AAC9D,MAAI,KAAK,aAAa,KAAK,UACzB,MAAK,UAAU,KAAK,KAAK;;CAI7B,QAAc;AACZ,OAAK,cAAc;AACnB,MAAI,KAAK,WAAW;AAClB,QAAK,UAAU,OAAO;AACtB,QAAK,YAAY;;;CAIrB,MAAc,8BAA8B;AAC1C,MAAI,KAAK,YACP;EAEF,MAAM,KAAK,KAAK;AAChB,OAAK,YAAY;AACjB,MAAI,MAAM,GAAG,eAAe,UAAU,UAAU,GAAG,eAAe,UAAU,QAC1E,IAAG,OAAO;AAGZ,QAAM,KAAK,SAAS"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["executions: Promise<void>[]"],"sources":["../../../node_modules/.pnpm/@moeru+std@0.1.0-beta.14/node_modules/@moeru/std/dist/sleep/index.js","../src/client.ts"],"sourcesContent":["const sleep = async (delay) => new Promise((resolve) => setTimeout(resolve, delay));\n\nexport { sleep };\n","import type {\n MetadataEventSource,\n WebSocketBaseEvent,\n WebSocketEvent,\n WebSocketEventOptionalSource,\n WebSocketEvents,\n} from '@proj-airi/server-shared/types'\n\nimport WebSocket from 'crossws/websocket'\nimport superjson from 'superjson'\n\nimport { sleep } from '@moeru/std'\nimport {\n MessageHeartbeat,\n MessageHeartbeatKind,\n} from '@proj-airi/server-shared/types'\n\nexport interface ClientOptions<C = undefined> {\n url?: string\n name: string\n possibleEvents?: Array<keyof WebSocketEvents<C>>\n token?: string\n identity?: MetadataEventSource\n heartbeat?: {\n readTimeout?: number\n message?: MessageHeartbeat | string\n }\n onError?: (error: unknown) => void\n onClose?: () => void\n autoConnect?: boolean\n autoReconnect?: boolean\n maxReconnectAttempts?: number\n onAnyMessage?: (data: WebSocketEvent<C>) => void\n onAnySend?: (data: WebSocketEvent<C>) => void\n}\n\nfunction createInstanceId() {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction createEventId() {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n}\n\nexport class Client<C = undefined> {\n private connected = false\n private connecting = false\n private websocket?: WebSocket\n private shouldClose = false\n private connectAttempt?: Promise<void>\n private connectTask?: Promise<void>\n private heartbeatTimer?: ReturnType<typeof setInterval>\n private readonly identity: MetadataEventSource\n\n private readonly opts: Required<Omit<ClientOptions<C>, 'token'>> & Pick<ClientOptions<C>, 'token'>\n private readonly eventListeners = new Map<\n keyof WebSocketEvents<C>,\n Set<(data: WebSocketBaseEvent<any, any>) => void | Promise<void>>\n >()\n\n constructor(options: ClientOptions<C>) {\n const identity = options.identity ?? {\n plugin: options.name,\n instanceId: createInstanceId(),\n }\n\n this.opts = {\n url: 'ws://localhost:6121/ws',\n onAnyMessage: () => {},\n onAnySend: () => {},\n possibleEvents: [],\n onError: () => {},\n onClose: () => {},\n autoConnect: true,\n autoReconnect: true,\n maxReconnectAttempts: -1,\n heartbeat: {\n readTimeout: 30_000,\n message: MessageHeartbeat.Ping,\n },\n ...options,\n identity,\n }\n\n this.identity = identity\n\n // Authentication listener is registered once only\n this.onEvent('module:authenticated', async (event) => {\n if (event.data.authenticated) {\n this.tryAnnounce()\n }\n else {\n await this.retryWithExponentialBackoff(() => this.tryAuthenticate())\n }\n })\n\n this.onEvent('error', async (event) => {\n if (event.data.message === 'not authenticated') {\n await this._reconnectDueToUnauthorized()\n }\n })\n\n this.onEvent('transport:connection:heartbeat', (event) => {\n if (event.data.kind === MessageHeartbeatKind.Ping) {\n this.sendHeartbeatPong()\n }\n })\n\n if (this.opts.autoConnect) {\n void this.connect()\n }\n }\n\n private async retryWithExponentialBackoff(fn: () => void | Promise<void>) {\n const { maxReconnectAttempts } = this.opts\n let attempts = 0\n\n // Loop until attempts exceed maxReconnectAttempts, or unlimited if -1\n while (true) {\n if (maxReconnectAttempts !== -1 && attempts >= maxReconnectAttempts) {\n console.error(`Maximum retry attempts (${maxReconnectAttempts}) reached`)\n return\n }\n\n try {\n await fn()\n return\n }\n catch (err) {\n this.opts.onError?.(err)\n const delay = Math.min(2 ** attempts * 1000, 30_000) // capped exponential backoff\n await sleep(delay)\n attempts++\n }\n }\n }\n\n private async tryReconnectWithExponentialBackoff() {\n if (this.shouldClose) {\n throw new Error('Client is closed')\n }\n\n await this.retryWithExponentialBackoff(() => this._connect())\n }\n\n private _connect(): Promise<void> {\n if (this.shouldClose || this.connected) {\n return Promise.resolve()\n }\n if (this.connecting) {\n return this.connectAttempt ?? Promise.resolve()\n }\n\n this.connectAttempt = new Promise((resolve, reject) => {\n this.connecting = true\n let settled = false\n\n const settle = (fn: () => void) => {\n if (settled)\n return\n\n settled = true\n this.connecting = false\n this.connectAttempt = undefined\n fn()\n }\n\n const ws = new WebSocket(this.opts.url)\n this.websocket = ws\n\n ws.onmessage = this.handleMessageBound\n ws.onerror = (event: any) => {\n settle(() => {\n this.connected = false\n\n this.opts.onError?.(event)\n reject(event?.error ?? new Error('WebSocket error'))\n })\n }\n ws.onclose = () => {\n if (!settled && !this.connected) {\n settle(() => {\n reject(new Error('WebSocket closed before open'))\n })\n return\n }\n\n if (this.connected) {\n this.connected = false\n this.stopHeartbeat()\n this.opts.onClose?.()\n }\n if (this.opts.autoReconnect && !this.shouldClose) {\n void this.tryReconnectWithExponentialBackoff()\n }\n }\n ws.onopen = () => {\n settle(() => {\n this.connected = true\n\n this.startHeartbeat()\n\n if (this.opts.token)\n this.tryAuthenticate()\n else\n this.tryAnnounce()\n\n resolve()\n })\n }\n })\n\n return this.connectAttempt\n }\n\n async connect() {\n if (this.connected) {\n return\n }\n if (this.connectTask) {\n return this.connectTask\n }\n\n this.connectTask = this.tryReconnectWithExponentialBackoff().finally(() => (this.connectTask = undefined))\n\n return this.connectTask\n }\n\n private tryAnnounce() {\n this.send({\n type: 'module:announce',\n data: {\n name: this.opts.name,\n identity: this.identity,\n possibleEvents: this.opts.possibleEvents,\n },\n })\n }\n\n private tryAuthenticate() {\n if (this.opts.token) {\n this.send({\n type: 'module:authenticate',\n data: { token: this.opts.token },\n })\n }\n }\n\n // bound reference avoids new closure allocation on every connect\n private readonly handleMessageBound = (event: MessageEvent) => {\n void this.handleMessage(event)\n }\n\n private async handleMessage(event: MessageEvent) {\n try {\n const data = superjson.parse<WebSocketEvent<C> | undefined>(event.data as string)\n if (!data) {\n console.warn('Received empty message')\n return\n }\n\n this.opts.onAnyMessage?.(data)\n const listeners = this.eventListeners.get(data.type)\n if (!listeners?.size) {\n return\n }\n\n // Execute all listeners concurrently\n const executions: Promise<void>[] = []\n for (const listener of listeners) {\n executions.push(Promise.resolve(listener(data as any)))\n }\n\n await Promise.allSettled(executions)\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 let listeners = this.eventListeners.get(event)\n if (!listeners) {\n listeners = new Set()\n this.eventListeners.set(event, listeners)\n }\n listeners.add(callback as any)\n }\n\n offEvent<E extends keyof WebSocketEvents<C>>(\n event: E,\n callback?: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void,\n ): void {\n const listeners = this.eventListeners.get(event)\n if (!listeners) {\n return\n }\n\n if (callback) {\n listeners.delete(callback as any)\n if (!listeners.size) {\n this.eventListeners.delete(event)\n }\n }\n else {\n this.eventListeners.delete(event)\n }\n }\n\n send(data: WebSocketEventOptionalSource<C>): void {\n if (this.websocket && this.connected) {\n const payload = {\n ...data,\n metadata: {\n ...data?.metadata,\n source: data?.metadata?.source ?? this.identity,\n event: {\n id: data?.metadata?.event?.id ?? createEventId(),\n ...data?.metadata?.event,\n },\n },\n } as WebSocketEvent<C>\n\n this.opts.onAnySend?.(payload)\n\n this.websocket.send(superjson.stringify(payload))\n }\n }\n\n sendRaw(data: string | ArrayBufferLike | ArrayBufferView): void {\n if (this.websocket && this.connected) {\n this.websocket.send(data)\n }\n }\n\n close(): void {\n this.shouldClose = true\n this.stopHeartbeat()\n if (this.websocket) {\n this.websocket.close()\n this.connected = false\n }\n }\n\n private startHeartbeat() {\n if (!this.opts.heartbeat?.readTimeout) {\n return\n }\n\n this.stopHeartbeat()\n\n const ping = () => this.sendHeartbeatPing()\n\n ping()\n this.heartbeatTimer = setInterval(ping, this.opts.heartbeat.readTimeout)\n }\n\n private stopHeartbeat() {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n }\n\n private sendNativeHeartbeat(kind: 'ping' | 'pong') {\n const websocket = this.websocket as WebSocket & {\n ping?: () => void\n pong?: () => void\n }\n\n if (kind === 'ping') {\n websocket.ping?.()\n }\n else {\n websocket.pong?.()\n }\n }\n\n private sendHeartbeatPing() {\n this.send({\n type: 'transport:connection:heartbeat',\n data: {\n kind: MessageHeartbeatKind.Ping,\n message: this.opts.heartbeat?.message ?? MessageHeartbeat.Ping,\n at: Date.now(),\n },\n })\n this.sendNativeHeartbeat('ping')\n }\n\n private sendHeartbeatPong() {\n this.send({\n type: 'transport:connection:heartbeat',\n data: {\n kind: MessageHeartbeatKind.Pong,\n message: MessageHeartbeat.Pong,\n at: Date.now(),\n },\n })\n this.sendNativeHeartbeat('pong')\n }\n\n private async _reconnectDueToUnauthorized() {\n if (this.shouldClose)\n return\n\n const ws = this.websocket\n this.connected = false\n if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {\n ws.close()\n }\n\n await this.connect()\n }\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proj-airi/server-sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.8.1-beta.
|
|
4
|
+
"version": "0.8.1-beta.10",
|
|
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",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"crossws": "^0.4.1",
|
|
36
36
|
"defu": "^6.1.4",
|
|
37
|
-
"
|
|
37
|
+
"superjson": "^2.2.6",
|
|
38
|
+
"@proj-airi/server-shared": "^0.8.1-beta.10"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@moeru/std": "0.1.0-beta.14"
|