usehuma 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # usehuma
2
+
3
+ **Privacy-first human verification SDK.**
4
+ No CAPTCHA. No Cloudflare. No PII collected. Just invisible behavioral biometrics.
5
+
6
+ [![npm](https://img.shields.io/npm/v/usehuma)](https://www.npmjs.com/package/usehuma)
7
+ [![license](https://img.shields.io/npm/l/usehuma)](./LICENSE)
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install usehuma
15
+ # or
16
+ yarn add usehuma
17
+ # or
18
+ pnpm add usehuma
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick start β€” Vanilla JS
24
+
25
+ ```js
26
+ import { init, verify } from 'usehuma';
27
+
28
+ // Start collecting signals as early as possible
29
+ init();
30
+
31
+ // Later β€” on form submit, signup, checkout:
32
+ const result = await verify({
33
+ apiKey: 'huma_live_...',
34
+ userId: 'user_123',
35
+ });
36
+
37
+ if (result.human) {
38
+ // βœ… proceed
39
+ } else {
40
+ // 🚫 block or challenge
41
+ }
42
+ ```
43
+
44
+ ---
45
+
46
+ ## React hook
47
+
48
+ ```tsx
49
+ import { useHuma } from 'usehuma/react';
50
+
51
+ function SignupForm() {
52
+ const { verify, loading, isHuman, score } = useHuma({
53
+ apiKey: 'huma_live_...',
54
+ userId: session.userId,
55
+ });
56
+
57
+ const handleSubmit = async (e) => {
58
+ e.preventDefault();
59
+ const result = await verify();
60
+ if (result?.human) {
61
+ // proceed with signup
62
+ }
63
+ };
64
+
65
+ return (
66
+ <form onSubmit={handleSubmit}>
67
+ {/* your form fields */}
68
+ <button type="submit" disabled={loading}>
69
+ {loading ? 'Verifying…' : 'Create account'}
70
+ </button>
71
+ {score !== null && <p>Trust score: {score}%</p>}
72
+ </form>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## HumaGate β€” conditional rendering
80
+
81
+ ```tsx
82
+ import { HumaGate } from 'usehuma/react';
83
+
84
+ <HumaGate
85
+ apiKey="huma_live_..."
86
+ userId={userId}
87
+ fallback={<div>Checking...</div>}
88
+ blocked={<div>Access denied.</div>}
89
+ >
90
+ <SensitiveContent />
91
+ </HumaGate>
92
+ ```
93
+
94
+ ---
95
+
96
+ ## HumaProvider β€” global context
97
+
98
+ ```tsx
99
+ import { HumaProvider, useHumaContext } from 'usehuma/react';
100
+
101
+ // Wrap your app:
102
+ <HumaProvider apiKey="huma_live_..." userId={userId} autoVerify>
103
+ <App />
104
+ </HumaProvider>
105
+
106
+ // Use anywhere inside:
107
+ function AnyChild() {
108
+ const { isHuman, score } = useHumaContext();
109
+ return <div>Human: {String(isHuman)} | Score: {score}%</div>;
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## HumaVerifyButton
116
+
117
+ ```tsx
118
+ import { HumaVerifyButton } from 'usehuma/react';
119
+
120
+ <HumaVerifyButton
121
+ apiKey="huma_live_..."
122
+ userId={userId}
123
+ onVerified={(result) => console.log('Human!', result.confidence)}
124
+ onBlocked={() => alert('Bot detected')}
125
+ >
126
+ Submit Order
127
+ </HumaVerifyButton>
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Next.js App Router
133
+
134
+ Works out of the box. All React exports include `"use client"`.
135
+
136
+ ```tsx
137
+ // app/signup/page.tsx
138
+ import { HumaGate } from 'usehuma/react';
139
+
140
+ export default function SignupPage() {
141
+ return (
142
+ <HumaGate apiKey={process.env.NEXT_PUBLIC_HUMA_KEY!} userId="anonymous">
143
+ <SignupForm />
144
+ </HumaGate>
145
+ );
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## API response
152
+
153
+ ```ts
154
+ {
155
+ human: boolean; // true = human verified
156
+ confidence: number; // 0–1 trust score
157
+ threshold: number; // your configured threshold
158
+ token: string; // h_verified_... verification proof
159
+ pii_stored: false; // we never store personal data
160
+ }
161
+ ```
162
+
163
+ ---
164
+
165
+ ## What signals are collected?
166
+
167
+ | Signal | What we measure |
168
+ |---|---|
169
+ | Mouse movement | Speed variance, direction changes |
170
+ | Keyboard | Keystroke interval variance |
171
+ | Scroll | Rhythm and timing variance |
172
+ | Clicks | Interval variance |
173
+ | Tab focus | Focus/blur events |
174
+ | Time on page | Dwell time |
175
+
176
+ **No IP address. No device fingerprint. No personal data. Ever.**
177
+
178
+ ---
179
+
180
+ ## Get your API key
181
+
182
+ β†’ [humaverify.com](https://humaverify.com)
183
+
184
+ ---
185
+
186
+ ## License
187
+
188
+ MIT Β© [UseHUMA](https://humaverify.com)
@@ -0,0 +1,90 @@
1
+ /**
2
+ * useHUMA β€” TypeScript Types
3
+ * https://humaverify.com
4
+ */
5
+ type SessionData = {
6
+ time_on_page_ms?: number;
7
+ key_interval_cv?: number;
8
+ key_count?: number;
9
+ scroll_interval_cv?: number;
10
+ scroll_count?: number;
11
+ mouse_speed_cv?: number;
12
+ mouse_direction_changes?: number;
13
+ mouse_sample_count?: number;
14
+ click_interval_cv?: number;
15
+ click_count?: number;
16
+ tab_switches?: number;
17
+ accessibility?: boolean;
18
+ };
19
+ type HumaVerifyResult = {
20
+ human: boolean;
21
+ confidence: number;
22
+ threshold: number;
23
+ token: string;
24
+ pii_stored: false;
25
+ };
26
+ type HumaError = {
27
+ error: string;
28
+ code?: string;
29
+ upgrade_url?: string;
30
+ retry_after?: number;
31
+ };
32
+ type HumaOptions = {
33
+ /** Your HUMA API key (huma_live_...) */
34
+ apiKey: string;
35
+ /** Your internal user ID */
36
+ userId: string;
37
+ /** Override the API endpoint (default: https://humaverify.com/api/v1/verify) */
38
+ endpoint?: string;
39
+ };
40
+ type HumaState = {
41
+ loading: boolean;
42
+ result: HumaVerifyResult | null;
43
+ error: HumaError | null;
44
+ /** true = verified human, false = bot, null = not yet verified */
45
+ isHuman: boolean | null;
46
+ /** 0–100 confidence score */
47
+ score: number | null;
48
+ /** Call manually to trigger verification */
49
+ verify: () => Promise<HumaVerifyResult | null>;
50
+ };
51
+
52
+ /**
53
+ * useHUMA β€” Core Signal Collector
54
+ * Invisible behavioral biometrics β€” no PII collected.
55
+ */
56
+
57
+ declare class HumaCollector {
58
+ private signals;
59
+ private listening;
60
+ private onMouseMove;
61
+ private onKeyDown;
62
+ private onScroll;
63
+ private onClick;
64
+ private onFocus;
65
+ private onBlur;
66
+ start(): void;
67
+ stop(): void;
68
+ extract(): SessionData;
69
+ debug(): Record<string, unknown>;
70
+ }
71
+ /** Send extracted signals to the HUMA API and return a result. */
72
+ declare function callHumaApi(opts: HumaOptions, sessionData: SessionData): Promise<HumaVerifyResult>;
73
+
74
+ /**
75
+ * useHUMA JavaScript SDK
76
+ * Privacy-first human verification β€” no PII, no Cloudflare dependency.
77
+ * https://humaverify.com
78
+ */
79
+
80
+ /** Auto-start signal collection (call once, early in your app). */
81
+ declare function init(): void;
82
+ /**
83
+ * Verify the current user as human using collected behavioral signals.
84
+ * Calls init() automatically if not already started.
85
+ */
86
+ declare function verify(opts: HumaOptions): Promise<HumaVerifyResult>;
87
+ /** Get raw collected signals β€” useful for debugging. */
88
+ declare function debug(): Record<string, unknown> | null;
89
+
90
+ export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, verify };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * useHUMA β€” TypeScript Types
3
+ * https://humaverify.com
4
+ */
5
+ type SessionData = {
6
+ time_on_page_ms?: number;
7
+ key_interval_cv?: number;
8
+ key_count?: number;
9
+ scroll_interval_cv?: number;
10
+ scroll_count?: number;
11
+ mouse_speed_cv?: number;
12
+ mouse_direction_changes?: number;
13
+ mouse_sample_count?: number;
14
+ click_interval_cv?: number;
15
+ click_count?: number;
16
+ tab_switches?: number;
17
+ accessibility?: boolean;
18
+ };
19
+ type HumaVerifyResult = {
20
+ human: boolean;
21
+ confidence: number;
22
+ threshold: number;
23
+ token: string;
24
+ pii_stored: false;
25
+ };
26
+ type HumaError = {
27
+ error: string;
28
+ code?: string;
29
+ upgrade_url?: string;
30
+ retry_after?: number;
31
+ };
32
+ type HumaOptions = {
33
+ /** Your HUMA API key (huma_live_...) */
34
+ apiKey: string;
35
+ /** Your internal user ID */
36
+ userId: string;
37
+ /** Override the API endpoint (default: https://humaverify.com/api/v1/verify) */
38
+ endpoint?: string;
39
+ };
40
+ type HumaState = {
41
+ loading: boolean;
42
+ result: HumaVerifyResult | null;
43
+ error: HumaError | null;
44
+ /** true = verified human, false = bot, null = not yet verified */
45
+ isHuman: boolean | null;
46
+ /** 0–100 confidence score */
47
+ score: number | null;
48
+ /** Call manually to trigger verification */
49
+ verify: () => Promise<HumaVerifyResult | null>;
50
+ };
51
+
52
+ /**
53
+ * useHUMA β€” Core Signal Collector
54
+ * Invisible behavioral biometrics β€” no PII collected.
55
+ */
56
+
57
+ declare class HumaCollector {
58
+ private signals;
59
+ private listening;
60
+ private onMouseMove;
61
+ private onKeyDown;
62
+ private onScroll;
63
+ private onClick;
64
+ private onFocus;
65
+ private onBlur;
66
+ start(): void;
67
+ stop(): void;
68
+ extract(): SessionData;
69
+ debug(): Record<string, unknown>;
70
+ }
71
+ /** Send extracted signals to the HUMA API and return a result. */
72
+ declare function callHumaApi(opts: HumaOptions, sessionData: SessionData): Promise<HumaVerifyResult>;
73
+
74
+ /**
75
+ * useHUMA JavaScript SDK
76
+ * Privacy-first human verification β€” no PII, no Cloudflare dependency.
77
+ * https://humaverify.com
78
+ */
79
+
80
+ /** Auto-start signal collection (call once, early in your app). */
81
+ declare function init(): void;
82
+ /**
83
+ * Verify the current user as human using collected behavioral signals.
84
+ * Calls init() automatically if not already started.
85
+ */
86
+ declare function verify(opts: HumaOptions): Promise<HumaVerifyResult>;
87
+ /** Get raw collected signals β€” useful for debugging. */
88
+ declare function debug(): Record<string, unknown> | null;
89
+
90
+ export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, verify };
package/dist/index.js ADDED
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ HumaCollector: () => HumaCollector,
24
+ callHumaApi: () => callHumaApi,
25
+ debug: () => debug,
26
+ init: () => init,
27
+ verify: () => verify
28
+ });
29
+ module.exports = __toCommonJS(src_exports);
30
+
31
+ // src/core.ts
32
+ function cv(arr) {
33
+ if (arr.length < 2) return 0;
34
+ const mean = arr.reduce((a, b) => a + b, 0) / arr.length;
35
+ if (mean === 0) return 0;
36
+ const variance = arr.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / arr.length;
37
+ return Math.sqrt(variance) / mean;
38
+ }
39
+ function mouseFeatures(pts) {
40
+ if (pts.length < 5) return { speed_cv: 0, direction_changes: 0, sample_count: 0 };
41
+ const speeds = [];
42
+ const directions = [];
43
+ for (let i = 1; i < pts.length; i++) {
44
+ const dx = pts[i].x - pts[i - 1].x;
45
+ const dy = pts[i].y - pts[i - 1].y;
46
+ const dt = Math.max(pts[i].t - pts[i - 1].t, 1);
47
+ speeds.push(Math.sqrt(dx * dx + dy * dy) / dt);
48
+ directions.push(Math.atan2(dy, dx));
49
+ }
50
+ let dirChanges = 0;
51
+ for (let j = 1; j < directions.length; j++) {
52
+ if (Math.abs(directions[j] - directions[j - 1]) > 0.3) dirChanges++;
53
+ }
54
+ return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };
55
+ }
56
+ var HumaCollector = class {
57
+ constructor() {
58
+ this.signals = {
59
+ mouse: [],
60
+ keys: [],
61
+ scrolls: [],
62
+ clicks: [],
63
+ focusEvents: [],
64
+ startTime: Date.now(),
65
+ lastKeyTime: null,
66
+ lastScrollTime: null
67
+ };
68
+ this.listening = false;
69
+ this.onMouseMove = (e) => {
70
+ if (this.signals.mouse.length < 200)
71
+ this.signals.mouse.push({ x: e.clientX, y: e.clientY, t: Date.now() });
72
+ };
73
+ this.onKeyDown = () => {
74
+ const now = Date.now();
75
+ if (this.signals.lastKeyTime !== null && this.signals.keys.length < 100)
76
+ this.signals.keys.push({ interval: now - this.signals.lastKeyTime });
77
+ this.signals.lastKeyTime = now;
78
+ };
79
+ this.onScroll = () => {
80
+ const now = Date.now();
81
+ if (this.signals.lastScrollTime !== null && this.signals.scrolls.length < 50)
82
+ this.signals.scrolls.push({ interval: now - this.signals.lastScrollTime });
83
+ this.signals.lastScrollTime = now;
84
+ };
85
+ this.onClick = () => {
86
+ if (this.signals.clicks.length < 50)
87
+ this.signals.clicks.push({ t: Date.now() });
88
+ };
89
+ this.onFocus = () => this.signals.focusEvents.push({ type: "focus", t: Date.now() });
90
+ this.onBlur = () => this.signals.focusEvents.push({ type: "blur", t: Date.now() });
91
+ }
92
+ start() {
93
+ if (this.listening || typeof window === "undefined") return;
94
+ this.listening = true;
95
+ document.addEventListener("mousemove", this.onMouseMove, { passive: true });
96
+ document.addEventListener("keydown", this.onKeyDown, { passive: true });
97
+ document.addEventListener("scroll", this.onScroll, { passive: true });
98
+ document.addEventListener("click", this.onClick, { passive: true });
99
+ window.addEventListener("focus", this.onFocus, { passive: true });
100
+ window.addEventListener("blur", this.onBlur, { passive: true });
101
+ }
102
+ stop() {
103
+ if (!this.listening) return;
104
+ this.listening = false;
105
+ document.removeEventListener("mousemove", this.onMouseMove);
106
+ document.removeEventListener("keydown", this.onKeyDown);
107
+ document.removeEventListener("scroll", this.onScroll);
108
+ document.removeEventListener("click", this.onClick);
109
+ window.removeEventListener("focus", this.onFocus);
110
+ window.removeEventListener("blur", this.onBlur);
111
+ }
112
+ extract() {
113
+ const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;
114
+ const m = mouseFeatures(mouse);
115
+ const clickIntervals = [];
116
+ for (let i = 1; i < clicks.length; i++)
117
+ clickIntervals.push(clicks[i].t - clicks[i - 1].t);
118
+ return {
119
+ time_on_page_ms: Date.now() - startTime,
120
+ key_interval_cv: cv(keys.map((k) => k.interval)),
121
+ key_count: keys.length,
122
+ scroll_interval_cv: cv(scrolls.map((s) => s.interval)),
123
+ scroll_count: scrolls.length,
124
+ mouse_speed_cv: m.speed_cv,
125
+ mouse_direction_changes: m.direction_changes,
126
+ mouse_sample_count: m.sample_count,
127
+ click_interval_cv: cv(clickIntervals),
128
+ click_count: clicks.length,
129
+ tab_switches: focusEvents.length
130
+ };
131
+ }
132
+ debug() {
133
+ return { signals: this.signals, features: this.extract() };
134
+ }
135
+ };
136
+ async function callHumaApi(opts, sessionData) {
137
+ var _a;
138
+ const endpoint = (_a = opts.endpoint) != null ? _a : "https://humaverify.com/api/v1/verify";
139
+ const res = await fetch(endpoint, {
140
+ method: "POST",
141
+ headers: {
142
+ Authorization: `Bearer ${opts.apiKey}`,
143
+ "Content-Type": "application/json"
144
+ },
145
+ body: JSON.stringify({ userId: opts.userId, sessionData })
146
+ });
147
+ if (!res.ok) {
148
+ const err = await res.json().catch(() => ({ error: "Unknown error" }));
149
+ throw err;
150
+ }
151
+ return res.json();
152
+ }
153
+
154
+ // src/index.ts
155
+ var _globalCollector = null;
156
+ function init() {
157
+ if (typeof window === "undefined") return;
158
+ if (_globalCollector) return;
159
+ _globalCollector = new HumaCollector();
160
+ _globalCollector.start();
161
+ }
162
+ async function verify(opts) {
163
+ if (!_globalCollector) init();
164
+ const sessionData = _globalCollector.extract();
165
+ return callHumaApi(opts, sessionData);
166
+ }
167
+ function debug() {
168
+ var _a;
169
+ return (_a = _globalCollector == null ? void 0 : _globalCollector.debug()) != null ? _a : null;
170
+ }
171
+ // Annotate the CommonJS export names for ESM import in node:
172
+ 0 && (module.exports = {
173
+ HumaCollector,
174
+ callHumaApi,
175
+ debug,
176
+ init,
177
+ verify
178
+ });
179
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["/**\n * useHUMA JavaScript SDK\n * Privacy-first human verification β€” no PII, no Cloudflare dependency.\n * https://humaverify.com\n */\n\nexport { HumaCollector, callHumaApi } from \"./core\";\nexport type {\n SessionData,\n HumaVerifyResult,\n HumaError,\n HumaOptions,\n HumaState,\n} from \"./types\";\n\n/**\n * Vanilla JS verify β€” for non-React apps.\n *\n * @example\n * import { verify } from 'usehuma';\n *\n * const result = await verify({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * });\n * if (result.human) { ... }\n */\nimport { HumaCollector, callHumaApi } from \"./core\";\nimport type { HumaOptions, HumaVerifyResult } from \"./types\";\n\nlet _globalCollector: HumaCollector | null = null;\n\n/** Auto-start signal collection (call once, early in your app). */\nexport function init() {\n if (typeof window === \"undefined\") return;\n if (_globalCollector) return;\n _globalCollector = new HumaCollector();\n _globalCollector.start();\n}\n\n/**\n * Verify the current user as human using collected behavioral signals.\n * Calls init() automatically if not already started.\n */\nexport async function verify(opts: HumaOptions): Promise<HumaVerifyResult> {\n if (!_globalCollector) init();\n const sessionData = _globalCollector!.extract();\n return callHumaApi(opts, sessionData);\n}\n\n/** Get raw collected signals β€” useful for debugging. */\nexport function debug(): Record<string, unknown> | null {\n return _globalCollector?.debug() as Record<string, unknown> | null ?? null;\n}\n","/**\n * useHUMA β€” Core Signal Collector\n * Invisible behavioral biometrics β€” no PII collected.\n */\n\nimport type { SessionData, HumaVerifyResult, HumaError, HumaOptions } from \"./types\";\n\ntype MousePoint = { x: number; y: number; t: number };\ntype KeyEntry = { interval: number };\ntype ScrollEntry = { interval: number };\ntype ClickEntry = { t: number };\ntype FocusEvent = { type: \"focus\" | \"blur\"; t: number };\n\ninterface Signals {\n mouse: MousePoint[];\n keys: KeyEntry[];\n scrolls: ScrollEntry[];\n clicks: ClickEntry[];\n focusEvents: FocusEvent[];\n startTime: number;\n lastKeyTime: number | null;\n lastScrollTime: number | null;\n}\n\nfunction cv(arr: number[]): number {\n if (arr.length < 2) return 0;\n const mean = arr.reduce((a, b) => a + b, 0) / arr.length;\n if (mean === 0) return 0;\n const variance = arr.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / arr.length;\n return Math.sqrt(variance) / mean;\n}\n\nfunction mouseFeatures(pts: MousePoint[]) {\n if (pts.length < 5) return { speed_cv: 0, direction_changes: 0, sample_count: 0 };\n const speeds: number[] = [];\n const directions: number[] = [];\n for (let i = 1; i < pts.length; i++) {\n const dx = pts[i].x - pts[i - 1].x;\n const dy = pts[i].y - pts[i - 1].y;\n const dt = Math.max(pts[i].t - pts[i - 1].t, 1);\n speeds.push(Math.sqrt(dx * dx + dy * dy) / dt);\n directions.push(Math.atan2(dy, dx));\n }\n let dirChanges = 0;\n for (let j = 1; j < directions.length; j++) {\n if (Math.abs(directions[j] - directions[j - 1]) > 0.3) dirChanges++;\n }\n return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };\n}\n\nexport class HumaCollector {\n private signals: Signals = {\n mouse: [], keys: [], scrolls: [], clicks: [], focusEvents: [],\n startTime: Date.now(), lastKeyTime: null, lastScrollTime: null,\n };\n private listening = false;\n\n private onMouseMove = (e: MouseEvent) => {\n if (this.signals.mouse.length < 200)\n this.signals.mouse.push({ x: e.clientX, y: e.clientY, t: Date.now() });\n };\n private onKeyDown = () => {\n const now = Date.now();\n if (this.signals.lastKeyTime !== null && this.signals.keys.length < 100)\n this.signals.keys.push({ interval: now - this.signals.lastKeyTime });\n this.signals.lastKeyTime = now;\n };\n private onScroll = () => {\n const now = Date.now();\n if (this.signals.lastScrollTime !== null && this.signals.scrolls.length < 50)\n this.signals.scrolls.push({ interval: now - this.signals.lastScrollTime });\n this.signals.lastScrollTime = now;\n };\n private onClick = () => {\n if (this.signals.clicks.length < 50)\n this.signals.clicks.push({ t: Date.now() });\n };\n private onFocus = () => this.signals.focusEvents.push({ type: \"focus\", t: Date.now() });\n private onBlur = () => this.signals.focusEvents.push({ type: \"blur\", t: Date.now() });\n\n start() {\n if (this.listening || typeof window === \"undefined\") return;\n this.listening = true;\n document.addEventListener(\"mousemove\", this.onMouseMove, { passive: true });\n document.addEventListener(\"keydown\", this.onKeyDown, { passive: true });\n document.addEventListener(\"scroll\", this.onScroll, { passive: true });\n document.addEventListener(\"click\", this.onClick, { passive: true });\n window.addEventListener(\"focus\", this.onFocus, { passive: true });\n window.addEventListener(\"blur\", this.onBlur, { passive: true });\n }\n\n stop() {\n if (!this.listening) return;\n this.listening = false;\n document.removeEventListener(\"mousemove\", this.onMouseMove);\n document.removeEventListener(\"keydown\", this.onKeyDown);\n document.removeEventListener(\"scroll\", this.onScroll);\n document.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"focus\", this.onFocus);\n window.removeEventListener(\"blur\", this.onBlur);\n }\n\n extract(): SessionData {\n const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;\n const m = mouseFeatures(mouse);\n const clickIntervals: number[] = [];\n for (let i = 1; i < clicks.length; i++)\n clickIntervals.push(clicks[i].t - clicks[i - 1].t);\n return {\n time_on_page_ms: Date.now() - startTime,\n key_interval_cv: cv(keys.map(k => k.interval)),\n key_count: keys.length,\n scroll_interval_cv: cv(scrolls.map(s => s.interval)),\n scroll_count: scrolls.length,\n mouse_speed_cv: m.speed_cv,\n mouse_direction_changes: m.direction_changes,\n mouse_sample_count: m.sample_count,\n click_interval_cv: cv(clickIntervals),\n click_count: clicks.length,\n tab_switches: focusEvents.length,\n };\n }\n\n debug(): Record<string, unknown> {\n return { signals: this.signals as unknown, features: this.extract() };\n }\n}\n\n/** Send extracted signals to the HUMA API and return a result. */\nexport async function callHumaApi(\n opts: HumaOptions,\n sessionData: SessionData\n): Promise<HumaVerifyResult> {\n const endpoint = opts.endpoint ?? \"https://humaverify.com/api/v1/verify\";\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ userId: opts.userId, sessionData }),\n });\n if (!res.ok) {\n const err: HumaError = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw err;\n }\n return res.json() as Promise<HumaVerifyResult>;\n}\n"],"mappings":"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","names":[]}