@massalabs/gossip-sdk 0.0.2-dev.20260507171428 → 0.0.2-dev.20260513170310

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.
@@ -1,7 +1,4 @@
1
- /**
2
- * Base REST client with retry and timeout support.
3
- * Shared by RestMessageProtocol and RestAuthProtocol.
4
- */
1
+ import { logger } from '../utils/logs.js';
5
2
  export class RestClient {
6
3
  constructor(baseUrl, timeout = 10000, retryAttempts = 3) {
7
4
  Object.defineProperty(this, "baseUrl", {
@@ -42,7 +39,7 @@ export class RestClient {
42
39
  }
43
40
  catch (error) {
44
41
  lastError = error;
45
- console.warn(`Request attempt ${attempt} failed:`, error);
42
+ logger.warn(`Request attempt ${attempt} failed:`, error);
46
43
  if (attempt < this.retryAttempts) {
47
44
  const delay = Math.pow(2, attempt) * 1000;
48
45
  await new Promise(resolve => setTimeout(resolve, delay));
@@ -1,6 +1,3 @@
1
- /**
2
- * SDK Event Emitter — type-safe event bus backed by mitt.
3
- */
4
1
  import type { Message, Discussion, Contact } from '../db';
5
2
  import type { SessionStatus } from '../wasm/bindings';
6
3
  export declare enum SdkEventType {
@@ -1,3 +1,4 @@
1
+ import { logger } from '../utils/logs.js';
1
2
  /**
2
3
  * SDK Event Emitter — type-safe event bus backed by mitt.
3
4
  */
@@ -44,7 +45,7 @@ export class SdkEventEmitter {
44
45
  handler(payload);
45
46
  }
46
47
  catch (error) {
47
- console.error(`[SdkEventEmitter] Error in ${String(event)} handler:`, error);
48
+ logger.error(`[SdkEventEmitter] Error in ${String(event)} handler:`, error);
48
49
  }
49
50
  }
50
51
  }
@@ -1,8 +1,4 @@
1
- /**
2
- * SDK Polling Manager
3
- *
4
- * Manages polling timers for messages, announcements, and session refresh.
5
- */
1
+ import { logger } from '../utils/logs.js';
6
2
  import { SdkEventType } from './SdkEventEmitter.js';
7
3
  const SESSION_STATUS_POLL_INTERVAL_MS = 3000;
8
4
  // ─────────────────────────────────────────────────────────────────────────────
@@ -42,7 +38,7 @@ export class SdkPolling {
42
38
  this.stop();
43
39
  this.callbacks = callbacks;
44
40
  this.eventEmitter = eventEmitter;
45
- console.log('[SdkPolling] Starting polling', {
41
+ logger.info('[SdkPolling] Starting polling', {
46
42
  messagesIntervalMs: config.polling.messagesIntervalMs,
47
43
  announcementsIntervalMs: config.polling.announcementsIntervalMs,
48
44
  sessionRefreshIntervalMs: config.polling.sessionRefreshIntervalMs,
@@ -1,7 +1,3 @@
1
- /**
2
- * BIP39 utilities for mnemonic generation, validation, and seed derivation
3
- * Using @scure/bip39 for browser compatibility
4
- */
5
1
  import { Account } from '@massalabs/massa-web3';
6
2
  export declare const PRIVATE_KEY_VERSION = 0;
7
3
  /**
@@ -1,3 +1,4 @@
1
+ import { logger } from '../utils/logs.js';
1
2
  /**
2
3
  * BIP39 utilities for mnemonic generation, validation, and seed derivation
3
4
  * Using @scure/bip39 for browser compatibility
@@ -55,8 +56,8 @@ export async function accountFromMnemonic(mnemonic, passphrase) {
55
56
  return account;
56
57
  }
57
58
  catch (error) {
58
- console.error('Error in accountFromMnemonic:', error);
59
- console.error('Error stack:', error instanceof Error ? error.stack : 'No stack');
59
+ logger.error('Error in accountFromMnemonic:', error);
60
+ logger.error('Error stack:', error instanceof Error ? error.stack : 'No stack');
60
61
  throw error;
61
62
  }
62
63
  }
@@ -39,7 +39,7 @@ export declare class SecureStorageWorkerApi {
39
39
  * tick cannot overlap with itself, but the explicit `cover()` RPC
40
40
  * could be called concurrently by the SDK consumer).
41
41
  *
42
- * PD-critical: errors are logged at `console.debug` only. Logging
42
+ * PD-critical: errors are logged at `logger.debug` only. Logging
43
43
  * at error level fingerprinted "secure-storage user with persistent
44
44
  * storage failure" in console history. Errors do not change the
45
45
  * scheduling cadence either: backing off would itself leak ("cover
@@ -1,3 +1,4 @@
1
+ import { logger } from '../utils/logs.js';
1
2
  /**
2
3
  * Web worker that hosts the secure-storage WASM module and runs SQL on
3
4
  * the embedded sqlite-wasm-rs SQLite, routing main DB I/O through our
@@ -61,7 +62,7 @@ export class SecureStorageWorkerApi {
61
62
  * tick cannot overlap with itself, but the explicit `cover()` RPC
62
63
  * could be called concurrently by the SDK consumer).
63
64
  *
64
- * PD-critical: errors are logged at `console.debug` only. Logging
65
+ * PD-critical: errors are logged at `logger.debug` only. Logging
65
66
  * at error level fingerprinted "secure-storage user with persistent
66
67
  * storage failure" in console history. Errors do not change the
67
68
  * scheduling cadence either: backing off would itself leak ("cover
@@ -81,7 +82,7 @@ export class SecureStorageWorkerApi {
81
82
  await flushEncrypted();
82
83
  }
83
84
  catch (err) {
84
- console.debug('[SecureStorage] cover traffic tick failed', err);
85
+ logger.debug('[SecureStorage] cover traffic tick failed', err);
85
86
  }
86
87
  finally {
87
88
  this.coverTickInProgress = false;
@@ -1,16 +1,3 @@
1
- /**
2
- * SQLite initialization module for the Gossip SDK.
3
- *
4
- * Uses wa-sqlite (WASM) with Drizzle ORM's sqlite-proxy driver.
5
- * Four execution paths:
6
- * - Browser/OPFS (opfsPath set): Web Worker + AccessHandlePoolVFS — fast, single-tab.
7
- * - Browser/IDB (idbName set): Web Worker + IDBBatchAtomicVFS — multi-tab safe.
8
- * - Node.js file (path set): In-process + NodeFsVFS — file persistence via node:fs.
9
- * - In-memory (tests): :memory: in-process — no persistence, fast, isolated.
10
- *
11
- * Each GossipSdk instance owns a DatabaseConnection, allowing multiple
12
- * independent SDK instances in the same process.
13
- */
14
1
  import { type SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy';
15
2
  import * as schema from './schema/index.js';
16
3
  export { SQL_NAMESPACE, SESSION_BLOB_NAMESPACE, SESSION_COUNT, } from './secure-storage-namespaces.js';
package/dist/db/sqlite.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { logger } from '../utils/logs.js';
1
2
  /**
2
3
  * SQLite initialization module for the Gossip SDK.
3
4
  *
@@ -354,7 +355,7 @@ export class DatabaseConnection {
354
355
  this.state.nativePlugin = null;
355
356
  this.state.useNativePlugin = false;
356
357
  if (import.meta.env?.DEV) {
357
- console.warn('[secureStorage] native plugin unavailable, falling back to WASM worker');
358
+ logger.warn('[secureStorage] native plugin unavailable, falling back to WASM worker');
358
359
  }
359
360
  }
360
361
  if (SecureStorageNative) {
package/dist/gossip.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { logger } from './utils/logs.js';
1
2
  /**
2
3
  * GossipSdk - SDK with clean lifecycle API
3
4
  *
@@ -205,12 +206,12 @@ class GossipSdk {
205
206
  async init(options) {
206
207
  if (this.state.status !== SdkStatus.UNINITIALIZED) {
207
208
  if (import.meta.env?.DEV) {
208
- console.warn('[GossipSdk] Already initialized');
209
+ logger.warn('[GossipSdk] Already initialized');
209
210
  }
210
211
  return this;
211
212
  }
212
213
  if (import.meta.env?.DEV) {
213
- console.log('[GossipSdk] Initializing SDK');
214
+ logger.info('[GossipSdk] Initializing SDK');
214
215
  }
215
216
  // Merge config with defaults
216
217
  const config = mergeConfig(options.config);
@@ -222,7 +223,7 @@ class GossipSdk {
222
223
  // Start WASM initialization
223
224
  startWasmInitialization();
224
225
  if (import.meta.env?.DEV) {
225
- console.log('[GossipSdk] Initializing SQLite');
226
+ logger.info('[GossipSdk] Initializing SQLite');
226
227
  }
227
228
  this._conn = await DatabaseConnection.create({ storage: options.storage });
228
229
  // Defer queries/profile when the database needs an unlock first.
@@ -230,7 +231,7 @@ class GossipSdk {
230
231
  this._queries = new Queries(this._conn);
231
232
  }
232
233
  if (import.meta.env?.DEV) {
233
- console.log('[GossipSdk] SQLite initialized');
234
+ logger.info('[GossipSdk] SQLite initialized');
234
235
  }
235
236
  // Create message protocol
236
237
  const messageProtocol = createMessageProtocol();
@@ -817,7 +818,7 @@ class GossipSdk {
817
818
  */
818
819
  startPolling() {
819
820
  if (this.state.status !== SdkStatus.SESSION_OPEN) {
820
- console.warn('[GossipSdk] Cannot start polling - no session open');
821
+ logger.warn('[GossipSdk] Cannot start polling - no session open');
821
822
  return;
822
823
  }
823
824
  const { config } = this.state;
@@ -876,9 +877,10 @@ class GossipSdk {
876
877
  if (this.state.status !== SdkStatus.SESSION_OPEN)
877
878
  return;
878
879
  // A/B test escape hatch: skip the entire pipeline if the debug flag
879
- // is set in the dev console. WARNING: if the app crashes/reloads
880
- // while this is on, the session state is lost. Gated on DEV so that
881
- // a production XSS cannot silently disable session persistence.
880
+ // is set from browser developer tools. WARNING: if the app
881
+ // crashes/reloads while this is on, the session state is lost. Gated
882
+ // on DEV so that a production XSS cannot silently disable session
883
+ // persistence.
882
884
  if (import.meta.env?.DEV &&
883
885
  typeof globalThis !== 'undefined' &&
884
886
  globalThis
@@ -1049,11 +1051,11 @@ class GossipSdk {
1049
1051
  });
1050
1052
  }
1051
1053
  if (stuck.length > 0 && import.meta.env?.DEV) {
1052
- console.log(`[GossipSdk] Reset ${stuck.length} stuck SENDING message(s) to WAITING_SESSION for auto-retry`);
1054
+ logger.info(`[GossipSdk] Reset ${stuck.length} stuck SENDING message(s) to WAITING_SESSION for auto-retry`);
1053
1055
  }
1054
1056
  }
1055
1057
  catch (error) {
1056
- console.error('[GossipSdk] Failed to reset stuck messages:', error);
1058
+ logger.error('[GossipSdk] Failed to reset stuck messages:', error);
1057
1059
  }
1058
1060
  }
1059
1061
  }
@@ -1,8 +1,3 @@
1
- /**
2
- * Contact Utilities
3
- *
4
- * Functions for managing contacts including updating names and deleting contacts.
5
- */
6
1
  import type { SessionModule } from '../wasm/session.js';
7
2
  import type { UserPublicKeys } from '../wasm/bindings.js';
8
3
  import type { Contact } from '../db/index.js';
@@ -1,3 +1,4 @@
1
+ import { logger } from './logs.js';
1
2
  /**
2
3
  * Contact Utilities
3
4
  *
@@ -43,7 +44,7 @@ export async function updateContactName(ownerUserId, contactUserId, newName, que
43
44
  return { success: true, trimmedName: trimmed };
44
45
  }
45
46
  catch (e) {
46
- console.error('updateContactName failed', e);
47
+ logger.error('updateContactName failed', e);
47
48
  return {
48
49
  success: false,
49
50
  reason: 'error',
@@ -88,7 +89,7 @@ export async function deleteContact(ownerUserId, contactUserId, session, queries
88
89
  return { success: true };
89
90
  }
90
91
  catch (e) {
91
- console.error('deleteContact failed', e);
92
+ logger.error('deleteContact failed', e);
92
93
  return {
93
94
  success: false,
94
95
  reason: 'error',
@@ -118,7 +119,7 @@ export async function addContact(ownerUserId, userId, name, publicKeys, queries)
118
119
  return { success: true, contact: newContact ?? undefined };
119
120
  }
120
121
  catch (error) {
121
- console.error('Error adding contact:', error);
122
+ logger.error('Error adding contact:', error);
122
123
  return {
123
124
  success: false,
124
125
  error: error instanceof Error ? error.message : 'Unknown error',
@@ -1,3 +1,4 @@
1
+ import { logger } from './logs.js';
1
2
  /**
2
3
  * Discussion Utilities
3
4
  *
@@ -30,7 +31,7 @@ export async function updateDiscussionPin(discussionId, pinned, queries) {
30
31
  return { success: true };
31
32
  }
32
33
  catch (e) {
33
- console.error('updateDiscussionPin failed', e);
34
+ logger.error('updateDiscussionPin failed', e);
34
35
  return {
35
36
  success: false,
36
37
  message: 'Failed to update pin. Please try again.',
@@ -62,7 +63,7 @@ export async function updateDiscussionName(discussionId, newName, queries) {
62
63
  return { success: true, trimmedName: customName ?? undefined };
63
64
  }
64
65
  catch (e) {
65
- console.error('updateDiscussionName failed', e);
66
+ logger.error('updateDiscussionName failed', e);
66
67
  return {
67
68
  success: false,
68
69
  reason: 'error',
@@ -2,6 +2,7 @@ export * from './announcementPayload.js';
2
2
  export * from './base64.js';
3
3
  export * from './contacts.js';
4
4
  export * from './discussions.js';
5
+ export * from './logs.js';
5
6
  export * from './queue.js';
6
7
  export * from './type.js';
7
8
  export * from './userId.js';
@@ -2,6 +2,7 @@ export * from './announcementPayload.js';
2
2
  export * from './base64.js';
3
3
  export * from './contacts.js';
4
4
  export * from './discussions.js';
5
+ export * from './logs.js';
5
6
  export * from './queue.js';
6
7
  export * from './type.js';
7
8
  export * from './userId.js';
@@ -1,9 +1,29 @@
1
1
  /**
2
- * Logger utility for SDK
2
+ * Shared logger utility for app + SDK runtime code.
3
3
  *
4
- * Provides structured console logging with module and context support.
5
- * All output goes to the terminal via console methods.
4
+ * Production safety rule: logs are emitted only through configured sinks.
5
+ * Release builds configure no sinks, so logging calls are inert without
6
+ * relying on minifier console stripping.
6
7
  */
8
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
9
+ export type LogSink = (level: LogLevel, message: unknown, args: readonly unknown[]) => void;
10
+ export interface LoggerConfig {
11
+ enabled: boolean;
12
+ minLevel: LogLevel;
13
+ persist: boolean;
14
+ }
15
+ export declare function configureLogging(next: Partial<LoggerConfig>): void;
16
+ export declare function getLoggingConfig(): LoggerConfig;
17
+ export declare function setLogSinks(nextSinks: LogSink[]): void;
18
+ export declare function addLogSink(sink: LogSink): void;
19
+ export declare function resetLoggingForTests(): void;
20
+ export declare const logger: {
21
+ debug: (...args: unknown[]) => void;
22
+ info: (...args: unknown[]) => void;
23
+ warn: (...args: unknown[]) => void;
24
+ error: (...args: unknown[]) => void;
25
+ child: (scope: string) => Logger;
26
+ };
7
27
  export declare class Logger {
8
28
  private module;
9
29
  private context;
@@ -1,9 +1,57 @@
1
1
  /**
2
- * Logger utility for SDK
2
+ * Shared logger utility for app + SDK runtime code.
3
3
  *
4
- * Provides structured console logging with module and context support.
5
- * All output goes to the terminal via console methods.
4
+ * Production safety rule: logs are emitted only through configured sinks.
5
+ * Release builds configure no sinks, so logging calls are inert without
6
+ * relying on minifier console stripping.
6
7
  */
8
+ const LEVEL_RANK = {
9
+ debug: 10,
10
+ info: 20,
11
+ warn: 30,
12
+ error: 40,
13
+ };
14
+ const DEFAULT_CONFIG = {
15
+ enabled: false,
16
+ minLevel: 'error',
17
+ persist: false,
18
+ };
19
+ let config = { ...DEFAULT_CONFIG };
20
+ let sinks = [];
21
+ function shouldLog(level) {
22
+ return config.enabled && LEVEL_RANK[level] >= LEVEL_RANK[config.minLevel];
23
+ }
24
+ function emit(level, args) {
25
+ if (!shouldLog(level))
26
+ return;
27
+ const message = args[0];
28
+ for (const sink of sinks) {
29
+ sink(level, message, args);
30
+ }
31
+ }
32
+ export function configureLogging(next) {
33
+ config = { ...config, ...next };
34
+ }
35
+ export function getLoggingConfig() {
36
+ return { ...config };
37
+ }
38
+ export function setLogSinks(nextSinks) {
39
+ sinks = [...nextSinks];
40
+ }
41
+ export function addLogSink(sink) {
42
+ sinks = [...sinks, sink];
43
+ }
44
+ export function resetLoggingForTests() {
45
+ config = { ...DEFAULT_CONFIG };
46
+ sinks = [];
47
+ }
48
+ export const logger = {
49
+ debug: (...args) => emit('debug', args),
50
+ info: (...args) => emit('info', args),
51
+ warn: (...args) => emit('warn', args),
52
+ error: (...args) => emit('error', args),
53
+ child: (scope) => new Logger(scope),
54
+ };
7
55
  export class Logger {
8
56
  constructor(module, context = '') {
9
57
  Object.defineProperty(this, "module", {
@@ -31,17 +79,17 @@ export class Logger {
31
79
  info(message, extra) {
32
80
  const main = this.formatMainMessage(message);
33
81
  if (extra !== undefined) {
34
- console.log(main, extra);
82
+ logger.info(main, extra);
35
83
  }
36
84
  else {
37
- console.log(main);
85
+ logger.info(main);
38
86
  }
39
87
  }
40
88
  error(messageOrError, extra) {
41
89
  const source = this.getSource();
42
90
  if (messageOrError instanceof Error) {
43
91
  const main = `[${source}] ${messageOrError.message}`;
44
- console.error(main, messageOrError, extra);
92
+ logger.error(main, messageOrError, extra);
45
93
  }
46
94
  else {
47
95
  const message = typeof messageOrError === 'string'
@@ -49,29 +97,29 @@ export class Logger {
49
97
  : JSON.stringify(messageOrError);
50
98
  const main = `[${source}] ${message}`;
51
99
  if (extra !== undefined) {
52
- console.error(main, extra);
100
+ logger.error(main, extra);
53
101
  }
54
102
  else {
55
- console.error(main);
103
+ logger.error(main);
56
104
  }
57
105
  }
58
106
  }
59
107
  debug(message, extra) {
60
108
  const main = this.formatMainMessage(message);
61
109
  if (extra !== undefined) {
62
- console.debug(main, extra);
110
+ logger.debug(main, extra);
63
111
  }
64
112
  else {
65
- console.debug(main);
113
+ logger.debug(main);
66
114
  }
67
115
  }
68
116
  warn(message, extra) {
69
117
  const main = this.formatMainMessage(message);
70
118
  if (extra !== undefined) {
71
- console.warn(main, extra);
119
+ logger.warn(main, extra);
72
120
  }
73
121
  else {
74
- console.warn(main);
122
+ logger.warn(main);
75
123
  }
76
124
  }
77
125
  // Chainable context builder
@@ -1,11 +1,3 @@
1
- /**
2
- * WASM Module Loader and Initialization Service
3
- *
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
- */
9
1
  /**
10
2
  * Initialize WASM modules if not already initialized
11
3
  * This function is idempotent - safe to call multiple times
@@ -1,3 +1,4 @@
1
+ import { logger } from '../utils/logs.js';
1
2
  /**
2
3
  * WASM Module Loader and Initialization Service
3
4
  *
@@ -66,7 +67,7 @@ export async function initializeWasm() {
66
67
  catch (error) {
67
68
  initError = error;
68
69
  isInitializing = false;
69
- console.error('[WASM] Failed to initialize WASM modules:', error);
70
+ logger.error('[WASM] Failed to initialize WASM modules:', error);
70
71
  throw error;
71
72
  }
72
73
  })();
@@ -91,6 +92,6 @@ export async function ensureWasmInitialized() {
91
92
  export function startWasmInitialization() {
92
93
  // Fire and forget - start initialization in background
93
94
  initializeWasm().catch(error => {
94
- console.error('[WASM] Background initialization error:', error);
95
+ logger.error('[WASM] Background initialization error:', error);
95
96
  });
96
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massalabs/gossip-sdk",
3
- "version": "0.0.2-dev.20260507171428",
3
+ "version": "0.0.2-dev.20260513170310",
4
4
  "description": "Gossip SDK for automation, chatbot, and integration use cases",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,6 +17,10 @@
17
17
  "./sw": {
18
18
  "types": "./dist/sw.d.ts",
19
19
  "import": "./dist/sw.js"
20
+ },
21
+ "./utils/logs.js": {
22
+ "types": "./dist/utils/logs.d.ts",
23
+ "import": "./dist/utils/logs.js"
20
24
  }
21
25
  },
22
26
  "scripts": {