JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 48
  • Score
    100M100P100Q84214F
  • License MIT

Framework-agnostic client-side database with localStorage, sessionStorage, and IndexedDB. Create databases, tables, save/fetch records, and run JSON queries.

Package Exports

  • @storion/storion

Readme

Storion

Framework-agnostic client-side database for the browser. Use it with React, Vue, Angular, Svelte, or vanilla JS. Create databases, tables, save and fetch records, and run JSON queries on localStorage, sessionStorage, or IndexedDB.

npm GitHub


Table of contents


Features

Feature Description
Framework-agnostic Works with any frontend; no framework-specific code.
Multiple stores Use localStorage, sessionStorage, or indexedDB.
Tables & schema Define tables with columns (int, float, boolean, string, json) and optional foreign keys.
CRUD Insert, fetch, update, and delete records with a simple API.
Query engine Run JSON queries: where, orderBy, limit, offset.
Config from file/URL Create a database from a config object or load config from a URL or file.
Change subscription Subscribe to table or row changes so components stay in sync without polling.
Cross-context Optional broadcaster + listener for extensions, background scripts, or multiple tabs.

Install

npm install @storion/storion

Quick start

import { createDatabase } from '@storion/storion';

// Create a database (localStorage, sessionStorage, or indexedDB)
const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage'
});

// Create a table
await db.createTable('users', [
  { name: 'id', type: 'int' },
  { name: 'email', type: 'string' },
  { name: 'name', type: 'string' },
  { name: 'active', type: 'boolean' }
]);

// Insert rows
await db.insert('users', { email: 'alice@example.com', name: 'Alice', active: true });
await db.insert('users', { email: 'bob@example.com', name: 'Bob', active: false });

// Fetch all or with options
const all = await db.fetch('users');
const active = await db.fetch('users', { filter: { active: true }, sortBy: 'name', limit: 10 });

// Run a query (where, orderBy, limit, offset)
const { rows, totalCount } = await db.query('users', {
  where: { field: 'active', op: 'eq', value: true },
  orderBy: [{ field: 'name', direction: 'asc' }],
  limit: 20,
  offset: 0
});

// Update and delete
await db.update('users', 1, { name: 'Alice Smith' });
await db.delete('users', 2);

Documentation

Documentation: https://storionjs.github.io/storion-docs/

Document Description
API reference Full API for createDatabase, Database methods, and helpers.
Config format How to define tables and databases in a config object or JSON file.
Query language where, orderBy, operators, and examples.

Database from config

You can create a database and its tables from a config object (e.g. from a JSON file).

Config in code

import { createDatabase } from '@storion/storion';

const config = {
  tables: {
    users: {
      columns: [
        { name: 'id', type: 'int' },
        { name: 'email', type: 'string' },
        { name: 'active', type: 'boolean' }
      ]
    },
    posts: {
      columns: [
        { name: 'id', type: 'int' },
        { name: 'title', type: 'string' },
        { name: 'user_id', type: 'int', references: { table: 'users', column: 'id' } }
      ]
    }
  }
};

const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage',
  config
});

Load config from URL

import { createDatabase, loadConfigFromUrl } from '@storion/storion';

const config = await loadConfigFromUrl('/config/db.json');
const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage',
  config
});

Load config from file (e.g. file input)

import { createDatabase, loadConfigFromFile } from '@storion/storion';

// <input type="file" id="configFile" accept=".json" />
const file = document.getElementById('configFile').files[0];
const config = await loadConfigFromFile(file);
const db = await createDatabase({
  name: 'imported',
  storage: 'localStorage',
  config
});

See Config format for the full schema and options.


Query language

Use db.query(tableName, query) with a JSON query object:

Key Description
where Filter: { field, op, value } or { and: [...] } / { or: [...] }.
orderBy Sort: [{ field, direction: 'asc' | 'desc' }].
limit / offset Pagination.

Operators: eq, ne, gt, gte, lt, lte, contains, startsWith, endsWith, in, notIn, isNull, isNotNull.

const { rows, totalCount } = await db.query('users', {
  where: {
    and: [
      { field: 'status', op: 'eq', value: 'active' },
      { field: 'name', op: 'contains', value: 'smith' }
    ]
  },
  orderBy: [{ field: 'created_at', direction: 'desc' }],
  limit: 10,
  offset: 0
});

Full reference: Query language.


Subscribing to changes

