@push.rocks/smartsocket 2.1.0 → 4.0.0

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/readme.md CHANGED
@@ -1,149 +1,295 @@
1
1
  # @push.rocks/smartsocket
2
- easy and secure websocket communication
3
2
 
4
- ## Install
3
+ Easy and secure WebSocket communication with native WebSocket support 🔌
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Native WebSocket** - Uses native WebSocket API (browser) and `ws` library (Node.js)
8
+ - 🔄 **Auto Reconnection** - Exponential backoff with configurable retry limits
9
+ - 📡 **RPC-style Function Calls** - Define and call functions across server/client
10
+ - 🏷️ **Connection Tagging** - Tag connections for easy identification and routing
11
+ - 🔗 **Smartserve Integration** - Works seamlessly with `@push.rocks/smartserve`
12
+ - 🔒 **Secure Communication** - WSS support for encrypted connections
13
+
14
+ ## Issue Reporting and Security
15
+
16
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
5
17
 
6
- To install @push.rocks/smartsocket, you can use npm or yarn as follows:
18
+ ## Install
7
19
 
8
20
  ```shell
9
21
  npm install @push.rocks/smartsocket --save
10
22
  ```
11
- or
23
+
24
+ or with pnpm:
25
+
12
26
  ```shell
13
- yarn add @push.rocks/smartsocket
27
+ pnpm add @push.rocks/smartsocket
14
28
  ```
15
29
 
16
30
  ## Usage
17
31
 
18
- @push.rocks/smartsocket offers a robust solution for easy and secure WebSocket communication, utilizing Typescript for clean and maintainable code. Below are comprehensive examples covering various scenarios and features provided by the module.
19
-
20
- ### Getting Started
21
-
22
- First, ensure you've installed the module as shown in the "Install" section. Once installed, you can start using @push.rocks/smartsocket in your project.
23
-
24
- ### Setting Up a WebSocket Server
25
-
26
- To create a WebSocket server that clients can connect to:
32
+ ### Quick Start - Server
27
33
 
28
34
  ```typescript
29
- import { Smartsocket } from '@push.rocks/smartsocket';
35
+ import { Smartsocket, SocketFunction } from '@push.rocks/smartsocket';
30
36
 
31
- // Create a new instance of Smartsocket for the server.
32
- const server = new Smartsocket({ alias: 'myServer' });
37
+ // Create server
38
+ const server = new Smartsocket({
39
+ alias: 'myServer',
40
+ port: 3000
41
+ });
33
42
 
34
- // Define a SocketFunction that clients can call
35
- server.addSocketFunction({
43
+ // Define a function that clients can call
44
+ const greetFunction = new SocketFunction({
36
45
  funcName: 'greet',
37
- funcDef: async (data) => {
38
- console.log(`Server received: ${data.message}`);
39
- return { reply: `Hello, ${data.name}!` };
46
+ funcDef: async (data, socketConnection) => {
47
+ console.log(`Received greeting from ${data.name}`);
48
+ return { message: `Hello, ${data.name}!` };
40
49
  }
41
50
  });
42
51
 
43
- // Start the Smartsocket server
44
- server.start().then(() => {
45
- console.log('WebSocket server is running...');
46
- });
47
- ```
52
+ server.addSocketFunction(greetFunction);
48
53
 
49
- ### Creating a WebSocket Client
54
+ // Start the server
55
+ await server.start();
56
+ console.log('WebSocket server running on port 3000');
57
+ ```
50
58
 
51
- Create a client that connects to the WebSocket server and interacts with it:
59
+ ### Quick Start - Client
52
60
 
53
61
  ```typescript
54
62
  import { SmartsocketClient } from '@push.rocks/smartsocket';
55
63
 
56
- // Create a SmartsocketClient instance and connect to the server
64
+ // Create client
57
65
  const client = new SmartsocketClient({
58
- url: 'ws://localhost',
66
+ url: 'http://localhost',
59
67
  port: 3000,
60
- alias: 'myClient'
68
+ alias: 'myClient',
69
+ autoReconnect: true
61
70
  });
62
71
 
