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