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

mpb-localkit

v1.4.18

Published

Type-driven, offline-first SDK for TypeScript developers

Readme

MPB LocalKit

Type-driven, offline-first SDK for TypeScript developers. Define your data schema once — get local storage, sync, auth, and error tracking out of the box.

Quick Start

npm install mpb-localkit
import { createApp, collection, z } from 'mpb-localkit'

const app = createApp({
  collections: {
    brews: collection(z.object({
      brewDate: z.date(),
      style: z.string(),
      og: z.number(),
      fg: z.number().optional(),
      notes: z.string().optional(),
    })),
  },
})

// Fully typed CRUD — works offline immediately
const brew = await app.brews.create({ brewDate: new Date(), style: 'IPA', og: 1.065 })
const allBrews = await app.brews.findMany()
await app.brews.update(brew._id, { fg: 1.012 })
await app.brews.delete(brew._id)

API Reference

createApp(config)

Creates a typed app instance with CRUD methods for each collection.

const app = createApp({
  collections: { ... },      // Required: collection definitions
  sync: {                    // Optional: sync configuration
    endpoint: 'https://...',
    interval: 30000,
    enabled: true,
  },
  errorTracking: {           // Optional: error tracking config
    enabled: true,
    snapshot: true,
    maxLocalErrors: 100,
  },
})

collection(schema)

Wraps a Zod schema into a typed collection descriptor. TypeScript infers the document type automatically.

const todos = collection(z.object({
  text: z.string(),
  done: z.boolean().default(false),
}))
// todos._inferredType is { text: string; done: boolean }

z

Re-exported from Zod. Use it to define your schemas.

Collection CRUD

Every collection in app.collections gets these methods:

// Create — returns the document with metadata fields
const doc = await app.todos.create({ text: 'Buy hops', done: false })
// doc._id, doc._collection, doc._updatedAt, doc._deleted are added automatically

// Read
const all = await app.todos.findMany()
const one = await app.todos.findOne(doc._id)

// Update — merges partial data
await app.todos.update(doc._id, { done: true })

// Delete — soft delete (sets _deleted: true for sync propagation)
await app.todos.delete(doc._id)

Auth

// Sign up
await app.auth.signUp({ email: '[email protected]', password: 'secret' })

// Sign in
await app.auth.signIn({ email: '[email protected]', password: 'secret' })

// Sign out
await app.auth.signOut()

// Current user
const user = app.auth.currentUser() // { id, email } | null

Passwords are hashed client-side (PBKDF2) before transmission. Sessions are JWTs cached locally for offline access.

Sync

Sync happens automatically when configured with an endpoint. LocalKit uses a Last-Write-Wins (LWW) protocol — the document with the highest _updatedAt timestamp wins conflicts.

// Manual sync
await app.sync()

Sync triggers automatically on:

  • Tab focus (returning to the app)
  • Network reconnect (coming back online)
  • Periodic interval (configurable, default 30s)

All reads and writes hit local storage immediately — sync never blocks the UI.

React Hooks

import { useCollection, useAuth, useSync } from 'mpb-localkit/react'
import { app } from './schema'

function BrewList() {
  const { data: brews, isLoading } = useCollection(app.brews)
  const { user, signIn, signOut } = useAuth(app)
  const { status, lastSyncAt } = useSync(app)

  if (isLoading) return <div>Loading...</div>

  return (
    <ul>
      {brews.map(brew => <li key={brew._id}>{brew.style}</li>)}
    </ul>
  )
}

CLI

# Start local dev (SDK works without a Worker)
npx mpb-localkit dev

# Generate Cloudflare Worker from your schema
npx mpb-localkit build --name my-app

# Build + deploy to Cloudflare
npx mpb-localkit deploy

The generated Worker handles auth, sync, and error storage via Cloudflare R2 + KV.

Architecture

Your App (React/Vue/Svelte)
        ↓
  Framework Bindings
  (useCollection, useAuth)
        ↓
     Core SDK
  ┌──────────────────────────────┐
  │  Schema  │  Sync  │  Auth   │
  │  Engine  │ Engine │ Module  │
  ├──────────────────────────────┤
  │      Local Storage           │
  │    (IndexedDB, via idb)      │
  └──────────────────────────────┘
        ↓ (when online)
  Cloudflare Worker (generated)
  ┌──────────────────────────────┐
  │  Auth   │  Sync  │  Errors  │
  │ Routes  │ Routes │  Routes  │
  ├──────────────────────────────┤
  │    Cloudflare R2 + KV        │
  └──────────────────────────────┘

