core-3nweb-client-lib 0.39.0 → 0.40.1

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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright (C) 2016 - 2017, 2020, 2023 3NSoft Inc.
2
+ Copyright (C) 2016 - 2017, 2020, 2023, 2025 3NSoft Inc.
3
3
 
4
4
  This program is free software: you can redistribute it and/or modify it under
5
5
  the terms of the GNU General Public License as published by the Free Software
@@ -70,9 +70,10 @@ declare namespace web3n.startup {
70
70
  * @param progressCB is a callback for progress notification
71
71
  */
72
72
  createUserParams(
73
- pass: string,
74
- progressCB: (progress: number) => void
73
+ pass: string, progressCB: ProgressCB
75
74
  ): Promise<void>;
75
+
76
+ watchBoot(observer: BootProcessObserver): () => void;
76
77
 
77
78
  }
78
79
 
@@ -106,8 +107,7 @@ declare namespace web3n.startup {
106
107
  * @param progressCB is a callback for progress notification
107
108
  */
108
109
  completeLoginAndLocalSetup(
109
- pass: string,
110
- progressCB: (progress: number) => void
110
+ pass: string, progressCB: ProgressCB
111
111
  ): Promise<boolean>;
112
112
 
113
113
  /**
@@ -119,12 +119,22 @@ declare namespace web3n.startup {
119
119
  * @param progressCB is a callback for progress notification
120
120
  */
121
121
  useExistingStorage(
122
- address: string, pass: string,
123
- progressCB: (progress: number) => void
122
+ address: string, pass: string, progressCB: ProgressCB
124
123
  ): Promise<boolean>;
124
+
125
+ watchBoot(observer: BootProcessObserver): () => void;
125
126
 
126
127
  }
127
128
 
129
+ type ProgressCB = (progress: number) => void;
130
+ type BootProcessObserver = Observer<BootEvent>;
131
+ interface BootEvent {
132
+ coreApp?: string;
133
+ message: string;
134
+ isError?: true;
135
+ isWarning?: true;
136
+ }
137
+
128
138
  interface W3N {
129
139
  signIn: startup.SignInService;
130
140
  signUp: startup.SignUpService;
@@ -87,24 +87,33 @@ class AnonymousInvites {
87
87
  });
88
88
  return serverJSON;
89
89
  }
