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

@plures/unum

v0.7.1

Published

Reactive Svelte bindings for PluresDB — backend-agnostic, TypeScript-first

Readme

@plures/unum

CI npm version License: MIT

Reactive PluresDB bindings for Svelte — backend-agnostic, TypeScript-first, Svelte 5 runes-ready.

Table of Contents

Features

  • Svelte 4 & 5 Compatible — store protocol and runes ($state, $derived, $effect)
  • TypeScript-first — fully typed generics throughout
  • Backend-agnostic — swap databases via the DbAdapter interface
  • 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

Requirements

| 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 |


Installation

npm install @plures/unum

Quick Start

import { 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);

API Reference

See docs/API.md for the complete exported API surface.

Adapter Setup

initDb(adapter)

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 })));

destroyDb()

Tear down the current adapter and reset the context.

getAdapter()

Return the current DbAdapter (throws if initDb() has not been called).

getRoot()

Return a root ChainNode from the current adapter.

db

Svelte-compatible readable store containing the current DbAdapter | null. Useful for lazy-init patterns.


Collections — createCollection<T>(path)

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 |


Graph — useGraph<N, E>(path)

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 |


Reactive Data — pluresData<T>(path, id?)

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 |


Derived & Bind — pluresDerived / pluresBind

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 field

Store — PluresStore<T> / createPluresStore<T>

A 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();

Adapters

createMemoryAdapter()

Fully in-memory adapter. Data is not persisted. Ideal for unit tests and server-side rendering.

import { initDb, createMemoryAdapter } from '@plures/unum';
initDb(createMemoryAdapter());

createPluresDbAdapter(db)

Wrap a PluresDB instance.

import PluresDB from 'pluresdb';
import { initDb, createPluresDbAdapter } from '@plures/unum';

initDb(createPluresDbAdapter(new PluresDB({ localStorage: true })));

createGunAdapter(gun)

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'] })));

createHyperswarmAdapter(swarm, inner)

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()));

Praxis Modules (@plures/unum/praxis)

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 }));

Utility Helpers

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 |


Custom Adapters

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
    },
  };
}

Examples

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 |


Migration Guide

Migrating from the PluresStore/pluresData (Svelte 4) API to the useGraph()/createCollection() runes API? See MIGRATION.md for a step-by-step guide.


Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository and create a feature branch (git checkout -b feat/my-feature).
  2. Install dependencies: npm install.
  3. Make your changes; add or update tests in tests/.
  4. Run the full test suite: npm test.
  5. Run the type-checker: npm run lint.
  6. Commit using Conventional Commits (feat:, fix:, chore:, docs:, etc.).
  7. Open a pull request against main.

Note: All public API functions must have JSDoc comments with @param and @example tags. The README must stay in sync with exports in src/index.ts.


Changelog

See CHANGELOG.md for a full history of notable changes.


License

MIT