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

@moltendb-web/angular

v2.4.0

Published

Official Angular Signals wrapper for MoltenDb

Readme

@moltendb-web/angular

Official Angular integration for MoltenDb, providing a seamless reactive developer experience using modern Angular Signals.

Requirements: Angular 17 or higher. This library uses Angular Signals and standalone APIs introduced in Angular 17.


What's New in v2

  • Bulk Delete with .where() — delete documents matching a filter clause without listing individual keys.
  • Delete .order() + count-only prune (query v2.3.0) — order bulk-delete matches by _seq before .count() is applied ('asc' default = oldest first, 'desc' = newest first), and prune the oldest/newest n documents with a bare .delete().count(n) (no .where()). See @moltendb-web/query.
  • Capped Collections (.maxSize()) — cap a collection to a maximum number of documents; oldest entries are evicted automatically when the limit is reached.
  • TTL Collections (.ttl()) — set a time-to-live (in seconds) on a collection; documents are removed automatically after expiry.

Demo

See the library in action with a real-world demo application:


Installation

npm install @moltendb-web/angular

@moltendb-web/core and @moltendb-web/query are automatically installed as dependencies — no need to install them separately.


Step 1: Configure Assets

MoltenDb runs its database engine inside a background Web Worker and relies on WebAssembly (WASM). You must tell Angular to serve these compiled files as public assets.

Update the assets array in your angular.json:

"assets": [
{
"glob": "moltendb-worker.js",
"input": "node_modules/@moltendb-web/core/dist",
"output": "/"
},
{
"glob": "moltendb_core.js",
"input": "node_modules/@moltendb-web/core/dist/wasm",
"output": "/wasm/"
},
{
"glob": "*.wasm",
"input": "node_modules/@moltendb-web/core/dist/wasm",
"output": "/wasm/"
}
]

Note: Restart your Angular dev server after modifying angular.json.


Step 2: Provide MoltenDb

Initialise MoltenDb in your root app.config.ts:

import {ApplicationConfig} from '@angular/core';
import {provideMoltenDb} from '@moltendb-web/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    provideMoltenDb({
      name: 'my_app_db',
      workerUrl: '/moltendb-worker.js',
    })
  ]
};

Step 3: Fetching and Mutating Data

Reactive data — moltenDbResource()

Use moltenDbResource to bind data to your template. It handles loading states, errors, and live collection updates automatically.

import {Component} from '@angular/core';
import {moltenDbResource} from '@moltendb-web/angular';

interface Laptop {
  _key: string;
  brand: string;
  model: string;
  price: number;
}

@Component({
  selector: 'app-laptops',
  template: `
    @if (laptops.isLoading() && !laptops.value()) {
      <p>Loading…</p>
    }
    @if (laptops.value(); as list) {
      <ul>
        @for (item of list; track item._key) {
          <li>{{ item.brand }} {{ item.model }} — {{ item.price | currency }}</li>
        }
      </ul>
    }
    @if (laptops.error()) {
      <p class="error">{{ laptops.error().message }}</p>
    }
  `
})
export class LaptopsComponent {
  laptops = moltenDbResource<Laptop[]>('laptops', (col) =>
      col.get()
          .where({in_stock: true})
          .sort([{field: 'price', order: 'asc'}])
          .exec() as Promise<Laptop[]>
  );
}

Imperative access — moltendbClient()

Use moltendbClient() for mutations and one-off queries triggered by user actions:

import {Component} from '@angular/core';
import {moltendbClient} from '@moltendb-web/angular';

@Component({...})
export class AdminComponent {
  private client = moltendbClient();

  async addLaptop() {
    await this.client.collection('laptops').set({
      lp_new: {brand: 'Framework', model: 'Laptop 16', price: 1049, in_stock: true}
    }).exec();
    // Any moltenDbResource watching 'laptops' refreshes automatically
  }
}

Hooks

moltendbClient()

Returns the MoltenDbClient instance for imperative database access. Must be called in an injection context.

moltenDbResource<T>(collection, queryFn, options?)

Creates a reactive resource bound to a collection. Automatically re-fetches when the collection is mutated. Must be called in an injection context.

Accepts an optional third options argument:

