Reactive PluresDB bindings for Svelte — backend-agnostic, TypeScript-first, Svelte 5 runes-ready.
- Features
- Requirements
- Installation
- Quick Start
- API Reference
- Custom Adapters
- Examples
- Migration Guide
- Contributing
- Changelog
- License
- Svelte 4 & 5 Compatible — store protocol and runes (
$state,$derived,$effect) - TypeScript-first — fully typed generics throughout
- Backend-agnostic — swap databases via the
DbAdapterinterface - Real-time — subscription-based reactivity, no polling
- P2P — optional Hyperswarm adapter for peer-to-peer graph sync
- Graph & Collection APIs — purpose-built primitives for complex data
| Requirement | Version |
|---|---|
| Node.js | ≥ 18 |
| Svelte | 4.x or 5.x |
| TypeScript | ≥ 5.0 (optional but recommended) |
Peer dependencies (install as needed):
| Package | Required for |
|---|---|
svelte |
All usage |
pluresdb |
createPluresDbAdapter() |
hyperswarm |
createHyperswarmAdapter() |
@plures/praxis |
Praxis rule-engine modules |
npm install @plures/unumimport { initDb, createMemoryAdapter, createCollection } from '@plures/unum';
// 1. Initialize with an adapter once at app startup
initDb(createMemoryAdapter());
// 2. Use reactive APIs anywhere
interface Task { title: string; done: boolean }
const tasks = createCollection<Task>('tasks');
const id = tasks.add({ title: 'Buy milk', done: false });
tasks.update(id, { done: true });
tasks.remove(id);See docs/API.md for the complete exported API surface.
Initialize unum with a database adapter. Call once at app startup before using any data bindings.
import { initDb, createPluresDbAdapter } from '@plures/unum';
import PluresDB from 'pluresdb';
initDb(createPluresDbAdapter(new PluresDB({ localStorage: true })));Tear down the current adapter and reset the context.
Return the current DbAdapter (throws if initDb() has not been called).
Return a root ChainNode from the current adapter.
Svelte-compatible readable store containing the current DbAdapter | null.
Useful for lazy-init patterns.
Create a reactive typed collection binding backed by PluresDB.
<script>
import { createCollection } from '@plures/unum';
interface Task { title: string; done: boolean }
const tasks = createCollection<Task>('tasks');
const pending = tasks.query(items => items.filter(i => !i.data.done));
</script>
{#each tasks.items as task}
<p class:done={task.data.done}>{task.data.title}</p>
{/each}CollectionRef<T> properties & methods
| Member | Description |
|---|---|
items |
Current items as Array<CollectionItem<T>> |
size |
Number of items |
add(data) |
Add an item; returns generated ID |
update(id, data) |
Merge partial data into an item |
remove(id) |
Delete an item |
get(id) |
Retrieve a single item (or undefined) |
query(selector) |
Create a derived reactive query (returns CollectionQuery<R>) |
subscribe(cb) |
Svelte 4 store protocol |
destroy() |
Tear down subscriptions |
Create a reactive typed graph binding backed by PluresDB.
<script>
import { useGraph } from '@plures/unum';
const graph = useGraph<{ label: string }, { weight: number }>('my-graph');
const heavy = graph.query(
(nodes, edges) => edges.filter(e => e.data.weight > 10)
);
</script>
{#each graph.nodes as node}
<p>{node.data.label}</p>
{/each}GraphRef<N, E> properties & methods
| Member | Description |
|---|---|
nodes |
Current nodes as Array<GraphNode<N>> |
edges |
Current edges as Array<GraphEdge<E>> |
state |
Full GraphState<N, E> snapshot |
addNode(data) |
Add a node; returns generated ID |
updateNode(id, data) |
Merge partial data into a node |
removeNode(id) |
Remove a node and all its incident edges |
addEdge(source, target, data?) |
Add a directed edge; returns generated ID |
updateEdge(id, data) |
Merge partial data into an edge |
removeEdge(id) |
Remove an edge |
query(selector) |
Create a derived reactive query (returns GraphQuery<T>) |
findPath(fromId, toId) |
BFS shortest path between two nodes |
subscribe(cb) |
Svelte 4 store protocol |
destroy() |
Tear down subscriptions |
Low-level reactive binding to a PluresDB path. Suitable for single-document or collection bindings without the typed Collection API.
<script>
import { pluresData } from '@plures/unum';
const todos = pluresData('todos');
</script>
{#each todos.list() as todo}
<p>{todo.text}</p>
{/each}DataRef<T> properties & methods
| Member | Description |
|---|---|
state |
Current raw state snapshot |
value |
Single item (id-bound) or item array (collection) |
list() |
Items as an array (collections only) |
add(data) |
Add an item to the collection |
update(idOrUpdater, updater?) |
Update item(s) |
remove(id?) |
Remove an item (or the bound item) |
subscribe(cb) |
Svelte store protocol |
destroy() |
Tear down subscriptions |
import { pluresData, pluresDerived, pluresBind } from '@plures/unum';
const todos = pluresData('todos');
const done = pluresDerived(todos, items => items.filter(i => i.done));
const title = pluresBind(todos, 'title'); // two-way binding for a fieldA Svelte-compatible writable store that syncs a single scalar value with
PluresDB. Implements the full store contract (subscribe / set / update).
import { initDb, createMemoryAdapter, createPluresStore } from '@plures/unum';
initDb(createMemoryAdapter());
const theme = createPluresStore<'light' | 'dark'>('settings/theme', 'light');
theme.set('dark');
theme.update(t => t === 'dark' ? 'light' : 'dark');
theme.destroy();Fully in-memory adapter. Data is not persisted. Ideal for unit tests and server-side rendering.
import { initDb, createMemoryAdapter } from '@plures/unum';
initDb(createMemoryAdapter());Wrap a PluresDB instance.
import PluresDB from 'pluresdb';
import { initDb, createPluresDbAdapter } from '@plures/unum';
initDb(createPluresDbAdapter(new PluresDB({ localStorage: true })));Wrap an existing Gun.js instance for migration scenarios.
import Gun from 'gun';
import { initDb } from '@plures/unum';
import { createGunAdapter } from '@plures/unum/adapters';
initDb(createGunAdapter(Gun({ peers: ['http://localhost:8765/gun'] })));Add P2P sync to any inner adapter via Hyperswarm.
import Hyperswarm from 'hyperswarm';
import { createHash } from 'node:crypto';
import { initDb, createMemoryAdapter } from '@plures/unum';
import { createHyperswarmAdapter } from '@plures/unum/adapters';
const swarm = new Hyperswarm();
swarm.join(createHash('sha256').update('my-topic').digest());
initDb(createHyperswarmAdapter(swarm, createMemoryAdapter()));Logic modules for the @plures/praxis rule engine.
| Module | Factory | Description |
|---|---|---|
| Merge Policy | mergePolicyModule(config) |
Conflict resolution for multi-source data |
| Schema Unification | schemaUnificationModule(config) |
Schema compatibility & coercion rules |
| Subscription Policy | subscriptionPolicyModule(config) |
Stream routing & auth gate |
| Freshness | freshnessModule(config) |
TTL / cache-invalidation rules |
import { PraxisRegistry } from '@plures/praxis';
import {
mergePolicyModule,
schemaUnificationModule,
subscriptionPolicyModule,
freshnessModule,
} from '@plures/unum/praxis';
const registry = new PraxisRegistry();
registry.registerModule(mergePolicyModule({ priorities: { remote: 10, local: 5 } }));
registry.registerModule(schemaUnificationModule());
registry.registerModule(subscriptionPolicyModule({ requireAuth: true }));
registry.registerModule(freshnessModule({ defaultTtlMs: 60_000 }));These helpers are exported from the main package entry point.
| Helper | Description |
|---|---|
isPluresAvailable(adapter) |
Type guard — returns true when adapter is non-null |
safeGet(obj, path, default?) |
Read a deeply nested property via dot-path |
safeMap(dbData, callback, filter?) |
Transform a DB record object into an array |
safeChain(adapter, path?) |
Build a ChainNode from a dot-separated path string |
Implement the DbAdapter interface to connect any backend:
import type { DbAdapter, ChainNode } from '@plures/unum';
export function createMyAdapter(): DbAdapter {
return {
root(): ChainNode {
// return a ChainNode bound to your backend
},
destroy?(): void {
// optional cleanup
},
};
}Runnable examples are in the examples/ directory:
| Example | Description |
|---|---|
examples/svelte-kit-demo |
Full SvelteKit app using createCollection and useGraph |
examples/deno-svelte-demo |
Deno + Svelte demo with the memory adapter |
Migrating from the PluresStore/pluresData (Svelte 4) API to the useGraph()/createCollection() runes API?
See MIGRATION.md for a step-by-step guide.
Contributions are welcome! Please follow these steps:
- Fork the repository and create a feature branch (
git checkout -b feat/my-feature). - Install dependencies:
npm install. - Make your changes; add or update tests in
tests/. - Run the full test suite:
npm test. - Run the type-checker:
npm run lint. - Commit using Conventional Commits (
feat:,fix:,chore:,docs:, etc.). - Open a pull request against
main.
Note: All public API functions must have JSDoc comments with
@paramand@exampletags. The README must stay in sync with exports insrc/index.ts.
See CHANGELOG.md for a full history of notable changes.
MIT