@proj-airi/server-sdk 0.9.0-beta.4 → 0.9.0-beta.7

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 CHANGED
@@ -1,6 +1,31 @@
1
1
  import { ContextUpdateStrategy, MessageHeartbeat, MetadataEventSource, ModuleConfigSchema, ModuleDependency, WebSocketBaseEvent, WebSocketEvent, WebSocketEventOptionalSource, WebSocketEventSource, WebSocketEvents } from "@proj-airi/server-shared/types";
2
2
  export * from "@proj-airi/server-shared/types";
3
3
 
4
+ //#region src/websocket-like.d.ts
5
+ interface WebSocketMessageEventLike<T = string> {
6
+ data: T;
7
+ }
8
+ interface WebSocketErrorEventLike {
9
+ error?: Error | unknown;
10
+ }
11
+ interface WebSocketLike {
12
+ readonly readyState: number;
13
+ onopen?: (event?: unknown) => void;
14
+ onmessage?: (event: WebSocketMessageEventLike) => void;
15
+ onerror?: (event: WebSocketErrorEventLike | unknown) => void;
16
+ onclose?: (event?: unknown) => void;
17
+ send: (data: string | ArrayBufferLike | ArrayBufferView) => void;
18
+ close: (code?: number, reason?: string) => void;
19
+ ping?: () => void;
20
+ pong?: () => void;
21
+ }
22
+ interface WebSocketLikeConstructor {
23
+ readonly OPEN: number;
24
+ readonly CLOSING: number;
25
+ readonly CLOSED: number;
26
+ new (url: string): WebSocketLike;
27
+ }
28
+ //#endregion
4
29
  //#region src/client.d.ts
5
30
  type ClientStatus = 'idle' | 'connecting' | 'authenticating' | 'announcing' | 'ready' | 'reconnecting' | 'closing' | 'closed' | 'failed';
