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

@clickterm/widget

v2.7.1

Published

Browser SDK (TypeScript) that embeds Clickterm clickwrap agreements into host pages as a modal dialog or inline checkbox.

Readme

@clickterm/widget

Browser SDK (TypeScript) that embeds Clickterm clickwrap agreements into host pages, either as a modal dialog or an inline checkbox.

npm

Install

npm install @clickterm/widget

Or load it directly from a CDN (UMD build, exposes window.Clickterm):

<script src="https://unpkg.com/@clickterm/widget"></script>
<!-- or -->
<script src="https://cdn.jsdelivr.net/npm/@clickterm/widget"></script>

Quick start

Dialog (modal) — with a bundler

import { ClicktermClient, ClicktermDialog } from '@clickterm/widget';

ClicktermClient.initialize('YOUR_APP_ID');

const result = await ClicktermDialog.show(
  {
    endUserId: 'user_12345',
    clickwrapTemplateId: 'YOUR_TEMPLATE_ID',
    templatePlaceholders: {
      fullName: "Ada",
      customPlaceholders: {
        "custom1": "Custom 1"
      }
    }
  },
  {},
  {
    onSuccess: (signature) => console.log('signed:', signature),
    onAlreadyAccepted: () => console.log('already accepted'),
    onCancel: () => console.log('dismissed'),
    onError: (err) => console.error(err),
    onComplete: (result) => console.log('dialog complete:', result),
  },
);

Bundle dialog

Initialize the client once, then pass the bundle ID, your stable end-user ID, the requested language, and any placeholders grouped by template ID:

import {
  ClicktermClient,
  ClicktermDialog,
  type ClicktermDialogError,
} from '@clickterm/widget';

ClicktermClient.initialize('YOUR_APP_ID');
// Development: ClicktermClient.initialize('YOUR_APP_ID', 'https://api.dev.clickterm.com');

const result = await ClicktermDialog.showBundle(
  {
    clickwrapBundleId: 'YOUR_BUNDLE_ID',
    endUserId: 'user_12345',
    language: 'en',
    templatePlaceholdersByTemplateId: {
      'TEMPLATE_ID': {
        fullName: 'Ada Lovelace',
        customPlaceholders: { plan: 'Pro' },
      },
    },
  },
  {},
  {
    onSuccess: (signature) => {
      // Forward the signature to your backend for verification.
      console.log('bundle signature:', signature);
    },
    onAlreadyAccepted: () => console.log('bundle already accepted'),
    onCancel: () => console.log('bundle canceled'),
    onError: (error: ClicktermDialogError) => {
      console.error(error.code, error.status, error.message);
      if (error.status === 410) {
        // The rendered bundle snapshot is stale. Start a new bundle Request.
      }
    },
    onComplete: (terminalResult) => {
      // Runs once after success, already accepted, cancellation, or terminal error.
      console.log('bundle complete:', terminalResult);
    },
  },
);

console.log(result.clicktermSignature);

All listeners are optional; the returned promise remains authoritative. Listener exceptions are isolated from the SDK result. The lifecycle ordering is:

| Outcome | Callbacks | Promise | |---|---|---| | Accepted or declined | onSuccess(signature), then onComplete(result) | Resolves with the signature result | | Already accepted | onAlreadyAccepted(), then onComplete(result) | Resolves with isAlreadyAccepted: true | | User canceled | onCancel(), then onComplete(result) | Resolves with isCanceled: true | | Retryable load or Apply error | onError(error) | Dialog remains active; no completion yet | | Terminal error | onError(error), then onComplete(result) | Rejects with the same structured error |

onError receives an Error with optional code and HTTP status. Use onCancel, rather than inferring cancellation from an error fallback result, to classify an explicit user cancellation.

The bundle UI displays reviewable documents in order and unlocks later documents as the user proceeds. Accept requires every pending required document to be selected; unchecked optional documents are submitted as declined. Decline All is a separate action and declines every pending required and optional document. Only PENDING items are submitted—existing ACCEPTED and UNVERIFIED evidence is represented by the signed bundle request token. Previously accepted content is displayed checked and locked.

Bundle SCROLL/CHECKBOX agreement mode comes from the bundle customization. Visual theme and logo settings come from the bundle's first template. Bundle UI copy uses the first resolved template language and falls back to the requested language; translations are loaded from the environment-specific Clickterm CDN. The dialog exposes accessible headings, document names, requirement context, and labeled checkbox controls to screen readers.

Inline (checkbox in your own form) — via script tag

<div id="my-consent"></div>

<script src="https://unpkg.com/@clickterm/widget"></script>
<script>
  const { ClicktermClient, ClicktermDom } = window.Clickterm;

  ClicktermClient.initialize('YOUR_APP_ID');

  ClicktermDom.renderInline('my-consent', {
    endUserId: 'user_12345',
    clickwrapTemplateId: 'YOUR_TEMPLATE_ID',
  }, {
    // Optional lifecycle signals — the SDK renders no loading/error UI of its own.
    onLoading: () => showConsentLoader(),                 // request in flight
    onReady: ({ outcome }) => hideConsentLoader(),        // 'RENDERED' | 'ALREADY_ACCEPTED' | 'EXISTING_SIGNATURE'
    onError: (err) => showConsentError(err),              // same error the promise rejects with
  }).then((handle) => {
    document.getElementById('my-form').addEventListener('submit', async (e) => {
      e.preventDefault();
      const result = await handle.finalize();
      console.log(result.status, result.clicktermSignature);
    });
  }).catch((err) => {
    // onError updates the host UI; the rejection still needs handling.
    console.error('Failed to render inline clickwrap:', err);
  });
