core-3nweb-client-lib 0.49.5 → 0.49.7

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 (81) hide show
  1. package/build/api-defs/files.d.ts +11 -5
  2. package/build/core/asmail/delivery/common.d.ts +8 -28
  3. package/build/core/asmail/delivery/common.js +1 -47
  4. package/build/core/asmail/delivery/index.js +3 -8
  5. package/build/core/asmail/delivery/msg.js +1 -7
  6. package/build/core/asmail/delivery/per-recipient-wip.js +3 -29
  7. package/build/core/asmail/inbox/index.d.ts +1 -2
  8. package/build/core/asmail/inbox/index.js +14 -15
  9. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.d.ts +11 -0
  10. package/build/core/asmail/inbox/msg-indexing/dataset/v0/sql-db-v-none.js +25 -0
  11. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.d.ts +33 -0
  12. package/build/core/asmail/inbox/msg-indexing/dataset/v1/logs.js +72 -0
  13. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.d.ts +28 -0
  14. package/build/core/asmail/inbox/msg-indexing/dataset/v1/sql-db.js +477 -0
  15. package/build/core/asmail/inbox/msg-indexing/index.d.ts +12 -26
  16. package/build/core/asmail/inbox/msg-indexing/index.js +47 -68
  17. package/build/core/asmail/inbox/msg-on-disk.js +1 -2
  18. package/build/core/asmail/index.js +8 -7
  19. package/build/core/asmail/msg/common.d.ts +0 -1
  20. package/build/core/asmail/msg/common.js +1 -1
  21. package/build/core/asmail/msg/packer.js +1 -3
  22. package/build/core/index.js +7 -4
  23. package/build/core/keyring/common.d.ts +1 -0
  24. package/build/core/keyring/common.js +2 -1
  25. package/build/core/keyring/index.d.ts +7 -44
  26. package/build/core/keyring/index.js +89 -222
  27. package/build/core/keyring/introductory-keys/published-intro-key.d.ts +17 -0
  28. package/build/core/keyring/introductory-keys/published-intro-key.js +190 -0
  29. package/build/core/keyring/peer-keys/dataset/v1/json-files.d.ts +105 -0
  30. package/build/core/keyring/peer-keys/dataset/v1/json-files.js +89 -0
  31. package/build/core/keyring/peer-keys/dataset/v2/logs.d.ts +43 -0
  32. package/build/core/keyring/peer-keys/dataset/v2/logs.js +116 -0
  33. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.d.ts +73 -0
  34. package/build/core/keyring/peer-keys/dataset/v2/peer-keys-db.js +543 -0
  35. package/build/core/keyring/peer-keys/index.d.ts +24 -0
  36. package/build/core/keyring/peer-keys/index.js +208 -0
  37. package/build/core/storage/synced/obj-files.js +10 -3
  38. package/build/core/storage/synced/obj-status.d.ts +1 -1
  39. package/build/core/storage/synced/obj-status.js +8 -2
  40. package/build/core/storage/synced/storage.d.ts +4 -4
  41. package/build/core/storage/system-folders/apps-data.d.ts +0 -1
  42. package/build/core/storage/system-folders/apps-data.js +2 -6
  43. package/build/lib-client/fs-utils/fs-sync-utils.d.ts +1 -1
  44. package/build/lib-client/fs-utils/fs-sync-utils.js +70 -13
  45. package/build/lib-client/xsp-fs/fs.js +3 -1
  46. package/build/lib-client/xsp-fs/node-in-fs.d.ts +1 -0
  47. package/build/lib-client/xsp-fs/node-in-fs.js +7 -2
  48. package/build/lib-client/xsp-fs/node-persistence.js +1 -3
  49. package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -2
  50. package/build/lib-common/dataset-sync/per-device-json-array-logs.d.ts +58 -0
  51. package/build/lib-common/dataset-sync/per-device-json-array-logs.js +432 -0
  52. package/build/lib-common/number-line.d.ts +10 -0
  53. package/build/lib-common/number-line.js +66 -0
  54. package/build/lib-common/objs-on-disk/obj-version-file.d.ts +1 -1
  55. package/build/lib-common/objs-on-disk/obj-version-file.js +3 -3
  56. package/build/lib-common/rng-def.d.ts +1 -0
  57. package/build/lib-common/rng-def.js +8 -0
  58. package/build/lib-sqlite-on-3nstorage/for-sqlite.d.ts +38 -0
  59. package/build/lib-sqlite-on-3nstorage/for-sqlite.js +162 -0
  60. package/build/lib-sqlite-on-3nstorage/index.d.ts +18 -5
  61. package/build/lib-sqlite-on-3nstorage/index.js +49 -26
  62. package/build/tests/caps-api/file-sink-checks/splice.js +72 -1
  63. package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +27 -1
  64. package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +12 -3
  65. package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +16 -12
  66. package/build/tests/computer.3nweb.core/keyrings.js +113 -28
  67. package/build/tests/jasmine.js +2 -2
  68. package/build/tests/units/number-line.js +9 -9
  69. package/package.json +2 -1
  70. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.d.ts +0 -52
  71. package/build/core/asmail/inbox/msg-indexing/logs-n-entries.js +0 -333
  72. package/build/core/asmail/inbox/msg-indexing/sql-indexing.d.ts +0 -4
  73. package/build/core/asmail/inbox/msg-indexing/sql-indexing.js +0 -319
  74. package/build/core/keyring/correspondent-keys.d.ts +0 -149
  75. package/build/core/keyring/correspondent-keys.js +0 -403
  76. package/build/core/keyring/id-to-email-map.d.ts +0 -38
  77. package/build/core/keyring/id-to-email-map.js +0 -105
  78. package/build/core/keyring/keyring-storage.d.ts +0 -9
  79. package/build/core/keyring/keyring-storage.js +0 -54
  80. package/build/core/keyring/published-intro-key.d.ts +0 -45
  81. package/build/core/keyring/published-intro-key.js +0 -200
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright 2025 3NSoft Inc.
3
+ Copyright 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,49 +16,134 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
+ const peer_keys_1 = require("../../core/keyring/peer-keys");
20
+ const fs_sync_utils_1 = require("../../lib-client/fs-utils/fs-sync-utils");
19
21
  const jasmine_utils_1 = require("../libs-for-tests/jasmine-utils");
