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

@viceme-ai/sdk

v0.8.0

Published

ViceMe hosted engagement and Website Work access SDK for static HTML, ESM, React, and Agent-generated sites.

Readme

@viceme-ai/sdk

ViceMe browser SDK for Shop-hosted engagement and Website Work access.

The release PR atomically owns the package version, runtime manifest, and changelog update. During the current preview stage, publication does not require a repository license file. The root LICENSE-PENDING.md records the deferred decision without blocking release; licensing will be finalized separately.

Install

pnpm add @viceme-ai/sdk

Static HTML

<div id="viceme-engagement"></div>
<script
  defer
  src="https://s3.viceme.cn/viceme-sdk/0.5.0/viceme.min.js"
  data-viceme-work="wrk_live_demo"
  data-viceme-region="cn"
  data-viceme-features="danmaku,tip"
  data-viceme-target="#viceme-engagement"
  data-viceme-theme="auto"
></script>

The feature declaration accepts danmaku, tip, or both without whitespace or duplicates.

Static CDN entry points always use an immutable exact-version directory. The CN host is s3.viceme.cn; GLOBAL uses s3.viceme.ai. The 0.5.0 URL above is the current source target and is unavailable until that exact release is published and verified.

With CSP, allow the exact regional S3 origin in script-src and connect-src, the exact regional Shop origin in frame-src, and keep object-src 'none'. A nonce with 'strict-dynamic' may authorize dynamic scripts, but the other directives still need exact origins. Do not use * or a ViceMe subdomain wildcard.

ESM

import { createViceMe } from '@viceme-ai/sdk';
import { mountDanmaku } from '@viceme-ai/sdk/danmaku';
import { mountTip } from '@viceme-ai/sdk/tip';

const client = createViceMe({ workKey: 'wrk_live_demo', region: 'cn' });
await client.ready();

const target = document.querySelector('#viceme-engagement');
if (!target) throw new Error('ViceMe target missing');

const results = await Promise.allSettled([
  mountDanmaku(client, { target, theme: 'auto' }),
  mountTip(client, { target, theme: 'auto', presentation: 'integrated' }),
]);
const mounted = results.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : []));

mounted.forEach((handle) => handle.destroy());
client.destroy();

Run those cleanup calls from the owning component's unmount path or another explicit lifecycle boundary, not from pagehide (which also covers bfcache). The integrated presentation uses the danmaku bar as its only visible launcher and opens the official Tip dialog. Standalone mountTip() remains inline by default.

createViceMe and ready() are purely local and never contact Shop. A live client reports build support for danmaku and tip; Shop remains authoritative for whether a Work enables either capability. The hosted /embed/danmaku iframe uses Shop's internal SDK to read and create anonymous messages through /v1/danmaku/messages. The /widget/tip/<workKey> iframe owns confirmation, payment, risk, and result authority. Visitors do not sign in to ViceMe and are anonymous to the creator; the observed parent Origin is attribution rather than an authorization gate.

Pass a selected public pair value: keys.test is wrk_test_... and keys.live is wrk_live_.... Other Work key shapes are rejected locally.

Access operations establish a short-lived, memory-only Work session on first use. They expose client.auth, client.access, and client.checkout; login, buyer recovery, and hosted checkout remain ViceMe-owned UI. The host never receives a general ViceMe session or payment credential. Tests can inject a deterministic transport and presenter through @viceme-ai/sdk/testing. Calling client.destroy() cancels in-flight access requests, closes the active SDK-owned sign-in or checkout layer, and rejects the interrupted call with CLIENT_DESTROYED; late responses cannot restore the in-memory Work token. Request cancellation and timeouts remain effective until the parsed response is delivered. Cancelling the client's optional signal preserves the caller's Error reason for requests and interactive presentations; a request timeout rejects with retryable NETWORK_TIMEOUT. A cancelled response cannot establish a Work session even if its body has already finished parsing.

Website Access v3 is negotiated during the first Access session request via supportedAccessProtocolVersions: [3]. An unversioned server response retains the legacy login/follow flow and CNY amountCents representation. V3 returns CNY or USD amountMinor prices and feature status; PENDING_CHANNEL may have no sale price and a non-null pricingIntent. The server decides which channels are ready. GLOBAL supports access integration and login; missing payment channels never fall back to CN or create an order.

V3 does not require, bind, or compare host Origin, including null/missing origins. Official HTTPS Shop URLs and server-authoritative identity, Work, expiry, and entitlement validation remain mandatory. Browser restrictions on Web Crypto, frames, navigation, and storage can still require a first-party recovery step.

access.require(featureKey) handles buyer identification, checkout, and a fresh access check. It never grants access from a payment or window message. V3 login opens directly from the initiating action; FOLLOW executes in that same user interaction without another confirmation. Shop owns automatic follow after an authenticated login/payment; anonymous purchase skips both login and follow.

