core-3nweb-client-lib 0.43.20 → 0.44.1

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.
Files changed (42) hide show
  1. package/build/api-defs/files.d.ts +233 -160
  2. package/build/core/index.d.ts +1 -0
  3. package/build/core/index.js +3 -2
  4. package/build/core/storage/index.d.ts +1 -0
  5. package/build/core/storage/index.js +4 -1
  6. package/build/core/storage/synced/storage.d.ts +1 -0
  7. package/build/core/storage/synced/storage.js +4 -1
  8. package/build/core-ipc/file.d.ts +47 -0
  9. package/build/core-ipc/file.js +121 -2
  10. package/build/core-ipc/fs.js +55 -62
  11. package/build/lib-client/asmail/recipient.js +4 -2
  12. package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
  13. package/build/lib-client/cryptor/cryptor.wasm +0 -0
  14. package/build/lib-client/fs-utils/files.js +5 -1
  15. package/build/lib-client/ws-utils.js +2 -7
  16. package/build/lib-client/xsp-fs/common.d.ts +2 -0
  17. package/build/lib-client/xsp-fs/common.js +2 -1
  18. package/build/lib-client/xsp-fs/exceptions.js +1 -1
  19. package/build/lib-client/xsp-fs/file-node.d.ts +3 -0
  20. package/build/lib-client/xsp-fs/file-node.js +55 -3
  21. package/build/lib-client/xsp-fs/file.d.ts +3 -0
  22. package/build/lib-client/xsp-fs/file.js +5 -1
  23. package/build/lib-client/xsp-fs/folder-node.d.ts +5 -4
  24. package/build/lib-client/xsp-fs/folder-node.js +257 -368
  25. package/build/lib-client/xsp-fs/fs.d.ts +6 -2
  26. package/build/lib-client/xsp-fs/fs.js +33 -2
  27. package/build/lib-client/xsp-fs/link-node.js +1 -1
  28. package/build/lib-client/xsp-fs/node-in-fs.d.ts +21 -0
  29. package/build/lib-client/xsp-fs/node-in-fs.js +172 -3
  30. package/build/lib-common/exceptions/file.d.ts +1 -1
  31. package/build/lib-common/exceptions/file.js +3 -2
  32. package/build/lib-common/ipc/generic-ipc.js +33 -28
  33. package/build/lib-common/ipc/ws-ipc.d.ts +2 -0
  34. package/build/lib-common/ipc/ws-ipc.js +35 -8
  35. package/build/lib-common/map-of-sets.d.ts +1 -0
  36. package/build/lib-common/map-of-sets.js +3 -0
  37. package/build/protos/asmail.proto.js +3315 -883
  38. package/build/protos/file.proto.js +1974 -0
  39. package/build/protos/fs.proto.js +3301 -869
  40. package/package.json +1 -1
  41. package/protos/file.proto +44 -0
  42. package/protos/fs.proto +42 -27
@@ -184,34 +184,15 @@ class FolderNode extends node_in_fs_1.NodeInFS {
184
184
  const newState = this.currentState;
185
185
  const { renamedNodes, addedNodes, removedNodes } = identifyChanges(originalState.nodes, newState.nodes);
186
186
  for (const node of removedNodes) {
187
- const event = {
188
- type: 'entry-removal',
189
- src: 'sync',
190
- path: this.name,
191
- newVersion: this.version,
192
- name: node.name
193
- };
194
- this.broadcastEvent(event, false, node.objId);
187
+ const { event, childObjId } = this.makeEntryRemovalEvent(this.version, 'sync', node);
188
+ this.broadcastEvent(event, false, childObjId);
195
189
  }
196
190
  for (const node of addedNodes) {
197
- const event = {
198
- type: 'entry-addition',
199
- src: 'sync',
200
- entry: nodeInfoToListingEntry(node),
201
- path: this.name,
202
- newVersion: this.version
203
- };
204
- this.broadcastEvent(event, false, node.objId);
191
+ const { event, childObjId } = this.makeEntryAdditionEvent(this.version, 'sync', node);
192
+ this.broadcastEvent(event, false, childObjId);
205
193
  }
206
194
  for (const { objId, oldName, newName } of renamedNodes) {
207
- const event = {
208
- type: 'entry-renaming',
209
- src: 'sync',
210
- oldName,
211
- newName,
212
- path: this.name,
213
- newVersion: this.version
214
- };
195
+ const event = this.makeEntryRenamingEvent(this.version, 'sync', newName, oldName);
215
196
  this.broadcastEvent(event, false, objId);
216
197
  }
217
198
  return removedNodes;
@@ -228,6 +209,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
228
209
  this.callRemoveObjOn('sync', nodeToRm);
229
210
  }