</script>

See docs/inline-clickwrap.md for the full inline integration guide, including the comprehensive semantic theme reference, validation rules, multiple clickwraps, placeholders, and edge cases.

Public API

All exports live on src/index.ts. Three static classes:

  • ClicktermClient.initialize(appId, baseUrl?) — configures the SDK. Must be called first.
  • ClicktermDialog.show(request, config?, listeners?) / showBundle(request, config?, listeners?) / showAcceptedContent(request, config?) — modal flows.
  • ClicktermDom.renderInline(containerId, request, options?) / finalizeAll(containerIds?) — inline checkbox flow. Inline options accept onChange, style, and the lifecycle callbacks onLoading / onReady / onError (onReady reports the render outcome: RENDERED, ALREADY_ACCEPTED, or EXISTING_SIGNATURE).
  • formatTimestamp(timestamp, settings?, includeTime?) — formats content timestamps using the response timezone, date, and clock settings.
  • formatNumber(value, settings?, decimalPlaces?) — formats a number using numberFormat; precision defaults to two decimal places.
  • formatCurrency(value, currencyCode, settings?, decimalPlaces?) — formats the supplied ISO 4217 currency using the independent currencyNumberFormat, currencyDisplay, and currencyPosition settings; precision defaults to two decimal places.

Clickwrap and bundle responses expose optional formattingSettings only at the response root. Content objects do not contain a separate formatting snapshot. Older backend versions may omit this metadata; the SDK preserves its legacy date formatting in that case.

The numeric formatters accept an explicit precision at each call site because the required precision depends on the displayed value rather than organization settings. Missing formatting metadata falls back to 1,234.50 and the default separators, symbol display, and before-amount position for the supplied currency.

TypeScript types for every request/response/option shape ship with the package.

Built-in HTTP retry policy

The SDK applies a fixed, internal retry policy to read/setup traffic only. It is not publicly configurable.

Eligible calls are:

  • POST /public-client/v1/clickwrap/request
  • GET /public-client/v1/clickwrap/content
  • GET /public-client/v1/clickwrap/customizations
  • POST /public-client/v1/clickwrap-bundle/request
  • GET /public-client/v1/clickwrap-bundle/customizations
  • the Clickterm CDN /sdk/clickterm-widget-translations.json translation request

Each eligible call gets at most two SDK-level attempts. Every attempt has a fresh five-second timeout. Before the second attempt, the SDK waits one second plus random jitter from zero to one second.

The SDK retries uncancelled transport and timeout failures, plus HTTP 408, 500, 502, 503, and 504. HTTP 429 is eligible only when Retry-After is a valid delta-seconds value or HTTP date resolving to zero through five seconds; the normal fixed backoff still applies. Cancellation, identifiable TLS/certificate failures, malformed requests, decoding failures, other 4xx responses, and other 5xx responses fail without retry. When an internal caller supplies a cancellation signal, cancellation also interrupts an active attempt or its backoff. The public dialog and inline loading calls do not expose a cancellation API, and no modal or inline handle exists until loading settles, so their active attempts remain bounded by the per-attempt timeout.

Agreement acceptance/decline (POST /public-client/v1/clickwrap and POST /public-client/v1/clickwrap-bundle) remains a one-shot request to avoid duplicate submissions. Browser-managed font, logo, and agreement-image loads are outside SDK retry handling. A customer backend verification call such as POST /clickwrap/verify is also outside this browser SDK and must use the customer's own transport policy.

Bundle tests

npm test -- --run tests/bundle-flow.spec.ts tests/dialog-callback-isolation.spec.ts

bundle-flow.spec.ts covers structured errors, accepted and unverified rows, step navigation, Decline All, accessibility names, customizations, and retry states. dialog-callback-isolation.spec.ts verifies callback ordering and that a host callback failure does not change the SDK result.

Local development

npm install
npm run start

Then open http://127.0.0.1:3000/ for the dialog demo or http://127.0.0.1:3000/inline.html for the inline demo. The Vite config also exposes a /cors-proxy?url=... endpoint so the demos can hit the Clickterm API directly from localhost.

Note on hot reload. If rollup's watch doesn't pick up changes reliably, this workaround works: npx nodemon --watch src --ext ts --exec "npm run start". No project-side changes needed.

Scripts

  • npm run start — runs watch:dev (rollup) and vite in parallel. Dev bundle written to public/dist/index.js.
  • npm run build — production build. Emits UMD, ESM, CJS, and .d.ts bundles under dist/.
  • npm run build:dev — one-shot dev bundle into public/dist/index.js.
  • npm run watch:dev — rollup in watch mode without starting Vite.
  • npm run types — emit .d.ts files only.

Project layout

  • src/index.ts — public exports (ClicktermClient, ClicktermDialog, ClicktermDom).
  • src/widget.ts — orchestrates the clickwrap lifecycle (fetch template → render → submit agreement).
  • src/client.tsHttpService wrapping the Clickterm /public-client/v1/clickwrap endpoints.
  • src/inline/ — inline-mode registry and handle implementations.
  • src/translations.ts — loads dialog translations from the environment-specific cdn.clickterm.com dev or production SDK path.

Release

  1. Bump version in package.json.
  2. Commit and push to main.
  3. Create a matching v* git tag (e.g. v2.3.0) and push it.
  4. Ensure the npm package @clickterm/widget has this GitHub repository configured as a trusted publisher.

The publish.yml workflow runs on any v* tag: it builds and publishes the package to npm using trusted publishing with --provenance. No NPM_TOKEN repo secret is required.

License

MIT