@mesh-kit/server 2.0.5 → 2.0.6

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.d.mts CHANGED
@@ -3,9 +3,9 @@ export { ServerOptions } from 'ws';
3
3
  import { LogLevel, Status, Command } from '@mesh-kit/shared';
4
4
  import { EventEmitter } from 'node:events';
5
5
  import { IncomingMessage } from 'node:http';
6
- import Redis$1, { RedisOptions, Redis } from 'ioredis';
7
- import { EventEmitter as EventEmitter$1 } from 'events';
6
+ import Redis, { RedisOptions, Redis as Redis$1 } from 'ioredis';
8
7
  import { Operation } from 'fast-json-patch';
8
+ import { EventEmitter as EventEmitter$1 } from 'events';
9
9
 
10
10
  declare class Latency {
11
11
  start: number;
@@ -112,6 +112,11 @@ interface PostgreSQLAdapterOptions extends PersistenceAdapterOptions {
112
112
  max?: number;
113
113
  }
114
114
 
115
+ interface AuthenticationError {
116
+ code?: number;
117
+ message?: string;
118
+ }
119
+ type AuthenticateConnectionFn = (req: IncomingMessage) => Promise<any> | any;
115
120
  type SocketMiddleware = (context: MeshContext<any>) => any | Promise<any>;
116
121
  interface MeshServerOptions extends ServerOptions {
117
122
  /**
@@ -172,6 +177,31 @@ interface MeshServerOptions extends ServerOptions {
172
177
  * @default "sqlite"
173
178
  */
174
179
  persistenceAdapter?: "sqlite" | "postgres";
180
+ /**
181
+ * Called during WebSocket upgrade to authenticate the connection.
182
+ * Receives the raw HTTP request with headers and cookies.
183
+ *
184
+ * Return any truthy value to accept the connection - the returned data
185
+ * will be automatically stored as the connection's initial metadata.
186
+ *
187
+ * Throw an error or return null/undefined to reject with 401 Unauthorized.
188
+ * Throw an object with { code, message } for custom HTTP status codes.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * authenticateConnection: async (req) => {
193
+ * const cookies = parseCookie(req.headers.cookie || "");
194
+ * const token = cookies["auth_token"];
195
+ *
196
+ * const user = await validateToken(token);
197
+ * if (!user) throw { code: 401, message: "Invalid token" };
198
+ *
199
+ * // returned data becomes connection metadata
200
+ * return { userId: user.id, email: user.email };
201
+ * }
202
+ * ```
203
+ */
204
+ authenticateConnection?: AuthenticateConnectionFn;
175
205
  }
176
206
  type ChannelPattern$1 = string | RegExp;
177
207
 
@@ -195,230 +225,10 @@ declare class Connection extends EventEmitter {
195
225
  close(): Promise<boolean>;
196
226
  }
197
227
 
198
- declare class RecordManager {
199
- private redis;
200
- private recordUpdateCallbacks;
201
- private recordRemovedCallbacks;
202
- private server;
203
- constructor(options: {
204
- redis: Redis;
205
- server: MeshServer;
206
- });
207
- /**
208
- * Gets the server instance associated with this record manager
209
- */
210
- getServer(): MeshServer;
211
- /**
212
- * Gets the Redis instance used by this record manager
213
- * This is used by the persistence manager to restore records
214
- */
215
- getRedis(): Redis;
216
- recordKey(recordId: string): string;
217
- recordVersionKey(recordId: string): string;
218
- /**
219
- * Retrieves a record from Redis by its unique identifier. Attempts to parse
220
- * the stored data as JSON before returning. If the record does not exist,
221
- * returns null.
222
- *
223
- * @param {string} recordId - The unique identifier of the record to retrieve.
224
- * @returns {Promise<any | null>} A promise that resolves to the parsed record object,
225
- * or null if the record does not exist.
226
- * @throws {SyntaxError} If the stored data is not valid JSON and cannot be parsed.
227
- * @throws {Error} If an error occurs during the Redis operation.
228
- */
229
- getRecord(recordId: string): Promise<any | null>;
230
- /**
231
- * Retrieves the version number associated with the specified record ID from Redis.
232
- * If no version is found, returns 0.
233
- *
234
- * @param {string} recordId - The unique identifier for the record whose version is to be retrieved.
235
- * @returns {Promise<number>} A promise that resolves to the version number of the record. Returns 0 if not found.
236
- * @throws {Error} If there is an issue communicating with Redis or parsing the version.
237
- */
238
- getVersion(recordId: string): Promise<number>;
239
- /**
240
- * Retrieves a record and its associated version from Redis.
241
- * Fetches both the record data and its version by their respective keys.
242
- *
243
- * @param {string} recordId - The unique identifier for the record to retrieve.
244
- * @returns {Promise<{ record: any | null; version: number }>}
245
- * A promise that resolves to an object containing the parsed record (or null if not found)
246
- * and its version number (0 if version data is not found or invalid).
247
- * @throws {Error} If there is a Redis error or if JSON parsing fails for the record data.
248
- */
249
- getRecordAndVersion(recordId: string): Promise<{
250
- record: any | null;
251
- version: number;
252
- }>;
253
- /**
254
- * Publishes an update to a record by computing and applying a JSON Patch,
255
- * incrementing the version, and persisting the updated value and version in Redis.
256
- * If there are no changes between the old and new value, returns null.
257
- *
258
- * @param {string} recordId - The unique identifier of the record to update.
259
- * @param {any} newValue - The new value to set for the record, or partial value when using merge strategy.
260
- * @param {"replace" | "merge" | "deepMerge"} [strategy="replace"] - Update strategy: "replace" (default) replaces the entire record, "merge" merges with existing object properties, "deepMerge" recursively merges nested objects.
261
- * @returns {Promise<{ patch: Operation[]; version: number; finalValue: any } | null>}
262
- * A promise resolving to an object containing the JSON Patch operations, new version number, and final merged value,
263
- * or null if there were no changes to publish.
264
- * @throws {Error} If there is a failure reading or writing to Redis, or during patch computation, the promise will be rejected with the error.
265
- */
266
- publishUpdate(recordId: string, newValue: any, strategy?: "replace" | "merge" | "deepMerge"): Promise<{
267
- patch: Operation[];
268
- version: number;
269
- finalValue: any;
270
- } | null>;
271
- /**
272
- * Deletes a record and its associated version from Redis storage.
273
- *
274
- * @param {string} recordId - The unique identifier of the record to be deleted.
275
- * @returns {Promise<{ version: number }|null>} A promise that resolves to the final version of the deleted record, or null if the record didn't exist.
276
- * @throws {Error} If an error occurs during the Redis pipeline execution, the promise will be rejected with the error.
277
- */
278
- deleteRecord(recordId: string): Promise<{
279
- version: number;
280
- } | null>;
281
- /**
282
- * Registers a callback function to be called when a record is updated.
283
- *
284
- * @param {(data: { recordId: string; value: any }) => Promise<void> | void} callback - The callback function to execute when a record is updated.
285
- * @returns {() => void} A function that, when called, will unregister the callback.
286
- */
287
- onRecordUpdate(callback: (data: {
288
- recordId: string;
289
- value: any;
290
- }) => Promise<void> | void): () => void;
291
- /**
292
- * Registers a callback function to be called when a record is removed.
293
- *
294
- * @param {(data: { recordId: string; value: any }) => Promise<void> | void} callback - The callback function to execute when a record is removed.
295
- * @returns {() => void} A function that, when called, will unregister the callback.
296
- */
297
- onRecordRemoved(callback: (data: {
298
- recordId: string;
299
- value: any;
300
- }) => Promise<void> | void): () => void;
301
- }
302
-
303
- type RecordPersistencePattern = string | RegExp | {
304
- writePattern: string | RegExp;
305
- restorePattern: string | RegExp;
306
- };
307
- declare class PersistenceManager extends EventEmitter$1 {
308
- private defaultAdapter;
309
- private channelPatterns;
310
- private recordPatterns;
311
- private messageBuffer;
312
- private recordBuffer;
313
- private flushTimers;
314
- private recordFlushTimer;
315
- private isShuttingDown;
316
- private initialized;
317
- private recordManager;
318
- private pendingRecordUpdates;
319
- private messageStream;
320
- constructor(options: {
321
- defaultAdapterOptions?: any;
322
- adapterType?: "sqlite" | "postgres";
323
- });
324
- /**
325
- * Sets the record manager reference for record restoration
326
- * @param recordManager The record manager instance
327
- */
328
- setRecordManager(recordManager: RecordManager): void;
329
- /**
330
- * Waits until the persistence manager is fully ready and initialized.
331
- *
332
- * @returns {Promise<void>} A promise that resolves when persistence is ready.
333
- */
334
- ready(): Promise<void>;
335
- /**
336
- * Processes any record updates that were buffered during initialization
337
- */
338
- private processPendingRecordUpdates;
339
- initialize(): Promise<void>;
340
- /**
341
- * Restores persisted records from storage into Redis on startup
342
- */
343
- restorePersistedRecords(): Promise<void>;
344
- /**
345
- * Handle a message received from the internal message stream.
346
- */
347
- private handleStreamMessage;
348
- /**
349
- * Enable persistence for channels matching the given pattern.
350
- * @param pattern string or regexp pattern to match channel names
351
- * @param options persistence options
352
- */
353
- enableChannelPersistence(pattern: string | RegExp, options?: ChannelPersistenceOptions): void;
354
- /**
355
- * Enable persistence for records matching the given pattern.
356
- * @param pattern string, regexp, or object with writePattern/restorePattern
357
- * @param options persistence options
358
- */
359
- enableRecordPersistence(pattern: RecordPersistencePattern, options?: RecordPersistenceOptions): void;
360
- /**
361
- * Check if a channel has persistence enabled and return its options.
362
- * @param channel channel name to check
363
- * @returns the persistence options if enabled, undefined otherwise
364
- */
365
- getChannelPersistenceOptions(channel: string): Required<ChannelPersistenceOptions> | undefined;
366
- /**
367
- * Check if a record has persistence enabled and return its options.
368
- * @param recordId record ID to check
369
- * @returns the persistence options if enabled, undefined otherwise
370
- */
371
- getRecordPersistenceOptions(recordId: string): Required<RecordPersistenceOptions> | undefined;
372
- /**
373
- * Handle an incoming message for potential persistence.
374
- * @param channel channel the message was published to
375
- * @param message the message content
376
- * @param instanceId id of the server instance
377
- */
378
- handleChannelMessage(channel: string, message: string, instanceId: string, timestamp?: number): void;
379
- /**
380
- * Flush buffered messages for a specific channel to its adapter.
381
- * @param channel channel to flush
382
- */
383
- private flushChannel;
384
- /**
385
- * Flush all buffered messages across all channels.
386
- */
387
- flushAll(): Promise<void>;
388
- /**
389
- * Get persisted messages for a channel.
390
- * @param channel channel to get messages for
391
- * @param since optional cursor (timestamp or message id) to retrieve messages after
392
- * @param limit maximum number of messages to retrieve
393
- */
394
- getMessages(channel: string, since?: string | number, limit?: number): Promise<PersistedMessage[]>;
395
- /**
396
- * Handles a record update for potential persistence
397
- * @param recordId ID of the record
398
- * @param value record value (will be stringified)
399
- * @param version record version
400
- */
401
- handleRecordUpdate(recordId: string, value: any, version: number): void;
402
- /**
403
- * Flush all buffered records to storage
404
- */
405
- flushRecords(): Promise<void>;
406
- /**
407
- * Retrieve persisted records matching a pattern
408
- * @param pattern pattern to match record IDs
409
- * @returns array of persisted records
410
- */
411
- getPersistedRecords(pattern: string): Promise<PersistedRecord[]>;
412
- /**
413
- * Shutdown the persistence manager, flushing pending messages and closing adapters.
414
- */
415
- shutdown(): Promise<void>;
416
- }
417
-
418
228
  declare class RoomManager {
419
229
  private redis;
420
230
  constructor(options: {
421
- redis: Redis$1;
231
+ redis: Redis;
422
232
  });
423
233
  private roomKey;
424
234
  private connectionsRoomKey;
@@ -554,7 +364,7 @@ declare class ConnectionManager {
554
364
  private localConnections;
555
365
  private roomManager;
556
366
  constructor(options: {
557
- redis: Redis$1;
367
+ redis: Redis;
558
368
  instanceId: string;
559
369
  roomManager: RoomManager;
560
370
  });
@@ -638,21 +448,21 @@ declare class RedisManager {
638
448
  * @returns The Redis client
639
449
  * @throws Error if Redis is not initialized
640
450
  */
641
- get redis(): Redis;
451
+ get redis(): Redis$1;
642
452
  /**
643
453
  * Gets the Redis client for publishing
644
454
  *
645
455
  * @returns The publishing Redis client
646
456
  * @throws Error if Redis is not initialized
647
457
  */
648
- get pubClient(): Redis;
458
+ get pubClient(): Redis$1;
649
459
  /**
650
460
  * Gets the Redis client for subscribing
651
461
  *
652
462
  * @returns The subscribing Redis client
653
463
  * @throws Error if Redis is not initialized
654
464
  */
655
- get subClient(): Redis;
465
+ get subClient(): Redis$1;
656
466
  /**
657
467
  * Disconnects all Redis clients
658
468
  */
@@ -695,7 +505,7 @@ declare class PresenceManager {
695
505
  private roomTTLs;
696
506
  private defaultTTL;
697
507
  constructor(options: {
698
- redis: Redis;
508
+ redis: Redis$1;
699
509
  roomManager: RoomManager;
700
510
  redisManager: RedisManager;
701
511
  enableExpirationEvents?: boolean;
@@ -768,6 +578,226 @@ declare class PresenceManager {
768
578
  cleanup(): Promise<void>;
769
579
  }
770
580
 
581
+ declare class RecordManager {
582
+ private redis;
583
+ private recordUpdateCallbacks;
584
+ private recordRemovedCallbacks;
585
+ private server;
586
+ constructor(options: {
587
+ redis: Redis$1;
588
+ server: MeshServer;
589
+ });
590
+ /**
591
+ * Gets the server instance associated with this record manager
592
+ */
593
+ getServer(): MeshServer;
594
+ /**
595
+ * Gets the Redis instance used by this record manager
596
+ * This is used by the persistence manager to restore records
597
+ */
598
+ getRedis(): Redis$1;
599
+ recordKey(recordId: string): string;
600
+ recordVersionKey(recordId: string): string;
601
+ /**
602
+ * Retrieves a record from Redis by its unique identifier. Attempts to parse
603
+ * the stored data as JSON before returning. If the record does not exist,
604
+ * returns null.
605
+ *
606
+ * @param {string} recordId - The unique identifier of the record to retrieve.
607
+ * @returns {Promise<any | null>} A promise that resolves to the parsed record object,
608
+ * or null if the record does not exist.
609
+ * @throws {SyntaxError} If the stored data is not valid JSON and cannot be parsed.
610
+ * @throws {Error} If an error occurs during the Redis operation.
611
+ */
612
+ getRecord(recordId: string): Promise<any | null>;
613
+ /**
614
+ * Retrieves the version number associated with the specified record ID from Redis.
615
+ * If no version is found, returns 0.
616
+ *
617
+ * @param {string} recordId - The unique identifier for the record whose version is to be retrieved.
618
+ * @returns {Promise<number>} A promise that resolves to the version number of the record. Returns 0 if not found.
619
+ * @throws {Error} If there is an issue communicating with Redis or parsing the version.
620
+ */
621
+ getVersion(recordId: string): Promise<number>;
622
+ /**
623
+ * Retrieves a record and its associated version from Redis.
624
+ * Fetches both the record data and its version by their respective keys.
625
+ *
626
+ * @param {string} recordId - The unique identifier for the record to retrieve.
627
+ * @returns {Promise<{ record: any | null; version: number }>}
628
+ * A promise that resolves to an object containing the parsed record (or null if not found)
629
+ * and its version number (0 if version data is not found or invalid).
630
+ * @throws {Error} If there is a Redis error or if JSON parsing fails for the record data.
631
+ */
632
+ getRecordAndVersion(recordId: string): Promise<{
633
+ record: any | null;
634
+ version: number;
635
+ }>;
636
+ /**
637
+ * Publishes an update to a record by computing and applying a JSON Patch,
638
+ * incrementing the version, and persisting the updated value and version in Redis.
639
+ * If there are no changes between the old and new value, returns null.
640
+ *
641
+ * @param {string} recordId - The unique identifier of the record to update.
642
+ * @param {any} newValue - The new value to set for the record, or partial value when using merge strategy.
643
+ * @param {"replace" | "merge" | "deepMerge"} [strategy="replace"] - Update strategy: "replace" (default) replaces the entire record, "merge" merges with existing object properties, "deepMerge" recursively merges nested objects.
644
+ * @returns {Promise<{ patch: Operation[]; version: number; finalValue: any } | null>}
645
+ * A promise resolving to an object containing the JSON Patch operations, new version number, and final merged value,
646
+ * or null if there were no changes to publish.
647
+ * @throws {Error} If there is a failure reading or writing to Redis, or during patch computation, the promise will be rejected with the error.
648
+ */
649
+ publishUpdate(recordId: string, newValue: any, strategy?: "replace" | "merge" | "deepMerge"): Promise<{
650
+ patch: Operation[];
651
+ version: number;
652
+ finalValue: any;
653
+ } | null>;
654
+ /**
655
+ * Deletes a record and its associated version from Redis storage.
656
+ *
657
+ * @param {string} recordId - The unique identifier of the record to be deleted.
658
+ * @returns {Promise<{ version: number }|null>} A promise that resolves to the final version of the deleted record, or null if the record didn't exist.
659
+ * @throws {Error} If an error occurs during the Redis pipeline execution, the promise will be rejected with the error.
660
+ */
661
+ deleteRecord(recordId: string): Promise<{
662
+ version: number;
663
+ } | null>;
664
+ /**
665
+ * Registers a callback function to be called when a record is updated.
666
+ *
667
+ * @param {(data: { recordId: string; value: any }) => Promise<void> | void} callback - The callback function to execute when a record is updated.
668
+ * @returns {() => void} A function that, when called, will unregister the callback.
669
+ */
670
+ onRecordUpdate(callback: (data: {
671
+ recordId: string;
672
+ value: any;
673
+ }) => Promise<void> | void): () => void;
674
+ /**
675
+ * Registers a callback function to be called when a record is removed.
676
+ *
677
+ * @param {(data: { recordId: string; value: any }) => Promise<void> | void} callback - The callback function to execute when a record is removed.
678
+ * @returns {() => void} A function that, when called, will unregister the callback.
679
+ */
680
+ onRecordRemoved(callback: (data: {
681
+ recordId: string;
682
+ value: any;
683
+ }) => Promise<void> | void): () => void;
684
+ }
685
+
686
+ type RecordPersistencePattern = {
687
+ writePattern: string | RegExp;
688
+ restorePattern: string;
689
+ };
690
+ declare class PersistenceManager extends EventEmitter$1 {
691
+ private defaultAdapter;
692
+ private channelPatterns;
693
+ private recordPatterns;
694
+ private messageBuffer;
695
+ private recordBuffer;
696
+ private flushTimers;
697
+ private recordFlushTimer;
698
+ private isShuttingDown;
699
+ private initialized;
700
+ private recordManager;
701
+ private pendingRecordUpdates;
702
+ private messageStream;
703
+ constructor(options: {
704
+ defaultAdapterOptions?: any;
705
+ adapterType?: "sqlite" | "postgres";
706
+ });
707
+ /**
708
+ * Sets the record manager reference for record restoration
709
+ * @param recordManager The record manager instance
710
+ */
711
+ setRecordManager(recordManager: RecordManager): void;
712
+ /**
713
+ * Waits until the persistence manager is fully ready and initialized.
714
+ *
715
+ * @returns {Promise<void>} A promise that resolves when persistence is ready.
716
+ */
717
+ ready(): Promise<void>;
718
+ /**
719
+ * Processes any record updates that were buffered during initialization
720
+ */
721
+ private processPendingRecordUpdates;
722
+ initialize(): Promise<void>;
723
+ /**
724
+ * Restores persisted records from storage into Redis on startup
725
+ */
726
+ restorePersistedRecords(): Promise<void>;
727
+ /**
728
+ * Handle a message received from the internal message stream.
729
+ */
730
+ private handleStreamMessage;
731
+ /**
732
+ * Enable persistence for channels matching the given pattern.
733
+ * @param pattern string or regexp pattern to match channel names
734
+ * @param options persistence options
735
+ */
736
+ enableChannelPersistence(pattern: string | RegExp, options?: ChannelPersistenceOptions): void;
737
+ /**
738
+ * Enable persistence for records matching the given pattern.
739
+ * @param pattern object with writePattern (for runtime matching) and restorePattern (for database queries)
740
+ * @param options persistence options
741
+ */
742
+ enableRecordPersistence(pattern: RecordPersistencePattern, options?: RecordPersistenceOptions): void;
743
+ /**
744
+ * Check if a channel has persistence enabled and return its options.
745
+ * @param channel channel name to check
746
+ * @returns the persistence options if enabled, undefined otherwise
747
+ */
748
+ getChannelPersistenceOptions(channel: string): Required<ChannelPersistenceOptions> | undefined;
749
+ /**
750
+ * Check if a record has persistence enabled and return its options.
751
+ * @param recordId record ID to check
752
+ * @returns the persistence options if enabled, undefined otherwise
753
+ */
754
+ getRecordPersistenceOptions(recordId: string): Required<RecordPersistenceOptions> | undefined;
755
+ /**
756
+ * Handle an incoming message for potential persistence.
757
+ * @param channel channel the message was published to
758
+ * @param message the message content
759
+ * @param instanceId id of the server instance
760
+ */
761
+ handleChannelMessage(channel: string, message: string, instanceId: string, timestamp?: number): void;
762
+ /**
763
+ * Flush buffered messages for a specific channel to its adapter.
764
+ * @param channel channel to flush
765
+ */
766
+ private flushChannel;
767
+ /**
768
+ * Flush all buffered messages across all channels.
769
+ */
770
+ flushAll(): Promise<void>;
771
+ /**
772
+ * Get persisted messages for a channel.
773
+ * @param channel channel to get messages for
774
+ * @param since optional cursor (timestamp or message id) to retrieve messages after
775
+ * @param limit maximum number of messages to retrieve
776
+ */
777
+ getMessages(channel: string, since?: string | number, limit?: number): Promise<PersistedMessage[]>;
778
+ /**
779
+ * Handles a record update for potential persistence
780
+ * @param recordId ID of the record
781
+ * @param value record value (will be stringified)
782
+ * @param version record version
783
+ */
784
+ handleRecordUpdate(recordId: string, value: any, version: number): void;
785
+ /**
786
+ * Flush all buffered records to storage
787
+ */
788
+ flushRecords(): Promise<void>;
789
+ /**
790
+ * Retrieve persisted records matching a pattern
791
+ * @param pattern pattern to match record IDs
792
+ * @returns array of persisted records
793
+ */
794
+ getPersistedRecords(pattern: string): Promise<PersistedRecord[]>;
795
+ /**
796
+ * Shutdown the persistence manager, flushing pending messages and closing adapters.
797
+ */
798
+ shutdown(): Promise<void>;
799
+ }
800
+
771
801
  declare class MeshServer extends WebSocketServer {
772
802
  readonly instanceId: string;
773
803
  private redisManager;
@@ -779,6 +809,7 @@ declare class MeshServer extends WebSocketServer {
779
809
  private collectionManager;
780
810
  private broadcastManager;
781
811
  private persistenceManager;
812
+ private authenticateConnection?;
782
813
  roomManager: RoomManager;
783
814
  recordManager: RecordManager;
784
815
  connectionManager: ConnectionManager;
@@ -1130,4 +1161,4 @@ declare class MessageStream extends EventEmitter$1 {
1130
1161
  }) => void): void;
1131
1162
  }
1132
1163
 
1133
- export { type ChannelPattern$1 as ChannelPattern, Connection, ConnectionManager, MeshContext, MeshServer, type MeshServerOptions, MessageStream, type PersistenceAdapter, type PersistenceAdapterOptions, PersistenceManager, type PostgreSQLAdapterOptions, PostgreSQLPersistenceAdapter, PresenceManager, RecordManager, type RecordPersistencePattern, RoomManager, SQLitePersistenceAdapter, type SocketMiddleware };
1164
+ export { type AuthenticateConnectionFn, type AuthenticationError, type ChannelPattern$1 as ChannelPattern, Connection, ConnectionManager, MeshContext, MeshServer, type MeshServerOptions, MessageStream, type PersistenceAdapter, type PersistenceAdapterOptions, PersistenceManager, type PostgreSQLAdapterOptions, PostgreSQLPersistenceAdapter, PresenceManager, RecordManager, type RecordPersistencePattern, RoomManager, SQLitePersistenceAdapter, type SocketMiddleware };