core-3nweb-client-lib 0.44.0 → 0.44.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.
@@ -55,6 +55,7 @@ export declare class Core {
55
55
  onDeviceSystemResume(): Promise<void>;
56
56
  get connectivityEvents(): {
57
57
  inbox$: import("rxjs").Observable<import("./asmail/inbox/inbox-events").InboxConnectionStatus>;
58
+ storage$: import("rxjs").Observable<import("./storage/synced/remote-events").StorageConnectionStatus>;
58
59
  };
59
60
  }
60
61
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2022, 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
@@ -350,7 +350,8 @@ class Core {
350
350
  }
351
351
  get connectivityEvents() {
352
352
  return {
353
- inbox$: this.asmail.connectivityEvent$
353
+ inbox$: this.asmail.connectivityEvent$,
354
+ storage$: this.storages.connectivityEvent$
354
355
  };
355
356
  }
356
357
  }
@@ -51,6 +51,7 @@ export declare class Storages implements FactoryOfFSs {
51
51
  migrateCoreAppDataOnFirstRun(type: StorageType, src: string, dst: string): Promise<void>;
52
52
  suspendNetworkActivity(): void;
53
53
  resumeNetworkActivity(): void;
54
+ get connectivityEvent$(): import("rxjs").Observable<import("./synced/remote-events").StorageConnectionStatus>;
54
55
  }
55
56
  export interface FactoryOfFSs {
56
57
  makeSyncedFSForApp(appFolder: string): Promise<WritableFS>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2017, 2019 - 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2017, 2019 - 2022, 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
@@ -421,6 +421,9 @@ class Storages {
421
421
  var _a;
422
422
  (_a = this.synced) === null || _a === void 0 ? void 0 : _a.storage.resumeNetworkActivity();
423
423
  }
424
+ get connectivityEvent$() {
425
+ return this.synced.storage.connectionEvent$;
426
+ }
424
427
  }
425
428
  exports.Storages = Storages;
426
429
  Object.freeze(Storages.prototype);
@@ -61,5 +61,6 @@ export declare class SyncedStore implements ISyncedStorage {
61
61
  close(): Promise<void>;
62
62
  suspendNetworkActivity(): void;
63
63
  resumeNetworkActivity(): void;
64
+ get connectionEvent$(): Observable<import("./remote-events").StorageConnectionStatus>;
64
65
  }
65
66
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2020, 2022, 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
@@ -245,6 +245,9 @@ class SyncedStore {
245
245
  resumeNetworkActivity() {
246
246
  this.remoteEvents.resumeNetworkActivity();
247
247
  }
248
+ get connectionEvent$() {
249
+ return this.remoteEvents.connectionEvent$;
250
+ }
248
251
  }
249
252
  exports.SyncedStore = SyncedStore;
250
253
  Object.freeze(SyncedStore.prototype);
@@ -163,8 +163,10 @@ class MailRecipient extends user_with_mid_session_1.ServiceUser {
163
163
  appPath: api.msgObj.genUrlEnd(msgId, objId, opts),
164
164
  method: 'GET',
165
165
  responseType: 'arraybuffer',
166
- responseHeaders: [api.HTTP_HEADER.objSegmentsLength,
167
- api.HTTP_HEADER.objHeaderLength]
166
+ responseHeaders: [
167
+ api.HTTP_HEADER.objSegmentsLength,
168
+ api.HTTP_HEADER.objHeaderLength
169
+ ]
168
170
  });
