document-drive 1.17.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -68,7 +68,7 @@ import {
68
68
  OperationError,
69
69
  SynchronizationUnitNotFoundError,
70
70
  } from "./error";
71
- import { ListenerManager } from "./listener/manager";
71
+ import { ListenerManager } from "./listener";
72
72
  import {
73
73
  CancelPullLoop,
74
74
  InternalTransmitter,
@@ -76,9 +76,9 @@ import {
76
76
  ITransmitter,
77
77
  PullResponderTransmitter,
78
78
  StrandUpdateSource,
79
+ SwitchboardPushTransmitter,
79
80
  } from "./listener/transmitter";
80
81
  import {
81
- AbstractDocumentDriveServer,
82
82
  AddOperationOptions,
83
83
  DefaultListenerManagerOptions,
84
84
  DocumentDriveServerOptions,
@@ -86,7 +86,9 @@ import {
86
86
  GetDocumentOptions,
87
87
  GetStrandsOptions,
88
88
  IBaseDocumentDriveServer,
89
+ IListenerManager,
89
90
  IOperationResult,
91
+ Listener,
90
92
  ListenerState,
91
93
  RemoteDriveAccessLevel,
92
94
  RemoteDriveOptions,
@@ -109,15 +111,47 @@ export * from "../read-mode";
109
111
 
110
112
  export const PULL_DRIVE_INTERVAL = 5000;
111
113
 
112
- export class BaseDocumentDriveServer
113
- extends AbstractDocumentDriveServer
114
- implements IBaseDocumentDriveServer
115
- {
114
+ interface ITransmitterFactory {
115
+ instance(
116
+ transmitterType: string,
117
+ listener: Listener,
118
+ driveServer: IBaseDocumentDriveServer,
119
+ ): ITransmitter;
120
+ }
121
+
122
+ export class TransmitterFactory implements ITransmitterFactory {
123
+ private readonly listenerManager: IListenerManager;
124
+
125
+ constructor(listenerManager: IListenerManager) {
126
+ this.listenerManager = listenerManager;
127
+ }
128
+
129
+ instance(
130
+ transmitterType: string,
131
+ listener: Listener,
132
+ driveServer: IBaseDocumentDriveServer,
133
+ ): ITransmitter {
134
+ switch (transmitterType) {
135
+ case "SwitchboardPush": {
136
+ return new SwitchboardPushTransmitter(listener.callInfo!.data!);
137
+ }
138
+ case "Internal": {
139
+ return new InternalTransmitter(listener, driveServer);
140
+ }
141
+ default: {
142
+ return new PullResponderTransmitter(listener, this.listenerManager);
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ export class BaseDocumentDriveServer implements IBaseDocumentDriveServer {
116
149
  private emitter = createNanoEvents<DriveEvents>();
117
150
  private cache: ICache;
118
151
  private documentModels: DocumentModel[];
119
152
  private storage: IDriveStorage;
120
- private listenerStateManager: ListenerManager;
153
+ private transmitterFactory: ITransmitterFactory;
154
+ private listenerManager: IListenerManager;
121
155
  private triggerMap = new Map<
122
156
  DocumentDriveState["id"],
123
157
  Map<Trigger["id"], CancelPullLoop>
@@ -138,7 +172,6 @@ export class BaseDocumentDriveServer
138
172
  queueManager: IQueueManager = new BaseQueueManager(),
139
173
  options?: DocumentDriveServerOptions,
140
174
  ) {
141
- super();
142
175
  this.options = {
143
176
  ...options,
144
177
  defaultDrives: {
@@ -154,11 +187,16 @@ export class BaseDocumentDriveServer
154
187
  : options.taskQueueMethod,
155
188
  };
156
189
 
157
- this.listenerStateManager = new ListenerManager(
190
+ // todo: pull this into the constructor -- there is a circular dependency right now
191
+ this.listenerManager = new ListenerManager(
158
192
  this,
159
193
  undefined,
160
- options?.listenerManager,
194
+ this.options.listenerManager,
161
195
  );
196
+
197
+ // todo: pull this into the constructor, depends on listenerManager
198
+ this.transmitterFactory = new TransmitterFactory(this.listenerManager);
199
+
162
200
  this.documentModels = documentModels;
163
201
  this.storage = storage;
164
202
  this.cache = cache;
@@ -491,7 +529,7 @@ export class BaseDocumentDriveServer
491
529
  this.getSynchronizationUnitsRevision(driveId, syncUnits)
492
530
  .then((syncUnitRevisions) => {
493
531
  for (const revision of syncUnitRevisions) {
494
- this.listenerStateManager
532
+ this.listenerManager
495
533
  .updateListenerRevision(
496
534
  pushListener.listenerId,
497
535
  driveId,
@@ -584,6 +622,8 @@ export class BaseDocumentDriveServer
584
622
  }
585
623
 
586
624
  private async _initialize() {
625
+ await this.listenerManager.initialize(this.handleListenerError);
626
+
587
627
  await this.queueManager.init(this.queueDelegate, (error) => {
588
628
  logger.error(`Error initializing queue manager`, error);
589
629
  errors.push(error);
@@ -608,22 +648,6 @@ export class BaseDocumentDriveServer
608
648
  await this.defaultDrivesManager.initializeDefaultRemoteDrives();
609
649
  }
610
650
 
611
- // if network connect comes back online
612
- // then triggers the listeners update
613
- if (typeof window !== "undefined") {
614
- window.addEventListener("online", () => {
615
- this.listenerStateManager
616
- .triggerUpdate(
617
- false,
618
- { type: "local" },
619
- this.handleListenerError.bind(this),
620
- )
621
- .catch((error) => {
622
- logger.error("Non handled error updating listeners", error);
623
- });
624
- });
625
- }
626
-
627
651
  return errors.length === 0 ? null : errors;
628
652
  }
629
653
 
@@ -635,7 +659,36 @@ export class BaseDocumentDriveServer
635
659
  await this.startSyncRemoteDrive(driveId);
636
660
  }
637
661
 
638
- await this.listenerStateManager.initDrive(drive);
662
+ for (const zodListener of drive.state.local.listeners) {
663
+ const transmitter = this.transmitterFactory.instance(
664
+ zodListener.callInfo?.transmitterType ?? "",
665
+ {
666
+ driveId,
667
+ listenerId: zodListener.listenerId,
668
+ block: zodListener.block,
669
+ filter: zodListener.filter,
670
+ system: zodListener.system,
671
+ label: zodListener.label || undefined,
672
+ callInfo: zodListener.callInfo || undefined,
673
+ },
674
+ this,
675
+ );
676
+
677
+ await this.listenerManager.setListener(driveId, {
678
+ block: zodListener.block,
679
+ driveId: drive.state.global.id,
680
+ filter: {
681
+ branch: zodListener.filter.branch ?? [],
682
+ documentId: zodListener.filter.documentId ?? [],
683
+ documentType: zodListener.filter.documentType,
684
+ scope: zodListener.filter.scope ?? [],
685
+ },
686
+ listenerId: zodListener.listenerId,
687
+ system: zodListener.system,
688
+ label: zodListener.label ?? "",
689
+ transmitter,
690
+ });
691
+ }
639
692
  }
640
693
 
641
694
  public async getSynchronizationUnits(
@@ -899,8 +952,8 @@ export class BaseDocumentDriveServer
899
952
  return [...this.documentModels];
900
953
  }
901
954
 
902
- async addDrive(drive: DriveInput): Promise<DocumentDriveDocument> {
903
- const id = drive.global.id || generateUUID();
955
+ async addDrive(input: DriveInput): Promise<DocumentDriveDocument> {
956
+ const id = input.global.id || generateUUID();
904
957
  if (!id) {
905
958
  throw new Error("Invalid Drive Id");
906
959
  }
@@ -911,13 +964,13 @@ export class BaseDocumentDriveServer
911
964
  }
912
965
 
913
966
  const document = utils.createDocument({
914
- state: drive,
967
+ state: input,
915
968
  });
916
969
 
917
970
  await this.storage.createDrive(id, document);
918
971
 
919
- if (drive.global.slug) {
920
- await this.cache.deleteDocument("drives-slug", drive.global.slug);
972
+ if (input.global.slug) {
973
+ await this.cache.deleteDocument("drives-slug", input.global.slug);
921
974
  }
922
975
 
923
976
  await this._initializeDrive(id);
@@ -966,13 +1019,13 @@ export class BaseDocumentDriveServer
966
1019
  }
967
1020
 
968
1021
  public async registerPullResponderTrigger(
969
- id: string,
1022
+ driveId: string,
970
1023
  url: string,
971
1024
  options: Pick<RemoteDriveOptions, "pullFilter" | "pullInterval">,
972
1025
  ) {
973
1026
  const pullTrigger =
974
1027
  await PullResponderTransmitter.createPullResponderTrigger(
975
- id,
1028
+ driveId,
976
1029
  url,
977
1030
  options,
978
1031
  );
@@ -980,12 +1033,12 @@ export class BaseDocumentDriveServer
980
1033
  return pullTrigger;
981
1034
  }
982
1035
 
983
- async deleteDrive(id: string) {
1036
+ async deleteDrive(driveId: string) {
984
1037
  const result = await Promise.allSettled([
985
- this.stopSyncRemoteDrive(id),
986
- this.listenerStateManager.removeDrive(id),
987
- this.cache.deleteDocument("drives", id),
988
- this.storage.deleteDrive(id),
1038
+ this.stopSyncRemoteDrive(driveId),
1039
+ this.listenerManager.removeDrive(driveId),
1040
+ this.cache.deleteDocument("drives", driveId),
1041
+ this.storage.deleteDrive(driveId),
989
1042
  ]);
990
1043
 
991
1044
  result.forEach((r) => {
@@ -999,10 +1052,10 @@ export class BaseDocumentDriveServer
999
1052
  return this.storage.getDrives();
1000
1053
  }
1001
1054
 
1002
- async getDrive(drive: string, options?: GetDocumentOptions) {
1055
+ async getDrive(driveId: string, options?: GetDocumentOptions) {
1003
1056
  let document: DocumentDriveDocument | undefined;
1004
1057
  try {
1005
- const cachedDocument = await this.cache.getDocument("drives", drive); // TODO support GetDocumentOptions
1058
+ const cachedDocument = await this.cache.getDocument("drives", driveId); // TODO support GetDocumentOptions
1006
1059
  if (cachedDocument && isDocumentDrive(cachedDocument)) {
1007
1060
  document = cachedDocument;
1008
1061
  if (isAtRevision(document, options?.revisions)) {
@@ -1012,13 +1065,13 @@ export class BaseDocumentDriveServer
1012
1065
  } catch (e) {
1013
1066
  logger.error("Error getting drive from cache", e);
1014
1067
  }
1015
- const driveStorage = document ?? (await this.storage.getDrive(drive));
1068
+ const driveStorage = document ?? (await this.storage.getDrive(driveId));
1016
1069
  const result = this._buildDocument(driveStorage, options);
1017
1070
  if (!isDocumentDrive(result)) {
1018
- throw new Error(`Document with id ${drive} is not a Document Drive`);
1071
+ throw new Error(`Document with id ${driveId} is not a Document Drive`);
1019
1072
  } else {
1020
1073
  if (!options?.revisions) {
1021
- this.cache.setDocument("drives", drive, result).catch(logger.error);
1074
+ this.cache.setDocument("drives", driveId, result).catch(logger.error);
1022
1075
  }
1023
1076
  return result;
1024
1077
  }
@@ -1044,10 +1097,14 @@ export class BaseDocumentDriveServer
1044
1097
  }
1045
1098
  }
1046
1099
 
1047
- async getDocument(drive: string, id: string, options?: GetDocumentOptions) {
1100
+ async getDocument(
1101
+ driveId: string,
1102
+ documentId: string,
1103
+ options?: GetDocumentOptions,
1104
+ ) {
1048
1105
  let cachedDocument: Document | undefined;
1049
1106
  try {
1050
- cachedDocument = await this.cache.getDocument(drive, id); // TODO support GetDocumentOptions
1107
+ cachedDocument = await this.cache.getDocument(driveId, documentId); // TODO support GetDocumentOptions
1051
1108
  if (cachedDocument && isAtRevision(cachedDocument, options?.revisions)) {
1052
1109
  return cachedDocument;
1053
1110
  }
@@ -1055,17 +1112,17 @@ export class BaseDocumentDriveServer
1055
1112
  logger.error("Error getting document from cache", e);
1056
1113
  }
1057
1114
  const documentStorage =
1058
- cachedDocument ?? (await this.storage.getDocument(drive, id));
1115
+ cachedDocument ?? (await this.storage.getDocument(driveId, documentId));
1059
1116
  const document = this._buildDocument(documentStorage, options);
1060
1117
 
1061
1118
  if (!options?.revisions) {
1062
- this.cache.setDocument(drive, id, document).catch(logger.error);
1119
+ this.cache.setDocument(driveId, documentId, document).catch(logger.error);
1063
1120
  }
1064
1121
  return document;
1065
1122
  }
1066
1123
 
1067
- getDocuments(drive: string) {
1068
- return this.storage.getDocuments(drive);
1124
+ getDocuments(driveId: string) {
1125
+ return this.storage.getDocuments(driveId);
1069
1126
  }
1070
1127
 
1071
1128
  protected async createDocument(driveId: string, input: CreateDocumentInput) {
@@ -1102,7 +1159,7 @@ export class BaseDocumentDriveServer
1102
1159
  for (const syncUnit of input.synchronizationUnits) {
1103
1160
  this.initSyncStatus(syncUnit.syncId, {
1104
1161
  pull: this.triggerMap.get(driveId) ? "INITIAL_SYNC" : undefined,
1105
- push: this.listenerStateManager.driveHasListeners(driveId)
1162
+ push: this.listenerManager.driveHasListeners(driveId)
1106
1163
  ? "SUCCESS"
1107
1164
  : undefined,
1108
1165
  });
@@ -1131,24 +1188,26 @@ export class BaseDocumentDriveServer
1131
1188
  return document;
1132
1189
  }
1133
1190
 
1134
- async deleteDocument(driveId: string, id: string) {
1191
+ async deleteDocument(driveId: string, documentId: string) {
1135
1192
  try {
1136
- const syncUnits = await this.getSynchronizationUnitsIds(driveId, [id]);
1193
+ const syncUnits = await this.getSynchronizationUnitsIds(driveId, [
1194
+ documentId,
1195
+ ]);
1137
1196
 
1138
1197
  // remove document sync units status when a document is deleted
1139
1198
  for (const syncUnit of syncUnits) {
1140
1199
  this.updateSyncUnitStatus(syncUnit.syncId, null);
1141
1200
  }
1142
- await this.listenerStateManager.removeSyncUnits(driveId, syncUnits);
1201
+ await this.listenerManager.removeSyncUnits(driveId, syncUnits);
1143
1202
  } catch (error) {
1144
1203
  logger.warn("Error deleting document", error);
1145
1204
  }
1146
- await this.cache.deleteDocument(driveId, id);
1147
- return this.storage.deleteDocument(driveId, id);
1205
+ await this.cache.deleteDocument(driveId, documentId);
1206
+ return this.storage.deleteDocument(driveId, documentId);
1148
1207
  }
1149
1208
 
1150
1209
  async _processOperations<T extends Document, A extends Action>(
1151
- drive: string,
1210
+ driveId: string,
1152
1211
  documentId: string | undefined,
1153
1212
  documentStorage: DocumentStorage<T>,
1154
1213
  operations: Operation<A | BaseAction>[],
@@ -1158,7 +1217,7 @@ export class BaseDocumentDriveServer
1158
1217
 
1159
1218
  const documentStorageWithState = await this._addDocumentResultingStage(
1160
1219
  documentStorage,
1161
- drive,
1220
+ driveId,
1162
1221
  documentId,
1163
1222
  );
1164
1223
 
@@ -1215,7 +1274,7 @@ export class BaseDocumentDriveServer
1215
1274
  const taskQueueMethod = this.options.taskQueueMethod;
1216
1275
  const task = () =>
1217
1276
  this._performOperation(
1218
- drive,
1277
+ driveId,
1219
1278
  documentId,
1220
1279
  document,
1221
1280
  nextOperation,
@@ -1256,7 +1315,7 @@ export class BaseDocumentDriveServer
1256
1315
 
1257
1316
  private async _addDocumentResultingStage<T extends Document>(
1258
1317
  document: DocumentStorage<T>,
1259
- drive: string,
1318
+ driveId: string,
1260
1319
  documentId?: string,
1261
1320
  options?: GetDocumentOptions,
1262
1321
  ): Promise<DocumentStorage<T>> {
@@ -1278,14 +1337,14 @@ export class BaseDocumentDriveServer
1278
1337
  if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
1279
1338
  lastRemainingOperation.resultingState = await (documentId
1280
1339
  ? this.storage.getOperationResultingState?.(
1281
- drive,
1340
+ driveId,
1282
1341
  documentId,
1283
1342
  lastRemainingOperation.index,
1284
1343
  lastRemainingOperation.scope,
1285
1344
  "main",
1286
1345
  )
1287
1346
  : this.storage.getDriveOperationResultingState?.(
1288
- drive,
1347
+ driveId,
1289
1348
  lastRemainingOperation.index,
1290
1349
  lastRemainingOperation.scope,
1291
1350
  "main",
@@ -1341,8 +1400,8 @@ export class BaseDocumentDriveServer
1341
1400
  }
1342
1401
 
1343
1402
  private async _performOperation<T extends Document>(
1344
- drive: string,
1345
- id: string | undefined,
1403
+ driveId: string,
1404
+ documentId: string | undefined,
1346
1405
  document: T,
1347
1406
  operation: Operation,
1348
1407
  skipHashValidation = false,
@@ -1366,16 +1425,16 @@ export class BaseDocumentDriveServer
1366
1425
  // if the latest operation doesn't have a resulting state then tries
1367
1426
  // to retrieve it from the db to avoid rerunning all the operations
1368
1427
  if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
1369
- lastRemainingOperation.resultingState = await (id
1428
+ lastRemainingOperation.resultingState = await (documentId
1370
1429
  ? this.storage.getOperationResultingState?.(
1371
- drive,
1372
- id,
1430
+ driveId,
1431
+ documentId,
1373
1432
  lastRemainingOperation.index,
1374
1433
  lastRemainingOperation.scope,
1375
1434
  "main",
1376
1435
  )
1377
1436
  : this.storage.getDriveOperationResultingState?.(
1378
- drive,
1437
+ driveId,
1379
1438
  lastRemainingOperation.index,
1380
1439
  lastRemainingOperation.scope,
1381
1440
  "main",
@@ -1390,20 +1449,21 @@ export class BaseDocumentDriveServer
1390
1449
  let handler: (() => Promise<unknown>) | undefined = undefined;
1391
1450
  switch (signal.type) {
1392
1451
  case "CREATE_CHILD_DOCUMENT":
1393
- handler = () => this.createDocument(drive, signal.input);
1452
+ handler = () => this.createDocument(driveId, signal.input);
1394
1453
  break;
1395
1454
  case "DELETE_CHILD_DOCUMENT":
1396
- handler = () => this.deleteDocument(drive, signal.input.id);
1455
+ handler = () => this.deleteDocument(driveId, signal.input.id);
1397
1456
  break;
1398
1457
  case "COPY_CHILD_DOCUMENT":
1399
1458
  handler = () =>
1400
- this.getDocument(drive, signal.input.id).then((documentToCopy) =>
1401
- this.createDocument(drive, {
1402
- id: signal.input.newId,
1403
- documentType: documentToCopy.documentType,
1404
- document: documentToCopy,
1405
- synchronizationUnits: signal.input.synchronizationUnits,
1406
- }),
1459
+ this.getDocument(driveId, signal.input.id).then(
1460
+ (documentToCopy) =>
1461
+ this.createDocument(driveId, {
1462
+ id: signal.input.newId,
1463
+ documentType: documentToCopy.documentType,
1464
+ document: documentToCopy,
1465
+ synchronizationUnits: signal.input.synchronizationUnits,
1466
+ }),
1407
1467
  );
1408
1468
  break;
1409
1469
  }
@@ -1449,50 +1509,53 @@ export class BaseDocumentDriveServer
1449
1509
  }
1450
1510
 
1451
1511
  addOperation(
1452
- drive: string,
1453
- id: string,
1512
+ driveId: string,
1513
+ documentId: string,
1454
1514
  operation: Operation,
1455
1515
  options?: AddOperationOptions,
1456
1516
  ): Promise<IOperationResult> {
1457
- return this.addOperations(drive, id, [operation], options);
1517
+ return this.addOperations(driveId, documentId, [operation], options);
1458
1518
  }
1459
1519
 
1460
1520
  private async _addOperations(
1461
- drive: string,
1462
- id: string,
1521
+ driveId: string,
1522
+ documentId: string,
1463
1523
  callback: (document: DocumentStorage) => Promise<{
1464
1524
  operations: Operation[];
1465
1525
  header: DocumentHeader;
1466
1526
  }>,
1467
1527
  ) {
1468
1528
  if (!this.storage.addDocumentOperationsWithTransaction) {
1469
- const documentStorage = await this.storage.getDocument(drive, id);
1529
+ const documentStorage = await this.storage.getDocument(
1530
+ driveId,
1531
+ documentId,
1532
+ );
1470
1533
  const result = await callback(documentStorage);
1471
1534
  // saves the applied operations to storage
1472
1535
  if (result.operations.length > 0) {
1473
1536
  await this.storage.addDocumentOperations(
1474
- drive,
1475
- id,
1537
+ driveId,
1538
+ documentId,
1476
1539
  result.operations,
1477
1540
  result.header,
1478
1541
  );
1479
1542
  }
1480
1543
  } else {
1481
1544
  await this.storage.addDocumentOperationsWithTransaction(
1482
- drive,
1483
- id,
1545
+ driveId,
1546
+ documentId,
1484
1547
  callback,
1485
1548
  );
1486
1549
  }
1487
1550
  }
1488
1551
 
1489
1552
  queueOperation(
1490
- drive: string,
1491
- id: string,
1553
+ driveId: string,
1554
+ documentId: string,
1492
1555
  operation: Operation,
1493
1556
  options?: AddOperationOptions,
1494
1557
  ): Promise<IOperationResult> {
1495
- return this.queueOperations(drive, id, [operation], options);
1558
+ return this.queueOperations(driveId, documentId, [operation], options);
1496
1559
  }
1497
1560
 
1498
1561
  private async resultIfExistingOperations(
@@ -1534,20 +1597,24 @@ export class BaseDocumentDriveServer
1534
1597
  }
1535
1598
 
1536
1599
  async queueOperations(
1537
- drive: string,
1538
- id: string,
1600
+ driveId: string,
1601
+ documentId: string,
1539
1602
  operations: Operation[],
1540
1603
  options?: AddOperationOptions,
1541
1604
  ) {
1542
1605
  // if operations are already stored then returns cached document
1543
- const result = await this.resultIfExistingOperations(drive, id, operations);
1606
+ const result = await this.resultIfExistingOperations(
1607
+ driveId,
1608
+ documentId,
1609
+ operations,
1610
+ );
1544
1611
  if (result) {
1545
1612
  return result;
1546
1613
  }
1547
1614
  try {
1548
1615
  const jobId = await this.queueManager.addJob({
1549
- driveId: drive,
1550
- documentId: id,
1616
+ driveId: driveId,
1617
+ documentId: documentId,
1551
1618
  operations,
1552
1619
  options,
1553
1620
  });
@@ -1581,24 +1648,24 @@ export class BaseDocumentDriveServer
1581
1648
  }
1582
1649
 
1583
1650
  async queueAction(
1584
- drive: string,
1585
- id: string,
1651
+ driveId: string,
1652
+ documentId: string,
1586
1653
  action: Action,
1587
1654
  options?: AddOperationOptions,
1588
1655
  ): Promise<IOperationResult> {
1589
- return this.queueActions(drive, id, [action], options);
1656
+ return this.queueActions(driveId, documentId, [action], options);
1590
1657
  }
1591
1658
 
1592
1659
  async queueActions(
1593
- drive: string,
1594
- id: string,
1660
+ driveId: string,
1661
+ documentId: string,
1595
1662
  actions: Action[],
1596
1663
  options?: AddOperationOptions,
1597
1664
  ): Promise<IOperationResult> {
1598
1665
  try {
1599
1666
  const jobId = await this.queueManager.addJob({
1600
- driveId: drive,
1601
- documentId: id,
1667
+ driveId: driveId,
1668
+ documentId: documentId,
1602
1669
  actions,
1603
1670
  options,
1604
1671
  });
@@ -1632,21 +1699,21 @@ export class BaseDocumentDriveServer
1632
1699
  }
1633
1700
 
1634
1701
  async queueDriveAction(
1635
- drive: string,
1702
+ driveId: string,
1636
1703
  action: DocumentDriveAction | BaseAction,
1637
1704
  options?: AddOperationOptions,
1638
1705
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1639
- return this.queueDriveActions(drive, [action], options);
1706
+ return this.queueDriveActions(driveId, [action], options);
1640
1707
  }
1641
1708
 
1642
1709
  async queueDriveActions(
1643
- drive: string,
1710
+ driveId: string,
1644
1711
  actions: (DocumentDriveAction | BaseAction)[],
1645
1712
  options?: AddOperationOptions,
1646
1713
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1647
1714
  try {
1648
1715
  const jobId = await this.queueManager.addJob({
1649
- driveId: drive,
1716
+ driveId: driveId,
1650
1717
  actions,
1651
1718
  options,
1652
1719
  });
@@ -1681,13 +1748,17 @@ export class BaseDocumentDriveServer
1681
1748
  }
1682
1749
 
1683
1750
  async addOperations(
1684
- drive: string,
1685
- id: string,
1751
+ driveId: string,
1752
+ documentId: string,
1686
1753
  operations: Operation[],
1687
1754
  options?: AddOperationOptions,
1688
1755
  ) {
1689
1756
  // if operations are already stored then returns the result
1690
- const result = await this.resultIfExistingOperations(drive, id, operations);
1757
+ const result = await this.resultIfExistingOperations(
1758
+ driveId,
1759
+ documentId,
1760
+ operations,
1761
+ );
1691
1762
  if (result) {
1692
1763
  return result;
1693
1764
  }
@@ -1697,33 +1768,39 @@ export class BaseDocumentDriveServer
1697
1768
  let error: Error | undefined;
1698
1769
 
1699
1770
  try {
1700
- await this._addOperations(drive, id, async (documentStorage) => {
1701
- const result = await this._processOperations(
1702
- drive,
1703
- id,
1704
- documentStorage,
1705
- operations,
1706
- );
1771
+ await this._addOperations(
1772
+ driveId,
1773
+ documentId,
1774
+ async (documentStorage) => {
1775
+ const result = await this._processOperations(
1776
+ driveId,
1777
+ documentId,
1778
+ documentStorage,
1779
+ operations,
1780
+ );
1707
1781
 
1708
- if (!result.document) {
1709
- logger.error("Invalid document");
1710
- throw result.error ?? new Error("Invalid document");
1711
- }
1782
+ if (!result.document) {
1783
+ logger.error("Invalid document");
1784
+ throw result.error ?? new Error("Invalid document");
1785
+ }
1712
1786
 
1713
- document = result.document;
1714
- error = result.error;
1715
- signals.push(...result.signals);
1716
- operationsApplied.push(...result.operationsApplied);
1787
+ document = result.document;
1788
+ error = result.error;
1789
+ signals.push(...result.signals);
1790
+ operationsApplied.push(...result.operationsApplied);
1717
1791
 
1718
- return {
1719
- operations: result.operationsApplied,
1720
- header: result.document,
1721
- newState: document.state,
1722
- };
1723
- });
1792
+ return {
1793
+ operations: result.operationsApplied,
1794
+ header: result.document,
1795
+ newState: document.state,
1796
+ };
1797
+ },
1798
+ );
1724
1799
 
1725
1800
  if (document) {
1726
- this.cache.setDocument(drive, id, document).catch(logger.error);
1801
+ this.cache
1802
+ .setDocument(driveId, documentId, document)
1803
+ .catch(logger.error);
1727
1804
  }
1728
1805
 
1729
1806
  // gets all the different scopes and branches combinations from the operations
@@ -1738,8 +1815,8 @@ export class BaseDocumentDriveServer
1738
1815
  );
1739
1816
 
1740
1817
  const syncUnits = await this.getSynchronizationUnits(
1741
- drive,
1742
- [id],
1818
+ driveId,
1819
+ [documentId],
1743
1820
  scopes,
1744
1821
  branches,
1745
1822
  );
@@ -1767,13 +1844,13 @@ export class BaseDocumentDriveServer
1767
1844
 
1768
1845
  const operationSource = this.getOperationSource(source);
1769
1846
 
1770
- this.listenerStateManager
1847
+ this.listenerManager
1771
1848
  .updateSynchronizationRevisions(
1772
- drive,
1849
+ driveId,
1773
1850
  syncUnits,
1774
1851
  source,
1775
1852
  () => {
1776
- this.updateSyncUnitStatus(drive, {
1853
+ this.updateSyncUnitStatus(driveId, {
1777
1854
  [operationSource]: "SYNCING",
1778
1855
  });
1779
1856
 
@@ -1788,7 +1865,7 @@ export class BaseDocumentDriveServer
1788
1865
  )
1789
1866
  .then((updates) => {
1790
1867
  if (updates.length) {
1791
- this.updateSyncUnitStatus(drive, {
1868
+ this.updateSyncUnitStatus(driveId, {
1792
1869
  [operationSource]: "SUCCESS",
1793
1870
  });
1794
1871
  }
@@ -1802,7 +1879,7 @@ export class BaseDocumentDriveServer
1802
1879
  .catch((error) => {
1803
1880
  logger.error("Non handled error updating sync revision", error);
1804
1881
  this.updateSyncUnitStatus(
1805
- drive,
1882
+ driveId,
1806
1883
  {
1807
1884
  [operationSource]: "ERROR",
1808
1885
  },
@@ -1854,11 +1931,11 @@ export class BaseDocumentDriveServer
1854
1931
  }
1855
1932
 
1856
1933
  addDriveOperation(
1857
- drive: string,
1934
+ driveId: string,
1858
1935
  operation: Operation<DocumentDriveAction | BaseAction>,
1859
1936
  options?: AddOperationOptions,
1860
1937
  ) {
1861
- return this.addDriveOperations(drive, [operation], options);
1938
+ return this.addDriveOperations(driveId, [operation], options);
1862
1939
  }
1863
1940
 
1864
1941
  async clearStorage() {
@@ -1870,35 +1947,35 @@ export class BaseDocumentDriveServer
1870
1947
  }
1871
1948
 
1872
1949
  private async _addDriveOperations(
1873
- drive: string,
1950
+ driveId: string,
1874
1951
  callback: (document: DocumentDriveStorage) => Promise<{
1875
1952
  operations: Operation<DocumentDriveAction | BaseAction>[];
1876
1953
  header: DocumentHeader;
1877
1954
  }>,
1878
1955
  ) {
1879
1956
  if (!this.storage.addDriveOperationsWithTransaction) {
1880
- const documentStorage = await this.storage.getDrive(drive);
1957
+ const documentStorage = await this.storage.getDrive(driveId);
1881
1958
  const result = await callback(documentStorage);
1882
1959
  // saves the applied operations to storage
1883
1960
  if (result.operations.length > 0) {
1884
1961
  await this.storage.addDriveOperations(
1885
- drive,
1962
+ driveId,
1886
1963
  result.operations,
1887
1964
  result.header,
1888
1965
  );
1889
1966
  }
1890
1967
  return result;
1891
1968
  } else {
1892
- return this.storage.addDriveOperationsWithTransaction(drive, callback);
1969
+ return this.storage.addDriveOperationsWithTransaction(driveId, callback);
1893
1970
  }
1894
1971
  }
1895
1972
 
1896
1973
  queueDriveOperation(
1897
- drive: string,
1974
+ driveId: string,
1898
1975
  operation: Operation<DocumentDriveAction | BaseAction>,
1899
1976
  options?: AddOperationOptions,
1900
1977
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1901
- return this.queueDriveOperations(drive, [operation], options);
1978
+ return this.queueDriveOperations(driveId, [operation], options);
1902
1979
  }
1903
1980
 
1904
1981
  private async resultIfExistingDriveOperations(
@@ -1935,13 +2012,13 @@ export class BaseDocumentDriveServer
1935
2012
  }
1936
2013
 
1937
2014
  async queueDriveOperations(
1938
- drive: string,
2015
+ driveId: string,
1939
2016
  operations: Operation<DocumentDriveAction | BaseAction>[],
1940
2017
  options?: AddOperationOptions,
1941
2018
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1942
2019
  // if operations are already stored then returns cached document
1943
2020
  const result = await this.resultIfExistingDriveOperations(
1944
- drive,
2021
+ driveId,
1945
2022
  operations,
1946
2023
  );
1947
2024
  if (result) {
@@ -1949,7 +2026,7 @@ export class BaseDocumentDriveServer
1949
2026
  }
1950
2027
  try {
1951
2028
  const jobId = await this.queueManager.addJob({
1952
- driveId: drive,
2029
+ driveId: driveId,
1953
2030
  operations,
1954
2031
  options,
1955
2032
  });
@@ -1984,7 +2061,7 @@ export class BaseDocumentDriveServer
1984
2061
  }
1985
2062
 
1986
2063
  async addDriveOperations(
1987
- drive: string,
2064
+ driveId: string,
1988
2065
  operations: Operation<DocumentDriveAction | BaseAction>[],
1989
2066
  options?: AddOperationOptions,
1990
2067
  ) {
@@ -1995,7 +2072,7 @@ export class BaseDocumentDriveServer
1995
2072
 
1996
2073
  // if operations are already stored then returns cached drive
1997
2074
  const result = await this.resultIfExistingDriveOperations(
1998
- drive,
2075
+ driveId,
1999
2076
  operations,
2000
2077
  );
2001
2078
  if (result) {
@@ -2003,11 +2080,11 @@ export class BaseDocumentDriveServer
2003
2080
  }
2004
2081
 
2005
2082
  try {
2006
- await this._addDriveOperations(drive, async (documentStorage) => {
2083
+ await this._addDriveOperations(driveId, async (documentStorage) => {
2007
2084
  const result = await this._processOperations<
2008
2085
  DocumentDriveDocument,
2009
2086
  DocumentDriveAction
2010
- >(drive, undefined, documentStorage, operations.slice());
2087
+ >(driveId, undefined, documentStorage, operations.slice());
2011
2088
  document = result.document;
2012
2089
  operationsApplied.push(...result.operationsApplied);
2013
2090
  signals.push(...result.signals);
@@ -2023,16 +2100,54 @@ export class BaseDocumentDriveServer
2023
2100
  throw error ?? new Error("Invalid Document Drive document");
2024
2101
  }
2025
2102
 
2026
- this.cache.setDocument("drives", drive, document).catch(logger.error);
2103
+ this.cache.setDocument("drives", driveId, document).catch(logger.error);
2027
2104
 
2028
2105
  for (const operation of operationsApplied) {
2029
2106
  switch (operation.type) {
2030
2107
  case "ADD_LISTENER": {
2031
- await this.addListener(drive, operation);
2108
+ const zodListener = operation.input.listener;
2109
+
2110
+ // create the transmitter
2111
+ const transmitter = this.transmitterFactory.instance(
2112
+ zodListener.callInfo?.transmitterType ?? "",
2113
+ {
2114
+ driveId,
2115
+ listenerId: zodListener.listenerId,
2116
+ block: zodListener.block,
2117
+ filter: zodListener.filter,
2118
+ system: zodListener.system,
2119
+ label: zodListener.label || undefined,
2120
+ callInfo: zodListener.callInfo || undefined,
2121
+ },
2122
+ this,
2123
+ );
2124
+
2125
+ // create the listener
2126
+ const listener = {
2127
+ ...zodListener,
2128
+ driveId: driveId,
2129
+ label: zodListener.label ?? "",
2130
+ system: zodListener.system ?? false,
2131
+ filter: {
2132
+ branch: zodListener.filter.branch ?? [],
2133
+ documentId: zodListener.filter.documentId ?? [],
2134
+ documentType: zodListener.filter.documentType ?? [],
2135
+ scope: zodListener.filter.scope ?? [],
2136
+ },
2137
+ callInfo: {
2138
+ data: zodListener.callInfo?.data ?? "",
2139
+ name: zodListener.callInfo?.name ?? "PullResponder",
2140
+ transmitterType:
2141
+ zodListener.callInfo?.transmitterType ?? "PullResponder",
2142
+ },
2143
+ transmitter,
2144
+ };
2145
+
2146
+ await this.addListener(driveId, listener);
2032
2147
  break;
2033
2148
  }
2034
2149
  case "REMOVE_LISTENER": {
2035
- await this.removeListener(drive, operation);
2150
+ await this.removeListener(driveId, operation);
2036
2151
  break;
2037
2152
  }
2038
2153
  }
@@ -2066,13 +2181,13 @@ export class BaseDocumentDriveServer
2066
2181
 
2067
2182
  const operationSource = this.getOperationSource(source);
2068
2183
 
2069
- this.listenerStateManager
2184
+ this.listenerManager
2070
2185
  .updateSynchronizationRevisions(
2071
- drive,
2186
+ driveId,
2072
2187
  [
2073
2188
  {
2074
2189
  syncId: "0",
2075
- driveId: drive,
2190
+ driveId: driveId,
2076
2191
  documentId: "",
2077
2192
  scope: "global",
2078
2193
  branch: "main",
@@ -2083,7 +2198,7 @@ export class BaseDocumentDriveServer
2083
2198
  ],
2084
2199
  source,
2085
2200
  () => {
2086
- this.updateSyncUnitStatus(drive, {
2201
+ this.updateSyncUnitStatus(driveId, {
2087
2202
  [operationSource]: "SYNCING",
2088
2203
  });
2089
2204
  },
@@ -2092,7 +2207,7 @@ export class BaseDocumentDriveServer
2092
2207
  )
2093
2208
  .then((updates) => {
2094
2209
  if (updates.length) {
2095
- this.updateSyncUnitStatus(drive, {
2210
+ this.updateSyncUnitStatus(driveId, {
2096
2211
  [operationSource]: "SUCCESS",
2097
2212
  });
2098
2213
  }
@@ -2100,7 +2215,7 @@ export class BaseDocumentDriveServer
2100
2215
  .catch((error) => {
2101
2216
  logger.error("Non handled error updating sync revision", error);
2102
2217
  this.updateSyncUnitStatus(
2103
- drive,
2218
+ driveId,
2104
2219
  { [operationSource]: "ERROR" },
2105
2220
  error as Error,
2106
2221
  );
@@ -2147,14 +2262,14 @@ export class BaseDocumentDriveServer
2147
2262
  }
2148
2263
 
2149
2264
  private _buildOperations<T extends Action>(
2150
- document: Document,
2265
+ documentId: Document,
2151
2266
  actions: (T | BaseAction)[],
2152
2267
  ): Operation<T | BaseAction>[] {
2153
2268
  const operations: Operation<T | BaseAction>[] = [];
2154
- const { reducer } = this.getDocumentModel(document.documentType);
2269
+ const { reducer } = this.getDocumentModel(documentId.documentType);
2155
2270
  for (const action of actions) {
2156
- document = reducer(document, action);
2157
- const operation = document.operations[action.scope].slice().pop();
2271
+ documentId = reducer(documentId, action);
2272
+ const operation = documentId.operations[action.scope].slice().pop();
2158
2273
  if (!operation) {
2159
2274
  throw new Error("Error creating operations");
2160
2275
  }
@@ -2164,44 +2279,73 @@ export class BaseDocumentDriveServer
2164
2279
  }
2165
2280
 
2166
2281
  async addAction(
2167
- drive: string,
2168
- id: string,
2282
+ driveId: string,
2283
+ documentId: string,
2169
2284
  action: Action,
2170
2285
  options?: AddOperationOptions,
2171
2286
  ): Promise<IOperationResult> {
2172
- return this.addActions(drive, id, [action], options);
2287
+ return this.addActions(driveId, documentId, [action], options);
2173
2288
  }
2174
2289
 
2175
2290
  async addActions(
2176
- drive: string,
2177
- id: string,
2291
+ driveId: string,
2292
+ documentId: string,
2178
2293
  actions: Action[],
2179
2294
  options?: AddOperationOptions,
2180
2295
  ): Promise<IOperationResult> {
2181
- const document = await this.getDocument(drive, id);
2296
+ const document = await this.getDocument(driveId, documentId);
2182
2297
  const operations = this._buildOperations(document, actions);
2183
- return this.addOperations(drive, id, operations, options);
2298
+ return this.addOperations(driveId, documentId, operations, options);
2184
2299
  }
2185
2300
 
2186
2301
  async addDriveAction(
2187
- drive: string,
2302
+ driveId: string,
2188
2303
  action: DocumentDriveAction | BaseAction,
2189
2304
  options?: AddOperationOptions,
2190
2305
  ): Promise<IOperationResult<DocumentDriveDocument>> {
2191
- return this.addDriveActions(drive, [action], options);
2306
+ return this.addDriveActions(driveId, [action], options);
2192
2307
  }
2193
2308
 
2194
2309
  async addDriveActions(
2195
- drive: string,
2310
+ driveId: string,
2196
2311
  actions: (DocumentDriveAction | BaseAction)[],
2197
2312
  options?: AddOperationOptions,
2198
2313
  ): Promise<IOperationResult<DocumentDriveDocument>> {
2199
- const document = await this.getDrive(drive);
2314
+ const document = await this.getDrive(driveId);
2200
2315
  const operations = this._buildOperations(document, actions);
2201
- const result = await this.addDriveOperations(drive, operations, options);
2316
+ const result = await this.addDriveOperations(driveId, operations, options);
2202
2317
  return result;
2203
2318
  }
2204
2319
 
2320
+ async detachDrive(driveId: string) {
2321
+ const documentDrive = await this.getDrive(driveId);
2322
+ const listeners = documentDrive.state.local.listeners || [];
2323
+ const triggers = documentDrive.state.local.triggers || [];
2324
+
2325
+ for (const listener of listeners) {
2326
+ await this.addDriveAction(
2327
+ driveId,
2328
+ actions.removeListener({ listenerId: listener.listenerId }),
2329
+ );
2330
+ }
2331
+
2332
+ for (const trigger of triggers) {
2333
+ await this.addDriveAction(
2334
+ driveId,
2335
+ actions.removeTrigger({ triggerId: trigger.id }),
2336
+ );
2337
+ }
2338
+
2339
+ await this.addDriveAction(
2340
+ driveId,
2341
+ actions.setSharingType({ type: "LOCAL" }),
2342
+ );
2343
+ }
2344
+
2345
+ async addListener(driveId: string, listener: Listener) {
2346
+ await this.listenerManager.setListener(driveId, listener);
2347
+ }
2348
+
2205
2349
  async addInternalListener(
2206
2350
  driveId: string,
2207
2351
  receiver: IReceiver,
@@ -2236,75 +2380,40 @@ export class BaseDocumentDriveServer
2236
2380
  return transmitter;
2237
2381
  }
2238
2382
 
2239
- async detachDrive(driveId: string) {
2240
- const documentDrive = await this.getDrive(driveId);
2241
- const listeners = documentDrive.state.local.listeners || [];
2242
- const triggers = documentDrive.state.local.triggers || [];
2243
-
2244
- for (const listener of listeners) {
2245
- await this.addDriveAction(
2246
- driveId,
2247
- actions.removeListener({ listenerId: listener.listenerId }),
2248
- );
2249
- }
2250
-
2251
- for (const trigger of triggers) {
2252
- await this.addDriveAction(
2253
- driveId,
2254
- actions.removeTrigger({ triggerId: trigger.id }),
2255
- );
2256
- }
2257
-
2258
- await this.addDriveAction(
2259
- driveId,
2260
- actions.setSharingType({ type: "LOCAL" }),
2261
- );
2262
- }
2263
-
2264
- private async addListener(
2265
- driveId: string,
2266
- operation: Operation<Action<"ADD_LISTENER", AddListenerInput>>,
2267
- ) {
2268
- const { listener } = operation.input;
2269
- await this.listenerStateManager.addListener({
2270
- ...listener,
2271
- driveId,
2272
- label: listener.label ?? "",
2273
- system: listener.system ?? false,
2274
- filter: {
2275
- branch: listener.filter.branch ?? [],
2276
- documentId: listener.filter.documentId ?? [],
2277
- documentType: listener.filter.documentType ?? [],
2278
- scope: listener.filter.scope ?? [],
2279
- },
2280
- callInfo: {
2281
- data: listener.callInfo?.data ?? "",
2282
- name: listener.callInfo?.name ?? "PullResponder",
2283
- transmitterType: listener.callInfo?.transmitterType ?? "PullResponder",
2284
- },
2285
- });
2286
- }
2287
-
2288
2383
  private async removeListener(
2289
2384
  driveId: string,
2290
2385
  operation: Operation<Action<"REMOVE_LISTENER", RemoveListenerInput>>,
2291
2386
  ) {
2292
2387
  const { listenerId } = operation.input;
2293
- await this.listenerStateManager.removeListener(driveId, listenerId);
2388
+ await this.listenerManager.removeListener(driveId, listenerId);
2294
2389
  }
2295
2390
 
2296
- getTransmitter(
2391
+ async getTransmitter(
2297
2392
  driveId: string,
2298
2393
  listenerId: string,
2299
2394
  ): Promise<ITransmitter | undefined> {
2300
- return this.listenerStateManager.getTransmitter(driveId, listenerId);
2395
+ const listener = await this.listenerManager.getListenerState(
2396
+ driveId,
2397
+ listenerId,
2398
+ );
2399
+ return listener?.listener.transmitter;
2301
2400
  }
2302
2401
 
2303
2402
  getListener(
2304
2403
  driveId: string,
2305
2404
  listenerId: string,
2306
2405
  ): Promise<ListenerState | undefined> {
2307
- return this.listenerStateManager.getListener(driveId, listenerId);
2406
+ let listenerState;
2407
+ try {
2408
+ listenerState = this.listenerManager.getListenerState(
2409
+ driveId,
2410
+ listenerId,
2411
+ );
2412
+ } catch {
2413
+ return Promise.resolve(undefined);
2414
+ }
2415
+
2416
+ return Promise.resolve(listenerState);
2308
2417
  }
2309
2418
 
2310
2419
  getSyncStatus(