core-3nweb-client-lib 0.44.5 → 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.
- package/build/api-defs/files.d.ts +5 -1
- package/build/core/asmail/inbox/inbox-events.d.ts +2 -0
- package/build/core/asmail/inbox/inbox-events.js +15 -2
- package/build/core/asmail/inbox/index.js +4 -3
- package/build/core/asmail/inbox/msg-downloader.d.ts +2 -1
- package/build/core/asmail/inbox/msg-downloader.js +4 -2
- package/build/core/storage/synced/downloader.d.ts +2 -1
- package/build/core/storage/synced/downloader.js +2 -1
- package/build/core/storage/synced/obj-files.d.ts +1 -1
- package/build/core/storage/synced/obj-files.js +5 -5
- package/build/core/storage/synced/remote-events.d.ts +1 -2
- package/build/core/storage/synced/remote-events.js +7 -16
- package/build/core/storage/synced/storage.js +3 -3
- package/build/core/storage/synced/upsyncer.d.ts +2 -6
- package/build/core/storage/synced/upsyncer.js +31 -28
- package/build/lib-client/objs-on-disk/obj-on-disk.d.ts +1 -0
- package/build/lib-client/objs-on-disk/obj-on-disk.js +7 -2
- package/build/lib-client/xsp-fs/fs.js +13 -0
- package/build/lib-common/async-iter.js +2 -1
- package/build/lib-common/awaitable-state.d.ts +7 -0
- package/build/lib-common/awaitable-state.js +43 -0
- package/package.json +1 -1
|
@@ -1201,7 +1201,7 @@ declare namespace web3n.files {
|
|
|
1201
1201
|
syncStatus: SyncStatus;
|
|
1202
1202
|
}
|
|
1203
1203
|
|
|
1204
|
-
type UploadEvent = UploadStartEvent | UploadProgressEvent | UploadDoneEvent;
|
|
1204
|
+
type UploadEvent = UploadStartEvent | UploadProgressEvent | UploadDisconnectedEvent | UploadDoneEvent;
|
|
1205
1205
|
|
|
1206
1206
|
interface UploadEventBase extends FSEvent {
|
|
1207
1207
|
uploadTaskId: number;
|
|
@@ -1220,6 +1220,10 @@ declare namespace web3n.files {
|
|
|
1220
1220
|
bytesLeftToUpload: number;
|
|
1221
1221
|
}
|
|
1222
1222
|
|
|
1223
|
+
interface UploadDisconnectedEvent extends UploadEventBase {
|
|
1224
|
+
type: 'upload-disconnected';
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1223
1227
|
interface UploadDoneEvent extends UploadEventBase {
|
|
1224
1228
|
type: 'upload-done';
|
|
1225
1229
|
}
|
|
@@ -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 -
|
|
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 =>
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
55
|
+
this.connection.setState();
|
|
56
56
|
}
|
|
57
57
|
else if ((ev.type === 'heartbeat-skip') || (ev.type === 'disconnected')) {
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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) {
|
|
@@ -73,6 +73,7 @@ export interface ObjDownloader {
|
|
|
73
73
|
getSegs: (objId: ObjId, version: number, start: number, end: number) => Promise<Uint8Array>;
|
|
74
74
|
splitSegsDownloads: (start: number, end: number) => Section[];
|
|
75
75
|
schedule: (download: Download) => void;
|
|
76
|
+
whenConnected: () => Promise<void>;
|
|
76
77
|
}
|
|
77
78
|
export interface InitDownloadParts {
|
|
78
79
|
layout: Layout;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2018 - 2020, 2022, 2025 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2018 - 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
|
|
@@ -423,7 +423,12 @@ class Download {
|
|
|
423
423
|
this.inBkgrnd.runData.currentDownload.deferred.resolve(bytes);
|
|
424
424
|
}
|
|
425
425
|
catch (exc) {
|
|
426
|
-
|
|
426
|
+
if (exc.type === 'connect') {
|
|
427
|
+
await this.downloader.whenConnected();
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
this.inBkgrnd.runData.currentDownload.deferred.reject(exc);
|
|
431
|
+
}
|
|
427
432
|
}
|
|
428
433
|
finally {
|
|
429
434
|
this.inBkgrnd.runData.currentDownload = undefined;
|
|
@@ -818,6 +818,19 @@ class S {
|
|
|
818
818
|
throw (0, common_1.setPathInExc)(exc, path);
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
|
+
// XXX WIP
|
|
822
|
+
// async compareLocalAndRemoteFileItems(
|
|
823
|
+
// path: string, itemName: string, remoteVersion?: number
|
|
824
|
+
// ): Promise<FileDiff> {
|
|
825
|
+
// const folderNode = await this.getFolderNode(path);
|
|
826
|
+
// const localChild = (await folderNode.getFile(itemName))!;
|
|
827
|
+
// const remoteChild = await folderNode.getRemoteItemNode(itemName, remoteVersion);
|
|
828
|
+
// if (remoteChild.type === 'file') {
|
|
829
|
+
// return await localChild.compareWithRemote(remoteChild);
|
|
830
|
+
// } else {
|
|
831
|
+
// throw makeFileException('notFile', `${path}/${itemName}`);
|
|
832
|
+
// }
|
|
833
|
+
// }
|
|
821
834
|
async adoptRemoteFolderItem(path, itemName, opts) {
|
|
822
835
|
const node = await this.getFolderNode(path);
|
|
823
836
|
try {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
See the GNU General Public License for more details.
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU General Public License along with
|
|
16
|
-
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
17
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
19
|
exports.asyncIteration = asyncIteration;
|
|
19
20
|
exports.asyncFind = asyncFind;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2017 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.AwaitableState = void 0;
|
|
20
|
+
const deferred_1 = require("./processes/deferred");
|
|
21
|
+
class AwaitableState {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.deferredStateSetPoint = (0, deferred_1.defer)();
|
|
24
|
+
Object.seal(this);
|
|
25
|
+
}
|
|
26
|
+
setState() {
|
|
27
|
+
if (this.deferredStateSetPoint) {
|
|
28
|
+
this.deferredStateSetPoint.resolve();
|
|
29
|
+
this.deferredStateSetPoint = undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
clearState() {
|
|
33
|
+
if (!this.deferredStateSetPoint) {
|
|
34
|
+
this.deferredStateSetPoint = (0, deferred_1.defer)();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async whenStateIsSet() {
|
|
38
|
+
var _a;
|
|
39
|
+
return (_a = this.deferredStateSetPoint) === null || _a === void 0 ? void 0 : _a.promise;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.AwaitableState = AwaitableState;
|
|
43
|
+
Object.freeze(exports);
|