@massalabs/gossip-sdk 0.0.2-dev.20260210150149 → 0.0.2-dev.20260211140005

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.
@@ -23,7 +23,7 @@ export declare const defaultMessageProtocol: MessageProtocolType;
23
23
  *
24
24
  * @example
25
25
  * ```typescript
26
- * import { setProtocolBaseUrl } from 'gossip-sdk';
26
+ * import { setProtocolBaseUrl } from '@massalabs/gossip-sdk';
27
27
  *
28
28
  * // Set custom API endpoint
29
29
  * setProtocolBaseUrl('https://my-server.com/api');
@@ -66,7 +66,7 @@ export const defaultMessageProtocol = MessageProtocolType.REST;
66
66
  *
67
67
  * @example
68
68
  * ```typescript
69
- * import { setProtocolBaseUrl } from 'gossip-sdk';
69
+ * import { setProtocolBaseUrl } from '@massalabs/gossip-sdk';
70
70
  *
71
71
  * // Set custom API endpoint
72
72
  * setProtocolBaseUrl('https://my-server.com/api');
@@ -43,6 +43,8 @@ export interface MessagesConfig {
43
43
  * resulting in message being re-sent on restart.
44
44
  */
45
45
  deduplicationWindowMs: number;
46
+ /** Delay before retrying a failed message send in ms (default: 5000 = 5 seconds) */
47
+ retryDelayMs: number;
46
48
  }
47
49
  /**
48
50
  * Announcement configuration
@@ -52,6 +54,8 @@ export interface AnnouncementsConfig {
52
54
  fetchLimit: number;
53
55
  /** Time before marking failed announcements as broken in ms (default: 3600000 = 1 hour) */
54
56
  brokenThresholdMs: number;
57
+ /** Delay before retrying a failed announcement send in ms (default: 15000 = 15 seconds) */
58
+ retryDelayMs: number;
55
59
  }
56
60
  /**
57
61
  * Complete SDK configuration
@@ -16,16 +16,18 @@ export const defaultSdkConfig = {
16
16
  enabled: false,
17
17
  messagesIntervalMs: 5000,
18
18
  announcementsIntervalMs: 10000,
19
- sessionRefreshIntervalMs: 30000,
19
+ sessionRefreshIntervalMs: 10000, //30000,
20
20
  },
21
21
  messages: {
22
22
  fetchDelayMs: 100,
23
23
  maxFetchIterations: 30,
24
24
  deduplicationWindowMs: 30000, // 30 seconds
25
+ retryDelayMs: 5000, // 5 seconds
25
26
  },
26
27
  announcements: {
27
28
  fetchLimit: 500,
28
29
  brokenThresholdMs: 60 * 60 * 1000, // 1 hour
30
+ retryDelayMs: 15000, // 15 seconds
29
31
  },
30
32
  };
31
33
  /**
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * import { getContacts, addContact, deleteContact } from 'gossip-sdk';
8
+ * import { getContacts, addContact, deleteContact } from '@massalabs/gossip-sdk';
9
9
  *
10
10
  * // Get all contacts
11
11
  * const contacts = await getContacts(userId);
package/dist/contacts.js CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * import { getContacts, addContact, deleteContact } from 'gossip-sdk';
8
+ * import { getContacts, addContact, deleteContact } from '@massalabs/gossip-sdk';
9
9
  *
10
10
  * // Get all contacts
11
11
  * const contacts = await getContacts(userId);
@@ -4,31 +4,40 @@
4
4
  * Type-safe event emitter for SDK events.
5
5
  */
6
6
  import type { Message, Discussion, Contact } from '../db';
