document-drive 1.0.0-alpha.77 → 1.0.0-alpha.78
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/package.json +1 -1
- package/src/queue/types.ts +2 -2
- package/src/server/index.ts +93 -46
- package/src/server/listener/manager.ts +20 -9
- package/src/server/listener/transmitter/pull-responder.ts +17 -4
- package/src/server/listener/transmitter/switchboard-push.ts +20 -3
- package/src/server/listener/transmitter/types.ts +10 -2
- package/src/server/types.ts +30 -22
package/package.json
CHANGED
package/src/queue/types.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Action, Operation } from "document-model/document";
|
|
2
|
-
import { IOperationResult } from "../server";
|
|
2
|
+
import { AddOperationOptions, IOperationResult } from "../server";
|
|
3
3
|
import type { Unsubscribe } from "nanoevents";
|
|
4
4
|
|
|
5
5
|
export interface BaseJob {
|
|
6
6
|
driveId: string;
|
|
7
7
|
documentId?: string
|
|
8
8
|
actions?: Action[]
|
|
9
|
-
|
|
9
|
+
options?: AddOperationOptions;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface OperationJob extends BaseJob {
|
package/src/server/index.ts
CHANGED
|
@@ -60,9 +60,11 @@ import {
|
|
|
60
60
|
InternalTransmitter,
|
|
61
61
|
IReceiver,
|
|
62
62
|
ITransmitter,
|
|
63
|
-
PullResponderTransmitter
|
|
63
|
+
PullResponderTransmitter,
|
|
64
|
+
StrandUpdateSource
|
|
64
65
|
} from './listener/transmitter';
|
|
65
66
|
import {
|
|
67
|
+
AddOperationOptions,
|
|
66
68
|
BaseDocumentDriveServer,
|
|
67
69
|
DriveEvents,
|
|
68
70
|
GetDocumentOptions,
|
|
@@ -138,7 +140,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
141
|
-
private async saveStrand(strand: StrandUpdate) {
|
|
143
|
+
private async saveStrand(strand: StrandUpdate, source: StrandUpdateSource) {
|
|
142
144
|
const operations: Operation[] = strand.operations.map(op => ({
|
|
143
145
|
...op,
|
|
144
146
|
scope: strand.scope,
|
|
@@ -149,13 +151,13 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
149
151
|
? this.queueDriveOperations(
|
|
150
152
|
strand.driveId,
|
|
151
153
|
operations as Operation<DocumentDriveAction | BaseAction>[],
|
|
152
|
-
|
|
154
|
+
{ source }
|
|
153
155
|
)
|
|
154
156
|
: this.queueOperations(
|
|
155
157
|
strand.driveId,
|
|
156
158
|
strand.documentId,
|
|
157
159
|
operations,
|
|
158
|
-
|
|
160
|
+
{ source }
|
|
159
161
|
));
|
|
160
162
|
|
|
161
163
|
if (result.status === 'ERROR') {
|
|
@@ -283,30 +285,30 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
283
285
|
driveId,
|
|
284
286
|
documentId,
|
|
285
287
|
operations,
|
|
286
|
-
|
|
288
|
+
options
|
|
287
289
|
}: OperationJob) => {
|
|
288
290
|
return documentId
|
|
289
|
-
? this.addOperations(driveId, documentId, operations,
|
|
291
|
+
? this.addOperations(driveId, documentId, operations, options)
|
|
290
292
|
: this.addDriveOperations(
|
|
291
293
|
driveId,
|
|
292
294
|
operations as Operation<
|
|
293
295
|
DocumentDriveAction | BaseAction
|
|
294
296
|
>[],
|
|
295
|
-
|
|
297
|
+
options
|
|
296
298
|
);
|
|
297
299
|
},
|
|
298
300
|
processActionJob: async ({
|
|
299
301
|
driveId,
|
|
300
302
|
documentId,
|
|
301
303
|
actions,
|
|
302
|
-
|
|
304
|
+
options
|
|
303
305
|
}: ActionJob) => {
|
|
304
306
|
return documentId
|
|
305
|
-
? this.addActions(driveId, documentId, actions,
|
|
307
|
+
? this.addActions(driveId, documentId, actions, options)
|
|
306
308
|
: this.addDriveActions(
|
|
307
309
|
driveId,
|
|
308
310
|
actions as Operation<DocumentDriveAction | BaseAction>[],
|
|
309
|
-
|
|
311
|
+
options
|
|
310
312
|
);
|
|
311
313
|
},
|
|
312
314
|
processJob: async (job: Job) => {
|
|
@@ -335,12 +337,16 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
335
337
|
errors.push(error);
|
|
336
338
|
});
|
|
337
339
|
|
|
338
|
-
// if network connect comes online
|
|
339
|
-
// triggers the listeners update
|
|
340
|
+
// if network connect comes back online
|
|
341
|
+
// then triggers the listeners update
|
|
340
342
|
if (typeof window !== 'undefined') {
|
|
341
343
|
window.addEventListener('online', () => {
|
|
342
344
|
this.listenerStateManager
|
|
343
|
-
.triggerUpdate(
|
|
345
|
+
.triggerUpdate(
|
|
346
|
+
false,
|
|
347
|
+
{ type: 'local' },
|
|
348
|
+
this.handleListenerError.bind(this)
|
|
349
|
+
)
|
|
344
350
|
.catch(error => {
|
|
345
351
|
logger.error(
|
|
346
352
|
'Non handled error updating listeners',
|
|
@@ -1072,9 +1078,9 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1072
1078
|
drive: string,
|
|
1073
1079
|
id: string,
|
|
1074
1080
|
operation: Operation,
|
|
1075
|
-
|
|
1081
|
+
options?: AddOperationOptions
|
|
1076
1082
|
): Promise<IOperationResult> {
|
|
1077
|
-
return this.addOperations(drive, id, [operation],
|
|
1083
|
+
return this.addOperations(drive, id, [operation], options);
|
|
1078
1084
|
}
|
|
1079
1085
|
|
|
1080
1086
|
private async _addOperations(
|
|
@@ -1110,9 +1116,9 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1110
1116
|
drive: string,
|
|
1111
1117
|
id: string,
|
|
1112
1118
|
operation: Operation,
|
|
1113
|
-
|
|
1119
|
+
options?: AddOperationOptions
|
|
1114
1120
|
): Promise<IOperationResult> {
|
|
1115
|
-
return this.queueOperations(drive, id, [operation],
|
|
1121
|
+
return this.queueOperations(drive, id, [operation], options);
|
|
1116
1122
|
}
|
|
1117
1123
|
|
|
1118
1124
|
private async resultIfExistingOperations(
|
|
@@ -1153,7 +1159,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1153
1159
|
drive: string,
|
|
1154
1160
|
id: string,
|
|
1155
1161
|
operations: Operation[],
|
|
1156
|
-
|
|
1162
|
+
options?: AddOperationOptions
|
|
1157
1163
|
) {
|
|
1158
1164
|
// if operations are already stored then returns cached document
|
|
1159
1165
|
const result = await this.resultIfExistingOperations(
|
|
@@ -1162,7 +1168,6 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1162
1168
|
operations
|
|
1163
1169
|
);
|
|
1164
1170
|
if (result) {
|
|
1165
|
-
console.log('Duplicated operations!');
|
|
1166
1171
|
return result;
|
|
1167
1172
|
}
|
|
1168
1173
|
try {
|
|
@@ -1170,7 +1175,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1170
1175
|
driveId: drive,
|
|
1171
1176
|
documentId: id,
|
|
1172
1177
|
operations,
|
|
1173
|
-
|
|
1178
|
+
options
|
|
1174
1179
|
});
|
|
1175
1180
|
|
|
1176
1181
|
return new Promise<IOperationResult>((resolve, reject) => {
|
|
@@ -1205,23 +1210,23 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1205
1210
|
drive: string,
|
|
1206
1211
|
id: string,
|
|
1207
1212
|
action: Action,
|
|
1208
|
-
|
|
1213
|
+
options?: AddOperationOptions
|
|
1209
1214
|
): Promise<IOperationResult> {
|
|
1210
|
-
return this.queueActions(drive, id, [action],
|
|
1215
|
+
return this.queueActions(drive, id, [action], options);
|
|
1211
1216
|
}
|
|
1212
1217
|
|
|
1213
1218
|
async queueActions(
|
|
1214
1219
|
drive: string,
|
|
1215
1220
|
id: string,
|
|
1216
1221
|
actions: Action[],
|
|
1217
|
-
|
|
1222
|
+
options?: AddOperationOptions
|
|
1218
1223
|
): Promise<IOperationResult> {
|
|
1219
1224
|
try {
|
|
1220
1225
|
const jobId = await this.queueManager.addJob({
|
|
1221
1226
|
driveId: drive,
|
|
1222
1227
|
documentId: id,
|
|
1223
1228
|
actions,
|
|
1224
|
-
|
|
1229
|
+
options
|
|
1225
1230
|
});
|
|
1226
1231
|
|
|
1227
1232
|
return new Promise<IOperationResult>((resolve, reject) => {
|
|
@@ -1255,20 +1260,20 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1255
1260
|
async queueDriveAction(
|
|
1256
1261
|
drive: string,
|
|
1257
1262
|
action: DocumentDriveAction | BaseAction,
|
|
1258
|
-
|
|
1263
|
+
options?: AddOperationOptions
|
|
1259
1264
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1260
|
-
return this.queueDriveActions(drive, [action],
|
|
1265
|
+
return this.queueDriveActions(drive, [action], options);
|
|
1261
1266
|
}
|
|
1262
1267
|
|
|
1263
1268
|
async queueDriveActions(
|
|
1264
1269
|
drive: string,
|
|
1265
1270
|
actions: (DocumentDriveAction | BaseAction)[],
|
|
1266
|
-
|
|
1271
|
+
options?: AddOperationOptions
|
|
1267
1272
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1268
1273
|
const jobId = await this.queueManager.addJob({
|
|
1269
1274
|
driveId: drive,
|
|
1270
1275
|
actions,
|
|
1271
|
-
|
|
1276
|
+
options
|
|
1272
1277
|
});
|
|
1273
1278
|
return new Promise<IOperationResult<DocumentDriveDocument>>(
|
|
1274
1279
|
(resolve, reject) => {
|
|
@@ -1302,7 +1307,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1302
1307
|
drive: string,
|
|
1303
1308
|
id: string,
|
|
1304
1309
|
operations: Operation[],
|
|
1305
|
-
|
|
1310
|
+
options?: AddOperationOptions
|
|
1306
1311
|
) {
|
|
1307
1312
|
// if operations are already stored then returns the result
|
|
1308
1313
|
const result = await this.resultIfExistingOperations(
|
|
@@ -1365,11 +1370,32 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1365
1370
|
scopes,
|
|
1366
1371
|
branches
|
|
1367
1372
|
);
|
|
1373
|
+
|
|
1374
|
+
// checks if any of the provided operations where reshufled
|
|
1375
|
+
const newOp = operationsApplied.find(
|
|
1376
|
+
appliedOp =>
|
|
1377
|
+
!operations.find(
|
|
1378
|
+
o =>
|
|
1379
|
+
o.id === appliedOp.id &&
|
|
1380
|
+
o.index === appliedOp.index &&
|
|
1381
|
+
o.skip === appliedOp.skip &&
|
|
1382
|
+
o.hash === appliedOp.hash
|
|
1383
|
+
)
|
|
1384
|
+
);
|
|
1385
|
+
|
|
1386
|
+
// if there are no new operations then reuses the provided source
|
|
1387
|
+
// otherwise sets it to local so listeners know that there were
|
|
1388
|
+
// new changes originating from this document drive server
|
|
1389
|
+
const source: StrandUpdateSource = newOp
|
|
1390
|
+
? { type: 'local' }
|
|
1391
|
+
: options?.source ?? { type: 'local' };
|
|
1392
|
+
|
|
1368
1393
|
// update listener cache
|
|
1369
1394
|
this.listenerStateManager
|
|
1370
1395
|
.updateSynchronizationRevisions(
|
|
1371
1396
|
drive,
|
|
1372
1397
|
syncUnits,
|
|
1398
|
+
source,
|
|
1373
1399
|
() => {
|
|
1374
1400
|
this.updateSyncStatus(drive, 'SYNCING');
|
|
1375
1401
|
|
|
@@ -1378,7 +1404,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1378
1404
|
}
|
|
1379
1405
|
},
|
|
1380
1406
|
this.handleListenerError.bind(this),
|
|
1381
|
-
forceSync
|
|
1407
|
+
options?.forceSync ?? source.type === 'local'
|
|
1382
1408
|
)
|
|
1383
1409
|
.then(updates => {
|
|
1384
1410
|
updates.length && this.updateSyncStatus(drive, 'SUCCESS');
|
|
@@ -1439,9 +1465,9 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1439
1465
|
addDriveOperation(
|
|
1440
1466
|
drive: string,
|
|
1441
1467
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
1442
|
-
|
|
1468
|
+
options?: AddOperationOptions
|
|
1443
1469
|
) {
|
|
1444
|
-
return this.addDriveOperations(drive, [operation],
|
|
1470
|
+
return this.addDriveOperations(drive, [operation], options);
|
|
1445
1471
|
}
|
|
1446
1472
|
|
|
1447
1473
|
async clearStorage() {
|
|
@@ -1482,9 +1508,9 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1482
1508
|
queueDriveOperation(
|
|
1483
1509
|
drive: string,
|
|
1484
1510
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
1485
|
-
|
|
1511
|
+
options?: AddOperationOptions
|
|
1486
1512
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1487
|
-
return this.queueDriveOperations(drive, [operation],
|
|
1513
|
+
return this.queueDriveOperations(drive, [operation], options);
|
|
1488
1514
|
}
|
|
1489
1515
|
|
|
1490
1516
|
private async resultIfExistingDriveOperations(
|
|
@@ -1523,7 +1549,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1523
1549
|
async queueDriveOperations(
|
|
1524
1550
|
drive: string,
|
|
1525
1551
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
1526
|
-
|
|
1552
|
+
options?: AddOperationOptions
|
|
1527
1553
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1528
1554
|
// if operations are already stored then returns cached document
|
|
1529
1555
|
const result = await this.resultIfExistingDriveOperations(
|
|
@@ -1537,7 +1563,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1537
1563
|
const jobId = await this.queueManager.addJob({
|
|
1538
1564
|
driveId: drive,
|
|
1539
1565
|
operations,
|
|
1540
|
-
|
|
1566
|
+
options
|
|
1541
1567
|
});
|
|
1542
1568
|
return new Promise<IOperationResult<DocumentDriveDocument>>(
|
|
1543
1569
|
(resolve, reject) => {
|
|
@@ -1570,7 +1596,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1570
1596
|
async addDriveOperations(
|
|
1571
1597
|
drive: string,
|
|
1572
1598
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
1573
|
-
|
|
1599
|
+
options?: AddOperationOptions
|
|
1574
1600
|
) {
|
|
1575
1601
|
let document: DocumentDriveDocument | undefined;
|
|
1576
1602
|
const operationsApplied: Operation<DocumentDriveAction | BaseAction>[] =
|
|
@@ -1646,7 +1672,27 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1646
1672
|
.filter(op => op.scope === 'global')
|
|
1647
1673
|
.slice()
|
|
1648
1674
|
.pop();
|
|
1675
|
+
|
|
1649
1676
|
if (lastOperation) {
|
|
1677
|
+
// checks if any of the provided operations where reshufled
|
|
1678
|
+
const newOp = operationsApplied.find(
|
|
1679
|
+
appliedOp =>
|
|
1680
|
+
!operations.find(
|
|
1681
|
+
o =>
|
|
1682
|
+
o.id === appliedOp.id &&
|
|
1683
|
+
o.index === appliedOp.index &&
|
|
1684
|
+
o.skip === appliedOp.skip &&
|
|
1685
|
+
o.hash === appliedOp.hash
|
|
1686
|
+
)
|
|
1687
|
+
);
|
|
1688
|
+
|
|
1689
|
+
// if there are no new operations then reuses the provided source
|
|
1690
|
+
// otherwise sets it to local so listeners know that there were
|
|
1691
|
+
// new changes originating from this document drive server
|
|
1692
|
+
const source: StrandUpdateSource = newOp
|
|
1693
|
+
? { type: 'local' }
|
|
1694
|
+
: options?.source ?? { type: 'local' };
|
|
1695
|
+
|
|
1650
1696
|
this.listenerStateManager
|
|
1651
1697
|
.updateSynchronizationRevisions(
|
|
1652
1698
|
drive,
|
|
@@ -1662,6 +1708,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1662
1708
|
revision: lastOperation.index
|
|
1663
1709
|
}
|
|
1664
1710
|
],
|
|
1711
|
+
source,
|
|
1665
1712
|
() => {
|
|
1666
1713
|
this.updateSyncStatus(drive, 'SYNCING');
|
|
1667
1714
|
|
|
@@ -1673,7 +1720,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1673
1720
|
}
|
|
1674
1721
|
},
|
|
1675
1722
|
this.handleListenerError.bind(this),
|
|
1676
|
-
forceSync
|
|
1723
|
+
options?.forceSync ?? source.type === 'local'
|
|
1677
1724
|
)
|
|
1678
1725
|
.then(updates => {
|
|
1679
1726
|
if (updates.length) {
|
|
@@ -1768,41 +1815,41 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1768
1815
|
drive: string,
|
|
1769
1816
|
id: string,
|
|
1770
1817
|
action: Action,
|
|
1771
|
-
|
|
1818
|
+
options?: AddOperationOptions
|
|
1772
1819
|
): Promise<IOperationResult> {
|
|
1773
|
-
return this.addActions(drive, id, [action],
|
|
1820
|
+
return this.addActions(drive, id, [action], options);
|
|
1774
1821
|
}
|
|
1775
1822
|
|
|
1776
1823
|
async addActions(
|
|
1777
1824
|
drive: string,
|
|
1778
1825
|
id: string,
|
|
1779
1826
|
actions: Action[],
|
|
1780
|
-
|
|
1827
|
+
options?: AddOperationOptions
|
|
1781
1828
|
): Promise<IOperationResult> {
|
|
1782
1829
|
const document = await this.getDocument(drive, id);
|
|
1783
1830
|
const operations = this._buildOperations(document, actions);
|
|
1784
|
-
return this.addOperations(drive, id, operations,
|
|
1831
|
+
return this.addOperations(drive, id, operations, options);
|
|
1785
1832
|
}
|
|
1786
1833
|
|
|
1787
1834
|
async addDriveAction(
|
|
1788
1835
|
drive: string,
|
|
1789
1836
|
action: DocumentDriveAction | BaseAction,
|
|
1790
|
-
|
|
1837
|
+
options?: AddOperationOptions
|
|
1791
1838
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1792
|
-
return this.addDriveActions(drive, [action],
|
|
1839
|
+
return this.addDriveActions(drive, [action], options);
|
|
1793
1840
|
}
|
|
1794
1841
|
|
|
1795
1842
|
async addDriveActions(
|
|
1796
1843
|
drive: string,
|
|
1797
1844
|
actions: (DocumentDriveAction | BaseAction)[],
|
|
1798
|
-
|
|
1845
|
+
options?: AddOperationOptions
|
|
1799
1846
|
): Promise<IOperationResult<DocumentDriveDocument>> {
|
|
1800
1847
|
const document = await this.getDrive(drive);
|
|
1801
1848
|
const operations = this._buildOperations(document, actions);
|
|
1802
1849
|
const result = await this.addDriveOperations(
|
|
1803
1850
|
drive,
|
|
1804
1851
|
operations,
|
|
1805
|
-
|
|
1852
|
+
options
|
|
1806
1853
|
);
|
|
1807
1854
|
return result;
|
|
1808
1855
|
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ListenerFilter
|
|
4
4
|
} from 'document-model-libs/document-drive';
|
|
5
5
|
import { OperationScope } from 'document-model/document';
|
|
6
|
+
import { logger } from '../../utils/logger';
|
|
6
7
|
import { OperationError } from '../error';
|
|
7
8
|
import {
|
|
8
9
|
BaseListenerManager,
|
|
@@ -17,8 +18,7 @@ import {
|
|
|
17
18
|
import { PullResponderTransmitter } from './transmitter';
|
|
18
19
|
import { InternalTransmitter } from './transmitter/internal';
|
|
19
20
|
import { SwitchboardPushTransmitter } from './transmitter/switchboard-push';
|
|
20
|
-
import { ITransmitter } from './transmitter/types';
|
|
21
|
-
import { logger } from '../../utils/logger';
|
|
21
|
+
import { ITransmitter, StrandUpdateSource } from './transmitter/types';
|
|
22
22
|
|
|
23
23
|
function debounce<T extends unknown[], R>(
|
|
24
24
|
func: (...args: T) => Promise<R>,
|
|
@@ -118,7 +118,10 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
118
118
|
return Promise.resolve(driveMap.delete(listenerId));
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
async removeSyncUnits(
|
|
121
|
+
async removeSyncUnits(
|
|
122
|
+
driveId: string,
|
|
123
|
+
syncUnits: Pick<SynchronizationUnit, 'syncId'>[]
|
|
124
|
+
) {
|
|
122
125
|
const listeners = this.listenerState.get(driveId);
|
|
123
126
|
if (!listeners) {
|
|
124
127
|
return;
|
|
@@ -134,6 +137,7 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
134
137
|
async updateSynchronizationRevisions(
|
|
135
138
|
driveId: string,
|
|
136
139
|
syncUnits: SynchronizationUnit[],
|
|
140
|
+
source: StrandUpdateSource,
|
|
137
141
|
willUpdate?: (listeners: Listener[]) => void,
|
|
138
142
|
onError?: (
|
|
139
143
|
error: Error,
|
|
@@ -175,7 +179,7 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
175
179
|
|
|
176
180
|
if (outdatedListeners.length) {
|
|
177
181
|
willUpdate?.(outdatedListeners);
|
|
178
|
-
return this.triggerUpdate(forceSync, onError);
|
|
182
|
+
return this.triggerUpdate(forceSync, source, onError);
|
|
179
183
|
}
|
|
180
184
|
return [];
|
|
181
185
|
}
|
|
@@ -214,6 +218,7 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
214
218
|
);
|
|
215
219
|
|
|
216
220
|
private async _triggerUpdate(
|
|
221
|
+
source: StrandUpdateSource,
|
|
217
222
|
onError?: (
|
|
218
223
|
error: Error,
|
|
219
224
|
driveId: string,
|
|
@@ -246,7 +251,8 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
246
251
|
|
|
247
252
|
const opData: OperationUpdate[] = [];
|
|
248
253
|
try {
|
|
249
|
-
const data = await this.drive.getOperationData(
|
|
254
|
+
const data = await this.drive.getOperationData(
|
|
255
|
+
// TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
|
|
250
256
|
driveId,
|
|
251
257
|
syncUnit.syncId,
|
|
252
258
|
{
|
|
@@ -282,8 +288,10 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
282
288
|
|
|
283
289
|
// TODO update listeners in parallel, blocking for listeners with block=true
|
|
284
290
|
try {
|
|
285
|
-
const listenerRevisions =
|
|
286
|
-
|
|
291
|
+
const listenerRevisions = await transmitter?.transmit(
|
|
292
|
+
strandUpdates,
|
|
293
|
+
source
|
|
294
|
+
);
|
|
287
295
|
|
|
288
296
|
listener.pendingTimeout = '0';
|
|
289
297
|
listener.listenerStatus = 'PENDING';
|
|
@@ -420,7 +428,9 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
420
428
|
this.listenerState.delete(driveId);
|
|
421
429
|
const transmitters = this.transmitters[driveId];
|
|
422
430
|
if (transmitters) {
|
|
423
|
-
await Promise.all(
|
|
431
|
+
await Promise.all(
|
|
432
|
+
Object.values(transmitters).map(t => t.disconnect?.())
|
|
433
|
+
);
|
|
424
434
|
}
|
|
425
435
|
}
|
|
426
436
|
|
|
@@ -456,7 +466,8 @@ export class ListenerManager extends BaseListenerManager {
|
|
|
456
466
|
|
|
457
467
|
const { documentId, driveId, scope, branch } = syncUnit;
|
|
458
468
|
try {
|
|
459
|
-
const operations = await this.drive.getOperationData(
|
|
469
|
+
const operations = await this.drive.getOperationData(
|
|
470
|
+
// DEAL WITH INVALID SYNC ID ERROR
|
|
460
471
|
driveId,
|
|
461
472
|
syncUnit.syncId,
|
|
462
473
|
{
|
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
StrandUpdate
|
|
17
17
|
} from '../../types';
|
|
18
18
|
import { ListenerManager } from '../manager';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
ITransmitter,
|
|
21
|
+
PullResponderTrigger,
|
|
22
|
+
StrandUpdateSource
|
|
23
|
+
} from './types';
|
|
20
24
|
|
|
21
25
|
export type OperationUpdateGraphQL = Omit<OperationUpdate, 'input'> & {
|
|
22
26
|
input: string;
|
|
@@ -215,7 +219,10 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
215
219
|
private static async executePull(
|
|
216
220
|
driveId: string,
|
|
217
221
|
trigger: PullResponderTrigger,
|
|
218
|
-
onStrandUpdate: (
|
|
222
|
+
onStrandUpdate: (
|
|
223
|
+
strand: StrandUpdate,
|
|
224
|
+
source: StrandUpdateSource
|
|
225
|
+
) => Promise<IOperationResult>,
|
|
219
226
|
onError: (error: Error) => void,
|
|
220
227
|
onRevisions?: (revisions: ListenerRevisionWithError[]) => void,
|
|
221
228
|
onAcknowledge?: (success: boolean) => void
|
|
@@ -246,7 +253,10 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
246
253
|
|
|
247
254
|
let error: Error | undefined = undefined;
|
|
248
255
|
try {
|
|
249
|
-
const result = await onStrandUpdate(strand
|
|
256
|
+
const result = await onStrandUpdate(strand, {
|
|
257
|
+
type: 'trigger',
|
|
258
|
+
trigger
|
|
259
|
+
});
|
|
250
260
|
if (result.error) {
|
|
251
261
|
throw result.error;
|
|
252
262
|
}
|
|
@@ -291,7 +301,10 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
291
301
|
static setupPull(
|
|
292
302
|
driveId: string,
|
|
293
303
|
trigger: PullResponderTrigger,
|
|
294
|
-
onStrandUpdate: (
|
|
304
|
+
onStrandUpdate: (
|
|
305
|
+
strand: StrandUpdate,
|
|
306
|
+
source: StrandUpdateSource
|
|
307
|
+
) => Promise<IOperationResult>,
|
|
295
308
|
onError: (error: Error) => void,
|
|
296
309
|
onRevisions?: (revisions: ListenerRevisionWithError[]) => void,
|
|
297
310
|
onAcknowledge?: (success: boolean) => void
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import stringify from 'json-stringify-deterministic';
|
|
2
2
|
import { gql, requestGraphql } from '../../../utils/graphql';
|
|
3
|
+
import { logger } from '../../../utils/logger';
|
|
3
4
|
import {
|
|
4
5
|
BaseDocumentDriveServer,
|
|
5
6
|
Listener,
|
|
6
7
|
ListenerRevision,
|
|
7
8
|
StrandUpdate
|
|
8
9
|
} from '../../types';
|
|
9
|
-
import { ITransmitter } from './types';
|
|
10
|
-
import { logger } from '../../../utils/logger';
|
|
10
|
+
import { ITransmitter, StrandUpdateSource } from './types';
|
|
11
11
|
|
|
12
12
|
export class SwitchboardPushTransmitter implements ITransmitter {
|
|
13
13
|
private drive: BaseDocumentDriveServer;
|
|
@@ -20,7 +20,24 @@ export class SwitchboardPushTransmitter implements ITransmitter {
|
|
|
20
20
|
this.targetURL = listener.callInfo!.data!;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async transmit(
|
|
23
|
+
async transmit(
|
|
24
|
+
strands: StrandUpdate[],
|
|
25
|
+
source: StrandUpdateSource
|
|
26
|
+
): Promise<ListenerRevision[]> {
|
|
27
|
+
if (
|
|
28
|
+
source.type === 'trigger' &&
|
|
29
|
+
source.trigger.data?.url === this.targetURL
|
|
30
|
+
) {
|
|
31
|
+
return strands.map(strand => ({
|
|
32
|
+
driveId: strand.driveId,
|
|
33
|
+
documentId: strand.documentId,
|
|
34
|
+
scope: strand.scope,
|
|
35
|
+
branch: strand.branch,
|
|
36
|
+
status: 'SUCCESS',
|
|
37
|
+
revision: strand.operations.at(-1)?.index ?? -1
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
// Send Graphql mutation to switchboard
|
|
25
42
|
try {
|
|
26
43
|
const { pushUpdates } = await requestGraphql<{
|
|
@@ -4,11 +4,19 @@ import {
|
|
|
4
4
|
} from 'document-model-libs/document-drive';
|
|
5
5
|
import { ListenerRevision, StrandUpdate } from '../..';
|
|
6
6
|
|
|
7
|
+
export type StrandUpdateSource =
|
|
8
|
+
| {
|
|
9
|
+
type: 'local';
|
|
10
|
+
}
|
|
11
|
+
| { type: 'trigger'; trigger: Trigger };
|
|
12
|
+
|
|
7
13
|
export interface ITransmitter {
|
|
8
|
-
transmit(
|
|
14
|
+
transmit(
|
|
15
|
+
strands: StrandUpdate[],
|
|
16
|
+
source: StrandUpdateSource
|
|
17
|
+
): Promise<ListenerRevision[]>;
|
|
9
18
|
disconnect?(): Promise<void>;
|
|
10
19
|
}
|
|
11
|
-
|
|
12
20
|
export interface InternalTransmitterService extends ITransmitter {
|
|
13
21
|
getName(): string;
|
|
14
22
|
}
|
package/src/server/types.ts
CHANGED
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
} from 'document-model/document';
|
|
21
21
|
import { Unsubscribe } from 'nanoevents';
|
|
22
22
|
import { OperationError } from './error';
|
|
23
|
-
import { ITransmitter } from './listener/transmitter/types';
|
|
23
|
+
import { ITransmitter, StrandUpdateSource } from './listener/transmitter/types';
|
|
24
24
|
|
|
25
25
|
export type DriveInput = State<
|
|
26
26
|
Omit<DocumentDriveState, '__typename' | 'id' | 'nodes'> & { id?: string },
|
|
@@ -59,7 +59,10 @@ export type SynchronizationUnit = {
|
|
|
59
59
|
revision: number;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
export type SynchronizationUnitQuery = Omit<
|
|
62
|
+
export type SynchronizationUnitQuery = Omit<
|
|
63
|
+
SynchronizationUnit,
|
|
64
|
+
'revision' | 'lastUpdated'
|
|
65
|
+
>;
|
|
63
66
|
|
|
64
67
|
export type Listener = {
|
|
65
68
|
driveId: string;
|
|
@@ -149,6 +152,11 @@ export type GetDocumentOptions = ReducerOptions & {
|
|
|
149
152
|
checkHashes?: boolean;
|
|
150
153
|
};
|
|
151
154
|
|
|
155
|
+
export type AddOperationOptions = {
|
|
156
|
+
forceSync?: boolean;
|
|
157
|
+
source: StrandUpdateSource;
|
|
158
|
+
};
|
|
159
|
+
|
|
152
160
|
export abstract class BaseDocumentDriveServer {
|
|
153
161
|
/** Public methods **/
|
|
154
162
|
abstract getDrives(): Promise<string[]>;
|
|
@@ -163,9 +171,7 @@ export abstract class BaseDocumentDriveServer {
|
|
|
163
171
|
options?: GetDocumentOptions
|
|
164
172
|
): Promise<DocumentDriveDocument>;
|
|
165
173
|
|
|
166
|
-
abstract getDriveBySlug(
|
|
167
|
-
slug: string,
|
|
168
|
-
): Promise<DocumentDriveDocument>;
|
|
174
|
+
abstract getDriveBySlug(slug: string): Promise<DocumentDriveDocument>;
|
|
169
175
|
|
|
170
176
|
abstract getDocuments(drive: string): Promise<string[]>;
|
|
171
177
|
abstract getDocument(
|
|
@@ -178,100 +184,101 @@ export abstract class BaseDocumentDriveServer {
|
|
|
178
184
|
drive: string,
|
|
179
185
|
id: string,
|
|
180
186
|
operation: Operation,
|
|
181
|
-
|
|
187
|
+
options?: AddOperationOptions
|
|
182
188
|
): Promise<IOperationResult>;
|
|
183
189
|
|
|
184
190
|
abstract addOperations(
|
|
185
191
|
drive: string,
|
|
186
192
|
id: string,
|
|
187
193
|
operations: Operation[],
|
|
188
|
-
|
|
194
|
+
options?: AddOperationOptions
|
|
189
195
|
): Promise<IOperationResult>;
|
|
190
196
|
|
|
191
197
|
abstract queueOperation(
|
|
192
198
|
drive: string,
|
|
193
199
|
id: string,
|
|
194
200
|
operation: Operation,
|
|
195
|
-
|
|
201
|
+
options?: AddOperationOptions
|
|
196
202
|
): Promise<IOperationResult>;
|
|
197
203
|
|
|
198
204
|
abstract queueOperations(
|
|
199
205
|
drive: string,
|
|
200
206
|
id: string,
|
|
201
207
|
operations: Operation[],
|
|
202
|
-
|
|
208
|
+
options?: AddOperationOptions
|
|
203
209
|
): Promise<IOperationResult>;
|
|
204
210
|
|
|
205
211
|
abstract queueAction(
|
|
206
212
|
drive: string,
|
|
207
213
|
id: string,
|
|
208
214
|
action: Action,
|
|
209
|
-
|
|
215
|
+
options?: AddOperationOptions
|
|
210
216
|
): Promise<IOperationResult>;
|
|
211
217
|
|
|
212
218
|
abstract queueActions(
|
|
213
219
|
drive: string,
|
|
214
220
|
id: string,
|
|
215
221
|
actions: Action[],
|
|
216
|
-
|
|
222
|
+
options?: AddOperationOptions
|
|
217
223
|
): Promise<IOperationResult>;
|
|
218
224
|
|
|
219
225
|
abstract addDriveOperation(
|
|
220
226
|
drive: string,
|
|
221
227
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
222
|
-
|
|
228
|
+
options?: AddOperationOptions
|
|
223
229
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
224
230
|
abstract addDriveOperations(
|
|
225
231
|
drive: string,
|
|
226
232
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
227
|
-
|
|
233
|
+
options?: AddOperationOptions
|
|
228
234
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
229
235
|
|
|
230
236
|
abstract queueDriveOperation(
|
|
231
237
|
drive: string,
|
|
232
238
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
233
|
-
|
|
239
|
+
options?: AddOperationOptions
|
|
234
240
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
241
|
+
|
|
235
242
|
abstract queueDriveOperations(
|
|
236
243
|
drive: string,
|
|
237
244
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
238
|
-
|
|
245
|
+
options?: AddOperationOptions
|
|
239
246
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
240
247
|
|
|
241
248
|
abstract queueDriveAction(
|
|
242
249
|
drive: string,
|
|
243
250
|
action: DocumentDriveAction | BaseAction,
|
|
244
|
-
|
|
251
|
+
options?: AddOperationOptions
|
|
245
252
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
246
253
|
|
|
247
254
|
abstract queueDriveActions(
|
|
248
255
|
drive: string,
|
|
249
256
|
actions: Array<DocumentDriveAction | BaseAction>,
|
|
250
|
-
|
|
257
|
+
options?: AddOperationOptions
|
|
251
258
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
252
259
|
|
|
253
260
|
abstract addAction(
|
|
254
261
|
drive: string,
|
|
255
262
|
id: string,
|
|
256
263
|
action: Action,
|
|
257
|
-
|
|
264
|
+
options?: AddOperationOptions
|
|
258
265
|
): Promise<IOperationResult>;
|
|
259
266
|
abstract addActions(
|
|
260
267
|
drive: string,
|
|
261
268
|
id: string,
|
|
262
269
|
actions: Action[],
|
|
263
|
-
|
|
270
|
+
options?: AddOperationOptions
|
|
264
271
|
): Promise<IOperationResult>;
|
|
265
272
|
|
|
266
273
|
abstract addDriveAction(
|
|
267
274
|
drive: string,
|
|
268
275
|
action: DocumentDriveAction | BaseAction,
|
|
269
|
-
|
|
276
|
+
options?: AddOperationOptions
|
|
270
277
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
271
278
|
abstract addDriveActions(
|
|
272
279
|
drive: string,
|
|
273
280
|
actions: (DocumentDriveAction | BaseAction)[],
|
|
274
|
-
|
|
281
|
+
options?: AddOperationOptions
|
|
275
282
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
276
283
|
|
|
277
284
|
abstract getSyncStatus(drive: string): SyncStatus;
|
|
@@ -351,7 +358,7 @@ export abstract class BaseListenerManager {
|
|
|
351
358
|
}
|
|
352
359
|
|
|
353
360
|
abstract initDrive(drive: DocumentDriveDocument): Promise<void>;
|
|
354
|
-
abstract removeDrive(driveId: DocumentDriveState[
|
|
361
|
+
abstract removeDrive(driveId: DocumentDriveState['id']): Promise<void>;
|
|
355
362
|
|
|
356
363
|
abstract addListener(listener: Listener): Promise<ITransmitter>;
|
|
357
364
|
abstract removeListener(
|
|
@@ -376,6 +383,7 @@ export abstract class BaseListenerManager {
|
|
|
376
383
|
abstract updateSynchronizationRevisions(
|
|
377
384
|
driveId: string,
|
|
378
385
|
syncUnits: SynchronizationUnit[],
|
|
386
|
+
source: StrandUpdateSource,
|
|
379
387
|
willUpdate?: (listeners: Listener[]) => void,
|
|
380
388
|
onError?: (
|
|
381
389
|
error: Error,
|