npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pixagram/lacerta-db

v0.9.1

Published

Lacerta-DB is a Javascript IndexedDB Database for Web Browsers. Simple, Fast, Secure.

Readme


"Store your data like a lizard stores heat — efficiently, locally, and securely."

LacertaDB is a zero-dependency, browser-only document database designed for Web3 applications, offline-first PWAs, and any browser application requiring robust local storage with encryption capabilities.


Table of Contents

| Section | Description | |---------|-------------| | Features | What LacertaDB offers | | Installation | How to install | | Quick Start | Get running in 2 minutes | | Architecture | System design overview | | API Reference | Complete method documentation | | Query Operators | MongoDB-style query syntax | | Aggregation Pipeline | Data transformation stages | | Indexing | B-Tree, Hash, Text, Geo indexes | | Encryption | AES-GCM-256 security | | Caching | LRU, LFU, TTL strategies | | Migrations | Schema version management | | Performance | Metrics and optimization | | Error Codes | Error handling reference | | Examples | Real-world usage patterns |


Features

🗄️ Storage

  • IndexedDB backend with connection pooling
  • OPFS for binary attachments
  • localStorage QuickStore for fast access
  • Automatic space management

🔐 Security

  • AES-GCM-256 encryption
  • PBKDF2 key derivation (100k iterations)
  • HMAC integrity verification
  • Private key vault storage

🔍 Querying

  • MongoDB-style query syntax
  • 20+ operators ($eq, $gt, $in, $regex...)
  • Aggregation pipeline with 7 stages
  • Full-text and geospatial search

⚡ Performance

  • B-Tree indexes for range queries
  • LRU/LFU/TTL caching strategies
  • Compression via CompressionStream
  • Async mutex for thread safety

Installation

npm install @pixagram/lacertadb

Dependencies (installed automatically):

npm install @pixagram/turboserial @pixagram/turbobase64

Quick Start

import { LacertaDB } from '@pixagram/lacertadb';

// Initialize
const lacerta = new LacertaDB();

// Get or create a database
const db = await lacerta.getDatabase('myapp');

// Create a collection
const users = await db.createCollection('users');

// Add documents
const userId = await users.add({
  name: 'Alice',
  email: '[email protected]',
  age: 28
});

// Query documents
const results = await users.query({ age: { $gte: 18 } });

// Update
await users.update(userId, { age: 29 });

// Delete
await users.delete(userId);
// Create encrypted database
const secureDb = await lacerta.getSecureDatabase('vault', '123456');

// All documents are automatically encrypted
const secrets = await secureDb.createCollection('secrets');
await secrets.add({ apiKey: 'sk-xxx-secret' });

// Change PIN (re-encrypts all data)
await secureDb.changePin('123456', 'newSecurePin!');

Architecture

LacertaDB follows a fractal architecture where each layer encapsulates complexity while exposing a simple interface.

┌─────────────────────────────────────────────────────────────────┐
│                         LacertaDB                               │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                      Database                             │  │
│  │  ┌─────────────────────────────────────────────────────┐  │  │
│  │  │                  Collection                         │  │  │
│  │  │  ┌───────────────────────────────────────────────┐  │  │  │
│  │  │  │                Document                       │  │  │  │
│  │  │  │  • Data (serialized)                          │  │  │  │
│  │  │  │  • Metadata (_id, _created, _modified)        │  │  │  │
│  │  │  │  • Attachments (OPFS references)              │  │  │  │
│  │  │  └───────────────────────────────────────────────┘  │  │  │
│  │  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐    │  │  │
│  │  │  │ IndexManager│ │CacheStrategy│ │  EventBus   │    │  │  │
│  │  │  └─────────────┘ └─────────────┘ └─────────────┘    │  │  │
│  │  └─────────────────────────────────────────────────────┘  │  │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐  │  │
│  │  │ QuickStore  │ │ Encryption  │ │ MigrationManager    │  │  │
│  │  └─────────────┘ └─────────────┘ └─────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │               Shared Infrastructure                         ││
│  │  ConnectionPool │ AsyncMutex │ QueryEngine │ Serializer     ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

| Component | Responsibility | Storage | |-----------|---------------|---------| | LacertaDB | Instance manager, backup/restore | Memory | | Database | Collection manager, encryption, settings | localStorage | | Collection | CRUD operations, queries, indexes | IndexedDB | | Document | Data container, pack/unpack | IndexedDB | | QuickStore | Fast key-value access | localStorage | | OPFSUtility | Binary file attachments | OPFS |

