core-3nweb-client-lib 0.20.9 → 0.23.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/common-caps.d.ts +6 -10
- package/build/api-defs/files.d.ts +8 -7
- package/build/core/asmail/inbox/attachments/fs.d.ts +2 -2
- package/build/core/asmail/inbox/attachments/fs.js +3 -2
- package/build/core/asmail/msg/common.d.ts +2 -2
- package/build/core/asmail/msg/common.js +3 -2
- package/build/core/asmail/msg/opener.d.ts +2 -2
- package/build/core/asmail/msg/opener.js +3 -2
- package/build/core/asmail/msg/packer.d.ts +2 -2
- package/build/core/asmail/msg/packer.js +7 -6
- package/build/core/index.d.ts +2 -2
- package/build/core/index.js +24 -28
- package/build/core/storage/index.js +2 -2
- package/build/core/storage/local/obj-files.js +8 -5
- package/build/core/storage/local/obj-status.js +4 -3
- package/build/core/storage/synced/obj-status.js +4 -3
- package/build/ipc-via-protobuf/bytes.js +1 -1
- package/build/ipc-via-protobuf/file.d.ts +4 -0
- package/build/ipc-via-protobuf/file.js +17 -15
- package/build/ipc-via-protobuf/fs.js +1 -1
- package/build/lib-client/3nstorage/xsp-fs/attrs.d.ts +28 -0
- package/build/lib-client/3nstorage/xsp-fs/attrs.js +337 -0
- package/build/lib-client/3nstorage/xsp-fs/common.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/common.js +3 -2
- package/build/lib-client/3nstorage/xsp-fs/file-node.d.ts +31 -18
- package/build/lib-client/3nstorage/xsp-fs/file-node.js +130 -118
- package/build/lib-client/3nstorage/xsp-fs/file.d.ts +0 -1
- package/build/lib-client/3nstorage/xsp-fs/file.js +14 -49
- package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.d.ts +1 -1
- package/build/lib-client/3nstorage/xsp-fs/folder-node-serialization.js +95 -91
- package/build/lib-client/3nstorage/xsp-fs/folder-node.d.ts +19 -24
- package/build/lib-client/3nstorage/xsp-fs/folder-node.js +133 -190
- package/build/lib-client/3nstorage/xsp-fs/fs.d.ts +3 -4
- package/build/lib-client/3nstorage/xsp-fs/fs.js +18 -21
- package/build/lib-client/3nstorage/xsp-fs/link-node.d.ts +13 -8
- package/build/lib-client/3nstorage/xsp-fs/link-node.js +46 -38
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.d.ts +17 -35
- package/build/lib-client/3nstorage/xsp-fs/node-in-fs.js +34 -127
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.d.ts +57 -0
- package/build/lib-client/3nstorage/xsp-fs/node-persistence.js +182 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.d.ts +3 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v1.js +87 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.d.ts +6 -0
- package/build/lib-client/3nstorage/xsp-fs/xsp-payload-v2.js +1022 -0
- package/build/lib-client/cryptor/cryptor-wasm.js +1 -1
- package/build/lib-client/cryptor/cryptor.wasm +0 -0
- package/build/lib-client/local-files/device-fs.js +7 -3
- package/build/lib-client/objs-on-disk/file-writing-proc.js +3 -2
- package/build/lib-client/objs-on-disk/obj-on-disk.js +8 -7
- package/build/lib-common/big-endian.d.ts +0 -24
- package/build/lib-common/big-endian.js +16 -78
- package/build/lib-common/exceptions/file.js +6 -2
- package/build/lib-common/obj-streaming/sink-utils.d.ts +0 -4
- package/build/lib-common/obj-streaming/sink-utils.js +4 -70
- package/build/lib-common/objs-on-disk/file-layout.js +2 -1
- package/build/lib-common/objs-on-disk/obj-file.js +2 -2
- package/build/lib-common/objs-on-disk/utils.js +2 -1
- package/build/lib-common/objs-on-disk/v1-obj-file-format.js +2 -1
- package/package.json +2 -2
- package/build/lib-client/files/file-attrs.d.ts +0 -76
- package/build/lib-client/files/file-attrs.js +0 -549
- package/build/lib-client/files/file-layout.d.ts +0 -56
- package/build/lib-client/files/file-layout.js +0 -456
- package/build/lib-client/files/file-sink.d.ts +0 -33
- package/build/lib-client/files/file-sink.js +0 -173
- package/build/lib-client/files/file-source.d.ts +0 -19
- package/build/lib-client/files/file-source.js +0 -115
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2015 - 2020 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2015 - 2020, 2022 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
See the GNU General Public License for more details.
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU General Public License along with
|
|
16
|
-
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
17
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
19
|
exports.FolderNode = void 0;
|
|
19
20
|
/**
|
|
@@ -31,7 +32,8 @@ const json_utils_1 = require("../../../lib-common/json-utils");
|
|
|
31
32
|
const xsp_files_1 = require("xsp-files");
|
|
32
33
|
const random = require("../../../lib-common/random-node");
|
|
33
34
|
const folder_node_serialization_1 = require("./folder-node-serialization");
|
|
34
|
-
const
|
|
35
|
+
const attrs_1 = require("./attrs");
|
|
36
|
+
const node_persistence_1 = require("./node-persistence");
|
|
35
37
|
function jsonToInfoAndAttrs(json) {
|
|
36
38
|
const folderInfo = {
|
|
37
39
|
nodes: json_utils_1.copy(json.nodes)
|
|
@@ -40,50 +42,30 @@ function jsonToInfoAndAttrs(json) {
|
|
|
40
42
|
node.key = buffer_utils_1.base64.open(node.key);
|
|
41
43
|
}
|
|
42
44
|
checkFolderInfo(folderInfo);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return { attrs, folderInfo };
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
return { folderInfo };
|
|
49
|
-
}
|
|
45
|
+
const attrs = new attrs_1.CommonAttrs(json.ctime, json.ctime);
|
|
46
|
+
return { attrs, folderInfo };
|
|
50
47
|
}
|
|
51
|
-
class
|
|
48
|
+
class FolderPersistance extends node_persistence_1.NodePersistance {
|
|
52
49
|
constructor(zNonce, key, cryptor) {
|
|
53
50
|
super(zNonce, key, cryptor);
|
|
54
51
|
Object.seal(this);
|
|
55
52
|
}
|
|
56
|
-
async
|
|
57
|
-
|
|
53
|
+
async write(folderInfo, version, attrs, xattrs) {
|
|
54
|
+
const bytes = folder_node_serialization_1.serializeFolderInfo(folderInfo);
|
|
55
|
+
return this.writeWhole(bytes, version, attrs, xattrs);
|
|
58
56
|
}
|
|
59
|
-
async
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (attrs) {
|
|
64
|
-
return {
|
|
65
|
-
folderInfo,
|
|
66
|
-
attrs: file_attrs_1.AttrsHolder.fromBytesReadonly(attrs)
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
return { folderInfo };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
throw error_1.errWithCause(err, `Cannot open folder object`);
|
|
75
|
-
}
|
|
57
|
+
async read(src) {
|
|
58
|
+
const { content, xattrs, attrs } = await this.readAll(src);
|
|
59
|
+
const folderInfo = folder_node_serialization_1.parseFolderInfo(content);
|
|
60
|
+
return { folderInfo, xattrs, attrs: attrs_1.CommonAttrs.fromAttrs(attrs) };
|
|
76
61
|
}
|
|
77
62
|
}
|
|
78
|
-
Object.freeze(
|
|
79
|
-
Object.freeze(
|
|
63
|
+
Object.freeze(FolderPersistance.prototype);
|
|
64
|
+
Object.freeze(FolderPersistance);
|
|
80
65
|
class FolderNode extends node_in_fs_1.NodeInFS {
|
|
81
66
|
constructor(storage, name, objId, zNonce, version, parentId, key, setNewAttrs) {
|
|
82
67
|
super(storage, 'folder', name, objId, version, parentId);
|
|
83
68
|
this.currentState = { nodes: {} };
|
|
84
|
-
this.transitionState = undefined;
|
|
85
|
-
this.transitionVersion = undefined;
|
|
86
|
-
this.transitionSaved = false;
|
|
87
69
|
if (!name && (objId || parentId)) {
|
|
88
70
|
throw new Error("Root folder must " +
|
|
89
71
|
"have both objId and parent as nulls.");
|
|
@@ -97,9 +79,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
97
79
|
}
|
|
98
80
|
zNonce = xsp_files_1.idToHeaderNonce(objId);
|
|
99
81
|
}
|
|
100
|
-
this.crypto = new
|
|
82
|
+
this.crypto = new FolderPersistance(zNonce, key, storage.cryptor);
|
|
101
83
|
if (setNewAttrs) {
|
|
102
|
-
this.attrs =
|
|
84
|
+
this.attrs = attrs_1.CommonAttrs.makeForTimeNow();
|
|
103
85
|
}
|
|
104
86
|
Object.seal(this);
|
|
105
87
|
}
|
|
@@ -107,7 +89,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
107
89
|
const zNonce = await random.bytes(xsp_files_1.NONCE_LENGTH);
|
|
108
90
|
const rf = new FolderNode(storage, undefined, null, zNonce, 0, undefined, key, true);
|
|
109
91
|
rf.storage.nodes.set(rf);
|
|
110
|
-
await rf.saveFirstVersion();
|
|
92
|
+
await rf.saveFirstVersion(undefined);
|
|
111
93
|
return rf;
|
|
112
94
|
}
|
|
113
95
|
static async rootFromObjBytes(storage, name, objId, src, key) {
|
|
@@ -122,9 +104,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
122
104
|
}
|
|
123
105
|
static async readNodeFromObjBytes(storage, name, objId, zNonce, src, key) {
|
|
124
106
|
const rf = new FolderNode(storage, name, objId, zNonce, src.version, undefined, key, false);
|
|
125
|
-
const { folderInfo, attrs } = await rf.crypto.
|
|
107
|
+
const { folderInfo, attrs, xattrs } = await rf.crypto.read(src);
|
|
126
108
|
rf.currentState = folderInfo;
|
|
127
|
-
rf.
|
|
109
|
+
rf.setUpdatedParams(src.version, attrs, xattrs);
|
|
128
110
|
return rf;
|
|
129
111
|
}
|
|
130
112
|
static async rootFromLinkParams(storage, params) {
|
|
@@ -145,9 +127,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
145
127
|
}
|
|
146
128
|
static rootFromJSON(storage, name, folderJson) {
|
|
147
129
|
const rf = new FolderNode(storage, name, 'readonly-root', EMPTY_ARR, 0, undefined, undefined, false);
|
|
148
|
-
const { folderInfo, attrs } = jsonToInfoAndAttrs(folderJson);
|
|
130
|
+
const { folderInfo, attrs, xattrs } = jsonToInfoAndAttrs(folderJson);
|
|
149
131
|
rf.currentState = folderInfo;
|
|
150
|
-
rf.
|
|
132
|
+
rf.setUpdatedParams(0, attrs, xattrs);
|
|
151
133
|
return rf;
|
|
152
134
|
}
|
|
153
135
|
list() {
|
|
@@ -267,15 +249,15 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
267
249
|
return this.getNode('link', name, undefOnMissing);
|
|
268
250
|
}
|
|
269
251
|
async fixMissingChildAndThrow(exc, childInfo) {
|
|
270
|
-
await this.doTransition(
|
|
271
|
-
delete
|
|
252
|
+
await this.doTransition(async (state, version) => {
|
|
253
|
+
delete state.nodes[childInfo.name];
|
|
272
254
|
const event = {
|
|
273
255
|
type: 'entry-removal',
|
|
274
256
|
path: this.name,
|
|
275
257
|
name: childInfo.name,
|
|
276
|
-
newVersion:
|
|
258
|
+
newVersion: version
|
|
277
259
|
};
|
|
278
|
-
|
|
260
|
+
return event;
|
|
279
261
|
}).catch(noop);
|
|
280
262
|
const fileExc = file_1.makeFileException(file_1.Code.notFound, childInfo.name, exc);
|
|
281
263
|
fileExc.inconsistentStateOfFS = true;
|
|
@@ -283,117 +265,59 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
283
265
|
}
|
|
284
266
|
/**
|
|
285
267
|
* This method prepares a transition state, runs given action, and completes
|
|
286
|
-
* transition to a new version.
|
|
287
|
-
* action returns (promise is unwrapped).
|
|
268
|
+
* transition to a new version.
|
|
288
269
|
* Note that if there is already an ongoing change, this transition will
|
|
289
270
|
* wait. Such behaviour is generally needed for folder as different processes
|
|
290
271
|
* may be sharing same elements in a file tree. In contrast, file
|
|
291
272
|
* operations should more often follow a throw instead wait approach.
|
|
292
|
-
* @param
|
|
293
|
-
* while false value indicates that saving will be done within a given action
|
|
294
|
-
* @param action is a function that is run when transition is started
|
|
273
|
+
* @param change is a function that is run when transition is started
|
|
295
274
|
*/
|
|
296
|
-
doTransition(
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (autoSave) {
|
|
314
|
-
await this.saveTransitionState();
|
|
315
|
-
}
|
|
316
|
-
// complete transition
|
|
317
|
-
if (!this.transitionSaved) {
|
|
318
|
-
throw new Error(`Transition state has not been saved`);
|
|
319
|
-
}
|
|
320
|
-
this.currentState = this.transitionState;
|
|
321
|
-
this.setCurrentVersion(this.transitionVersion);
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
|
-
finally {
|
|
325
|
-
// cleanup after both completion and fail
|
|
326
|
-
this.clearTransitionState();
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
clearTransitionState() {
|
|
331
|
-
this.transitionState = undefined;
|
|
332
|
-
this.transitionVersion = undefined;
|
|
333
|
-
this.transitionSaved = false;
|
|
334
|
-
}
|
|
335
|
-
async saveTransitionState() {
|
|
336
|
-
if (!this.transitionState || !this.transitionVersion) {
|
|
337
|
-
throw new Error(`Transition is not set correctly`);
|
|
338
|
-
}
|
|
339
|
-
if (this.transitionSaved) {
|
|
340
|
-
throw new Error(`Transition has already been saved.`);
|
|
341
|
-
}
|
|
342
|
-
const attrs = this.attrs.modifiableCopy();
|
|
343
|
-
const encSub = await this.crypto.pack(this.transitionState, this.transitionVersion, attrs);
|
|
344
|
-
await this.storage.saveObj(this.objId, this.transitionVersion, encSub);
|
|
345
|
-
this.transitionSaved = true;
|
|
346
|
-
attrs.setReadonly();
|
|
275
|
+
async doTransition(change) {
|
|
276
|
+
await this.doChange(true, () => this.performTransition(change));
|
|
277
|
+
}
|
|
278
|
+
async performTransition(change) {
|
|
279
|
+
// start transition and prepare transition state
|
|
280
|
+
// Note on copy: byte arrays are not cloned
|
|
281
|
+
const state = json_utils_1.copy(this.currentState);
|
|
282
|
+
const version = this.version + 1;
|
|
283
|
+
const attrs = this.attrs.copy();
|
|
284
|
+
// do action within transition state
|
|
285
|
+
const event = await change(state, version);
|
|
286
|
+
// save transition state
|
|
287
|
+
const encSub = await this.crypto.write(state, version, attrs, this.xattrs);
|
|
288
|
+
await this.storage.saveObj(this.objId, version, encSub);
|
|
289
|
+
// complete transition
|
|
290
|
+
this.currentState = state;
|
|
291
|
+
this.setCurrentVersion(version);
|
|
347
292
|
this.attrs = attrs;
|
|
348
|
-
|
|
349
|
-
addToTransitionState(f, key) {
|
|
350
|
-
const nodeInfo = {
|
|
351
|
-
name: f.name,
|
|
352
|
-
objId: f.objId,
|
|
353
|
-
key
|
|
354
|
-
};
|
|
355
|
-
if (f.type === 'folder') {
|
|
356
|
-
nodeInfo.isFolder = true;
|
|
357
|
-
}
|
|
358
|
-
else if (f.type === 'file') {
|
|
359
|
-
nodeInfo.isFile = true;
|
|
360
|
-
}
|
|
361
|
-
else if (f.type === 'link') {
|
|
362
|
-
nodeInfo.isLink = true;
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
throw new Error(`Unknown type of file system entity: ${f.type}`);
|
|
366
|
-
}
|
|
367
|
-
this.transitionState.nodes[nodeInfo.name] = nodeInfo;
|
|
293
|
+
this.broadcastEvent(event);
|
|
368
294
|
}
|
|
369
295
|
/**
|
|
370
296
|
* This function only creates folder node, but it doesn't insert it anywhere.
|
|
371
297
|
* @param name
|
|
372
298
|
*/
|
|
373
|
-
async makeAndSaveNewChildFolderNode(name) {
|
|
299
|
+
async makeAndSaveNewChildFolderNode(name, changes) {
|
|
374
300
|
const key = await random.bytes(xsp_files_1.KEY_LENGTH);
|
|
375
301
|
const childObjId = await this.storage.generateNewObjId();
|
|
376
302
|
const node = new FolderNode(this.storage, name, childObjId, undefined, 0, this.objId, key, true);
|
|
377
|
-
await node.saveFirstVersion().catch((exc) => {
|
|
303
|
+
await node.saveFirstVersion(changes).catch((exc) => {
|
|
378
304
|
if (!exc.objExists) {
|
|
379
305
|
throw exc;
|
|
380
306
|
}
|
|
381
307
|
// call this method recursively, if obj id is already used in storage
|
|
382
|
-
return this.makeAndSaveNewChildFolderNode(name);
|
|
308
|
+
return this.makeAndSaveNewChildFolderNode(name, changes);
|
|
383
309
|
});
|
|
384
310
|
return { node, key };
|
|
385
311
|
}
|
|
386
|
-
async saveFirstVersion() {
|
|
312
|
+
async saveFirstVersion(changes) {
|
|
387
313
|
await this.doChange(false, async () => {
|
|
388
314
|
if (this.version > 0) {
|
|
389
315
|
throw new Error(`Can call this function only for zeroth version, not ${this.version}`);
|
|
390
316
|
}
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
attrs.setReadonly();
|
|
396
|
-
this.attrs = attrs;
|
|
317
|
+
const { attrs, xattrs, newVersion } = super.getParamsForUpdate(changes);
|
|
318
|
+
const encSub = await this.crypto.write(this.currentState, newVersion, attrs, xattrs);
|
|
319
|
+
await this.storage.saveObj(this.objId, newVersion, encSub);
|
|
320
|
+
super.setUpdatedParams(newVersion, attrs, xattrs);
|
|
397
321
|
});
|
|
398
322
|
}
|
|
399
323
|
/**
|
|
@@ -420,7 +344,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
420
344
|
async makeAndSaveNewChildLinkNode(name, params) {
|
|
421
345
|
const key = await random.bytes(xsp_files_1.KEY_LENGTH);
|
|
422
346
|
const node = await link_node_1.LinkNode.makeForNew(this.storage, this.objId, name, key);
|
|
423
|
-
await node.
|
|
347
|
+
await node.save(params).catch((exc) => {
|
|
424
348
|
if (!exc.objExists) {
|
|
425
349
|
throw exc;
|
|
426
350
|
}
|
|
@@ -429,19 +353,17 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
429
353
|
});
|
|
430
354
|
return { node, key };
|
|
431
355
|
}
|
|
432
|
-
create(type, name, exclusive, linkParams) {
|
|
433
|
-
return this.
|
|
356
|
+
create(type, name, exclusive, changes, linkParams) {
|
|
357
|
+
return this.doChange(true, async () => {
|
|
434
358
|
// do check for concurrent creation of a node
|
|
435
359
|
if (this.getNodeInfo(name, true)) {
|
|
436
360
|
if (exclusive) {
|
|
437
361
|
throw file_1.makeFileException(file_1.Code.alreadyExists, name);
|
|
438
362
|
}
|
|
439
363
|
else if (type === 'folder') {
|
|
440
|
-
this.clearTransitionState();
|
|
441
364
|
return (await this.getNode('folder', name));
|
|
442
365
|
}
|
|
443
366
|
else if (type === 'file') {
|
|
444
|
-
this.clearTransitionState();
|
|
445
367
|
return (await this.getNode('file', name));
|
|
446
368
|
}
|
|
447
369
|
else if (type === 'link') {
|
|
@@ -451,62 +373,63 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
451
373
|
throw new Error(`Unknown type of node: ${type}`);
|
|
452
374
|
}
|
|
453
375
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
(
|
|
459
|
-
|
|
460
|
-
else if (type === 'folder') {
|
|
461
|
-
({ node, key } = await this.makeAndSaveNewChildFolderNode(name));
|
|
462
|
-
}
|
|
463
|
-
else if (type === 'link') {
|
|
464
|
-
({ node, key } = await this.makeAndSaveNewChildLinkNode(name, linkParams));
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
throw new Error(`Unknown type of node: ${type}`);
|
|
468
|
-
}
|
|
469
|
-
this.addToTransitionState(node, key);
|
|
470
|
-
await this.saveTransitionState(); // manual save
|
|
471
|
-
this.storage.nodes.set(node);
|
|
472
|
-
const event = {
|
|
473
|
-
type: 'entry-addition',
|
|
474
|
-
path: this.name,
|
|
475
|
-
newVersion: this.transitionVersion,
|
|
476
|
-
entry: {
|
|
477
|
-
name: node.name,
|
|
478
|
-
isFile: (node.type === 'file'),
|
|
479
|
-
isFolder: (node.type === 'folder'),
|
|
480
|
-
isLink: (node.type === 'link')
|
|
376
|
+
let node = undefined;
|
|
377
|
+
await this.performTransition(async (state, version) => {
|
|
378
|
+
// create new node
|
|
379
|
+
let key;
|
|
380
|
+
if (type === 'file') {
|
|
381
|
+
({ node, key } = await this.makeAndSaveNewChildFileNode(name));
|
|
481
382
|
}
|
|
482
|
-
|
|
483
|
-
|
|
383
|
+
else if (type === 'folder') {
|
|
384
|
+
({ node, key } = await this.makeAndSaveNewChildFolderNode(name, changes));
|
|
385
|
+
}
|
|
386
|
+
else if (type === 'link') {
|
|
387
|
+
({ node, key } = await this.makeAndSaveNewChildLinkNode(name, linkParams));
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
throw new Error(`Unknown type of node: ${type}`);
|
|
391
|
+
}
|
|
392
|
+
addToTransitionState(state, node, key);
|
|
393
|
+
this.storage.nodes.set(node);
|
|
394
|
+
const event = {
|
|
395
|
+
type: 'entry-addition',
|
|
396
|
+
path: this.name,
|
|
397
|
+
newVersion: version,
|
|
398
|
+
entry: {
|
|
399
|
+
name: node.name,
|
|
400
|
+
isFile: (node.type === 'file'),
|
|
401
|
+
isFolder: (node.type === 'folder'),
|
|
402
|
+
isLink: (node.type === 'link')
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
return event;
|
|
406
|
+
});
|
|
484
407
|
return node;
|
|
485
408
|
});
|
|
486
409
|
}
|
|
487
|
-
createFolder(name, exclusive) {
|
|
488
|
-
return this.create('folder', name, exclusive);
|
|
410
|
+
createFolder(name, exclusive, changes) {
|
|
411
|
+
return this.create('folder', name, exclusive, changes);
|
|
489
412
|
}
|
|
490
413
|
createFile(name, exclusive) {
|
|
491
414
|
return this.create('file', name, exclusive);
|
|
492
415
|
}
|
|
493
416
|
async createLink(name, params) {
|
|
494
|
-
await this.create('link', name, true, params);
|
|
417
|
+
await this.create('link', name, true, undefined, params);
|
|
495
418
|
}
|
|
496
419
|
async removeChild(f) {
|
|
497
|
-
await this.doTransition(
|
|
498
|
-
const childJSON =
|
|
420
|
+
await this.doTransition(async (state, version) => {
|
|
421
|
+
const childJSON = state.nodes[f.name];
|
|
499
422
|
if (!childJSON || (childJSON.objId !== f.objId)) {
|
|
500
423
|
throw new Error(`Not a child given: name==${f.name}, objId==${f.objId}, parentId==${f.parentId}, this folder objId==${this.objId}`);
|
|
501
424
|
}
|
|
502
|
-
delete
|
|
425
|
+
delete state.nodes[f.name];
|
|
503
426
|
const event = {
|
|
504
427
|
type: 'entry-removal',
|
|
505
428
|
path: this.name,
|
|
506
429
|
name: f.name,
|
|
507
|
-
newVersion:
|
|
430
|
+
newVersion: version
|
|
508
431
|
};
|
|
509
|
-
|
|
432
|
+
return event;
|
|
510
433
|
});
|
|
511
434
|
// explicitly do not wait on a result of child's delete, cause if it fails
|
|
512
435
|
// we just get traceable garbage, yet, the rest of a live/non-deleted tree
|
|
@@ -514,10 +437,10 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
514
437
|
f.localDelete();
|
|
515
438
|
}
|
|
516
439
|
changeChildName(initName, newName) {
|
|
517
|
-
return this.doTransition(
|
|
518
|
-
const child =
|
|
519
|
-
delete
|
|
520
|
-
|
|
440
|
+
return this.doTransition(async (state, version) => {
|
|
441
|
+
const child = state.nodes[initName];
|
|
442
|
+
delete state.nodes[child.name];
|
|
443
|
+
state.nodes[newName] = child;
|
|
521
444
|
child.name = newName;
|
|
522
445
|
const childNode = this.storage.nodes.get(child.objId);
|
|
523
446
|
if (childNode) {
|
|
@@ -528,9 +451,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
528
451
|
path: this.name,
|
|
529
452
|
newName,
|
|
530
453
|
oldName: initName,
|
|
531
|
-
newVersion:
|
|
454
|
+
newVersion: version
|
|
532
455
|
};
|
|
533
|
-
|
|
456
|
+
return event;
|
|
534
457
|
});
|
|
535
458
|
}
|
|
536
459
|
async moveChildTo(childName, dst, nameInDst) {
|
|
@@ -549,22 +472,22 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
549
472
|
]);
|
|
550
473
|
}
|
|
551
474
|
async moveChildOut(name) {
|
|
552
|
-
await this.doTransition(
|
|
553
|
-
delete
|
|
475
|
+
await this.doTransition(async (state, version) => {
|
|
476
|
+
delete state.nodes[name];
|
|
554
477
|
const event = {
|
|
555
478
|
type: 'entry-removal',
|
|
556
479
|
path: this.name,
|
|
557
480
|
name,
|
|
558
|
-
newVersion:
|
|
481
|
+
newVersion: version
|
|
559
482
|
};
|
|
560
|
-
|
|
483
|
+
return event;
|
|
561
484
|
});
|
|
562
485
|
}
|
|
563
486
|
async moveChildIn(newName, child) {
|
|
564
487
|
child = json_utils_1.copy(child);
|
|
565
|
-
await this.doTransition(
|
|
488
|
+
await this.doTransition(async (state, version) => {
|
|
566
489
|
child.name = newName;
|
|
567
|
-
|
|
490
|
+
state.nodes[child.name] = child;
|
|
568
491
|
const event = {
|
|
569
492
|
type: 'entry-addition',
|
|
570
493
|
path: this.name,
|
|
@@ -574,9 +497,9 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
574
497
|
isFolder: child.isFolder,
|
|
575
498
|
isLink: child.isLink
|
|
576
499
|
},
|
|
577
|
-
newVersion:
|
|
500
|
+
newVersion: version
|
|
578
501
|
};
|
|
579
|
-
|
|
502
|
+
return event;
|
|
580
503
|
});
|
|
581
504
|
}
|
|
582
505
|
async getFolderInThisSubTree(path, createIfMissing = false, exclusiveCreate = false) {
|
|
@@ -604,7 +527,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
604
527
|
throw err;
|
|
605
528
|
}
|
|
606
529
|
try {
|
|
607
|
-
f = await this.createFolder(path[0], exclusiveCreate);
|
|
530
|
+
f = await this.createFolder(path[0], exclusiveCreate, undefined);
|
|
608
531
|
}
|
|
609
532
|
catch (exc) {
|
|
610
533
|
if (exc.alreadyExists && !exclusiveCreate) {
|
|
@@ -704,7 +627,7 @@ class FolderNode extends node_in_fs_1.NodeInFS {
|
|
|
704
627
|
if (newVersion <= this.version) {
|
|
705
628
|
return;
|
|
706
629
|
}
|
|
707
|
-
const { folderInfo, attrs } = await this.crypto.
|
|
630
|
+
const { folderInfo, attrs } = await this.crypto.read(src);
|
|
708
631
|
const initState = this.currentState;
|
|
709
632
|
this.currentState = checkFolderInfo(folderInfo);
|
|
710
633
|
this.setCurrentVersion(newVersion);
|
|
@@ -765,4 +688,24 @@ function checkFolderInfo(folderJson) {
|
|
|
765
688
|
// TODO throw if folderJson is not ok
|
|
766
689
|
return folderJson;
|
|
767
690
|
}
|
|
691
|
+
function addToTransitionState(state, f, key) {
|
|
692
|
+
const nodeInfo = {
|
|
693
|
+
name: f.name,
|
|
694
|
+
objId: f.objId,
|
|
695
|
+
key
|
|
696
|
+
};
|
|
697
|
+
if (f.type === 'folder') {
|
|
698
|
+
nodeInfo.isFolder = true;
|
|
699
|
+
}
|
|
700
|
+
else if (f.type === 'file') {
|
|
701
|
+
nodeInfo.isFile = true;
|
|
702
|
+
}
|
|
703
|
+
else if (f.type === 'link') {
|
|
704
|
+
nodeInfo.isLink = true;
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
throw new Error(`Unknown type of file system entity: ${f.type}`);
|
|
708
|
+
}
|
|
709
|
+
state.nodes[nodeInfo.name] = nodeInfo;
|
|
710
|
+
}
|
|
768
711
|
Object.freeze(exports);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { FolderNode, FolderLinkParams,
|
|
1
|
+
import { FolderNode, FolderLinkParams, FolderInJSON } from './folder-node';
|
|
2
2
|
import { FileNode } from './file-node';
|
|
3
3
|
import { Storage } from './common';
|
|
4
4
|
import { LinkParameters } from '../../files';
|
|
5
5
|
import { NodeInFS } from './node-in-fs';
|
|
6
|
-
import { EntityAttrs } from '../../files/file-attrs';
|
|
7
6
|
declare type Stats = web3n.files.Stats;
|
|
8
7
|
declare type FS = web3n.files.FS;
|
|
9
8
|
declare type WritableFS = web3n.files.WritableFS;
|
|
@@ -46,7 +45,7 @@ export declare class XspFS implements WritableFS {
|
|
|
46
45
|
* @param key is a file key of a root object
|
|
47
46
|
*/
|
|
48
47
|
static fromExistingRoot(storage: Storage, key: Uint8Array): Promise<WritableFS>;
|
|
49
|
-
static fromASMailMsgRootFromJSON(storage: Storage, folderJson:
|
|
48
|
+
static fromASMailMsgRootFromJSON(storage: Storage, folderJson: FolderInJSON, rootName?: string): ReadonlyFS;
|
|
50
49
|
/**
|
|
51
50
|
* Note that this method doesn't close storage.
|
|
52
51
|
*/
|
|
@@ -97,7 +96,7 @@ declare class V implements WritableFSVersionedAPI {
|
|
|
97
96
|
root: FolderNode;
|
|
98
97
|
constructor();
|
|
99
98
|
getOrCreateFile(path: string, flags: FileFlags): Promise<FileNode>;
|
|
100
|
-
get(path: string): Promise<NodeInFS<any
|
|
99
|
+
get(path: string): Promise<NodeInFS<any>>;
|
|
101
100
|
updateXAttrs(path: string, changes: XAttrsChanges): Promise<number>;
|
|
102
101
|
getXAttr(path: string, xaName: string): Promise<{
|
|
103
102
|
attr: any;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2015 - 2020 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2015 - 2020, 2022 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -36,7 +36,8 @@ function splitPathIntoParts(path) {
|
|
|
36
36
|
}
|
|
37
37
|
function setExcPath(path) {
|
|
38
38
|
return (exc) => {
|
|
39
|
-
if (exc.notFound || exc.notDirectory || exc.alreadyExists
|
|
39
|
+
if (exc.notFound || exc.notDirectory || exc.alreadyExists
|
|
40
|
+
|| exc.notFile) {
|
|
40
41
|
exc.path = path;
|
|
41
42
|
}
|
|
42
43
|
throw exc;
|
|
@@ -189,27 +190,25 @@ class XspFS {
|
|
|
189
190
|
}
|
|
190
191
|
async stat(path) {
|
|
191
192
|
const node = await this.v.get(path);
|
|
193
|
+
const attrs = node.getAttrs();
|
|
194
|
+
const stats = {
|
|
195
|
+
ctime: new Date(attrs.ctime),
|
|
196
|
+
mtime: new Date(attrs.mtime),
|
|
197
|
+
version: node.version,
|
|
198
|
+
writable: this.writable,
|
|
199
|
+
};
|
|
192
200
|
if (node.type === 'file') {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
writable: this.writable,
|
|
197
|
-
size: await src.getSize(),
|
|
198
|
-
version
|
|
199
|
-
};
|
|
201
|
+
stats.size = node.size;
|
|
202
|
+
stats.isFile = true;
|
|
203
|
+
return stats;
|
|
200
204
|
}
|
|
201
205
|
else if (node.type === 'folder') {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
writable: this.writable,
|
|
205
|
-
version: node.version
|
|
206
|
-
};
|
|
206
|
+
stats.isFolder = true;
|
|
207
|
+
return stats;
|
|
207
208
|
}
|
|
208
209
|
else if (node.type === 'link') {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
isLink: true
|
|
212
|
-
};
|
|
210
|
+
stats.isLink = true;
|
|
211
|
+
return stats;
|
|
213
212
|
}
|
|
214
213
|
else {
|
|
215
214
|
throw new Error(`Unknown type of fs node`);
|
|
@@ -515,9 +514,7 @@ class V {
|
|
|
515
514
|
}
|
|
516
515
|
async readBytes(path, start, end) {
|
|
517
516
|
const file = await this.getOrCreateFile(path, {});
|
|
518
|
-
|
|
519
|
-
const bytes = await file_2.readBytesFrom(src, start, end);
|
|
520
|
-
return { bytes, version };
|
|
517
|
+
return await file.readBytes(start, end);
|
|
521
518
|
}
|
|
522
519
|
writeTxtFile(path, txt, flags = WRITE_NONEXCL_FLAGS) {
|
|
523
520
|
const bytes = buffer_utils_1.utf8.pack(txt);
|