core-3nweb-client-lib 0.44.4 → 0.44.6

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.
@@ -675,6 +675,7 @@ declare namespace web3n.files {
675
675
 
676
676
  interface OptionsToDiffFileVersions {
677
677
  remoteVersion?: number;
678
+ // XXX implicit assumption that mtime changes only when file content changes
678
679
  compareContentIfSameMTime?: boolean;
679
680
  }
680
681
 
@@ -1200,7 +1201,7 @@ declare namespace web3n.files {
1200
1201
  syncStatus: SyncStatus;
1201
1202
  }
1202
1203
 
1203
- type UploadEvent = UploadStartEvent | UploadProgressEvent | UploadDoneEvent;
1204
+ type UploadEvent = UploadStartEvent | UploadProgressEvent | UploadDisconnectedEvent | UploadDoneEvent;
1204
1205
 
1205
1206
  interface UploadEventBase extends FSEvent {
1206
1207
  uploadTaskId: number;
@@ -1219,6 +1220,10 @@ declare namespace web3n.files {
1219
1220
  bytesLeftToUpload: number;
1220
1221
  }
1221
1222
 
1223
+ interface UploadDisconnectedEvent extends UploadEventBase {
1224
+ type: 'upload-disconnected';
1225
+ }
1226
+
1222
1227
  interface UploadDoneEvent extends UploadEventBase {
1223
1228
  type: 'upload-done';
1224
1229
  }
@@ -1493,6 +1498,12 @@ declare namespace web3n.files {
1493
1498
  */
1494
1499
  getRemoteFolderItem(path: string, remoteItemName: string, remoteVersion?: number): Promise<ReadonlyFS>;
1495
1500
 
1501
+ // compareLocalAndRemoteFileItems(path: string, itemName: string, remoteVersion?: number): Promise<FileDiff>;
1502
+
1503
+ // compareLocalAndRemoteFolderItems(
1504
+ // path: string, itemName: string, remoteVersion?: number
1505
+ // ): Promise<FolderContentDiff>;
1506
+
1496
1507
  // XXX method to work around damaged files
1497
1508
  // reloadFromServer(path: string): Promise<SyncStatus>;
1498
1509
 
@@ -1631,6 +1642,13 @@ declare namespace web3n.files {
1631
1642
  };
1632
1643
  }
1633
1644
 
1645
+ // interface FolderContentDiff extends CommonDiff {
1646
+ // local: ({
1647
+
1648
+ // } & ListingEntry)[];
1649
+ // remote: ({} & ListingEntry)[];
1650
+ // }
1651
+
1634
1652
  interface WritableFSSyncAPI extends ReadonlyFSSyncAPI {
1635
1653
 
1636
1654
  /**
@@ -31,8 +31,10 @@ export declare class InboxEvents {
31
31
  readonly connectionEvent$: Observable<InboxConnectionStatus>;
32
32
  private readonly wsProc;
33
33
  private disconnectedAt;
34
+ private connection;
34
35
  constructor(msgReceiver: MailRecipient, getMsg: (msgId: string) => Promise<IncomingMessage>, listNewMsgs: (fromTS: number) => Promise<MsgInfo[]>, rmMsg: (msgId: string) => Promise<void>, logError: LogError);
35
36
  private makeProc;
37
+ whenConnected(): Promise<void>;
36
38
  private getMessage;
37
39
  subscribe<T>(event: InboxEventType, observer: Observer<T>): () => void;
38
40
  close(): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2017, 2019, 2022, 2024 - 2025 3NSoft Inc.
3
+ Copyright (C) 2017, 2019, 2022, 2024 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -22,6 +22,7 @@ const retrieval_1 = require("../../../lib-common/service-api/asmail/retrieval");
22
22
  const operators_1 = require("rxjs/operators");
23
23
  const utils_for_observables_1 = require("../../../lib-common/utils-for-observables");
24
24
  const ws_ipc_1 = require("../../../lib-common/ipc/ws-ipc");
25
+ const awaitable_state_1 = require("../../../lib-common/awaitable-state");
25
26
  function toInboxConnectionStatus(status, params) {
26
27
  return (0, ws_ipc_1.addToStatus)(status, {
27
28
  service: 'inbox',
@@ -52,6 +53,7 @@ class InboxEvents {
52
53
  this.connectionEvents = new rxjs_1.Subject();
53
54
  this.connectionEvent$ = this.connectionEvents.asObservable().pipe((0, operators_1.share)());
54
55
  this.disconnectedAt = undefined;
56
+ this.connection = new awaitable_state_1.AwaitableState();
55
57
  this.wsProc = new ws_ipc_1.WebSocketListening(SERVER_EVENTS_RESTART_WAIT_SECS, this.makeProc.bind(this));
56
58
  this.wsProc.startListening();
57
59
  Object.seal(this);
@@ -60,7 +62,15 @@ class InboxEvents {
60
62
  const proc$ = (0, rxjs_1.from)(this.msgReceiver.openEventSource().then(({ client, heartbeat }) => {
61
63
  const channel = retrieval_1.msgRecievedCompletely.EVENT_NAME;
62
64
  heartbeat.subscribe({
63
- next: ev => this.connectionEvents.next(toInboxConnectionStatus(ev))
65
+ next: ev => {
66
+ this.connectionEvents.next(toInboxConnectionStatus(ev));
67
+ if (ev.type === 'heartbeat') {
68
+ this.connection.setState();
69
+ }
70
+ else if ((ev.type === 'heartbeat-skip') || (ev.type === 'disconnected')) {
71
+ this.connection.clearState();
72
+ }
73
+ }
64
74
  });
65
75
  return new rxjs_1.Observable(obs => client.subscribe(channel, obs));
66
76
  }))
@@ -76,6 +86,9 @@ class InboxEvents {
76
86
  this.listMsgsFromDisconnectedPeriod();
77
87
  return proc$;
78
88
  }
89
+ async whenConnected() {
90
+ return this.connection.whenStateIsSet();
91
+ }
79
92
  async getMessage(msgId) {
80
93
  try {
81
94
  return await this.getMsg(msgId);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -58,12 +58,13 @@ class InboxOnServer {
58
58
  try {
59
59
  (0, file_1.ensureCorrectFS)(syncedFS, 'synced', true);
60
60
  const msgReceiver = new recipient_1.MailRecipient(r.address, r.getSigner, () => r.asmailResolver(r.address), r.makeNet());
61
- const downloader = new msg_downloader_1.MsgDownloader(msgReceiver);
61
+ const downloader = new msg_downloader_1.MsgDownloader(msgReceiver, () => inbox.inboxEvents.whenConnected());
62
62
  const cache = await cached_msgs_1.CachedMessages.makeFor(cachePath, downloader, r.logError);
63
63
  const indexSyncedFS = await (0, fs_sync_utils_1.getOrMakeAndUploadFolderIn)(syncedFS, MSG_INDEX_FOLDER);
64
64
  const index = await msg_indexing_1.MsgIndex.make(indexSyncedFS);
65
65
  await (0, fs_sync_utils_1.uploadFolderChangesIfAny)(syncedFS);
66
- return new InboxOnServer(r.correspondents, msgReceiver, r.getStorages, r.cryptor, downloader, cache, index, r.logError);
66
+ const inbox = new InboxOnServer(r.correspondents, msgReceiver, r.getStorages, r.cryptor, downloader, cache, index, r.logError);
67
+ return inbox;
67
68
  }
68
69
  catch (err) {
69
70
  throw (0, error_1.errWithCause)(err, 'Failed to initialize Inbox');
@@ -3,8 +3,9 @@ import { ObjDownloader } from "../../../lib-client/objs-on-disk/obj-on-disk";
3
3
  import { MsgMeta } from "../../../lib-common/service-api/asmail/retrieval";
4
4
  export declare class MsgDownloader {
5
5
  private readonly msgReceiver;
6
+ private readonly whenConnected;
6
7
  private readonly runner;
7
- constructor(msgReceiver: MailRecipient);
8
+ constructor(msgReceiver: MailRecipient, whenConnected: () => Promise<void>);
8
9
  getMsgMeta(msgId: string): Promise<MsgMeta>;
9
10
  getObjDownloader(msgId: string): ObjDownloader;
10
11
  private getLayoutWithHeaderAndFirstSegs;
@@ -21,8 +21,9 @@ const obj_on_disk_1 = require("../../../lib-client/objs-on-disk/obj-on-disk");
21
21
  const MAX_GETTING_CHUNK = 512 * 1024;
22
22
  const DOWNLOAD_START_CHUNK = 128 * 1024;
23
23
  class MsgDownloader {
24
- constructor(msgReceiver) {
24
+ constructor(msgReceiver, whenConnected) {
25
25
  this.msgReceiver = msgReceiver;
26
+ this.whenConnected = whenConnected;
26
27
  this.runner = new obj_on_disk_1.DownloadsRunner();
27
28
  Object.freeze(this);
28
29
  }
@@ -34,7 +35,8 @@ class MsgDownloader {
34
35
  getLayoutWithHeaderAndFirstSegs: (objId, v) => this.getLayoutWithHeaderAndFirstSegs(msgId, objId),
35
36
  getSegs: (objId, v, start, end) => this.getSegs(msgId, objId, start, end),
36
37
  splitSegsDownloads: (start, end) => (0, obj_on_disk_1.splitSegsDownloads)(start, end, MAX_GETTING_CHUNK),
37
- schedule: this.runner.schedule.bind(this.runner)
38
+ schedule: this.runner.schedule.bind(this.runner),
39
+ whenConnected: this.whenConnected
38
40
  };
39
41
  }
40
42
  async getLayoutWithHeaderAndFirstSegs(msgId, objId) {
@@ -3,8 +3,9 @@ import { ObjId } from '../../../lib-client/xsp-fs/common';
3
3
  import { ObjDownloader, InitDownloadParts, Section, Download } from '../../../lib-client/objs-on-disk/obj-on-disk';
4
4
  export declare class Downloader implements ObjDownloader {
5
5
  private readonly remoteStorage;
6
+ readonly whenConnected: () => Promise<void>;
6
7
  private readonly runner;
7
- constructor(remoteStorage: StorageOwner);
8
+ constructor(remoteStorage: StorageOwner, whenConnected: () => Promise<void>);
8
9
  getLayoutWithHeaderAndFirstSegs(objId: ObjId, version: number): Promise<InitDownloadParts>;
9
10
  schedule(download: Download): void;
10
11
  getSegs(objId: ObjId, version: number, start: number, end: number): Promise<Uint8Array>;
@@ -21,8 +21,9 @@ const obj_on_disk_1 = require("../../../lib-client/objs-on-disk/obj-on-disk");
21
21
  const MAX_GETTING_CHUNK = 512 * 1024;
22
22
  const DOWNLOAD_START_CHUNK = 128 * 1024;
23
23
  class Downloader {
24
- constructor(remoteStorage) {
24
+ constructor(remoteStorage, whenConnected) {
25
25
  this.remoteStorage = remoteStorage;
26
+ this.whenConnected = whenConnected;
26
27
  this.runner = new obj_on_disk_1.DownloadsRunner();
27
28
  Object.seal(this);
28
29
  }
@@ -27,7 +27,7 @@ export declare class ObjFiles {
27
27
  private readonly downloader;
28
28
  private readonly gc;
29
29
  private constructor();
30
- static makeFor(path: string, remote: RemoteStorage, logError: LogError): Promise<ObjFiles>;
30
+ static makeFor(path: string, remote: RemoteStorage, whenConnected: () => Promise<void>, logError: LogError): Promise<ObjFiles>;
31
31
  private canMoveObjToDeeperCache;
32
32
  findObj(objId: ObjId): Promise<SyncedObj | undefined>;
33
33
  getObjInCache(objId: ObjId): SyncedObj | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2016 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2016 - 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -42,12 +42,12 @@ exports.REMOTE_FILE_NAME_EXT = 'v';
42
42
  * SyncedObj's.
43
43
  */
44
44
  class ObjFiles {
45
- constructor(folders, remote, logError) {
45
+ constructor(folders, remote, whenConnected, logError) {
46
46
  this.folders = folders;
47
47
  this.logError = logError;
48
48
  this.objs = (0, timed_cache_1.makeTimedCache)(60 * 1000);
49
49
  this.sync = makeSynchronizer();
50
- this.downloader = new downloader_1.Downloader(remote);
50
+ this.downloader = new downloader_1.Downloader(remote, whenConnected);
51
51
  this.gc = new obj_files_gc_1.GC(this.sync, obj => {
52
52
  if (this.objs.get(obj.objId) === obj) {
53
53
  this.objs.delete(obj.objId);
@@ -55,10 +55,10 @@ class ObjFiles {
55
55
  }, objId => this.folders.removeFolderOf(objId));
56
56
  Object.freeze(this);
57
57
  }
58
- static async makeFor(path, remote, logError) {
58
+ static async makeFor(path, remote, whenConnected, logError) {
59
59
  const canMove = (objId, objFolderPath) => objFiles.canMoveObjToDeeperCache(objId, objFolderPath);
60
60
  const folders = await obj_folders_1.ObjFolders.makeWithGenerations(path, canMove, logError);
61
- const objFiles = new ObjFiles(folders, remote, logError);
61
+ const objFiles = new ObjFiles(folders, remote, whenConnected, logError);
62
62
  return objFiles;
63
63
  }
64
64
  async canMoveObjToDeeperCache(objId, objFolderPath) {
@@ -19,13 +19,12 @@ export declare class RemoteEvents {
19
19
  private readonly connectionEvents;
20
20
  readonly connectionEvent$: Observable<StorageConnectionStatus>;
21
21
  private readonly wsProc;
22
- private deferredConnection;
22
+ private connection;
23
23
  constructor(remoteStorage: StorageOwner, files: ObjFiles, broadcastNodeEvent: Storage['broadcastNodeEvent'], logError: LogError);
24
24
  private makeProc;
25
25
  startListening(): void;
26
26
  close(): Promise<void>;
27
27
  whenConnected(): Promise<void>;
28
- private signalConnected;
29
28
  private absorbObjChange;
30
29
  private absorbObjRemoval;
31
30
  private absorbObjVersionArchival;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2019 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2019 - 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -21,7 +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
+ const awaitable_state_1 = require("../../../lib-common/awaitable-state");
25
25
  function toStorageConnectionStatus(status, params) {
26
26
  return (0, ws_ipc_1.addToStatus)(status, {
27
27
  service: 'storage',
@@ -41,23 +41,21 @@ class RemoteEvents {
41
41
  this.logError = logError;
42
42
  this.connectionEvents = new rxjs_1.Subject();
43
43
  this.connectionEvent$ = this.connectionEvents.asObservable().pipe((0, operators_1.share)());
44
- this.deferredConnection = (0, deferred_1.defer)();
44
+ this.connection = new awaitable_state_1.AwaitableState();
45
45
  this.wsProc = new ws_ipc_1.WebSocketListening(SERVER_EVENTS_RESTART_WAIT_SECS, this.makeProc.bind(this));
46
46
  Object.seal(this);
47
47
  }
48
48
  makeProc() {
49
49
  return (0, rxjs_1.from)(this.remoteStorage.openEventSource().then(({ client, heartbeat }) => {
50
- this.signalConnected();
50
+ this.connection.setState();
51
51
  heartbeat.subscribe({
52
52
  next: ev => {
53
53
  this.connectionEvents.next(toStorageConnectionStatus(ev));
54
54
  if (ev.type === 'heartbeat') {
55
- this.signalConnected();
55
+ this.connection.setState();
56
56
  }
57
57
  else if ((ev.type === 'heartbeat-skip') || (ev.type === 'disconnected')) {
58
- if (!this.deferredConnection) {
59
- this.deferredConnection = (0, deferred_1.defer)();
60
- }
58
+ this.connection.clearState();
61
59
  }
62
60
  }
63
61
  });
@@ -79,14 +77,7 @@ class RemoteEvents {
79
77
  this.wsProc.close();
80
78
  }
81
79
  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
- }
80
+ return this.connection.whenStateIsSet();
90
81
  }
91
82
  absorbObjChange(client) {
92
83
  return (new rxjs_1.Observable(obs => client.subscribe(owner_1.events.objChanged.EVENT_NAME, obs)))
@@ -40,12 +40,12 @@ class SyncedStore {
40
40
  this.nodes = new common_1.NodesContainer();
41
41
  this.events = new utils_for_observables_1.Broadcast();
42
42
  this.remoteEvents = new remote_events_1.RemoteEvents(this.remoteStorage, this.files, this.broadcastNodeEvent.bind(this), this.logError);
43
- this.uploader = new upsyncer_1.UpSyncer(this.remoteStorage, this.logError);
43
+ this.uploader = new upsyncer_1.UpSyncer(this.remoteStorage, this.whenConnected.bind(this), this.logError);
44
44
  Object.seal(this);
45
45
  }
46
46
  static async makeAndStart(path, user, getSigner, getStorages, cryptor, remoteServiceUrl, net, logError) {
47
47
  const remote = storage_owner_1.StorageOwner.make(user, getSigner, remoteServiceUrl, net);
48
- const objFiles = await obj_files_1.ObjFiles.makeFor(path, remote, logError);
48
+ const objFiles = await obj_files_1.ObjFiles.makeFor(path, remote, () => s.whenConnected(), logError);
49
49
  const s = new SyncedStore(objFiles, remote, getStorages, cryptor, logError);
50
50
  s.uploader.start();
51
51
  return {
@@ -57,7 +57,7 @@ class SyncedStore {
57
57
  }
58
58
  static async makeAndStartWithoutRemote(path, user, getStorages, cryptor, remoteServiceUrl, net, logError) {
59
59
  const { remote, setMid } = storage_owner_1.StorageOwner.makeBeforeMidSetup(user, remoteServiceUrl, net);
60
- const objFiles = await obj_files_1.ObjFiles.makeFor(path, remote, logError);
60
+ const objFiles = await obj_files_1.ObjFiles.makeFor(path, remote, () => s.whenConnected(), logError);
61
61
  const s = new SyncedStore(objFiles, remote, getStorages, cryptor, logError);
62
62
  return {
63
63
  syncedStore: (0, common_1.wrapSyncStorageImplementation)(s),
@@ -8,19 +8,15 @@ import { UploadEventSink, UploadHeaderChange } from "../../../lib-client/xsp-fs/
8
8
  export type FileWriteTapOperator = MonoTypeOperatorFunction<FileWrite[]>;
9
9
  export declare class UpSyncer {
10
10
  private readonly remoteStorage;
11
+ private readonly whenConnected;
11
12
  private readonly logError;
12
13
  private readonly execPools;
13
14
  private readonly tasksByObjIds;
14
15
  private readonly tasksByIds;
15
16
  private readonly syncedUploadStarts;
16
- constructor(remoteStorage: StorageOwner, logError: LogError);
17
+ constructor(remoteStorage: StorageOwner, whenConnected: () => Promise<void>, logError: LogError);
17
18
  start(): void;
18
19
  stop(): Promise<void>;
19
- /**
20
- * Creates an rxjs operator to tap saving process, starting upload while
21
- * writing is ongoing.
22
- */
23
- tapFileWrite(obj: SyncedObj, isNew: boolean, newVersion: number, baseVersion?: number): FileWriteTapOperator;
24
20
  removeCurrentVersionOf(obj: SyncedObj): Promise<void>;
25
21
  startUploadFromDisk(obj: SyncedObj, localVersion: number, uploadVersion: number, uploadHeader: UploadHeaderChange | undefined, createOnRemote: boolean, eventSink: UploadEventSink | undefined): Promise<{
26
22
  completion: Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2020, 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -27,8 +27,9 @@ const synced_1 = require("../../../lib-common/processes/synced");
27
27
  const MAX_CHUNK_SIZE = 512 * 1024;
28
28
  const MAX_FAST_UPLOAD = 2 * 1024 * 1024;
29
29
  class UpSyncer {
30
- constructor(remoteStorage, logError) {
30
+ constructor(remoteStorage, whenConnected, logError) {
31
31
  this.remoteStorage = remoteStorage;
32
+ this.whenConnected = whenConnected;
32
33
  this.logError = logError;
33
34
  this.tasksByObjIds = new Map();
34
35
  this.tasksByIds = new Map();
@@ -45,25 +46,19 @@ class UpSyncer {
45
46
  async stop() {
46
47
  await this.execPools.stop(); // implicitly cancels all upsync tasks
47
48
  }
48
- /**
49
- * Creates an rxjs operator to tap saving process, starting upload while
50
- * writing is ongoing.
51
- */
52
- tapFileWrite(obj, isNew, newVersion, baseVersion) {
53
- throw new Error('UpSyncer.tapFileWrite() not implemented');
54
- // const objUploads = this.getOrMakeUploadsFor(obj);
55
- // return objUploads.tapFileWrite(isNew, newVersion, baseVersion);
56
- }
57
49
  async removeCurrentVersionOf(obj) {
58
50
  try {
59
51
  await this.remoteStorage.deleteObj(obj.objId);
60
52
  }
61
53
  catch (exc) {
62
- // XXX
63
- // - we need to distinguish errors and put this work somewhere
64
- // to run when we go online, for example.
65
- await this.logError(exc, `Uploading of obj removal failed.`);
66
- return;
54
+ if (exc.type === 'connect') {
55
+ await this.whenConnected();
56
+ return this.removeCurrentVersionOf(obj);
57
+ }
58
+ else {
59
+ await this.logError(exc, `Uploading of obj removal failed.`);
60
+ return;
61
+ }
67
62
  }
68
63
  await obj.recordRemovalUploadAndGC();
69
64
  }
@@ -88,7 +83,7 @@ class UpSyncer {
88
83
  if (uploadHeader) {
89
84
  await obj.saveUploadHeaderFile(uploadHeader);
90
85
  }
91
- const task = await UploadTask.for(obj, localVersion, uploadVersion, uploadHeader === null || uploadHeader === void 0 ? void 0 : uploadHeader.uploadHeader, syncedBase, createOnRemote, uploadTaskId, eventSink, this.remoteStorage, async () => {
86
+ const task = await UploadTask.for(obj, localVersion, uploadVersion, uploadHeader === null || uploadHeader === void 0 ? void 0 : uploadHeader.uploadHeader, syncedBase, createOnRemote, uploadTaskId, eventSink, this.remoteStorage, this.whenConnected, async () => {
92
87
  if (this.tasksByIds.delete(task.taskId)) {
93
88
  this.tasksByObjIds.delete(task.objId);
94
89
  }
@@ -113,9 +108,10 @@ exports.UpSyncer = UpSyncer;
113
108
  Object.freeze(UpSyncer.prototype);
114
109
  Object.freeze(UpSyncer);
115
110
  class UploadTask {
116
- constructor(taskId, remoteStorage, objId, objStatus, src, info, uploadHeader, doAtCompletion, eventSink) {
111
+ constructor(taskId, remoteStorage, whenConnected, objId, objStatus, src, info, uploadHeader, doAtCompletion, eventSink) {
117
112
  this.taskId = taskId;
118
113
  this.remoteStorage = remoteStorage;
114
+ this.whenConnected = whenConnected;
119
115
  this.objId = objId;
120
116
  this.objStatus = objStatus;
121
117
  this.src = src;
@@ -129,7 +125,7 @@ class UploadTask {
129
125
  this.emitUploadEvent('upload-started', { totalBytesToUpload: this.totalBytesToUpload });
130
126
  Object.seal(this);
131
127
  }
132
- static async for(obj, localVersion, uploadVersion, uploadHeader, syncedBase, createObj, taskId, eventSink, remoteStorage, doAtCompletion) {
128
+ static async for(obj, localVersion, uploadVersion, uploadHeader, syncedBase, createObj, taskId, eventSink, remoteStorage, whenConnected, doAtCompletion) {
133
129
  const src = await obj.getObjSrcFromLocalAndSyncedBranch(localVersion);
134
130
  let needUpload;
135
131
  if (syncedBase) {
@@ -148,7 +144,7 @@ class UploadTask {
148
144
  };
149
145
  const objStatus = obj.statusObj();
150
146
  await objStatus.recordUploadStart(info);
151
- return new UploadTask(taskId, remoteStorage, obj.objId, objStatus, src, info, uploadHeader, doAtCompletion, eventSink);
147
+ return new UploadTask(taskId, remoteStorage, whenConnected, obj.objId, objStatus, src, info, uploadHeader, doAtCompletion, eventSink);
152
148
  }
153
149
  neededExecutor() {
154
150
  return (!this.info.needUpload ? undefined : this.execLabel);
@@ -213,14 +209,21 @@ class UploadTask {
213
209
  }
214
210
  }
215
211
  catch (exc) {
216
- this.info.needUpload = undefined;
217
- this.uploadCompletion.reject((0, exceptions_1.makeFSSyncException)(`obj-upload`, {
218
- message: `Fail to upload local version ${this.info.uploadVersion}`,
219
- localVersion: this.info.uploadVersion,
220
- cause: exc
221
- }));
222
- await this.objStatus.recordUploadCancellation(this.info);
223
- return true;
212
+ if (exc.type === 'connect') {
213
+ this.emitUploadEvent('upload-disconnected', {});
214
+ await this.whenConnected();
215
+ return false;
216
+ }
217
+ else {
218
+ this.info.needUpload = undefined;
219
+ this.uploadCompletion.reject((0, exceptions_1.makeFSSyncException)(`obj-upload`, {
220
+ message: `Fail to upload local version ${this.info.uploadVersion}`,
221
+ localVersion: this.info.uploadVersion,
222
+ cause: exc
223
+ }));
224
+ await this.objStatus.recordUploadCancellation(this.info);
225
+ return true;
226
+ }
224
227
  }
225
228
  }
226
229
  async startOrderedUpload(upload) {