document-drive 1.17.2 → 1.18.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,28 @@ 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
+ zodListener as any,
666
+ this,
667
+ );
668
+
669
+ await this.listenerManager.setListener(driveId, {
670
+ block: zodListener.block,
671
+ driveId: drive.state.global.id,
672
+ filter: {
673
+ branch: zodListener.filter.branch ?? [],
674
+ documentId: zodListener.filter.documentId ?? [],
675
+ documentType: zodListener.filter.documentType,
676
+ scope: zodListener.filter.scope ?? [],
677
+ },
678
+ listenerId: zodListener.listenerId,
679
+ system: zodListener.system,
680
+ label: zodListener.label ?? "",
681
+ transmitter,
682
+ });
683
+ }
639
684
  }
640
685
 
641
686
  public async getSynchronizationUnits(
@@ -899,8 +944,8 @@ export class BaseDocumentDriveServer
899
944
  return [...this.documentModels];
900
945
  }
901
946
 
902
- async addDrive(drive: DriveInput): Promise<DocumentDriveDocument> {
903
- const id = drive.global.id || generateUUID();
947
+ async addDrive(input: DriveInput): Promise<DocumentDriveDocument> {
948
+ const id = input.global.id || generateUUID();
904
949
  if (!id) {
905
950
  throw new Error("Invalid Drive Id");
906
951
  }
@@ -911,13 +956,13 @@ export class BaseDocumentDriveServer
911
956
  }
912
957
 
913
958
  const document = utils.createDocument({
914
- state: drive,
959
+ state: input,
915
960
  });
916
961
 
917
962
  await this.storage.createDrive(id, document);
918
963
 
919
- if (drive.global.slug) {
920
- await this.cache.deleteDocument("drives-slug", drive.global.slug);
964
+ if (input.global.slug) {
965
+ await this.cache.deleteDocument("drives-slug", input.global.slug);
921
966
  }
922
967
 
923
968
  await this._initializeDrive(id);
@@ -966,13 +1011,13 @@ export class BaseDocumentDriveServer
966
1011
  }
967
1012
 
968
1013
  public async registerPullResponderTrigger(
969
- id: string,
1014
+ driveId: string,
970
1015
  url: string,
971
1016
  options: Pick<RemoteDriveOptions, "pullFilter" | "pullInterval">,
972
1017
  ) {
973
1018
  const pullTrigger =
974
1019
  await PullResponderTransmitter.createPullResponderTrigger(
975
- id,
1020
+ driveId,
976
1021
  url,
977
1022
  options,
978
1023
  );
@@ -980,12 +1025,12 @@ export class BaseDocumentDriveServer
980
1025
  return pullTrigger;
981
1026
  }
982
1027
 
983
- async deleteDrive(id: string) {
1028
+ async deleteDrive(driveId: string) {
984
1029
  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),
1030
+ this.stopSyncRemoteDrive(driveId),
1031
+ this.listenerManager.removeDrive(driveId),
1032
+ this.cache.deleteDocument("drives", driveId),
1033
+ this.storage.deleteDrive(driveId),
989
1034
  ]);
990
1035
 
