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

ai-database

v2.0.2

Published

AI-powered database interface primitives with mdxld conventions

Readme

ai-database

Your data, flowing like conversation.

import { DB } from 'ai-database'

const { db } = DB({
  Lead: { name: 'string', company: 'Company.leads' },
  Company: { name: 'string' }
})

// Chain without await
const leads = db.Lead.list()
const qualified = await leads.filter(l => l.score > 80)

// Batch relationship loading
const enriched = await leads.map(lead => ({
  name: lead.name,
  company: lead.company,  // Batch loaded!
}))

Promise Pipelining

Chain database operations without await:

const leads = db.Lead.list()
const topLeads = leads.filter(l => l.score > 80)
const names = topLeads.map(l => l.name)

// Only await when you need the result
const result = await names

Batch Relationship Loading

Eliminate N+1 queries automatically:

// Old way - N+1 queries
const leads = await db.Lead.list()
for (const lead of leads) {
  const company = await db.Company.get(lead.companyId)  // N queries!
}

// New way - batch loaded
const enriched = await db.Lead.list().map(lead => ({
  lead,
  company: lead.company,  // All companies loaded in ONE query
}))

Natural Language Queries

Ask your database questions:

const results = await db.Lead`who closed deals this month?`
const pending = await db.Order`what's stuck in processing?`

Real-World Examples

Sales Pipeline

const { db } = DB({
  Lead: {
    name: 'string',
    email: 'string',
    score: 'number',
    company: 'Company.leads',
  },
  Company: {
    name: 'string',
    industry: 'string',
  }
})

// Find high-value leads with their companies
const qualified = await db.Lead.list()
  .filter(lead => lead.score > 80)
  .map(lead => ({
    lead,
    company: lead.company,
  }))

// Ask questions naturally
const results = await db.Lead`who hasn't responded in 2 weeks?`

Customer Success

const { db } = DB({
  Customer: {
    name: 'string',
    healthScore: 'number',
    mrr: 'number',
    csm: 'User.customers',
  },
  User: { name: 'string' }
})

// At-risk customers with their CSMs
const atRisk = await db.Customer.list()
  .filter(c => c.healthScore < 50)
  .map(c => ({
    customer: c,
    csm: c.csm,
    mrr: c.mrr,
  }))

Order Management

const { db } = DB({
  Order: {
    status: 'string',
    total: 'number',
    customer: 'Customer.orders',
    items: ['OrderItem.order'],
  },
  OrderItem: { product: 'string', quantity: 'number' },
  Customer: { name: 'string' }
})

// Pending orders with all details
const pending = await db.Order
  .find({ status: 'pending' })
  .map(order => ({
    order,
    customer: order.customer,
    items: order.items,
  }))

Schema Definition

Define once, get typed operations everywhere:

const { db, events, actions, nouns, verbs } = DB({
  Post: {
    title: 'string',
    content: 'markdown',
    author: 'Author.posts',  // Creates both directions
  },
  Author: {
    name: 'string',
    // posts: Post[] auto-created from backref
  }
})

Field Types

| Type | Description | |------|-------------| | string | Text | | number | Numeric | | boolean | True/false | | date | Date only | | datetime | Date and time | | markdown | Rich text | | json | Structured data | | url | URL string |

Relationships

// One-to-many: Post has one Author, Author has many Posts
Post: { author: 'Author.posts' }

// Many-to-many: Post has many Tags, Tag has many Posts
Post: { tags: ['Tag.posts'] }

CRUD Operations

All operations return DBPromise for chaining:

// Read
const lead = await db.Lead.get('lead-123')
const leads = await db.Lead.list()
const first = await db.Lead.first()
const found = await db.Lead.find({ status: 'active' })

// Search
const results = await db.Lead.search('enterprise SaaS')

// Write (returns regular Promise)
const lead = await db.Lead.create({ name: 'Acme Corp' })
await db.Lead.update(lead.$id, { score: 90 })
await db.Lead.delete(lead.$id)

Chainable Methods

db.Lead.list()
  .filter(l => l.score > 50)
  .sort((a, b) => b.score - a.score)
  .limit(10)
  .map(l => ({ name: l.name, score: l.score }))

Events

React to changes in real-time:

events.on('Lead.created', event => {
  notifySlack(`New lead: ${event.data.name}`)
})

events.on('*.updated', event => {
  logChange(event)
})

forEach - Large-Scale Processing

Process thousands of items with concurrency, progress tracking, and error handling:

// Simple iteration
await db.Lead.forEach(lead => {
  console.log(lead.name)
})