User Data → Serialize → Compress → Encrypt → Store
                                              ↓
User Data ← Deserialize ← Decompress ← Decrypt ← Retrieve

| Stage | Technology | Optional | |-------|------------|----------| | Serialize | TurboSerial (CBOR-like) | No | | Compress | CompressionStream (deflate) | Yes | | Encrypt | AES-GCM-256 | Yes | | Store | IndexedDB / OPFS | No |


API Reference

LacertaDB (Main Entry Point)

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | getDatabase | name, options? | Promise<Database> | Get or create database | | getSecureDatabase | name, pin, salt?, config? | Promise<Database> | Get encrypted database | | dropDatabase | name | Promise<void> | Delete database completely | | listDatabases | — | string[] | List all database names | | createBackup | password? | Promise<string> | Export all databases | | restoreBackup | data, password? | Promise<Object> | Import backup data | | close | — | void | Close all connections | | destroy | — | void | Clean up all resources |


Database

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | createCollection | name, options? | Promise<Collection> | Create new collection | | getCollection | name | Promise<Collection> | Get existing collection | | dropCollection | name | Promise<void> | Delete collection | | listCollections | — | string[] | List collection names |

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | verifyPin | pin | Promise<boolean> | Verify PIN is correct | | changePin | oldPin, newPin | Promise<string> | Change PIN, re-encrypt data | | getEncryptionStatus | — | Object | Get encryption state info | | storePrivateKey | name, key, auth? | Promise<boolean> | Store encrypted key | | getPrivateKey | name, auth? | Promise<string> | Retrieve decrypted key | | listPrivateKeys | — | Promise<Array> | List stored key names | | deletePrivateKey | name | Promise<boolean> | Delete stored key |

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | getStats | — | Object | Database statistics | | updateSettings | settings | void | Update database settings | | export | format?, password? | Promise<string> | Export database | | import | data, format?, password? | Promise<Object> | Import data | | clearAll | — | Promise<void> | Clear all collections | | destroy | — | Promise<void> | Destroy database |

| Property | Type | Description | |----------|------|-------------| | name | string | Database name | | isEncrypted | boolean | Encryption status | | metadata | DatabaseMetadata | Size, document counts | | settings | Settings | Configuration | | quickStore | QuickStore | Fast localStorage access | | performanceMonitor | PerformanceMonitor | Metrics collector |


Collection

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | add | data, options? | Promise<string> | Add document | | get | docId, options? | Promise<Object> | Get by ID | | getAll | options? | Promise<Array> | Get all documents | | update | docId, updates, options? | Promise<string> | Update document | | delete | docId, options? | Promise<void> | Delete document |

Add Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | id | string | auto | Custom document ID | | compressed | boolean | true | Enable compression | | permanent | boolean | false | Protect from auto-cleanup | | attachments | Array<File\|Blob> | [] | Binary attachments |

Get Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | includeAttachments | boolean | false | Load binary attachments |

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | batchAdd | documents, options? | Promise<string[]> | Add multiple | | batchUpdate | updates | Promise<string[]> | Update multiple | | batchDelete | docIds | Promise<void> | Delete multiple |

// Batch add
const ids = await collection.batchAdd([
  { name: 'Alice' },
  { name: 'Bob' },
  { name: 'Charlie' }
]);

// Batch update
await collection.batchUpdate([
  { id: 'doc1', updates: { status: 'active' } },
  { id: 'doc2', updates: { status: 'active' } }
]);

// Batch delete
await collection.batchDelete(['doc1', 'doc2', 'doc3']);

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | query | filter, options? | Promise<Array> | Query documents | | aggregate | pipeline | Promise<Array> | Run aggregation | | count | filter? | Promise<number> | Count documents |

Query Options:

| Option | Type | Description | |--------|------|-------------| | sort | Object | Sort specification { field: 1 } or { field: -1 } | | skip | number | Skip N documents | | limit | number | Limit results | | projection | Object | Field selection |

