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

@flexstore/core

v0.3.9

Published

FlexStore sync engine — SQLite WASM local store, protocol transport, background scheduler

Readme

@flexstore/core

Framework-agnostic FlexStore sync engine for browsers.

Repository: github.com/flexstoresync/flexstore-core

Data flow

flowchart TB
  subgraph App["Your UI"]
    UI[React / Vue / Angular / vanilla]
  end

  subgraph Core["@flexstore/core"]
    CLIENT[SyncClient]
    SCHED[Scheduler]
    TRANS[Transport]
    CLIENT --> SCHED
    SCHED --> TRANS
  end

  subgraph Local["Browser local store (default)"]
    IDB[(IndexedDB)]
  end

  subgraph Remote["flexstoresync server"]
    API[Sync API]
    LEDGER[(Ledger)]
    API <--> LEDGER
  end

  UI <-->|create / query / subscribe| CLIENT
  CLIENT <-->|upsert / pending / cursors| IDB
  TRANS <-->|push / pull / check-deltas| API

Write path (offline-first):

sequenceDiagram
  participant UI as App UI
  participant SC as SyncClient
  participant DB as IndexedDbStore
  participant IDB as IndexedDB
  participant API as Sync API

  UI->>SC: create / update / remove
  SC->>DB: upsert row + sync_status
  DB->>IDB: persist rows
  SC-->>UI: immediate local read

  Note over SC,API: background when online
  SC->>API: check-deltas → push → pull
  API-->>SC: remote changes
  SC->>DB: apply pulled rows
  SC-->>UI: subscribe callback

npm install @flexstore/core

Store adapters

| Adapter | Export | When | | ------- | ------ | ---- | | IndexedDB (default) | IndexedDbStore | Persistent browser storage — no WASM setup | | SQLite WASM (opt-in) | SqliteStore | SQL surface when you need it later |

import { createSyncClient, SqliteStore } from '@flexstore/core';

createSyncClient({
  ...config,
  store: new SqliteStore(['todos'], 'flexstore-sqlite'),
});

Implement FlexStoreAdapter (see index.d.ts) for React Native, Node, etc.

Local-store schema migrations (IndexedDbStore)

The default IndexedDbStore maps each ResourceDefinition to its own IndexedDB object store. Object stores can only be created during the onupgradeneeded phase, which fires only when the database version increases.

When you add a new resource to your registry on an already-installed browser:

  1. The JS bundle still knows the new resource name (the SDK passes it to IndexedDbStore).
  2. The physical IndexedDB database still has only the object stores from the previous version.
  3. indexedDB.open(name, version) against an existing DB does not fire onupgradeneeded if version is unchanged, so the new object store is never created.
  4. The first useQuery('newResource') or client.create('newResource', …) throws: DOMException: Failed to execute 'transaction' on 'IDBDatabase': one of the specified object stores was not found.

Action. Bump the IndexedDB schema version whenever the resource registry grows:

  1. App-owned (recommended): src/sync/local-schema.ts + idbSchemaVersion in sync config.
  2. Core default: DEFAULT_IDB_SCHEMA_VERSION in src/store/idb.js for reference apps.

Custom per-version hooks: pass idbMigrations: [{ version, description, up(db, ctx) }] on sync config.

// packages/core/src/store/idb.js
export const DEFAULT_IDB_SCHEMA_VERSION = 3;

The onupgradeneeded handler is idempotent — it calls createObjectStore only for stores that are missing — so existing data in older stores is preserved across the upgrade.

// src/store/idb.js
const IDB_SCHEMA_VERSION = 2; // bump when adding new resources

The SqliteStore adapter is unaffected: it uses CREATE TABLE IF NOT EXISTS, so adding a new resource just adds the table on next init.

Workaround for affected users today. Open DevTools → Application → IndexedDB → delete the flexstore-idb:<dbName> database, then reload. The next session re-creates the database with all current resources.

Quick start

import { createSyncClient, defineResource, resourceRegistry } from '@flexstore/core';

const todos = defineResource({
  name: 'todos',
  attributes: { title: 'string', done: 'boolean' },
});

const client = createSyncClient({
  baseUrl: 'http://localhost:8088',
  apiKey: 'my-key',
  tenantId: 'my-tenant',
  resources: resourceRegistry(todos),
  // store: default IndexedDbStore (persistent)
});

await client.start();
await client.create('todos', { title: 'Buy milk', done: false });
const open = await client.query('todos', { done: false });

Pub/sub + fallback polling

When pubsubUrl is set, the client subscribes to SSE sync hints for near-real-time pulls. A background check-deltas timer still runs as a safety net:

| Mode | Config | Default | | ---- | ------ | ------- | | Pub/sub connected | pubsubFallbackPollMs / pollIntervalConnectedMs | 60s | | Pub/sub down / off | pollIntervalDisconnectedMs | 4s |

createSyncClient({
  baseUrl: 'http://localhost:8088',
  pubsubUrl: 'http://localhost:8090',
  pubsubFallbackPollMs: 60_000, // or 0 for hints-only while connected
  pollIntervalDisconnectedMs: 4_000,
  // ...
});

useSyncStatus() / client.status expose pollIntervalMs (active timer) and pubsubFallbackPollMs (configured fallback).

Automatic background sync on writes

You do not need to call syncNow() after a mutation. Every create / update / remove writes locally (instant, UI never blocks) and schedules a debounced background sync. Rapid successive writes — e.g. a checkout inserting many line and payment rows — coalesce into a single sync. Scheduling respects paused / blocked, so it stays offline-first.

Tune the debounce with syncDebounceMs (default 300ms; 0 = sync immediately on each write):

createSyncClient({
  baseUrl: 'http://localhost:8088',
  syncDebounceMs: 300, // coalesce bursts of writes into one sync
  // ...
});

Reserve explicit syncNow() for user-initiated "refresh now" actions (e.g. a manual Refresh button that forces an immediate pull).

Framework wrappers

| Package | Repo | | ------- | ---- | | @flexstore/react | flexstore-react | | @flexstore/vue | flexstore-vue (composables) | | Angular | Use createSyncClient + inject a singleton service (see packages/angular in monorepo) |

Related

| Repo | Role | | ---- | ---- | | flexstore-self-host | Docker sync server | | flexstore | Monorepo + design docs |

License

MIT — see LICENSE.