internxt-crypto 0.0.14 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +44 -72
  2. package/dist/aes-B8TA-JKq.mjs +49 -0
  3. package/dist/aes-B8TA-JKq.mjs.map +1 -0
  4. package/dist/aes-CuW_SPl-.js +67 -0
  5. package/dist/aes-CuW_SPl-.js.map +1 -0
  6. package/dist/asymmetric-crypto.d.mts +8 -31
  7. package/dist/asymmetric-crypto.d.mts.map +1 -1
  8. package/dist/asymmetric-crypto.d.ts +8 -31
  9. package/dist/asymmetric-crypto.d.ts.map +1 -1
  10. package/dist/asymmetric-crypto.js +30 -7
  11. package/dist/asymmetric-crypto.js.map +1 -0
  12. package/dist/asymmetric-crypto.mjs +28 -2
  13. package/dist/asymmetric-crypto.mjs.map +1 -0
  14. package/dist/constants.d.mts +3 -10
  15. package/dist/constants.d.mts.map +1 -1
  16. package/dist/constants.d.ts +3 -10
  17. package/dist/constants.d.ts.map +1 -1
  18. package/dist/constants.js +3 -17
  19. package/dist/constants.js.map +1 -1
  20. package/dist/constants.mjs +3 -10
  21. package/dist/constants.mjs.map +1 -1
  22. package/dist/core-Bj26rNdA.js +227 -0
  23. package/dist/core-Bj26rNdA.js.map +1 -0
  24. package/dist/core-pEGygy6S.mjs +173 -0
  25. package/dist/core-pEGygy6S.mjs.map +1 -0
  26. package/dist/derive-key.d.mts +1 -74
  27. package/dist/derive-key.d.mts.map +1 -1
  28. package/dist/derive-key.d.ts +1 -74
  29. package/dist/derive-key.d.ts.map +1 -1
  30. package/dist/derive-key.js +3 -11
  31. package/dist/derive-key.mjs +2 -2
  32. package/dist/derive-password.d.mts +22 -0
  33. package/dist/derive-password.d.mts.map +1 -0
  34. package/dist/derive-password.d.ts +22 -0
  35. package/dist/derive-password.d.ts.map +1 -0
  36. package/dist/derive-password.js +5 -0
  37. package/dist/derive-password.mjs +3 -0
  38. package/dist/deriveKeysFromKey-C4McqO6_.js +47 -0
  39. package/dist/deriveKeysFromKey-C4McqO6_.js.map +1 -0
  40. package/dist/deriveKeysFromKey-DPU_h7wr.mjs +35 -0
  41. package/dist/deriveKeysFromKey-DPU_h7wr.mjs.map +1 -0
  42. package/dist/{deriveKeysFromPassword-BYaiSGKL.js → deriveKeysFromPassword-C7xAGGsh.js} +5 -172
  43. package/dist/deriveKeysFromPassword-C7xAGGsh.js.map +1 -0
  44. package/dist/{deriveKeysFromPassword-XIwzErlL.mjs → deriveKeysFromPassword-DXgSvTNC.mjs} +6 -125
  45. package/dist/deriveKeysFromPassword-DXgSvTNC.mjs.map +1 -0
  46. package/dist/email-crypto.d.mts +24 -46
  47. package/dist/email-crypto.d.mts.map +1 -1
  48. package/dist/email-crypto.d.ts +24 -46
  49. package/dist/email-crypto.d.ts.map +1 -1
  50. package/dist/email-crypto.js +121 -12
  51. package/dist/email-crypto.js.map +1 -0
  52. package/dist/email-crypto.mjs +115 -2
  53. package/dist/email-crypto.mjs.map +1 -0
  54. package/dist/email-search.d.mts.map +1 -1
  55. package/dist/email-search.d.ts.map +1 -1
  56. package/dist/email-search.js +25 -597
  57. package/dist/email-search.mjs +2 -573
  58. package/dist/{emailEncryptionKey-B9xHw1h_.mjs → emailEncryptionKey-BtSPYd9M.mjs} +22 -45
  59. package/dist/emailEncryptionKey-BtSPYd9M.mjs.map +1 -0
  60. package/dist/{emailEncryptionKey-CQNe5W_T.js → emailEncryptionKey-C4X1Bwy4.js} +22 -45
  61. package/dist/emailEncryptionKey-C4X1Bwy4.js.map +1 -0
  62. package/dist/{genID-3Er5IowC.mjs → genID-D08K7JPr.mjs} +4 -40
  63. package/dist/genID-D08K7JPr.mjs.map +1 -0
  64. package/dist/{genID-YJSl3YhL.js → genID-cWOPmspk.js} +2 -50
  65. package/dist/genID-cWOPmspk.js.map +1 -0
  66. package/dist/hash.d.mts +24 -36
  67. package/dist/hash.d.mts.map +1 -1
  68. package/dist/hash.d.ts +24 -36
  69. package/dist/hash.d.ts.map +1 -1
  70. package/dist/hash.js +88 -10
  71. package/dist/hash.js.map +1 -0
  72. package/dist/hash.mjs +82 -2
  73. package/dist/hash.mjs.map +1 -0
  74. package/dist/index.d.mts +9 -8
  75. package/dist/index.d.mts.map +1 -1
  76. package/dist/index.d.ts +9 -8
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +56 -79
  79. package/dist/index.mjs +13 -11
  80. package/dist/keystore-crypto.d.mts +6 -6
  81. package/dist/keystore-crypto.d.mts.map +1 -1
  82. package/dist/keystore-crypto.d.ts +6 -6
  83. package/dist/keystore-crypto.d.ts.map +1 -1
  84. package/dist/keystore-crypto.js +1 -1
  85. package/dist/keystore-crypto.mjs +1 -1
  86. package/dist/post-quantum-crypto.js +49 -4
  87. package/dist/post-quantum-crypto.js.map +1 -0
  88. package/dist/post-quantum-crypto.mjs +46 -2
  89. package/dist/post-quantum-crypto.mjs.map +1 -0
  90. package/dist/search-B9YM3XAA.mjs +587 -0
  91. package/dist/search-B9YM3XAA.mjs.map +1 -0
  92. package/dist/search-DUTm4-rN.js +731 -0
  93. package/dist/search-DUTm4-rN.js.map +1 -0
  94. package/dist/symmetric-crypto.d.mts +11 -40
  95. package/dist/symmetric-crypto.d.mts.map +1 -1
  96. package/dist/symmetric-crypto.d.ts +11 -40
  97. package/dist/symmetric-crypto.d.ts.map +1 -1
  98. package/dist/symmetric-crypto.js +2 -6
  99. package/dist/symmetric-crypto.mjs +2 -2
  100. package/dist/types.d.mts +13 -40
  101. package/dist/types.d.mts.map +1 -1
  102. package/dist/types.d.ts +13 -40
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js.map +1 -1
  105. package/dist/types.mjs.map +1 -1
  106. package/dist/utils.d.mts +1 -19
  107. package/dist/utils.d.mts.map +1 -1
  108. package/dist/utils.d.ts +1 -19
  109. package/dist/utils.d.ts.map +1 -1
  110. package/dist/utils.js +1 -3
  111. package/dist/utils.mjs +2 -2
  112. package/dist/xwing-CQmQX6hn.mjs +16 -0
  113. package/dist/xwing-CQmQX6hn.mjs.map +1 -0
  114. package/dist/xwing-iRWVRtNE.js +34 -0
  115. package/dist/xwing-iRWVRtNE.js.map +1 -0
  116. package/package.json +3 -2
  117. package/dist/aes-DCb9dJqb.mjs +0 -161
  118. package/dist/aes-DCb9dJqb.mjs.map +0 -1
  119. package/dist/aes-DQo1MyCz.js +0 -203
  120. package/dist/aes-DQo1MyCz.js.map +0 -1
  121. package/dist/deriveKeysFromPassword-BYaiSGKL.js.map +0 -1
  122. package/dist/deriveKeysFromPassword-XIwzErlL.mjs.map +0 -1
  123. package/dist/email-search.js.map +0 -1
  124. package/dist/email-search.mjs.map +0 -1
  125. package/dist/emailEncryptionKey-B9xHw1h_.mjs.map +0 -1
  126. package/dist/emailEncryptionKey-CQNe5W_T.js.map +0 -1
  127. package/dist/emailKeys-C0KhtfNP.js +0 -572
  128. package/dist/emailKeys-C0KhtfNP.js.map +0 -1
  129. package/dist/emailKeys-D4lp9NRv.mjs +0 -488
  130. package/dist/emailKeys-D4lp9NRv.mjs.map +0 -1
  131. package/dist/genID-3Er5IowC.mjs.map +0 -1
  132. package/dist/genID-YJSl3YhL.js.map +0 -1
  133. package/dist/keys-BfcaiAOv.mjs +0 -90
  134. package/dist/keys-BfcaiAOv.mjs.map +0 -1
  135. package/dist/keys-CinT7kEY.js +0 -125
  136. package/dist/keys-CinT7kEY.js.map +0 -1
  137. package/dist/kyber768-SXnIo-Nw.mjs +0 -47
  138. package/dist/kyber768-SXnIo-Nw.mjs.map +0 -1
  139. package/dist/kyber768-WUVc2fIR.js +0 -65
  140. package/dist/kyber768-WUVc2fIR.js.map +0 -1
  141. package/dist/mac-BMb1F0dN.js +0 -170
  142. package/dist/mac-BMb1F0dN.js.map +0 -1
  143. package/dist/mac-ZpRlx8Fs.mjs +0 -116
  144. package/dist/mac-ZpRlx8Fs.mjs.map +0 -1
