LocalMode
Core

Events

Type-safe event system for reactive updates

LocalMode provides a type-safe event system for building reactive applications. Subscribe to VectorDB lifecycle events, embedding operations, and custom events for real-time UI updates.

Overview

The event system enables:

  • Reactive UI updates — Re-render components when data changes
  • Cross-component communication — Notify different parts of your app
  • Debugging & logging — Track all database operations
  • Custom integrations — Build workflows on top of database events

Quick Start

import { createEventEmitter, VectorDBEvents } from '@localmode/core';

// Create an event emitter
const events = createEventEmitter<VectorDBEvents>();

// Subscribe to events
events.on('add', ({ id }) => {
  console.log('Document added:', id);
});

// Emit events
events.emit('add', { id: 'doc-1' });

Creating Event Emitters

Typed Event Emitter

import { createEventEmitter, VectorDBEvents } from '@localmode/core';

// Create with built-in VectorDB event types
const dbEvents = createEventEmitter<VectorDBEvents>();

// Or create a new EventEmitter class directly
import { EventEmitter } from '@localmode/core';
const emitter = new EventEmitter<VectorDBEvents>();

Custom Event Types

import { EventEmitter } from '@localmode/core';

// Define your custom event types
interface MyAppEvents {
  userLogin: { userId: string; timestamp: Date };
  searchPerformed: { query: string; resultCount: number };
  documentProcessed: { docId: string; chunks: number };
}

const appEvents = new EventEmitter<MyAppEvents>();

// Type-safe subscriptions
appEvents.on('userLogin', ({ userId, timestamp }) => {
  console.log(`User ${userId} logged in at ${timestamp}`);
});

// Type-safe emissions
appEvents.emit('userLogin', {
  userId: 'user-123',
  timestamp: new Date(),
});

VectorDB Events

Built-in event types for VectorDB operations:

Prop

Type

Embedding Events

Event types for embedding operations:

Prop

Type

Event Methods

on(event, callback)

Subscribe to an event. Returns an unsubscribe function.

const unsubscribe = events.on('add', ({ id }) => {
  console.log('Added:', id);
});

// Later: unsubscribe
unsubscribe();

once(event, callback)

Subscribe for a single emission only.

events.once('modelLoad', ({ modelId }) => {
  console.log('Model loaded (first time only):', modelId);
});

emit(event, data)

Emit an event synchronously.

events.emit('add', { id: 'doc-1', collection: 'default' });

emitAsync(event, data)

Emit an event and wait for all async handlers to complete.

await events.emitAsync('add', { id: 'doc-1' });
// All handlers (including async ones) have completed

off(event?)

Remove listeners.

// Remove all listeners for specific event
events.off('add');

// Remove all listeners for all events
events.off();

Utility Methods

// Get listener count
const count = events.listenerCount('add');

// Check if there are any listeners
const hasListeners = events.hasListeners('add');

// Get all event names with listeners
const eventNames = events.eventNames();

Global Event Bus

LocalMode provides a global event bus for app-wide events:

import { globalEventBus } from '@localmode/core';

// Subscribe anywhere in your app
globalEventBus.on('add', ({ id }) => {
  console.log('Document added somewhere:', id);
});

// Useful for:
// - Debugging all database operations
// - Syncing state across components
// - Global logging

The global event bus receives events from all VectorDB instances, making it useful for centralized logging and state management.

Event Middleware

Create middleware that emits events for VectorDB operations:

import { wrapVectorDB, createEventEmitter, eventMiddleware } from '@localmode/core';

// Create event emitter
const events = createEventEmitter();

// Subscribe to events
events.on('add', ({ id }) => console.log('Added:', id));
events.on('delete', ({ id }) => console.log('Deleted:', id));

// Create DB with event middleware
const db = wrapVectorDB({
  db: baseDb,
  middleware: eventMiddleware(events),
});

// Now all operations emit events automatically
await db.add({ id: 'doc-1', vector, metadata });
// Console: "Added: doc-1"

React Integration

Custom Hook

import { useEffect, useState } from 'react';
import { createEventEmitter, VectorDBEvents } from '@localmode/core';

const events = createEventEmitter<VectorDBEvents>();

function useVectorDBEvents() {
  const [documentCount, setDocumentCount] = useState(0);
  const [lastOperation, setLastOperation] = useState<string | null>(null);

  useEffect(() => {
    const unsubscribeAdd = events.on('add', () => {
      setDocumentCount((c) => c + 1);
      setLastOperation('add');
    });

    const unsubscribeDelete = events.on('delete', () => {
      setDocumentCount((c) => c - 1);
      setLastOperation('delete');
    });

    const unsubscribeClear = events.on('clear', () => {
      setDocumentCount(0);
      setLastOperation('clear');
    });

    return () => {
      unsubscribeAdd();
      unsubscribeDelete();
      unsubscribeClear();
    };
  }, []);

  return { documentCount, lastOperation };
}

Search Analytics

function SearchAnalytics() {
  const [searches, setSearches] = useState<
    Array<{
      query: string;
      results: number;
      duration: number;
    }>
  >([]);

  useEffect(() => {
    return globalEventBus.on('search', ({ resultsCount, k, durationMs }) => {
      setSearches((prev) => [
        ...prev.slice(-99), // Keep last 100
        { query: 'unknown', results: resultsCount, duration: durationMs },
      ]);
    });
  }, []);

  const avgDuration = searches.reduce((sum, s) => sum + s.duration, 0) / searches.length;

  return (
    <div>
      <p>Total searches: {searches.length}</p>
      <p>Average duration: {avgDuration.toFixed(2)}ms</p>
    </div>
  );
}

Full Example

import {
  createVectorDB,
  embed,
  createEventEmitter,
  VectorDBEvents,
  EmbeddingEvents,
} from '@localmode/core';
import { transformers } from '@localmode/transformers';

// Create event emitters
const dbEvents = createEventEmitter<VectorDBEvents>();
const embedEvents = createEventEmitter<EmbeddingEvents>();

// Set up logging
dbEvents.on('add', ({ id }) => console.log(`[DB] Added: ${id}`));
dbEvents.on('search', ({ resultsCount, durationMs }) => {
  console.log(`[DB] Search: ${resultsCount} results in ${durationMs}ms`);
});
dbEvents.on('error', ({ operation, error }) => {
  console.error(`[DB] Error in ${operation}:`, error);
});

embedEvents.on('embedStart', ({ valueCount }) => {
  console.log(`[Embed] Starting ${valueCount} values`);
});
embedEvents.on('embedComplete', ({ valueCount, durationMs, tokens }) => {
  console.log(`[Embed] Completed ${valueCount} values in ${durationMs}ms (${tokens} tokens)`);
});
embedEvents.on('modelLoad', ({ modelId, durationMs }) => {
  console.log(`[Embed] Model ${modelId} loaded in ${durationMs}ms`);
});

// Create database
const db = await createVectorDB({ name: 'documents', dimensions: 384 });
const model = transformers.embedding('Xenova/all-MiniLM-L6-v2');

// Manually emit events (or use middleware)
async function addDocument(text: string) {
  const id = crypto.randomUUID();

  embedEvents.emit('embedStart', { valueCount: 1 });
  const start = performance.now();

  const { embedding, usage } = await embed({ model, value: text });

  embedEvents.emit('embedComplete', {
    valueCount: 1,
    durationMs: performance.now() - start,
    tokens: usage.tokens,
  });

  await db.add({ id, vector: embedding, metadata: { text } });
  dbEvents.emit('add', { id });

  return id;
}

On this page