@massalabs/gossip-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +484 -0
  2. package/package.json +41 -0
  3. package/src/api/messageProtocol/index.ts +53 -0
  4. package/src/api/messageProtocol/mock.ts +13 -0
  5. package/src/api/messageProtocol/rest.ts +209 -0
  6. package/src/api/messageProtocol/types.ts +70 -0
  7. package/src/config/protocol.ts +97 -0
  8. package/src/config/sdk.ts +131 -0
  9. package/src/contacts.ts +210 -0
  10. package/src/core/SdkEventEmitter.ts +91 -0
  11. package/src/core/SdkPolling.ts +134 -0
  12. package/src/core/index.ts +9 -0
  13. package/src/crypto/bip39.ts +84 -0
  14. package/src/crypto/encryption.ts +77 -0
  15. package/src/db.ts +465 -0
  16. package/src/gossipSdk.ts +994 -0
  17. package/src/index.ts +211 -0
  18. package/src/services/announcement.ts +653 -0
  19. package/src/services/auth.ts +95 -0
  20. package/src/services/discussion.ts +380 -0
  21. package/src/services/message.ts +1055 -0
  22. package/src/services/refresh.ts +234 -0
  23. package/src/sw.ts +17 -0
  24. package/src/types/events.ts +108 -0
  25. package/src/types.ts +70 -0
  26. package/src/utils/base64.ts +39 -0
  27. package/src/utils/contacts.ts +161 -0
  28. package/src/utils/discussions.ts +55 -0
  29. package/src/utils/logs.ts +86 -0
  30. package/src/utils/messageSerialization.ts +257 -0
  31. package/src/utils/queue.ts +106 -0
  32. package/src/utils/type.ts +7 -0
  33. package/src/utils/userId.ts +114 -0
  34. package/src/utils/validation.ts +144 -0
  35. package/src/utils.ts +47 -0
  36. package/src/wasm/encryption.ts +108 -0
  37. package/src/wasm/index.ts +20 -0
  38. package/src/wasm/loader.ts +123 -0
  39. package/src/wasm/session.ts +276 -0
  40. package/src/wasm/userKeys.ts +31 -0
  41. package/test/config/protocol.spec.ts +31 -0
  42. package/test/config/sdk.spec.ts +163 -0
  43. package/test/db/helpers.spec.ts +142 -0
  44. package/test/db/operations.spec.ts +128 -0
  45. package/test/db/states.spec.ts +535 -0
  46. package/test/integration/discussion-flow.spec.ts +422 -0
  47. package/test/integration/messaging-flow.spec.ts +708 -0
  48. package/test/integration/sdk-lifecycle.spec.ts +325 -0
  49. package/test/mocks/index.ts +9 -0
  50. package/test/mocks/mockMessageProtocol.ts +100 -0
  51. package/test/services/auth.spec.ts +311 -0
  52. package/test/services/discussion.spec.ts +279 -0
  53. package/test/services/message-deduplication.spec.ts +299 -0
  54. package/test/services/message-startup.spec.ts +331 -0
  55. package/test/services/message.spec.ts +817 -0
  56. package/test/services/refresh.spec.ts +199 -0
  57. package/test/services/session-status.spec.ts +349 -0
  58. package/test/session/wasm.spec.ts +227 -0
  59. package/test/setup.ts +52 -0
  60. package/test/utils/contacts.spec.ts +156 -0
  61. package/test/utils/discussions.spec.ts +66 -0
  62. package/test/utils/queue.spec.ts +52 -0
  63. package/test/utils/serialization.spec.ts +120 -0
  64. package/test/utils/userId.spec.ts +120 -0
  65. package/test/utils/validation.spec.ts +223 -0
  66. package/test/utils.ts +212 -0
  67. package/tsconfig.json +26 -0
  68. package/tsconfig.tsbuildinfo +1 -0
  69. package/vitest.config.ts +28 -0
