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

@cococopi/humanlock

v0.1.0

Published

Client-side automated-agent and AI computer-use detection for high-trust flows. Framework-agnostic core with React, Vue, Svelte and vanilla adapters, plus signed verdicts for server-side scoring.

Readme

humanlock

Client-side detection of automated agents and AI computer-use for high-trust flows — test and entrance-exam delivery, account opening, checkout, anything where "is a real person driving this?" matters.

Zero runtime dependencies. Framework-agnostic core with React, Vue, Svelte and vanilla bindings. Signed verdicts for server-side scoring.


Read this before you rely on it

This library raises the cost of automation and catches the naive majority. It is a layer, not a wall. Specifically:

  • A vision-driven agent — one that looks at screenshots and clicks by coordinate, which is how modern computer-use models operate — sees exactly the same pixels your user sees. Nothing client-side can show it a different screen. Honeypots are invisible to it because it never reads the DOM.
  • A determined adversary with a real browser profile, real input events, and humanised motion curves bypasses every check here.
  • Your client bundle is fully readable and patchable by whoever runs it. A client verdict is worth nothing on its own.

What this actually buys you: cheap, decisive detection of headless stacks, script-injected events, driver-synthesised input, DOM-driven agents, and unnaturally rigid motor behaviour — plus a signed evidence trail your server can score. Treat it as one input to a decision, never the decision.

It is not a substitute for live proctoring, identity verification, or server-side analysis of answer patterns.


What it looks at

| Category | Examples | Default weight | | --- | --- | --- | | environment | navigator.webdriver, automation user agents, empty plugin lists, software WebGL renderers, zero-sized viewports | 1.0 | | integrity | patched dispatchEvent / getBoundingClientRect / isTrusted, automation globals (__playwright, _phantom), ChromeDriver DOM markers | 1.2 | | input | events with isTrusted === false, pointer motion lacking device deltas | 1.1 | | honeypot | interaction with decoy controls, offscreen form-field traps, decoy removal | 1.6 | | behavior | pointer path straightness, micro-tremor residual, velocity constancy, positional teleports, typing cadence regularity, superhuman key intervals | 1.0 |

Each observation becomes a Signal with a severity. Signals are combined per category (probabilistically) and then across categories (weighted) into a risk score in 0..1, plus a confidence in how much evidence the score rests on.


Install

npm install @cococopi/humanlock

ESM and CJS builds, TypeScript types included. Node >= 18 if you use the server verifier.


Quick start

Vanilla

<script type="module">
  import { mount, shouldChallenge } from "@cococopi/humanlock";

  const guard = mount({ sessionKey: window.__HL_SESSION_KEY });

  document.addEventListener("submit", async (event) => {
    event.preventDefault();
    const assessment = guard.assess();
    const trace = await guard.report(); // signed, or null without a sessionKey
    await fetch("/api/session/verify", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ trace }),
    });
  });
</script>

React

import { useHumanLock } from "@cococopi/humanlock/react";
import { shouldChallenge } from "@cococopi/humanlock";

function StartExam({ sessionKey }: { sessionKey: string }) {
  const { assessment, report } = useHumanLock({ sessionKey });

  const risky = assessment ? shouldChallenge(assessment) : false;
  if (!assessment) return <p>Preparing…</p>;

  return (
    <button disabled={risky} onClick={() => void report()}>
      {risky ? "Verifying your session…" : "Begin"}
    </button>
  );
}

Vue

<script setup lang="ts">
import { useHumanLock } from "@cococopi/humanlock/vue";

const { assessment, report } = useHumanLock({ sessionKey: window.__HL_SESSION_KEY });
</script>

<template>
  <button :disabled="(assessment?.verdict ?? 'human') !== 'human'" @click="report">
    Begin
  </button>
</template>

Svelte

<script lang="ts">
  import { humanlock } from "@cococopi/humanlock/svelte";
  export let sessionKey: string;
</script>

<form use:humanlock={{ sessionKey }} on:submit={onSubmit}>…</form>

Server-side verification

The client's verdict is untrusted. Verify the signature, then apply your own policy.

import { verifyTrace } from "@cococopi/humanlock/server";

app.post("/api/session/verify", (req, res) => {
  const result = verifyTrace(req.body.trace, sessionKeyFor(req), {
    maxAgeMs: 5 * 60 * 1000,
    minConfidence: 0.4,
  });

  if (!result.valid) {
    // "low_confidence" means "gather more evidence", not "cheat detected".
    return res.json({ action: result.reason === "low_confidence" ? "observe" : "reject" });
  }

  if (result.payload.verdict === "automated") {
    return res.json({ action: "human_review" });
  }
  return res.json({ action: "allow" });
});

verifyTrace checks the HMAC, the algorithm tag, freshness, and minimum confidence, and uses a timing-safe comparison. It does not prove the client was honest — a user can read their own session key. It proves the payload was not edited or replayed across sessions. Reject reused nonce values in your own store to close the replay hole.


Configuration

createHumanLock({
  sessionKey: "per-session-secret", // required for signed traces
  strictMode: false,                // see the warning below
  observe: { pointer: true, keyboard: true, clipboard: true, focus: true, visibility: true },
  thresholds: { suspicious: 0.35, automated: 0.7 },
  weights: { honeypot: 1.6, integrity: 1.2 },
  honeypots: { enabled: true, count: 3, mode: "invisible", formFieldTrap: true },
  minPointerSamples: 12,
  minKeySamples: 6,
  environmentIntervalMs: 0,
  debug: false,
});

strictMode

When true, any signal marked automated short-circuits the verdict to "automated".

It defaults to false on purpose. Gating access on single automated-flagged signals — especially motor-precision behaviour scores — reliably fails screen-reader users, keyboard-only users, and people with motor impairments. That is discrimination, and in the US and EU it is an ADA / EAA problem, not just a support-ticket problem. Enable strictMode only behind a human review step.


Accessibility

  • Honeypots at the default mode: "invisible" are removed from the accessibility tree (aria-hidden) and from the tab order (tabindex="-1"), and are pointer-unreachable. No human can activate them by accident, including assistive-technology users.
  • mode: "visual" renders decoys a real person can see and click. It raises the catch rate against coordinate-driven agents at the cost of confusing real users. Choose deliberately.
  • Behavioural scores degrade for users with tremor, low dexterity, or atypical input devices. Never make a motor score the sole basis for locking someone out, and provide a human-review path.

Signals reference

Every signal carries { id, category, severity, automated, detail }. Common ids:

  • environment.webdriver_flag, environment.automation_user_agent, environment.software_renderer
  • integrity.dispatch_event_patched, integrity.get_bounding_client_rect_patched, integrity.automation_global, integrity.driver_dom_marker
  • input.synthetic_events, input.missing_pointer_delta
  • honeypot.interacted, honeypot.form_field_filled, honeypot.removed
  • behavior.pointer_teleport, behavior.pointer_dead_straight, behavior.pointer_no_tremor, behavior.pointer_constant_velocity, behavior.typing_metronomic, behavior.typing_superhuman

Use debug: true to log full assessments while integrating.


Performance notes

  • The decoy layer adds a fixed handful of hidden nodes and one MutationObserver. Negligible.
  • Pointer sampling is capped (BehaviorAnalyzer retains the most recent 600 samples by default) so long sessions cannot grow memory without bound.
  • assess() is synchronous and allocation-light; call it on submit rather than on every frame. The React and Vue bindings poll once per second; drop the interval if you only need submit-time evaluation.

Development

npm install
npm run typecheck
npm test
npm run build

License

MIT