@push.rocks/smartsocket 3.0.0 → 4.0.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/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "@push.rocks/smartsocket",
3
- "version": "3.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.",
5
5
  "main": "dist_ts/index.js",
6
6
  "typings": "dist_ts/index.d.ts",
7
7
  "type": "module",
8
+ "scripts": {
9
+ "test": "(tstest test/ --verbose)",
10
+ "build": "tsbuild --web && tsbundle",
11
+ "buildDocs": "tsdoc"
12
+ },
8
13
  "repository": {
9
14
  "type": "git",
10
15
  "url": "https://code.foss.global/push.rocks/smartsocket.git"
@@ -12,30 +17,30 @@
12
17
  "author": "Task Venture Capital GmbH",
13
18
  "license": "MIT",
14
19
  "bugs": {
15
- "url": "https://community.foss.global/"
20
+ "url": "https://code.foss.global/push.rocks/smartsocket/issues"
16
21
  },
17
22
  "homepage": "https://code.foss.global/push.rocks/smartsocket",
18
23
  "dependencies": {
19
24
  "@api.global/typedrequest-interfaces": "^3.0.19",
20
25
  "@push.rocks/isohash": "^2.0.1",
21
26
  "@push.rocks/isounique": "^1.0.5",
22
- "@push.rocks/lik": "^6.2.2",
27
+ "@push.rocks/lik": "^6.4.1",
23
28
  "@push.rocks/smartdelay": "^3.0.5",
24
29
  "@push.rocks/smartenv": "^6.0.0",
25
- "@push.rocks/smartjson": "^5.2.0",
26
- "@push.rocks/smartlog": "^3.1.10",
30
+ "@push.rocks/smartjson": "^6.0.1",
31
+ "@push.rocks/smartlog": "^3.2.2",
27
32
  "@push.rocks/smartpromise": "^4.2.3",
28
33
  "@push.rocks/smartrx": "^3.0.10",
29
- "@push.rocks/smarttime": "^4.1.1",
30
- "ws": "^8.18.3"
34
+ "@push.rocks/smarttime": "^4.2.3",
35
+ "ws": "^8.20.0"
31
36
  },
32
37
  "devDependencies": {
33
- "@git.zone/tsbuild": "^3.1.2",
34
- "@git.zone/tsbundle": "^2.6.3",
35
- "@git.zone/tsrun": "^2.0.0",
36
- "@git.zone/tstest": "^3.1.3",
37
- "@push.rocks/smartserve": "^1.1.0",
38
- "@types/node": "^24.10.1",
38
+ "@git.zone/tsbuild": "^4.4.0",
39
+ "@git.zone/tsbundle": "^2.10.1",
40
+ "@git.zone/tsrun": "^2.0.3",
41
+ "@git.zone/tstest": "^3.6.3",
42
+ "@push.rocks/smartserve": "^2.0.4",
43
+ "@types/node": "^25.6.0",
39
44
  "@types/ws": "^8.18.1"
40
45
  },
41
46
  "private": false,
@@ -48,6 +53,8 @@
48
53
  "dist_ts_web/**/*",
49
54
  "assets/**/*",
50
55
  "cli.js",
56
+ ".smartconfig.json",
57
+ "license",
51
58
  "npmextra.json",
52
59
  "readme.md"
53
60
  ],
@@ -67,9 +74,5 @@
67
74
  "secure",
68
75
  "rpc"
69
76
  ],
