@mono-labs/dev 0.1.276 → 0.1.278
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAI1D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAEhE,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACzE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,mBAAmB;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAI1D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAEhE,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACzE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,mBAAmB;;;;;;0BAsL9D,SAAS;EAEhC"}
|
package/dist/websocket/index.js
CHANGED
|
@@ -75,6 +75,36 @@ function attachSocketAdapter(wss, config) {
|
|
|
75
75
|
const disconnectHandler = config?.disconnectHandler ?? (async () => { });
|
|
76
76
|
// Reverse lookup: WebSocket → connectionId
|
|
77
77
|
const wsToConnectionId = new WeakMap();
|
|
78
|
+
// Heartbeat tracking
|
|
79
|
+
const pingEnabled = config?.ping ?? true;
|
|
80
|
+
const pingInterval = config?.pingInterval ?? 30_000;
|
|
81
|
+
const pingTimeout = config?.pingTimeout ?? 10_000;
|
|
82
|
+
const aliveSet = new Set();
|
|
83
|
+
let heartbeatTimer;
|
|
84
|
+
if (pingEnabled) {
|
|
85
|
+
heartbeatTimer = setInterval(() => {
|
|
86
|
+
for (const ws of wss.clients) {
|
|
87
|
+
if (!aliveSet.has(ws)) {
|
|
88
|
+
const cid = wsToConnectionId.get(ws);
|
|
89
|
+
if (debug)
|
|
90
|
+
console.log(`[socket-adapter] ping timeout — terminating connectionId=${cid}`);
|
|
91
|
+
ws.terminate();
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
aliveSet.delete(ws);
|
|
95
|
+
ws.ping();
|
|
96
|
+
if (debug) {
|
|
97
|
+
const cid = wsToConnectionId.get(ws);
|
|
98
|
+
console.log(`[socket-adapter] ping sent to connectionId=${cid}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, pingInterval);
|
|
102
|
+
wss.on('close', () => {
|
|
103
|
+
clearInterval(heartbeatTimer);
|
|
104
|
+
if (debug)
|
|
105
|
+
console.log(`[socket-adapter] heartbeat interval cleared`);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
78
108
|
wss.on('connection', async (ws, req) => {
|
|
79
109
|
// 1. Extract token from query string
|
|
80
110
|
const url = new URL(req.url ?? '', `http://${req.headers.host ?? 'localhost'}`);
|
|
@@ -113,6 +143,15 @@ function attachSocketAdapter(wss, config) {
|
|
|
113
143
|
ws.send(JSON.stringify(welcomeMessage));
|
|
114
144
|
if (debug)
|
|
115
145
|
console.log(`[socket-adapter] welcome sent to ${connectionId}${userContext ? ` userId=${userContext.userId}` : ''}`);
|
|
146
|
+
// 6b. Register for heartbeat
|
|
147
|
+
if (pingEnabled) {
|
|
148
|
+
aliveSet.add(ws);
|
|
149
|
+
ws.on('pong', () => {
|
|
150
|
+
aliveSet.add(ws);
|
|
151
|
+
if (debug)
|
|
152
|
+
console.log(`[socket-adapter] pong received from connectionId=${connectionId}`);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
116
155
|
// 7. Route incoming messages through ActionRouter
|
|
117
156
|
ws.on('message', async (raw) => {
|
|
118
157
|
const rawBody = raw.toString();
|
|
@@ -137,6 +176,7 @@ function attachSocketAdapter(wss, config) {
|
|
|
137
176
|
if (debug) {
|
|
138
177
|
console.log(`[socket-adapter] $disconnect connectionId=${connectionId} code=${code} reason=${reason.toString()}`);
|
|
139
178
|
}
|
|
179
|
+
aliveSet.delete(ws);
|
|
140
180
|
await channelStore.removeAll(connectionId);
|
|
141
181
|
await disconnectHandler(connectionId);
|
|
142
182
|
connectionRegistry.unregister(connectionId);
|
|
@@ -58,5 +58,11 @@ export interface SocketAdapterConfig {
|
|
|
58
58
|
channelStore?: import('./channel-store').ChannelStore;
|
|
59
59
|
useRedis?: boolean;
|
|
60
60
|
redis?: RedisConfig;
|
|
61
|
+
/** Enable ws.ping heartbeat (default: true) */
|
|
62
|
+
ping?: boolean;
|
|
63
|
+
/** Ping interval in ms (default: 30000) */
|
|
64
|
+
pingInterval?: number;
|
|
65
|
+
/** Time to wait for pong before considering connection dead (default: 10000) */
|
|
66
|
+
pingTimeout?: number;
|
|
61
67
|
}
|
|
62
68
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/websocket/types.ts"],"names":[],"mappings":"AAEA,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB;AAED,sFAAsF;AACtF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AAEjC,6FAA6F;AAC7F,MAAM,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE7F,mEAAmE;AACnE,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,YAAY,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAA;CAC/C;AAED,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACpC,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,EAAE,mBAAmB,CAAA;IACnC,gBAAgB,EAAE,kBAAkB,CAAA;IACpC,WAAW,EAAE,oBAAoB,CAAA;CACjC;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACb;AAED,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GAAG,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,oBAAoB,KACrB,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAEjC,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,CAC9B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KACtB,OAAO,CAAC;IACZ,QAAQ,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAClC,CAAC,CAAA;AAEF,oCAAoC;AACpC,MAAM,MAAM,mBAAmB,GAAG,CAAC,YAAY,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE/E,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,cAAc,CAAC,EAAE,gBAAgB,CAAA;IACjC,iBAAiB,CAAC,EAAE,mBAAmB,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACtC,cAAc,CAAC,EAAE,aAAa,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,iBAAiB,EAAE,YAAY,CAAA;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/websocket/types.ts"],"names":[],"mappings":"AAEA,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB;AAED,sFAAsF;AACtF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AAEjC,6FAA6F;AAC7F,MAAM,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE7F,mEAAmE;AACnE,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,YAAY,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAA;CAC/C;AAED,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACpC,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,EAAE,mBAAmB,CAAA;IACnC,gBAAgB,EAAE,kBAAkB,CAAA;IACpC,WAAW,EAAE,oBAAoB,CAAA;CACjC;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACb;AAED,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GAAG,CAC3B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,oBAAoB,KACrB,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAEjC,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,CAC9B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KACtB,OAAO,CAAC;IACZ,QAAQ,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAClC,CAAC,CAAA;AAEF,oCAAoC;AACpC,MAAM,MAAM,mBAAmB,GAAG,CAAC,YAAY,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE/E,0BAA0B;AAC1B,MAAM,WAAW,WAAW;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,cAAc,CAAC,EAAE,gBAAgB,CAAA;IACjC,iBAAiB,CAAC,EAAE,mBAAmB,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACtC,cAAc,CAAC,EAAE,aAAa,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,iBAAiB,EAAE,YAAY,CAAA;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB"}
|
package/package.json
CHANGED
package/src/websocket/index.ts
CHANGED
|
@@ -79,6 +79,37 @@ export function attachSocketAdapter(wss: WebSocketServer, config?: SocketAdapter
|
|
|
79
79
|
// Reverse lookup: WebSocket → connectionId
|
|
80
80
|
const wsToConnectionId = new WeakMap<WebSocket, ConnectionId>()
|
|
81
81
|
|
|
82
|
+
// Heartbeat tracking
|
|
83
|
+
const pingEnabled = config?.ping ?? true
|
|
84
|
+
const pingInterval = config?.pingInterval ?? 30_000
|
|
85
|
+
const pingTimeout = config?.pingTimeout ?? 10_000
|
|
86
|
+
const aliveSet = new Set<WebSocket>()
|
|
87
|
+
let heartbeatTimer: ReturnType<typeof setInterval> | undefined
|
|
88
|
+
|
|
89
|
+
if (pingEnabled) {
|
|
90
|
+
heartbeatTimer = setInterval(() => {
|
|
91
|
+
for (const ws of wss.clients) {
|
|
92
|
+
if (!aliveSet.has(ws)) {
|
|
93
|
+
const cid = wsToConnectionId.get(ws)
|
|
94
|
+
if (debug) console.log(`[socket-adapter] ping timeout — terminating connectionId=${cid}`)
|
|
95
|
+
ws.terminate()
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
aliveSet.delete(ws)
|
|
99
|
+
ws.ping()
|
|
100
|
+
if (debug) {
|
|
101
|
+
const cid = wsToConnectionId.get(ws)
|
|
102
|
+
console.log(`[socket-adapter] ping sent to connectionId=${cid}`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, pingInterval)
|
|
106
|
+
|
|
107
|
+
wss.on('close', () => {
|
|
108
|
+
clearInterval(heartbeatTimer)
|
|
109
|
+
if (debug) console.log(`[socket-adapter] heartbeat interval cleared`)
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
wss.on('connection', async (ws: WebSocket, req) => {
|
|
83
114
|
// 1. Extract token from query string
|
|
84
115
|
const url = new URL(req.url ?? '', `http://${req.headers.host ?? 'localhost'}`)
|
|
@@ -118,6 +149,15 @@ export function attachSocketAdapter(wss: WebSocketServer, config?: SocketAdapter
|
|
|
118
149
|
ws.send(JSON.stringify(welcomeMessage))
|
|
119
150
|
if (debug) console.log(`[socket-adapter] welcome sent to ${connectionId}${userContext ? ` userId=${userContext.userId}` : ''}`)
|
|
120
151
|
|
|
152
|
+
// 6b. Register for heartbeat
|
|
153
|
+
if (pingEnabled) {
|
|
154
|
+
aliveSet.add(ws)
|
|
155
|
+
ws.on('pong', () => {
|
|
156
|
+
aliveSet.add(ws)
|
|
157
|
+
if (debug) console.log(`[socket-adapter] pong received from connectionId=${connectionId}`)
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
121
161
|
// 7. Route incoming messages through ActionRouter
|
|
122
162
|
ws.on('message', async (raw) => {
|
|
123
163
|
const rawBody = raw.toString()
|
|
@@ -150,6 +190,7 @@ export function attachSocketAdapter(wss: WebSocketServer, config?: SocketAdapter
|
|
|
150
190
|
)
|
|
151
191
|
}
|
|
152
192
|
|
|
193
|
+
aliveSet.delete(ws)
|
|
153
194
|
await channelStore.removeAll(connectionId)
|
|
154
195
|
await disconnectHandler(connectionId)
|
|
155
196
|
connectionRegistry.unregister(connectionId)
|
package/src/websocket/types.ts
CHANGED
|
@@ -71,4 +71,10 @@ export interface SocketAdapterConfig {
|
|
|
71
71
|
channelStore?: import('./channel-store').ChannelStore
|
|
72
72
|
useRedis?: boolean
|
|
73
73
|
redis?: RedisConfig
|
|
74
|
+
/** Enable ws.ping heartbeat (default: true) */
|
|
75
|
+
ping?: boolean
|
|
76
|
+
/** Ping interval in ms (default: 30000) */
|
|
77
|
+
pingInterval?: number
|
|
78
|
+
/** Time to wait for pong before considering connection dead (default: 10000) */
|
|
79
|
+
pingTimeout?: number
|
|
74
80
|
}
|