@massalabs/gossip-sdk 0.0.2-dev.20260220052333 → 0.0.2-dev.20260220053835

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 (60) hide show
  1. package/README.md +2 -2
  2. package/dist/config/sdk.js +1 -1
  3. package/dist/contacts.d.ts +11 -17
  4. package/dist/contacts.js +20 -26
  5. package/dist/core/SdkEventEmitter.d.ts +2 -0
  6. package/dist/core/SdkEventEmitter.js +2 -0
  7. package/dist/core/SdkPolling.d.ts +2 -5
  8. package/dist/core/SdkPolling.js +1 -4
  9. package/dist/core/index.d.ts +1 -1
  10. package/dist/core/index.js +1 -0
  11. package/dist/db.d.ts +34 -56
  12. package/dist/db.js +39 -229
  13. package/dist/gossip.d.ts +54 -23
  14. package/dist/gossip.js +149 -120
  15. package/dist/index.d.ts +13 -4
  16. package/dist/index.js +13 -4
  17. package/dist/queries/activeSeekers.d.ts +2 -0
  18. package/dist/queries/activeSeekers.js +18 -0
  19. package/dist/queries/announcementCursors.d.ts +2 -0
  20. package/dist/queries/announcementCursors.js +20 -0
  21. package/dist/queries/contacts.d.ts +13 -0
  22. package/dist/queries/contacts.js +43 -0
  23. package/dist/queries/discussions.d.ts +21 -0
  24. package/dist/queries/discussions.js +73 -0
  25. package/dist/queries/index.d.ts +7 -0
  26. package/dist/queries/index.js +7 -0
  27. package/dist/queries/messages.d.ts +27 -0
  28. package/dist/queries/messages.js +120 -0
  29. package/dist/queries/pendingAnnouncements.d.ts +4 -0
  30. package/dist/queries/pendingAnnouncements.js +11 -0
  31. package/dist/queries/userProfile.d.ts +22 -0
  32. package/dist/queries/userProfile.js +116 -0
  33. package/dist/schema.d.ts +1280 -0
  34. package/dist/schema.js +164 -0
  35. package/dist/services/announcement.d.ts +8 -7
  36. package/dist/services/announcement.js +95 -121
  37. package/dist/services/auth.d.ts +1 -3
  38. package/dist/services/auth.js +4 -11
  39. package/dist/services/discussion.d.ts +8 -15
  40. package/dist/services/discussion.js +62 -62
  41. package/dist/services/message.d.ts +15 -26
  42. package/dist/services/message.js +282 -270
  43. package/dist/services/refresh.d.ts +3 -5
  44. package/dist/services/refresh.js +10 -13
  45. package/dist/sqlite-worker.d.ts +12 -0
  46. package/dist/sqlite-worker.js +106 -0
  47. package/dist/sqlite.d.ts +79 -0
  48. package/dist/sqlite.js +448 -0
  49. package/dist/utils/contacts.d.ts +2 -5
  50. package/dist/utils/contacts.js +23 -39
  51. package/dist/utils/discussions.d.ts +7 -2
  52. package/dist/utils/discussions.js +24 -5
  53. package/dist/utils/logs.js +1 -3
  54. package/dist/utils/validation.d.ts +2 -4
  55. package/dist/utils/validation.js +5 -10
  56. package/package.json +5 -7
  57. package/dist/api/messageProtocol/mock.d.ts +0 -12
  58. package/dist/api/messageProtocol/mock.js +0 -12
  59. package/dist/types/events.d.ts +0 -80
  60. package/dist/types/events.js +0 -7
