core-3nweb-client-lib 0.49.7 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/build/api-defs/asmail.d.ts +1 -0
  2. package/build/api-defs/files.d.ts +1 -1
  3. package/build/core/asmail/delivery/per-recipient-wip.js +4 -0
  4. package/build/core/asmail/inbox/index.d.ts +8 -31
  5. package/build/core/asmail/inbox/index.js +119 -164
  6. package/build/core/asmail/inbox/msg-indexing/index.js +8 -9
  7. package/build/core/asmail/inbox/msg-on-disk.d.ts +0 -5
  8. package/build/core/asmail/inbox/msg-on-disk.js +1 -22
  9. package/build/core/asmail/index.js +4 -4
  10. package/build/core/asmail/sending-params/index.d.ts +13 -14
  11. package/build/core/asmail/sending-params/index.js +15 -35
  12. package/build/core/asmail/sending-params/invitations-anon.d.ts +7 -31
  13. package/build/core/asmail/sending-params/invitations-anon.js +77 -127
  14. package/build/core/asmail/sending-params/own-params.d.ts +5 -25
  15. package/build/core/asmail/sending-params/own-params.js +144 -97
  16. package/build/core/asmail/sending-params/params-from-others.d.ts +6 -16
  17. package/build/core/asmail/sending-params/params-from-others.js +88 -58
  18. package/build/core/index.d.ts +2 -1
  19. package/build/core/index.js +4 -4
  20. package/build/core/keyring/index.js +2 -3
  21. package/build/core/keyring/introductory-keys/published-intro-key.d.ts +2 -2
  22. package/build/core/keyring/introductory-keys/published-intro-key.js +71 -71
  23. package/build/core/keyring/peer-keys/index.js +6 -7
  24. package/build/core/storage/index.d.ts +2 -1
  25. package/build/core/storage/index.js +4 -3
  26. package/build/core/storage/system-folders/index.d.ts +1 -2
  27. package/build/core/storage/system-folders/index.js +18 -22
  28. package/build/lib-client/asmail/recipient.d.ts +1 -0
  29. package/build/lib-client/asmail/recipient.js +11 -0
  30. package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
  31. package/build/lib-client/fs-utils/fs-sync-utils.js +21 -15
  32. package/build/lib-client/xsp-fs/folder-node.js +13 -3
  33. package/build/lib-client/xsp-fs/util/file-based-json.js +1 -1
  34. package/build/lib-common/dataset-sync/per-device-json-array-logs.js +43 -28
  35. package/build/lib-common/dataset-sync/single-file.d.ts +12 -0
  36. package/build/lib-common/dataset-sync/single-file.js +73 -0
  37. package/build/lib-common/dataset-sync/utils.d.ts +16 -0
  38. package/build/lib-common/dataset-sync/utils.js +72 -0
  39. package/build/lib-common-on-node/device-fs-places.d.ts +3 -0
  40. package/build/{lib-client/xsp-fs/util/for-arrays.js → lib-common-on-node/device-fs-places.js} +21 -13
  41. package/build/tests/computer.3nweb.core/keyrings.js +2 -2
  42. package/build/tests/libs-for-tests/core-runner.js +2 -1
  43. package/package.json +1 -1
  44. package/build/lib-client/xsp-fs/util/for-arrays.d.ts +0 -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,46 +16,26 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.SendingParamsHolder = void 0;
19
+ exports.makeSendingParamsHolder = makeSendingParamsHolder;
20
20
  const params_from_others_1 = require("./params-from-others");
21
21
  const own_params_1 = require("./own-params");
22
22
  const invitations_anon_1 = require("./invitations-anon");
23
23
  const PARAMS_FROM_OTHERS_FILE = 'params-from-others.json';
24
24
  const OWN_PARAMS_FILE = 'own-params.json';
25
25
  const ANONYM_INVITES_FILE = 'anonymous-invites.json';
