@principal-ai/control-tower-core 0.1.2 → 0.1.4

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/dist/index.mjs ADDED
@@ -0,0 +1,1402 @@
1
+ // src/abstractions/EventEmitter.ts
2
+ class TypedEventEmitter {
3
+ listeners = new Map;
4
+ onceListeners = new Map;
5
+ on(event, listener) {
6
+ if (!this.listeners.has(event)) {
7
+ this.listeners.set(event, new Set);
8
+ }
9
+ this.listeners.get(event).add(listener);
10
+ return () => this.off(event, listener);
11
+ }
12
+ once(event, listener) {
13
+ if (!this.onceListeners.has(event)) {
14
+ this.onceListeners.set(event, new Set);
15
+ }
16
+ this.onceListeners.get(event).add(listener);
17
+ return () => this.off(event, listener);
18
+ }
19
+ off(event, listener) {
20
+ this.listeners.get(event)?.delete(listener);
21
+ this.onceListeners.get(event)?.delete(listener);
22
+ }
23
+ async emit(event, data) {
24
+ const listeners = this.listeners.get(event);
25
+ const onceListeners = this.onceListeners.get(event);
26
+ if (onceListeners) {
27
+ const listenersToCall = Array.from(onceListeners);
28
+ this.onceListeners.delete(event);
29
+ for (const listener of listenersToCall) {
30
+ await listener(data);
31
+ }
32
+ }
33
+ if (listeners) {
34
+ for (const listener of Array.from(listeners)) {
35
+ await listener(data);
36
+ }
37
+ }
38
+ }
39
+ removeAllListeners(event) {
40
+ if (event) {
41
+ this.listeners.delete(event);
42
+ this.onceListeners.delete(event);
43
+ } else {
44
+ this.listeners.clear();
45
+ this.onceListeners.clear();
46
+ }
47
+ }
48
+ listenerCount(event) {
49
+ const regular = this.listeners.get(event)?.size ?? 0;
50
+ const once = this.onceListeners.get(event)?.size ?? 0;
51
+ return regular + once;
52
+ }
53
+ }
54
+ // src/abstractions/RoomManager.ts
55
+ class RoomManager {
56
+ rooms = new Map;
57
+ }
58
+ // src/abstractions/LockManager.ts
59
+ class LockManager {
60
+ lockState = {
61
+ locks: new Map,
62
+ queue: new Map,
63
+ userLocks: new Map
64
+ };
65
+ }
66
+ // src/abstractions/DefaultRoomManager.ts
67
+ class DefaultRoomManager extends RoomManager {
68
+ async createRoom(id, config) {
69
+ const room = {
70
+ id,
71
+ name: config.name || id,
72
+ createdAt: Date.now(),
73
+ maxUsers: config.maxUsers,
74
+ maxHistory: config.maxHistory,
75
+ permissions: config.permissions,
76
+ metadata: config.metadata || {}
77
+ };
78
+ const roomState = {
79
+ room,
80
+ users: new Map,
81
+ eventHistory: [],
82
+ locks: new Map
83
+ };
84
+ this.rooms.set(id, roomState);
85
+ return room;
86
+ }
87
+ async deleteRoom(id) {
88
+ this.rooms.delete(id);
89
+ }
90
+ async joinRoom(roomId, user) {
91
+ const roomState = this.rooms.get(roomId);
92
+ if (!roomState) {
93
+ throw new Error(`Room ${roomId} not found`);
94
+ }
95
+ if (roomState.room.maxUsers && roomState.users.size >= roomState.room.maxUsers) {
96
+ throw new Error(`Room ${roomId} is full`);
97
+ }
98
+ roomState.users.set(user.id, user);
99
+ }
100
+ async leaveRoom(roomId, userId) {
101
+ const roomState = this.rooms.get(roomId);
102
+ if (!roomState) {
103
+ return;
104
+ }
105
+ roomState.users.delete(userId);
106
+ }
107
+ async broadcastToRoom(roomId, event, _excludeUserId) {
108
+ const roomState = this.rooms.get(roomId);
109
+ if (!roomState) {
110
+ throw new Error(`Room ${roomId} not found`);
111
+ }
112
+ await this.addEventToHistory(roomId, event);
113
+ }
114
+ async getRoomState(roomId) {
115
+ return this.rooms.get(roomId) || null;
116
+ }
117
+ async getUsersInRoom(roomId) {
118
+ const roomState = this.rooms.get(roomId);
119
+ if (!roomState) {
120
+ return [];
121
+ }
122
+ return Array.from(roomState.users.values());
123
+ }
124
+ async addEventToHistory(roomId, event) {
125
+ const roomState = this.rooms.get(roomId);
126
+ if (!roomState) {
127
+ throw new Error(`Room ${roomId} not found`);
128
+ }
129
+ roomState.eventHistory.push(event);
130
+ if (roomState.room.maxHistory && roomState.eventHistory.length > roomState.room.maxHistory) {
131
+ roomState.eventHistory = roomState.eventHistory.slice(-roomState.room.maxHistory);
132
+ }
133
+ }
134
+ async getEventHistory(roomId, limit) {
135
+ const roomState = this.rooms.get(roomId);
136
+ if (!roomState) {
137
+ return [];
138
+ }
139
+ const history = roomState.eventHistory;
140
+ return limit ? history.slice(-limit) : history;
141
+ }
142
+ async updateUserStatus(roomId, userId, status) {
143
+ const roomState = this.rooms.get(roomId);
144
+ if (!roomState) {
145
+ throw new Error(`Room ${roomId} not found`);
146
+ }
147
+ const user = roomState.users.get(userId);
148
+ if (user) {
149
+ user.status = status;
150
+ user.lastActivity = Date.now();
151
+ }
152
+ }
153
+ }
154
+ // src/abstractions/DefaultLockManager.ts
155
+ class DefaultLockManager extends LockManager {
156
+ async acquireLock(userId, username, request) {
157
+ const { path, type = "file", priority = "normal", ttl, branch, metadata } = request;
158
+ const userLocks = this.lockState.userLocks.get(userId) || new Set;
159
+ if (userLocks.has(path)) {
160
+ throw new Error(`User ${username} already has a lock on ${path}`);
161
+ }
162
+ const existingLock = await this.getLock(path);
163
+ if (existingLock) {
164
+ await this.addToQueue(userId, username, request);
165
+ throw new Error(`Path ${path} is already locked by ${existingLock.username}, added to queue`);
166
+ }
167
+ const lockId = this.generateId();
168
+ const lock = {
169
+ id: lockId,
170
+ type,
171
+ path,
172
+ userId,
173
+ username,
174
+ acquiredAt: Date.now(),
175
+ expiresAt: ttl ? Date.now() + ttl : undefined,
176
+ priority,
177
+ branch,
178
+ metadata
179
+ };
180
+ this.lockState.locks.set(lockId, lock);
181
+ this.lockState.userLocks.set(userId, userLocks.add(path));
182
+ return lock;
183
+ }
184
+ async releaseLock(lockId) {
185
+ const lock = this.lockState.locks.get(lockId);
186
+ if (!lock) {
187
+ throw new Error(`Lock ${lockId} not found`);
188
+ }
189
+ const userLocks = this.lockState.userLocks.get(lock.userId);
190
+ if (userLocks) {
191
+ userLocks.delete(lock.path);
192
+ if (userLocks.size === 0) {
193
+ this.lockState.userLocks.delete(lock.userId);
194
+ }
195
+ }
196
+ this.lockState.locks.delete(lockId);
197
+ await this.processQueue(lock.path);
198
+ }
199
+ async releaseUserLocks(userId) {
200
+ const userLocks = this.lockState.userLocks.get(userId);
201
+ if (!userLocks) {
202
+ return;
203
+ }
204
+ const lockIds = Array.from(this.lockState.locks.entries()).filter(([, lock]) => lock.userId === userId).map(([lockId]) => lockId);
205
+ for (const lockId of lockIds) {
206
+ await this.releaseLock(lockId);
207
+ }
208
+ }
209
+ async getLock(path) {
210
+ for (const lock of this.lockState.locks.values()) {
211
+ if (lock.path === path) {
212
+ return lock;
213
+ }
214
+ }
215
+ return null;
216
+ }
217
+ async getUserLocks(userId) {
218
+ return Array.from(this.lockState.locks.values()).filter((lock) => lock.userId === userId);
219
+ }
220
+ async getAllLocks() {
221
+ return Array.from(this.lockState.locks.values());
222
+ }
223
+ async isLocked(path) {
224
+ return await this.getLock(path) !== null;
225
+ }
226
+ async getQueueLength(path) {
227
+ const queue = this.lockState.queue.get(path);
228
+ return queue ? queue.length : 0;
229
+ }
230
+ async extendLock(lockId, ttl) {
231
+ const lock = this.lockState.locks.get(lockId);
232
+ if (!lock) {
233
+ throw new Error(`Lock ${lockId} not found`);
234
+ }
235
+ lock.expiresAt = Date.now() + ttl;
236
+ return lock;
237
+ }
238
+ async addToQueue(userId, username, request) {
239
+ const { path } = request;
240
+ const queue = this.lockState.queue.get(path) || [];
241
+ const queueItem = {
242
+ ...request,
243
+ userId,
244
+ username,
245
+ requestedAt: Date.now(),
246
+ resolve: () => {},
247
+ reject: () => {}
248
+ };
249
+ queue.push(queueItem);
250
+ this.lockState.queue.set(path, queue);
251
+ }
252
+ async removeFromQueue(path, userId) {
253
+ const queue = this.lockState.queue.get(path);
254
+ if (!queue) {
255
+ return;
256
+ }
257
+ const index = queue.findIndex((item) => item.userId === userId);
258
+ if (index !== -1) {
259
+ queue.splice(index, 1);
260
+ if (queue.length === 0) {
261
+ this.lockState.queue.delete(path);
262
+ }
263
+ }
264
+ }
265
+ generateId() {
266
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
267
+ }
268
+ async processQueue(path) {
269
+ const queue = this.lockState.queue.get(path);
270
+ if (!queue || queue.length === 0) {
271
+ return;
272
+ }
273
+ const item = queue.shift();
274
+ if (queue.length === 0) {
275
+ this.lockState.queue.delete(path);
276
+ }
277
+ try {
278
+ const lock = await this.acquireLock(item.userId, item.username, {
279
+ path: item.path,
280
+ type: item.type,
281
+ priority: item.priority,
282
+ ttl: item.ttl,
283
+ branch: item.branch,
284
+ metadata: item.metadata
285
+ });
286
+ item.resolve(lock);
287
+ } catch (error) {
288
+ item.reject(error);
289
+ }
290
+ }
291
+ }
292
+ // src/adapters/mock/MockTransportAdapter.ts
293
+ class MockTransportAdapter {
294
+ state = "disconnected";
295
+ messageHandlers = new Set;
296
+ errorHandlers = new Set;
297
+ closeHandlers = new Set;
298
+ messageQueue = [];
299
+ simulateLatency = 0;
300
+ shouldFailConnection = false;
301
+ connectedUrl = null;
302
+ constructor(options) {
303
+ this.simulateLatency = options?.simulateLatency ?? 0;
304
+ this.shouldFailConnection = options?.shouldFailConnection ?? false;
305
+ }
306
+ async connect(url, _options) {
307
+ if (this.shouldFailConnection) {
308
+ this.state = "disconnected";
309
+ const error = new Error("Mock connection failed");
310
+ this.errorHandlers.forEach((handler) => handler(error));
311
+ throw error;
312
+ }
313
+ this.state = "connecting";
314
+ this.connectedUrl = url;
315
+ if (this.simulateLatency > 0) {
316
+ await new Promise((resolve) => setTimeout(resolve, this.simulateLatency));
317
+ }
318
+ this.state = "connected";
319
+ }
320
+ async disconnect() {
321
+ if (this.state === "disconnected")
322
+ return;
323
+ this.state = "disconnecting";
324
+ if (this.simulateLatency > 0) {
325
+ await new Promise((resolve) => setTimeout(resolve, this.simulateLatency / 2));
326
+ }
327
+ this.state = "disconnected";
328
+ this.connectedUrl = null;
329
+ this.closeHandlers.forEach((handler) => handler(1000, "Normal closure"));
330
+ this.messageQueue = [];
331
+ }
332
+ async send(message) {
333
+ if (this.state !== "connected") {
334
+ throw new Error("Not connected");
335
+ }
336
+ if (this.simulateLatency > 0) {
337
+ await new Promise((resolve) => setTimeout(resolve, this.simulateLatency));
338
+ }
339
+ this.messageQueue.push(message);
340
+ }
341
+ onMessage(handler) {
342
+ this.messageHandlers.add(handler);
343
+ }
344
+ onError(handler) {
345
+ this.errorHandlers.add(handler);
346
+ }
347
+ onClose(handler) {
348
+ this.closeHandlers.add(handler);
349
+ }
350
+ getState() {
351
+ return this.state;
352
+ }
353
+ isConnected() {
354
+ return this.state === "connected";
355
+ }
356
+ simulateMessage(message) {
357
+ if (this.state !== "connected") {
358
+ throw new Error("Cannot simulate message when not connected");
359
+ }
360
+ this.messageHandlers.forEach((handler) => handler(message));
361
+ }
362
+ simulateError(error) {
363
+ this.errorHandlers.forEach((handler) => handler(error));
364
+ }
365
+ simulateClose(code, reason) {
366
+ this.state = "disconnected";
367
+ this.closeHandlers.forEach((handler) => handler(code, reason));
368
+ }
369
+ getMessageQueue() {
370
+ return [...this.messageQueue];
371
+ }
372
+ clearMessageQueue() {
373
+ this.messageQueue = [];
374
+ }
375
+ getConnectedUrl() {
376
+ return this.connectedUrl;
377
+ }
378
+ }
379
+ // src/adapters/mock/MockStorageAdapter.ts
380
+ class MockStorageAdapter {
381
+ storage = new Map;
382
+ simulateLatency = 0;
383
+ shouldFailOperations = false;
384
+ constructor(options) {
385
+ this.simulateLatency = options?.simulateLatency ?? 0;
386
+ this.shouldFailOperations = options?.shouldFailOperations ?? false;
387
+ }
388
+ async get(key) {
389
+ await this.simulateDelay();
390
+ if (this.shouldFailOperations) {
391
+ throw new Error("Mock storage operation failed");
392
+ }
393
+ const item = this.storage.get(key);
394
+ if (!item)
395
+ return null;
396
+ if (item.expiresAt && item.expiresAt < Date.now()) {
397
+ this.storage.delete(key);
398
+ return null;
399
+ }
400
+ return item.value;
401
+ }
402
+ async set(key, value, ttl) {
403
+ await this.simulateDelay();
404
+ if (this.shouldFailOperations) {
405
+ throw new Error("Mock storage operation failed");
406
+ }
407
+ const item = {
408
+ value,
409
+ expiresAt: ttl ? Date.now() + ttl : undefined
410
+ };
411
+ this.storage.set(key, item);
412
+ }
413
+ async delete(key) {
414
+ await this.simulateDelay();
415
+ if (this.shouldFailOperations) {
416
+ throw new Error("Mock storage operation failed");
417
+ }
418
+ this.storage.delete(key);
419
+ }
420
+ async exists(key) {
421
+ await this.simulateDelay();
422
+ if (this.shouldFailOperations) {
423
+ throw new Error("Mock storage operation failed");
424
+ }
425
+ const item = this.storage.get(key);
426
+ if (!item)
427
+ return false;
428
+ if (item.expiresAt && item.expiresAt < Date.now()) {
429
+ this.storage.delete(key);
430
+ return false;
431
+ }
432
+ return true;
433
+ }
434
+ async scan(pattern) {
435
+ await this.simulateDelay();
436
+ if (this.shouldFailOperations) {
437
+ throw new Error("Mock storage operation failed");
438
+ }
439
+ const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".").replace(/\[!/g, "[^").replace(/\[/g, "[").replace(/\]/g, "]");
440
+ const regex = new RegExp(`^${regexPattern}$`);
441
+ const keys = [];
442
+ for (const [key, item] of this.storage.entries()) {
443
+ if (item.expiresAt && item.expiresAt < Date.now()) {
444
+ this.storage.delete(key);
445
+ continue;
446
+ }
447
+ if (regex.test(key)) {
448
+ keys.push(key);
449
+ }
450
+ }
451
+ return keys;
452
+ }
453
+ async clear() {
454
+ await this.simulateDelay();
455
+ if (this.shouldFailOperations) {
456
+ throw new Error("Mock storage operation failed");
457
+ }
458
+ this.storage.clear();
459
+ }
460
+ async transaction(operations) {
461
+ await this.simulateDelay();
462
+ if (this.shouldFailOperations) {
463
+ throw new Error("Mock storage operation failed");
464
+ }
465
+ const results = [];
466
+ const rollback = [];
467
+ try {
468
+ for (const op of operations) {
469
+ switch (op.type) {
470
+ case "get": {
471
+ const value = await this.get(op.key);
472
+ results.push(value);
473
+ break;
474
+ }
475
+ case "set": {
476
+ const oldValue = await this.get(op.key);
477
+ await this.set(op.key, op.value, op.ttl);
478
+ rollback.push(() => {
479
+ if (oldValue !== null) {
480
+ this.storage.set(op.key, { value: oldValue });
481
+ } else {
482
+ this.storage.delete(op.key);
483
+ }
484
+ });
485
+ results.push(true);
486
+ break;
487
+ }
488
+ case "delete": {
489
+ const oldValue = await this.get(op.key);
490
+ await this.delete(op.key);
491
+ rollback.push(() => {
492
+ if (oldValue !== null) {
493
+ this.storage.set(op.key, { value: oldValue });
494
+ }
495
+ });
496
+ results.push(true);
497
+ break;
498
+ }
499
+ case "exists": {
500
+ const exists = await this.exists(op.key);
501
+ results.push(exists);
502
+ break;
503
+ }
504
+ }
505
+ }
506
+ return results;
507
+ } catch (error) {
508
+ for (const rollbackFn of rollback.reverse()) {
509
+ rollbackFn();
510
+ }
511
+ throw error;
512
+ }
513
+ }
514
+ async simulateDelay() {
515
+ if (this.simulateLatency > 0) {
516
+ await new Promise((resolve) => setTimeout(resolve, this.simulateLatency));
517
+ }
518
+ }
519
+ getSize() {
520
+ return this.storage.size;
521
+ }
522
+ getRawStorage() {
523
+ return new Map(this.storage);
524
+ }
525
+ setFailOperations(fail) {
526
+ this.shouldFailOperations = fail;
527
+ }
528
+ }
529
+ // src/adapters/mock/MockAuthAdapter.ts
530
+ class MockAuthAdapter {
531
+ tokens = new Map;
532
+ revokedTokens = new Set;
533
+ users = new Map;
534
+ simulateLatency = 0;
535
+ shouldFailAuth = false;
536
+ tokenCounter = 0;
537
+ constructor(options) {
538
+ this.simulateLatency = options?.simulateLatency ?? 0;
539
+ this.shouldFailAuth = options?.shouldFailAuth ?? false;
540
+ this.addUser("testuser", "password123", {
541
+ userId: "user-1",
542
+ email: "test@example.com",
543
+ username: "testuser",
544
+ permissions: ["read", "write"]
545
+ });
546
+ this.addUser("admin", "admin123", {
547
+ userId: "user-admin",
548
+ email: "admin@example.com",
549
+ username: "admin",
550
+ permissions: ["read", "write", "admin"]
551
+ });
552
+ }
553
+ async validateToken(token) {
554
+ await this.simulateDelay();
555
+ if (this.shouldFailAuth) {
556
+ throw new Error("Mock auth validation failed");
557
+ }
558
+ if (this.revokedTokens.has(token)) {
559
+ throw new Error("Token has been revoked");
560
+ }
561
+ const payload = this.tokens.get(token);
562
+ if (!payload) {
563
+ throw new Error("Invalid token");
564
+ }
565
+ if (payload.expiresAt && payload.expiresAt < Date.now()) {
566
+ this.tokens.delete(token);
567
+ throw new Error("Token expired");
568
+ }
569
+ return payload;
570
+ }
571
+ async generateToken(payload) {
572
+ await this.simulateDelay();
573
+ if (this.shouldFailAuth) {
574
+ throw new Error("Mock token generation failed");
575
+ }
576
+ const token = `mock-token-${++this.tokenCounter}`;
577
+ const expiresAt = payload.expiresAt ?? Date.now() + 3600000;
578
+ this.tokens.set(token, { ...payload, expiresAt });
579
+ return token;
580
+ }
581
+ async refreshToken(token) {
582
+ await this.simulateDelay();
583
+ if (this.shouldFailAuth) {
584
+ throw new Error("Mock token refresh failed");
585
+ }
586
+ const payload = await this.validateToken(token);
587
+ await this.revokeToken(token);
588
+ const newPayload = {
589
+ ...payload,
590
+ expiresAt: Date.now() + 3600000
591
+ };
592
+ return this.generateToken(newPayload);
593
+ }
594
+ async revokeToken(token) {
595
+ await this.simulateDelay();
596
+ if (this.shouldFailAuth) {
597
+ throw new Error("Mock token revocation failed");
598
+ }
599
+ this.tokens.delete(token);
600
+ this.revokedTokens.add(token);
601
+ }
602
+ async authenticate(credentials) {
603
+ await this.simulateDelay();
604
+ if (this.shouldFailAuth) {
605
+ throw new Error("Mock authentication failed");
606
+ }
607
+ switch (credentials.type) {
608
+ case "password": {
609
+ if (!credentials.username || !credentials.password) {
610
+ throw new Error("Username and password required");
611
+ }
612
+ const user = this.users.get(credentials.username);
613
+ if (!user || user.password !== credentials.password) {
614
+ throw new Error("Invalid credentials");
615
+ }
616
+ const token = await this.generateToken(user.payload);
617
+ const refreshToken = `refresh-${token}`;
618
+ this.tokens.set(refreshToken, user.payload);
619
+ return {
620
+ token,
621
+ refreshToken,
622
+ user: user.payload,
623
+ expiresIn: 3600
624
+ };
625
+ }
626
+ case "token": {
627
+ if (!credentials.token) {
628
+ throw new Error("Token required");
629
+ }
630
+ const payload = await this.validateToken(credentials.token);
631
+ return {
632
+ token: credentials.token,
633
+ user: payload,
634
+ expiresIn: 3600
635
+ };
636
+ }
637
+ case "oauth": {
638
+ if (!credentials.code) {
639
+ throw new Error("OAuth code required");
640
+ }
641
+ const payload = {
642
+ userId: `oauth-user-${Date.now()}`,
643
+ email: "oauth@example.com",
644
+ username: "oauthuser",
645
+ permissions: ["read", "write"]
646
+ };
647
+ const token = await this.generateToken(payload);
648
+ return {
649
+ token,
650
+ user: payload,
651
+ expiresIn: 3600
652
+ };
653
+ }
654
+ default:
655
+ throw new Error("Unsupported credential type");
656
+ }
657
+ }
658
+ async simulateDelay() {
659
+ if (this.simulateLatency > 0) {
660
+ await new Promise((resolve) => setTimeout(resolve, this.simulateLatency));
661
+ }
662
+ }
663
+ addUser(username, password, payload) {
664
+ this.users.set(username, { password, payload });
665
+ }
666
+ removeUser(username) {
667
+ this.users.delete(username);
668
+ }
669
+ getTokenCount() {
670
+ return this.tokens.size;
671
+ }
672
+ getRevokedTokenCount() {
673
+ return this.revokedTokens.size;
674
+ }
675
+ clearTokens() {
676
+ this.tokens.clear();
677
+ this.revokedTokens.clear();
678
+ }
679
+ setFailAuth(fail) {
680
+ this.shouldFailAuth = fail;
681
+ }
682
+ }
683
+ // src/client/BaseClient.ts
684
+ class BaseClient extends TypedEventEmitter {
685
+ transport;
686
+ auth;
687
+ storage;
688
+ config;
689
+ connectionState = "disconnected";
690
+ currentRoomId = null;
691
+ currentRoomState = null;
692
+ authToken = null;
693
+ userId = null;
694
+ reconnectAttempts = 0;
695
+ reconnectTimer = null;
696
+ lastConnectionUrl = null;
697
+ lastCredentials = null;
698
+ constructor(config) {
699
+ super();
700
+ this.config = config;
701
+ this.transport = config.transport;
702
+ this.auth = config.auth;
703
+ this.storage = config.storage;
704
+ this.transport.onMessage(this.handleMessage.bind(this));
705
+ this.transport.onError(this.handleError.bind(this));
706
+ this.transport.onClose(this.handleClose.bind(this));
707
+ }
708
+ async connect(url, credentials) {
709
+ if (this.connectionState !== "disconnected") {
710
+ throw new Error("Client is already connected or connecting");
711
+ }
712
+ this.connectionState = "connecting";
713
+ this.lastConnectionUrl = url;
714
+ this.lastCredentials = credentials || null;
715
+ try {
716
+ if (credentials && this.auth) {
717
+ const authResult = await this.auth.authenticate(credentials);
718
+ this.authToken = authResult.token;
719
+ this.userId = authResult.user.userId;
720
+ }
721
+ const options = {
722
+ url,
723
+ reconnect: false,
724
+ ...this.config.reconnection
725
+ };
726
+ await this.transport.connect(url, options);
727
+ this.connectionState = "connected";
728
+ await this.emit("connected", { url });
729
+ } catch (error) {
730
+ this.connectionState = "disconnected";
731
+ await this.emit("error", { error });
732
+ throw error;
733
+ }
734
+ }
735
+ async disconnect() {
736
+ if (this.connectionState === "disconnected") {
737
+ return;
738
+ }
739
+ this.connectionState = "disconnecting";
740
+ if (this.reconnectTimer) {
741
+ clearTimeout(this.reconnectTimer);
742
+ this.reconnectTimer = null;
743
+ }
744
+ if (this.currentRoomId) {
745
+ await this.leaveRoom();
746
+ }
747
+ try {
748
+ await this.transport.disconnect();
749
+ } finally {
750
+ this.connectionState = "disconnected";
751
+ this.currentRoomId = null;
752
+ this.currentRoomState = null;
753
+ this.authToken = null;
754
+ this.userId = null;
755
+ this.reconnectAttempts = 0;
756
+ await this.emit("disconnected", { code: 1000, reason: "Client disconnected" });
757
+ }
758
+ }
759
+ async reconnect() {
760
+ if (!this.lastConnectionUrl) {
761
+ throw new Error("No previous connection to reconnect to");
762
+ }
763
+ if (this.connectionState !== "disconnected") {
764
+ await this.disconnect();
765
+ }
766
+ await this.connect(this.lastConnectionUrl, this.lastCredentials || undefined);
767
+ }
768
+ async joinRoom(roomId) {
769
+ if (this.connectionState !== "connected") {
770
+ throw new Error("Client must be connected to join a room");
771
+ }
772
+ if (this.currentRoomId) {
773
+ await this.leaveRoom();
774
+ }
775
+ const message = {
776
+ id: this.generateId(),
777
+ type: "join_room",
778
+ payload: { roomId, token: this.authToken },
779
+ timestamp: Date.now()
780
+ };
781
+ await this.transport.send(message);
782
+ }
783
+ async leaveRoom() {
784
+ if (!this.currentRoomId) {
785
+ return;
786
+ }
787
+ const message = {
788
+ id: this.generateId(),
789
+ type: "leave_room",
790
+ payload: { roomId: this.currentRoomId },
791
+ timestamp: Date.now()
792
+ };
793
+ await this.transport.send(message);
794
+ this.currentRoomId = null;
795
+ this.currentRoomState = null;
796
+ }
797
+ async broadcast(event) {
798
+ if (this.connectionState !== "connected" || !this.currentRoomId) {
799
+ throw new Error("Client must be connected and in a room to broadcast events");
800
+ }
801
+ const message = {
802
+ id: this.generateId(),
803
+ type: "broadcast_event",
804
+ payload: { event, roomId: this.currentRoomId },
805
+ timestamp: Date.now()
806
+ };
807
+ await this.transport.send(message);
808
+ }
809
+ async requestLock(request) {
810
+ if (this.connectionState !== "connected" || !this.currentRoomId) {
811
+ throw new Error("Client must be connected and in a room to request locks");
812
+ }
813
+ const message = {
814
+ id: this.generateId(),
815
+ type: "request_lock",
816
+ payload: { request, roomId: this.currentRoomId },
817
+ timestamp: Date.now()
818
+ };
819
+ await this.transport.send(message);
820
+ }
821
+ async releaseLock(lockId) {
822
+ if (this.connectionState !== "connected" || !this.currentRoomId) {
823
+ throw new Error("Client must be connected and in a room to release locks");
824
+ }
825
+ const message = {
826
+ id: this.generateId(),
827
+ type: "release_lock",
828
+ payload: { lockId, roomId: this.currentRoomId },
829
+ timestamp: Date.now()
830
+ };
831
+ await this.transport.send(message);
832
+ }
833
+ getConnectionState() {
834
+ return this.connectionState;
835
+ }
836
+ getRoomState() {
837
+ return this.currentRoomState;
838
+ }
839
+ getPresence() {
840
+ return this.currentRoomState ? Array.from(this.currentRoomState.users.values()) : [];
841
+ }
842
+ getCurrentRoomId() {
843
+ return this.currentRoomId;
844
+ }
845
+ getUserId() {
846
+ return this.userId;
847
+ }
848
+ async handleMessage(message) {
849
+ try {
850
+ switch (message.type) {
851
+ case "room_joined":
852
+ await this.handleRoomJoined(message.payload);
853
+ break;
854
+ case "room_left":
855
+ await this.handleRoomLeft(message.payload);
856
+ break;
857
+ case "event_broadcast":
858
+ await this.handleEventBroadcast(message.payload);
859
+ break;
860
+ case "lock_acquired":
861
+ await this.handleLockAcquired(message.payload);
862
+ break;
863
+ case "lock_released":
864
+ await this.handleLockReleased(message.payload);
865
+ break;
866
+ case "lock_denied":
867
+ await this.handleLockDenied(message.payload);
868
+ break;
869
+ case "presence_updated":
870
+ await this.handlePresenceUpdated(message.payload);
871
+ break;
872
+ case "error":
873
+ await this.handleServerError(message.payload);
874
+ break;
875
+ }
876
+ } catch (error) {
877
+ await this.emit("error", { error });
878
+ }
879
+ }
880
+ async handleError(error) {
881
+ await this.emit("error", { error });
882
+ }
883
+ async handleClose(code, reason) {
884
+ const wasConnected = this.connectionState === "connected";
885
+ this.connectionState = "disconnected";
886
+ if (wasConnected) {
887
+ await this.emit("disconnected", { code, reason });
888
+ if (this.config.reconnection?.enabled && this.lastConnectionUrl) {
889
+ this.scheduleReconnect();
890
+ }
891
+ }
892
+ }
893
+ async handleRoomJoined(payload) {
894
+ this.currentRoomId = payload.roomId;
895
+ this.currentRoomState = payload.state;
896
+ await this.emit("room_joined", payload);
897
+ }
898
+ async handleRoomLeft(payload) {
899
+ if (this.currentRoomId === payload.roomId) {
900
+ this.currentRoomId = null;
901
+ this.currentRoomState = null;
902
+ }
903
+ await this.emit("room_left", payload);
904
+ }
905
+ async handleEventBroadcast(payload) {
906
+ await this.emit("event_received", payload);
907
+ }
908
+ async handleLockAcquired(payload) {
909
+ await this.emit("lock_acquired", payload);
910
+ }
911
+ async handleLockReleased(payload) {
912
+ await this.emit("lock_released", payload);
913
+ }
914
+ async handleLockDenied(payload) {
915
+ await this.emit("lock_denied", payload);
916
+ }
917
+ async handlePresenceUpdated(payload) {
918
+ if (this.currentRoomState) {
919
+ this.currentRoomState.users = new Map(payload.users.map((user) => [user.id, user]));
920
+ }
921
+ await this.emit("presence_updated", payload);
922
+ }
923
+ async handleServerError(payload) {
924
+ const error = new Error(payload.error);
925
+ await this.emit("error", { error });
926
+ }
927
+ async scheduleReconnect() {
928
+ if (!this.config.reconnection || this.reconnectAttempts >= this.config.reconnection.maxAttempts) {
929
+ return;
930
+ }
931
+ const delay = Math.min(this.config.reconnection.initialDelay * Math.pow(this.config.reconnection.backoffFactor, this.reconnectAttempts), this.config.reconnection.maxDelay);
932
+ this.reconnectAttempts++;
933
+ await this.emit("reconnecting", { attempt: this.reconnectAttempts, delay });
934
+ this.reconnectTimer = setTimeout(async () => {
935
+ try {
936
+ await this.reconnect();
937
+ this.reconnectAttempts = 0;
938
+ await this.emit("reconnected", { url: this.lastConnectionUrl });
939
+ } catch {
940
+ this.scheduleReconnect();
941
+ }
942
+ }, delay);
943
+ }
944
+ generateId() {
945
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
946
+ }
947
+ }
948
+ // src/client/ClientBuilder.ts
949
+ class ClientBuilder {
950
+ transport;
951
+ auth;
952
+ storage;
953
+ reconnection;
954
+ withTransport(transport) {
955
+ this.transport = transport;
956
+ return this;
957
+ }
958
+ withAuth(auth) {
959
+ this.auth = auth;
960
+ return this;
961
+ }
962
+ withStorage(storage) {
963
+ this.storage = storage;
964
+ return this;
965
+ }
966
+ withReconnection(config) {
967
+ this.reconnection = config;
968
+ return this;
969
+ }
970
+ build() {
971
+ if (!this.transport) {
972
+ throw new Error("Transport adapter is required");
973
+ }
974
+ const config = {
975
+ transport: this.transport,
976
+ auth: this.auth,
977
+ storage: this.storage,
978
+ reconnection: this.reconnection || {
979
+ enabled: true,
980
+ maxAttempts: 5,
981
+ initialDelay: 1000,
982
+ maxDelay: 30000,
983
+ backoffFactor: 2
984
+ }
985
+ };
986
+ return new BaseClient(config);
987
+ }
988
+ }
989
+ // src/server/BaseServer.ts
990
+ class BaseServer extends TypedEventEmitter {
991
+ transport;
992
+ auth;
993
+ storage;
994
+ roomManager;
995
+ lockManager;
996
+ config;
997
+ clients = new Map;
998
+ clientMessageHandlers = new Map;
999
+ running = false;
1000
+ constructor(config) {
1001
+ super();
1002
+ this.config = config;
1003
+ this.transport = config.transport;
1004
+ this.auth = config.auth;
1005
+ this.storage = config.storage;
1006
+ this.roomManager = config.roomManager;
1007
+ this.lockManager = config.lockManager;
1008
+ this.transport.onMessage(this.handleTransportMessage.bind(this));
1009
+ this.transport.onError(this.handleTransportError.bind(this));
1010
+ this.transport.onClose(this.handleTransportClose.bind(this));
1011
+ }
1012
+ async start(port) {
1013
+ if (this.running) {
1014
+ throw new Error("Server is already running");
1015
+ }
1016
+ try {
1017
+ await this.transport.connect(`ws://localhost:${port}`, { url: `ws://localhost:${port}` });
1018
+ this.running = true;
1019
+ await this.emit("started", { port });
1020
+ } catch (error) {
1021
+ await this.emit("error", { error, context: "server_start" });
1022
+ throw error;
1023
+ }
1024
+ }
1025
+ async stop() {
1026
+ if (!this.running) {
1027
+ return;
1028
+ }
1029
+ this.running = false;
1030
+ for (const [clientId] of Array.from(this.clients)) {
1031
+ await this.disconnectClient(clientId, "Server shutting down");
1032
+ }
1033
+ try {
1034
+ await this.transport.disconnect();
1035
+ } catch (error) {
1036
+ await this.emit("error", { error, context: "server_stop" });
1037
+ }
1038
+ this.clients.clear();
1039
+ this.clientMessageHandlers.clear();
1040
+ await this.emit("stopped", {});
1041
+ }
1042
+ async handleTransportMessage(message) {
1043
+ const payload = message.payload;
1044
+ const { clientId, ...clientMessage } = payload;
1045
+ const clientMsg = {
1046
+ id: message.id,
1047
+ type: message.type,
1048
+ payload: clientMessage,
1049
+ timestamp: message.timestamp
1050
+ };
1051
+ if (!clientId) {
1052
+ await this.emit("error", {
1053
+ error: new Error("Message missing clientId"),
1054
+ context: "transport_message"
1055
+ });
1056
+ return;
1057
+ }
1058
+ const handler = this.clientMessageHandlers.get(clientId);
1059
+ if (handler) {
1060
+ await handler(clientMsg);
1061
+ } else {
1062
+ await this.emit("error", {
1063
+ error: new Error(`No handler for client ${clientId}`),
1064
+ context: "transport_message"
1065
+ });
1066
+ }
1067
+ }
1068
+ async handleTransportError(error) {
1069
+ await this.emit("error", { error, context: "transport" });
1070
+ }
1071
+ async handleTransportClose(_code, _reason) {
1072
+ if (this.running) {
1073
+ await this.stop();
1074
+ }
1075
+ }
1076
+ async addClient(clientId) {
1077
+ const client = {
1078
+ id: clientId,
1079
+ userId: "",
1080
+ roomId: null,
1081
+ authenticated: false,
1082
+ connectedAt: Date.now()
1083
+ };
1084
+ this.clients.set(clientId, client);
1085
+ this.clientMessageHandlers.set(clientId, this.createClientMessageHandler(clientId));
1086
+ await this.emit("client_connected", { client });
1087
+ }
1088
+ async disconnectClient(clientId, reason) {
1089
+ const client = this.clients.get(clientId);
1090
+ if (!client) {
1091
+ return;
1092
+ }
1093
+ if (client.roomId) {
1094
+ await this.roomManager.leaveRoom(client.roomId, client.userId);
1095
+ await this.emit("client_left_room", { clientId, roomId: client.roomId });
1096
+ }
1097
+ await this.lockManager.releaseUserLocks(client.userId);
1098
+ this.clients.delete(clientId);
1099
+ this.clientMessageHandlers.delete(clientId);
1100
+ await this.emit("client_disconnected", { clientId, reason });
1101
+ }
1102
+ createClientMessageHandler(clientId) {
1103
+ return async (message) => {
1104
+ try {
1105
+ const client = this.clients.get(clientId);
1106
+ if (!client) {
1107
+ return;
1108
+ }
1109
+ switch (message.type) {
1110
+ case "authenticate":
1111
+ await this.handleAuthenticate(clientId, message.payload);
1112
+ break;
1113
+ case "join_room":
1114
+ await this.handleJoinRoom(clientId, message.payload);
1115
+ break;
1116
+ case "leave_room":
1117
+ await this.handleLeaveRoom(clientId);
1118
+ break;
1119
+ case "broadcast_event":
1120
+ await this.handleBroadcastEvent(clientId, message.payload);
1121
+ break;
1122
+ case "request_lock":
1123
+ await this.handleLockRequest(clientId, message.payload);
1124
+ break;
1125
+ case "release_lock":
1126
+ await this.handleLockRelease(clientId, message.payload);
1127
+ break;
1128
+ case "ping":
1129
+ await this.sendToClient(clientId, { type: "pong", timestamp: Date.now() });
1130
+ break;
1131
+ default:
1132
+ await this.sendToClient(clientId, {
1133
+ type: "error",
1134
+ error: `Unknown message type: ${message.type}`
1135
+ });
1136
+ }
1137
+ } catch (error) {
1138
+ await this.sendToClient(clientId, {
1139
+ type: "error",
1140
+ error: error.message
1141
+ });
1142
+ await this.emit("error", { error, context: `client_${clientId}` });
1143
+ }
1144
+ };
1145
+ }
1146
+ async handleAuthenticate(clientId, payload) {
1147
+ if (!this.auth) {
1148
+ await this.sendToClient(clientId, { type: "auth_result", success: false, error: "Authentication not configured" });
1149
+ return;
1150
+ }
1151
+ try {
1152
+ const tokenPayload = await this.auth.validateToken(payload.token);
1153
+ const client = this.clients.get(clientId);
1154
+ if (client) {
1155
+ client.userId = tokenPayload.userId;
1156
+ client.authenticated = true;
1157
+ await this.emit("client_authenticated", { clientId, userId: tokenPayload.userId });
1158
+ }
1159
+ await this.sendToClient(clientId, { type: "auth_result", success: true });
1160
+ } catch (error) {
1161
+ await this.sendToClient(clientId, { type: "auth_result", success: false, error: error.message });
1162
+ }
1163
+ }
1164
+ async handleJoinRoom(clientId, payload) {
1165
+ const client = this.clients.get(clientId);
1166
+ if (!client) {
1167
+ return;
1168
+ }
1169
+ if (payload.token && !client.authenticated) {
1170
+ await this.handleAuthenticate(clientId, { token: payload.token });
1171
+ }
1172
+ if (!client.authenticated) {
1173
+ await this.sendToClient(clientId, { type: "error", error: "Authentication required to join room" });
1174
+ return;
1175
+ }
1176
+ try {
1177
+ if (client.roomId) {
1178
+ await this.roomManager.leaveRoom(client.roomId, client.userId);
1179
+ await this.emit("client_left_room", { clientId, roomId: client.roomId });
1180
+ }
1181
+ let roomState = await this.roomManager.getRoomState(payload.roomId);
1182
+ if (!roomState) {
1183
+ const roomConfig = { ...this.config.defaultRoomConfig, id: payload.roomId };
1184
+ const room = await this.roomManager.createRoom(payload.roomId, roomConfig);
1185
+ roomState = await this.roomManager.getRoomState(payload.roomId);
1186
+ await this.emit("room_created", { room });
1187
+ }
1188
+ if (!roomState) {
1189
+ throw new Error("Failed to create or get room state");
1190
+ }
1191
+ const user = {
1192
+ id: client.userId,
1193
+ username: client.userId,
1194
+ status: "online",
1195
+ joinedAt: Date.now(),
1196
+ lastActivity: Date.now(),
1197
+ permissions: roomState.room.permissions || ["read", "write"]
1198
+ };
1199
+ await this.roomManager.joinRoom(payload.roomId, user);
1200
+ client.roomId = payload.roomId;
1201
+ await this.emit("client_joined_room", { clientId, roomId: payload.roomId });
1202
+ await this.sendToClient(clientId, {
1203
+ type: "room_joined",
1204
+ roomId: payload.roomId,
1205
+ state: roomState
1206
+ });
1207
+ const history = await this.roomManager.getEventHistory(payload.roomId, 50);
1208
+ if (history.length > 0) {
1209
+ await this.sendToClient(clientId, {
1210
+ type: "event_history",
1211
+ events: history
1212
+ });
1213
+ }
1214
+ } catch (error) {
1215
+ await this.sendToClient(clientId, { type: "error", error: error.message });
1216
+ }
1217
+ }
1218
+ async handleLeaveRoom(clientId) {
1219
+ const client = this.clients.get(clientId);
1220
+ if (!client || !client.roomId) {
1221
+ return;
1222
+ }
1223
+ const roomId = client.roomId;
1224
+ await this.roomManager.leaveRoom(roomId, client.userId);
1225
+ client.roomId = null;
1226
+ await this.emit("client_left_room", { clientId, roomId });
1227
+ await this.sendToClient(clientId, { type: "room_left", roomId });
1228
+ }
1229
+ async handleBroadcastEvent(clientId, payload) {
1230
+ const client = this.clients.get(clientId);
1231
+ if (!client || client.roomId !== payload.roomId) {
1232
+ await this.sendToClient(clientId, { type: "error", error: "Not in specified room" });
1233
+ return;
1234
+ }
1235
+ const enrichedEvent = {
1236
+ ...payload.event,
1237
+ metadata: {
1238
+ ...payload.event.metadata,
1239
+ userId: client.userId,
1240
+ timestamp: Date.now(),
1241
+ roomId: payload.roomId
1242
+ }
1243
+ };
1244
+ await this.roomManager.addEventToHistory(payload.roomId, enrichedEvent);
1245
+ await this.roomManager.broadcastToRoom(payload.roomId, enrichedEvent, client.userId);
1246
+ await this.emit("event_broadcast", {
1247
+ roomId: payload.roomId,
1248
+ event: enrichedEvent,
1249
+ fromClientId: clientId
1250
+ });
1251
+ }
1252
+ async handleLockRequest(clientId, payload) {
1253
+ const client = this.clients.get(clientId);
1254
+ if (!client || client.roomId !== payload.roomId) {
1255
+ await this.sendToClient(clientId, { type: "error", error: "Not in specified room" });
1256
+ return;
1257
+ }
1258
+ try {
1259
+ const lock = await this.lockManager.acquireLock(client.userId, client.userId, payload.request);
1260
+ await this.emit("lock_acquired", { lock, clientId });
1261
+ await this.sendToClient(clientId, { type: "lock_acquired", lock });
1262
+ await this.roomManager.broadcastToRoom(payload.roomId, {
1263
+ id: this.generateId(),
1264
+ type: "lock_status",
1265
+ timestamp: Date.now(),
1266
+ userId: client.userId,
1267
+ roomId: payload.roomId,
1268
+ data: { lock, action: "acquired" },
1269
+ metadata: { userId: client.userId, timestamp: Date.now(), roomId: payload.roomId }
1270
+ });
1271
+ } catch (error) {
1272
+ await this.sendToClient(clientId, { type: "lock_denied", request: payload.request, reason: error.message });
1273
+ }
1274
+ }
1275
+ async handleLockRelease(clientId, payload) {
1276
+ const client = this.clients.get(clientId);
1277
+ if (!client) {
1278
+ return;
1279
+ }
1280
+ try {
1281
+ await this.lockManager.releaseLock(payload.lockId);
1282
+ await this.emit("lock_released", { lockId: payload.lockId, clientId });
1283
+ await this.sendToClient(clientId, { type: "lock_released", lockId: payload.lockId });
1284
+ if (client.roomId) {
1285
+ await this.roomManager.broadcastToRoom(client.roomId, {
1286
+ id: this.generateId(),
1287
+ type: "lock_status",
1288
+ timestamp: Date.now(),
1289
+ userId: client.userId,
1290
+ roomId: client.roomId,
1291
+ data: { lockId: payload.lockId, action: "released" },
1292
+ metadata: { userId: client.userId, timestamp: Date.now(), roomId: client.roomId }
1293
+ });
1294
+ }
1295
+ } catch (error) {
1296
+ await this.sendToClient(clientId, { type: "error", error: error.message });
1297
+ }
1298
+ }
1299
+ async sendToClient(clientId, message) {
1300
+ const transportMessage = {
1301
+ id: this.generateId(),
1302
+ type: "server_message",
1303
+ payload: { clientId, ...message },
1304
+ timestamp: Date.now()
1305
+ };
1306
+ await this.transport.send(transportMessage);
1307
+ }
1308
+ generateId() {
1309
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
1310
+ }
1311
+ async createRoom(roomId, config) {
1312
+ const room = await this.roomManager.createRoom(roomId, config);
1313
+ await this.emit("room_created", { room });
1314
+ return room;
1315
+ }
1316
+ async deleteRoom(roomId) {
1317
+ await this.roomManager.deleteRoom(roomId);
1318
+ await this.emit("room_deleted", { roomId });
1319
+ }
1320
+ getConnectedClients() {
1321
+ return Array.from(this.clients.values());
1322
+ }
1323
+ getClient(clientId) {
1324
+ return this.clients.get(clientId) || null;
1325
+ }
1326
+ isRunning() {
1327
+ return this.running;
1328
+ }
1329
+ }
1330
+ // src/server/ServerBuilder.ts
1331
+ class ServerBuilder {
1332
+ transport;
1333
+ auth;
1334
+ storage;
1335
+ roomManager;
1336
+ lockManager;
1337
+ defaultRoomConfig;
1338
+ withTransport(transport) {
1339
+ this.transport = transport;
1340
+ return this;
1341
+ }
1342
+ withAuth(auth) {
1343
+ this.auth = auth;
1344
+ return this;
1345
+ }
1346
+ withStorage(storage) {
1347
+ this.storage = storage;
1348
+ return this;
1349
+ }
1350
+ withRoomManager(roomManager) {
1351
+ this.roomManager = roomManager;
1352
+ return this;
1353
+ }
1354
+ withLockManager(lockManager) {
1355
+ this.lockManager = lockManager;
1356
+ return this;
1357
+ }
1358
+ withDefaultRoomConfig(config) {
1359
+ this.defaultRoomConfig = config;
1360
+ return this;
1361
+ }
1362
+ build() {
1363
+ if (!this.transport) {
1364
+ throw new Error("Transport adapter is required");
1365
+ }
1366
+ if (!this.roomManager) {
1367
+ throw new Error("Room manager is required");
1368
+ }
1369
+ if (!this.lockManager) {
1370
+ throw new Error("Lock manager is required");
1371
+ }
1372
+ const config = {
1373
+ transport: this.transport,
1374
+ auth: this.auth,
1375
+ storage: this.storage,
1376
+ roomManager: this.roomManager,
1377
+ lockManager: this.lockManager,
1378
+ defaultRoomConfig: this.defaultRoomConfig || {
1379
+ maxUsers: 50,
1380
+ maxHistory: 100,
1381
+ permissions: ["read", "write"]
1382
+ }
1383
+ };
1384
+ return new BaseServer(config);
1385
+ }
1386
+ }
1387
+ export {
1388
+ TypedEventEmitter,
1389
+ ServerBuilder,
1390
+ RoomManager,
1391
+ MockTransportAdapter,
1392
+ MockStorageAdapter,
1393
+ MockAuthAdapter,
1394
+ LockManager,
1395
+ DefaultRoomManager,
1396
+ DefaultLockManager,
1397
+ ClientBuilder,
1398
+ BaseServer,
1399
+ BaseClient
1400
+ };
1401
+
1402
+ //# debugId=FFED7FD7481F9AA464756E2164756E21