20
22
  const setups_1 = require("../libs-for-tests/setups");
21
23
  const test_utils_1 = require("./test-utils");
22
- const address = `address Bob Perkins @company.inc`;
23
- const introPKeyFromServer = {
24
- use: 'asmail-pub-key',
25
- alg: 'NaCl-box-CXSP',
26
- kid: 'EKnB33DQsmpq2RiL',
27
- k: 'hDeWvNYajaAFUijY23SlXFKhIFOuMJLWtIBJYtiToEQ='
24
+ const random_node_1 = require("../../lib-common-on-node/random-node");
25
+ const inspector_1 = require("inspector");
26
+ const common_1 = require("../../core/keyring/common");
27
+ const canonical_address_1 = require("../../lib-common/canonical-address");
28
+ const bytes_equal_1 = require("../libs-for-tests/bytes-equal");
29
+ const logger = {
30
+ appLog: async () => { },
31
+ logError: async (err, msg) => {
32
+ inspector_1.console.error(`\n --- test logError called with ---\nMessage: ${msg}\nError:`, err);
33
+ },
34
+ logWarning: async (msg, err) => {
35
+ inspector_1.console.error(`\n --- test logWarning called with ---\nMessage: ${msg}\nError:`, err);
36
+ },
37
+ recordUnhandledRejectionsInProcess: () => { }
28
38
  };
39
+ async function logErrorInTest(err, msg) {
40
+ inspector_1.console.error(`\n --- test logError callled with ---\nMessage: ${msg}\nError:`, err);
41
+ }
29
42
  describe('ASMail keyring', () => {
30
43
  const baseSetup = (0, setups_1.setupWithUsers)();
31
44
  const testFolder = `keyring-test`;
32
- let keyring;
33
45
  const { fsSetup: setup, setupDevsAndFSs } = (0, test_utils_1.makeSetupWithTwoDevsFSs)(testFolder);
34
46
  (0, jasmine_utils_1.beforeAllWithTimeoutLog)(async () => {
35
47
  await setupDevsAndFSs(baseSetup);
36
48
  }, 20000);
37
- beforeEach(async () => {
38
- const fs = setup.dev1FS();
39
- // keyring = await makeAndKeyRing(cryptor, fs, {
40
- // find: kid => { throw Error(`publishedKeys.find() mock`); },
41
- // update: async () => { throw Error(`publishedKeys.update() mock`); }
42
- // });
43
- });
44
49
  (0, jasmine_utils_1.afterEachCond)(async () => {
45
50
  if (!setup.isUp) {
46
51
  return;
47
52
  }
48
53
  await setup.resetFS();
49
54
  });
50
- (0, jasmine_utils_1.itCond)(`.generateKeysToSend()`, async () => {
51
- // const {
52
- // currentPair, encryptor, msgCount
53
- // } = await keyring.generateKeysToSend(address, introPKeyFromServer);
54
- // // DEBUG
55
- // // console.log(`currentPair`, currentPair, `
56
- // // msgCount`, msgCount);
57
- // const {
58
- // currentPair: cp, msgCount: count
59
- // } = await keyring.generateKeysToSend(address);
60
- // // DEBUG
61
- // // console.log(`currentPair`, cp, `
62
- // // msgCount`, count);
55
+ (0, jasmine_utils_1.itCond)(`peers keyring supports key pair operations for a full messaging chain from intro`, async () => {
56
+ const dev1FS = setup.dev1FS();
57
+ const dev1LocalFS = setup.dev1LocalFS();
58
+ // run different peers' keyrings from different folders
59
+ const peer1 = `keyring tester 1 @rock.cafe`;
60
+ const peer2 = `keyring tester 2 @rock.cafe`;
61
+ const p1FS = await (0, fs_sync_utils_1.getOrMakeSyncedDirOnInit)(dev1FS, peer1);
62
+ const p1LocalFS = await dev1LocalFS.writableSubRoot(`local fs for ${peer1}`);
63
+ const p2FS = await (0, fs_sync_utils_1.getOrMakeSyncedDirOnInit)(dev1FS, peer2);
64
+ const p2LocalFS = await dev1LocalFS.writableSubRoot(`local fs for ${peer2}`);
65
+ const peerKeys1 = await (0, peer_keys_1.makePeersKeyring)(p1FS, p1LocalFS, random_node_1.bytes, logger);
66
+ const peerKeys2 = await (0, peer_keys_1.makePeersKeyring)(p2FS, p2LocalFS, random_node_1.bytes, logger);
67
+ try {
68
+ // -----------------------------------
69
+ // Message 1: peer1 to peer2, peers initially don't know each other (don't have established key pairs)
70
+ // -----------------------------------
71
+ // peer1 somehow gets intro key for peer2
72
+ const p2IntroPair = await (0, common_1.generateKeyPair)(random_node_1.bytes);
73
+ // peer1, to send first message with introductory pair, generates and save suggested crypto
74
+ let suggestedCrypto = await peerKeys1.suggestPairForPeerIntroKey(peer2, p2IntroPair.pkey);
75
+ expect(peerKeys1.needIntroKeyFor(peer2)).toBeTrue();
76
+ expect(suggestedCrypto.senderKid).toBe(p2IntroPair.pkey.kid);
77
+ // peer2 saves suggested crypto from decrypted message, crypto based on introductory key
78
+ expect(peerKeys2.needIntroKeyFor(peer1)).toBeTrue();
79
+ await peerKeys2.absorbNextPairSuggestedByPeer(peer1, suggestedCrypto, p2IntroPair);
80
+ const estPair1 = suggestedCrypto;
81
+ expect(peerKeys2.needIntroKeyFor(peer1)).toBeFalse();
82
+ // -----------------------------------
83
+ // Message 2: peer2 to peer1
84
+ // -----------------------------------
85
+ // peer2, to send reply, generates and saves suggested crypto
86
+ let forSending = await peerKeys2.getSendingCryptoWithinEstablishedPair((0, canonical_address_1.toCanonicalAddress)(peer1));
87
+ expect(estPair1.pids).toContain(forSending.currentPair.pid);
88
+ expect(forSending.nextMsgCrypto).withContext(`next crypto is generated asap to jump off introductory key`).toBeDefined();
89
+ // peer1, when message is received with pair id (pid), finds keys to decrypt message
90
+ let decr = peerKeys1.findEstablishedReceptionPairs(forSending.currentPair.pid);
91
+ expect(decr).toBeDefined();
92
+ expect(decr[0].peerCAddr).toBe((0, canonical_address_1.toCanonicalAddress)(peer2));
93
+ expect(decr[0].ratchetStage).toBe('suggested');
94
+ expect((0, bytes_equal_1.bytesEqual)(forSending.msgMasterKey, decr[0].msgMasterKey)).toBeTrue();
95
+ // peer1 marks found crypto pair as being in use
96
+ await peerKeys1.markPairAsInUse(decr[0].peerCAddr, decr[0].peerKId, decr[0].recipientKId);
97
+ // peer1 saves next crypto from decrypted message
98
+ expect(peerKeys1.needIntroKeyFor(peer2)).toBeTrue();
99
+ await peerKeys1.absorbNextPairSuggestedByPeer(peer2, forSending.nextMsgCrypto);
100
+ const estPair2 = forSending.nextMsgCrypto;
101
+ expect(peerKeys1.needIntroKeyFor(peer2)).toBeFalse();
102
+ // -----------------------------------
103
+ // Message 3: peer1 to peer2
104
+ // -----------------------------------
105
+ // peer1 gets crypto to send to peer2
106
+ forSending = await peerKeys1.getSendingCryptoWithinEstablishedPair((0, canonical_address_1.toCanonicalAddress)(peer2));
107
+ expect(estPair2.pids).toContain(forSending.currentPair.pid);
108
+ expect(forSending.nextMsgCrypto).withContext(`attempt to rotate pairs will come after some period of time`).toBeUndefined();
109
+ // peer2 finds pair to decrypt message
110
+ decr = peerKeys2.findEstablishedReceptionPairs(forSending.currentPair.pid);
111
+ expect(decr).toBeDefined();
112
+ expect(decr[0].peerCAddr).toBe((0, canonical_address_1.toCanonicalAddress)(peer1));
113
+ expect(decr[0].ratchetStage).toBe('suggested');
114
+ expect((0, bytes_equal_1.bytesEqual)(forSending.msgMasterKey, decr[0].msgMasterKey)).toBeTrue();
115
+ // peer2 marks found crypto pair as being in use
116
+ await peerKeys2.markPairAsInUse(decr[0].peerCAddr, decr[0].peerKId, decr[0].recipientKId);
117
+ // -----------------------------------
118
+ // Message 4: peer2 to peer1
119
+ // -----------------------------------
120
+ // peer2 gets crypto to send to peer1
121
+ forSending = await peerKeys2.getSendingCryptoWithinEstablishedPair((0, canonical_address_1.toCanonicalAddress)(peer1));
122
+ expect(estPair1.pids).toContain(forSending.currentPair.pid);
123
+ expect(forSending.nextMsgCrypto).withContext(`attempt to rotate pairs will come after some period of time`).toBeUndefined();
124
+ // peer1, when message is received with pair id (pid), finds keys to decrypt message
125
+ decr = peerKeys1.findEstablishedReceptionPairs(forSending.currentPair.pid);
126
+ expect(decr).toBeDefined();
127
+ expect(decr[0].peerCAddr).toBe((0, canonical_address_1.toCanonicalAddress)(peer2));
128
+ expect(decr[0].ratchetStage).toBe('in_use');
129
+ expect((0, bytes_equal_1.bytesEqual)(forSending.msgMasterKey, decr[0].msgMasterKey)).toBeTrue();
130
+ // -----------------------------------
131
+ // Message 5: peer1 to peer2
132
+ // -----------------------------------
133
+ // peer1 gets crypto to send to peer2
134
+ forSending = await peerKeys1.getSendingCryptoWithinEstablishedPair((0, canonical_address_1.toCanonicalAddress)(peer2));
135
+ expect(estPair2.pids).toContain(forSending.currentPair.pid);
136
+ expect(forSending.nextMsgCrypto).withContext(`attempt to rotate pairs will come after some period of time`).toBeUndefined();
137
+ // peer2 finds pair to decrypt message
138
+ decr = peerKeys2.findEstablishedReceptionPairs(forSending.currentPair.pid);
139
+ expect(decr).toBeDefined();
140
+ expect(decr[0].peerCAddr).toBe((0, canonical_address_1.toCanonicalAddress)(peer1));
141
+ expect(decr[0].ratchetStage).toBe('in_use');
142
+ expect((0, bytes_equal_1.bytesEqual)(forSending.msgMasterKey, decr[0].msgMasterKey)).toBeTrue();
143
+ }
144
+ finally {
145
+ await peerKeys1.close().catch(exc => logErrorInTest(exc));
146
+ await peerKeys2.close().catch(exc => logErrorInTest(exc));
147
+ }
63
148
  }, undefined, setup);
64
149
  });
@@ -34,9 +34,9 @@ const ALL_SPECS = [
34
34
  'caps-api/mailerid.js',
35
35
  'caps-api/storage.js',
36
36
  'caps-api/asmail.js',
37
- // 'computer.3nweb.core/*.js',
37
+ 'computer.3nweb.core/keyrings.js',
38
38
  'computer.3nweb.core/id-manager.js',
39
- // 'computer.3nweb.core/inbox/*.js'
39
+ 'computer.3nweb.core/inbox/*.js'
40
40
  ];
41
41
  jas.loadConfig({
42
42
  spec_dir: 'build/tests',
@@ -16,48 +16,48 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- const common_1 = require("../../core/asmail/delivery/common");
20
19
  const json_utils_1 = require("../../lib-common/json-utils");
20
+ const number_line_1 = require("../../lib-common/number-line");
21
21
  describe(`Function addToNumberLineSegments`, () => {
22
22
  it(`inserts number to empty segments array`, () => {
23
23
  const segments = [];
24
24
  const n = 1;
25
- (0, common_1.addToNumberLineSegments)(segments, n);
25
+ (0, number_line_1.addToNumberLineSegments)(segments, n);
26
26
  expect((0, json_utils_1.deepEqual)(segments, [[n, n]])).toBeTruthy();
27
27
  });
28
28
  it(`expands existing segment on lower side`, () => {
29
29
  let segments = [[1, 1], [4, 4]];
30
- (0, common_1.addToNumberLineSegments)(segments, 3);
30
+ (0, number_line_1.addToNumberLineSegments)(segments, 3);
31
31
  expect((0, json_utils_1.deepEqual)(segments, [[1, 1], [3, 4]])).toBeTruthy();
32
32
  });
33
33
  it(`expands existing segment on higher side`, () => {
34
34
  const segments = [[1, 1], [4, 4]];
35
- (0, common_1.addToNumberLineSegments)(segments, 2);
35
+ (0, number_line_1.addToNumberLineSegments)(segments, 2);
36
36
  expect((0, json_utils_1.deepEqual)(segments, [[1, 2], [4, 4]])).toBeTruthy();
37
37
  });
38
38
  it(`expands existing segment and merges it with an overlapping one`, () => {
39
39
  const segments = [[1, 2], [4, 4]];
40
- (0, common_1.addToNumberLineSegments)(segments, 3);
40
+ (0, number_line_1.addToNumberLineSegments)(segments, 3);
41
41
  expect((0, json_utils_1.deepEqual)(segments, [[1, 4]])).toBeTruthy();
42
42
  });
43
43
  it(`changes nothing when number is already in an existing segment`, () => {
44
44
  const segments = [[1, 2], [4, 4]];
45
- (0, common_1.addToNumberLineSegments)(segments, 2);
45
+ (0, number_line_1.addToNumberLineSegments)(segments, 2);
46
46
  expect((0, json_utils_1.deepEqual)(segments, [[1, 2], [4, 4]])).toBeTruthy();
47
47
  });
48
48
  it(`adds new segment on a lower side`, () => {
49
49
  const segments = [[4, 6], [10, 12]];
50
- (0, common_1.addToNumberLineSegments)(segments, 2);
50
+ (0, number_line_1.addToNumberLineSegments)(segments, 2);
51
51
  expect((0, json_utils_1.deepEqual)(segments, [[2, 2], [4, 6], [10, 12]])).toBeTruthy();
52
52
  });
53
53
  it(`adds new segment in the middle`, () => {
54
54
  const segments = [[4, 6], [10, 12]];
55
- (0, common_1.addToNumberLineSegments)(segments, 8);
55
+ (0, number_line_1.addToNumberLineSegments)(segments, 8);
56
56
  expect((0, json_utils_1.deepEqual)(segments, [[4, 6], [8, 8], [10, 12]])).toBeTruthy();
57
57
  });
58
58
  it(`adds new segment on a higher side`, () => {
59
59
  const segments = [[4, 6], [10, 12]];
60
- (0, common_1.addToNumberLineSegments)(segments, 15);
60
+ (0, number_line_1.addToNumberLineSegments)(segments, 15);
61
61
  expect((0, json_utils_1.deepEqual)(segments, [[4, 6], [10, 12], [15, 15]])).toBeTruthy();
62
62
  });
63
63
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-3nweb-client-lib",
3
- "version": "0.49.5",
3
+ "version": "0.49.7",
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",
@@ -48,6 +48,7 @@
48
48
  "@types/punycode": "^2.1.1",
49
49
  "@types/ws": "^8.18.1",
50
50
  "jasmine": "^5.13.0",
51
+ "lodash": "^4.18.1",
51
52
  "napi-nacl": "^1.2.1",
52
53
  "protobufjs-cli": "^1.0.2",
53
54
  "spec-3nweb-server": "^1.8.6",
@@ -1,52 +0,0 @@
1
- import { MsgKeyInfo, MsgKeyRole } from '../../../keyring';
2
- type WritableFS = web3n.files.WritableFS;
3
- type MsgInfo = web3n.asmail.MsgInfo;
4
- interface MsgOpenedLog {
5
- msgState: 'opened';
6
- msgId: string;
7
- msgType: string;
8
- deliveryTS: number;
9
- keyB64: string;
10
- keyStatus: MsgKeyRole;
11
- mainObjHeaderOfs: number;
12
- removeAfter?: number;
13
- }
14
- interface MsgRemovedLog {
15
- msgState: 'removed';
16
- msgId: string;
17
- deliveryTS: number;
18
- }
19
- type MsgLog = MsgOpenedLog | MsgRemovedLog;
20
- export interface MsgKey {
21
- msgKey: Uint8Array;
22
- msgKeyRole: MsgKeyRole;
23
- mainObjHeaderOfs: number;
24
- }
25
- export interface IndexedRecords {
26
- add(msgInfo: MsgInfo, decrInfo: MsgKeyInfo, removeAfter: number): Promise<void>;
27
- remove(msgId: string): Promise<number | undefined>;
28
- listMsgs(fromTS: number | undefined): Promise<MsgInfo[]>;
29
- getKeyFor(msgId: string, deliveryTS: number): Promise<MsgKey | undefined>;
30
- msgExists(msgInfo: MsgInfo): Promise<boolean>;
31
- stopSyncing(): void;
32
- }
33
- export declare function makeJsonBasedIndexedRecords(logs: MsgLogs): IndexedRecords;
34
- export declare class MsgLogs {
35
- private readonly fs;
36
- private latest;
37
- private fileTSs;
38
- private readonly changeProc;
39
- private constructor();
40
- static makeAndStartSyncing(logsFS: WritableFS): Promise<MsgLogs>;
41
- stopSyncing(): void;
42
- add(msgInfo: MsgInfo, decrInfo: MsgKeyInfo, removeAfter: number | undefined): Promise<void>;
43
- private updateLogsFile;
44
- getLogsFromFile(fileTS: number | undefined): Promise<MsgLog[] | undefined>;
45
- private fileTSforMsgTS;
46
- remove(msgId: string, deliveryTS: number): Promise<void>;
47
- private scheduleRemovalOfMsgOpenedLog;
48
- getMsgOpenedLog(msgId: string, deliveryTS: number): Promise<MsgOpenedLog | undefined>;
49
- getLogFileTSs(): number[];
50
- getLatestMsgLogs(): MsgLog[];
51
- }
52
- export {};
@@ -1,333 +0,0 @@
1
- "use strict";
2
- /*
3
- Copyright (C) 2016 - 2020, 2023 3NSoft Inc.
4
-
5
- This program is free software: you can redistribute it and/or modify it under
6
- the terms of the GNU General Public License as published by the Free Software
7
- Foundation, either version 3 of the License, or (at your option) any later
8
- version.
9
-
10
- This program is distributed in the hope that it will be useful, but
11
- WITHOUT ANY WARRANTY; without even the implied warranty of
12
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
- See the GNU General Public License for more details.
14
-
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/>.
17
- */
18
- Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.MsgLogs = void 0;
20
- exports.makeJsonBasedIndexedRecords = makeJsonBasedIndexedRecords;
21
- const synced_1 = require("../../../../lib-common/processes/synced");
22
- const buffer_utils_1 = require("../../../../lib-common/buffer-utils");
23
- const timed_cache_1 = require("../../../../lib-common/timed-cache");
24
- const error_1 = require("../../../../lib-common/exceptions/error");
25
- const LIMIT_RECORDS_PER_FILE = 200;
26
- const LATEST_LOG = 'latest.json';
27
- const LOG_EXT = '.json';
28
- const INDEX_FNAME_REGEXP = /^\d+\.json$/;
29
- function logsFileName(fileTS) {
30
- return (fileTS ? LATEST_LOG : `${fileTS}${LOG_EXT}`);
31
- }
32
- function fileTSOrderComparator(a, b) {
33
- return (a - b);
34
- }
35
- function insertInto(records, rec) {
36
- if (records.length === 0) {
37
- records.push(rec);
38
- return;
39
- }
40
- const ts = rec.deliveryTS;
41
- for (let i = (records.length - 1); i >= 0; i -= 1) {
42
- if (records[i].deliveryTS <= ts) {
43
- records.splice(i + 1, 0, rec);
44
- return;
45
- }
46
- }
47
- records.splice(0, 0, rec);
48
- }
49
- function removeMsgOpenedLogFrom(logs, msgId) {
50
- const indToRm = logs.findIndex(log => ((log.msgId === msgId) && (log.msgState === 'opened')));
51
- if (indToRm > -1) {
52
- logs.splice(indToRm, 1);
53
- return true;
54
- }
55
- else {
56
- return false;
57
- }
58
- }
59
- /**
60
- * This is a catch callback, which returns undefined on file(folder) not found
61
- * exception, and re-throws all other exceptions/errors.
62
- */
63
- function notFoundOrReThrow(exc) {
64
- if (!exc.notFound) {
65
- throw exc;
66
- }
67
- return;
68
- }
69
- function makeJsonBasedIndexedRecords(logs) {
70
- return new JsonBasedIndexedRecords(logs);
71
- }
72
- function toMsgOpenedLog(msgInfo, decrInfo, removeAfter) {
73
- if (!decrInfo.key) {
74
- throw new Error(`Given message decryption info doesn't have a key for message ${msgInfo.msgId}`);
75
- }
76
- return {
77
- msgState: 'opened',
78
- msgType: msgInfo.msgType,
79
- msgId: msgInfo.msgId,
80
- deliveryTS: msgInfo.deliveryTS,
81
- keyB64: buffer_utils_1.base64.pack(decrInfo.key),
82
- keyStatus: decrInfo.keyStatus,
83
- mainObjHeaderOfs: decrInfo.msgKeyPackLen,
84
- removeAfter
85
- };
86
- }
87
- function keyInfoFrom(log) {
88
- return {
89
- msgKey: buffer_utils_1.base64.open(log.keyB64),
90
- msgKeyRole: log.keyStatus,
91
- mainObjHeaderOfs: log.mainObjHeaderOfs
92
- };
93
- }
94
- function addOnlyNonRemovedMsgs(dst, ignore, src) {
95
- for (const log of src) {
96
- if (log.msgState === 'removed') {
97
- ignore.add(log.msgId);
98
- }
99
- }
100
- for (const log of src) {
101
- if ((log.msgState === 'opened') && !ignore.has(log.msgId)) {
102
- dst.push(log);
103
- }
104
- }
105
- }
106
- function cutEarlierMsgs(msgs, fromTS) {
107
- let timedEnd = msgs.length;
108
- for (let i = (msgs.length - 1); i >= 0; i -= 1) {
109
- const msg = msgs[i];
110
- if (msg.deliveryTS < fromTS) {
111
- timedEnd = i;
112
- }
113
- else {
114
- break;
115
- }
116
- }
117
- if (timedEnd < msgs.length) {
118
- msgs.splice(timedEnd, (msgs.length - timedEnd));
119
- }
120
- }
121
- class JsonBasedIndexedRecords {
122
- constructor(logs) {
123
- this.logs = logs;
124
- this.cachedLogs = (0, timed_cache_1.makeTimedCache)(10 * 60 * 1000);
125
- Object.seal(this);
126
- }
127
- async add(msgInfo, decrInfo, removeAfter) {
128
- const msg = toMsgOpenedLog(msgInfo, decrInfo, removeAfter);
129
- this.cachedLogs.set(msg.msgId, msg);
130
- }
131
- async remove(msgId) {
132
- let foundLog = this.cachedLogs.get(msgId);
133
- if (foundLog) {
134
- this.cachedLogs.delete(msgId);
135
- return foundLog.deliveryTS;
136
- }
137
- for (const fileTS of [undefined, ...this.logs.getLogFileTSs()]) {
138
- let logs;
139
- if ((fileTS === undefined)) {
140
- logs = this.logs.getLatestMsgLogs();
141
- }
142
- else {
143
- const logsFromFile = await this.logs.getLogsFromFile(fileTS);
144
- if (!logsFromFile) {
145
- continue;
146
- }
147
- logs = logsFromFile;
148
- }
149
- for (const log of logs) {
150
- if (log.msgId === msgId) {
151
- return log.deliveryTS;
152
- }
153
- }
154
- }
155
- return undefined;
156
- }
157
- async listMsgs(fromTS) {
158
- const ignore = new Set();
159
- const msgs = [];
160
- addOnlyNonRemovedMsgs(msgs, ignore, this.logs.getLatestMsgLogs());
161
- const fileTSs = this.logs.getLogFileTSs();
162
- if (fromTS === undefined) {
163
- for (const fileTS of fileTSs) {
164
- const logs = await this.logs.getLogsFromFile(fileTS);
165
- if (!logs) {
166
- continue;
167
- }
168
- addOnlyNonRemovedMsgs(msgs, ignore, logs);
169
- }
170
- }
171
- else {
172
- for (const fileTS of fileTSs) {
173
- if (fromTS > fileTS) {
174
- break;
175
- }
176
- const logs = await this.logs.getLogsFromFile(fileTS);
177
- if (!logs) {
178
- continue;
179
- }
180
- addOnlyNonRemovedMsgs(msgs, ignore, logs);
181
- }
182
- }
183
- // note sorting later messages to array's head
184
- msgs.sort((m1, m2) => (m2.deliveryTS - m1.deliveryTS));
185
- if (fromTS !== undefined) {
186
- cutEarlierMsgs(msgs, fromTS);
187
- }
188
- for (const log of msgs) {
189
- this.cachedLogs.set(log.msgId, log);
190
- }
191
- return msgs.map(log => ({
192
- msgId: log.msgId,
193
- msgType: log.msgType,
194
- deliveryTS: log.deliveryTS
195
- }));
196
- }
197
- async getKeyFor(msgId, deliveryTS) {
198
- let log = this.cachedLogs.get(msgId);
199
- if (log) {
200
- return keyInfoFrom(log);
201
- }
202
- log = await this.logs.getMsgOpenedLog(msgId, deliveryTS);
203
- if (!log) {
204
- return undefined;
205
- }
206
- this.cachedLogs.set(log.msgId, log);
207
- return keyInfoFrom(log);
208
- }
209
- async msgExists(msgInfo) {
210
- const foundLog = this.cachedLogs.get(msgInfo.msgId);
211
- return (!!foundLog && (foundLog.deliveryTS === msgInfo.deliveryTS));
212
- }
213
- stopSyncing() { }
214
- }
215
- Object.freeze(JsonBasedIndexedRecords.prototype);
216
- Object.freeze(JsonBasedIndexedRecords);
217
- class MsgLogs {
218
- constructor(fs, latest, fileTSs) {
219
- this.fs = fs;
220
- this.latest = latest;
221
- this.fileTSs = fileTSs;
222
- this.changeProc = new synced_1.SingleProc();
223
- Object.seal(this);
224
- }
225
- static async makeAndStartSyncing(logsFS) {
226
- const fName = logsFileName(undefined);
227
- let latest = await logsFS.readJSONFile(fName)
228
- .catch(notFoundOrReThrow);
229
- if (!latest) {
230
- latest = [];
231
- await logsFS.writeJSONFile(fName, latest, { create: true, exclusive: true });
232
- }
233
- const fileTSs = (await logsFS.listFolder('.'))
234
- .map(f => f.name)
235
- .filter(fName => fName.match(INDEX_FNAME_REGEXP))
236
- .map(fName => parseInt(fName.substring(0, fName.length - LOG_EXT.length)))
237
- .filter(fileTS => !isNaN(fileTS))
238
- .sort(fileTSOrderComparator);
239
- const logs = new MsgLogs(logsFS, latest, fileTSs);
240
- return logs;
241
- }
242
- stopSyncing() {
243
- // XXX fill this with content
244
- }
245
- async add(msgInfo, decrInfo, removeAfter) {
246
- const msg = toMsgOpenedLog(msgInfo, decrInfo, removeAfter);
247
- return this.changeProc.startOrChain(async () => {
248
- const fileTS = this.fileTSforMsgTS(msg.deliveryTS);
249
- const logs = ((fileTS === undefined) ?
250
- this.latest : await this.getLogsFromFile(fileTS));
251
- if (!logs) {
252
- throw (0, error_1.errWithCause)(`${logsFileName(fileTS)} not found`, `Expectation fail: there should be some message records.`);
253
- }
254
- insertInto(logs, msg);
255
- await this.updateLogsFile(fileTS, logs);
256
- });
257
- }
258
- async updateLogsFile(fileTS, logs) {
259
- await this.fs.writeJSONFile(logsFileName(fileTS), logs, { create: false });
260
- }
261
- async getLogsFromFile(fileTS) {
262
- const fName = logsFileName(undefined);
263
- const logs = await this.fs.readJSONFile(fName)
264
- .catch(notFoundOrReThrow);
265
- return logs;
266
- }
267
- fileTSforMsgTS(deliveryTS) {
268
- if ((this.fileTSs.length === 0)
269
- || (deliveryTS >= this.fileTSs[this.fileTSs.length - 1])) {
270
- return undefined;
271
- }
272
- let fileTS = this.fileTSs[this.fileTSs.length - 1];
273
- for (let i = (this.fileTSs.length - 2); i <= 0; i -= 1) {
274
- if (deliveryTS >= this.fileTSs[i]) {
275
- break;
276
- }
277
- else {
278
- fileTS = this.fileTSs[i];
279
- }
280
- }
281
- return fileTS;
282
- }
283
- async remove(msgId, deliveryTS) {
284
- const msgRmLog = {
285
- msgState: 'removed',
286
- msgId,
287
- deliveryTS
288
- };
289
- return this.changeProc.startOrChain(async () => {
290
- this.latest.push(msgRmLog);
291
- const fileTS = this.fileTSforMsgTS(deliveryTS);
292
- if (fileTS === undefined) {
293
- removeMsgOpenedLogFrom(this.latest, msgId);
294
- }
295
- await this.updateLogsFile(undefined, this.latest);
296
- if (fileTS !== undefined) {
297
- this.scheduleRemovalOfMsgOpenedLog(fileTS, msgId);
298
- }
299
- });
300
- }
301
- scheduleRemovalOfMsgOpenedLog(fileTS, msgId) {
302
- this.changeProc.startOrChain(async () => {
303
- const logs = await this.getLogsFromFile(fileTS);
304
- if (!logs) {
305
- return;
306
- }
307
- const changed = removeMsgOpenedLogFrom(logs, msgId);
308
- if (!changed) {
309
- return;
310
- }
311
- await this.updateLogsFile(fileTS, logs);
312
- });
313
- }
314
- async getMsgOpenedLog(msgId, deliveryTS) {
315
- const fileTS = this.fileTSforMsgTS(deliveryTS);
316
- const logs = ((fileTS === undefined) ?
317
- this.latest : await this.getLogsFromFile(fileTS));
318
- if (!logs) {
319
- return undefined;
320
- }
321
- return logs.find(log => ((log.msgId === msgId) && (log.msgState === 'opened')));
322
- }
323
- getLogFileTSs() {
324
- return this.fileTSs.concat([]);
325
- }
326
- getLatestMsgLogs() {
327
- return this.latest.concat([]);
328
- }
329
- }
330
- exports.MsgLogs = MsgLogs;
331
- Object.freeze(MsgLogs.prototype);
332
- Object.freeze(MsgLogs);
333
- Object.freeze(exports);
@@ -1,4 +0,0 @@
1
- import { IndexedRecords } from './logs-n-entries';
2
- type WritableFS = web3n.files.WritableFS;
3
- export declare function makeSqliteBasedIndexedRecords(syncedFS: WritableFS): Promise<IndexedRecords>;
4
- export {};