| Option | Type | Default | Description | |----------------|------|-------------|---------------------------------------------------------------------------------------------------------------| | initialValue | T | undefined | Seeds the value signal before the first fetch resolves in the browser, and is what value() stays at on the server (no fetch is ever attempted there) |

const laptops = moltenDbResource<Laptop[]>('laptops', (col) => col.get().exec() as Promise<Laptop[]>, {
  initialValue: []
});
// laptops.value() is `[]` immediately — in the browser, before the first fetch resolves,
// and forever on the server, instead of `undefined`.

Returns a MoltenDbResource<T> with three readonly signals:

| Signal | Type | Description | |-------------|--------------------------|-------------------------------------| | value | Signal<T \| undefined> | The latest query result, or options?.initialValue (defaults to undefined) before the first fetch resolves | | isLoading | Signal<boolean> | true while a fetch is in progress | | error | Signal<any \| null> | The last error, or null if none |

moltenDbIsLeader()

Returns true if the current tab is the Leader — the tab running the WASM worker and performing actual writes. Other tabs act as follower proxies. Must be called in an injection context.

import {Component} from '@angular/core';
import {moltenDbIsLeader} from '@moltendb-web/angular';

@Component({
  selector: 'app-tab-badge',
  template: `<span>{{ isLeader() ? '👑 Leader' : '🔗 Follower' }}</span>`
})
export class TabBadgeComponent {
  isLeader = moltenDbIsLeader;
}

moltenDbTerminate()

Terminates the MoltenDb worker. Must be called in an injection context.

moltenDbClearOpfs() (v2.0.0)

Flushes and closes the OPFS sync handle. Call this before moltenDbTerminate() — without it the browser throws a " No modification allowed" error when removing the OPFS directory. Must be called in an injection context.

import { Component } from '@angular/core';
import { moltenDbClearOpfs, moltenDbTerminate } from '@moltendb-web/angular';

@Component({
  selector: 'app-reset-button',
  template: `<button (click)="handleReset()">🗑 Reset All Data</button>`
})
export class ResetButtonComponent {
  private clearOpfs = moltenDbClearOpfs;
  private terminate = moltenDbTerminate;

  async handleReset() {
    if (!confirm('This will delete all local data and reload. Continue?')) return;
    // 1. Flush and close the OPFS sync handle
    await this.clearOpfs();
    // 2. Now safe to terminate the worker
    this.terminate();
    location.reload();
  }
}

moltenDbEvents(listener) (v2.0.0 — auto-unsubscribe)

Subscribes to real-time mutation events. The listener is called with a DbEvent whenever any document is created, updated, deleted, or a collection is dropped. The subscription is automatically cleaned up when the injection context (component/service) is destroyed — no ngOnDestroy or manual unsubscription needed. Must be called in an injection context.

import {Component} from '@angular/core';
import {moltenDbEvents} from '@moltendb-web/angular';
import type {DbEvent} from '@moltendb-web/angular';

@Component({
  selector: 'app-live-feed',
  template: `
    <ul>
      @for (e of events; track e) {
        <li>{{ e.event }} — {{ e.collection }}/{{ e.key }}</li>
      }
    </ul>
  `
})
export class LiveFeedComponent {
  events: DbEvent[] = [];

  constructor() {
    moltenDbEvents((evt) => {
      this.events = [evt, ...this.events].slice(0, 50);
    });
    // ✅ No ngOnDestroy needed — subscription is auto-cleaned up when component is destroyed
  }
}

API Reference

| Export | Type | Description | |-----------------------------------------|----------------|------------------------------------------------------------------------------------------------------------------------| | provideMoltenDb(config) | Provider | Registers MoltenDb as an Angular environment provider | | moltendbClient() | Injection hook | Returns the MoltenDbClient instance | | moltenDbIsLeader() | Injection hook | Returns true if the current tab is the Leader | | moltenDbTerminate() | Injection hook | Terminates the MoltenDb worker | | moltenDbClearOpfs() | Injection hook | (v2.0.0) Flush and close the OPFS sync handle — call before moltenDbTerminate() | | moltenDbResource(collection, queryFn, options?) | Injection hook | Reactive resource with value, isLoading, error signals, auto-refresh, and an optional initialValue | | moltenDbEvents(listener) | Injection hook | (v2.0.0) Subscribe to real-time DbEvent mutation events; auto-unsubscribes when the injection context is destroyed | | DbEvent | Type | Event object emitted on mutations: { event, collection, key, new_v } | | AngularMoltenDbOptions | Interface | Config passed to provideMoltenDb — extends MoltenDbOptions with a required name field | | MoltenDbResource<T> | Interface | Return type of moltenDbResource: { value, isLoading, error } signals | | MoltenDbResourceOptions<T> | Interface | Options accepted by moltenDbResource: { initialValue? } |