991
1036
  result.forEach((r) => {
@@ -999,10 +1044,10 @@ export class BaseDocumentDriveServer
999
1044
  return this.storage.getDrives();
1000
1045
  }
1001
1046
 
1002
- async getDrive(drive: string, options?: GetDocumentOptions) {
1047
+ async getDrive(driveId: string, options?: GetDocumentOptions) {
1003
1048
  let document: DocumentDriveDocument | undefined;
1004
1049
  try {
1005
- const cachedDocument = await this.cache.getDocument("drives", drive); // TODO support GetDocumentOptions
1050
+ const cachedDocument = await this.cache.getDocument("drives", driveId); // TODO support GetDocumentOptions
1006
1051
  if (cachedDocument && isDocumentDrive(cachedDocument)) {
1007
1052
  document = cachedDocument;
1008
1053
  if (isAtRevision(document, options?.revisions)) {
@@ -1012,13 +1057,13 @@ export class BaseDocumentDriveServer
1012
1057
  } catch (e) {
1013
1058
  logger.error("Error getting drive from cache", e);
1014
1059
  }
1015
- const driveStorage = document ?? (await this.storage.getDrive(drive));
1060
+ const driveStorage = document ?? (await this.storage.getDrive(driveId));
1016
1061
  const result = this._buildDocument(driveStorage, options);
1017
1062
  if (!isDocumentDrive(result)) {
1018
- throw new Error(`Document with id ${drive} is not a Document Drive`);
1063
+ throw new Error(`Document with id ${driveId} is not a Document Drive`);
1019
1064
  } else {
1020
1065
  if (!options?.revisions) {
1021
- this.cache.setDocument("drives", drive, result).catch(logger.error);
1066
+ this.cache.setDocument("drives", driveId, result).catch(logger.error);
1022
1067
  }
1023
1068
  return result;
1024
1069
  }
@@ -1044,10 +1089,14 @@ export class BaseDocumentDriveServer
1044
1089
  }
1045
1090
  }
1046
1091
 
1047
- async getDocument(drive: string, id: string, options?: GetDocumentOptions) {
1092
+ async getDocument(
1093
+ driveId: string,
1094
+ documentId: string,
1095
+ options?: GetDocumentOptions,
1096
+ ) {
1048
1097
  let cachedDocument: Document | undefined;
1049
1098
  try {
1050
- cachedDocument = await this.cache.getDocument(drive, id); // TODO support GetDocumentOptions
1099
+ cachedDocument = await this.cache.getDocument(driveId, documentId); // TODO support GetDocumentOptions
1051
1100
  if (cachedDocument && isAtRevision(cachedDocument, options?.revisions)) {
1052
1101
  return cachedDocument;
1053
1102
  }
@@ -1055,17 +1104,17 @@ export class BaseDocumentDriveServer
1055
1104
  logger.error("Error getting document from cache", e);
1056
1105
  }
1057
1106
  const documentStorage =
1058
- cachedDocument ?? (await this.storage.getDocument(drive, id));
1107
+ cachedDocument ?? (await this.storage.getDocument(driveId, documentId));
1059
1108
  const document = this._buildDocument(documentStorage, options);
1060
1109
 
1061
1110
  if (!options?.revisions) {
1062
- this.cache.setDocument(drive, id, document).catch(logger.error);
1111
+ this.cache.setDocument(driveId, documentId, document).catch(logger.error);
1063
1112
  }
1064
1113
  return document;
1065
1114
  }
1066
1115
 
1067
- getDocuments(drive: string) {
1068
- return this.storage.getDocuments(drive);
1116
+ getDocuments(driveId: string) {
1117
+ return this.storage.getDocuments(driveId);
1069
1118
  }
1070
1119
 
1071
1120
  protected async createDocument(driveId: string, input: CreateDocumentInput) {
@@ -1102,7 +1151,7 @@ export class BaseDocumentDriveServer
1102
1151
  for (const syncUnit of input.synchronizationUnits) {
1103
1152
  this.initSyncStatus(syncUnit.syncId, {
1104
1153
  pull: this.triggerMap.get(driveId) ? "INITIAL_SYNC" : undefined,
1105
- push: this.listenerStateManager.driveHasListeners(driveId)
1154
+ push: this.listenerManager.driveHasListeners(driveId)
1106
1155
  ? "SUCCESS"
1107
1156
  : undefined,
1108
1157
  });
@@ -1131,24 +1180,26 @@ export class BaseDocumentDriveServer
1131
1180
  return document;
1132
1181
  }
1133
1182
 
1134
- async deleteDocument(driveId: string, id: string) {
1183
+ async deleteDocument(driveId: string, documentId: string) {
1135
1184
  try {
1136
- const syncUnits = await this.getSynchronizationUnitsIds(driveId, [id]);
1185
+ const syncUnits = await this.getSynchronizationUnitsIds(driveId, [
1186
+ documentId,
1187
+ ]);
1137
1188
 
1138
1189
  // remove document sync units status when a document is deleted
1139
1190
  for (const syncUnit of syncUnits) {
1140
1191
  this.updateSyncUnitStatus(syncUnit.syncId, null);
1141
1192
  }
1142
- await this.listenerStateManager.removeSyncUnits(driveId, syncUnits);
1193
+ await this.listenerManager.removeSyncUnits(driveId, syncUnits);
1143
1194
  } catch (error) {
1144
1195
  logger.warn("Error deleting document", error);
1145
1196
  }
1146
- await this.cache.deleteDocument(driveId, id);
1147
- return this.storage.deleteDocument(driveId, id);
1197
+ await this.cache.deleteDocument(driveId, documentId);
1198
+ return this.storage.deleteDocument(driveId, documentId);
1148
1199
  }
1149
1200
 
1150
1201
  async _processOperations<T extends Document, A extends Action>(
1151
- drive: string,
1202
+ driveId: string,
1152
1203
  documentId: string | undefined,
1153
1204
  documentStorage: DocumentStorage<T>,
1154
1205
  operations: Operation<A | BaseAction>[],
@@ -1158,7 +1209,7 @@ export class BaseDocumentDriveServer
1158
1209
 
1159
1210
  const documentStorageWithState = await this._addDocumentResultingStage(
1160
1211
  documentStorage,
1161
- drive,
1212
+ driveId,
1162
1213
  documentId,
1163
1214
  );
1164
1215
 
@@ -1215,7 +1266,7 @@ export class BaseDocumentDriveServer
1215
1266
  const taskQueueMethod = this.options.taskQueueMethod;
1216
1267
  const task = () =>
1217
1268
  this._performOperation(
1218
- drive,
1269
+ driveId,
1219
1270
  documentId,
1220
1271
  document,
1221
1272
  nextOperation,
@@ -1256,7 +1307,7 @@ export class BaseDocumentDriveServer
1256
1307
 
1257
1308
  private async _addDocumentResultingStage<T extends Document>(
1258
1309
  document: DocumentStorage<T>,
1259
- drive: string,
1310
+ driveId: string,
1260
1311
  documentId?: string,
1261
1312
  options?: GetDocumentOptions,
1262
1313
  ): Promise<DocumentStorage<T>> {
@@ -1278,14 +1329,14 @@ export class BaseDocumentDriveServer
1278
1329
  if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
1279
1330
  lastRemainingOperation.resultingState = await (documentId
1280
1331
  ? this.storage.getOperationResultingState?.(
1281
- drive,
1332
+ driveId,
1282
1333
  documentId,
1283
1334
  lastRemainingOperation.index,
1284
1335
  lastRemainingOperation.scope,
1285
1336
  "main",
1286
1337
  )
1287
1338
  : this.storage.getDriveOperationResultingState?.(
1288
- drive,
1339
+ driveId,
1289
1340
  lastRemainingOperation.index,
1290
1341
  lastRemainingOperation.scope,
1291
1342
  "main",
@@ -1341,8 +1392,8 @@ export class BaseDocumentDriveServer
1341
1392
  }
1342
1393
 
1343
1394
  private async _performOperation<T extends Document>(
1344
- drive: string,
1345
- id: string | undefined,
1395
+ driveId: string,
1396
+ documentId: string | undefined,
1346
1397
  document: T,
1347
1398
  operation: Operation,
1348
1399
  skipHashValidation = false,
@@ -1366,16 +1417,16 @@ export class BaseDocumentDriveServer
1366
1417
  // if the latest operation doesn't have a resulting state then tries
1367
1418
  // to retrieve it from the db to avoid rerunning all the operations
1368
1419
  if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
1369
- lastRemainingOperation.resultingState = await (id
1420
+ lastRemainingOperation.resultingState = await (documentId
1370
1421
  ? this.storage.getOperationResultingState?.(
1371
- drive,
1372
- id,
1422
+ driveId,
1423
+ documentId,
1373
1424
  lastRemainingOperation.index,
1374
1425
  lastRemainingOperation.scope,
1375
1426
  "main",
1376
1427
  )
1377
1428
  : this.storage.getDriveOperationResultingState?.(
1378
- drive,
1429
+ driveId,
1379
1430
  lastRemainingOperation.index,
1380
1431
  lastRemainingOperation.scope,
1381
1432
  "main",
@@ -1390,20 +1441,21 @@ export class BaseDocumentDriveServer
1390
1441
  let handler: (() => Promise<unknown>) | undefined = undefined;
1391
1442
  switch (signal.type) {
1392
1443
  case "CREATE_CHILD_DOCUMENT":
1393
- handler = () => this.createDocument(drive, signal.input);
1444
+ handler = () => this.createDocument(driveId, signal.input);
1394
1445
  break;
1395
1446
  case "DELETE_CHILD_DOCUMENT":
1396
- handler = () => this.deleteDocument(drive, signal.input.id);
1447
+ handler = () => this.deleteDocument(driveId, signal.input.id);
1397
1448
  break;
1398
1449
  case "COPY_CHILD_DOCUMENT":
1399
1450
  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
- }),
1451
+ this.getDocument(driveId, signal.input.id).then(
1452
+ (documentToCopy) =>
1453
+ this.createDocument(driveId, {
1454
+ id: signal.input.newId,
1455
+ documentType: documentToCopy.documentType,
1456
+ document: documentToCopy,
1457
+ synchronizationUnits: signal.input.synchronizationUnits,
1458
+ }),
1407
1459
  );
1408
1460
  break;
1409
1461
  }
@@ -1449,50 +1501,53 @@ export class BaseDocumentDriveServer
1449
1501
  }
1450
1502
 
1451
1503
  addOperation(
1452
- drive: string,
1453
- id: string,
1504
+ driveId: string,
1505
+ documentId: string,
1454
1506
  operation: Operation,
1455
1507
  options?: AddOperationOptions,
1456
1508
  ): Promise<IOperationResult> {
1457
- return this.addOperations(drive, id, [operation], options);
1509
+ return this.addOperations(driveId, documentId, [operation], options);
1458
1510
  }
1459
1511
 
1460
1512
  private async _addOperations(
1461
- drive: string,
1462
- id: string,
1513
+ driveId: string,
1514
+ documentId: string,
1463
1515
  callback: (document: DocumentStorage) => Promise<{
1464
1516
  operations: Operation[];
1465
1517
  header: DocumentHeader;
1466
1518
  }>,
1467
1519
  ) {
1468
1520
  if (!this.storage.addDocumentOperationsWithTransaction) {
1469
- const documentStorage = await this.storage.getDocument(drive, id);
1521
+ const documentStorage = await this.storage.getDocument(
1522
+ driveId,
1523
+ documentId,
1524
+ );
1470
1525
  const result = await callback(documentStorage);
1471
1526
  // saves the applied operations to storage
1472
1527
  if (result.operations.length > 0) {
1473
1528
  await this.storage.addDocumentOperations(
1474
- drive,
1475
- id,
1529
+ driveId,
1530
+ documentId,
1476
1531
  result.operations,
1477
1532
  result.header,
1478
1533
  );
1479
1534
  }
1480
1535
  } else {
1481
1536
  await this.storage.addDocumentOperationsWithTransaction(
1482
- drive,
1483
- id,
1537
+ driveId,
1538
+ documentId,
1484
1539
  callback,
1485
1540
  );
1486
1541
  }
1487
1542
  }
1488
1543
 
1489
1544
  queueOperation(
1490
- drive: string,
1491
- id: string,
1545
+ driveId: string,
1546
+ documentId: string,
1492
1547
  operation: Operation,
1493
1548
  options?: AddOperationOptions,
1494
1549
  ): Promise<IOperationResult> {
1495
- return this.queueOperations(drive, id, [operation], options);
1550
+ return this.queueOperations(driveId, documentId, [operation], options);
1496
1551
  }
1497
1552
 
1498
1553
  private async resultIfExistingOperations(
@@ -1534,20 +1589,24 @@ export class BaseDocumentDriveServer
1534
1589
  }
1535
1590
 
1536
1591
  async queueOperations(
1537
- drive: string,
1538
- id: string,
1592
+ driveId: string,
1593
+ documentId: string,
1539
1594
  operations: Operation[],
1540
1595
  options?: AddOperationOptions,
1541
1596
  ) {
1542
1597
  // if operations are already stored then returns cached document
1543
- const result = await this.resultIfExistingOperations(drive, id, operations);
1598
+ const result = await this.resultIfExistingOperations(
1599
+ driveId,
1600
+ documentId,
1601
+ operations,
1602
+ );
1544
1603
  if (result) {
1545
1604
  return result;
1546
1605
  }
1547
1606
  try {
1548
1607
  const jobId = await this.queueManager.addJob({
1549
- driveId: drive,
1550
- documentId: id,
1608
+ driveId: driveId,
1609
+ documentId: documentId,
1551
1610
  operations,
1552
1611
  options,
1553
1612
  });
@@ -1581,24 +1640,24 @@ export class BaseDocumentDriveServer
1581
1640
  }
1582
1641
 
1583
1642
  async queueAction(
1584
- drive: string,
1585
- id: string,
1643
+ driveId: string,
1644
+ documentId: string,
1586
1645
  action: Action,
1587
1646
  options?: AddOperationOptions,
1588
1647
  ): Promise<IOperationResult> {
1589
- return this.queueActions(drive, id, [action], options);
1648
+ return this.queueActions(driveId, documentId, [action], options);
1590
1649
  }
1591
1650
 
1592
1651
  async queueActions(
1593
- drive: string,
1594
- id: string,
1652
+ driveId: string,
1653
+ documentId: string,
1595
1654
  actions: Action[],
1596
1655
  options?: AddOperationOptions,
1597
1656
  ): Promise<IOperationResult> {
1598
1657
  try {
1599
1658
  const jobId = await this.queueManager.addJob({
1600
- driveId: drive,
1601
- documentId: id,
1659
+ driveId: driveId,
1660
+ documentId: documentId,
1602
1661
  actions,
1603
1662
  options,
1604
1663
  });
@@ -1632,21 +1691,21 @@ export class BaseDocumentDriveServer
1632
1691
  }
1633
1692
 
1634
1693
  async queueDriveAction(
1635
- drive: string,
1694
+ driveId: string,
1636
1695
  action: DocumentDriveAction | BaseAction,
1637
1696
  options?: AddOperationOptions,
1638
1697
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1639
- return this.queueDriveActions(drive, [action], options);
1698
+ return this.queueDriveActions(driveId, [action], options);
1640
1699
  }
1641
1700
 
1642
1701
  async queueDriveActions(
1643
- drive: string,
1702
+ driveId: string,
1644
1703
  actions: (DocumentDriveAction | BaseAction)[],
1645
1704
  options?: AddOperationOptions,
1646
1705
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1647
1706
  try {
1648
1707
  const jobId = await this.queueManager.addJob({
1649
- driveId: drive,
1708
+ driveId: driveId,
1650
1709
  actions,
1651
1710
  options,
1652
1711
  });
@@ -1681,13 +1740,17 @@ export class BaseDocumentDriveServer
1681
1740
  }
1682
1741
 
1683
1742
  async addOperations(
1684
- drive: string,
1685
- id: string,
1743
+ driveId: string,
1744
+ documentId: string,
1686
1745
  operations: Operation[],
1687
1746
  options?: AddOperationOptions,
1688
1747
  ) {
1689
1748
  // if operations are already stored then returns the result
1690
- const result = await this.resultIfExistingOperations(drive, id, operations);
1749
+ const result = await this.resultIfExistingOperations(
1750
+ driveId,
1751
+ documentId,
1752
+ operations,
1753
+ );
1691
1754
  if (result) {
1692
1755
  return result;
1693
1756
  }
@@ -1697,33 +1760,39 @@ export class BaseDocumentDriveServer
1697
1760
  let error: Error | undefined;
1698
1761
 
1699
1762
  try {
1700
- await this._addOperations(drive, id, async (documentStorage) => {
1701
- const result = await this._processOperations(
1702
- drive,
1703
- id,
1704
- documentStorage,
1705
- operations,
1706
- );
1763
+ await this._addOperations(
1764
+ driveId,
1765
+ documentId,
1766
+ async (documentStorage) => {
1767
+ const result = await this._processOperations(
1768
+ driveId,
1769
+ documentId,
1770
+ documentStorage,
1771
+ operations,
1772
+ );
1707
1773
 
1708
- if (!result.document) {
1709
- logger.error("Invalid document");
1710
- throw result.error ?? new Error("Invalid document");
1711
- }
1774
+ if (!result.document) {
1775
+ logger.error("Invalid document");
1776
+ throw result.error ?? new Error("Invalid document");
1777
+ }
1712
1778
 
1713
- document = result.document;
1714
- error = result.error;
1715
- signals.push(...result.signals);
1716
- operationsApplied.push(...result.operationsApplied);
1779
+ document = result.document;
1780
+ error = result.error;
1781
+ signals.push(...result.signals);
1782
+ operationsApplied.push(...result.operationsApplied);
1717
1783
 
1718
- return {
1719
- operations: result.operationsApplied,
1720
- header: result.document,
1721
- newState: document.state,
1722
- };
1723
- });
1784
+ return {
1785
+ operations: result.operationsApplied,
1786
+ header: result.document,
1787
+ newState: document.state,
1788
+ };
1789
+ },
1790
+ );
1724
1791
 
1725
1792
  if (document) {
1726
- this.cache.setDocument(drive, id, document).catch(logger.error);
1793
+ this.cache
1794
+ .setDocument(driveId, documentId, document)
1795
+ .catch(logger.error);
1727
1796
  }
1728
1797
 
1729
1798
  // gets all the different scopes and branches combinations from the operations
@@ -1738,8 +1807,8 @@ export class BaseDocumentDriveServer
1738
1807
  );
1739
1808
 
1740
1809
  const syncUnits = await this.getSynchronizationUnits(
1741
- drive,
1742
- [id],
1810
+ driveId,
1811
+ [documentId],
1743
1812
  scopes,
1744
1813
  branches,
1745
1814
  );
@@ -1767,13 +1836,13 @@ export class BaseDocumentDriveServer
1767
1836
 
1768
1837
  const operationSource = this.getOperationSource(source);
1769
1838
 
1770
- this.listenerStateManager
1839
+ this.listenerManager
1771
1840
  .updateSynchronizationRevisions(
1772
- drive,
1841
+ driveId,
1773
1842
  syncUnits,
1774
1843
  source,
1775
1844
  () => {
1776
- this.updateSyncUnitStatus(drive, {
1845
+ this.updateSyncUnitStatus(driveId, {
1777
1846
  [operationSource]: "SYNCING",
1778
1847
  });
1779
1848
 
@@ -1788,7 +1857,7 @@ export class BaseDocumentDriveServer
1788
1857
  )
1789
1858
  .then((updates) => {
1790
1859
  if (updates.length) {
1791
- this.updateSyncUnitStatus(drive, {
1860
+ this.updateSyncUnitStatus(driveId, {
1792
1861
  [operationSource]: "SUCCESS",
1793
1862
  });
1794
1863
  }
@@ -1802,7 +1871,7 @@ export class BaseDocumentDriveServer
1802
1871
  .catch((error) => {
1803
1872
  logger.error("Non handled error updating sync revision", error);
1804
1873
  this.updateSyncUnitStatus(
1805
- drive,
1874
+ driveId,
1806
1875
  {
1807
1876
  [operationSource]: "ERROR",
1808
1877
  },
@@ -1854,11 +1923,11 @@ export class BaseDocumentDriveServer
1854
1923
  }
1855
1924
 
1856
1925
  addDriveOperation(
1857
- drive: string,
1926
+ driveId: string,
1858
1927
  operation: Operation<DocumentDriveAction | BaseAction>,
1859
1928
  options?: AddOperationOptions,
1860
1929
  ) {
1861
- return this.addDriveOperations(drive, [operation], options);
1930
+ return this.addDriveOperations(driveId, [operation], options);
1862
1931
  }
1863
1932
 
1864
1933
  async clearStorage() {
@@ -1870,35 +1939,35 @@ export class BaseDocumentDriveServer
1870
1939
  }
1871
1940
 
1872
1941
  private async _addDriveOperations(
1873
- drive: string,
1942
+ driveId: string,
1874
1943
  callback: (document: DocumentDriveStorage) => Promise<{
1875
1944
  operations: Operation<DocumentDriveAction | BaseAction>[];
1876
1945
  header: DocumentHeader;
1877
1946
  }>,
1878
1947
  ) {
1879
1948
  if (!this.storage.addDriveOperationsWithTransaction) {
1880
- const documentStorage = await this.storage.getDrive(drive);
1949
+ const documentStorage = await this.storage.getDrive(driveId);
1881
1950
  const result = await callback(documentStorage);
1882
1951
  // saves the applied operations to storage
1883
1952
  if (result.operations.length > 0) {
1884
1953
  await this.storage.addDriveOperations(
1885
- drive,
1954
+ driveId,
1886
1955
  result.operations,
1887
1956
  result.header,
1888
1957
  );
1889
1958
  }
1890
1959
  return result;
1891
1960
  } else {
1892
- return this.storage.addDriveOperationsWithTransaction(drive, callback);
1961
+ return this.storage.addDriveOperationsWithTransaction(driveId, callback);
1893
1962
  }
1894
1963
  }
1895
1964
 
1896
1965
  queueDriveOperation(
1897
- drive: string,
1966
+ driveId: string,
1898
1967
  operation: Operation<DocumentDriveAction | BaseAction>,
1899
1968
  options?: AddOperationOptions,
1900
1969
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1901
- return this.queueDriveOperations(drive, [operation], options);
1970
+ return this.queueDriveOperations(driveId, [operation], options);
1902
1971
  }
1903
1972
 
1904
1973
  private async resultIfExistingDriveOperations(
@@ -1935,13 +2004,13 @@ export class BaseDocumentDriveServer
1935
2004
  }
1936
2005
 
1937
2006
  async queueDriveOperations(
1938
- drive: string,
2007
+ driveId: string,
1939
2008
  operations: Operation<DocumentDriveAction | BaseAction>[],
1940
2009
  options?: AddOperationOptions,
1941
2010
  ): Promise<IOperationResult<DocumentDriveDocument>> {
1942
2011
  // if operations are already stored then returns cached document
1943
2012
  const result = await this.resultIfExistingDriveOperations(
1944
- drive,
2013
+ driveId,
1945
2014
  operations,
1946
2015
  );
1947
2016
  if (result) {
@@ -1949,7 +2018,7 @@ export class BaseDocumentDriveServer
1949
2018
  }
1950
2019
  try {
1951
2020
  const jobId = await this.queueManager.addJob({
1952
- driveId: drive,
2021
+ driveId: driveId,
1953
2022
  operations,
1954
2023
  options,
1955
2024
  });
@@ -1984,7 +2053,7 @@ export class BaseDocumentDriveServer
1984
2053
  }
1985
2054
 
1986
2055
  async addDriveOperations(
1987
- drive: string,
2056
+ driveId: string,
1988
2057
  operations: Operation<DocumentDriveAction | BaseAction>[],
1989
2058
  options?: AddOperationOptions,
1990
2059
  ) {
@@ -1995,7 +2064,7 @@ export class BaseDocumentDriveServer
1995
2064
 
1996
2065
  // if operations are already stored then returns cached drive
1997
2066
  const result = await this.resultIfExistingDriveOperations(
1998
- drive,
2067
+ driveId,
1999
2068
  operations,
2000
2069
  );
2001
2070
  if (result) {
@@ -2003,11 +2072,11 @@ export class BaseDocumentDriveServer
2003
2072
  }
2004
2073
 
2005
2074
  try {
2006
- await this._addDriveOperations(drive, async (documentStorage) => {
2075
+ await this._addDriveOperations(driveId, async (documentStorage) => {
2007
2076
  const result = await this._processOperations<
2008
2077
  DocumentDriveDocument,
2009
2078
  DocumentDriveAction
2010
- >(drive, undefined, documentStorage, operations.slice());
2079
+ >(driveId, undefined, documentStorage, operations.slice());
2011
2080
  document = result.document;
2012
2081
  operationsApplied.push(...result.operationsApplied);
2013
2082
  signals.push(...result.signals);
@@ -2023,16 +2092,46 @@ export class BaseDocumentDriveServer
2023
2092
  throw error ?? new Error("Invalid Document Drive document");
2024
2093
  }
2025
2094
 
2026
- this.cache.setDocument("drives", drive, document).catch(logger.error);
2095
+ this.cache.setDocument("drives", driveId, document).catch(logger.error);
2027
2096
 
2028
2097
  for (const operation of operationsApplied) {
2029
2098
  switch (operation.type) {
2030
2099
  case "ADD_LISTENER": {
2031
- await this.addListener(drive, operation);
2100
+ const zodListener = operation.input.listener;
2101
+
2102
+ // create the transmitter
2103
+ const transmitter = this.transmitterFactory.instance(
2104
+ zodListener.callInfo?.transmitterType ?? "",
2105
+ zodListener as any,
2106
+ this,
2107
+ );
2108
+
2109
+ // create the listener
2110
+ const listener = {
2111
+ ...zodListener,
2112
+ driveId: driveId,
2113
+ label: zodListener.label ?? "",
2114
+ system: zodListener.system ?? false,
2115
+ filter: {
2116
+ branch: zodListener.filter.branch ?? [],
2117
+ documentId: zodListener.filter.documentId ?? [],
2118
+ documentType: zodListener.filter.documentType ?? [],
2119
+ scope: zodListener.filter.scope ?? [],
2120
+ },
2121
+ callInfo: {
2122
+ data: zodListener.callInfo?.data ?? "",
2123
+ name: zodListener.callInfo?.name ?? "PullResponder",
2124
+ transmitterType:
2125
+ zodListener.callInfo?.transmitterType ?? "PullResponder",
2126
+ },
2127
+ transmitter,
2128
+ };
2129
+
2130
+ await this.addListener(driveId, listener);
2032
2131
  break;
2033
2132
  }
2034
2133
  case "REMOVE_LISTENER": {
2035
- await this.removeListener(drive, operation);
2134
+ await this.removeListener(driveId, operation);
2036
2135
  break;
2037
2136
  }
2038
2137
  }
@@ -2066,13 +2165,13 @@ export class BaseDocumentDriveServer
2066
2165
 
2067
2166
  const operationSource = this.getOperationSource(source);
2068
2167
 
2069
- this.listenerStateManager
2168
+ this.listenerManager
2070
2169
  .updateSynchronizationRevisions(
2071
- drive,
2170
+ driveId,
2072
2171
  [
2073
2172
  {
2074
2173
  syncId: "0",
2075
- driveId: drive,
2174
+ driveId: driveId,
2076
2175
  documentId: "",
2077
2176
  scope: "global",
2078
2177
  branch: "main",
@@ -2083,7 +2182,7 @@ export class BaseDocumentDriveServer
2083
2182
  ],
2084
2183
  source,
2085
2184
  () => {
2086
- this.updateSyncUnitStatus(drive, {
2185
+ this.updateSyncUnitStatus(driveId, {
2087
2186
  [operationSource]: "SYNCING",
2088
2187
  });
2089
2188
  },
@@ -2092,7 +2191,7 @@ export class BaseDocumentDriveServer
2092
2191
  )
2093
2192
  .then((updates) => {
2094
2193
  if (updates.length) {
2095
- this.updateSyncUnitStatus(drive, {
2194
+ this.updateSyncUnitStatus(driveId, {
2096
2195
  [operationSource]: "SUCCESS",
2097
2196
  });
2098
2197
  }
@@ -2100,7 +2199,7 @@ export class BaseDocumentDriveServer
2100
2199
  .catch((error) => {
2101
2200
  logger.error("Non handled error updating sync revision", error);
2102
2201
  this.updateSyncUnitStatus(
2103
- drive,
2202
+ driveId,
2104
2203
  { [operationSource]: "ERROR" },
2105
2204
  error as Error,
2106
2205
  );
@@ -2147,14 +2246,14 @@ export class BaseDocumentDriveServer
2147
2246
  }
2148
2247
 
2149
2248
  private _buildOperations<T extends Action>(
2150
- document: Document,
2249
+ documentId: Document,
2151
2250
  actions: (T | BaseAction)[],
2152
2251
  ): Operation<T | BaseAction>[] {
2153
2252
  const operations: Operation<T | BaseAction>[] = [];
2154
- const { reducer } = this.getDocumentModel(document.documentType);
2253
+ const { reducer } = this.getDocumentModel(documentId.documentType);
2155
2254
  for (const action of actions) {
2156
- document = reducer(document, action);
2157
- const operation = document.operations[action.scope].slice().pop();
2255
+ documentId = reducer(documentId, action);
2256
+ const operation = documentId.operations[action.scope].slice().pop();
2158
2257
  if (!operation) {
2159
2258
  throw new Error("Error creating operations");
2160
2259
  }
@@ -2164,44 +2263,73 @@ export class BaseDocumentDriveServer
2164
2263
  }
2165
2264
 
2166
2265
  async addAction(
2167
- drive: string,
2168
- id: string,
2266
+ driveId: string,
2267
+ documentId: string,
2169
2268
  action: Action,
2170
2269
  options?: AddOperationOptions,
2171
2270
  ): Promise<IOperationResult> {
2172
- return this.addActions(drive, id, [action], options);
2271
+ return this.addActions(driveId, documentId, [action], options);
2173
2272
  }
2174
2273
 
2175
2274
  async addActions(
2176
- drive: string,
2177
- id: string,
2275
+ driveId: string,
2276
+ documentId: string,
2178
2277
  actions: Action[],
2179
2278
  options?: AddOperationOptions,
2180
2279
  ): Promise<IOperationResult> {
2181
- const document = await this.getDocument(drive, id);
2280
+ const document = await this.getDocument(driveId, documentId);
2182
2281
  const operations = this._buildOperations(document, actions);
2183
- return this.addOperations(drive, id, operations, options);
2282
+ return this.addOperations(driveId, documentId, operations, options);
2184
2283
  }
2185
2284
 
2186
2285
  async addDriveAction(
2187
- drive: string,
2286
+ driveId: string,
2188
2287
  action: DocumentDriveAction | BaseAction,
2189
2288
  options?: AddOperationOptions,
2190
2289
  ): Promise<IOperationResult<DocumentDriveDocument>> {
2191
- return this.addDriveActions(drive, [action], options);
2290
+ return this.addDriveActions(driveId, [action], options);
2192
2291
  }
2193
2292
 
2194
2293
  async addDriveActions(
2195
- drive: string,
2294
+ driveId: string,
2196
2295
  actions: (DocumentDriveAction | BaseAction)[],
2197
2296
  options?: AddOperationOptions,
2198
2297
  ): Promise<IOperationResult<DocumentDriveDocument>> {
2199
- const document = await this.getDrive(drive);
2298
+ const document = await this.getDrive(driveId);
2200
2299
  const operations = this._buildOperations(document, actions);
2201
- const result = await this.addDriveOperations(drive, operations, options);
2300
+ const result = await this.addDriveOperations(driveId, operations, options);
2202
2301
  return result;
2203
2302
  }
2204
2303
 
2304
+ async detachDrive(driveId: string) {
2305
+ const documentDrive = await this.getDrive(driveId);
2306
+ const listeners = documentDrive.state.local.listeners || [];
2307
+ const triggers = documentDrive.state.local.triggers || [];
2308
+
2309
+ for (const listener of listeners) {
2310
+ await this.addDriveAction(
2311
+ driveId,
2312
+ actions.removeListener({ listenerId: listener.listenerId }),
2313
+ );
2314
+ }
2315
+
2316
+ for (const trigger of triggers) {
2317
+ await this.addDriveAction(
2318
+ driveId,
2319
+ actions.removeTrigger({ triggerId: trigger.id }),
2320
+ );
2321
+ }
2322
+
2323
+ await this.addDriveAction(
2324
+ driveId,
2325
+ actions.setSharingType({ type: "LOCAL" }),
2326
+ );
2327
+ }
2328
+
2329
+ async addListener(driveId: string, listener: Listener) {
2330
+ await this.listenerManager.setListener(driveId, listener);
2331
+ }
2332
+
2205
2333
  async addInternalListener(
2206
2334
  driveId: string,
2207
2335
  receiver: IReceiver,
@@ -2236,75 +2364,40 @@ export class BaseDocumentDriveServer
2236
2364
  return transmitter;
2237
2365
  }
2238
2366
 
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
2367
  private async removeListener(
2289
2368
  driveId: string,
2290
2369
  operation: Operation<Action<"REMOVE_LISTENER", RemoveListenerInput>>,
2291
2370
  ) {
2292
2371
  const { listenerId } = operation.input;
2293
- await this.listenerStateManager.removeListener(driveId, listenerId);
2372
+ await this.listenerManager.removeListener(driveId, listenerId);
2294
2373
  }
2295
2374
 
2296
- getTransmitter(
2375
+ async getTransmitter(
2297
2376
  driveId: string,
2298
2377
  listenerId: string,
2299
2378
  ): Promise<ITransmitter | undefined> {
2300
- return this.listenerStateManager.getTransmitter(driveId, listenerId);
2379
+ const listener = await this.listenerManager.getListenerState(
2380
+ driveId,
2381
+ listenerId,
2382
+ );
2383
+ return listener?.listener.transmitter;
2301
2384
  }
2302
2385
 
2303
2386
  getListener(
2304
2387
  driveId: string,
2305
2388
  listenerId: string,
2306
2389
  ): Promise<ListenerState | undefined> {
2307
- return this.listenerStateManager.getListener(driveId, listenerId);
2390
+ let listenerState;
2391
+ try {
2392
+ listenerState = this.listenerManager.getListenerState(
2393
+ driveId,
2394
+ listenerId,
2395
+ );
2396
+ } catch {
2397
+ return Promise.resolve(undefined);
2398
+ }
2399
+
2400
+ return Promise.resolve(listenerState);
2308
2401
  }
2309
2402
 
2310
2403
  getSyncStatus(