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

@wishcore/wish-sdk

v0.5.0

Published

Wish API for node. Used for building Wish Apps. TS framework layers over the native wish-sdk-rs core (napi).

Readme

Wish SDK

Node.js SDK for building peer-to-peer applications on the Wish protocol.

  • Cryptographic identity-based P2P networking
  • Protocol class pattern for defining endpoints and peer lifecycle
  • Document sync via SyncProtocol
  • WebSocket + CBOR server for frontends (createWebRpc)
  • Promise-based ready state and identity management (RpcApp)

Install

npm install @wishcore/wish-sdk

The SDK is currently beta-tagged. To install the latest beta explicitly:

npm install @wishcore/wish-sdk@beta

The document-store dependency is the Rust-backed napi package in this repo (wish/bindings/node/document-store); inside wish-stack everything resolves via file: paths — see an example app's package.json (apps/notes/backend). The npm registry publish (stable versions for external use) is pending.

Two things to know before your first app

1. Your app doesn't own identities — it asks for one

Identities are minted and managed by the Wish app (Dashboard). Your app requests an identity grant and the user approves it from the tray. The SDK exposes this as app.requestIdentity(). Before the grant arrives, app.identities is empty.

The exception is admin tooling (e.g. the Dashboard itself, the wish CLI). Those pass permissions: { admin: true } and can call admin RPCs like identity.create directly. Regular apps never need that.

2. A Wish app is a long-running process, not a script

Peer connections, document sync, and presence all live inside the running app process. There's no "one-shot CLI" shape — the typical layout is:

  • Daemon: RpcApp + your Protocols + createWebRpc for a UI/CLI to attach
  • Clients: anything that talks to the daemon's WebRpc (a webview, a CLI, …)

If you want one-shot commands, write a thin CLI that connects to the daemon over WebRpc, not a fresh App per invocation.

Project setup

// package.json — top-level await needs "type": "module"
{
    "type": "module",
    "dependencies": {
        "@wishcore/wish-sdk": "^0.4.0-beta-26"
    }
}

Quick start

import { RpcApp, Protocol } from '@wishcore/wish-sdk';

class ChatProtocol extends Protocol {
    online(peer, client) { console.log('Peer online:', peer.toString()); }
    offline(peer)         { console.log('Peer offline:', peer.toString()); }
}

const app = new RpcApp({
    name: 'Chat',
    // Auto-detects ~/.wish/core.sock; override with coreUnixSocket or corePort.
    protocols: { chat: new ChatProtocol() },
});

await app.whenReady;

if (app.identities.length === 0) {
    // Dashboard will prompt the user. Resolves once granted.
    await app.requestIdentity();
}
console.log('Identity:', app.identities[0].name);

Requires a running wish-core. Get it via the Wish desktop app, or download the standalone binary from developer.wishtech.fi/downloads/wish-core/.

Protocol pattern

Define peer-facing endpoints by extending Protocol. Each peer that runs the same protocol gets a typed session.

class MyProtocol extends Protocol {
    constructor() {
        super();
        this.endpoint('greet', { doc: 'Send greeting' }, async (req, res) => {
            res.send({ message: 'Hello from ' + req.args[0] });
        });
    }

    online(peer, client) {
        client.request('greet', ['Alice'], (err, data) => {
            console.log(data?.message);
        });
    }

    offline(peer) { /* peer left */ }
}

SyncProtocol

Bridges document-store to wish-core. The app declares which root documents to pull via getRoots; the sync engine handles discovery, incremental transfer, and signature checks.

import { RpcApp, SyncProtocol, createWebRpc } from '@wishcore/wish-sdk';
import { DocumentStore, SqlitePersistence } from 'document-store';
import Database from 'better-sqlite3';

const db = new Database('./data/app.db');
const store = new DocumentStore({ storage: new SqlitePersistence(db) as any });
store.registerType('note');

const webRpc = createWebRpc({ port: 8090 });

const sync = new SyncProtocol({
    store,
    // Required: return the set of root hashes you want to sync, per identity.
    // Typical pattern: maintain a `pullRoots` document type the user controls
    // (e.g. by accepting invites or following others), and query it here.
    getRoots: async (uid) => {
        const rows = db.prepare(
            `SELECT json_extract(doc, '$.roots') as roots
             FROM pullRoots
             WHERE json_extract(doc, '$.uid.__buf') = ?`,
        ).all(uid.toString('hex')) as any[];
        return rows.flatMap(r => JSON.parse(r.roots || '[]')
            .map((h: any) => Buffer.from(h.__buf, 'hex')));
    },
    onSync: ({ injected }) => {
        if (injected) webRpc.emit('signals', ['document.changed']);
    },
});

const app = new RpcApp({
    name: 'Notes',
    protocols: { notes: sync },
});

await app.whenReady;

For a richer setup (custom sign/verify callbacks, schema validation, share resolvers) see the document-store guide or the Reason app source.

Notifying peers after a write

When your app writes a document, tell sync to push the change:

const [errors, hash] = await store.add('note', {
    uid: app.identities[0].uid,
    title: 'Hello',
    share: { self: true },
});
if (!errors) sync.notifyPeers();

Subclassing

class MyAppProtocol extends SyncProtocol {
    constructor(store: DocumentStore, getRoots: SyncProtocolOptions['getRoots']) {
        super({ store, getRoots });
        this.endpoint('custom.method', { doc: 'My endpoint' }, async (_req, res) => {
            res.send({ result: 'ok' });
        });
    }

    online(peer, client) {
        console.log('Peer online:', peer.toString());
        super.online(peer, client);  // Start sync for this peer
    }
}

createWebRpc

WebSocket + CBOR server for frontends and CLIs:

import { createWebRpc } from '@wishcore/wish-sdk';

const webRpc = createWebRpc({ port: 8090 });

webRpc.endpoint('note.list', async (_req, res) => {
    const notes = await store.collection('note').find({}).toArray();
    res.send(notes);
});

webRpc.emit('signals', ['document.changed']);

RpcApp

Manages the wish-core connection, identity lifecycle, and protocol routing.

const app = new RpcApp({
    name: 'MyApp',
    // Auto-detects ~/.wish/core.sock. Override only if needed:
    // coreUnixSocket: '/custom/path/core.sock',
    // corePort: 9094,
    protocols: { myapp: new MyProtocol() },
});

await app.whenReady;

console.log(app.identities);   // [] until the user grants one

if (app.identities.length === 0) {
    await app.requestIdentity();  // Resolves on user approval in Dashboard
}

app.on('identity', (identities) => { /* updated grant list */ });

Testing

npm test

pretest downloads the stable wish-core binary for your platform (x64/arm64 Linux, arm64 Darwin) from developer.wishtech.fi/downloads/wish-core/, verifies sha256, and drops it at test/.bin/wish-core (gitignored). A small mocha bootstrap (test/_setup.cjs) points WISH at it. To force a refresh, delete test/.bin/; to pin a specific build, set WISH explicitly before running.

# Switch to the rolling test channel (newest unreleased SHAs)
CHANNEL=test npm run fetch-wish-core

# Override entirely
WISH=/abs/path/to/wish-core npm test
WISH=/abs/path/to/wish-core npm run test-only -- test/identity.spec.ts

See test/README.md for the multi-peer test patterns (WishCoreRunner, importContactWithTransport, sync E2E).

Documentation

License

Apache-2.0