When multiple components share the same Database instance, they can subscribe to change events so that when one component inserts, updates, or deletes data, the others receive an event and can refresh—without polling.

const db = await createDatabase({ name: 'myapp', storage: 'localStorage' });

// Subscribe to all changes in a table
const unsubscribe = db.subscribe('todos', (event) => {
  console.log(event.type, event.row); // 'insert' | 'update' | 'delete' | 'tableCreated' | 'tableDeleted'
  // Refresh your UI or state here
});

// Subscribe to all tables:  db.subscribe((event) => { ... })
// Subscribe to one row:     db.subscribe('todos', 1, (event) => { ... })

// When done:
unsubscribe();

See API — db.subscribe for details.


Cross-context sync (e.g. extensions)

Use Storion in one context (e.g. Chrome extension or background script) and stream change events to another (e.g. web app UI) with a broadcaster + listener.

import { createDatabase, createChangeListener } from '@storion/storion';

// 1) Producer (e.g. extension popup/background)
const db = await createDatabase({ name: 'myapp', storage: 'localStorage' });

db.setChangeBroadcaster({
  async broadcastChange(event) {
    if (typeof chrome !== 'undefined' && chrome.tabs?.query) {
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        const tab = tabs?.[0];
        if (tab?.id) chrome.tabs.sendMessage(tab.id, { action: 'storionChangeEvent', event });
      });
    } else {
      window.postMessage({ source: 'storion-change', payload: event }, '*');
    }
  }
});

// 2) Consumer (e.g. web app or another tab)
const transport = {
  onMessage(handler) {
    const listener = (ev) => {
      if (ev.data?.source !== 'storion-change') return;
      handler(ev.data.payload);
    };
    window.addEventListener('message', listener);
    return () => window.removeEventListener('message', listener);
  }
};

const stop = createChangeListener(transport, (event) => {
  console.log('Cross-context change:', event.type, event.tableName, event.row);
});
// Later: stop();

See API — createChangeListener and API — setChangeBroadcaster for details.


Storage backends

Backend Description
localStorage Persists across sessions; same origin; ~5MB typical.
sessionStorage Cleared when the tab/window closes; same origin.
indexedDB Async; larger quota; good for bigger datasets.

All data for a given storage key is stored in one place (default key: __LS_DB__). Multiple logical databases (different names) can coexist under the same key.


Usage with React / Vue / Angular

Use the same API in any framework. Share one Database instance (e.g. via context, service, or singleton) so db.subscribe() keeps all components in sync.

Example with React:

import { createDatabase } from '@storion/storion';
import { useEffect, useState } from 'react';

function UserList() {
  const [db, setDb] = useState(null);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    let unsubscribe;
    createDatabase({ name: 'myapp', storage: 'localStorage' }).then(async (database) => {
      setDb(database);
      const { rows } = await database.query('users', { limit: 50 });
      setUsers(rows);
      unsubscribe = database.subscribe('users', async () => {
        const { rows: next } = await database.query('users', { limit: 50 });
        setUsers(next);
      });
    });
    return () => unsubscribe?.();
  }, []);

  if (!db) return <div>Loading...</div>;
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

No framework-specific bindings—call the async API and set state as needed.


API reference

Method / API Description
createDatabase(options) Create or connect to a DB (name, storage, optional config).
loadConfigFromUrl(url) Fetch config JSON from a URL.
loadConfigFromFile(file) Read config from a File (e.g. file input).
db.createTable(name, columns) Create a table.
db.listTables() List table names.
db.getTable(name) Get table structure and rows.
db.insert(table, row) Insert a row (id auto if omitted).
db.fetch(table, options?) Fetch rows (optional filter, sort, limit).
db.query(table, query) Run JSON query; returns { rows, totalCount }.
db.update(table, id, data) Update a row by id.
db.delete(table, id) Delete a row by id.
db.deleteTable(name) Delete a table.
db.exportConfig() Export DB as config-like object.
db.subscribe(callback) / db.subscribe(table, callback) / db.subscribe(table, rowId, callback) Subscribe to change events; returns unsubscribe().
db.unsubscribe(id) Remove a subscription by id.
db.setChangeBroadcaster(broadcaster) Optional: broadcast changes to another context.
createChangeListener(transport, onChange) Listen for change events from another context via a custom transport.

Full details: API reference.


Resource URL
GitHub https://github.com/storionjs/storion
npm https://www.npmjs.com/package/@storion/storion
Issues https://github.com/storionjs/storion/issues
License MIT