@@ -0,0 +1,73 @@
1
+ import { eq, and, gt, sql } from 'drizzle-orm';
2
+ import * as schema from '../schema';
3
+ import { getSqliteDb, getLastInsertRowId } from '../sqlite';
4
+ export async function getDiscussionByOwnerAndContact(ownerUserId, contactUserId) {
5
+ return getSqliteDb()
6
+ .select()
7
+ .from(schema.discussions)
8
+ .where(and(eq(schema.discussions.ownerUserId, ownerUserId), eq(schema.discussions.contactUserId, contactUserId)))
9
+ .get();
10
+ }
11
+ export async function getDiscussionsByOwner(ownerUserId) {
12
+ return getSqliteDb()
13
+ .select()
14
+ .from(schema.discussions)
15
+ .where(eq(schema.discussions.ownerUserId, ownerUserId))
16
+ .all();
17
+ }
18
+ export async function getDiscussionById(id) {
19
+ return getSqliteDb()
20
+ .select()
21
+ .from(schema.discussions)
22
+ .where(eq(schema.discussions.id, id))
23
+ .get();
24
+ }
25
+ export async function insertDiscussion(values) {
26
+ await getSqliteDb().insert(schema.discussions).values(values);
27
+ return getLastInsertRowId();
28
+ }
29
+ export async function updateDiscussionById(id, data) {
30
+ await getSqliteDb()
31
+ .update(schema.discussions)
32
+ .set(data)
33
+ .where(eq(schema.discussions.id, id));
34
+ }
35
+ export async function deleteDiscussionById(id) {
36
+ await getSqliteDb()
37
+ .delete(schema.discussions)
38
+ .where(eq(schema.discussions.id, id));
39
+ }
40
+ export async function deleteDiscussionsByOwnerAndContact(ownerUserId, contactUserId) {
41
+ await getSqliteDb()
42
+ .delete(schema.discussions)
43
+ .where(and(eq(schema.discussions.ownerUserId, ownerUserId), eq(schema.discussions.contactUserId, contactUserId)));
44
+ }
45
+ /**
46
+ * Atomically increment unreadCount by 1.
47
+ */
48
+ export async function incrementUnreadCount(discussionId) {
49
+ await getSqliteDb()
50
+ .update(schema.discussions)
51
+ .set({
52
+ unreadCount: sql `${schema.discussions.unreadCount} + 1`,
53
+ })
54
+ .where(eq(schema.discussions.id, discussionId));
55
+ }
56
+ /**
57
+ * Atomically decrement unreadCount by 1, ensuring it never goes below 0.
58
+ */
59
+ export async function decrementUnreadCount(discussionId) {
60
+ await getSqliteDb()
61
+ .update(schema.discussions)
62
+ .set({
63
+ unreadCount: sql `MAX(${schema.discussions.unreadCount} - 1, 0)`,
64
+ })
65
+ .where(and(eq(schema.discussions.id, discussionId), gt(schema.discussions.unreadCount, 0)));
66
+ }
67
+ export async function getDiscussionsByOwnerAndStatus(ownerUserId, status) {
68
+ return getSqliteDb()
69
+ .select()
70
+ .from(schema.discussions)
71
+ .where(and(eq(schema.discussions.ownerUserId, ownerUserId), eq(schema.discussions.status, status)))
72
+ .all();
73
+ }
@@ -0,0 +1,7 @@
1
+ export * from './contacts';
2
+ export * from './discussions';
3
+ export * from './messages';
4
+ export * from './userProfile';
5
+ export * from './pendingAnnouncements';
6
+ export * from './announcementCursors';
7
+ export * from './activeSeekers';
@@ -0,0 +1,7 @@
1
+ export * from './contacts';
2
+ export * from './discussions';
3
+ export * from './messages';
4
+ export * from './userProfile';
5
+ export * from './pendingAnnouncements';
6
+ export * from './announcementCursors';
7
+ export * from './activeSeekers';
@@ -0,0 +1,27 @@
1
+ import * as schema from '../schema';
2
+ import { MessageStatus } from '../db';
3
+ export type MessageRow = typeof schema.messages.$inferSelect;
4
+ export type MessageInsert = typeof schema.messages.$inferInsert;
5
+ export declare function getMessageById(id: number): Promise<MessageRow | undefined>;
6
+ export declare function getMessagesByOwnerAndContact(ownerUserId: string, contactUserId: string): Promise<MessageRow[]>;
7
+ export declare function getMessageByOwnerAndSeeker(ownerUserId: string, seeker: Uint8Array): Promise<MessageRow | undefined>;
8
+ export declare function findMessageByMessageId(ownerUserId: string, contactUserId: string, messageId: Uint8Array): Promise<MessageRow | undefined>;
9
+ export declare function insertMessage(values: MessageInsert): Promise<number>;
10
+ export declare function batchInsertMessages(values: MessageInsert[]): Promise<void>;
11
+ export declare function updateMessageById(id: number, data: Partial<MessageInsert>): Promise<void>;
12
+ export declare function deleteMessagesByOwnerAndContact(ownerUserId: string, contactUserId: string): Promise<void>;
13
+ export declare function deleteDeliveredKeepAliveMessages(ownerUserId: string): Promise<void>;
14
+ export declare function getOutgoingSentMessagesByOwner(ownerUserId: string): Promise<MessageRow[]>;
15
+ export declare function getWaitingMessageCount(ownerUserId: string, contactUserId: string): Promise<number>;
16
+ export declare function getSendQueueMessages(ownerUserId: string, contactUserId: string): Promise<MessageRow[]>;
17
+ export declare function getMessagesByStatus(status: MessageStatus): Promise<MessageRow[]>;
18
+ /**
19
+ * Reset outgoing messages to WAITING_SESSION, clearing encryption data.
20
+ * @param statuses - If provided, only reset messages with these statuses.
21
+ * If omitted, reset ALL outgoing messages for the contact.
22
+ */
23
+ export declare function resetSendQueueMessages(ownerUserId: string, contactUserId: string, statuses?: MessageStatus[]): Promise<void>;
24
+ export declare function getAnnouncementMessagesByContact(ownerUserId: string, contactUserId: string): Promise<MessageRow[]>;
25
+ export declare function findDuplicateIncomingMessage(ownerUserId: string, contactUserId: string, content: string, windowStart: Date, windowEnd: Date): Promise<{
26
+ id: number;
27
+ } | undefined>;
@@ -0,0 +1,120 @@
1
+ import { eq, and, sql, inArray, asc } from 'drizzle-orm';
2
+ import * as schema from '../schema';
3
+ import { getSqliteDb, getLastInsertRowId } from '../sqlite';
4
+ import { MessageDirection, MessageStatus, MessageType } from '../db';
5
+ export async function getMessageById(id) {
6
+ return getSqliteDb()
7
+ .select()
8
+ .from(schema.messages)
9
+ .where(eq(schema.messages.id, id))
10
+ .get();
11
+ }
12
+ export async function getMessagesByOwnerAndContact(ownerUserId, contactUserId) {
13
+ return getSqliteDb()
14
+ .select()
15
+ .from(schema.messages)
16
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId)))
17
+ .orderBy(asc(schema.messages.timestamp), asc(schema.messages.id))
18
+ .all();
19
+ }
20
+ export async function getMessageByOwnerAndSeeker(ownerUserId, seeker) {
21
+ return getSqliteDb()
22
+ .select()
23
+ .from(schema.messages)
24
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.seeker, seeker)))
25
+ .get();
26
+ }
27
+ export async function findMessageByMessageId(ownerUserId, contactUserId, messageId) {
28
+ return getSqliteDb()
29
+ .select()
30
+ .from(schema.messages)
31
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.messageId, messageId)))
32
+ .get();
33
+ }
34
+ export async function insertMessage(values) {
35
+ await getSqliteDb().insert(schema.messages).values(values);
36
+ return getLastInsertRowId();
37
+ }
38
+ export async function batchInsertMessages(values) {
39
+ if (values.length === 0)
40
+ return;
41
+ await getSqliteDb().insert(schema.messages).values(values);
42
+ }
43
+ export async function updateMessageById(id, data) {
44
+ await getSqliteDb()
45
+ .update(schema.messages)
46
+ .set(data)
47
+ .where(eq(schema.messages.id, id));
48
+ }
49
+ export async function deleteMessagesByOwnerAndContact(ownerUserId, contactUserId) {
50
+ await getSqliteDb()
51
+ .delete(schema.messages)
52
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId)));
53
+ }
54
+ export async function deleteDeliveredKeepAliveMessages(ownerUserId) {
55
+ await getSqliteDb()
56
+ .delete(schema.messages)
57
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.status, MessageStatus.DELIVERED), eq(schema.messages.type, MessageType.KEEP_ALIVE)));
58
+ }
59
+ export async function getOutgoingSentMessagesByOwner(ownerUserId) {
60
+ return getSqliteDb()
61
+ .select()
62
+ .from(schema.messages)
63
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.direction, MessageDirection.OUTGOING), eq(schema.messages.status, MessageStatus.SENT)))
64
+ .all();
65
+ }
66
+ export async function getWaitingMessageCount(ownerUserId, contactUserId) {
67
+ const result = await getSqliteDb()
68
+ .select({ count: sql `count(*)` })
69
+ .from(schema.messages)
70
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.status, MessageStatus.WAITING_SESSION)))
71
+ .get();
72
+ return result?.count ?? 0;
73
+ }
74
+ export async function getSendQueueMessages(ownerUserId, contactUserId) {
75
+ return getSqliteDb()
76
+ .select()
77
+ .from(schema.messages)
78
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.direction, MessageDirection.OUTGOING), inArray(schema.messages.status, [
79
+ MessageStatus.WAITING_SESSION,
80
+ MessageStatus.READY,
81
+ ])))
82
+ .orderBy(asc(schema.messages.timestamp), asc(schema.messages.id))
83
+ .all();
84
+ }
85
+ export async function getMessagesByStatus(status) {
86
+ return getSqliteDb()
87
+ .select()
88
+ .from(schema.messages)
89
+ .where(eq(schema.messages.status, status))
90
+ .all();
91
+ }
92
+ /**
93
+ * Reset outgoing messages to WAITING_SESSION, clearing encryption data.
94
+ * @param statuses - If provided, only reset messages with these statuses.
95
+ * If omitted, reset ALL outgoing messages for the contact.
96
+ */
97
+ export async function resetSendQueueMessages(ownerUserId, contactUserId, statuses) {
98
+ await getSqliteDb()
99
+ .update(schema.messages)
100
+ .set({
101
+ status: MessageStatus.WAITING_SESSION,
102
+ encryptedMessage: null,
103
+ seeker: null,
104
+ })
105
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.direction, MessageDirection.OUTGOING), statuses ? inArray(schema.messages.status, statuses) : undefined));
106
+ }
107
+ export async function getAnnouncementMessagesByContact(ownerUserId, contactUserId) {
108
+ return getSqliteDb()
109
+ .select()
110
+ .from(schema.messages)
111
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.direction, MessageDirection.INCOMING), eq(schema.messages.type, MessageType.ANNOUNCEMENT)))
112
+ .all();
113
+ }
114
+ export async function findDuplicateIncomingMessage(ownerUserId, contactUserId, content, windowStart, windowEnd) {
115
+ return getSqliteDb()
116
+ .select({ id: schema.messages.id })
117
+ .from(schema.messages)
118
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.contactUserId, contactUserId), eq(schema.messages.direction, MessageDirection.INCOMING), eq(schema.messages.content, content), sql `${schema.messages.timestamp} >= ${windowStart.getTime()}`, sql `${schema.messages.timestamp} <= ${windowEnd.getTime()}`))
119
+ .get();
120
+ }
@@ -0,0 +1,4 @@
1
+ import * as schema from '../schema';
2
+ export type PendingAnnouncementRow = typeof schema.pendingAnnouncements.$inferSelect;
3
+ export declare function getAllPendingAnnouncements(): Promise<PendingAnnouncementRow[]>;
4
+ export declare function deletePendingAnnouncementsByIds(ids: number[]): Promise<void>;
@@ -0,0 +1,11 @@
1
+ import { inArray } from 'drizzle-orm';
2
+ import * as schema from '../schema';
3
+ import { getSqliteDb } from '../sqlite';
4
+ export async function getAllPendingAnnouncements() {
5
+ return getSqliteDb().select().from(schema.pendingAnnouncements).all();
6
+ }
7
+ export async function deletePendingAnnouncementsByIds(ids) {
8
+ await getSqliteDb()
9
+ .delete(schema.pendingAnnouncements)
10
+ .where(inArray(schema.pendingAnnouncements.id, ids));
11
+ }
@@ -0,0 +1,22 @@
1
+ import * as schema from '../schema';
2
+ import type { UserProfile } from '../db';
3
+ export type UserProfileRow = typeof schema.userProfile.$inferSelect;
4
+ export type UserProfileInsert = typeof schema.userProfile.$inferInsert;
5
+ /** Convert a DB row (security as JSON string) to a domain UserProfile. */
6
+ export declare function rowToUserProfile(row: UserProfileRow): UserProfile;
7
+ /** Convert a domain UserProfile to a DB-ready insert row (security as JSON string). */
8
+ export declare function userProfileToRow(profile: UserProfile): UserProfileInsert;
9
+ export declare function getUserProfileField(userId: string): Promise<UserProfileRow | undefined>;
10
+ export declare function updateUserProfileById(userId: string, data: Partial<UserProfileInsert>): Promise<void>;
11
+ export declare function getUserProfileByUsernameLower(username: string): Promise<{
12
+ userId: string;
13
+ } | undefined>;
14
+ export declare function getMostRecentUserProfile(): Promise<UserProfileRow | undefined>;
15
+ export declare function getAllUserProfiles(): Promise<UserProfileRow[]>;
16
+ export declare function getUserProfileCount(): Promise<number>;
17
+ export declare function insertUserProfile(values: UserProfileInsert): Promise<void>;
18
+ export declare function deleteUserProfile(userId: string): Promise<void>;
19
+ export declare function getUserProfileByUsernameLowerExcluding(username: string, excludeUserId: string): Promise<{
20
+ userId: string;
21
+ } | undefined>;
22
+ export declare function upsertUserProfile(values: UserProfileInsert): Promise<void>;
@@ -0,0 +1,116 @@
1
+ import { and, desc, eq, ne, sql } from 'drizzle-orm';
2
+ import * as schema from '../schema';
3
+ import { getSqliteDb } from '../sqlite';
4
+ /**
5
+ * Restore a Uint8Array from JSON-parsed data.
6
+ * Handles: number[] (correct format), {0:x, 1:y, ...} (corrupted from
7
+ * bare JSON.stringify of Uint8Array), or already a Uint8Array.
8
+ */
9
+ function toUint8Array(val) {
10
+ if (val instanceof Uint8Array)
11
+ return val;
12
+ if (Array.isArray(val))
13
+ return new Uint8Array(val);
14
+ if (typeof val === 'object' && val !== null) {
15
+ const obj = val;
16
+ const len = Object.keys(obj).length;
17
+ const arr = new Uint8Array(len);
18
+ for (let i = 0; i < len; i++)
19
+ arr[i] = obj[String(i)];
20
+ return arr;
21
+ }
22
+ return new Uint8Array();
23
+ }
24
+ /** Convert a DB row (security as JSON string) to a domain UserProfile. */
25
+ export function rowToUserProfile(row) {
26
+ const raw = typeof row.security === 'string' ? JSON.parse(row.security) : row.security;
27
+ const security = {
28
+ ...raw,
29
+ encKeySalt: toUint8Array(raw.encKeySalt),
30
+ mnemonicBackup: {
31
+ ...raw.mnemonicBackup,
32
+ encryptedMnemonic: toUint8Array(raw.mnemonicBackup.encryptedMnemonic),
33
+ createdAt: new Date(raw.mnemonicBackup.createdAt),
34
+ },
35
+ };
36
+ return { ...row, security };
37
+ }
38
+ /** Convert a domain UserProfile to a DB-ready insert row (security as JSON string). */
39
+ export function userProfileToRow(profile) {
40
+ const security = {
41
+ ...profile.security,
42
+ encKeySalt: Array.from(profile.security.encKeySalt),
43
+ mnemonicBackup: {
44
+ ...profile.security.mnemonicBackup,
45
+ encryptedMnemonic: Array.from(profile.security.mnemonicBackup.encryptedMnemonic),
46
+ },
47
+ };
48
+ return {
49
+ ...profile,
50
+ security: JSON.stringify(security),
51
+ lastPublicKeyPush: profile.lastPublicKeyPush ?? null,
52
+ };
53
+ }
54
+ export async function getUserProfileField(userId) {
55
+ return getSqliteDb()
56
+ .select()
57
+ .from(schema.userProfile)
58
+ .where(eq(schema.userProfile.userId, userId))
59
+ .get();
60
+ }
61
+ export async function updateUserProfileById(userId, data) {
62
+ await getSqliteDb()
63
+ .update(schema.userProfile)
64
+ .set(data)
65
+ .where(eq(schema.userProfile.userId, userId));
66
+ }
67
+ export async function getUserProfileByUsernameLower(username) {
68
+ return getSqliteDb()
69
+ .select({ userId: schema.userProfile.userId })
70
+ .from(schema.userProfile)
71
+ .where(sql `LOWER(${schema.userProfile.username}) = ${username.trim().toLowerCase()}`)
72
+ .get();
73
+ }
74
+ export async function getMostRecentUserProfile() {
75
+ return getSqliteDb()
76
+ .select()
77
+ .from(schema.userProfile)
78
+ .orderBy(desc(schema.userProfile.lastSeen))
79
+ .limit(1)
80
+ .get();
81
+ }
82
+ export async function getAllUserProfiles() {
83
+ return getSqliteDb().select().from(schema.userProfile).all();
84
+ }
85
+ export async function getUserProfileCount() {
86
+ const result = await getSqliteDb()
87
+ .select({ count: sql `count(*)` })
88
+ .from(schema.userProfile)
89
+ .get();
90
+ return result?.count ?? 0;
91
+ }
92
+ export async function insertUserProfile(values) {
93
+ await getSqliteDb().insert(schema.userProfile).values(values);
94
+ }
95
+ export async function deleteUserProfile(userId) {
96
+ await getSqliteDb()
97
+ .delete(schema.userProfile)
98
+ .where(eq(schema.userProfile.userId, userId));
99
+ }
100
+ export async function getUserProfileByUsernameLowerExcluding(username, excludeUserId) {
101
+ return getSqliteDb()
102
+ .select({ userId: schema.userProfile.userId })
103
+ .from(schema.userProfile)
104
+ .where(and(sql `LOWER(${schema.userProfile.username}) = ${username.trim().toLowerCase()}`, ne(schema.userProfile.userId, excludeUserId)))
105
+ .get();
106
+ }
107
+ export async function upsertUserProfile(values) {
108
+ const { userId: _, ...rest } = values;
109
+ await getSqliteDb()
110
+ .insert(schema.userProfile)
111
+ .values(values)
112
+ .onConflictDoUpdate({
113
+ target: schema.userProfile.userId,
114
+ set: rest,
115
+ });
116
+ }