const results = await collection.query(
  { status: 'active', age: { $gte: 18 } },
  { 
    sort: { createdAt: -1 },
    skip: 10,
    limit: 20,
    projection: { name: 1, email: 1 }
  }
);

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | createIndex | fieldPath, options? | Promise<string> | Create index | | dropIndex | indexName | Promise<void> | Remove index | | getIndexes | — | Promise<Object> | List indexes with stats | | verifyIndexes | — | Promise<Object> | Verify index integrity |

| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| | on | event, callback | void | Subscribe to event | | off | event, callback? | void | Unsubscribe |

Available Events:

| Event | Payload | Description | |-------|---------|-------------| | beforeAdd | documentData | Before document insert | | afterAdd | document | After document insert | | beforeUpdate | { docId, updates } | Before update | | afterUpdate | document | After update | | beforeDelete | docId | Before deletion | | afterDelete | docId | After deletion | | beforeGet | docId | Before retrieval | | afterGet | document | After retrieval |

collection.on('afterAdd', async (doc) => {
  console.log(`Document ${doc._id} created`);
  await notifyWebhook(doc);
});

Query Operators

LacertaDB supports MongoDB-compatible query operators.

Comparison Operators

| Operator | Description | Example | |----------|-------------|---------| | $eq | Equal | { status: { $eq: 'active' } } | | $ne | Not equal | { status: { $ne: 'deleted' } } | | $gt | Greater than | { age: { $gt: 18 } } | | $gte | Greater or equal | { score: { $gte: 90 } } | | $lt | Less than | { price: { $lt: 100 } } | | $lte | Less or equal | { qty: { $lte: 10 } } | | $in | In array | { status: { $in: ['a', 'b'] } } | | $nin | Not in array | { role: { $nin: ['guest'] } } |

Logical Operators

| Operator | Description | Example | |----------|-------------|---------| | $and | Logical AND | { $and: [{ a: 1 }, { b: 2 }] } | | $or | Logical OR | { $or: [{ a: 1 }, { b: 2 }] } | | $not | Logical NOT | { $not: { status: 'deleted' } } | | $nor | Neither | { $nor: [{ a: 1 }, { b: 2 }] } |

Element Operators

| Operator | Description | Example | |----------|-------------|---------| | $exists | Field exists | { email: { $exists: true } } | | $type | Type check | { age: { $type: 'number' } } |

Array Operators

| Operator | Description | Example | |----------|-------------|---------| | $all | Contains all | { tags: { $all: ['a', 'b'] } } | | $elemMatch | Element match | { items: { $elemMatch: { qty: { $gt: 5 } } } } | | $size | Array size | { tags: { $size: 3 } } |

String Operators

| Operator | Description | Example | |----------|-------------|---------| | $regex | Regex match | { name: { $regex: '^John' } } | | $text | Text search | { bio: { $text: 'developer' } } |

// Find active users over 18 with verified email
const users = await collection.query({
  $and: [
    { status: 'active' },
    { age: { $gte: 18 } },
    { emailVerified: { $exists: true } }
  ]
});

// Find products in price range with specific tags
const products = await collection.query({
  $and: [
    { price: { $gte: 10, $lte: 100 } },
    { tags: { $all: ['sale', 'featured'] } },
    { category: { $in: ['electronics', 'accessories'] } }
  ]
});

// Text search with regex
const articles = await collection.query({
  $or: [
    { title: { $regex: 'blockchain' } },
    { content: { $text: 'cryptocurrency' } }
  ]
});

Aggregation Pipeline

Transform and analyze data using pipeline stages.

| Stage | Description | Example | |-------|-------------|---------| | $match | Filter documents | { $match: { status: 'active' } } | | $project | Shape output | { $project: { name: 1, email: 1 } } | | $sort | Order results | { $sort: { date: -1 } } | | $limit | Limit results | { $limit: 10 } | | $skip | Skip results | { $skip: 20 } | | $group | Group & aggregate | { $group: { _id: '$category', count: { $count: 1 } } } | | $lookup | Join collections | See example below |

Group Accumulators

| Accumulator | Description | |-------------|-------------| | $sum | Sum values | | $avg | Average values | | $min | Minimum value | | $max | Maximum value | | $count | Count documents |

// Sales report by category
const report = await orders.aggregate([
  { $match: { status: 'completed' } },
  { $group: {
      _id: '$category',
      totalSales: { $sum: '$amount' },
      avgOrder: { $avg: '$amount' },
      orderCount: { $count: 1 }
  }},
  { $sort: { totalSales: -1 } },
  { $limit: 10 }
]);