63
- client.connect().then(() => {
64
- console.log('Connected to WebSocket server');
72
+ // Connect to server
73
+ await client.connect();
74
+
75
+ // Call server function
76
+ const response = await client.serverCall('greet', { name: 'Alice' });
77
+ console.log(response.message); // "Hello, Alice!"
78
+ ```
79
+
80
+ ### Connection Options
81
+
82
+ The `SmartsocketClient` supports several configuration options:
83
+
84
+ ```typescript
85
+ const client = new SmartsocketClient({
86
+ url: 'http://localhost', // Server URL (http/https)
87
+ port: 3000, // Server port
88
+ alias: 'myClient', // Client identifier
89
+ autoReconnect: true, // Auto-reconnect on disconnect
90
+ maxRetries: 100, // Max reconnection attempts (default: 100)
91
+ initialBackoffDelay: 1000, // Initial backoff in ms (default: 1000)
92
+ maxBackoffDelay: 60000 // Max backoff in ms (default: 60000)
65
93
  });
94
+ ```
66
95
 
67
- // Define a function to call the server's 'greet' function
68
- async function greetServer(name) {
69
- const response = await client.serverCall('greet', { name: name, message: 'Hello!' });
70
- console.log(`Server replied: ${response.reply}`);
71
- }
96
+ ### Two-Way Function Calls
97
+
98
+ Both server and client can define and call functions on each other:
72
99
 
73
- // Use the function
74
- greetServer('Alice');
100
+ ```typescript
101
+ // Server calling client
102
+ const clientFunction = new SocketFunction({
103
+ funcName: 'clientTask',
104
+ funcDef: async (data) => {
105
+ return { result: 'Task completed' };
106
+ }
107
+ });
108
+
109
+ // On client
110
+ client.addSocketFunction(clientFunction);
111
+
112
+ // On server - call the client
113
+ const socketConnection = server.socketConnections.findSync(conn => conn.alias === 'myClient');
114
+ const result = await server.clientCall('clientTask', { task: 'doSomething' }, socketConnection);
75
115
  ```
76
116
 
77
- ### Handling Disconnections and Reconnections
117
+ ### Connection Tagging
78
118
 
79
- @push.rocks/smartsocket provides mechanisms to handle client disconnections and attempt reconnections:
119
+ Tag connections to identify and group them:
80
120
 
81
121
  ```typescript
82
- client.on('disconnect', () => {
83
- console.log('Disconnected from server. Attempting to reconnect...');
84
- client.connect();
122
+ // On client
123
+ await client.addTag({
124
+ id: 'role',
125
+ payload: 'admin'
126
+ });
127
+
128
+ // On server - find tagged connections
129
+ const adminConnections = server.socketConnections.getArray().filter(async conn => {
130
+ const tag = await conn.getTagById('role');
131
+ return tag?.payload === 'admin';
132
+ });
133
+
134
+ // Server can also tag connections
135
+ await socketConnection.addTag({
136
+ id: 'verified',
137
+ payload: true
85
138
  });
86
139
  ```
87
140
 
88
- ### Sending Binary Data
141
+ ### Integration with Smartserve
89
142
 
90
- The library supports the transmission of binary data efficiently:
143
+ Use smartsocket with `@push.rocks/smartserve` for advanced HTTP/WebSocket handling:
91
144
 
92
145
  ```typescript
93
- import fs from 'fs';
146
+ import { SmartServe } from '@push.rocks/smartserve';
147
+ import { Smartsocket } from '@push.rocks/smartsocket';
94
148
 
95
- // Function to send a binary file to the server
96
- async function sendBinaryData(filePath) {
97
- const fileBuffer = fs.readFileSync(filePath);
98
- await client.serverCall('sendFile', { file: fileBuffer });
99
- }
149
+ // Create smartsocket without a port (hooks mode)
150
+ const smartsocket = new Smartsocket({ alias: 'myServer' });
151
+
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
+ });
158
+
159
+ // Add socket functions as usual
160
+ smartsocket.addSocketFunction(myFunction);
161
+
162
+ // Start smartserve (smartsocket hooks mode doesn't need start())
163
+ await smartserve.start();
164
+ ```
165
+
166
+ ### Handling Disconnections
167
+
168
+ The client automatically handles reconnection with exponential backoff:
169
+
170
+ ```typescript
171
+ const client = new SmartsocketClient({
172
+ url: 'http://localhost',
173
+ port: 3000,
174
+ alias: 'myClient',
175
+ autoReconnect: true,
176
+ maxRetries: 10,
177
+ initialBackoffDelay: 500,
178
+ maxBackoffDelay: 5000
179
+ });
180
+
181
+ // Listen for connection status changes
182
+ client.eventSubject.subscribe(status => {
183
+ console.log('Connection status:', status);
184
+ // Status can be: 'new', 'connecting', 'connected', 'disconnecting', 'timedOut'
185
+ });
100
186
 
101
- sendBinaryData('./path/to/your/file.png');
187
+ await client.connect();
188
+
189
+ // Manually disconnect without auto-reconnect
190
+ await client.disconnect();
191
+
192
+ // Stop the client completely (disables auto-reconnect)
193
+ await client.stop();
102
194
  ```
103
195
 
104
- ### Securing Your WebSocket Communication
196
+ ### Secure Connections (WSS)
105
197
 
106
- @push.rocks/smartsocket leverages secure WebSocket (WSS) connections to ensure that data transferred between the client and server is encrypted. When setting up your Smartsocket server or client, use `wss://` in your URL to enable secure communication.
198
+ For secure WebSocket connections, use HTTPS URLs:
107
199
 
108
- ### Advanced Usage
200
+ ```typescript
201
+ const client = new SmartsocketClient({
202
+ url: 'https://secure.example.com', // HTTPS triggers WSS
203
+ port: 443,
204
+ alias: 'secureClient'
205
+ });
206
+ ```
207
+
208
+ ### TypedRequest Integration
109
209
 
110
- #### Mesh Networking
210
+ For strongly-typed RPC calls, define interfaces:
211
+
212
+ ```typescript
213
+ interface IGreetRequest {
214
+ method: 'greet';
215
+ request: { name: string };
216
+ response: { message: string };
217
+ }
111
218
 
112
- @push.rocks/smartsocket allows for the creation of complex mesh network configurations, enabling servers to communicate with other servers, forming a robust network with multiple nodes.
219
+ // Type-safe server call
220
+ const response = await client.serverCall<IGreetRequest>('greet', { name: 'Bob' });
221
+ // response is typed as { message: string }
222
+ ```
113
223
 
114
- #### Scaling with @push.rocks/smartsocket
224
+ ## API Reference
115
225
 
116
- To scale your WebSocket services, you can utilize load balancers and ensure your @push.rocks/smartsocket instances are stateless to allow for horizontal scaling.
226
+ ### Smartsocket (Server)
117
227
 
118
- ### Conclusion
228
+ | Method | Description |
229
+ |--------|-------------|
230
+ | `start()` | Start the WebSocket server (not needed in hooks mode) |
231
+ | `stop()` | Stop the server and close all connections |
232
+ | `addSocketFunction(fn)` | Register a function that clients can call |
233
+ | `clientCall(funcName, data, connection)` | Call a function on a specific client |
234
+ | `getSmartserveWebSocketHooks()` | Get hooks for smartserve integration |
119
235
 
120
- This guide has covered how to set up basic WebSocket communication with @push.rocks/smartsocket, handle disconnections/reconnections, secure your communication, send binary data, and briefly touched on advanced concepts like mesh networking and scaling.
236
+ ### SmartsocketClient
121
237
 
122
- For more detailed documentation, visit [the official @push.rocks/smartsocket GitLab repository](https://gitlab.com/pushrocks/smartsocket).
238
+ | Method | Description |
239
+ |--------|-------------|
240
+ | `connect()` | Connect to the server |
241
+ | `disconnect()` | Disconnect from the server |
242
+ | `stop()` | Disconnect and disable auto-reconnect |
243
+ | `serverCall(funcName, data)` | Call a function on the server |
244
+ | `addSocketFunction(fn)` | Register a function the server can call |
245
+ | `addTag(tag)` | Add a tag to the connection |
246
+ | `getTagById(id)` | Get a tag by its ID |
247
+ | `removeTagById(id)` | Remove a tag by its ID |
123
248
 
124
- Remember, WebSocket communication with @push.rocks/smartsocket is not only about sending and receiving messages. It's about creating a fast, reliable, and secure communication channel for your real-time applications.
249
+ ### SocketFunction
125
250
 
126
- Happy coding!
251
+ ```typescript
252
+ const fn = new SocketFunction({
253
+ funcName: 'myFunction',
254
+ funcDef: async (data, socketConnection) => {
255
+ // data: the request payload
256
+ // socketConnection: the calling connection
257
+ return { result: 'response' };
258
+ }
259
+ });
260
+ ```
127
261
 
128
- ---
262
+ ## Architecture
129
263
 
130
- Please note, the documentation above is a starting point. Depending on the complexity and requirements of your application, you may need to explore more features and configurations provided by @push.rocks/smartsocket. Always refer to the official documentation for the most current information and best practices.
264
+ ```
265
+ ┌─────────────────┐ WebSocket ┌─────────────────┐
266
+ │ SmartsocketClient │◄────────────────────────►│ Smartsocket │
267
+ │ (Browser/Node) │ Native WebSocket │ (Server) │
268
+ └─────────────────┘ └─────────────────┘
269
+ │ │
270
+ │ SocketFunction SocketFunction │
271
+ │ (serverCall) (clientCall) │
272
+ │ │
273
+ └──────────────── RPC-style Calls ──────────────┘
274
+ ```
131
275
 
132
276
  ## License and Legal Information
133
277
 
134
- This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
278
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
135
279
 
136
280
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
137
281
 
138
282
  ### Trademarks
139
283
 
140
- This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
284
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
285
+
286
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
141
287
 
142
288
  ### Company Information
143
289
 
144
- Task Venture Capital GmbH
145
- Registered at District court Bremen HRB 35230 HB, Germany
290
+ Task Venture Capital GmbH
291
+ Registered at District Court Bremen HRB 35230 HB, Germany
146
292
 
147
- For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
293
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
148
294
 
149
295
  By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartsocket',
6
- version: '2.1.0',
6
+ version: '4.0.0',
7
7
  description: 'Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.'
8
8
  }
@@ -1,2 +1,3 @@
1
1
  export * from './connection.js';
2
2
  export * from './tag.js';
3
+ export * from './message.js';
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Message types for the smartsocket protocol
3
+ */
4
+ export type TMessageType =
5
+ | 'authRequest' // Server requests authentication from client
6
+ | 'auth' // Client provides authentication data
7
+ | 'authResponse' // Server responds to authentication
8
+ | 'serverReady' // Server signals it's fully ready
9
+ | 'function' // Function call request
10
+ | 'functionResponse' // Function call response
11
+ | 'tagUpdate'; // Tag store synchronization
12
+
13
+ /**
14
+ * Base message interface for all smartsocket messages
15
+ */
16
+ export interface ISocketMessage<T = unknown> {
17
+ type: TMessageType;
18
+ id?: string; // For request/response correlation
19
+ payload: T;
20
+ }
21
+
22
+ /**
23
+ * Authentication request payload (server -> client)
24
+ */
25
+ export interface IAuthRequestPayload {
26
+ serverAlias: string;
27
+ }
28
+
29
+ /**
30
+ * Authentication data payload (client -> server)
31
+ */
32
+ export interface IAuthPayload {
33
+ alias: string;
34
+ }
35
+
36
+ /**
37
+ * Authentication response payload (server -> client)
38
+ */
39
+ export interface IAuthResponsePayload {
40
+ success: boolean;
41
+ error?: string;
42
+ }
43
+
44
+ /**
45
+ * Function call payload
46
+ */
47
+ export interface IFunctionCallPayload<T = unknown> {
48
+ funcName: string;
49
+ funcData: T;
50
+ }
51
+
52
+ /**
53
+ * Tag update payload
54
+ */
55
+ export interface ITagUpdatePayload {
56
+ tags: Record<string, unknown>;
57
+ }
58
+
59
+ /**
60
+ * Helper type for creating typed messages
61
+ */
62
+ export type TAuthRequestMessage = ISocketMessage<IAuthRequestPayload>;
63
+ export type TAuthMessage = ISocketMessage<IAuthPayload>;
64
+ export type TAuthResponseMessage = ISocketMessage<IAuthResponsePayload>;
65
+ export type TFunctionMessage = ISocketMessage<IFunctionCallPayload>;
66
+ export type TFunctionResponseMessage = ISocketMessage<IFunctionCallPayload>;
67
+ export type TTagUpdateMessage = ISocketMessage<ITagUpdatePayload>;
@@ -26,7 +26,6 @@ export class Smartsocket {
26
26
  public alias: string;
27
27
  public smartenv = new plugins.smartenv.Smartenv();
28
28
  public options: ISmartsocketConstructorOptions;
29
- public io: pluginsTyped.socketIo.Server;
30
29
  public socketConnections = new plugins.lik.ObjectMap<SocketConnection>();
31
30
  public socketFunctions = new plugins.lik.ObjectMap<SocketFunction<any>>();
32
31
  public socketRequests = new plugins.lik.ObjectMap<SocketRequest<any>>();
@@ -40,26 +39,59 @@ export class Smartsocket {
40
39
  this.alias = plugins.isounique.uni(this.options.alias);
41
40
  }
42
41
 
43
- public async setExternalServer(serverType: 'smartexpress', serverArg: any) {
44
- await this.socketServer.setExternalServer(serverType, serverArg);
42
+ /**
43
+ * Returns WebSocket hooks for integration with smartserve
44
+ * Pass these hooks to SmartServe's websocket config
45
+ */
46
+ public getSmartserveWebSocketHooks(): pluginsTyped.ISmartserveWebSocketHooks {
47
+ return this.socketServer.getSmartserveWebSocketHooks();
45
48
  }
46
49
 
47
50
  /**
48
51
  * starts smartsocket
49
52
  */
50
53
  public async start() {
51
- const socketIoModule = await this.smartenv.getSafeNodeModule('socket.io');
52
- this.io = new socketIoModule.Server(await this.socketServer.getServerForSocketIo(), {
53
- cors: {
54
- allowedHeaders: '*',
55
- methods: '*',
56
- origin: '*',
57
- },
58
- });
59
54
  await this.socketServer.start();
60
- this.io.on('connection', (socketArg) => {
61
- this._handleSocketConnection(socketArg);
55
+ }
56
+
57
+ /**
58
+ * Handle a new WebSocket connection
59
+ * Called by SocketServer when a new connection is established
60
+ */
61
+ public async handleNewConnection(socket: pluginsTyped.TWebSocket | pluginsTyped.IWebSocketLike) {
62
+ const socketConnection: SocketConnection = new SocketConnection({
63
+ alias: undefined,
64
+ authenticated: false,
65
+ side: 'server',
66
+ smartsocketHost: this,
67
+ socket: socket,
62
68
  });
69
+
70
+ logger.log('info', 'Socket connected. Trying to authenticate...');
71
+ this.socketConnections.add(socketConnection);
72
+
73
+ // Handle disconnection
74
+ const handleClose = () => {
75
+ this.socketConnections.remove(socketConnection);
76
+ socketConnection.eventSubject.next('disconnected');
77
+ };
78
+
79
+ (socket as pluginsTyped.IWebSocketLike).addEventListener('close', handleClose);
80
+ (socket as pluginsTyped.IWebSocketLike).addEventListener('error', handleClose);
81
+
82
+ try {
83
+ await socketConnection.authenticate();
84
+ await socketConnection.listenToFunctionRequests();
85
+
86
+ // Signal that the server is ready
87
+ socketConnection.sendMessage({
88
+ type: 'serverReady',
89
+ payload: {},
90
+ });
91
+ } catch (err) {
92
+ logger.log('warn', `Authentication failed: ${err}`);
93
+ this.socketConnections.remove(socketConnection);
94
+ }
63
95
  }
64
96
 
65
97
  /**
@@ -77,10 +109,9 @@ export class Smartsocket {
77
109
  }
78
110
  });
79
111
  this.socketConnections.wipe();
80
- this.io.close();
81
112
 
82
- // stop the corresponging server
83
- this.socketServer.stop();
113
+ // stop the corresponding server
114
+ await this.socketServer.stop();
84
115
  }
85
116
 
86
117
  // communication
@@ -110,28 +141,4 @@ export class Smartsocket {
110
141
  public addSocketFunction(socketFunction: SocketFunction<any>) {
111
142
  this.socketFunctions.add(socketFunction);
112
143
  }
113
-
114
- /**
115
- * the standard handler for new socket connections
116
- */
117
- private async _handleSocketConnection(socketArg: pluginsTyped.socketIo.Socket) {
118
- const socketConnection: SocketConnection = new SocketConnection({
119
- alias: undefined,
120
- authenticated: false,
121
- side: 'server',
122
- smartsocketHost: this,
123
- socket: socketArg,
124
- });
125
- logger.log('info', 'Socket connected. Trying to authenticate...');
126
- this.socketConnections.add(socketConnection);
127
- const disconnectSubscription = socketConnection.eventSubject.subscribe((eventArg) => {
128
- if (eventArg === 'disconnected') {
129
- this.socketConnections.remove(socketConnection);
130
- disconnectSubscription.unsubscribe();
131
- }
132
- });
133
- await socketConnection.authenticate();
134
- await socketConnection.listenToFunctionRequests();
135
- await socketConnection.socket.emit('serverFullyReactive');
136
- }
137
144
  }