7
- export type SdkEventType = 'message' | 'messageSent' | 'messageFailed' | 'discussionRequest' | 'discussionStatusChanged' | 'sessionBroken' | 'sessionRenewed' | 'error';
7
+ export declare enum SdkEventType {
8
+ MESSAGE_RECEIVED = "messageReceived",
9
+ MESSAGE_SENT = "messageSent",
10
+ MESSAGE_FAILED = "messageFailed",
11
+ SESSION_REQUESTED = "sessionRequested",
12
+ SESSION_CREATED = "sessionCreated",
13
+ SESSION_RENEWED = "sessionRenewed",
14
+ SESSION_ACCEPTED = "sessionAccepted",
15
+ ERROR = "error"
16
+ }
8
17
  export interface SdkEventHandlers {
9
- message: (message: Message) => void;
10
- messageSent: (message: Message) => void;
11
- messageFailed: (message: Message, error: Error) => void;
12
- discussionRequest: (discussion: Discussion, contact: Contact) => void;
13
- discussionStatusChanged: (discussion: Discussion) => void;
14
- sessionBroken: (discussion: Discussion) => void;
15
- sessionRenewed: (discussion: Discussion) => void;
16
- error: (error: Error, context: string) => void;
18
+ [SdkEventType.MESSAGE_RECEIVED]: (message: Message) => void;
19
+ [SdkEventType.MESSAGE_SENT]: (message: Message) => void;
20
+ [SdkEventType.MESSAGE_FAILED]: (message: Message, error: Error) => void;
21
+ [SdkEventType.SESSION_REQUESTED]: (discussion: Discussion, contact: Contact) => void;
22
+ [SdkEventType.SESSION_CREATED]: (discussion: Discussion) => void;
23
+ [SdkEventType.SESSION_RENEWED]: (discussion: Discussion) => void;
24
+ [SdkEventType.SESSION_ACCEPTED]: (contactUserId: string) => void;
25
+ [SdkEventType.ERROR]: (error: Error, context: string) => void;
17
26
  }
