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

@icedigital/aquabase

v0.7.1

Published

Ultra-fast encrypted real-time database client. Offline-first, zero codegen, cloud sync.

Readme

Aquabase

Ultra-fast offline-first database with automatic cloud sync, user auth, and file storage. Zero codegen, pure TypeScript.

Works in Node.js, Bun, Deno, and Browser.

Install

npm install aquabase

Quick Start

import { Aquabase } from "aquabase";

const app = new Aquabase({
  projectId: "your_project_id",
  apiKey: "aq_pub_...",
  // encrypted: true,          // optional: encrypt ALL collections
  // jwt: "user-jwt-token",    // optional: for auth/self rules
});

app.connect();

// Database
const users = app.collection("users");
await users.put("u1", { name: "Ana", age: 25 });
const user = await users.get("u1");

// Encrypted collection (AES-256-GCM)
const session = app.collection("_session", { local: true, encrypted: true });
await session.put("current", { jwt: token, email: "[email protected]" });

// Auth
const { uid, token } = await app.auth.register("[email protected]", "secret123");

// Storage
await app.storage.upload("avatars", "photo.jpg", file);

// Logs
app.logs.connect();
app.logs.info("db", "User created account");

How It Works

  1. All reads/writes go to the local cache — instant, no network latency
  2. When online, changes sync to the server in background (auto-batched)
  3. When offline, failed ops queue to disk automatically
  4. On reconnect, the queue flushes to the server — no data loss

Collections

const users = app.collection("users");

// Document CRUD
await users.doc("u1").set({ name: "Ana", age: 25 });
const user = await users.doc("u1").get();
users.doc("u1").delete();

// Auto-generated ID
const id = await users.add({ name: "Carlos", age: 30 });
console.log(id); // e.g. 'aB3xK9mQ7pR2wT4vN1'

// Bulk operations (single WS frame per chunk)
await users.bulkSet(
  new Map([
    ["u1", { name: "Ana" }],
    ["u2", { name: "Luis" }],
  ]),
);

const results = await users.bulkGet(["u1", "u2"]);
await users.bulkDelete(["u1", "u2"]);

Real-Time

Watch a document or a query for changes — fires immediately with current data, then on every update from any device.

const users = app.collection("users");

// Watch a single document
const unsub = users.watch("u1", (user) => {
  if (user) renderUser(user);
});

// Watch a query
const unsub2 = users.where("status", { eq: "active" }).watch((results) => {
  for (const [id, user] of results) {
    console.log(id, user);
  }
});

// Stop watching
unsub();
unsub2();

Server Fallback

// Read from cache, fallback to server if not cached
const data = await users.getOrFetch("u1");

Local Collections

Same API as any collection, but data stays on the device — no server sync.

const students = app.collection("_students", { local: true });

// Same API as any collection
await students.put("s1", { name: "Ana", age: 12, grade: "6A" });
const student = await students.get("s1");
students.delete("s1");

await students.bulkSet(
  new Map([
    ["s1", { name: "Ana", age: 12 }],
    ["s2", { name: "Luis", age: 13 }],
  ]),
);

A collection name cannot be both local and remote simultaneously.

Encryption

AES-256-GCM encryption, two modes:

// Per-collection: only sensitive collections are encrypted
const session = app.collection("_session", { local: true, encrypted: true });
await session.put("current", { jwt: token, email: "[email protected]" });

// Global: ALL collections are encrypted
const app = new Aquabase({
  projectId: "your_project_id",
  apiKey: "aq_pub_...",
  encrypted: true,
});

When encrypted: true is set globally, every collection is encrypted automatically — no need to set encrypted on each one.

Date Handling

DateTime/Date objects are automatically serialized as milliseconds since epoch (UTC). On read, timestamps are returned as number — convert in your app:

// Write
await users.put("u1", { name: "Ana", createdAt: new Date() });

// Read
const user = await users.get<{ name: string; createdAt: number }>("u1");
const date = new Date(user.createdAt);

This format is cross-platform compatible with the Flutter SDK.

Indexes

Indexes are automatic. The first time you use .where('field', ...), the SDK registers that field as indexed and syncs it to the server. From that point on, every put() auto-generates tags for queried fields. No manual configuration needed.

Note: Existing documents need a re-write (put()) to become queryable by a newly indexed field.

Equality

const attendances = app.collection("attendances");

// .where() auto-registers 'date' and 'type' as indexed fields
const results = await attendances
  .where("date", { eq: "2026-03-18" })
  .where("type", { eq: "present" })
  .get();

// Real-time query
const unsub = attendances
  .where("date", { eq: "2026-03-18" })
  .watch((results) => {
    for (const [id, data] of results) {
      console.log(id, data);
    }
  });
unsub();

Range Queries

Use from/to for inclusive ranges (most common). The SDK automatically selects the BTreeMap index.

const scores = app.collection("scores");

// Scores between 80 and 100
const high = await scores
  .where("score", { from: 80, to: 100 })
  .get();

// March attendances for a student
const march = await attendances
  .where("date", { from: "2026-03-01", to: "2026-03-31" })
  .where("studentId", { eq: "stu_001" })
  .get();

// Exclusive comparisons
const above80 = await scores
  .where("score", { gt: 80 })
  .get();

All available operators:

