@cocreate/server-mesh 1.2.1 → 1.2.2

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,65 +1,355 @@
1
- # CoCreate-socket-mesh
1
+ # @cocreate/server-mesh
2
2
 
3
- A pluggable, event-driven WebSocket clustering and P2P mesh driver for CoCreate Server, enabling dynamic server-to-server state synchronization across multi-cloud and bare-metal environments.
3
+ A high-performance, self-healing, peer-to-peer (P2P) encrypted transport fabric built for distributed cluster orchestration, real-time tenant routing, and inter-node event synchronization across the CoCreate ecosystem.
4
4
 
5
- ![GitHub latest release](https://img.shields.io/github/v/release/CoCreate-app/CoCreate-socket-mesh?style=flat-square)
6
- ![GitHub contributors](https://img.shields.io/github/contributors/CoCreate-app/CoCreate-socket-mesh?style=flat-square)
7
- ![License](https://img.shields.io/static/v1?style=flat-square&label=license&message=SSPL&color=green)
8
- ![Hiring](https://img.shields.io/static/v1?style=flat-square&label=&message=Hiring&color=blueviolet)
5
+ ---
9
6
 
10
- ![CoCreate](https://cdn.cocreate.app/logo.png)
7
+ ## Documentation
11
8
 
12
- [CoCreate Docs](https://cocreate.app/docs/socket-mesh)
9
+ For complete API references, deployment guides, and architecture documentation, visit the CoCreate Server Mesh documentation.
13
10
 
14
- We want this library to be community-driven, and CoCreate led. We need your help to realize this goal. To help make sure we are building the right things in the right order, we ask that you create [issues](https://github.com/CoCreate-app/Realtime_Admin_CRM_and_CMS/issues) and [pull requests](https://github.com/CoCreate-app/Realtime_Admin_CRM_and_CMS/pulls) or merely upvote or comment on existing issues or pull requests.
11
+ ## Table of Contents
15
12
 
16
- We appreciate your continued support, thank you!
13
+ - [Overview](#overview)
14
+ - [Key Features](#key-features)
15
+ - [Architecture & Message Pipeline](#architecture--message-pipeline)
16
+ - [Installation](#installation)
17
+ - [Quick Start](#quick-start)
18
+ - [Routing Directives & Targeting Matrix](#routing-directives--targeting-matrix)
19
+ - [Tenant-Aware Routing Protocols](#tenant-aware-routing-protocols)
20
+ - [Self-Healing & Reconnection Logic](#self-healing--reconnection-logic)
21
+ - [Programmatic API Reference](#programmatic-api-reference)
22
+ - [Security & TLS Specification](#security--tls-specification)
23
+ - [Environment Variables](#environment-variables)
24
+ - [Announcements](#announcements)
25
+ - [Roadmap](#roadmap)
26
+ - [How to Contribute](#how-to-contribute)
27
+ - [About](#about)
28
+ - [License](#license)
17
29
 
18
- # Table of Contents
30
+ ---
19
31
 
20
- - [Table of Contents](#table-of-contents)
21
- - [Announcements](#announcements)
22
- - [Roadmap](#roadmap)
23
- - [How to Contribute](#how-to-contribute)
24
- - [About](#about)
25
- - [License](#license)
32
+ ## Overview
26
33
 
27
- <a name="announcements"></a>
34
+ `@cocreate/server-mesh` serves as the real-time inter-server transport plane for `@cocreate/server`. It establishes zero-trust, mutual P2P encrypted WebSocket tunnels between cluster nodes, enabling seamless multi-tenant data propagation, worker thread relays, and distributed cluster command execution.
28
35
 
29
- # Announcements
36
+ Designed as a pure transport fabric, `@cocreate/server-mesh` blindly routes and emits network payloads as standard `EventEmitter` events across the cluster without introducing business-logic overhead.
30
37
 
31
- All updates to this library are documented in our [CHANGELOG](https://github.com/CoCreate-app/CoCreate-socket-mesh/blob/master/CHANGELOG.md) and [releases](https://github.com/CoCreate-app/CoCreate-socket-mesh/releases). You may also subscribe to email for releases and breaking changes.
38
+ ---
32
39
 
33
- <a name="roadmap"></a>
40
+ ## Key Features
34
41
 
35
- # Roadmap
42
+ - **Zero-Trust Ephemeral TLS:** Automatically generates temporary TLS credentials for secure node-to-node communication over the mesh network.
43
+ - **Polymorphic Target Directives:** Unicast, multicast, broadcast, worker-specific, or tenant-aware routing using a unified payload format.
44
+ - **Cryptographic Traceability:** Automatically stamps every outgoing frame with a unique `crypto.randomUUID()` identifier for distributed tracing and replay protection.
45
+ - **Tenant Isolation:** Dynamically tracks tenant-to-worker and tenant-to-server mappings so messages are only delivered to nodes actively serving an organization.
46
+ - **Self-Healing Reconnection:** Uses exponential backoff with randomized jitter to prevent reconnection storms after network interruptions.
47
+ - **Cluster IPC Integration:** Transparently bridges Node.js Primary and Worker processes while extending communication across distributed servers.
48
+ - **EventEmitter API:** Receive mesh messages using familiar Node.js event listeners.
36
49
 
37
- If you are interested in the future direction of this project, please take a look at our open [issues](https://github.com/CoCreate-app/CoCreate-socket-mesh/issues) and [pull requests](https://github.com/CoCreate-app/CoCreate-socket-mesh/pulls). We would love to hear your feedback.
50
+ ---
38
51
 
39
- <a name="about"></a>
52
+ ## Architecture & Message Pipeline
40
53
 
41
- # About
54
+ When a payload is dispatched through `@cocreate/server-mesh`, the routing engine evaluates execution context (Primary vs Worker), determines the target destination, and forwards the payload through IPC or encrypted peer connections.
42
55
 
43
- CoCreate-socket-mesh is guided and supported by the CoCreate Developer Experience Team.
56
+ $$
57
+ \text{Worker Thread}
58
+ \xrightarrow{\text{IPC}}
59
+ \text{Primary Process}
60
+ \xrightarrow{\text{Route Resolution}}
61
+ \begin{cases}
62
+ \text{Local Workers} \\
63
+ \text{Remote Peer Servers}
64
+ \end{cases}
65
+ $$
44
66
 
45
- Please Email the Developer Experience Team [here](mailto:develop@cocreate.app) in case of any queries.
67
+ ### Worker Context
46
68
 
47
- CoCreate-socket-mesh is maintained and funded by CoCreate. The names and logos for CoCreate are trademarks of CoCreate, LLC.
69
+ Worker processes:
48
70
 
49
- <a name="contribute"></a>
71
+ - Send outbound messages through native `process.send()`
72
+ - Receive inbound mesh traffic through IPC
73
+ - Never establish direct peer connections
50
74
 
51
- # How to Contribute
75
+ ### Primary Context
52
76
 
53
- We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](https://github.com/CoCreate-app/CoCreate-socket-mesh/blob/master/CONTRIBUTING.md) guide for details.
77
+ The Primary process:
54
78
 
55
- <a name="license"></a>
79
+ - Hosts the secure mesh WebSocket server
80
+ - Maintains encrypted peer connections
81
+ - Tracks tenant routing information
82
+ - Routes messages between workers and remote servers
56
83
 
57
- # License
84
+ ---
85
+
86
+ ## Installation
87
+
88
+ ### NPM
89
+
90
+ ```bash
91
+ npm install @cocreate/server-mesh
92
+ ```
93
+
94
+ ### Yarn
95
+
96
+ ```bash
97
+ yarn add @cocreate/server-mesh
98
+ ```
99
+
100
+ > **Note**
101
+ >
102
+ > `@cocreate/server-mesh` integrates directly with `@cocreate/server`, but it can also be initialized independently.
103
+
104
+ ---
105
+
106
+ ## Quick Start
107
+
108
+ ### Initialize
109
+
110
+ ```javascript
111
+ import CoCreateServer from "@cocreate/server";
112
+ import ServerMesh from "@cocreate/server-mesh";
113
+
114
+ await ServerMesh.init(CoCreateServer);
115
+ ```
116
+
117
+ ### Send a Message
118
+
119
+ Unicast to a single server:
120
+
121
+ ```javascript
122
+ ServerMesh.send({
123
+ method: "cache.flush",
124
+ server: "srv_64f8a1bc90d1",
125
+ data: {
126
+ cache: "users"
127
+ }
128
+ });
129
+ ```
130
+
131
+ Multicast to multiple servers:
132
+
133
+ ```javascript
134
+ ServerMesh.send({
135
+ method: "worker.task",
136
+ server: ["srv_64f8a1bc90d1", "srv_90e3b2ac81f4"],
137
+ worker: 2,
138
+ data: {
139
+ taskId: 1042
140
+ }
141
+ });
142
+ ```
143
+
144
+ Broadcast to every node:
145
+
146
+ ```javascript
147
+ ServerMesh.send({
148
+ broadcast: true,
149
+ method: "reload.configuration"
150
+ });
151
+ ```
152
+
153
+ ### Listen for Messages
154
+
155
+ ```javascript
156
+ ServerMesh.on("cache.flush", (message) => {
157
+ console.log(message.data);
158
+ });
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Routing Directives & Targeting Matrix
164
+
165
+ | Target | Payload | Description |
166
+ | --- | --- | --- |
167
+ | Single Server | `{ server: "srv1" }` | Routes only to the specified server. |
168
+ | Multiple Servers | `{ server: ["srv1","srv2"] }` | Sends to every listed server. |
169
+ | Worker | `{ server: "srv1", worker: 2 }` | Routes to a specific worker on the target server. |
170
+ | Local Workers | `{ worker: [1,3] }` | Sends only to selected local workers. |
171
+ | Broadcast | `{ broadcast: true }` | Delivers to every connected server and local worker. |
172
+
173
+ ---
174
+
175
+ ## Tenant-Aware Routing Protocols
176
+
177
+ The mesh minimizes unnecessary network traffic by maintaining live organization routing maps.
178
+
179
+ ```text
180
+ Client Connect
181
+
182
+
183
+ mesh.registerOrg
184
+
185
+
186
+ Map Organization → Worker
187
+
188
+
189
+ Client Disconnect
190
+
191
+
192
+ 60 Second Grace Timer
193
+
194
+
195
+ mesh.deregisterOrg
196
+
197
+
198
+ mesh.orgDeleted Broadcast
199
+ ```
200
+
201
+ ### Organization Events
202
+
203
+ | Event | Description |
204
+ | --- | --- |
205
+ | `mesh.registerOrg` | Registers a worker serving an organization. |
206
+ | `mesh.deregisterOrg` | Removes a worker's organization mapping after all connections close. |
207
+ | `mesh.orgAdded` | Broadcast to peer servers when an organization becomes active. |
208
+ | `mesh.orgDeleted` | Broadcast when an organization is no longer active on a server. |
209
+ | `mesh.orgData` | Routes organization-specific payloads only to subscribed servers and workers. |
210
+
211
+ ---
212
+
213
+ ## Self-Healing & Reconnection Logic
214
+
215
+ When peer servers disconnect, reconnection attempts use exponential backoff combined with randomized jitter to prevent synchronized reconnect storms.
216
+
217
+ $$
218
+ T_{delay}
219
+ =
220
+ \min\left(30000,\,
221
+ 1000\times1.5^{attempt}
222
+ \right)
223
+ +
224
+ random(500,4500)
225
+ $$
226
+
227
+ Connections automatically retry until:
228
+
229
+ - the peer becomes available,
230
+ - the server shuts down,
231
+ - or the connection is intentionally closed.
232
+
233
+ ---
234
+
235
+ ## Programmatic API Reference
236
+
237
+ ### `ServerMesh.init(server)`
238
+
239
+ Initializes the mesh networking layer.
240
+
241
+ ```javascript
242
+ await ServerMesh.init(server);
243
+ ```
244
+
245
+ ---
246
+
247
+ ### `ServerMesh.send(payload)`
248
+
249
+ Routes a payload across the cluster.
250
+
251
+ ```javascript
252
+ ServerMesh.send({
253
+ method: "custom.command",
254
+ server: "srv123",
255
+ data: {
256
+ key: "value"
257
+ }
258
+ });
259
+ ```
260
+
261
+ ---
262
+
263
+ ### `ServerMesh.close()`
264
+
265
+ Gracefully closes all mesh connections and releases resources.
266
+
267
+ ```javascript
268
+ await ServerMesh.close();
269
+ ```
270
+
271
+ ---
272
+
273
+ ### Events
274
+
275
+ ```javascript
276
+ ServerMesh.on("mesh.orgAdded", handler);
277
+
278
+ ServerMesh.on("mesh.orgDeleted", handler);
279
+
280
+ ServerMesh.on("mesh.orgData", handler);
281
+
282
+ ServerMesh.on("mesh.raw_message", handler);
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Security & TLS Specification
288
+
289
+ - **Encrypted Transport:** All peer communication occurs over mutually authenticated encrypted WebSocket connections.
290
+ - **Dedicated Mesh Port:** Mesh traffic is isolated from public HTTP and HTTPS traffic.
291
+ - **Ephemeral Certificates:** Temporary TLS certificates are generated during startup and automatically replaced on restart.
292
+ - **Server Authentication:** Incoming peers must successfully complete server identity validation before joining the mesh.
293
+ - **UUID Tracing:** Every transmitted frame receives a unique identifier for distributed tracing and replay protection.
294
+
295
+ ---
296
+
297
+ ## Environment Variables
298
+
299
+ | Variable | Default | Description |
300
+ | --- | --- | --- |
301
+ | `MESH_PORT` | `8090` | Port used for mesh communication. |
302
+ | `CLUSTER_SECRET` | — | Shared secret used during mesh authentication. |
303
+ | `SERVER_ID` | Auto Generated | Persistent identifier for the current server node. |
304
+
305
+ ---
306
+
307
+ ## Announcements
308
+
309
+ Release notes, performance improvements, protocol updates, and networking enhancements are published with every release. Follow the repository to stay informed about new features and compatibility changes.
310
+
311
+ ---
312
+
313
+ ## Roadmap
314
+
315
+ Upcoming enhancements include:
316
+
317
+ - Native QUIC / HTTP/3 transport
318
+ - Adaptive routing based on latency measurements
319
+ - Automatic peer discovery across local networks
320
+ - Streaming binary frame optimizations
321
+ - Dynamic topology-aware routing
322
+
323
+ ---
324
+
325
+ ## How to Contribute
326
+
327
+ We welcome pull requests, bug reports, documentation improvements, performance optimizations, and security reviews.
328
+
329
+ Please review the project's `CONTRIBUTING.md` before submitting changes, and use the GitHub Issues page to report bugs or request new features.
330
+
331
+ Community feedback helps prioritize development and improve the platform.
332
+
333
+ ---
334
+
335
+ ## About
336
+
337
+ `@cocreate/server-mesh` is designed, built, and maintained by the CoCreate Developer Experience Team.
338
+
339
+ It serves as the distributed communication backbone of the CoCreate ecosystem, providing secure, low-latency messaging between clustered servers.
340
+
341
+ For questions, architecture discussions, or deployment guidance, visit the project's GitHub Discussions or join the CoCreate community.
342
+
343
+ ---
344
+
345
+ ## License
58
346
 
59
347
  This software is dual-licensed under the GNU Affero General Public License version 3 (AGPLv3) and a commercial license.
60
348
 
61
- - **Open Source Use**: For open-source projects and non-commercial use, this software is available under the AGPLv3. The AGPLv3 allows you to freely use, modify, and distribute this software, provided that all modifications and derivative works are also licensed under the AGPLv3. For the full license text, see the [LICENSE file](https://github.com/CoCreate-app/CoCreate-socket-mesh/blob/master/LICENSE).
349
+ ### Open Source
350
+
351
+ For open-source projects and non-commercial use, this software is available under the AGPLv3. See the `LICENSE` file for the complete license text.
62
352
 
63
- - **Commercial Use**: For-profit companies and individuals intending to use this software for commercial purposes must obtain a commercial license. The commercial license is available when you sign up for an API key on our [website](https://cocreate.app). This license permits proprietary use and modification of the software without the copyleft requirements of the AGPLv3. It is ideal for integrating this software into proprietary commercial products and applications.
353
+ ### Commercial
64
354
 
65
- If you have not purchased a commercial license and intend to use this software for commercial purposes, you are required to sign up for an API key on our website.
355
+ Organizations requiring proprietary use may obtain a commercial license from CoCreate.
package/package.json CHANGED
@@ -1,16 +1,20 @@
1
1
  {
2
2
  "name": "@cocreate/server-mesh",
3
- "version": "1.2.1",
4
- "description": "Pluggable, event-driven WebSocket clustering and P2P mesh driver for CoCreate Server, enabling dynamic server-to-server state synchronization across multi-cloud and bare-metal environments.",
3
+ "version": "1.2.2",
4
+ "description": "Decentralized, event-driven P2P server mesh and transport fabric for CoCreate Server. Optimizes multi-tenant cluster concurrency with dynamic, tenant-isolated WebSocket data tunnels.",
5
5
  "keywords": [
6
- "cocreate-socket",
7
- "low-code",
8
- "realtime",
9
- "realtime-framework",
10
- "collaboration",
11
- "shared-editing",
12
- "html5-framework",
13
- "javascript-framework"
6
+ "cocreate",
7
+ "cocreate-server",
8
+ "websocket-clustering",
9
+ "p2p-mesh",
10
+ "multi-tenancy",
11
+ "realtime-collaboration",
12
+ "state-synchronization",
13
+ "distributed-systems",
14
+ "service-mesh",
15
+ "headless-server",
16
+ "crdt",
17
+ "zero-dependency"
14
18
  ],
15
19
  "publishConfig": {
16
20
  "access": "public"
package/src/inbound.js ADDED
@@ -0,0 +1,226 @@
1
+ import { WebSocketServer } from 'ws';
2
+ import https from 'node:https';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import { execSync } from 'node:child_process';
7
+ import crypto from 'node:crypto';
8
+
9
+ /**
10
+ * ServerMeshInbound manages inbound HTTPS and WebSocket secure listening transports,
11
+ * ephemeral TLS certificate generation, inbound socket heartbeats, and inbound message processing.
12
+ */
13
+ export class ServerMeshInbound {
14
+ constructor(mesh) {
15
+ this.mesh = mesh;
16
+ this.server = null;
17
+ this.meshPort = process.env.MESH_PORT || 8090;
18
+ this.secureMeshServer = null;
19
+ this.meshWss = null;
20
+ this.heartbeatInterval = null;
21
+ this.heartbeatMs = 30000;
22
+ }
23
+
24
+ /**
25
+ * Initializes the inbound server transport with host server context and IPC listeners.
26
+ *
27
+ * @param {Object} server - CoCreateServer instance context
28
+ * @returns {Promise<void>}
29
+ */
30
+ async init(server) {
31
+ this.server = server;
32
+
33
+ if (this.server && typeof this.server.on === 'function') {
34
+ this.server.on('mesh.inbound.status', () => {
35
+ console.log(`[@cocreate/server-mesh/inbound] Inbound transport status: ${this.secureMeshServer ? 'listening' : 'stopped'}`);
36
+ });
37
+
38
+ // Fallback listener for legacy event name
39
+ this.server.on('mesh.server.status', () => {
40
+ console.log(`[@cocreate/server-mesh/inbound] Inbound transport status: ${this.secureMeshServer ? 'listening' : 'stopped'}`);
41
+ });
42
+
43
+ this.server.on('mesh.inbound.relay', (payload) => {
44
+ if (payload && payload.data) {
45
+ this.broadcastInboundLocal(payload);
46
+ }
47
+ });
48
+ }
49
+
50
+ await this.startServer();
51
+ this.startHeartbeat();
52
+ }
53
+
54
+ /**
55
+ * Generates short-lived RSA-2048 X.509 TLS credentials in temporary storage.
56
+ *
57
+ * @returns {{key: Buffer, cert: Buffer}|null}
58
+ */
59
+ generateTLSCredentials() {
60
+ const tmpDir = os.tmpdir();
61
+ const keyPath = path.join(tmpDir, `mesh-key-${Date.now()}-${crypto.randomBytes(4).toString('hex')}.pem`);
62
+ const certPath = path.join(tmpDir, `mesh-cert-${Date.now()}-${crypto.randomBytes(4).toString('hex')}.pem`);
63
+
64
+ try {
65
+ execSync(`openssl req -x509 -newkey rsa:2048 -keyout "${keyPath}" -out "${certPath}" -days 1 -nodes -subj "/CN=localhost"`, { stdio: 'ignore' });
66
+ const key = fs.readFileSync(keyPath);
67
+ const cert = fs.readFileSync(certPath);
68
+
69
+ try { fs.unlinkSync(keyPath); } catch {}
70
+ try { fs.unlinkSync(certPath); } catch {}
71
+
72
+ return { key, cert };
73
+ } catch (err) {
74
+ console.error('[@cocreate/server-mesh/inbound] Failed to generate ephemeral TLS credentials:', err.message);
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Binds HTTPS and WebSocketServer listeners on MESH_PORT and configures inbound socket handlers.
81
+ */
82
+ async startServer() {
83
+ const credentials = this.generateTLSCredentials();
84
+ if (!credentials) {
85
+ throw new Error("Unable to boot secure mesh server: TLS credential generation failed.");
86
+ }
87
+
88
+ this.secureMeshServer = https.createServer(credentials);
89
+
90
+ this.secureMeshServer.on('error', (err) => {
91
+ if (err.code === 'EADDRINUSE') {
92
+ console.error(`\n[@cocreate/server-mesh/inbound] [FATAL] Port ${this.meshPort} is already in use!`);
93
+ process.exit(1);
94
+ } else {
95
+ console.error(`[@cocreate/server-mesh/inbound] HTTPS transport error:`, err);
96
+ }
97
+ });
98
+
99
+ await new Promise((resolve) => {
100
+ this.secureMeshServer.listen(this.meshPort, '0.0.0.0', () => {
101
+ console.log(`[@cocreate/server-mesh/inbound] [PRIMARY] Inbound mesh listening on 0.0.0.0:${this.meshPort}`);
102
+ resolve();
103
+ });
104
+ });
105
+
106
+ this.meshWss = new WebSocketServer({ server: this.secureMeshServer });
107
+
108
+ this.meshWss.on('connection', (ws, req) => {
109
+ const ip = req.socket.remoteAddress;
110
+ let remoteServerId = null;
111
+
112
+ const protocolHeader = req.headers['sec-websocket-protocol'];
113
+ if (protocolHeader) {
114
+ try {
115
+ const parsed = JSON.parse(decodeURIComponent(protocolHeader));
116
+ remoteServerId = parsed.id;
117
+ } catch (err) {
118
+ // Ignore protocol decoding errors
119
+ }
120
+ }
121
+
122
+ const identifier = remoteServerId || ip;
123
+ console.log(`[@cocreate/server-mesh/inbound] Secure inbound peer connection established: ${identifier}`);
124
+
125
+ ws.isAlive = true;
126
+ ws.remoteAddress = ip;
127
+ ws.on('pong', () => { ws.isAlive = true; });
128
+
129
+ ws.on('message', (rawData) => {
130
+ this.handleInboundSocketMessage(rawData, ws);
131
+ });
132
+
133
+ ws.on('close', () => {
134
+ console.log(`[@cocreate/server-mesh/inbound] Inbound peer connection ${identifier} dropped.`);
135
+ this.mesh.activeConnections.delete(identifier);
136
+ if (remoteServerId) this.mesh.activeConnections.delete(ip);
137
+ this.mesh.removeSocketFromAllOrgServers(ws);
138
+ });
139
+
140
+ ws.on('error', (err) => {
141
+ console.error(`[@cocreate/server-mesh/inbound] Inbound socket error (${identifier}):`, err.message);
142
+ });
143
+
144
+ this.mesh.activeConnections.set(identifier, ws);
145
+ if (remoteServerId) this.mesh.activeConnections.set(ip, ws);
146
+ });
147
+ }
148
+
149
+ // ... existing code ...
150
+
151
+ /**
152
+ * Relays inbound payloads down to local worker processes.
153
+ *
154
+ * @param {Object} payload
155
+ */
156
+ broadcastInboundLocal(payload) {
157
+ if (this.server && typeof this.server.send === 'function') {
158
+ try {
159
+ this.server.send({
160
+ method: 'mesh.inbound',
161
+ broadcast: payload.broadcast ?? true,
162
+ workers: payload.workers,
163
+ master: false,
164
+ data: typeof payload.data === 'string' ? payload.data : JSON.stringify(payload.data)
165
+ });
166
+ } catch (err) {
167
+ console.error("[@cocreate/server-mesh/inbound] Failed to relay inbound message to workers:", err.message);
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Starts interval timer to ping connected clients and evict dead sockets.
174
+ */
175
+ startHeartbeat() {
176
+ if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
177
+
178
+ this.heartbeatInterval = setInterval(() => {
179
+ if (!this.meshWss) return;
180
+
181
+ this.meshWss.clients.forEach((ws) => {
182
+ if (ws.isAlive === false) {
183
+ console.warn(`[@cocreate/server-mesh/inbound] Unresponsive client socket detected. Terminating connection.`);
184
+ return ws.terminate();
185
+ }
186
+ ws.isAlive = false;
187
+ try {
188
+ ws.ping();
189
+ } catch (err) {
190
+ console.error("[@cocreate/server-mesh/inbound] Ping error:", err.message);
191
+ }
192
+ });
193
+ }, this.heartbeatMs);
194
+
195
+ if (this.heartbeatInterval.unref) {
196
+ this.heartbeatInterval.unref();
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Tears down the server instance, ending heartbeats and closing listening sockets.
202
+ *
203
+ * @returns {Promise<void>}
204
+ */
205
+ async close() {
206
+ if (this.heartbeatInterval) {
207
+ clearInterval(this.heartbeatInterval);
208
+ this.heartbeatInterval = null;
209
+ }
210
+
211
+ if (this.meshWss) {
212
+ try { this.meshWss.close(); } catch {}
213
+ }
214
+
215
+ return new Promise((resolve) => {
216
+ if (this.secureMeshServer) {
217
+ this.secureMeshServer.close(() => {
218
+ console.log(`[@cocreate/server-mesh/inbound] Secure port ${this.meshPort} successfully released.`);
219
+ resolve();
220
+ });
221
+ } else {
222
+ resolve();
223
+ }
224
+ });
225
+ }
226
+ }