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

@next-model/local-storage-connector

v1.2.1

Published

Browser localStorage connector for next-model. Inherits from MemoryConnector.

Downloads

293

Readme

@next-model/local-storage-connector

Browser localStorage connector for @next-model/core.

LocalStorageConnector extends MemoryConnector, so every behaviour you get from the in-memory store (filter operators, ordering, aggregates, transactions with rollback, schema DSL, JS-expression execute) carries over verbatim. The only differences are persistence and id management.

Installation

pnpm add @next-model/core @next-model/local-storage-connector
# or: npm install @next-model/core @next-model/local-storage-connector

@next-model/core is declared as a peerDependency — install it alongside the connector so pnpm/npm materialise it into your app's node_modules.

Constructing the connector

import { LocalStorageConnector } from '@next-model/local-storage-connector';

// Browser: picks up globalThis.localStorage automatically
const connector = new LocalStorageConnector();

// With an injected store (Node tests, sandboxed environments)
import { MemoryLocalStorage } from '@next-model/local-storage-connector/dist/__mocks__/MemoryLocalStorage.js';
const connector = new LocalStorageConnector({ localStorage: new MemoryLocalStorage() });

// Namespacing keys
const connector = new LocalStorageConnector({
  prefix: 'app:',
  suffix: ':v2',
});

When neither localStorage (option) nor globalThis.localStorage is available, the constructor throws — letting you fail fast in environments where the Web Storage API is not present.

Attaching a typed schema

Pass a DatabaseSchema (from @next-model/core's defineSchema(...)) so Model({ connector, tableName: 'users' }) can infer per-table props at the type level. Both call shapes work:

import { defineSchema } from '@next-model/core';

const schema = defineSchema({
  users: { columns: { id: { type: 'integer', primary: true }, email: { type: 'string' } } },
});

// Single-arg form (preferred):
const connector = new LocalStorageConnector({ prefix: 'app:', schema });

// Legacy two-arg form (still supported):
const connector = new LocalStorageConnector({ prefix: 'app:' }, { schema });

Existing call sites without a schema keep working unchanged.

Materialising tables with ensureSchema()

Inherited from MemoryConnector: when a schema is attached, connector.ensureSchema() iterates every declared table and initialises it idempotently. Returns { created, existing }. Call once on app boot:

const connector = new LocalStorageConnector({ prefix: 'app:' }, { schema });
const { created } = await connector.ensureSchema();

Wiring a Model

import { Model } from '@next-model/core';
import { LocalStorageConnector } from '@next-model/local-storage-connector';

const connector = new LocalStorageConnector();

class Note extends Model({
  tableName: 'notes',
  connector,
  init: (props: { title: string; body: string }) => props,
}) {}

await Note.create({ title: 'Hello', body: 'world' });
await Note.count();           // 1

Feature → connector specifics

Storage layout

Each table is stored under ${prefix}${tableName}${suffix} as a JSON-serialised array. A sidecar key ${prefix}${tableName}${suffix}__nextId tracks the next auto-increment id so deleted rows never have their id reused.

Reads load the table on first access of a request; writes serialise back after every mutation. There is no in-memory cache, so swapping localStorage between tabs (e.g. browser sync) is observed on the next call.

Filter operators, aggregates, ordering

Inherited from MemoryConnector. All operators ($and, $or, $not, $in, $notIn, $null, $notNull, $between, $notBetween, $gt/$gte/$lt/$lte, $like, $async, $raw) and the same ordering / limit / skip semantics apply. $like uses the MemoryConnector's pattern matcher, not SQL LIKE.

execute(query, bindings)

Inherited from MemoryConnector: query is a function (storage, ...bindings) => any[] that walks the in-memory storage map directly. The JS-source-string form that older versions accepted has been removed — passing a string now throws UnsupportedOperationError with a migration hint. SQL connectors keep accepting SQL strings.

Transactions

Inherited from MemoryConnector: transaction(fn) snapshots storage and lastIds (via structuredClone), runs fn, and either commits in place or rolls back to the snapshot on throw. The whole localStorage tree is not synchronised across tabs during the transaction, so concurrent writers from another tab can race a rollback.

batchInsert

Each row is pushed to the in-memory table array, then the table is serialised back to localStorage. Auto-increment ids come from the per-table __nextId counter (incremented even after deletes).

Schema DSL

createTable(name, blueprint) initialises an empty table array; dropTable(name) removes both the data array and the __nextId counter; hasTable(name) checks whether the data key exists. Column types declared in the blueprint are validated up-front via defineTable but not enforced at runtime — localStorage has no schema layer.

Changelog

See HISTORY.md.