core-3nweb-client-lib 0.49.7 → 0.50.0
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/asmail.d.ts +1 -0
- package/build/api-defs/files.d.ts +1 -1
- package/build/core/asmail/delivery/per-recipient-wip.js +4 -0
- package/build/core/asmail/inbox/index.d.ts +8 -31
- package/build/core/asmail/inbox/index.js +119 -164
- package/build/core/asmail/inbox/msg-indexing/index.js +8 -9
- package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
- package/build/core/asmail/inbox/msg-on-disk.js +1 -22
- package/build/core/asmail/index.js +4 -4
- package/build/core/asmail/sending-params/index.d.ts +13 -14
- package/build/core/asmail/sending-params/index.js +15 -35
- package/build/core/asmail/sending-params/invitations-anon.d.ts +7 -31
- package/build/core/asmail/sending-params/invitations-anon.js +77 -127
- package/build/core/asmail/sending-params/own-params.d.ts +5 -25
- package/build/core/asmail/sending-params/own-params.js +144 -97
- package/build/core/asmail/sending-params/params-from-others.d.ts +6 -16
- package/build/core/asmail/sending-params/params-from-others.js +88 -58
- package/build/core/index.d.ts +2 -1
- package/build/core/index.js +4 -4
- package/build/core/keyring/index.js +2 -3
- package/build/core/keyring/introductory-keys/published-intro-key.d.ts +2 -2
- package/build/core/keyring/introductory-keys/published-intro-key.js +71 -71
- package/build/core/keyring/peer-keys/index.js +6 -7
- package/build/core/storage/index.d.ts +2 -1
- package/build/core/storage/index.js +4 -3
- package/build/core/storage/system-folders/index.d.ts +1 -2
- package/build/core/storage/system-folders/index.js +18 -22
- package/build/lib-client/asmail/recipient.d.ts +1 -0
- package/build/lib-client/asmail/recipient.js +11 -0
- package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
- package/build/lib-client/fs-utils/fs-sync-utils.js +21 -15
- package/build/lib-client/xsp-fs/folder-node.js +13 -3
- package/build/lib-client/xsp-fs/util/file-based-json.js +1 -1
- package/build/lib-common/dataset-sync/per-device-json-array-logs.js +43 -28
- package/build/lib-common/dataset-sync/single-file.d.ts +12 -0
- package/build/lib-common/dataset-sync/single-file.js +73 -0
- package/build/lib-common/dataset-sync/utils.d.ts +16 -0
- package/build/lib-common/dataset-sync/utils.js +72 -0
- package/build/lib-common-on-node/device-fs-places.d.ts +3 -0
- package/build/{lib-client/xsp-fs/util/for-arrays.js → lib-common-on-node/device-fs-places.js} +21 -13
- package/build/tests/computer.3nweb.core/keyrings.js +2 -2
- package/build/tests/libs-for-tests/core-runner.js +2 -1
- package/package.json +1 -1
- package/build/lib-client/xsp-fs/util/for-arrays.d.ts +0 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.MailRecipient = void 0;
|
|
20
20
|
exports.makeMsgNotFoundException = makeMsgNotFoundException;
|
|
21
|
+
exports.makeFailToDecryptMsgException = makeFailToDecryptMsgException;
|
|
21
22
|
exports.makeObjNotFoundException = makeObjNotFoundException;
|
|
22
23
|
exports.makeMsgIsBrokenException = makeMsgIsBrokenException;
|
|
23
24
|
const request_utils_1 = require("../request-utils");
|
|
@@ -37,6 +38,16 @@ function makeMsgNotFoundException(msgId) {
|
|
|
37
38
|
};
|
|
38
39
|
return exc;
|
|
39
40
|
}
|
|
41
|
+
function makeFailToDecryptMsgException(msgId) {
|
|
42
|
+
const exc = {
|
|
43
|
+
runtimeException: true,
|
|
44
|
+
type: 'inbox',
|
|
45
|
+
msgId,
|
|
46
|
+
failToDecrypt: true,
|
|
47
|
+
stack: (0, runtime_1.getStackHere)(1)
|
|
48
|
+
};
|
|
49
|
+
return exc;
|
|
50
|
+
}
|
|
40
51
|
function makeObjNotFoundException(msgId, objId) {
|
|
41
52
|
const exc = {
|
|
42
53
|
runtimeException: true,
|
|
@@ -5,7 +5,7 @@ type RemoteEvent = web3n.files.RemoteEvent;
|
|
|
5
5
|
type FileEvent = web3n.files.FileEvent;
|
|
6
6
|
type FolderEvent = web3n.files.FolderEvent;
|
|
7
7
|
export declare function getRemoteFolderChanges(fs: WritableFS): Promise<void>;
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function getOrMakeDirOnInit(syncedFS: WritableFS, dir: string): Promise<WritableFS>;
|
|
9
9
|
export declare function uploadFolderChangesIfAny(fs: WritableFS): Promise<void>;
|
|
10
10
|
export declare function observableFromTreeEvents(fs: ReadonlyFS, rootPath: string): Observable<RemoteEvent | FileEvent | FolderEvent>;
|
|
11
11
|
export {};
|
|
@@ -17,12 +17,11 @@
|
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.getRemoteFolderChanges = getRemoteFolderChanges;
|
|
20
|
-
exports.
|
|
20
|
+
exports.getOrMakeDirOnInit = getOrMakeDirOnInit;
|
|
21
21
|
exports.uploadFolderChangesIfAny = uploadFolderChangesIfAny;
|
|
22
22
|
exports.observableFromTreeEvents = observableFromTreeEvents;
|
|
23
23
|
const rxjs_1 = require("rxjs");
|
|
24
24
|
const exceptions_1 = require("../xsp-fs/exceptions");
|
|
25
|
-
const assert_1 = require("../../lib-common/assert");
|
|
26
25
|
async function getRemoteFolderChanges(fs) {
|
|
27
26
|
let { state } = await fs.v.sync.status('');
|
|
28
27
|
if (state === 'behind') {
|
|
@@ -36,13 +35,16 @@ async function getRemoteFolderChanges(fs) {
|
|
|
36
35
|
});
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
|
-
async function
|
|
38
|
+
async function getOrMakeDirOnInit(syncedFS, dir) {
|
|
40
39
|
// Except for initial time, all folders exist and synced into parent, so, we shortcut sync check, if dir exists.
|
|
41
40
|
// This allows to start while offline, when initialization has been done.
|
|
42
41
|
if (await syncedFS.checkFolderPresence(dir)) {
|
|
43
42
|
return await syncedFS.writableSubRoot(dir, { create: false });
|
|
44
43
|
}
|
|
45
|
-
await
|
|
44
|
+
await attemptToBringToSyncedState(syncedFS)
|
|
45
|
+
.catch(exc => {
|
|
46
|
+
console.error(`\n ------`, dir, exc);
|
|
47
|
+
});
|
|
46
48
|
if (await syncedFS.checkFolderPresence(dir)) {
|
|
47
49
|
return await syncedFS.writableSubRoot(dir, { create: false });
|
|
48
50
|
}
|
|
@@ -58,15 +60,10 @@ async function getOrMakeSyncedDirOnInit(syncedFS, dir) {
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
function noop() { }
|
|
61
|
-
async function
|
|
63
|
+
async function attemptToBringToSyncedState(syncedFS) {
|
|
62
64
|
const { existsInSyncedParent, state, remote } = await syncedFS.v.sync.status('');
|
|
63
|
-
|
|
64
|
-
if (state === 'synced') {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
else if (state === 'behind') {
|
|
65
|
+
if (state === 'behind') {
|
|
68
66
|
await syncedFS.v.sync.adoptRemote('.');
|
|
69
|
-
return;
|
|
70
67
|
}
|
|
71
68
|
else if (state === 'unsynced') {
|
|
72
69
|
await syncedFS.v.sync.upload('.').catch(async (exc) => {
|
|
@@ -77,11 +74,20 @@ async function ensureIsInSyncedState(syncedFS) {
|
|
|
77
74
|
await recursivelyUploadNewTreeItems(syncedFS, '.');
|
|
78
75
|
}
|
|
79
76
|
});
|
|
80
|
-
return;
|
|
81
77
|
}
|
|
82
|
-
|
|
78
|
+
else if (state === 'conflicting') {
|
|
79
|
+
await syncedFS.v.sync.absorbRemoteFolderChanges('.');
|
|
80
|
+
await syncedFS.v.sync.upload('.', { uploadVersion: remote.latest }).catch(async (exc) => {
|
|
81
|
+
if (exc.type !== 'fs-sync') {
|
|
82
|
+
throw exc;
|
|
83
|
+
}
|
|
84
|
+
else if (exc.childNeverUploaded) {
|
|
85
|
+
await recursivelyUploadNewTreeItems(syncedFS, '.', remote.latest);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
|
-
async function recursivelyUploadNewTreeItems(fs, path) {
|
|
90
|
+
async function recursivelyUploadNewTreeItems(fs, path, uploadVersion) {
|
|
85
91
|
for (const item of await fs.listFolder(path)) {
|
|
86
92
|
const { remote, synced } = await fs.v.sync.status(`${path}/${item.name}`);
|
|
87
93
|
if (remote || synced) {
|
|
@@ -92,7 +98,7 @@ async function recursivelyUploadNewTreeItems(fs, path) {
|
|
|
92
98
|
}
|
|
93
99
|
await fs.v.sync.upload(`${path}/${item.name}`);
|
|
94
100
|
}
|
|
95
|
-
await fs.v.sync.upload(path);
|
|
101
|
+
await fs.v.sync.upload(path, { uploadVersion });
|
|
96
102
|
}
|
|
97
103
|
// export async function getOrMakeAndUploadFolderIn(fs: WritableFS, folder: string): Promise<WritableFS> {
|
|
98
104
|
// try {
|
|
@@ -36,7 +36,6 @@ const folder_node_serialization_1 = require("./folder-node-serialization");
|
|
|
36
36
|
const attrs_1 = require("./attrs");
|
|
37
37
|
const node_persistence_1 = require("./node-persistence");
|
|
38
38
|
const assert_1 = require("../../lib-common/assert");
|
|
39
|
-
const for_arrays_1 = require("./util/for-arrays");
|
|
40
39
|
function jsonToInfoAndAttrs(json) {
|
|
41
40
|
const folderInfo = {
|
|
42
41
|
nodes: (0, json_utils_1.copy)(json.nodes)
|
|
@@ -870,7 +869,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
870
869
|
async uploadRemovalOf(removedNodes) {
|
|
871
870
|
const rmObjs = [];
|
|
872
871
|
for (const node of removedNodes) {
|
|
873
|
-
|
|
872
|
+
appendArray(rmObjs, await this.listRemovedInTreeToUploadRm(node));
|
|
874
873
|
}
|
|
875
874
|
await this.uploadRemovalOfObjs(rmObjs);
|
|
876
875
|
}
|
|
@@ -896,7 +895,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
896
895
|
const src = await storage.getObjSrc(node.objId, versionBeforeRm, true);
|
|
897
896
|
const folderContent = await FolderPersistance.readFolderContent(node.objId, node.key, src, storage.cryptor, storage.random);
|
|
898
897
|
for (const child of Object.values(folderContent.nodes)) {
|
|
899
|
-
|
|
898
|
+
appendArray(objs, await this.listRemovedInTreeToUploadRm(child));
|
|
900
899
|
}
|
|
901
900
|
return objs;
|
|
902
901
|
}
|
|
@@ -1136,6 +1135,17 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
1136
1135
|
exports.FolderNode = FolderNode;
|
|
1137
1136
|
Object.freeze(FolderNode.prototype);
|
|
1138
1137
|
Object.freeze(FolderNode);
|
|
1138
|
+
function appendArray(arr, elems) {
|
|
1139
|
+
if (elems !== undefined) {
|
|
1140
|
+
if (Array.isArray(elems)) {
|
|
1141
|
+
arr.push(...elems);
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
arr.push(elems);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return arr;
|
|
1148
|
+
}
|
|
1139
1149
|
const EMPTY_ARR = new Uint8Array(0);
|
|
1140
1150
|
function noop() { }
|
|
1141
1151
|
function checkFolderInfo(folderJson) {
|
|
@@ -20,7 +20,7 @@ exports.JsonFileProc = void 0;
|
|
|
20
20
|
const rxjs_1 = require("rxjs");
|
|
21
21
|
const operators_1 = require("rxjs/operators");
|
|
22
22
|
const synced_1 = require("../../../lib-common/processes/synced");
|
|
23
|
-
// XXX File proc can have general flow for sync
|
|
23
|
+
// XXX File proc can have general flow for sync, missing now!
|
|
24
24
|
class JsonFileProc {
|
|
25
25
|
constructor(onFileEvent, order = new synced_1.SingleProc()) {
|
|
26
26
|
this.onFileEvent = onFileEvent;
|
|
@@ -21,7 +21,6 @@ exports.startDatasetSync = startDatasetSync;
|
|
|
21
21
|
const synced_1 = require("../processes/synced");
|
|
22
22
|
const buffer_utils_1 = require("../buffer-utils");
|
|
23
23
|
const rng_def_1 = require("../rng-def");
|
|
24
|
-
const sink_utils_1 = require("../obj-streaming/sink-utils");
|
|
25
24
|
const DEVICE_ID_FILE = 'device-id';
|
|
26
25
|
const DEV_LOGS_PROCESSING_FILE = 'dev-logs-processing';
|
|
27
26
|
const DEV_ID_LEN = 24;
|
|
@@ -122,11 +121,11 @@ async function makeRecentChangesLogs(logsFS, localLogsFS, areOpsEqual, logError)
|
|
|
122
121
|
return;
|
|
123
122
|
}
|
|
124
123
|
try {
|
|
125
|
-
const content = (await logsFS.
|
|
124
|
+
const content = (await logsFS.readTxtFile(logFile));
|
|
126
125
|
if (!content) {
|
|
127
126
|
return;
|
|
128
127
|
}
|
|
129
|
-
const appendedEvents = deserializeLogs(
|
|
128
|
+
const appendedEvents = deserializeLogs(content);
|
|
130
129
|
processFromLogFile(devId, appendedEvents, sink);
|
|
131
130
|
}
|
|
132
131
|
catch (err) {
|
|
@@ -211,28 +210,37 @@ async function makeRecentChangesLogs(logsFS, localLogsFS, areOpsEqual, logError)
|
|
|
211
210
|
async function saveAndUploadIfNeeded() {
|
|
212
211
|
if (needToSave && (logs.length > numOfEntriesOnLastSave)) {
|
|
213
212
|
needToSave = false;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
213
|
+
// XXX do more efficient appending
|
|
214
|
+
// sink.slice is not doing efficient appending job, and when versions go into 1000's it blows things up.
|
|
215
|
+
// When appending can be done properly, we can switch to a more efficient
|
|
216
|
+
//
|
|
217
|
+
// if (numOfEntriesOnLastSave > 0) {
|
|
218
|
+
// const logsToAppend = logs.slice(numOfEntriesOnLastSave);
|
|
219
|
+
// const logsTxt = serializeLogs(logsToAppend);
|
|
220
|
+
// const sink = await thisDevLogsFile.getByteSink(false);
|
|
221
|
+
// try {
|
|
222
|
+
// await appendFiniteSink(sink, utf8.pack(','), false);
|
|
223
|
+
// await appendFiniteSink(sink, utf8.pack(logsTxt), true);
|
|
224
|
+
// } catch (err) {
|
|
225
|
+
// await sink.done(err);
|
|
226
|
+
// }
|
|
227
|
+
// numOfEntriesOnLastSave += logsToAppend.length;
|
|
228
|
+
// } else {
|
|
229
|
+
// const logsTxt = serializeLogs(logs);
|
|
230
|
+
// const numOfLogs = logs.length
|
|
231
|
+
// await thisDevLogsFile.writeTxt(logsTxt)
|
|
232
|
+
// .catch(_exc => {
|
|
233
|
+
// needToSave = true;
|
|
234
|
+
// });
|
|
235
|
+
// numOfEntriesOnLastSave = numOfLogs;
|
|
236
|
+
// }
|
|
237
|
+
const logsTxt = serializeLogs(logs);
|
|
238
|
+
const numOfLogs = logs.length;
|
|
239
|
+
await thisDevLogsFile.writeTxt(logsTxt)
|
|
240
|
+
.catch(_exc => {
|
|
241
|
+
needToSave = true;
|
|
242
|
+
});
|
|
243
|
+
numOfEntriesOnLastSave = numOfLogs;
|
|
236
244
|
needToUpload = true;
|
|
237
245
|
}
|
|
238
246
|
if (needToUpload) {
|
|
@@ -298,7 +306,13 @@ async function makeRecentChangesLogs(logsFS, localLogsFS, areOpsEqual, logError)
|
|
|
298
306
|
await logsFS.v.sync.upload('');
|
|
299
307
|
}
|
|
300
308
|
else if (state === 'unsynced') {
|
|
301
|
-
await logsFS.v.sync.upload('')
|
|
309
|
+
await logsFS.v.sync.upload('')
|
|
310
|
+
.catch(async (exc) => {
|
|
311
|
+
if (exc.childNeverUploaded) {
|
|
312
|
+
await localLogsFS.deleteFile(exc.childName);
|
|
313
|
+
return syncLogsFS();
|
|
314
|
+
}
|
|
315
|
+
});
|
|
302
316
|
}
|
|
303
317
|
}
|
|
304
318
|
return {
|
|
@@ -403,15 +417,16 @@ function startDatasetSync(periodicCheckAndUploadOfData, millisBeforeNextRun, log
|
|
|
403
417
|
// We pass this single process point out to wrap fn's with it, in an attempt to explicitly keeping it at the
|
|
404
418
|
// boundary of incoming operations, either from fn calls, or from handling changes passed via fs events.
|
|
405
419
|
const changeProc = new synced_1.SingleProc();
|
|
406
|
-
function
|
|
420
|
+
function datasetCheckAndSyncAction() {
|
|
407
421
|
changeProc.startOrChain(periodicCheckAndUploadOfData);
|
|
408
422
|
}
|
|
409
423
|
let interval = undefined;
|
|
410
424
|
function resetSyncInterval() {
|
|
425
|
+
var _a, _b;
|
|
411
426
|
if (interval !== undefined) {
|
|
412
427
|
clearInterval(interval);
|
|
413
428
|
}
|
|
414
|
-
interval = setInterval(
|
|
429
|
+
interval = (_b = (_a = setInterval(datasetCheckAndSyncAction, millisBeforeNextRun())).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
415
430
|
}
|
|
416
431
|
resetSyncInterval();
|
|
417
432
|
// XXX we should watch for reconnect events and run similar to start checks of possibly missed ops
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SingleProc } from "../processes/synced";
|
|
2
|
+
type WritableFS = web3n.files.WritableFS;
|
|
3
|
+
type WritableFile = web3n.files.WritableFile;
|
|
4
|
+
export declare function initSyncedFile(syncedFS: WritableFS, fName: string, initNewFile: (newFile: WritableFile) => Promise<void>): Promise<{
|
|
5
|
+
file: WritableFile;
|
|
6
|
+
changeProc: SingleProc;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function watchAndApplyChangesFromOtherDevices(file: WritableFile, changeProc: SingleProc, onChangeFromOtherDevices: (fileVersion: number, changeProc: SingleProc, file: WritableFile) => Promise<void>, onConflictingChangeFromOtherDevices: (changeProc: SingleProc, file: WritableFile) => Promise<void>): {
|
|
9
|
+
stopFileWatching: () => void;
|
|
10
|
+
triggerUpload: () => void;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2026 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.initSyncedFile = initSyncedFile;
|
|
20
|
+
exports.watchAndApplyChangesFromOtherDevices = watchAndApplyChangesFromOtherDevices;
|
|
21
|
+
const synced_1 = require("../processes/synced");
|
|
22
|
+
const utils_1 = require("./utils");
|
|
23
|
+
async function initSyncedFile(syncedFS, fName, initNewFile) {
|
|
24
|
+
const changeProc = new synced_1.SingleProc();
|
|
25
|
+
const file = await syncedFS.writableFile(fName);
|
|
26
|
+
if (file.isNew) {
|
|
27
|
+
await initNewFile(file);
|
|
28
|
+
}
|
|
29
|
+
await (0, utils_1.runRetryingButNotBlockingOnDisconnect)(async function ensureFileIsInFSTree() {
|
|
30
|
+
const { existsInSyncedParent, synced } = await file.v.sync.status(false);
|
|
31
|
+
if (!synced) {
|
|
32
|
+
await file.v.sync.upload();
|
|
33
|
+
}
|
|
34
|
+
if (!existsInSyncedParent) {
|
|
35
|
+
await syncedFS.v.sync.upload('.');
|
|
36
|
+
}
|
|
37
|
+
}, () => file.v.sync.whenConnected(), changeProc);
|
|
38
|
+
return { file, changeProc };
|
|
39
|
+
}
|
|
40
|
+
function watchAndApplyChangesFromOtherDevices(file, changeProc, onChangeFromOtherDevices, onConflictingChangeFromOtherDevices) {
|
|
41
|
+
function whenConnected() {
|
|
42
|
+
return file.v.sync.whenConnected();
|
|
43
|
+
}
|
|
44
|
+
const stopFileWatching = file.watch({
|
|
45
|
+
next: ev => {
|
|
46
|
+
if (ev.type === 'remote-change') {
|
|
47
|
+
(0, utils_1.runRetryingButNotBlockingOnDisconnect)(() => checkStatusAndSyncFile(true), whenConnected, changeProc);
|
|
48
|
+
}
|
|
49
|
+
else if ((ev.type === 'file-change') && (ev.src === 'sync')) {
|
|
50
|
+
onChangeFromOtherDevices(ev.newVersion, changeProc, file);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
function triggerUpload() {
|
|
55
|
+
(0, utils_1.runRetryingButNotBlockingOnDisconnect)(() => file.v.sync.upload(), whenConnected, changeProc);
|
|
56
|
+
}
|
|
57
|
+
async function checkStatusAndSyncFile(skipServerCheck) {
|
|
58
|
+
const { state } = await file.v.sync.status(skipServerCheck);
|
|
59
|
+
if (state === 'behind') {
|
|
60
|
+
await file.v.sync.adoptRemote();
|
|
61
|
+
}
|
|
62
|
+
else if (state === 'conflicting') {
|
|
63
|
+
onConflictingChangeFromOtherDevices(changeProc, file);
|
|
64
|
+
}
|
|
65
|
+
else if (state === 'unsynced') {
|
|
66
|
+
triggerUpload();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// starting initial check with changeProc, without waiting here
|
|
70
|
+
(0, utils_1.runRetryingButNotBlockingOnDisconnect)(checkStatusAndSyncFile, whenConnected);
|
|
71
|
+
return { stopFileWatching, triggerUpload };
|
|
72
|
+
}
|
|
73
|
+
Object.freeze(exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SingleProc } from "../processes/synced";
|
|
2
|
+
/**
|
|
3
|
+
* Returned promise resolves when action runs first time, successfully or not.
|
|
4
|
+
* On disconnect action will be triggered again but outside of return promise.
|
|
5
|
+
* @param action
|
|
6
|
+
* @param whenConnected
|
|
7
|
+
* @param changeProc when given, is used to chain action invocation
|
|
8
|
+
*/
|
|
9
|
+
export declare function runRetryingButNotBlockingOnDisconnect(action: () => Promise<any>, whenConnected: () => Promise<void>, changeProc?: SingleProc): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Returned promise resolves when action ultimately runs or fails, after all possible disconnects.
|
|
12
|
+
* @param action
|
|
13
|
+
* @param whenConnected
|
|
14
|
+
* @param changeProc when given, is used to chain action invocation
|
|
15
|
+
*/
|
|
16
|
+
export declare function runRetryingOnDisconnectTillDone<T>(action: () => Promise<T>, whenConnected: () => Promise<void>, changeProc?: SingleProc): Promise<T>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2026 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.runRetryingButNotBlockingOnDisconnect = runRetryingButNotBlockingOnDisconnect;
|
|
20
|
+
exports.runRetryingOnDisconnectTillDone = runRetryingOnDisconnectTillDone;
|
|
21
|
+
/**
|
|
22
|
+
* Returned promise resolves when action runs first time, successfully or not.
|
|
23
|
+
* On disconnect action will be triggered again but outside of return promise.
|
|
24
|
+
* @param action
|
|
25
|
+
* @param whenConnected
|
|
26
|
+
* @param changeProc when given, is used to chain action invocation
|
|
27
|
+
*/
|
|
28
|
+
async function runRetryingButNotBlockingOnDisconnect(action, whenConnected, changeProc) {
|
|
29
|
+
if (changeProc) {
|
|
30
|
+
await changeProc.startOrChain(action).catch((exc) => {
|
|
31
|
+
if (exc.type === 'connect') {
|
|
32
|
+
whenConnected().then(() => runRetryingButNotBlockingOnDisconnect(() => changeProc.startOrChain(action), whenConnected, changeProc));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
await action().catch((exc) => {
|
|
38
|
+
if (exc.type === 'connect') {
|
|
39
|
+
whenConnected().then(() => runRetryingButNotBlockingOnDisconnect(action, whenConnected));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returned promise resolves when action ultimately runs or fails, after all possible disconnects.
|
|
46
|
+
* @param action
|
|
47
|
+
* @param whenConnected
|
|
48
|
+
* @param changeProc when given, is used to chain action invocation
|
|
49
|
+
*/
|
|
50
|
+
async function runRetryingOnDisconnectTillDone(action, whenConnected, changeProc) {
|
|
51
|
+
if (changeProc) {
|
|
52
|
+
return await changeProc.startOrChain(action).catch((exc) => {
|
|
53
|
+
if (exc.type === 'connect') {
|
|
54
|
+
return whenConnected().then(() => runRetryingOnDisconnectTillDone(() => changeProc.startOrChain(action), whenConnected, changeProc));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw exc;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return await action().catch((exc) => {
|
|
63
|
+
if (exc.type === 'connect') {
|
|
64
|
+
return whenConnected().then(() => runRetryingOnDisconnectTillDone(action, whenConnected));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw exc;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
Object.freeze(exports);
|
package/build/{lib-client/xsp-fs/util/for-arrays.js → lib-common-on-node/device-fs-places.js}
RENAMED
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
3
|
Copyright (C) 2022 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
|
|
7
7
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
8
|
version.
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
This program is distributed in the hope that it will be useful, but
|
|
11
11
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
12
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
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
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
exports.sysFilesOnDevice = sysFilesOnDevice;
|
|
20
|
+
const fs_collection_1 = require("../lib-client/fs-utils/fs-collection");
|
|
21
|
+
const lib_index_1 = require("../lib-index");
|
|
22
|
+
async function sysFilesOnDevice() {
|
|
23
|
+
const c = (0, fs_collection_1.makeFSCollection)();
|
|
24
|
+
if (process.platform === 'win32') {
|
|
25
|
+
const sysDrive = process.env.SystemDrive;
|
|
26
|
+
await c.set(sysDrive, {
|
|
27
|
+
isFolder: true,
|
|
28
|
+
item: await lib_index_1.DeviceFS.makeWritable(sysDrive)
|
|
29
|
+
});
|
|
28
30
|
}
|
|
29
|
-
|
|
31
|
+
else {
|
|
32
|
+
await c.set('', {
|
|
33
|
+
isFolder: true,
|
|
34
|
+
item: await lib_index_1.DeviceFS.makeWritable('/')
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return { isCollection: true, item: c };
|
|
30
38
|
}
|
|
31
39
|
Object.freeze(exports);
|
|
@@ -58,9 +58,9 @@ describe('ASMail keyring', () => {
|
|
|
58
58
|
// run different peers' keyrings from different folders
|
|
59
59
|
const peer1 = `keyring tester 1 @rock.cafe`;
|
|
60
60
|
const peer2 = `keyring tester 2 @rock.cafe`;
|
|
61
|
-
const p1FS = await (0, fs_sync_utils_1.
|
|
61
|
+
const p1FS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(dev1FS, peer1);
|
|
62
62
|
const p1LocalFS = await dev1LocalFS.writableSubRoot(`local fs for ${peer1}`);
|
|
63
|
-
const p2FS = await (0, fs_sync_utils_1.
|
|
63
|
+
const p2FS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(dev1FS, peer2);
|
|
64
64
|
const p2LocalFS = await dev1LocalFS.writableSubRoot(`local fs for ${peer2}`);
|
|
65
65
|
const peerKeys1 = await (0, peer_keys_1.makePeersKeyring)(p1FS, p1LocalFS, random_node_1.bytes, logger);
|
|
66
66
|
const peerKeys2 = await (0, peer_keys_1.makePeersKeyring)(p2FS, p2LocalFS, random_node_1.bytes, logger);
|
|
@@ -31,6 +31,7 @@ const dns_1 = require("dns");
|
|
|
31
31
|
const napi_nacl_1 = require("napi-nacl");
|
|
32
32
|
const request_from_node_1 = require("../../lib-common-on-node/request-from-node");
|
|
33
33
|
const websocket_from_node_1 = require("../../lib-common-on-node/websocket-from-node");
|
|
34
|
+
const device_fs_places_1 = require("../../lib-common-on-node/device-fs-places");
|
|
34
35
|
exports.testApp = {
|
|
35
36
|
appDomain: 'test.3nweb.app',
|
|
36
37
|
capsRequested: {
|
|
@@ -91,7 +92,7 @@ class CoreRunner {
|
|
|
91
92
|
resolve(texts);
|
|
92
93
|
}
|
|
93
94
|
}))
|
|
94
|
-
}), napi_nacl_1.makeNativeCryptor, random_node_1.bytes);
|
|
95
|
+
}), device_fs_places_1.sysFilesOnDevice, napi_nacl_1.makeNativeCryptor, random_node_1.bytes);
|
|
95
96
|
}
|
|
96
97
|
async close() {
|
|
97
98
|
if (this.appCaps) {
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function appendArray<T>(arr: T[], elems: T[] | T | undefined): T[];
|