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

@mochi.js/challenges

v0.2.1

Published

Convenience layer for Cloudflare Turnstile auto-click — opt-in, behavioral-synth-backed, no new fingerprint surface.

Readme

@mochi.js/challenges

Convenience layer for common bot-defense challenge widgets in mochi.

v0.2 scope: Cloudflare Turnstile visible-checkbox auto-click only.

Out of scope (deferred):

  • hCaptcha — same shape, separate task (v0.3)
  • reCAPTCHA v2 / v3 — different mechanism (audio / visual challenges)
  • 3rd-party solver API integrations (2captcha / anti-captcha) — v0.3+ via onEscalation

What this is — and isn't

This is not a captcha solver. The visible Turnstile checkbox is a behavioral test: Cloudflare watches the cursor trajectory, the dwell, and a few hundred other signals around the click. The hard part is the behavioral profile, which mochi already does (@mochi.js/behavioral's Bezier+Fitts synth, the inject pipeline's matrix consistency, the wreq TLS fingerprint). The actual click is the easy part — this package exists so you don't have to write page.humanClick('iframe[src*="challenges.cloudflare.com"]') yourself in every flow.

For escalated variants (image / audio / managed-failed), this package fires onEscalation(reason) and bails. It will not click randomly into a challenge iframe.

Install

This package ships with @mochi.js/core v0.2+. You don't add it to your project directly.

Usage

Recommended: launch option

import { mochi } from "@mochi.js/core";

const session = await mochi.launch({
  profile: "...",
  seed: "...",
  challenges: {
    turnstile: {
      autoClick: true,
      timeout: 30_000,
      onSolved: (token) => console.log("turnstile passed:", token.slice(0, 8) + "…"),
      onEscalation: (reason) => console.warn("turnstile escalation:", reason),
    },
  },
});

// Every page from this session auto-clicks Turnstile.
const page = await session.newPage();
await page.goto("https://example.com");

Manual: installTurnstileAutoClick

import { installTurnstileAutoClick } from "@mochi.js/challenges";

const session = await mochi.launch({ profile: "...", seed: "..." });
const page = await session.newPage();
const dispose = installTurnstileAutoClick(page, {
  timeout: 30_000,
  onSolved: () => console.log("turnstile passed"),
  onEscalation: (reason) => console.warn("escalation:", reason),
});

await page.goto("https://example.com");
// ... do stuff ...
dispose();

How it works

  1. Detection. A small inject script is mounted on the page's main world via Page.addScriptToEvaluateOnNewDocument({ runImmediately: true, worldName: "" }) (PLAN.md §8.4). The script installs a MutationObserver filtered to iframe inserts only — it does not fire on every DOM mutation.
  2. Channel. When a Turnstile iframe is detected, the inject emits a tagged console.debug({__mochi_event:"turnstile-detected", …}) event and exposes a Symbol-keyed snapshot reader on document (the only externally observable surface; non-enumerable + non-configurable so page script can't tamper).
  3. Click. The mochi-side poller drains the snapshot, finds the iframe via DOM.getBoxModel, and dispatches a click via page.humanClick(...) — the same Bezier+Fitts synth the rest of the framework uses. We never reinvent the synth.
  4. Token. After the click, the inject reader watches the parent page's hidden cf-turnstile-response field. When a token appears, onSolved(token) fires.
  5. Escalation. If the iframe src matches /challenge.html (image/audio) or /managed.html (failed-bot variant), or the token doesn't appear within opts.timeout, onEscalation(reason) fires and we bail on that widget.

Invariants

  • Uses existing behavioral synth — Bezier path + Fitts's-Law dwell from @mochi.js/behavioral. No new fingerprint surface.
  • No new globals — the inject script's only observable property is a Symbol-keyed function on document. The Symbol is non-enumerable, writable:false, configurable:false.
  • No new postMessage handlers, no new event listeners on window.
  • Iframe-only MutationObserver filter — perf invariant; the observer rejects mutations cheaply before doing any work.
  • PLAN.md §8.2 — never sends Runtime.enable. Detection is poll-based via Runtime.callFunctionOn against the document objectId.
  • PLAN.md §8.4 — main world (worldName: "") for the inject script. Any non-empty world name is detectable.

When to bring a 3rd-party solver

Roughly: the visible-checkbox flow covers the common case. If your target consistently escalates to image / audio challenges, that's a signal that mochi's stealth posture isn't passing the bot heuristics — fix the upstream signal first, then reach for a solver.

The onEscalation callback receives "image-challenge" | "managed" | "timeout" and lets you fire your solver of choice. v0.3 will ship a first-party solver hook surface.

Reference