6
31
  interface ClientHeartbeatOptions {
@@ -20,6 +45,8 @@ interface ClientOptions<C = undefined> {
20
45
  url?: string;
21
46
  name: string;
22
47
  token?: string;
48
+ websocketConstructor?: WebSocketLikeConstructor;
49
+ connectTimeoutMs?: number;
23
50
  possibleEvents?: Array<keyof WebSocketEvents<C>>;
24
51
  identity?: MetadataEventSource;
25
52
  dependencies?: ModuleDependency[];
@@ -49,6 +76,7 @@ declare class Client<C = undefined> {
49
76
  private status;
50
77
  private readonly identity;
51
78
  private readonly heartbeat;
79
+ private readonly websocketConstructor;
52
80
  private readonly opts;
53
81
  private readonly eventListeners;
54
82
  private readonly stateListeners;
@@ -93,5 +121,5 @@ declare class Client<C = undefined> {
93
121
  private reconnectAfterProtocolError;
94
122
  }
95
123
  //#endregion
96
- export { Client, ClientHeartbeatOptions, ClientOptions, ClientStateChangeContext, ClientStatus, ConnectOptions, ContextUpdateStrategy, WebSocketEventSource };
124
+ export { Client, ClientHeartbeatOptions, ClientOptions, ClientStateChangeContext, ClientStatus, ConnectOptions, ContextUpdateStrategy, WebSocketErrorEventLike, WebSocketEventSource, WebSocketLike, WebSocketLikeConstructor, WebSocketMessageEventLike };
97
125
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import WebSocket from "crossws/websocket";
1
+ import NativeWebSocket from "crossws/websocket";
2
2
  import superjson from "superjson";
3
3
  import { errorMessageFrom, sleep } from "@moeru/std";
4
4
  import { isTerminalAuthenticationServerErrorMessage, parseServerErrorMessage } from "@proj-airi/server-shared";
@@ -45,10 +45,12 @@ var Client = class {
45
45
  status = "idle";
46
46
  identity;
47
47
  heartbeat;
48
+ websocketConstructor;
48
49
  opts;
49
50
  eventListeners = /* @__PURE__ */ new Map();
50
51
  stateListeners = /* @__PURE__ */ new Set();
51
52
  constructor(options) {
53
+ const { websocketConstructor, ...clientOptions } = options;
52
54
  const identity = options.identity ?? {
53
55
  kind: "plugin",
54
56
  plugin: { id: options.name },
@@ -57,6 +59,7 @@ var Client = class {
57
59
  const heartbeat = normalizeHeartbeatOptions(options.heartbeat);
58
60
  this.opts = {
59
61
  url: "ws://localhost:6121/ws",
62
+ connectTimeoutMs: 15e3,
60
63
  onAnyMessage: () => {},
61
64
  onAnySend: () => {},
62
65
  possibleEvents: [],
@@ -69,12 +72,13 @@ var Client = class {
69
72
  autoConnect: true,
70
73
  autoReconnect: true,
71
74
  maxReconnectAttempts: -1,
72
- ...options,
75
+ ...clientOptions,
73
76
  heartbeat,
74
77
  identity
75
78
  };
76
79
  this.identity = identity;
77
80
  this.heartbeat = heartbeat;
81
+ this.websocketConstructor = websocketConstructor ?? NativeWebSocket;
78
82
  if (this.opts.autoConnect) this.connect();
79
83
  }
80
84
  get connectionStatus() {
@@ -84,7 +88,7 @@ var Client = class {
84
88
  return this.status === "ready";
85
89
  }
86
90
  get isSocketOpen() {
87
- return this.websocket?.readyState === WebSocket.OPEN;
91
+ return this.websocket?.readyState === this.websocketConstructor.OPEN;
88
92
  }
89
93
  get lastError() {
90
94
  return this.failureReason;
@@ -152,7 +156,7 @@ var Client = class {
152
156
  this.rejectAttempt(/* @__PURE__ */ new Error("Client closed"));
153
157
  const websocket = this.websocket;
154
158
  this.websocket = void 0;
155
- if (websocket && websocket.readyState !== WebSocket.CLOSED && websocket.readyState !== WebSocket.CLOSING) websocket.close();
159
+ if (websocket && websocket.readyState !== this.websocketConstructor.CLOSED && websocket.readyState !== this.websocketConstructor.CLOSING) websocket.close();
156
160
  this.transitionTo("closed");
157
161
  }
158
162
  async runConnectLoop() {
@@ -189,7 +193,8 @@ var Client = class {
189
193
  throw new Error("Client is closed");
190
194
  }
191
195
  connectOnce() {
192
- const ws = new WebSocket(this.opts.url);
196
+ const WebSocketConstructor = this.websocketConstructor;
197
+ const ws = new WebSocketConstructor(this.opts.url);
193
198
  this.websocket = ws;
194
199
  this.lastReadAt = Date.now();
195
200
  this.lastPingAt = 0;
@@ -204,11 +209,21 @@ var Client = class {
204
209
  };
205
210
  this.connectionAttempt = attempt;
206
211
  const isCurrentSocket = () => this.websocket === ws;
212
+ const connectTimeoutMs = this.opts.connectTimeoutMs;
213
+ const connectTimer = setTimeout(() => {
214
+ if (ws.readyState === WebSocket.OPEN) return;
215
+ ws.close();
216
+ deferred.reject(/* @__PURE__ */ new Error(`Connection timeout after ${connectTimeoutMs}ms`));
217
+ }, connectTimeoutMs);
218
+ const clearConnectTimer = () => {
219
+ clearTimeout(connectTimer);
220
+ };
207
221
  ws.onmessage = (event) => {
208
222
  if (!isCurrentSocket()) return;
209
223
  this.handleMessage(event);
210
224
  };
211
225
  ws.onerror = (event) => {
226
+ clearConnectTimer();
212
227
  if (!isCurrentSocket()) return;
213
228
  const error = event?.error instanceof Error ? event.error : /* @__PURE__ */ new Error("WebSocket error");
214
229
  if (this.connectionAttempt) this.handleSocketFailure(error, ws);
@@ -218,6 +233,7 @@ var Client = class {
218
233
  }
219
234
  };
220
235
  ws.onclose = () => {
236
+ clearConnectTimer();
221
237
  if (!isCurrentSocket()) return;
222
238
  const wasReady = this.status === "ready";
223
239
  this.cleanupSocket(ws);
@@ -225,12 +241,14 @@ var Client = class {
225
241
  if (this.shouldClose) return;
226
242
  if (wasReady && this.opts.autoReconnect) {
227
243
  this.pendingReconnect = true;
244
+ this.transitionTo("idle");
228
245
  this.connect();
229
246
  return;
230
247
  }
231
248
  this.rejectAttempt(/* @__PURE__ */ new Error("WebSocket closed"));
232
249
  };
233
250
  ws.onopen = () => {
251
+ clearConnectTimer();
234
252
  if (!isCurrentSocket()) return;
235
253
  this.startHeartbeat();
236
254
  if (this.opts.token) {
@@ -249,7 +267,7 @@ var Client = class {
249
267
  if (socket && this.websocket !== socket) return;
250
268
  const currentSocket = socket ?? this.websocket;
251
269
  this.cleanupSocket(socket);
252
- if (currentSocket && currentSocket.readyState !== WebSocket.CLOSED && currentSocket.readyState !== WebSocket.CLOSING) currentSocket.close();
270
+ if (currentSocket && currentSocket.readyState !== this.websocketConstructor.CLOSED && currentSocket.readyState !== this.websocketConstructor.CLOSING) currentSocket.close();
253
271
  this.rejectAttempt(error);
254
272
  }
255
273
  cleanupSocket(socket) {
@@ -384,6 +402,16 @@ var Client = class {
384
402
  throw new Error("Authentication failed");
385
403
  case "module:announced":
386
404
  if (!this.isSelfAnnouncement(data)) return;
405
+ if (this.status === "ready") return;
406
+ if (this.connectionAttempt) this.connectionAttempt.announced = true;
407
+ this.reconnectAttempts = 0;
408
+ this.transitionTo("ready");
409
+ this.resolveAttempt();
410
+ this.opts.onReady?.();
411
+ return;
412
+ case "registry:modules:sync":
413
+ if (this.status !== "announcing" || !this.connectionAttempt) return;
414
+ if (!(data.data?.modules ?? []).some((m) => m.name === this.opts.name && m.identity?.id === this.identity.id)) return;
387
415
  if (this.connectionAttempt) this.connectionAttempt.announced = true;
388
416
  this.reconnectAttempts = 0;
389
417
  this.transitionTo("ready");
@@ -472,12 +500,13 @@ var Client = class {
472
500
  const websocket = this.websocket;
473
501
  this.cleanupSocket(websocket);
474
502
  this.rejectAttempt(error);
475
- if (websocket && websocket.readyState !== WebSocket.CLOSED && websocket.readyState !== WebSocket.CLOSING) websocket.close();
503
+ if (websocket && websocket.readyState !== this.websocketConstructor.CLOSED && websocket.readyState !== this.websocketConstructor.CLOSING) websocket.close();
476
504
  if (hadSocket) this.opts.onClose?.();
477
505
  if (!this.opts.autoReconnect) {
478
506
  this.transitionTo("failed");
479
507
  return;
480
508
  }
509
+ this.transitionTo("idle");
481
510
  this.connect();
482
511
  }
483
512
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type {\n MetadataEventSource,\n ModuleConfigSchema,\n ModuleDependency,\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 { errorMessageFrom, sleep } from '@moeru/std'\nimport { isTerminalAuthenticationServerErrorMessage, parseServerErrorMessage } from '@proj-airi/server-shared'\nimport { MessageHeartbeat, MessageHeartbeatKind } from '@proj-airi/server-shared/types'\n\nexport type ClientStatus\n = | 'idle'\n | 'connecting'\n | 'authenticating'\n | 'announcing'\n | 'ready'\n | 'reconnecting'\n | 'closing'\n | 'closed'\n | 'failed'\n\nexport interface ClientHeartbeatOptions {\n pingInterval?: number\n readTimeout?: number\n message?: MessageHeartbeat | string\n}\n\nexport interface ClientStateChangeContext {\n previousStatus: ClientStatus\n status: ClientStatus\n}\n\nexport interface ConnectOptions {\n abortSignal?: AbortSignal\n timeout?: number\n}\n\nexport interface ClientOptions<C = undefined> {\n url?: string\n name: string\n token?: string\n\n possibleEvents?: Array<keyof WebSocketEvents<C>>\n identity?: MetadataEventSource\n dependencies?: ModuleDependency[]\n configSchema?: ModuleConfigSchema\n heartbeat?: ClientHeartbeatOptions\n\n autoConnect?: boolean\n autoReconnect?: boolean\n maxReconnectAttempts?: number\n\n onError?: (error: unknown) => void\n onClose?: () => void\n onReady?: () => void\n onStateChange?: (context: ClientStateChangeContext) => void\n\n onAnyMessage?: (data: WebSocketEvent<C>) => void\n onAnySend?: (data: WebSocketEvent<C>) => void\n}\n\ninterface ConnectionAttempt {\n announced: boolean\n authenticated: boolean\n promise: Promise<void>\n reject: (error: Error) => void\n resolve: () => void\n socket: WebSocket\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\nfunction createDeferredPromise() {\n let resolve!: () => void\n let reject!: (error: Error) => void\n\n const promise = new Promise<void>((innerResolve, innerReject) => {\n resolve = innerResolve\n reject = innerReject\n })\n\n return { promise, reject, resolve }\n}\n\nfunction normalizeHeartbeatOptions(heartbeat?: ClientHeartbeatOptions): Required<ClientHeartbeatOptions> {\n const readTimeout = heartbeat?.readTimeout ?? 30_000\n const pingInterval = heartbeat?.pingInterval ?? Math.max(1_000, Math.floor(readTimeout / 2))\n\n return {\n readTimeout,\n pingInterval: Math.min(pingInterval, readTimeout),\n message: heartbeat?.message ?? MessageHeartbeat.Ping,\n }\n}\n\nexport class Client<C = undefined> {\n private websocket?: WebSocket\n private shouldClose = false\n private connectTask?: Promise<void>\n private heartbeatTimer?: ReturnType<typeof setInterval>\n private lastPingAt = 0\n private lastReadAt = 0\n private reconnectAttempts = 0\n private pendingReconnect = false\n private connectionAttempt?: ConnectionAttempt\n private failureReason?: Error\n private status: ClientStatus = 'idle'\n private readonly identity: MetadataEventSource\n private readonly heartbeat: Required<ClientHeartbeatOptions>\n\n private readonly opts: Required<Omit<ClientOptions<C>, 'token' | 'heartbeat'>> & Pick<ClientOptions<C>, 'token'> & {\n heartbeat: Required<ClientHeartbeatOptions>\n }\n\n private readonly eventListeners = new Map<\n keyof WebSocketEvents<C>,\n Set<(data: WebSocketBaseEvent<any, any>) => void | Promise<void>>\n >()\n\n private readonly stateListeners = new Set<(context: ClientStateChangeContext) => void>()\n\n constructor(options: ClientOptions<C>) {\n const identity = options.identity ?? {\n kind: 'plugin',\n plugin: { id: options.name },\n id: createInstanceId(),\n }\n\n const heartbeat = normalizeHeartbeatOptions(options.heartbeat)\n\n this.opts = {\n url: 'ws://localhost:6121/ws',\n onAnyMessage: () => {},\n onAnySend: () => {},\n possibleEvents: [],\n dependencies: [],\n configSchema: undefined,\n onError: () => {},\n onClose: () => {},\n onReady: () => {},\n onStateChange: () => {},\n autoConnect: true,\n autoReconnect: true,\n maxReconnectAttempts: -1,\n ...options,\n heartbeat,\n identity,\n }\n\n this.identity = identity\n this.heartbeat = heartbeat\n\n if (this.opts.autoConnect) {\n void this.connect()\n }\n }\n\n get connectionStatus() {\n return this.status\n }\n\n get isReady() {\n return this.status === 'ready'\n }\n\n get isSocketOpen() {\n return this.websocket?.readyState === WebSocket.OPEN\n }\n\n get lastError() {\n return this.failureReason\n }\n\n async connect(options?: ConnectOptions) {\n if (this.shouldClose) {\n throw new Error('Client is closed')\n }\n\n if (this.status === 'ready') {\n return\n }\n\n if (this.connectTask) {\n return this.waitForConnection(this.connectTask, options)\n }\n\n this.connectTask = this.runConnectLoop().finally(() => {\n this.connectTask = undefined\n })\n\n return this.waitForConnection(this.connectTask, options)\n }\n\n ready(options?: ConnectOptions) {\n return this.connect(options)\n }\n\n ensureConnected(options?: ConnectOptions) {\n return this.connect(options)\n }\n\n onConnectionStateChange(callback: (context: ClientStateChangeContext) => void): () => void {\n this.stateListeners.add(callback)\n\n return () => {\n this.stateListeners.delete(callback)\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\n listeners.add(callback as any)\n\n return () => {\n this.offEvent(event, callback)\n }\n }\n\n offEvent<E extends keyof WebSocketEvents<C>>(\n event: E,\n callback?: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<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>): boolean {\n if (!this.isSocketOpen || !this.websocket) {\n return false\n }\n\n const payload = this.createPayload(data)\n this.opts.onAnySend?.(payload)\n this.websocket.send(superjson.stringify(payload))\n\n return true\n }\n\n sendOrThrow(data: WebSocketEventOptionalSource<C>): void {\n if (!this.send(data)) {\n throw new Error(`Client is not connected, current status: ${this.status}`)\n }\n }\n\n sendRaw(data: string | ArrayBufferLike | ArrayBufferView): boolean {\n if (!this.isSocketOpen || !this.websocket) {\n return false\n }\n\n this.websocket.send(data)\n return true\n }\n\n close(): void {\n this.shouldClose = true\n this.pendingReconnect = false\n this.transitionTo('closing')\n this.stopHeartbeat()\n this.rejectAttempt(new Error('Client closed'))\n\n const websocket = this.websocket\n this.websocket = undefined\n\n if (websocket && websocket.readyState !== WebSocket.CLOSED && websocket.readyState !== WebSocket.CLOSING) {\n websocket.close()\n }\n\n this.transitionTo('closed')\n }\n\n private async runConnectLoop() {\n this.pendingReconnect = false\n\n while (!this.shouldClose) {\n const reconnecting = this.reconnectAttempts > 0\n this.transitionTo(reconnecting ? 'reconnecting' : 'connecting')\n\n try {\n await this.connectOnce()\n this.reconnectAttempts = 0\n return\n }\n catch (error) {\n const normalizedError = error instanceof Error ? error : new Error(errorMessageFrom(error) ?? 'Failed to connect websocket client')\n this.failureReason = normalizedError\n this.opts.onError?.(normalizedError)\n\n if (this.shouldClose) {\n throw normalizedError\n }\n\n if (isTerminalAuthenticationServerErrorMessage(normalizedError.message)) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n if (!this.opts.autoReconnect && reconnecting) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n if (!this.canRetry()) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n const delay = this.getReconnectDelay(this.reconnectAttempts)\n this.reconnectAttempts += 1\n await sleep(delay)\n }\n }\n\n throw new Error('Client is closed')\n }\n\n private connectOnce(): Promise<void> {\n const ws = new WebSocket(this.opts.url)\n this.websocket = ws\n this.lastReadAt = Date.now()\n this.lastPingAt = 0\n\n const deferred = createDeferredPromise()\n const attempt: ConnectionAttempt = {\n announced: false,\n authenticated: !this.opts.token,\n promise: deferred.promise,\n reject: deferred.reject,\n resolve: deferred.resolve,\n socket: ws,\n }\n\n this.connectionAttempt = attempt\n\n const isCurrentSocket = () => this.websocket === ws\n\n ws.onmessage = (event: MessageEvent) => {\n if (!isCurrentSocket()) {\n return\n }\n\n void this.handleMessage(event)\n }\n\n ws.onerror = (event: any) => {\n if (!isCurrentSocket()) {\n return\n }\n\n const error = event?.error instanceof Error ? event.error : new Error('WebSocket error')\n if (this.connectionAttempt) {\n this.handleSocketFailure(error, ws)\n }\n else {\n this.opts.onError?.(error)\n void this.reconnectAfterProtocolError(error)\n }\n }\n\n ws.onclose = () => {\n if (!isCurrentSocket()) {\n return\n }\n\n const wasReady = this.status === 'ready'\n this.cleanupSocket(ws)\n this.opts.onClose?.()\n\n if (this.shouldClose) {\n return\n }\n\n if (wasReady && this.opts.autoReconnect) {\n this.pendingReconnect = true\n void this.connect()\n return\n }\n\n this.rejectAttempt(new Error('WebSocket closed'))\n }\n\n ws.onopen = () => {\n if (!isCurrentSocket()) {\n return\n }\n\n this.startHeartbeat()\n\n if (this.opts.token) {\n attempt.authenticated = false\n this.transitionTo('authenticating')\n this.tryAuthenticate()\n }\n else {\n attempt.authenticated = true\n this.transitionTo('announcing')\n this.tryAnnounce()\n }\n }\n\n return attempt.promise\n }\n\n private handleSocketFailure(error: Error, socket?: WebSocket) {\n if (socket && this.websocket !== socket) {\n return\n }\n\n const currentSocket = socket ?? this.websocket\n this.cleanupSocket(socket)\n\n if (currentSocket && currentSocket.readyState !== WebSocket.CLOSED && currentSocket.readyState !== WebSocket.CLOSING) {\n currentSocket.close()\n }\n\n this.rejectAttempt(error)\n }\n\n private cleanupSocket(socket?: WebSocket) {\n if (socket && this.websocket !== socket) {\n return\n }\n\n this.stopHeartbeat()\n\n if (!socket || this.websocket === socket) {\n this.websocket = undefined\n }\n }\n\n private rejectAttempt(error: Error) {\n if (!this.connectionAttempt) {\n return\n }\n\n const attempt = this.connectionAttempt\n this.connectionAttempt = undefined\n attempt.reject(error)\n }\n\n private resolveAttempt() {\n if (!this.connectionAttempt) {\n return\n }\n\n const attempt = this.connectionAttempt\n this.connectionAttempt = undefined\n attempt.resolve()\n }\n\n private canRetry() {\n return this.opts.maxReconnectAttempts === -1 || this.reconnectAttempts < this.opts.maxReconnectAttempts\n }\n\n private getReconnectDelay(attempts: number) {\n return Math.min(2 ** attempts * 1_000, 30_000)\n }\n\n private transitionTo(status: ClientStatus) {\n if (this.status === status) {\n return\n }\n\n const previousStatus = this.status\n this.status = status\n const context = { previousStatus, status }\n\n this.opts.onStateChange?.(context)\n\n for (const listener of this.stateListeners) {\n listener(context)\n }\n }\n\n private async waitForConnection(connectPromise: Promise<void>, options?: ConnectOptions) {\n if (!options?.timeout && !options?.abortSignal) {\n return connectPromise\n }\n\n const timeout = options?.timeout\n if (typeof timeout !== 'undefined' && timeout <= 0) {\n throw new Error(`Connection timed out after ${timeout}ms`)\n }\n\n const abortSignal = options?.abortSignal\n if (abortSignal?.aborted) {\n throw new Error('Connection aborted')\n }\n\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n let removeAbortListener: (() => void) | undefined\n\n try {\n await Promise.race([\n connectPromise,\n new Promise<void>((_, reject) => {\n if (typeof timeout !== 'undefined') {\n timeoutHandle = setTimeout(() => {\n reject(new Error(`Connection timed out after ${timeout}ms`))\n }, timeout)\n }\n\n if (abortSignal) {\n const onAbort = () => {\n reject(new Error('Connection aborted'))\n }\n\n abortSignal.addEventListener('abort', onAbort, { once: true })\n removeAbortListener = () => abortSignal.removeEventListener('abort', onAbort)\n }\n }),\n ])\n }\n finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle)\n }\n\n removeAbortListener?.()\n }\n }\n\n private tryAnnounce() {\n this.sendOrThrow({\n type: 'module:announce',\n data: {\n name: this.opts.name,\n identity: this.identity,\n possibleEvents: this.opts.possibleEvents,\n dependencies: this.opts.dependencies,\n configSchema: this.opts.configSchema,\n },\n })\n }\n\n private tryAuthenticate() {\n if (!this.opts.token) {\n return\n }\n\n this.sendOrThrow({\n type: 'module:authenticate',\n data: { token: this.opts.token },\n })\n }\n\n private async handleMessage(event: MessageEvent) {\n this.lastReadAt = Date.now()\n\n try {\n const data = this.parseMessage(event.data as string)\n this.opts.onAnyMessage?.(data)\n\n await this.handleControlMessage(data)\n await this.dispatchMessage(data)\n }\n catch (error) {\n const normalizedError = error instanceof Error ? error : new Error(errorMessageFrom(error) ?? 'Failed to handle websocket message')\n this.opts.onError?.(normalizedError)\n\n if (this.connectionAttempt && this.status !== 'ready') {\n this.handleSocketFailure(normalizedError)\n }\n }\n }\n\n private parseMessage(raw: string): WebSocketEvent<C> {\n try {\n const parsed = superjson.parse<WebSocketEvent<C> | undefined>(raw)\n if (parsed && typeof parsed === 'object' && 'type' in parsed) {\n return parsed\n }\n }\n catch {\n // Try standard JSON next.\n }\n\n const parsed = JSON.parse(raw) as WebSocketEvent<C>\n if (!parsed || typeof parsed !== 'object' || !('type' in parsed)) {\n throw new Error('Received invalid websocket message')\n }\n\n return parsed\n }\n\n private async handleControlMessage(data: WebSocketEvent<C>) {\n switch (data.type) {\n case 'error': {\n const message = data.data?.message\n if (!message || typeof message !== 'string') {\n return\n }\n\n const parsedServerError = parseServerErrorMessage(message)\n if (parsedServerError.authentication) {\n const error = new Error(message)\n if (parsedServerError.terminal) {\n this.shouldClose = true\n this.handleSocketFailure(error)\n this.transitionTo('failed')\n return\n }\n\n await this.reconnectAfterProtocolError(error)\n return\n }\n\n if (parsedServerError.code !== 'unknown') {\n throw new Error(parsedServerError.message)\n }\n\n throw new Error(message)\n }\n\n case 'module:authenticated': {\n if (data.data.authenticated) {\n if (!this.connectionAttempt || this.connectionAttempt.authenticated) {\n return\n }\n\n this.connectionAttempt.authenticated = true\n this.transitionTo('announcing')\n this.tryAnnounce()\n return\n }\n\n throw new Error('Authentication failed')\n }\n\n case 'module:announced': {\n if (!this.isSelfAnnouncement(data)) {\n return\n }\n\n if (this.connectionAttempt) {\n this.connectionAttempt.announced = true\n }\n\n this.reconnectAttempts = 0\n this.transitionTo('ready')\n this.resolveAttempt()\n this.opts.onReady?.()\n return\n }\n\n case 'transport:connection:heartbeat': {\n if (data.data.kind === MessageHeartbeatKind.Ping) {\n this.sendHeartbeatPong()\n }\n }\n }\n }\n\n private isSelfAnnouncement(event: WebSocketBaseEvent<'module:announced', WebSocketEvents<C>['module:announced']>) {\n return event.data.name === this.opts.name && event.data.identity?.id === this.identity.id\n }\n\n private async dispatchMessage(data: WebSocketEvent<C>) {\n const listeners = this.eventListeners.get(data.type)\n if (!listeners?.size) {\n return\n }\n\n const results = await Promise.allSettled(\n Array.from(listeners).map(listener => Promise.resolve(listener(data as any))),\n )\n\n for (const result of results) {\n if (result.status === 'rejected') {\n this.opts.onError?.(result.reason)\n }\n }\n }\n\n private createPayload(data: WebSocketEventOptionalSource<C>) {\n return {\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\n private startHeartbeat() {\n if (!this.heartbeat.readTimeout || !this.heartbeat.pingInterval) {\n return\n }\n\n this.stopHeartbeat()\n this.lastReadAt = Date.now()\n this.lastPingAt = 0\n\n const interval = Math.max(1_000, Math.min(this.heartbeat.pingInterval, Math.floor(this.heartbeat.readTimeout / 2)))\n this.heartbeatTimer = setInterval(() => {\n if (!this.isSocketOpen) {\n return\n }\n\n const now = Date.now()\n if (now - this.lastReadAt > this.heartbeat.readTimeout) {\n void this.reconnectAfterProtocolError(new Error(`Read timeout after ${this.heartbeat.readTimeout}ms`))\n return\n }\n\n if (now - this.lastPingAt >= this.heartbeat.pingInterval) {\n this.sendHeartbeatPing()\n }\n }, interval)\n }\n\n private stopHeartbeat() {\n if (!this.heartbeatTimer) {\n return\n }\n\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\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.lastPingAt = Date.now()\n this.send({\n type: 'transport:connection:heartbeat',\n data: {\n kind: MessageHeartbeatKind.Ping,\n message: this.heartbeat.message,\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 reconnectAfterProtocolError(error: Error) {\n if (this.shouldClose || this.pendingReconnect) {\n return\n }\n\n this.pendingReconnect = true\n const hadSocket = !!this.websocket\n\n if (!this.connectionAttempt || this.status === 'ready') {\n this.opts.onError?.(error)\n }\n\n const websocket = this.websocket\n this.cleanupSocket(websocket)\n this.rejectAttempt(error)\n\n if (websocket && websocket.readyState !== WebSocket.CLOSED && websocket.readyState !== WebSocket.CLOSING) {\n websocket.close()\n }\n\n if (hadSocket) {\n this.opts.onClose?.()\n }\n\n if (!this.opts.autoReconnect) {\n this.transitionTo('failed')\n return\n }\n\n void this.connect()\n }\n}\n"],"mappings":";;;;;;AA6EA,SAAS,mBAAmB;AAC1B,QAAO,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;;AAG7E,SAAS,gBAAgB;AACvB,QAAO,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,GAAG;;AAG9E,SAAS,wBAAwB;CAC/B,IAAI;CACJ,IAAI;AAOJ,QAAO;EAAE,SALO,IAAI,SAAe,cAAc,gBAAgB;AAC/D,aAAU;AACV,YAAS;IACT;EAEgB;EAAQ;EAAS;;AAGrC,SAAS,0BAA0B,WAAsE;CACvG,MAAM,cAAc,WAAW,eAAe;CAC9C,MAAM,eAAe,WAAW,gBAAgB,KAAK,IAAI,KAAO,KAAK,MAAM,cAAc,EAAE,CAAC;AAE5F,QAAO;EACL;EACA,cAAc,KAAK,IAAI,cAAc,YAAY;EACjD,SAAS,WAAW,WAAW,iBAAiB;EACjD;;AAGH,IAAa,SAAb,MAAmC;CACjC;CACA,cAAsB;CACtB;CACA;CACA,aAAqB;CACrB,aAAqB;CACrB,oBAA4B;CAC5B,mBAA2B;CAC3B;CACA;CACA,SAA+B;CAC/B;CACA;CAEA;CAIA,iCAAkC,IAAI,KAGnC;CAEH,iCAAkC,IAAI,KAAkD;CAExF,YAAY,SAA2B;EACrC,MAAM,WAAW,QAAQ,YAAY;GACnC,MAAM;GACN,QAAQ,EAAE,IAAI,QAAQ,MAAM;GAC5B,IAAI,kBAAkB;GACvB;EAED,MAAM,YAAY,0BAA0B,QAAQ,UAAU;AAE9D,OAAK,OAAO;GACV,KAAK;GACL,oBAAoB;GACpB,iBAAiB;GACjB,gBAAgB,EAAE;GAClB,cAAc,EAAE;GAChB,cAAc,KAAA;GACd,eAAe;GACf,eAAe;GACf,eAAe;GACf,qBAAqB;GACrB,aAAa;GACb,eAAe;GACf,sBAAsB;GACtB,GAAG;GACH;GACA;GACD;AAED,OAAK,WAAW;AAChB,OAAK,YAAY;AAEjB,MAAI,KAAK,KAAK,YACP,MAAK,SAAS;;CAIvB,IAAI,mBAAmB;AACrB,SAAO,KAAK;;CAGd,IAAI,UAAU;AACZ,SAAO,KAAK,WAAW;;CAGzB,IAAI,eAAe;AACjB,SAAO,KAAK,WAAW,eAAe,UAAU;;CAGlD,IAAI,YAAY;AACd,SAAO,KAAK;;CAGd,MAAM,QAAQ,SAA0B;AACtC,MAAI,KAAK,YACP,OAAM,IAAI,MAAM,mBAAmB;AAGrC,MAAI,KAAK,WAAW,QAClB;AAGF,MAAI,KAAK,YACP,QAAO,KAAK,kBAAkB,KAAK,aAAa,QAAQ;AAG1D,OAAK,cAAc,KAAK,gBAAgB,CAAC,cAAc;AACrD,QAAK,cAAc,KAAA;IACnB;AAEF,SAAO,KAAK,kBAAkB,KAAK,aAAa,QAAQ;;CAG1D,MAAM,SAA0B;AAC9B,SAAO,KAAK,QAAQ,QAAQ;;CAG9B,gBAAgB,SAA0B;AACxC,SAAO,KAAK,QAAQ,QAAQ;;CAG9B,wBAAwB,UAAmE;AACzF,OAAK,eAAe,IAAI,SAAS;AAEjC,eAAa;AACX,QAAK,eAAe,OAAO,SAAS;;;CAIxC,QACE,OACA,UACY;EACZ,IAAI,YAAY,KAAK,eAAe,IAAI,MAAM;AAC9C,MAAI,CAAC,WAAW;AACd,+BAAY,IAAI,KAAK;AACrB,QAAK,eAAe,IAAI,OAAO,UAAU;;AAG3C,YAAU,IAAI,SAAgB;AAE9B,eAAa;AACX,QAAK,SAAS,OAAO,SAAS;;;CAIlC,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,MAAgD;AACnD,MAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,UAC9B,QAAO;EAGT,MAAM,UAAU,KAAK,cAAc,KAAK;AACxC,OAAK,KAAK,YAAY,QAAQ;AAC9B,OAAK,UAAU,KAAK,UAAU,UAAU,QAAQ,CAAC;AAEjD,SAAO;;CAGT,YAAY,MAA6C;AACvD,MAAI,CAAC,KAAK,KAAK,KAAK,CAClB,OAAM,IAAI,MAAM,4CAA4C,KAAK,SAAS;;CAI9E,QAAQ,MAA2D;AACjE,MAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,UAC9B,QAAO;AAGT,OAAK,UAAU,KAAK,KAAK;AACzB,SAAO;;CAGT,QAAc;AACZ,OAAK,cAAc;AACnB,OAAK,mBAAmB;AACxB,OAAK,aAAa,UAAU;AAC5B,OAAK,eAAe;AACpB,OAAK,8BAAc,IAAI,MAAM,gBAAgB,CAAC;EAE9C,MAAM,YAAY,KAAK;AACvB,OAAK,YAAY,KAAA;AAEjB,MAAI,aAAa,UAAU,eAAe,UAAU,UAAU,UAAU,eAAe,UAAU,QAC/F,WAAU,OAAO;AAGnB,OAAK,aAAa,SAAS;;CAG7B,MAAc,iBAAiB;AAC7B,OAAK,mBAAmB;AAExB,SAAO,CAAC,KAAK,aAAa;GACxB,MAAM,eAAe,KAAK,oBAAoB;AAC9C,QAAK,aAAa,eAAe,iBAAiB,aAAa;AAE/D,OAAI;AACF,UAAM,KAAK,aAAa;AACxB,SAAK,oBAAoB;AACzB;YAEK,OAAO;IACZ,MAAM,kBAAkB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,iBAAiB,MAAM,IAAI,qCAAqC;AACnI,SAAK,gBAAgB;AACrB,SAAK,KAAK,UAAU,gBAAgB;AAEpC,QAAI,KAAK,YACP,OAAM;AAGR,QAAI,2CAA2C,gBAAgB,QAAQ,EAAE;AACvE,UAAK,aAAa,SAAS;AAC3B,WAAM;;AAGR,QAAI,CAAC,KAAK,KAAK,iBAAiB,cAAc;AAC5C,UAAK,aAAa,SAAS;AAC3B,WAAM;;AAGR,QAAI,CAAC,KAAK,UAAU,EAAE;AACpB,UAAK,aAAa,SAAS;AAC3B,WAAM;;IAGR,MAAM,QAAQ,KAAK,kBAAkB,KAAK,kBAAkB;AAC5D,SAAK,qBAAqB;AAC1B,UAAM,MAAM,MAAM;;;AAItB,QAAM,IAAI,MAAM,mBAAmB;;CAGrC,cAAqC;EACnC,MAAM,KAAK,IAAI,UAAU,KAAK,KAAK,IAAI;AACvC,OAAK,YAAY;AACjB,OAAK,aAAa,KAAK,KAAK;AAC5B,OAAK,aAAa;EAElB,MAAM,WAAW,uBAAuB;EACxC,MAAM,UAA6B;GACjC,WAAW;GACX,eAAe,CAAC,KAAK,KAAK;GAC1B,SAAS,SAAS;GAClB,QAAQ,SAAS;GACjB,SAAS,SAAS;GAClB,QAAQ;GACT;AAED,OAAK,oBAAoB;EAEzB,MAAM,wBAAwB,KAAK,cAAc;AAEjD,KAAG,aAAa,UAAwB;AACtC,OAAI,CAAC,iBAAiB,CACpB;AAGG,QAAK,cAAc,MAAM;;AAGhC,KAAG,WAAW,UAAe;AAC3B,OAAI,CAAC,iBAAiB,CACpB;GAGF,MAAM,QAAQ,OAAO,iBAAiB,QAAQ,MAAM,wBAAQ,IAAI,MAAM,kBAAkB;AACxF,OAAI,KAAK,kBACP,MAAK,oBAAoB,OAAO,GAAG;QAEhC;AACH,SAAK,KAAK,UAAU,MAAM;AACrB,SAAK,4BAA4B,MAAM;;;AAIhD,KAAG,gBAAgB;AACjB,OAAI,CAAC,iBAAiB,CACpB;GAGF,MAAM,WAAW,KAAK,WAAW;AACjC,QAAK,cAAc,GAAG;AACtB,QAAK,KAAK,WAAW;AAErB,OAAI,KAAK,YACP;AAGF,OAAI,YAAY,KAAK,KAAK,eAAe;AACvC,SAAK,mBAAmB;AACnB,SAAK,SAAS;AACnB;;AAGF,QAAK,8BAAc,IAAI,MAAM,mBAAmB,CAAC;;AAGnD,KAAG,eAAe;AAChB,OAAI,CAAC,iBAAiB,CACpB;AAGF,QAAK,gBAAgB;AAErB,OAAI,KAAK,KAAK,OAAO;AACnB,YAAQ,gBAAgB;AACxB,SAAK,aAAa,iBAAiB;AACnC,SAAK,iBAAiB;UAEnB;AACH,YAAQ,gBAAgB;AACxB,SAAK,aAAa,aAAa;AAC/B,SAAK,aAAa;;;AAItB,SAAO,QAAQ;;CAGjB,oBAA4B,OAAc,QAAoB;AAC5D,MAAI,UAAU,KAAK,cAAc,OAC/B;EAGF,MAAM,gBAAgB,UAAU,KAAK;AACrC,OAAK,cAAc,OAAO;AAE1B,MAAI,iBAAiB,cAAc,eAAe,UAAU,UAAU,cAAc,eAAe,UAAU,QAC3G,eAAc,OAAO;AAGvB,OAAK,cAAc,MAAM;;CAG3B,cAAsB,QAAoB;AACxC,MAAI,UAAU,KAAK,cAAc,OAC/B;AAGF,OAAK,eAAe;AAEpB,MAAI,CAAC,UAAU,KAAK,cAAc,OAChC,MAAK,YAAY,KAAA;;CAIrB,cAAsB,OAAc;AAClC,MAAI,CAAC,KAAK,kBACR;EAGF,MAAM,UAAU,KAAK;AACrB,OAAK,oBAAoB,KAAA;AACzB,UAAQ,OAAO,MAAM;;CAGvB,iBAAyB;AACvB,MAAI,CAAC,KAAK,kBACR;EAGF,MAAM,UAAU,KAAK;AACrB,OAAK,oBAAoB,KAAA;AACzB,UAAQ,SAAS;;CAGnB,WAAmB;AACjB,SAAO,KAAK,KAAK,yBAAyB,MAAM,KAAK,oBAAoB,KAAK,KAAK;;CAGrF,kBAA0B,UAAkB;AAC1C,SAAO,KAAK,IAAI,KAAK,WAAW,KAAO,IAAO;;CAGhD,aAAqB,QAAsB;AACzC,MAAI,KAAK,WAAW,OAClB;EAGF,MAAM,iBAAiB,KAAK;AAC5B,OAAK,SAAS;EACd,MAAM,UAAU;GAAE;GAAgB;GAAQ;AAE1C,OAAK,KAAK,gBAAgB,QAAQ;AAElC,OAAK,MAAM,YAAY,KAAK,eAC1B,UAAS,QAAQ;;CAIrB,MAAc,kBAAkB,gBAA+B,SAA0B;AACvF,MAAI,CAAC,SAAS,WAAW,CAAC,SAAS,YACjC,QAAO;EAGT,MAAM,UAAU,SAAS;AACzB,MAAI,OAAO,YAAY,eAAe,WAAW,EAC/C,OAAM,IAAI,MAAM,8BAA8B,QAAQ,IAAI;EAG5D,MAAM,cAAc,SAAS;AAC7B,MAAI,aAAa,QACf,OAAM,IAAI,MAAM,qBAAqB;EAGvC,IAAI;EACJ,IAAI;AAEJ,MAAI;AACF,SAAM,QAAQ,KAAK,CACjB,gBACA,IAAI,SAAe,GAAG,WAAW;AAC/B,QAAI,OAAO,YAAY,YACrB,iBAAgB,iBAAiB;AAC/B,4BAAO,IAAI,MAAM,8BAA8B,QAAQ,IAAI,CAAC;OAC3D,QAAQ;AAGb,QAAI,aAAa;KACf,MAAM,gBAAgB;AACpB,6BAAO,IAAI,MAAM,qBAAqB,CAAC;;AAGzC,iBAAY,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAC9D,iCAA4B,YAAY,oBAAoB,SAAS,QAAQ;;KAE/E,CACH,CAAC;YAEI;AACN,OAAI,cACF,cAAa,cAAc;AAG7B,0BAAuB;;;CAI3B,cAAsB;AACpB,OAAK,YAAY;GACf,MAAM;GACN,MAAM;IACJ,MAAM,KAAK,KAAK;IAChB,UAAU,KAAK;IACf,gBAAgB,KAAK,KAAK;IAC1B,cAAc,KAAK,KAAK;IACxB,cAAc,KAAK,KAAK;IACzB;GACF,CAAC;;CAGJ,kBAA0B;AACxB,MAAI,CAAC,KAAK,KAAK,MACb;AAGF,OAAK,YAAY;GACf,MAAM;GACN,MAAM,EAAE,OAAO,KAAK,KAAK,OAAO;GACjC,CAAC;;CAGJ,MAAc,cAAc,OAAqB;AAC/C,OAAK,aAAa,KAAK,KAAK;AAE5B,MAAI;GACF,MAAM,OAAO,KAAK,aAAa,MAAM,KAAe;AACpD,QAAK,KAAK,eAAe,KAAK;AAE9B,SAAM,KAAK,qBAAqB,KAAK;AACrC,SAAM,KAAK,gBAAgB,KAAK;WAE3B,OAAO;GACZ,MAAM,kBAAkB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,iBAAiB,MAAM,IAAI,qCAAqC;AACnI,QAAK,KAAK,UAAU,gBAAgB;AAEpC,OAAI,KAAK,qBAAqB,KAAK,WAAW,QAC5C,MAAK,oBAAoB,gBAAgB;;;CAK/C,aAAqB,KAAgC;AACnD,MAAI;GACF,MAAM,SAAS,UAAU,MAAqC,IAAI;AAClE,OAAI,UAAU,OAAO,WAAW,YAAY,UAAU,OACpD,QAAO;UAGL;EAIN,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,EAAE,UAAU,QACvD,OAAM,IAAI,MAAM,qCAAqC;AAGvD,SAAO;;CAGT,MAAc,qBAAqB,MAAyB;AAC1D,UAAQ,KAAK,MAAb;GACE,KAAK,SAAS;IACZ,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,WAAW,OAAO,YAAY,SACjC;IAGF,MAAM,oBAAoB,wBAAwB,QAAQ;AAC1D,QAAI,kBAAkB,gBAAgB;KACpC,MAAM,QAAQ,IAAI,MAAM,QAAQ;AAChC,SAAI,kBAAkB,UAAU;AAC9B,WAAK,cAAc;AACnB,WAAK,oBAAoB,MAAM;AAC/B,WAAK,aAAa,SAAS;AAC3B;;AAGF,WAAM,KAAK,4BAA4B,MAAM;AAC7C;;AAGF,QAAI,kBAAkB,SAAS,UAC7B,OAAM,IAAI,MAAM,kBAAkB,QAAQ;AAG5C,UAAM,IAAI,MAAM,QAAQ;;GAG1B,KAAK;AACH,QAAI,KAAK,KAAK,eAAe;AAC3B,SAAI,CAAC,KAAK,qBAAqB,KAAK,kBAAkB,cACpD;AAGF,UAAK,kBAAkB,gBAAgB;AACvC,UAAK,aAAa,aAAa;AAC/B,UAAK,aAAa;AAClB;;AAGF,UAAM,IAAI,MAAM,wBAAwB;GAG1C,KAAK;AACH,QAAI,CAAC,KAAK,mBAAmB,KAAK,CAChC;AAGF,QAAI,KAAK,kBACP,MAAK,kBAAkB,YAAY;AAGrC,SAAK,oBAAoB;AACzB,SAAK,aAAa,QAAQ;AAC1B,SAAK,gBAAgB;AACrB,SAAK,KAAK,WAAW;AACrB;GAGF,KAAK,iCACH,KAAI,KAAK,KAAK,SAAS,qBAAqB,KAC1C,MAAK,mBAAmB;;;CAMhC,mBAA2B,OAAuF;AAChH,SAAO,MAAM,KAAK,SAAS,KAAK,KAAK,QAAQ,MAAM,KAAK,UAAU,OAAO,KAAK,SAAS;;CAGzF,MAAc,gBAAgB,MAAyB;EACrD,MAAM,YAAY,KAAK,eAAe,IAAI,KAAK,KAAK;AACpD,MAAI,CAAC,WAAW,KACd;EAGF,MAAM,UAAU,MAAM,QAAQ,WAC5B,MAAM,KAAK,UAAU,CAAC,KAAI,aAAY,QAAQ,QAAQ,SAAS,KAAY,CAAC,CAAC,CAC9E;AAED,OAAK,MAAM,UAAU,QACnB,KAAI,OAAO,WAAW,WACpB,MAAK,KAAK,UAAU,OAAO,OAAO;;CAKxC,cAAsB,MAAuC;AAC3D,SAAO;GACL,GAAG;GACH,UAAU;IACR,GAAG,MAAM;IACT,QAAQ,MAAM,UAAU,UAAU,KAAK;IACvC,OAAO;KACL,IAAI,MAAM,UAAU,OAAO,MAAM,eAAe;KAChD,GAAG,MAAM,UAAU;KACpB;IACF;GACF;;CAGH,iBAAyB;AACvB,MAAI,CAAC,KAAK,UAAU,eAAe,CAAC,KAAK,UAAU,aACjD;AAGF,OAAK,eAAe;AACpB,OAAK,aAAa,KAAK,KAAK;AAC5B,OAAK,aAAa;EAElB,MAAM,WAAW,KAAK,IAAI,KAAO,KAAK,IAAI,KAAK,UAAU,cAAc,KAAK,MAAM,KAAK,UAAU,cAAc,EAAE,CAAC,CAAC;AACnH,OAAK,iBAAiB,kBAAkB;AACtC,OAAI,CAAC,KAAK,aACR;GAGF,MAAM,MAAM,KAAK,KAAK;AACtB,OAAI,MAAM,KAAK,aAAa,KAAK,UAAU,aAAa;AACjD,SAAK,4CAA4B,IAAI,MAAM,sBAAsB,KAAK,UAAU,YAAY,IAAI,CAAC;AACtG;;AAGF,OAAI,MAAM,KAAK,cAAc,KAAK,UAAU,aAC1C,MAAK,mBAAmB;KAEzB,SAAS;;CAGd,gBAAwB;AACtB,MAAI,CAAC,KAAK,eACR;AAGF,gBAAc,KAAK,eAAe;AAClC,OAAK,iBAAiB,KAAA;;CAGxB,oBAA4B,MAAuB;EACjD,MAAM,YAAY,KAAK;AAKvB,MAAI,SAAS,OACX,WAAU,QAAQ;MAGlB,WAAU,QAAQ;;CAItB,oBAA4B;AAC1B,OAAK,aAAa,KAAK,KAAK;AAC5B,OAAK,KAAK;GACR,MAAM;GACN,MAAM;IACJ,MAAM,qBAAqB;IAC3B,SAAS,KAAK,UAAU;IACxB,IAAI,KAAK,KAAK;IACf;GACF,CAAC;AACF,OAAK,oBAAoB,OAAO;;CAGlC,oBAA4B;AAC1B,OAAK,KAAK;GACR,MAAM;GACN,MAAM;IACJ,MAAM,qBAAqB;IAC3B,SAAS,iBAAiB;IAC1B,IAAI,KAAK,KAAK;IACf;GACF,CAAC;AACF,OAAK,oBAAoB,OAAO;;CAGlC,MAAc,4BAA4B,OAAc;AACtD,MAAI,KAAK,eAAe,KAAK,iBAC3B;AAGF,OAAK,mBAAmB;EACxB,MAAM,YAAY,CAAC,CAAC,KAAK;AAEzB,MAAI,CAAC,KAAK,qBAAqB,KAAK,WAAW,QAC7C,MAAK,KAAK,UAAU,MAAM;EAG5B,MAAM,YAAY,KAAK;AACvB,OAAK,cAAc,UAAU;AAC7B,OAAK,cAAc,MAAM;AAEzB,MAAI,aAAa,UAAU,eAAe,UAAU,UAAU,UAAU,eAAe,UAAU,QAC/F,WAAU,OAAO;AAGnB,MAAI,UACF,MAAK,KAAK,WAAW;AAGvB,MAAI,CAAC,KAAK,KAAK,eAAe;AAC5B,QAAK,aAAa,SAAS;AAC3B;;AAGG,OAAK,SAAS"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type {\n MetadataEventSource,\n ModuleConfigSchema,\n ModuleDependency,\n WebSocketBaseEvent,\n WebSocketEvent,\n WebSocketEventOptionalSource,\n WebSocketEvents,\n} from '@proj-airi/server-shared/types'\n\nimport type { WebSocketLike, WebSocketLikeConstructor, WebSocketMessageEventLike } from './websocket-like'\n\nimport NativeWebSocket from 'crossws/websocket'\nimport superjson from 'superjson'\n\nimport { errorMessageFrom, sleep } from '@moeru/std'\nimport { isTerminalAuthenticationServerErrorMessage, parseServerErrorMessage } from '@proj-airi/server-shared'\nimport { MessageHeartbeat, MessageHeartbeatKind } from '@proj-airi/server-shared/types'\n\nexport type ClientStatus\n = | 'idle'\n | 'connecting'\n | 'authenticating'\n | 'announcing'\n | 'ready'\n | 'reconnecting'\n | 'closing'\n | 'closed'\n | 'failed'\n\nexport interface ClientHeartbeatOptions {\n pingInterval?: number\n readTimeout?: number\n message?: MessageHeartbeat | string\n}\n\nexport interface ClientStateChangeContext {\n previousStatus: ClientStatus\n status: ClientStatus\n}\n\nexport interface ConnectOptions {\n abortSignal?: AbortSignal\n timeout?: number\n}\n\nexport interface ClientOptions<C = undefined> {\n url?: string\n name: string\n token?: string\n websocketConstructor?: WebSocketLikeConstructor\n\n connectTimeoutMs?: number\n possibleEvents?: Array<keyof WebSocketEvents<C>>\n identity?: MetadataEventSource\n dependencies?: ModuleDependency[]\n configSchema?: ModuleConfigSchema\n heartbeat?: ClientHeartbeatOptions\n\n autoConnect?: boolean\n autoReconnect?: boolean\n maxReconnectAttempts?: number\n\n onError?: (error: unknown) => void\n onClose?: () => void\n onReady?: () => void\n onStateChange?: (context: ClientStateChangeContext) => void\n\n onAnyMessage?: (data: WebSocketEvent<C>) => void\n onAnySend?: (data: WebSocketEvent<C>) => void\n}\n\ninterface ConnectionAttempt {\n announced: boolean\n authenticated: boolean\n promise: Promise<void>\n reject: (error: Error) => void\n resolve: () => void\n socket: WebSocketLike\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\nfunction createDeferredPromise() {\n let resolve!: () => void\n let reject!: (error: Error) => void\n\n const promise = new Promise<void>((innerResolve, innerReject) => {\n resolve = innerResolve\n reject = innerReject\n })\n\n return { promise, reject, resolve }\n}\n\nfunction normalizeHeartbeatOptions(heartbeat?: ClientHeartbeatOptions): Required<ClientHeartbeatOptions> {\n const readTimeout = heartbeat?.readTimeout ?? 30_000\n const pingInterval = heartbeat?.pingInterval ?? Math.max(1_000, Math.floor(readTimeout / 2))\n\n return {\n readTimeout,\n pingInterval: Math.min(pingInterval, readTimeout),\n message: heartbeat?.message ?? MessageHeartbeat.Ping,\n }\n}\n\nexport class Client<C = undefined> {\n private websocket?: WebSocketLike\n private shouldClose = false\n private connectTask?: Promise<void>\n private heartbeatTimer?: ReturnType<typeof setInterval>\n private lastPingAt = 0\n private lastReadAt = 0\n private reconnectAttempts = 0\n private pendingReconnect = false\n private connectionAttempt?: ConnectionAttempt\n private failureReason?: Error\n private status: ClientStatus = 'idle'\n private readonly identity: MetadataEventSource\n private readonly heartbeat: Required<ClientHeartbeatOptions>\n private readonly websocketConstructor: WebSocketLikeConstructor\n\n private readonly opts:\n & Required<Omit<ClientOptions<C>, 'token' | 'heartbeat' | 'websocketConstructor' | 'configSchema'>>\n & Pick<ClientOptions<C>, 'token' | 'heartbeat' | 'configSchema'>\n\n private readonly eventListeners = new Map<\n keyof WebSocketEvents<C>,\n Set<(data: WebSocketBaseEvent<any, any>) => void | Promise<void>>\n >()\n\n private readonly stateListeners = new Set<(context: ClientStateChangeContext) => void>()\n\n constructor(options: ClientOptions<C>) {\n const { websocketConstructor, ...clientOptions } = options\n const identity = options.identity ?? {\n kind: 'plugin',\n plugin: { id: options.name },\n id: createInstanceId(),\n }\n\n const heartbeat = normalizeHeartbeatOptions(options.heartbeat)\n\n this.opts = {\n url: 'ws://localhost:6121/ws',\n connectTimeoutMs: 15_000,\n onAnyMessage: () => {},\n onAnySend: () => {},\n possibleEvents: [],\n dependencies: [],\n configSchema: undefined,\n onError: () => {},\n onClose: () => {},\n onReady: () => {},\n onStateChange: () => {},\n autoConnect: true,\n autoReconnect: true,\n maxReconnectAttempts: -1,\n ...clientOptions,\n heartbeat,\n identity,\n }\n\n this.identity = identity\n this.heartbeat = heartbeat\n this.websocketConstructor = websocketConstructor ?? (NativeWebSocket as unknown as WebSocketLikeConstructor)\n\n if (this.opts.autoConnect) {\n void this.connect()\n }\n }\n\n get connectionStatus() {\n return this.status\n }\n\n get isReady() {\n return this.status === 'ready'\n }\n\n get isSocketOpen() {\n return this.websocket?.readyState === this.websocketConstructor.OPEN\n }\n\n get lastError() {\n return this.failureReason\n }\n\n async connect(options?: ConnectOptions) {\n if (this.shouldClose) {\n throw new Error('Client is closed')\n }\n\n if (this.status === 'ready') {\n return\n }\n\n if (this.connectTask) {\n return this.waitForConnection(this.connectTask, options)\n }\n\n this.connectTask = this.runConnectLoop().finally(() => {\n this.connectTask = undefined\n })\n\n return this.waitForConnection(this.connectTask, options)\n }\n\n ready(options?: ConnectOptions) {\n return this.connect(options)\n }\n\n ensureConnected(options?: ConnectOptions) {\n return this.connect(options)\n }\n\n onConnectionStateChange(callback: (context: ClientStateChangeContext) => void): () => void {\n this.stateListeners.add(callback)\n\n return () => {\n this.stateListeners.delete(callback)\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\n listeners.add(callback as any)\n\n return () => {\n this.offEvent(event, callback)\n }\n }\n\n offEvent<E extends keyof WebSocketEvents<C>>(\n event: E,\n callback?: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<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>): boolean {\n if (!this.isSocketOpen || !this.websocket) {\n return false\n }\n\n const payload = this.createPayload(data)\n this.opts.onAnySend?.(payload)\n this.websocket.send(superjson.stringify(payload))\n\n return true\n }\n\n sendOrThrow(data: WebSocketEventOptionalSource<C>): void {\n if (!this.send(data)) {\n throw new Error(`Client is not connected, current status: ${this.status}`)\n }\n }\n\n sendRaw(data: string | ArrayBufferLike | ArrayBufferView): boolean {\n if (!this.isSocketOpen || !this.websocket) {\n return false\n }\n\n this.websocket.send(data)\n return true\n }\n\n close(): void {\n this.shouldClose = true\n this.pendingReconnect = false\n this.transitionTo('closing')\n this.stopHeartbeat()\n this.rejectAttempt(new Error('Client closed'))\n\n const websocket = this.websocket\n this.websocket = undefined\n\n if (websocket && websocket.readyState !== this.websocketConstructor.CLOSED && websocket.readyState !== this.websocketConstructor.CLOSING) {\n websocket.close()\n }\n\n this.transitionTo('closed')\n }\n\n private async runConnectLoop() {\n this.pendingReconnect = false\n\n while (!this.shouldClose) {\n const reconnecting = this.reconnectAttempts > 0\n this.transitionTo(reconnecting ? 'reconnecting' : 'connecting')\n\n try {\n await this.connectOnce()\n this.reconnectAttempts = 0\n return\n }\n catch (error) {\n const normalizedError = error instanceof Error ? error : new Error(errorMessageFrom(error) ?? 'Failed to connect websocket client')\n this.failureReason = normalizedError\n this.opts.onError?.(normalizedError)\n\n if (this.shouldClose) {\n throw normalizedError\n }\n\n if (isTerminalAuthenticationServerErrorMessage(normalizedError.message)) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n if (!this.opts.autoReconnect && reconnecting) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n if (!this.canRetry()) {\n this.transitionTo('failed')\n throw normalizedError\n }\n\n const delay = this.getReconnectDelay(this.reconnectAttempts)\n this.reconnectAttempts += 1\n await sleep(delay)\n }\n }\n\n throw new Error('Client is closed')\n }\n\n private connectOnce(): Promise<void> {\n const WebSocketConstructor = this.websocketConstructor\n const ws = new WebSocketConstructor(this.opts.url)\n this.websocket = ws\n this.lastReadAt = Date.now()\n this.lastPingAt = 0\n\n const deferred = createDeferredPromise()\n const attempt: ConnectionAttempt = {\n announced: false,\n authenticated: !this.opts.token,\n promise: deferred.promise,\n reject: deferred.reject,\n resolve: deferred.resolve,\n socket: ws,\n }\n\n this.connectionAttempt = attempt\n\n const isCurrentSocket = () => this.websocket === ws\n const connectTimeoutMs = this.opts.connectTimeoutMs\n const connectTimer = setTimeout(() => {\n if (ws.readyState === WebSocket.OPEN) {\n return\n }\n\n ws.close()\n deferred.reject(new Error(`Connection timeout after ${connectTimeoutMs}ms`))\n }, connectTimeoutMs)\n\n const clearConnectTimer = () => {\n clearTimeout(connectTimer)\n }\n\n ws.onmessage = (event: WebSocketMessageEventLike) => {\n if (!isCurrentSocket()) {\n return\n }\n\n void this.handleMessage(event)\n }\n\n ws.onerror = (event: any) => {\n clearConnectTimer()\n\n if (!isCurrentSocket()) {\n return\n }\n\n const error = event?.error instanceof Error ? event.error : new Error('WebSocket error')\n if (this.connectionAttempt) {\n this.handleSocketFailure(error, ws)\n }\n else {\n this.opts.onError?.(error)\n void this.reconnectAfterProtocolError(error)\n }\n }\n\n ws.onclose = () => {\n clearConnectTimer()\n\n if (!isCurrentSocket()) {\n return\n }\n\n const wasReady = this.status === 'ready'\n this.cleanupSocket(ws)\n this.opts.onClose?.()\n\n if (this.shouldClose) {\n return\n }\n\n if (wasReady && this.opts.autoReconnect) {\n this.pendingReconnect = true\n this.transitionTo('idle')\n void this.connect()\n return\n }\n\n this.rejectAttempt(new Error('WebSocket closed'))\n }\n\n ws.onopen = () => {\n clearConnectTimer()\n\n if (!isCurrentSocket()) {\n return\n }\n\n this.startHeartbeat()\n\n if (this.opts.token) {\n attempt.authenticated = false\n this.transitionTo('authenticating')\n this.tryAuthenticate()\n }\n else {\n attempt.authenticated = true\n this.transitionTo('announcing')\n this.tryAnnounce()\n }\n }\n\n return attempt.promise\n }\n\n private handleSocketFailure(error: Error, socket?: WebSocketLike) {\n if (socket && this.websocket !== socket) {\n return\n }\n\n const currentSocket = socket ?? this.websocket\n this.cleanupSocket(socket)\n\n if (currentSocket && currentSocket.readyState !== this.websocketConstructor.CLOSED && currentSocket.readyState !== this.websocketConstructor.CLOSING) {\n currentSocket.close()\n }\n\n this.rejectAttempt(error)\n }\n\n private cleanupSocket(socket?: WebSocketLike) {\n if (socket && this.websocket !== socket) {\n return\n }\n\n this.stopHeartbeat()\n\n if (!socket || this.websocket === socket) {\n this.websocket = undefined\n }\n }\n\n private rejectAttempt(error: Error) {\n if (!this.connectionAttempt) {\n return\n }\n\n const attempt = this.connectionAttempt\n this.connectionAttempt = undefined\n attempt.reject(error)\n }\n\n private resolveAttempt() {\n if (!this.connectionAttempt) {\n return\n }\n\n const attempt = this.connectionAttempt\n this.connectionAttempt = undefined\n attempt.resolve()\n }\n\n private canRetry() {\n return this.opts.maxReconnectAttempts === -1 || this.reconnectAttempts < this.opts.maxReconnectAttempts\n }\n\n private getReconnectDelay(attempts: number) {\n return Math.min(2 ** attempts * 1_000, 30_000)\n }\n\n private transitionTo(status: ClientStatus) {\n if (this.status === status) {\n return\n }\n\n const previousStatus = this.status\n this.status = status\n const context = { previousStatus, status }\n\n this.opts.onStateChange?.(context)\n\n for (const listener of this.stateListeners) {\n listener(context)\n }\n }\n\n private async waitForConnection(connectPromise: Promise<void>, options?: ConnectOptions) {\n if (!options?.timeout && !options?.abortSignal) {\n return connectPromise\n }\n\n const timeout = options?.timeout\n if (typeof timeout !== 'undefined' && timeout <= 0) {\n throw new Error(`Connection timed out after ${timeout}ms`)\n }\n\n const abortSignal = options?.abortSignal\n if (abortSignal?.aborted) {\n throw new Error('Connection aborted')\n }\n\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n let removeAbortListener: (() => void) | undefined\n\n try {\n await Promise.race([\n connectPromise,\n new Promise<void>((_, reject) => {\n if (typeof timeout !== 'undefined') {\n timeoutHandle = setTimeout(() => {\n reject(new Error(`Connection timed out after ${timeout}ms`))\n }, timeout)\n }\n\n if (abortSignal) {\n const onAbort = () => {\n reject(new Error('Connection aborted'))\n }\n\n abortSignal.addEventListener('abort', onAbort, { once: true })\n removeAbortListener = () => abortSignal.removeEventListener('abort', onAbort)\n }\n }),\n ])\n }\n finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle)\n }\n\n removeAbortListener?.()\n }\n }\n\n private tryAnnounce() {\n this.sendOrThrow({\n type: 'module:announce',\n data: {\n name: this.opts.name,\n identity: this.identity,\n possibleEvents: this.opts.possibleEvents,\n dependencies: this.opts.dependencies,\n configSchema: this.opts.configSchema,\n },\n })\n }\n\n private tryAuthenticate() {\n if (!this.opts.token) {\n return\n }\n\n this.sendOrThrow({\n type: 'module:authenticate',\n data: { token: this.opts.token },\n })\n }\n\n private async handleMessage(event: WebSocketMessageEventLike) {\n this.lastReadAt = Date.now()\n\n try {\n const data = this.parseMessage(event.data as string)\n this.opts.onAnyMessage?.(data)\n\n await this.handleControlMessage(data)\n await this.dispatchMessage(data)\n }\n catch (error) {\n const normalizedError = error instanceof Error ? error : new Error(errorMessageFrom(error) ?? 'Failed to handle websocket message')\n this.opts.onError?.(normalizedError)\n\n if (this.connectionAttempt && this.status !== 'ready') {\n this.handleSocketFailure(normalizedError)\n }\n }\n }\n\n private parseMessage(raw: string): WebSocketEvent<C> {\n try {\n const parsed = superjson.parse<WebSocketEvent<C> | undefined>(raw)\n if (parsed && typeof parsed === 'object' && 'type' in parsed) {\n return parsed\n }\n }\n catch {\n // Try standard JSON next.\n }\n\n const parsed = JSON.parse(raw) as WebSocketEvent<C>\n if (!parsed || typeof parsed !== 'object' || !('type' in parsed)) {\n throw new Error('Received invalid websocket message')\n }\n\n return parsed\n }\n\n private async handleControlMessage(data: WebSocketEvent<C>) {\n switch (data.type) {\n case 'error': {\n const message = data.data?.message\n if (!message || typeof message !== 'string') {\n return\n }\n\n const parsedServerError = parseServerErrorMessage(message)\n if (parsedServerError.authentication) {\n const error = new Error(message)\n if (parsedServerError.terminal) {\n this.shouldClose = true\n this.handleSocketFailure(error)\n this.transitionTo('failed')\n return\n }\n\n await this.reconnectAfterProtocolError(error)\n return\n }\n\n if (parsedServerError.code !== 'unknown') {\n throw new Error(parsedServerError.message)\n }\n\n throw new Error(message)\n }\n\n case 'module:authenticated': {\n if (data.data.authenticated) {\n if (!this.connectionAttempt || this.connectionAttempt.authenticated) {\n return\n }\n\n this.connectionAttempt.authenticated = true\n this.transitionTo('announcing')\n this.tryAnnounce()\n return\n }\n\n throw new Error('Authentication failed')\n }\n\n case 'module:announced': {\n if (!this.isSelfAnnouncement(data)) {\n return\n }\n\n if (this.status === 'ready') {\n return\n }\n\n if (this.connectionAttempt) {\n this.connectionAttempt.announced = true\n }\n\n this.reconnectAttempts = 0\n this.transitionTo('ready')\n this.resolveAttempt()\n this.opts.onReady?.()\n return\n }\n\n case 'registry:modules:sync': {\n // Fallback: If the status is stuck at 'announcing' but the sync already contains this module,\n // it means the announce succeeded; the server simply didn't send back 'module:announced'\n if (this.status !== 'announcing' || !this.connectionAttempt) {\n return\n }\n\n const modules = (data.data as any)?.modules as Array<{\n name: string\n identity?: { id?: string }\n }> ?? []\n\n const selfRegistered = modules.some(\n m => m.name === this.opts.name\n && m.identity?.id === this.identity.id,\n )\n\n if (!selfRegistered) {\n return\n }\n\n if (this.connectionAttempt) {\n this.connectionAttempt.announced = true\n }\n\n this.reconnectAttempts = 0\n this.transitionTo('ready')\n this.resolveAttempt()\n this.opts.onReady?.()\n return\n }\n\n case 'transport:connection:heartbeat': {\n if (data.data.kind === MessageHeartbeatKind.Ping) {\n this.sendHeartbeatPong()\n }\n }\n }\n }\n\n private isSelfAnnouncement(event: WebSocketBaseEvent<'module:announced', WebSocketEvents<C>['module:announced']>) {\n return event.data.name === this.opts.name && event.data.identity?.id === this.identity.id\n }\n\n private async dispatchMessage(data: WebSocketEvent<C>) {\n const listeners = this.eventListeners.get(data.type)\n if (!listeners?.size) {\n return\n }\n\n const results = await Promise.allSettled(\n Array.from(listeners).map(listener => Promise.resolve(listener(data as any))),\n )\n\n for (const result of results) {\n if (result.status === 'rejected') {\n this.opts.onError?.(result.reason)\n }\n }\n }\n\n private createPayload(data: WebSocketEventOptionalSource<C>) {\n return {\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\n private startHeartbeat() {\n if (!this.heartbeat.readTimeout || !this.heartbeat.pingInterval) {\n return\n }\n\n this.stopHeartbeat()\n this.lastReadAt = Date.now()\n this.lastPingAt = 0\n\n const interval = Math.max(1_000, Math.min(this.heartbeat.pingInterval, Math.floor(this.heartbeat.readTimeout / 2)))\n this.heartbeatTimer = setInterval(() => {\n if (!this.isSocketOpen) {\n return\n }\n\n const now = Date.now()\n if (now - this.lastReadAt > this.heartbeat.readTimeout) {\n void this.reconnectAfterProtocolError(new Error(`Read timeout after ${this.heartbeat.readTimeout}ms`))\n return\n }\n\n if (now - this.lastPingAt >= this.heartbeat.pingInterval) {\n this.sendHeartbeatPing()\n }\n }, interval)\n }\n\n private stopHeartbeat() {\n if (!this.heartbeatTimer) {\n return\n }\n\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n\n private sendNativeHeartbeat(kind: 'ping' | 'pong') {\n const websocket = this.websocket as WebSocketLike & {\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.lastPingAt = Date.now()\n this.send({\n type: 'transport:connection:heartbeat',\n data: {\n kind: MessageHeartbeatKind.Ping,\n message: this.heartbeat.message,\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 reconnectAfterProtocolError(error: Error) {\n if (this.shouldClose || this.pendingReconnect) {\n return\n }\n\n this.pendingReconnect = true\n const hadSocket = !!this.websocket\n\n if (!this.connectionAttempt || this.status === 'ready') {\n this.opts.onError?.(error)\n }\n\n const websocket = this.websocket\n this.cleanupSocket(websocket)\n this.rejectAttempt(error)\n\n if (websocket && websocket.readyState !== this.websocketConstructor.CLOSED && websocket.readyState !== this.websocketConstructor.CLOSING) {\n websocket.close()\n }\n\n if (hadSocket) {\n this.opts.onClose?.()\n }\n\n if (!this.opts.autoReconnect) {\n this.transitionTo('failed')\n return\n }\n\n this.transitionTo('idle')\n void this.connect()\n }\n}\n"],"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.9.0-beta.4",
4
+ "version": "0.9.0-beta.7",
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",
@@ -35,7 +35,7 @@
35
35
  "@moeru/std": "0.1.0-beta.17",
36
36
  "crossws": "^0.4.4",
37
37
  "superjson": "^2.2.6",
38
- "@proj-airi/server-shared": "^0.9.0-beta.4"
38
+ "@proj-airi/server-shared": "^0.9.0-beta.7"
39
39
  },
40
40
  "scripts": {
41
41
  "dev": "pnpm run build",