@majikah/majik-message 0.3.7 → 0.3.8
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.
- package/dist/core/contacts/majik-contact-manager.d.ts +7 -1
- package/dist/core/contacts/majik-contact-manager.js +102 -2
- package/dist/core/contacts/types.d.ts +1 -0
- package/dist/core/storage/chats/adapter-sql.js +21 -20
- package/dist/core/storage/contact-directory/contacts/adapter-sql.d.ts +2 -0
- package/dist/core/storage/contact-directory/contacts/adapter-sql.js +35 -11
- package/dist/core/storage/contact-directory/groups/adapter-sql.js +10 -9
- package/dist/core/storage/keystore/adapter-sql.d.ts +2 -0
- package/dist/core/storage/keystore/adapter-sql.js +35 -11
- package/dist/core/storage/sql-schema.d.ts +15 -0
- package/dist/core/storage/sql-schema.js +32 -18
- package/dist/core/storage/storage-adapter.d.ts +8 -0
- package/dist/majik-message.d.ts +5 -0
- package/dist/majik-message.js +24 -1
- package/package.json +3 -3
|
@@ -2,9 +2,11 @@ import { MajikContact, MajikContactData, MajikContactGroup, MajikContactGroupMet
|
|
|
2
2
|
import { MajikContactDirectory } from "./majik-contact-directory";
|
|
3
3
|
import { MajikContactGroupManager } from "./majik-contact-groups";
|
|
4
4
|
import { MAJIK_API_RESPONSE } from "../types";
|
|
5
|
-
import { MajikContactManagerJSON } from "./types";
|
|
5
|
+
import { ContactManagerQueryMode, MajikContactManagerJSON } from "./types";
|
|
6
6
|
import { MajikContactStorageAdapter } from "../storage/contact-directory/contacts/_types";
|
|
7
7
|
import { MajikContactGroupStorageAdapter } from "../storage/contact-directory/groups/_types";
|
|
8
|
+
import { MajikRecipient } from "@majikah/majik-envelope";
|
|
9
|
+
import { ExpectedSigner } from "@majikah/majik-signature";
|
|
8
10
|
export interface MajikContactManagerAdapters {
|
|
9
11
|
contacts?: MajikContactStorageAdapter;
|
|
10
12
|
groups?: MajikContactGroupStorageAdapter;
|
|
@@ -83,6 +85,10 @@ export declare class MajikContactManager {
|
|
|
83
85
|
getContact(id: string): MajikContact | undefined;
|
|
84
86
|
getContactByFingerprint(fingerprint: string): MajikContact | undefined;
|
|
85
87
|
getContactByPublicKeyBase64(publicKeyBase64: string): Promise<MajikContact | undefined>;
|
|
88
|
+
getContactsByIds(ids: string[], strict?: boolean): MajikContact[];
|
|
89
|
+
getContactsByPublicKeys(publicKeys: string[], strict?: boolean): Promise<MajikContact[]>;
|
|
90
|
+
getMajikRecipients(mode: ContactManagerQueryMode | undefined, input: string[], strict?: boolean): Promise<MajikRecipient[]>;
|
|
91
|
+
getExpectedSigners(mode: ContactManagerQueryMode | undefined, input: string[], strict?: boolean): Promise<ExpectedSigner[]>;
|
|
86
92
|
hasContact(id: string): boolean;
|
|
87
93
|
hasFingerprint(fingerprint: string): boolean;
|
|
88
94
|
hasContactByPublicKeyBase64(publicKeyBase64: string): Promise<boolean>;
|
|
@@ -2,11 +2,12 @@ import { MajikContact, } from "@majikah/majik-contact";
|
|
|
2
2
|
import { MajikContactDirectory } from "./majik-contact-directory";
|
|
3
3
|
import { MajikContactGroupManager } from "./majik-contact-groups";
|
|
4
4
|
import { MajikContactManagerError } from "./errors";
|
|
5
|
-
import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, } from "../utils/utilities";
|
|
5
|
+
import { arrayBufferToBase64, arrayToBase64, base64ToArrayBuffer, base64ToUint8Array, } from "../utils/utilities";
|
|
6
6
|
import { KEY_ALGO } from "../crypto/constants";
|
|
7
7
|
import { gunzipSync, gzipSync } from "fflate";
|
|
8
8
|
import { InMemoryContactAdapter } from "../storage/contact-directory/contacts/adapter-memory";
|
|
9
9
|
import { InMemoryContactGroupAdapter } from "../storage/contact-directory/groups/adapter-memory";
|
|
10
|
+
import { MajikEnvelope } from "@majikah/majik-envelope";
|
|
10
11
|
export class MajikContactManager {
|
|
11
12
|
directory;
|
|
12
13
|
groupManager;
|
|
@@ -191,7 +192,106 @@ export class MajikContactManager {
|
|
|
191
192
|
return this.directory.getContactByFingerprint(fingerprint);
|
|
192
193
|
}
|
|
193
194
|
async getContactByPublicKeyBase64(publicKeyBase64) {
|
|
194
|
-
return this.directory.getContactByPublicKeyBase64(publicKeyBase64);
|
|
195
|
+
return await this.directory.getContactByPublicKeyBase64(publicKeyBase64);
|
|
196
|
+
}
|
|
197
|
+
getContactsByIds(ids, strict = false) {
|
|
198
|
+
if (!ids?.length)
|
|
199
|
+
return [];
|
|
200
|
+
const seen = new Set();
|
|
201
|
+
const results = [];
|
|
202
|
+
for (const id of ids) {
|
|
203
|
+
if (seen.has(id))
|
|
204
|
+
continue;
|
|
205
|
+
seen.add(id);
|
|
206
|
+
const contact = this.directory.getContact(id);
|
|
207
|
+
if (!contact) {
|
|
208
|
+
if (strict) {
|
|
209
|
+
throw new MajikContactManagerError(`Contact not found: ${id}`);
|
|
210
|
+
}
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
results.push(contact);
|
|
214
|
+
}
|
|
215
|
+
return results;
|
|
216
|
+
}
|
|
217
|
+
async getContactsByPublicKeys(publicKeys, strict = false) {
|
|
218
|
+
if (!publicKeys?.length)
|
|
219
|
+
return [];
|
|
220
|
+
const uniqueKeys = [...new Set(publicKeys)];
|
|
221
|
+
const contacts = await Promise.all(uniqueKeys.map(async (key) => {
|
|
222
|
+
const contact = await this.directory.getContactByPublicKeyBase64(key);
|
|
223
|
+
if (!contact && strict) {
|
|
224
|
+
throw new MajikContactManagerError(`Contact not found for publicKey: ${key}`);
|
|
225
|
+
}
|
|
226
|
+
return contact;
|
|
227
|
+
}));
|
|
228
|
+
return contacts.filter((c) => Boolean(c));
|
|
229
|
+
}
|
|
230
|
+
async getMajikRecipients(mode = "id", input, strict) {
|
|
231
|
+
if (!input?.length)
|
|
232
|
+
throw new MajikContactManagerError("At least 1 id/key is required");
|
|
233
|
+
const contacts = mode === "public_key"
|
|
234
|
+
? await this.getContactsByPublicKeys(input, strict)
|
|
235
|
+
: this.getContactsByIds(input, strict);
|
|
236
|
+
if (!contacts || contacts.length === 0)
|
|
237
|
+
return [];
|
|
238
|
+
const recipients = [];
|
|
239
|
+
const seen = new Set();
|
|
240
|
+
const invalidContacts = [];
|
|
241
|
+
for (const contact of contacts) {
|
|
242
|
+
if (!contact)
|
|
243
|
+
continue;
|
|
244
|
+
// dedupe by fingerprint
|
|
245
|
+
if (seen.has(contact.fingerprint))
|
|
246
|
+
continue;
|
|
247
|
+
const mlPubKey = base64ToUint8Array(contact.mlKey);
|
|
248
|
+
if (!mlPubKey) {
|
|
249
|
+
invalidContacts.push(contact.fingerprint);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const builtMajikRecipient = await MajikEnvelope.buildMajikRecipientFromContact(contact);
|
|
253
|
+
recipients.push(builtMajikRecipient);
|
|
254
|
+
seen.add(contact.fingerprint);
|
|
255
|
+
}
|
|
256
|
+
if (invalidContacts.length > 0) {
|
|
257
|
+
throw new MajikContactManagerError(`Invalid ML-KEM public key for contact(s): ${invalidContacts.join(", ")}`);
|
|
258
|
+
}
|
|
259
|
+
return recipients;
|
|
260
|
+
}
|
|
261
|
+
async getExpectedSigners(mode = "id", input, strict) {
|
|
262
|
+
if (!input?.length)
|
|
263
|
+
throw new MajikContactManagerError("At least 1 id/key is required");
|
|
264
|
+
const contacts = mode === "public_key"
|
|
265
|
+
? await this.getContactsByPublicKeys(input, strict)
|
|
266
|
+
: this.getContactsByIds(input, strict);
|
|
267
|
+
if (!contacts || contacts.length === 0)
|
|
268
|
+
return [];
|
|
269
|
+
const signers = [];
|
|
270
|
+
const seen = new Set();
|
|
271
|
+
const invalidContacts = [];
|
|
272
|
+
for (const contact of contacts) {
|
|
273
|
+
if (!contact)
|
|
274
|
+
continue;
|
|
275
|
+
// dedupe by fingerprint
|
|
276
|
+
if (seen.has(contact.fingerprint))
|
|
277
|
+
continue;
|
|
278
|
+
const mlDsaPublicKey = contact.mlDsaPublicKeyBase64;
|
|
279
|
+
if (!mlDsaPublicKey?.trim()) {
|
|
280
|
+
invalidContacts.push(contact.fingerprint);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const expectedSigner = {
|
|
284
|
+
edPublicKey: contact.edPublicKeyBase64,
|
|
285
|
+
mlDsaPublicKey: contact.mlDsaPublicKeyBase64,
|
|
286
|
+
signerId: contact.fingerprint,
|
|
287
|
+
};
|
|
288
|
+
signers.push(expectedSigner);
|
|
289
|
+
seen.add(contact.fingerprint);
|
|
290
|
+
}
|
|
291
|
+
if (invalidContacts.length > 0) {
|
|
292
|
+
throw new MajikContactManagerError(`Invalid ML-KEM public key for contact(s): ${invalidContacts.join(", ")}`);
|
|
293
|
+
}
|
|
294
|
+
return signers;
|
|
195
295
|
}
|
|
196
296
|
hasContact(id) {
|
|
197
297
|
return this.directory.hasContact(id);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SerializedMajikContact, SerializedMajikContactGroup } from "@majikah/majik-contact";
|
|
2
|
+
export type ContactManagerQueryMode = "id" | "public_key";
|
|
2
3
|
export interface MajikContactManagerJSON {
|
|
3
4
|
contacts: MajikContactDirectoryData;
|
|
4
5
|
groups: MajikContactGroupManagerData;
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
+
import { MAJIKAH_SQL_TABLES } from "../sql-schema";
|
|
1
2
|
export class SQLiteInvoiceAdapter {
|
|
2
3
|
db;
|
|
3
4
|
constructor(db) {
|
|
4
5
|
this.db = db;
|
|
5
6
|
}
|
|
6
|
-
async save(message, source =
|
|
7
|
-
const resolvedSource = source ??
|
|
8
|
-
await this.db.run(`INSERT OR REPLACE INTO
|
|
7
|
+
async save(message, source = `local`) {
|
|
8
|
+
const resolvedSource = source ?? `local`;
|
|
9
|
+
await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}
|
|
9
10
|
(id, json, created_at, source)
|
|
10
11
|
VALUES (?, ?, ?, ?)`, [message.id, JSON.stringify(message), message.timestamp, resolvedSource]);
|
|
11
12
|
}
|
|
12
13
|
async getById(id, source) {
|
|
13
14
|
const row = source
|
|
14
|
-
? await this.db.get(
|
|
15
|
-
: await this.db.get(
|
|
15
|
+
? await this.db.get(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ? AND source = ?`, [id, source])
|
|
16
|
+
: await this.db.get(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ?`, [id]);
|
|
16
17
|
return row ? JSON.parse(row.json) : null;
|
|
17
18
|
}
|
|
18
19
|
async list(source) {
|
|
19
20
|
const rows = source
|
|
20
|
-
? await this.db.all(
|
|
21
|
-
: await this.db.all(
|
|
21
|
+
? await this.db.all(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE source = ?`, [source])
|
|
22
|
+
: await this.db.all(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}`);
|
|
22
23
|
return rows.map((r) => JSON.parse(r.json));
|
|
23
24
|
}
|
|
24
25
|
async remove(id, source) {
|
|
@@ -26,42 +27,42 @@ export class SQLiteInvoiceAdapter {
|
|
|
26
27
|
if (!exists)
|
|
27
28
|
return false;
|
|
28
29
|
if (source) {
|
|
29
|
-
await this.db.run(
|
|
30
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ? AND source = ?`, [id, source]);
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
|
-
await this.db.run(
|
|
33
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ?`, [id]);
|
|
33
34
|
}
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
36
37
|
async clear(source) {
|
|
37
38
|
if (source) {
|
|
38
|
-
await this.db.run(
|
|
39
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE source = ?`, [
|
|
39
40
|
source,
|
|
40
41
|
]);
|
|
41
42
|
}
|
|
42
43
|
else {
|
|
43
|
-
await this.db.run(
|
|
44
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
async count(source) {
|
|
47
48
|
const row = source
|
|
48
|
-
? await this.db.get(
|
|
49
|
-
: await this.db.get(
|
|
49
|
+
? await this.db.get(`SELECT COUNT(*) as n FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE source = ?`, [source])
|
|
50
|
+
: await this.db.get(`SELECT COUNT(*) as n FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}`);
|
|
50
51
|
return row?.n ?? 0;
|
|
51
52
|
}
|
|
52
53
|
async exists(id, source) {
|
|
53
54
|
const row = source
|
|
54
|
-
? await this.db.get(
|
|
55
|
-
: await this.db.get(
|
|
55
|
+
? await this.db.get(`SELECT 1 FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ? AND source = ?`, [id, source])
|
|
56
|
+
: await this.db.get(`SELECT 1 FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ?`, [id]);
|
|
56
57
|
return !!row;
|
|
57
58
|
}
|
|
58
|
-
async bulkSave(messages, source =
|
|
59
|
+
async bulkSave(messages, source = `local`) {
|
|
59
60
|
if (messages.length === 0)
|
|
60
61
|
return;
|
|
61
|
-
const resolvedSource = source ??
|
|
62
|
+
const resolvedSource = source ?? `local`;
|
|
62
63
|
await this.db.transaction(async (tx) => {
|
|
63
64
|
for (const msg of messages) {
|
|
64
|
-
await tx.run(`INSERT OR REPLACE INTO
|
|
65
|
+
await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}
|
|
65
66
|
(id, json, created_at, source)
|
|
66
67
|
VALUES (?, ?, ?, ?, ?, ?)`, [msg.id, JSON.stringify(msg), msg.timestamp, resolvedSource]);
|
|
67
68
|
}
|
|
@@ -73,10 +74,10 @@ export class SQLiteInvoiceAdapter {
|
|
|
73
74
|
await this.db.transaction(async (tx) => {
|
|
74
75
|
for (const id of ids) {
|
|
75
76
|
if (source) {
|
|
76
|
-
await tx.run(
|
|
77
|
+
await tx.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ? AND source = ?`, [id, source]);
|
|
77
78
|
}
|
|
78
79
|
else {
|
|
79
|
-
await tx.run(
|
|
80
|
+
await tx.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} WHERE id = ?`, [id]);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SerializedMajikContact } from "@majikah/majik-contact";
|
|
2
2
|
import { MajikContactStorageAdapter } from "./_types";
|
|
3
3
|
import { SQLiteDatabase } from "../../sql-db-manager";
|
|
4
|
+
import { StorageQuery } from "../../storage-adapter";
|
|
4
5
|
export declare class SQLiteContactAdapter implements MajikContactStorageAdapter {
|
|
5
6
|
private db;
|
|
6
7
|
constructor(db: SQLiteDatabase);
|
|
@@ -13,4 +14,5 @@ export declare class SQLiteContactAdapter implements MajikContactStorageAdapter
|
|
|
13
14
|
exists(id: string): Promise<boolean>;
|
|
14
15
|
bulkSave(contacts: SerializedMajikContact[]): Promise<void>;
|
|
15
16
|
bulkRemove(ids: string[]): Promise<void>;
|
|
17
|
+
query(query: StorageQuery<SerializedMajikContact>): Promise<SerializedMajikContact[]>;
|
|
16
18
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { MAJIKAH_SQL_TABLES } from "../../sql-schema";
|
|
1
2
|
export class SQLiteContactAdapter {
|
|
2
3
|
db;
|
|
3
4
|
constructor(db) {
|
|
4
5
|
this.db = db;
|
|
5
6
|
}
|
|
6
7
|
async save(contact) {
|
|
7
|
-
await this.db.run(`INSERT OR REPLACE INTO
|
|
8
|
+
await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
|
|
8
9
|
(id, json, fingerprint, label, created_at, updated_at)
|
|
9
10
|
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
10
11
|
contact.id,
|
|
@@ -16,31 +17,29 @@ export class SQLiteContactAdapter {
|
|
|
16
17
|
]);
|
|
17
18
|
}
|
|
18
19
|
async getById(id) {
|
|
19
|
-
const row = await this.db.get(
|
|
20
|
+
const row = await this.db.get(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS} WHERE id = ?`, [id]);
|
|
20
21
|
return row ? JSON.parse(row.json) : null;
|
|
21
22
|
}
|
|
22
23
|
async list() {
|
|
23
|
-
const rows = await this.db.all(
|
|
24
|
+
const rows = await this.db.all(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}`);
|
|
24
25
|
return rows.map((r) => JSON.parse(r.json));
|
|
25
26
|
}
|
|
26
27
|
async remove(id) {
|
|
27
28
|
const exists = await this.exists(id);
|
|
28
29
|
if (!exists)
|
|
29
30
|
return false;
|
|
30
|
-
await this.db.run(
|
|
31
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS} WHERE id = ?`, [id]);
|
|
31
32
|
return true;
|
|
32
33
|
}
|
|
33
34
|
async clear() {
|
|
34
|
-
await this.db.run(
|
|
35
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}`);
|
|
35
36
|
}
|
|
36
37
|
async count() {
|
|
37
|
-
const row = await this.db.get(
|
|
38
|
+
const row = await this.db.get(`SELECT COUNT(*) as n FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}`);
|
|
38
39
|
return row?.n ?? 0;
|
|
39
40
|
}
|
|
40
41
|
async exists(id) {
|
|
41
|
-
const row = await this.db.get(
|
|
42
|
-
id,
|
|
43
|
-
]);
|
|
42
|
+
const row = await this.db.get(`SELECT 1 FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS} WHERE id = ?`, [id]);
|
|
44
43
|
return !!row;
|
|
45
44
|
}
|
|
46
45
|
async bulkSave(contacts) {
|
|
@@ -48,7 +47,7 @@ export class SQLiteContactAdapter {
|
|
|
48
47
|
return;
|
|
49
48
|
await this.db.transaction(async (tx) => {
|
|
50
49
|
for (const c of contacts) {
|
|
51
|
-
await tx.run(`INSERT OR REPLACE INTO
|
|
50
|
+
await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}
|
|
52
51
|
(id, json, fingerprint, label, created_at, updated_at)
|
|
53
52
|
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
54
53
|
c.id,
|
|
@@ -66,8 +65,33 @@ export class SQLiteContactAdapter {
|
|
|
66
65
|
return;
|
|
67
66
|
await this.db.transaction(async (tx) => {
|
|
68
67
|
for (const id of ids) {
|
|
69
|
-
await tx.run(
|
|
68
|
+
await tx.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS} WHERE id = ?`, [id]);
|
|
70
69
|
}
|
|
71
70
|
});
|
|
72
71
|
}
|
|
72
|
+
async query(query) {
|
|
73
|
+
const clauses = [];
|
|
74
|
+
const values = [];
|
|
75
|
+
if (query.where) {
|
|
76
|
+
for (const [key, value] of Object.entries(query.where)) {
|
|
77
|
+
clauses.push(`${key} = ?`);
|
|
78
|
+
values.push(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let sql = `SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}`;
|
|
82
|
+
if (clauses.length > 0) {
|
|
83
|
+
sql += ` WHERE ${clauses.join(" AND ")}`;
|
|
84
|
+
}
|
|
85
|
+
if (query.orderBy) {
|
|
86
|
+
sql += ` ORDER BY ${String(query.orderBy)} ${query.orderDirection ?? "asc"}`;
|
|
87
|
+
}
|
|
88
|
+
if (query.limit) {
|
|
89
|
+
sql += ` LIMIT ${query.limit}`;
|
|
90
|
+
}
|
|
91
|
+
if (query.offset) {
|
|
92
|
+
sql += ` OFFSET ${query.offset}`;
|
|
93
|
+
}
|
|
94
|
+
const rows = await this.db.all(sql, values);
|
|
95
|
+
return rows.map((r) => JSON.parse(r.json));
|
|
96
|
+
}
|
|
73
97
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { MAJIKAH_SQL_TABLES } from "../../sql-schema";
|
|
1
2
|
export class SQLiteContactGroupAdapter {
|
|
2
3
|
db;
|
|
3
4
|
constructor(db) {
|
|
4
5
|
this.db = db;
|
|
5
6
|
}
|
|
6
7
|
async save(group) {
|
|
7
|
-
await this.db.run(`INSERT OR REPLACE INTO
|
|
8
|
+
await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
|
|
8
9
|
(id, json, name, created_at, updated_at, is_system)
|
|
9
10
|
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
10
11
|
group.id,
|
|
@@ -16,29 +17,29 @@ export class SQLiteContactGroupAdapter {
|
|
|
16
17
|
]);
|
|
17
18
|
}
|
|
18
19
|
async getById(id) {
|
|
19
|
-
const row = await this.db.get(
|
|
20
|
+
const row = await this.db.get(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS} WHERE id = ?`, [id]);
|
|
20
21
|
return row ? JSON.parse(row.json) : null;
|
|
21
22
|
}
|
|
22
23
|
async list() {
|
|
23
|
-
const rows = await this.db.all(
|
|
24
|
+
const rows = await this.db.all(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}`);
|
|
24
25
|
return rows.map((r) => JSON.parse(r.json));
|
|
25
26
|
}
|
|
26
27
|
async remove(id) {
|
|
27
28
|
const exists = await this.exists(id);
|
|
28
29
|
if (!exists)
|
|
29
30
|
return false;
|
|
30
|
-
await this.db.run(
|
|
31
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS} WHERE id = ?`, [id]);
|
|
31
32
|
return true;
|
|
32
33
|
}
|
|
33
34
|
async clear() {
|
|
34
|
-
await this.db.run(
|
|
35
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}`);
|
|
35
36
|
}
|
|
36
37
|
async count() {
|
|
37
|
-
const row = await this.db.get(
|
|
38
|
+
const row = await this.db.get(`SELECT COUNT(*) as n FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}`);
|
|
38
39
|
return row?.n ?? 0;
|
|
39
40
|
}
|
|
40
41
|
async exists(id) {
|
|
41
|
-
const row = await this.db.get(
|
|
42
|
+
const row = await this.db.get(`SELECT 1 FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS} WHERE id = ?`, [id]);
|
|
42
43
|
return !!row;
|
|
43
44
|
}
|
|
44
45
|
async bulkSave(groups) {
|
|
@@ -46,7 +47,7 @@ export class SQLiteContactGroupAdapter {
|
|
|
46
47
|
return;
|
|
47
48
|
await this.db.transaction(async (tx) => {
|
|
48
49
|
for (const g of groups) {
|
|
49
|
-
await tx.run(`INSERT OR REPLACE INTO
|
|
50
|
+
await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}
|
|
50
51
|
(id, json, name, created_at, updated_at, is_system)
|
|
51
52
|
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
52
53
|
g.id,
|
|
@@ -64,7 +65,7 @@ export class SQLiteContactGroupAdapter {
|
|
|
64
65
|
return;
|
|
65
66
|
await this.db.transaction(async (tx) => {
|
|
66
67
|
for (const id of ids) {
|
|
67
|
-
await tx.run(
|
|
68
|
+
await tx.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS} WHERE id = ?`, [id]);
|
|
68
69
|
}
|
|
69
70
|
});
|
|
70
71
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MajikKeyJSON } from "@majikah/majik-key";
|
|
2
2
|
import { MajikKeyStorageAdapter } from "./_types";
|
|
3
3
|
import { SQLiteDatabase } from "../sql-db-manager";
|
|
4
|
+
import { StorageQuery } from "../storage-adapter";
|
|
4
5
|
export declare class SQLiteKeystoreAdapter implements MajikKeyStorageAdapter {
|
|
5
6
|
private db;
|
|
6
7
|
constructor(db: SQLiteDatabase);
|
|
@@ -13,4 +14,5 @@ export declare class SQLiteKeystoreAdapter implements MajikKeyStorageAdapter {
|
|
|
13
14
|
exists(id: string): Promise<boolean>;
|
|
14
15
|
bulkSave(keys: MajikKeyJSON[]): Promise<void>;
|
|
15
16
|
bulkRemove(ids: string[]): Promise<void>;
|
|
17
|
+
query(query: StorageQuery<MajikKeyJSON>): Promise<MajikKeyJSON[]>;
|
|
16
18
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { MAJIKAH_SQL_TABLES } from "../sql-schema";
|
|
1
2
|
export class SQLiteKeystoreAdapter {
|
|
2
3
|
db;
|
|
3
4
|
constructor(db) {
|
|
4
5
|
this.db = db;
|
|
5
6
|
}
|
|
6
7
|
async save(key) {
|
|
7
|
-
await this.db.run(`INSERT OR REPLACE INTO
|
|
8
|
+
await this.db.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
|
|
8
9
|
(id, json, timestamp, public_key)
|
|
9
10
|
VALUES (?, ?, ?, ?)`, [
|
|
10
11
|
key.id,
|
|
@@ -14,31 +15,29 @@ export class SQLiteKeystoreAdapter {
|
|
|
14
15
|
]);
|
|
15
16
|
}
|
|
16
17
|
async getById(id) {
|
|
17
|
-
const row = await this.db.get("SELECT json FROM
|
|
18
|
+
const row = await this.db.get("SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS} WHERE id = ?", [id]);
|
|
18
19
|
return row ? JSON.parse(row.json) : null;
|
|
19
20
|
}
|
|
20
21
|
async list() {
|
|
21
|
-
const rows = await this.db.all(
|
|
22
|
+
const rows = await this.db.all(`SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}`);
|
|
22
23
|
return rows.map((r) => JSON.parse(r.json));
|
|
23
24
|
}
|
|
24
25
|
async remove(id) {
|
|
25
26
|
const exists = await this.exists(id);
|
|
26
27
|
if (!exists)
|
|
27
28
|
return false;
|
|
28
|
-
await this.db.run(
|
|
29
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS} WHERE id = ?`, [id]);
|
|
29
30
|
return true;
|
|
30
31
|
}
|
|
31
32
|
async clear() {
|
|
32
|
-
await this.db.run(
|
|
33
|
+
await this.db.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}`);
|
|
33
34
|
}
|
|
34
35
|
async count() {
|
|
35
|
-
const row = await this.db.get(
|
|
36
|
+
const row = await this.db.get(`SELECT COUNT(*) as n FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}`);
|
|
36
37
|
return row?.n ?? 0;
|
|
37
38
|
}
|
|
38
39
|
async exists(id) {
|
|
39
|
-
const row = await this.db.get(
|
|
40
|
-
id,
|
|
41
|
-
]);
|
|
40
|
+
const row = await this.db.get(`SELECT 1 FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS} WHERE id = ?`, [id]);
|
|
42
41
|
return !!row;
|
|
43
42
|
}
|
|
44
43
|
async bulkSave(keys) {
|
|
@@ -46,7 +45,7 @@ export class SQLiteKeystoreAdapter {
|
|
|
46
45
|
return;
|
|
47
46
|
await this.db.transaction(async (tx) => {
|
|
48
47
|
for (const g of keys) {
|
|
49
|
-
await tx.run(`INSERT OR REPLACE INTO
|
|
48
|
+
await tx.run(`INSERT OR REPLACE INTO ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}
|
|
50
49
|
(id, json, timestamp, public_key)
|
|
51
50
|
VALUES (?, ?, ?, ?)`, [
|
|
52
51
|
g.id,
|
|
@@ -62,8 +61,33 @@ export class SQLiteKeystoreAdapter {
|
|
|
62
61
|
return;
|
|
63
62
|
await this.db.transaction(async (tx) => {
|
|
64
63
|
for (const id of ids) {
|
|
65
|
-
await tx.run(
|
|
64
|
+
await tx.run(`DELETE FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS} WHERE id = ?`, [id]);
|
|
66
65
|
}
|
|
67
66
|
});
|
|
68
67
|
}
|
|
68
|
+
async query(query) {
|
|
69
|
+
const clauses = [];
|
|
70
|
+
const values = [];
|
|
71
|
+
if (query.where) {
|
|
72
|
+
for (const [key, value] of Object.entries(query.where)) {
|
|
73
|
+
clauses.push(`${key} = ?`);
|
|
74
|
+
values.push(value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let sql = `SELECT json FROM ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}`;
|
|
78
|
+
if (clauses.length > 0) {
|
|
79
|
+
sql += ` WHERE ${clauses.join(" AND ")}`;
|
|
80
|
+
}
|
|
81
|
+
if (query.orderBy) {
|
|
82
|
+
sql += ` ORDER BY ${String(query.orderBy)} ${query.orderDirection ?? "asc"}`;
|
|
83
|
+
}
|
|
84
|
+
if (query.limit) {
|
|
85
|
+
sql += ` LIMIT ${query.limit}`;
|
|
86
|
+
}
|
|
87
|
+
if (query.offset) {
|
|
88
|
+
sql += ` OFFSET ${query.offset}`;
|
|
89
|
+
}
|
|
90
|
+
const rows = await this.db.all(sql, values);
|
|
91
|
+
return rows.map((r) => JSON.parse(r.json));
|
|
92
|
+
}
|
|
69
93
|
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
type MajikahSQLSchema = string;
|
|
2
|
+
/**
|
|
3
|
+
* Centralized SQLite table registry.
|
|
4
|
+
* - `as const` keeps literal types
|
|
5
|
+
* - `MajikahSQLTable` becomes a strict union type
|
|
6
|
+
*/
|
|
7
|
+
export declare const MAJIKAH_SQL_TABLES: {
|
|
8
|
+
readonly MAJIK_CLIENT_STATE: "majik_client_state";
|
|
9
|
+
readonly MAJIK_KEYS: "majik_keys";
|
|
10
|
+
readonly MAJIK_MESSAGE_CHATS: "majik_message_chats";
|
|
11
|
+
readonly MAJIK_MESSAGE_FILES: "majik_message_files";
|
|
12
|
+
readonly MAJIK_MESSAGE_THREAD_MAILS: "majik_message_thread_mails";
|
|
13
|
+
readonly MAJIK_CONTACTS: "majik_contacts";
|
|
14
|
+
readonly MAJIK_CONTACT_GROUPS: "majik_contact_groups";
|
|
15
|
+
};
|
|
16
|
+
export type MajikahSQLTable = (typeof MAJIKAH_SQL_TABLES)[keyof typeof MAJIKAH_SQL_TABLES];
|
|
2
17
|
export declare function buildSchemaSQL(schemas: MajikahSQLSchema[]): MajikahSQLSchema;
|
|
3
18
|
export declare const MAJIKAH_SQL_SCHEMA_MAJIK_CLIENT_STATE: MajikahSQLSchema;
|
|
4
19
|
export declare const MAJIKAH_SQL_SCHEMA_MAJIK_KEYS: MajikahSQLSchema;
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized SQLite table registry.
|
|
3
|
+
* - `as const` keeps literal types
|
|
4
|
+
* - `MajikahSQLTable` becomes a strict union type
|
|
5
|
+
*/
|
|
6
|
+
export const MAJIKAH_SQL_TABLES = {
|
|
7
|
+
MAJIK_CLIENT_STATE: "majik_client_state",
|
|
8
|
+
MAJIK_KEYS: "majik_keys",
|
|
9
|
+
MAJIK_MESSAGE_CHATS: "majik_message_chats",
|
|
10
|
+
MAJIK_MESSAGE_FILES: "majik_message_files",
|
|
11
|
+
MAJIK_MESSAGE_THREAD_MAILS: "majik_message_thread_mails",
|
|
12
|
+
MAJIK_CONTACTS: "majik_contacts",
|
|
13
|
+
MAJIK_CONTACT_GROUPS: "majik_contact_groups",
|
|
14
|
+
};
|
|
1
15
|
function normalizeSQL(sql) {
|
|
2
16
|
return sql
|
|
3
17
|
.trim()
|
|
@@ -19,28 +33,28 @@ export function buildSchemaSQL(schemas) {
|
|
|
19
33
|
.join("\n\n");
|
|
20
34
|
}
|
|
21
35
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_CLIENT_STATE = `
|
|
22
|
-
CREATE TABLE IF NOT EXISTS
|
|
36
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_CLIENT_STATE} (
|
|
23
37
|
key TEXT PRIMARY KEY,
|
|
24
38
|
value TEXT NOT NULL,
|
|
25
39
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
26
40
|
);
|
|
27
41
|
`;
|
|
28
42
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_KEYS = `
|
|
29
|
-
CREATE TABLE IF NOT EXISTS
|
|
43
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_KEYS} (
|
|
30
44
|
id TEXT PRIMARY KEY,
|
|
31
45
|
json TEXT NOT NULL,
|
|
32
46
|
timestamp TEXT NOT NULL,
|
|
33
47
|
public_key TEXT NOT NULL
|
|
34
48
|
);
|
|
35
49
|
|
|
36
|
-
CREATE INDEX IF NOT EXISTS idx_majik_keys_timestamp
|
|
37
|
-
ON
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_majik_keys_timestamp
|
|
51
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}(timestamp);
|
|
38
52
|
|
|
39
|
-
CREATE INDEX IF NOT EXISTS idx_majik_keys_public_key
|
|
40
|
-
ON
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_majik_keys_public_key
|
|
54
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_KEYS}(public_key);
|
|
41
55
|
`;
|
|
42
56
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_MESSAGE_CHATS = `
|
|
43
|
-
CREATE TABLE IF NOT EXISTS
|
|
57
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS} (
|
|
44
58
|
id TEXT PRIMARY KEY,
|
|
45
59
|
json TEXT NOT NULL,
|
|
46
60
|
created_at TEXT NOT NULL,
|
|
@@ -49,14 +63,14 @@ CREATE TABLE IF NOT EXISTS majik_message_chats (
|
|
|
49
63
|
);
|
|
50
64
|
|
|
51
65
|
CREATE INDEX IF NOT EXISTS idx_majik_message_chats_created_at
|
|
52
|
-
ON
|
|
66
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}(created_at);
|
|
53
67
|
|
|
54
68
|
|
|
55
69
|
CREATE INDEX IF NOT EXISTS idx_majik_message_chats_source
|
|
56
|
-
ON
|
|
70
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_CHATS}(source);
|
|
57
71
|
`;
|
|
58
72
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_MESSAGE_FILES = `
|
|
59
|
-
CREATE TABLE IF NOT EXISTS
|
|
73
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_FILES} (
|
|
60
74
|
id TEXT PRIMARY KEY,
|
|
61
75
|
json TEXT NOT NULL,
|
|
62
76
|
created_at TEXT NOT NULL,
|
|
@@ -66,14 +80,14 @@ CREATE TABLE IF NOT EXISTS majik_message_files (
|
|
|
66
80
|
);
|
|
67
81
|
|
|
68
82
|
CREATE INDEX IF NOT EXISTS idx_majik_message_files_created_at
|
|
69
|
-
ON
|
|
83
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_FILES}(created_at);
|
|
70
84
|
|
|
71
85
|
|
|
72
86
|
CREATE INDEX IF NOT EXISTS idx_majik_message_files_source
|
|
73
|
-
ON
|
|
87
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_MESSAGE_FILES}(source);
|
|
74
88
|
`;
|
|
75
89
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_CONTACTS = `
|
|
76
|
-
CREATE TABLE IF NOT EXISTS
|
|
90
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS} (
|
|
77
91
|
id TEXT PRIMARY KEY,
|
|
78
92
|
json TEXT NOT NULL,
|
|
79
93
|
fingerprint TEXT,
|
|
@@ -82,11 +96,11 @@ CREATE TABLE IF NOT EXISTS majik_contacts (
|
|
|
82
96
|
updated_at TEXT
|
|
83
97
|
);
|
|
84
98
|
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_majik_contacts_created_at
|
|
86
|
-
ON
|
|
99
|
+
CREATE INDEX IF NOT EXISTS idx_majik_contacts_created_at
|
|
100
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_CONTACTS}(created_at);
|
|
87
101
|
`;
|
|
88
102
|
export const MAJIKAH_SQL_SCHEMA_MAJIK_CONTACT_GROUPS = `
|
|
89
|
-
CREATE TABLE IF NOT EXISTS
|
|
103
|
+
CREATE TABLE IF NOT EXISTS ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS} (
|
|
90
104
|
id TEXT PRIMARY KEY,
|
|
91
105
|
json TEXT NOT NULL,
|
|
92
106
|
name TEXT,
|
|
@@ -95,8 +109,8 @@ CREATE TABLE IF NOT EXISTS majik_contact_groups (
|
|
|
95
109
|
is_system INTEGER DEFAULT 0 CHECK(is_system IN (0,1))
|
|
96
110
|
);
|
|
97
111
|
|
|
98
|
-
CREATE INDEX IF NOT EXISTS idx_majik_contact_groups_created_at
|
|
99
|
-
ON
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_majik_contact_groups_created_at
|
|
113
|
+
ON ${MAJIKAH_SQL_TABLES.MAJIK_CONTACT_GROUPS}(created_at);
|
|
100
114
|
`;
|
|
101
115
|
export const MAJIKAH_SQL_SCHEMA_FULL = buildSchemaSQL([
|
|
102
116
|
MAJIKAH_SQL_SCHEMA_MAJIK_CLIENT_STATE,
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type StorageSource = "local" | "cloud";
|
|
2
|
+
export interface StorageQuery<T> {
|
|
3
|
+
where?: Partial<T>;
|
|
4
|
+
limit?: number;
|
|
5
|
+
offset?: number;
|
|
6
|
+
orderBy?: keyof T;
|
|
7
|
+
orderDirection?: "asc" | "desc";
|
|
8
|
+
}
|
|
2
9
|
export interface MajikStorageAdapter<T extends {
|
|
3
10
|
id: string;
|
|
4
11
|
}> {
|
|
@@ -11,4 +18,5 @@ export interface MajikStorageAdapter<T extends {
|
|
|
11
18
|
exists(id: string, source?: StorageSource): Promise<boolean>;
|
|
12
19
|
bulkSave(items: T[], source?: StorageSource): Promise<void>;
|
|
13
20
|
bulkRemove(ids: string[], source?: StorageSource): Promise<void>;
|
|
21
|
+
query?(query: StorageQuery<T>, source?: StorageSource): Promise<T[]>;
|
|
14
22
|
}
|
package/dist/majik-message.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { DecryptFileOptions, EncryptFileOptions, EncryptFileResult, MAJIK_A
|
|
|
5
5
|
import { MajikMessageChat } from "./core/database/chat/majik-message-chat";
|
|
6
6
|
import { MajikMessageIdentity } from "./core/database/system/identity";
|
|
7
7
|
import { MajikKey } from "@majikah/majik-key";
|
|
8
|
+
import { type MajikRecipient } from "@majikah/majik-envelope";
|
|
8
9
|
import { MajikFile, MajikFileJSON } from "@majikah/majik-file";
|
|
9
10
|
import { EnvelopeInfo, ExpectedSigner, MajikSignature, SealInfo, SealVerificationResult, SignatoriesFilter, SignatoriesResult, SignatoryInfo, type MajikSignatureJSON, type MajikSignerPublicKeys, type VerificationResult } from "@majikah/majik-signature";
|
|
10
11
|
import { MajikContactManager, MajikContactManagerAdapters } from "./core/contacts/majik-contact-manager";
|
|
@@ -153,6 +154,10 @@ export declare class MajikMessage {
|
|
|
153
154
|
hasOwnIdentity(fingerprint: string): Promise<boolean>;
|
|
154
155
|
getContactByID(id: string): MajikContact | null;
|
|
155
156
|
getContactByPublicKey(publicKeyBase64: string): Promise<MajikContact | null>;
|
|
157
|
+
getContactsByID(ids: string[], strict?: boolean): MajikContact[];
|
|
158
|
+
getContactsByPublicKey(publicKeys: string[]): Promise<MajikContact[]>;
|
|
159
|
+
getMajikRecipientsByPublicKey(publicKeys: string[], strict?: boolean): Promise<MajikRecipient[]>;
|
|
160
|
+
getExpectedSignersByPublicKey(publicKeys: string[], strict?: boolean): Promise<ExpectedSigner[]>;
|
|
156
161
|
exportContactAsJSON(id: string): Promise<string | null>;
|
|
157
162
|
exportContactAsString(id: string): Promise<string | null>;
|
|
158
163
|
importContactFromJSON(jsonStr: string): Promise<MAJIK_API_RESPONSE>;
|
package/dist/majik-message.js
CHANGED
|
@@ -408,6 +408,22 @@ export class MajikMessage {
|
|
|
408
408
|
return ((await this._contacts.getContactByPublicKeyBase64(publicKeyBase64)) ??
|
|
409
409
|
null);
|
|
410
410
|
}
|
|
411
|
+
getContactsByID(ids, strict = false) {
|
|
412
|
+
if (!ids?.length)
|
|
413
|
+
throw new Error("At least 1 id is required");
|
|
414
|
+
return this._contacts.getContactsByIds(ids, strict);
|
|
415
|
+
}
|
|
416
|
+
async getContactsByPublicKey(publicKeys) {
|
|
417
|
+
if (!publicKeys?.length)
|
|
418
|
+
throw new Error("At least 1 public key is required");
|
|
419
|
+
return await this._contacts.getContactsByPublicKeys(publicKeys);
|
|
420
|
+
}
|
|
421
|
+
async getMajikRecipientsByPublicKey(publicKeys, strict) {
|
|
422
|
+
return await this._contacts.getMajikRecipients("public_key", publicKeys, strict);
|
|
423
|
+
}
|
|
424
|
+
async getExpectedSignersByPublicKey(publicKeys, strict) {
|
|
425
|
+
return await this._contacts.getExpectedSigners("public_key", publicKeys, strict);
|
|
426
|
+
}
|
|
411
427
|
async exportContactAsJSON(id) {
|
|
412
428
|
if (!id?.trim())
|
|
413
429
|
throw new Error("Invalid contact ID");
|
|
@@ -426,7 +442,14 @@ export class MajikMessage {
|
|
|
426
442
|
async importContactFromString(base64Str) {
|
|
427
443
|
if (!base64Str?.trim())
|
|
428
444
|
throw new Error("Invalid contact string");
|
|
429
|
-
|
|
445
|
+
const response = await this._contacts.importContactFromString(base64Str);
|
|
446
|
+
if (response.success) {
|
|
447
|
+
this._emit("new-contact", response.data);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
this._emit("error", response.message);
|
|
451
|
+
}
|
|
452
|
+
return response;
|
|
430
453
|
}
|
|
431
454
|
async exportContactCompressed(contact) {
|
|
432
455
|
if (!contact?.id?.trim())
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@majikah/majik-message",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "Post-quantum end-to-end encryption with ML-KEM-768. Seed phrase–based accounts. Auto-expiring messages. Offline-ready. Exportable encrypted messages. Tamper-proof threads with blockchain-like integrity. Quantum-resistant messaging.",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.8",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "Zelijah",
|
|
8
8
|
"main": "./dist/index.js",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@bokuweb/zstd-wasm": "^0.0.27",
|
|
83
83
|
"@majikah/majik-contact": "^0.0.4",
|
|
84
|
-
"@majikah/majik-envelope": "^0.0.
|
|
84
|
+
"@majikah/majik-envelope": "^0.0.4",
|
|
85
85
|
"@majikah/majik-file": "^0.1.4",
|
|
86
86
|
"@majikah/majik-key": "^0.2.7",
|
|
87
87
|
"@majikah/majik-signature": "^0.0.17",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@noble/post-quantum": "^0.5.4",
|
|
90
90
|
"@scure/bip39": "^1.6.0",
|
|
91
91
|
"@stablelib/aes": "^2.0.1",
|
|
92
|
-
"@stablelib/ed25519": "^2.0
|
|
92
|
+
"@stablelib/ed25519": "^2.1.0",
|
|
93
93
|
"@stablelib/gcm": "^2.0.1",
|
|
94
94
|
"@stablelib/pbkdf2": "^2.0.1",
|
|
95
95
|
"@stablelib/random": "^2.0.1",
|