// With AI and concurrency
const result = await db.Lead.forEach(async lead => {
  const analysis = await ai`analyze ${lead}`
  await db.Lead.update(lead.$id, { analysis })
}, {
  concurrency: 10,
  onProgress: p => console.log(`${p.completed}/${p.total} (${p.rate.toFixed(1)}/s)`),
})

// With error handling and retries
await db.Order.forEach(async order => {
  await sendInvoice(order)
}, {
  concurrency: 5,
  maxRetries: 3,
  retryDelay: attempt => 1000 * Math.pow(2, attempt),  // Exponential backoff
  onError: (err, order) => err.code === 'RATE_LIMIT' ? 'retry' : 'continue',
})

console.log(`Completed: ${result.completed}, Failed: ${result.failed}`)

forEach Options

| Option | Type | Description | |--------|------|-------------| | concurrency | number | Max parallel operations (default: 1) | | maxRetries | number | Retries per item (default: 0) | | retryDelay | number \| fn | Delay between retries | | onProgress | fn | Progress callback | | onError | 'continue' \| 'retry' \| 'skip' \| 'stop' \| fn | Error handling | | timeout | number | Timeout per item in ms | | persist | boolean \| string | Enable durability (string = custom action name) | | resume | string | Resume from action ID |

Durable forEach

Persist progress to survive crashes:

// Enable persistence - auto-names action as "Lead.forEach"
const result = await db.Lead.forEach(processLead, {
  concurrency: 10,
  persist: true,
})

console.log(`Action ID: ${result.actionId}`)

Custom action name:

await db.Lead.forEach(processLead, {
  persist: 'analyze-leads',  // Custom action name
})

Resume after a crash:

await db.Lead.forEach(processLead, {
  resume: 'action-123',  // Skips already-processed items
})

Actions

Track long-running operations:

const action = await actions.create({
  type: 'import-leads',
  data: { file: 'leads.csv' },
  total: 1000,
})

await actions.update(action.id, { progress: 500 })
await actions.update(action.id, { status: 'completed' })

Installation

pnpm add ai-database

Configuration

DATABASE_URL=./content         # filesystem (default)
DATABASE_URL=sqlite://./data   # SQLite
DATABASE_URL=:memory:          # in-memory

Documentation

Document Database Interface

In addition to the schema-first graph model, ai-database also exports environment-agnostic types for document-based storage (MDX files with frontmatter). These types are used by @mdxdb/* adapters and work in any JavaScript runtime (Node.js, Bun, Deno, Workers, Browser).

import type {
  DocumentDatabase,
  DocListOptions,
  DocSearchOptions,
  Document,
} from 'ai-database'

// The DocumentDatabase interface
interface DocumentDatabase<TData> {
  list(options?: DocListOptions): Promise<DocListResult<TData>>
  search(options: DocSearchOptions): Promise<DocSearchResult<TData>>
  get(id: string, options?: DocGetOptions): Promise<Document<TData> | null>
  set(id: string, doc: Document<TData>, options?: DocSetOptions): Promise<DocSetResult>
  delete(id: string, options?: DocDeleteOptions): Promise<DocDeleteResult>
  close?(): Promise<void>
}

Document Types

| Type | Description | |------|-------------| | Document<TData> | MDX document with id, type, context, data, and content | | DocumentDatabase<TData> | Interface for document storage adapters | | DocListOptions | Options for listing documents (limit, offset, sortBy, type, prefix) | | DocListResult<TData> | List result with documents, total, hasMore | | DocSearchOptions | Search options (query, fields, semantic) | | DocSearchResult<TData> | Search result with scores | | DocGetOptions | Get options (includeAst, includeCode) | | DocSetOptions | Set options (createOnly, updateOnly, version) | | DocSetResult | Set result (id, version, created) | | DocDeleteOptions | Delete options (soft, version) | | DocDeleteResult | Delete result (id, deleted) |

View Types

For bi-directional relationship rendering:

| Type | Description | |------|-------------| | ViewManager | Interface for managing views | | ViewDocument | View template definition | | ViewContext | Context for rendering a view | | ViewRenderResult | Rendered markdown and entities | | ViewSyncResult | Mutations from extracting edited markdown | | DocumentDatabaseWithViews | Database with view support |

Usage with @mdxdb adapters

// Filesystem adapter
import { createFsDatabase } from '@mdxdb/fs'
const db = createFsDatabase({ root: './content' })

// API adapter
import { createApiDatabase } from '@mdxdb/api'
const db = createApiDatabase({ baseUrl: 'https://api.example.com' })

// SQLite adapter
import { createSqliteDatabase } from '@mdxdb/sqlite'
const db = createSqliteDatabase({ path: './data.db' })

// Same DocumentDatabase interface regardless of backend
const doc = await db.get('posts/hello-world')
await db.set('posts/new', { data: { title: 'New Post' }, content: '# Hello' })

Related