169
171
  if (rep.status === api.msgObj.SC.ok) {
170
172
  if (!(rep.data instanceof Uint8Array)) {
@@ -46,21 +46,16 @@ function openSocket(url, sessionId) {
46
46
  ws.on('error', initOnError);
47
47
  ws.on('unexpected-response', onNonOkReply);
48
48
  ws.once('open', () => {
49
- const okReply = {
49
+ opening === null || opening === void 0 ? void 0 : opening.resolve({
50
50
  url,
51
51
  method: 'GET',
52
52
  status: 200,
53
53
  data: ws
54
- };
55
- opening === null || opening === void 0 ? void 0 : opening.resolve(okReply);
54
+ });
56
55
  opening = undefined;
57
56
  ws.removeListener('error', initOnError);
58
57
  ws.removeListener('unexpected-response', onNonOkReply);
59
58
  });
60
- ws.on('error', err => {
61
- // XXX we need
62
- console.error(`Error in ${ws.url} connection`, err);
63
- });
64
59
  return opening.promise;
65
60
  }
66
61
  Object.freeze(exports);
@@ -2,6 +2,7 @@ import type { ScryptGenParams } from '../key-derivation';
2
2
  import type { AsyncSBoxCryptor, Subscribe, ObjSource } from 'xsp-files';
3
3
  import type { Observable } from 'rxjs';
4
4
  import type { LogError } from '../logging/log-to-file';
5
+ import { StorageConnectionStatus } from '../../core/storage/synced/remote-events';
5
6
  export type { AsyncSBoxCryptor } from 'xsp-files';
6
7
  export type { FolderInJSON } from './folder-node';
7
8
  type StorageType = web3n.files.FSType;
@@ -119,6 +120,7 @@ export interface SyncedStorage extends Storage {
119
120
  getNumOfBytesNeedingDownload(objId: ObjId, version: number): Promise<number | 'unknown'>;
120
121
  suspendNetworkActivity(): void;
121
122
  resumeNetworkActivity(): void;
123
+ connectionEvent$: Observable<StorageConnectionStatus>;
122
124
  }
123
125
  export interface SyncedObjStatus extends LocalObjStatus {
124
126
  syncStatus(): SyncStatus;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2017, 2019 - 2020, 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2015 - 2017, 2019 - 2020, 2022, 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
@@ -126,6 +126,7 @@ function wrapSyncStorageImplementation(impl) {
126
126
  wrap.suspendNetworkActivity = impl.suspendNetworkActivity.bind(impl);
127
127
  wrap.resumeNetworkActivity = impl.resumeNetworkActivity.bind(impl);
128
128
  wrap.getNumOfBytesNeedingDownload = impl.getNumOfBytesNeedingDownload.bind(impl);
129
+ wrap.connectionEvent$ = impl.connectionEvent$;
129
130
  return Object.freeze(wrap);
130
131
  }
131
132
  function isSyncedStorage(storage) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2016 - 2017, 2025 3NSoft Inc.
3
+ Copyright (C) 2016 - 2017, 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
@@ -504,6 +504,7 @@ class EventsReceivingSide extends RequestingSide {
504
504
  * @param err
505
505
  */
506
506
  completeEvent(ipcChannel, err) {
507
+ var _a, _b, _c, _d;
507
508
  const listeners = this.listeners.get(ipcChannel);
508
509
  if (!listeners) {
509
510
  return;
@@ -512,14 +513,10 @@ class EventsReceivingSide extends RequestingSide {
512
513
  for (const listener of listeners) {
513
514
  try {
514
515
  if (err === undefined) {
515
- if (listener.observer.complete) {
516
- listener.observer.complete();
517
- }
516
+ (_b = (_a = listener.observer).complete) === null || _b === void 0 ? void 0 : _b.call(_a);
518
517
  }
519
518
  else {
520
- if (listener.observer.error) {
521
- listener.observer.error(err);
522
- }
519
+ (_d = (_c = listener.observer).error) === null || _d === void 0 ? void 0 : _d.call(_c, err);
523
520
  }
524
521
  }
525
522
  catch (err2) {
@@ -528,7 +525,24 @@ class EventsReceivingSide extends RequestingSide {
528
525
  }
529
526
  }
530
527
  handleCompletion(err) {
528
+ var _a, _b, _c, _d, _e;
531
529
  super.handleCompletion(err);
530
+ for (const ipcChannel of this.listeners.keys()) {
531
+ const listeners = (_a = this.listeners.get(ipcChannel)) !== null && _a !== void 0 ? _a : [];
532
+ for (const listener of listeners) {
533
+ try {
534
+ if (err === undefined) {
535
+ (_c = (_b = listener.observer).complete) === null || _c === void 0 ? void 0 : _c.call(_b);
536
+ }
537
+ else {
538
+ (_e = (_d = listener.observer).error) === null || _e === void 0 ? void 0 : _e.call(_d, err);
539
+ }
540
+ }
541
+ catch (err2) {
542
+ console.error(err2);
543
+ }
544
+ }
545
+ }
532
546
  this.listeners.clear();
533
547
  }
534
548
  subscribe(channel, observer) {
@@ -635,37 +649,31 @@ class MultiObserverWrap {
635
649
  return () => { this.obs.delete(obs); };
636
650
  }
637
651
  next(o) {
652
+ var _a;
638
653
  if (this.isDone) {
639
654
  return;
640
655
  }
641
656
  for (const obs of this.obs) {
642
- if (!obs.next) {
643
- continue;
644
- }
645
- obs.next(o);
657
+ (_a = obs.next) === null || _a === void 0 ? void 0 : _a.call(obs, o);
646
658
  }
647
659
  }
648
660
  error(err) {
661
+ var _a;
649
662
  if (this.isDone) {
650
663
  return;
651
664
  }
652
665
  for (const obs of this.obs) {
653
- if (!obs.error) {
654
- continue;
655
- }
656
- obs.error(err);
666
+ (_a = obs.error) === null || _a === void 0 ? void 0 : _a.call(obs, err);
657
667
  }
658
668
  this.setDone();
659
669
  }
660
670
  complete() {
671
+ var _a;
661
672
  if (this.isDone) {
662
673
  return;
663
674
  }
664
675
  for (const obs of this.obs) {
665
- if (!obs.complete) {
666
- continue;
667
- }
668
- obs.complete();
676
+ (_a = obs.complete) === null || _a === void 0 ? void 0 : _a.call(obs);
669
677
  }
670
678
  this.setDone();
671
679
  }
@@ -697,29 +705,26 @@ class SingleObserverWrap {
697
705
  this.obs = obs;
698
706
  }
699
707
  next(o) {
708
+ var _a, _b;
700
709
  if (this.isDone || !this.obs) {
701
710
  return;
702
711
  }
703
- if (this.obs.next) {
704
- this.obs.next(o);
705
- }
712
+ (_b = (_a = this.obs) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, o);
706
713
  }
707
714
  error(err) {
715
+ var _a, _b;
708
716
  if (this.isDone) {
709
717
  return;
710
718
  }
711
- if (this.obs && this.obs.error) {
712
- this.obs.error(err);
713
- }
719
+ (_b = (_a = this.obs) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err);
714
720
  this.setDone();
715
721
  }
716
722
  complete() {
723
+ var _a, _b;
717
724
  if (this.isDone) {
718
725
  return;
719
726
  }
720
- if (this.obs && this.obs.complete) {
721
- this.obs.complete();
722
- }
727
+ (_b = (_a = this.obs) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a);
723
728
  this.setDone();
724
729
  }
725
730
  setDone() {
@@ -10,6 +10,7 @@ export interface WSException extends web3n.RuntimeException {
10
10
  }
11
11
  export declare function makeWSException(params: Partial<WSException>, flags?: Partial<WSException>): WSException;
12
12
  export interface ConnectionStatus {
13
+ type: 'heartbeat' | 'heartbeat-skip' | 'disconnected' | 'connected';
13
14
  url: string;
14
15
  /**
15
16
  * ping number is a number of millisecond between previous and current data receiving from server.
@@ -19,6 +20,7 @@ export interface ConnectionStatus {
19
20
  * This mirrors a "slow socket" exception, thrown to data sending process.
20
21
  */
21
22
  slowSocket?: true;
23
+ missingPongsFromServer?: number;
22
24
  socketClosed?: true;
23
25
  error?: any;
24
26
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2017, 2025 3NSoft Inc.
3
+ Copyright (C) 2017, 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
@@ -13,7 +13,8 @@
13
13
  See the GNU General Public License for more details.
14
14
 
15
15
  You should have received a copy of the GNU General Public License along with
16
- this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
17
18
  Object.defineProperty(exports, "__esModule", { value: true });
18
19
  exports.WebSocketListening = exports.makeEventfulServer = void 0;
19
20
  exports.makeWSException = makeWSException;
@@ -30,6 +31,7 @@ function makeWSException(params, flags) {
30
31
  return (0, runtime_1.makeRuntimeException)('websocket', params, flags !== null && flags !== void 0 ? flags : {});
31
32
  }
32
33
  const MAX_TXT_BUFFER = 64 * 1024;
34
+ const CLIENT_SIDE_PING_PERIOD = 10 * 1000;
33
35
  /**
34
36
  * This creates a json communication point on a given web socket.
35
37
  * Point may have many listeners, allowing for single parsing of incoming
@@ -39,6 +41,21 @@ const MAX_TXT_BUFFER = 64 * 1024;
39
41
  function makeJsonCommPoint(ws) {
40
42
  const observers = new generic_ipc_1.MultiObserverWrap();
41
43
  const { heartbeat, healthyBeat, otherBeat } = makeHeartbeat(ws.url);
44
+ let outstandingPongs = 0;
45
+ let closedByPingProc = false;
46
+ const pingRepeat = setInterval(() => {
47
+ if (outstandingPongs >= 2) {
48
+ ws.close();
49
+ closedByPingProc = true;
50
+ clearInterval(pingRepeat);
51
+ return;
52
+ }
53
+ if (outstandingPongs > 0) {
54
+ otherBeat('heartbeat-skip', { missingPongsFromServer: outstandingPongs });
55
+ }
56
+ ws.ping();
57
+ outstandingPongs += 1;
58
+ }, CLIENT_SIDE_PING_PERIOD);
42
59
  ws.on('message', data => {
43
60
  if (observers.done) {
44
61
  return;
@@ -49,7 +66,8 @@ function makeJsonCommPoint(ws) {
49
66
  }
50
67
  catch (err) {
51
68
  ws.close();
52
- otherBeat(err, true);
69
+ clearInterval(pingRepeat);
70
+ otherBeat('disconnected', err, true);
53
71
  observers.error(err);
54
72
  return;
55
73
  }
@@ -57,12 +75,13 @@ function makeJsonCommPoint(ws) {
57
75
  healthyBeat();
58
76
  });
59
77
  ws.on('close', (code, reason) => {
78
+ clearInterval(pingRepeat);
60
79
  if (code === 1000) {
61
- otherBeat({ socketClosed: true }, true);
80
+ otherBeat('disconnected', { socketClosed: true }, true);
62
81
  observers.complete();
63
82
  }
64
83
  else {
65
- otherBeat({ error: { code, reason } }, true);
84
+ otherBeat('disconnected', { error: { code, reason } }, true);
66
85
  observers.error(makeWSException({
67
86
  socketClosed: true,
68
87
  cause: { code, reason }
@@ -70,24 +89,30 @@ function makeJsonCommPoint(ws) {
70
89
  }
71
90
  });
72
91
  ws.on('error', (err) => {
73
- otherBeat(err, true);
92
+ otherBeat('disconnected', err, true);
74
93
  observers.error(makeWSException({ cause: err }));
94
+ clearInterval(pingRepeat);
75
95
  ws.close();
76
96
  });
77
97
  ws.on('ping', () => {
78
98
  ws.pong();
79
99
  healthyBeat();
80
100
  });
101
+ ws.on('pong', () => {
102
+ healthyBeat();
103
+ outstandingPongs = 0;
104
+ });
81
105
  const comm = {
82
106
  subscribe: obs => observers.add(obs),
83
107
  postMessage(env) {
84
108
  if (ws.bufferedAmount > MAX_TXT_BUFFER) {
85
- otherBeat({ slowSocket: true });
109
+ otherBeat('heartbeat', { slowSocket: true });
86
110
  throw makeWSException({ socketSlow: true });
87
111
  }
88
112
  ws.send(JSON.stringify(env));
89
113
  }
90
114
  };
115
+ otherBeat('connected', {});
91
116
  return { comm, heartbeat };
92
117
  }
93
118
  function makeHeartbeat(url) {
@@ -96,13 +121,15 @@ function makeHeartbeat(url) {
96
121
  function healthyBeat() {
97
122
  const now = Date.now();
98
123
  status.next({
124
+ type: 'heartbeat',
99
125
  url,
100
126
  ping: now - lastInfo
101
127
  });
102
128
  lastInfo = now;
103
129
  }
104
- function otherBeat(params, end = false) {
130
+ function otherBeat(type, params, end = false) {
105
131
  status.next({
132
+ type,
106
133
  url,
107
134
  ...params
108
135
  });
@@ -6,4 +6,5 @@ export declare class MapOfSets<TKey, TValue> {
6
6
  remove(key: TKey, value: TValue): void;
7
7
  removeAll(key: TKey): void;
8
8
  clear(): void;
9
+ keys(): MapIterator<TKey>;
9
10
  }
@@ -48,6 +48,9 @@ class MapOfSets {
48
48
  clear() {
49
49
  this.map.clear();
50
50
  }
51
+ keys() {
52
+ return this.map.keys();
53
+ }
51
54
  }
52
55
  exports.MapOfSets = MapOfSets;
53
56
  Object.freeze(MapOfSets.prototype);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-3nweb-client-lib",
3
- "version": "0.44.0",
3
+ "version": "0.44.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",