CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-web3-core

Core tools and utilities for the web3.js ecosystem, providing foundational layer functionality for blockchain interactions.

Pending
Overview
Eval results
Files

event-system.mddocs/

Event System

The Web3 event system provides robust event emission and handling capabilities with type-safe event listeners, Promise-Event hybrids, and comprehensive lifecycle management. It serves as the foundation for all asynchronous communication within the Web3 ecosystem.

Capabilities

Web3EventEmitter Class

Core event emitter implementation with type-safe event handling and listener management.

/**
 * Web3 event emitter with type-safe event handling and listener management
 * @template T - Event map type defining available events and their data types
 */
class Web3EventEmitter<T extends Web3EventMap = Web3EventMap> implements Web3Emitter<T> {
  constructor();
  
  // Event listener management
  on<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  once<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  off<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  
  // Event emission
  emit<K extends Web3EventKey<T>>(eventName: K, params: T[K]): void;
  
  // Listener inspection
  listenerCount<K extends Web3EventKey<T>>(eventName: K): number;
  listeners<K extends Web3EventKey<T>>(eventName: K): Function[];
  eventNames(): (string | symbol)[];
  
  // Management
  removeAllListeners(): EventEmitter;
  setMaxListenerWarningThreshold(maxListenersWarningThreshold: number): void;
  getMaxListeners(): number;
}

Usage Examples:

import { Web3EventEmitter } from "web3-core";

// Define event map for type safety
interface MyEvents {
  dataReceived: { data: string; timestamp: number };
  error: { message: string; code: number };
  connected: { address: string };
  disconnected: undefined;
}

// Create typed event emitter
const emitter = new Web3EventEmitter<MyEvents>();

// Add event listeners with type safety
emitter.on("dataReceived", ({ data, timestamp }) => {
  console.log(`Received data: ${data} at ${new Date(timestamp)}`);
});

emitter.on("error", ({ message, code }) => {
  console.error(`Error ${code}: ${message}`);
});

emitter.on("connected", ({ address }) => {
  console.log(`Connected to ${address}`);
});

emitter.on("disconnected", () => {
  console.log("Disconnected");
});

// Emit events
emitter.emit("dataReceived", { data: "Hello World", timestamp: Date.now() });
emitter.emit("error", { message: "Connection failed", code: 500 });
emitter.emit("connected", { address: "127.0.0.1:8080" });
emitter.emit("disconnected", undefined);

// One-time listener
emitter.once("connected", ({ address }) => {
  console.log(`First connection to ${address}`);
});

// Remove specific listener
const errorHandler = ({ message, code }: { message: string; code: number }) => {
  console.error(`Error: ${message}`);
};
emitter.on("error", errorHandler);
emitter.off("error", errorHandler);

// Inspect listeners
console.log("Error listeners:", emitter.listenerCount("error"));
console.log("All event names:", emitter.eventNames());

Web3PromiEvent Class

Promise-Event hybrid that combines Promise functionality with event emission for long-running operations.

/**
 * Promise-like object that also emits events during execution
 * @template ResolveType - Type of the final resolved value
 * @template EventMap - Event map for intermediate events during execution
 */
class Web3PromiEvent<ResolveType, EventMap extends Web3EventMap = Web3EventMap> 
  extends Web3EventEmitter<EventMap> 
  implements Promise<ResolveType> {
  
  constructor(executor: PromiseExecutor<ResolveType>);
  
  // Promise interface
  then<TResult1 = ResolveType, TResult2 = never>(
    onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
  ): Promise<TResult1 | TResult2>;
  
  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
  ): Promise<ResolveType | TResult>;
  
  finally(onfinally?: (() => void) | null): Promise<ResolveType>;
  
  // Enhanced event methods that return this for chaining
  on<K extends Web3EventKey<EventMap>>(eventName: K, fn: Web3EventCallback<EventMap[K]>): this;
  once<K extends Web3EventKey<EventMap>>(eventName: K, fn: Web3EventCallback<EventMap[K]>): this;
  
  // Promise symbol
  readonly [Symbol.toStringTag]: 'Promise';
}

/**
 * Promise executor function type
 * @template T - Type of resolved value
 */
type PromiseExecutor<T> = (
  resolve: (value: T) => void, 
  reject: (reason: unknown) => void
) => void;

Usage Examples:

import { Web3PromiEvent } from "web3-core";

// Define event map for transaction process
interface TransactionEvents {
  sending: { txHash: string };
  sent: { txHash: string };
  transactionHash: { txHash: string };
  confirmation: { confirmationNumber: number; receipt: any };
}