All writes hit IndexedDB first — your app works fully offline. The sync engine pushes/pulls changes to the Cloudflare Worker when connectivity is available.

Document Shape

Every document has these metadata fields added automatically:

{
  _id: string          // UUIDv7 — sortable by creation time
  _collection: string  // Collection name
  _updatedAt: number   // Unix ms — the sync cursor
  _deleted: boolean    // Soft delete for sync propagation
  // ...your fields
}

Create a New App

The fastest way to start is with the scaffolder:

npm create mpb-localkit@latest
# or
pnpm create mpb-localkit@latest

It prompts for project name, framework, auth method, sync transport, and deploy target — then scaffolds a ready-to-run project and installs dependencies.

? Project name: my-app
? Framework: React
? Auth method: Email + Password
? Sync transport: Auto (WebSocket with HTTP fallback)
? Deploy target: Cloudflare Workers

Done! Your project is ready.
  cd my-app
  pnpm run dev

Transport Configuration

LocalKit supports three sync transports, configurable at createApp time:

HTTP Transport (default)

Polling-based sync. Works everywhere, no persistent connection required.

const app = createApp({
  collections: { ... },
  sync: {
    transport: 'http',
    endpoint: 'https://your-worker.workers.dev',
    interval: 30000,
  },
})

WebSocket Transport

Persistent connection for real-time sync. The server pushes changes as they happen.

const app = createApp({
  collections: { ... },
  sync: {
    transport: 'websocket',
    endpoint: 'wss://your-worker.workers.dev',
  },
})

Auto Transport (recommended)

Starts a WebSocket connection and falls back to HTTP polling if WebSocket is unavailable. Best of both worlds.

const app = createApp({
  collections: { ... },
  sync: {
    transport: 'auto',
    endpoint: 'https://your-worker.workers.dev',
  },
})

With auto, LocalKit upgrades to WebSocket when the server supports it and degrades gracefully on restricted networks.

WebSocket Sync

When using transport: 'websocket' or transport: 'auto', LocalKit maintains a persistent WebSocket connection to your sync Worker. The Worker sends change events as they occur — no polling delay.

import { createApp, collection, z } from 'mpb-localkit'

const app = createApp({
  collections: {
    messages: collection(z.object({
      text: z.string(),
      author: z.string(),
    })),
  },
  sync: {
    transport: 'auto',
    endpoint: 'https://chat.your-subdomain.workers.dev',
  },
})

// Writes go to IndexedDB immediately (no await on network)
await app.messages.create({ text: 'Hello!', author: 'alice' })

// The Worker broadcasts the change to all connected clients via WebSocket
// Recipients see the new message arrive without polling

The WebSocket connection is managed automatically:

  • Reconnects with exponential backoff on disconnect
  • Replays missed changes on reconnect using the LWW cursor
  • Falls back to HTTP polling when WebSocket is unavailable (Auto mode)

Better Auth Integration

LocalKit integrates with Better Auth for full-featured server-side authentication.

Setup

import { createApp, collection, z } from 'mpb-localkit'

const app = createApp({
  collections: {
    notes: collection(z.object({ text: z.string() })),
  },
  auth: {
    type: 'better-auth',
    baseURL: 'https://auth.your-domain.com',
  },
  sync: {
    transport: 'auto',
    endpoint: 'https://sync.your-domain.com',
  },
})

Session handling

Better Auth sessions are cached locally so the app stays functional offline. The session is re-validated against the server on reconnect.

// Sign in — stores session locally for offline access
await app.auth.signIn({ email: '[email protected]', password: 'secret' })

// Current user is available immediately (from local cache)
const user = app.auth.currentUser() // { id, email, name } | null

// Sync automatically includes the session token in requests
await app.sync()

// Sign out clears the local session
await app.auth.signOut()

React hooks with Better Auth

import { useAuth, useCollection } from 'mpb-localkit/react'
import { app } from './schema'

function App() {
  const { user, signIn, signOut, isLoading } = useAuth(app)
  const { data: notes } = useCollection(app.notes)

  if (isLoading) return <div>Loading...</div>
  if (!user) return <SignInForm onSignIn={signIn} />

  return (
    <div>
      <p>Signed in as {user.email}</p>
      <ul>{notes.map(n => <li key={n._id}>{n.text}</li>)}</ul>
      <button onClick={signOut}>Sign out</button>
    </div>
  )
}

License

MIT