tool-db 2.5.6 → 2.6.2

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 (95) hide show
  1. package/README.md +1 -2
  2. package/bundle.js +1 -1
  3. package/dist/adapters-base/networkAdapter.js +3 -2
  4. package/dist/adapters-base/networkAdapter.js.map +1 -1
  5. package/dist/adapters-base/storageAdapter.d.ts +15 -0
  6. package/dist/adapters-base/storageAdapter.js +68 -0
  7. package/dist/adapters-base/storageAdapter.js.map +1 -1
  8. package/dist/adapters-base/userAdapter.d.ts +2 -2
  9. package/dist/adapters-base/userAdapter.js +46 -2
  10. package/dist/adapters-base/userAdapter.js.map +1 -1
  11. package/dist/crdt/counterCrdt.js +2 -1
  12. package/dist/crdt/counterCrdt.js.map +1 -1
  13. package/dist/crdt/listCrdt.js +2 -2
  14. package/dist/crdt/listCrdt.js.map +1 -1
  15. package/dist/crdt/mapCrdt.js +6 -4
  16. package/dist/crdt/mapCrdt.js.map +1 -1
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.js +11 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/messageHandlers/handleCrdtPut.js +51 -40
  21. package/dist/messageHandlers/handleCrdtPut.js.map +1 -1
  22. package/dist/messageHandlers/handleFunction.js +1 -1
  23. package/dist/messageHandlers/handleFunction.js.map +1 -1
  24. package/dist/messageHandlers/handlePong.js +14 -10
  25. package/dist/messageHandlers/handlePong.js.map +1 -1
  26. package/dist/toolDbAnonSignIn.d.ts +1 -1
  27. package/dist/toolDbAnonSignIn.js +46 -1
  28. package/dist/toolDbAnonSignIn.js.map +1 -1
  29. package/dist/toolDbCrdtGet.js +1 -1
  30. package/dist/toolDbCrdtGet.js.map +1 -1
  31. package/dist/toolDbKeysSignIn.js +52 -8
  32. package/dist/toolDbKeysSignIn.js.map +1 -1
  33. package/dist/toolDbQueryKeys.js +7 -2
  34. package/dist/toolDbQueryKeys.js.map +1 -1
  35. package/dist/toolDbSignIn.js +52 -4
  36. package/dist/toolDbSignIn.js.map +1 -1
  37. package/dist/toolDbSignUp.js +1 -1
  38. package/dist/toolDbSignUp.js.map +1 -1
  39. package/dist/tooldb.js +46 -3
  40. package/dist/tooldb.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -0
  42. package/dist/types/tooldb.d.ts +3 -2
  43. package/dist/utils/encoding/arrayBufferToString.js +2 -6
  44. package/dist/utils/encoding/arrayBufferToString.js.map +1 -1
  45. package/dist/utils/encoding/base64ToUint8.d.ts +1 -0
  46. package/dist/utils/encoding/base64ToUint8.js +17 -0
  47. package/dist/utils/encoding/base64ToUint8.js.map +1 -0
  48. package/dist/utils/encoding/fromBase64.d.ts +1 -0
  49. package/dist/utils/encoding/fromBase64.js +7 -0
  50. package/dist/utils/encoding/fromBase64.js.map +1 -0
  51. package/dist/utils/encoding/stringToArrayBuffer.js +2 -6
  52. package/dist/utils/encoding/stringToArrayBuffer.js.map +1 -1
  53. package/dist/utils/encoding/toBase64.d.ts +1 -0
  54. package/dist/utils/encoding/toBase64.js +7 -0
  55. package/dist/utils/encoding/toBase64.js.map +1 -0
  56. package/dist/utils/encoding/uint8ToBase64.d.ts +1 -0
  57. package/dist/utils/encoding/uint8ToBase64.js +15 -0
  58. package/dist/utils/encoding/uint8ToBase64.js.map +1 -0
  59. package/dist/utils/generateGroupKey.d.ts +5 -0
  60. package/dist/utils/generateGroupKey.js +16 -0
  61. package/dist/utils/generateGroupKey.js.map +1 -0
  62. package/dist/utils/proofOfWork.d.ts +8 -1
  63. package/dist/utils/proofOfWork.js +14 -0
  64. package/dist/utils/proofOfWork.js.map +1 -1
  65. package/dist/utils/verifyMessage.d.ts +1 -1
  66. package/dist/utils/verifyMessage.js +2 -2
  67. package/dist/utils/verifyMessage.js.map +1 -1
  68. package/lib/adapters-base/networkAdapter.ts +3 -2
  69. package/lib/adapters-base/storageAdapter.ts +22 -0
  70. package/lib/adapters-base/userAdapter.ts +2 -2
  71. package/lib/crdt/counterCrdt.ts +2 -1
  72. package/lib/crdt/listCrdt.ts +2 -2
  73. package/lib/crdt/mapCrdt.ts +6 -4
  74. package/lib/index.ts +6 -0
  75. package/lib/messageHandlers/handleCrdtPut.ts +68 -58
  76. package/lib/messageHandlers/handleFunction.ts +1 -1
  77. package/lib/messageHandlers/handlePong.ts +19 -12
  78. package/lib/toolDbAnonSignIn.ts +2 -2
  79. package/lib/toolDbCrdtGet.ts +1 -1
  80. package/lib/toolDbKeysSignIn.ts +4 -7
  81. package/lib/toolDbQueryKeys.ts +8 -2
  82. package/lib/toolDbSignIn.ts +8 -3
  83. package/lib/toolDbSignUp.ts +1 -1
  84. package/lib/tooldb.ts +2 -2
  85. package/lib/types/tooldb.ts +3 -2
  86. package/lib/utils/encoding/arrayBufferToString.ts +3 -7
  87. package/lib/utils/encoding/base64ToUint8.ts +13 -0
  88. package/lib/utils/encoding/fromBase64.ts +4 -0
  89. package/lib/utils/encoding/stringToArrayBuffer.ts +3 -7
  90. package/lib/utils/encoding/toBase64.ts +4 -0
  91. package/lib/utils/encoding/uint8ToBase64.ts +11 -0
  92. package/lib/utils/generateGroupKey.ts +11 -0
  93. package/lib/utils/proofOfWork.ts +16 -1
  94. package/lib/utils/verifyMessage.ts +3 -3
  95. package/package.json +2 -2
