@massalabs/gossip-sdk 0.0.2-dev.20260130145448 → 0.0.2-dev.20260211110128

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.
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import { type Contact, type GossipDatabase } from './db';
21
21
  import type { UpdateContactNameResult, DeleteContactResult } from './utils/contacts';
22
- import type { UserPublicKeys } from '#wasm';
22
+ import type { UserPublicKeys } from './wasm/bindings';
23
23
  import type { SessionModule } from './wasm/session';
24
24
  export type { UpdateContactNameResult, DeleteContactResult };
25
25
  /**
@@ -46,8 +46,9 @@ import { type MessageResult, type SendMessageResult } from './services/message';
46
46
  import { AuthService } from './services/auth';
47
47
  import type { DeleteContactResult, UpdateContactNameResult } from './utils/contacts';
48
48
  import { type ValidationResult } from './utils/validation';
49
- import type { UserPublicKeys } from '#wasm';
49
+ import type { UserPublicKeys } from './wasm/bindings';
50
50
  import { type SdkEventType, type SdkEventHandlers } from './core/SdkEventEmitter';
51
+ import { AnnouncementPayload } from './utils/announcementPayload';
51
52
  export type { SdkEventType, SdkEventHandlers };
52
53
  export interface GossipSdkInitOptions {
53
54
  /** Database instance */
@@ -210,7 +211,7 @@ interface MessageServiceAPI {
210
211
  }
