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

@monoverse/voicebot-node

v0.3.0

Published

Server-side ingest client for VoiceBot — push your catalog & structure to the signed ingest endpoint (HMAC-SHA256, protocol v2). The Node/TS twin of the Laravel/WordPress sync producers.

Downloads

385

Readme

@monoverse/voicebot-node

Push your catalog and site structure to VoiceBot from any Node.js backend, so the AI assistant can answer grounded in your real products, categories, pages and shipping/payment options.

This package is a server-side producer client for the VoiceBot canonical signed-ingest contract (Sync Protocol v2). It pairs with VoiceBot once, then streams your data over an HMAC-SHA256-signed protocol: a full snapshot for the baseline and small incremental deltas after that. It is the Node/TypeScript twin of the Laravel and WordPress sync producers.

This is not a browser widget. For the talking UI use @monoverse/voicebot-react (or the raw pk_ embed). This package only pushes data to the ingest endpoint — voice/chat stays on the server.

  • Signed — every request is HMAC-SHA256 signed (protocol v2). The shared secret is stored encrypted at rest (AES-256-GCM) and is never logged, printed, or returned.
  • Memory-safe — snapshots stream as gzipped NDJSON straight to/from a temp file; the full catalog is never held in memory.
  • Correct under failure — signed requests are never auto-retried on 429/5xx (the server consumes the request nonce before it can error, so a blind retry is a replay). Resends are an explicit, opt-in resign-and-resend that mints a fresh nonce.
  • Type-safe — TypeScript strict, zero any in the public API, ships .d.ts.
  • Dependency-light — zero runtime dependencies (uses built-in node:crypto, global fetch).

Install

npm install @monoverse/voicebot-node

Requires Node.js 18+. The ingest base URL must be https:// (plaintext http:// is rejected — an HMAC over http would leak the signature); localhost / 127.0.0.1 are allowed for local dev.

Push your catalog

import { VoiceBotClient, product, category } from '@monoverse/voicebot-node';

const client = new VoiceBotClient({
  baseUrl: 'https://api.monoverse.tech',
  siteUrl: 'https://shop.example.com',
  // Full-entropy 32-byte key (hex/base64/Buffer) — `openssl rand -hex 32`. Encrypts the
  // stored credentials at rest. A passphrase fallback is allowed but must be >= 16 chars.
  encryptionKey: process.env.VOICEBOT_ENCRYPTION_KEY!,
  credentialsFile: './.voicebot-credentials.enc', // omit for in-memory only
});

// 1. Pair once with the VB-XXXX-XXXX code from your VoiceBot dashboard.
//    The tenant id + shared secret are stored encrypted; the secret is never returned.
await client.pair('VB-ABCD-1234');

// 2. Push a full snapshot (run once, then on a schedule). Stream entities — never
//    materialise the whole catalog. Money is INTEGER MINOR UNITS; categories/tags are slugs.
async function* catalog() {
  yield category({ externalId: 'cat:phones', name: 'Phones', slug: 'phones' });
  yield product({
    externalId: 'shop:product:iphone-15-pro',
    name: 'iPhone 15 Pro',
    priceAmount: 54999, // 549.99 → 54999 minor units
    currency: 'UAH',
    stockStatus: 'instock',
    categories: ['phones'],
    permalink: 'https://shop.example.com/p/iphone-15-pro',
  });
}
const result = await client.snapshot(catalog());
console.log(`snapshot ${result.syncId}: ${result.total} records`, result.counts);

Incremental deltas (the scheduled path)

After the baseline snapshot, push only what changed. Operations are batched (≤500 ops / ≤5 MB per batch); each batch carries a fresh batch_id that doubles as its Idempotency-Key.

import { toUpsertOperation, deleteOperation, product } from '@monoverse/voicebot-node';

await client.sendEvents([
  toUpsertOperation(product({ externalId: 'shop:product:iphone-15-pro', name: 'iPhone 15 Pro', priceAmount: 52999 })),
  deleteOperation('product', 'shop:product:discontinued-sku'),
]);

Heartbeat and unpair

const status = await client.status(); // entity counts + last sync session
await client.unpair();                // disconnect server-side, then wipe local credentials

Mapping rules that matter

  • Money is integer minor units. 199.99 UAH19999. The builders throw on a float or a negative — convert with Math.round(price * 100).
  • Categories and tags are slug lists (['phones', 'audio']), not objects.
  • page.contentText is plain text — strip HTML before sending.
  • externalId is your stable id. Use a namespaced shape, e.g. shop:product:{id}, and keep parent references consistent (category({ parentExternalId: 'shop:category:42' })).
  • A full snapshot is the source of truth: anything missing from it is tombstoned server-side, so an empty snapshot is refused (it would wipe the catalog).

Entity coverage

site, product, variation, category, tag, attribute, page, post, cpt, menu, menu_item, shipping_method, payment_method (plus environment, form, popup via the generic entity() builder). Send only the kinds you have.

Reliability & retries

| Path | Signed? | Retry policy | | --- | --- | --- | | pair | no | retries on 429/5xx/connection (no nonce spent) | | init / finalize / events / status / unpair | yes | no auto-retry; opt into resignRetries to resign-and-resend with a fresh nonce | | snapshot upload (PUT) | no (single-use URL) | not retried |

// Opt into resign-and-resend on idempotent calls (each retry mints a fresh nonce):
await client.sendEvents(ops, { resignRetries: 2 });

Security

  • The shared secret is stored encrypted at rest with AES-256-GCM (node:crypto), keyed by your encryptionKey, and is never logged, printed, or serializedCredentials redacts it from JSON.stringify, console.log, and toString.
  • Use a full-entropy 32-byte key for encryptionKey — generate one with openssl rand -hex 32 and keep it in your secret manager / env. A 32-byte Buffer, 64-char hex, or 32-byte base64 string is used directly. A plain passphrase is an explicit weaker fallback (scrypt-stretched) and must be at least 16 chars — anything shorter is rejected so a low-entropy at-rest key never goes unnoticed.
  • Every signed request carries an HMAC over METHOD\npath\nts\nnonce\nbody_sha256; the exact bytes hashed are the exact bytes sent, so the server's body check always matches. This is pinned by a cross-language conformance test against the shared signing vectors.
  • Requests use a fresh 16-byte nonce and a current timestamp (server enforces a 300s replay window).

License

MIT © Monoverse.