18
27
  export declare class SdkEventEmitter {
19
28
  private handlers;
20
29
  /**
21
30
  * Register an event handler
22
31
  */
23
- on<K extends SdkEventType>(event: K, handler: SdkEventHandlers[K]): void;
32
+ on<K extends keyof SdkEventHandlers>(event: K, handler: SdkEventHandlers[K]): void;
24
33
  /**
25
34
  * Remove an event handler
26
35
  */
27
- off<K extends SdkEventType>(event: K, handler: SdkEventHandlers[K]): void;
36
+ off<K extends keyof SdkEventHandlers>(event: K, handler: SdkEventHandlers[K]): void;
28
37
  /**
29
38
  * Emit an event to all registered handlers
30
39
  */
31
- emit<K extends SdkEventType>(event: K, ...args: Parameters<SdkEventHandlers[K]>): void;
40
+ emit<K extends keyof SdkEventHandlers>(event: K, ...args: Parameters<SdkEventHandlers[K]>): void;
32
41
  /**
33
42
  * Remove all handlers for all events
34
43
  */
@@ -4,6 +4,20 @@
4
4
  * Type-safe event emitter for SDK events.
5
5
  */
6
6
  // ─────────────────────────────────────────────────────────────────────────────
7
+ // Event Types
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+ export var SdkEventType;
10
+ (function (SdkEventType) {
11
+ SdkEventType["MESSAGE_RECEIVED"] = "messageReceived";
12
+ SdkEventType["MESSAGE_SENT"] = "messageSent";
13
+ SdkEventType["MESSAGE_FAILED"] = "messageFailed";
14
+ SdkEventType["SESSION_REQUESTED"] = "sessionRequested";
15
+ SdkEventType["SESSION_CREATED"] = "sessionCreated";
16
+ SdkEventType["SESSION_RENEWED"] = "sessionRenewed";
17
+ SdkEventType["SESSION_ACCEPTED"] = "sessionAccepted";
18
+ SdkEventType["ERROR"] = "error";
19
+ })(SdkEventType || (SdkEventType = {}));
20
+ // ─────────────────────────────────────────────────────────────────────────────
7
21
  // Event Emitter Class
8
22
  // ─────────────────────────────────────────────────────────────────────────────
9
23
  export class SdkEventEmitter {
@@ -13,14 +27,14 @@ export class SdkEventEmitter {
13
27
  configurable: true,
14
28
  writable: true,
15
29
  value: {
16
- message: new Set(),
17
- messageSent: new Set(),
18
- messageFailed: new Set(),
19
- discussionRequest: new Set(),
20
- discussionStatusChanged: new Set(),
21
- sessionBroken: new Set(),
22
- sessionRenewed: new Set(),
23
- error: new Set(),
30
+ [SdkEventType.MESSAGE_RECEIVED]: new Set(),
31
+ [SdkEventType.MESSAGE_SENT]: new Set(),
32
+ [SdkEventType.MESSAGE_FAILED]: new Set(),
33
+ [SdkEventType.SESSION_REQUESTED]: new Set(),
34
+ [SdkEventType.SESSION_CREATED]: new Set(),
35
+ [SdkEventType.SESSION_RENEWED]: new Set(),
36
+ [SdkEventType.SESSION_ACCEPTED]: new Set(),
37
+ [SdkEventType.ERROR]: new Set(),
24
38
  }
25
39
  });
26
40
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { SdkConfig } from '../config/sdk';
7
7
  import type { Discussion } from '../db';
8
+ import { SdkEventEmitter } from './SdkEventEmitter';
8
9
  export interface PollingCallbacks {
9
10
  /** Fetch messages from protocol */
10
11
  fetchMessages: () => Promise<void>;
@@ -14,16 +15,15 @@ export interface PollingCallbacks {
14
15
  handleSessionRefresh: (discussions: Discussion[]) => Promise<void>;
15
16
  /** Get active discussions for session refresh */
16
17
  getActiveDiscussions: () => Promise<Discussion[]>;
17
- /** Called when a polling error occurs */
18
- onError: (error: Error, context: string) => void;
19
18
  }
20
19
  export declare class SdkPolling {
21
20
  private timers;
22
21
  private callbacks;
22
+ private eventEmitter;
23
23
  /**
24
24
  * Start polling with the given configuration and callbacks.
25
25
  */
26
- start(config: SdkConfig, callbacks: PollingCallbacks): void;
26
+ start(config: SdkConfig, callbacks: PollingCallbacks, eventEmitter: SdkEventEmitter): void;
27
27
  /**
28
28
  * Stop all polling timers.
29
29
  */
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Manages polling timers for messages, announcements, and session refresh.
5
5
  */
6
+ import { SdkEventType } from './SdkEventEmitter';
6
7
  // ─────────────────────────────────────────────────────────────────────────────
7
8
  // Polling Manager Class
8
9
  // ─────────────────────────────────────────────────────────────────────────────
@@ -24,14 +25,21 @@ export class SdkPolling {
24
25
  writable: true,
25
26
  value: null
26
27
  });
28
+ Object.defineProperty(this, "eventEmitter", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: null
33
+ });
27
34
  }
28
35
  /**
29
36
  * Start polling with the given configuration and callbacks.
30
37
  */
31
- start(config, callbacks) {
38
+ start(config, callbacks, eventEmitter) {
32
39
  // Stop any existing timers first
33
40
  this.stop();
34
41
  this.callbacks = callbacks;
42
+ this.eventEmitter = eventEmitter;
35
43
  console.log('[SdkPolling] Starting polling', {
36
44
  messagesIntervalMs: config.polling.messagesIntervalMs,
37
45
  announcementsIntervalMs: config.polling.announcementsIntervalMs,
@@ -43,8 +51,8 @@ export class SdkPolling {
43
51
  await this.callbacks?.fetchMessages();
44
52
  }
45
53
  catch (error) {
46
- console.error('[SdkPolling] Message polling error:', error);
47
- this.callbacks?.onError(error instanceof Error ? error : new Error(String(error)), 'message_polling');
54
+ const err = error instanceof Error ? error : new Error(String(error));
55
+ this.eventEmitter?.emit(SdkEventType.ERROR, err, 'message_polling');
48
56
  }
49
57
  }, config.polling.messagesIntervalMs);
50
58
  // Start announcement polling
@@ -53,8 +61,8 @@ export class SdkPolling {
53
61
  await this.callbacks?.fetchAnnouncements();
54
62
  }
55
63
  catch (error) {
56
- console.error('[SdkPolling] Announcement polling error:', error);
57
- this.callbacks?.onError(error instanceof Error ? error : new Error(String(error)), 'announcement_polling');
64
+ const err = error instanceof Error ? error : new Error(String(error));
65
+ this.eventEmitter?.emit(SdkEventType.ERROR, err, 'announcement_polling');
58
66
  }
59
67
  }, config.polling.announcementsIntervalMs);