Configuration

AngularMoltenDbOptions extends the core MoltenDbOptions with one required field:

| Option | Type | Default | Description | |---------------------|---------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| | name | string | required | Database name (used as the OPFS directory name) | | inMemory | boolean | false | Run entirely in RAM — no OPFS writes. Data persists as long as at least one tab is open; any tab refresh or close wipes the shared store for all tabs | | encryptionKey | string | undefined | Password for at-rest encryption. If omitted, data is stored as plain JSON | | writeMode | 'async' \| 'sync' | 'async' | Storage write mode: 'async' for high throughput or 'sync' for durable writes | | maxBodySize | number | undefined | Maximum request body size in bytes | | maxKeysPerRequest | number | 1000 | Maximum number of keys allowed per JSON request | | workerUrl | string \| URL | undefined | Custom URL or path to moltendb-worker.js |


Server-Side Rendering (SSR)

MoltenDbService detects whether it is running in the browser or on the server (PLATFORM_ID / isPlatformBrowser) and never boots WASM, OPFS, or the Web Locks API outside the browser. You no longer need to guard every MoltenDb-using route yourself just to avoid a server crash — the library does it internally:

  • moltendbClient() / db / client — on the server these are lightweight no-op stubs with the same public shape as the real implementations, so you never need ?. to call them. Builder chains keep working synchronously (.collection('x').set({...}), .get(), .delete().keys('a'), .sort([...]), etc.), but the terminal .exec() call always rejects with a clear, actionable error: "[MoltenDb] MoltenDb is not available in a server-side rendering context." — no hang, no synchronous throw mid-chain.
  • moltenDbResource() — resolves synchronously to a documented empty state on the server: isLoading() is false, error() is null, and value() is options?.initialValue (or undefined if you didn't pass one). No fetch is attempted, so templates using @if (resource.isLoading()) / @if (resource.value(); as list) / @if (resource.error()) render their empty/no-data branch immediately with no console errors about navigator.locks, WASM, or OPFS. Pass { initialValue: [] } for list-returning queryFns to get a typed empty array on the server instead of undefined.

There is no public readiness API — no isReady getter on the service and no standalone moltenDbReady() function is exported (or planned). isReady is a private, internal implementation detail used only by moltenDbResource() to know when it's safe to fetch; it is intentionally not part of the public API surface. Gate UI on the database instead through the moltenDbResource() signals shown above:

import {Component} from '@angular/core';
import {moltenDbResource} from '@moltendb-web/angular';

@Component({
  selector: 'app-greetings',
  template: `
    @if (greetings.isLoading()) {
      <p>⚙ Loading…</p>
    }
    @if (greetings.value(); as list) {
      <ul>
        @for (item of list; track item._key) {
          <li>{{ item.text }}</li>
        }
      </ul>
    }
    @if (greetings.error()) {
      <p class="error">{{ greetings.error().message }}</p>
    }
  `
})
export class GreetingsComponent {
  greetings = moltenDbResource('greetings', (col) => col.get().exec());
}

On the server this renders the "no data yet" branch immediately (nothing matches isLoading()/error(), and value() is undefined since no initialValue was passed here); in the browser it behaves exactly as before, becoming reactive once MoltenDb boots.

Because of this, excluding MoltenDb-using routes from prerendering (e.g. RenderMode.Client in app.routes.server.ts) is now a recommendation for a better first paint, not a requirement to avoid a build/SSR crash.

Notes

  • provideMoltenDb() uses Angular's APP_INITIALIZER to block app bootstrap until the database is ready — no need to check a readiness flag in most components. This only applies in the browser; on the server MoltenDb is never booted, so there is nothing to wait for.
  • Multiple apps using the same name will share the same underlying OPFS storage and sync across tabs via the built-in leader/follower mechanism.
  • moltenDbResource re-fetches automatically when the bound collection is mutated by any tab — no manual refresh needed.