// Create PromiEvent for transaction sending
function sendTransaction(txData: any): Web3PromiEvent<any, TransactionEvents> {
  return new Web3PromiEvent((resolve, reject) => {
    // Simulate transaction process
    setTimeout(() => {
      const txHash = "0x1234567890abcdef...";
      
      // Emit sending event
      promiEvent.emit("sending", { txHash });
      
      setTimeout(() => {
        // Emit sent event
        promiEvent.emit("sent", { txHash });
        promiEvent.emit("transactionHash", { txHash });
        
        // Simulate confirmations 
        let confirmationCount = 0;
        const confirmationInterval = setInterval(() => {
          confirmationCount++;
          promiEvent.emit("confirmation", { 
            confirmationNumber: confirmationCount, 
            receipt: { txHash, status: 1, blockNumber: 18000000 + confirmationCount }
          });
          
          if (confirmationCount >= 3) {
            clearInterval(confirmationInterval);
            resolve({ txHash, confirmations: confirmationCount });
          }
        }, 1000);
      }, 2000);
    }, 1000);
  });
}

// Use PromiEvent - can be used as both Promise and EventEmitter
const txPromiEvent = sendTransaction({ to: "0x742d35Cc6634C0532925a3b8D0d3", value: "1000000000000000000" });

// Listen to events during execution
txPromiEvent
  .on("sending", ({ txHash }) => {
    console.log("Transaction sending:", txHash);
  })
  .on("sent", ({ txHash }) => {
    console.log("Transaction sent:", txHash);
  })
  .on("transactionHash", ({ txHash }) => {
    console.log("Transaction hash received:", txHash);
  })
  .on("confirmation", ({ confirmationNumber, receipt }) => {
    console.log(`Confirmation ${confirmationNumber}:`, receipt.blockNumber);
  });

// Use as Promise
txPromiEvent
  .then((result) => {
    console.log("Transaction completed:", result);
  })
  .catch((error) => {
    console.error("Transaction failed:", error);
  });

// Or with async/await
try {
  const result = await txPromiEvent;
  console.log("Transaction successful:", result.txHash);
} catch (error) {
  console.error("Transaction error:", error);
}

Event Type System

Type definitions for creating type-safe event maps and handlers.

/**
 * Base event map type - maps event names to their data types
 */
type Web3EventMap = Record<string, unknown>;

/**
 * Event key type - ensures key exists in event map
 * @template T - Event map type
 */
type Web3EventKey<T extends Web3EventMap> = string & keyof T;

/**
 * Event callback function type
 * @template T - Event data type
 */
type Web3EventCallback<T> = (params: T) => void | Promise<void>;

/**
 * Event emitter interface definition
 * @template T - Event map type
 */
interface Web3Emitter<T extends Web3EventMap> {
  on<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  once<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  off<K extends Web3EventKey<T>>(eventName: K, fn: Web3EventCallback<T[K]>): void;
  emit<K extends Web3EventKey<T>>(eventName: K, params: T[K]): void;
}

Usage Examples:

// Define complex event map
interface BlockchainEvents {
  blockReceived: { 
    blockNumber: number; 
    blockHash: string; 
    timestamp: number; 
    transactions: string[] 
  };
  transactionPending: { 
    txHash: string; 
    from: string; 
    to: string; 
    value: string 
  };
  error: { 
    code: number; 
    message: string; 
    details?: any 
  };
  connected: { 
    provider: string; 
    networkId: number 
  };
  disconnected: undefined;
}

// Create typed emitter
class BlockchainMonitor extends Web3EventEmitter<BlockchainEvents> {
  constructor() {
    super();
    this.setMaxListenerWarningThreshold(50); // Higher threshold for blockchain events
  }
  
  startMonitoring() {
    // Simulate blockchain monitoring
    setInterval(() => {
      this.emit("blockReceived", {
        blockNumber: Math.floor(Math.random() * 1000000),
        blockHash: `0x${Math.random().toString(16).substr(2, 64)}`,
        timestamp: Date.now(),
        transactions: [`0x${Math.random().toString(16).substr(2, 64)}`]
      });
    }, 12000); // Every 12 seconds
  }
  
  reportError(code: number, message: string, details?: any) {
    this.emit("error", { code, message, details });
  }
}

// Use typed monitor
const monitor = new BlockchainMonitor();

monitor.on("blockReceived", ({ blockNumber, blockHash, timestamp, transactions }) => {
  console.log(`Block ${blockNumber} (${blockHash}) received at ${new Date(timestamp)}`);
  console.log(`Contains ${transactions.length} transactions`);
});

monitor.on("error", ({ code, message, details }) => {
  console.error(`Blockchain error ${code}: ${message}`, details);
});

monitor.startMonitoring();

Advanced Event Patterns

Common patterns and best practices for event-driven Web3 applications.

/**
 * Event listener management utilities
 */