60
68
  // Start session refresh polling
@@ -66,8 +74,8 @@ export class SdkPolling {
66
74
  }
67
75
  }
68
76
  catch (error) {
69
- console.error('[SdkPolling] Session refresh polling error:', error);
70
- this.callbacks?.onError(error instanceof Error ? error : new Error(String(error)), 'session_refresh_polling');
77
+ const err = error instanceof Error ? error : new Error(String(error));
78
+ this.eventEmitter?.emit(SdkEventType.ERROR, err, 'session_update');
71
79
  }
72
80
  }, config.polling.sessionRefreshIntervalMs);
73
81
  }
@@ -88,6 +96,7 @@ export class SdkPolling {
88
96
  this.timers.sessionRefresh = null;
89
97
  }
90
98
  this.callbacks = null;
99
+ this.eventEmitter = null;
91
100
  }
92
101
  /**
93
102
  * Check if polling is currently running.
package/dist/db.d.ts CHANGED
@@ -23,6 +23,8 @@ export interface Message {
23
23
  contactUserId: string;
24
24
  content: string;
25
25
  serializedContent?: Uint8Array;
26
+ encryptedMessage?: Uint8Array;
27
+ whenToSend?: Date;
26
28
  type: MessageType;
27
29
  direction: MessageDirection;
28
30
  status: MessageStatus;
@@ -37,7 +39,6 @@ export interface Message {
37
39
  originalContent?: string;
38
40
  originalSeeker: Uint8Array;
39
41
  };
40
- encryptedMessage?: Uint8Array;
41
42
  }
42
43
  export interface UserProfile {
43
44
  userId: string;
@@ -65,13 +66,9 @@ export interface UserProfile {
65
66
  lastPublicKeyPush?: Date;
66
67
  lastBulletinCounter?: string;
67
68
  }
68
- export declare enum DiscussionStatus {
69
- PENDING = "pending",
70
- ACTIVE = "active",
71
- CLOSED = "closed",// closed by the user
72
- BROKEN = "broken",// The session is killed. Need to be reinitiated
73
- SEND_FAILED = "sendFailed",// The discussion was initiated by the session manager but could not be broadcasted on network
74
- RECONNECTING = "reconnecting"
69
+ export declare enum DiscussionDirection {
70
+ RECEIVED = "received",
71
+ INITIATED = "initiated"
75
72
  }
76
73
  export declare enum MessageDirection {
77
74
  INCOMING = "incoming",
@@ -79,35 +76,35 @@ export declare enum MessageDirection {
79
76
  }
80
77
  export declare enum MessageStatus {
81
78
  WAITING_SESSION = "waiting_session",// Waiting for active session with peer
82
- SENDING = "sending",
79
+ READY = "ready",
83
80
  SENT = "sent",
84
81
  DELIVERED = "delivered",
85
- READ = "read",
86
- FAILED = "failed"
87
- }
88
- export declare enum DiscussionDirection {
89
- INITIATED = "initiated",
90
- RECEIVED = "received"
82
+ READ = "read"
91
83
  }
92
84
  export declare enum MessageType {
93
85
  TEXT = "text",
86
+ ANNOUNCEMENT = "announcement",
94
87
  KEEP_ALIVE = "keep_alive",
95
88
  IMAGE = "image",
96
89
  FILE = "file",
97
90
  AUDIO = "audio",
98
91
  VIDEO = "video"
99
92
  }
93
+ export interface readyAnnouncement {
94
+ announcement_bytes: Uint8Array;
95
+ when_to_send: Date;
96
+ }
97
+ export type SendAnnouncement = null | readyAnnouncement;
100
98
  export interface Discussion {
101
99
  id?: number;
102
100
  ownerUserId: string;
103
101
  contactUserId: string;
102
+ weAccepted: boolean;
103
+ sendAnnouncement: SendAnnouncement;
104
104
  direction: DiscussionDirection;
105
- status: DiscussionStatus;
106
- nextSeeker?: Uint8Array;
107
- initiationAnnouncement?: Uint8Array;
108
- announcementMessage?: string;
109
105
  lastSyncTimestamp?: Date;
110
106
  customName?: string;
107
+ lastAnnouncementMessage?: string;
111
108
  lastMessageId?: number;
112
109
  lastMessageContent?: string;
113
110
  lastMessageTimestamp?: Date;
@@ -147,11 +144,6 @@ export declare class GossipDatabase extends Dexie {
147
144
  getDiscussionsByOwner(ownerUserId: string): Promise<Discussion[]>;
148
145
  getUnreadCountByOwner(ownerUserId: string): Promise<number>;
149
146
  getDiscussionByOwnerAndContact(ownerUserId: string, contactUserId: string): Promise<Discussion | undefined>;
150
- /**
151
- * Get all active discussions with their sync status
152
- * @returns Array of active discussions
153
- */
154
- getActiveDiscussionsByOwner(ownerUserId: string): Promise<Discussion[]>;
155
147
  markMessagesAsRead(ownerUserId: string, contactUserId: string): Promise<void>;
