@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/.smartconfig.json +38 -0
- package/dist_bundle/bundle.js +2407 -1890
- package/dist_bundle/bundle.js.map +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/interfaces/message.d.ts +4 -6
- package/dist_ts/smartsocket.classes.smartsocket.d.ts +1 -1
- package/dist_ts/smartsocket.classes.smartsocket.js +1 -1
- package/dist_ts/smartsocket.classes.smartsocketclient.d.ts +4 -3
- package/dist_ts/smartsocket.classes.smartsocketclient.js +20 -8
- package/dist_ts/smartsocket.classes.socketconnection.d.ts +8 -9
- package/dist_ts/smartsocket.classes.socketconnection.js +15 -8
- package/dist_ts/smartsocket.classes.socketrequest.d.ts +1 -1
- package/dist_ts/smartsocket.classes.socketrequest.js +5 -1
- package/dist_ts/smartsocket.classes.socketserver.js +10 -6
- package/dist_ts/smartsocket.pluginstyped.d.ts +22 -0
- package/license +21 -0
- package/npmextra.json +14 -7
- package/package.json +22 -19
- package/readme.md +14 -10
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/interfaces/message.ts +4 -4
- package/ts/smartsocket.classes.smartsocket.ts +3 -3
- package/ts/smartsocket.classes.smartsocketclient.ts +23 -10
- package/ts/smartsocket.classes.socketconnection.ts +28 -21
- package/ts/smartsocket.classes.socketrequest.ts +5 -1
- package/ts/smartsocket.classes.socketserver.ts +12 -10
- package/ts/smartsocket.pluginstyped.ts +23 -0
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartsocket",
|
|
3
|
-
"version": "
|
|
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://
|
|
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.
|
|
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": "^
|
|
26
|
-
"@push.rocks/smartlog": "^3.
|
|
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.
|
|
30
|
-
"ws": "^8.
|
|
34
|
+
"@push.rocks/smarttime": "^4.2.3",
|
|
35
|
+
"ws": "^8.20.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
|
-
"@git.zone/tsbuild": "^
|
|
34
|
-
"@git.zone/tsbundle": "^2.
|
|
35
|
-
"@git.zone/tsrun": "^2.0.
|
|
36
|
-
"@git.zone/tstest": "^3.
|
|
37
|
-
"@push.rocks/smartserve": "^
|
|
38
|
-
"@types/node": "^
|
|
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
|
-
"
|
|
71
|
-
|
|
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 {
|
|
146
|
+
import { SmartServe } from '@push.rocks/smartserve';
|
|
147
147
|
import { Smartsocket } from '@push.rocks/smartsocket';
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
// Create smartsocket without a port (hooks mode)
|
|
150
150
|
const smartsocket = new Smartsocket({ alias: 'myServer' });
|
|
151
151
|
|
|
152
|
-
//
|
|
153
|
-
|
|
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
|
-
//
|
|
156
|
-
|
|
159
|
+
// Add socket functions as usual
|
|
160
|
+
smartsocket.addSocketFunction(myFunction);
|
|
157
161
|
|
|
158
|
-
//
|
|
159
|
-
|
|
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
|
-
| `
|
|
234
|
+
| `getSmartserveWebSocketHooks()` | Get hooks for smartserve integration |
|
|
231
235
|
|
|
232
236
|
### SmartsocketClient
|
|
233
237
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartsocket',
|
|
6
|
-
version: '
|
|
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
|
}
|
package/ts/interfaces/message.ts
CHANGED
|
@@ -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 =
|
|
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:
|
|
49
|
+
funcData: T;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Tag update payload
|
|
54
54
|
*/
|
|
55
55
|
export interface ITagUpdatePayload {
|
|
56
|
-
tags:
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
212
|
+
socketConnection.addTag(this.tagStore[keyArg]);
|
|
205
213
|
}
|
|
206
|
-
this.tagStoreSubscription =
|
|
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
|
-
|
|
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
|
|
25
|
+
alias?: string;
|
|
26
26
|
authenticated: boolean;
|
|
27
27
|
side: TSocketConnectionSide;
|
|
28
28
|
smartsocketHost: Smartsocket | SmartsocketClient;
|
|
29
|
-
socket:
|
|
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
|
|
46
|
+
public alias?: string;
|
|
47
47
|
public side: TSocketConnectionSide;
|
|
48
48
|
public authenticated: boolean = false;
|
|
49
49
|
public smartsocketRef: Smartsocket | SmartsocketClient;
|
|
50
|
-
public socket:
|
|
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
|
-
*
|
|
185
|
-
* @param tagIdArg
|
|
192
|
+
* Gets a tag by id
|
|
186
193
|
*/
|
|
187
|
-
public
|
|
194
|
+
public getTagById(tagIdArg: interfaces.ITag['id']): interfaces.ITag | undefined {
|
|
188
195
|
return this.tagStore[tagIdArg];
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
/**
|
|
192
|
-
*
|
|
199
|
+
* Removes a tag from a connection
|
|
193
200
|
*/
|
|
194
|
-
public
|
|
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:
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
46
|
+
const wsServer = new wsModule.WebSocketServer({ server: httpServer });
|
|
47
|
+
this.wsServer = wsServer;
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
wsServer.on('connection', (ws: pluginsTyped.ws.WebSocket) => {
|
|
48
50
|
this.smartsocket.handleNewConnection(ws);
|
|
49
51
|
});
|
|
50
52
|
|
|
51
|
-
|
|
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
|
|
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):
|
|
157
|
-
const messageListeners: Array<(event:
|
|
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
|
|
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 {
|