// Join users with their orders
const usersWithOrders = await users.aggregate([
  { $lookup: {
      from: 'orders',
      localField: '_id',
      foreignField: 'userId',
      as: 'orders'
  }},
  { $project: {
      name: 1,
      email: 1,
      orderCount: { $size: '$orders' }
  }}
]);

// Top customers this month
const topCustomers = await orders.aggregate([
  { $match: {
      date: { $gte: startOfMonth }
  }},
  { $group: {
      _id: '$customerId',
      total: { $sum: '$amount' },
      orders: { $count: 1 }
  }},
  { $sort: { total: -1 } },
  { $limit: 5 }
]);

Indexing

Indexes dramatically improve query performance for large collections.

Index Types

| Type | Best For | Example | |------|----------|---------| | btree | Range queries, sorting | { type: 'btree' } | | hash | Exact match lookups | { type: 'hash' } | | text | Full-text search | { type: 'text' } | | geo | Location queries | { type: 'geo' } |

Creating Indexes

// B-Tree index (default)
await collection.createIndex('email', { unique: true });

// Hash index for fast lookups
await collection.createIndex('userId', { type: 'hash' });

// Text index for search
await collection.createIndex('content', { type: 'text' });

// Geo index for locations
await collection.createIndex('location', { type: 'geo' });

// Sparse index (skip null values)
await collection.createIndex('optionalField', { sparse: true });

Index Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | name | string | fieldPath | Custom index name | | type | string | 'btree' | Index type | | unique | boolean | false | Enforce uniqueness | | sparse | boolean | false | Skip null/undefined |

// Create geo index
await places.createIndex('coordinates', { type: 'geo' });

// Find nearby (within 10km)
const nearby = await places.query({
  coordinates: {
    $near: {
      coordinates: { lat: 47.3769, lng: 8.5417 },
      maxDistance: 10 // kilometers
    }
  }
});

// Find within bounds
const inArea = await places.query({
  coordinates: {
    $within: {
      minLat: 47.0,
      maxLat: 48.0,
      minLng: 8.0,
      maxLng: 9.0
    }
  }
});

Encryption

LacertaDB provides AES-GCM-256 encryption with PBKDF2 key derivation.

Security Specifications

| Parameter | Value | |-----------|-------| | Algorithm | AES-GCM-256 | | Key Derivation | PBKDF2 | | Iterations | 100,000 | | Hash | SHA-256 | | Salt Length | 32 bytes | | IV Length | 12 bytes |

Usage

// Create encrypted database
const db = await lacerta.getSecureDatabase('vault', 'mySecretPin123');

// All documents are automatically encrypted
const secrets = await db.createCollection('secrets');
await secrets.add({ 
  apiKey: 'sk-live-xxx',
  privateData: 'sensitive information'
});

// Verify PIN
const isValid = await db.verifyPin('mySecretPin123'); // true

// Change PIN (re-encrypts ALL data)
await db.changePin('mySecretPin123', 'newStrongerPin!');

// Check encryption status
const status = db.getEncryptionStatus();
// { isEncrypted: true, initialized: true, algorithm: 'AES-GCM-256', kdf: 'PBKDF2' }

Private Key Vault

Store cryptographic keys securely:

// Store a private key
await db.storePrivateKey('wallet-main', privateKeyString, 'optionalAuth');

// Retrieve
const key = await db.getPrivateKey('wallet-main', 'optionalAuth');

// List stored keys
const keys = await db.listPrivateKeys();
// [{ name: 'wallet-main', createdAt: 1699..., updatedAt: null }]

// Delete
await db.deletePrivateKey('wallet-main');

Caching

Configure caching to optimize read performance.

Cache Strategies

| Strategy | Description | Best For | |----------|-------------|----------| | lru | Least Recently Used | General purpose | | lfu | Least Frequently Used | Hot data patterns | | ttl | Time To Live | Expiring data | | none | Disabled | Write-heavy workloads |

Configuration

// Configure cache per collection
collection.configureCacheStrategy({
  type: 'lru',      // 'lru' | 'lfu' | 'ttl' | 'none'
  maxSize: 200,     // Max cached items
  ttl: 120000,      // TTL in milliseconds
  enabled: true
});

// Clear cache manually
collection.clearCache();

