@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.
- package/README.md +2 -2
- package/dist/config/sdk.js +1 -1
- package/dist/contacts.d.ts +11 -17
- package/dist/contacts.js +20 -26
- package/dist/core/SdkEventEmitter.d.ts +2 -0
- package/dist/core/SdkEventEmitter.js +2 -0
- package/dist/core/SdkPolling.d.ts +2 -5
- package/dist/core/SdkPolling.js +1 -4
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -0
- package/dist/db.d.ts +34 -56
- package/dist/db.js +39 -229
- package/dist/gossip.d.ts +54 -23
- package/dist/gossip.js +149 -120
- package/dist/index.d.ts +13 -4
- package/dist/index.js +13 -4
- package/dist/queries/activeSeekers.d.ts +2 -0
- package/dist/queries/activeSeekers.js +18 -0
- package/dist/queries/announcementCursors.d.ts +2 -0
- package/dist/queries/announcementCursors.js +20 -0
- package/dist/queries/contacts.d.ts +13 -0
- package/dist/queries/contacts.js +43 -0
- package/dist/queries/discussions.d.ts +21 -0
- package/dist/queries/discussions.js +73 -0
- package/dist/queries/index.d.ts +7 -0
- package/dist/queries/index.js +7 -0
- package/dist/queries/messages.d.ts +27 -0
- package/dist/queries/messages.js +120 -0
- package/dist/queries/pendingAnnouncements.d.ts +4 -0
- package/dist/queries/pendingAnnouncements.js +11 -0
- package/dist/queries/userProfile.d.ts +22 -0
- package/dist/queries/userProfile.js +116 -0
- package/dist/schema.d.ts +1280 -0
- package/dist/schema.js +164 -0
- package/dist/services/announcement.d.ts +8 -7
- package/dist/services/announcement.js +95 -121
- package/dist/services/auth.d.ts +1 -3
- package/dist/services/auth.js +4 -11
- package/dist/services/discussion.d.ts +8 -15
- package/dist/services/discussion.js +62 -62
- package/dist/services/message.d.ts +15 -26
- package/dist/services/message.js +282 -270
- package/dist/services/refresh.d.ts +3 -5
- package/dist/services/refresh.js +10 -13
- package/dist/sqlite-worker.d.ts +12 -0
- package/dist/sqlite-worker.js +106 -0
- package/dist/sqlite.d.ts +79 -0
- package/dist/sqlite.js +448 -0
- package/dist/utils/contacts.d.ts +2 -5
- package/dist/utils/contacts.js +23 -39
- package/dist/utils/discussions.d.ts +7 -2
- package/dist/utils/discussions.js +24 -5
- package/dist/utils/logs.js +1 -3
- package/dist/utils/validation.d.ts +2 -4
- package/dist/utils/validation.js +5 -10
- package/package.json +5 -7
- package/dist/api/messageProtocol/mock.d.ts +0 -12
- package/dist/api/messageProtocol/mock.js +0 -12
- package/dist/types/events.d.ts +0 -80
- package/dist/types/events.js +0 -7
package/dist/db.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Gossip Database
|
|
2
|
+
* Gossip Database Types
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Type definitions for the Gossip messenger data models.
|
|
5
|
+
* Database operations use SQLite via Drizzle ORM (see sqlite.ts and schema.ts).
|
|
6
6
|
*/
|
|
7
|
-
import Dexie from 'dexie';
|
|
8
7
|
// Constants
|
|
9
8
|
export const MESSAGE_ID_SIZE = 12;
|
|
9
|
+
// Unified discussion interface combining protocol state and UI metadata
|
|
10
|
+
export var DiscussionStatus;
|
|
11
|
+
(function (DiscussionStatus) {
|
|
12
|
+
DiscussionStatus["PENDING"] = "pending";
|
|
13
|
+
DiscussionStatus["ACTIVE"] = "active";
|
|
14
|
+
DiscussionStatus["CLOSED"] = "closed";
|
|
15
|
+
DiscussionStatus["BROKEN"] = "broken";
|
|
16
|
+
DiscussionStatus["SEND_FAILED"] = "sendFailed";
|
|
17
|
+
DiscussionStatus["RECONNECTING"] = "reconnecting";
|
|
18
|
+
})(DiscussionStatus || (DiscussionStatus = {}));
|
|
10
19
|
export var DiscussionDirection;
|
|
11
20
|
(function (DiscussionDirection) {
|
|
12
21
|
DiscussionDirection["RECEIVED"] = "received";
|
|
@@ -21,9 +30,11 @@ export var MessageStatus;
|
|
|
21
30
|
(function (MessageStatus) {
|
|
22
31
|
MessageStatus["WAITING_SESSION"] = "waiting_session";
|
|
23
32
|
MessageStatus["READY"] = "ready";
|
|
33
|
+
MessageStatus["SENDING"] = "sending";
|
|
24
34
|
MessageStatus["SENT"] = "sent";
|
|
25
35
|
MessageStatus["DELIVERED"] = "delivered";
|
|
26
36
|
MessageStatus["READ"] = "read";
|
|
37
|
+
MessageStatus["FAILED"] = "failed";
|
|
27
38
|
})(MessageStatus || (MessageStatus = {}));
|
|
28
39
|
export var MessageType;
|
|
29
40
|
(function (MessageType) {
|
|
@@ -35,230 +46,29 @@ export var MessageType;
|
|
|
35
46
|
MessageType["AUDIO"] = "audio";
|
|
36
47
|
MessageType["VIDEO"] = "video";
|
|
37
48
|
})(MessageType || (MessageType = {}));
|
|
38
|
-
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
enumerable: true,
|
|
45
|
-
configurable: true,
|
|
46
|
-
writable: true,
|
|
47
|
-
value: void 0
|
|
48
|
-
});
|
|
49
|
-
Object.defineProperty(this, "messages", {
|
|
50
|
-
enumerable: true,
|
|
51
|
-
configurable: true,
|
|
52
|
-
writable: true,
|
|
53
|
-
value: void 0
|
|
54
|
-
});
|
|
55
|
-
Object.defineProperty(this, "userProfile", {
|
|
56
|
-
enumerable: true,
|
|
57
|
-
configurable: true,
|
|
58
|
-
writable: true,
|
|
59
|
-
value: void 0
|
|
60
|
-
});
|
|
61
|
-
Object.defineProperty(this, "discussions", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: void 0
|
|
66
|
-
});
|
|
67
|
-
Object.defineProperty(this, "pendingEncryptedMessages", {
|
|
68
|
-
enumerable: true,
|
|
69
|
-
configurable: true,
|
|
70
|
-
writable: true,
|
|
71
|
-
value: void 0
|
|
72
|
-
});
|
|
73
|
-
Object.defineProperty(this, "pendingAnnouncements", {
|
|
74
|
-
enumerable: true,
|
|
75
|
-
configurable: true,
|
|
76
|
-
writable: true,
|
|
77
|
-
value: void 0
|
|
78
|
-
});
|
|
79
|
-
Object.defineProperty(this, "activeSeekers", {
|
|
80
|
-
enumerable: true,
|
|
81
|
-
configurable: true,
|
|
82
|
-
writable: true,
|
|
83
|
-
value: void 0
|
|
84
|
-
});
|
|
85
|
-
this.version(13).stores({
|
|
86
|
-
contacts: '++id, ownerUserId, userId, name, isOnline, lastSeen, createdAt, [ownerUserId+userId] , [ownerUserId+name]',
|
|
87
|
-
messages: '++id, ownerUserId, contactUserId, type, direction, status, timestamp, seeker, [ownerUserId+contactUserId], [ownerUserId+status], [ownerUserId+contactUserId+status], [ownerUserId+seeker], [ownerUserId+contactUserId+direction], [ownerUserId+direction+status]',
|
|
88
|
-
userProfile: 'userId, username, status, lastSeen',
|
|
89
|
-
discussions: '++id, ownerUserId, &[ownerUserId+contactUserId], status, [ownerUserId+status], lastSyncTimestamp, unreadCount, lastMessageTimestamp, createdAt, updatedAt',
|
|
90
|
-
pendingEncryptedMessages: '++id, fetchedAt, seeker',
|
|
91
|
-
pendingAnnouncements: '++id, fetchedAt, &announcement',
|
|
92
|
-
activeSeekers: '++id, seeker',
|
|
93
|
-
});
|
|
94
|
-
// Add hooks for automatic timestamps
|
|
95
|
-
this.contacts.hook('creating', function (_primKey, obj, _trans) {
|
|
96
|
-
obj.createdAt = new Date();
|
|
97
|
-
});
|
|
98
|
-
this.userProfile.hook('creating', function (_primKey, obj, _trans) {
|
|
99
|
-
obj.createdAt = new Date();
|
|
100
|
-
obj.updatedAt = new Date();
|
|
101
|
-
});
|
|
102
|
-
this.userProfile.hook('updating', function (modifications, _primKey, _obj, _trans) {
|
|
103
|
-
modifications.updatedAt = new Date();
|
|
104
|
-
});
|
|
105
|
-
this.discussions.hook('creating', function (_primKey, obj, _trans) {
|
|
106
|
-
obj.createdAt = new Date();
|
|
107
|
-
obj.updatedAt = new Date();
|
|
108
|
-
});
|
|
109
|
-
this.discussions.hook('updating', function (modifications, _primKey, _obj, _trans) {
|
|
110
|
-
modifications.updatedAt = new Date();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
// Helper methods for common operations
|
|
114
|
-
/** CONTACTS */
|
|
115
|
-
async getContactsByOwner(ownerUserId) {
|
|
116
|
-
return await this.contacts
|
|
117
|
-
.where('ownerUserId')
|
|
118
|
-
.equals(ownerUserId)
|
|
119
|
-
.toArray();
|
|
120
|
-
}
|
|
121
|
-
async getContactByOwnerAndUserId(ownerUserId, userId) {
|
|
122
|
-
return await this.contacts
|
|
123
|
-
.where('[ownerUserId+userId]')
|
|
124
|
-
.equals([ownerUserId, userId])
|
|
125
|
-
.first();
|
|
126
|
-
}
|
|
127
|
-
/** DISCUSSIONS */
|
|
128
|
-
async getDiscussionsByOwner(ownerUserId) {
|
|
129
|
-
const all = await this.discussions
|
|
130
|
-
.where('ownerUserId')
|
|
131
|
-
.equals(ownerUserId)
|
|
132
|
-
.toArray();
|
|
133
|
-
return all.sort((a, b) => {
|
|
134
|
-
if (a.lastMessageTimestamp && b.lastMessageTimestamp) {
|
|
135
|
-
return (b.lastMessageTimestamp.getTime() - a.lastMessageTimestamp.getTime());
|
|
136
|
-
}
|
|
137
|
-
if (a.lastMessageTimestamp)
|
|
138
|
-
return -1;
|
|
139
|
-
if (b.lastMessageTimestamp)
|
|
140
|
-
return 1;
|
|
141
|
-
return b.createdAt.getTime() - a.createdAt.getTime();
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
async getUnreadCountByOwner(ownerUserId) {
|
|
145
|
-
const discussions = await this.discussions
|
|
146
|
-
.where('ownerUserId')
|
|
147
|
-
.equals(ownerUserId)
|
|
148
|
-
.toArray();
|
|
149
|
-
return discussions.reduce((total, d) => total + d.unreadCount, 0);
|
|
150
|
-
}
|
|
151
|
-
async getDiscussionByOwnerAndContact(ownerUserId, contactUserId) {
|
|
152
|
-
if (!ownerUserId || !contactUserId) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
return await this.discussions
|
|
156
|
-
.where('[ownerUserId+contactUserId]')
|
|
157
|
-
.equals([ownerUserId, contactUserId])
|
|
158
|
-
.first();
|
|
159
|
-
}
|
|
160
|
-
async markMessagesAsRead(ownerUserId, contactUserId) {
|
|
161
|
-
await this.messages
|
|
162
|
-
.where('[ownerUserId+contactUserId+status]')
|
|
163
|
-
.equals([ownerUserId, contactUserId, MessageStatus.DELIVERED])
|
|
164
|
-
.and(msg => msg.direction === MessageDirection.INCOMING)
|
|
165
|
-
.modify({ status: MessageStatus.READ });
|
|
166
|
-
await this.discussions
|
|
167
|
-
.where('[ownerUserId+contactUserId]')
|
|
168
|
-
.equals([ownerUserId, contactUserId])
|
|
169
|
-
.modify({ unreadCount: 0 });
|
|
170
|
-
}
|
|
171
|
-
async getMessagesForContactByOwner(ownerUserId, contactUserId, limit = 50) {
|
|
172
|
-
return await this.messages
|
|
173
|
-
.where('[ownerUserId+contactUserId]')
|
|
174
|
-
.equals([ownerUserId, contactUserId])
|
|
175
|
-
.reverse()
|
|
176
|
-
.limit(limit)
|
|
177
|
-
.toArray();
|
|
178
|
-
}
|
|
179
|
-
async addMessage(message) {
|
|
180
|
-
const messageId = await this.messages.add(message);
|
|
181
|
-
// Get existing discussion
|
|
182
|
-
const discussion = await this.getDiscussionByOwnerAndContact(message.ownerUserId, message.contactUserId);
|
|
183
|
-
if (discussion) {
|
|
184
|
-
await this.discussions.update(discussion.id, {
|
|
185
|
-
lastMessageId: messageId,
|
|
186
|
-
lastMessageContent: message.content,
|
|
187
|
-
lastMessageTimestamp: message.timestamp,
|
|
188
|
-
unreadCount: message.direction === MessageDirection.INCOMING &&
|
|
189
|
-
![MessageType.ANNOUNCEMENT, MessageType.KEEP_ALIVE].includes(message.type)
|
|
190
|
-
? discussion.unreadCount + 1
|
|
191
|
-
: discussion.unreadCount,
|
|
192
|
-
updatedAt: new Date(),
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
// Note: For new messages, a discussion should already exist from the protocol
|
|
197
|
-
// If not, we'll create a minimal one (this shouldn't normally happen)
|
|
198
|
-
console.log('Warning: Creating discussion for contact without protocol setup:', message.contactUserId);
|
|
199
|
-
await this.discussions.put({
|
|
200
|
-
ownerUserId: message.ownerUserId,
|
|
201
|
-
contactUserId: message.contactUserId,
|
|
202
|
-
lastMessageId: messageId,
|
|
203
|
-
lastMessageContent: message.content,
|
|
204
|
-
lastMessageTimestamp: message.timestamp,
|
|
205
|
-
unreadCount: message.direction === MessageDirection.INCOMING &&
|
|
206
|
-
![MessageType.ANNOUNCEMENT, MessageType.KEEP_ALIVE].includes(message.type)
|
|
207
|
-
? 1
|
|
208
|
-
: 0,
|
|
209
|
-
updatedAt: new Date(),
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return messageId;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Update the last sync timestamp for a discussion
|
|
216
|
-
* @param discussionId - The discussion ID
|
|
217
|
-
* @param timestamp - The sync timestamp
|
|
218
|
-
*/
|
|
219
|
-
async updateLastSyncTimestamp(discussionId, timestamp) {
|
|
220
|
-
await this.discussions.update(discussionId, {
|
|
221
|
-
lastSyncTimestamp: timestamp,
|
|
222
|
-
updatedAt: new Date(),
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
async deleteDb() {
|
|
226
|
-
await this.close();
|
|
227
|
-
await this.delete();
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Set all active seekers, replacing any existing ones
|
|
231
|
-
* @param seekers - Array of seeker Uint8Arrays to store
|
|
232
|
-
*/
|
|
233
|
-
async setActiveSeekers(seekers) {
|
|
234
|
-
await this.transaction('rw', this.activeSeekers, async () => {
|
|
235
|
-
// Clear all existing seekers
|
|
236
|
-
await this.activeSeekers.clear();
|
|
237
|
-
// Bulk add all new seekers
|
|
238
|
-
if (seekers.length > 0) {
|
|
239
|
-
await this.activeSeekers.bulkAdd(seekers.map(seeker => ({
|
|
240
|
-
seeker,
|
|
241
|
-
})));
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Get all active seekers from the database
|
|
247
|
-
* @returns Array of seeker Uint8Arrays
|
|
248
|
-
*/
|
|
249
|
-
async getActiveSeekers() {
|
|
250
|
-
const activeSeekers = await this.activeSeekers.toArray();
|
|
251
|
-
return activeSeekers.map(item => item.seeker);
|
|
252
|
-
}
|
|
49
|
+
/** Serialize a SendAnnouncement to a JSON string for SQLite text column */
|
|
50
|
+
export function serializeSendAnnouncement(announcement) {
|
|
51
|
+
return JSON.stringify({
|
|
52
|
+
announcement_bytes: Array.from(announcement.announcement_bytes),
|
|
53
|
+
when_to_send: announcement.when_to_send.toISOString(),
|
|
54
|
+
});
|
|
253
55
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
56
|
+
/** Deserialize a SendAnnouncement JSON string from SQLite back to an object */
|
|
57
|
+
export function deserializeSendAnnouncement(json) {
|
|
58
|
+
const parsed = JSON.parse(json);
|
|
59
|
+
return {
|
|
60
|
+
announcement_bytes: new Uint8Array(parsed.announcement_bytes),
|
|
61
|
+
when_to_send: new Date(parsed.when_to_send),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Convert a raw SQLite discussion row to a Discussion object.
|
|
65
|
+
* Deserializes sendAnnouncement from JSON text to SendAnnouncement. */
|
|
66
|
+
export function rowToDiscussion(row) {
|
|
67
|
+
return {
|
|
68
|
+
...row,
|
|
69
|
+
sendAnnouncement: typeof row.sendAnnouncement === 'string'
|
|
70
|
+
? deserializeSendAnnouncement(row.sendAnnouncement)
|
|
71
|
+
: null,
|
|
72
|
+
lastAnnouncementMessage: row.announcementMessage ?? undefined,
|
|
73
|
+
};
|
|
264
74
|
}
|
package/dist/gossip.d.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* GossipSdk
|
|
2
|
+
* GossipSdk - Singleton SDK with clean lifecycle API
|
|
3
3
|
*
|
|
4
4
|
* @example
|
|
5
5
|
* ```typescript
|
|
6
|
-
* import {
|
|
7
|
-
*
|
|
8
|
-
* const sdk = createGossipSdk();
|
|
6
|
+
* import { gossipSdk } from '@massalabs/gossip-sdk';
|
|
9
7
|
*
|
|
10
8
|
* // Initialize once at app startup
|
|
11
|
-
* await
|
|
9
|
+
* await gossipSdk.init({
|
|
10
|
+
* db,
|
|
12
11
|
* protocolBaseUrl: 'https://api.example.com',
|
|
13
12
|
* });
|
|
14
13
|
*
|
|
15
14
|
* // Open session (login) - SDK handles keys/session internally
|
|
16
|
-
* await
|
|
15
|
+
* await gossipSdk.openSession({
|
|
17
16
|
* mnemonic: 'word1 word2 ...',
|
|
18
17
|
* onPersist: async (blob) => { /* save to db *\/ },
|
|
19
18
|
* });
|
|
20
19
|
*
|
|
21
20
|
* // Or restore existing session
|
|
22
|
-
* await
|
|
21
|
+
* await gossipSdk.openSession({
|
|
23
22
|
* mnemonic: 'word1 word2 ...',
|
|
24
23
|
* encryptedSession: savedBlob,
|
|
25
24
|
* encryptionKey: key,
|
|
@@ -27,28 +26,29 @@
|
|
|
27
26
|
* });
|
|
28
27
|
*
|
|
29
28
|
* // Use clean API
|
|
30
|
-
* await
|
|
31
|
-
* await
|
|
32
|
-
* const contacts = await
|
|
29
|
+
* await gossipSdk.messages.send(contactId, 'Hello!');
|
|
30
|
+
* await gossipSdk.discussions.start(contact);
|
|
31
|
+
* const contacts = await gossipSdk.contacts.list(ownerUserId);
|
|
33
32
|
*
|
|
34
33
|
* // Events
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* gossipSdk.on('message', (msg) => { ... });
|
|
35
|
+
* gossipSdk.on('discussionRequest', (discussion, contact) => { ... });
|
|
37
36
|
*
|
|
38
37
|
* // Logout
|
|
39
|
-
* await
|
|
38
|
+
* await gossipSdk.closeSession();
|
|
40
39
|
* ```
|
|
41
40
|
*/
|
|
42
|
-
import { type Contact, type Discussion, type Message
|
|
41
|
+
import { type Contact, type Discussion, type Message } from './db';
|
|
43
42
|
import { type SdkConfig, type DeepPartial } from './config/sdk';
|
|
43
|
+
import { SessionStatus, SessionConfig } from './assets/generated/wasm/gossip_wasm';
|
|
44
44
|
import { EncryptionKey } from './wasm/encryption';
|
|
45
45
|
import { type AnnouncementReceptionResult } from './services/announcement';
|
|
46
|
-
import {
|
|
46
|
+
import { DiscussionInitializationResult } from './services/discussion';
|
|
47
47
|
import { type MessageResult, type SendMessageResult } from './services/message';
|
|
48
48
|
import { AuthService } from './services/auth';
|
|
49
49
|
import type { DeleteContactResult, UpdateContactNameResult } from './utils/contacts';
|
|
50
50
|
import { type ValidationResult } from './utils/validation';
|
|
51
|
-
import {
|
|
51
|
+
import type { UserPublicKeys } from './wasm/bindings';
|
|
52
52
|
import { SdkEventType, type SdkEventHandlers } from './core/SdkEventEmitter';
|
|
53
53
|
import { AnnouncementPayload } from './utils/announcementPayload';
|
|
54
54
|
import { Result } from './utils/type';
|
|
@@ -64,16 +64,26 @@ export interface GossipSdkInitOptions {
|
|
|
64
64
|
protocolBaseUrl?: string;
|
|
65
65
|
/** SDK configuration (optional - uses defaults if not provided) */
|
|
66
66
|
config?: DeepPartial<SdkConfig>;
|
|
67
|
+
/** URL to wa-sqlite.wasm (for bundlers that rewrite asset paths) */
|
|
68
|
+
wasmUrl?: string;
|
|
69
|
+
/**
|
|
70
|
+
* OPFS directory path for persistent SQLite storage.
|
|
71
|
+
* When set, data persists across page reloads via OPFS.
|
|
72
|
+
* When omitted, uses an in-memory database (data lost on reload).
|
|
73
|
+
*/
|
|
74
|
+
opfsPath?: string;
|
|
67
75
|
}
|
|
68
76
|
export interface OpenSessionOptions {
|
|
69
77
|
/** BIP39 mnemonic phrase */
|
|
70
78
|
mnemonic: string;
|
|
71
79
|
/** Existing encrypted session blob (for restoring session) */
|
|
72
80
|
encryptedSession?: Uint8Array;
|
|
73
|
-
/** Encryption key for decrypting session
|
|
81
|
+
/** Encryption key for decrypting session */
|
|
74
82
|
encryptionKey?: EncryptionKey;
|
|
75
83
|
/** Callback when session state changes (for persistence) */
|
|
76
84
|
onPersist?: (encryptedBlob: Uint8Array, encryptionKey: EncryptionKey) => Promise<void>;
|
|
85
|
+
/** Encryption key for persisting session (required if onPersist is provided) */
|
|
86
|
+
persistEncryptionKey?: EncryptionKey;
|
|
77
87
|
/** Custom session configuration (optional, uses defaults if not provided) */
|
|
78
88
|
sessionConfig?: SessionConfig;
|
|
79
89
|
}
|
|
@@ -94,7 +104,7 @@ declare class GossipSdk {
|
|
|
94
104
|
/**
|
|
95
105
|
* Initialize the SDK. Call once at app startup.
|
|
96
106
|
*/
|
|
97
|
-
init(options
|
|
107
|
+
init(options: GossipSdkInitOptions): Promise<void>;
|
|
98
108
|
/**
|
|
99
109
|
* Open a session (login).
|
|
100
110
|
* Generates keys from mnemonic and initializes session.
|
|
@@ -123,7 +133,15 @@ declare class GossipSdk {
|
|
|
123
133
|
* Get encrypted session blob for persistence.
|
|
124
134
|
* Throws if no session is open.
|
|
125
135
|
*/
|
|
126
|
-
getEncryptedSession(): Uint8Array;
|
|
136
|
+
getEncryptedSession(encryptionKey: EncryptionKey): Uint8Array;
|
|
137
|
+
/**
|
|
138
|
+
* Configure session persistence after session is opened.
|
|
139
|
+
* Use this when you need to set up persistence after account creation.
|
|
140
|
+
*
|
|
141
|
+
* @param encryptionKey - Key to encrypt session blob
|
|
142
|
+
* @param onPersist - Callback to save encrypted session blob
|
|
143
|
+
*/
|
|
144
|
+
configurePersistence(encryptionKey: EncryptionKey, onPersist: (encryptedBlob: Uint8Array, encryptionKey: EncryptionKey) => Promise<void>): void;
|
|
127
145
|
/** Auth service (available after init, before session) */
|
|
128
146
|
get auth(): AuthService;
|
|
129
147
|
/** Message service */
|
|
@@ -134,7 +152,6 @@ declare class GossipSdk {
|
|
|
134
152
|
get announcements(): AnnouncementServiceAPI;
|
|
135
153
|
/** Contact management */
|
|
136
154
|
get contacts(): ContactsAPI;
|
|
137
|
-
get db(): GossipDatabase;
|
|
138
155
|
/**
|
|
139
156
|
* Update state for all discussions:
|
|
140
157
|
* - Cleanup orphaned peers
|
|
@@ -164,6 +181,20 @@ declare class GossipSdk {
|
|
|
164
181
|
off<K extends SdkEventType>(event: K, handler: SdkEventHandlers[K]): void;
|
|
165
182
|
private requireSession;
|
|
166
183
|
private handleSessionPersist;
|
|
184
|
+
/**
|
|
185
|
+
* Reset messages stuck in SENDING status to WAITING_SESSION.
|
|
186
|
+
*
|
|
187
|
+
* Per spec: SENDING is a transient state that should never be persisted.
|
|
188
|
+
* If the app crashes/closes during a send, the message would be stuck forever.
|
|
189
|
+
*
|
|
190
|
+
* By resetting to WAITING_SESSION:
|
|
191
|
+
* - Message will be re-encrypted with current session keys
|
|
192
|
+
* - Message will be automatically sent when session is active
|
|
193
|
+
* - No manual user intervention required
|
|
194
|
+
*
|
|
195
|
+
* We also clear encryptedMessage and seeker since they may be stale.
|
|
196
|
+
*/
|
|
197
|
+
private resetStuckSendingMessages;
|
|
167
198
|
}
|
|
168
199
|
interface MessageServiceAPI {
|
|
169
200
|
/** Get a message by its ID */
|
|
@@ -181,7 +212,7 @@ interface MessageServiceAPI {
|
|
|
181
212
|
}
|
|
182
213
|
interface DiscussionServiceAPI {
|
|
183
214
|
/** Start a new discussion with a contact */
|
|
184
|
-
start(contact: Contact, payload?: AnnouncementPayload): Promise<Result<
|
|
215
|
+
start(contact: Contact, payload?: AnnouncementPayload): Promise<Result<DiscussionInitializationResult, Error>>;
|
|
185
216
|
/** Accept an incoming discussion request */
|
|
186
217
|
accept(discussion: Discussion): Promise<Result<Uint8Array, Error>>;
|
|
187
218
|
/** Renew a broken discussion */
|
|
@@ -233,6 +264,6 @@ interface PollingAPI {
|
|
|
233
264
|
/** Whether polling is currently running */
|
|
234
265
|
isRunning: boolean;
|
|
235
266
|
}
|
|
236
|
-
/**
|
|
237
|
-
export declare
|
|
267
|
+
/** The singleton GossipSdk instance */
|
|
268
|
+
export declare const gossipSdk: GossipSdk;
|
|
238
269
|
export { GossipSdk };
|