@principal-ai/control-tower-core 0.1.0 → 0.1.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.
Files changed (84) hide show
  1. package/README.md +84 -0
  2. package/dist/abstractions/AuthAdapter.d.ts +9 -0
  3. package/dist/abstractions/AuthAdapter.d.ts.map +1 -0
  4. package/dist/abstractions/AuthAdapter.js +1 -0
  5. package/dist/abstractions/DefaultLockManager.d.ts +18 -0
  6. package/dist/abstractions/DefaultLockManager.d.ts.map +1 -0
  7. package/dist/abstractions/DefaultLockManager.js +152 -0
  8. package/dist/abstractions/DefaultRoomManager.d.ts +15 -0
  9. package/dist/abstractions/DefaultRoomManager.d.ts.map +1 -0
  10. package/dist/abstractions/DefaultRoomManager.js +91 -0
  11. package/dist/abstractions/EventEmitter.d.ts +14 -0
  12. package/dist/abstractions/EventEmitter.d.ts.map +1 -0
  13. package/dist/abstractions/EventEmitter.js +56 -0
  14. package/dist/abstractions/LockManager.d.ts +16 -0
  15. package/dist/abstractions/LockManager.d.ts.map +1 -0
  16. package/dist/abstractions/LockManager.js +9 -0
  17. package/dist/abstractions/RoomManager.d.ts +15 -0
  18. package/dist/abstractions/RoomManager.d.ts.map +1 -0
  19. package/dist/abstractions/RoomManager.js +5 -0
  20. package/dist/abstractions/StorageAdapter.d.ts +25 -0
  21. package/dist/abstractions/StorageAdapter.d.ts.map +1 -0
  22. package/dist/abstractions/StorageAdapter.js +1 -0
  23. package/dist/abstractions/TransportAdapter.d.ts +17 -0
  24. package/dist/abstractions/TransportAdapter.d.ts.map +1 -0
  25. package/dist/abstractions/TransportAdapter.js +1 -0
  26. package/dist/abstractions/index.d.ts +9 -0
  27. package/dist/abstractions/index.d.ts.map +1 -0
  28. package/dist/abstractions/index.js +5 -0
  29. package/dist/adapters/mock/MockAuthAdapter.d.ts +27 -0
  30. package/dist/adapters/mock/MockAuthAdapter.d.ts.map +1 -0
  31. package/dist/adapters/mock/MockAuthAdapter.js +161 -0
  32. package/dist/adapters/mock/MockStorageAdapter.d.ts +27 -0
  33. package/dist/adapters/mock/MockStorageAdapter.d.ts.map +1 -0
  34. package/dist/adapters/mock/MockStorageAdapter.js +162 -0
  35. package/dist/adapters/mock/MockTransportAdapter.d.ts +31 -0
  36. package/dist/adapters/mock/MockTransportAdapter.d.ts.map +1 -0
  37. package/dist/adapters/mock/MockTransportAdapter.js +90 -0
  38. package/dist/adapters/mock/index.d.ts +4 -0
  39. package/dist/adapters/mock/index.d.ts.map +1 -0
  40. package/dist/adapters/mock/index.js +3 -0
  41. package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts +40 -0
  42. package/dist/adapters/websocket/WebSocketTransportAdapter.d.ts.map +1 -0
  43. package/dist/adapters/websocket/WebSocketTransportAdapter.js +204 -0
  44. package/dist/adapters/websocket/index.d.ts +2 -0
  45. package/dist/adapters/websocket/index.d.ts.map +1 -0
  46. package/dist/adapters/websocket/index.js +1 -0
  47. package/dist/client/BaseClient.d.ts +103 -0
  48. package/dist/client/BaseClient.d.ts.map +1 -0
  49. package/dist/client/BaseClient.js +282 -0
  50. package/dist/client/ClientBuilder.d.ts +16 -0
  51. package/dist/client/ClientBuilder.d.ts.map +1 -0
  52. package/dist/client/ClientBuilder.js +37 -0
  53. package/dist/client/index.d.ts +3 -0
  54. package/dist/client/index.d.ts.map +1 -0
  55. package/dist/client/index.js +3 -0
  56. package/dist/index.d.ts +7 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +9 -1205
  59. package/dist/index.js.map +23 -6
  60. package/dist/server/BaseServer.d.ts +116 -0
  61. package/dist/server/BaseServer.d.ts.map +1 -0
  62. package/dist/server/BaseServer.js +422 -0
  63. package/dist/server/ServerBuilder.d.ts +32 -0
  64. package/dist/server/ServerBuilder.d.ts.map +1 -0
  65. package/dist/server/ServerBuilder.js +66 -0
  66. package/dist/server/index.d.ts +3 -0
  67. package/dist/server/index.d.ts.map +1 -0
  68. package/dist/server/index.js +3 -0
  69. package/dist/types/auth.d.ts +40 -0
  70. package/dist/types/auth.d.ts.map +1 -0
  71. package/dist/types/auth.js +1 -0
  72. package/dist/types/events.d.ts +66 -0
  73. package/dist/types/events.d.ts.map +1 -0
  74. package/dist/types/events.js +1 -0
  75. package/dist/types/index.d.ts +23 -0
  76. package/dist/types/index.d.ts.map +1 -0
  77. package/dist/types/index.js +1 -0
  78. package/dist/types/lock.d.ts +35 -0
  79. package/dist/types/lock.d.ts.map +1 -0
  80. package/dist/types/lock.js +1 -0
  81. package/dist/types/room.d.ts +40 -0
  82. package/dist/types/room.d.ts.map +1 -0
  83. package/dist/types/room.js +1 -0
  84. package/package.json +7 -3
