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

@snaplyagent/sdk

v2.23.0

Published

Consent-first screen capture SDK for the web — Snaply Agent.

Readme

@snaplyagent/sdk

The web SDK for Snaply Agent — consent-first screen capture for support teams. A support agent asks to see the screen, the person allows it, and only then is anything captured. Nothing is taken silently, and marked regions are covered before the image is encoded, so the real pixels never leave the browser.

Zero runtime dependencies. Ships ESM and CJS with TypeScript types.

Install

npm install @snaplyagent/sdk

Ships ESM and CJS with TypeScript types. Any bundler that resolves bare imports works — there is no build configuration to add.

Usage

Attach callbacks before init. A support request can arrive as soon as the device registers, and anything attached afterwards would miss it.

import { Snaply } from "@snaplyagent/sdk";

// Anonymous is fine — identity is optional.
await Snaply.init({ key: "snap_live_…" });

// When the user signs in:
await Snaply.identify({ id: "usr_20481", name: "Maya K.", phone: "+14155550142" });

Snaply.on({
  onRequestShown: () => {},
  onAllowed: (capture) => console.log(capture.imageUrl),
  onDenied: () => {},
  onExpired: () => {},
  // Live view stopped — by the user, the agent, or the call ending.
  onLiveEnded: (session) => {},
  // Remote control started or ended. The SDK shows its own prompt and banner; this is only a hook.
  onRemoteControl: (active) => {},
  onError: (error) => {},
});

// On sign-out:
await Snaply.reset();

// What the person reads out so support can find this browser:
Snaply.showSupportCode();

Every callback is optional. init rejects with a SnaplyError — see Errors.

Environments

environment selects which Snaply deployment to talk to — they are separate backends (each its own database), matching the dev/staging/live pipeline: "development"dev.snaplyagent.com, "staging"staging.snaplyagent.com, "live" (default) → snaplyagent.com. Use baseUrl for a self-hosted host.

await Snaply.init({ key: "…", environment: process.env.NODE_ENV === "production" ? "live" : "development" });

Each environment has its own products and keys. (Within one environment, a product also has Test vs Live keys — snap_test_… / snap_live_…, where test captures are excluded from quota/billing; that's a separate axis.)

Redaction

Mark any element with the snaply-redact attribute and it is covered by a REDACTED box in every screenshot and live-view frame — painted into the image on the device before it uploads, so the support agent never sees the content. Password inputs are redacted automatically. Nothing else is — mark whatever you want hidden. What counts as sensitive is your call, not the SDK's, and the same rule holds on iOS and Android, so one marking pass covers every platform.

<input snaply-redact type="text" name="ssn" />
<div snaply-redact="Card on file">•••• 4242</div>   <!-- attribute value overrides the label -->

The box defaults to a red fill with a white REDACTED caption. Customize it globally at init, or per element:

Snaply.init({
  key: "snap_live_…",
  redaction: { color: "#14201A", label: "HIDDEN", labelColor: "#ffffff" },
});

Per-element overrides: snaply-redact="Custom label", data-snaply-redact-color, data-snaply-redact-label-color. An empty label paints a plain box with no caption. While support has in-app control, redacted fields also stay locked (support can't read or type into them).

identify also takes an optional phone (E.164). Send it and the workspace's screen-pop API (POST /v1/calls/incoming) can resolve an incoming caller-ID to this user and pop them onto the agent's console — see the SDK reference.

Anonymous callers get a short support code (Snaply.supportCode(), e.g. 74-315) to read to the agent. Snaply.showSupportCode() presents it in a built-in corner card (same chrome as the consent prompt, follows the device's light/dark theme) with a Done button; if the device is identified — no code — it's a silent no-op. Use supportCode() instead if you'd rather render your own UI.

Consent model

init registers the device (the backend validates the API key and, for web/hybrid products, the request Origin against the product's domain allowlist — a mismatch returns 403 origin_mismatch), then opens a WebSocket for presence and consent push. When an agent requests a screenshot, the backend pushes a consent message; the SDK shows a bottom-right prompt card (copy adapts to the product's capture mode and follows the device's light/dark theme). Only after the user taps Allow does the SDK snapshot the viewport and upload it — nothing is captured silently. Views you mark are redacted before capture (see below).

Nothing is captured until the person allows it — that is the product, not a setting. The SDK rasterises your page only: it cannot see other tabs, other windows, or anything outside the browser, so no screen-share permission is requested and no browser picker appears. While a live session runs an on-screen indicator stays up, and the SDK stops sending the moment it can no longer prove that indicator is attached.

Errors

SnaplyError carries status and code: invalid_key (401), origin_mismatch / account_suspended / workspace_suspended / product_paused / seat_suspended / mode_not_allowed (403), device_offline / device_backgrounded (409), request_expired (410), quota_exceeded / device_limit (429).

device_limit is the one to expect during integration: it comes back from init() when the environment has already registered its maximum number of devices.

Vendor ID

There isn't one, and there will not be. Browsers expose no per-device identifier, and Snaply will not derive one from a fingerprint — that is precisely the cross-site identifier browsers spend their engineering budget preventing. vendorId is always null on the web, and the native SDKs' collectVendorId flag has no effect here. Identity is the install-scoped UUID, so a new browser, a cleared profile or a private window arrives as a new device.

Notes

  • The key is not a secret. It identifies the workspace, ships in your public JavaScript, and is checked server-side against the product's allowed domains — a copy lifted from your bundle is useless from anywhere else.
  • The device token is never persisted. Every page load registers afresh; installId (a non-secret UUID in localStorage) is what keeps it the same device across reloads.
  • No runtime dependencies. The whole SDK is the tarball you install; nothing is fetched at run time, and nothing is loaded from a CDN.

Develop

npm install
npm test          # vitest (paths + prompt UI, jsdom)
npm run build     # tsup → dist (ESM + CJS + d.ts)

demo/index.html is a live end-to-end demo against a local backend (snap_live_Xq81LdM4v7ZsHy3t, the seeded Acme Web key). Serve the repo root and open it, then request a capture from the agent console. Add ?anon to register anonymously and try the support-code popup.

Docs

Full documentation: https://snaplyagent.com/docs