usehuma 1.0.0 → 1.1.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/dist/index.d.mts CHANGED
@@ -14,6 +14,13 @@ type SessionData = {
14
14
  click_interval_cv?: number;
15
15
  click_count?: number;
16
16
  tab_switches?: number;
17
+ tap_count?: number;
18
+ tap_interval_cv?: number;
19
+ tap_duration_cv?: number;
20
+ tap_force_cv?: number;
21
+ touch_move_count?: number;
22
+ touch_speed_cv?: number;
23
+ multi_touch_count?: number;
17
24
  accessibility?: boolean;
18
25
  };
19
26
  type HumaVerifyResult = {
@@ -63,6 +70,9 @@ declare class HumaCollector {
63
70
  private onClick;
64
71
  private onFocus;
65
72
  private onBlur;
73
+ private onTouchStart;
74
+ private onTouchEnd;
75
+ private onTouchMove;
66
76
  start(): void;
67
77
  stop(): void;
68
78
  extract(): SessionData;
@@ -71,12 +81,6 @@ declare class HumaCollector {
71
81
  /** Send extracted signals to the HUMA API and return a result. */
72
82
  declare function callHumaApi(opts: HumaOptions, sessionData: SessionData): Promise<HumaVerifyResult>;
73
83
 
74
- /**
75
- * useHUMA JavaScript SDK
76
- * Privacy-first human verification — no PII, no Cloudflare dependency.
77
- * https://humaverify.com
78
- */
79
-
80
84
  /** Auto-start signal collection (call once, early in your app). */
81
85
  declare function init(): void;
82
86
  /**
@@ -86,5 +90,60 @@ declare function init(): void;
86
90
  declare function verify(opts: HumaOptions): Promise<HumaVerifyResult>;
87
91
  /** Get raw collected signals — useful for debugging. */
88
92
  declare function debug(): Record<string, unknown> | null;
93
+ /**
94
+ * Server-side verify — pass pre-collected signals forwarded from the browser.
95
+ * Use this in your API routes / server actions when you want the final
96
+ * verify call to happen on your backend, not the browser.
97
+ *
98
+ * @example
99
+ * // In your Next.js API route:
100
+ * import { verifyWithSignals } from 'usehuma';
101
+ *
102
+ * const result = await verifyWithSignals({
103
+ * apiKey: process.env.HUMA_API_KEY!,
104
+ * userId: req.body.userId,
105
+ * signals: req.body.humaSignals, // forwarded from browser via Huma.debug()
106
+ * });
107
+ */
108
+ declare function verifyWithSignals(opts: {
109
+ apiKey: string;
110
+ userId: string;
111
+ signals: SessionData;
112
+ endpoint?: string;
113
+ }): Promise<HumaVerifyResult>;
114
+ /**
115
+ * Request a step-up challenge when verify() confidence is borderline (30–70%).
116
+ * Returns challenge data to render to the user.
117
+ *
118
+ * @example
119
+ * const result = await verify({ apiKey, userId });
120
+ * if (!result.human && result.confidence > 0.3) {
121
+ * const ch = await requestChallenge({ apiKey, userId });
122
+ * // Render ch.payload to user, collect answer, then:
123
+ * const solved = await solveChallenge({ apiKey, challengeId: ch.challenge_id, answer });
124
+ * if (solved.passed) continueLogin(solved.token);
125
+ * }
126
+ */
127
+ declare function requestChallenge(opts: {
128
+ apiKey: string;
129
+ userId: string;
130
+ endpoint?: string;
131
+ }): Promise<{
132
+ challenge_id: string;
133
+ type: string;
134
+ payload: Record<string, unknown>;
135
+ expires_in_seconds: number;
136
+ }>;
137
+ declare function solveChallenge(opts: {
138
+ apiKey: string;
139
+ challengeId: string;
140
+ answer: string;
141
+ endpoint?: string;
142
+ }): Promise<{
143
+ passed: boolean;
144
+ token?: string;
145
+ message?: string;
146
+ attempts_remaining?: number;
147
+ }>;
89
148
 
90
- export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, verify };
149
+ export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, requestChallenge, solveChallenge, verify, verifyWithSignals };
package/dist/index.d.ts CHANGED
@@ -14,6 +14,13 @@ type SessionData = {
14
14
  click_interval_cv?: number;
15
15
  click_count?: number;
16
16
  tab_switches?: number;
17
+ tap_count?: number;
18
+ tap_interval_cv?: number;
19
+ tap_duration_cv?: number;
20
+ tap_force_cv?: number;
21
+ touch_move_count?: number;
22
+ touch_speed_cv?: number;
23
+ multi_touch_count?: number;
17
24
  accessibility?: boolean;
18
25
  };
19
26
  type HumaVerifyResult = {
@@ -63,6 +70,9 @@ declare class HumaCollector {
63
70
  private onClick;
64
71
  private onFocus;
65
72
  private onBlur;
73
+ private onTouchStart;
74
+ private onTouchEnd;
75
+ private onTouchMove;
66
76
  start(): void;
67
77
  stop(): void;
68
78
  extract(): SessionData;
@@ -71,12 +81,6 @@ declare class HumaCollector {
71
81
  /** Send extracted signals to the HUMA API and return a result. */
72
82
  declare function callHumaApi(opts: HumaOptions, sessionData: SessionData): Promise<HumaVerifyResult>;
73
83
 
74
- /**
75
- * useHUMA JavaScript SDK
76
- * Privacy-first human verification — no PII, no Cloudflare dependency.
77
- * https://humaverify.com
78
- */
79
-
80
84
  /** Auto-start signal collection (call once, early in your app). */
81
85
  declare function init(): void;
82
86
  /**
@@ -86,5 +90,60 @@ declare function init(): void;
86
90
  declare function verify(opts: HumaOptions): Promise<HumaVerifyResult>;
87
91
  /** Get raw collected signals — useful for debugging. */
88
92
  declare function debug(): Record<string, unknown> | null;
93
+ /**
94
+ * Server-side verify — pass pre-collected signals forwarded from the browser.
95
+ * Use this in your API routes / server actions when you want the final
96
+ * verify call to happen on your backend, not the browser.
97
+ *
98
+ * @example
99
+ * // In your Next.js API route:
100
+ * import { verifyWithSignals } from 'usehuma';
101
+ *
102
+ * const result = await verifyWithSignals({
103
+ * apiKey: process.env.HUMA_API_KEY!,
104
+ * userId: req.body.userId,
105
+ * signals: req.body.humaSignals, // forwarded from browser via Huma.debug()
106
+ * });
107
+ */
108
+ declare function verifyWithSignals(opts: {
109
+ apiKey: string;
110
+ userId: string;
111
+ signals: SessionData;
112
+ endpoint?: string;
113
+ }): Promise<HumaVerifyResult>;
114
+ /**
115
+ * Request a step-up challenge when verify() confidence is borderline (30–70%).
116
+ * Returns challenge data to render to the user.
117
+ *
118
+ * @example
119
+ * const result = await verify({ apiKey, userId });
120
+ * if (!result.human && result.confidence > 0.3) {
121
+ * const ch = await requestChallenge({ apiKey, userId });
122
+ * // Render ch.payload to user, collect answer, then:
123
+ * const solved = await solveChallenge({ apiKey, challengeId: ch.challenge_id, answer });
124
+ * if (solved.passed) continueLogin(solved.token);
125
+ * }
126
+ */
127
+ declare function requestChallenge(opts: {
128
+ apiKey: string;
129
+ userId: string;
130
+ endpoint?: string;
131
+ }): Promise<{
132
+ challenge_id: string;
133
+ type: string;
134
+ payload: Record<string, unknown>;
135
+ expires_in_seconds: number;
136
+ }>;
137
+ declare function solveChallenge(opts: {
138
+ apiKey: string;
139
+ challengeId: string;
140
+ answer: string;
141
+ endpoint?: string;
142
+ }): Promise<{
143
+ passed: boolean;
144
+ token?: string;
145
+ message?: string;
146
+ attempts_remaining?: number;
147
+ }>;
89
148
 
90
- export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, verify };
149
+ export { HumaCollector, type HumaError, type HumaOptions, type HumaState, type HumaVerifyResult, type SessionData, callHumaApi, debug, init, requestChallenge, solveChallenge, verify, verifyWithSignals };
package/dist/index.js CHANGED
@@ -24,7 +24,10 @@ __export(src_exports, {
24
24
  callHumaApi: () => callHumaApi,
25
25
  debug: () => debug,
26
26
  init: () => init,
27
- verify: () => verify
27
+ requestChallenge: () => requestChallenge,
28
+ solveChallenge: () => solveChallenge,
29
+ verify: () => verify,
30
+ verifyWithSignals: () => verifyWithSignals
28
31
  });
29
32
  module.exports = __toCommonJS(src_exports);
30
33
 
@@ -53,6 +56,22 @@ function mouseFeatures(pts) {
53
56
  }
54
57
  return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };
55
58
  }
59
+ function touchFeatures(taps, moves, multiTouchCount) {
60
+ const tapIntervals = [];
61
+ for (let i = 1; i < taps.length; i++) tapIntervals.push(taps[i].t - taps[i - 1].t);
62
+ const durations = taps.map((t) => t.duration_ms);
63
+ const forces = taps.filter((t) => t.force > 0).map((t) => t.force);
64
+ const speeds = moves.map((m) => m.speed);
65
+ return {
66
+ tap_count: taps.length,
67
+ tap_interval_cv: cv(tapIntervals),
68
+ tap_duration_cv: cv(durations),
69
+ tap_force_cv: cv(forces),
70
+ touch_move_count: moves.length,
71
+ touch_speed_cv: cv(speeds),
72
+ multi_touch_count: multiTouchCount
73
+ };
74
+ }
56
75
  var HumaCollector = class {
57
76
  constructor() {
58
77
  this.signals = {
@@ -61,9 +80,13 @@ var HumaCollector = class {
61
80
  scrolls: [],
62
81
  clicks: [],
63
82
  focusEvents: [],
83
+ taps: [],
84
+ touchMoves: [],
85
+ multiTouchCount: 0,
64
86
  startTime: Date.now(),
65
87
  lastKeyTime: null,
66
- lastScrollTime: null
88
+ lastScrollTime: null,
89
+ activeTouches: {}
67
90
  };
68
91
  this.listening = false;
69
92
  this.onMouseMove = (e) => {
@@ -88,6 +111,46 @@ var HumaCollector = class {
88
111
  };
89
112
  this.onFocus = () => this.signals.focusEvents.push({ type: "focus", t: Date.now() });
90
113
  this.onBlur = () => this.signals.focusEvents.push({ type: "blur", t: Date.now() });
114
+ this.onTouchStart = (e) => {
115
+ const now = Date.now();
116
+ if (e.touches.length > 1) this.signals.multiTouchCount++;
117
+ for (let i = 0; i < e.changedTouches.length; i++) {
118
+ const t = e.changedTouches[i];
119
+ this.signals.activeTouches[t.identifier] = { t: now, x: t.clientX, y: t.clientY };
120
+ }
121
+ };
122
+ this.onTouchEnd = (e) => {
123
+ var _a;
124
+ const now = Date.now();
125
+ for (let i = 0; i < e.changedTouches.length; i++) {
126
+ const touch = e.changedTouches[i];
127
+ const start = this.signals.activeTouches[touch.identifier];
128
+ if (!start) continue;
129
+ delete this.signals.activeTouches[touch.identifier];
130
+ if (this.signals.taps.length < 80) {
131
+ this.signals.taps.push({
132
+ t: now,
133
+ duration_ms: now - start.t,
134
+ force: (_a = touch.force) != null ? _a : 0,
135
+ touch_count: e.touches.length + 1
136
+ });
137
+ }
138
+ }
139
+ };
140
+ this.onTouchMove = (e) => {
141
+ const now = Date.now();
142
+ for (let i = 0; i < e.changedTouches.length; i++) {
143
+ const touch = e.changedTouches[i];
144
+ const start = this.signals.activeTouches[touch.identifier];
145
+ if (!start) continue;
146
+ const dx = touch.clientX - start.x;
147
+ const dy = touch.clientY - start.y;
148
+ const dt = Math.max(now - start.t, 1);
149
+ if (this.signals.touchMoves.length < 100)
150
+ this.signals.touchMoves.push({ speed: Math.sqrt(dx * dx + dy * dy) / dt, t: now });
151
+ this.signals.activeTouches[touch.identifier] = { t: now, x: touch.clientX, y: touch.clientY };
152
+ }
153
+ };
91
154
  }
92
155
  start() {
93
156
  if (this.listening || typeof window === "undefined") return;
@@ -96,6 +159,9 @@ var HumaCollector = class {
96
159
  document.addEventListener("keydown", this.onKeyDown, { passive: true });
97
160
  document.addEventListener("scroll", this.onScroll, { passive: true });
98
161
  document.addEventListener("click", this.onClick, { passive: true });
162
+ document.addEventListener("touchstart", this.onTouchStart, { passive: true });
163
+ document.addEventListener("touchend", this.onTouchEnd, { passive: true });
164
+ document.addEventListener("touchmove", this.onTouchMove, { passive: true });
99
165
  window.addEventListener("focus", this.onFocus, { passive: true });
100
166
  window.addEventListener("blur", this.onBlur, { passive: true });
101
167
  }
@@ -106,12 +172,16 @@ var HumaCollector = class {
106
172
  document.removeEventListener("keydown", this.onKeyDown);
107
173
  document.removeEventListener("scroll", this.onScroll);
108
174
  document.removeEventListener("click", this.onClick);
175
+ document.removeEventListener("touchstart", this.onTouchStart);
176
+ document.removeEventListener("touchend", this.onTouchEnd);
177
+ document.removeEventListener("touchmove", this.onTouchMove);
109
178
  window.removeEventListener("focus", this.onFocus);
110
179
  window.removeEventListener("blur", this.onBlur);
111
180
  }
112
181
  extract() {
113
- const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;
182
+ const { keys, scrolls, clicks, focusEvents, startTime, mouse, taps, touchMoves, multiTouchCount } = this.signals;
114
183
  const m = mouseFeatures(mouse);
184
+ const t = touchFeatures(taps, touchMoves, multiTouchCount);
115
185
  const clickIntervals = [];
116
186
  for (let i = 1; i < clicks.length; i++)
117
187
  clickIntervals.push(clicks[i].t - clicks[i - 1].t);
@@ -126,7 +196,14 @@ var HumaCollector = class {
126
196
  mouse_sample_count: m.sample_count,
127
197
  click_interval_cv: cv(clickIntervals),
128
198
  click_count: clicks.length,
129
- tab_switches: focusEvents.length
199
+ tab_switches: focusEvents.length,
200
+ tap_count: t.tap_count,
201
+ tap_interval_cv: t.tap_interval_cv,
202
+ tap_duration_cv: t.tap_duration_cv,
203
+ tap_force_cv: t.tap_force_cv,
204
+ touch_move_count: t.touch_move_count,
205
+ touch_speed_cv: t.touch_speed_cv,
206
+ multi_touch_count: t.multi_touch_count
130
207
  };
131
208
  }
132
209
  debug() {
@@ -168,12 +245,42 @@ function debug() {
168
245
  var _a;
169
246
  return (_a = _globalCollector == null ? void 0 : _globalCollector.debug()) != null ? _a : null;
170
247
  }
248
+ async function verifyWithSignals(opts) {
249
+ return callHumaApi(
250
+ { apiKey: opts.apiKey, userId: opts.userId, endpoint: opts.endpoint },
251
+ opts.signals
252
+ );
253
+ }
254
+ async function requestChallenge(opts) {
255
+ var _a;
256
+ const base = ((_a = opts.endpoint) != null ? _a : "https://humaverify.com/api/v1/verify").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "");
257
+ const res = await fetch(`${base}/api/v1/challenge/create`, {
258
+ method: "POST",
259
+ headers: { Authorization: `Bearer ${opts.apiKey}`, "Content-Type": "application/json" },
260
+ body: JSON.stringify({ userId: opts.userId })
261
+ });
262
+ if (!res.ok) throw await res.json().catch(() => ({ error: "Failed to create challenge" }));
263
+ return res.json();
264
+ }
265
+ async function solveChallenge(opts) {
266
+ var _a;
267
+ const base = ((_a = opts.endpoint) != null ? _a : "https://humaverify.com/api/v1/verify").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "");
268
+ const res = await fetch(`${base}/api/v1/challenge/verify`, {
269
+ method: "POST",
270
+ headers: { Authorization: `Bearer ${opts.apiKey}`, "Content-Type": "application/json" },
271
+ body: JSON.stringify({ challenge_id: opts.challengeId, answer: opts.answer })
272
+ });
273
+ return res.json();
274
+ }
171
275
  // Annotate the CommonJS export names for ESM import in node:
172
276
  0 && (module.exports = {
173
277
  HumaCollector,
174
278
  callHumaApi,
175
279
  debug,
176
280
  init,
177
- verify
281
+ requestChallenge,
282
+ solveChallenge,
283
+ verify,
284
+ verifyWithSignals
178
285
  });
179
286
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwBA,SAAS,GAAG,KAAuB;AACjC,MAAI,IAAI,SAAS,EAAG,QAAO;AAC3B,QAAM,OAAO,IAAI,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI;AAClD,MAAI,SAAS,EAAG,QAAO;AACvB,QAAM,WAAW,IAAI,OAAO,CAAC,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI;AAC1E,SAAO,KAAK,KAAK,QAAQ,IAAI;AAC/B;AAEA,SAAS,cAAc,KAAmB;AACxC,MAAI,IAAI,SAAS,EAAG,QAAO,EAAE,UAAU,GAAG,mBAAmB,GAAG,cAAc,EAAE;AAChF,QAAM,SAAmB,CAAC;AAC1B,QAAM,aAAuB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,EAAE;AACjC,UAAM,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,EAAE;AACjC,UAAM,KAAK,KAAK,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,EAAE,GAAG,CAAC;AAC9C,WAAO,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE,IAAI,EAAE;AAC7C,eAAW,KAAK,KAAK,MAAM,IAAI,EAAE,CAAC;AAAA,EACpC;AACA,MAAI,aAAa;AACjB,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,QAAI,KAAK,IAAI,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,CAAC,IAAI,IAAK;AAAA,EACzD;AACA,SAAO,EAAE,UAAU,GAAG,MAAM,GAAG,mBAAmB,YAAY,cAAc,IAAI,OAAO;AACzF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAApB;AACL,SAAQ,UAAmB;AAAA,MACzB,OAAO,CAAC;AAAA,MAAG,MAAM,CAAC;AAAA,MAAG,SAAS,CAAC;AAAA,MAAG,QAAQ,CAAC;AAAA,MAAG,aAAa,CAAC;AAAA,MAC5D,WAAW,KAAK,IAAI;AAAA,MAAG,aAAa;AAAA,MAAM,gBAAgB;AAAA,IAC5D;AACA,SAAQ,YAAY;AAEpB,SAAQ,cAAc,CAAC,MAAkB;AACvC,UAAI,KAAK,QAAQ,MAAM,SAAS;AAC9B,aAAK,QAAQ,MAAM,KAAK,EAAE,GAAG,EAAE,SAAS,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,EAAE,CAAC;AAAA,IACzE;AACA,SAAQ,YAAY,MAAM;AACxB,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,KAAK,QAAQ,gBAAgB,QAAQ,KAAK,QAAQ,KAAK,SAAS;AAClE,aAAK,QAAQ,KAAK,KAAK,EAAE,UAAU,MAAM,KAAK,QAAQ,YAAY,CAAC;AACrE,WAAK,QAAQ,cAAc;AAAA,IAC7B;AACA,SAAQ,WAAW,MAAM;AACvB,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,KAAK,QAAQ,mBAAmB,QAAQ,KAAK,QAAQ,QAAQ,SAAS;AACxE,aAAK,QAAQ,QAAQ,KAAK,EAAE,UAAU,MAAM,KAAK,QAAQ,eAAe,CAAC;AAC3E,WAAK,QAAQ,iBAAiB;AAAA,IAChC;AACA,SAAQ,UAAU,MAAM;AACtB,UAAI,KAAK,QAAQ,OAAO,SAAS;AAC/B,aAAK,QAAQ,OAAO,KAAK,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC;AAAA,IAC9C;AACA,SAAQ,UAAU,MAAM,KAAK,QAAQ,YAAY,KAAK,EAAE,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE,CAAC;AACtF,SAAQ,SAAU,MAAM,KAAK,QAAQ,YAAY,KAAK,EAAE,MAAM,QAAS,GAAG,KAAK,IAAI,EAAE,CAAC;AAAA;AAAA,EAEtF,QAAQ;AACN,QAAI,KAAK,aAAa,OAAO,WAAW,YAAa;AACrD,SAAK,YAAY;AACjB,aAAS,iBAAiB,aAAa,KAAK,aAAa,EAAE,SAAS,KAAK,CAAC;AAC1E,aAAS,iBAAiB,WAAa,KAAK,WAAa,EAAE,SAAS,KAAK,CAAC;AAC1E,aAAS,iBAAiB,UAAa,KAAK,UAAa,EAAE,SAAS,KAAK,CAAC;AAC1E,aAAS,iBAAiB,SAAa,KAAK,SAAa,EAAE,SAAS,KAAK,CAAC;AAC1E,WAAO,iBAAiB,SAAe,KAAK,SAAa,EAAE,SAAS,KAAK,CAAC;AAC1E,WAAO,iBAAiB,QAAe,KAAK,QAAa,EAAE,SAAS,KAAK,CAAC;AAAA,EAC5E;AAAA,EAEA,OAAO;AACL,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AACjB,aAAS,oBAAoB,aAAa,KAAK,WAAW;AAC1D,aAAS,oBAAoB,WAAa,KAAK,SAAS;AACxD,aAAS,oBAAoB,UAAa,KAAK,QAAQ;AACvD,aAAS,oBAAoB,SAAa,KAAK,OAAO;AACtD,WAAO,oBAAoB,SAAe,KAAK,OAAO;AACtD,WAAO,oBAAoB,QAAe,KAAK,MAAM;AAAA,EACvD;AAAA,EAEA,UAAuB;AACrB,UAAM,EAAE,MAAM,SAAS,QAAQ,aAAa,WAAW,MAAM,IAAI,KAAK;AACtE,UAAM,IAAI,cAAc,KAAK;AAC7B,UAAM,iBAA2B,CAAC;AAClC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ;AACjC,qBAAe,KAAK,OAAO,CAAC,EAAE,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;AACnD,WAAO;AAAA,MACL,iBAAsB,KAAK,IAAI,IAAI;AAAA,MACnC,iBAAsB,GAAG,KAAK,IAAI,OAAK,EAAE,QAAQ,CAAC;AAAA,MAClD,WAAsB,KAAK;AAAA,MAC3B,oBAAsB,GAAG,QAAQ,IAAI,OAAK,EAAE,QAAQ,CAAC;AAAA,MACrD,cAAsB,QAAQ;AAAA,MAC9B,gBAAsB,EAAE;AAAA,MACxB,yBAAyB,EAAE;AAAA,MAC3B,oBAAsB,EAAE;AAAA,MACxB,mBAAsB,GAAG,cAAc;AAAA,MACvC,aAAsB,OAAO;AAAA,MAC7B,cAAsB,YAAY;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,QAAiC;AAC/B,WAAO,EAAE,SAAS,KAAK,SAAoB,UAAU,KAAK,QAAQ,EAAE;AAAA,EACtE;AACF;AAGA,eAAsB,YACpB,MACA,aAC2B;AApI7B;AAqIE,QAAM,YAAW,UAAK,aAAL,YAAiB;AAClC,QAAM,MAAM,MAAM,MAAM,UAAU;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK,MAAM;AAAA,MACpC,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,EAAE,QAAQ,KAAK,QAAQ,YAAY,CAAC;AAAA,EAC3D,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAiB,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,gBAAgB,EAAE;AAChF,UAAM;AAAA,EACR;AACA,SAAO,IAAI,KAAK;AAClB;;;ADrHA,IAAI,mBAAyC;AAGtC,SAAS,OAAO;AACrB,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,iBAAkB;AACtB,qBAAmB,IAAI,cAAc;AACrC,mBAAiB,MAAM;AACzB;AAMA,eAAsB,OAAO,MAA8C;AACzE,MAAI,CAAC,iBAAkB,MAAK;AAC5B,QAAM,cAAc,iBAAkB,QAAQ;AAC9C,SAAO,YAAY,MAAM,WAAW;AACtC;AAGO,SAAS,QAAwC;AAnDxD;AAoDE,UAAO,0DAAkB,YAAlB,YAA+D;AACxE;","names":[]}
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/**\n * Server-side verify — pass pre-collected signals forwarded from the browser.\n * Use this in your API routes / server actions when you want the final\n * verify call to happen on your backend, not the browser.\n *\n * @example\n * // In your Next.js API route:\n * import { verifyWithSignals } from 'usehuma';\n *\n * const result = await verifyWithSignals({\n * apiKey: process.env.HUMA_API_KEY!,\n * userId: req.body.userId,\n * signals: req.body.humaSignals, // forwarded from browser via Huma.debug()\n * });\n */\nexport async function verifyWithSignals(opts: {\n apiKey: string;\n userId: string;\n signals: import(\"./types\").SessionData;\n endpoint?: string;\n}): Promise<import(\"./types\").HumaVerifyResult> {\n return callHumaApi(\n { apiKey: opts.apiKey, userId: opts.userId, endpoint: opts.endpoint },\n opts.signals\n );\n}\n\n/**\n * Request a step-up challenge when verify() confidence is borderline (30–70%).\n * Returns challenge data to render to the user.\n *\n * @example\n * const result = await verify({ apiKey, userId });\n * if (!result.human && result.confidence > 0.3) {\n * const ch = await requestChallenge({ apiKey, userId });\n * // Render ch.payload to user, collect answer, then:\n * const solved = await solveChallenge({ apiKey, challengeId: ch.challenge_id, answer });\n * if (solved.passed) continueLogin(solved.token);\n * }\n */\nexport async function requestChallenge(opts: {\n apiKey: string;\n userId: string;\n endpoint?: string;\n}): Promise<{ challenge_id: string; type: string; payload: Record<string, unknown>; expires_in_seconds: number }> {\n const base = (opts.endpoint ?? \"https://humaverify.com/api/v1/verify\")\n .replace(/\\/api\\/v1\\/verify$/, \"\")\n .replace(/\\/$/, \"\");\n const res = await fetch(`${base}/api/v1/challenge/create`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${opts.apiKey}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ userId: opts.userId }),\n });\n if (!res.ok) throw await res.json().catch(() => ({ error: \"Failed to create challenge\" }));\n return res.json();\n}\n\nexport async function solveChallenge(opts: {\n apiKey: string;\n challengeId: string;\n answer: string;\n endpoint?: string;\n}): Promise<{ passed: boolean; token?: string; message?: string; attempts_remaining?: number }> {\n const base = (opts.endpoint ?? \"https://humaverify.com/api/v1/verify\")\n .replace(/\\/api\\/v1\\/verify$/, \"\")\n .replace(/\\/$/, \"\");\n const res = await fetch(`${base}/api/v1/challenge/verify`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${opts.apiKey}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ challenge_id: opts.challengeId, answer: opts.answer }),\n });\n return res.json();\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 };\ntype TapEntry = { t: number; duration_ms: number; force: number; touch_count: number };\ntype TouchMove = { speed: number; t: number };\ntype ActiveTouch = { t: number; x: number; y: number };\n\ninterface Signals {\n mouse: MousePoint[];\n keys: KeyEntry[];\n scrolls: ScrollEntry[];\n clicks: ClickEntry[];\n focusEvents: FocusEvent[];\n taps: TapEntry[];\n touchMoves: TouchMove[];\n multiTouchCount: number;\n startTime: number;\n lastKeyTime: number | null;\n lastScrollTime: number | null;\n activeTouches: Record<number, ActiveTouch>;\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\nfunction touchFeatures(taps: TapEntry[], moves: TouchMove[], multiTouchCount: number) {\n const tapIntervals: number[] = [];\n for (let i = 1; i < taps.length; i++) tapIntervals.push(taps[i].t - taps[i - 1].t);\n const durations = taps.map(t => t.duration_ms);\n const forces = taps.filter(t => t.force > 0).map(t => t.force);\n const speeds = moves.map(m => m.speed);\n return {\n tap_count: taps.length,\n tap_interval_cv: cv(tapIntervals),\n tap_duration_cv: cv(durations),\n tap_force_cv: cv(forces),\n touch_move_count: moves.length,\n touch_speed_cv: cv(speeds),\n multi_touch_count: multiTouchCount,\n };\n}\n\nexport class HumaCollector {\n private signals: Signals = {\n mouse: [], keys: [], scrolls: [], clicks: [], focusEvents: [],\n taps: [], touchMoves: [], multiTouchCount: 0,\n startTime: Date.now(), lastKeyTime: null, lastScrollTime: null,\n activeTouches: {},\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 private onTouchStart = (e: TouchEvent) => {\n const now = Date.now();\n if (e.touches.length > 1) this.signals.multiTouchCount++;\n for (let i = 0; i < e.changedTouches.length; i++) {\n const t = e.changedTouches[i];\n this.signals.activeTouches[t.identifier] = { t: now, x: t.clientX, y: t.clientY };\n }\n };\n\n private onTouchEnd = (e: TouchEvent) => {\n const now = Date.now();\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const start = this.signals.activeTouches[touch.identifier];\n if (!start) continue;\n delete this.signals.activeTouches[touch.identifier];\n if (this.signals.taps.length < 80) {\n this.signals.taps.push({\n t: now,\n duration_ms: now - start.t,\n force: (touch as Touch & { force?: number }).force ?? 0,\n touch_count: e.touches.length + 1,\n });\n }\n }\n };\n\n private onTouchMove = (e: TouchEvent) => {\n const now = Date.now();\n for (let i = 0; i < e.changedTouches.length; i++) {\n const touch = e.changedTouches[i];\n const start = this.signals.activeTouches[touch.identifier];\n if (!start) continue;\n const dx = touch.clientX - start.x;\n const dy = touch.clientY - start.y;\n const dt = Math.max(now - start.t, 1);\n if (this.signals.touchMoves.length < 100)\n this.signals.touchMoves.push({ speed: Math.sqrt(dx * dx + dy * dy) / dt, t: now });\n this.signals.activeTouches[touch.identifier] = { t: now, x: touch.clientX, y: touch.clientY };\n }\n };\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 document.addEventListener(\"touchstart\", this.onTouchStart, { passive: true });\n document.addEventListener(\"touchend\", this.onTouchEnd, { passive: true });\n document.addEventListener(\"touchmove\", this.onTouchMove, { 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 document.removeEventListener(\"touchstart\", this.onTouchStart);\n document.removeEventListener(\"touchend\", this.onTouchEnd);\n document.removeEventListener(\"touchmove\", this.onTouchMove);\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, taps, touchMoves, multiTouchCount } = this.signals;\n const m = mouseFeatures(mouse);\n const t = touchFeatures(taps, touchMoves, multiTouchCount);\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 tap_count: t.tap_count,\n tap_interval_cv: t.tap_interval_cv,\n tap_duration_cv: t.tap_duration_cv,\n tap_force_cv: t.tap_force_cv,\n touch_move_count: t.touch_move_count,\n touch_speed_cv: t.touch_speed_cv,\n multi_touch_count: t.multi_touch_count,\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":[]}
package/dist/index.mjs CHANGED
@@ -23,6 +23,22 @@ function mouseFeatures(pts) {
23
23
  }
24
24
  return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };
25
25
  }
26
+ function touchFeatures(taps, moves, multiTouchCount) {
27
+ const tapIntervals = [];
28
+ for (let i = 1; i < taps.length; i++) tapIntervals.push(taps[i].t - taps[i - 1].t);
29
+ const durations = taps.map((t) => t.duration_ms);
30
+ const forces = taps.filter((t) => t.force > 0).map((t) => t.force);
31
+ const speeds = moves.map((m) => m.speed);
32
+ return {
33
+ tap_count: taps.length,
34
+ tap_interval_cv: cv(tapIntervals),
35
+ tap_duration_cv: cv(durations),
36
+ tap_force_cv: cv(forces),
37
+ touch_move_count: moves.length,
38
+ touch_speed_cv: cv(speeds),
39
+ multi_touch_count: multiTouchCount
40
+ };
41
+ }
26
42
  var HumaCollector = class {
27
43
  constructor() {
28
44
  this.signals = {
@@ -31,9 +47,13 @@ var HumaCollector = class {
31
47
  scrolls: [],
32
48
  clicks: [],
33
49
  focusEvents: [],
50
+ taps: [],
51
+ touchMoves: [],
52
+ multiTouchCount: 0,
34
53
  startTime: Date.now(),
35
54
  lastKeyTime: null,
36
- lastScrollTime: null
55
+ lastScrollTime: null,
56
+ activeTouches: {}
37
57
  };
38
58
  this.listening = false;
39
59
  this.onMouseMove = (e) => {
@@ -58,6 +78,46 @@ var HumaCollector = class {
58
78
  };
59
79
  this.onFocus = () => this.signals.focusEvents.push({ type: "focus", t: Date.now() });
60
80
  this.onBlur = () => this.signals.focusEvents.push({ type: "blur", t: Date.now() });
81
+ this.onTouchStart = (e) => {
82
+ const now = Date.now();
83
+ if (e.touches.length > 1) this.signals.multiTouchCount++;
84
+ for (let i = 0; i < e.changedTouches.length; i++) {
85
+ const t = e.changedTouches[i];
86
+ this.signals.activeTouches[t.identifier] = { t: now, x: t.clientX, y: t.clientY };
87
+ }
88
+ };
89
+ this.onTouchEnd = (e) => {
90
+ var _a;
91
+ const now = Date.now();
92
+ for (let i = 0; i < e.changedTouches.length; i++) {
93
+ const touch = e.changedTouches[i];
94
+ const start = this.signals.activeTouches[touch.identifier];
95
+ if (!start) continue;
96
+ delete this.signals.activeTouches[touch.identifier];
97
+ if (this.signals.taps.length < 80) {
98
+ this.signals.taps.push({
99
+ t: now,
100
+ duration_ms: now - start.t,
101
+ force: (_a = touch.force) != null ? _a : 0,
102
+ touch_count: e.touches.length + 1
103
+ });
104
+ }
105
+ }
106
+ };
107
+ this.onTouchMove = (e) => {
108
+ const now = Date.now();
109
+ for (let i = 0; i < e.changedTouches.length; i++) {
110
+ const touch = e.changedTouches[i];
111
+ const start = this.signals.activeTouches[touch.identifier];
112
+ if (!start) continue;
113
+ const dx = touch.clientX - start.x;
114
+ const dy = touch.clientY - start.y;
115
+ const dt = Math.max(now - start.t, 1);
116
+ if (this.signals.touchMoves.length < 100)
117
+ this.signals.touchMoves.push({ speed: Math.sqrt(dx * dx + dy * dy) / dt, t: now });
118
+ this.signals.activeTouches[touch.identifier] = { t: now, x: touch.clientX, y: touch.clientY };
119
+ }
120
+ };
61
121
  }
62
122
  start() {
63
123
  if (this.listening || typeof window === "undefined") return;
@@ -66,6 +126,9 @@ var HumaCollector = class {
66
126
  document.addEventListener("keydown", this.onKeyDown, { passive: true });
67
127
  document.addEventListener("scroll", this.onScroll, { passive: true });
68
128
  document.addEventListener("click", this.onClick, { passive: true });
129
+ document.addEventListener("touchstart", this.onTouchStart, { passive: true });
130
+ document.addEventListener("touchend", this.onTouchEnd, { passive: true });
131
+ document.addEventListener("touchmove", this.onTouchMove, { passive: true });
69
132
  window.addEventListener("focus", this.onFocus, { passive: true });
70
133
  window.addEventListener("blur", this.onBlur, { passive: true });
71
134
  }
@@ -76,12 +139,16 @@ var HumaCollector = class {
76
139
  document.removeEventListener("keydown", this.onKeyDown);
77
140
  document.removeEventListener("scroll", this.onScroll);
78
141
  document.removeEventListener("click", this.onClick);
142
+ document.removeEventListener("touchstart", this.onTouchStart);
143
+ document.removeEventListener("touchend", this.onTouchEnd);
144
+ document.removeEventListener("touchmove", this.onTouchMove);
79
145
  window.removeEventListener("focus", this.onFocus);
80
146
  window.removeEventListener("blur", this.onBlur);
81
147
  }
82
148
  extract() {
83
- const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;
149
+ const { keys, scrolls, clicks, focusEvents, startTime, mouse, taps, touchMoves, multiTouchCount } = this.signals;
84
150
  const m = mouseFeatures(mouse);
151
+ const t = touchFeatures(taps, touchMoves, multiTouchCount);
85
152
  const clickIntervals = [];
86
153
  for (let i = 1; i < clicks.length; i++)
87
154
  clickIntervals.push(clicks[i].t - clicks[i - 1].t);
@@ -96,7 +163,14 @@ var HumaCollector = class {
96
163
  mouse_sample_count: m.sample_count,
97
164
  click_interval_cv: cv(clickIntervals),
98
165
  click_count: clicks.length,
99
- tab_switches: focusEvents.length
166
+ tab_switches: focusEvents.length,
167
+ tap_count: t.tap_count,
168
+ tap_interval_cv: t.tap_interval_cv,
169
+ tap_duration_cv: t.tap_duration_cv,
170
+ tap_force_cv: t.tap_force_cv,
171
+ touch_move_count: t.touch_move_count,
172
+ touch_speed_cv: t.touch_speed_cv,
173
+ multi_touch_count: t.multi_touch_count
100
174
  };
101
175
  }
102
176
  debug() {
@@ -138,11 +212,41 @@ function debug() {
138
212
  var _a;
139
213
  return (_a = _globalCollector == null ? void 0 : _globalCollector.debug()) != null ? _a : null;
140
214
  }
215
+ async function verifyWithSignals(opts) {
216
+ return callHumaApi(
217
+ { apiKey: opts.apiKey, userId: opts.userId, endpoint: opts.endpoint },
218
+ opts.signals
219
+ );
220
+ }
221
+ async function requestChallenge(opts) {
222
+ var _a;
223
+ const base = ((_a = opts.endpoint) != null ? _a : "https://humaverify.com/api/v1/verify").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "");
224
+ const res = await fetch(`${base}/api/v1/challenge/create`, {
225
+ method: "POST",
226
+ headers: { Authorization: `Bearer ${opts.apiKey}`, "Content-Type": "application/json" },
227
+ body: JSON.stringify({ userId: opts.userId })
228
+ });
229
+ if (!res.ok) throw await res.json().catch(() => ({ error: "Failed to create challenge" }));
230
+ return res.json();
231
+ }
232
+ async function solveChallenge(opts) {
233
+ var _a;
234
+ const base = ((_a = opts.endpoint) != null ? _a : "https://humaverify.com/api/v1/verify").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "");
235
+ const res = await fetch(`${base}/api/v1/challenge/verify`, {
236
+ method: "POST",
237
+ headers: { Authorization: `Bearer ${opts.apiKey}`, "Content-Type": "application/json" },
238
+ body: JSON.stringify({ challenge_id: opts.challengeId, answer: opts.answer })
239
+ });
240
+ return res.json();
241
+ }
141
242
  export {
142
243
  HumaCollector,
143
244
  callHumaApi,
144
245
  debug,
145
246
  init,
146
- verify
247
+ requestChallenge,
248
+ solveChallenge,
249
+ verify,
250
+ verifyWithSignals
147
251
  };
148
252
  //# sourceMappingURL=index.mjs.map