core-3nweb-client-lib 0.26.1 → 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/build/api-defs/asmail.d.ts +1 -1
- package/build/api-defs/files.d.ts +278 -69
- package/build/core/app-files.js +7 -7
- package/build/core/asmail/config/common.js +2 -2
- 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 +5 -5
- package/build/core/asmail/delivery/msg.js +4 -4
- package/build/core/asmail/delivery/per-recipient-wip.js +1 -1
- package/build/core/asmail/inbox/attachments/fs.js +5 -1
- package/build/core/asmail/inbox/cached-msgs.js +1 -1
- package/build/core/asmail/inbox/inbox-events.js +4 -4
- package/build/core/asmail/inbox/index.js +10 -10
- package/build/core/asmail/inbox/msg-downloader.js +1 -1
- package/build/core/asmail/inbox/msg-indexing.js +1 -1
- package/build/core/asmail/inbox/msg-on-disk.js +5 -5
- 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 +2 -1
- 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 +2 -2
- package/build/core/asmail/sending-params/params-from-others.js +1 -1
- package/build/core/id-manager.js +6 -3
- package/build/core/index.d.ts +2 -1
- package/build/core/index.js +14 -14
- package/build/core/sign-in.js +5 -5
- package/build/core/sign-up.js +9 -9
- package/build/core/storage/common/json-saving.js +2 -2
- package/build/core/storage/common/obj-info-file.d.ts +12 -4
- package/build/core/storage/common/obj-info-file.js +66 -34
- 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 +56 -76
- package/build/core/storage/local/obj-files-gc.d.ts +2 -0
- package/build/core/storage/local/obj-files-gc.js +49 -37
- package/build/core/storage/local/obj-files.d.ts +4 -7
- package/build/core/storage/local/obj-files.js +7 -10
- package/build/core/storage/local/obj-status.d.ts +12 -6
- package/build/core/storage/local/obj-status.js +24 -9
- package/build/core/storage/local/storage.d.ts +9 -6
- package/build/core/storage/local/storage.js +29 -18
- package/build/core/storage/synced/downloader.js +1 -1
- package/build/core/storage/synced/obj-files-gc.d.ts +5 -1
- package/build/core/storage/synced/obj-files-gc.js +91 -37
- package/build/core/storage/synced/obj-files.d.ts +42 -36
- package/build/core/storage/synced/obj-files.js +178 -147
- package/build/core/storage/synced/obj-status.d.ts +87 -85
- package/build/core/storage/synced/obj-status.js +463 -259
- package/build/core/storage/synced/remote-events.d.ts +11 -12
- package/build/core/storage/synced/remote-events.js +73 -56
- package/build/core/storage/synced/storage.d.ts +18 -9
- package/build/core/storage/synced/storage.js +108 -48
- 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 +12 -7
- package/build/core/storage/synced/upsyncer.js +205 -280
- 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 +58 -57
- package/build/ipc-via-protobuf/bytes.js +16 -17
- package/build/ipc-via-protobuf/connector-clients-side.d.ts +1 -0
- package/build/ipc-via-protobuf/connector-clients-side.js +14 -15
- 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 +474 -126
- package/build/ipc-via-protobuf/fs.d.ts +8 -0
- package/build/ipc-via-protobuf/fs.js +577 -142
- 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 +21 -21
- package/build/ipc-via-protobuf/storage-cap.js +12 -12
- package/build/ipc.js +7 -2
- package/build/lib-client/3nstorage/exceptions.d.ts +3 -0
- package/build/lib-client/3nstorage/exceptions.js +13 -1
- package/build/lib-client/3nstorage/service.d.ts +16 -2
- package/build/lib-client/3nstorage/service.js +104 -38
- 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 +42 -18
- package/build/lib-client/3nstorage/xsp-fs/common.js +29 -19
- package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +1 -0
- package/build/lib-client/3nstorage/xsp-fs/file-node.js +17 -13
- package/build/lib-client/3nstorage/xsp-fs/file.d.ts +31 -6
- package/build/lib-client/3nstorage/xsp-fs/file.js +73 -25
- 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 +575 -179
- package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +35 -4
- package/build/lib-client/3nstorage/xsp-fs/fs.js +231 -110
- 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 -24
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +229 -123
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +17 -18
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +3 -3
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +53 -53
- 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 +18 -16
- 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 +6 -6
- 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 -6
- package/build/lib-client/fs-collection.js +1 -1
- 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 +7 -7
- 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 +3 -3
- package/build/lib-client/objs-on-disk/obj-folders.js +31 -31
- 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 +13 -13
- package/build/lib-client/server-events.d.ts +3 -3
- package/build/lib-client/server-events.js +9 -8
- 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 +2 -2
- 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 +16 -16
- package/build/lib-common/byte-streaming/pipe.js +1 -1
- package/build/lib-common/byte-streaming/wrapping.js +13 -13
- 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 +4 -0
- package/build/lib-common/ipc/ws-ipc.js +2 -2
- 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 +96 -35
- package/build/lib-common/objs-on-disk/utils.d.ts +1 -0
- package/build/lib-common/objs-on-disk/utils.js +3 -3
- package/build/lib-common/objs-on-disk/v1-obj-file-format.js +14 -14
- package/build/lib-common/processes/labelled-exec-pools.d.ts +1 -1
- package/build/lib-common/processes/labelled-exec-pools.js +1 -1
- package/build/lib-common/processes/pressure.js +2 -2
- package/build/lib-common/processes/synced.js +1 -1
- package/build/lib-common/processes/timeout.js +2 -2
- package/build/lib-common/random-node.js +7 -7
- package/build/lib-common/service-api/3nstorage/owner.d.ts +95 -35
- package/build/lib-common/service-api/3nstorage/owner.js +82 -40
- 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.js +4 -4
- 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 +12955 -7496
- package/build/protos/file.proto.js +4867 -2744
- package/build/protos/fs.proto.js +9227 -3768
- package/package.json +6 -5
- package/protos/file.proto +91 -19
- package/protos/fs.proto +107 -8
|
@@ -24,9 +24,11 @@ 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");
|
|
31
|
+
const exceptions_1 = require("../exceptions");
|
|
30
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");
|
|
@@ -34,9 +36,10 @@ 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,12 +54,12 @@ 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
|
}
|
|
@@ -77,19 +80,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
77
80
|
constructor(storage, name, objId, zNonce, version, parentId, key, setNewAttrs) {
|
|
78
81
|
super(storage, 'folder', name, objId, version, parentId);
|
|
79
82
|
this.currentState = { nodes: {} };
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
"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.`);
|
|
83
85
|
}
|
|
84
86
|
else if (objId === null) {
|
|
85
87
|
new Error("Missing objId for non-root folder");
|
|
86
88
|
}
|
|
87
|
-
if (!zNonce) {
|
|
88
|
-
if (!objId) {
|
|
89
|
-
throw new Error(`Missing object id for folder, when zeroth nonce is not given`);
|
|
90
|
-
}
|
|
91
|
-
zNonce = xsp_files_1.idToHeaderNonce(objId);
|
|
92
|
-
}
|
|
93
89
|
this.crypto = new FolderPersistance(zNonce, key, storage.cryptor);
|
|
94
90
|
if (setNewAttrs) {
|
|
95
91
|
this.attrs = attrs_1.CommonAttrs.makeForTimeNow();
|
|
@@ -104,20 +100,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
104
100
|
return rf;
|
|
105
101
|
}
|
|
106
102
|
static async rootFromObjBytes(storage, name, objId, src, key) {
|
|
107
|
-
|
|
108
|
-
if (!objId) {
|
|
109
|
-
const header = await src.readHeader();
|
|
110
|
-
zNonce = xsp_files_1.calculateNonce(header.subarray(0, xsp_files_1.NONCE_LENGTH), -src.version);
|
|
111
|
-
}
|
|
112
|
-
const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, zNonce, src, key);
|
|
103
|
+
const rf = await FolderNode.readNodeFromObjBytes(storage, name, objId, src, key);
|
|
113
104
|
rf.storage.nodes.set(rf);
|
|
114
105
|
return rf;
|
|
115
106
|
}
|
|
116
|
-
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
|
+
}
|
|
117
116
|
const rf = new FolderNode(storage, name, objId, zNonce, src.version, undefined, key, false);
|
|
118
|
-
|
|
119
|
-
rf.currentState = folderInfo;
|
|
120
|
-
rf.setUpdatedParams(src.version, attrs, xattrs);
|
|
117
|
+
await rf.setCurrentStateFrom(src);
|
|
121
118
|
return rf;
|
|
122
119
|
}
|
|
123
120
|
static async rootFromLinkParams(storage, params) {
|
|
@@ -132,36 +129,94 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
132
129
|
existingNode.crypto.compareKey(params.fKey);
|
|
133
130
|
return existingNode;
|
|
134
131
|
}
|
|
135
|
-
const src = await storage.
|
|
132
|
+
const src = await storage.getObjSrc(params.objId);
|
|
136
133
|
const key = buffer_utils_1.base64.open(params.fKey);
|
|
137
134
|
return FolderNode.rootFromObjBytes(storage, params.folderName, params.objId, src, key);
|
|
138
135
|
}
|
|
139
136
|
static rootFromJSON(storage, name, folderJson) {
|
|
140
|
-
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);
|
|
141
138
|
const { folderInfo, attrs, xattrs } = jsonToInfoAndAttrs(folderJson);
|
|
142
139
|
rf.currentState = folderInfo;
|
|
143
140
|
rf.setUpdatedParams(0, attrs, xattrs);
|
|
144
141
|
return rf;
|
|
145
142
|
}
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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;
|
|
157
192
|
}
|
|
158
|
-
|
|
159
|
-
|
|
193
|
+
catch (exc) {
|
|
194
|
+
throw (0, common_1.setPathInExc)(exc, this.name);
|
|
160
195
|
}
|
|
161
|
-
|
|
196
|
+
});
|
|
197
|
+
if (!objsToRm) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
for (const objToRm of objsToRm) {
|
|
201
|
+
(await this.getOrMakeChildNodeForInfo(objToRm)).removeObj('sync');
|
|
162
202
|
}
|
|
203
|
+
}
|
|
204
|
+
list() {
|
|
205
|
+
const lst = Object.values(this.currentState.nodes)
|
|
206
|
+
.map(nodeInfoToListingEntry);
|
|
163
207
|
return { lst, version: this.version };
|
|
164
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
|
+
}
|
|
165
220
|
listFolders() {
|
|
166
221
|
return Object.keys(this.currentState.nodes).filter(name => !!this.currentState.nodes[name].isFolder);
|
|
167
222
|
}
|
|
@@ -174,30 +229,12 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
174
229
|
return;
|
|
175
230
|
}
|
|
176
231
|
else {
|
|
177
|
-
throw file_1.makeFileException(file_1.Code.notFound, name);
|
|
232
|
+
throw (0, file_1.makeFileException)(file_1.Code.notFound, name);
|
|
178
233
|
}
|
|
179
234
|
}
|
|
180
235
|
hasChild(childName, throwIfMissing = false) {
|
|
181
236
|
return !!this.getNodeInfo(childName, !throwIfMissing);
|
|
182
237
|
}
|
|
183
|
-
/**
|
|
184
|
-
* @param objId
|
|
185
|
-
* @return either node (promise for node), or a deferred, which promise has
|
|
186
|
-
* been registered under a given id, and, therefore, has to be resolved with
|
|
187
|
-
* node.
|
|
188
|
-
*/
|
|
189
|
-
getNodeOrArrangePromise(objId) {
|
|
190
|
-
const { node, nodePromise } = this.storage.nodes.getNodeOrPromise(objId);
|
|
191
|
-
if (node) {
|
|
192
|
-
return { nodeOrPromise: node };
|
|
193
|
-
}
|
|
194
|
-
if (nodePromise) {
|
|
195
|
-
return { nodeOrPromise: nodePromise };
|
|
196
|
-
}
|
|
197
|
-
const deferred = deferred_1.defer();
|
|
198
|
-
this.storage.nodes.setPromise(objId, deferred.promise);
|
|
199
|
-
return { deferred };
|
|
200
|
-
}
|
|
201
238
|
async getNode(type, name, undefOnMissing = false) {
|
|
202
239
|
const childInfo = this.getNodeInfo(name, undefOnMissing);
|
|
203
240
|
if (!childInfo) {
|
|
@@ -205,36 +242,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
205
242
|
}
|
|
206
243
|
if (type) {
|
|
207
244
|
if ((type === 'file') && !childInfo.isFile) {
|
|
208
|
-
throw file_1.makeFileException(file_1.Code.notFile, childInfo.name);
|
|
245
|
+
throw (0, file_1.makeFileException)(file_1.Code.notFile, childInfo.name);
|
|
209
246
|
}
|
|
210
247
|
else if ((type === 'folder') && !childInfo.isFolder) {
|
|
211
|
-
throw file_1.makeFileException(file_1.Code.notDirectory, childInfo.name);
|
|
248
|
+
throw (0, file_1.makeFileException)(file_1.Code.notDirectory, childInfo.name);
|
|
212
249
|
}
|
|
213
250
|
else if ((type === 'link') && !childInfo.isLink) {
|
|
214
|
-
throw file_1.makeFileException(file_1.Code.notLink, childInfo.name);
|
|
251
|
+
throw (0, file_1.makeFileException)(file_1.Code.notLink, childInfo.name);
|
|
215
252
|
}
|
|
216
253
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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;
|
|
220
263
|
}
|
|
264
|
+
const deferred = (0, deferred_1.defer)();
|
|
265
|
+
this.storage.nodes.setPromise(info.objId, deferred.promise);
|
|
221
266
|
try {
|
|
222
267
|
let node;
|
|
223
|
-
if (
|
|
224
|
-
node = await file_node_1.FileNode.makeForExisting(this.storage, this.objId, name,
|
|
225
|
-
}
|
|
226
|
-
else if (
|
|
227
|
-
const src = await this.storage.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// f.currentState = folderInfo;
|
|
233
|
-
// f.attrs = (attrs ? attrs : AttrsHolder.makeReadonlyForFolder(0));
|
|
234
|
-
node = await FolderNode.readNodeFromObjBytes(this.storage, childInfo.name, childInfo.objId, undefined, src, childInfo.key);
|
|
235
|
-
}
|
|
236
|
-
else if (childInfo.isLink) {
|
|
237
|
-
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);
|
|
238
277
|
}
|
|
239
278
|
else {
|
|
240
279
|
throw new Error(`Unknown type of fs node`);
|
|
@@ -245,9 +284,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
245
284
|
catch (exc) {
|
|
246
285
|
deferred.reject(exc);
|
|
247
286
|
if (exc.objNotFound) {
|
|
248
|
-
await this.fixMissingChildAndThrow(exc,
|
|
287
|
+
await this.fixMissingChildAndThrow(exc, info);
|
|
249
288
|
}
|
|
250
|
-
throw error_1.errWithCause(exc, `
|
|
289
|
+
throw (0, error_1.errWithCause)(exc, `Failed to instantiate fs node '${this.name}/${info.name}'`);
|
|
251
290
|
}
|
|
252
291
|
}
|
|
253
292
|
getFolder(name, undefOnMissing = false) {
|
|
@@ -261,16 +300,21 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
261
300
|
}
|
|
262
301
|
async fixMissingChildAndThrow(exc, childInfo) {
|
|
263
302
|
await this.doTransition(async (state, version) => {
|
|
303
|
+
const presentChild = state.nodes[childInfo.name];
|
|
304
|
+
if (!presentChild || (presentChild.objId !== childInfo.objId)) {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
264
307
|
delete state.nodes[childInfo.name];
|
|
265
308
|
const event = {
|
|
266
309
|
type: 'entry-removal',
|
|
267
310
|
path: this.name,
|
|
311
|
+
src: 'local',
|
|
268
312
|
name: childInfo.name,
|
|
269
313
|
newVersion: version
|
|
270
314
|
};
|
|
271
|
-
return event;
|
|
315
|
+
return { event, childObjId: childInfo.objId };
|
|
272
316
|
}).catch(noop);
|
|
273
|
-
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);
|
|
274
318
|
fileExc.inconsistentStateOfFS = true;
|
|
275
319
|
throw fileExc;
|
|
276
320
|
}
|
|
@@ -289,11 +333,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
289
333
|
async performTransition(change) {
|
|
290
334
|
// start transition and prepare transition state
|
|
291
335
|
// Note on copy: byte arrays are not cloned
|
|
292
|
-
const state = json_utils_1.copy(this.currentState);
|
|
336
|
+
const state = (0, json_utils_1.copy)(this.currentState);
|
|
293
337
|
const version = this.version + 1;
|
|
294
338
|
const attrs = this.attrs.copy();
|
|
295
339
|
// do action within transition state
|
|
296
|
-
const
|
|
340
|
+
const changeEvents = await change(state, version);
|
|
297
341
|
// save transition state
|
|
298
342
|
const encSub = await this.crypto.write(state, version, attrs, this.xattrs);
|
|
299
343
|
await this.storage.saveObj(this.objId, version, encSub);
|
|
@@ -301,7 +345,15 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
301
345
|
this.currentState = state;
|
|
302
346
|
this.setCurrentVersion(version);
|
|
303
347
|
this.attrs = attrs;
|
|
304
|
-
|
|
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
|
+
}
|
|
305
357
|
}
|
|
306
358
|
/**
|
|
307
359
|
* This function only creates folder node, but it doesn't insert it anywhere.
|
|
@@ -310,7 +362,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
310
362
|
async makeAndSaveNewChildFolderNode(name, changes) {
|
|
311
363
|
const key = await random.bytes(xsp_files_1.KEY_LENGTH);
|
|
312
364
|
const childObjId = await this.storage.generateNewObjId();
|
|
313
|
-
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);
|
|
314
366
|
await node.saveFirstVersion(changes).catch((exc) => {
|
|
315
367
|
if (!exc.objExists) {
|
|
316
368
|
throw exc;
|
|
@@ -369,7 +421,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
369
421
|
// do check for concurrent creation of a node
|
|
370
422
|
if (this.getNodeInfo(name, true)) {
|
|
371
423
|
if (exclusive) {
|
|
372
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, name);
|
|
424
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, name);
|
|
373
425
|
}
|
|
374
426
|
else if (type === 'folder') {
|
|
375
427
|
return (await this.getNode('folder', name));
|
|
@@ -405,15 +457,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
405
457
|
const event = {
|
|
406
458
|
type: 'entry-addition',
|
|
407
459
|
path: this.name,
|
|
460
|
+
src: 'local',
|
|
408
461
|
newVersion: version,
|
|
409
|
-
entry:
|
|
410
|
-
name: node.name,
|
|
411
|
-
isFile: (node.type === 'file'),
|
|
412
|
-
isFolder: (node.type === 'folder'),
|
|
413
|
-
isLink: (node.type === 'link')
|
|
414
|
-
}
|
|
462
|
+
entry: nodeToListingEntry(node)
|
|
415
463
|
};
|
|
416
|
-
return event;
|
|
464
|
+
return { event, childObjId: node.objId };
|
|
417
465
|
});
|
|
418
466
|
return node;
|
|
419
467
|
});
|
|
@@ -437,15 +485,16 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
437
485
|
const event = {
|
|
438
486
|
type: 'entry-removal',
|
|
439
487
|
path: this.name,
|
|
488
|
+
src: 'local',
|
|
440
489
|
name: f.name,
|
|
441
490
|
newVersion: version
|
|
442
491
|
};
|
|
443
|
-
return event;
|
|
492
|
+
return { event, childObjId: f.objId };
|
|
444
493
|
});
|
|
445
494
|
// explicitly do not wait on a result of child's delete, cause if it fails
|
|
446
495
|
// we just get traceable garbage, yet, the rest of a live/non-deleted tree
|
|
447
496
|
// stays consistent
|
|
448
|
-
f.
|
|
497
|
+
f.removeObj();
|
|
449
498
|
}
|
|
450
499
|
changeChildName(initName, newName) {
|
|
451
500
|
return this.doTransition(async (state, version) => {
|
|
@@ -460,16 +509,17 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
460
509
|
const event = {
|
|
461
510
|
type: 'entry-renaming',
|
|
462
511
|
path: this.name,
|
|
512
|
+
src: 'local',
|
|
463
513
|
newName,
|
|
464
514
|
oldName: initName,
|
|
465
515
|
newVersion: version
|
|
466
516
|
};
|
|
467
|
-
return event;
|
|
517
|
+
return { event, childObjId: child.objId };
|
|
468
518
|
});
|
|
469
519
|
}
|
|
470
520
|
async moveChildTo(childName, dst, nameInDst) {
|
|
471
521
|
if (dst.hasChild(nameInDst)) {
|
|
472
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, nameInDst);
|
|
522
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, nameInDst);
|
|
473
523
|
}
|
|
474
524
|
if (dst === this) {
|
|
475
525
|
// In this case we only need to change child's name
|
|
@@ -480,40 +530,38 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
480
530
|
// we have two transitions here, in this and in dst.
|
|
481
531
|
const moveLabel = getMoveLabel();
|
|
482
532
|
await dst.moveChildIn(nameInDst, childJSON, moveLabel);
|
|
483
|
-
await this.moveChildOut(childName, moveLabel);
|
|
533
|
+
await this.moveChildOut(childName, childJSON.objId, moveLabel);
|
|
484
534
|
}
|
|
485
535
|
}
|
|
486
|
-
async moveChildOut(name, moveLabel) {
|
|
536
|
+
async moveChildOut(name, childObjId, moveLabel) {
|
|
487
537
|
await this.doTransition(async (state, version) => {
|
|
538
|
+
const child = state.nodes[name];
|
|
488
539
|
delete state.nodes[name];
|
|
489
540
|
const event = {
|
|
490
541
|
type: 'entry-removal',
|
|
491
542
|
path: this.name,
|
|
543
|
+
src: 'local',
|
|
492
544
|
name,
|
|
493
545
|
newVersion: version,
|
|
494
546
|
moveLabel
|
|
495
547
|
};
|
|
496
|
-
return event;
|
|
548
|
+
return { event, childObjId };
|
|
497
549
|
});
|
|
498
550
|
}
|
|
499
551
|
async moveChildIn(newName, child, moveLabel) {
|
|
500
|
-
child = json_utils_1.copy(child);
|
|
552
|
+
child = (0, json_utils_1.copy)(child);
|
|
501
553
|
await this.doTransition(async (state, version) => {
|
|
502
554
|
child.name = newName;
|
|
503
555
|
state.nodes[child.name] = child;
|
|
504
556
|
const event = {
|
|
505
557
|
type: 'entry-addition',
|
|
506
558
|
path: this.name,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
isFile: child.isFile,
|
|
510
|
-
isFolder: child.isFolder,
|
|
511
|
-
isLink: child.isLink
|
|
512
|
-
},
|
|
559
|
+
src: 'local',
|
|
560
|
+
entry: nodeInfoToListingEntry(child),
|
|
513
561
|
newVersion: version,
|
|
514
562
|
moveLabel
|
|
515
563
|
};
|
|
516
|
-
return event;
|
|
564
|
+
return { event, childObjId: child.objId };
|
|
517
565
|
});
|
|
518
566
|
}
|
|
519
567
|
async getFolderInThisSubTree(path, createIfMissing = false, exclusiveCreate = false) {
|
|
@@ -526,7 +574,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
526
574
|
// existing folder at this point
|
|
527
575
|
if (path.length === 1) {
|
|
528
576
|
if (exclusiveCreate) {
|
|
529
|
-
throw file_1.makeFileException(file_1.Code.alreadyExists, path[0]);
|
|
577
|
+
throw (0, file_1.makeFileException)(file_1.Code.alreadyExists, path[0]);
|
|
530
578
|
}
|
|
531
579
|
else {
|
|
532
580
|
return f;
|
|
@@ -583,10 +631,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
583
631
|
}
|
|
584
632
|
return content;
|
|
585
633
|
}
|
|
586
|
-
async delete(
|
|
587
|
-
if (remoteEvent) {
|
|
588
|
-
return super.delete(true);
|
|
589
|
-
}
|
|
634
|
+
async delete(src) {
|
|
590
635
|
const childrenNodes = await this.getAllNodes();
|
|
591
636
|
await this.storage.removeObj(this.objId);
|
|
592
637
|
this.storage.nodes.delete(this);
|
|
@@ -595,14 +640,11 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
595
640
|
const event = {
|
|
596
641
|
type: 'removed',
|
|
597
642
|
path: this.name,
|
|
598
|
-
|
|
643
|
+
src
|
|
599
644
|
};
|
|
600
645
|
this.broadcastEvent(event, true);
|
|
601
|
-
// explicitly do not wait on a result of child's delete, cause if it fails
|
|
602
|
-
// we just get traceable garbage, yet, the rest of a live/non-deleted tree
|
|
603
|
-
// stays consistent
|
|
604
646
|
for (const node of childrenNodes) {
|
|
605
|
-
node.
|
|
647
|
+
node.removeObj(src);
|
|
606
648
|
}
|
|
607
649
|
}
|
|
608
650
|
getParamsForLink() {
|
|
@@ -621,77 +663,214 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
621
663
|
};
|
|
622
664
|
return linkParams;
|
|
623
665
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
async
|
|
636
|
-
|
|
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) {
|
|
637
680
|
return;
|
|
638
681
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
+
});
|
|
643
715
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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);
|
|
742
|
+
}
|
|
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;
|
|
656
755
|
const event = {
|
|
657
|
-
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',
|
|
658
786
|
path: this.name,
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
newName: addedEntries[0],
|
|
787
|
+
src: 'sync',
|
|
788
|
+
entry: nodeInfoToListingEntry(remoteChildNode),
|
|
662
789
|
newVersion
|
|
663
790
|
};
|
|
664
|
-
|
|
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;
|
|
665
819
|
}
|
|
666
820
|
else {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
newVersion
|
|
690
|
-
};
|
|
691
|
-
this.broadcastEvent(event);
|
|
692
|
-
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (remote.isArchived) {
|
|
824
|
+
return this.diffWithArchivedRemote(isCurrentLocal);
|
|
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);
|
|
693
843
|
}
|
|
694
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
|
+
};
|
|
873
|
+
}
|
|
695
874
|
}
|
|
696
875
|
exports.FolderNode = FolderNode;
|
|
697
876
|
Object.freeze(FolderNode.prototype);
|
|
@@ -702,6 +881,32 @@ function checkFolderInfo(folderJson) {
|
|
|
702
881
|
// TODO throw if folderJson is not ok
|
|
703
882
|
return folderJson;
|
|
704
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
|
+
}
|
|
705
910
|
function addToTransitionState(state, f, key) {
|
|
706
911
|
const nodeInfo = {
|
|
707
912
|
name: f.name,
|
|
@@ -722,4 +927,195 @@ function addToTransitionState(state, f, key) {
|
|
|
722
927
|
}
|
|
723
928
|
state.nodes[nodeInfo.name] = nodeInfo;
|
|
724
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
|
+
}
|
|
725
1121
|
Object.freeze(exports);
|