core-3nweb-client-lib 0.44.1 → 0.44.3

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.
@@ -622,6 +622,12 @@ declare namespace web3n.files {
622
622
 
623
623
  interface ReadonlyFileSyncAPI {
624
624
 
625
+ /**
626
+ * When connection exception is caught, use this to await connection to continue.
627
+ * This returns a promise, resolvable when connected to storage server.
628
+ */
629
+ whenConnected(): Promise<void>;
630
+
625
631
  /**
626
632
  * Returns synchronization status of this object.
627
633
  * @param skipServerCheck is optional parameter to skip server check, that may be handy in offline
@@ -1404,6 +1410,12 @@ declare namespace web3n.files {
1404
1410
 
1405
1411
  interface ReadonlyFSSyncAPI {
1406
1412
 
1413
+ /**
1414
+ * When connection exception is caught, use this to await connection to continue.
1415
+ * This returns a promise, resolvable when connected to storage server.
1416
+ */
1417
+ whenConnected(): Promise<void>;
1418
+
1407
1419
  /**
1408
1420
  * Returns synchronization status of item at given path.
1409
1421
  * @param path
@@ -1481,7 +1493,7 @@ declare namespace web3n.files {
1481
1493
  */
1482
1494
  getRemoteFolderItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<ReadonlyFS>;
1483
1495
 
1484
- // XXX method to work around demaged files
1496
+ // XXX method to work around damaged files
1485
1497
  // reloadFromServer(path: string): Promise<SyncStatus>;
1486
1498
 
1487
1499
  }
@@ -1559,16 +1571,16 @@ declare namespace web3n.files {
1559
1571
  interface FolderDiff extends CommonDiff {
1560
1572
 
1561
1573
  /**
1562
- * Items that were removed. Pointing to where removal is done in remote (r), or local (l) branches.
1574
+ * Items that were removed.
1563
1575
  *
1564
1576
  * Consider the following example.
1565
1577
  * Item that is removed in local version is present in both remote and synced versions.
1566
1578
  * Hence, difference between local and remote versions is due to removal in local branch.
1567
1579
  */
1568
1580
  removed?: {
1569
- name: string;
1570
- removedIn: 'l'|'r';
1571
- }[];
1581
+ inRemote?: string[];
1582
+ inLocal?: string[];
1583
+ };
1572
1584
 
1573
1585
  /**
1574
1586
  * Items that were renamed. Pointing to where renaming is done in remote (r), or local (l) branches.
@@ -1583,15 +1595,15 @@ declare namespace web3n.files {
1583
1595
  }[];
1584
1596
 
1585
1597
  /**
1586
- * Items that were added. Pointing to where addition is done in remote (r), or local (l) branches.
1598
+ * Items that were added.
1587
1599
  *
1588
1600
  * When item added in remote branch, then it is present in remote version under the referenced name.
1589
1601
  * Synced (older) state of folder doesn't have it, and neither does local.
1590
1602
  */
1591
1603
  added?: {
1592
- name: string;
1593
- addedIn: 'l'|'r';
1594
- }[];
1604
+ inRemote?: string[];
1605
+ inLocal?: string[];
1606
+ };
1595
1607
 
1596
1608
  /**
1597
1609
  * Items that reencrypted and now have different keys.
@@ -17,5 +17,6 @@ export declare class IdKeysStorage {
17
17
  };
18
18
  getSavedKey(): Promise<JsonKey | undefined>;
19
19
  private setStorageFS;
20
+ private ensureDataFolderIsUploaded;
20
21
  }
21
22
  export {};
@@ -72,9 +72,6 @@ class IdKeysStorage {
72
72
  this.fs = fs;
73
73
  if (keysToSave) {
74
74
  await this.fs.writeJSONFile(LOGIN_KEY_FILE_NAME, keysToSave);
75
- // XXX must add work with not-online condition
76
- await this.fs.v.sync.upload(LOGIN_KEY_FILE_NAME);
77
- await this.fs.v.sync.upload('');
78
75
  }
79
76
  else {
80
77
  try {
@@ -84,6 +81,24 @@ class IdKeysStorage {
84
81
  throw (0, error_1.errWithCause)(exc, `Fail expection read of login MailerId keys from the storage`);
85
82
  }
86
83
  }
84
+ this.ensureDataFolderIsUploaded();
85
+ }
86
+ async ensureDataFolderIsUploaded() {
87
+ try {
88
+ const fileStatus = await this.fs.v.sync.status(LOGIN_KEY_FILE_NAME, false);
89
+ if (fileStatus.state === 'unsynced') {
90
+ await this.fs.v.sync.upload(LOGIN_KEY_FILE_NAME);
91
+ }
92
+ const folderStatus = await this.fs.v.sync.status('', true);
93
+ if (folderStatus.state === 'unsynced') {
94
+ await this.fs.v.sync.upload('');
95
+ }
96
+ }
97
+ catch (exc) {
98
+ if (exc.type === 'connect') {
99
+ return this.fs.v.sync.whenConnected().then(() => this.ensureDataFolderIsUploaded());
100
+ }
101
+ }
87
102
  }
88
103
  }
89
104
  exports.IdKeysStorage = IdKeysStorage;
@@ -19,10 +19,13 @@ export declare class RemoteEvents {
19
19
  private readonly connectionEvents;
20
20
  readonly connectionEvent$: Observable<StorageConnectionStatus>;
21
21
  private readonly wsProc;
22
+ private deferredConnection;
22
23
  constructor(remoteStorage: StorageOwner, files: ObjFiles, broadcastNodeEvent: Storage['broadcastNodeEvent'], logError: LogError);
23
24
  private makeProc;
24
25
  startListening(): void;
25
26
  close(): Promise<void>;
27
+ whenConnected(): Promise<void>;
28
+ private signalConnected;
26
29
  private absorbObjChange;
27
30
  private absorbObjRemoval;
28
31
  private absorbObjVersionArchival;
@@ -21,6 +21,7 @@ const rxjs_1 = require("rxjs");
21
21
  const owner_1 = require("../../../lib-common/service-api/3nstorage/owner");
22
22
  const operators_1 = require("rxjs/operators");
23
23
  const ws_ipc_1 = require("../../../lib-common/ipc/ws-ipc");
24
+ const deferred_1 = require("../../../lib-common/processes/deferred");
24
25
  function toStorageConnectionStatus(status, params) {
25
26
  return (0, ws_ipc_1.addToStatus)(status, {
26
27
  service: 'storage',
@@ -40,13 +41,25 @@ class RemoteEvents {
40
41
  this.logError = logError;
41
42
  this.connectionEvents = new rxjs_1.Subject();
42
43
  this.connectionEvent$ = this.connectionEvents.asObservable().pipe((0, operators_1.share)());
44
+ this.deferredConnection = (0, deferred_1.defer)();
43
45
  this.wsProc = new ws_ipc_1.WebSocketListening(SERVER_EVENTS_RESTART_WAIT_SECS, this.makeProc.bind(this));
44
46
  Object.seal(this);
45
47
  }
46
48
  makeProc() {
47
49
  return (0, rxjs_1.from)(this.remoteStorage.openEventSource().then(({ client, heartbeat }) => {
50
+ this.signalConnected();
48
51
  heartbeat.subscribe({
49
- next: ev => this.connectionEvents.next(toStorageConnectionStatus(ev))
52
+ next: ev => {
53
+ this.connectionEvents.next(toStorageConnectionStatus(ev));
54
+ if (ev.type === 'heartbeat') {
55
+ this.signalConnected();
56
+ }
57
+ else if ((ev.type === 'heartbeat-skip') || (ev.type === 'disconnected')) {
58
+ if (!this.deferredConnection) {
59
+ this.deferredConnection = (0, deferred_1.defer)();
60
+ }
61
+ }
62
+ }
50
63
  });
51
64
  return [
52
65
  this.absorbObjChange(client),
@@ -65,6 +78,16 @@ class RemoteEvents {
65
78
  this.connectionEvents.complete();
66
79
  this.wsProc.close();
67
80
  }
81
+ async whenConnected() {
82
+ var _a;
83
+ return (_a = this.deferredConnection) === null || _a === void 0 ? void 0 : _a.promise;
84
+ }
85
+ signalConnected() {
86
+ if (this.deferredConnection) {
87
+ this.deferredConnection.resolve();
88
+ this.deferredConnection = undefined;
89
+ }
90
+ }
68
91
  absorbObjChange(client) {
69
92
  return (new rxjs_1.Observable(obs => client.subscribe(owner_1.events.objChanged.EVENT_NAME, obs)))
70
93
  .pipe((0, operators_1.mergeMap)(async ({ newVer: newRemoteVersion, objId }) => {
@@ -62,5 +62,6 @@ export declare class SyncedStore implements ISyncedStorage {
62
62
  suspendNetworkActivity(): void;
63
63
  resumeNetworkActivity(): void;
64
64
  get connectionEvent$(): Observable<import("./remote-events").StorageConnectionStatus>;
65
+ whenConnected(): Promise<void>;
65
66
  }
66
67
  export {};
@@ -248,6 +248,9 @@ class SyncedStore {
248
248
  get connectionEvent$() {
249
249
  return this.remoteEvents.connectionEvent$;
250
250
  }
251
+ whenConnected() {
252
+ return this.remoteEvents.whenConnected();
253
+ }
251
254
  }
252
255
  exports.SyncedStore = SyncedStore;
253
256
  Object.freeze(SyncedStore.prototype);
@@ -151,6 +151,10 @@ export declare namespace vGetByteSink {
151
151
  function wrapService(fn: WritableFileVersionedAPI['getByteSink'], expServices: CoreSideServices): ExposedFn;
152
152
  function makeCaller(caller: Caller, objPath: string[]): WritableFileVersionedAPI['getByteSink'];
153
153
  }
154
+ export declare namespace vsWhenConnected {
155
+ function wrapService(fn: ReadonlyFileSyncAPI['whenConnected']): ExposedFn;
156
+ function makeCaller(caller: Caller, objPath: string[]): ReadonlyFileSyncAPI['whenConnected'];
157
+ }
154
158
  export declare namespace vsStartDownload {
155
159
  const replyType: ProtoType<{
156
160
  startedDownload?: {
@@ -16,7 +16,7 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.vsDiffCurrentAndRemoteVersions = exports.vListVersions = exports.vsUpload = exports.vsStartUpload = exports.vsStartDownload = exports.vGetByteSink = exports.updateXAttrs = exports.vGetByteSource = exports.vReadJSON = exports.vReadTxt = exports.vReadBytes = exports.vListXAttrs = exports.vGetXAttr = exports.vStat = exports.readBytes = exports.fileMsgType = void 0;
19
+ exports.vsDiffCurrentAndRemoteVersions = exports.vListVersions = exports.vsUpload = exports.vsStartUpload = exports.vsStartDownload = exports.vsWhenConnected = exports.vGetByteSink = exports.updateXAttrs = exports.vGetByteSource = exports.vReadJSON = exports.vReadTxt = exports.vReadBytes = exports.vListXAttrs = exports.vGetXAttr = exports.vStat = exports.readBytes = exports.fileMsgType = void 0;
20
20
  exports.makeFileCaller = makeFileCaller;
21
21
  exports.exposeFileService = exposeFileService;
22
22
  exports.packStats = packStats;
@@ -100,6 +100,7 @@ function makeFileCaller(caller, fileMsg) {
100
100
  if (fileMsg.isSynced) {
101
101
  const vsPath = (0, protobuf_msg_1.methodPathFor)(vPath, 'sync');
102
102
  file.v.sync = {
103
+ whenConnected: vsWhenConnected.makeCaller(caller, vsPath),
103
104
  status: vsStatus.makeCaller(caller, vsPath),
104
105
  isRemoteVersionOnDisk: vsIsRemoteVersionOnDisk.makeCaller(caller, vsPath),
105
106
  startDownload: vsStartDownload.makeCaller(caller, vsPath),
@@ -156,6 +157,7 @@ function exposeFileService(file, expServices) {
156
157
  }
157
158
  if (file.v.sync) {
158
159
  implExp.v.sync = {
160
+ whenConnected: vsWhenConnected.wrapService(file.v.sync.whenConnected),
159
161
  status: vsStatus.wrapService(file.v.sync.status),
160
162
  isRemoteVersionOnDisk: vsIsRemoteVersionOnDisk.wrapService(file.v.sync.isRemoteVersionOnDisk),
161
163
  startDownload: vsStartDownload.wrapService(file.v.sync.startDownload),
@@ -1084,6 +1086,25 @@ var vGetByteSink;
1084
1086
  vGetByteSink.makeCaller = makeCaller;
1085
1087
  })(vGetByteSink || (exports.vGetByteSink = vGetByteSink = {}));
1086
1088
  Object.freeze(vGetByteSink);
1089
+ var vsWhenConnected;
1090
+ (function (vsWhenConnected) {
1091
+ function wrapService(fn) {
1092
+ return buf => {
1093
+ const promise = fn();
1094
+ return { promise };
1095
+ };
1096
+ }
1097
+ vsWhenConnected.wrapService = wrapService;
1098
+ function makeCaller(caller, objPath) {
1099
+ const path = (0, protobuf_msg_1.methodPathFor)(objPath, 'whenConnected');
1100
+ return () => caller
1101
+ .startPromiseCall(path, undefined)
1102
+ .then(noop);
1103
+ }
1104
+ vsWhenConnected.makeCaller = makeCaller;
1105
+ })(vsWhenConnected || (exports.vsWhenConnected = vsWhenConnected = {}));
1106
+ Object.freeze(vsWhenConnected);
1107
+ function noop() { }
1087
1108
  var vsStatus;
1088
1109
  (function (vsStatus) {
1089
1110
  function wrapService(fn) {
@@ -101,6 +101,7 @@ function makeFSCaller(caller, fsMsg) {
101
101
  if (fsMsg.isSynced) {
102
102
  const vsPath = (0, protobuf_msg_1.methodPathFor)(vPath, 'sync');
103
103
  fs.v.sync = {
104
+ whenConnected: file.vsWhenConnected.makeCaller(caller, vsPath),
104
105
  status: vsStatus.makeCaller(caller, vsPath),
105
106
  isRemoteVersionOnDisk: vsIsRemoteVersionOnDisk.makeCaller(caller, vsPath),
106
107
  startDownload: vsDownload.makeCaller(caller, vsPath),
@@ -186,6 +187,7 @@ function exposeFSService(fs, expServices) {
186
187
  }
187
188
  if (fs.v.sync) {
188
189
  implExp.v.sync = {
190
+ whenConnected: file.vsWhenConnected.wrapService(fs.v.sync.whenConnected),
189
191
  status: vsStatus.wrapService(fs.v.sync.status),
190
192
  isRemoteVersionOnDisk: vsIsRemoteVersionOnDisk.wrapService(fs.v.sync.isRemoteVersionOnDisk),
191
193
  startDownload: vsDownload.wrapService(fs.v.sync.startDownload),
@@ -2213,10 +2215,8 @@ function folderDiffToMsg(diff) {
2213
2215
  }
2214
2216
  return {
2215
2217
  ...file.commonDiffToMsg(diff),
2216
- added: (diff.added && (diff.added.length > 0) ?
2217
- diff.added.map(({ name, addedIn }) => ({ name, addedIn })) : undefined),
2218
- removed: (diff.removed && (diff.removed.length > 0) ?
2219
- diff.removed.map(({ name, removedIn }) => ({ name, removedIn })) : undefined),
2218
+ added: diff.added,
2219
+ removed: diff.removed,
2220
2220
  renamed: (diff.renamed && (diff.renamed.length > 0) ?
2221
2221
  diff.renamed.map(({ local, remote, renamedIn }) => ({ local, remote, renamedIn })) : undefined),
2222
2222
  rekeyed: (diff.rekeyed && (diff.rekeyed.length > 0) ?
@@ -2230,13 +2230,37 @@ function folderDiffFromMsg(msg) {
2230
2230
  }
2231
2231
  return {
2232
2232
  ...file.commonDiffFromMsg(msg),
2233
- added: (msg.added ? msg.added : undefined),
2234
- removed: (msg.removed ? msg.removed : undefined),
2235
- renamed: (msg.renamed ? msg.renamed : undefined),
2236
- rekeyed: (msg.rekeyed ? msg.rekeyed : undefined),
2237
- nameOverlaps: (msg.nameOverlaps ? msg.nameOverlaps : undefined),
2233
+ added: reduceEmptyIn(msg.added),
2234
+ removed: reduceEmptyIn(msg.removed),
2235
+ renamed: reduceEmptyArr(msg.renamed),
2236
+ rekeyed: reduceEmptyArr(msg.rekeyed),
2237
+ nameOverlaps: reduceEmptyArr(msg.nameOverlaps),
2238
2238
  };
2239
2239
  }
2240
+ function reduceEmptyIn(c) {
2241
+ if (!c) {
2242
+ return;
2243
+ }
2244
+ if (c.inLocal && (c.inLocal.length > 0)) {
2245
+ if (c.inRemote && (c.inRemote.length > 0)) {
2246
+ return c;
2247
+ }
2248
+ else {
2249
+ return { inLocal: c.inLocal };
2250
+ }
2251
+ }
2252
+ else {
2253
+ if (c.inRemote && (c.inRemote.length > 0)) {
2254
+ return { inRemote: c.inRemote };
2255
+ }
2256
+ else {
2257
+ return;
2258
+ }
2259
+ }
2260
+ }
2261
+ function reduceEmptyArr(arr) {
2262
+ return (arr && (arr.length > 0) ? arr : undefined);
2263
+ }
2240
2264
  var vsDiffCurrentAndRemoteFolderVersions;
2241
2265
  (function (vsDiffCurrentAndRemoteFolderVersions) {
2242
2266
  const requestType = protobuf_type_1.ProtoType.for(fs_proto_1.fs.DiffCurrentAndRemoteFolderVersionsRequestBody);
@@ -86,6 +86,7 @@ function wrapWritableFileSyncAPI(sImpl) {
86
86
  return;
87
87
  }
88
88
  const w = {
89
+ whenConnected: sImpl.whenConnected.bind(sImpl),
89
90
  status: sImpl.status.bind(sImpl),
90
91
  startDownload: sImpl.startDownload.bind(sImpl),
91
92
  isRemoteVersionOnDisk: sImpl.isRemoteVersionOnDisk.bind(sImpl),
@@ -140,6 +141,7 @@ function wrapReadonlyFileSyncAPI(sImpl) {
140
141
  return;
141
142
  }
142
143
  const w = {
144
+ whenConnected: sImpl.whenConnected.bind(sImpl),
143
145
  status: sImpl.status.bind(sImpl),
144
146
  startDownload: sImpl.startDownload.bind(sImpl),
145
147
  isRemoteVersionOnDisk: sImpl.isRemoteVersionOnDisk.bind(sImpl),
@@ -235,6 +237,7 @@ function wrapWritableFSSyncAPI(sImpl) {
235
237
  return;
236
238
  }
237
239
  const w = {
240
+ whenConnected: sImpl.whenConnected.bind(sImpl),
238
241
  status: sImpl.status.bind(sImpl),
239
242
  startDownload: sImpl.startDownload.bind(sImpl),
240
243
  isRemoteVersionOnDisk: sImpl.isRemoteVersionOnDisk.bind(sImpl),
@@ -303,6 +306,7 @@ function wrapReadonlyFSSyncAPI(sImpl) {
303
306
  return;
304
307
  }
305
308
  const w = {
309
+ whenConnected: sImpl.whenConnected.bind(sImpl),
306
310
  status: sImpl.status.bind(sImpl),
307
311
  startDownload: sImpl.startDownload.bind(sImpl),
308
312
  isRemoteVersionOnDisk: sImpl.isRemoteVersionOnDisk.bind(sImpl),
@@ -121,6 +121,7 @@ export interface SyncedStorage extends Storage {
121
121
  suspendNetworkActivity(): void;
122
122
  resumeNetworkActivity(): void;
123
123
  connectionEvent$: Observable<StorageConnectionStatus>;
124
+ whenConnected(): Promise<void>;
124
125
  }
125
126
  export interface SyncedObjStatus extends LocalObjStatus {
126
127
  syncStatus(): SyncStatus;
@@ -127,6 +127,7 @@ function wrapSyncStorageImplementation(impl) {
127
127
  wrap.resumeNetworkActivity = impl.resumeNetworkActivity.bind(impl);
128
128
  wrap.getNumOfBytesNeedingDownload = impl.getNumOfBytesNeedingDownload.bind(impl);
129
129
  wrap.connectionEvent$ = impl.connectionEvent$;
130
+ wrap.whenConnected = impl.whenConnected.bind(impl);
130
131
  return Object.freeze(wrap);
131
132
  }
132
133
  function isSyncedStorage(storage) {
@@ -44,6 +44,7 @@ export declare class FileNode extends NodeInFS<FilePersistance> {
44
44
  private static initWithAttrs;
45
45
  protected setCurrentStateFrom(src: ObjSource): Promise<void>;
46
46
  private setUpdatedState;
47
+ getStorage(): Storage;
47
48
  getStats(flags?: VersionedReadFlags): Promise<Stats>;
48
49
  readSrc(flags: VersionedReadFlags | undefined): Promise<{
49
50
  src: FileByteSource;
@@ -126,6 +126,9 @@ class FileNode extends node_in_fs_1.NodeInFS {
126
126
  this.fileSize = fileAttrs.size;
127
127
  super.setUpdatedParams(version, fileAttrs.attrs, fileAttrs.xattrs);
128
128
  }
129
+ getStorage() {
130
+ return this.storage;
131
+ }
129
132
  async getStats(flags) {
130
133
  const { stats, attrs } = await this.getStatsAndSize(flags);
131
134
  stats.size = (attrs ? attrs.size : this.fileSize);
@@ -100,6 +100,7 @@ declare class V implements WritableFileVersionedAPI, N {
100
100
  declare class S implements WritableFileSyncAPI {
101
101
  private readonly n;
102
102
  constructor(n: N);
103
+ whenConnected(): Promise<void>;
103
104
  startUpload(opts?: OptionsToUploadLocal): Promise<{
104
105
  uploadVersion: number;
105
106
  uploadTaskId: number;
@@ -224,6 +224,9 @@ class S {
224
224
  this.n = n;
225
225
  Object.freeze(this);
226
226
  }
227
+ async whenConnected() {
228
+ return (await this.n.getNode()).getStorage().whenConnected();
229
+ }
227
230
  async startUpload(opts) {
228
231
  this.n.ensureIsWritable();
229
232
  const node = await this.n.getNode();
@@ -1096,13 +1096,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1096
1096
  const nodes = state.nodes;
1097
1097
  const remoteNodes = remote.folderInfo.nodes;
1098
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
- }
1099
+ if (removed === null || removed === void 0 ? void 0 : removed.inRemote) {
1100
+ for (const name of removed.inRemote) {
1101
+ const node = nodes[name];
1102
+ delete nodes[name];
1103
+ events.push(this.makeEntryRemovalEvent(newVersion, 'sync', node));
1106
1104
  }
1107
1105
  }
1108
1106
  if (renamed) {
@@ -1122,19 +1120,17 @@ class FolderNode extends node_in_fs_1.NodeInFS {
1122
1120
  }
1123
1121
  }
1124
1122
  }
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
- }
1123
+ if (added === null || added === void 0 ? void 0 : added.inRemote) {
1124
+ for (const name of added.inRemote) {
1125
+ const node = remoteNodes[name];
1126
+ (0, assert_1.assert)(!!node);
1127
+ if (nameOverlaps === null || nameOverlaps === void 0 ? void 0 : nameOverlaps.includes(name)) {
1128
+ while (nodes[node.name]) {
1129
+ node.name = `${node.name}${opts === null || opts === void 0 ? void 0 : opts.postfixForNameOverlaps}`;
1134
1130
  }
1135
- nodes[node.name] = node;
1136
- events.push(this.makeEntryAdditionEvent(newVersion, 'sync', node));
1137
1131
  }
1132
+ nodes[node.name] = node;
1133
+ events.push(this.makeEntryAdditionEvent(newVersion, 'sync', node));
1138
1134
  }
1139
1135
  }
1140
1136
  return events;
@@ -1188,9 +1184,9 @@ function addToTransitionState(state, f, key) {
1188
1184
  function diffNodes(current, remote, synced) {
1189
1185
  const currentNodesById = orderByIds(current.nodes);
1190
1186
  const syncedNodesById = orderByIds(synced.nodes);
1191
- const removed = [];
1187
+ const removed = { inLocal: [], inRemote: [] };
1192
1188
  const renamed = [];
1193
- const added = [];
1189
+ const added = { inLocal: [], inRemote: [] };
1194
1190
  const rekeyed = [];
1195
1191
  const nameOverlaps = [];
1196
1192
  for (const remoteNode of Object.values(remote.nodes)) {
@@ -1229,10 +1225,10 @@ function diffNodes(current, remote, synced) {
1229
1225
  else {
1230
1226
  const syncedNode = syncedNodesById.get(remoteNode.objId);
1231
1227
  if (syncedNode) {
1232
- removed.push({ name: remoteNode.name, removedIn: 'l' });
1228
+ removed.inLocal.push(remoteNode.name);
1233
1229
  }
1234
1230
  else {
1235
- added.push({ name: remoteNode.name, addedIn: 'r' });
1231
+ added.inRemote.push(remoteNode.name);
1236
1232
  }
1237
1233
  if (current.nodes[remoteNode.name]) {
1238
1234
  nameOverlaps.push(remoteNode.name);
@@ -1242,13 +1238,13 @@ function diffNodes(current, remote, synced) {
1242
1238
  for (const localNode of currentNodesById.values()) {
1243
1239
  const syncedNode = syncedNodesById.get(localNode.objId);
1244
1240
  if (!syncedNode) {
1245
- added.push({ name: localNode.name, addedIn: 'l' });
1241
+ added.inLocal.push(localNode.name);
1246
1242
  }
1247
1243
  }
1248
1244
  return {
1249
- removed: ((removed.length > 0) ? removed : undefined),
1245
+ removed: reduceEmptyIn(removed),
1250
1246
  renamed: ((renamed.length > 0) ? renamed : undefined),
1251
- added: ((added.length > 0) ? added : undefined),
1247
+ added: reduceEmptyIn(added),
1252
1248
  rekeyed: ((rekeyed.length > 0) ? rekeyed : undefined),
1253
1249
  nameOverlaps: ((nameOverlaps.length > 0) ? nameOverlaps : undefined),
1254
1250
  };
@@ -1291,4 +1287,22 @@ function identifyChanges(originalNodes, newNodes) {
1291
1287
  }
1292
1288
  return { addedNodes, removedNodes, renamedNodes };
1293
1289
  }
1290
+ function reduceEmptyIn(c) {
1291
+ if (c.inLocal.length > 0) {
1292
+ if (c.inRemote.length > 0) {
1293
+ return c;
1294
+ }
1295
+ else {
1296
+ return { inLocal: c.inLocal };
1297
+ }
1298
+ }
1299
+ else {
1300
+ if (c.inRemote.length > 0) {
1301
+ return { inRemote: c.inRemote };
1302
+ }
1303
+ else {
1304
+ return;
1305
+ }
1306
+ }
1307
+ }
1294
1308
  Object.freeze(exports);
@@ -166,6 +166,7 @@ declare class V implements WritableFSVersionedAPI, N {
166
166
  declare class S implements WritableFSSyncAPI {
167
167
  private readonly n;
168
168
  constructor(n: N);
169
+ whenConnected(): Promise<void>;
169
170
  startUpload(path: string, opts?: OptionsToUploadLocal): Promise<{
170
171
  uploadVersion: number;
171
172
  uploadTaskId: number;
@@ -708,6 +708,9 @@ class S {
708
708
  this.n = n;
709
709
  Object.freeze(this);
710
710
  }
711
+ whenConnected() {
712
+ return this.n.getRootIfNotClosed('').getStorage().whenConnected();
713
+ }
711
714
  async startUpload(path, opts) {
712
715
  this.n.ensureIsWritable();
713
716
  const node = await this.n.get(path);
@@ -859,24 +862,9 @@ class S {
859
862
  throw (0, file_1.makeFileException)('notDirectory', `${path}/${remoteItemName}`);
860
863
  }
861
864
  }
862
- // async adoptAllRemoteItems(
863
- // path: string, opts?: OptionsToAdoptAllRemoteItems
864
- // ): Promise<number|undefined> {
865
- // const folderNode = await this.getFolderNode(path);
866
- // return await folderNode.adoptItemsFromRemoteVersion(opts);
867
- // }
868
865
  async mergeFolderCurrentAndRemoteVersions(path, opts) {
869
866
  const folderNode = await this.getFolderNode(path);
870
- const newLocalVersion = await folderNode.mergeCurrentAndRemoteVersions(opts);
871
- if (newLocalVersion && (newLocalVersion < 0)) {
872
- const { folderPath } = split(path);
873
- if (folderPath.length > 0) {
874
- const parent = await this.n.get(folderPath.join('/'));
875
- // XXX removing folder in parent -- what happens with children?
876
- await parent.removeChild(folderNode);
877
- }
878
- }
879
- return newLocalVersion;
867
+ return await folderNode.mergeCurrentAndRemoteVersions(opts);
880
868
  }
881
869
  }
882
870
  Object.freeze(S.prototype);