@@ -38,73 +38,83 @@ export default function handleCrdtPut(
38
38
  this.store
39
39
  .get(finalMessage.data.k)
40
40
  .then((oldData) => {
41
- try {
42
- const parsedOldData: VerificationData<any> = JSON.parse(oldData);
41
+ if (oldData) {
42
+ try {
43
+ const parsedOldData: VerificationData<any> = JSON.parse(oldData);
43
44
 
44
- let newMessage = finalMessage;
45
+ let newMessage = finalMessage;
45
46
 
46
- // Merge old document with new data incoming and save it
47
- // Add handles for all kinds of CRDT we add
48
- let oldDoc:
49
- | MapCrdt<any>
50
- | ListCrdt<any>
51
- | CounterCrdt<any>
52
- | undefined;
47
+ // Merge old document with new data incoming and save it
48
+ // Add handles for all kinds of CRDT we add
49
+ let oldDoc:
50
+ | MapCrdt<any>
51
+ | ListCrdt<any>
52
+ | CounterCrdt<any>
53
+ | undefined;
53
54
 
54
- if (parsedOldData.c === CRDT_MAP) {
55
- oldDoc = new MapCrdt(
56
- this.userAccount.getAddress() || "",
57
- parsedOldData.v
58
- );
59
- }
55
+ if (parsedOldData.c === CRDT_MAP) {
56
+ oldDoc = new MapCrdt(
57
+ this.userAccount.getAddress() || "",
58
+ parsedOldData.v
59
+ );
60
+ }
60
61
 
61
- if (parsedOldData.c === CRDT_LIST) {
62
- oldDoc = new ListCrdt(
63
- this.userAccount.getAddress() || "",
64
- parsedOldData.v
65
- );
66
- }
62
+ if (parsedOldData.c === CRDT_LIST) {
63
+ oldDoc = new ListCrdt(
64
+ this.userAccount.getAddress() || "",
65
+ parsedOldData.v
66
+ );
67
+ }
67
68
 
68
- if (parsedOldData.c === CRDT_COUNTER) {
69
- oldDoc = new CounterCrdt(
70
- this.userAccount.getAddress() || "",
71
- parsedOldData.v
72
- );
73
- }
69
+ if (parsedOldData.c === CRDT_COUNTER) {
70
+ oldDoc = new CounterCrdt(
71
+ this.userAccount.getAddress() || "",
72
+ parsedOldData.v
73
+ );
74
+ }
74
75
 
75
- let changesMerged:
76
- | MapChanges<any>[]
77
- | ListChanges<any>[]
78
- | CounterChanges[] = [];
76
+ let changesMerged:
77
+ | MapChanges<any>[]
78
+ | ListChanges<any>[]
79
+ | CounterChanges[] = [];
79
80
 
80
- if (oldDoc) {
81
- oldDoc.mergeChanges(finalMessage.data.v);
82
- changesMerged = oldDoc.getChanges();
83
- }
84
- newMessage = {
85
- ...finalMessage,
86
- };
87
- newMessage.data.v = changesMerged;
81
+ if (oldDoc) {
82
+ oldDoc.mergeChanges(finalMessage.data.v);
83
+ changesMerged = oldDoc.getChanges();
84
+ }
85
+ newMessage = {
86
+ ...finalMessage,
87
+ };
88
+ newMessage.data.v = changesMerged;
88
89
 
89
- if (parsedOldData.t < finalMessage.data.t) {
90
- const key = newMessage.data.k;
91
- this.triggerKeyListener(key, newMessage.data);
92
- this.store
93
- .put(newMessage.data.k, JSON.stringify(newMessage.data))
94
- .catch((e) => {
95
- // do nothing
96
- });
97
- } else {
98
- const key = message.data.k;
99
- this.triggerKeyListener(key, parsedOldData);
90
+ if (parsedOldData.t < finalMessage.data.t) {
91
+ const key = newMessage.data.k;
92
+ this.triggerKeyListener(key, newMessage.data);
93
+ this.store
94
+ .put(newMessage.data.k, JSON.stringify(newMessage.data))
95
+ .catch((e) => {
96
+ // do nothing
97
+ });
98
+ } else {
99
+ const key = message.data.k;
100
+ this.triggerKeyListener(key, parsedOldData);
101
+ }
102
+ // } else {
103
+ // this.logger(
104
+ // `${message.k} has old data, but its newer. old ${parsedOldData.t} < new ${message.t}`
105
+ // );
106
+ // }
107
+ } catch (e) {
108
+ this.logger("Couldnt parse crdt data", oldData, e);
100
109
  }
101
- // } else {
102
- // this.logger(
103
- // `${message.k} has old data, but its newer. old ${parsedOldData.t} < new ${message.t}`
104
- // );
105
- // }
106
- } catch (e) {
107
- this.logger("Couldnt parse crdt data", oldData, e);
110
+ } else {
111
+ const key = finalMessage.data.k;
112
+ this.triggerKeyListener(key, finalMessage.data);
113
+ this.store
114
+ .put(finalMessage.data.k, JSON.stringify(finalMessage.data))
115
+ .catch((e) => {
116
+ this.logger("Couldnt insert crdt data", e);
117
+ });
108
118
  }
109
119
  })
110
120
  .catch((e) => {
@@ -22,7 +22,7 @@ export default function handleFunction(
22
22
  })
23
23
  .catch((e) => {
24
24
  const messageReturn: FunctionReturnMessage = {
25
- return: JSON.stringify(e),
25
+ return: e.toString(),
26
26
  type: "functionReturn",
27
27
  id: message.id,
28
28
  code: "ERR",
@@ -11,19 +11,26 @@ export default function handlePong(
11
11
  }
12
12
 
13
13
  message.servers.forEach((peer) => {
14
- verifyPeer(this, peer).then((verified) => {
15
- // Verify integrity and topic
16
- if (verified && peer.topic === this.options.topic) {
17
- // Add this peer to our list of peers
18
- const filteredPeers = this.serverPeers.filter(
19
- (p) => p.address === peer.address
20
- );
21
- if (filteredPeers.length === 0 && peer.host && peer.port) {
22
- // Add this peer to the list
23
- this.serverPeers.push(peer);
14
+ // Check for duplicates first (synchronously) to avoid race conditions
15
+ const filteredPeers = this.serverPeers.filter(
16
+ (p) => p.address === peer.address
17
+ );
18
+
19
+ if (filteredPeers.length === 0 && peer.host && peer.port) {
20
+ verifyPeer(this, peer).then((verified) => {
21
+ // Verify integrity and topic
22
+ if (verified && peer.topic === this.options.topic) {
23
+ // Double-check for duplicates after async verification
24
+ const recheck = this.serverPeers.filter(
25
+ (p) => p.address === peer.address
26
+ );
27
+ if (recheck.length === 0) {
28
+ // Add this peer to the list
29
+ this.serverPeers.push(peer);
30
+ }
24
31
  }
25
- }
26
- });
32
+ });
33
+ }
27
34
  });
28
35
 
29
36
  this.onPeerConnect(this.peerAccount.getAddress() || "");
@@ -1,5 +1,5 @@
1
1
  import { ToolDb } from ".";
2
2
 
3
- export default function toolDbAnonSignIn(this: ToolDb): void {
4
- this.userAccount.anonUser();
3
+ export default async function toolDbAnonSignIn(this: ToolDb): Promise<void> {
4
+ await this.userAccount.anonUser();
5
5
  }
@@ -41,7 +41,7 @@ export default function toolDbCrdtGet<T = any>(
41
41
  resolve(null);
42
42
  }
43
43
  })
44
- .catch((e) => reject(null));
44
+ .catch((e) => resolve(null));
45
45
  }, timeoutMs);
46
46
 
47
47
  this.addIdListener(msgId, (msg) => {
@@ -1,16 +1,13 @@
1
1
  import { ToolDb, randomAnimal } from ".";
2
2
 
3
- export default function toolDbKeysSignIn(
3
+ export default async function toolDbKeysSignIn(
4
4
  this: ToolDb,
5
5
  privateKey: string,
6
6
  username?: string
7
7
  ): Promise<unknown> {
8
8
  if (!this.userAccount) return Promise.resolve(undefined);
9
9
 
10
- return this.userAccount
11
- .getAccountFromPrivate(privateKey)
12
- .then((newAccount) => {
13
- this.userAccount.setUser(newAccount, username || randomAnimal());
14
- return newAccount;
15
- });
10
+ const newAccount = await this.userAccount.getAccountFromPrivate(privateKey);
11
+ await this.userAccount.setUser(newAccount, username || randomAnimal());
12
+ return newAccount;
16
13
  }
@@ -27,14 +27,18 @@ export default function toolDbQueryKeys(
27
27
  let foundKeys: string[] = [];
28
28
  let timeout: NodeJS.Timeout | undefined;
29
29
 
30
+ let gotLocalKeys = false;
31
+
30
32
  this.store
31
33
  .query(finalKey)
32
34
  .then((localKeys) => {
33
35
  foundKeys = [...foundKeys, ...localKeys];
36
+ gotLocalKeys = true;
34
37
  timeout = setTimeout(finishListening, timeoutMs);
35
38
  })
36
39
  .catch((e) => {
37
- // do nothing
40
+ gotLocalKeys = true;
41
+ timeout = setTimeout(finishListening, timeoutMs);
38
42
  });
39
43
 
40
44
  const finishListening = () => {
@@ -50,7 +54,9 @@ export default function toolDbQueryKeys(
50
54
  if (timeout) {
51
55
  clearTimeout(timeout);
52
56
  }
53
- timeout = setTimeout(finishListening, timeoutMs);
57
+ if (gotLocalKeys) {
58
+ timeout = setTimeout(finishListening, timeoutMs);
59
+ }
54
60
  }
55
61
  });
56
62
 
@@ -7,7 +7,7 @@ export default function toolDbSignIn(
7
7
  to?: string[]
8
8
  ): Promise<unknown | undefined> {
9
9
  return new Promise((resolve, reject) => {
10
- this.getData<unknown>(`==${user}`, false, 5000, to).then((_user) => {
10
+ this.getData<unknown>(`==${user}`, false, 2000, to).then((_user) => {
11
11
  if (!_user) {
12
12
  reject("Could not find user");
13
13
  return;
@@ -16,17 +16,22 @@ export default function toolDbSignIn(
16
16
  try {
17
17
  this.userAccount
18
18
  .decryptAccount(_user, sha256(password))
19
- .then((newAccount) => {
20
- this.userAccount.setUser(
19
+ .then(async (newAccount) => {
20
+ await this.userAccount.setUser(
21
21
  newAccount,
22
22
  user || `Anonymous ${randomAnimal()}`
23
23
  );
24
24
 
25
25
  resolve(_user);
26
+ })
27
+ .catch((e) => {
28
+ reject(e);
26
29
  });
27
30
  } catch (e) {
28
31
  reject(e);
29
32
  }
33
+ }).catch((e) => {
34
+ reject(e);
30
35
  });
31
36
  });
32
37
  }
@@ -25,7 +25,7 @@ export default async function toolDbSignUp(
25
25
  userData
26
26
  )}${account.getAddress()}${timestamp}`;
27
27
 
28
- proofOfWork(userDataString, 0)
28
+ proofOfWork(userDataString, this.options.pow)
29
29
  .then(({ hash, nonce }) => {
30
30
  account.signData(hash).then((signature) => {
31
31
  const signupMessage: VerificationData = {
package/lib/tooldb.ts CHANGED
@@ -320,8 +320,8 @@ export default class ToolDb extends EventEmitter {
320
320
  .then((val) => {
321
321
  this.peerAccount
322
322
  .decryptAccount(JSON.parse(val), DEFAULT_KEYS)
323
- .then((a) => {
324
- this.peerAccount.setUser(a, randomAnimal());
323
+ .then(async (a) => {
324
+ await this.peerAccount.setUser(a, randomAnimal());
325
325
  })
326
326
  .finally(() => {
327
327
  this.emit("init", this.userAccount.getAddress());
@@ -46,9 +46,10 @@ export interface ToolDbOptions {
46
46
  */
47
47
  wait: number;
48
48
  /**
49
- * Port to listen incoming connections (server only)
49
+ * Proof of work difficulty (number of leading zeroes required).
50
+ * Set to null to bypass POW calculations entirely (useful for testing/CI).
50
51
  */
51
- pow: number;
52
+ pow: number | null;
52
53
  /**
53
54
  * Whether we are a server or not
54
55
  */
@@ -1,8 +1,4 @@
1
- export default function arrayBufferToString(arr: ArrayBuffer) {
2
- const byteArray = new Uint8Array(arr);
3
- let byteString = "";
4
- for (let i = 0; i < byteArray.byteLength; i += 1) {
5
- byteString += String.fromCodePoint(byteArray[i]);
6
- }
7
- return byteString;
1
+ export default function arrayBufferToString(arr: ArrayBuffer): string {
2
+ const decoder = new TextDecoder();
3
+ return decoder.decode(arr);
8
4
  }
@@ -0,0 +1,13 @@
1
+ import fromBase64 from "./fromBase64";
2
+
3
+ export default function base64ToUint8(based: string): Uint8Array {
4
+ const str = fromBase64(based);
5
+
6
+ const buf = new ArrayBuffer(str.length);
7
+ const bufView = new Uint8Array(buf);
8
+ for (let i = 0, strLen = str.length; i < strLen; i += 1) {
9
+ bufView[i] = str.charCodeAt(i);
10
+ }
11
+ return bufView;
12
+ }
13
+
@@ -0,0 +1,4 @@
1
+ export default function fromBase64(str: string) {
2
+ return decodeURIComponent(escape(global.atob(str)));
3
+ }
4
+
@@ -1,8 +1,4 @@
1
- export default function stringToArrayBuffer(str: string) {
2
- const buf = new ArrayBuffer(str.length);
3
- const bufView = new Uint8Array(buf);
4
- for (let i = 0, strLen = str.length; i < strLen; i += 1) {
5
- bufView[i] = str.charCodeAt(i);
6
- }
7
- return buf;
1
+ export default function stringToArrayBuffer(str: string): ArrayBuffer {
2
+ const encoder = new TextEncoder();
3
+ return encoder.encode(str).buffer;
8
4
  }
@@ -0,0 +1,4 @@
1
+ export default function toBase64(str: string) {
2
+ return global.btoa(unescape(encodeURIComponent(str)));
3
+ }
4
+
@@ -0,0 +1,11 @@
1
+ import toBase64 from "./toBase64";
2
+
3
+ export default function uint8ToBase64(byteArray: Uint8Array): string {
4
+ let byteString = "";
5
+ for (let i = 0; i < byteArray.byteLength; i += 1) {
6
+ byteString += String.fromCodePoint(byteArray[i]);
7
+ }
8
+
9
+ return toBase64(byteString);
10
+ }
11
+
@@ -0,0 +1,11 @@
1
+ import arrayBufferToHex from "./encoding/arrayBufferToHex";
2
+
3
+ /**
4
+ * Generate a random 256-bit symmetric key for group encryption.
5
+ * Returns the key as a hex string for easy storage and transmission.
6
+ */
7
+ export default function generateGroupKey(): string {
8
+ const key = crypto.getRandomValues(new Uint8Array(32));
9
+ return arrayBufferToHex(key.buffer);
10
+ }
11
+
@@ -1,10 +1,25 @@
1
1
  import { sha256 } from "..";
2
2
 
3
+ /**
4
+ * Calculates proof of work by finding a nonce that produces a hash
5
+ * with the required number of leading zeroes.
6
+ * @param value The value to hash
7
+ * @param difficulty Number of leading zeroes required. Pass null to skip POW entirely.
8
+ * @returns Promise with nonce and hash
9
+ */
3
10
  export default function proofOfWork(
4
11
  value: string,
5
- difficulty: number
12
+ difficulty: number | null
6
13
  ): Promise<{ nonce: number; hash: string }> {
7
14
  return new Promise((resolve) => {
15
+ // When difficulty is null, skip POW calculation entirely
16
+ // Just return hash with nonce 0 (useful for testing/CI)
17
+ if (difficulty === null || difficulty === 0) {
18
+ const hash = sha256(`${value}0`);
19
+ resolve({ nonce: 0, hash });
20
+ return;
21
+ }
22
+
8
23
  let nonce = 0;
9
24
  let hash = sha256(`${value}${nonce}`);
10
25
  while (hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) {
@@ -9,7 +9,7 @@ import { ToolDb, VerifyResult, VerificationData, sha256 } from "..";
9
9
  export default async function verifyMessage<T>(
10
10
  this: ToolDb,
11
11
  msg: Partial<VerificationData<T>>,
12
- pow = 0
12
+ pow: number | null = 0
13
13
  ): Promise<VerifyResult> {
14
14
  // this.logger("verify: ", msg);
15
15
  const strData = JSON.stringify(msg.v);
@@ -66,8 +66,8 @@ export default async function verifyMessage<T>(
66
66
  // Verify hash and nonce (adjust zeroes for difficulty of the network)
67
67
  // While this POW does not enforce security per-se, it does make it harder
68
68
  // for attackers to spam the network, and could be adjusted by peers.
69
- // Disabled for now because it is painful on large requests
70
- if (pow > 0) {
69
+ // When pow is null, skip POW verification entirely (useful for testing/CI)
70
+ if (pow !== null && pow > 0) {
71
71
  if (msg.h.slice(0, pow) !== new Array(pow).fill("0").join("")) {
72
72
  // this.logger("No valid hash (no pow)");
73
73
  return VerifyResult.NoProofOfWork;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tool-db",
3
- "version": "2.5.6",
3
+ "version": "2.6.2",
4
4
  "description": "A decentralized database model for federated p2p networks.n",
5
5
  "author": "Manwe <manuel.etchegaray7@gmail.com>",
6
6
  "homepage": "https://github.com/Manwe-777/tool-db#readme",
@@ -28,5 +28,5 @@
28
28
  "typescript": "^4.7.4",
29
29
  "uglify-js": "^3.16.2"
30
30
  },
31
- "gitHead": "ca8b876dcf1e78ad92d9369961d7479e07df0954"
31
+ "gitHead": "d1ce553320888c1c5f0b99d6e7c06580cb4dadfd"
32
32
  }