core-3nweb-client-lib 0.25.6 → 0.27.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/README.md +2 -2
- package/build/api-defs/asmail.d.ts +1 -1
- package/build/api-defs/files.d.ts +282 -70
- package/build/core/app-files.js +7 -7
- package/build/core/asmail/config/common.js +4 -4
- package/build/core/asmail/config/index.js +2 -2
- package/build/core/asmail/config/published-intro-key.js +1 -1
- package/build/core/asmail/delivery/common.js +7 -7
- package/build/core/asmail/delivery/index.js +7 -6
- package/build/core/asmail/delivery/msg.js +8 -7
- package/build/core/asmail/delivery/per-recipient-wip.js +3 -3
- package/build/core/asmail/inbox/attachments/fs.js +11 -1
- package/build/core/asmail/inbox/cached-msgs.js +3 -3
- package/build/core/asmail/inbox/inbox-events.js +5 -4
- package/build/core/asmail/inbox/index.js +12 -12
- package/build/core/asmail/inbox/msg-downloader.js +3 -3
- package/build/core/asmail/inbox/msg-indexing.js +4 -4
- package/build/core/asmail/inbox/msg-on-disk.js +7 -7
- package/build/core/asmail/index.d.ts +3 -3
- package/build/core/asmail/index.js +13 -8
- package/build/core/asmail/key-verification.js +5 -5
- package/build/core/asmail/keyring/common.js +7 -6
- package/build/core/asmail/keyring/correspondent-keys.js +8 -7
- package/build/core/asmail/keyring/id-to-email-map.js +2 -1
- package/build/core/asmail/keyring/index.d.ts +7 -8
- package/build/core/asmail/keyring/index.js +15 -14
- package/build/core/asmail/keyring/keyring-storage.js +4 -3
- package/build/core/asmail/msg/opener.js +3 -3
- package/build/core/asmail/msg/packer.js +13 -13
- package/build/core/asmail/sending-params/own-params.js +4 -4
- package/build/core/asmail/sending-params/params-from-others.js +3 -3
- package/build/core/id-manager.js +8 -5
- package/build/core/index.d.ts +2 -1
- package/build/core/index.js +14 -14
- package/build/core/sign-in.d.ts +5 -4
- package/build/core/sign-in.js +12 -14
- package/build/core/sign-up.d.ts +1 -0
- package/build/core/sign-up.js +15 -11
- package/build/core/storage/common/json-saving.d.ts +21 -0
- package/build/core/storage/common/json-saving.js +82 -0
- package/build/core/storage/common/obj-info-file.d.ts +51 -0
- package/build/core/storage/common/obj-info-file.js +153 -5
- package/build/core/storage/common/utils.d.ts +2 -0
- package/build/core/storage/common/utils.js +32 -0
- package/build/core/storage/index.d.ts +3 -17
- package/build/core/storage/index.js +57 -77
- package/build/core/storage/local/obj-files-gc.d.ts +2 -0
- package/build/core/storage/local/obj-files-gc.js +53 -39
- package/build/core/storage/local/obj-files.d.ts +6 -9
- package/build/core/storage/local/obj-files.js +16 -19
- package/build/core/storage/local/obj-status.d.ts +20 -30
- package/build/core/storage/local/obj-status.js +46 -113
- package/build/core/storage/local/storage.d.ts +15 -5
- package/build/core/storage/local/storage.js +37 -18
- package/build/core/storage/synced/downloader.js +7 -6
- package/build/core/storage/synced/obj-files-gc.d.ts +6 -1
- package/build/core/storage/synced/obj-files-gc.js +106 -13
- package/build/core/storage/synced/obj-files.d.ts +46 -47
- package/build/core/storage/synced/obj-files.js +207 -154
- package/build/core/storage/synced/obj-status.d.ts +103 -42
- package/build/core/storage/synced/obj-status.js +525 -137
- package/build/core/storage/synced/remote-events.d.ts +11 -12
- package/build/core/storage/synced/remote-events.js +80 -57
- package/build/core/storage/synced/storage.d.ts +24 -5
- package/build/core/storage/synced/storage.js +123 -38
- package/build/core/storage/synced/upload-header-file.d.ts +4 -0
- package/build/core/storage/synced/upload-header-file.js +64 -0
- package/build/core/storage/synced/upsyncer.d.ts +15 -9
- package/build/core/storage/synced/upsyncer.js +219 -246
- package/build/core/storage/system-folders/apps-data.d.ts +16 -0
- package/build/core/storage/system-folders/apps-data.js +110 -0
- package/build/core/storage/system-folders/index.d.ts +18 -0
- package/build/core/storage/system-folders/index.js +77 -0
- package/build/core-ipc/common-caps.js +3 -3
- package/build/core-ipc/generic.js +8 -8
- package/build/core-ipc/startup-caps.js +2 -2
- package/build/cryptors.js +6 -2
- package/build/ipc-via-protobuf/asmail-cap.js +67 -83
- package/build/ipc-via-protobuf/bytes.js +16 -17
- package/build/ipc-via-protobuf/connector-clients-side.d.ts +3 -0
- package/build/ipc-via-protobuf/connector-clients-side.js +62 -25
- package/build/ipc-via-protobuf/connector-services-side.js +10 -10
- package/build/ipc-via-protobuf/connector.js +4 -4
- package/build/ipc-via-protobuf/file.d.ts +48 -12
- package/build/ipc-via-protobuf/file.js +476 -120
- package/build/ipc-via-protobuf/fs.d.ts +8 -0
- package/build/ipc-via-protobuf/fs.js +592 -159
- package/build/ipc-via-protobuf/log-cap.js +2 -2
- package/build/ipc-via-protobuf/mailerid.js +3 -3
- package/build/ipc-via-protobuf/protobuf-msg.d.ts +1 -0
- package/build/ipc-via-protobuf/protobuf-msg.js +11 -7
- package/build/ipc-via-protobuf/startup-cap.js +23 -23
- package/build/ipc-via-protobuf/storage-cap.js +12 -12
- package/build/ipc.js +7 -2
- package/build/lib-client/3nstorage/exceptions.d.ts +12 -8
- package/build/lib-client/3nstorage/exceptions.js +31 -10
- package/build/lib-client/3nstorage/service.d.ts +16 -2
- package/build/lib-client/3nstorage/service.js +109 -39
- package/build/lib-client/3nstorage/util/file-based-json.d.ts +2 -1
- package/build/lib-client/3nstorage/util/file-based-json.js +1 -1
- package/build/lib-client/3nstorage/xsp-fs/attrs.js +17 -17
- package/build/lib-client/3nstorage/xsp-fs/common.d.ts +52 -14
- package/build/lib-client/3nstorage/xsp-fs/common.js +31 -16
- package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +1 -0
- package/build/lib-client/3nstorage/xsp-fs/file-node.js +18 -14
- package/build/lib-client/3nstorage/xsp-fs/file.d.ts +31 -6
- package/build/lib-client/3nstorage/xsp-fs/file.js +74 -23
- package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.js +4 -4
- package/build/lib-client/3nstorage/xsp-fs/folder-node.d.ts +24 -11
- package/build/lib-client/3nstorage/xsp-fs/folder-node.js +599 -189
- package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +45 -9
- package/build/lib-client/3nstorage/xsp-fs/fs.js +326 -74
- package/build/lib-client/3nstorage/xsp-fs/link-node.d.ts +1 -0
- package/build/lib-client/3nstorage/xsp-fs/link-node.js +7 -2
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.d.ts +30 -20
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +239 -106
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +18 -19
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +5 -5
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +56 -56
- package/build/lib-client/3nweb-signup.js +4 -4
- package/build/lib-client/asmail/recipient.js +15 -15
- package/build/lib-client/asmail/sender.js +22 -22
- package/build/lib-client/asmail/service-config.js +3 -3
- package/build/lib-client/cryptor/cryptor-in-worker.js +19 -17
- package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
- package/build/lib-client/cryptor/cryptor.js +4 -2
- package/build/lib-client/cryptor/cryptor.wasm +0 -0
- package/build/lib-client/cryptor/in-proc-js.js +1 -1
- package/build/lib-client/cryptor/in-proc-wasm.js +7 -7
- package/build/lib-client/cryptor/worker-js.js +2 -2
- package/build/lib-client/cryptor/worker-wasm.js +2 -2
- package/build/lib-client/files-select.js +1 -1
- package/build/lib-client/files.d.ts +1 -1
- package/build/lib-client/files.js +71 -4
- package/build/lib-client/fs-collection.js +3 -2
- package/build/lib-client/fs-sync-utils.d.ts +5 -0
- package/build/lib-client/fs-sync-utils.js +61 -0
- package/build/lib-client/fs-view.d.ts +14 -0
- package/build/lib-client/fs-view.js +33 -0
- package/build/lib-client/key-derivation.js +1 -1
- package/build/lib-client/local-files/dev-file-sink.js +9 -9
- package/build/lib-client/local-files/dev-file-src.js +2 -2
- package/build/lib-client/local-files/device-fs.d.ts +1 -1
- package/build/lib-client/local-files/device-fs.js +56 -54
- package/build/lib-client/logging/log-to-file.d.ts +1 -1
- package/build/lib-client/logging/log-to-file.js +11 -11
- package/build/lib-client/mailer-id/login.js +7 -7
- package/build/lib-client/mailer-id/provisioner.js +12 -12
- package/build/lib-client/objs-on-disk/file-writing-proc.js +5 -5
- package/build/lib-client/objs-on-disk/obj-folders.js +33 -33
- package/build/lib-client/objs-on-disk/obj-on-disk.d.ts +13 -2
- package/build/lib-client/objs-on-disk/obj-on-disk.js +24 -9
- package/build/lib-client/request-utils.d.ts +1 -0
- package/build/lib-client/request-utils.js +14 -14
- package/build/lib-client/server-events.d.ts +3 -3
- package/build/lib-client/server-events.js +12 -10
- package/build/lib-client/service-locator.js +10 -10
- package/build/lib-client/user-with-mid-session.js +7 -7
- package/build/lib-client/user-with-pkl-session.js +25 -25
- package/build/lib-client/ws-utils.js +3 -3
- package/build/lib-common/async-cryptor-wrap.js +4 -4
- package/build/lib-common/async-fs-node.d.ts +5 -3
- package/build/lib-common/async-fs-node.js +19 -18
- package/build/lib-common/byte-streaming/pipe.js +1 -1
- package/build/lib-common/byte-streaming/wrapping.js +17 -17
- package/build/lib-common/canonical-address.js +1 -1
- package/build/lib-common/exceptions/error.d.ts +1 -0
- package/build/lib-common/exceptions/error.js +7 -6
- package/build/lib-common/exceptions/file.js +10 -1
- package/build/lib-common/ipc/generic-ipc.js +2 -2
- package/build/lib-common/ipc/ws-ipc.js +2 -2
- package/build/lib-common/json-utils.js +2 -1
- package/build/lib-common/mid-sigs-NaCl-Ed.js +14 -14
- package/build/lib-common/objs-on-disk/file-layout.d.ts +19 -0
- package/build/lib-common/objs-on-disk/file-layout.js +130 -12
- package/build/lib-common/objs-on-disk/obj-file.d.ts +13 -2
- package/build/lib-common/objs-on-disk/obj-file.js +99 -37
- package/build/lib-common/objs-on-disk/utils.d.ts +1 -0
- package/build/lib-common/objs-on-disk/utils.js +4 -4
- package/build/lib-common/objs-on-disk/v1-obj-file-format.js +14 -14
- package/build/lib-common/processes/deferred.d.ts +6 -0
- package/build/lib-common/processes/deferred.js +30 -0
- package/build/lib-common/processes/labelled-exec-pools.d.ts +33 -0
- package/build/lib-common/processes/labelled-exec-pools.js +141 -0
- package/build/lib-common/processes/pressure.d.ts +7 -0
- package/build/lib-common/processes/pressure.js +56 -0
- package/build/lib-common/processes/sleep.d.ts +1 -0
- package/build/lib-common/processes/sleep.js +26 -0
- package/build/lib-common/{processes.d.ts → processes/synced.d.ts} +0 -40
- package/build/lib-common/{processes.js → processes/synced.js} +187 -204
- package/build/lib-common/processes/timeout.d.ts +1 -0
- package/build/lib-common/processes/timeout.js +51 -0
- package/build/lib-common/random-node.js +7 -7
- package/build/lib-common/service-api/3nstorage/owner.d.ts +100 -39
- package/build/lib-common/service-api/3nstorage/owner.js +85 -42
- package/build/lib-common/service-api/asmail/delivery.js +2 -2
- package/build/lib-common/service-api/asmail/retrieval.js +1 -1
- package/build/lib-common/timed-cache.d.ts +1 -0
- package/build/lib-common/timed-non-weak-cache.d.ts +1 -0
- package/build/lib-common/timed-non-weak-cache.js +11 -0
- package/build/lib-common/utils-for-observables.d.ts +15 -1
- package/build/lib-common/utils-for-observables.js +70 -19
- package/build/lib-common/weak-cache.d.ts +1 -0
- package/build/lib-common/weak-cache.js +12 -1
- package/build/lib-index.d.ts +2 -1
- package/build/lib-index.js +10 -7
- package/build/protos/asmail.proto.js +12912 -7127
- package/build/protos/file.proto.js +4848 -2399
- package/build/protos/fs.proto.js +9230 -3445
- package/package.json +8 -7
- package/protos/file.proto +91 -11
- package/protos/fs.proto +107 -8
- package/build/core/storage/synced/upsync-status.d.ts +0 -41
- package/build/core/storage/synced/upsync-status.js +0 -158
|
@@ -24,19 +24,22 @@ exports.FolderNode = void 0;
|
|
|
24
24
|
const buffer_utils_1 = require("../../../lib-common/buffer-utils");
|
|
25
25
|
const file_1 = require("../../../lib-common/exceptions/file");
|
|
26
26
|
const error_1 = require("../../../lib-common/exceptions/error");
|
|
27
|
+
const common_1 = require("./common");
|
|
27
28
|
const node_in_fs_1 = require("./node-in-fs");
|
|
28
29
|
const file_node_1 = require("./file-node");
|
|
29
30
|
const link_node_1 = require("./link-node");
|
|
30
|
-
const
|
|
31
|
+
const exceptions_1 = require("../exceptions");
|
|
32
|
+
const deferred_1 = require("../../../lib-common/processes/deferred");
|
|
31
33
|
const json_utils_1 = require("../../../lib-common/json-utils");
|
|
32
34
|
const xsp_files_1 = require("xsp-files");
|
|
33
35
|
const random = require("../../../lib-common/random-node");
|
|
34
36
|
const folder_node_serialization_1 = require("./folder-node-serialization");
|
|
35
37
|
const attrs_1 = require("./attrs");
|
|
36
38
|
const node_persistence_1 = require("./node-persistence");
|
|
39
|
+
const assert_1 = require("../../../lib-common/assert");
|
|
37
40
|
function jsonToInfoAndAttrs(json) {
|
|
38
41
|
const folderInfo = {
|
|
39
|
-
nodes: json_utils_1.copy(json.nodes)
|
|
42
|
+
nodes: (0, json_utils_1.copy)(json.nodes)
|
|
40
43
|
};
|
|
41
44
|
for (const node of Object.values(folderInfo.nodes)) {
|
|
42
45
|
node.key = buffer_utils_1.base64.open(node.key);
|
|
@@ -51,34 +54,38 @@ class FolderPersistance extends node_persistence_1.NodePersistance {
|
|
|
51
54
|
Object.seal(this);
|
|
52
55
|
}
|
|
53
56
|
async write(folderInfo, version, attrs, xattrs) {
|
|
54
|
-
const bytes = folder_node_serialization_1.serializeFolderInfo(folderInfo);
|
|
57
|
+
const bytes = (0, folder_node_serialization_1.serializeFolderInfo)(folderInfo);
|
|
55
58
|
return this.writeWhole(bytes, version, attrs, xattrs);
|
|
56
59
|
}
|
|
57
60
|
async read(src) {
|
|
58
61
|
const { content, xattrs, attrs } = await this.readAll(src);
|
|
59
|
-
const folderInfo = folder_node_serialization_1.parseFolderInfo(content);
|
|
62
|
+
const folderInfo = (0, folder_node_serialization_1.parseFolderInfo)(content);
|
|
60
63
|
return { folderInfo, xattrs, attrs: attrs_1.CommonAttrs.fromAttrs(attrs) };
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
Object.freeze(FolderPersistance.prototype);
|
|
64
67
|
Object.freeze(FolderPersistance);
|
|
68
|
+
let nextMoveLabel = Math.floor(Math.random() / 2 * Number.MAX_SAFE_INTEGER);
|
|
69
|
+
function getMoveLabel() {
|
|
70
|
+
const label = nextMoveLabel;
|
|
71
|
+
if (nextMoveLabel >= Number.MAX_SAFE_INTEGER) {
|
|
72
|
+
nextMoveLabel = 0;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
nextMoveLabel += 1;
|
|
76
|
+
}
|
|
77
|
+
return label;
|
|
78
|
+
}
|
|
65
79
|
class FolderNode extends node_in_fs_1.NodeInFS {
|
|
66
80
|
constructor(storage, name, objId, zNonce, version, parentId, key, setNewAttrs) {
|
|
67
81
|
super(storage, 'folder', name, objId, version, parentId);
|
|
68
82
|
this.currentState = { nodes: {} };
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
"have both objId and parent as nulls.");
|
|
83
|
+
if (name === undefined) {
|
|
84
|
+
(0, assert_1.assert)(!objId && !parentId, `Root folder must have both objId and parent as nulls.`);
|
|
72
85
|
}
|
|
73
86
|
else if (objId === null) {
|
|
74
87
|
new Error("Missing objId for non-root folder");
|
|
75
88
|
}
|
|
76
|
-
if (!zNonce) {
|
|
77
|
-
if (!objId) {
|
|
78
|
-
throw new Error(`Missing object id for folder, when zeroth nonce is not given`);
|
|
79
|
-
}
|
|
80
|
-
zNonce = xsp_files_1.idToHeaderNonce(objId);
|
|
81
|
-
}
|
|
82
89
|
this.crypto = new FolderPersistance(zNonce, key, storage.cryptor);
|
|
83
90
|
if (setNewAttrs) {
|
|
84
91
|
this.attrs = attrs_1.CommonAttrs.makeForTimeNow();
|
|
@@ -93,20 +100,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
93
100
|
return rf;
|
|
94
101
|
}
|
|
95
102
|
static async rootFromObjBytes(storage, name, objId, src, key) {
|
|
96
|
-
|
|
97
|
-
if (!objId) {
|
|
98
|
-
const header = await src.readHeader();
|
|
99
|
-
zNonce = xsp_files_1.calculateNonce(header.subarray(0, xsp_files_1.NONCE_LENGTH), -src.version);
|
|
100
|
-
}
|
|
101
|
-
const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, zNonce, src, key);
|
|
103
|
+
const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, src, key);
|
|
102
104
|
rf.storage.nodes.set(rf);
|
|
103
105
|
return rf;
|
|
104
106
|
}
|
|
105
|
-
static async readNodeFromObjBytes(storage, name, objId,
|
|
107
|
+
static async readNodeFromObjBytes(storage, name, objId, src, key) {
|
|
108
|
+
let zNonce;
|
|
109
|
+
if (objId) {
|
|
110
|
+
zNonce = (0, xsp_files_1.idToHeaderNonce)(objId);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const header = await src.readHeader();
|
|
114
|
+
zNonce = (0, xsp_files_1.calculateNonce)(header.subarray(0, xsp_files_1.NONCE_LENGTH), -src.version);
|
|
115
|
+
}
|
|
106
116
|
const rf = new FolderNode(storage, name, objId, zNonce, src.version, undefined, key, false);
|
|
107
|
-
|
|
108
|
-
rf.currentState = folderInfo;
|
|
109
|
-
rf.setUpdatedParams(src.version, attrs, xattrs);
|
|
117
|
+
await rf.setCurrentStateFrom(src);
|
|
110
118
|
return rf;
|
|
111
119
|
}
|
|
112
120
|
static async rootFromLinkParams(storage, params) {
|
|
@@ -121,36 +129,94 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
121
129
|
existingNode.crypto.compareKey(params.fKey);
|
|
122
130
|
return existingNode;
|
|
123
131
|
}
|
|
124
|
-
const src = await storage.
|
|
132
|
+
const src = await storage.getObjSrc(params.objId);
|
|
125
133
|
const key = buffer_utils_1.base64.open(params.fKey);
|
|
126
134
|
return FolderNode.rootFromObjBytes(storage, params.folderName, params.objId, src, key);
|
|
127
135
|
}
|
|
128
136
|
static rootFromJSON(storage, name, folderJson) {
|
|
129
|
-
const rf = new FolderNode(storage, name, 'readonly-root', EMPTY_ARR, 0, undefined,
|
|
137
|
+
const rf = new FolderNode(storage, name, 'readonly-root', EMPTY_ARR, 0, undefined, EMPTY_ARR, false);
|
|
130
138
|
const { folderInfo, attrs, xattrs } = jsonToInfoAndAttrs(folderJson);
|
|
131
139
|
rf.currentState = folderInfo;
|
|
132
140
|
rf.setUpdatedParams(0, attrs, xattrs);
|
|
133
141
|
return rf;
|
|
134
142
|
}
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
async setCurrentStateFrom(src) {
|
|
144
|
+
const { folderInfo, attrs, xattrs } = await this.crypto.read(src);
|
|
145
|
+
this.currentState = folderInfo;
|
|
146
|
+
this.setUpdatedParams(src.version, attrs, xattrs);
|
|
147
|
+
}
|
|
148
|
+
async adoptRemote(opts) {
|
|
149
|
+
const objsToRm = await this.doChange(true, async () => {
|
|
150
|
+
try {
|
|
151
|
+
const adopted = await this.syncedStorage().adoptRemote(this.objId, opts);
|
|
152
|
+
if (!adopted) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const src = await this.storage.getObjSrc(this.objId, adopted);
|
|
156
|
+
const originalState = this.currentState;
|
|
157
|
+
await this.setCurrentStateFrom(src);
|
|
158
|
+
const newState = this.currentState;
|
|
159
|
+
const { renamedNodes, addedNodes, removedNodes } = identifyChanges(originalState.nodes, newState.nodes);
|
|
160
|
+
for (const node of removedNodes) {
|
|
161
|
+
const event = {
|
|
162
|
+
type: 'entry-removal',
|
|
163
|
+
src: 'sync',
|
|
164
|
+
path: this.name,
|
|
165
|
+
newVersion: this.version,
|
|
166
|
+
name: node.name
|
|
167
|
+
};
|
|
168
|
+
this.broadcastEvent(event, false, node.objId);
|
|
169
|
+
}
|
|
170
|
+
for (const node of addedNodes) {
|
|
171
|
+
const event = {
|
|
172
|
+
type: 'entry-addition',
|
|
173
|
+
src: 'sync',
|
|
174
|
+
entry: nodeInfoToListingEntry(node),
|
|
175
|
+
path: this.name,
|
|
176
|
+
newVersion: this.version
|
|
177
|
+
};
|
|
178
|
+
this.broadcastEvent(event, false, node.objId);
|
|
179
|
+
}
|
|
180
|
+
for (const { objId, oldName, newName } of renamedNodes) {
|
|
181
|
+
const event = {
|
|
182
|
+
type: 'entry-renaming',
|
|
183
|
+
src: 'sync',
|
|
184
|
+
oldName,
|
|
185
|
+
newName,
|
|
186
|
+
path: this.name,
|
|
187
|
+
newVersion: this.version
|
|
188
|
+
};
|
|
189
|
+
this.broadcastEvent(event, false, objId);
|
|
190
|
+
}
|
|
191
|
+
return removedNodes;
|
|
146
192
|
}
|
|
147
|
-
|
|
148
|
-
|
|
193
|
+
catch (exc) {
|
|
194
|
+
throw (0, common_1.setPathInExc)(exc, this.name);
|
|
149
195
|
}
|
|
150
|
-
|
|
196
|
+
});
|
|
197
|
+
if (!objsToRm) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
for (const objToRm of objsToRm) {
|
|
201
|
+
(await this.getOrMakeChildNodeForInfo(objToRm)).removeObj('sync');
|
|
151
202
|
}
|
|
203
|
+
}
|
|
204
|
+
list() {
|
|
205
|
+
const lst = Object.values(this.currentState.nodes)
|
|
206
|
+
.map(nodeInfoToListingEntry);
|
|
152
207
|
return { lst, version: this.version };
|
|
153
208
|
}
|
|
209
|
+
async childExistsInSyncedVersion(childObjId) {
|
|
210
|
+
const status = await this.syncStatus();
|
|
211
|
+
if (status.state === 'synced') {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (!status.synced) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
// XXX FolderNode.childExistsInSyncedVersion() should implement check in synced version
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
154
220
|
listFolders() {
|
|
155
221
|
return Object.keys(this.currentState.nodes).filter(name => !!this.currentState.nodes[name].isFolder);
|
|
156
222
|
}
|
|
@@ -163,30 +229,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
163
229
|
return;
|
|
164
230
|
}
|
|
165
231
|
else {
|
|
166
|
-
throw file_1.makeFileException(file_1.Code.notFound, name);
|
|
232
|
+
throw (0, file_1.makeFileException)(file_1.Code.notFound, name);
|
|
167
233
|
}
|
|
168
234
|
}
|
|
169
235
|
hasChild(childName, throwIfMissing = false) {
|
|
170
236
|
return !!this.getNodeInfo(childName, !throwIfMissing);
|
|
171
237
|
}
|
|
172
|
-
/**
|
|
173
|
-
* @param objId
|
|
174
|
-
* @return either node (promise for node), or a deferred, which promise has
|
|
175
|
-
* been registered under a given id, and, therefore, has to be resolved with
|
|
176
|
-
* node.
|
|
177
|
-
*/
|
|
178
|
-
getNodeOrArrangePromise(objId) {
|
|
179
|
-
const { node, nodePromise } = this.storage.nodes.getNodeOrPromise(objId);
|
|
180
|
-
if (node) {
|
|
181
|
-
return { nodeOrPromise: node };
|
|
182
|
-
}
|
|
183
|
-
if (nodePromise) {
|
|
184
|
-
return { nodeOrPromise: nodePromise };
|
|
185
|
-
}
|
|
186
|
-
const deferred = processes_1.defer();
|
|
187
|
-
this.storage.nodes.setPromise(objId, deferred.promise);
|
|
188
|
-
return { deferred };
|
|
189
|
-
}
|
|
190
238
|
async getNode(type, name, undefOnMissing = false) {
|
|
191
239
|
const childInfo = this.getNodeInfo(name, undefOnMissing);
|
|
192
240
|
if (!childInfo) {
|
|
@@ -194,36 +242,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
194
242
|
}
|
|
195
243
|
if (type) {
|
|
196
244
|
if ((type === 'file') && !childInfo.isFile) {
|
|
197
|
-
throw file_1.makeFileException(file_1.Code.notFile, childInfo.name);
|
|
245
|
+
throw (0, file_1.makeFileException)(file_1.Code.notFile, childInfo.name);
|
|
198
246
|
}
|
|
199
247
|
else if ((type === 'folder') && !childInfo.isFolder) {
|
|
200
|
-
throw file_1.makeFileException(file_1.Code.notDirectory, childInfo.name);
|
|
248
|
+
throw (0, file_1.makeFileException)(file_1.Code.notDirectory, childInfo.name);
|
|
201
249
|
}
|
|
202
250
|
else if ((type === 'link') && !childInfo.isLink) {
|
|
203
|
-
throw file_1.makeFileException(file_1.Code.notLink, childInfo.name);
|
|
251
|
+
throw (0, file_1.makeFileException)(file_1.Code.notLink, childInfo.name);
|
|
204
252
|
}
|
|
205
253
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
254
|
+
return this.getOrMakeChildNodeForInfo(childInfo);
|
|
255
|
+
}
|
|
256
|
+
async getOrMakeChildNodeForInfo(info) {
|
|
257
|
+
const { node, nodePromise } = this.storage.nodes.getNodeOrPromise(info.objId);
|
|
258
|
+
if (node) {
|
|
259
|
+
return node;
|
|
260
|
+
}
|
|
261
|
+
if (nodePromise) {
|
|
262
|
+
return nodePromise;
|
|
209
263
|
}
|
|
264
|
+
const deferred = (0, deferred_1.defer)();
|
|
265
|
+
this.storage.nodes.setPromise(info.objId, deferred.promise);
|
|
210
266
|
try {
|
|
211
267
|
let node;
|
|
212
|
-
if (
|
|
213
|
-
node = await file_node_1.FileNode.makeForExisting(this.storage, this.objId, name,
|
|
214
|
-
}
|
|
215
|
-
else if (
|
|
216
|
-
const src = await this.storage.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// f.currentState = folderInfo;
|
|
222
|
-
// f.attrs = (attrs ? attrs : AttrsHolder.makeReadonlyForFolder(0));
|
|
223
|
-
node = await FolderNode.readNodeFromObjBytes(this.storage, childInfo.name, childInfo.objId, undefined, src, childInfo.key);
|
|
224
|
-
}
|
|
225
|
-
else if (childInfo.isLink) {
|
|
226
|
-
node = await link_node_1.LinkNode.makeForExisting(this.storage, this.objId, name, childInfo.objId, childInfo.key);
|
|
268
|
+
if (info.isFile) {
|
|
269
|
+
node = await file_node_1.FileNode.makeForExisting(this.storage, this.objId, info.name, info.objId, info.key);
|
|
270
|
+
}
|
|
271
|
+
else if (info.isFolder) {
|
|
272
|
+
const src = await this.storage.getObjSrc(info.objId);
|
|
273
|
+
node = await FolderNode.readNodeFromObjBytes(this.storage, info.name, info.objId, src, info.key);
|
|
274
|
+
}
|
|
275
|
+
else if (info.isLink) {
|
|
276
|
+
node = await link_node_1.LinkNode.makeForExisting(this.storage, this.objId, info.name, info.objId, info.key);
|
|
227
277
|
}
|
|
228
278
|
else {
|
|
229
279
|
throw new Error(`Unknown type of fs node`);
|
|
@@ -234,9 +284,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
234
284
|
catch (exc) {
|
|
235
285
|
deferred.reject(exc);
|
|
236
286
|
if (exc.objNotFound) {
|
|
237
|
-
await this.fixMissingChildAndThrow(exc,
|
|
287
|
+
await this.fixMissingChildAndThrow(exc, info);
|
|
238
288
|
}
|
|
239
|
-
throw error_1.errWithCause(exc, `
|
|
289
|
+
throw (0, error_1.errWithCause)(exc, `Failed to instantiate fs node '${this.name}/${info.name}'`);
|
|
240
290
|
}
|
|
241
291
|
}
|
|
242
292
|
getFolder(name, undefOnMissing = false) {
|
|
@@ -250,16 +300,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
250
300
|
}
|
|
251
301
|
async fixMissingChildAndThrow(exc, childInfo) {
|
|
252
302
|
await this.doTransition(async (state, version) => {
|
|
303
|
+
const presentChild = state.nodes[childInfo.name];
|
|
304
|
+
if (!presentChild || (presentChild.objId !== childInfo.objId)) {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
253
307
|
delete state.nodes[childInfo.name];
|
|
254
308
|
const event = {
|
|
255
309
|
type: 'entry-removal',
|
|
256
310
|
path: this.name,
|
|
311
|
+
src: 'local',
|
|
257
312
|
name: childInfo.name,
|
|
258
313
|
newVersion: version
|
|
259
314
|
};
|
|
260
|
-
return event;
|
|
315
|
+
return { event, childObjId: childInfo.objId };
|
|
261
316
|
}).catch(noop);
|
|
262
|
-
const fileExc = file_1.makeFileException(file_1.Code.notFound, childInfo.name, exc);
|
|
317
|
+
const fileExc = (0, file_1.makeFileException)(file_1.Code.notFound, childInfo.name, exc);
|
|
263
318
|
fileExc.inconsistentStateOfFS = true;
|
|
264
319
|
throw fileExc;
|
|
265
320
|
}
|
|
@@ -278,11 +333,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
278
333
|
async performTransition(change) {
|
|
279
334
|
// start transition and prepare transition state
|
|
280
335
|
// Note on copy: byte arrays are not cloned
|
|
281
|
-
const state = json_utils_1.copy(this.currentState);
|
|
336
|
+
const state = (0, json_utils_1.copy)(this.currentState);
|
|
282
337
|
const version = this.version + 1;
|
|
283
338
|
const attrs = this.attrs.copy();
|
|
284
339
|
// do action within transition state
|
|
285
|
-
const
|
|
340
|
+
const changeEvents = await change(state, version);
|
|
286
341
|
// save transition state
|
|
287
342
|
const encSub = await this.crypto.write(state, version, attrs, this.xattrs);
|
|
288
343
|
await this.storage.saveObj(this.objId, version, encSub);
|
|
@@ -290,7 +345,15 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
290
345
|
this.currentState = state;
|
|
291
346
|
this.setCurrentVersion(version);
|
|
292
347
|
this.attrs = attrs;
|
|
293
|
-
|
|
348
|
+
if (Array.isArray(changeEvents)) {
|
|
349
|
+
for (const { event, childObjId } of changeEvents) {
|
|
350
|
+
this.broadcastEvent(event, false, childObjId);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
const { event, childObjId } = changeEvents;
|
|
355
|
+
this.broadcastEvent(event, false, childObjId);
|
|
356
|
+
}
|
|
294
357
|
}
|
|
295
358
|
/**
|
|
296
359
|
* This function only creates folder node, but it doesn't insert it anywhere.
|
|
@@ -299,7 +362,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
299
362
|
async makeAndSaveNewChildFolderNode(name, changes) {
|
|
300
363
|
const key = await random.bytes(xsp_files_1.KEY_LENGTH);
|
|
301
364
|
const childObjId = await this.storage.generateNewObjId();
|
|
302
|
-
const node = new FolderNode(this.storage, name, childObjId,
|
|
365
|
+
const node = new FolderNode(this.storage, name, childObjId, (0, xsp_files_1.idToHeaderNonce)(childObjId), 0, this.objId, key, true);
|
|
303
366
|
await node.saveFirstVersion(changes).catch((exc) => {
|
|
304
367
|
if (!exc.objExists) {
|
|
305
368
|
throw exc;
|
|
@@ -358,7 +421,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
358
421
|
// do check for concurrent creation of a node
|
|
359
422
|
if (this.getNodeInfo(name, true)) {
|
|
360
423
|
if (exclusive) {
|
|
361
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, name);
|
|
424
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, name);
|
|
362
425
|
}
|
|
363
426
|
else if (type === 'folder') {
|
|
364
427
|
return (await this.getNode('folder', name));
|
|
@@ -394,15 +457,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
394
457
|
const event = {
|
|
395
458
|
type: 'entry-addition',
|
|
396
459
|
path: this.name,
|
|
460
|
+
src: 'local',
|
|
397
461
|
newVersion: version,
|
|
398
|
-
entry:
|
|
399
|
-
name: node.name,
|
|
400
|
-
isFile: (node.type === 'file'),
|
|
401
|
-
isFolder: (node.type === 'folder'),
|
|
402
|
-
isLink: (node.type === 'link')
|
|
403
|
-
}
|
|
462
|
+
entry: nodeToListingEntry(node)
|
|
404
463
|
};
|
|
405
|
-
return event;
|
|
464
|
+
return { event, childObjId: node.objId };
|
|
406
465
|
});
|
|
407
466
|
return node;
|
|
408
467
|
});
|
|
@@ -426,15 +485,16 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
426
485
|
const event = {
|
|
427
486
|
type: 'entry-removal',
|
|
428
487
|
path: this.name,
|
|
488
|
+
src: 'local',
|
|
429
489
|
name: f.name,
|
|
430
490
|
newVersion: version
|
|
431
491
|
};
|
|
432
|
-
return event;
|
|
492
|
+
return { event, childObjId: f.objId };
|
|
433
493
|
});
|
|
434
494
|
// explicitly do not wait on a result of child's delete, cause if it fails
|
|
435
495
|
// we just get traceable garbage, yet, the rest of a live/non-deleted tree
|
|
436
496
|
// stays consistent
|
|
437
|
-
f.
|
|
497
|
+
f.removeObj();
|
|
438
498
|
}
|
|
439
499
|
changeChildName(initName, newName) {
|
|
440
500
|
return this.doTransition(async (state, version) => {
|
|
@@ -449,57 +509,59 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
449
509
|
const event = {
|
|
450
510
|
type: 'entry-renaming',
|
|
451
511
|
path: this.name,
|
|
512
|
+
src: 'local',
|
|
452
513
|
newName,
|
|
453
514
|
oldName: initName,
|
|
454
515
|
newVersion: version
|
|
455
516
|
};
|
|
456
|
-
return event;
|
|
517
|
+
return { event, childObjId: child.objId };
|
|
457
518
|
});
|
|
458
519
|
}
|
|
459
520
|
async moveChildTo(childName, dst, nameInDst) {
|
|
460
521
|
if (dst.hasChild(nameInDst)) {
|
|
461
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, nameInDst);
|
|
522
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, nameInDst);
|
|
462
523
|
}
|
|
463
524
|
if (dst === this) {
|
|
464
525
|
// In this case we only need to change child's name
|
|
465
|
-
|
|
526
|
+
await this.changeChildName(childName, nameInDst);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
const childJSON = this.getNodeInfo(childName);
|
|
530
|
+
// we have two transitions here, in this and in dst.
|
|
531
|
+
const moveLabel = getMoveLabel();
|
|
532
|
+
await dst.moveChildIn(nameInDst, childJSON, moveLabel);
|
|
533
|
+
await this.moveChildOut(childName, childJSON.objId, moveLabel);
|
|
466
534
|
}
|
|
467
|
-
const childJSON = this.getNodeInfo(childName);
|
|
468
|
-
// we have two transitions here, in this and in dst.
|
|
469
|
-
await Promise.all([
|
|
470
|
-
await dst.moveChildIn(nameInDst, childJSON),
|
|
471
|
-
await this.moveChildOut(childName)
|
|
472
|
-
]);
|
|
473
535
|
}
|
|
474
|
-
async moveChildOut(name) {
|
|
536
|
+
async moveChildOut(name, childObjId, moveLabel) {
|
|
475
537
|
await this.doTransition(async (state, version) => {
|
|
538
|
+
const child = state.nodes[name];
|
|
476
539
|
delete state.nodes[name];
|
|
477
540
|
const event = {
|
|
478
541
|
type: 'entry-removal',
|
|
479
542
|
path: this.name,
|
|
543
|
+
src: 'local',
|
|
480
544
|
name,
|
|
481
|
-
newVersion: version
|
|
545
|
+
newVersion: version,
|
|
546
|
+
moveLabel
|
|
482
547
|
};
|
|
483
|
-
return event;
|
|
548
|
+
return { event, childObjId };
|
|
484
549
|
});
|
|
485
550
|
}
|
|
486
|
-
async moveChildIn(newName, child) {
|
|
487
|
-
child = json_utils_1.copy(child);
|
|
551
|
+
async moveChildIn(newName, child, moveLabel) {
|
|
552
|
+
child = (0, json_utils_1.copy)(child);
|
|
488
553
|
await this.doTransition(async (state, version) => {
|
|
489
554
|
child.name = newName;
|
|
490
555
|
state.nodes[child.name] = child;
|
|
491
556
|
const event = {
|
|
492
557
|
type: 'entry-addition',
|
|
493
558
|
path: this.name,
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
isLink: child.isLink
|
|
499
|
-
},
|
|
500
|
-
newVersion: version
|
|
559
|
+
src: 'local',
|
|
560
|
+
entry: nodeInfoToListingEntry(child),
|
|
561
|
+
newVersion: version,
|
|
562
|
+
moveLabel
|
|
501
563
|
};
|
|
502
|
-
return event;
|
|
564
|
+
return { event, childObjId: child.objId };
|
|
503
565
|
});
|
|
504
566
|
}
|
|
505
567
|
async getFolderInThisSubTree(path, createIfMissing = false, exclusiveCreate = false) {
|
|
@@ -512,7 +574,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
512
574
|
// existing folder at this point
|
|
513
575
|
if (path.length === 1) {
|
|
514
576
|
if (exclusiveCreate) {
|
|
515
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, path[0]);
|
|
577
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, path[0]);
|
|
516
578
|
}
|
|
517
579
|
else {
|
|
518
580
|
return f;
|
|
@@ -569,10 +631,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
569
631
|
}
|
|
570
632
|
return content;
|
|
571
633
|
}
|
|
572
|
-
async delete(
|
|
573
|
-
if (remoteEvent) {
|
|
574
|
-
return super.delete(true);
|
|
575
|
-
}
|
|
634
|
+
async delete(src) {
|
|
576
635
|
const childrenNodes = await this.getAllNodes();
|
|
577
636
|
await this.storage.removeObj(this.objId);
|
|
578
637
|
this.storage.nodes.delete(this);
|
|
@@ -581,14 +640,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
581
640
|
const event = {
|
|
582
641
|
type: 'removed',
|
|
583
642
|
path: this.name,
|
|
584
|
-
|
|
643
|
+
src
|
|
585
644
|
};
|
|
586
645
|
this.broadcastEvent(event, true);
|
|
587
|
-
// explicitly do not wait on a result of child's delete, cause if it fails
|
|
588
|
-
// we just get traceable garbage, yet, the rest of a live/non-deleted tree
|
|
589
|
-
// stays consistent
|
|
590
646
|
for (const node of childrenNodes) {
|
|
591
|
-
node.
|
|
647
|
+
node.removeObj(src);
|
|
592
648
|
}
|
|
593
649
|
}
|
|
594
650
|
getParamsForLink() {
|
|
@@ -607,76 +663,213 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
607
663
|
};
|
|
608
664
|
return linkParams;
|
|
609
665
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
async
|
|
622
|
-
|
|
666
|
+
async upload(opts) {
|
|
667
|
+
// XXX temporary use of super's method
|
|
668
|
+
return super.upload(opts);
|
|
669
|
+
// const storage = this.syncedStorage();
|
|
670
|
+
// XXX
|
|
671
|
+
// - use identifyChanges() to find changes, minding particular version,
|
|
672
|
+
// ensuring that concurrent change won't mess things up.
|
|
673
|
+
// - items not present in uploaded version, relative to latest sync,
|
|
674
|
+
// i.e. removed, removal should be uploaded with
|
|
675
|
+
// storage.uploadObjRemoval(objId);
|
|
676
|
+
}
|
|
677
|
+
async needUpload(localVersion) {
|
|
678
|
+
const toUpload = await super.needUpload(localVersion);
|
|
679
|
+
if (!toUpload) {
|
|
623
680
|
return;
|
|
624
681
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
682
|
+
// XXX can put the following into private ensureAllChildrenUploaded(),
|
|
683
|
+
// but it also need to get a consistent state, while current one can
|
|
684
|
+
// be broken by simultaneous change in folder.
|
|
685
|
+
const storage = this.syncedStorage();
|
|
686
|
+
if (toUpload.localVersion === this.version) {
|
|
687
|
+
for (const { objId, name } of Object.values(this.currentState.nodes)) {
|
|
688
|
+
if ((await storage.status(objId)).neverUploaded()) {
|
|
689
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
690
|
+
childNeverUploaded: true, childName: name
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
throw new Error(`Reading particular version is not implemented, yet`);
|
|
697
|
+
}
|
|
698
|
+
return toUpload;
|
|
699
|
+
}
|
|
700
|
+
async adoptRemoteFolderItem(itemName, opts) {
|
|
701
|
+
if (opts === null || opts === void 0 ? void 0 : opts.localVersion) {
|
|
702
|
+
if (this.version !== opts.localVersion) {
|
|
703
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
704
|
+
versionMismatch: true,
|
|
705
|
+
message: `Given local version ${opts.localVersion} is not equal to current ${this.version}`
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
const { remote } = await this.syncStatus();
|
|
710
|
+
if (!remote) {
|
|
711
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
712
|
+
versionMismatch: true,
|
|
713
|
+
message: `No remote versions`
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
if (opts === null || opts === void 0 ? void 0 : opts.remoteVersion) {
|
|
717
|
+
if (opts.remoteVersion !== remote.latest) {
|
|
718
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
719
|
+
versionMismatch: true,
|
|
720
|
+
message: `Unknown remote version ${opts.remoteVersion}`
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else if (!remote.latest) {
|
|
725
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
726
|
+
remoteIsArchived: true
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
const storage = this.syncedStorage();
|
|
730
|
+
const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remote.latest);
|
|
731
|
+
const { folderInfo: { nodes: remoteNodes } } = await this.crypto.read(srcOfRemote);
|
|
732
|
+
const remoteChildNode = remoteNodes[itemName];
|
|
733
|
+
if (!remoteChildNode) {
|
|
734
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
735
|
+
remoteFolderItemNotFound: true,
|
|
736
|
+
message: `Item '${itemName}' is not found in remote version`
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
const localNodeToRm = this.getNodeInfo(itemName, true);
|
|
740
|
+
if (!localNodeToRm) {
|
|
741
|
+
return await this.addRemoteChild(remoteChildNode);
|
|
629
742
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
743
|
+
else if (localNodeToRm.objId === remoteChildNode.objId) {
|
|
744
|
+
return await this.setRemoteNodeInfo(remoteChildNode);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
return await this.replaceLocalChildWithRemote(localNodeToRm, remoteChildNode);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
async addRemoteChild(remoteChildNode) {
|
|
751
|
+
let newVersion;
|
|
752
|
+
await this.doTransition(async (state, version) => {
|
|
753
|
+
newVersion = version;
|
|
754
|
+
state.nodes[remoteChildNode.name] = remoteChildNode;
|
|
642
755
|
const event = {
|
|
643
|
-
type: 'entry-
|
|
756
|
+
type: 'entry-addition',
|
|
757
|
+
path: this.name,
|
|
758
|
+
src: 'sync',
|
|
759
|
+
entry: nodeInfoToListingEntry(remoteChildNode),
|
|
760
|
+
newVersion
|
|
761
|
+
};
|
|
762
|
+
return { event, childObjId: remoteChildNode.objId };
|
|
763
|
+
});
|
|
764
|
+
// XXX child should update sync info, and adopt change.
|
|
765
|
+
return newVersion;
|
|
766
|
+
}
|
|
767
|
+
async setRemoteNodeInfo(remoteChildNode) {
|
|
768
|
+
let newVersion;
|
|
769
|
+
await this.doTransition(async (state, version) => {
|
|
770
|
+
newVersion = version;
|
|
771
|
+
state.nodes[remoteChildNode.name] = remoteChildNode;
|
|
772
|
+
return [];
|
|
773
|
+
});
|
|
774
|
+
// XXX child should update sync info, and adopt change, cause changed key
|
|
775
|
+
// requires changed cypher.
|
|
776
|
+
return newVersion;
|
|
777
|
+
}
|
|
778
|
+
async replaceLocalChildWithRemote(origChildNode, remoteChildNode) {
|
|
779
|
+
let newVersion;
|
|
780
|
+
await this.doTransition(async (state, version) => {
|
|
781
|
+
newVersion = version;
|
|
782
|
+
const presentNode = state.nodes[remoteChildNode.name];
|
|
783
|
+
state.nodes[remoteChildNode.name] = remoteChildNode;
|
|
784
|
+
const additionEvent = {
|
|
785
|
+
type: 'entry-addition',
|
|
644
786
|
path: this.name,
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
newName: addedEntries[0],
|
|
787
|
+
src: 'sync',
|
|
788
|
+
entry: nodeInfoToListingEntry(remoteChildNode),
|
|
648
789
|
newVersion
|
|
649
790
|
};
|
|
650
|
-
|
|
791
|
+
const rmEvent = {
|
|
792
|
+
type: 'entry-removal',
|
|
793
|
+
path: this.name,
|
|
794
|
+
src: 'sync',
|
|
795
|
+
name: origChildNode.name
|
|
796
|
+
};
|
|
797
|
+
return [
|
|
798
|
+
{ event: rmEvent, childObjId: origChildNode.objId },
|
|
799
|
+
{ event: additionEvent, childObjId: remoteChildNode.objId }
|
|
800
|
+
];
|
|
801
|
+
});
|
|
802
|
+
// XXX
|
|
803
|
+
// remote child should update sync info, and adopt change.
|
|
804
|
+
// original child:
|
|
805
|
+
// - if only local, should be removed,
|
|
806
|
+
// - else should update sync info, and adopt both removal and change.
|
|
807
|
+
(await this.getOrMakeChildNodeForInfo(origChildNode)).removeObj('sync');
|
|
808
|
+
return newVersion;
|
|
809
|
+
}
|
|
810
|
+
async diffCurrentAndRemote(remoteVersion) {
|
|
811
|
+
var _a;
|
|
812
|
+
const { state, remote } = await this.syncStatus();
|
|
813
|
+
let isCurrentLocal;
|
|
814
|
+
if (state === 'behind') {
|
|
815
|
+
isCurrentLocal = false;
|
|
816
|
+
}
|
|
817
|
+
else if (state === 'conflicting') {
|
|
818
|
+
isCurrentLocal = true;
|
|
651
819
|
}
|
|
652
820
|
else {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
path: this.name,
|
|
658
|
-
isRemote: true,
|
|
659
|
-
entry: {
|
|
660
|
-
name: addedNode.name,
|
|
661
|
-
isFile: addedNode.isFile,
|
|
662
|
-
isFolder: addedNode.isFolder,
|
|
663
|
-
isLink: addedNode.isLink
|
|
664
|
-
},
|
|
665
|
-
newVersion
|
|
666
|
-
};
|
|
667
|
-
this.broadcastEvent(event);
|
|
668
|
-
});
|
|
669
|
-
removedEntries.forEach(name => {
|
|
670
|
-
const event = {
|
|
671
|
-
type: 'entry-removal',
|
|
672
|
-
path: this.name,
|
|
673
|
-
isRemote: true,
|
|
674
|
-
name,
|
|
675
|
-
newVersion
|
|
676
|
-
};
|
|
677
|
-
this.broadcastEvent(event);
|
|
678
|
-
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (remote.isArchived) {
|
|
824
|
+
return this.diffWithArchivedRemote(isCurrentLocal);
|
|
679
825
|
}
|
|
826
|
+
else {
|
|
827
|
+
if (remoteVersion) {
|
|
828
|
+
if ((remoteVersion !== remote.latest)
|
|
829
|
+
&& !((_a = remote.archived) === null || _a === void 0 ? void 0 : _a.includes(remoteVersion))) {
|
|
830
|
+
throw (0, exceptions_1.makeFSSyncException)(this.name, {
|
|
831
|
+
versionMismatch: true,
|
|
832
|
+
message: `Unknown remote version ${remoteVersion}`
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
remoteVersion = remote.latest;
|
|
838
|
+
}
|
|
839
|
+
const storage = this.syncedStorage();
|
|
840
|
+
const srcOfRemote = await storage.getObjSrcOfRemoteVersion(this.objId, remote.latest);
|
|
841
|
+
const { folderInfo, attrs, xattrs } = await this.crypto.read(srcOfRemote);
|
|
842
|
+
return this.diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
diffWithArchivedRemote(isCurrentLocal) {
|
|
846
|
+
return {
|
|
847
|
+
currentVersion: this.version,
|
|
848
|
+
isCurrentLocal,
|
|
849
|
+
isRemoteArchived: true,
|
|
850
|
+
ctime: {
|
|
851
|
+
current: new Date(this.attrs.ctime)
|
|
852
|
+
},
|
|
853
|
+
mtime: {
|
|
854
|
+
current: new Date(this.attrs.ctime)
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
diffWithRemote(isCurrentLocal, remoteVersion, folderInfo, attrs, xattrs) {
|
|
859
|
+
const { ctime, mtime } = diffAttrs(this.attrs, attrs);
|
|
860
|
+
const { inCurrent, inRemote, nameOverlaps } = diffNodes(this.currentState, folderInfo);
|
|
861
|
+
return {
|
|
862
|
+
currentVersion: this.version,
|
|
863
|
+
isCurrentLocal,
|
|
864
|
+
isRemoteArchived: false,
|
|
865
|
+
remoteVersion,
|
|
866
|
+
inCurrent,
|
|
867
|
+
inRemote,
|
|
868
|
+
nameOverlaps,
|
|
869
|
+
ctime,
|
|
870
|
+
mtime,
|
|
871
|
+
xattrs: diffXAttrs(this.xattrs, xattrs)
|
|
872
|
+
};
|
|
680
873
|
}
|
|
681
874
|
}
|
|
682
875
|
exports.FolderNode = FolderNode;
|
|
@@ -688,6 +881,32 @@ function checkFolderInfo(folderJson) {
|
|
|
688
881
|
// TODO throw if folderJson is not ok
|
|
689
882
|
return folderJson;
|
|
690
883
|
}
|
|
884
|
+
function nodeInfoToListingEntry({ name, isFile, isFolder, isLink }) {
|
|
885
|
+
if (isFolder) {
|
|
886
|
+
return { name, isFolder };
|
|
887
|
+
}
|
|
888
|
+
else if (isFile) {
|
|
889
|
+
return { name, isFile };
|
|
890
|
+
}
|
|
891
|
+
else if (isLink) {
|
|
892
|
+
return { name, isLink };
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
return { name };
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function nodeToListingEntry({ name, type }) {
|
|
899
|
+
switch (type) {
|
|
900
|
+
case 'file':
|
|
901
|
+
return { name, isFolder: true };
|
|
902
|
+
case 'folder':
|
|
903
|
+
return { name, isFolder: true };
|
|
904
|
+
case 'link':
|
|
905
|
+
return { name, isLink: true };
|
|
906
|
+
default:
|
|
907
|
+
return { name };
|
|
908
|
+
}
|
|
909
|
+
}
|
|
691
910
|
function addToTransitionState(state, f, key) {
|
|
692
911
|
const nodeInfo = {
|
|
693
912
|
name: f.name,
|
|
@@ -708,4 +927,195 @@ function addToTransitionState(state, f, key) {
|
|
|
708
927
|
}
|
|
709
928
|
state.nodes[nodeInfo.name] = nodeInfo;
|
|
710
929
|
}
|
|
930
|
+
function diffAttrs(current, remote) {
|
|
931
|
+
return {
|
|
932
|
+
ctime: {
|
|
933
|
+
current: new Date(current.ctime),
|
|
934
|
+
remote: new Date(remote.ctime)
|
|
935
|
+
},
|
|
936
|
+
mtime: {
|
|
937
|
+
current: new Date(current.mtime),
|
|
938
|
+
remote: new Date(remote.mtime)
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function diffNodes(current, remote) {
|
|
943
|
+
const inCurrent = getOnlyNotEqualNodes(current.nodes, remote.nodes);
|
|
944
|
+
const inRemote = getOnlyNotEqualNodes(remote.nodes, current.nodes);
|
|
945
|
+
const combined = combineCheckingNameOverlaps(inCurrent, inRemote);
|
|
946
|
+
return (combined ? combined : {});
|
|
947
|
+
}
|
|
948
|
+
function getOnlyNotEqualNodes(src, exclude) {
|
|
949
|
+
const collected = [];
|
|
950
|
+
for (const [name, nodeInSrc] of Object.entries(src)) {
|
|
951
|
+
const nodeInExclude = exclude[name];
|
|
952
|
+
if (!nodeInExclude
|
|
953
|
+
|| !areNodesEqual(nodeInSrc, nodeInExclude)) {
|
|
954
|
+
collected.push(nodeInfoToListingEntry(nodeInSrc));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return ((collected.length > 0) ? collected : undefined);
|
|
958
|
+
}
|
|
959
|
+
function areNodesEqual(n1, n2) {
|
|
960
|
+
if (n1.name !== n2.name) {
|
|
961
|
+
return false;
|
|
962
|
+
}
|
|
963
|
+
if (n1.isFile && !n2.isFile) {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
if (n1.isFolder && !n2.isFolder) {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
if (n1.isLink && !n2.isLink) {
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
if (n1.objId !== n2.objId) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
if (!areBytesEqual(n1.key, n2.key)) {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
function combineCheckingNameOverlaps(inCurrent, inRemote) {
|
|
981
|
+
if (inCurrent) {
|
|
982
|
+
if (inRemote) {
|
|
983
|
+
const nameOverlaps = [];
|
|
984
|
+
for (const { name: nameInCurrent } of inCurrent) {
|
|
985
|
+
if (inRemote.find(({ name }) => (name === nameInCurrent))) {
|
|
986
|
+
nameOverlaps.push(nameInCurrent);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
if (nameOverlaps.length > 0) {
|
|
990
|
+
return { inCurrent, inRemote, nameOverlaps };
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
return { inCurrent, inRemote };
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
return { inCurrent };
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
if (inRemote) {
|
|
1002
|
+
return { inRemote };
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function diffXAttrs(current, remote) {
|
|
1010
|
+
const haveCurrentXAttrs = (current && (current.list().length > 0));
|
|
1011
|
+
const haveRemoteXAttrs = (remote && (remote.list().length > 0));
|
|
1012
|
+
if (haveCurrentXAttrs && haveRemoteXAttrs) {
|
|
1013
|
+
const inCurrent = getOnlyNotEqualXAttrs(current, remote);
|
|
1014
|
+
const inRemote = getOnlyNotEqualXAttrs(remote, current);
|
|
1015
|
+
return combineCheckingNameOverlaps(inCurrent, inRemote);
|
|
1016
|
+
}
|
|
1017
|
+
else if (haveCurrentXAttrs && !haveRemoteXAttrs) {
|
|
1018
|
+
return { inCurrent: allXAttrsToDiff(current) };
|
|
1019
|
+
}
|
|
1020
|
+
else if (!haveCurrentXAttrs && haveRemoteXAttrs) {
|
|
1021
|
+
return { inRemote: allXAttrsToDiff(remote) };
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
function allXAttrsToDiff(src) {
|
|
1028
|
+
const collected = [];
|
|
1029
|
+
for (const name of src.list()) {
|
|
1030
|
+
const value = src.get(name);
|
|
1031
|
+
if (value !== undefined) {
|
|
1032
|
+
collected.push({ name, value });
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return collected;
|
|
1036
|
+
}
|
|
1037
|
+
function getOnlyNotEqualXAttrs(src, exclude) {
|
|
1038
|
+
const collected = [];
|
|
1039
|
+
for (const name of src.list()) {
|
|
1040
|
+
const valueInSrc = src.get(name);
|
|
1041
|
+
if (valueInSrc === undefined) {
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const valueInExclude = exclude.get(name);
|
|
1045
|
+
if ((valueInExclude === undefined)
|
|
1046
|
+
|| !areXAttrValuesEqual(valueInSrc, valueInExclude)) {
|
|
1047
|
+
collected.push({ name, value: valueInSrc });
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return ((collected.length > 0) ? collected : undefined);
|
|
1051
|
+
}
|
|
1052
|
+
function areXAttrValuesEqual(v1, v2) {
|
|
1053
|
+
if (Buffer.isBuffer(v1) || ArrayBuffer.isView(v1)) {
|
|
1054
|
+
if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)) {
|
|
1055
|
+
return areBytesEqual(v1, v2);
|
|
1056
|
+
}
|
|
1057
|
+
{
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
else if (typeof v1 === 'string') {
|
|
1062
|
+
if (typeof v2 === 'string') {
|
|
1063
|
+
return (v1 === v2);
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
else {
|
|
1070
|
+
if (Buffer.isBuffer(v2) || ArrayBuffer.isView(v2)
|
|
1071
|
+
|| (typeof v2 === 'string')) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
return (0, json_utils_1.deepEqual)(v1, v2);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function areBytesEqual(b1, b2) {
|
|
1080
|
+
if (b1.length !== b2.length) {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
for (let i = 0; i < b1.length; i += 1) {
|
|
1084
|
+
if (b1[i] !== b2[i]) {
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
1090
|
+
function identifyChanges(originalNodes, newNodes) {
|
|
1091
|
+
const removedNodes = [];
|
|
1092
|
+
const addedNodes = [];
|
|
1093
|
+
const renamedNodes = [];
|
|
1094
|
+
for (const [name, node] of Object.entries(originalNodes)) {
|
|
1095
|
+
const newNode = newNodes[name];
|
|
1096
|
+
if (newNode && (newNode.objId === node.objId)) {
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
removedNodes.push(node);
|
|
1100
|
+
}
|
|
1101
|
+
for (const [name, newNode] of Object.entries(newNodes)) {
|
|
1102
|
+
const node = originalNodes[name];
|
|
1103
|
+
if (node && (newNode.objId === node.objId)) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
const indInRm = removedNodes.findIndex(n => (n.objId === newNode.objId));
|
|
1107
|
+
if (indInRm < 0) {
|
|
1108
|
+
addedNodes.push(newNode);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
const oldNode = removedNodes.splice(indInRm, 1)[0];
|
|
1112
|
+
renamedNodes.push({
|
|
1113
|
+
oldName: oldNode.name,
|
|
1114
|
+
newName: newNode.name,
|
|
1115
|
+
objId: newNode.objId
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return { addedNodes, removedNodes, renamedNodes };
|
|
1120
|
+
}
|
|
711
1121
|
Object.freeze(exports);
|