@principal-ai/control-tower-core 0.1.25 → 0.2.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.
Files changed (117) hide show
  1. package/dist/abstractions/AuthAdapter.d.ts +1 -1
  2. package/dist/abstractions/AuthAdapter.d.ts.map +1 -1
  3. package/dist/abstractions/DefaultLockManager.d.ts +2 -2
  4. package/dist/abstractions/DefaultLockManager.d.ts.map +1 -1
  5. package/dist/abstractions/DefaultLockManager.js +7 -8
  6. package/dist/abstractions/DefaultPresenceManager.d.ts +4 -4
  7. package/dist/abstractions/DefaultPresenceManager.d.ts.map +1 -1
  8. package/dist/abstractions/DefaultPresenceManager.js +25 -25
  9. package/dist/abstractions/DefaultRoomManager.d.ts +3 -3
  10. package/dist/abstractions/DefaultRoomManager.d.ts.map +1 -1
  11. package/dist/abstractions/DefaultRoomManager.js +6 -4
  12. package/dist/abstractions/EventEmitter.d.ts.map +1 -1
  13. package/dist/abstractions/LockManager.d.ts +1 -1
  14. package/dist/abstractions/LockManager.d.ts.map +1 -1
  15. package/dist/abstractions/LockManager.js +1 -1
  16. package/dist/abstractions/PresenceExtension.d.ts +2 -2
  17. package/dist/abstractions/PresenceExtension.d.ts.map +1 -1
  18. package/dist/abstractions/PresenceManager.d.ts +1 -1
  19. package/dist/abstractions/PresenceManager.d.ts.map +1 -1
  20. package/dist/abstractions/PresenceManager.js +5 -5
  21. package/dist/abstractions/RoomManager.d.ts +2 -2
  22. package/dist/abstractions/RoomManager.d.ts.map +1 -1
  23. package/dist/abstractions/StorageAdapter.d.ts +4 -4
  24. package/dist/abstractions/StorageAdapter.d.ts.map +1 -1
  25. package/dist/abstractions/TransportAdapter.d.ts +4 -4
  26. package/dist/abstractions/TransportAdapter.d.ts.map +1 -1
  27. package/dist/abstractions/index.d.ts +11 -11
  28. package/dist/abstractions/index.d.ts.map +1 -1
  29. package/dist/abstractions/index.js +9 -9
  30. package/dist/adapters/mock/MockAuthAdapter.d.ts +2 -2
  31. package/dist/adapters/mock/MockAuthAdapter.d.ts.map +1 -1
  32. package/dist/adapters/mock/MockAuthAdapter.js +13 -11
  33. package/dist/adapters/mock/MockRTCDataChannel.d.ts +83 -0
  34. package/dist/adapters/mock/MockRTCDataChannel.d.ts.map +1 -0
  35. package/dist/adapters/mock/MockRTCDataChannel.js +146 -0
  36. package/dist/adapters/mock/MockRTCPeerConnection.d.ts +168 -0
  37. package/dist/adapters/mock/MockRTCPeerConnection.d.ts.map +1 -0
  38. package/dist/adapters/mock/MockRTCPeerConnection.js +449 -0
  39. package/dist/adapters/mock/MockStorageAdapter.d.ts +1 -1
  40. package/dist/adapters/mock/MockStorageAdapter.d.ts.map +1 -1
  41. package/dist/adapters/mock/MockStorageAdapter.js +18 -18
  42. package/dist/adapters/mock/MockTransportAdapter.d.ts +2 -2
  43. package/dist/adapters/mock/MockTransportAdapter.d.ts.map +1 -1
  44. package/dist/adapters/mock/MockTransportAdapter.js +38 -38
  45. package/dist/adapters/mock/index.d.ts +5 -3
  46. package/dist/adapters/mock/index.d.ts.map +1 -1
  47. package/dist/adapters/mock/index.js +10 -5
  48. package/dist/adapters/webrtc/WebRTCSignalingAdapter.d.ts +135 -0
  49. package/dist/adapters/webrtc/WebRTCSignalingAdapter.d.ts.map +1 -0
  50. package/dist/adapters/webrtc/WebRTCSignalingAdapter.js +368 -0
  51. package/dist/adapters/webrtc/index.d.ts +2 -0
  52. package/dist/adapters/webrtc/index.d.ts.map +1 -0
  53. package/dist/adapters/webrtc/index.js +5 -0
  54. package/dist/adapters/websocket/BrowserWebSocketTransportAdapter.d.ts +75 -0
  55. package/dist/adapters/websocket/BrowserWebSocketTransportAdapter.d.ts.map +1 -0
  56. package/dist/adapters/websocket/BrowserWebSocketTransportAdapter.js +231 -0
  57. package/dist/adapters/websocket/WebSocketClientTransportAdapter.d.ts +3 -3
  58. package/dist/adapters/websocket/WebSocketClientTransportAdapter.d.ts.map +1 -1
  59. package/dist/adapters/websocket/WebSocketClientTransportAdapter.js +38 -38
  60. package/dist/adapters/websocket/WebSocketServerTransportAdapter.d.ts +7 -7
  61. package/dist/adapters/websocket/WebSocketServerTransportAdapter.d.ts.map +1 -1
  62. package/dist/adapters/websocket/WebSocketServerTransportAdapter.js +94 -91
  63. package/dist/adapters/websocket/browser.d.ts +2 -0
  64. package/dist/adapters/websocket/browser.d.ts.map +1 -0
  65. package/dist/adapters/websocket/browser.js +6 -0
  66. package/dist/adapters/websocket/index.d.ts +3 -2
  67. package/dist/adapters/websocket/index.d.ts.map +1 -1
  68. package/dist/adapters/websocket/index.js +7 -3
  69. package/dist/adapters/websocket/node.d.ts +3 -0
  70. package/dist/adapters/websocket/node.d.ts.map +1 -0
  71. package/dist/adapters/websocket/node.js +8 -0
  72. package/dist/client/BaseClient.d.ts +6 -6
  73. package/dist/client/BaseClient.d.ts.map +1 -1
  74. package/dist/client/BaseClient.js +86 -72
  75. package/dist/client/ClientBuilder.d.ts +5 -5
  76. package/dist/client/ClientBuilder.d.ts.map +1 -1
  77. package/dist/client/ClientBuilder.js +3 -3
  78. package/dist/client/PresenceClient.d.ts +4 -4
  79. package/dist/client/PresenceClient.d.ts.map +1 -1
  80. package/dist/client/PresenceClient.js +30 -30
  81. package/dist/client/index.d.ts +3 -3
  82. package/dist/client/index.d.ts.map +1 -1
  83. package/dist/index.d.ts +7 -6
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +27 -19
  86. package/dist/index.js.map +27 -23
  87. package/dist/index.mjs +1585 -558
  88. package/dist/index.mjs.map +27 -23
  89. package/dist/server/BaseServer.d.ts +13 -13
  90. package/dist/server/BaseServer.d.ts.map +1 -1
  91. package/dist/server/BaseServer.js +218 -143
  92. package/dist/server/ExperimentalAPI.d.ts +7 -7
  93. package/dist/server/ExperimentalAPI.d.ts.map +1 -1
  94. package/dist/server/ExperimentalAPI.js +22 -22
  95. package/dist/server/ServerBuilder.d.ts +11 -11
  96. package/dist/server/ServerBuilder.d.ts.map +1 -1
  97. package/dist/server/ServerBuilder.js +10 -10
  98. package/dist/server/index.d.ts +3 -3
  99. package/dist/server/index.d.ts.map +1 -1
  100. package/dist/server/index.js +3 -3
  101. package/dist/types/auth.d.ts.map +1 -1
  102. package/dist/types/events.d.ts +10 -10
  103. package/dist/types/events.d.ts.map +1 -1
  104. package/dist/types/experimental.d.ts +2 -2
  105. package/dist/types/experimental.d.ts.map +1 -1
  106. package/dist/types/experimental.js +1 -1
  107. package/dist/types/index.d.ts +7 -7
  108. package/dist/types/index.d.ts.map +1 -1
  109. package/dist/types/index.js +2 -2
  110. package/dist/types/lock.d.ts +2 -2
  111. package/dist/types/lock.d.ts.map +1 -1
  112. package/dist/types/presence.d.ts +3 -3
  113. package/dist/types/presence.d.ts.map +1 -1
  114. package/dist/types/room.d.ts +4 -4
  115. package/dist/types/room.d.ts.map +1 -1
  116. package/dist/types/room.js +2 -2
  117. package/package.json +15 -7
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseServer = void 0;
4
- const room_js_1 = require("../types/room.js");
5
4
  const EventEmitter_js_1 = require("../abstractions/EventEmitter.js");