Migrations

Manage schema changes across versions.

const migration = new MigrationManager(db);

// Define migrations
migration.addMigration({
  version: '1.1.0',
  name: 'Add user roles',
  up: async (doc) => ({
    ...doc,
    role: doc.role || 'user',
    permissions: doc.permissions || []
  }),
  down: async (doc) => {
    const { role, permissions, ...rest } = doc;
    return rest;
  }
});

migration.addMigration({
  version: '1.2.0',
  name: 'Normalize email',
  up: async (doc) => ({
    ...doc,
    email: doc.email?.toLowerCase()
  }),
  down: async (doc) => doc // No rollback needed
});

// Run migrations
await migration.runMigrations('1.2.0');

// Rollback if needed
await migration.rollback('1.0.0');

Performance Monitoring

Built-in performance tracking and optimization.

// Start monitoring
lacerta.performanceMonitor.startMonitoring();

// Get statistics
const stats = lacerta.performanceMonitor.getStats();
console.log(stats);
// {
//   opsPerSec: 150,
//   avgLatency: '2.34',
//   cacheHitRate: '87.5',
//   memoryUsageMB: '45.20'
// }

// Get optimization suggestions
const tips = lacerta.performanceMonitor.getOptimizationTips();
// ['Performance is optimal. No issues detected.']

// Stop monitoring
lacerta.performanceMonitor.stopMonitoring();

Error Codes

| Code | Description | Solution | |------|-------------|----------| | DOCUMENT_NOT_FOUND | Document doesn't exist | Verify document ID | | COLLECTION_NOT_FOUND | Collection doesn't exist | Create collection first | | COLLECTION_EXISTS | Collection already exists | Use getCollection instead | | ENCRYPTION_NOT_INITIALIZED | Encryption required but not set up | Use getSecureDatabase | | ENCRYPTION_REQUIRED | Document encrypted, db not unlocked | Unlock with correct PIN | | INVALID_PIN | Wrong PIN provided | Verify PIN | | NOT_ENCRYPTED | Operation requires encryption | Use encrypted database | | PERMANENT_DOCUMENT_PROTECTION | Cannot delete permanent document | Use force: true | | QUOTA_EXCEEDED | Storage limit reached | Free up space | | MUTEX_TIMEOUT | Operation timed out | Retry or check deadlock | | TRANSACTION_FAILED | IndexedDB transaction failed | Retry operation | | INDEX_EXISTS | Index already exists | Drop existing index first | | UNIQUE_CONSTRAINT_VIOLATION | Duplicate value in unique index | Use unique values |

Error Handling

import { LacertaDBError } from '@pixagram/lacertadb';

try {
  await collection.get('nonexistent');
} catch (error) {
  if (error instanceof LacertaDBError) {
    console.log(error.code);      // 'DOCUMENT_NOT_FOUND'
    console.log(error.message);   // 'Document with id...'
    console.log(error.timestamp); // ISO date string
  }
}

Examples

const lacerta = new LacertaDB();
const db = await lacerta.getSecureDatabase('app', 'adminPin123');

// Create collections
const users = await db.createCollection('users');
const sessions = await db.createCollection('sessions');

// Create indexes
await users.createIndex('email', { unique: true });
await users.createIndex('username', { unique: true });
await sessions.createIndex('userId');
await sessions.createIndex('expiresAt');

// Register user
async function registerUser(data) {
  const userId = await users.add({
    ...data,
    email: data.email.toLowerCase(),
    createdAt: Date.now(),
    status: 'pending'
  });
  return userId;
}

// Login
async function login(email, password) {
  const [user] = await users.query({ 
    email: email.toLowerCase(),
    status: 'active'
  });
  
  if (!user || !verifyPassword(password, user.passwordHash)) {
    throw new Error('Invalid credentials');
  }
  
  const sessionId = await sessions.add({
    userId: user._id,
    createdAt: Date.now(),
    expiresAt: Date.now() + 24 * 60 * 60 * 1000
  });
  
  return { user, sessionId };
}

// Cleanup expired sessions
async function cleanupSessions() {
  const expired = await sessions.query({
    expiresAt: { $lt: Date.now() }
  });
  await sessions.batchDelete(expired.map(s => s._id));
}
const db = await lacerta.getDatabase('shop');
const carts = await db.createCollection('carts');
const products = await db.createCollection('products');