package/README.md ADDED
@@ -0,0 +1,484 @@
1
+ # Gossip SDK
2
+
3
+ A platform-agnostic SDK for the Gossip messenger app. This SDK enables automation, chatbot integrations, and programmatic access to Gossip functionality.
4
+
5
+ ## Overview
6
+
7
+ The Gossip SDK provides a clean, typed interface for:
8
+
9
+ - **Account Management** - Create, load, restore, and manage user accounts
10
+ - **Contact Management** - Add, update, and delete contacts
11
+ - **Discussion Management** - Initialize and manage encrypted discussions
12
+ - **Message Operations** - Send and receive encrypted messages
13
+ - **Announcement Handling** - Process protocol announcements
14
+ - **Session Management** - Automatic session renewal and persistence
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ cd gossip-sdk
20
+ npm install
21
+ ```
22
+
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
+ ## Quick Start
30
+
31
+ The SDK uses a singleton pattern with a simple lifecycle:
32
+
33
+ ```typescript
34
+ import { gossipSdk, GossipDatabase } from 'gossip-sdk';
35
+
36
+ // 1. Initialize once at app startup
37
+ const db = new GossipDatabase();
38
+ await db.open();
39
+
40
+ await gossipSdk.init({
41
+ db,
42
+ protocolBaseUrl: 'https://api.example.com',
43
+ });
44
+
45
+ // 2. Open session (login)
46
+ await gossipSdk.openSession({
47
+ 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
+ });
57
+
58
+ // 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);
62
+
63
+ // 4. Listen to events
64
+ gossipSdk.on('message', msg => {
65
+ console.log('New message:', msg.content);
66
+ });
67
+
68
+ // 5. Logout
69
+ await gossipSdk.closeSession();
70
+ ```
71
+
72
+ ## Lifecycle
73
+
74
+ ### 1. Initialize
75
+
76
+ Call `init()` once at app startup to configure the database and protocol:
77
+
78
+ ```typescript
79
+ await gossipSdk.init({
80
+ // Required: Database instance
81
+ db: new GossipDatabase(),
82
+
83
+ // Optional: API base URL (uses default if not provided)
84
+ protocolBaseUrl: 'https://api.usegossip.com',
85
+
86
+ // Optional: Configuration overrides
87
+ config: {
88
+ polling: {
89
+ enabled: true,
90
+ messagesIntervalMs: 3000,
91
+ },
92
+ },
93
+ });
94
+ ```
95
+
96
+ ### 2. Open Session (Login)
97
+
98
+ Call `openSession()` to authenticate and create a cryptographic session:
99
+
100
+ ```typescript
101
+ // New account (no existing session)
102
+ await gossipSdk.openSession({
103
+ mnemonic: 'word1 word2 word3 ... word12',
104
+ });
105
+
106
+ // Restore existing session
107
+ await gossipSdk.openSession({
108
+ mnemonic: 'word1 word2 word3 ... word12',
109
+ encryptedSession: savedBlob,
110
+ encryptionKey: savedKey,
111
+ });
112
+
113
+ // With persistence (saves session on changes)
114
+ await gossipSdk.openSession({
115
+ mnemonic: 'word1 word2 word3 ... word12',
116
+ persistEncryptionKey: encryptionKey,
117
+ onPersist: async (blob, key) => {
118
+ await db.userProfile.update(userId, { session: blob });
119
+ },
120
+ });
121
+ ```
122
+
123
+ ### 3. Close Session (Logout)
124
+
125
+ ```typescript
126
+ await gossipSdk.closeSession();
127
+ ```
128
+
129
+ ## Service APIs
130
+
131
+ All services are available after `openSession()` is called.
132
+
133
+ ### Messages
134
+
135
+ ```typescript
136
+ // Send a message
137
+ const result = await gossipSdk.messages.send({
138
+ ownerUserId: gossipSdk.userId,
139
+ contactUserId: contactId,
140
+ content: 'Hello!',
141
+ type: MessageType.TEXT,
142
+ direction: MessageDirection.OUTGOING,
143
+ status: MessageStatus.SENDING,
144
+ timestamp: new Date(),
145
+ });
146
+
147
+ // Fetch new messages from server
148
+ const fetchResult = await gossipSdk.messages.fetch();
149
+
150
+ // Resend failed messages
151
+ await gossipSdk.messages.resend(failedMessagesMap);
152
+
153
+ // Find message by seeker
154
+ const msg = await gossipSdk.messages.findBySeeker(seeker, ownerUserId);
155
+ ```
156
+
157
+ ### Discussions
158
+
159
+ ```typescript
160
+ // Start a new discussion
161
+ const { discussionId } = await gossipSdk.discussions.start(contact, 'Hello!');
162
+
163
+ // Accept an incoming discussion request
164
+ await gossipSdk.discussions.accept(discussion);
165
+
166
+ // Renew a broken session
167
+ await gossipSdk.discussions.renew(contactUserId);
168
+
169
+ // Check if discussion can send messages
170
+ const canSend = await gossipSdk.discussions.isStable(
171
+ ownerUserId,
172
+ contactUserId
173
+ );
174
+
175
+ // List all discussions
176
+ const discussions = await gossipSdk.discussions.list(ownerUserId);
177
+
178
+ // Get a specific discussion
179
+ const discussion = await gossipSdk.discussions.get(ownerUserId, contactUserId);
180
+ ```
181
+
182
+ ### Contacts
183
+
184
+ ```typescript
185
+ // List all contacts
186
+ const contacts = await gossipSdk.contacts.list(ownerUserId);
187
+
188
+ // Get a specific contact
189
+ const contact = await gossipSdk.contacts.get(ownerUserId, contactUserId);
190
+
191
+ // Add a new contact
192
+ const result = await gossipSdk.contacts.add(
193
+ ownerUserId,
194
+ contactUserId,
195
+ 'Alice',
196
+ publicKeys
197
+ );
198
+
199
+ // Update contact name
200
+ await gossipSdk.contacts.updateName(ownerUserId, contactUserId, 'Alice Smith');
201
+
202
+ // Delete contact and all associated data
203
+ await gossipSdk.contacts.delete(ownerUserId, contactUserId);
204
+ ```
205
+
206
+ ### Announcements
207
+
208
+ ```typescript
209
+ // Fetch and process announcements from server
210
+ const result = await gossipSdk.announcements.fetch();
211
+
212
+ // Resend failed announcements
213
+ await gossipSdk.announcements.resend(failedDiscussions);
214
+ ```
215
+
216
+ ### Auth (Available before session)
217
+
218
+ ```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);
239
+ ```
240
+
241
+ ## Events
242
+
243
+ Subscribe to SDK events for real-time updates:
244
+
245
+ ```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
+ });
254
+
255
+ gossipSdk.on('messageFailed', (message, error) => {
256
+ // Message failed to send
257
+ });
258
+
259
+ // 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
+ });
276
+
277
+ // Error handling
278
+ gossipSdk.on('error', (error, context) => {
279
+ console.error(`Error in ${context}:`, error);
280
+ });
281
+
282
+ // Unsubscribe
283
+ gossipSdk.off('message', handler);
284
+ ```
285
+
286
+ ## Polling
287
+
288
+ The SDK can automatically poll for messages, announcements, and session refresh:
289
+
290
+ ```typescript
291
+ // Enable via config
292
+ await gossipSdk.init({
293
+ db,
294
+ config: {
295
+ polling: {
296
+ enabled: true,
297
+ messagesIntervalMs: 5000,
298
+ announcementsIntervalMs: 10000,
299
+ sessionRefreshIntervalMs: 30000,
300
+ },
301
+ },
302
+ });
303
+
304
+ // Or control manually
305
+ gossipSdk.polling.start();
306
+ gossipSdk.polling.stop();
307
+ console.log(gossipSdk.polling.isRunning);
308
+ ```
309
+
310
+ ## Session Info
311
+
312
+ Access session information after `openSession()`:
313
+
314
+ ```typescript
315
+ // User ID (encoded string)
316
+ const userId = gossipSdk.userId;
317
+
318
+ // User ID (raw bytes)
319
+ const userIdBytes = gossipSdk.userIdBytes;
320
+
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()
327
+
328
+ // Get encrypted session for manual persistence
329
+ const blob = gossipSdk.getEncryptedSession(encryptionKey);
330
+ ```
331
+
332
+ ## Configuration
333
+
334
+ Full configuration options with defaults:
335
+
336
+ ```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
+ });
369
+ ```
370
+
371
+ ## Utilities
372
+
373
+ ```typescript
374
+ const utils = gossipSdk.utils;
375
+
376
+ // Validate user ID format
377
+ const result = utils.validateUserId(userId);
378
+ if (!result.valid) console.error(result.error);
379
+
380
+ // Validate username format
381
+ const result = utils.validateUsername(username);
382
+
383
+ // Encode/decode user IDs
384
+ const encoded = utils.encodeUserId(rawBytes);
385
+ const decoded = utils.decodeUserId(encodedString);
386
+ ```
387
+
388
+ ## Session Persistence
389
+
390
+ For restoring sessions across app restarts:
391
+
392
+ ```typescript
393
+ // Option 1: Provide persistence config in openSession
394
+ await gossipSdk.openSession({
395
+ mnemonic,
396
+ persistEncryptionKey: key,
397
+ onPersist: async (blob, key) => {
398
+ // Save blob to your storage
399
+ await db.userProfile.update(userId, { session: blob });
400
+ },
401
+ });
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
+ ```
410
+
411
+ ## Auto-Renewal Behavior
412
+
413
+ The SDK automatically handles session recovery:
414
+
415
+ 1. **Session Lost** - When a session is killed/lost, messages are queued as `WAITING_SESSION`
416
+ 2. **Auto-Renewal** - SDK emits `onSessionRenewalNeeded` and attempts renewal
417
+ 3. **Auto-Accept** - When peer sends announcement, SDK auto-accepts for existing contacts
418
+ 4. **Message Processing** - After session becomes active, queued messages are sent automatically
419
+
420
+ See [STATUS-REFERENCE.md](./docs/STATUS-REFERENCE.md) for detailed status documentation.
421
+
422
+ ## Types
423
+
424
+ Import types from the SDK:
425
+
426
+ ```typescript
427
+ import type {
428
+ UserProfile,
429
+ Contact,
430
+ Discussion,
431
+ Message,
432
+ DiscussionStatus,
433
+ DiscussionDirection,
434
+ MessageStatus,
435
+ MessageDirection,
436
+ MessageType,
437
+ } from 'gossip-sdk';
438
+ ```
439
+
440
+ ## Testing
441
+
442
+ ```bash
443
+ npm test # Watch mode
444
+ npm run test:run # Single run
445
+ npm run test:coverage # With coverage report
446
+ ```
447
+
448
+ Tests use `fake-indexeddb` to simulate IndexedDB in Node.js environment.
449
+
450
+ ## Architecture
451
+
452
+ ```
453
+ gossip-sdk/
454
+ ├── src/
455
+ │ ├── gossipSdk.ts # Main singleton SDK class
456
+ │ ├── db.ts # Database (Dexie) implementation
457
+ │ ├── contacts.ts # Contact operations
458
+ │ ├── api/
459
+ │ │ └── messageProtocol/ # REST protocol implementation
460
+ │ ├── config/
461
+ │ │ ├── protocol.ts # API configuration
462
+ │ │ └── sdk.ts # SDK configuration
463
+ │ ├── core/
464
+ │ │ ├── SdkEventEmitter.ts # Event system
465
+ │ │ └── SdkPolling.ts # Polling manager
466
+ │ ├── services/
467
+ │ │ ├── auth.ts # Auth service
468
+ │ │ ├── message.ts # Message service
469
+ │ │ ├── discussion.ts # Discussion service
470
+ │ │ ├── announcement.ts # Announcement service
471
+ │ │ └── refresh.ts # Session refresh service
472
+ │ ├── types/
473
+ │ │ └── events.ts # Event type definitions
474
+ │ ├── utils/ # Utility modules
475
+ │ └── wasm/ # WASM module wrappers
476
+ ├── test/ # Test files
477
+ ├── docs/
478
+ │ └── STATUS-REFERENCE.md # Status documentation
479
+ └── README.md
480
+ ```
481
+
482
+ ## License
483
+
484
+ MIT
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@massalabs/gossip-sdk",
3
+ "version": "0.0.1",
4
+ "description": "Gossip SDK for automation, chatbot, and integration use cases",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./sw": {
14
+ "types": "./dist/sw.d.ts",
15
+ "import": "./dist/sw.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "tsc && mkdir -p dist/assets/generated/wasm && cp -R src/assets/generated/wasm/* dist/assets/generated/wasm/",
20
+ "test": "vitest",
21
+ "test:run": "vitest run",
22
+ "test:coverage": "vitest run --coverage"
23
+ },
24
+ "keywords": [
25
+ "gossip",
26
+ "messenger",
27
+ "sdk",
28
+ "massa",
29
+ "blockchain"
30
+ ],
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "fake-indexeddb": "^6.2.5",
34
+ "typescript": "^5.9.3",
35
+ "vitest": "^4.0.17"
36
+ },
37
+ "peerDependencies": {
38
+ "dexie": "^4.0.0",
39
+ "zustand": "^5.0.0"
40
+ }
41
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Message Protocol Module
3
+ *
4
+ * Factory functions and exports for message protocol implementations.
5
+ */
6
+
7
+ export type {
8
+ EncryptedMessage,
9
+ IMessageProtocol,
10
+ MessageProtocolResponse,
11
+ BulletinItem,
12
+ } from './types';
13
+ export { RestMessageProtocol } from './rest';
14
+ export { MessageProtocol } from './mock';
15
+
16
+ import type { IMessageProtocol } from './types';
17
+ import {
18
+ defaultMessageProtocol,
19
+ protocolConfig,
20
+ type MessageProtocolType,
21
+ } from '../../config/protocol';
22
+
23
+ import { RestMessageProtocol } from './rest';
24
+ import { MessageProtocol } from './mock';
25
+
26
+ /**
27
+ * Factory function to create message protocol instances
28
+ */
29
+ export function createMessageProtocol(
30
+ type: MessageProtocolType = defaultMessageProtocol,
31
+ config?: Partial<{ baseUrl: string; timeout: number; retryAttempts: number }>
32
+ ): IMessageProtocol {
33
+ switch (type) {
34
+ case 'rest': {
35
+ return new RestMessageProtocol(
36
+ config?.baseUrl || protocolConfig.baseUrl,
37
+ config?.timeout || 10000,
38
+ config?.retryAttempts || 3
39
+ );
40
+ }
41
+ case 'mock': {
42
+ return new MessageProtocol(
43
+ config?.baseUrl || protocolConfig.baseUrl,
44
+ config?.timeout || 10000,
45
+ config?.retryAttempts || 3
46
+ );
47
+ }
48
+ default:
49
+ throw new Error(`Unsupported message protocol type: ${type}`);
50
+ }
51
+ }
52
+
53
+ export const restMessageProtocol = createMessageProtocol();
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Message Protocol Implementation
3
+ *
4
+ * Provides a concrete protocol class backed by the REST implementation.
5
+ * This uses the real Gossip API rather than a mock transport.
6
+ */
7
+
8
+ import { RestMessageProtocol } from './rest';
9
+
10
+ /**
11
+ * Create a MessageProtocol instance backed by REST.
12
+ */
13
+ export class MessageProtocol extends RestMessageProtocol {}