@massalabs/gossip-sdk 0.0.2-dev.20260211172752 → 0.0.2-dev.20260212071538

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 CHANGED
@@ -1,12 +1,11 @@
1
1
  # Gossip SDK
2
2
 
3
- A platform-agnostic SDK for the Gossip messenger app. This SDK enables automation, chatbot integrations, and programmatic access to Gossip functionality.
3
+ A platform-agnostic SDK for the Gossip messenger app. Enables automation, chatbot integrations, and programmatic access to Gossip functionality.
4
4
 
5
5
  ## Overview
6
6
 
7
7
  The Gossip SDK provides a clean, typed interface for:
8
8
 
9
- - **Account Management** - Create, load, restore, and manage user accounts
10
9
  - **Contact Management** - Add, update, and delete contacts
11
10
  - **Discussion Management** - Initialize and manage encrypted discussions
12
11
  - **Message Operations** - Send and receive encrypted messages
@@ -16,73 +15,53 @@ The Gossip SDK provides a clean, typed interface for:
16
15
  ## Installation
17
16
 
18
17
  ```bash
19
- cd gossip-sdk
20
- npm install
18
+ npm install @massalabs/gossip-sdk
21
19
  ```
22
20
 
23
- ### Peer Dependencies
24
-
25
- The SDK requires these peer dependencies (provided by the host project):
26
-
27
- - `dexie` ^4.0.0 - IndexedDB wrapper for local storage
28
-
29
21
  ## Quick Start
30
22
 
31
- The SDK uses a singleton pattern with a simple lifecycle:
23
+ The SDK uses a factory pattern — each call to `createGossipSdk()` returns a new instance:
32
24
 
33
25
  ```typescript
34
- import { gossipSdk, GossipDatabase } from 'gossip-sdk';
26
+ import { createGossipSdk } from '@massalabs/gossip-sdk';
35
27
 
36
- // 1. Initialize once at app startup
37
- const db = new GossipDatabase();
38
- await db.open();
28
+ const sdk = createGossipSdk();
39
29
 
40
- await gossipSdk.init({
41
- db,
42
- protocolBaseUrl: 'https://api.example.com',
43
- });
30
+ // 1. Initialize (optional config)
31
+ await sdk.init();
44
32
 
45
33
  // 2. Open session (login)
46
- await gossipSdk.openSession({
34
+ await sdk.openSession({
47
35
  mnemonic: 'word1 word2 word3 ... word12',
48
- // For existing session:
49
- encryptedSession: savedBlob,
50
- encryptionKey,
51
- // For persistence:
52
- onPersist: async (blob, key) => {
53
- await saveToStorage(blob, key);
54
- },
55
36
  });
56
37
 
57
38
  // 3. Use the SDK
58
- const contacts = await gossipSdk.contacts.list(gossipSdk.userId);
59
- await gossipSdk.discussions.start(contact, 'Hello!');
60
- await gossipSdk.messages.send(message);
39
+ const contacts = await sdk.contacts.list(sdk.userId);
40
+ await sdk.discussions.start(contact);
41
+ await sdk.messages.send(message);
61
42
 
62
43
  // 4. Listen to events
63
- gossipSdk.on('message', msg => {
64
- console.log('New message:', msg.content);
44
+ sdk.on(SdkEventType.MESSAGE_RECEIVED, msg => {
45
+ console.log('New message:', msg);
65
46
  });
66
47
 
67
48
  // 5. Logout
68
- await gossipSdk.closeSession();
49
+ await sdk.closeSession();
69
50
  ```
70
51
 
71
52
  ## Lifecycle
72
53
 
73
54
  ### 1. Initialize
74
55
 
75
- Call `init()` once at app startup to configure the database and protocol:
56
+ Call `init()` once at app startup. All options are optional:
76
57
 