230
211
  }
212
+ makeEntryAdditionEvent(newVersion, src, node, moveLabel) {
213
+ const event = {
214
+ type: 'entry-addition',
215
+ src,
216
+ entry: nodeInfoToListingEntry(node),
217
+ path: this.name,
218
+ newVersion,
219
+ moveLabel
220
+ };
221
+ return { event, childObjId: node.objId };
222
+ }
223
+ makeEntryRemovalEvent(newVersion, src, { name, objId }, moveLabel) {
224
+ const event = {
225
+ type: 'entry-removal',
226
+ src,
227
+ path: this.name,
228
+ newVersion,
229
+ name,
230
+ moveLabel
231
+ };
232
+ return { event, childObjId: objId };
233
+ }
234
+ makeEntryRenamingEvent(newVersion, src, newName, oldName) {
235
+ return {
236
+ type: 'entry-renaming',
237
+ src,
238
+ oldName,
239
+ newName,
240
+ path: this.name,
241
+ newVersion
242
+ };
243
+ }
231
244
  callRemoveObjOn(src, node) {
232
245
  if (node.type === 'folder') {
233
246
  node.removeFolderObj(src);
@@ -358,64 +371,67 @@ class FolderNode extends node_in_fs_1.NodeInFS {
358
371
  return [];
359
372
  }
360
373
  delete state.nodes[childInfo.name];
361
- const event = {
362
- type: 'entry-removal',
363
- path: this.name,
364
- src: 'local',
365
- name: childInfo.name,
366
- newVersion: version
367
- };
368
- return { event, childObjId: childInfo.objId };
374
+ return this.makeEntryRemovalEvent(version, 'local', childInfo);
369
375
  }).catch(noop);
370
376
  const fileExc = (0, file_1.makeFileException)('notFound', childInfo.name, exc);
371
377
  fileExc.inconsistentStateOfFS = true;
372
378
  throw fileExc;
373
379
  }