70
- "scripts": {
71
- "test": "(tstest test/ --verbose)",
72
- "build": "(tsbuild --web --allowimplicitany && tsbundle --from ./ts/index.ts --to dist_bundle/bundle.js)",
73
- "buildDocs": "tsdoc"
74
- }
75
- }
77
+ "packageManager": "pnpm@10.28.2"
78
+ }
package/readme.md CHANGED
@@ -143,20 +143,24 @@ await socketConnection.addTag({
143
143
  Use smartsocket with `@push.rocks/smartserve` for advanced HTTP/WebSocket handling:
144
144
 
145
145
  ```typescript
146
- import { Smartserve } from '@push.rocks/smartserve';
146
+ import { SmartServe } from '@push.rocks/smartserve';
147
147
  import { Smartsocket } from '@push.rocks/smartsocket';
148
148
 
149
- const smartserve = new Smartserve({ port: 3000 });
149
+ // Create smartsocket without a port (hooks mode)
150
150
  const smartsocket = new Smartsocket({ alias: 'myServer' });
151
151
 
152
- // Set smartserve as external server
153
- await smartsocket.setExternalServer('smartserve', smartserve);
152
+ // Get WebSocket hooks and pass them to SmartServe
153
+ const wsHooks = smartsocket.getSmartserveWebSocketHooks();
154
+ const smartserve = new SmartServe({
155
+ port: 3000,
156
+ websocket: wsHooks
157
+ });
154
158
 
155
- // Get WebSocket hooks for smartserve
156
- const wsHooks = smartsocket.socketServer.getSmartserveWebSocketHooks();
159
+ // Add socket functions as usual
160
+ smartsocket.addSocketFunction(myFunction);
157
161
 
158
- // Configure smartserve with the hooks
159
- // (see smartserve documentation for integration details)
162
+ // Start smartserve (smartsocket hooks mode doesn't need start())
163
+ await smartserve.start();
160
164
  ```
161
165
 
162
166
  ### Handling Disconnections
@@ -223,11 +227,11 @@ const response = await client.serverCall<IGreetRequest>('greet', { name: 'Bob' }
223
227
 
224
228
  | Method | Description |
225
229
  |--------|-------------|
226
- | `start()` | Start the WebSocket server |
230
+ | `start()` | Start the WebSocket server (not needed in hooks mode) |
227
231
  | `stop()` | Stop the server and close all connections |
228
232
  | `addSocketFunction(fn)` | Register a function that clients can call |
229
233
  | `clientCall(funcName, data, connection)` | Call a function on a specific client |
230
- | `setExternalServer(type, server)` | Use an external server (smartserve) |
234
+ | `getSmartserveWebSocketHooks()` | Get hooks for smartserve integration |
231
235
 
232
236
  ### SmartsocketClient
233
237
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartsocket',
6
- version: '3.0.0',
6
+ version: '4.0.1',
7
7
  description: 'Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.'
8
8
  }
@@ -13,7 +13,7 @@ export type TMessageType =
13
13
  /**
14
14
  * Base message interface for all smartsocket messages
15
15
  */
16
- export interface ISocketMessage<T = any> {
16
+ export interface ISocketMessage<T = unknown> {
17
17
  type: TMessageType;
18
18
  id?: string; // For request/response correlation
19
19
  payload: T;
@@ -44,16 +44,16 @@ export interface IAuthResponsePayload {
44
44
  /**
45
45
  * Function call payload
46
46
  */
47
- export interface IFunctionCallPayload {
47
+ export interface IFunctionCallPayload<T = unknown> {
48
48
  funcName: string;
49
- funcData: any;
49
+ funcData: T;
50
50
  }
51
51
 
52
52
  /**
53
53
  * Tag update payload
54
54
  */
55
55
  export interface ITagUpdatePayload {
56
- tags: { [key: string]: any };
56
+ tags: Record<string, unknown>;
57
57
  }
58
58
 
59
59
  /**
@@ -58,7 +58,7 @@ export class Smartsocket {
58
58
  * Handle a new WebSocket connection
59
59
  * Called by SocketServer when a new connection is established
60
60
  */
61
- public async handleNewConnection(socket: WebSocket | pluginsTyped.ws.WebSocket) {
61
+ public async handleNewConnection(socket: pluginsTyped.TWebSocket | pluginsTyped.IWebSocketLike) {
62
62
  const socketConnection: SocketConnection = new SocketConnection({
63
63
  alias: undefined,
64
64
  authenticated: false,
@@ -76,8 +76,8 @@ export class Smartsocket {
76
76
  socketConnection.eventSubject.next('disconnected');
77
77
  };
78
78
 
79
- socket.addEventListener('close', handleClose);
80
- socket.addEventListener('error', handleClose);
79
+ (socket as pluginsTyped.IWebSocketLike).addEventListener('close', handleClose);
80
+ (socket as pluginsTyped.IWebSocketLike).addEventListener('error', handleClose);
81
81
 
82
82
  try {
83
83
  await socketConnection.authenticate();
@@ -28,10 +28,10 @@ export class SmartsocketClient {
28
28
  public shortId = plugins.isounique.uni();
29
29
 
30
30
  // the shortId of the remote we connect to
31
- public remoteShortId: string = null;
31
+ public remoteShortId: string | null = null;
32
32
 
33
33
  public alias: string;
34
- public socketConnection: SocketConnection;
34
+ public socketConnection?: SocketConnection;
35
35
  public serverUrl: string;
36
36
  public serverPort: number;
37
37
  public autoReconnect: boolean;
@@ -50,7 +50,7 @@ export class SmartsocketClient {
50
50
 
51
51
  // tagStore
52
52
  private tagStore: { [key: string]: interfaces.ITag } = {};
53
- private tagStoreSubscription: plugins.smartrx.rxjs.Subscription;
53
+ private tagStoreSubscription?: plugins.smartrx.rxjs.Subscription;
54
54
 
55
55
  /**
56
56
  * adds a tag to a connection
@@ -86,7 +86,7 @@ export class SmartsocketClient {
86
86
  this.alias = optionsArg.alias;
87
87
  this.serverUrl = optionsArg.url;
88
88
  this.serverPort = optionsArg.port;
89
- this.autoReconnect = optionsArg.autoReconnect;
89
+ this.autoReconnect = optionsArg.autoReconnect ?? false;
90
90
  this.maxRetries = optionsArg.maxRetries ?? 100; // Default to 100 retries
91
91
  this.initialBackoffDelay = optionsArg.initialBackoffDelay ?? 1000; // Default to 1 second
92
92
  this.maxBackoffDelay = optionsArg.maxBackoffDelay ?? 60000; // Default to 1 minute
@@ -98,11 +98,18 @@ export class SmartsocketClient {
98
98
  }
99
99
 
100
100
  private isReconnecting = false;
101
+ private isConnecting = false;
101
102
 
102
103
  /**
103
104
  * connect the client to the server
104
105
  */
105
106
  public async connect() {
107
+ // Prevent duplicate connection attempts
108
+ if (this.isConnecting) {
109
+ return;
110
+ }
111
+ this.isConnecting = true;
112
+
106
113
  // Only reset retry counters on fresh connection (not during auto-reconnect)
107
114
  if (!this.isReconnecting) {
108
115
  this.currentRetryCount = 0;
@@ -141,6 +148,7 @@ export class SmartsocketClient {
141
148
  smartsocketHost: this,
142
149
  socket: socket as any,
143
150
  });
151
+ const socketConnection = this.socketConnection;
144
152
 
145
153
  // Increment attempt ID to invalidate any pending timers from previous attempts
146
154
  this.connectionAttemptId++;
@@ -176,7 +184,7 @@ export class SmartsocketClient {
176
184
  this.remoteShortId = authRequestPayload.serverAlias;
177
185
 
178
186
  // Send authentication data
179
- this.socketConnection.sendMessage({
187
+ socketConnection.sendMessage({
180
188
  type: 'auth',
181
189
  payload: { alias: this.alias },
182
190
  });
@@ -186,7 +194,7 @@ export class SmartsocketClient {
186
194
  const authResponse = message.payload as interfaces.IAuthResponsePayload;
187
195
  if (authResponse.success) {
188
196
  logger.log('info', 'client is authenticated');
189
- this.socketConnection.authenticated = true;
197
+ socketConnection.authenticated = true;
190
198
  } else {
191
199
  logger.log('warn', `authentication failed: ${authResponse.error}`);
192
200
  await this.disconnect();
@@ -195,15 +203,15 @@ export class SmartsocketClient {
195
203
 
196
204
  case 'serverReady':
197
205
  // Set up function request listening
198
- await this.socketConnection.listenToFunctionRequests();
206
+ await socketConnection.listenToFunctionRequests();
199
207
 
200
208
  // Handle retagging
201
209
  const oldTagStore = this.tagStore;
202
210
  this.tagStoreSubscription?.unsubscribe();
203
211
  for (const keyArg of Object.keys(this.tagStore)) {
204
- this.socketConnection.addTag(this.tagStore[keyArg]);
212
+ socketConnection.addTag(this.tagStore[keyArg]);
205
213
  }
206
- this.tagStoreSubscription = this.socketConnection.tagStoreObservable.subscribe(
214
+ this.tagStoreSubscription = socketConnection.tagStoreObservable.subscribe(
207
215
  (tagStoreArg) => {
208
216
  this.tagStore = tagStoreArg;
209
217
  }
@@ -213,12 +221,13 @@ export class SmartsocketClient {
213
221
  await this.addTag(oldTagStore[tag]);
214
222
  }
215
223
  this.updateStatus('connected');
224
+ this.isConnecting = false;
216
225
  done.resolve();
217
226
  break;
218
227
 
219
228
  default:
220
229
  // Other messages are handled by SocketConnection
221
- this.socketConnection.handleMessage(message);
230
+ socketConnection.handleMessage(message);
222
231
  break;
223
232
  }
224
233
  } catch (err) {
@@ -262,6 +271,7 @@ export class SmartsocketClient {
262
271
  return;
263
272
  }
264
273
  this.disconnectRunning = true;
274
+ this.isConnecting = false;
265
275
  this.updateStatus('disconnecting');
266
276
  this.tagStoreSubscription?.unsubscribe();
267
277
 
@@ -332,6 +342,9 @@ export class SmartsocketClient {
332
342
  functionNameArg: T['method'],
333
343
  dataArg: T['request']
334
344
  ): Promise<T['response']> {
345
+ if (!this.socketConnection) {
346
+ throw new Error('Cannot call server without an active socket connection');
347
+ }
335
348
  const socketRequest = new SocketRequest<T>(this, {
336
349
  side: 'requesting',
337
350
  originSocketConnection: this.socketConnection,
@@ -22,11 +22,11 @@ export type TSocketConnectionSide = 'server' | 'client';
22
22
  * interface for constructor of class SocketConnection
23
23
  */
24
24
  export interface ISocketConnectionConstructorOptions {
25
- alias: string;
25
+ alias?: string;
26
26
  authenticated: boolean;
27
27
  side: TSocketConnectionSide;
28
28
  smartsocketHost: Smartsocket | SmartsocketClient;
29
- socket: WebSocket | pluginsTyped.ws.WebSocket;
29
+ socket: pluginsTyped.TWebSocket | pluginsTyped.IWebSocketLike;
30
30
  }
31
31
 
32
32
  /**
@@ -43,11 +43,11 @@ export let allSocketConnections = new plugins.lik.ObjectMap<SocketConnection>();
43
43
  * class SocketConnection represents a websocket connection
44
44
  */
45
45
  export class SocketConnection {
46
- public alias: string;
46
+ public alias?: string;
47
47
  public side: TSocketConnectionSide;
48
48
  public authenticated: boolean = false;
49
49
  public smartsocketRef: Smartsocket | SmartsocketClient;
50
- public socket: WebSocket | pluginsTyped.ws.WebSocket;
50
+ public socket: pluginsTyped.TWebSocket | pluginsTyped.IWebSocketLike;
51
51
 
52
52
  public eventSubject = new plugins.smartrx.rxjs.Subject<interfaces.TConnectionStatus>();
53
53
  public eventStatus: interfaces.TConnectionStatus = 'new';
@@ -82,13 +82,13 @@ export class SocketConnection {
82
82
  public handleMessage(messageData: interfaces.ISocketMessage): void {
83
83
  switch (messageData.type) {
84
84
  case 'function':
85
- this.handleFunctionCall(messageData);
85
+ this.handleFunctionCall(messageData as interfaces.ISocketMessage<interfaces.IFunctionCallPayload>);
86
86
  break;
87
87
  case 'functionResponse':
88
- this.handleFunctionResponse(messageData);
88
+ this.handleFunctionResponse(messageData as interfaces.ISocketMessage<interfaces.IFunctionCallPayload>);
89
89
  break;
90
90
  case 'tagUpdate':
91
- this.handleTagUpdate(messageData);
91
+ this.handleTagUpdate(messageData as interfaces.ISocketMessage<interfaces.ITagUpdatePayload>);
92
92
  break;
93
93
  default:
94
94
  // Authentication messages are handled by the server/client classes
@@ -96,7 +96,11 @@ export class SocketConnection {
96
96
  }
97
97
  }
98
98
 
99
- private handleFunctionCall(messageData: interfaces.ISocketMessage): void {
99
+ private handleFunctionCall(messageData: interfaces.ISocketMessage<interfaces.IFunctionCallPayload>): void {
100
+ if (!messageData.id) {
101
+ logger.log('warn', 'received function call without request id');
102
+ return;
103
+ }
100
104
  const requestData: ISocketRequestDataObject<any> = {
101
105
  funcCallData: {
102
106
  funcName: messageData.payload.funcName,
@@ -123,7 +127,11 @@ export class SocketConnection {
123
127
  }
124
128
  }
125
129
 
126
- private handleFunctionResponse(messageData: interfaces.ISocketMessage): void {
130
+ private handleFunctionResponse(messageData: interfaces.ISocketMessage<interfaces.IFunctionCallPayload>): void {
131
+ if (!messageData.id) {
132
+ logger.log('warn', 'received function response without request id');
133
+ return;
134
+ }
127
135
  const responseData: ISocketRequestDataObject<any> = {
128
136
  funcCallData: {
129
137
  funcName: messageData.payload.funcName,
@@ -141,7 +149,7 @@ export class SocketConnection {
141
149
  }
142
150
  }
143
151
 
144
- private handleTagUpdate(messageData: interfaces.ISocketMessage): void {
152
+ private handleTagUpdate(messageData: interfaces.ISocketMessage<interfaces.ITagUpdatePayload>): void {
145
153
  const tagStoreArg = messageData.payload.tags as interfaces.TTagStore;
146
154
  if (!plugins.smartjson.deepEqualObjects(this.tagStore, tagStoreArg)) {
147
155
  this.tagStore = tagStoreArg;
@@ -181,17 +189,16 @@ export class SocketConnection {
181
189
  }
182
190
 
183
191
  /**
184
- * gets a tag by id
185
- * @param tagIdArg
192
+ * Gets a tag by id
186
193
  */
187
- public async getTagById(tagIdArg: interfaces.ITag['id']) {
194
+ public getTagById(tagIdArg: interfaces.ITag['id']): interfaces.ITag | undefined {
188
195
  return this.tagStore[tagIdArg];
189
196
  }
190
197
 
191
198
  /**
192
- * removes a tag from a connection
199
+ * Removes a tag from a connection
193
200
  */
194
- public async removeTagById(tagIdArg: interfaces.ITag['id']) {
201
+ public removeTagById(tagIdArg: interfaces.ITag['id']): void {
195
202
  delete this.tagStore[tagIdArg];
196
203
  this.tagStoreObservable.next(this.tagStore);
197
204
  this.sendMessage({
@@ -209,7 +216,7 @@ export class SocketConnection {
209
216
  const done = plugins.smartpromise.defer<SocketConnection>();
210
217
 
211
218
  // Set up message handler for authentication
212
- const messageHandler = (event: MessageEvent | { data: string }) => {
219
+ const messageHandler = (event: pluginsTyped.TMessageEvent) => {
213
220
  try {
214
221
  const data = typeof event.data === 'string' ? event.data : event.data.toString();
215
222
  const message: interfaces.ISocketMessage = JSON.parse(data);
@@ -241,11 +248,11 @@ export class SocketConnection {
241
248
  }
242
249
  }
243
250
  } catch (err) {
244
- // Not a valid message, ignore
251
+ logger.log('warn', `Failed to parse auth message: ${err instanceof Error ? err.message : String(err)}`);
245
252
  }
246
253
  };
247
254
 
248
- this.socket.addEventListener('message', messageHandler as any);
255
+ (this.socket as pluginsTyped.IWebSocketLike).addEventListener('message', messageHandler);
249
256
 
250
257
  // Request authentication
251
258
  const requestAuthPayload: interfaces.TAuthRequestMessage = {
@@ -268,17 +275,17 @@ export class SocketConnection {
268
275
  const done = plugins.smartpromise.defer();
269
276
  if (this.authenticated) {
270
277
  // Set up message handler for all messages
271
- const messageHandler = (event: MessageEvent | { data: string }) => {
278
+ const messageHandler = (event: pluginsTyped.TMessageEvent) => {
272
279
  try {
273
280
  const data = typeof event.data === 'string' ? event.data : event.data.toString();
274
281
  const message: interfaces.ISocketMessage = JSON.parse(data);
275
282
  this.handleMessage(message);
276
283
  } catch (err) {
277
- // Not a valid JSON message, ignore
284
+ logger.log('warn', `Failed to parse socket message: ${err instanceof Error ? err.message : String(err)}`);
278
285
  }
279
286
  };
280
287
 
281
- this.socket.addEventListener('message', messageHandler as any);
288
+ (this.socket as pluginsTyped.IWebSocketLike).addEventListener('message', messageHandler);
282
289
 
283
290
  logger.log(
284
291
  'info',
@@ -27,7 +27,7 @@ export interface ISocketRequestConstructorOptions<
27
27
  side: TSocketRequestSide;
28
28
  originSocketConnection: SocketConnection;
29
29
  shortId: string;
30
- funcCallData?: ISocketFunctionCallDataRequest<T>;
30
+ funcCallData: ISocketFunctionCallDataRequest<T>;
31
31
  }
32
32
 
33
33
  /**
@@ -128,6 +128,10 @@ export class SocketRequest<T extends plugins.typedrequestInterfaces.ITypedReques
128
128
  };
129
129
  this.originSocketConnection.sendMessage(message);
130
130
  this.smartsocketRef.socketRequests.remove(this);
131
+ })
132
+ .catch((error) => {
133
+ logger.log('error', `Function invocation failed for ${this.funcCallData.funcName}: ${error instanceof Error ? error.message : String(error)}`);
134
+ this.smartsocketRef.socketRequests.remove(this);
131
135
  });
132
136
  }
133
137
  }
@@ -11,8 +11,8 @@ import { logger } from './smartsocket.logging.js';
11
11
  */
12
12
  export class SocketServer {
13
13
  private smartsocket: Smartsocket;
14
- private httpServer: pluginsTyped.http.Server | pluginsTyped.https.Server;
15
- private wsServer: pluginsTyped.ws.WebSocketServer;
14
+ private httpServer: pluginsTyped.http.Server | pluginsTyped.https.Server | null = null;
15
+ private wsServer: pluginsTyped.ws.WebSocketServer | null = null;
16
16
 
17
17
  /**
18
18
  * whether httpServer is standalone (created by us)
@@ -38,17 +38,19 @@ export class SocketServer {
38
38
  const httpModule = await this.smartsocket.smartenv.getSafeNodeModule('http');
39
39
  const wsModule = await this.smartsocket.smartenv.getSafeNodeModule('ws');
40
40
 
41
- this.httpServer = httpModule.createServer();
41
+ const httpServer = httpModule.createServer();
42
+ this.httpServer = httpServer;
42
43
  this.standaloneServer = true;
43
44
 
44
45
  // Create WebSocket server attached to HTTP server
45
- this.wsServer = new wsModule.WebSocketServer({ server: this.httpServer });
46
+ const wsServer = new wsModule.WebSocketServer({ server: httpServer });
47
+ this.wsServer = wsServer;
46
48
 
47
- this.wsServer.on('connection', (ws: pluginsTyped.ws.WebSocket) => {
49
+ wsServer.on('connection', (ws: pluginsTyped.ws.WebSocket) => {
48
50
  this.smartsocket.handleNewConnection(ws);
49
51
  });
50
52
 
51
- this.httpServer.listen(this.smartsocket.options.port, () => {
53
+ httpServer.listen(this.smartsocket.options.port, () => {
52
54
  logger.log(
53
55
  'success',
54
56
  `Server started in standalone mode on port ${this.smartsocket.options.port}`
@@ -114,7 +116,7 @@ export class SocketServer {
114
116
  onOpen: async (peer: pluginsTyped.ISmartserveWebSocketPeer) => {
115
117
  // Create a wrapper that adapts ISmartserveWebSocketPeer to WebSocket-like interface
116
118
  const wsLikeSocket = this.createWsLikeFromPeer(peer);
117
- await this.smartsocket.handleNewConnection(wsLikeSocket as any);
119
+ await this.smartsocket.handleNewConnection(wsLikeSocket);
118
120
  },
119
121
  onMessage: async (peer: pluginsTyped.ISmartserveWebSocketPeer, message: pluginsTyped.ISmartserveWebSocketMessage) => {
120
122
  // Dispatch message to the SocketConnection via the adapter
@@ -153,8 +155,8 @@ export class SocketServer {
153
155
  * Creates a WebSocket-like object from a smartserve peer
154
156
  * This allows our SocketConnection to work with both native WebSocket and smartserve peers
155
157
  */
156
- private createWsLikeFromPeer(peer: pluginsTyped.ISmartserveWebSocketPeer): any {
157
- const messageListeners: Array<(event: any) => void> = [];
158
+ private createWsLikeFromPeer(peer: pluginsTyped.ISmartserveWebSocketPeer): pluginsTyped.IWebSocketLike {
159
+ const messageListeners: Array<(event: pluginsTyped.TMessageEvent) => void> = [];
158
160
  const closeListeners: Array<() => void> = [];
159
161
  const errorListeners: Array<() => void> = [];
160
162
 
@@ -174,7 +176,7 @@ export class SocketServer {
174
176
  });
175
177
 
176
178
  return {
177
- readyState: peer.readyState,
179
+ get readyState() { return peer.readyState; },
178
180
  send: (data: string) => peer.send(data),
179
181
  close: (code?: number, reason?: string) => peer.close(code, reason),
180
182
  addEventListener: (event: string, listener: any) => {
@@ -13,6 +13,29 @@ export namespace ws {
13
13
  export type RawData = wsTypes.RawData;
14
14
  }
15
15
 
16
+ /**
17
+ * Unified WebSocket type supporting both browser and Node.js environments
18
+ */
19
+ export type TWebSocket = WebSocket | ws.WebSocket;
20
+
21
+ /**
22
+ * Message event type for WebSocket messages (browser and Node.js compatible)
23
+ */
24
+ export type TMessageEvent = MessageEvent | { data: string };
25
+
26
+ /**
27
+ * WebSocket-like interface for adapters (e.g., smartserve peer adapter)
28
+ */
29
+ export interface IWebSocketLike {
30
+ readyState: number;
31
+ send(data: string): void;
32
+ close(code?: number, reason?: string): void;
33
+ addEventListener(event: 'message', listener: (event: TMessageEvent) => void): void;
34
+ addEventListener(event: 'close', listener: () => void): void;
35
+ addEventListener(event: 'error', listener: () => void): void;
36
+ removeEventListener?(event: string, listener: (...args: any[]) => void): void;
37
+ }
38
+
16
39
  // smartserve compatibility interface (for setExternalServer)
17
40
  // This mirrors the IWebSocketPeer interface from smartserve
18
41
  export interface ISmartserveWebSocketPeer {