core-3nweb-client-lib 0.49.8 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api-defs/files.d.ts +1 -1
- package/build/core/asmail/delivery/per-recipient-wip.js +4 -0
- package/build/core/asmail/inbox/index.js +5 -1
- package/build/core/asmail/inbox/msg-indexing/index.js +8 -9
- package/build/core/asmail/index.js +3 -3
- package/build/core/asmail/sending-params/index.d.ts +13 -14
- package/build/core/asmail/sending-params/index.js +15 -35
- package/build/core/asmail/sending-params/invitations-anon.d.ts +7 -31
- package/build/core/asmail/sending-params/invitations-anon.js +77 -127
- package/build/core/asmail/sending-params/own-params.d.ts +5 -25
- package/build/core/asmail/sending-params/own-params.js +144 -97
- package/build/core/asmail/sending-params/params-from-others.d.ts +6 -16
- package/build/core/asmail/sending-params/params-from-others.js +88 -58
- package/build/core/index.d.ts +2 -1
- package/build/core/index.js +4 -4
- package/build/core/keyring/index.js +1 -1
- package/build/core/keyring/introductory-keys/published-intro-key.js +15 -15
- package/build/core/keyring/peer-keys/index.js +6 -7
- package/build/core/storage/index.d.ts +2 -1
- package/build/core/storage/index.js +4 -3
- package/build/core/storage/system-folders/index.d.ts +1 -2
- package/build/core/storage/system-folders/index.js +18 -22
- package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
- package/build/lib-client/fs-utils/fs-sync-utils.js +21 -15
- package/build/lib-client/xsp-fs/util/file-based-json.js +1 -1
- package/build/lib-common/dataset-sync/per-device-json-array-logs.js +42 -27
- package/build/lib-common/dataset-sync/single-file.d.ts +4 -2
- package/build/lib-common/dataset-sync/single-file.js +27 -31
- package/build/lib-common/dataset-sync/utils.d.ts +16 -0
- package/build/lib-common/dataset-sync/utils.js +72 -0
- package/build/lib-common-on-node/device-fs-places.d.ts +3 -0
- package/build/lib-common-on-node/device-fs-places.js +39 -0
- package/build/tests/computer.3nweb.core/keyrings.js +2 -2
- package/build/tests/libs-for-tests/core-runner.js +2 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2017 - 2018, 2025 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2017 - 2018, 2025 - 2026 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -16,126 +16,173 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
20
|
-
const file_based_json_1 = require("../../../lib-client/xsp-fs/util/file-based-json");
|
|
19
|
+
exports.makeOwnSendingParams = makeOwnSendingParams;
|
|
21
20
|
const json_utils_1 = require("../../../lib-common/json-utils");
|
|
21
|
+
const single_file_1 = require("../../../lib-common/dataset-sync/single-file");
|
|
22
22
|
const DEFAULT_INVITE_LABEL = 'Default';
|
|
23
23
|
const DEFAULT_INVITE_MAX_MSG_SIZE = 1024 * 1024 * 1024;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class OwnSendingParams {
|
|
31
|
-
constructor(anonInvites) {
|
|
32
|
-
this.anonInvites = anonInvites;
|
|
33
|
-
this.params = new Map();
|
|
34
|
-
this.defaultParams = undefined;
|
|
35
|
-
this.getFor = async (address) => {
|
|
36
|
-
let p = this.params.get(address);
|
|
37
|
-
if (p) {
|
|
38
|
-
if (p.suggested) {
|
|
39
|
-
return p.suggested;
|
|
40
|
-
}
|
|
41
|
-
else if (p.inUse) {
|
|
42
|
-
return; // undefined, cause params are known to correspondent,
|
|
43
|
-
// and there are no params to suggest for future use.
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// XXX Or, instead, should we set defaultParams?
|
|
47
|
-
if (!this.defaultParams) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
p = {
|
|
51
|
-
address,
|
|
52
|
-
suggested: (0, json_utils_1.copy)(this.defaultParams)
|
|
53
|
-
};
|
|
54
|
-
p.suggested.timestamp = Date.now();
|
|
55
|
-
this.params.set(p.address, p);
|
|
56
|
-
await this.fileProc.order.startOrChain(() => this.persist());
|
|
57
|
-
return p.suggested;
|
|
58
|
-
};
|
|
59
|
-
this.setAsInUse = async (address, invite) => {
|
|
60
|
-
await this.fileProc.order.startOrChain(async () => {
|
|
61
|
-
const p = this.params.get(address);
|
|
62
|
-
if (!p || !p.suggested) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (p.suggested.invitation !== invite) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
p.inUse = p.suggested;
|
|
69
|
-
p.suggested = undefined;
|
|
70
|
-
await this.persist();
|
|
71
|
-
});
|
|
72
|
-
};
|
|
73
|
-
this.fileProc = new file_based_json_1.JsonFileProc(this.onFileEvent.bind(this));
|
|
74
|
-
Object.seal(this);
|
|
24
|
+
async function makeOwnSendingParams(fs, fileName, anonInvites) {
|
|
25
|
+
let params = {};
|
|
26
|
+
let defaultParams = undefined;
|
|
27
|
+
const { file: paramsFile, changeProc } = await (0, single_file_1.initSyncedFile)(fs, fileName, newFile => newFile.writeJSON(currentToFileJSON()));
|
|
28
|
+
if (!paramsFile.isNew) {
|
|
29
|
+
({ defaultParams, params } = parseFromJSON(await paramsFile.readJSON()));
|
|
75
30
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await
|
|
79
|
-
await ownParams.absorbChangesFromFile();
|
|
80
|
-
if (!ownParams.defaultParams) {
|
|
81
|
-
await ownParams.setDefaultParams();
|
|
82
|
-
}
|
|
83
|
-
return ownParams;
|
|
31
|
+
const { stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(paramsFile, changeProc, onChangeFromOtherDevices, doOnConflict);
|
|
32
|
+
if (paramsFile.isNew) {
|
|
33
|
+
await setDefaultParams();
|
|
84
34
|
}
|
|
85
|
-
async setDefaultParams() {
|
|
86
|
-
await
|
|
87
|
-
const invites =
|
|
35
|
+
async function setDefaultParams() {
|
|
36
|
+
await changeProc.startOrChain(async () => {
|
|
37
|
+
const invites = anonInvites.getAll();
|
|
88
38
|
const defaultInvite = invites.get(DEFAULT_INVITE_LABEL);
|
|
89
39
|
if (defaultInvite) {
|
|
90
|
-
|
|
40
|
+
defaultParams = {
|
|
91
41
|
timestamp: 0,
|
|
92
42
|
invitation: defaultInvite.invite
|
|
93
43
|
};
|
|
94
44
|
}
|
|
95
45
|
else {
|
|
96
|
-
const invitation = await
|
|
97
|
-
|
|
46
|
+
const invitation = await anonInvites.create(DEFAULT_INVITE_LABEL, DEFAULT_INVITE_MAX_MSG_SIZE);
|
|
47
|
+
defaultParams = {
|
|
98
48
|
timestamp: 0,
|
|
99
49
|
invitation
|
|
100
50
|
};
|
|
101
51
|
}
|
|
102
|
-
await
|
|
52
|
+
await persist();
|
|
103
53
|
});
|
|
104
54
|
}
|
|
105
|
-
|
|
55
|
+
function currentToFileJSON() {
|
|
106
56
|
return {
|
|
107
|
-
default:
|
|
108
|
-
senderSpecific:
|
|
57
|
+
default: defaultParams,
|
|
58
|
+
senderSpecific: Object.values(params)
|
|
109
59
|
};
|
|
110
60
|
}
|
|
111
|
-
async persist() {
|
|
112
|
-
await
|
|
113
|
-
|
|
114
|
-
async absorbChangesFromFile() {
|
|
115
|
-
const { json } = await this.fileProc.get(false);
|
|
116
|
-
// we may add checks to json data
|
|
117
|
-
this.params.clear();
|
|
118
|
-
json.senderSpecific.forEach(p => this.params.set(p.address, p));
|
|
119
|
-
this.defaultParams = json.default;
|
|
61
|
+
async function persist() {
|
|
62
|
+
await paramsFile.writeJSON(currentToFileJSON());
|
|
63
|
+
triggerUpload();
|
|
120
64
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
65
|
+
function parseFromJSON(json) {
|
|
66
|
+
const params = {};
|
|
67
|
+
for (const p of json.senderSpecific) {
|
|
68
|
+
params[p.address] = p;
|
|
124
69
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
70
|
+
return {
|
|
71
|
+
defaultParams: json.default,
|
|
72
|
+
params
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function onChangeFromOtherDevices(newVersion) {
|
|
76
|
+
return changeProc.startOrChain(async () => {
|
|
77
|
+
const { version, json } = await paramsFile.v.readJSON();
|
|
78
|
+
if (version === newVersion) {
|
|
79
|
+
({ defaultParams, params } = parseFromJSON(json));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function doOnConflict() {
|
|
84
|
+
return changeProc.startOrChain(async () => {
|
|
85
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
86
|
+
const { remote, state } = await paramsFile.v.sync.status(false);
|
|
87
|
+
if ((state !== 'conflicting') || !(remote === null || remote === void 0 ? void 0 : remote.latest)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const onRemote = parseFromJSON((await paramsFile.v.readJSON({ remoteVersion: remote.latest })).json);
|
|
91
|
+
let uploadLocal = false;
|
|
92
|
+
if (!onRemote.defaultParams && defaultParams) {
|
|
93
|
+
uploadLocal = true;
|
|
94
|
+
}
|
|
95
|
+
else if (onRemote.defaultParams && !defaultParams) {
|
|
96
|
+
defaultParams = onRemote.defaultParams;
|
|
97
|
+
}
|
|
98
|
+
else if (onRemote.defaultParams && defaultParams) {
|
|
99
|
+
if (onRemote.defaultParams.timestamp >= defaultParams.timestamp) {
|
|
100
|
+
defaultParams = onRemote.defaultParams;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
uploadLocal = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const [addr, p] of Object.entries(params)) {
|
|
107
|
+
const remP = onRemote.params[addr];
|
|
108
|
+
if (!remP) {
|
|
109
|
+
uploadLocal = true;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if ((0, json_utils_1.deepEqual)(p, remP)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const tsP = Math.max((_b = (_a = p === null || p === void 0 ? void 0 : p.inUse) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : 0, (_d = (_c = p === null || p === void 0 ? void 0 : p.suggested) === null || _c === void 0 ? void 0 : _c.timestamp) !== null && _d !== void 0 ? _d : 0);
|
|
116
|
+
const tsRem = Math.max((_f = (_e = remP === null || remP === void 0 ? void 0 : remP.inUse) === null || _e === void 0 ? void 0 : _e.timestamp) !== null && _f !== void 0 ? _f : 0, (_h = (_g = remP === null || remP === void 0 ? void 0 : remP.suggested) === null || _g === void 0 ? void 0 : _g.timestamp) !== null && _h !== void 0 ? _h : 0);
|
|
117
|
+
if (tsP > tsRem) {
|
|
118
|
+
uploadLocal = true;
|
|
119
|
+
}
|
|
120
|
+
else if (tsP < tsRem) {
|
|
121
|
+
params[addr] = remP;
|
|
122
|
+
}
|
|
123
|
+
else if (tsP !== 0) {
|
|
124
|
+
if (((_j = remP.inUse) === null || _j === void 0 ? void 0 : _j.timestamp) === tsP) {
|
|
125
|
+
params[addr] = remP;
|
|
126
|
+
}
|
|
127
|
+
else if (((_k = p.inUse) === null || _k === void 0 ? void 0 : _k.timestamp) === tsP) {
|
|
128
|
+
uploadLocal = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (uploadLocal) {
|
|
133
|
+
await paramsFile.writeJSON(currentToFileJSON());
|
|
134
|
+
await paramsFile.v.sync.upload({ uploadVersion: remote.latest + 1 });
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
await paramsFile.v.sync.adoptRemote({ remoteVersion: remote.latest });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function getUpdated(address) {
|
|
142
|
+
return changeProc.startOrChain(async () => {
|
|
143
|
+
let p = params[address];
|
|
144
|
+
if (p) {
|
|
145
|
+
if (p.suggested) {
|
|
146
|
+
return p.suggested;
|
|
147
|
+
}
|
|
148
|
+
else if (p.inUse) {
|
|
149
|
+
return; // undefined, cause params are known to correspondent,
|
|
150
|
+
// and there are no params to suggest for future use.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// XXX Or, instead, should we set defaultParams?
|
|
154
|
+
if (!defaultParams) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
p = {
|
|
158
|
+
address,
|
|
159
|
+
suggested: (0, json_utils_1.copy)(defaultParams)
|
|
160
|
+
};
|
|
161
|
+
p.suggested.timestamp = Date.now();
|
|
162
|
+
params[p.address] = p;
|
|
163
|
+
await persist();
|
|
164
|
+
return p.suggested;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function setAsUsed(address, invite) {
|
|
168
|
+
return changeProc.startOrChain(async () => {
|
|
169
|
+
const p = params[address];
|
|
170
|
+
if (!(p === null || p === void 0 ? void 0 : p.suggested) || (p.suggested.invitation !== invite)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
p.inUse = p.suggested;
|
|
174
|
+
p.suggested = undefined;
|
|
175
|
+
await persist();
|
|
176
|
+
});
|
|
132
177
|
}
|
|
133
|
-
async close() {
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
async function close() {
|
|
179
|
+
anonInvites.close();
|
|
180
|
+
stopFileWatching();
|
|
136
181
|
}
|
|
182
|
+
return {
|
|
183
|
+
close,
|
|
184
|
+
getUpdated,
|
|
185
|
+
setAsUsed
|
|
186
|
+
};
|
|
137
187
|
}
|
|
138
|
-
exports.OwnSendingParams = OwnSendingParams;
|
|
139
|
-
Object.freeze(OwnSendingParams.prototype);
|
|
140
|
-
Object.freeze(OwnSendingParams);
|
|
141
188
|
Object.freeze(exports);
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { SendingParamsHolder } from '../sending-params';
|
|
2
|
-
export { SendingParams } from '../msg/common';
|
|
3
|
-
type
|
|
4
|
-
type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
private readonly fileProc;
|
|
9
|
-
private constructor();
|
|
10
|
-
static makeAndInit(file: WritableFile): Promise<ParamsFromOthers>;
|
|
11
|
-
private absorbRemoteChanges;
|
|
12
|
-
protected onFileEvent(ev: FileEvent): Promise<void>;
|
|
13
|
-
getFor: ExposedFuncs['get'];
|
|
14
|
-
setFor: ExposedFuncs['set'];
|
|
15
|
-
private persist;
|
|
16
|
-
close(): Promise<void>;
|
|
17
|
-
}
|
|
2
|
+
export type { SendingParams } from '../msg/common';
|
|
3
|
+
type WritableFS = web3n.files.WritableFS;
|
|
4
|
+
export type ParamsFromOthers = Awaited<ReturnType<typeof makeParamsFromOthers>>;
|
|
5
|
+
export declare function makeParamsFromOthers(fs: WritableFS, fileName: string): Promise<SendingParamsHolder['otherSides'] & {
|
|
6
|
+
close: () => Promise<void>;
|
|
7
|
+
}>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
Copyright (C) 2017 - 2018, 2025 3NSoft Inc.
|
|
3
|
+
Copyright (C) 2017 - 2018, 2025 - 2026 3NSoft Inc.
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under
|
|
6
6
|
the terms of the GNU General Public License as published by the Free Software
|
|
@@ -16,73 +16,103 @@
|
|
|
16
16
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
exports.makeParamsFromOthers = makeParamsFromOthers;
|
|
20
|
+
const single_file_1 = require("../../../lib-common/dataset-sync/single-file");
|
|
21
|
+
async function makeParamsFromOthers(fs, fileName) {
|
|
22
|
+
const { file: paramsFile, changeProc } = await (0, single_file_1.initSyncedFile)(fs, fileName, newFile => newFile.writeJSON([]));
|
|
23
|
+
function parseParamsFromJSON(json) {
|
|
24
|
+
if (Array.isArray(json)) {
|
|
25
|
+
// older serialization form
|
|
26
|
+
const p = {};
|
|
27
|
+
for (const paramsForSending of json) {
|
|
28
|
+
const { address, timestamp, auth, invitation } = paramsForSending;
|
|
29
|
+
p[address] = { timestamp, auth, invitation };
|
|
28
30
|
}
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const p = { address };
|
|
38
|
-
copyParams(params, p);
|
|
39
|
-
this.params.set(p.address, p);
|
|
40
|
-
await this.persist();
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
this.fileProc = new file_based_json_1.JsonFileProc(this.onFileEvent.bind(this));
|
|
44
|
-
Object.seal(this);
|
|
45
|
-
}
|
|
46
|
-
static async makeAndInit(file) {
|
|
47
|
-
const paramsFromOthers = new ParamsFromOthers();
|
|
48
|
-
await paramsFromOthers.fileProc.start(file, []);
|
|
49
|
-
await paramsFromOthers.absorbRemoteChanges();
|
|
50
|
-
return paramsFromOthers;
|
|
31
|
+
return p;
|
|
32
|
+
}
|
|
33
|
+
else if (json && (typeof json === 'object')) {
|
|
34
|
+
return json;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
51
39
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
let params = parseParamsFromJSON(await paramsFile.readJSON());
|
|
41
|
+
async function onChangeFromOtherDevices(newVersion) {
|
|
42
|
+
changeProc.startOrChain(async () => {
|
|
43
|
+
const { version, json } = await paramsFile.v.readJSON();
|
|
44
|
+
if (version === newVersion) {
|
|
45
|
+
const incomingParams = parseParamsFromJSON(json);
|
|
46
|
+
mergeIncomingParams(incomingParams);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
60
49
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
function mergeIncomingParams(incomingParams) {
|
|
51
|
+
for (const [address, inP] of Object.entries(incomingParams)) {
|
|
52
|
+
const existing = params[address];
|
|
53
|
+
if (existing && (existing.timestamp >= inP.timestamp)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
params[address] = inP;
|
|
64
57
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
throw new Error(`Unexpected removal of file with invites info`);
|
|
71
|
-
default:
|
|
58
|
+
}
|
|
59
|
+
function doOnConflict() {
|
|
60
|
+
return changeProc.startOrChain(async () => {
|
|
61
|
+
const { remote, state } = await paramsFile.v.sync.status(false);
|
|
62
|
+
if ((state !== 'conflicting') || !(remote === null || remote === void 0 ? void 0 : remote.latest)) {
|
|
72
63
|
return;
|
|
64
|
+
}
|
|
65
|
+
const onRemote = parseParamsFromJSON((await paramsFile.v.readJSON({ remoteVersion: remote.latest })).json);
|
|
66
|
+
const numRemRecs = Object.keys(onRemote).length;
|
|
67
|
+
const numExistingRecs = Object.keys(params).length;
|
|
68
|
+
let paramsWhereUpdated = (numExistingRecs > numRemRecs);
|
|
69
|
+
for (const [address, inP] of Object.entries(onRemote)) {
|
|
70
|
+
const existing = params[address];
|
|
71
|
+
if (existing.timestamp < inP.timestamp) {
|
|
72
|
+
params[address] = inP;
|
|
73
|
+
paramsWhereUpdated = true;
|
|
74
|
+
}
|
|
75
|
+
else if (existing.timestamp > inP.timestamp) {
|
|
76
|
+
paramsWhereUpdated = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (paramsWhereUpdated) {
|
|
80
|
+
await paramsFile.v.sync.upload({ uploadVersion: remote.latest + 1 });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await paramsFile.v.sync.adoptRemote({ remoteVersion: remote.latest });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const { stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(paramsFile, changeProc, onChangeFromOtherDevices, doOnConflict);
|
|
88
|
+
function get(address) {
|
|
89
|
+
const p = params[address];
|
|
90
|
+
if (!p) {
|
|
91
|
+
return;
|
|
73
92
|
}
|
|
93
|
+
return copyParams(p);
|
|
74
94
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
;
|
|
96
|
+
async function set(address, params) {
|
|
97
|
+
return changeProc.startOrChain(async () => {
|
|
98
|
+
const existing = params[address];
|
|
99
|
+
if (existing && (existing.timestamp >= params.timestamp)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
params[address] = copyParams(params);
|
|
103
|
+
await paramsFile.writeJSON(params);
|
|
104
|
+
triggerUpload();
|
|
105
|
+
});
|
|
78
106
|
}
|
|
79
|
-
async close() {
|
|
80
|
-
|
|
107
|
+
async function close() {
|
|
108
|
+
stopFileWatching();
|
|
81
109
|
}
|
|
110
|
+
return {
|
|
111
|
+
get,
|
|
112
|
+
set,
|
|
113
|
+
close
|
|
114
|
+
};
|
|
82
115
|
}
|
|
83
|
-
exports.ParamsFromOthers = ParamsFromOthers;
|
|
84
|
-
Object.freeze(ParamsFromOthers.prototype);
|
|
85
|
-
Object.freeze(ParamsFromOthers);
|
|
86
116
|
/**
|
|
87
117
|
* This copies SendingParams' fields, returning a copy, which was either
|
|
88
118
|
* created, or given.
|
package/build/core/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type { makeCryptor } from 'ecma-nacl-cryptors';
|
|
|
7
7
|
type RequestedCAPs = web3n.caps.common.RequestedCAPs;
|
|
8
8
|
type W3N = web3n.caps.common.W3N;
|
|
9
9
|
type ProgressCB = web3n.startup.ProgressCB;
|
|
10
|
+
type FSItem = web3n.files.FSItem;
|
|
10
11
|
export interface CoreConf {
|
|
11
12
|
dataDir: string;
|
|
12
13
|
signUpUrl: string;
|
|
@@ -27,7 +28,7 @@ export declare class Core {
|
|
|
27
28
|
private closingProc;
|
|
28
29
|
private isInitialized;
|
|
29
30
|
private constructor();
|
|
30
|
-
static make(conf: CoreConf, makeNet: MakeNet, makeResolver: ServiceLocatorMaker, makeCryptor: makeCryptor, random: AsyncRNG): Core;
|
|
31
|
+
static make(conf: CoreConf, makeNet: MakeNet, makeResolver: ServiceLocatorMaker, sysFilesOnDevice: () => Promise<FSItem>, makeCryptor: makeCryptor, random: AsyncRNG): Core;
|
|
31
32
|
start(saveStorageKeyForAutologin?: (storageKey: Uint8Array) => void): {
|
|
32
33
|
capsForStartup: web3n.startup.W3N;
|
|
33
34
|
coreInit: Promise<string>;
|
package/build/core/index.js
CHANGED
|
@@ -31,7 +31,7 @@ const constants_1 = require("./storage/common/constants");
|
|
|
31
31
|
const config_1 = require("./asmail/config");
|
|
32
32
|
const deferred_1 = require("../lib-common/processes/deferred");
|
|
33
33
|
class Core {
|
|
34
|
-
constructor(makeNet, makeResolver, makeCryptor, random, appDirs, logger, signUpUrl) {
|
|
34
|
+
constructor(makeNet, makeResolver, makeCryptor, random, appDirs, sysFilesOnDevice, logger, signUpUrl) {
|
|
35
35
|
this.makeNet = makeNet;
|
|
36
36
|
this.makeResolver = makeResolver;
|
|
37
37
|
this.random = random;
|
|
@@ -43,14 +43,14 @@ class Core {
|
|
|
43
43
|
this.closingProc = undefined;
|
|
44
44
|
this.isInitialized = false;
|
|
45
45
|
this.cryptor = makeCryptor(this.logger.logError, this.logger.logWarning);
|
|
46
|
-
this.storages = new storage_1.Storages(this.cryptor.cryptor.sbox, this.random, this.appDirs.storagePathFor);
|
|
46
|
+
this.storages = new storage_1.Storages(this.cryptor.cryptor.sbox, this.random, this.appDirs.storagePathFor, sysFilesOnDevice);
|
|
47
47
|
this.asmail = new asmail_1.ASMail(this.cryptor.cryptor.sbox, this.random, this.makeNet, this.appDirs.inboxPathFor, this.logger);
|
|
48
48
|
Object.seal(this);
|
|
49
49
|
}
|
|
50
|
-
static make(conf, makeNet, makeResolver, makeCryptor, random) {
|
|
50
|
+
static make(conf, makeNet, makeResolver, sysFilesOnDevice, makeCryptor, random) {
|
|
51
51
|
const dirs = (0, app_files_1.appDirs)(conf.dataDir);
|
|
52
52
|
const logger = (0, log_to_file_1.makeLogger)(dirs.getUtilFS());
|
|
53
|
-
const core = new Core(makeNet, makeResolver, makeCryptor, random, dirs, logger, conf.signUpUrl);
|
|
53
|
+
const core = new Core(makeNet, makeResolver, makeCryptor, random, dirs, sysFilesOnDevice, logger, conf.signUpUrl);
|
|
54
54
|
return core;
|
|
55
55
|
}
|
|
56
56
|
start(saveStorageKeyForAutologin) {
|
|
@@ -33,7 +33,7 @@ Object.defineProperty(exports, "KEY_USE", { enumerable: true, get: function () {
|
|
|
33
33
|
const INTRO_KEYS_FOLDER = 'introductory-keys';
|
|
34
34
|
async function makeKeyrings(cryptor, random, logger, fs, localFS, getSigner, pkeyOnServer) {
|
|
35
35
|
const workLabel = cryptor_work_labels_1.cryptoWorkLabels.makeRandom('asmail');
|
|
36
|
-
const introKeysFS = await (0, fs_sync_utils_1.
|
|
36
|
+
const introKeysFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(fs, INTRO_KEYS_FOLDER);
|
|
37
37
|
const publishedKeys = await (0, published_intro_key_1.makeHolderOfPublishedIntroKey)(introKeysFS, getSigner, random, pkeyOnServer);
|
|
38
38
|
const peerKeys = await (0, peer_keys_1.makePeersKeyring)(fs, localFS, random, logger);
|
|
39
39
|
async function generateIntroKeysToSendMsg(address, introPKeyFromServer) {
|
|
@@ -26,17 +26,23 @@ const MAX_NUM_OF_PREV = 3;
|
|
|
26
26
|
const INTRO_KEY_ON_SERVER_FILE = `published-on-server.json`;
|
|
27
27
|
async function makeHolderOfPublishedIntroKey(introKeysFS, getSigner, random, pkeyOnServer) {
|
|
28
28
|
var _a;
|
|
29
|
-
const keysFile = await (0, single_file_1.initSyncedFile)(introKeysFS, INTRO_KEY_ON_SERVER_FILE, newFile => newFile.writeJSON({ previous: [] }));
|
|
30
|
-
let keys;
|
|
31
|
-
async function
|
|
32
|
-
|
|
29
|
+
const { file: keysFile, changeProc } = await (0, single_file_1.initSyncedFile)(introKeysFS, INTRO_KEY_ON_SERVER_FILE, newFile => newFile.writeJSON({ previous: [] }));
|
|
30
|
+
let keys = await keysFile.readJSON();
|
|
31
|
+
async function onChangeFromOtherDevices(newVersion) {
|
|
32
|
+
changeProc.startOrChain(async () => {
|
|
33
|
+
const { version, json } = await keysFile.v.readJSON();
|
|
34
|
+
if (version === newVersion) {
|
|
35
|
+
keys = json;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
33
38
|
}
|
|
34
|
-
|
|
35
|
-
const { changeProc, stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(keysFile, () => changeProc.startOrChain(setKeysFromFile), doOnConflict);
|
|
39
|
+
const { stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(keysFile, changeProc, onChangeFromOtherDevices, doOnConflict);
|
|
36
40
|
if (keysFile.isNew) {
|
|
37
41
|
await update();
|
|
38
42
|
}
|
|
39
|
-
|
|
43
|
+
else {
|
|
44
|
+
updateIfCurrentExpired();
|
|
45
|
+
}
|
|
40
46
|
let periodicExpiryCheck = setTimeout(updateIfCurrentExpired, UPDATE_BEFORE_EXPIRY * 1000 / 20);
|
|
41
47
|
(_a = periodicExpiryCheck.unref) === null || _a === void 0 ? void 0 : _a.call(periodicExpiryCheck);
|
|
42
48
|
async function makeNewIntroKey() {
|
|
@@ -77,8 +83,8 @@ async function makeHolderOfPublishedIntroKey(introKeysFS, getSigner, random, pke
|
|
|
77
83
|
function doOnConflict() {
|
|
78
84
|
return changeProc.startOrChain(async () => {
|
|
79
85
|
var _a, _b, _c, _d, _e, _f;
|
|
80
|
-
const { remote } = await keysFile.v.sync.status(false);
|
|
81
|
-
if (!remote) {
|
|
86
|
+
const { remote, state } = await keysFile.v.sync.status(false);
|
|
87
|
+
if ((state !== 'conflicting') || !(remote === null || remote === void 0 ? void 0 : remote.latest)) {
|
|
82
88
|
return;
|
|
83
89
|
}
|
|
84
90
|
const onRemote = (await keysFile.v.readJSON({ remoteVersion: remote.latest })).json;
|
|
@@ -98,12 +104,6 @@ async function makeHolderOfPublishedIntroKey(introKeysFS, getSigner, random, pke
|
|
|
98
104
|
await pkeyOnServer.setOnServer((_f = (_e = keys.current) === null || _e === void 0 ? void 0 : _e.certs) !== null && _f !== void 0 ? _f : null);
|
|
99
105
|
await keysFile.v.sync.upload({ uploadVersion: remote.latest + 1 });
|
|
100
106
|
}
|
|
101
|
-
})
|
|
102
|
-
.catch((exc) => {
|
|
103
|
-
if (exc.type === 'connect') {
|
|
104
|
-
// XXX need to have watcher for connection to complete upload
|
|
105
|
-
// Should this whole try-to-connect method be generic to reuse it?
|
|
106
|
-
}
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
@@ -30,9 +30,9 @@ const LOGS_DIR = 'peer-keys-logs';
|
|
|
30
30
|
const PEER_KEYS_FOLDER = 'peer-keys-db';
|
|
31
31
|
const NUM_OF_LOGS_TO_START_CUT = 200;
|
|
32
32
|
async function makePeersKeyring(keyringFS, localFS, random, logger) {
|
|
33
|
-
const logsFS = await (0, fs_sync_utils_1.
|
|
33
|
+
const logsFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(keyringFS, LOGS_DIR);
|
|
34
34
|
const logsLocalFS = await localFS.writableSubRoot(LOGS_DIR);
|
|
35
|
-
const peerKeysFS = await (0, fs_sync_utils_1.
|
|
35
|
+
const peerKeysFS = await (0, fs_sync_utils_1.getOrMakeDirOnInit)(keyringFS, PEER_KEYS_FOLDER);
|
|
36
36
|
// XXX log and saving reduction(!):
|
|
37
37
|
// - we don't need to log message counts
|
|
38
38
|
// - we don't have to immediately save changes in message counts
|
|
@@ -165,11 +165,10 @@ async function makePeersKeyring(keyringFS, localFS, random, logger) {
|
|
|
165
165
|
await logs.recordGettingCryptoFromPeer(entryToLog, peerAddr);
|
|
166
166
|
}
|
|
167
167
|
async function periodicCheckAndUploadOfData() {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// }
|
|
168
|
+
if (logs.numOfLogs() >= NUM_OF_LOGS_TO_START_CUT) {
|
|
169
|
+
const syncedIndexDbVersion = await keysDB.saveAndSync();
|
|
170
|
+
await logs.cutLogOnDatasetSyncAndUpload(syncedIndexDbVersion);
|
|
171
|
+
}
|
|
173
172
|
}
|
|
174
173
|
function millisBeforeNextRun() {
|
|
175
174
|
return 30 * 60 * 1000 + Math.floor(10 * 60 * 1000 * Math.random());
|
|
@@ -17,10 +17,11 @@ export declare class Storages implements FactoryOfFSs {
|
|
|
17
17
|
private readonly cryptor;
|
|
18
18
|
private readonly random;
|
|
19
19
|
private readonly storageDirForUser;
|
|
20
|
+
private readonly sysFilesOnDevice;
|
|
20
21
|
private synced;
|
|
21
22
|
private local;
|
|
22
23
|
private preCloseWaits;
|
|
23
|
-
constructor(cryptor: AsyncSBoxCryptor, random: AsyncRNG, storageDirForUser: StoragePathForUser);
|
|
24
|
+
constructor(cryptor: AsyncSBoxCryptor, random: AsyncRNG, storageDirForUser: StoragePathForUser, sysFilesOnDevice: () => Promise<FSItem>);
|
|
24
25
|
makeStorageCAP(appDomain: string, policy: StoragePolicy): {
|
|
25
26
|
cap: Service;
|
|
26
27
|
close: () => void;
|
|
@@ -216,10 +216,11 @@ class StorageAndFS {
|
|
|
216
216
|
Object.freeze(StorageAndFS.prototype);
|
|
217
217
|
Object.freeze(StorageAndFS);
|
|
218
218
|
class Storages {
|
|
219
|
-
constructor(cryptor, random, storageDirForUser) {
|
|
219
|
+
constructor(cryptor, random, storageDirForUser, sysFilesOnDevice) {
|
|
220
220
|
this.cryptor = cryptor;
|
|
221
221
|
this.random = random;
|
|
222
222
|
this.storageDirForUser = storageDirForUser;
|
|
223
|
+
this.sysFilesOnDevice = sysFilesOnDevice;
|
|
223
224
|
this.synced = undefined;
|
|
224
225
|
this.local = undefined;
|
|
225
226
|
this.preCloseWaits = new Set();
|
|
@@ -349,7 +350,7 @@ class Storages {
|
|
|
349
350
|
fs = await this.local.userFS();
|
|
350
351
|
}
|
|
351
352
|
else if (type === 'device') {
|
|
352
|
-
|
|
353
|
+
return await (0, system_folders_1.userFilesOnDevice)();
|
|
353
354
|
}
|
|
354
355
|
else {
|
|
355
356
|
throw new Error(`Unknown storage type ${type}`);
|
|
@@ -375,7 +376,7 @@ class Storages {
|
|
|
375
376
|
};
|
|
376
377
|
}
|
|
377
378
|
else if (type === 'device') {
|
|
378
|
-
return
|
|
379
|
+
return this.sysFilesOnDevice();
|
|
379
380
|
}
|
|
380
381
|
else {
|
|
381
382
|
throw new Error(`Unknown storage type ${type}`);
|