77
58
  ```typescript
78
- await gossipSdk.init({
79
- // Required: Database instance
80
- db: new GossipDatabase(),
59
+ // Uses GOSSIP_API_URL / VITE_GOSSIP_API_URL env var, or defaults to api.usegossip.com
60
+ await sdk.init();
81
61
 
82
- // Optional: API base URL (uses default if not provided)
62
+ // Or with explicit config
63
+ await sdk.init({
83
64
  protocolBaseUrl: 'https://api.usegossip.com',
84
-
85
- // Optional: Configuration overrides
86
65
  config: {
87
66
  polling: {
88
67
  enabled: true,
@@ -98,23 +77,23 @@ Call `openSession()` to authenticate and create a cryptographic session:
98
77
 
99
78
  ```typescript
100
79
  // New account (no existing session)
101
- await gossipSdk.openSession({
80
+ await sdk.openSession({
102
81
  mnemonic: 'word1 word2 word3 ... word12',
103
82
  });
104
83
 
105
84
  // Restore existing session
106
- await gossipSdk.openSession({
85
+ await sdk.openSession({
107
86
  mnemonic: 'word1 word2 word3 ... word12',
108
87
  encryptedSession: savedBlob,
109
88
  encryptionKey: savedKey,
110
89
  });
111
90
 
112
91
  // With persistence (saves session on changes)
113
- await gossipSdk.openSession({
92
+ await sdk.openSession({
114
93
  mnemonic: 'word1 word2 word3 ... word12',
115
94
  encryptionKey,
116
95
  onPersist: async (blob, key) => {
117
- await db.userProfile.update(userId, { session: blob });
96
+ await storage.save({ session: blob });
118
97
  },
119
98
  });
120
99
  ```
@@ -122,7 +101,7 @@ await gossipSdk.openSession({
122
101
  ### 3. Close Session (Logout)
123
102
 
124
103
  ```typescript
125
- await gossipSdk.closeSession();
104
+ await sdk.closeSession();
126
105
  ```
127
106
 
128
107
  ## Service APIs
@@ -133,8 +112,8 @@ All services are available after `openSession()` is called.
133
112
 
134
113
  ```typescript
135
114
  // Send a message
136
- const result = await gossipSdk.messages.send({
137
- ownerUserId: gossipSdk.userId,
115
+ const result = await sdk.messages.send({
116
+ ownerUserId: sdk.userId,
138
117
  contactUserId: contactId,
139
118
  content: 'Hello!',
140
119
  type: MessageType.TEXT,
@@ -144,51 +123,48 @@ const result = await gossipSdk.messages.send({
144
123
  });
145
124
 
146
125
  // Fetch new messages from server
147
- const fetchResult = await gossipSdk.messages.fetch();
148
-
149
- // Resend failed messages
150
- await gossipSdk.messages.resend(failedMessagesMap);
126
+ const fetchResult = await sdk.messages.fetch();
151
127
 
152
128
  // Find message by seeker
153
- const msg = await gossipSdk.messages.findBySeeker(seeker, ownerUserId);
129
+ const msg = await sdk.messages.findBySeeker(seeker, ownerUserId);
130
+
131
+ // Mark as read
132
+ await sdk.messages.markAsRead(messageId);
154
133
  ```
155
134
 
156
135
  ### Discussions
157
136
 
158
137
  ```typescript
159
138
  // Start a new discussion
160
- const { discussionId } = await gossipSdk.discussions.start(contact, 'Hello!');
139
+ const result = await sdk.discussions.start(contact);
161
140
 
162
141
  // Accept an incoming discussion request
163
- await gossipSdk.discussions.accept(discussion);
142
+ await sdk.discussions.accept(discussion);
164
143
 
165
144
  // Renew a broken session
166
- await gossipSdk.discussions.renew(contactUserId);
145
+ await sdk.discussions.renew(contactUserId);
167
146
 
168
- // Check if discussion can send messages
169
- const canSend = await gossipSdk.discussions.isStable(
170
- ownerUserId,
171
- contactUserId
172
- );
147
+ // Get session status for a contact
148
+ const status = sdk.discussions.getStatus(contactUserId);
173
149
 
174
150
  // List all discussions
175
- const discussions = await gossipSdk.discussions.list(ownerUserId);
151
+ const discussions = await sdk.discussions.list(ownerUserId);
176
152
 
177
153
  // Get a specific discussion
178
- const discussion = await gossipSdk.discussions.get(ownerUserId, contactUserId);
154
+ const discussion = await sdk.discussions.get(ownerUserId, contactUserId);
179
155
  ```
180
156
 
181
157
  ### Contacts
182
158
 
183
159
  ```typescript
184
160
  // List all contacts
185
- const contacts = await gossipSdk.contacts.list(ownerUserId);
161
+ const contacts = await sdk.contacts.list(ownerUserId);
186
162
 
187
163
  // Get a specific contact
188
- const contact = await gossipSdk.contacts.get(ownerUserId, contactUserId);
164
+ const contact = await sdk.contacts.get(ownerUserId, contactUserId);
189
165
 
190
166
  // Add a new contact
191
- const result = await gossipSdk.contacts.add(
167
+ const result = await sdk.contacts.add(
192
168
  ownerUserId,
193
169
  contactUserId,
194
170
  'Alice',
@@ -196,100 +172,54 @@ const result = await gossipSdk.contacts.add(
196
172
  );
197
173
 
198
174
  // Update contact name
199
- await gossipSdk.contacts.updateName(ownerUserId, contactUserId, 'Alice Smith');
175
+ await sdk.contacts.updateName(ownerUserId, contactUserId, 'Alice Smith');
200
176
 
201
177
  // Delete contact and all associated data
202
- await gossipSdk.contacts.delete(ownerUserId, contactUserId);
178
+ await sdk.contacts.delete(ownerUserId, contactUserId);
203
179
  ```
204
180
 
205
181
  ### Announcements
206
182
 
207
183
  ```typescript
208
184
  // Fetch and process announcements from server
209
- const result = await gossipSdk.announcements.fetch();
210
-
211
- // Resend failed announcements
212
- await gossipSdk.announcements.resend(failedDiscussions);
213
- ```
214
-
215
- ### Auth (Available before session)
216
-
217
- ```typescript
218
- // Create a new account
219
- const result = await gossipSdk.auth.createAccount(
220
- username,
221
- mnemonic,
222
- encryptionKey
223
- );
224
-
225
- // Restore account from mnemonic
226
- const result = await gossipSdk.auth.restoreAccount(
227
- username,
228
- mnemonic,
229
- encryptionKey
230
- );
185
+ const result = await sdk.announcements.fetch();
231
186
  ```
232
187
 
233
- ### Refresh
188
+ ### Auth (Available after init, before session)
234
189
 
235
190
  ```typescript
236
- // Handle session refresh for active discussions
237
- await gossipSdk.refresh.handleSessionRefresh(activeDiscussions);
191
+ // Publish public key so the user is discoverable
192
+ await sdk.auth.ensurePublicKeyPublished(publicKey, userId);
238
193
  ```
239
194
 
240
195
  ## Events
241
196
 
242
- Subscribe to SDK events for real-time updates:
197
+ Subscribe to SDK events using `SdkEventType`:
243
198
 
244
199
  ```typescript
245
- // Message events
246
- gossipSdk.on('message', message => {
247
- // New message received
248
- });
200
+ import { SdkEventType } from '@massalabs/gossip-sdk';
249
201
 
250
- gossipSdk.on('messageSent', message => {
251
- // Message sent successfully
252
- });
253
-
254
- gossipSdk.on('messageFailed', (message, error) => {
255
- // Message failed to send
256
- });
202
+ // Message events
203
+ sdk.on(SdkEventType.MESSAGE_RECEIVED, message => { ... });
204
+ sdk.on(SdkEventType.MESSAGE_SENT, message => { ... });
257
205
 
258
206
  // Discussion events
259
- gossipSdk.on('discussionRequest', (discussion, contact) => {
260
- // Incoming discussion request
261
- });
262
-
263
- gossipSdk.on('discussionStatusChanged', discussion => {
264
- // Discussion status changed
265
- });
266
-
267
- // Session events
268
- gossipSdk.on('sessionBroken', discussion => {
269
- // Session broken (deprecated - use auto-renewal)
270
- });
271
-
272
- gossipSdk.on('sessionRenewed', discussion => {
273
- // Session successfully renewed
274
- });
207
+ sdk.on(SdkEventType.SESSION_REQUESTED, (discussion, contact) => { ... });
275
208
 
276
209
  // Error handling
277
- gossipSdk.on('error', (error, context) => {
210
+ sdk.on(SdkEventType.ERROR, (error, context) => {
278
211
  console.error(`Error in ${context}:`, error);
279
212
  });
280
213
 
281
214
  // Unsubscribe
282
- gossipSdk.off('message', handler);
215
+ sdk.off(SdkEventType.MESSAGE_RECEIVED, handler);
283
216
  ```
284
217
 
285
218
  ## Polling
286
219
 
287
- The SDK can automatically poll for messages, announcements, and session refresh:
288
-
289
220
  ```typescript
290
221
  // Enable via config
291
- await gossipSdk.init({
292
- db,
222
+ await sdk.init({
293
223
  config: {
294
224
  polling: {
295
225
  enabled: true,
@@ -301,76 +231,37 @@ await gossipSdk.init({
301
231
  });
302
232
 
303
233
  // Or control manually
304
- gossipSdk.polling.start();
305
- gossipSdk.polling.stop();
306
- console.log(gossipSdk.polling.isRunning);
234
+ sdk.polling.start();
235
+ sdk.polling.stop();
236
+ console.log(sdk.polling.isRunning);
307
237
  ```
308
238
 
309
239
  ## Session Info
310
240
 
311
- Access session information after `openSession()`:
312
-
313
241
  ```typescript
314
- // User ID (encoded string)
315
- const userId = gossipSdk.userId;
316
-
317
- // User ID (raw bytes)
318
- const userIdBytes = gossipSdk.userIdBytes;
319
-
320
- // Public keys
321
- const publicKeys = gossipSdk.publicKeys;
242
+ const userId = sdk.userId; // Encoded string
243
+ const userIdBytes = sdk.userIdBytes; // Raw bytes
244
+ const publicKeys = sdk.publicKeys;
322
245
 
323
- // Check session state
324
- console.log(gossipSdk.isInitialized); // true after init()
325
- console.log(gossipSdk.isSessionOpen); // true after openSession()
246
+ console.log(sdk.isInitialized); // true after init()
247
+ console.log(sdk.isSessionOpen); // true after openSession()
326
248
 
327
249
  // Get encrypted session for manual persistence
328
- const blob = gossipSdk.getEncryptedSession(encryptionKey);
250
+ const blob = sdk.getEncryptedSession();
329
251
  ```
330
252
 
331
- ## Configuration
253
+ ## State Update
332
254
 
333
- Full configuration options with defaults:
255
+ Trigger a full state refresh for all discussions (session renewal, queued messages, keep-alives):
334
256
 
335
257
  ```typescript
336
- await gossipSdk.init({
337
- db,
338
- config: {
339
- // Network settings
340
- protocol: {
341
- baseUrl: 'https://api.usegossip.com', // API endpoint
342
- timeout: 10000, // Request timeout (ms)
343
- retryAttempts: 3, // Retry count
344
- },
345
-
346
- // Polling settings
347
- polling: {
348
- enabled: false, // Auto-start polling
349
- messagesIntervalMs: 5000, // Message fetch interval
350
- announcementsIntervalMs: 10000, // Announcement fetch interval
351
- sessionRefreshIntervalMs: 30000, // Session refresh interval
352
- },
353
-
354
- // Message settings
355
- messages: {
356
- fetchDelayMs: 100, // Delay between fetch iterations
357
- maxFetchIterations: 30, // Max iterations per fetch call
358
- deduplicationWindowMs: 30000, // Duplicate detection window
359
- },
360
-
361
- // Announcement settings
362
- announcements: {
363
- fetchLimit: 500, // Max announcements per request
364
- brokenThresholdMs: 3600000, // Time before marking broken (1 hour)
365
- },
366
- },
367
- });
258
+ await sdk.updateState();
368
259
  ```
369
260
 
370
261
  ## Utilities
371
262
 
372
263
  ```typescript
373
- const utils = gossipSdk.utils;
264
+ const utils = sdk.utils;
374
265
 
375
266
  // Validate user ID format
376
267
  const result = utils.validateUserId(userId);
@@ -384,16 +275,47 @@ const encoded = utils.encodeUserId(rawBytes);
384
275
  const decoded = utils.decodeUserId(encodedString);
385
276
  ```
386
277
 
278
+ ## Configuration
279
+
280
+ Full configuration options with defaults:
281
+
282
+ ```typescript
283
+ await sdk.init({
284
+ config: {
285
+ protocol: {
286
+ baseUrl: 'https://api.usegossip.com',
287
+ timeout: 10000,
288
+ retryAttempts: 3,
289
+ },
290
+ polling: {
291
+ enabled: false,
292
+ messagesIntervalMs: 5000,
293
+ announcementsIntervalMs: 10000,
294
+ sessionRefreshIntervalMs: 30000,
295
+ },
296
+ messages: {
297
+ fetchDelayMs: 100,
298
+ maxFetchIterations: 30,
299
+ deduplicationWindowMs: 30000,
300
+ },
301
+ announcements: {
302
+ fetchLimit: 500,
303
+ brokenThresholdMs: 3600000,
304
+ },
305
+ },
306
+ });
307
+ ```
308
+
387
309
  ## Session Persistence
388
310
 
389
- For restoring sessions across app restarts, pass `encryptionKey` and `onPersist` when opening the session:
311
+ For restoring sessions across app restarts:
390
312
 
391
313
  ```typescript
392
- await gossipSdk.openSession({
314
+ await sdk.openSession({
393
315
  mnemonic,
394
316
  encryptionKey,
395
317
  onPersist: async (blob, key) => {
396
- await db.userProfile.update(userId, { session: blob });
318
+ await storage.save({ session: blob });
397
319
  },
398
320
  });
399
321
  ```
@@ -407,24 +329,20 @@ The SDK automatically handles session recovery:
407
329
  3. **Auto-Accept** - When peer sends announcement, SDK auto-accepts for existing contacts
408
330
  4. **Message Processing** - After session becomes active, queued messages are sent automatically
409
331
 
410
- See [STATUS-REFERENCE.md](./docs/STATUS-REFERENCE.md) for detailed status documentation.
411
-
412
332
  ## Types
413
333
 
414
- Import types from the SDK:
415
-
416
334
  ```typescript
417
335
  import type {
418
336
  UserProfile,
419
337
  Contact,
420
338
  Discussion,
421
339
  Message,
422
- DiscussionStatus,
423
- DiscussionDirection,
424
340
  MessageStatus,
425
341
  MessageDirection,
426
342
  MessageType,
427
- } from 'gossip-sdk';
343
+ } from '@massalabs/gossip-sdk';
344
+
345
+ import { SessionStatus, SdkEventType } from '@massalabs/gossip-sdk';
428
346
  ```
429
347
 
430
348
  ## Testing
@@ -432,7 +350,6 @@ import type {
432
350
  ```bash
433
351
  npm test # Watch mode
434
352
  npm run test:run # Single run
435
- npm run test:coverage # With coverage report
436
353
  ```
437
354
 
438
355
  Tests use `fake-indexeddb` to simulate IndexedDB in Node.js environment.
@@ -442,7 +359,7 @@ Tests use `fake-indexeddb` to simulate IndexedDB in Node.js environment.
442
359
  ```
443
360
  gossip-sdk/
444
361
  ├── src/
445
- │ ├── gossipSdk.ts # Main singleton SDK class
362
+ │ ├── gossipSdk.ts # SDK class & factory
446
363
  │ ├── db.ts # Database (Dexie) implementation
447
364
  │ ├── contacts.ts # Contact operations
448
365
  │ ├── api/
@@ -459,14 +376,10 @@ gossip-sdk/
459
376
  │ │ ├── discussion.ts # Discussion service
460
377
  │ │ ├── announcement.ts # Announcement service
461
378
  │ │ └── refresh.ts # Session refresh service
462
- │ ├── types/
463
- │ │ └── events.ts # Event type definitions
379
+ │ ├── types/ # Type definitions
464
380
  │ ├── utils/ # Utility modules
465
381
  │ └── wasm/ # WASM module wrappers
466
- ├── test/ # Test files
467
- ├── docs/
468
- │ └── STATUS-REFERENCE.md # Status documentation
469
- └── README.md
382
+ └── test/ # Test files
470
383
  ```
471
384
 
472
385
  ## License
package/dist/db.d.ts CHANGED
@@ -166,17 +166,6 @@ export declare class GossipDatabase extends Dexie {
166
166
  getActiveSeekers(): Promise<Uint8Array[]>;
167
167
  }
168
168
  /**
169
- * Get the database instance.
170
- * Creates a default instance if none was set via setDb().
169
+ * Get the database instance. Creates a default instance on first call.
171
170
  */
172
- export declare function getDb(): GossipDatabase;
173
- /**
174
- * Set the database instance.
175
- * Call this before using any SDK functions if you need a custom db instance.
176
- */
177
- export declare function setDb(database: GossipDatabase): void;
178
- /**
179
- * Get the database instance.
180
- * Creates a default instance if none was set via setDb().
181
- */
182
- export declare const db: GossipDatabase;
171
+ export declare function gossipDb(): GossipDatabase;
package/dist/db.js CHANGED
@@ -245,55 +245,14 @@ export class GossipDatabase extends Dexie {
245
245
  return activeSeekers.map(item => item.seeker);
246
246
  }
247
247
  }
248
- // Database instance - initialized lazily or via setDb()
248
+ // Database instance - lazily initialized singleton
249
249
  let _db = null;
250
- let _warnedGlobalDbAccess = false;
251
- // Store a reference to the Proxy to detect it
252
- let _proxyDb = null;
253
250
  /**
254
- * Get the database instance.
255
- * Creates a default instance if none was set via setDb().
251
+ * Get the database instance. Creates a default instance on first call.
256
252
  */
257
- export function getDb() {
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)) {
253
+ export function gossipDb() {
254
+ if (!_db || !(_db instanceof GossipDatabase)) {
260
255
  _db = new GossipDatabase();
261
256
  }
262
257
  return _db;
263
258
  }
264
- /**
265
- * Set the database instance.
266
- * Call this before using any SDK functions if you need a custom db instance.
267
- */
268
- export function setDb(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
- }
281
- }
282
- /**
283
- * Get the database instance.
284
- * Creates a default instance if none was set via setDb().
285
- */
286
- export const db = (_proxyDb = new Proxy({}, {
287
- get(_target, prop) {
288
- if (!_warnedGlobalDbAccess) {
289
- _warnedGlobalDbAccess = true;
290
- console.warn('[GossipSdk] Global db access is deprecated. Use createGossipSdk() or setDb().');
291
- }
292
- const target = getDb();
293
- const value = Reflect.get(target, prop);
294
- if (typeof value === 'function') {
295
- return value.bind(target);
296
- }
297
- return value;
298
- },
299
- }));