Optional recovery controls use the same protocol:

await client.access.restorePurchase('download'); // Remains anonymous.
await client.access.claimPurchase('download'); // User explicitly chooses login and claim.

The official page owns purchase receipts, account selection, and claim consent. The SDK obtains only a short-lived Work-scoped buyer or user credential through PKCE challenge/result/exchange calls. These calls omit cookies. It does not accept credentials from postMessage, query strings, or fragments. Explicit claim replaces the current anonymous context only after a verified user exchange; failed/cancelled claim keeps existing purchase authorization.

If an iframe cannot access first-party storage, “在当前页面继续” navigates to the official page. Only the challenge, state, verifier, and selected feature/purpose are saved in sessionStorage for at most five minutes. After returning, invoking the same action or require() for the same feature resumes the authorized recovery/claim handshake. No buyer/user token, recovery secret, or payment result is persisted by the SDK. Unavailable navigation storage is reported instead of pretending that a purchase was recovered.

Work-session refresh is single-flight and server expiry retries at most once. Refreshing a Work session drops user/buyer context; the next authoritative check can request recovery again. Logout, identity replacement, and destruction invalidate late identity results. Authorization reads that race a session change are retried against the current identity; successful mutations are not replayed merely because another request refreshed a session. Both explicit destroy() and caller cancellation stop bridge polling and close the layer; the caller's Error reason remains intact.

const decisions = await client.access.checkMany(['members', 'pro-tools']);
if (!decisions['pro-tools']?.allowed) {
  await client.access.require('pro-tools');
}

The Tip subpath exports TipPaidDetail and TipWidgetCloseDetail for the sanitized viceme:tip-paid and viceme:widget-close CustomEvent details. TipPaidDetail contains only status, trusted work.id/title, amount, and CNY; it contains no key, provider, order number, token, or transaction identifier.

The danmaku mount hashes the canonical page URL locally, combines it with a 10% scroll bucket, and sends only the opaque anchor to the hosted iframe. Destroying the mount removes its nodes, listeners, debounce timer, and location poll.

The Tip mount sends no amount, provider, token, or application ID. It enables interaction only after a trusted resize handshake. In integrated mode it also accepts open requests only from the matching trusted danmaku controls, then restores focus there after close. Shop resets its hosted payment surface on Escape before sending close; the SDK forwards sanitized close and paid notifications, and removes its iframe, timer, media listener, and message listener on destroy.

Headless Tip

This additive API targets 0.5.0. Do not expect createTip or @viceme-ai/sdk/tip/testing from the immutable npm 0.4.0 package.

import { createViceMe } from '@viceme-ai/sdk';
import { createTip } from '@viceme-ai/sdk/tip';

const client = createViceMe({ workKey: 'wrk_live_demo', region: 'cn' });
const tip = createTip(client);
const config = await tip.getConfig();

button.addEventListener('click', async () => {
  const result = await tip.open({
    amountCents: config.amount.minCents,
    provider: config.providers[0],
    locale: 'zh-CN',
    appearance: 'auto',
  });
  console.log(result.status);
});

function destroyTip() {
  tip.destroy();
  client.destroy();
}

Call destroyTip() from the owning component or route cleanup. Do not bind it to pagehide, because that event also fires when a page enters the bfcache.

getConfig() strictly parses the credential-free regional GET /v1/work-sdk/<encoded-workKey>/tip-config response. Call open() directly from user activation so it can create its secure full-screen frame in the same call stack. Only PAID, CANCELLED, and UNKNOWN cross back; no order number, token, payment action, or transaction ID is exposed. scene, metadata, and testMode are rejected or absent from the API.

The config environment remains server-authoritative. The SDK checks only that wrk_test_... is paired with SANDBOX and wrk_live_... with PRODUCTION, and rejects inconsistent responses.

The first Tip release is CN/CNY only. region: 'global' fails locally with CAPABILITY_DISABLED without a config request or iframe. Anonymous Tip does not use the legacy WeChat JSAPI user/OpenID path.

TIP_CONFIG_INVALID is a non-retryable Shop/SDK contract mismatch and should be reported rather than retried. TIP_OPEN_IN_PROGRESS means the existing call must settle first. TIP_READY_TIMEOUT is retryable after the failed call has cleaned itself up. On every component or route unmount, call tip.destroy() before client.destroy() so an in-flight flow settles as UNKNOWN and leaves no portal or message listener behind.

Immutable exact-version CDN ESM exports the same implementation from <origin>/viceme-sdk/<version>/index.js and tip.js without adding a window global.

Use the scoped fake in components and Storybook:

import { createTestTip } from '@viceme-ai/sdk/tip/testing';

const paid = createTestTip({ config, outcome: 'PAID' });
const failed = createTestTip({ config, outcome: new Error('fixture failure') });

The config may also be an Error. SANDBOX is always supplied by server config for a test Work, never by a production SDK switch.