211
212
  interface DiscussionServiceAPI {
212
213
  /** Start a new discussion with a contact */
213
- start(contact: Contact, message?: string): Promise<{
214
+ start(contact: Contact, payload: AnnouncementPayload): Promise<{
214
215
  discussionId: number;
215
216
  announcement: Uint8Array;
216
217
  }>;
package/dist/gossipSdk.js CHANGED
@@ -322,7 +322,7 @@ class GossipSdkImpl {
322
322
  findBySeeker: (seeker, ownerUserId) => this._message.findMessageBySeeker(seeker, ownerUserId),
323
323
  };
324
324
  this._discussionsAPI = {
325
- start: (contact, message) => this._discussion.initialize(contact, message),
325
+ start: (contact, payload) => this._discussion.initialize(contact, payload),
326
326
  accept: discussion => this._discussion.accept(discussion),
327
327
  renew: contactUserId => this._discussion.renew(contactUserId),
328
328
  isStable: (ownerUserId, contactUserId) => this._discussion.isStableState(ownerUserId, contactUserId),
package/dist/index.d.ts CHANGED
@@ -4,9 +4,9 @@
4
4
  * Main entry point for the Gossip SDK.
5
5
  * Works in both browser and Node.js environments.
6
6
  *
7
- * WASM is loaded via the #wasm subpath import which resolves conditionally:
8
- * - Browser: web target (uses import.meta.url)
9
- * - Node: nodejs target (uses fs, no import.meta.url)
7
+ * WASM is loaded via the web target build with runtime detection:
8
+ * - Browser: init() uses import.meta.url + fetch
9
+ * - Node.js / Jiti: init(bytes) reads the .wasm file from disk
10
10
  *
11
11
  * @example
12
12
  * ```typescript
@@ -35,6 +35,8 @@ export { getContacts, getContact, addContact, updateContactName, deleteContact,
35
35
  export type { UpdateContactNameResult, DeleteContactResult, } from './utils/contacts';
36
36
  export { updateDiscussionName } from './utils/discussions';
37
37
  export type { UpdateDiscussionNameResult } from './utils/discussions';
38
+ export { encodeAnnouncementPayload, decodeAnnouncementPayload, } from './utils/announcementPayload';
39
+ export type { AnnouncementPayload } from './utils/announcementPayload';
38
40
  export * from './types';
39
41
  export { createMessageProtocol, restMessageProtocol, RestMessageProtocol, MessageProtocol, } from './api/messageProtocol';
40
42
  export type { IMessageProtocol, EncryptedMessage, MessageProtocolResponse, BulletinItem, } from './api/messageProtocol';
@@ -56,4 +58,4 @@ export { MESSAGE_TYPE_KEEP_ALIVE, serializeKeepAliveMessage, serializeRegularMes
56
58
  export type { DeserializedMessage } from './utils/messageSerialization';
57
59
  export { generateMnemonic, validateMnemonic, mnemonicToSeed, accountFromMnemonic, PRIVATE_KEY_VERSION, } from './crypto/bip39';
58
60
  export { encrypt, decrypt, deriveKey } from './crypto/encryption';
59
- export { UserPublicKeys, UserSecretKeys, SessionStatus, SessionConfig, SessionManagerWrapper, SendMessageOutput, ReceiveMessageOutput, AnnouncementResult, generate_user_keys, } from '#wasm';
61
+ export { UserPublicKeys, UserSecretKeys, SessionStatus, SessionConfig, SessionManagerWrapper, SendMessageOutput, ReceiveMessageOutput, AnnouncementResult, generate_user_keys, } from './wasm/bindings';
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@
4
4
  * Main entry point for the Gossip SDK.
5
5
  * Works in both browser and Node.js environments.
6
6
  *
7
- * WASM is loaded via the #wasm subpath import which resolves conditionally:
8
- * - Browser: web target (uses import.meta.url)
9
- * - Node: nodejs target (uses fs, no import.meta.url)
7
+ * WASM is loaded via the web target build with runtime detection:
8
+ * - Browser: init() uses import.meta.url + fetch
9
+ * - Node.js / Jiti: init(bytes) reads the .wasm file from disk
10
10
  *
11
11
  * @example
12
12
  * ```typescript
@@ -34,6 +34,8 @@ export { RefreshService } from './services/refresh';
34
34
  export { getContacts, getContact, addContact, updateContactName, deleteContact, } from './contacts';
35
35
  // Discussion utilities
36
36
  export { updateDiscussionName } from './utils/discussions';
37
+ // Announcement payload helpers
38
+ export { encodeAnnouncementPayload, decodeAnnouncementPayload, } from './utils/announcementPayload';
37
39
  // Types
38
40
  export * from './types';
39
41
  // Message Protocol
@@ -58,4 +60,4 @@ export { MESSAGE_TYPE_KEEP_ALIVE, serializeKeepAliveMessage, serializeRegularMes
58
60
  export { generateMnemonic, validateMnemonic, mnemonicToSeed, accountFromMnemonic, PRIVATE_KEY_VERSION, } from './crypto/bip39';
59
61
  export { encrypt, decrypt, deriveKey } from './crypto/encryption';
60
62
  // WASM types re-exported for convenience
61
- export { UserPublicKeys, UserSecretKeys, SessionStatus, SessionConfig, SessionManagerWrapper, SendMessageOutput, ReceiveMessageOutput, AnnouncementResult, generate_user_keys, } from '#wasm';
63
+ export { UserPublicKeys, UserSecretKeys, SessionStatus, SessionConfig, SessionManagerWrapper, SendMessageOutput, ReceiveMessageOutput, AnnouncementResult, generate_user_keys, } from './wasm/bindings';
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { type Discussion, type GossipDatabase } from '../db';
7
7
  import { IMessageProtocol } from '../api/messageProtocol';
8
- import { UserPublicKeys } from '#wasm';
8
+ import { UserPublicKeys } from '../wasm/bindings';
9
9
  import { SessionModule } from '../wasm/session';
10
10
  import { GossipSdkEvents } from '../types/events';
11
11
  import { SdkConfig } from '../config/sdk';
@@ -29,7 +29,7 @@ export declare class AnnouncementService {
29
29
  counter?: string;
30
30
  error?: string;
31
31
  }>;
32
- establishSession(contactPublicKeys: UserPublicKeys, userData?: Uint8Array): Promise<{
32
+ establishSession(contactPublicKeys: UserPublicKeys, payloadBytes?: Uint8Array): Promise<{
33
33
  success: boolean;
34
34
  error?: string;
35
35
  announcement: Uint8Array;
@@ -5,10 +5,11 @@
5
5
  */
6
6
  import { DiscussionStatus, DiscussionDirection, } from '../db';
7
7
  import { decodeUserId, encodeUserId } from '../utils/userId';
8
- import { SessionStatus } from '#wasm';
8
+ import { SessionStatus } from '../wasm/bindings';
9
9
  import { sessionStatusToString } from '../wasm/session';
10
10
  import { Logger } from '../utils/logs';
11
11
  import { defaultSdkConfig } from '../config/sdk';
12
+ import { decodeAnnouncementPayload } from '../utils/announcementPayload';
12
13
  const logger = new Logger('AnnouncementService');
13
14
  export const EstablishSessionError = 'Session manager failed to establish outgoing session';
14
15
  export class AnnouncementService {
@@ -73,11 +74,11 @@ export class AnnouncementService {
73
74
  };
74
75
  }
75
76
  }
76
- async establishSession(contactPublicKeys, userData) {
77
+ async establishSession(contactPublicKeys, payloadBytes) {
77
78
  const log = logger.forMethod('establishSession');
78
79
  const contactUserId = encodeUserId(contactPublicKeys.derive_id());
79
80
  // CRITICAL: await to ensure session state is persisted before sending
80
- const announcement = await this.session.establishOutgoingSession(contactPublicKeys, userData);
81
+ const announcement = await this.session.establishOutgoingSession(contactPublicKeys, payloadBytes);
81
82
  if (announcement.length === 0) {
82
83
  log.error('empty announcement returned', { contactUserId });
83
84
  return {
@@ -367,51 +368,7 @@ export class AnnouncementService {
367
368
  return { success: true };
368
369
  }
369
370
  log.info('announcement intended for us — decrypting');
370
- let rawMessage;
371
- if (result.user_data?.length > 0) {
372
- try {
373
- rawMessage = new TextDecoder().decode(result.user_data);
374
- }
375
- catch (error) {
376
- log.error('failed to decode user data', error);
377
- }
378
- }
379
- // Parse announcement message format:
380
- // - JSON format: {"u":"username","m":"message"} (current)
381
- // - Legacy colon format: "username:message" (backwards compat)
382
- // - Plain text: "message" (oldest format)
383
- // The username is used as the initial contact name if present.
384
- // TODO: Remove legacy colon and plain text format support once all clients are updated
385
- let extractedUsername;
386
- let announcementMessage;
387
- if (rawMessage) {
388
- // Try JSON format first (starts with '{')
389
- if (rawMessage.startsWith('{')) {
390
- try {
391
- const parsed = JSON.parse(rawMessage);
392
- extractedUsername = parsed.u?.trim() || undefined;
393
- announcementMessage = parsed.m?.trim() || undefined;
394
- }
395
- catch {
396
- // Invalid JSON, treat as plain text
397
- announcementMessage = rawMessage;
398
- }
399
- }
400
- else {
401
- // Legacy format: check for colon separator
402
- const colonIndex = rawMessage.indexOf(':');
403
- if (colonIndex !== -1) {
404
- extractedUsername =
405
- rawMessage.slice(0, colonIndex).trim() || undefined;
406
- announcementMessage =
407
- rawMessage.slice(colonIndex + 1).trim() || undefined;
408
- }
409
- else {
410
- // Plain text (oldest format)
411
- announcementMessage = rawMessage;
412
- }
413
- }
414
- }
371
+ const { username, message } = decodeAnnouncementPayload(result.user_data);
415
372
  const announcerPkeys = result.announcer_public_keys;
416
373
  const contactUserIdRaw = announcerPkeys.derive_id();
417
374
  const contactUserId = encodeUserId(contactUserIdRaw);
@@ -426,7 +383,7 @@ export class AnnouncementService {
426
383
  const isNewContact = !contact;
427
384
  if (isNewContact) {
428
385
  // Use extracted username if present, otherwise generate temporary name
429
- const name = extractedUsername ||
386
+ const name = username ||
430
387
  (await this._generateTemporaryContactName(this.session.userIdEncoded));
431
388
  await this.db.contacts.add({
432
389
  ownerUserId: this.session.userIdEncoded,
@@ -445,7 +402,7 @@ export class AnnouncementService {
445
402
  log.error('contact lookup failed after creation');
446
403
  throw new Error('Could not find or create contact');
447
404
  }
448
- const { discussionId } = await this._handleReceivedDiscussion(this.session.userIdEncoded, contactUserId, announcementMessage);
405
+ const { discussionId } = await this._handleReceivedDiscussion(this.session.userIdEncoded, contactUserId, message);
449
406
  // Emit event for new discussion request
450
407
  if (this.events.onDiscussionRequest) {
451
408
  const discussion = await this.db.discussions.get(discussionId);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Handles storing and retrieving public keys by userId hash via the auth API.
5
5
  */
6
- import { UserPublicKeys } from '#wasm';
6
+ import { UserPublicKeys } from '../wasm/bindings';
7
7
  import { IMessageProtocol } from '../api/messageProtocol/types';
8
8
  import { type GossipDatabase } from '../db';
9
9
  export type PublicKeyResult = {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Handles storing and retrieving public keys by userId hash via the auth API.
5
5
  */
6
- import { UserPublicKeys } from '#wasm';
6
+ import { UserPublicKeys } from '../wasm/bindings';
7
7
  import { decodeUserId } from '../utils/userId';
8
8
  import { encodeToBase64, decodeFromBase64 } from '../utils/base64';
9
9
  export class AuthService {
@@ -6,6 +6,7 @@
6
6
  import { type Discussion, type Contact, type GossipDatabase } from '../db';
7
7
  import { AnnouncementService } from './announcement';
8
8
  import { SessionModule } from '../wasm/session';
9
+ import { AnnouncementPayload } from '../utils/announcementPayload';
9
10
  import { GossipSdkEvents } from '../types/events';
10
11
  /**
11
12
  * Service for managing discussions between users.
@@ -33,10 +34,21 @@ export declare class DiscussionService {
33
34
  /**
34
35
  * Initialize a discussion with a contact using SessionManager
35
36
  * @param contact - The contact to start a discussion with
36
- * @param message - Optional message to include in the announcement
37
+ * @param payload - Optional payload to include in the announcement (username and message)
37
38
  * @returns The discussion ID and the created announcement
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const payload: AnnouncementPayload = {
43
+ * username: 'alice',
44
+ * message: 'Hello!',
45
+ * };
46
+ *
47
+ * const { discussionId, announcement } =
48
+ * await discussionService.initialize(contact, payload);
49
+ * ```
38
50
  */
39
- initialize(contact: Contact, message?: string): Promise<{
51
+ initialize(contact: Contact, payload?: AnnouncementPayload): Promise<{
40
52
  discussionId: number;
41
53
  announcement: Uint8Array;
42
54
  }>;
@@ -4,10 +4,11 @@
4
4
  * Class-based service for initializing, accepting, and managing discussions.
5
5
  */
6
6
  import { DiscussionStatus, MessageDirection, MessageStatus, DiscussionDirection, } from '../db';
7
- import { UserPublicKeys, SessionStatus } from '#wasm';
7
+ import { UserPublicKeys, SessionStatus } from '../wasm/bindings';
8
8
  import { EstablishSessionError } from './announcement';
9
9
  import { sessionStatusToString } from '../wasm/session';
10
10
  import { decodeUserId } from '../utils/userId';
11
+ import { encodeAnnouncementPayload, } from '../utils/announcementPayload';
11
12
  import { Logger } from '../utils/logs';
12
13
  const logger = new Logger('DiscussionService');
13
14
  /**
@@ -61,19 +62,29 @@ export class DiscussionService {
61
62
  /**
62
63
  * Initialize a discussion with a contact using SessionManager
63
64
  * @param contact - The contact to start a discussion with
64
- * @param message - Optional message to include in the announcement
65
+ * @param payload - Optional payload to include in the announcement (username and message)
65
66
  * @returns The discussion ID and the created announcement
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const payload: AnnouncementPayload = {
71
+ * username: 'alice',
72
+ * message: 'Hello!',
73
+ * };
74
+ *
75
+ * const { discussionId, announcement } =
76
+ * await discussionService.initialize(contact, payload);
77
+ * ```
66
78
  */
67
- async initialize(contact, message) {
79
+ async initialize(contact, payload) {
68
80
  const log = logger.forMethod('initialize');
69
81
  try {
70
82
  const userId = this.session.userIdEncoded;
71
- // Encode message as UTF-8 if provided
72
- const userData = message
73
- ? new TextEncoder().encode(message)
74
- : new Uint8Array(0);
75
- log.info(`${userId} is establishing session with contact ${contact.name}`);
76
- const result = await this.announcementService.establishSession(UserPublicKeys.from_bytes(contact.publicKeys), userData);
83
+ let payloadBytes;
84
+ if (payload) {
85
+ payloadBytes = encodeAnnouncementPayload(payload.username, payload.message);
86
+ }
87
+ const result = await this.announcementService.establishSession(UserPublicKeys.from_bytes(contact.publicKeys), payloadBytes);
77
88
  let status = DiscussionStatus.PENDING;
78
89
  if (!result.success) {
79
90
  log.error(`Failed to establish session with contact ${contact.name}, got error: ${result.error}`);
@@ -85,26 +96,6 @@ export class DiscussionService {
85
96
  else {
86
97
  log.info(`session established with contact and announcement sent: ${result.announcement.length}... bytes`);
87
98
  }
88
- // Parse announcement message to extract only the actual message content.
89
- // The message parameter may be JSON format: {"u":"username","m":"message"}
90
- // We only want to store the "m" (message) field, not the full JSON.
91
- let parsedAnnouncementMessage;
92
- if (message) {
93
- if (message.startsWith('{')) {
94
- try {
95
- const parsed = JSON.parse(message);
96
- parsedAnnouncementMessage = parsed.m?.trim() || undefined;
97
- }
98
- catch {
99
- // Invalid JSON, treat as plain text
100
- parsedAnnouncementMessage = message;
101
- }
102
- }
103
- else {
104
- parsedAnnouncementMessage = message;
105
- }
106
- }
107
- // Persist discussion immediately with the announcement for reliable retry
108
99
  const discussionId = await this.db.discussions.add({
109
100
  ownerUserId: userId,
110
101
  contactUserId: contact.userId,
@@ -112,7 +103,7 @@ export class DiscussionService {
112
103
  status: status,
113
104
  nextSeeker: undefined,
114
105
  initiationAnnouncement: result.announcement,
115
- announcementMessage: parsedAnnouncementMessage,
106
+ announcementMessage: payload?.message,
116
107
  unreadCount: 0,
117
108
  createdAt: new Date(),
118
109
  updatedAt: new Date(),
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { DiscussionStatus, MessageDirection, MessageStatus, MessageType, } from '../db';
8
8
  import { decodeUserId, encodeUserId } from '../utils/userId';
9
- import { SessionStatus } from '#wasm';
9
+ import { SessionStatus } from '../wasm/bindings';
10
10
  import { serializeRegularMessage, serializeReplyMessage, serializeForwardMessage, serializeKeepAliveMessage, deserializeMessage, } from '../utils/messageSerialization';
11
11
  import { encodeToBase64 } from '../utils/base64';
12
12
  import { sessionStatusToString } from '../wasm/session';
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { MessageDirection, MessageStatus, MessageType, } from '../db';
8
8
  import { sessionStatusToString } from '../wasm/session';
9
- import { SessionStatus } from '#wasm';
9
+ import { SessionStatus } from '../wasm/bindings';
10
10
  import { decodeUserId, encodeUserId } from '../utils/userId';
11
11
  import { Logger } from '../utils/logs';
12
12
  const logger = new Logger('RefreshService');
@@ -0,0 +1,6 @@
1
+ export interface AnnouncementPayload {
2
+ username?: string;
3
+ message?: string;
4
+ }
5
+ export declare const encodeAnnouncementPayload: (username?: string, message?: string) => Uint8Array | undefined;
6
+ export declare const decodeAnnouncementPayload: (data?: Uint8Array) => AnnouncementPayload;
@@ -0,0 +1,23 @@
1
+ import { Args } from '@massalabs/massa-web3';
2
+ export const encodeAnnouncementPayload = (username, message) => {
3
+ const u = username?.trim() || '';
4
+ const m = message?.trim() || '';
5
+ if (!u && !m) {
6
+ return undefined;
7
+ }
8
+ return new Args().addString(u).addString(m).serialize();
9
+ };
10
+ export const decodeAnnouncementPayload = (data) => {
11
+ if (!data) {
12
+ return { username: undefined, message: undefined };
13
+ }
14
+ try {
15
+ const args = new Args(data);
16
+ const username = args.nextString() || undefined;
17
+ const message = args.nextString() || undefined;
18
+ return { username, message };
19
+ }
20
+ catch {
21
+ return { username: undefined, message: undefined };
22
+ }
23
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * WASM Bindings Barrel
3
+ *
4
+ * Re-exports all WASM-generated types and functions from the web target.
5
+ * This centralizes the WASM import so the rest of the codebase uses
6
+ * a standard relative import instead of conditional #wasm subpath imports.
7
+ */
8
+ export { default as init } from '../assets/generated/wasm/gossip_wasm';
9
+ export * from '../assets/generated/wasm/gossip_wasm';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * WASM Bindings Barrel
3
+ *
4
+ * Re-exports all WASM-generated types and functions from the web target.
5
+ * This centralizes the WASM import so the rest of the codebase uses
6
+ * a standard relative import instead of conditional #wasm subpath imports.
7
+ */
8
+ export { default as init } from '../assets/generated/wasm/gossip_wasm';
9
+ export * from '../assets/generated/wasm/gossip_wasm';
@@ -5,7 +5,7 @@
5
5
  * and AEAD (Authenticated Encryption with Additional Data) operations,
6
6
  * ensuring proper initialization before calling any WASM functions.
7
7
  */
8
- import { EncryptionKey, Nonce } from '#wasm';
8
+ import { EncryptionKey, Nonce } from './bindings';
9
9
  export { EncryptionKey, Nonce };
10
10
  /**
11
11
  * Generate a new random encryption key (64 bytes)
@@ -6,7 +6,7 @@
6
6
  * ensuring proper initialization before calling any WASM functions.
7
7
  */
8
8
  import { ensureWasmInitialized } from './loader';
9
- import { EncryptionKey, Nonce, aead_encrypt as _aead_encrypt, aead_decrypt as _aead_decrypt, } from '#wasm';
9
+ import { EncryptionKey, Nonce, aead_encrypt as _aead_encrypt, aead_decrypt as _aead_decrypt, } from './bindings';
10
10
  // Re-export classes
11
11
  export { EncryptionKey, Nonce };
12
12
  /**
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * WASM Module Loader and Initialization Service
3
3
  *
4
- * This file handles WASM initialization. The actual wasm module is resolved
5
- * via the #wasm import which conditionally loads the correct target:
6
- * - Browser: web target (has init function, uses import.meta.url + fetch)
7
- * - Node: nodejs target (auto-initializes, no init function needed)
4
+ * This file handles WASM initialization. It uses a single web-target build
5
+ * and detects the runtime to load the WASM binary appropriately:
6
+ * - Browser: init() with no args (uses import.meta.url + fetch internally)
7
+ * - Node.js / Jiti: init(bytes) with WASM bytes read from the filesystem
8
8
  */
9
9
  /**
10
10
  * Initialize WASM modules if not already initialized
@@ -1,15 +1,12 @@
1
1
  /**
2
2
  * WASM Module Loader and Initialization Service
3
3
  *
4
- * This file handles WASM initialization. The actual wasm module is resolved
5
- * via the #wasm import which conditionally loads the correct target:
6
- * - Browser: web target (has init function, uses import.meta.url + fetch)
7
- * - Node: nodejs target (auto-initializes, no init function needed)
4
+ * This file handles WASM initialization. It uses a single web-target build
5
+ * and detects the runtime to load the WASM binary appropriately:
6
+ * - Browser: init() with no args (uses import.meta.url + fetch internally)
7
+ * - Node.js / Jiti: init(bytes) with WASM bytes read from the filesystem
8
8
  */
9
- import * as wasmModule from '#wasm';
10
- // The web target has a default export (init function), nodejs target doesn't
11
- const init = wasmModule
12
- .default;
9
+ import { init } from './bindings';
13
10
  /**
14
11
  * WASM Initialization State
15
12
  */
@@ -17,6 +14,14 @@ let isInitializing = false;
17
14
  let isInitialized = false;
18
15
  let initializationPromise = null;
19
16
  let initError = null;
17
+ /**
18
+ * Detect if running in a Node.js-like environment (Node, Bun, Jiti, etc.)
19
+ */
20
+ function isNodeRuntime() {
21
+ return (typeof process !== 'undefined' &&
22
+ process.versions != null &&
23
+ process.versions.node != null);
24
+ }
20
25
  /**
21
26
  * Initialize WASM modules if not already initialized
22
27
  * This function is idempotent - safe to call multiple times
@@ -35,13 +40,21 @@ export async function initializeWasm() {
35
40
  initError = null;
36
41
  initializationPromise = (async () => {
37
42
  try {
38
- // The #wasm import resolves to the correct target based on environment:
39
- // - Browser (web target): has init() function that needs to be called
40
- // - Node (nodejs target): auto-initializes on import, no init needed
41
- if (typeof init === 'function') {
43
+ if (isNodeRuntime()) {
44
+ // Node.js / Jiti: read WASM bytes from filesystem and pass to init()
45
+ // Dynamic imports ensure these Node.js modules are tree-shaken in browser builds
46
+ const fs = await import('node:fs');
47
+ const url = await import('node:url');
48
+ const path = await import('node:path');
49
+ const currentDir = path.dirname(url.fileURLToPath(import.meta.url));
50
+ const wasmPath = path.resolve(currentDir, '../assets/generated/wasm/gossip_wasm_bg.wasm');
51
+ const wasmBytes = fs.readFileSync(wasmPath);
52
+ await init(wasmBytes);
53
+ }
54
+ else {
55
+ // Browser: use default loading (import.meta.url + fetch internally)
42
56
  await init();
43
57
  }
44
- // For nodejs target, wasm is already initialized on import
45
58
  isInitialized = true;
46
59
  isInitializing = false;
47
60
  }
@@ -4,7 +4,7 @@
4
4
  * This file contains the real WASM implementation of the SessionModule
5
5
  * using SessionManagerWrapper and related WASM classes.
6
6
  */
7
- import { UserPublicKeys, UserSecretKeys, ReceiveMessageOutput, SendMessageOutput, SessionStatus, EncryptionKey, SessionConfig, AnnouncementResult, UserKeys } from '#wasm';
7
+ import { UserPublicKeys, UserSecretKeys, ReceiveMessageOutput, SendMessageOutput, SessionStatus, EncryptionKey, SessionConfig, AnnouncementResult, UserKeys } from './bindings';
8
8
  import { UserProfile } from '../db';
9
9
  export declare class SessionModule {
10
10
  private sessionManager;
@@ -4,7 +4,7 @@
4
4
  * This file contains the real WASM implementation of the SessionModule
5
5
  * using SessionManagerWrapper and related WASM classes.
6
6
  */
7
- import { SessionManagerWrapper, SessionStatus, SessionConfig, } from '#wasm';
7
+ import { SessionManagerWrapper, SessionStatus, SessionConfig, } from './bindings';
8
8
  import { encodeUserId } from '../utils/userId';
9
9
  export class SessionModule {
10
10
  constructor(userKeys, onPersist, config) {
@@ -4,7 +4,7 @@
4
4
  * This file provides proxy functions for user key generation,
5
5
  * ensuring proper initialization before calling any WASM functions.
6
6
  */
7
- import { UserKeys } from '#wasm';
7
+ import { UserKeys } from './bindings';
8
8
  export { UserKeys };
9
9
  /**
10
10
  * Generate user keys from a passphrase using password-based key derivation
@@ -5,7 +5,7 @@
5
5
  * ensuring proper initialization before calling any WASM functions.
6
6
  */
7
7
  import { ensureWasmInitialized } from './loader';
8
- import { generate_user_keys as _generate_user_keys, UserKeys } from '#wasm';
8
+ import { generate_user_keys as _generate_user_keys, UserKeys, } from './bindings';
9
9
  // Re-export classes
10
10
  export { UserKeys };
11
11
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massalabs/gossip-sdk",
3
- "version": "0.0.2-dev.20260130145448",
3
+ "version": "0.0.2-dev.20260211110128",
4
4
  "description": "Gossip SDK for automation, chatbot, and integration use cases",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,12 +9,6 @@
9
9
  "dist",
10
10
  "README.md"
11
11
  ],
12
- "imports": {
13
- "#wasm": {
14
- "browser": "./dist/assets/generated/wasm/gossip_wasm.js",
15
- "default": "./dist/assets/generated/wasm-node/gossip_wasm.js"
16
- }
17
- },
18
12
  "exports": {
19
13
  ".": {
20
14
  "types": "./dist/index.d.ts",
@@ -28,11 +22,13 @@
28
22
  "scripts": {
29
23
  "prebuild": "rimraf dist",
30
24
  "build": "tsc && npm run copy:wasm",
31
- "copy:wasm": "mkdir -p dist/assets/generated/wasm dist/assets/generated/wasm-node && cp -R src/assets/generated/wasm/* dist/assets/generated/wasm/ && cp -R src/assets/generated/wasm-node/* dist/assets/generated/wasm-node/",
25
+ "copy:wasm": "mkdir -p dist/assets/generated/wasm && cp -R src/assets/generated/wasm/* dist/assets/generated/wasm/",
32
26
  "test": "vitest",
33
27
  "test:run": "vitest run",
34
28
  "test:coverage": "vitest run --coverage",
35
- "test:e2e": "vitest run test/e2e/"
29
+ "test:e2e": "vitest run test/e2e/",
30
+ "lint": "eslint .",
31
+ "lint:fix": "eslint . --fix"
36
32
  },
37
33
  "keywords": [
38
34
  "gossip",