5
+ const room_js_1 = require("../types/room.js");
6
6
  const ExperimentalAPI_js_1 = require("./ExperimentalAPI.js");
7
7
  class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
8
8
  constructor(config) {
@@ -25,21 +25,24 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
25
25
  config: config.experimental || {
26
26
  enableBroadcast: false,
27
27
  warnOnExperimentalUse: true,
28
- trackUsageMetrics: false
28
+ trackUsageMetrics: false,
29
29
  },
30
30
  logger: console,
31
31
  emitMetric: (event) => {
32
- void this.emit('experimental_usage', event);
32
+ void this.emit("experimental_usage", event);
33
33
  },
34
34
  getClients: () => Array.from(this.clients.values()),
35
- sendToClient: (clientId, message) => this.sendToClient(clientId, message)
35
+ sendToClient: (clientId, message) => this.sendToClient(clientId, message),
36
36
  });
37
37
  // Determine mode based on configuration
38
- this.mode = (config.httpServer || config.webSocketServer) ? 'integration' : 'standalone';
38
+ this.mode =
39
+ config.httpServer || config.webSocketServer
40
+ ? "integration"
41
+ : "standalone";
39
42
  // Connect auth adapter to transport if both exist and transport supports it
40
43
  if (this.auth && this.transport) {
41
44
  const transportWithAuth = this.transport;
42
- if (typeof transportWithAuth.setAuthAdapter === 'function') {
45
+ if (typeof transportWithAuth.setAuthAdapter === "function") {
43
46
  transportWithAuth.setAuthAdapter(this.auth);
44
47
  }
45
48
  }
@@ -66,29 +69,29 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
66
69
  const changes = await this.presenceManager.processHeartbeats();
67
70
  // Emit presence change events
68
71
  for (const change of changes) {
69
- await this.emit('presence_changed', {
72
+ await this.emit("presence_changed", {
70
73
  userId: change.userId,
71
74
  status: change.status,
72
- timestamp: change.timestamp
75
+ timestamp: change.timestamp,
73
76
  });
74
77
  // Broadcast presence updates if enabled
75
78
  if (config.broadcastPresenceUpdates) {
76
79
  await this.experimental.broadcastAuthenticated({
77
- type: 'presence:status_changed',
80
+ type: "presence:status_changed",
78
81
  payload: {
79
82
  userId: change.userId,
80
83
  status: change.status,
81
84
  previousStatus: change.previousStatus,
82
- reason: change.reason
83
- }
85
+ reason: change.reason,
86
+ },
84
87
  });
85
88
  }
86
89
  }
87
90
  }
88
91
  catch (error) {
89
- await this.emit('error', {
92
+ await this.emit("error", {
90
93
  error: error,
91
- context: 'heartbeat_processor'
94
+ context: "heartbeat_processor",
92
95
  });
93
96
  }
94
97
  }, interval);
@@ -104,62 +107,70 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
104
107
  }