// Add to cart
async function addToCart(userId, productId, quantity) {
  const [existing] = await carts.query({
    userId,
    productId
  });
  
  if (existing) {
    await carts.update(existing._id, {
      quantity: existing.quantity + quantity,
      updatedAt: Date.now()
    });
  } else {
    await carts.add({
      userId,
      productId,
      quantity,
      addedAt: Date.now()
    });
  }
}

// Get cart with product details
async function getCart(userId) {
  const cartItems = await carts.query({ userId });
  
  const enriched = await Promise.all(
    cartItems.map(async (item) => {
      const product = await products.get(item.productId);
      return {
        ...item,
        product,
        subtotal: product.price * item.quantity
      };
    })
  );
  
  return {
    items: enriched,
    total: enriched.reduce((sum, i) => sum + i.subtotal, 0)
  };
}

// Cart analytics
async function getCartAnalytics() {
  return await carts.aggregate([
    { $group: {
        _id: '$productId',
        totalQuantity: { $sum: '$quantity' },
        cartCount: { $count: 1 }
    }},
    { $sort: { totalQuantity: -1 } },
    { $limit: 10 }
  ]);
}
const db = await lacerta.getDatabase('geo');
const places = await db.createCollection('places');

// Create geo index
await places.createIndex('location', { type: 'geo' });
await places.createIndex('name', { type: 'text' });

// Add place
async function addPlace(data) {
  return await places.add({
    name: data.name,
    location: { lat: data.lat, lng: data.lng },
    category: data.category,
    rating: data.rating || 0,
    createdAt: Date.now()
  });
}

// Find nearby restaurants
async function findNearbyRestaurants(lat, lng, radiusKm = 5) {
  return await places.query({
    location: {
      $near: {
        coordinates: { lat, lng },
        maxDistance: radiusKm
      }
    },
    category: 'restaurant'
  }, {
    sort: { rating: -1 }
  });
}

// Search places by name
async function searchPlaces(query, bounds) {
  return await places.query({
    $and: [
      { name: { $text: query } },
      { location: { $within: bounds } }
    ]
  });
}
const db = await lacerta.getSecureDatabase('wallet', userPin);

// Store wallet keys
async function storeWallet(walletName, privateKey, mnemonic) {
  await db.storePrivateKey(`${walletName}-key`, privateKey);
  await db.storePrivateKey(`${walletName}-mnemonic`, mnemonic);
  
  // Store metadata (not the actual keys)
  const wallets = await db.getCollection('wallets');
  await wallets.add({
    name: walletName,
    address: deriveAddress(privateKey),
    createdAt: Date.now()
  }, { id: walletName, permanent: true });
}

// Sign transaction
async function signTransaction(walletName, tx) {
  const privateKey = await db.getPrivateKey(`${walletName}-key`);
  return signWithKey(tx, privateKey);
}

// Export wallet (encrypted)
async function exportWallet(walletName, exportPassword) {
  const privateKey = await db.getPrivateKey(`${walletName}-key`);
  const mnemonic = await db.getPrivateKey(`${walletName}-mnemonic`);
  
  const wallets = await db.getCollection('wallets');
  const metadata = await wallets.get(walletName);
  
  return await db.export('encrypted', exportPassword);
}

QuickStore

For simple, fast key-value access using localStorage:

const quick = db.quickStore;

// CRUD operations
quick.add('user-pref', { theme: 'dark', language: 'en' });
const prefs = quick.get('user-pref');
quick.update('user-pref', { ...prefs, theme: 'light' });
quick.delete('user-pref');

// Query (supports same operators as Collection)
const results = quick.query({ theme: 'dark' });

// Get all
const all = quick.getAll();

// Clear
quick.clear();

// Size
console.log(quick.size); // number of items

⚠️ Note: QuickStore uses localStorage which has ~5-10MB limit. Use Collections for larger datasets.


Browser Compatibility

| Browser | IndexedDB | OPFS | CompressionStream | |---------|-----------|------|-------------------| | Chrome 86+ | ✅ | ✅ | ✅ | | Firefox 111+ | ✅ | ✅ | ✅ | | Safari 15.4+ | ✅ | ⚠️ Partial | ✅ | | Edge 86+ | ✅ | ✅ | ✅ |


License

MIT © Pixagram SA