374
- async adoptItemsFromRemoteVersion(opts) {
375
- const { state, remote } = await this.syncStatus();
376
- if ((state !== 'conflicting') && (state !== 'behind')) {
377
- return;
378
- }
379
- const remoteVersion = versionFromRemoteBranch(remote, opts === null || opts === void 0 ? void 0 : opts.remoteVersion);
380
- if (!remoteVersion) {
381
- throw 'something';
382
- }
383
- const { folderInfo: remoteState } = await this.readRemoteVersion(remoteVersion);
384
- return await this.doChangeWhileTrackingVersion(opts === null || opts === void 0 ? void 0 : opts.localVersion, async (state) => {
385
- const { inRemote, nameOverlaps, differentKeys } = diffNodes(state, remoteState);
386
- if (differentKeys) {
387
- throw 'something';
388
- }
389
- if (!inRemote) {
390
- return [];
391
- }
392
- const addedNodes = new Set();
393
- if (nameOverlaps) {
394
- const postfix = opts === null || opts === void 0 ? void 0 : opts.postfixForNameOverlaps;
395
- if ((postfix === undefined) || (postfix.length < 0)) {
396
- throw 'something';
397
- }
398
- for (const itemName of nameOverlaps) {
399
- const node = remoteState.nodes[itemName];
400
- let newName = `${itemName}${postfix}`;
401
- while (state.nodes[newName]) {
402
- newName = `${newName}${postfix}`;
403
- }
404
- node.name = newName;
405
- state.nodes[newName] = node;
406
- addedNodes.add(itemName);
407
- }
408
- }
409
- for (const { name: itemName } of inRemote) {
410
- if (addedNodes.has(itemName)) {
411
- continue;
412
- }
413
- const node = remoteState.nodes[itemName];
414
- state.nodes[itemName] = node;
415
- }
416
- return [];
417
- });
418
- }
380
+ // XXX should we keep this, when merge with certain inputs will do same action
381
+ // async adoptItemsFromRemoteVersion(opts: OptionsToAdoptAllRemoteItems|undefined): Promise<number|undefined> {
382
+ // const { state, remote } = await this.syncStatus();
383
+ // if ((state !== 'conflicting') && (state !== 'behind')) {
384
+ // return;
385
+ // }
386
+ // const remoteVersion = versionFromRemoteBranch(remote!, opts?.remoteVersion);
387
+ // if (!remoteVersion) {
388
+ // throw 'something';
389
+ // }
390
+ // const { folderInfo: remoteState } = await this.readRemoteVersion(remoteVersion);
391
+ // const synced = undefined;
392
+ // return await this.doChangeWhileTrackingVersion(opts?.localVersion, async (state, newVersion) => {
393
+ // const { inRemote, nameOverlaps, differentKeys } = diffNodes(state, remoteState, synced);
394
+ // if (differentKeys) {
395
+ // throw makeFSSyncException(this.name, {
396
+ // message: `There are different keys, and implementation for it is not implemented, yet.`
397
+ // });
398
+ // }
399
+ // const events: { event: FSEvent; childObjId: string; }[] = [];
400
+ // if (!inRemote) {
401
+ // return events;
402
+ // }
403
+ // const addedNodes = new Set<string>();
404
+ // if (nameOverlaps) {
405
+ // const postfix = opts?.postfixForNameOverlaps;
406
+ // if ((postfix === undefined) || (postfix.length < 0)) {
407
+ // // XXX better exception
408
+ // throw 'something';
409
+ // }
410
+ // for (const itemName of nameOverlaps) {
411
+ // const node = remoteState.nodes[itemName];
412
+ // let newName = `${itemName}${postfix}`;
413
+ // while (state.nodes[newName]) {
414
+ // newName = `${newName}${postfix}`;
415
+ // }
416
+ // node.name = newName;
417
+ // state.nodes[newName] = node;
418
+ // addedNodes.add(itemName);
419
+ // const { event, childObjId } = this.makeEntryAdditionEvent(newVersion, 'sync', node);
420
+ // events.push({ event, childObjId });
421
+ // }
422
+ // }
423
+ // for (const { name: itemName } of inRemote) {
424
+ // if (addedNodes.has(itemName)) {
425
+ // continue;
426
+ // }
427
+ // const node = remoteState.nodes[itemName];
428
+ // state.nodes[itemName] = node;
429
+ // const { event, childObjId } = this.makeEntryAdditionEvent(newVersion, 'sync', node);
430
+ // events.push({ event, childObjId });
431
+ // }
432
+ // return events;
433
+ // });
434
+ // }
419
435
  /**
420
436
  * If no initial local version given, then this performs like doTransition(change) method.
421
437
  * When initial local version is given and check, exceptions are thrown if versions mismatch,
@@ -578,16 +594,10 @@ class FolderNode extends node_in_fs_1.NodeInFS {
578
594
  else {
579
595
  throw new Error(`Unknown type of node: ${type}`);
580
596
  }
581
- addToTransitionState(state, node, key);
597
+ const nodeInfo = addToTransitionState(state, node, key);
582
598
  this.storage.nodes.set(node);
583
- const event = {
584
- type: 'entry-addition',
585
- path: this.name,
586
- src: 'local',
587
- newVersion: version,
588
- entry: nodeToListingEntry(node)
589
- };
590
- return { event, childObjId: node.objId };
599
+ const { event, childObjId } = this.makeEntryAdditionEvent(version, 'local', nodeInfo);
600
+ return { event, childObjId };
591
601
  });
592
602
  return node;
593
603
  });
@@ -603,28 +613,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
603
613
  }
604
614
  async removeChild(f) {
605
615
  await this.doTransition(async (state, version) => {
606
- const childJSON = state.nodes[f.name];
607
- if (!childJSON || (childJSON.objId !== f.objId)) {
616
+ const child = state.nodes[f.name];
617
+ if (!child || (child.objId !== f.objId)) {
608
618
  throw new Error(`Not a child given: name==${f.name}, objId==${f.objId}, parentId==${f.parentId}, this folder objId==${this.objId}`);
609
619
  }
610
620
  delete state.nodes[f.name];
611
- const event = {
612
- type: 'entry-removal',
613
- path: this.name,
614
- src: 'local',
615
- name: f.name,
616
- newVersion: version
617
- };
618
- return { event, childObjId: f.objId };
621
+ return this.makeEntryRemovalEvent(version, 'local', child);
619
622
  });
620
623
  // explicitly do not wait on a result of child's delete, cause if it fails
621
624
  // we just get traceable garbage, yet, the rest of a live/non-deleted tree
622
625
  // stays consistent
623
626
  this.callRemoveObjOn('local', f);
624
627
  }
625
- changeChildName(initName, newName) {
628
+ changeChildName(oldName, newName) {
626
629
  return this.doTransition(async (state, version) => {
627
- const child = state.nodes[initName];
630
+ const child = state.nodes[oldName];
628
631
  delete state.nodes[child.name];
629
632
  state.nodes[newName] = child;
630
633
  child.name = newName;
@@ -632,15 +635,10 @@ class FolderNode extends node_in_fs_1.NodeInFS {
632
635
  if (childNode) {
633
636
  childNode.name = newName;
634
637
  }
635
- const event = {
636
- type: 'entry-renaming',
637
- path: this.name,
638
- src: 'local',
639
- newName,
640
- oldName: initName,
641
- newVersion: version
638
+ return {
639
+ event: this.makeEntryRenamingEvent(version, 'local', newName, oldName),
640
+ childObjId: child.objId
642
641
  };
643
- return { event, childObjId: child.objId };
644
642
  });
645
643
  }
646
644
  async moveChildTo(childName, dst, nameInDst) {
@@ -659,19 +657,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
659
657
  await this.moveChildOut(childName, childJSON.objId, moveLabel);
660
658
  }
661
659
  }
662
- async moveChildOut(name, childObjId, moveLabel) {
660
+ async moveChildOut(name, _childObjId, moveLabel) {
663
661
  await this.doTransition(async (state, version) => {
664
662
  const child = state.nodes[name];
665
663
  delete state.nodes[name];
666
- const event = {
667
- type: 'entry-removal',
668
- path: this.name,
669
- src: 'local',
670
- name,
671
- newVersion: version,
672
- moveLabel
673
- };
674
- return { event, childObjId };
664
+ return this.makeEntryRemovalEvent(version, 'local', child, moveLabel);
675
665
  });
676
666
  }
677
667
  async moveChildIn(newName, child, moveLabel) {
@@ -679,15 +669,8 @@ class FolderNode extends node_in_fs_1.NodeInFS {
679
669
  await this.doTransition(async (state, version) => {
680
670
  child.name = newName;
681
671
  state.nodes[child.name] = child;
682
- const event = {
683
- type: 'entry-addition',
684
- path: this.name,
685
- src: 'local',
686
- entry: nodeInfoToListingEntry(child),
687
- newVersion: version,
688
- moveLabel
689
- };
690
- return { event, childObjId: child.objId };
672
+ const { event, childObjId } = this.makeEntryAdditionEvent(version, 'local', child, moveLabel);
673
+ return { event, childObjId };
691
674
  });
692
675
  }
693
676
  async getFolderInThisSubTree(path, createIfMissing = false, exclusiveCreate = false) {
@@ -1030,14 +1013,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1030
1013
  return this.doChangeWhileTrackingVersion(initLocalVersion, async (state, newVersion) => {
1031
1014
  remoteChildNode.name = childName;
1032
1015
  state.nodes[remoteChildNode.name] = remoteChildNode;
1033
- const event = {
1034
- type: 'entry-addition',
1035
- path: this.name,
1036
- src: 'sync',
1037
- entry: nodeInfoToListingEntry(remoteChildNode),
1038
- newVersion
1039
- };
1040
- return { event, childObjId: remoteChildNode.objId };
1016
+ return this.makeEntryAdditionEvent(newVersion, 'sync', remoteChildNode);
1041
1017
  });
1042
1018
  }
1043
1019
  async replaceLocalChildWithRemote(remoteChildNode, initLocalVersion) {
@@ -1045,22 +1021,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1045
1021
  const newVersion = await this.doChangeWhileTrackingVersion(initLocalVersion, async (state, newVersion) => {
1046
1022
  origChildNode = state.nodes[remoteChildNode.name];
1047
1023
  state.nodes[remoteChildNode.name] = remoteChildNode;
1048
- const additionEvent = {
1049
- type: 'entry-addition',
1050
- path: this.name,
1051
- src: 'sync',
1052
- entry: nodeInfoToListingEntry(remoteChildNode),
1053
- newVersion
1054
- };
1055
- const rmEvent = {
1056
- type: 'entry-removal',
1057
- path: this.name,
1058
- src: 'sync',
1059
- name: origChildNode.name
1060
- };
1061
1024
  return [
1062
- { event: rmEvent, childObjId: origChildNode.objId },
1063
- { event: additionEvent, childObjId: remoteChildNode.objId }
1025
+ this.makeEntryRemovalEvent(newVersion, 'sync', origChildNode),
1026
+ this.makeEntryAdditionEvent(newVersion, 'sync', remoteChildNode)
1064
1027
  ];
1065
1028
  });
1066
1029
  const origChild = await this.getOrMakeChildNodeForInfo(origChildNode);
@@ -1074,57 +1037,23 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1074
1037
  return await this.crypto.read(srcOfRemote);
1075
1038
  }
1076
1039
  async diffCurrentAndRemote(remoteVersion) {
1077
- const { state, remote } = await this.syncStatus();
1078
- let isCurrentLocal;
1079
- if (state === 'behind') {
1080
- isCurrentLocal = false;
1081
- }
1082
- else if (state === 'conflicting') {
1083
- isCurrentLocal = true;
1084
- }
1085
- else {
1040
+ const v = await this.getRemoteVersionToDiff(remoteVersion);
1041
+ if (!v) {
1086
1042
  return;
1087
1043
  }
1088
- remoteVersion = versionFromRemoteBranch(remote, remoteVersion);
1089
- if (remoteVersion) {
1090
- const { folderInfo, attrs, xattrs } = await this.readRemoteVersion(remoteVersion);
1091
- return this.diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs);
1044
+ else if (v.rm) {
1045
+ return v.rmDiff;
1092
1046
  }
1093
1047
  else {
1094
- return this.diffWithArchivedRemote(isCurrentLocal);
1048
+ const { isCurrentLocal, remoteVersion, syncedVersion } = v;
1049
+ const remote = await this.readRemoteVersion(remoteVersion);
1050
+ const synced = await this.readRemoteVersion(syncedVersion);
1051
+ return {
1052
+ ...this.commonDiffWithRemote(isCurrentLocal, remoteVersion, remote, syncedVersion, synced),
1053
+ ...diffNodes(this.currentState, remote.folderInfo, synced.folderInfo),
1054
+ };
1095
1055
  }
1096
1056
  }
1097
- diffWithArchivedRemote(isCurrentLocal) {
1098
- return {
1099
- currentVersion: this.version,
1100
- isCurrentLocal,
1101
- isRemoteRemoved: true,
1102
- ctime: {
1103
- current: new Date(this.attrs.ctime)
1104
- },
1105
- mtime: {
1106
- current: new Date(this.attrs.ctime)
1107
- }
1108
- };
1109
- }
1110
- diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs) {
1111
- const { ctime, mtime } = diffAttrs(this.attrs, attrs);
1112
- const { inCurrent, inRemote, nameOverlaps, differentKeys, differentNames } = diffNodes(this.currentState, folderInfo);
1113
- return {
1114
- currentVersion: this.version,
1115
- isCurrentLocal,
1116
- isRemoteRemoved: false,
1117
- remoteVersion,
1118
- inCurrent,
1119
- inRemote,
1120
- nameOverlaps,
1121
- differentKeys,
1122
- differentNames,
1123
- ctime,
1124
- mtime,
1125
- xattrs: diffXAttrs(this.xattrs, xattrs)
1126
- };
1127
- }
1128
1057
  async getRemoteItemNode(remoteItemName, remoteVersion) {
1129
1058
  const remoteChildNodeInfo = await this.getRemoteChildNodeInfo(remoteItemName, remoteVersion);
1130
1059
  return await this.getOrMakeChildNodeForInfo(remoteChildNodeInfo, false);
@@ -1132,6 +1061,85 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1132
1061
  getStorage() {
1133
1062
  return this.storage;
1134
1063
  }
1064
+ async mergeCurrentAndRemoteVersions(opts) {
1065
+ const diff = await this.diffCurrentAndRemote(opts === null || opts === void 0 ? void 0 : opts.remoteVersion);
1066
+ if (!diff) {
1067
+ return;
1068
+ }
1069
+ else if (diff.isRemoteRemoved) {
1070
+ return -1;
1071
+ }
1072
+ else if (!diff.isCurrentLocal) {
1073
+ await this.adoptRemote({ remoteVersion: diff.remoteVersion });
1074
+ return this.version;
1075
+ }
1076
+ if ((opts === null || opts === void 0 ? void 0 : opts.localVersion) && (opts.localVersion !== diff.currentVersion)) {
1077
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
1078
+ versionMismatch: true,
1079
+ message: `Given local version ${opts.localVersion} is not equal to current ${diff.currentVersion}`
1080
+ });
1081
+ }
1082
+ const { added, removed, renamed, rekeyed, nameOverlaps } = diff;
1083
+ if (rekeyed) {
1084
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
1085
+ message: `There are different keys, and implementation for it is not implemented, yet.`
1086
+ });
1087
+ }
1088
+ if (nameOverlaps && !(opts === null || opts === void 0 ? void 0 : opts.postfixForNameOverlaps)) {
1089
+ throw (0, exceptions_1.makeFSSyncException)(this.name, {
1090
+ nameOverlaps,
1091
+ message: `There are name overlaps, but prefix for adding to remote item names isn't given.`
1092
+ });
1093
+ }
1094
+ const remote = await this.readRemoteVersion(diff.remoteVersion);
1095
+ return await this.doChangeWhileTrackingVersion(diff.currentVersion, async (state, newVersion) => {
1096
+ const nodes = state.nodes;
1097
+ const remoteNodes = remote.folderInfo.nodes;
1098
+ const events = [];
1099
+ if (removed) {
1100
+ for (const { name, removedIn } of removed) {
1101
+ if (removedIn === 'r') {
1102
+ const node = nodes[name];
1103
+ delete nodes[name];
1104
+ events.push(this.makeEntryRemovalEvent(newVersion, 'sync', node));
1105
+ }
1106
+ }
1107
+ }
1108
+ if (renamed) {
1109
+ for (const { local, remote, renamedIn } of renamed) {
1110
+ if (renamedIn === 'r') {
1111
+ const node = nodes[local];
1112
+ (0, assert_1.assert)(!!node);
1113
+ node.name = remote;
1114
+ nodes[node.name] = node;
1115
+ events.push({
1116
+ event: this.makeEntryRenamingEvent(newVersion, 'sync', remote, local),
1117
+ childObjId: node.objId
1118
+ });
1119
+ }
1120
+ else if (renamedIn === 'l&r') {
1121
+ // XXX use mtime to suggest which name to adopt -- TODO
1122
+ }
1123
+ }
1124
+ }
1125
+ if (added) {
1126
+ for (const { name, addedIn } of added) {
1127
+ if (addedIn === 'r') {
1128
+ const node = remoteNodes[name];
1129
+ (0, assert_1.assert)(!!node);
1130
+ if (nameOverlaps === null || nameOverlaps === void 0 ? void 0 : nameOverlaps.includes(name)) {
1131
+ while (nodes[node.name]) {
1132
+ node.name = `${node.name}${opts === null || opts === void 0 ? void 0 : opts.postfixForNameOverlaps}`;
1133
+ }
1134
+ }
1135
+ nodes[node.name] = node;
1136
+ events.push(this.makeEntryAdditionEvent(newVersion, 'sync', node));
1137
+ }
1138
+ }
1139
+ }
1140
+ return events;
1141
+ });
1142
+ }
1135
1143
  }
1136
1144
  exports.FolderNode = FolderNode;
1137
1145
  Object.freeze(FolderNode.prototype);
@@ -1156,18 +1164,6 @@ function nodeInfoToListingEntry({ name, isFile, isFolder, isLink }) {
1156
1164
  return { name };
1157
1165
  }
1158
1166
  }
1159
- function nodeToListingEntry({ name, type }) {
1160
- switch (type) {
1161
- case 'file':
1162
- return { name, isFolder: true };
1163
- case 'folder':
1164
- return { name, isFolder: true };
1165
- case 'link':
1166
- return { name, isLink: true };
1167
- default:
1168
- return { name };
1169
- }
1170
- }
1171
1167
  function addToTransitionState(state, f, key) {
1172
1168
  const nodeInfo = {
1173
1169
  name: f.name,
@@ -1187,170 +1183,82 @@ function addToTransitionState(state, f, key) {
1187
1183
  throw new Error(`Unknown type of file system entity: ${f.type}`);
1188
1184
  }
1189
1185
  state.nodes[nodeInfo.name] = nodeInfo;
1186
+ return nodeInfo;
1190
1187
  }
1191
- function diffAttrs(current, remote) {
1192
- return {
1193
- ctime: {
1194
- current: new Date(current.ctime),
1195
- remote: new Date(remote.ctime)
1196
- },
1197
- mtime: {
1198
- current: new Date(current.mtime),
1199
- remote: new Date(remote.mtime)
1200
- }
1201
- };
1202
- }
1203
- function diffNodes(current, remote) {
1204
- const inRemote = [];
1205
- const nodesInCurrentById = new Map();
1206
- for (const node of Object.values(current.nodes)) {
1207
- nodesInCurrentById.set(node.objId, node);
1208
- }
1188
+ function diffNodes(current, remote, synced) {
1189
+ const currentNodesById = orderByIds(current.nodes);
1190
+ const syncedNodesById = orderByIds(synced.nodes);
1191
+ const removed = [];
1192
+ const renamed = [];
1193
+ const added = [];
1194
+ const rekeyed = [];
1209
1195
  const nameOverlaps = [];
1210
- const differentNames = [];
1211
- const differentKeys = [];
1212
1196
  for (const remoteNode of Object.values(remote.nodes)) {
1213
- const localNode = nodesInCurrentById.get(remoteNode.objId);
1197
+ const localNode = currentNodesById.get(remoteNode.objId);
1214
1198
  if (localNode) {
1215
- if (localNode.name !== remoteNode.name) {
1216
- differentNames.push({
1217
- localName: localNode.name,
1218
- remoteName: remoteNode.name
1199
+ if (localNode.name === remoteNode.name) {
1200
+ // nodes are same
1201
+ }
1202
+ else {
1203
+ const syncedNode = syncedNodesById.get(remoteNode.objId);
1204
+ (0, assert_1.assert)(!!synced);
1205
+ const localChanged = (syncedNode.name !== localNode.name);
1206
+ const remoteChanged = (syncedNode.name !== remoteNode.name);
1207
+ renamed.push({
1208
+ local: localNode.name,
1209
+ remote: remoteNode.name,
1210
+ renamedIn: (localChanged ? (remoteChanged ? 'l&r' : 'l') : 'r')
1219
1211
  });
1212
+ if (current.nodes[remoteNode.name]) {
1213
+ nameOverlaps.push(remoteNode.name);
1214
+ }
1220
1215
  }
1221
- if (!areBytesEqual(localNode.key, remoteNode.key)) {
1222
- differentKeys.push(localNode.name);
1216
+ if (!(0, node_in_fs_1.areBytesEqual)(localNode.key, remoteNode.key)) {
1217
+ const syncedNode = syncedNodesById.get(remoteNode.objId);
1218
+ (0, assert_1.assert)(!!synced);
1219
+ const localChanged = !(0, node_in_fs_1.areBytesEqual)(localNode.key, syncedNode.key);
1220
+ const remoteChanged = !(0, node_in_fs_1.areBytesEqual)(localNode.key, syncedNode.key);
1221
+ rekeyed.push({
1222
+ local: localNode.name,
1223
+ remote: remoteNode.name,
1224
+ rekeyedIn: (localChanged ? (remoteChanged ? 'l&r' : 'l') : 'r')
1225
+ });
1223
1226
  }
1224
- nodesInCurrentById.delete(remoteNode.objId);
1227
+ currentNodesById.delete(localNode.objId);
1225
1228
  }
1226
1229
  else {
1227
- inRemote.push(nodeInfoToListingEntry(remoteNode));
1230
+ const syncedNode = syncedNodesById.get(remoteNode.objId);
1231
+ if (syncedNode) {
1232
+ removed.push({ name: remoteNode.name, removedIn: 'l' });
1233
+ }
1234
+ else {
1235
+ added.push({ name: remoteNode.name, addedIn: 'r' });
1236
+ }
1228
1237
  if (current.nodes[remoteNode.name]) {
1229
1238
  nameOverlaps.push(remoteNode.name);
1230
1239
  }
1231
1240
  }
1232
1241
  }
1233
- const inCurrent = [];
1234
- for (const localNode of nodesInCurrentById.values()) {
1235
- inCurrent.push(nodeInfoToListingEntry(localNode));
1242
+ for (const localNode of currentNodesById.values()) {
1243
+ const syncedNode = syncedNodesById.get(localNode.objId);
1244
+ if (!syncedNode) {
1245
+ added.push({ name: localNode.name, addedIn: 'l' });
1246
+ }
1236
1247
  }
1237
1248
  return {
1238
- inCurrent: ((inCurrent.length > 0) ? inCurrent : undefined),
1239
- inRemote: ((inRemote.length > 0) ? inRemote : undefined),
1240
- differentNames: ((differentNames.length > 0) ? differentNames : undefined),
1249
+ removed: ((removed.length > 0) ? removed : undefined),
1250
+ renamed: ((renamed.length > 0) ? renamed : undefined),
1251
+ added: ((added.length > 0) ? added : undefined),
1252
+ rekeyed: ((rekeyed.length > 0) ? rekeyed : undefined),
1241
1253
  nameOverlaps: ((nameOverlaps.length > 0) ? nameOverlaps : undefined),
1242
- differentKeys: ((differentKeys.length > 0) ? differentKeys : undefined)
1243
1254
  };
1244
1255
  }
1245
- function combineCheckingNameOverlaps(inCurrent, inRemote) {
1246
- if (inCurrent) {
1247
- if (inRemote) {
1248
- const nameOverlaps = [];
1249
- for (const { name: nameInCurrent } of inCurrent) {
1250
- if (inRemote.find(({ name }) => (name === nameInCurrent))) {
1251
- nameOverlaps.push(nameInCurrent);
1252
- }
1253
- }
1254
- if (nameOverlaps.length > 0) {
1255
- return { inCurrent, inRemote, nameOverlaps };
1256
- }
1257
- else {
1258
- return { inCurrent, inRemote };
1259
- }
1260
- }
1261
- else {
1262
- return { inCurrent };
1263
- }
1264
- }
1265
- else {
1266
- if (inRemote) {
1267
- return { inRemote };
1268
- }
1269
- else {
1270
- return;
1271
- }
1272
- }
1273
- }
1274
- function diffXAttrs(current, remote) {
1275
- const haveCurrentXAttrs = (current && (current.list().length > 0));
1276
- const haveRemoteXAttrs = (remote && (remote.list().length > 0));
1277
- if (haveCurrentXAttrs && haveRemoteXAttrs) {
1278
- const inCurrent = getOnlyNotEqualXAttrs(current, remote);
1279
- const inRemote = getOnlyNotEqualXAttrs(remote, current);
1280
- return combineCheckingNameOverlaps(inCurrent, inRemote);
1281
- }
1282
- else if (haveCurrentXAttrs && !haveRemoteXAttrs) {
1283
- return { inCurrent: allXAttrsToDiff(current) };
1284
- }
1285
- else if (!haveCurrentXAttrs && haveRemoteXAttrs) {
1286
- return { inRemote: allXAttrsToDiff(remote) };
1287
- }
1288
- else {
1289
- return;
1290
- }
1291
- }
1292
- function allXAttrsToDiff(src) {
1293
- const collected = [];
1294
- for (const name of src.list()) {
1295
- const value = src.get(name);
1296
- if (value !== undefined) {
1297
- collected.push({ name, value });
1298
- }
1256
+ function orderByIds(nodes) {
1257
+ const nodesById = new Map();
1258
+ for (const node of Object.values(nodes)) {
1259
+ nodesById.set(node.objId, node);
1299
1260
  }
1300
- return collected;
1301
- }
1302
- function getOnlyNotEqualXAttrs(src, exclude) {
1303
- const collected = [];
1304
- for (const name of src.list()) {
1305
- const valueInSrc = src.get(name);
1306
- if (valueInSrc === undefined) {
1307
- continue;
1308
- }
1309
- const valueInExclude = exclude.get(name);
1310
- if ((valueInExclude === undefined)
1311
- || !areXAttrValuesEqual(valueInSrc, valueInExclude)) {
1312
- collected.push({ name, value: valueInSrc });
1313
- }
1314
- }
1315
- return ((collected.length > 0) ? collected : undefined);
1316
- }
1317
- function areXAttrValuesEqual(v1, v2) {
1318
- if (Buffer.isBuffer(v1) || ArrayBuffer.isView(v1)) {
1319
- if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)) {
1320
- return areBytesEqual(v1, v2);
1321
- }
1322
- {
1323
- return false;
1324
- }
1325
- }
1326
- else if (typeof v1 === 'string') {
1327
- if (typeof v2 === 'string') {
1328
- return (v1 === v2);
1329
- }
1330
- else {
1331
- return false;
1332
- }
1333
- }
1334
- else {
1335
- if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)
1336
- || (typeof v2 === 'string')) {
1337
- return false;
1338
- }
1339
- else {
1340
- return (0, json_utils_1.deepEqual)(v1, v2);
1341
- }
1342
- }
1343
- }
1344
- function areBytesEqual(b1, b2) {
1345
- if (b1.length !== b2.length) {
1346
- return false;
1347
- }
1348
- for (let i = 0; i < b1.length; i += 1) {
1349
- if (b1[i] !== b2[i]) {
1350
- return false;
1351
- }
1352
- }
1353
- return true;
1261
+ return nodesById;
1354
1262
  }
1355
1263
  function identifyChanges(originalNodes, newNodes) {
1356
1264
  const removedNodes = [];
@@ -1383,23 +1291,4 @@ function identifyChanges(originalNodes, newNodes) {
1383
1291
  }
1384
1292
  return { addedNodes, removedNodes, renamedNodes };
1385
1293
  }
1386
- function versionFromRemoteBranch(remote, remoteVersion) {
1387
- var _a;
1388
- if (remote.isArchived) {
1389
- return;
1390
- }
1391
- if (remoteVersion) {
1392
- if ((remoteVersion !== remote.latest)
1393
- && !((_a = remote.archived) === null || _a === void 0 ? void 0 : _a.includes(remoteVersion))) {
1394
- throw (0, exceptions_1.makeFSSyncException)(this.name, {
1395
- versionMismatch: true,
1396
- message: `Unknown remote version ${remoteVersion}`
1397
- });
1398
- }
1399
- return remoteVersion;
1400
- }
1401
- else {
1402
- return remote.latest;
1403
- }
1404
- }
1405
1294
  Object.freeze(exports);