105
108
  // Server Lifecycle
106
109
  async start(port) {
107
- if (this.mode === 'integration') {
108
- throw new Error('Cannot use start() in integration mode. Use initialize() instead.');
110
+ if (this.mode === "integration") {
111
+ throw new Error("Cannot use start() in integration mode. Use initialize() instead.");
109
112
  }
110
113
  if (!port) {
111
- throw new Error('Port is required for standalone mode');
114
+ throw new Error("Port is required for standalone mode");
112
115
  }
113
116
  if (this.running) {
114
- throw new Error('Server is already running');
117
+ throw new Error("Server is already running");
115
118
  }
116
119
  try {
117
- await this.transport.connect(`ws://localhost:${port}`, { url: `ws://localhost:${port}` });
120
+ await this.transport.connect(`ws://localhost:${port}`, {
121
+ url: `ws://localhost:${port}`,
122
+ });
118
123
  this.running = true;
119
124
  this.initialized = true;
120
- await this.emit('started', { port });
125
+ await this.emit("started", { port });
121
126
  }
122
127
  catch (error) {
123
- await this.emit('error', { error: error, context: 'server_start' });
128
+ await this.emit("error", {
129
+ error: error,
130
+ context: "server_start",
131
+ });
124
132
  throw error;
125
133
  }
126
134
  }
127
135
  async initialize() {
128
- if (this.mode === 'standalone') {
129
- throw new Error('Cannot use initialize() in standalone mode. Use start() instead.');
136
+ if (this.mode === "standalone") {
137
+ throw new Error("Cannot use initialize() in standalone mode. Use start() instead.");
130
138
  }
131
139
  if (this.initialized) {
132
- throw new Error('Server is already initialized');
140
+ throw new Error("Server is already initialized");
133
141
  }
134
142
  try {
135
143
  const transport = this.transport;
136
144
  if (this.config.webSocketServer) {
137
145
  // Attach to existing WebSocket server
138
- if (typeof transport.attachToWebSocketServer === 'function') {
146
+ if (typeof transport.attachToWebSocketServer === "function") {
139
147
  await transport.attachToWebSocketServer(this.config.webSocketServer);
140
148
  }
141
149
  else {
142
- throw new Error('Transport adapter does not support attachToWebSocketServer');
150
+ throw new Error("Transport adapter does not support attachToWebSocketServer");
143
151
  }
144
152
  }
145
153
  else if (this.config.httpServer) {
146
154
  // Attach to existing HTTP server
147
- if (typeof transport.attach === 'function') {
148
- await transport.attach(this.config.httpServer, this.config.webSocketPath || '/ws');
155
+ if (typeof transport.attach === "function") {
156
+ await transport.attach(this.config.httpServer, this.config.webSocketPath || "/ws");
149
157
  }
150
158
  else {
151
- throw new Error('Transport adapter does not support attach');
159
+ throw new Error("Transport adapter does not support attach");
152
160
  }
153
161
  }
154
162
  else {
155
- throw new Error('Either httpServer or webSocketServer must be provided in integration mode');
163
+ throw new Error("Either httpServer or webSocketServer must be provided in integration mode");
156
164
  }
157
165
  this.running = true;
158
166
  this.initialized = true;
159
- await this.emit('started', { port: 0 }); // Port 0 indicates integration mode
167
+ await this.emit("started", { port: 0 }); // Port 0 indicates integration mode
160
168
  }
161
169
  catch (error) {
162
- await this.emit('error', { error: error, context: 'server_initialize' });
170
+ await this.emit("error", {
171
+ error: error,
172
+ context: "server_initialize",
173
+ });
163
174
  throw error;
164
175
  }
165
176
  }
@@ -172,13 +183,16 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
172
183
  this.stopHeartbeatProcessor();
173
184
  // Disconnect all clients
174
185
  for (const [clientId] of Array.from(this.clients)) {
175
- await this.disconnectClient(clientId, 'Server shutting down');
186
+ await this.disconnectClient(clientId, "Server shutting down");
176
187
  }
177
188
  try {
178
189
  await this.transport.disconnect();
179
190
  }
180
191
  catch (error) {
181
- await this.emit('error', { error: error, context: 'server_stop' });
192
+ await this.emit("error", {
193
+ error: error,
194
+ context: "server_stop",
195
+ });
182
196
  }
183
197
  this.clients.clear();
184
198
  this.clientMessageHandlers.clear();
@@ -188,22 +202,22 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
188
202
  if (this.presenceManager) {
189
203
  await this.presenceManager.clear();
190
204
  }
191
- await this.emit('stopped', {});
205
+ await this.emit("stopped", {});
192
206
  }
193
207
  // Client Management
194
208
  async handleTransportMessage(message) {
195
209
  // Handle special transport messages
196
- if (message.type === 'connection') {
210
+ if (message.type === "connection") {
197
211
  await this.handleConnectionMessage(message);
198
212
  return;
199
213
  }
200
- if (message.type === 'client_authenticated') {
214
+ if (message.type === "client_authenticated") {
201
215
  await this.handleClientAuthenticatedMessage(message);
202
216
  return;
203
217
  }
204
- if (message.type === 'disconnect') {
218
+ if (message.type === "disconnect") {
205
219
  const payload = message.payload;
206
- await this.disconnectClient(payload.clientId, payload.reason || 'Client disconnected');
220
+ await this.disconnectClient(payload.clientId, payload.reason || "Client disconnected");
207
221
  return;
208
222
  }
209
223
  // Transport messages contain clientId in the payload for routing
@@ -213,12 +227,12 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
213
227
  id: message.id,
214
228
  type: type || message.type, // Use inner type if present, otherwise outer type
215
229
  payload: clientMessage,
216
- timestamp: message.timestamp
230
+ timestamp: message.timestamp,
217
231
  };
218
232
  if (!clientId) {
219
- await this.emit('error', {
220
- error: new Error('Message missing clientId'),
221
- context: 'transport_message'
233
+ await this.emit("error", {
234
+ error: new Error("Message missing clientId"),
235
+ context: "transport_message",
222
236
  });
223
237
  return;
224
238
  }
@@ -227,9 +241,9 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
227
241
  await handler(clientMsg);
228
242
  }
229
243
  else {
230
- await this.emit('error', {
244
+ await this.emit("error", {
231
245
  error: new Error(`No handler for client ${clientId}`),
232
- context: 'transport_message'
246
+ context: "transport_message",
233
247
  });
234
248
  }
235
249
  }
@@ -239,55 +253,57 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
239
253
  const roomIds = new Set();
240
254
  const client = {
241
255
  id: payload.clientId,
242
- userId: payload.userId || '',
256
+ userId: payload.userId || "",
243
257
  roomIds,
244
258
  get roomId() {
245
- return roomIds.size > 0 ? (roomIds.values().next().value || null) : null;
259
+ return roomIds.size > 0 ? roomIds.values().next().value || null : null;
246
260
  },
247
261
  authenticated: payload.authenticated,
248
- connectedAt: Date.now()
262
+ connectedAt: Date.now(),
249
263
  };
250
264
  this.clients.set(payload.clientId, client);
251
265
  this.clientMessageHandlers.set(payload.clientId, this.createClientMessageHandler(payload.clientId));
252
- await this.emit('client_connected', { client });
266
+ await this.emit("client_connected", { client });
253
267
  // Register with presence manager if authenticated
254
- if (payload.authenticated && this.presenceManager?.isEnabled() && payload.userId) {
268
+ if (payload.authenticated &&
269
+ this.presenceManager?.isEnabled() &&
270
+ payload.userId) {
255
271
  try {
256
272
  await this.presenceManager.connectDevice(payload.userId, payload.clientId, {
257
273
  connectedAt: Date.now(),
258
274
  lastActivity: Date.now(),
259
- metadata: payload.metadata
275
+ metadata: payload.metadata,
260
276
  });
261
- await this.emit('presence_device_connected', {
277
+ await this.emit("presence_device_connected", {
262
278
  userId: payload.userId,
263
279
  deviceId: payload.clientId,
264
- timestamp: Date.now()
280
+ timestamp: Date.now(),
265
281
  });
266
282
  // Broadcast presence update
267
283
  const presenceConfig = this.presenceManager.getConfig();
268
284
  if (presenceConfig.broadcastPresenceUpdates) {
269
285
  await this.experimental.broadcastAuthenticated({
270
- type: 'presence:user_online',
286
+ type: "presence:user_online",
271
287
  payload: {
272
288
  userId: payload.userId,
273
289
  deviceId: payload.clientId,
274
- status: 'online'
275
- }
290
+ status: "online",
291
+ },
276
292
  });
277
293
  }
278
294
  }
279
295
  catch (error) {
280
- await this.emit('error', {
296
+ await this.emit("error", {
281
297
  error: error,
282
- context: 'presence_connect_device'
298
+ context: "presence_connect_device",
283
299
  });
284
300
  }
285
301
  }
286
302
  if (payload.authenticated) {
287
- await this.emit('client_authenticated', {
303
+ await this.emit("client_authenticated", {
288
304
  clientId: payload.clientId,
289
- userId: payload.userId || '',
290
- metadata: payload.metadata
305
+ userId: payload.userId || "",
306
+ metadata: payload.metadata,
291
307
  });
292
308
  }
293
309
  }
@@ -303,15 +319,15 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
303
319
  this.userClientMap.set(payload.userId, new Set());
304
320
  }
305
321
  this.userClientMap.get(payload.userId).add(payload.clientId);
306
- await this.emit('client_authenticated', {
322
+ await this.emit("client_authenticated", {
307
323
  clientId: payload.clientId,
308
324
  userId: payload.userId,
309
- metadata: payload.metadata
325
+ metadata: payload.metadata,
310
326
  });
311
327
  }
312
328
  }
313
329
  async handleTransportError(error) {
314
- await this.emit('error', { error, context: 'transport' });
330
+ await this.emit("error", { error, context: "transport" });
315
331
  }
316
332
  async handleTransportClose(_code, _reason) {
317
333
  // Transport close means server is shutting down
@@ -323,18 +339,18 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
323
339
  const roomIds = new Set();
324
340
  const client = {
325
341
  id: clientId,
326
- userId: '',
342
+ userId: "",
327
343
  roomIds,
328
344
  get roomId() {
329
- return roomIds.size > 0 ? (roomIds.values().next().value || null) : null;
345
+ return roomIds.size > 0 ? roomIds.values().next().value || null : null;
330
346
  },
331
347
  authenticated: false,
332
- connectedAt: Date.now()
348
+ connectedAt: Date.now(),
333
349
  };
334
350
  this.clients.set(clientId, client);
335
351
  // Set up message handler for this client
336
352
  this.clientMessageHandlers.set(clientId, this.createClientMessageHandler(clientId));
337
- await this.emit('client_connected', { client });
353
+ await this.emit("client_connected", { client });
338
354
  }
339
355
  async disconnectClient(clientId, reason) {
340
356
  const client = this.clients.get(clientId);
@@ -356,43 +372,44 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
356
372
  if (this.presenceManager?.isEnabled() && client.userId) {
357
373
  try {
358
374
  const presence = await this.presenceManager.disconnectDevice(client.userId, clientId);
359
- await this.emit('presence_device_disconnected', {
375
+ await this.emit("presence_device_disconnected", {
360
376
  userId: client.userId,
361
377
  deviceId: clientId,
362
- timestamp: Date.now()
378
+ timestamp: Date.now(),
363
379
  });
364
380
  // Broadcast presence update if user went offline or into grace period
365
381
  const presenceConfig = this.presenceManager.getConfig();
366
- if (presenceConfig.broadcastPresenceUpdates && (!presence || presence.status === 'offline')) {
382
+ if (presenceConfig.broadcastPresenceUpdates &&
383
+ (!presence || presence.status === "offline")) {
367
384
  await this.experimental.broadcastAuthenticated({
368
- type: 'presence:user_offline',
385
+ type: "presence:user_offline",
369
386
  payload: {
370
387
  userId: client.userId,
371
388
  deviceId: clientId,
372
- status: presence?.status || 'offline',
373
- gracePeriod: presenceConfig.gracePeriod
374
- }
389
+ status: presence?.status || "offline",
390
+ gracePeriod: presenceConfig.gracePeriod,
391
+ },
375
392
  });
376
393
  }
377
394
  }
378
395
  catch (error) {
379
- await this.emit('error', {
396
+ await this.emit("error", {
380
397
  error: error,
381
- context: 'presence_disconnect_device'
398
+ context: "presence_disconnect_device",
382
399
  });
383
400
  }
384
401
  }
385
402
  // Leave all rooms the client is in
386
403
  for (const roomId of client.roomIds) {
387
404
  await this.roomManager.leaveRoom(roomId, client.userId);
388
- await this.emit('client_left_room', { clientId, roomId });
405
+ await this.emit("client_left_room", { clientId, roomId });
389
406
  }
390
407
  // Release all locks held by this client
391
408
  await this.lockManager.releaseUserLocks(client.userId);
392
409
  // Clean up
393
410
  this.clients.delete(clientId);
394
411
  this.clientMessageHandlers.delete(clientId);
395
- await this.emit('client_disconnected', { clientId, reason });
412
+ await this.emit("client_disconnected", { clientId, reason });
396
413
  }
397
414
  createClientMessageHandler(clientId) {
398
415
  return async (message) => {
@@ -402,57 +419,72 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
402
419
  return;
403
420
  }
404
421
  // Auto-update presence activity on ANY message
405
- if (this.presenceManager?.isEnabled() && client.userId && client.authenticated) {
406
- await this.updatePresenceActivity(client.userId, clientId, 'message');
422
+ if (this.presenceManager?.isEnabled() &&
423
+ client.userId &&
424
+ client.authenticated) {
425
+ await this.updatePresenceActivity(client.userId, clientId, "message");
407
426
  }
408
427
  switch (message.type) {
409
- case 'authenticate':
428
+ case "authenticate":
410
429
  await this.handleAuthenticate(clientId, message.payload);
411
430
  break;
412
- case 'join_room':
431
+ case "join_room":
413
432
  await this.handleJoinRoom(clientId, message.payload);
414
433
  break;
415
- case 'leave_room':
434
+ case "leave_room":
416
435
  await this.handleLeaveRoom(clientId, message.payload);
417
436
  break;
418
- case 'broadcast_event':
437
+ case "broadcast_event":
419
438
  await this.handleBroadcastEvent(clientId, message.payload);
420
439
  break;
421
- case 'request_lock':
440
+ case "request_lock":
422
441
  await this.handleLockRequest(clientId, message.payload);
423
442
  break;
424
- case 'release_lock':
443
+ case "release_lock":
425
444
  await this.handleLockRelease(clientId, message.payload);
426
445
  break;
427
- case 'ping':
428
- await this.sendToClient(clientId, { type: 'pong', timestamp: Date.now() });
446
+ case "ping":
447
+ await this.sendToClient(clientId, {
448
+ type: "pong",
449
+ timestamp: Date.now(),
450
+ });
429
451
  // Presence activity already updated automatically above
430
452
  break;
431
- case 'heartbeat':
453
+ case "heartbeat":
432
454
  // Heartbeat messages are used to keep presence activity updated
433
455
  // The activity is already tracked above (line ~570), so we just acknowledge
434
- await this.sendToClient(clientId, { type: 'heartbeat_ack', timestamp: Date.now() });
456
+ await this.sendToClient(clientId, {
457
+ type: "heartbeat_ack",
458
+ timestamp: Date.now(),
459
+ });
435
460
  break;
436
461
  default:
437
462
  await this.sendToClient(clientId, {
438
- type: 'error',
439
- error: `Unknown message type: ${message.type}`
463
+ type: "error",
464
+ error: `Unknown message type: ${message.type}`,
440
465
  });
441
466
  }
442
467
  }
443
468
  catch (error) {
444
469
  await this.sendToClient(clientId, {
445
- type: 'error',
446
- error: error.message
470
+ type: "error",
471
+ error: error.message,
472
+ });
473
+ await this.emit("error", {
474
+ error: error,
475
+ context: `client_${clientId}`,
447
476
  });
448
- await this.emit('error', { error: error, context: `client_${clientId}` });
449
477
  }
450
478
  };
451
479
  }
452
480
  // Message Handlers
453
481
  async handleAuthenticate(clientId, payload) {
454
482
  if (!this.auth) {
455
- await this.sendToClient(clientId, { type: 'auth_result', success: false, error: 'Authentication not configured' });
483
+ await this.sendToClient(clientId, {
484
+ type: "auth_result",
485
+ success: false,
486
+ error: "Authentication not configured",
487
+ });
456
488
  return;
457
489
  }
458
490
  try {
@@ -467,16 +499,24 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
467
499
  this.userClientMap.set(tokenPayload.userId, new Set());
468
500
  }
469
501
  this.userClientMap.get(tokenPayload.userId).add(clientId);
470
- await this.emit('client_authenticated', {
502
+ await this.emit("client_authenticated", {
471
503
  clientId,
472
504
  userId: tokenPayload.userId,
473
- metadata: tokenPayload.metadata
505
+ metadata: tokenPayload.metadata,
474
506
  });
475
507
  }
476
- await this.sendToClient(clientId, { type: 'auth_result', success: true, userId: tokenPayload.userId });
508
+ await this.sendToClient(clientId, {
509
+ type: "auth_result",
510
+ success: true,
511
+ userId: tokenPayload.userId,
512
+ });
477
513
  }
478
514
  catch (error) {
479
- await this.sendToClient(clientId, { type: 'auth_result', success: false, error: error.message });
515
+ await this.sendToClient(clientId, {
516
+ type: "auth_result",
517
+ success: false,
518
+ error: error.message,
519
+ });
480
520
  }
481
521
  }
482
522
  async handleJoinRoom(clientId, payload) {
@@ -489,62 +529,77 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
489
529
  await this.handleAuthenticate(clientId, { token: payload.token });
490
530
  }
491
531
  if (!client.authenticated) {
492
- await this.sendToClient(clientId, { type: 'error', error: 'Authentication required to join room' });
532
+ await this.sendToClient(clientId, {
533
+ type: "error",
534
+ error: "Authentication required to join room",
535
+ });
493
536
  return;
494
537
  }
495
538
  try {
496
539
  // Check if already in the room
497
540
  if (client.roomIds.has(payload.roomId)) {
498
- await this.sendToClient(clientId, { type: 'error', error: 'Already in this room' });
541
+ await this.sendToClient(clientId, {
542
+ type: "error",
543
+ error: "Already in this room",
544
+ });
499
545
  return;
500
546
  }
501
547
  // Get or create room
502
548
  let roomState = await this.roomManager.getRoomState(payload.roomId);
503
549
  if (!roomState) {
504
- const roomConfig = { ...this.config.defaultRoomConfig, id: payload.roomId };
550
+ const roomConfig = {
551
+ ...this.config.defaultRoomConfig,
552
+ id: payload.roomId,
553
+ };
505
554
  const room = await this.roomManager.createRoom(payload.roomId, roomConfig);
506
555
  roomState = await this.roomManager.getRoomState(payload.roomId);
507
- await this.emit('room_created', { room });
556
+ await this.emit("room_created", { room });
508
557
  }
509
558
  if (!roomState) {
510
- throw new Error('Failed to create or get room state');
559
+ throw new Error("Failed to create or get room state");
511
560
  }
512
561
  // Join room
513
562
  const user = {
514
563
  id: client.userId,
515
564
  username: client.userId, // TODO: Get from auth token
516
- status: 'online',
565
+ status: "online",
517
566
  joinedAt: Date.now(),
518
567
  lastActivity: Date.now(),
519
- permissions: roomState.room.permissions || ['read', 'write']
568
+ permissions: roomState.room.permissions || ["read", "write"],
520
569
  };
521
570
  await this.roomManager.joinRoom(payload.roomId, user);
522
571
  client.roomIds.add(payload.roomId);
523
- await this.emit('client_joined_room', { clientId, roomId: payload.roomId });
572
+ await this.emit("client_joined_room", {
573
+ clientId,
574
+ roomId: payload.roomId,
575
+ });
524
576
  // Get updated room state after joining (so it includes the current user)
525
577
  const updatedRoomState = await this.roomManager.getRoomState(payload.roomId);
526
578
  if (!updatedRoomState) {
527
- throw new Error('Failed to get updated room state after joining');
579
+ throw new Error("Failed to get updated room state after joining");
528
580
  }
529
581
  // Convert Maps to plain objects for JSON serialization
530
582
  const serializableState = (0, room_js_1.serializeRoomState)(updatedRoomState);
531
583
  // Send room state to client
532
584
  await this.sendToClient(clientId, {
533
- type: 'room_joined',
585
+ type: "room_joined",
534
586
  roomId: payload.roomId,
535
- state: serializableState
587
+ state: serializableState,
536
588
  });
537
589
  // Send event history
538
590
  const history = await this.roomManager.getEventHistory(payload.roomId, 50);
539
591
  if (history.length > 0) {
540
592
  await this.sendToClient(clientId, {
541
- type: 'event_history',
542
- events: history
593
+ type: "event_history",
594
+ events: history,
543
595
  });
544
596
  }
545
597
  }
546
598
  catch (error) {
547
- await this.sendToClient(clientId, { type: 'error', error: error.message });
599
+ await this.sendToClient(clientId, {
600
+ type: "error",
601
+ error: error.message,
602
+ });
548
603
  }
549
604
  }
550
605
  async handleLeaveRoom(clientId, payload) {
@@ -566,14 +621,17 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
566
621
  }
567
622
  await this.roomManager.leaveRoom(roomId, client.userId);
568
623
  client.roomIds.delete(roomId);
569
- await this.emit('client_left_room', { clientId, roomId });
570
- await this.sendToClient(clientId, { type: 'room_left', roomId });
624
+ await this.emit("client_left_room", { clientId, roomId });
625
+ await this.sendToClient(clientId, { type: "room_left", roomId });
571
626
  }
572
627
  }
573
628
  async handleBroadcastEvent(clientId, payload) {
574
629
  const client = this.clients.get(clientId);
575
630
  if (!client || !client.roomIds.has(payload.roomId)) {
576
- await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
631
+ await this.sendToClient(clientId, {
632
+ type: "error",
633
+ error: "Not in specified room",
634
+ });
577
635
  return;
578
636
  }
579
637
  // Add metadata to event
@@ -583,42 +641,53 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
583
641
  ...payload.event.metadata,
584
642
  userId: client.userId,
585
643
  timestamp: Date.now(),
586
- roomId: payload.roomId
587
- }
644
+ roomId: payload.roomId,
645
+ },
588
646
  };
589
647
  // Add to history
590
648
  await this.roomManager.addEventToHistory(payload.roomId, enrichedEvent);
591
649
  // Broadcast to room
592
650
  await this.roomManager.broadcastToRoom(payload.roomId, enrichedEvent, client.userId);
593
- await this.emit('event_broadcast', {
651
+ await this.emit("event_broadcast", {
594
652
  roomId: payload.roomId,
595
653
  event: enrichedEvent,
596
- fromClientId: clientId
654
+ fromClientId: clientId,
597
655
  });
598
656
  }
599
657
  async handleLockRequest(clientId, payload) {
600
658
  const client = this.clients.get(clientId);
601
659
  if (!client || !client.roomIds.has(payload.roomId)) {
602
- await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
660
+ await this.sendToClient(clientId, {
661
+ type: "error",
662
+ error: "Not in specified room",
663
+ });
603
664
  return;
604
665
  }
605
666
  try {
606
667
  const lock = await this.lockManager.acquireLock(client.userId, client.userId, payload.request);
607
- await this.emit('lock_acquired', { lock, clientId });
608
- await this.sendToClient(clientId, { type: 'lock_acquired', lock });
668
+ await this.emit("lock_acquired", { lock, clientId });
669
+ await this.sendToClient(clientId, { type: "lock_acquired", lock });
609
670
  // Broadcast lock status to room
610
671
  await this.roomManager.broadcastToRoom(payload.roomId, {
611
672
  id: this.generateId(),
612
- type: 'lock_status',
673
+ type: "lock_status",
613
674
  timestamp: Date.now(),
614
675
  userId: client.userId,
615
676
  roomId: payload.roomId,
616
- data: { lock, action: 'acquired' },
617
- metadata: { userId: client.userId, timestamp: Date.now(), roomId: payload.roomId }
677
+ data: { lock, action: "acquired" },
678
+ metadata: {
679
+ userId: client.userId,
680
+ timestamp: Date.now(),
681
+ roomId: payload.roomId,
682
+ },
618
683
  });
619
684
  }
620
685
  catch (error) {
621
- await this.sendToClient(clientId, { type: 'lock_denied', request: payload.request, reason: error.message });
686
+ await this.sendToClient(clientId, {
687
+ type: "lock_denied",
688
+ request: payload.request,
689
+ reason: error.message,
690
+ });
622
691
  }
623
692
  }
624
693
  async handleLockRelease(clientId, payload) {
@@ -628,32 +697,38 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
628
697
  }
629
698
  try {
630
699
  await this.lockManager.releaseLock(payload.lockId);
631
- await this.emit('lock_released', { lockId: payload.lockId, clientId });
632
- await this.sendToClient(clientId, { type: 'lock_released', lockId: payload.lockId });
700
+ await this.emit("lock_released", { lockId: payload.lockId, clientId });
701
+ await this.sendToClient(clientId, {
702
+ type: "lock_released",
703
+ lockId: payload.lockId,
704
+ });
633
705
  // Broadcast lock status to all rooms the client is in
634
706
  for (const roomId of client.roomIds) {
635
707
  await this.roomManager.broadcastToRoom(roomId, {
636
708
  id: this.generateId(),
637
- type: 'lock_status',
709
+ type: "lock_status",
638
710
  timestamp: Date.now(),
639
711
  userId: client.userId,
640
712
  roomId,
641
- data: { lockId: payload.lockId, action: 'released' },
642
- metadata: { userId: client.userId, timestamp: Date.now(), roomId }
713
+ data: { lockId: payload.lockId, action: "released" },
714
+ metadata: { userId: client.userId, timestamp: Date.now(), roomId },
643
715
  });
644
716
  }
645
717
  }
646
718
  catch (error) {
647
- await this.sendToClient(clientId, { type: 'error', error: error.message });
719
+ await this.sendToClient(clientId, {
720
+ type: "error",
721
+ error: error.message,
722
+ });
648
723
  }
649
724
  }
650
725
  // Utility Methods
651
726
  async sendToClient(clientId, message) {
652
727
  const transportMessage = {
653
728
  id: this.generateId(),
654
- type: 'server_message',
729
+ type: "server_message",
655
730
  payload: { clientId, ...message },
656
- timestamp: Date.now()
731
+ timestamp: Date.now(),
657
732
  };
658
733
  await this.transport.send(transportMessage);
659
734
  }
@@ -663,12 +738,12 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
663
738
  // Public API for external management
664
739
  async createRoom(roomId, config) {
665
740
  const room = await this.roomManager.createRoom(roomId, config);
666
- await this.emit('room_created', { room });
741
+ await this.emit("room_created", { room });
667
742
  return room;
668
743
  }
669
744
  async deleteRoom(roomId) {
670
745
  await this.roomManager.deleteRoom(roomId);
671
- await this.emit('room_deleted', { roomId });
746
+ await this.emit("room_deleted", { roomId });
672
747
  }
673
748
  getConnectedClients() {
674
749
  return Array.from(this.clients.values());
@@ -701,19 +776,19 @@ class BaseServer extends EventEmitter_js_1.TypedEventEmitter {
701
776
  userId,
702
777
  deviceId,
703
778
  timestamp: Date.now(),
704
- activityType
779
+ activityType,
705
780
  });
706
- await this.emit('presence_activity', {
781
+ await this.emit("presence_activity", {
707
782
  userId,
708
783
  deviceId,
709
- timestamp: Date.now()
784
+ timestamp: Date.now(),
710
785
  });
711
786
  }
712
787
  catch (error) {
713
788
  // Don't throw, just log the error
714
- await this.emit('error', {
789
+ await this.emit("error", {
715
790
  error: error,
716
- context: 'presence_update_activity'
791
+ context: "presence_update_activity",
717
792
  });
718
793
  }
719
794
  }