@@ -0,0 +1,116 @@
1
+ import type { Event, Room, RoomConfig, Lock } from '../types';
2
+ import type { ITransportAdapter } from '../abstractions/TransportAdapter';
3
+ import type { IAuthAdapter } from '../abstractions/AuthAdapter';
4
+ import type { IStorageAdapter } from '../abstractions/StorageAdapter';
5
+ import type { RoomManager } from '../abstractions/RoomManager';
6
+ import type { LockManager } from '../abstractions/LockManager';
7
+ import { TypedEventEmitter } from '../abstractions/EventEmitter';
8
+ import type { Server as HttpServer } from 'http';
9
+ import type { Server as HttpsServer } from 'https';
10
+ import type { WebSocketServer } from 'ws';
11
+ export interface ServerConfig {
12
+ transport: ITransportAdapter;
13
+ auth?: IAuthAdapter;
14
+ storage?: IStorageAdapter;
15
+ roomManager: RoomManager;
16
+ lockManager: LockManager;
17
+ defaultRoomConfig?: Partial<RoomConfig>;
18
+ httpServer?: HttpServer | HttpsServer;
19
+ webSocketPath?: string;
20
+ webSocketServer?: WebSocketServer;
21
+ }
22
+ export interface ConnectedClient {
23
+ id: string;
24
+ userId: string;
25
+ roomId: string | null;
26
+ authenticated: boolean;
27
+ connectedAt: number;
28
+ }
29
+ export interface ServerEvents {
30
+ started: {
31
+ port: number;
32
+ };
33
+ stopped: {};
34
+ client_connected: {
35
+ client: ConnectedClient;
36
+ };
37
+ client_disconnected: {
38
+ clientId: string;
39
+ reason: string;
40
+ };
41
+ client_authenticated: {
42
+ clientId: string;
43
+ userId: string;
44
+ };
45
+ room_created: {
46
+ room: Room;
47
+ };
48
+ room_deleted: {
49
+ roomId: string;
50
+ };
51
+ client_joined_room: {
52
+ clientId: string;
53
+ roomId: string;
54
+ };
55
+ client_left_room: {
56
+ clientId: string;
57
+ roomId: string;
58
+ };
59
+ event_broadcast: {
60
+ roomId: string;
61
+ event: Event;
62
+ fromClientId: string;
63
+ };
64
+ lock_acquired: {
65
+ lock: Lock;
66
+ clientId: string;
67
+ };
68
+ lock_released: {
69
+ lockId: string;
70
+ clientId: string;
71
+ };
72
+ error: {
73
+ error: Error;
74
+ context?: string;
75
+ };
76
+ [key: string]: unknown;
77
+ }
78
+ export declare class BaseServer extends TypedEventEmitter<ServerEvents> {
79
+ private transport;
80
+ private auth?;
81
+ private storage?;
82
+ private roomManager;
83
+ private lockManager;
84
+ private config;
85
+ private clients;
86
+ private clientMessageHandlers;
87
+ private running;
88
+ private initialized;
89
+ private mode;
90
+ constructor(config: ServerConfig);
91
+ start(port?: number): Promise<void>;
92
+ initialize(): Promise<void>;
93
+ stop(): Promise<void>;
94
+ private handleTransportMessage;
95
+ private handleTransportError;
96
+ private handleTransportClose;
97
+ private addClient;
98
+ private disconnectClient;
99
+ private createClientMessageHandler;
100
+ private handleAuthenticate;
101
+ private handleJoinRoom;
102
+ private handleLeaveRoom;
103
+ private handleBroadcastEvent;
104
+ private handleLockRequest;
105
+ private handleLockRelease;
106
+ private sendToClient;
107
+ private generateId;
108
+ createRoom(roomId: string, config: RoomConfig): Promise<Room>;
109
+ deleteRoom(roomId: string): Promise<void>;
110
+ getConnectedClients(): ConnectedClient[];
111
+ getClient(clientId: string): ConnectedClient | null;
112
+ isRunning(): boolean;
113
+ getMode(): 'standalone' | 'integration';
114
+ isInitialized(): boolean;
115
+ }
116
+ //# sourceMappingURL=BaseServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseServer.d.ts","sourceRoot":"","sources":["../../src/server/BaseServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,422 @@
1
+ import { TypedEventEmitter } from '../abstractions/EventEmitter';
2
+ export class BaseServer extends TypedEventEmitter {
3
+ constructor(config) {
4
+ super();
5
+ this.clients = new Map();
6
+ this.clientMessageHandlers = new Map();
7
+ this.running = false;
8
+ this.initialized = false;
9
+ this.config = config;
10
+ this.transport = config.transport;
11
+ this.auth = config.auth;
12
+ this.storage = config.storage;
13
+ this.roomManager = config.roomManager;
14
+ this.lockManager = config.lockManager;
15
+ // Determine mode based on configuration
16
+ this.mode = (config.httpServer || config.webSocketServer) ? 'integration' : 'standalone';
17
+ // Set up transport event handlers
18
+ this.transport.onMessage(this.handleTransportMessage.bind(this));
19
+ this.transport.onError(this.handleTransportError.bind(this));
20
+ this.transport.onClose(this.handleTransportClose.bind(this));
21
+ }
22
+ // Server Lifecycle
23
+ async start(port) {
24
+ if (this.mode === 'integration') {
25
+ throw new Error('Cannot use start() in integration mode. Use initialize() instead.');
26
+ }
27
+ if (!port) {
28
+ throw new Error('Port is required for standalone mode');
29
+ }
30
+ if (this.running) {
31
+ throw new Error('Server is already running');
32
+ }
33
+ try {
34
+ await this.transport.connect(`ws://localhost:${port}`, { url: `ws://localhost:${port}` });
35
+ this.running = true;
36
+ this.initialized = true;
37
+ await this.emit('started', { port });
38
+ }
39
+ catch (error) {
40
+ await this.emit('error', { error: error, context: 'server_start' });
41
+ throw error;
42
+ }
43
+ }
44
+ async initialize() {
45
+ if (this.mode === 'standalone') {
46
+ throw new Error('Cannot use initialize() in standalone mode. Use start() instead.');
47
+ }
48
+ if (this.initialized) {
49
+ throw new Error('Server is already initialized');
50
+ }
51
+ try {
52
+ const transport = this.transport;
53
+ if (this.config.webSocketServer) {
54
+ // Attach to existing WebSocket server
55
+ if (typeof transport.attachToWebSocketServer === 'function') {
56
+ await transport.attachToWebSocketServer(this.config.webSocketServer);
57
+ }
58
+ else {
59
+ throw new Error('Transport adapter does not support attachToWebSocketServer');
60
+ }
61
+ }
62
+ else if (this.config.httpServer) {
63
+ // Attach to existing HTTP server
64
+ if (typeof transport.attach === 'function') {
65
+ await transport.attach(this.config.httpServer, this.config.webSocketPath || '/ws');
66
+ }
67
+ else {
68
+ throw new Error('Transport adapter does not support attach');
69
+ }
70
+ }
71
+ else {
72
+ throw new Error('Either httpServer or webSocketServer must be provided in integration mode');
73
+ }
74
+ this.running = true;
75
+ this.initialized = true;
76
+ await this.emit('started', { port: 0 }); // Port 0 indicates integration mode
77
+ }
78
+ catch (error) {
79
+ await this.emit('error', { error: error, context: 'server_initialize' });
80
+ throw error;
81
+ }
82
+ }
83
+ async stop() {
84
+ if (!this.running) {
85
+ return;
86
+ }
87
+ this.running = false;
88
+ // Disconnect all clients
89
+ for (const [clientId] of Array.from(this.clients)) {
90
+ await this.disconnectClient(clientId, 'Server shutting down');
91
+ }
92
+ try {
93
+ await this.transport.disconnect();
94
+ }
95
+ catch (error) {
96
+ await this.emit('error', { error: error, context: 'server_stop' });
97
+ }
98
+ this.clients.clear();
99
+ this.clientMessageHandlers.clear();
100
+ await this.emit('stopped', {});
101
+ }
102
+ // Client Management
103
+ async handleTransportMessage(message) {
104
+ // Transport messages contain clientId in the payload for routing
105
+ const payload = message.payload;
106
+ const { clientId, ...clientMessage } = payload;
107
+ const clientMsg = {
108
+ id: message.id,
109
+ type: message.type,
110
+ payload: clientMessage,
111
+ timestamp: message.timestamp
112
+ };
113
+ if (!clientId) {
114
+ await this.emit('error', {
115
+ error: new Error('Message missing clientId'),
116
+ context: 'transport_message'
117
+ });
118
+ return;
119
+ }
120
+ const handler = this.clientMessageHandlers.get(clientId);
121
+ if (handler) {
122
+ await handler(clientMsg);
123
+ }
124
+ else {
125
+ await this.emit('error', {
126
+ error: new Error(`No handler for client ${clientId}`),
127
+ context: 'transport_message'
128
+ });
129
+ }
130
+ }
131
+ async handleTransportError(error) {
132
+ await this.emit('error', { error, context: 'transport' });
133
+ }
134
+ async handleTransportClose(_code, _reason) {
135
+ // Transport close means server is shutting down
136
+ if (this.running) {
137
+ await this.stop();
138
+ }
139
+ }
140
+ async addClient(clientId) {
141
+ const client = {
142
+ id: clientId,
143
+ userId: '',
144
+ roomId: null,
145
+ authenticated: false,
146
+ connectedAt: Date.now()
147
+ };
148
+ this.clients.set(clientId, client);
149
+ // Set up message handler for this client
150
+ this.clientMessageHandlers.set(clientId, this.createClientMessageHandler(clientId));
151
+ await this.emit('client_connected', { client });
152
+ }
153
+ async disconnectClient(clientId, reason) {
154
+ const client = this.clients.get(clientId);
155
+ if (!client) {
156
+ return;
157
+ }
158
+ // Leave room if in one
159
+ if (client.roomId) {
160
+ await this.roomManager.leaveRoom(client.roomId, client.userId);
161
+ await this.emit('client_left_room', { clientId, roomId: client.roomId });
162
+ }
163
+ // Release all locks held by this client
164
+ await this.lockManager.releaseUserLocks(client.userId);
165
+ // Clean up
166
+ this.clients.delete(clientId);
167
+ this.clientMessageHandlers.delete(clientId);
168
+ await this.emit('client_disconnected', { clientId, reason });
169
+ }
170
+ createClientMessageHandler(clientId) {
171
+ return async (message) => {
172
+ try {
173
+ const client = this.clients.get(clientId);
174
+ if (!client) {
175
+ return;
176
+ }
177
+ switch (message.type) {
178
+ case 'authenticate':
179
+ await this.handleAuthenticate(clientId, message.payload);
180
+ break;
181
+ case 'join_room':
182
+ await this.handleJoinRoom(clientId, message.payload);
183
+ break;
184
+ case 'leave_room':
185
+ await this.handleLeaveRoom(clientId);
186
+ break;
187
+ case 'broadcast_event':
188
+ await this.handleBroadcastEvent(clientId, message.payload);
189
+ break;
190
+ case 'request_lock':
191
+ await this.handleLockRequest(clientId, message.payload);
192
+ break;
193
+ case 'release_lock':
194
+ await this.handleLockRelease(clientId, message.payload);
195
+ break;
196
+ case 'ping':
197
+ await this.sendToClient(clientId, { type: 'pong', timestamp: Date.now() });
198
+ break;
199
+ default:
200
+ await this.sendToClient(clientId, {
201
+ type: 'error',
202
+ error: `Unknown message type: ${message.type}`
203
+ });
204
+ }
205
+ }
206
+ catch (error) {
207
+ await this.sendToClient(clientId, {
208
+ type: 'error',
209
+ error: error.message
210
+ });
211
+ await this.emit('error', { error: error, context: `client_${clientId}` });
212
+ }
213
+ };
214
+ }
215
+ // Message Handlers
216
+ async handleAuthenticate(clientId, payload) {
217
+ if (!this.auth) {
218
+ await this.sendToClient(clientId, { type: 'auth_result', success: false, error: 'Authentication not configured' });
219
+ return;
220
+ }
221
+ try {
222
+ const tokenPayload = await this.auth.validateToken(payload.token);
223
+ const client = this.clients.get(clientId);
224
+ if (client) {
225
+ client.userId = tokenPayload.userId;
226
+ client.authenticated = true;
227
+ await this.emit('client_authenticated', { clientId, userId: tokenPayload.userId });
228
+ }
229
+ await this.sendToClient(clientId, { type: 'auth_result', success: true });
230
+ }
231
+ catch (error) {
232
+ await this.sendToClient(clientId, { type: 'auth_result', success: false, error: error.message });
233
+ }
234
+ }
235
+ async handleJoinRoom(clientId, payload) {
236
+ const client = this.clients.get(clientId);
237
+ if (!client) {
238
+ return;
239
+ }
240
+ // Authenticate if token provided
241
+ if (payload.token && !client.authenticated) {
242
+ await this.handleAuthenticate(clientId, { token: payload.token });
243
+ }
244
+ if (!client.authenticated) {
245
+ await this.sendToClient(clientId, { type: 'error', error: 'Authentication required to join room' });
246
+ return;
247
+ }
248
+ try {
249
+ // Leave current room if any
250
+ if (client.roomId) {
251
+ await this.roomManager.leaveRoom(client.roomId, client.userId);
252
+ await this.emit('client_left_room', { clientId, roomId: client.roomId });
253
+ }
254
+ // Get or create room
255
+ let roomState = await this.roomManager.getRoomState(payload.roomId);
256
+ if (!roomState) {
257
+ const roomConfig = { ...this.config.defaultRoomConfig, id: payload.roomId };
258
+ const room = await this.roomManager.createRoom(payload.roomId, roomConfig);
259
+ roomState = await this.roomManager.getRoomState(payload.roomId);
260
+ await this.emit('room_created', { room });
261
+ }
262
+ if (!roomState) {
263
+ throw new Error('Failed to create or get room state');
264
+ }
265
+ // Join room
266
+ const user = {
267
+ id: client.userId,
268
+ username: client.userId, // TODO: Get from auth token
269
+ status: 'online',
270
+ joinedAt: Date.now(),
271
+ lastActivity: Date.now(),
272
+ permissions: roomState.room.permissions || ['read', 'write']
273
+ };
274
+ await this.roomManager.joinRoom(payload.roomId, user);
275
+ client.roomId = payload.roomId;
276
+ await this.emit('client_joined_room', { clientId, roomId: payload.roomId });
277
+ // Send room state to client
278
+ await this.sendToClient(clientId, {
279
+ type: 'room_joined',
280
+ roomId: payload.roomId,
281
+ state: roomState
282
+ });
283
+ // Send event history
284
+ const history = await this.roomManager.getEventHistory(payload.roomId, 50);
285
+ if (history.length > 0) {
286
+ await this.sendToClient(clientId, {
287
+ type: 'event_history',
288
+ events: history
289
+ });
290
+ }
291
+ }
292
+ catch (error) {
293
+ await this.sendToClient(clientId, { type: 'error', error: error.message });
294
+ }
295
+ }
296
+ async handleLeaveRoom(clientId) {
297
+ const client = this.clients.get(clientId);
298
+ if (!client || !client.roomId) {
299
+ return;
300
+ }
301
+ const roomId = client.roomId;
302
+ await this.roomManager.leaveRoom(roomId, client.userId);
303
+ client.roomId = null;
304
+ await this.emit('client_left_room', { clientId, roomId });
305
+ await this.sendToClient(clientId, { type: 'room_left', roomId });
306
+ }
307
+ async handleBroadcastEvent(clientId, payload) {
308
+ const client = this.clients.get(clientId);
309
+ if (!client || client.roomId !== payload.roomId) {
310
+ await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
311
+ return;
312
+ }
313
+ // Add metadata to event
314
+ const enrichedEvent = {
315
+ ...payload.event,
316
+ metadata: {
317
+ ...payload.event.metadata,
318
+ userId: client.userId,
319
+ timestamp: Date.now(),
320
+ roomId: payload.roomId
321
+ }
322
+ };
323
+ // Add to history
324
+ await this.roomManager.addEventToHistory(payload.roomId, enrichedEvent);
325
+ // Broadcast to room
326
+ await this.roomManager.broadcastToRoom(payload.roomId, enrichedEvent, client.userId);
327
+ await this.emit('event_broadcast', {
328
+ roomId: payload.roomId,
329
+ event: enrichedEvent,
330
+ fromClientId: clientId
331
+ });
332
+ }
333
+ async handleLockRequest(clientId, payload) {
334
+ const client = this.clients.get(clientId);
335
+ if (!client || client.roomId !== payload.roomId) {
336
+ await this.sendToClient(clientId, { type: 'error', error: 'Not in specified room' });
337
+ return;
338
+ }
339
+ try {
340
+ const lock = await this.lockManager.acquireLock(client.userId, client.userId, payload.request);
341
+ await this.emit('lock_acquired', { lock, clientId });
342
+ await this.sendToClient(clientId, { type: 'lock_acquired', lock });
343
+ // Broadcast lock status to room
344
+ await this.roomManager.broadcastToRoom(payload.roomId, {
345
+ id: this.generateId(),
346
+ type: 'lock_status',
347
+ timestamp: Date.now(),
348
+ userId: client.userId,
349
+ roomId: payload.roomId,
350
+ data: { lock, action: 'acquired' },
351
+ metadata: { userId: client.userId, timestamp: Date.now(), roomId: payload.roomId }
352
+ });
353
+ }
354
+ catch (error) {
355
+ await this.sendToClient(clientId, { type: 'lock_denied', request: payload.request, reason: error.message });
356
+ }
357
+ }
358
+ async handleLockRelease(clientId, payload) {
359
+ const client = this.clients.get(clientId);
360
+ if (!client) {
361
+ return;
362
+ }
363
+ try {
364
+ await this.lockManager.releaseLock(payload.lockId);
365
+ await this.emit('lock_released', { lockId: payload.lockId, clientId });
366
+ await this.sendToClient(clientId, { type: 'lock_released', lockId: payload.lockId });
367
+ // Broadcast lock status to room if client is in one
368
+ if (client.roomId) {
369
+ await this.roomManager.broadcastToRoom(client.roomId, {
370
+ id: this.generateId(),
371
+ type: 'lock_status',
372
+ timestamp: Date.now(),
373
+ userId: client.userId,
374
+ roomId: client.roomId,
375
+ data: { lockId: payload.lockId, action: 'released' },
376
+ metadata: { userId: client.userId, timestamp: Date.now(), roomId: client.roomId }
377
+ });
378
+ }
379
+ }
380
+ catch (error) {
381
+ await this.sendToClient(clientId, { type: 'error', error: error.message });
382
+ }
383
+ }
384
+ // Utility Methods
385
+ async sendToClient(clientId, message) {
386
+ const transportMessage = {
387
+ id: this.generateId(),
388
+ type: 'server_message',
389
+ payload: { clientId, ...message },
390
+ timestamp: Date.now()
391
+ };
392
+ await this.transport.send(transportMessage);
393
+ }
394
+ generateId() {
395
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
396
+ }
397
+ // Public API for external management
398
+ async createRoom(roomId, config) {
399
+ const room = await this.roomManager.createRoom(roomId, config);
400
+ await this.emit('room_created', { room });
401
+ return room;
402
+ }
403
+ async deleteRoom(roomId) {
404
+ await this.roomManager.deleteRoom(roomId);
405
+ await this.emit('room_deleted', { roomId });
406
+ }
407
+ getConnectedClients() {
408
+ return Array.from(this.clients.values());
409
+ }
410
+ getClient(clientId) {
411
+ return this.clients.get(clientId) || null;
412
+ }
413
+ isRunning() {
414
+ return this.running;
415
+ }
416
+ getMode() {
417
+ return this.mode;
418
+ }
419
+ isInitialized() {
420
+ return this.initialized;
421
+ }
422
+ }
@@ -0,0 +1,32 @@
1
+ import type { ITransportAdapter } from '../abstractions/TransportAdapter';
2
+ import type { IAuthAdapter } from '../abstractions/AuthAdapter';
3
+ import type { IStorageAdapter } from '../abstractions/StorageAdapter';
4
+ import type { RoomManager } from '../abstractions/RoomManager';
5
+ import type { LockManager } from '../abstractions/LockManager';
6
+ import type { RoomConfig } from '../types';
7
+ import { BaseServer } from './BaseServer';
8
+ import type { Server as HttpServer } from 'http';
9
+ import type { Server as HttpsServer } from 'https';
10
+ import type { WebSocketServer } from 'ws';
11
+ export declare class ServerBuilder {
12
+ private transport?;
13
+ private auth?;
14
+ private storage?;
15
+ private roomManager?;
16
+ private lockManager?;
17
+ private defaultRoomConfig?;
18
+ private httpServer?;
19
+ private webSocketPath?;
20
+ private webSocketServer?;
21
+ withTransport(transport: ITransportAdapter): this;
22
+ withAuth(auth: IAuthAdapter): this;
23
+ withStorage(storage: IStorageAdapter): this;
24
+ withRoomManager(roomManager: RoomManager): this;
25
+ withLockManager(lockManager: LockManager): this;
26
+ withDefaultRoomConfig(config: Partial<RoomConfig>): this;
27
+ withHttpServer(server: HttpServer | HttpsServer): this;
28
+ withWebSocketPath(path: string): this;
29
+ withWebSocketServer(wss: WebSocketServer): this;
30
+ build(): BaseServer;
31
+ }
32
+ //# sourceMappingURL=ServerBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServerBuilder.d.ts","sourceRoot":"","sources":["../../src/server/ServerBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAqB,MAAM,cAAc,CAAC;AAC7D,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;IAE1C,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,KAAK,IAAI,UAAU;CA+BpB"}
@@ -0,0 +1,66 @@
1
+ import { BaseServer } from './BaseServer';
2
+ export class ServerBuilder {
3
+ withTransport(transport) {
4
+ this.transport = transport;
5
+ return this;
6
+ }
7
+ withAuth(auth) {
8
+ this.auth = auth;
9
+ return this;
10
+ }
11
+ withStorage(storage) {
12
+ this.storage = storage;
13
+ return this;
14
+ }
15
+ withRoomManager(roomManager) {
16
+ this.roomManager = roomManager;
17
+ return this;
18
+ }
19
+ withLockManager(lockManager) {
20
+ this.lockManager = lockManager;
21
+ return this;
22
+ }
23
+ withDefaultRoomConfig(config) {
24
+ this.defaultRoomConfig = config;
25
+ return this;
26
+ }
27
+ withHttpServer(server) {
28
+ this.httpServer = server;
29
+ return this;
30
+ }
31
+ withWebSocketPath(path) {
32
+ this.webSocketPath = path;
33
+ return this;
34
+ }
35
+ withWebSocketServer(wss) {
36
+ this.webSocketServer = wss;
37
+ return this;
38
+ }
39
+ build() {
40
+ if (!this.transport) {
41
+ throw new Error('Transport adapter is required');
42
+ }
43
+ if (!this.roomManager) {
44
+ throw new Error('Room manager is required');
45
+ }
46
+ if (!this.lockManager) {
47
+ throw new Error('Lock manager is required');
48
+ }
49
+ const config = {
50
+ transport: this.transport,
51
+ auth: this.auth,
52
+ storage: this.storage,
53
+ roomManager: this.roomManager,
54
+ lockManager: this.lockManager,
55
+ defaultRoomConfig: this.defaultRoomConfig || {
56
+ maxUsers: 50,
57
+ maxHistory: 100,
58
+ permissions: ['read', 'write']
59
+ },
60
+ httpServer: this.httpServer,
61
+ webSocketPath: this.webSocketPath,
62
+ webSocketServer: this.webSocketServer
63
+ };
64
+ return new BaseServer(config);
65
+ }
66
+ }
@@ -0,0 +1,3 @@
1
+ export { BaseServer, type ServerConfig, type ServerEvents, type ConnectedClient } from './BaseServer';
2
+ export { ServerBuilder } from './ServerBuilder';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Server-side implementations
2
+ export { BaseServer } from './BaseServer';
3
+ export { ServerBuilder } from './ServerBuilder';