26
- class SendingParamsHolder {
27
- constructor(paramsFromOthers, ownParams) {
28
- this.paramsFromOthers = paramsFromOthers;
29
- this.ownParams = ownParams;
30
- this.otherSides = {
31
- get: this.paramsFromOthers.getFor,
32
- set: this.paramsFromOthers.setFor
33
- };
34
- this.thisSide = {
35
- getUpdated: this.ownParams.getFor,
36
- setAsUsed: this.ownParams.setAsInUse
37
- };
38
- Object.freeze(this);
39
- }
40
- static async makeAndInit(fs, anonInvitesOnServer, random) {
41
- const [paramsFromOthers, ownParams] = await Promise.all([
42
- fs.writableFile(PARAMS_FROM_OTHERS_FILE)
43
- .then(f => params_from_others_1.ParamsFromOthers.makeAndInit(f)),
44
- fs.writableFile(ANONYM_INVITES_FILE)
45
- .then(async (anonInvitesFile) => {
46
- const anonInvites = await invitations_anon_1.AnonymousInvites.makeAndInit(anonInvitesFile, anonInvitesOnServer, random);
47
- return await own_params_1.OwnSendingParams.makeAndInit(await fs.writableFile(OWN_PARAMS_FILE), anonInvites);
48
- })
49
- ]);
50
- await fs.close();
51
- return new SendingParamsHolder(paramsFromOthers, ownParams);
52
- }
53
- async close() {
54
- await this.ownParams.close();
55
- await this.paramsFromOthers.close();
26
+ async function makeSendingParamsHolder(fs, anonInvitesOnServer, random) {
27
+ const [otherSides, thisSide] = await Promise.all([
28
+ (0, params_from_others_1.makeParamsFromOthers)(fs, PARAMS_FROM_OTHERS_FILE),
29
+ (0, own_params_1.makeOwnSendingParams)(fs, OWN_PARAMS_FILE, await (0, invitations_anon_1.makeAnonymousInvites)(fs, ANONYM_INVITES_FILE, anonInvitesOnServer, random))
30
+ ]);
31
+ async function close() {
32
+ await thisSide.close();
33
+ await otherSides.close();
56
34
  }
35
+ return {
36
+ thisSide,
37
+ otherSides,
38
+ close
39
+ };
57
40
  }
58
- exports.SendingParamsHolder = SendingParamsHolder;
59
- Object.freeze(SendingParamsHolder.prototype);
60
- Object.freeze(SendingParamsHolder);
61
41
  Object.freeze(exports);
@@ -1,37 +1,13 @@
1
1
  import { ParamOnServer } from '../../../lib-client/asmail/service-config';
2
2
  import { AsyncRNG } from '../../../lib-common/rng-def';
3
- type WritableFile = web3n.files.WritableFile;
4
- interface InvitesJSON {
5
- invites: {
6
- [invite: string]: {
7
- label: string;
8
- msgMaxSize: number;
9
- };
10
- };
11
- }
12
- export declare class AnonymousInvites {
13
- private readonly anonInvitesOnServer;
14
- private readonly random;
15
- private invites;
16
- private readonly fileProc;
17
- private constructor();
18
- static makeAndInit(file: WritableFile, anonInvitesOnServer: ParamOnServer<'anon-sender/invites'>, random: AsyncRNG): Promise<AnonymousInvites>;
19
- private onFileEvent;
20
- protected setFromJSON(json: InvitesJSON): void;
21
- private toFileJSON;
22
- private persist;
23
- private absorbRemoteChanges;
24
- private toServerParam;
25
- private toServiceJSON;
26
- private syncServiceSetting;
27
- getAll(): Map<string, {
3
+ type WritableFS = web3n.files.WritableFS;
4
+ export type AnonymousInvites = Awaited<ReturnType<typeof makeAnonymousInvites>>;
5
+ export declare function makeAnonymousInvites(fs: WritableFS, fileName: string, anonInvitesOnServer: ParamOnServer<'anon-sender/invites'>, random: AsyncRNG): Promise<{
6
+ create: (label: string, msgMaxSize: number) => Promise<string>;
7
+ getAll: () => Map<string, {
28
8
  invite: string;
29
9
  msgMaxSize: number;
30
10
  }>;
31
- create(label: string, msgMaxSize: number): Promise<string>;
32
- private generateNewRandomInvite;
33
- private findByLabel;
34
- setMsgMaxSize(label: string, msgMaxSize: number): Promise<void>;
35
- close(): Promise<void>;
36
- }
11
+ close: () => Promise<void>;
12
+ }>;
37
13
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2018, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 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,160 +16,110 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.AnonymousInvites = void 0;
20
- const file_based_json_1 = require("../../../lib-client/xsp-fs/util/file-based-json");
21
- const json_utils_1 = require("../../../lib-common/json-utils");
19
+ exports.makeAnonymousInvites = makeAnonymousInvites;
22
20
  const rng_def_1 = require("../../../lib-common/rng-def");
21
+ const single_file_1 = require("../../../lib-common/dataset-sync/single-file");
23
22
  const INVITE_TOKEN_LEN = 40;
24
- class AnonymousInvites {
25
- constructor(anonInvitesOnServer, random) {
26
- this.anonInvitesOnServer = anonInvitesOnServer;
27
- this.random = random;
28
- this.invites = {};
29
- this.fileProc = new file_based_json_1.JsonFileProc(this.onFileEvent.bind(this));
30
- Object.seal(this);
31
- }
32
- static async makeAndInit(file, anonInvitesOnServer, random) {
33
- const anonInvites = new AnonymousInvites(anonInvitesOnServer, random);
34
- await anonInvites.fileProc.start(file, () => anonInvites.toFileJSON());
35
- // XXX these are part of proper syncing logic
36
- // await anonInvites.absorbRemoteChanges();
37
- // await anonInvites.syncServiceSetting();
38
- return anonInvites;
39
- }
40
- async onFileEvent(ev) {
41
- if (ev.src === 'local') {
42
- return;
23
+ async function makeAnonymousInvites(fs, fileName, anonInvitesOnServer, random) {
24
+ const { file: invitesFile, changeProc } = await (0, single_file_1.initSyncedFile)(fs, fileName, newFile => newFile.writeJSON({ invites: {} }));
25
+ function parseInvitesFromJSON(json) {
26
+ const invites = json.invites;
27
+ if (invites || (typeof invites === 'object')) {
28
+ return invites;
43
29
  }
44
- switch (ev.type) {
45
- case 'file-change':
46
- await this.fileProc.order.startOrChain(() => this.absorbRemoteChanges());
47
- break;
48
- case 'removed':
49
- throw new Error(`Unexpected removal of file with parameter "anon-sender/invites"`);
50
- default:
51
- return;
30
+ else {
31
+ return {};
52
32
  }
53
33
  }
54
- setFromJSON(json) {
55
- this.invites = json.invites;
56
- }
57
- toFileJSON() {
58
- return {
59
- invites: this.invites
60
- };
34
+ let invites = parseInvitesFromJSON(await invitesFile.readJSON());
35
+ async function onChangeFromOtherDevices(newVersion) {
36
+ changeProc.startOrChain(async () => {
37
+ const { version, json } = await invitesFile.v.readJSON();
38
+ if (version === newVersion) {
39
+ invites = parseInvitesFromJSON(json);
40
+ }
41
+ });
61
42
  }
62
- async persist() {
63
- await this.fileProc.order.startOrChain(async () => {
64
- await this.anonInvitesOnServer.setOnServer(this.toServerParam());
65
- await this.fileProc.save(this.toFileJSON(), false);
43
+ function doOnConflict() {
44
+ return changeProc.startOrChain(async () => {
45
+ const { remote, state } = await invitesFile.v.sync.status(false);
46
+ if ((state !== 'conflicting') || !(remote === null || remote === void 0 ? void 0 : remote.latest)) {
47
+ return;
48
+ }
49
+ const onRemote = (await invitesFile.v.readJSON({ remoteVersion: remote.latest })).json;
50
+ const numOfInvitesOnRemote = Object.keys(onRemote.invites).length;
51
+ const numOfCurrentInvites = Object.keys(invites).length;
52
+ const joined = { ...onRemote.invites, ...invites };
53
+ const numOfJoined = Object.keys(joined).length;
54
+ if ((numOfCurrentInvites === numOfInvitesOnRemote) && (numOfCurrentInvites === numOfJoined)) {
55
+ await invitesFile.v.sync.adoptRemote({ remoteVersion: remote.latest });
56
+ }
57
+ else {
58
+ invites = joined;
59
+ await invitesFile.writeJSON({ invites });
60
+ await invitesFile.v.sync.upload({ uploadVersion: remote.latest + 1 });
61
+ await anonInvitesOnServer.setOnServer(invitesToServerParamForm(invites));
62
+ }
66
63
  });
67
64
  }
68
- async absorbRemoteChanges() {
69
- // XXX
70
- // - check for changes: what is needed here from fileProc, and what is
71
- // generic in absorbing remote changes to refactor it into JsonFileProc
72
- // - absorb and sync, if needed: what can be in JsonFileProc
73
- // Code from pre-v.sync:
74
- // const { json } = await this.fileProc.get();
75
- // this.setFromJSON(json);
65
+ const { stopFileWatching, triggerUpload } = (0, single_file_1.watchAndApplyChangesFromOtherDevices)(invitesFile, changeProc, onChangeFromOtherDevices, doOnConflict);
66
+ async function persist() {
67
+ await anonInvitesOnServer.setOnServer(invitesToServerParamForm(invites));
68
+ await invitesFile.writeJSON({ invites });
69
+ triggerUpload();
76
70
  }
77
- toServerParam() {
71
+ function invitesToServerParamForm(invites) {
78
72
  const serverParam = {};
79
- for (const [invite, { msgMaxSize }] of Object.entries(this.invites)) {
73
+ for (const [invite, { msgMaxSize }] of Object.entries(invites)) {
80
74
  serverParam[invite] = msgMaxSize;
81
75
  }
82
76
  return serverParam;
83
77
  }
84
- toServiceJSON() {
85
- const serverJSON = {};
86
- Object.entries(this.invites)
87
- .forEach(([invite, params]) => {
88
- serverJSON[invite] = params.msgMaxSize;
89
- });
90
- return serverJSON;
91
- }
92
- async syncServiceSetting(rethrowConnectExc = false) {
93
- // XXX we may have the following bug here:
94
- // Device with older version of param gets to this point, and sets older
95
- // value.
96
- // To protect aginst this case, absorbing from file must ensure highest
97
- // synced version is read.
98
- try {
99
- const infoOnServer = await this.anonInvitesOnServer.getFromServer()
100
- .catch((exc) => {
101
- if (exc.type === 'connect') {
102
- return;
103
- }
104
- else {
105
- throw exc;
106
- }
107
- });
108
- const currentVal = this.toServiceJSON();
109
- if (!(0, json_utils_1.deepEqual)(infoOnServer, currentVal)) {
110
- await this.anonInvitesOnServer.setOnServer(currentVal);
111
- }
112
- }
113
- catch (exc) {
114
- if (!rethrowConnectExc
115
- && (exc.type === 'connect')) {
116
- return;
117
- }
118
- throw exc;
119
- }
120
- }
121
- getAll() {
78
+ function getAll() {
122
79
  const byLabel = new Map();
123
- Object.entries(this.invites)
80
+ Object.entries(invites)
124
81
  .forEach(([invite, params]) => {
125
82
  byLabel.set(params.label, { invite, msgMaxSize: params.msgMaxSize });
126
83
  });
127
84
  return byLabel;
128
85
  }
129
- ;
130
- async create(label, msgMaxSize) {
131
- const existingInvite = this.findByLabel(label);
132
- if (existingInvite) {
133
- throw new Error(`Anonymous sender invite already exists with label ${label}`);
134
- }
135
- const invite = await this.generateNewRandomInvite();
136
- this.invites[invite] = { label, msgMaxSize };
137
- try {
138
- await this.persist();
139
- return invite;
140
- }
141
- catch (exc) {
142
- delete this.invites[invite];
143
- throw exc;
144
- }
86
+ function create(label, msgMaxSize) {
87
+ return changeProc.startOrChain(async () => {
88
+ const existingInvite = findByLabel(label);
89
+ if (existingInvite) {
90
+ throw new Error(`Anonymous sender invite already exists with label ${label}`);
91
+ }
92
+ const invite = await generateNewRandomInvite();
93
+ invites[invite] = { label, msgMaxSize };
94
+ try {
95
+ await persist();
96
+ return invite;
97
+ }
98
+ catch (exc) {
99
+ delete invites[invite];
100
+ throw exc;
101
+ }
102
+ });
145
103
  }
146
- ;
147
- async generateNewRandomInvite() {
104
+ async function generateNewRandomInvite() {
148
105
  let invite;
149
106
  do {
150
- invite = await (0, rng_def_1.stringOfB64Chars)(INVITE_TOKEN_LEN, this.random);
151
- } while (this.invites[invite]);
107
+ invite = await (0, rng_def_1.stringOfB64Chars)(INVITE_TOKEN_LEN, random);
108
+ } while (invites[invite]);
152
109
  return invite;
153
110
  }
154
- findByLabel(label) {
155
- const found = Object.entries(this.invites)
111
+ function findByLabel(label) {
112
+ const found = Object.entries(invites)
156
113
  .find(([_, params]) => (params.label === label));
157
114
  return (found ? found[0] : undefined);
158
115
  }
159
- async setMsgMaxSize(label, msgMaxSize) {
160
- const invite = this.findByLabel(label);
161
- if (!invite) {
162
- throw new Error(`There is no anonymous sender invite with label ${label}`);
163
- }
164
- this.invites[invite].msgMaxSize = msgMaxSize;
165
- await this.persist();
166
- }
167
- ;
168
- async close() {
169
- await this.fileProc.close();
116
+ async function close() {
117
+ stopFileWatching();
170
118
  }
119
+ return {
120
+ create,
121
+ getAll,
122
+ close
123
+ };
171
124
  }
172
- exports.AnonymousInvites = AnonymousInvites;
173
- Object.freeze(AnonymousInvites.prototype);
174
- Object.freeze(AnonymousInvites);
175
125
  Object.freeze(exports);
@@ -1,28 +1,8 @@
1
1
  import { SendingParamsHolder } from './index';
2
2
  import { AnonymousInvites } from './invitations-anon';
3
- type ExposedFuncs = SendingParamsHolder['thisSide'];
4
- type WritableFile = web3n.files.WritableFile;
5
- type FileEvent = web3n.files.FileEvent;
6
- /**
7
- * Instance of this class keeps track of sending parameters, which user gives to
8
- * other correspondents for sending messages back. These parameters may have
9
- * invitation tokens, and this class uses config service to register tokens on
10
- * a server.
11
- */
12
- export declare class OwnSendingParams {
13
- private readonly anonInvites;
14
- private readonly params;
15
- private defaultParams;
16
- private readonly fileProc;
17
- private constructor();
18
- static makeAndInit(file: WritableFile, anonInvites: AnonymousInvites): Promise<OwnSendingParams>;
19
- private setDefaultParams;
20
- private toFileJSON;
21
- private persist;
22
- private absorbChangesFromFile;
23
- protected onFileEvent(ev: FileEvent): Promise<void>;
24
- getFor: ExposedFuncs['getUpdated'];
25
- setAsInUse: ExposedFuncs['setAsUsed'];
26
- close(): Promise<void>;
27
- }
3
+ type WritableFS = web3n.files.WritableFS;
4
+ export type OwnSendingParams = Awaited<ReturnType<typeof makeOwnSendingParams>>;
5
+ export declare function makeOwnSendingParams(fs: WritableFS, fileName: string, anonInvites: AnonymousInvites): Promise<SendingParamsHolder['thisSide'] & {
6
+ close: () => Promise<void>;
7
+ }>;
28
8
  export {};
@@ -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.OwnSendingParams = void 0;
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
- * Instance of this class keeps track of sending parameters, which user gives to
26
- * other correspondents for sending messages back. These parameters may have
27
- * invitation tokens, and this class uses config service to register tokens on
28
- * a server.
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
- static async makeAndInit(file, anonInvites) {
77
- const ownParams = new OwnSendingParams(anonInvites);
78
- await ownParams.fileProc.start(file, () => ownParams.toFileJSON());
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 this.fileProc.order.startOrChain(async () => {
87
- const invites = this.anonInvites.getAll();
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
- this.defaultParams = {
40
+ defaultParams = {
91
41
  timestamp: 0,
92
42
  invitation: defaultInvite.invite
93
43
  };
94
44
  }
95
45
  else {
96
- const invitation = await this.anonInvites.create(DEFAULT_INVITE_LABEL, DEFAULT_INVITE_MAX_MSG_SIZE);
97
- this.defaultParams = {
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 this.persist();
52
+ await persist();
103
53
  });
104
54
  }
105
- toFileJSON() {
55
+ function currentToFileJSON() {
106
56
  return {
107
- default: this.defaultParams,
108
- senderSpecific: Array.from(this.params.values())
57
+ default: defaultParams,
58
+ senderSpecific: Object.values(params)
109
59
  };
110
60
  }
111
- async persist() {
112
- await this.fileProc.save(this.toFileJSON(), false);
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
- async onFileEvent(ev) {
122
- if (ev.src === 'local') {
123
- return;
65
+ function parseFromJSON(json) {
66
+ const params = {};
67
+ for (const p of json.senderSpecific) {
68
+ params[p.address] = p;
124
69
  }
125
- if (ev.type === 'removed') {
126
- throw new Error(`Unexpected removal of file with invites info`);
127
- }
128
- if (ev.type !== 'file-change') {
129
- return;
130
- }
131
- await this.fileProc.order.startOrChain(() => this.absorbChangesFromFile());
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
- await this.anonInvites.close();
135
- await this.fileProc.close();
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);