| Operator | Type | Description | |----------|------|-------------| | eq | any | Exact equality | | from | num / string | Range start (≥ inclusive) | | to | num / string | Range end (≤ inclusive) | | gt | num / string | Greater than (> exclusive) | | lt | num / string | Less than (< exclusive) |

Works with dates (ISO 8601), numbers, and strings. Numbers use IEEE 754 big-endian encoding, so -5 < 0 < 100 is always correct.

Ordering

Use orderBy() to sort results client-side. Works with any numeric or string field.

// Scores highest first
const top = await scores
  .where("classId", { eq: "math_101" })
  .orderBy("score", { descending: true })
  .get();

// Attendances sorted by date
const sorted = await attendances
  .where("studentId", { eq: "stu_001" })
  .orderBy("date")
  .get();

Historical data: Existing documents written before a field was registered as range-indexed are not automatically re-indexed. Call put() again on those documents to make them appear in range queries.

Custom Server URL

const app = new Aquabase({
  projectId: "your_project_id",
  apiKey: "aq_pub_...",
  url: "http://localhost:3280",
});

Auth

// Email/password
const { uid, token } = await app.auth.register("[email protected]", "secret123");
const result = await app.auth.login("[email protected]", "secret123");

// OAuth (Google, GitHub)
const google = await app.auth.signInWithOAuth("google", googleIdToken);
const github = await app.auth.signInWithOAuth("github", githubCode);

// Connect with user identity
const userApp = new Aquabase({
  projectId: "...",
  apiKey: "aq_pub_...",
  jwt: result.token,
});
userApp.connect();

Storage

await app.storage.createBucket({ name: "avatars", public: true });
await app.storage.upload("avatars", "photo.jpg", file);

const blob = await app.storage.download("avatars", "photo.jpg");
const files = await app.storage.listFiles("avatars");
await app.storage.deleteFile("avatars", "photo.jpg");

Logs

Structured log collection with auto-batching.

app.logs.connect();

app.logs.info("db", "User created account");
app.logs.warn("auth", "Invalid password attempt", "192.168.1.1");
app.logs.error("ws", "Connection timeout");

const records = await app.logs.query({
  since: Date.now() * 1_000_000 - 3600e9,
  minLevel: LogLevel.Warn,
  channel: "auth",
  limit: 100,
});

app.logs.close();

API Reference

Constructor Options

| Option | Type | Default | Description | | ----------- | ---------------- | -------------- | ---------------------------------------- | | projectId | string | — | Project ID for cache identity | | apiKey | string | — | API key for the server | | url | string | Aquabase cloud | Server URL | | jwt | string | — | JWT for authenticated users | | encrypted | boolean | false | Encrypt ALL collections (AES-256-GCM) |

Aquabase

| Method | Description | | ------------------------------ | --------------------------------------------- | | collection(name) | Get a collection (synced) | | collection(name, {local: true}) | Get a local-only collection | | collection(name, {encrypted: true}) | Get an encrypted collection (AES-256-GCM) | | connect() | Connect to server | | isConnected | Server connection status | | pendingOps | Queued offline ops count | | close() | Close connection + release resources |

Collection

| Method | Description | | ------------------- | ------------------------------------------------- | | doc(id) | Document reference → .set() .get() .delete() | | add(obj) | Write with auto-generated ID, returns the ID | | bulkSet(docs) | Batch write | | bulkGet(ids) | Batch read | | bulkDelete(ids) | Batch delete | | where(f, ...) | Query Builder → .orderBy() .get() .watch() | | watch(id, cb) | Real-time doc watch. Returns unsubscribe |

Auth

| Method | Description | | ------------------------------------------ | ------------------------------------------- | | app.auth.register(email, password) | Register user, returns JWT + uid | | app.auth.login(email, password) | Login user, returns JWT + uid | | app.auth.signInWithOAuth(provider, cred) | OAuth login (Google/GitHub), auto-registers | | app.auth.refresh(token) | Refresh an expired JWT |

Storage

| Method | Description | | ----------------------------------------------- | ------------------------- | | app.storage.listBuckets() | List all buckets | | app.storage.createBucket(opts) | Create a bucket | | app.storage.deleteBucket(name) | Delete bucket + all files | | app.storage.upload(bucket, path, data, type?) | Upload/overwrite a file | | app.storage.download(bucket, path) | Download as Blob | | app.storage.deleteFile(bucket, path) | Delete a file |

Logs

| Method | Description | | ----------------------------------- | --------------------------------------------- | | app.logs.connect() | Connect to server (auto-called on first push) | | app.logs.info(channel, msg, src) | Push info-level log | | app.logs.warn(channel, msg, src) | Push warn-level log | | app.logs.error(channel, msg, src) | Push error-level log | | app.logs.flush() | Flush buffer to server | | app.logs.query(opts?) | Query stored logs | | app.logs.close() | Disconnect + release resources |

Architecture

| Component | Implementation | | ------------- | --------------------------------------------------- | | Serialization | MsgPack (msgpackr) — Date → ms epoch auto-conversion | | L1 Cache | SIEVE eviction (NSDI '24) | | L2 Storage | CompactCache — snapshot reads + append-only disk | | Cloud Sync | WebSocket + OP_PIPELINE auto-batching | | Offline Queue | Persisted to disk, auto-flush on reconnect |

License

Proprietary