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

@vocantly/widget

v0.1.26

Published

Drop-in embeddable support chat widget for Vocantly, built on @vocantly/sdk.

Readme

Vocantly Widget

A drop-in embeddable support chat widget built on the headless @vocantly/sdk. One <script> renders a floating chat bubble + panel; your visitors chat with the AI bot (and human agents) with zero UI work on your side.

dist/vocantly-widget.js is a single self-contained file (~63 KB) — the SDK, its deps, and the UI are all bundled in. No import maps, no build step for the host.

The security model (important)

The widget runs in the browser, so it only ever holds an SDK token (scoped to one app + one visitor, short-lived). Your API key + secret never touch the browser. The flow:

Your backend (holds API key)                     Browser (this widget)
  POST /apps/:id/support-sessions   ── token + conversation_id ──▶  VocantlyWidget.init({...})
  (X-Api-Key + X-Api-Secret)

Your server calls the provisioning endpoint, then hands only { token, conversation_id } to the page.

Install

Option A — script tag (any website, via CDN):

<!-- unpkg / jsDelivr serve the bundled IIFE; global: VocantlyWidget -->
<script src="https://unpkg.com/@vocantly/widget"></script>
<!-- pin a version:  https://unpkg.com/@vocantly/[email protected] -->

Option B — npm (React / Vue / Next / bundlers):

npm install @vocantly/widget
import { init } from '@vocantly/widget';
init({ wsUrl, token, conversationId, /* … */ });

Both ship the SDK + deps bundled in — zero peer dependencies. Usage is the same either way:

<script src="https://unpkg.com/@vocantly/widget"></script>
<script>
  VocantlyWidget.init({
    wsUrl: 'wss://your-realtime/ws',
    token: 'SDK_TOKEN_FROM_YOUR_BACKEND',
    conversationId: 'CONVERSATION_ID_FROM_YOUR_BACKEND',
    apiUrl: 'https://your-backend/api/v1',   // optional — enables message history
    title: 'Support',
    subtitle: 'We reply in a few minutes',
    primaryColor: '#195EFF',
    position: 'bottom-right',                // or 'bottom-left'
    greeting: 'Hi! 👋 How can we help?',
    autoOpen: false,

    // Optional pre-chat contact capture (needs apiUrl):
    collectContact: true,                    // ask name + email before chatting
    requireEmail: false,                     // if true, email is mandatory to start
    contactPrompt: 'Before we start — how can we reach you?',
  });
</script>

Your backend (one call to provision)

// server-side — keep the secret here
const r = await fetch(`${BASE}/apps/${APP_ID}/support-sessions`, {
  method: 'POST',
  headers: { 'X-Api-Key': PUBLIC_KEY, 'X-Api-Secret': SECRET_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ external_user_id: yourUserId, name, email }),
});
const { token, conversation_id } = (await r.json()).data;   // hand these to the browser

No backend? Anonymous embed with just your public key

For a static landing page with unauthenticated visitors and no server, use publicKey instead of token/conversationId. The widget self-provisions an anonymous session — no secret in the browser:

<script src="https://unpkg.com/@vocantly/widget"></script>
<script>
  VocantlyWidget.init({
    publicKey: 'pk_…',                         // your app PUBLIC key (safe to expose)
    wsUrl:  'wss://ws.vocantly.com/ws',
    apiUrl: 'https://api.vocantly.com/api/v1',  // required in this mode
    collectContact: true,
  });
</script>

One prerequisite — allow your site's origin. Anonymous embedding is off by default. Add your domain(s) to the app's allowed_origins (dashboard, or via the API):

PATCH /apps/:id   { "allowed_origins": ["https://acme.com", "https://www.acme.com"] }

The widget generates a stable visitor id (localStorage) and caches {token, conversationId} so a reload resumes the same conversation. Requests are origin-checked server-side and rate-limited.

Provide either publicKey (anonymous) or token + conversationId (backend-provisioned).

API

| Call | Effect | |---|---| | VocantlyWidget.init(opts) | Mount + connect. Returns { open, close, send, destroy }. | | VocantlyWidget.open() / .close() | Toggle the panel. | | VocantlyWidget.destroy() | Disconnect + remove from the DOM. |

Pre-chat contact capture

With collectContact: true, the widget asks the visitor for their name + (optional) email before chatting and saves it to their customer profile via PATCH /sdk/customers/me (authenticated by the SDK token — no secrets in the browser). Agents then see the visitor's contact details in the inbox. Requires apiUrl to be set. Use requireEmail: true to make email mandatory.

Build

npm run build       # → dist/vocantly-widget.js (minified IIFE, global VocantlyWidget)
npm run build:dev   # non-minified + sourcemap

The build bundles ../vocantly-js-sdk/dist/index.js. If you rebuild the SDK, rebuild the widget.

Demo

npm run serve:demo   # serves this folder on :5173
# open http://localhost:5173/   (redirects to the demo)

Avoid ports Chrome blocks as "unsafe" (e.g. 5060 = SIP → ERR_UNSAFE_PORT). 5173 is safe. The demo is a mock business page. Enter a workspace owner's email/password + a Development App ID and click Boot — it provisions via /apps/:id/support-sessions (dev login stands in for your backend) and mounts the widget. Requires backend :3001, Go realtime :8080, and the app to have the bot enabled + an active FAQ.

Known limitation

Bot replies are currently delivered live but not persisted server-side, so they won't appear in message history on reload (customer/agent messages do). Persisting bot messages is a backend follow-up.