156
148
  getMessagesForContactByOwner(ownerUserId: string, contactUserId: string, limit?: number): Promise<Message[]>;
157
149
  addMessage(message: Omit<Message, 'id'>): Promise<number>;
package/dist/db.js CHANGED
@@ -5,16 +5,11 @@
5
5
  * Provides tables for contacts, messages, discussions, user profiles, and more.
6
6
  */
7
7
  import Dexie from 'dexie';
8
- // Unified discussion interface combining protocol state and UI metadata
9
- export var DiscussionStatus;
10
- (function (DiscussionStatus) {
11
- DiscussionStatus["PENDING"] = "pending";
12
- DiscussionStatus["ACTIVE"] = "active";
13
- DiscussionStatus["CLOSED"] = "closed";
14
- DiscussionStatus["BROKEN"] = "broken";
15
- DiscussionStatus["SEND_FAILED"] = "sendFailed";
16
- DiscussionStatus["RECONNECTING"] = "reconnecting";
17
- })(DiscussionStatus || (DiscussionStatus = {}));
8
+ export var DiscussionDirection;
9
+ (function (DiscussionDirection) {
10
+ DiscussionDirection["RECEIVED"] = "received";
11
+ DiscussionDirection["INITIATED"] = "initiated";
12
+ })(DiscussionDirection || (DiscussionDirection = {}));
18
13
  export var MessageDirection;