listenerCount<K extends Web3EventKey<T>>(eventName: K): number;
listeners<K extends Web3EventKey<T>>(eventName: K): Function[];
eventNames(): (string | symbol)[];
removeAllListeners(): EventEmitter;
setMaxListenerWarningThreshold(maxListenersWarningThreshold: number): void;
getMaxListeners(): number;

Usage Examples:

// Pattern: Event debouncing
class DebouncedEmitter extends Web3EventEmitter<{ dataChanged: string }> {
  private debounceTimer?: NodeJS.Timeout;
  
  emitDebounced(data: string) {
    if (this.debounceTimer) {
      clearTimeout(this.debounceTimer);
    }
    
    this.debounceTimer = setTimeout(() => {
      this.emit("dataChanged", data);
    }, 300); // 300ms debounce
  }
}

// Pattern: Event aggregation
class EventAggregator extends Web3EventEmitter<{ batch: string[] }> {
  private buffer: string[] = [];
  private batchTimer?: NodeJS.Timeout;
  
  add(item: string) {
    this.buffer.push(item);
    
    if (!this.batchTimer) {
      this.batchTimer = setTimeout(() => {
        this.emit("batch", [...this.buffer]);
        this.buffer = [];
        this.batchTimer = undefined;
      }, 1000); // Batch every second
    }
  }
}

// Pattern: Error handling with retry
class ResilientEmitter extends Web3EventEmitter<{ 
  data: any; 
  error: Error; 
  retry: { attempt: number; maxAttempts: number } 
}> {
  async emitWithRetry(data: any, maxAttempts = 3) {
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        this.emit("data", data);
        return; // Success
      } catch (error) {
        this.emit("retry", { attempt, maxAttempts });
        
        if (attempt === maxAttempts) {
          this.emit("error", error as Error);
          throw error;
        }
        
        // Wait before retry
        await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
      }
    }
  }
}

// Pattern: Event metrics and monitoring
class MonitoredEmitter extends Web3EventEmitter<{ data: any; metrics: any }> {
  private eventCounts = new Map<string, number>();
  private startTime = Date.now();
  
  emit<K extends string>(eventName: K, params: any): void {
    // Track event metrics
    const currentCount = this.eventCounts.get(eventName) || 0;
    this.eventCounts.set(eventName, currentCount + 1);
    
    // Emit metrics periodically
    if (currentCount % 100 === 0) {
      this.emit("metrics", {
        eventName,
        count: currentCount + 1,
        rate: (currentCount + 1) / ((Date.now() - this.startTime) / 1000),
        listeners: this.listenerCount(eventName as any)
      });
    }
    
    super.emit(eventName as any, params);
  }
  
  getMetrics() {
    const uptime = (Date.now() - this.startTime) / 1000;
    const metrics: any = { uptime, events: {} };
    
    for (const [eventName, count] of this.eventCounts) {
      metrics.events[eventName] = {
        count,
        rate: count / uptime,
        listeners: this.listenerCount(eventName as any)
      };
    }
    
    return metrics;
  }
}

// Usage of advanced patterns
const debouncedEmitter = new DebouncedEmitter();
debouncedEmitter.on("dataChanged", (data) => {
  console.log("Debounced data:", data);
});

const aggregator = new EventAggregator();
aggregator.on("batch", (items) => {
  console.log("Batch of items:", items);
});

const resilientEmitter = new ResilientEmitter();
resilientEmitter.on("retry", ({ attempt, maxAttempts }) => {
  console.log(`Retry attempt ${attempt}/${maxAttempts}`);
});

const monitoredEmitter = new MonitoredEmitter();
monitoredEmitter.on("metrics", (metrics) => {
  console.log("Event metrics:", metrics);
});

Memory Management

Best practices for preventing memory leaks in event-driven applications.

// ✅ Good: Remove listeners when done
const cleanup = () => {
  emitter.removeAllListeners();
  // Or remove specific listeners
  emitter.off("data", specificHandler);
};

// ✅ Good: Use once() for one-time events
emitter.once("connected", () => {
  console.log("Connected - this will only fire once");
});

// ✅ Good: Monitor listener counts
if (emitter.listenerCount("data") > 10) {
  console.warn("Too many data listeners, possible memory leak");
}

// ✅ Good: Set appropriate max listener threshold
emitter.setMaxListenerWarningThreshold(20);

// ❌ Bad: Adding listeners in loops without cleanup
for (let i = 0; i < 100; i++) {
  emitter.on("data", () => { /* handler */ }); // Memory leak!
}

Install with Tessl CLI

npx tessl i tessl/npm-web3-core

docs

batch-processing.md

configuration-management.md

context-management.md

event-system.md

index.md

provider-integration.md

request-management.md

subscription-management.md

tile.json