@principal-ai/control-tower-core 0.1.7 → 0.1.8
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 +53 -0
- package/dist/abstractions/DefaultPresenceManager.d.ts +40 -0
- package/dist/abstractions/DefaultPresenceManager.d.ts.map +1 -0
- package/dist/abstractions/DefaultPresenceManager.js +256 -0
- package/dist/abstractions/PresenceManager.d.ts +127 -0
- package/dist/abstractions/PresenceManager.d.ts.map +1 -0
- package/dist/abstractions/PresenceManager.js +80 -0
- package/dist/abstractions/index.d.ts +2 -0
- package/dist/abstractions/index.d.ts.map +1 -1
- package/dist/abstractions/index.js +5 -1
- package/dist/adapters/mock/MockTransportAdapter.d.ts +37 -1
- package/dist/adapters/mock/MockTransportAdapter.d.ts.map +1 -1
- package/dist/adapters/mock/MockTransportAdapter.js +101 -2
- package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts.map +1 -1
- package/dist/adapters/websocket/WebSocketTransportAdapter.js +5 -3
- package/dist/index.js.map +9 -7
- package/dist/index.mjs +511 -30
- package/dist/index.mjs.map +9 -7
- package/dist/server/BaseServer.d.ts +59 -0
- package/dist/server/BaseServer.d.ts.map +1 -1
- package/dist/server/BaseServer.js +219 -28
- package/dist/server/ServerBuilder.d.ts +11 -0
- package/dist/server/ServerBuilder.d.ts.map +1 -1
- package/dist/server/ServerBuilder.js +13 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/presence.d.ts +163 -0
- package/dist/types/presence.d.ts.map +1 -0
- package/dist/types/presence.js +8 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import type { IAuthAdapter } from '../abstractions/AuthAdapter.js';
|
|
|
4
4
|
import type { IStorageAdapter } from '../abstractions/StorageAdapter.js';
|
|
5
5
|
import type { RoomManager } from '../abstractions/RoomManager.js';
|
|
6
6
|
import type { LockManager } from '../abstractions/LockManager.js';
|
|
7
|
+
import type { PresenceManager } from '../abstractions/PresenceManager.js';
|
|
7
8
|
import { TypedEventEmitter } from '../abstractions/EventEmitter.js';
|
|
8
9
|
import { ExperimentalAPI } from './ExperimentalAPI.js';
|
|
9
10
|
import type { Server as HttpServer } from 'http';
|
|
@@ -15,6 +16,7 @@ export interface ServerConfig {
|
|
|
15
16
|
storage?: IStorageAdapter;
|
|
16
17
|
roomManager: RoomManager;
|
|
17
18
|
lockManager: LockManager;
|
|
19
|
+
presenceManager?: PresenceManager;
|
|
18
20
|
defaultRoomConfig?: Partial<RoomConfig>;
|
|
19
21
|
httpServer?: HttpServer | HttpsServer;
|
|
20
22
|
webSocketPath?: string;
|
|
@@ -24,6 +26,21 @@ export interface ServerConfig {
|
|
|
24
26
|
export interface ConnectedClient {
|
|
25
27
|
id: string;
|
|
26
28
|
userId: string;
|
|
29
|
+
/**
|
|
30
|
+
* Set of room IDs the client is currently in
|
|
31
|
+
*
|
|
32
|
+
* Multi-room support: A client can be in multiple rooms simultaneously.
|
|
33
|
+
* This enables the two-tier architecture where clients can maintain
|
|
34
|
+
* global presence while participating in multiple repository rooms.
|
|
35
|
+
*/
|
|
36
|
+
roomIds: Set<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Legacy single-room accessor for backward compatibility
|
|
39
|
+
*
|
|
40
|
+
* Returns the first room the client is in, or null if not in any rooms.
|
|
41
|
+
*
|
|
42
|
+
* @deprecated Use roomIds for multi-room support
|
|
43
|
+
*/
|
|
27
44
|
roomId: string | null;
|
|
28
45
|
authenticated: boolean;
|
|
29
46
|
connectedAt: number;
|
|
@@ -76,6 +93,26 @@ export interface ServerEvents {
|
|
|
76
93
|
context?: string;
|
|
77
94
|
};
|
|
78
95
|
experimental_usage: ExperimentalUsageEvent;
|
|
96
|
+
presence_changed: {
|
|
97
|
+
userId: string;
|
|
98
|
+
status: string;
|
|
99
|
+
timestamp: number;
|
|
100
|
+
};
|
|
101
|
+
presence_device_connected: {
|
|
102
|
+
userId: string;
|
|
103
|
+
deviceId: string;
|
|
104
|
+
timestamp: number;
|
|
105
|
+
};
|
|
106
|
+
presence_device_disconnected: {
|
|
107
|
+
userId: string;
|
|
108
|
+
deviceId: string;
|
|
109
|
+
timestamp: number;
|
|
110
|
+
};
|
|
111
|
+
presence_activity: {
|
|
112
|
+
userId: string;
|
|
113
|
+
deviceId: string;
|
|
114
|
+
timestamp: number;
|
|
115
|
+
};
|
|
79
116
|
[key: string]: unknown;
|
|
80
117
|
}
|
|
81
118
|
export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
|
|
@@ -84,12 +121,14 @@ export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
|
|
|
84
121
|
private storage?;
|
|
85
122
|
private roomManager;
|
|
86
123
|
private lockManager;
|
|
124
|
+
private presenceManager?;
|
|
87
125
|
private config;
|
|
88
126
|
private clients;
|
|
89
127
|
private clientMessageHandlers;
|
|
90
128
|
private running;
|
|
91
129
|
private initialized;
|
|
92
130
|
private mode;
|
|
131
|
+
private heartbeatIntervalId?;
|
|
93
132
|
/**
|
|
94
133
|
* Experimental APIs namespace
|
|
95
134
|
*
|
|
@@ -100,6 +139,14 @@ export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
|
|
|
100
139
|
*/
|
|
101
140
|
readonly experimental: ExperimentalAPI;
|
|
102
141
|
constructor(config: ServerConfig);
|
|
142
|
+
/**
|
|
143
|
+
* Start periodic heartbeat processing for presence management
|
|
144
|
+
*/
|
|
145
|
+
private startHeartbeatProcessor;
|
|
146
|
+
/**
|
|
147
|
+
* Stop heartbeat processor
|
|
148
|
+
*/
|
|
149
|
+
private stopHeartbeatProcessor;
|
|
103
150
|
start(port?: number): Promise<void>;
|
|
104
151
|
initialize(): Promise<void>;
|
|
105
152
|
stop(): Promise<void>;
|
|
@@ -126,5 +173,17 @@ export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
|
|
|
126
173
|
isRunning(): boolean;
|
|
127
174
|
getMode(): 'standalone' | 'integration';
|
|
128
175
|
isInitialized(): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Update presence activity for a user's device
|
|
178
|
+
*
|
|
179
|
+
* @param userId - User identifier
|
|
180
|
+
* @param deviceId - Device/client identifier
|
|
181
|
+
* @param activityType - Type of activity
|
|
182
|
+
*/
|
|
183
|
+
private updatePresenceActivity;
|
|
184
|
+
/**
|
|
185
|
+
* Get presence manager (if configured)
|
|
186
|
+
*/
|
|
187
|
+
getPresenceManager(): PresenceManager | undefined;
|
|
129
188
|
}
|
|
130
189
|
//# sourceMappingURL=BaseServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseServer.d.ts","sourceRoot":"","sources":["../../src/server/BaseServer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"BaseServer.d.ts","sourceRoot":"","sources":["../../src/server/BaseServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
16
16
|
this.storage = config.storage;
|
|
17
17
|
this.roomManager = config.roomManager;
|
|
18
18
|
this.lockManager = config.lockManager;
|
|
19
|
+
this.presenceManager = config.presenceManager;
|
|
19
20
|
// Initialize experimental API namespace
|
|
20
21
|
this.experimental = new ExperimentalAPI_js_1.ExperimentalAPI({
|
|
21
22
|
config: config.experimental || {
|
|
@@ -43,6 +44,60 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
43
44
|
this.transport.onMessage(this.handleTransportMessage.bind(this));
|
|
44
45
|
this.transport.onError(this.handleTransportError.bind(this));
|
|
45
46
|
this.transport.onClose(this.handleTransportClose.bind(this));
|
|
47
|
+
// Start heartbeat processor if presence is enabled
|
|
48
|
+
if (this.presenceManager?.isEnabled()) {
|
|
49
|
+
this.startHeartbeatProcessor();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Start periodic heartbeat processing for presence management
|
|
54
|
+
*/
|
|
55
|
+
startHeartbeatProcessor() {
|
|
56
|
+
if (!this.presenceManager) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const config = this.presenceManager.getConfig();
|
|
60
|
+
const interval = config.heartbeatInterval;
|
|
61
|
+
this.heartbeatIntervalId = setInterval(async () => {
|
|
62
|
+
try {
|
|
63
|
+
const changes = await this.presenceManager.processHeartbeats();
|
|
64
|
+
// Emit presence change events
|
|
65
|
+
for (const change of changes) {
|
|
66
|
+
await this.emit('presence_changed', {
|
|
67
|
+
userId: change.userId,
|
|
68
|
+
status: change.status,
|
|
69
|
+
timestamp: change.timestamp
|
|
70
|
+
});
|
|
71
|
+
// Broadcast presence updates if enabled
|
|
72
|
+
if (config.broadcastPresenceUpdates) {
|
|
73
|
+
await this.experimental.broadcastAuthenticated({
|
|
74
|
+
type: 'presence:status_changed',
|
|
75
|
+
payload: {
|
|
76
|
+
userId: change.userId,
|
|
77
|
+
status: change.status,
|
|
78
|
+
previousStatus: change.previousStatus,
|
|
79
|
+
reason: change.reason
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
await this.emit('error', {
|
|
87
|
+
error: error,
|
|
88
|
+
context: 'heartbeat_processor'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}, interval);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Stop heartbeat processor
|
|
95
|
+
*/
|
|
96
|
+
stopHeartbeatProcessor() {
|
|
97
|
+
if (this.heartbeatIntervalId) {
|
|
98
|
+
clearInterval(this.heartbeatIntervalId);
|
|
99
|
+
this.heartbeatIntervalId = undefined;
|
|
100
|
+
}
|
|
46
101
|
}
|
|
47
102
|
// Server Lifecycle
|
|
48
103
|
async start(port) {
|
|
@@ -110,6 +165,8 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
110
165
|
return;
|
|
111
166
|
}
|
|
112
167
|
this.running = false;
|
|
168
|
+
// Stop heartbeat processor
|
|
169
|
+
this.stopHeartbeatProcessor();
|
|
113
170
|
// Disconnect all clients
|
|
114
171
|
for (const [clientId] of Array.from(this.clients)) {
|
|
115
172
|
await this.disconnectClient(clientId, 'Server shutting down');
|
|
@@ -122,6 +179,10 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
122
179
|
}
|
|
123
180
|
this.clients.clear();
|
|
124
181
|
this.clientMessageHandlers.clear();
|
|
182
|
+
// Clear presence data
|
|
183
|
+
if (this.presenceManager) {
|
|
184
|
+
await this.presenceManager.clear();
|
|
185
|
+
}
|
|
125
186
|
await this.emit('stopped', {});
|
|
126
187
|
}
|
|
127
188
|
// Client Management
|
|
@@ -135,12 +196,17 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
135
196
|
await this.handleClientAuthenticatedMessage(message);
|
|
136
197
|
return;
|
|
137
198
|
}
|
|
199
|
+
if (message.type === 'disconnect') {
|
|
200
|
+
const payload = message.payload;
|
|
201
|
+
await this.disconnectClient(payload.clientId, payload.reason || 'Client disconnected');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
138
204
|
// Transport messages contain clientId in the payload for routing
|
|
139
205
|
const payload = message.payload;
|
|
140
|
-
const { clientId, ...clientMessage } = payload;
|
|
206
|
+
const { clientId, type, ...clientMessage } = payload;
|
|
141
207
|
const clientMsg = {
|
|
142
208
|
id: message.id,
|
|
143
|
-
type: message.type,
|
|
209
|
+
type: type || message.type, // Use inner type if present, otherwise outer type
|
|
144
210
|
payload: clientMessage,
|
|
145
211
|
timestamp: message.timestamp
|
|
146
212
|
};
|
|
@@ -165,16 +231,53 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
165
231
|
async handleConnectionMessage(message) {
|
|
166
232
|
const payload = message.payload;
|
|
167
233
|
// Add the client with initial auth state
|
|
234
|
+
const roomIds = new Set();
|
|
168
235
|
const client = {
|
|
169
236
|
id: payload.clientId,
|
|
170
237
|
userId: payload.userId || '',
|
|
171
|
-
|
|
238
|
+
roomIds,
|
|
239
|
+
get roomId() {
|
|
240
|
+
return roomIds.size > 0 ? (roomIds.values().next().value || null) : null;
|
|
241
|
+
},
|
|
172
242
|
authenticated: payload.authenticated,
|
|
173
243
|
connectedAt: Date.now()
|
|
174
244
|
};
|
|
175
245
|
this.clients.set(payload.clientId, client);
|
|
176
246
|
this.clientMessageHandlers.set(payload.clientId, this.createClientMessageHandler(payload.clientId));
|
|
177
247
|
await this.emit('client_connected', { client });
|
|
248
|
+
// Register with presence manager if authenticated
|
|
249
|
+
if (payload.authenticated && this.presenceManager?.isEnabled() && payload.userId) {
|
|
250
|
+
try {
|
|
251
|
+
await this.presenceManager.connectDevice(payload.userId, payload.clientId, {
|
|
252
|
+
connectedAt: Date.now(),
|
|
253
|
+
lastActivity: Date.now(),
|
|
254
|
+
metadata: payload.metadata
|
|
255
|
+
});
|
|
256
|
+
await this.emit('presence_device_connected', {
|
|
257
|
+
userId: payload.userId,
|
|
258
|
+
deviceId: payload.clientId,
|
|
259
|
+
timestamp: Date.now()
|
|
260
|
+
});
|
|
261
|
+
// Broadcast presence update
|
|
262
|
+
const presenceConfig = this.presenceManager.getConfig();
|
|
263
|
+
if (presenceConfig.broadcastPresenceUpdates) {
|
|
264
|
+
await this.experimental.broadcastAuthenticated({
|
|
265
|
+
type: 'presence:user_online',
|
|
266
|
+
payload: {
|
|
267
|
+
userId: payload.userId,
|
|
268
|
+
deviceId: payload.clientId,
|
|
269
|
+
status: 'online'
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
await this.emit('error', {
|
|
276
|
+
error: error,
|
|
277
|
+
context: 'presence_connect_device'
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
178
281
|
if (payload.authenticated) {
|
|
179
282
|
await this.emit('client_authenticated', {
|
|
180
283
|
clientId: payload.clientId,
|
|
@@ -204,10 +307,14 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
204
307
|
}
|
|
205
308
|
}
|
|
206
309
|
async addClient(clientId) {
|
|
310
|
+
const roomIds = new Set();
|
|
207
311
|
const client = {
|
|
208
312
|
id: clientId,
|
|
209
313
|
userId: '',
|
|
210
|
-
|
|
314
|
+
roomIds,
|
|
315
|
+
get roomId() {
|
|
316
|
+
return roomIds.size > 0 ? (roomIds.values().next().value || null) : null;
|
|
317
|
+
},
|
|
211
318
|
authenticated: false,
|
|
212
319
|
connectedAt: Date.now()
|
|
213
320
|
};
|
|
@@ -221,10 +328,40 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
221
328
|
if (!client) {
|
|
222
329
|
return;
|
|
223
330
|
}
|
|
224
|
-
//
|
|
225
|
-
if (client.
|
|
226
|
-
|
|
227
|
-
|
|
331
|
+
// Unregister from presence manager
|
|
332
|
+
if (this.presenceManager?.isEnabled() && client.userId) {
|
|
333
|
+
try {
|
|
334
|
+
const presence = await this.presenceManager.disconnectDevice(client.userId, clientId);
|
|
335
|
+
await this.emit('presence_device_disconnected', {
|
|
336
|
+
userId: client.userId,
|
|
337
|
+
deviceId: clientId,
|
|
338
|
+
timestamp: Date.now()
|
|
339
|
+
});
|
|
340
|
+
// Broadcast presence update if user went offline or into grace period
|
|
341
|
+
const presenceConfig = this.presenceManager.getConfig();
|
|
342
|
+
if (presenceConfig.broadcastPresenceUpdates && (!presence || presence.status === 'offline')) {
|
|
343
|
+
await this.experimental.broadcastAuthenticated({
|
|
344
|
+
type: 'presence:user_offline',
|
|
345
|
+
payload: {
|
|
346
|
+
userId: client.userId,
|
|
347
|
+
deviceId: clientId,
|
|
348
|
+
status: presence?.status || 'offline',
|
|
349
|
+
gracePeriod: presenceConfig.gracePeriod
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
await this.emit('error', {
|
|
356
|
+
error: error,
|
|
357
|
+
context: 'presence_disconnect_device'
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Leave all rooms the client is in
|
|
362
|
+
for (const roomId of client.roomIds) {
|
|
363
|
+
await this.roomManager.leaveRoom(roomId, client.userId);
|
|
364
|
+
await this.emit('client_left_room', { clientId, roomId });
|
|
228
365
|
}
|
|
229
366
|
// Release all locks held by this client
|
|
230
367
|
await this.lockManager.releaseUserLocks(client.userId);
|
|
@@ -248,7 +385,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
248
385
|
await this.handleJoinRoom(clientId, message.payload);
|
|
249
386
|
break;
|
|
250
387
|
case 'leave_room':
|
|
251
|
-
await this.handleLeaveRoom(clientId);
|
|
388
|
+
await this.handleLeaveRoom(clientId, message.payload);
|
|
252
389
|
break;
|
|
253
390
|
case 'broadcast_event':
|
|
254
391
|
await this.handleBroadcastEvent(clientId, message.payload);
|
|
@@ -261,6 +398,10 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
261
398
|
break;
|
|
262
399
|
case 'ping':
|
|
263
400
|
await this.sendToClient(clientId, { type: 'pong', timestamp: Date.now() });
|
|
401
|
+
// Update presence activity on ping
|
|
402
|
+
if (this.presenceManager?.isEnabled() && client.userId) {
|
|
403
|
+
await this.updatePresenceActivity(client.userId, clientId, 'heartbeat');
|
|
404
|
+
}
|
|
264
405
|
break;
|
|
265
406
|
default:
|
|
266
407
|
await this.sendToClient(clientId, {
|
|
@@ -312,10 +453,10 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
312
453
|
return;
|
|
313
454
|
}
|
|
314
455
|
try {
|
|
315
|
-
//
|
|
316
|
-
if (client.roomId) {
|
|
317
|
-
await this.
|
|
318
|
-
|
|
456
|
+
// Check if already in the room
|
|
457
|
+
if (client.roomIds.has(payload.roomId)) {
|
|
458
|
+
await this.sendToClient(clientId, { type: 'error', error: 'Already in this room' });
|
|
459
|
+
return;
|
|
319
460
|
}
|
|
320
461
|
// Get or create room
|
|
321
462
|
let roomState = await this.roomManager.getRoomState(payload.roomId);
|
|
@@ -338,7 +479,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
338
479
|
permissions: roomState.room.permissions || ['read', 'write']
|
|
339
480
|
};
|
|
340
481
|
await this.roomManager.joinRoom(payload.roomId, user);
|
|
341
|
-
client.
|
|
482
|
+
client.roomIds.add(payload.roomId);
|
|
342
483
|
await this.emit('client_joined_room', { clientId, roomId: payload.roomId });
|
|
343
484
|
// Send room state to client
|
|
344
485
|
await this.sendToClient(clientId, {
|
|
@@ -359,20 +500,32 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
359
500
|
await this.sendToClient(clientId, { type: 'error', error: error.message });
|
|
360
501
|
}
|
|
361
502
|
}
|
|
362
|
-
async handleLeaveRoom(clientId) {
|
|
503
|
+
async handleLeaveRoom(clientId, payload) {
|
|
363
504
|
const client = this.clients.get(clientId);
|
|
364
|
-
if (!client
|
|
505
|
+
if (!client) {
|
|
365
506
|
return;
|
|
366
507
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
508
|
+
// If roomId is specified, leave that specific room
|
|
509
|
+
// Otherwise, leave all rooms (backward compatibility)
|
|
510
|
+
const roomsToLeave = payload?.roomId
|
|
511
|
+
? [payload.roomId]
|
|
512
|
+
: Array.from(client.roomIds);
|
|
513
|
+
if (roomsToLeave.length === 0) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
for (const roomId of roomsToLeave) {
|
|
517
|
+
if (!client.roomIds.has(roomId)) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
await this.roomManager.leaveRoom(roomId, client.userId);
|
|
521
|
+
client.roomIds.delete(roomId);
|
|
522
|
+
await this.emit('client_left_room', { clientId, roomId });
|
|
523
|
+
await this.sendToClient(clientId, { type: 'room_left', roomId });
|
|
524
|
+
}
|
|
372
525
|
}
|
|
373
526
|
async handleBroadcastEvent(clientId, payload) {
|
|
374
527
|
const client = this.clients.get(clientId);
|
|
375
|
-
if (!client || client.
|
|
528
|
+
if (!client || !client.roomIds.has(payload.roomId)) {
|
|
376
529
|
await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
|
|
377
530
|
return;
|
|
378
531
|
}
|
|
@@ -398,7 +551,7 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
398
551
|
}
|
|
399
552
|
async handleLockRequest(clientId, payload) {
|
|
400
553
|
const client = this.clients.get(clientId);
|
|
401
|
-
if (!client || client.
|
|
554
|
+
if (!client || !client.roomIds.has(payload.roomId)) {
|
|
402
555
|
await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
|
|
403
556
|
return;
|
|
404
557
|
}
|
|
@@ -430,16 +583,16 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
430
583
|
await this.lockManager.releaseLock(payload.lockId);
|
|
431
584
|
await this.emit('lock_released', { lockId: payload.lockId, clientId });
|
|
432
585
|
await this.sendToClient(clientId, { type: 'lock_released', lockId: payload.lockId });
|
|
433
|
-
// Broadcast lock status to
|
|
434
|
-
|
|
435
|
-
await this.roomManager.broadcastToRoom(
|
|
586
|
+
// Broadcast lock status to all rooms the client is in
|
|
587
|
+
for (const roomId of client.roomIds) {
|
|
588
|
+
await this.roomManager.broadcastToRoom(roomId, {
|
|
436
589
|
id: this.generateId(),
|
|
437
590
|
type: 'lock_status',
|
|
438
591
|
timestamp: Date.now(),
|
|
439
592
|
userId: client.userId,
|
|
440
|
-
roomId
|
|
593
|
+
roomId,
|
|
441
594
|
data: { lockId: payload.lockId, action: 'released' },
|
|
442
|
-
metadata: { userId: client.userId, timestamp: Date.now(), roomId
|
|
595
|
+
metadata: { userId: client.userId, timestamp: Date.now(), roomId }
|
|
443
596
|
});
|
|
444
597
|
}
|
|
445
598
|
}
|
|
@@ -485,5 +638,43 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
|
|
|
485
638
|
isInitialized() {
|
|
486
639
|
return this.initialized;
|
|
487
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Update presence activity for a user's device
|
|
643
|
+
*
|
|
644
|
+
* @param userId - User identifier
|
|
645
|
+
* @param deviceId - Device/client identifier
|
|
646
|
+
* @param activityType - Type of activity
|
|
647
|
+
*/
|
|
648
|
+
async updatePresenceActivity(userId, deviceId, activityType) {
|
|
649
|
+
if (!this.presenceManager?.isEnabled()) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
await this.presenceManager.updateActivity({
|
|
654
|
+
userId,
|
|
655
|
+
deviceId,
|
|
656
|
+
timestamp: Date.now(),
|
|
657
|
+
activityType
|
|
658
|
+
});
|
|
659
|
+
await this.emit('presence_activity', {
|
|
660
|
+
userId,
|
|
661
|
+
deviceId,
|
|
662
|
+
timestamp: Date.now()
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
// Don't throw, just log the error
|
|
667
|
+
await this.emit('error', {
|
|
668
|
+
error: error,
|
|
669
|
+
context: 'presence_update_activity'
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Get presence manager (if configured)
|
|
675
|
+
*/
|
|
676
|
+
getPresenceManager() {
|
|
677
|
+
return this.presenceManager;
|
|
678
|
+
}
|
|
488
679
|
}
|
|
489
680
|
exports.BaseServer = BaseServer;
|
|
@@ -3,6 +3,7 @@ import type { IAuthAdapter } from '../abstractions/AuthAdapter.js';
|
|
|
3
3
|
import type { IStorageAdapter } from '../abstractions/StorageAdapter.js';
|
|
4
4
|
import type { RoomManager } from '../abstractions/RoomManager.js';
|
|
5
5
|
import type { LockManager } from '../abstractions/LockManager.js';
|
|
6
|
+
import type { PresenceManager } from '../abstractions/PresenceManager.js';
|
|
6
7
|
import type { RoomConfig, ExperimentalFeatureConfig } from '../types/index.js';
|
|
7
8
|
import { BaseServer } from './BaseServer.js';
|
|
8
9
|
import type { Server as HttpServer } from 'http';
|
|
@@ -14,6 +15,7 @@ export declare class ServerBuilder {
|
|
|
14
15
|
private storage?;
|
|
15
16
|
private roomManager?;
|
|
16
17
|
private lockManager?;
|
|
18
|
+
private presenceManager?;
|
|
17
19
|
private defaultRoomConfig?;
|
|
18
20
|
private httpServer?;
|
|
19
21
|
private webSocketPath?;
|
|
@@ -24,6 +26,15 @@ export declare class ServerBuilder {
|
|
|
24
26
|
withStorage(storage: IStorageAdapter): this;
|
|
25
27
|
withRoomManager(roomManager: RoomManager): this;
|
|
26
28
|
withLockManager(lockManager: LockManager): this;
|
|
29
|
+
/**
|
|
30
|
+
* Configure presence management for global user tracking
|
|
31
|
+
*
|
|
32
|
+
* Enables two-tier connection architecture where users maintain
|
|
33
|
+
* global presence while participating in multiple rooms.
|
|
34
|
+
*
|
|
35
|
+
* @param presenceManager - PresenceManager implementation
|
|
36
|
+
*/
|
|
37
|
+
withPresenceManager(presenceManager: PresenceManager): this;
|
|
27
38
|
withDefaultRoomConfig(config: Partial<RoomConfig>): this;
|
|
28
39
|
withHttpServer(server: HttpServer | HttpsServer): this;
|
|
29
40
|
withWebSocketPath(path: string): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerBuilder.d.ts","sourceRoot":"","sources":["../../src/server/ServerBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAqB,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAE1C,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAC,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAC,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAC,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAC,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,oBAAoB,CAAC,CAA4B;IAEzD,aAAa,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKjD,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKlC,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAK3C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAKxD,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAKtD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKrC,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,wBAAwB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAKnE,KAAK,IAAI,UAAU;
|
|
1
|
+
{"version":3,"file":"ServerBuilder.d.ts","sourceRoot":"","sources":["../../src/server/ServerBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAqB,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAE1C,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAC,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAC,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAC,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAC,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,oBAAoB,CAAC,CAA4B;IAEzD,aAAa,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKjD,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKlC,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAK3C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,mBAAmB,CAAC,eAAe,EAAE,eAAe,GAAG,IAAI;IAK3D,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAKxD,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAKtD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKrC,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAK/C;;;;;;;OAOG;IACH,wBAAwB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAKnE,KAAK,IAAI,UAAU;CAqCpB"}
|
|
@@ -23,6 +23,18 @@ class ServerBuilder {
|
|
|
23
23
|
this.lockManager = lockManager;
|
|
24
24
|
return this;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Configure presence management for global user tracking
|
|
28
|
+
*
|
|
29
|
+
* Enables two-tier connection architecture where users maintain
|
|
30
|
+
* global presence while participating in multiple rooms.
|
|
31
|
+
*
|
|
32
|
+
* @param presenceManager - PresenceManager implementation
|
|
33
|
+
*/
|
|
34
|
+
withPresenceManager(presenceManager) {
|
|
35
|
+
this.presenceManager = presenceManager;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
26
38
|
withDefaultRoomConfig(config) {
|
|
27
39
|
this.defaultRoomConfig = config;
|
|
28
40
|
return this;
|
|
@@ -67,6 +79,7 @@ class ServerBuilder {
|
|
|
67
79
|
storage: this.storage,
|
|
68
80
|
roomManager: this.roomManager,
|
|
69
81
|
lockManager: this.lockManager,
|
|
82
|
+
presenceManager: this.presenceManager,
|
|
70
83
|
defaultRoomConfig: this.defaultRoomConfig || {
|
|
71
84
|
maxUsers: 50,
|
|
72
85
|
maxHistory: 100,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -21,4 +21,5 @@ export type MessageHandler = (message: Message) => void | Promise<void>;
|
|
|
21
21
|
export type ErrorHandler = (error: Error) => void;
|
|
22
22
|
export type CloseHandler = (code: number, reason: string) => void;
|
|
23
23
|
export { ExperimentalFeatureConfig, ClientPredicate, BroadcastResult, BroadcastOptions, ExperimentalUsageEvent, ExperimentalFeatureError } from './experimental.js';
|
|
24
|
+
export { PresenceStatus, DeviceInfo, UserPresence, PresenceConfig, PresenceChangeEvent, ActivityUpdate } from './presence.js';
|
|
24
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACX,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,aAAa,EACb,SAAS,EACV,MAAM,WAAW,CAAC;AAEnB,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,CAAC;AAE5F,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAElE,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACX,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,aAAa,EACb,SAAS,EACV,MAAM,WAAW,CAAC;AAEnB,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,WAAW,GAAG,eAAe,GAAG,cAAc,CAAC;AAE5F,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAElE,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,cAAc,EACf,MAAM,eAAe,CAAC"}
|