19
14
  (function (MessageDirection) {
20
15
  MessageDirection["INCOMING"] = "incoming";
@@ -23,20 +18,15 @@ export var MessageDirection;
23
18
  export var MessageStatus;
24
19
  (function (MessageStatus) {
25
20
  MessageStatus["WAITING_SESSION"] = "waiting_session";
26
- MessageStatus["SENDING"] = "sending";
21
+ MessageStatus["READY"] = "ready";
27
22
  MessageStatus["SENT"] = "sent";
28
23
  MessageStatus["DELIVERED"] = "delivered";
29
24
  MessageStatus["READ"] = "read";
30
- MessageStatus["FAILED"] = "failed";
31
25
  })(MessageStatus || (MessageStatus = {}));
32
- export var DiscussionDirection;
33
- (function (DiscussionDirection) {
34
- DiscussionDirection["INITIATED"] = "initiated";
35
- DiscussionDirection["RECEIVED"] = "received";
36
- })(DiscussionDirection || (DiscussionDirection = {}));
37
26
  export var MessageType;
38
27
  (function (MessageType) {
39
28
  MessageType["TEXT"] = "text";
29
+ MessageType["ANNOUNCEMENT"] = "announcement";
40
30
  MessageType["KEEP_ALIVE"] = "keep_alive";
41
31
  MessageType["IMAGE"] = "image";
42
32
  MessageType["FILE"] = "file";
@@ -165,16 +155,6 @@ export class GossipDatabase extends Dexie {
165
155
  .equals([ownerUserId, contactUserId])
166
156
  .first();
167
157
  }
168
- /**
169
- * Get all active discussions with their sync status
170
- * @returns Array of active discussions
171
- */
172
- async getActiveDiscussionsByOwner(ownerUserId) {
173
- return await this.discussions
174
- .where('[ownerUserId+status]')
175
- .equals([ownerUserId, DiscussionStatus.ACTIVE])
176
- .toArray();
177
- }
178
158
  async markMessagesAsRead(ownerUserId, contactUserId) {
179
159
  await this.messages
180
160
  .where('[ownerUserId+contactUserId+status]')
@@ -216,16 +196,10 @@ export class GossipDatabase extends Dexie {
216
196
  await this.discussions.put({
217
197
  ownerUserId: message.ownerUserId,
218
198
  contactUserId: message.contactUserId,
219
- direction: message.direction === MessageDirection.INCOMING
220
- ? DiscussionDirection.RECEIVED
221
- : DiscussionDirection.INITIATED,
222
- status: DiscussionStatus.PENDING,
223
- nextSeeker: undefined,
224
199
  lastMessageId: messageId,
225
200
  lastMessageContent: message.content,
226
201
  lastMessageTimestamp: message.timestamp,
227
202
  unreadCount: message.direction === MessageDirection.INCOMING ? 1 : 0,
228
- createdAt: new Date(),
229
203
  updatedAt: new Date(),
230
204
  });
231
205
  }
@@ -274,12 +248,15 @@ export class GossipDatabase extends Dexie {
274
248
  // Database instance - initialized lazily or via setDb()
275
249
  let _db = null;
276
250
  let _warnedGlobalDbAccess = false;
251
+ // Store a reference to the Proxy to detect it
252
+ let _proxyDb = null;
277
253
  /**
278
254
  * Get the database instance.
279
255
  * Creates a default instance if none was set via setDb().
280
256
  */
281
257
  export function getDb() {
282
- if (!_db) {
258
+ // Prevent infinite recursion: if _db is the Proxy itself or not a real instance, create a new instance
259
+ if (!_db || _db === _proxyDb || !(_db instanceof GossipDatabase)) {
283
260
  _db = new GossipDatabase();
284
261
  }
285
262
  return _db;
@@ -289,13 +266,24 @@ export function getDb() {
289
266
  * Call this before using any SDK functions if you need a custom db instance.
290
267
  */
291
268
  export function setDb(database) {
292
- _db = database;
269
+ // Prevent setting the Proxy itself to avoid infinite recursion
270
+ // The Proxy is not an instance of GossipDatabase, so we can detect it that way
271
+ if (!(database instanceof GossipDatabase) || database === _proxyDb) {
272
+ // If Proxy is passed, ensure _db exists by calling getDb()
273
+ // This will reuse existing _db if it was already created (e.g., by db.open())
274
+ // or create a new one if needed. This ensures we always use the same instance.
275
+ getDb();
276
+ // Don't overwrite _db - just ensure it exists and is consistent
277
+ }
278
+ else {
279
+ _db = database;
280
+ }
293
281
  }
294
282
  /**
295
283
  * Get the database instance.
296
284
  * Creates a default instance if none was set via setDb().
297
285
  */
298
- export const db = new Proxy({}, {
286
+ export const db = (_proxyDb = new Proxy({}, {
299
287
  get(_target, prop) {
300
288
  if (!_warnedGlobalDbAccess) {
301
289
  _warnedGlobalDbAccess = true;
@@ -308,4 +296,4 @@ export const db = new Proxy({}, {
308
296
  }
309
297
  return value;
310
298
  },
311
- });
299
+ }));
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @example
5
5
  * ```typescript
6
- * import { gossipSdk } from 'gossip-sdk';
6
+ * import { gossipSdk } from '@massalabs/gossip-sdk';
7
7
  *
8
8
  * // Initialize once at app startup
9
9
  * await gossipSdk.init({
@@ -40,15 +40,25 @@
40
40
  */
41
41
  import { GossipDatabase, type Contact, type Discussion, type Message } from './db';
42
42
  import { type SdkConfig, type DeepPartial } from './config/sdk';
43
+ import { SessionStatus, SessionConfig } from './assets/generated/wasm/gossip_wasm';
43
44
  import { EncryptionKey } from './wasm/encryption';
44
45
  import { type AnnouncementReceptionResult } from './services/announcement';
46
+ import { discussionInitializationResult } from './services/discussion';
45
47
  import { type MessageResult, type SendMessageResult } from './services/message';
46
48
  import { AuthService } from './services/auth';
47
49
  import type { DeleteContactResult, UpdateContactNameResult } from './utils/contacts';
48
50
  import { type ValidationResult } from './utils/validation';
49
51
  import type { UserPublicKeys } from './wasm/bindings';
50
- import { type SdkEventType, type SdkEventHandlers } from './core/SdkEventEmitter';
51
- export type { SdkEventType, SdkEventHandlers };
52
+ import { SdkEventType, type SdkEventHandlers } from './core/SdkEventEmitter';
53
+ import { AnnouncementPayload } from './utils/announcementPayload';
54
+ import { Result } from './utils/type';
55
+ export type { SdkEventHandlers };
56
+ export { SdkEventType };
57
+ export declare enum SdkStatus {
58
+ UNINITIALIZED = "uninitialized",
59
+ INITIALIZED = "initialized",
60
+ SESSION_OPEN = "session_open"
61
+ }
52
62
  export interface GossipSdkInitOptions {
53
63
  /** Database instance */
54
64
  db: GossipDatabase;
@@ -68,6 +78,8 @@ export interface OpenSessionOptions {
68
78
  onPersist?: (encryptedBlob: Uint8Array, encryptionKey: EncryptionKey) => Promise<void>;
69
79
  /** Encryption key for persisting session (required if onPersist is provided) */
70
80
  persistEncryptionKey?: EncryptionKey;
81
+ /** Custom session configuration (optional, uses defaults if not provided) */
82
+ sessionConfig?: SessionConfig;
71
83
  }
72
84
  declare class GossipSdkImpl {
73
85
  private state;
@@ -83,7 +95,6 @@ declare class GossipSdkImpl {
83
95
  private _discussionsAPI;
84
96
  private _announcementsAPI;
85
97
  private _contactsAPI;
86
- private _refreshAPI;
87
98
  /**
88
99
  * Initialize the SDK. Call once at app startup.
89
100
  */
@@ -135,8 +146,14 @@ declare class GossipSdkImpl {
135
146
  get announcements(): AnnouncementServiceAPI;
136
147
  /** Contact management */
137
148
  get contacts(): ContactsAPI;
138
- /** Refresh/sync service */
139
- get refresh(): RefreshServiceAPI;
149
+ /**
150
+ * Update state for all discussions:
151
+ * - Cleanup orphaned peers
152
+ * - Refresh sessions and trigger create_session for lost sessions
153
+ * - Send queued announcements
154
+ * - Send queued messages and keep-alives
155
+ */
156
+ updateState(): Promise<void>;
140
157
  /** Utility functions */
141
158
  get utils(): SdkUtils;
142
159
  /** Current SDK configuration (read-only) */
@@ -157,69 +174,31 @@ declare class GossipSdkImpl {
157
174
  */
158
175
  off<K extends SdkEventType>(event: K, handler: SdkEventHandlers[K]): void;
159
176
  private requireSession;
160
- /**
161
- * Handle automatic session renewal when session is lost.
162
- * Called by onSessionRenewalNeeded event.
163
- */
164
- private handleSessionRenewal;
165
- /**
166
- * Handle automatic session accept when peer has sent us an announcement.
167
- * Called by onSessionAcceptNeeded event.
168
- * This is different from renewal - we respond to their session request.
169
- */
170
- private handleSessionAccept;
171
- /**
172
- * Handle session becoming Active after peer accepts our announcement.
173
- * Called by onSessionBecameActive event.
174
- *
175
- * This is different from handleSessionAccept:
176
- * - handleSessionAccept: WE accept a session (peer initiated)
177
- * - handleSessionBecameActive: PEER accepts our session (we initiated)
178
- */
179
- private handleSessionBecameActive;
180
177
  private handleSessionPersist;
181
- /**
182
- * Reset any messages stuck in SENDING status to FAILED.
183
- * This handles the case where the app crashed or was closed during message send.
184
- * Per spec: SENDING should never be persisted - if we find it on startup, it failed.
185
- */
186
- /**
187
- * Reset messages stuck in SENDING status to WAITING_SESSION.
188
- *
189
- * Per spec: SENDING is a transient state that should never be persisted.
190
- * If the app crashes/closes during a send, the message would be stuck forever.
191
- *
192
- * By resetting to WAITING_SESSION:
193
- * - Message will be re-encrypted with current session keys
194
- * - Message will be automatically sent when session is active
195
- * - No manual user intervention required
196
- *
197
- * We also clear encryptedMessage and seeker since they may be stale.
198
- */
199
- private resetStuckSendingMessages;
200
178
  }
201
179
  interface MessageServiceAPI {
180
+ /** Get a message by its ID */
181
+ get(id: number): Promise<Message | undefined>;
182
+ /** Get all messages for a contact */
183
+ getMessages(contactUserId: string): Promise<Message[]>;
202
184
  /** Send a message */
203
185
  send(message: Omit<Message, 'id'>): Promise<SendMessageResult>;
204
186
  /** Fetch and decrypt messages from the protocol */
205
187
  fetch(): Promise<MessageResult>;
206
- /** Resend failed messages */
207
- resend(messages: Map<string, Message[]>): Promise<void>;
208
188
  /** Find a message by its seeker */
209
189
  findBySeeker(seeker: Uint8Array, ownerUserId: string): Promise<Message | undefined>;
190
+ /** Mark a message as read */
191
+ markAsRead(id: number): Promise<boolean>;
210
192
  }
211
193
  interface DiscussionServiceAPI {
212
194
  /** Start a new discussion with a contact */
213
- start(contact: Contact, message?: string): Promise<{
214
- discussionId: number;
215
- announcement: Uint8Array;
216
- }>;
195
+ start(contact: Contact, payload?: AnnouncementPayload): Promise<Result<discussionInitializationResult, Error>>;
217
196
  /** Accept an incoming discussion request */
218
- accept(discussion: Discussion): Promise<void>;
197
+ accept(discussion: Discussion): Promise<Result<Uint8Array, Error>>;
219
198
  /** Renew a broken discussion */
220
- renew(contactUserId: string): Promise<void>;
221
- /** Check if a discussion is in a stable state */
222
- isStable(ownerUserId: string, contactUserId: string): Promise<boolean>;
199
+ renew(contactUserId: string): Promise<Result<Uint8Array, Error>>;
200
+ /** Get the status of a discussion */
201
+ getStatus(contactUserId: string): SessionStatus;
223
202
  /** List all discussions for the owner */
224
203
  list(ownerUserId: string): Promise<Discussion[]>;
225
204
  /** Get a specific discussion */
@@ -228,8 +207,6 @@ interface DiscussionServiceAPI {
228
207
  interface AnnouncementServiceAPI {
229
208
  /** Fetch and process announcements from the protocol */
230
209
  fetch(): Promise<AnnouncementReceptionResult>;
231
- /** Resend failed announcements */
232
- resend(failedDiscussions: Discussion[]): Promise<void>;
233
210
  }
234
211
  interface ContactsAPI {
235
212
  /** List all contacts for the owner */
@@ -247,10 +224,6 @@ interface ContactsAPI {
247
224
  /** Delete a contact and all related data */
248
225
  delete(ownerUserId: string, contactUserId: string): Promise<DeleteContactResult>;
249
226
  }
250
- interface RefreshServiceAPI {
251
- /** Handle session refresh (keep-alive, broken sessions, etc.) */
252
- handleSessionRefresh(activeDiscussions: Discussion[]): Promise<void>;
253
- }
254
227
  interface SdkUtils {
255
228
  /** Validate a user ID format */
256
229
  validateUserId(userId: string): ValidationResult;