@@ -1,3 +1,47 @@
1
- import { n as encapsulateKyber, r as generateKyberKeys, t as decapsulateKyber } from "./kyber768-SXnIo-Nw.mjs";
1
+ import { ml_kem768 } from "@noble/post-quantum/ml-kem.js";
2
2
 
3
- export { decapsulateKyber, encapsulateKyber, generateKyberKeys };
3
+ //#region src/post-quantum-crypto/kyber768.ts
4
+ /**
5
+ * Generates public and secret Kyber keys
6
+ *
7
+ * @param seed - The optional seed
8
+ * @returns The generated Kyber key pair.
9
+ */
10
+ function generateKyberKeys(seed) {
11
+ try {
12
+ return ml_kem768.keygen(seed);
13
+ } catch (error) {
14
+ throw new Error("Failed to generate Kyber keys", { cause: error });
15
+ }
16
+ }
17
+ /**
18
+ * Generates a shared secret of 256-bits and encapsulates it to the given public key using Kyber algorithms
19
+ *
20
+ * @param publicKey - The public Kyber key
21
+ * @returns The resulting encapsulation and the generated shared secret.
22
+ */
23
+ function encapsulateKyber(publicKey) {
24
+ try {
25
+ return ml_kem768.encapsulate(publicKey);
26
+ } catch (error) {
27
+ throw new Error("Failed to encapsulate", { cause: error });
28
+ }
29
+ }
30
+ /**
31
+ * Decapsulates the encapsulated shared secret using Kyber algorithms
32
+ *
33
+ * @param cipherText - The encapsulated key
34
+ * @param secretKey - The private Kyber key
35
+ * @returns The resulting decapsulated shared secret.
36
+ */
37
+ function decapsulateKyber(cipherText, secretKey) {
38
+ try {
39
+ return ml_kem768.decapsulate(cipherText, secretKey);
40
+ } catch (error) {
41
+ throw new Error("Failed to decapsulate", { cause: error });
42
+ }
43
+ }
44
+
45
+ //#endregion
46
+ export { decapsulateKyber, encapsulateKyber, generateKyberKeys };
47
+ //# sourceMappingURL=post-quantum-crypto.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-quantum-crypto.mjs","names":["kyber"],"sources":["../src/post-quantum-crypto/kyber768.ts"],"sourcesContent":["import { ml_kem768 as kyber } from '@noble/post-quantum/ml-kem.js';\n\n/**\n * Generates public and secret Kyber keys\n *\n * @param seed - The optional seed\n * @returns The generated Kyber key pair.\n */\nexport function generateKyberKeys(seed?: Uint8Array): {\n publicKey: Uint8Array;\n secretKey: Uint8Array;\n} {\n try {\n return kyber.keygen(seed);\n } catch (error) {\n throw new Error('Failed to generate Kyber keys', { cause: error });\n }\n}\n\n/**\n * Generates a shared secret of 256-bits and encapsulates it to the given public key using Kyber algorithms\n *\n * @param publicKey - The public Kyber key\n * @returns The resulting encapsulation and the generated shared secret.\n */\nexport function encapsulateKyber(publicKey: Uint8Array): {\n cipherText: Uint8Array;\n sharedSecret: Uint8Array;\n} {\n try {\n return kyber.encapsulate(publicKey);\n } catch (error) {\n throw new Error('Failed to encapsulate', { cause: error });\n }\n}\n\n/**\n * Decapsulates the encapsulated shared secret using Kyber algorithms\n *\n * @param cipherText - The encapsulated key\n * @param secretKey - The private Kyber key\n * @returns The resulting decapsulated shared secret.\n */\nexport function decapsulateKyber(cipherText: Uint8Array, secretKey: Uint8Array): Uint8Array {\n try {\n return kyber.decapsulate(cipherText, secretKey);\n } catch (error) {\n throw new Error('Failed to decapsulate', { cause: error });\n }\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,kBAAkB,MAGhC;AACA,KAAI;AACF,SAAOA,UAAM,OAAO,KAAK;UAClB,OAAO;AACd,QAAM,IAAI,MAAM,iCAAiC,EAAE,OAAO,OAAO,CAAC;;;;;;;;;AAUtE,SAAgB,iBAAiB,WAG/B;AACA,KAAI;AACF,SAAOA,UAAM,YAAY,UAAU;UAC5B,OAAO;AACd,QAAM,IAAI,MAAM,yBAAyB,EAAE,OAAO,OAAO,CAAC;;;;;;;;;;AAW9D,SAAgB,iBAAiB,YAAwB,WAAmC;AAC1F,KAAI;AACF,SAAOA,UAAM,YAAY,YAAY,UAAU;UACxC,OAAO;AACd,QAAM,IAAI,MAAM,yBAAyB,EAAE,OAAO,OAAO,CAAC"}
@@ -0,0 +1,587 @@
1
+ import { CONTEXT_INDEX, DB_LABEL, DB_VERSION, MAX_CACHE_SIZE, MAX_EMAIL_PER_BATCH } from "./constants.mjs";
2
+ import { s as UTF8ToUint8 } from "./genID-D08K7JPr.mjs";
3
+ import { t as deriveSymmetricKeyFromContext } from "./deriveKeysFromKey-DPU_h7wr.mjs";
4
+ import { i as encryptEmailBodyWithKey, t as decryptEmailBody } from "./core-pEGygy6S.mjs";
5
+ import { deleteDB, openDB } from "idb";
6
+ import { Index } from "flexsearch";
7
+
8
+ //#region src/email-search/indexedDB.ts
9
+ /**
10
+ * Returns IndexedDB database name for the given user
11
+ *
12
+ * @param userID - The user ID
13
+ * @returns The database name
14
+ */
15
+ const getDatabaseName = (userID) => {
16
+ return `ES:${userID}:DB`;
17
+ };
18
+ /**
19
+ * Opens IndexedDB database for the given user
20
+ *
21
+ * @param userID - The user ID
22
+ * @returns The database
23
+ */
24
+ const openDatabase = async (userID) => {
25
+ try {
26
+ return openDB(getDatabaseName(userID), DB_VERSION, { upgrade(db) {
27
+ if (!db.objectStoreNames.contains(DB_LABEL)) db.createObjectStore(DB_LABEL, { keyPath: "id" }).createIndex("byTime", "params.createdAt");
28
+ } });
29
+ } catch (error) {
30
+ throw new Error(`Cannot open a database for the user ${userID}`, { cause: error });
31
+ }
32
+ };
33
+ /**
34
+ * Closes the IndexedDB database
35
+ *
36
+ * @param esDB - The database
37
+ */
38
+ const closeDatabase = (esDB) => {
39
+ return esDB.close();
40
+ };
41
+ /**
42
+ * Deletes IndexedDB database for the given user
43
+ *
44
+ * @param userID - The user ID
45
+ * @returns The database
46
+ */
47
+ const deleteDatabase = async (userID) => {
48
+ return deleteDB(getDatabaseName(userID));
49
+ };
50
+ /**
51
+ * Derives database encryption key for the given user
52
+ *
53
+ * @param userID - The user ID
54
+ * @returns The symmetric key for protecting database
55
+ */
56
+ const deriveIndexKey = async (baseKey) => {
57
+ return deriveSymmetricKeyFromContext(CONTEXT_INDEX, baseKey);
58
+ };
59
+ /**
60
+ * Encrypts the given email and stores it in the IndexedDB database
61
+ *
62
+ * @param newEmailToStore - The email for storing
63
+ * @param indexKey - The symmetric key for protecting database
64
+ * @param esDB - The database
65
+ */
66
+ const encryptAndStoreEmail = async (newEmailToStore, indexKey, esDB) => {
67
+ try {
68
+ const encryptedEmail = {
69
+ encEmailBody: await encryptEmailBodyWithKey(newEmailToStore.body, indexKey),
70
+ params: newEmailToStore.params,
71
+ id: newEmailToStore.id
72
+ };
73
+ await esDB.put(DB_LABEL, encryptedEmail);
74
+ } catch (error) {
75
+ throw new Error("Cannot encrypt and add the given email to the database", { cause: error });
76
+ }
77
+ };
78
+ /**
79
+ * Encrypts the given set of emails and stores it in the IndexedDB database
80
+ *
81
+ * @param newEmailsToStore - The set of emails for storing
82
+ * @param indexKey - The symmetric key key for protecting database
83
+ * @param esDB - The database
84
+ */
85
+ const encryptAndStoreManyEmail = async (newEmailsToStore, indexKey, esDB) => {
86
+ try {
87
+ const encryptedEmails = await Promise.all(newEmailsToStore.map(async (email) => {
88
+ return {
89
+ encEmailBody: await encryptEmailBodyWithKey(email.body, indexKey),
90
+ params: email.params,
91
+ id: email.id
92
+ };
93
+ }));
94
+ const tr = esDB.transaction(DB_LABEL, "readwrite");
95
+ await Promise.all([...encryptedEmails.map((encEmail) => tr.store.put(encEmail)), tr.done]);
96
+ } catch (error) {
97
+ throw new Error("Cannot encrypt and add emails to the database", { cause: error });
98
+ }
99
+ };
100
+ /**
101
+ * Decrypts the given email
102
+ *
103
+ * @param indexKey - The symmetric key key for protecting database
104
+ * @param encryptedEmail - The encrypted email
105
+ * @returns The decrypted email
106
+ */
107
+ const decryptEmail = async (indexKey, encryptedEmail) => {
108
+ try {
109
+ return {
110
+ body: await decryptEmailBody(encryptedEmail.encEmailBody, indexKey),
111
+ params: encryptedEmail.params,
112
+ id: encryptedEmail.id
113
+ };
114
+ } catch (error) {
115
+ throw new Error("Cannot decrypt the given email", { cause: error });
116
+ }
117
+ };
118
+ /**
119
+ * Fetches the email from the database and decrypts it
120
+ *
121
+ * @param emailID - The email identifier
122
+ * @param indexKey - The symmetric key key for protecting database
123
+ * @param encryptedEmail - The encrypted email
124
+ * @returns The decrypted email
125
+ */
126
+ const getAndDecryptEmail = async (emailID, indexKey, esDB) => {
127
+ try {
128
+ const encryptedEmail = await esDB.get(DB_LABEL, emailID);
129
+ if (!encryptedEmail) throw new Error(`DB cannot find email with id ${emailID}`);
130
+ return decryptEmail(indexKey, encryptedEmail);
131
+ } catch (error) {
132
+ throw new Error(`Cannot fetch the email ${emailID} from the database`, { cause: error });
133
+ }
134
+ };
135
+ /**
136
+ * Fetches all email from the database and decrypts them
137
+ *
138
+ * @param indexKey - The symmetric key key for protecting database
139
+ * @param esDB - The database
140
+ * @returns The decrypted emails
141
+ */
142
+ const getAndDecryptAllEmails = async (indexKey, esDB) => {
143
+ try {
144
+ const encryptedEmails = await esDB.getAll(DB_LABEL);
145
+ return (await Promise.all(encryptedEmails.map(async (encEmail) => {
146
+ return {
147
+ body: await decryptEmailBody(encEmail.encEmailBody, indexKey),
148
+ params: encEmail.params,
149
+ id: encEmail.id
150
+ };
151
+ }))).filter((email) => email !== null);
152
+ } catch (error) {
153
+ throw new Error("Cannot fetch and decrypt all emails from the database", { cause: error });
154
+ }
155
+ };
156
+ /**
157
+ * Deletes the email from the database
158
+ *
159
+ * @param emailID - The email identifier
160
+ * @param esDB - The database
161
+ */
162
+ const deleteEmail = async (emailID, esDB) => {
163
+ await esDB.delete(DB_LABEL, emailID);
164
+ };
165
+ /**
166
+ * Returns the number of stored email
167
+ *
168
+ * @param esDB - The database
169
+ * @returns The number of stored emails
170
+ */
171
+ const getEmailCount = async (esDB) => {
172
+ return await esDB.count(DB_LABEL);
173
+ };
174
+ /**
175
+ * Removes the given number of oldests emails from the database
176
+ *
177
+ * @param emailsToDelete - The number of emails to delete
178
+ * @param esDB - The database
179
+ */
180
+ const deleteOldestEmails = async (emailsToDelete, esDB) => {
181
+ try {
182
+ const tx = esDB.transaction(DB_LABEL, "readwrite");
183
+ let cursor = await tx.store.index("byTime").openCursor();
184
+ let deletedCount = 0;
185
+ while (cursor && deletedCount < emailsToDelete) {
186
+ await cursor.delete();
187
+ deletedCount++;
188
+ cursor = await cursor.continue();
189
+ }
190
+ await tx.done;
191
+ } catch (error) {
192
+ throw new Error(`Cannot delete ${emailsToDelete} oldests emails from the database`, { cause: error });
193
+ }
194
+ };
195
+ /**
196
+ * Enforces the maximum email number in the database
197
+ *
198
+ * @param esDB - The database
199
+ * @param max - The maximum allowed number of emails
200
+ */
201
+ const enforceMaxEmailNumber = async (esDB, max) => {
202
+ try {
203
+ const currentCount = await getEmailCount(esDB);
204
+ if (currentCount <= max) return;
205
+ await deleteOldestEmails(currentCount - max, esDB);
206
+ } catch (error) {
207
+ throw new Error(`Cannot enforce the maximum of ${max} emails on the database`, { cause: error });
208
+ }
209
+ };
210
+ /**
211
+ * Fetches all emails from the database, decrypts them and sortes the results in the specified order
212
+ *
213
+ * @param esDB - The database
214
+ * @param indexKey - The symmetric key key for protecting database
215
+ * @param direction - The order of sorting. If 'next' - oldest first, if 'prev' - newest first
216
+ * @returns Decryped emails in the specified order
217
+ */
218
+ const fetchEmails = async (esDB, indexKey, direction) => {
219
+ try {
220
+ const index = esDB.transaction(DB_LABEL, "readonly").store.index("byTime");
221
+ const encryptedEmails = [];
222
+ let cursor = await index.openCursor(null, direction);
223
+ while (cursor) {
224
+ encryptedEmails.push(cursor.value);
225
+ cursor = await cursor.continue();
226
+ }
227
+ return await Promise.all(encryptedEmails.map((encryptedEmail) => decryptEmail(indexKey, encryptedEmail)));
228
+ } catch (error) {
229
+ throw new Error("Cannot fetch emails from database", { cause: error });
230
+ }
231
+ };
232
+ /**
233
+ * Fetches all emails from the database, decrypts them and sortes the results based on the creation time (newest first)
234
+ *
235
+ * @param esDB - The database
236
+ * @param indexKey - The symmetric Uint8Array key for protecting database
237
+ * @returns The number of stored emails
238
+ */
239
+ const getAllEmailsSortedNewestFirst = async (esDB, indexKey) => {
240
+ return fetchEmails(esDB, indexKey, "prev");
241
+ };
242
+ /**
243
+ * Fetches all emails from the database, decrypts them and sortes the results based on the creation time (oldest first)
244
+ *
245
+ * @param esDB - The database
246
+ * @param indexKey - The symmetric key for protecting database
247
+ * @returns The number of stored emails
248
+ */
249
+ const getAllEmailsSortedOldestFirst = async (esDB, indexKey) => {
250
+ return fetchEmails(esDB, indexKey, "next");
251
+ };
252
+ /**
253
+ * Fetches a batch of emails from the database, decrypts them and sortes the results based on the creation time (newest first)
254
+ *
255
+ * @param esDB - The database
256
+ * @param indexKey - The symmetric key for protecting database
257
+ * @param batchSize - The size of the batch
258
+ * @param startCursor - The starting point (optional). If not given, starts from the beginning
259
+ * @returns The number of stored emails
260
+ */
261
+ const getEmailBatch = async (esDB, indexKey, batchSize, startCursor) => {
262
+ try {
263
+ const index = esDB.transaction(DB_LABEL, "readonly").store.index("byTime");
264
+ const encryptedEmails = [];
265
+ let cursor;
266
+ if (startCursor) {
267
+ const range = IDBKeyRange.upperBound(startCursor, true);
268
+ cursor = await index.openCursor(range, "prev");
269
+ } else cursor = await index.openCursor(null, "prev");
270
+ let count = 0;
271
+ let nextCursor;
272
+ while (cursor && count < batchSize) {
273
+ encryptedEmails.push(cursor.value);
274
+ nextCursor = cursor.key;
275
+ count++;
276
+ cursor = await cursor.continue();
277
+ }
278
+ return {
279
+ emails: await Promise.all(encryptedEmails.map((encryptedEmail) => decryptEmail(indexKey, encryptedEmail))),
280
+ nextCursor: count === batchSize ? nextCursor : void 0
281
+ };
282
+ } catch (error) {
283
+ throw new Error(`Cannot fetch email batch of ${batchSize} from the database`, { cause: error });
284
+ }
285
+ };
286
+
287
+ //#endregion
288
+ //#region src/email-search/utils.ts
289
+ /**
290
+ * Converts an Email type into a Uint8Array array.
291
+ *
292
+ * @param email - The email.
293
+ * @returns The Uint8Array array representation of the Email type.
294
+ */
295
+ function emailToBinary(email) {
296
+ try {
297
+ return UTF8ToUint8(JSON.stringify(email));
298
+ } catch (error) {
299
+ throw new Error("Failed to convert EmailBody to Uint8Array", { cause: error });
300
+ }
301
+ }
302
+
303
+ //#endregion
304
+ //#region src/email-search/mailCache.ts
305
+ /**
306
+ * Estimates the email size in the memory
307
+ *
308
+ * @param email - The email
309
+ * @returns The estimation of the email size
310
+ */
311
+ function sizeOfEmail(email) {
312
+ return emailToBinary(email).byteLength;
313
+ }
314
+ /**
315
+ * Creates an empty cache variable
316
+ *
317
+ * @returns The empty cache
318
+ */
319
+ function createEmptyCache() {
320
+ return {
321
+ esCache: /* @__PURE__ */ new Map(),
322
+ cacheSize: 0,
323
+ isCacheLimited: false,
324
+ isCacheReady: true
325
+ };
326
+ }
327
+ /**
328
+ * Fetches all emails from the database in batches and cahces them
329
+ *
330
+ * @param indexKey - The symmetric key for protecting database
331
+ * @param esCache - The cache to add emails too
332
+ * @param esDB - The database
333
+ */
334
+ const createCacheFromDB = async (indexKey, esDB) => {
335
+ const esCache = createEmptyCache();
336
+ esCache.isCacheReady = false;
337
+ try {
338
+ const count = await getEmailCount(esDB);
339
+ if (!count) {
340
+ esCache.isCacheReady = true;
341
+ return esCache;
342
+ }
343
+ if (count <= MAX_EMAIL_PER_BATCH) addEmailsToCache(await getAllEmailsSortedNewestFirst(esDB, indexKey), esCache);
344
+ else {
345
+ let nextCursor = void 0;
346
+ let cacheFull = false;
347
+ while (!cacheFull) {
348
+ const { emails, nextCursor: newCursor } = await getEmailBatch(esDB, indexKey, MAX_EMAIL_PER_BATCH, nextCursor);
349
+ if (!newCursor || !emails.length) break;
350
+ nextCursor = newCursor;
351
+ if (!addEmailsToCache(emails, esCache)) cacheFull = true;
352
+ }
353
+ }
354
+ return esCache;
355
+ } catch (error) {
356
+ throw new Error(`Email caching failed: ${error}`, { cause: error });
357
+ }
358
+ };
359
+ /**
360
+ * Gets an email from the cache
361
+ *
362
+ * @param emailID - The email identifier
363
+ * @param esCache - The email cache
364
+ * @returns The found email or throws an error
365
+ */
366
+ const getEmailFromCache = async (emailID, esCache) => {
367
+ const email = esCache.esCache.get(emailID);
368
+ if (!email) throw new Error(`Email not found in cache for ID: ${emailID}`);
369
+ return email;
370
+ };
371
+ /**
372
+ * Removes the email from cache
373
+ *
374
+ * @param emailID - The email identifier
375
+ * @param esCache - The email cache
376
+ */
377
+ const deleteEmailFromCache = async (emailID, esCache) => {
378
+ try {
379
+ const size = sizeOfEmail(await getEmailFromCache(emailID, esCache));
380
+ if (esCache.esCache.delete(emailID)) esCache.cacheSize -= size;
381
+ } catch (error) {
382
+ throw new Error(`Failed to delete email with ID ${emailID}`, { cause: error });
383
+ }
384
+ };
385
+ /**
386
+ * Adds emails to the cache
387
+ *
388
+ * @param emails - The emails to add
389
+ * @param esCache - The email cache
390
+ * @returns TRUE if all emails were added sucessfully, or FALSE and error reason.
391
+ */
392
+ function addEmailsToCache(emails, esCache) {
393
+ try {
394
+ for (const email of emails) {
395
+ const result = addEmailToCache(email, esCache);
396
+ if (!result.success) return result;
397
+ }
398
+ return { success: true };
399
+ } catch (error) {
400
+ throw new Error("Failed to add emails to the cache", { cause: error });
401
+ }
402
+ }
403
+ /**
404
+ * Adds email to the cache
405
+ *
406
+ * @param email - The email to add
407
+ * @param esCache - The email cache
408
+ * @returns TRUE if the email was added sucessfully, or FALSE and error reason.
409
+ */
410
+ const addEmailToCache = (email, esCache) => {
411
+ try {
412
+ if (esCache.esCache.has(email.id)) return {
413
+ success: false,
414
+ reason: "email already exists in cache"
415
+ };
416
+ const emailSize = sizeOfEmail(email);
417
+ if (esCache.cacheSize + emailSize > MAX_CACHE_SIZE) {
418
+ esCache.isCacheLimited = true;
419
+ return {
420
+ success: false,
421
+ reason: "hit cache limit"
422
+ };
423
+ }
424
+ esCache.esCache.set(email.id, email);
425
+ esCache.cacheSize += emailSize;
426
+ return { success: true };
427
+ } catch (error) {
428
+ throw new Error("Failed to add email to the cache", { cause: error });
429
+ }
430
+ };
431
+
432
+ //#endregion
433
+ //#region src/email-search/search.ts
434
+ const SUBJECT_OPTIONS = {
435
+ preset: "match",
436
+ tokenize: "forward",
437
+ resolution: 9,
438
+ minlength: 2,
439
+ optimize: true,
440
+ fastupdate: true,
441
+ cache: true
442
+ };
443
+ const BODY_OPTIONS = {
444
+ ...SUBJECT_OPTIONS,
445
+ minlength: 3,
446
+ resolution: 6
447
+ };
448
+ const FROM_OPTIONS = {
449
+ ...SUBJECT_OPTIONS,
450
+ tokenize: "strict",
451
+ minlength: 1
452
+ };
453
+ const TO_OPTIONS = {
454
+ ...SUBJECT_OPTIONS,
455
+ tokenize: "strict",
456
+ minlength: 1
457
+ };
458
+ /**
459
+ * Creates new email search index
460
+ *
461
+ * @returns The email search index
462
+ */
463
+ const createSearchIndex = () => ({
464
+ subjectIndex: new Index(SUBJECT_OPTIONS),
465
+ bodyIndex: new Index(BODY_OPTIONS),
466
+ fromIndex: new Index(FROM_OPTIONS),
467
+ toIndex: new Index(TO_OPTIONS),
468
+ isReady: false
469
+ });
470
+ /**
471
+ * Adds the given email to the email search index
472
+ *
473
+ * @param email - The email to add
474
+ * @param searchIndex - The email search index
475
+ */
476
+ const addEmailToSearchIndex = (email, searchIndex) => {
477
+ try {
478
+ const emailId = email.id;
479
+ searchIndex.subjectIndex.add(emailId, email.body.subject);
480
+ searchIndex.bodyIndex.add(emailId, email.body.text);
481
+ const senderText = `${email.params.sender.name || ""} ${email.params.sender.email || ""}`.trim();
482
+ searchIndex.fromIndex.add(emailId, senderText);
483
+ const recipientsText = (email.params.recipients?.length ? email.params.recipients : [email.params.recipient]).map((recipient) => `${recipient.name || ""} ${recipient.email || ""}`.trim()).join(" ");
484
+ searchIndex.toIndex.add(emailId, recipientsText);
485
+ } catch (error) {
486
+ throw new Error("Failed to add email to the search index", { cause: error });
487
+ }
488
+ };
489
+ /**
490
+ * Removes the email from the email search index
491
+ *
492
+ * @param emailID - The email identifier
493
+ * @param searchIndex - The email search index
494
+ */
495
+ const removeEmailFromSearchIndex = (emailID, searchIndex) => {
496
+ try {
497
+ searchIndex.subjectIndex.remove(emailID);
498
+ searchIndex.bodyIndex.remove(emailID);
499
+ searchIndex.fromIndex.remove(emailID);
500
+ searchIndex.toIndex.remove(emailID);
501
+ } catch (error) {
502
+ throw new Error(`Failed to remove email with ID ${emailID} from the search index`, { cause: error });
503
+ }
504
+ };
505
+ /**
506
+ * Buils the email search index from the email cache
507
+ *
508
+ * @param esCache - The email cache
509
+ * @returns The email search index
510
+ */
511
+ const buildSearchIndexFromCache = async (esCache) => {
512
+ try {
513
+ const searchIndex = createSearchIndex();
514
+ searchIndex.isReady = false;
515
+ for (const email of esCache.esCache.values()) addEmailToSearchIndex(email, searchIndex);
516
+ searchIndex.isReady = true;
517
+ return searchIndex;
518
+ } catch (error) {
519
+ throw new Error("Failed build an email search index from the cache", { cause: error });
520
+ }
521
+ };
522
+ /**
523
+ * Searches in teh
524
+ *
525
+ * @param query - The string to search for
526
+ * @param esCache - The email cache
527
+ * @param searchIndex - The email search index
528
+ * @param opetions - The optional search limitations
529
+ * @returns The result of the email search (emails ans their corresponding result weights)
530
+ */
531
+ const searchEmails = async (query, esCache, searchIndex, options = {}) => {
532
+ try {
533
+ if (!searchIndex.isReady || !query.trim()) return [];
534
+ const { fields = [
535
+ "subject",
536
+ "body",
537
+ "from",
538
+ "to"
539
+ ], limit = 50, boost = {
540
+ subject: 3,
541
+ body: 1,
542
+ from: 2,
543
+ to: 2
544
+ } } = options;
545
+ const results = /* @__PURE__ */ new Map();
546
+ const searchPromises = fields.map(async (field) => {
547
+ let fieldResults;
548
+ switch (field) {
549
+ case "subject":
550
+ fieldResults = await searchIndex.subjectIndex.searchAsync(query);
551
+ break;
552
+ case "body":
553
+ fieldResults = await searchIndex.bodyIndex.searchAsync(query);
554
+ break;
555
+ case "from":
556
+ fieldResults = await searchIndex.fromIndex.searchAsync(query);
557
+ break;
558
+ case "to":
559
+ fieldResults = await searchIndex.toIndex.searchAsync(query);
560
+ break;
561
+ default: return;
562
+ }
563
+ const fieldBoost = boost[field] || 1;
564
+ fieldResults.forEach((emailId) => {
565
+ const currentScore = results.get(String(emailId)) || 0;
566
+ results.set(String(emailId), currentScore + fieldBoost);
567
+ });
568
+ });
569
+ await Promise.all(searchPromises);
570
+ const emailResults = [];
571
+ for (const [emailId, score] of results) {
572
+ const email = esCache.esCache.get(emailId);
573
+ if (email) emailResults.push({
574
+ email,
575
+ score
576
+ });
577
+ }
578
+ emailResults.sort((a, b) => (b.score || 0) - (a.score || 0));
579
+ return emailResults.slice(0, limit);
580
+ } catch (error) {
581
+ throw new Error("Email search failed", { cause: error });
582
+ }
583
+ };
584
+
585
+ //#endregion
586
+ export { getEmailCount as C, getEmailBatch as S, enforceMaxEmailNumber as _, addEmailToCache as a, getAndDecryptAllEmails as b, deleteEmailFromCache as c, deleteDatabase as d, deleteEmail as f, encryptAndStoreManyEmail as g, encryptAndStoreEmail as h, searchEmails as i, getEmailFromCache as l, deriveIndexKey as m, buildSearchIndexFromCache as n, addEmailsToCache as o, deleteOldestEmails as p, removeEmailFromSearchIndex as r, createCacheFromDB as s, addEmailToSearchIndex as t, closeDatabase as u, getAllEmailsSortedNewestFirst as v, openDatabase as w, getAndDecryptEmail as x, getAllEmailsSortedOldestFirst as y };
587
+ //# sourceMappingURL=search-B9YM3XAA.mjs.map