90
- async syncServiceSetting() {
90
+ async syncServiceSetting(rethrowConnectExc = false) {
91
91
  // XXX we may have the following bug here:
92
92
  // Device with older version of param gets to this point, and sets older
93
93
  // value.
94
94
  // To protect aginst this case, absorbing from file must ensure highest
95
95
  // synced version is read.
96
- const infoOnServer = await this.anonInvitesOnServer.getFromServer()
97
- .catch((exc) => {
98
- if (exc.type === 'connect') {
99
- return;
96
+ try {
97
+ const infoOnServer = await this.anonInvitesOnServer.getFromServer()
98
+ .catch((exc) => {
99
+ if (exc.type === 'connect') {
100
+ return;
101
+ }
102
+ else {
103
+ throw exc;
104
+ }
105
+ });
106
+ const currentVal = this.toServiceJSON();
107
+ if (!(0, json_utils_1.deepEqual)(infoOnServer, currentVal)) {
108
+ await this.anonInvitesOnServer.setOnServer(currentVal);
100
109
  }
101
- else {
102
- throw exc;
110
+ }
111
+ catch (exc) {
112
+ if (!rethrowConnectExc
113
+ && (exc.type === 'connect')) {
114
+ return;
103
115
  }
104
- });
105
- const currentVal = this.toServiceJSON();
106
- if (!(0, json_utils_1.deepEqual)(infoOnServer, currentVal)) {
107
- await this.anonInvitesOnServer.setOnServer(currentVal);
116
+ throw exc;
108
117
  }
109
118
  }
110
119
  getAll() {
@@ -20,13 +20,14 @@ export declare class Core {
20
20
  private asmail;
21
21
  private keyrings;
22
22
  private idManager;
23
- private isInitialized;
24
23
  private closingProc;
24
+ private isInitialized;
25
25
  private constructor();
26
26
  static make(conf: CoreConf, makeNet: MakeNet, makeResolver: ServiceLocatorMaker, makeCryptor: makeCryptor): Core;
27
27
  start(): {
28
28
  capsForStartup: web3n.startup.W3N;
29
29
  coreInit: Promise<string>;
30
+ coreAppsInit: Promise<void>;
30
31
  };
31
32
  private initForNewUser;
32
33
  private initForExistingUserWithoutCache;
@@ -40,11 +41,9 @@ export declare class Core {
40
41
  private makeKeyringsCAP;
41
42
  private makeLogCAP;
42
43
  private makeMailerIdCAP;
43
- private closeBroadcast;
44
- close$: import("rxjs").Observable<void>;
45
44
  close(): Promise<void>;
46
45
  private performDataMigrationsAtInit;
47
- private initCore;
46
+ private initCoreApps;
48
47
  getStorages(): FactoryOfFSs;
49
48
  }
50
49
  export {};
@@ -24,13 +24,12 @@ const sign_in_1 = require("./startup/sign-in");
24
24
  const asmail_1 = require("./asmail");
25
25
  const error_1 = require("../lib-common/exceptions/error");
26
26
  const json_utils_1 = require("../lib-common/json-utils");
27
- const rxjs_1 = require("rxjs");
28
27
  const log_to_file_1 = require("../lib-client/logging/log-to-file");
29
- const operators_1 = require("rxjs/operators");
30
28
  const app_files_1 = require("./app-files");
31
29
  const keyring_1 = require("./keyring");
32
30
  const constants_1 = require("./storage/common/constants");
33
31
  const config_1 = require("./asmail/config");
32
+ const deferred_1 = require("../lib-common/processes/deferred");
34
33
  class Core {
35
34
  constructor(makeNet, makeResolver, makeCryptor, appDirs, logger, signUpUrl) {
36
35
  this.makeNet = makeNet;
@@ -39,95 +38,184 @@ class Core {
39
38
  this.logger = logger;
40
39
  this.signUpUrl = signUpUrl;
41
40
  this.idManager = undefined;
42
- this.isInitialized = false;
43
41
  this.closingProc = undefined;
44
- this.initForNewUser = async (u) => {
42
+ this.isInitialized = false;
43
+ this.cryptor = makeCryptor(this.logger.logError, this.logger.logWarning);
44
+ this.storages = new storage_1.Storages(this.cryptor.cryptor.sbox, this.appDirs.storagePathFor);
45
+ this.keyrings = new keyring_1.Keyrings(this.cryptor.cryptor.sbox);
46
+ this.asmail = new asmail_1.ASMail(this.cryptor.cryptor.sbox, this.makeNet, this.appDirs.inboxPathFor, this.logger);
47
+ Object.seal(this);
48
+ }
49
+ static make(conf, makeNet, makeResolver, makeCryptor) {
50
+ const dirs = (0, app_files_1.appDirs)(conf.dataDir);
51
+ const logger = (0, log_to_file_1.makeLogger)(dirs.getUtilFS());
52
+ const core = new Core(makeNet, makeResolver, makeCryptor, dirs, logger, conf.signUpUrl);
53
+ return core;
54
+ }
55
+ start() {
56
+ const { promise: midPromise, resolve: midDone } = (0, deferred_1.defer)();
57
+ const { watchBoot, emitBootEvent } = makeForBootEvents();
58
+ const signUp = new sign_up_1.SignUp(this.signUpUrl, this.cryptor.cryptor, this.makeNet, this.appDirs.getUsersOnDisk, user => this.initForNewUser(user, midDone, emitBootEvent), watchBoot, this.logger.logError);
59
+ const signIn = new sign_in_1.SignIn(this.cryptor.cryptor, addr => this.initForExistingUserWithoutCache(addr, midDone, emitBootEvent), (addr, storageKey) => this.initForExistingUserWithCache(addr, storageKey, midDone, emitBootEvent), this.appDirs.getUsersOnDisk, watchBoot, this.logger.logError);
60
+ const capsForStartup = {
61
+ signUp: signUp.exposedService(),
62
+ signIn: signIn.exposedService()
63
+ };
64
+ Object.freeze(capsForStartup);
65
+ const coreInit = midPromise.then(idManager => {
66
+ this.idManager = idManager;
67
+ return this.idManager.getId();
68
+ });
69
+ const coreAppsInit = coreInit.then(async () => {
70
+ // XXX This should be removed, at some point, as there will be no more
71
+ // users with very old data folders.
72
+ await this.performDataMigrationsAtInit();
73
+ await this.initCoreApps(emitBootEvent);
74
+ this.isInitialized = true;
75
+ });
76
+ return { coreInit, coreAppsInit, capsForStartup };
77
+ }
78
+ ;
79
+ async initForNewUser(u, done, emitBootEvent) {
80
+ var _a;
81
+ emitBootEvent({
82
+ message: `Initializing system for new user ${u.address}`
83
+ });
84
+ try {
45
85
  // 1) init of id manager without setting fs
46
- const stepTwo = await id_manager_1.IdManager.initWithoutStore(u.address, this.makeResolver('mailerid'), () => this.makeNet(), this.logger.logError, this.logger.logWarning);
86
+ const stepTwo = await id_manager_1.IdManager.initWithoutStore(u.address, this.makeResolver('mailerid'), this.makeNet, this.logger.logError, this.logger.logWarning);
47
87
  if (!stepTwo) {
48
- throw new Error(`MailerId server doesn't recognize identity ${u.address}`);
88
+ const message = `MailerId server doesn't recognize identity ${u.address}`;
89
+ emitBootEvent({ message, isError: true });
90
+ throw new Error(message);
49
91
  }
92
+ emitBootEvent({ message: `✔️ started login to provision MailerId` });
50
93
  // 2) complete id manager login, without use of fs
51
94
  const idManagerInit = await stepTwo(u.midSKey.default);
52
95
  if (!idManagerInit) {
53
- throw new Error(`Failed to provision MailerId identity`);
96
+ const message = `Failed to provision MailerId identity`;
97
+ emitBootEvent({ message, isError: true });
98
+ throw new Error(message);
54
99
  }
100
+ emitBootEvent({ message: `✔️ MailerId certificate provisioned` });
55
101
  const { idManager, setupManagerStorage } = idManagerInit;
56
102
  // 3) initialize all storages
103
+ emitBootEvent({ message: `Setting up main storage for new user` });
57
104
  const storesUp = await this.storages.initFreshForNewUser(u.address, idManager.getSigner, u.storeParams, u.storeSKey, this.makeNet, this.makeResolver('3nstorage'), this.logger.logError);
58
105
  if (!storesUp) {
59
- throw new Error(`Stores failed to initialize`);
106
+ const message = `Main store failed to initialize for new user`;
107
+ emitBootEvent({ message, isError: true });
108
+ throw new Error(message);
60
109
  }
110
+ emitBootEvent({ message: `✔️ main storage initialized` });
61
111
  // 3) give id manager fs, in which it will record labeled key(s)
112
+ emitBootEvent({ coreApp: constants_1.MAILERID_APP_NAME, message: `setting up storage` });
62
113
  await setupManagerStorage(await this.storages.makeSyncedFSForApp(constants_1.MAILERID_APP_NAME), [u.midSKey.labeled]);
63
- return idManager;
64
- };
65
- this.initForExistingUserWithoutCache = async (address) => {
114
+ emitBootEvent({ coreApp: constants_1.MAILERID_APP_NAME, message: `✔️ storage setup completed` });
115
+ done(idManager);
116
+ }
117
+ catch (exc) {
118
+ if (exc.type === 'connect') {
119
+ emitBootEvent({ message: `🔌 fail due to loss of connectivity`, isError: true });
120
+ }
121
+ else {
122
+ emitBootEvent({ message: (_a = exc.message) !== null && _a !== void 0 ? _a : (0, error_1.stringifyErr)(exc), isError: true });
123
+ }
124
+ throw exc;
125
+ }
126
+ }
127
+ async initForExistingUserWithoutCache(address, done, emitBootEvent) {
128
+ var _a;
129
+ emitBootEvent({
130
+ message: `Initializing system for user ${address}, without local cache on this device`
131
+ });
132
+ try {
66
133
  // 1) init of id manager without setting fs
67
- const stepTwo = await id_manager_1.IdManager.initWithoutStore(address, this.makeResolver('mailerid'), () => this.makeNet(), this.logger.logError, this.logger.logWarning);
134
+ const stepTwo = await id_manager_1.IdManager.initWithoutStore(address, this.makeResolver('mailerid'), this.makeNet, this.logger.logError, this.logger.logWarning);
68
135
  if (!stepTwo) {
136
+ emitBootEvent({
137
+ isError: true, message: `MailerId server doesn't recognize identity ${address}`
138
+ });
69
139
  return;
70
140
  }
71
141
  return async (midLoginKey, storageKey) => {
72
- // 2) complete id manager login, without use of fs
73
- const idManagerInit = await stepTwo(midLoginKey);
74
- if (!idManagerInit) {
75
- return;
142
+ var _a;
143
+ try {
144
+ // 2) complete id manager login, without use of fs
145
+ const idManagerInit = await stepTwo(midLoginKey);
146
+ if (!idManagerInit) {
147
+ emitBootEvent({
148
+ isError: true, message: `password/key is incorrect to provision MailerId certificate`
149
+ });
150
+ return false;
151
+ }
152
+ emitBootEvent({ message: `✔️ MailerId certificate provisioned` });
153
+ const { idManager, setupManagerStorage } = idManagerInit;
154
+ // 3) initialize all storages
155
+ emitBootEvent({ message: `Setting up main storage without local cache` });
156
+ const storeDone = await this.storages.initFromRemote(address, idManager.getSigner, storageKey, this.makeNet, this.makeResolver('3nstorage'), this.logger.logError);
157
+ if (!storeDone) {
158
+ emitBootEvent({ message: `Main store failed to initialize`, isError: true });
159
+ return false;
160
+ }
161
+ emitBootEvent({ message: `✔️ main storage initialized` });
162
+ // 4) complete initialization of id manager
163
+ emitBootEvent({ coreApp: constants_1.MAILERID_APP_NAME, message: `setting up storage` });
164
+ await setupManagerStorage(await this.storages.makeSyncedFSForApp(constants_1.MAILERID_APP_NAME));
165
+ emitBootEvent({ coreApp: constants_1.MAILERID_APP_NAME, message: `✔️ storage setup completed` });
166
+ done(idManager);
167
+ return true;
76
168
  }
77
- const { idManager, setupManagerStorage } = idManagerInit;
78
- // 3) initialize all storages
79
- const storeDone = await this.storages.initFromRemote(address, idManager.getSigner, storageKey, this.makeNet, this.makeResolver('3nstorage'), this.logger.logError);
80
- if (!storeDone) {
81
- return;
169
+ catch (exc) {
170
+ if (exc.type === 'connect') {
171
+ emitBootEvent({ message: `🔌 fail due to loss of connectivity`, isError: true });
172
+ }
173
+ else {
174
+ emitBootEvent({ message: (_a = exc.message) !== null && _a !== void 0 ? _a : (0, error_1.stringifyErr)(exc), isError: true });
175
+ }
176
+ throw exc;
82
177
  }
83
- // 4) complete initialization of id manager
84
- await setupManagerStorage(await this.storages.makeSyncedFSForApp(constants_1.MAILERID_APP_NAME));
85
- return idManager;
86
178
  };
87
- };
88
- this.initForExistingUserWithCache = async (address, storageKey) => {
89
- // XXX this shouldn't fail without network. All data is on the disk!
179
+ }
180
+ catch (exc) {
181
+ if (exc.type === 'connect') {
182
+ emitBootEvent({ message: `🔌 fail due to loss of connectivity`, isError: true });
183
+ }
184
+ else {
185
+ emitBootEvent({ message: (_a = exc.message) !== null && _a !== void 0 ? _a : (0, error_1.stringifyErr)(exc), isError: true });
186
+ }
187
+ throw exc;
188
+ }
189
+ }
190
+ async initForExistingUserWithCache(address, storageKey, done, emitBootEvent) {
191
+ var _a;
192
+ emitBootEvent({
193
+ message: `Initializing system for user ${address}, with local cache on this device`
194
+ });
195
+ try {
196
+ emitBootEvent({ message: `Unlocking data from local cache with provided password/key` });
90
197
  const completeStorageInit = await this.storages.startInitFromCache(address, storageKey, this.makeNet, this.makeResolver('3nstorage'), this.logger.logError);
91
198
  if (!completeStorageInit) {
92
- return;
199
+ emitBootEvent({
200
+ isError: true, message: `password/key is incorrect to decrypt local caches, or caches are damaged`
201
+ });
202
+ return false;
93
203
  }
94
- const idManager = await id_manager_1.IdManager.initFromCachedStore(address, await this.storages.makeSyncedFSForApp(constants_1.MAILERID_APP_NAME), this.makeResolver('mailerid'), () => this.makeNet(), this.logger.logError, this.logger.logWarning);
204
+ emitBootEvent({ message: `✔️ main storage is opened` });
205
+ const idManager = await id_manager_1.IdManager.initFromCachedStore(address, await this.storages.makeSyncedFSForApp(constants_1.MAILERID_APP_NAME), this.makeResolver('mailerid'), this.makeNet, this.logger.logError, this.logger.logWarning);
95
206
  if (!idManager) {
96
- return;
207
+ return false;
97
208
  }
209
+ emitBootEvent({ message: `✔️ MailerId manager is initialized` });
98
210
  completeStorageInit(idManager.getSigner);
99
- return idManager;
100
- };
101
- this.closeBroadcast = new rxjs_1.Subject();
102
- this.close$ = this.closeBroadcast.asObservable();
103
- this.cryptor = makeCryptor(this.logger.logError, this.logger.logWarning);
104
- this.storages = new storage_1.Storages(this.cryptor.cryptor.sbox, this.appDirs.storagePathFor);
105
- this.keyrings = new keyring_1.Keyrings(this.cryptor.cryptor.sbox);
106
- this.asmail = new asmail_1.ASMail(this.cryptor.cryptor.sbox, this.makeNet, this.appDirs.inboxPathFor, this.logger);
107
- Object.seal(this);
108
- }
109
- static make(conf, makeNet, makeResolver, makeCryptor) {
110
- const dirs = (0, app_files_1.appDirs)(conf.dataDir);
111
- const logger = (0, log_to_file_1.makeLogger)(dirs.getUtilFS());
112
- const core = new Core(makeNet, makeResolver, makeCryptor, dirs, logger, conf.signUpUrl);
113
- return core;
114
- }
115
- start() {
116
- const signUp = new sign_up_1.SignUp(this.signUpUrl, this.cryptor.cryptor, this.makeNet.bind(this), this.appDirs.getUsersOnDisk, this.logger.logError);
117
- const signIn = new sign_in_1.SignIn(this.cryptor.cryptor, this.initForExistingUserWithoutCache, this.initForExistingUserWithCache, this.appDirs.getUsersOnDisk, this.logger.logError);
118
- const capsForStartup = {
119
- signUp: signUp.exposedService(),
120
- signIn: signIn.exposedService()
121
- };
122
- Object.freeze(capsForStartup);
123
- const initFromSignUp$ = signUp.newUser$
124
- .pipe((0, operators_1.mergeMap)(this.initForNewUser, 1));
125
- const initFromSignIn$ = signIn.existingUser$;
126
- const coreInit = (0, rxjs_1.lastValueFrom)((0, rxjs_1.merge)(initFromSignIn$, initFromSignUp$)
127
- .pipe((0, operators_1.take)(1), (0, operators_1.mergeMap)(idManager => this.initCore(idManager), 1)));
128
- return { coreInit, capsForStartup };
211
+ done(idManager);
212
+ return true;
213
+ }
214
+ catch (exc) {
215
+ emitBootEvent({ message: (_a = exc.message) !== null && _a !== void 0 ? _a : (0, error_1.stringifyErr)(exc), isError: true });
216
+ throw exc;
217
+ }
129
218
  }
130
- ;
131
219
  makeCAPsForApp(appDomain, requestedCAPs) {
132
220
  if (!this.isInitialized || this.closingProc) {
133
221
  throw new Error(`Core is either not yet initialized, or is already closed.`);
@@ -204,7 +292,6 @@ class Core {
204
292
  }
205
293
  await this.cryptor.close();
206
294
  this.cryptor = undefined;
207
- this.closeBroadcast.next();
208
295
  })();
209
296
  }
210
297
  await this.closingProc;
@@ -214,27 +301,27 @@ class Core {
214
301
  await this.storages.migrateCoreAppDataOnFirstRun('synced', `${constants_1.ASMAIL_APP_NAME}/config/introductory-key.json`, `${constants_1.KEYRINGS_APP_NAME}/introductory-keys/published-on-server.json`);
215
302
  await this.storages.migrateCoreAppDataOnFirstRun('synced', `${constants_1.ASMAIL_APP_NAME}/config/anonymous/invites.json`, `${constants_1.ASMAIL_APP_NAME}/sending-params/anonymous-invites.json`);
216
303
  }
217
- async initCore(idManager) {
304
+ async initCoreApps(emitBootEvent) {
305
+ var _a;
218
306
  try {
219
- this.idManager = idManager;
220
307
  const address = this.idManager.getId();
221
308
  const getSigner = this.idManager.getSigner;
222
309
  const asmailServerConfig = new config_1.ConfigOfASMailServer(address, getSigner, this.makeResolver('asmail'), this.makeNet());
223
- // XXX This should be removed, at some point, as there will be no more
224
- // users with very old data folders.
225
- await this.performDataMigrationsAtInit();
226
- // XXX some of these starting can be done any way
310
+ emitBootEvent({ coreApp: constants_1.KEYRINGS_APP_NAME, message: `starting initialization` });
227
311
  const keyringsSyncedFS = await this.storages.makeSyncedFSForApp(constants_1.KEYRINGS_APP_NAME);
228
312
  await this.keyrings.init(keyringsSyncedFS, this.idManager.getSigner, asmailServerConfig.makeParamSetterAndGetter('init-pub-key'));
313
+ emitBootEvent({ coreApp: constants_1.KEYRINGS_APP_NAME, message: `✔️ initialized` });
314
+ emitBootEvent({ coreApp: constants_1.ASMAIL_APP_NAME, message: `starting initialization` });
229
315
  const inboxSyncedFS = await this.storages.makeSyncedFSForApp(constants_1.ASMAIL_APP_NAME);
230
316
  const inboxLocalFS = await this.storages.makeLocalFSForApp(constants_1.ASMAIL_APP_NAME);
231
317
  await this.asmail.init(this.idManager.getId(), this.idManager.getSigner, inboxSyncedFS, inboxLocalFS, this.storages.storageGetterForASMail(), this.makeResolver, asmailServerConfig, this.keyrings.forASMail());
232
- this.isInitialized = true;
233
- return this.idManager.getId();
318
+ emitBootEvent({ coreApp: constants_1.ASMAIL_APP_NAME, message: `✔️ initialized` });
234
319
  }
235
320
  catch (err) {
236
- throw (0, error_1.errWithCause)(err, 'Failed to initialize core');
321
+ emitBootEvent({ isError: true, message: (_a = err.message) !== null && _a !== void 0 ? _a : (0, error_1.stringifyErr)(err) });
322
+ throw (0, error_1.errWithCause)(err, 'Failed to initialize core apps');
237
323
  }
324
+ emitBootEvent(true);
238
325
  }
239
326
  getStorages() {
240
327
  return this.storages.wrap();
@@ -243,6 +330,30 @@ class Core {
243
330
  exports.Core = Core;
244
331
  Object.freeze(Core.prototype);
245
332
  Object.freeze(Core);
333
+ function makeForBootEvents() {
334
+ let bootProcObserver = undefined;
335
+ return {
336
+ watchBoot(obs) {
337
+ var _a;
338
+ if (bootProcObserver) {
339
+ (_a = bootProcObserver.error) === null || _a === void 0 ? void 0 : _a.call(bootProcObserver, `New observer is added, and it stops the previous one.`);
340
+ }
341
+ bootProcObserver = obs;
342
+ return () => {
343
+ bootProcObserver = undefined;
344
+ };
345
+ },
346
+ emitBootEvent(ev) {
347
+ var _a, _b;
348
+ if (ev === true) {
349
+ (_a = bootProcObserver === null || bootProcObserver === void 0 ? void 0 : bootProcObserver.complete) === null || _a === void 0 ? void 0 : _a.call(bootProcObserver);
350
+ }
351
+ else {
352
+ (_b = bootProcObserver === null || bootProcObserver === void 0 ? void 0 : bootProcObserver.next) === null || _b === void 0 ? void 0 : _b.call(bootProcObserver, ev);
353
+ }
354
+ }
355
+ };
356
+ }
246
357
  function makeStoragePolicy(appDomain, requestedCAPs) {
247
358
  if (!requestedCAPs.storage) {
248
359
  throw new Error(`Missing storage setting in app's manifest`);
@@ -1,29 +1,27 @@
1
- import type { IdManager } from '../id-manager';
2
1
  import { ScryptGenParams } from '../../lib-client/key-derivation';
3
2
  import type { GetUsersOnDisk } from '../app-files';
4
3
  import { Cryptor } from '../../lib-client/cryptor/cryptor';
5
4
  import { LogError } from '../../lib-client/logging/log-to-file';
6
5
  export type GenerateKey = (derivParams: ScryptGenParams) => Promise<Uint8Array>;
7
6
  export type StartInitWithoutCache = (address: string) => Promise<CompleteInitWithoutCache | undefined>;
8
- export type CompleteInitWithoutCache = (midLoginKey: GenerateKey, storageKey: GenerateKey) => Promise<IdManager | undefined>;
9
- export type InitWithCache = (address: string, storageKey: GenerateKey) => Promise<IdManager | undefined>;
7
+ export type CompleteInitWithoutCache = (midLoginKey: GenerateKey, storageKey: GenerateKey) => Promise<boolean>;
8
+ export type InitWithCache = (address: string, storageKey: GenerateKey) => Promise<boolean>;
10
9
  type SignInService = web3n.startup.SignInService;
10
+ type ProgressCB = web3n.startup.ProgressCB;
11
11
  export declare class SignIn {
12
12
  private cryptor;
13
13
  private startInitWithoutCache;
14
14
  private initWithCache;
15
15
  private getUsersOnDisk;
16
+ private readonly watchBoot;
16
17
  private readonly logError;
17
18
  private completeInitWithoutCache;
18
- constructor(cryptor: Cryptor, startInitWithoutCache: StartInitWithoutCache, initWithCache: InitWithCache, getUsersOnDisk: GetUsersOnDisk, logError: LogError);
19
+ constructor(cryptor: Cryptor, startInitWithoutCache: StartInitWithoutCache, initWithCache: InitWithCache, getUsersOnDisk: GetUsersOnDisk, watchBoot: SignInService['watchBoot'], logError: LogError);
19
20
  exposedService(): SignInService;
20
21
  private startLoginToRemoteStorage;
21
22
  private completeLoginAndLocalSetup;
22
- private readonly doneBroadcast;
23
- readonly existingUser$: import("rxjs").Observable<IdManager>;
24
23
  private useExistingStorage;
25
24
  private logAndWrap;
26
25
  }
27
- export type ProgressCB = (p: number) => void;
28
26
  export declare function makeKeyGenProgressCB(progressStart: number, progressEnd: number, progressCB: ProgressCB): ProgressCB;
29
27
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2018, 2020, 2022 3NSoft Inc.
3
+ Copyright (C) 2015 - 2018, 2020, 2022, 2025 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
@@ -19,75 +19,62 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.SignIn = void 0;
20
20
  exports.makeKeyGenProgressCB = makeKeyGenProgressCB;
21
21
  const key_derivation_1 = require("../../lib-client/key-derivation");
22
- const rxjs_1 = require("rxjs");
23
22
  const error_1 = require("../../lib-common/exceptions/error");
24
23
  class SignIn {
25
- constructor(cryptor, startInitWithoutCache, initWithCache, getUsersOnDisk, logError) {
24
+ constructor(cryptor, startInitWithoutCache, initWithCache, getUsersOnDisk, watchBoot, logError) {
26
25
  this.cryptor = cryptor;
27
26
  this.startInitWithoutCache = startInitWithoutCache;
28
27
  this.initWithCache = initWithCache;
29
28
  this.getUsersOnDisk = getUsersOnDisk;
29
+ this.watchBoot = watchBoot;
30
30
  this.logError = logError;
31
31
  this.completeInitWithoutCache = undefined;
32
- this.startLoginToRemoteStorage = async (address) => {
33
- try {
34
- this.completeInitWithoutCache = await this.startInitWithoutCache(address);
35
- return !!this.completeInitWithoutCache;
36
- }
37
- catch (err) {
38
- throw await this.logAndWrap(err, 'Fail to start login to remote storage');
39
- }
40
- };
41
- this.completeLoginAndLocalSetup = async (pass, progressCB) => {
42
- if (!this.completeInitWithoutCache) {
43
- throw new Error(`Call method startLoginToRemoteStorage() before calling this.`);
44
- }
45
- try {
46
- const midKeyProgressCB = makeKeyGenProgressCB(0, 50, progressCB);
47
- const midKeyGen = async (params) => (await (0, key_derivation_1.deriveMidKeyPair)(this.cryptor, pass, params, midKeyProgressCB)).skey;
48
- const storeKeyProgressCB = makeKeyGenProgressCB(51, 100, progressCB);
49
- const storeKeyGen = params => (0, key_derivation_1.deriveStorageSKey)(this.cryptor, pass, params, storeKeyProgressCB);
50
- const idManager = await this.completeInitWithoutCache(midKeyGen, storeKeyGen);
51
- if (!idManager) {
52
- return false;
53
- }
54
- this.doneBroadcast.next(idManager);
55
- return true;
56
- }
57
- catch (err) {
58
- throw await this.logAndWrap(err, 'Fail to initialize from a state without cache');
59
- }
60
- };
61
- this.doneBroadcast = new rxjs_1.Subject();
62
- this.existingUser$ = this.doneBroadcast.asObservable();
63
- this.useExistingStorage = async (user, pass, progressCB) => {
64
- try {
65
- const storeKeyProgressCB = makeKeyGenProgressCB(0, 99, progressCB);
66
- const storeKeyGen = params => (0, key_derivation_1.deriveStorageSKey)(this.cryptor, pass, params, storeKeyProgressCB);
67
- const idManager = await this.initWithCache(user, storeKeyGen);
68
- if (idManager) {
69
- this.doneBroadcast.next(idManager);
70
- return true;
71
- }
72
- else {
73
- return false;
74
- }
75
- }
76
- catch (err) {
77
- throw await this.logAndWrap(err, 'Failing to start in a state with cache');
78
- }
79
- };
80
32
  Object.seal(this);
81
33
  }
82
34
  exposedService() {
83
35
  const service = {
84
- completeLoginAndLocalSetup: this.completeLoginAndLocalSetup,
36
+ completeLoginAndLocalSetup: this.completeLoginAndLocalSetup.bind(this),
85
37
  getUsersOnDisk: this.getUsersOnDisk,
86
- startLoginToRemoteStorage: this.startLoginToRemoteStorage,
87
- useExistingStorage: this.useExistingStorage
38
+ startLoginToRemoteStorage: this.startLoginToRemoteStorage.bind(this),
39
+ useExistingStorage: this.useExistingStorage.bind(this),
40
+ watchBoot: this.watchBoot
88
41
  };
89
42
  return Object.freeze(service);
90
43
  }
44
+ async startLoginToRemoteStorage(address) {
45
+ try {
46
+ this.completeInitWithoutCache = await this.startInitWithoutCache(address);
47
+ return !!this.completeInitWithoutCache;
48
+ }
49
+ catch (err) {
50
+ throw await this.logAndWrap(err, 'Fail to start login to remote storage');
51
+ }
52
+ }
53
+ async completeLoginAndLocalSetup(pass, progressCB) {
54
+ if (!this.completeInitWithoutCache) {
55
+ throw new Error(`Call method startLoginToRemoteStorage() before calling this.`);
56
+ }
57
+ try {
58
+ const midKeyProgressCB = makeKeyGenProgressCB(0, 50, progressCB);
59
+ const midKeyGen = async (params) => (await (0, key_derivation_1.deriveMidKeyPair)(this.cryptor, pass, params, midKeyProgressCB)).skey;
60
+ const storeKeyProgressCB = makeKeyGenProgressCB(51, 100, progressCB);
61
+ const storeKeyGen = params => (0, key_derivation_1.deriveStorageSKey)(this.cryptor, pass, params, storeKeyProgressCB);
62
+ return await this.completeInitWithoutCache(midKeyGen, storeKeyGen);
63
+ }
64
+ catch (err) {
65
+ throw await this.logAndWrap(err, 'Fail to initialize from a state without cache');
66
+ }
67
+ }
68
+ async useExistingStorage(user, pass, progressCB) {
69
+ try {
70
+ const storeKeyProgressCB = makeKeyGenProgressCB(0, 99, progressCB);
71
+ const storeKeyGen = params => (0, key_derivation_1.deriveStorageSKey)(this.cryptor, pass, params, storeKeyProgressCB);
72
+ return await this.initWithCache(user, storeKeyGen);
73
+ }
74
+ catch (err) {
75
+ throw await this.logAndWrap(err, 'Failing to start in a state with cache');
76
+ }
77
+ }
91
78
  async logAndWrap(err, msg) {
92
79
  await this.logError(err, msg);
93
80
  return (0, error_1.errWithCause)(err, msg);
@@ -15,14 +15,18 @@ export declare class SignUp {
15
15
  private cryptor;
16
16
  private makeNet;
17
17
  private getUsersOnDisk;
18
+ private initForNewUser;
19
+ private readonly watchBoot;
18
20
  private readonly logError;
19
21
  private mid;
20
22
  private store;
21
23
  private serviceURL;
22
24
  private netLazyInit;
23
25
  private get net();
24
- constructor(serviceURL: string, cryptor: Cryptor, makeNet: () => NetClient, getUsersOnDisk: GetUsersOnDisk, logError: LogError);
26
+ constructor(serviceURL: string, cryptor: Cryptor, makeNet: () => NetClient, getUsersOnDisk: GetUsersOnDisk, initForNewUser: (u: CreatedUser) => Promise<void>, watchBoot: SignUpService['watchBoot'], logError: LogError);
25
27
  private setServiceURL;
28
+ private getAvailableDomains;
29
+ private getAvailableAddresses;
26
30
  exposedService(): SignUpService;
27
31
  private createUserParams;
28
32
  private genStorageParams;
@@ -30,8 +34,6 @@ export declare class SignUp {
30
34
  private addUser;
31
35
  private logAndWrap;
32
36
  private forgetKeys;
33
- private doneBroadcast;
34
- newUser$: import("rxjs").Observable<CreatedUser>;
35
37
  }
36
38
  export interface CreatedUser {
37
39
  address: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2020, 2022 - 2023 3NSoft Inc.
3
+ Copyright (C) 2015 - 2020, 2022 - 2023, 2025 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
@@ -26,7 +26,6 @@ const keyDeriv = require("../../lib-client/key-derivation");
26
26
  const random = require("../../lib-common/random-node");
27
27
  const ecma_nacl_1 = require("ecma-nacl");
28
28
  const sign_in_1 = require("./sign-in");
29
- const rxjs_1 = require("rxjs");
30
29
  const error_1 = require("../../lib-common/exceptions/error");
31
30
  /**
32
31
  * With these parameters scrypt shall use memory around:
@@ -63,54 +62,21 @@ class SignUp {
63
62
  }
64
63
  return this.netLazyInit;
65
64
  }
66
- constructor(serviceURL, cryptor, makeNet, getUsersOnDisk, logError) {
65
+ constructor(serviceURL, cryptor, makeNet, getUsersOnDisk, initForNewUser, watchBoot, logError) {
67
66
  this.cryptor = cryptor;
68
67
  this.makeNet = makeNet;
69
68
  this.getUsersOnDisk = getUsersOnDisk;
69
+ this.initForNewUser = initForNewUser;
70
+ this.watchBoot = watchBoot;
70
71
  this.logError = logError;
71
72
  this.mid = undefined;
72
73
  this.store = undefined;
73
74
  this.serviceURL = undefined;
74
75
  this.netLazyInit = undefined;
75
- this.createUserParams = async (pass, progressCB) => {
76
- await this.genMidParams(pass, 0, 50, progressCB);
77
- await this.genStorageParams(pass, 51, 100, progressCB);
78
- };
79
- this.addUser = async (address, signupToken) => {
80
- for (const user of await this.getUsersOnDisk()) {
81
- if ((0, canonical_address_1.areAddressesEqual)(address, user)) {
82
- throw new Error(`Account ${user} already exists on a disk.`);
83
- }
84
- }
85
- const accountCreated = await (0, _3nweb_signup_1.addUser)(this.net, this.serviceURL, {
86
- userId: address,
87
- mailerId: this.mid.params,
88
- storage: this.store.params,
89
- signupToken
90
- }).catch(async (err) => {
91
- throw await this.logAndWrap(err, `Failed to create user account ${address}.`);
92
- });
93
- if (!accountCreated) {
94
- return false;
95
- }
96
- this.doneBroadcast.next({
97
- address,
98
- midSKey: {
99
- default: this.mid.defaultSKey,
100
- labeled: this.mid.labeledSKey
101
- },
102
- storeSKey: this.store.skey,
103
- storeParams: this.store.params.kdParams
104
- });
105
- this.forgetKeys();
106
- return true;
107
- };
108
- this.doneBroadcast = new rxjs_1.Subject();
109
- this.newUser$ = this.doneBroadcast.asObservable();
110
76
  this.setServiceURL(serviceURL);
111
77
  Object.seal(this);
112
78
  }
113
- setServiceURL(serviceURL) {
79
+ async setServiceURL(serviceURL) {
114
80
  const url = (0, url_1.parse)(serviceURL);
115
81
  if (url.protocol !== 'https:') {
116
82
  throw new Error("Url protocol must be https.");
@@ -120,17 +86,28 @@ class SignUp {
120
86
  this.serviceURL += '/';
121
87
  }
122
88
  }
89
+ async getAvailableDomains(signupToken) {
90
+ return await (0, _3nweb_signup_1.checkAvailableDomains)(this.net, this.serviceURL, signupToken);
91
+ }
92
+ async getAvailableAddresses(name, signupToken) {
93
+ return await (0, _3nweb_signup_1.checkAvailableAddressesForName)(this.net, this.serviceURL, name, signupToken);
94
+ }
123
95
  exposedService() {
124
96
  const service = {
125
- setSignUpServer: async (srvUrl) => this.setServiceURL(srvUrl),
126
- getAvailableDomains: signupToken => (0, _3nweb_signup_1.checkAvailableDomains)(this.net, this.serviceURL, signupToken),
127
- addUser: this.addUser,
128
- createUserParams: this.createUserParams,
129
- getAvailableAddresses: (name, signupToken) => (0, _3nweb_signup_1.checkAvailableAddressesForName)(this.net, this.serviceURL, name, signupToken),
130
- isActivated: async () => { throw new Error(`Not implemented, yet`); }
97
+ setSignUpServer: this.setServiceURL.bind(this),
98
+ getAvailableDomains: this.getAvailableDomains.bind(this),
99
+ addUser: this.addUser.bind(this),
100
+ createUserParams: this.createUserParams.bind(this),
101
+ getAvailableAddresses: this.getAvailableAddresses.bind(this),
102
+ isActivated: () => { throw new Error(`Not implemented, yet`); },
103
+ watchBoot: this.watchBoot
131
104
  };
132
105
  return Object.freeze(service);
133
106
  }
107
+ async createUserParams(pass, progressCB) {
108
+ await this.genMidParams(pass, 0, 50, progressCB);
109
+ await this.genStorageParams(pass, 51, 100, progressCB);
110
+ }
134
111
  async genStorageParams(pass, progressStart, progressEnd, originalProgressCB) {
135
112
  const derivParams = {
136
113
  logN: defaultDerivParams.logN,
@@ -169,6 +146,35 @@ class SignUp {
169
146
  }
170
147
  };
171
148
  }
149
+ async addUser(address, signupToken) {
150
+ for (const user of await this.getUsersOnDisk()) {
151
+ if ((0, canonical_address_1.areAddressesEqual)(address, user)) {
152
+ throw new Error(`Account ${user} already exists on a disk.`);
153
+ }
154
+ }
155
+ const accountCreated = await (0, _3nweb_signup_1.addUser)(this.net, this.serviceURL, {
156
+ userId: address,
157
+ mailerId: this.mid.params,
158
+ storage: this.store.params,
159
+ signupToken
160
+ }).catch(async (err) => {
161
+ throw await this.logAndWrap(err, `Failed to create user account ${address}.`);
162
+ });
163
+ if (!accountCreated) {
164
+ return false;
165
+ }
166
+ this.initForNewUser({
167
+ address,
168
+ midSKey: {
169
+ default: this.mid.defaultSKey,
170
+ labeled: this.mid.labeledSKey
171
+ },
172
+ storeSKey: this.store.skey,
173
+ storeParams: this.store.params.kdParams
174
+ });
175
+ this.forgetKeys();
176
+ return true;
177
+ }
172
178
  async logAndWrap(err, msg) {
173
179
  await this.logError(err, msg);
174
180
  return (0, error_1.errWithCause)(err, msg);
@@ -44,7 +44,8 @@ function wrapSignInCAP(cap) {
44
44
  (_b = obs.complete) === null || _b === void 0 ? void 0 : _b.call(obs);
45
45
  }, err => { var _a; return (_a = obs.error) === null || _a === void 0 ? void 0 : _a.call(obs, err); });
46
46
  return noop;
47
- })
47
+ }),
48
+ watchBoot: (0, service_side_wrap_1.wrapObservingFunc)(cap.watchBoot)
48
49
  };
49
50
  }
50
51
  function makeSignInCaller(caller, objPath) {
@@ -66,7 +67,8 @@ function makeSignInCaller(caller, objPath) {
66
67
  obsFn(obs, addr, pass);
67
68
  return promise;
68
69
  };
69
- })()
70
+ })(),
71
+ watchBoot: (0, caller_side_wrap_1.makeObservableFuncCaller)(caller, objPath.concat('watchBoot'))
70
72
  };
71
73
  }
72
74
  function callSignIn(caller, objPath, method) {
@@ -104,7 +106,8 @@ function wrapSignUpCAP(cap) {
104
106
  .then(() => { var _a; return (_a = obs.complete) === null || _a === void 0 ? void 0 : _a.call(obs); }, err => { var _a; return (_a = obs.error) === null || _a === void 0 ? void 0 : _a.call(obs, err); });
105
107
  return noop;
106
108
  }),
107
- isActivated: (0, service_side_wrap_1.wrapReqReplySrvMethod)(cap, 'isActivated')
109
+ isActivated: (0, service_side_wrap_1.wrapReqReplySrvMethod)(cap, 'isActivated'),
110
+ watchBoot: (0, service_side_wrap_1.wrapObservingFunc)(cap.watchBoot)
108
111
  };
109
112
  }
110
113
  function noop() { }
@@ -129,7 +132,8 @@ function makeSignUpCaller(caller, objPath) {
129
132
  return completion.promise;
130
133
  };
131
134
  })(),
132
- isActivated: callSignUp(caller, objPath, 'isActivated')
135
+ isActivated: callSignUp(caller, objPath, 'isActivated'),
136
+ watchBoot: (0, caller_side_wrap_1.makeObservableFuncCaller)(caller, objPath.concat('watchBoot'))
133
137
  };
134
138
  }
135
139
  Object.freeze(exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-3nweb-client-lib",
3
- "version": "0.39.0",
3
+ "version": "0.40.1",
4
4
  "description": "3NWeb client core library, embeddable into different environments",
5
5
  "main": "build/lib-index.js",
6
6
  "types": "build/lib-index.d.ts",