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 +66 -7
- package/dist/index.d.ts +66 -7
- package/dist/index.js +112 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +108 -4
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +49 -1
- package/dist/react.d.ts +49 -1
- package/dist/react.js +242 -3
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +242 -4
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["/**\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","/**\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"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"sourcesContent":["/**\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","/**\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"],"mappings":"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","names":[]}
|
package/dist/react.d.mts
CHANGED
|
@@ -128,6 +128,54 @@ declare function useHumaSession(opts: UseHumaSessionOptions): {
|
|
|
128
128
|
anomaly: boolean;
|
|
129
129
|
stop: () => void;
|
|
130
130
|
};
|
|
131
|
+
type ChallengeTarget = {
|
|
132
|
+
id: string;
|
|
133
|
+
x: number;
|
|
134
|
+
y: number;
|
|
135
|
+
label: string;
|
|
136
|
+
};
|
|
137
|
+
type ChallengeFailed = {
|
|
138
|
+
passed: false;
|
|
139
|
+
message: string;
|
|
140
|
+
attempts_remaining?: number;
|
|
141
|
+
};
|
|
142
|
+
type HumaChallengeProps = {
|
|
143
|
+
/** challenge_id from POST /api/v1/challenge/create */
|
|
144
|
+
challengeId: string;
|
|
145
|
+
/** type from challenge create response */
|
|
146
|
+
type: "click_targets" | "pattern";
|
|
147
|
+
/** payload from challenge create response */
|
|
148
|
+
payload: {
|
|
149
|
+
instruction: string;
|
|
150
|
+
targets?: ChallengeTarget[];
|
|
151
|
+
sequence?: number[];
|
|
152
|
+
};
|
|
153
|
+
apiKey: string;
|
|
154
|
+
endpoint?: string;
|
|
155
|
+
onPassed: (token: string) => void;
|
|
156
|
+
onFailed?: (result: ChallengeFailed) => void;
|
|
157
|
+
/** Called when user exhausts all attempts */
|
|
158
|
+
onBlocked?: () => void;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Renders a step-up verification challenge when behavioral signals are inconclusive.
|
|
162
|
+
* Supports "click_targets" and "pattern" challenge types.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* // After verify() returns { human: false, confidence: 0.45 }:
|
|
166
|
+
* const ch = await fetch('/api/v1/challenge/create', { ... });
|
|
167
|
+
* return (
|
|
168
|
+
* <HumaChallenge
|
|
169
|
+
* challengeId={ch.challenge_id}
|
|
170
|
+
* type={ch.type}
|
|
171
|
+
* payload={ch.payload}
|
|
172
|
+
* apiKey="huma_live_..."
|
|
173
|
+
* onPassed={(token) => continueLogin(token)}
|
|
174
|
+
* onBlocked={() => router.push('/blocked')}
|
|
175
|
+
* />
|
|
176
|
+
* );
|
|
177
|
+
*/
|
|
178
|
+
declare function HumaChallenge({ challengeId, type, payload, apiKey, endpoint, onPassed, onFailed, onBlocked, }: HumaChallengeProps): react_jsx_runtime.JSX.Element;
|
|
131
179
|
type HumaVerifyButtonProps = {
|
|
132
180
|
apiKey: string;
|
|
133
181
|
userId: string;
|
|
@@ -154,4 +202,4 @@ type HumaVerifyButtonProps = {
|
|
|
154
202
|
*/
|
|
155
203
|
declare function HumaVerifyButton({ apiKey, userId, endpoint, onVerified, onBlocked, children, className, style, }: HumaVerifyButtonProps): react_jsx_runtime.JSX.Element;
|
|
156
204
|
|
|
157
|
-
export { HumaGate, HumaProvider, HumaVerifyButton, useHuma, useHumaContext, useHumaSession };
|
|
205
|
+
export { HumaChallenge, HumaGate, HumaProvider, HumaVerifyButton, useHuma, useHumaContext, useHumaSession };
|
package/dist/react.d.ts
CHANGED
|
@@ -128,6 +128,54 @@ declare function useHumaSession(opts: UseHumaSessionOptions): {
|
|
|
128
128
|
anomaly: boolean;
|
|
129
129
|
stop: () => void;
|
|
130
130
|
};
|
|
131
|
+
type ChallengeTarget = {
|
|
132
|
+
id: string;
|
|
133
|
+
x: number;
|
|
134
|
+
y: number;
|
|
135
|
+
label: string;
|
|
136
|
+
};
|
|
137
|
+
type ChallengeFailed = {
|
|
138
|
+
passed: false;
|
|
139
|
+
message: string;
|
|
140
|
+
attempts_remaining?: number;
|
|
141
|
+
};
|
|
142
|
+
type HumaChallengeProps = {
|
|
143
|
+
/** challenge_id from POST /api/v1/challenge/create */
|
|
144
|
+
challengeId: string;
|
|
145
|
+
/** type from challenge create response */
|
|
146
|
+
type: "click_targets" | "pattern";
|
|
147
|
+
/** payload from challenge create response */
|
|
148
|
+
payload: {
|
|
149
|
+
instruction: string;
|
|
150
|
+
targets?: ChallengeTarget[];
|
|
151
|
+
sequence?: number[];
|
|
152
|
+
};
|
|
153
|
+
apiKey: string;
|
|
154
|
+
endpoint?: string;
|
|
155
|
+
onPassed: (token: string) => void;
|
|
156
|
+
onFailed?: (result: ChallengeFailed) => void;
|
|
157
|
+
/** Called when user exhausts all attempts */
|
|
158
|
+
onBlocked?: () => void;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Renders a step-up verification challenge when behavioral signals are inconclusive.
|
|
162
|
+
* Supports "click_targets" and "pattern" challenge types.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* // After verify() returns { human: false, confidence: 0.45 }:
|
|
166
|
+
* const ch = await fetch('/api/v1/challenge/create', { ... });
|
|
167
|
+
* return (
|
|
168
|
+
* <HumaChallenge
|
|
169
|
+
* challengeId={ch.challenge_id}
|
|
170
|
+
* type={ch.type}
|
|
171
|
+
* payload={ch.payload}
|
|
172
|
+
* apiKey="huma_live_..."
|
|
173
|
+
* onPassed={(token) => continueLogin(token)}
|
|
174
|
+
* onBlocked={() => router.push('/blocked')}
|
|
175
|
+
* />
|
|
176
|
+
* );
|
|
177
|
+
*/
|
|
178
|
+
declare function HumaChallenge({ challengeId, type, payload, apiKey, endpoint, onPassed, onFailed, onBlocked, }: HumaChallengeProps): react_jsx_runtime.JSX.Element;
|
|
131
179
|
type HumaVerifyButtonProps = {
|
|
132
180
|
apiKey: string;
|
|
133
181
|
userId: string;
|
|
@@ -154,4 +202,4 @@ type HumaVerifyButtonProps = {
|
|
|
154
202
|
*/
|
|
155
203
|
declare function HumaVerifyButton({ apiKey, userId, endpoint, onVerified, onBlocked, children, className, style, }: HumaVerifyButtonProps): react_jsx_runtime.JSX.Element;
|
|
156
204
|
|
|
157
|
-
export { HumaGate, HumaProvider, HumaVerifyButton, useHuma, useHumaContext, useHumaSession };
|
|
205
|
+
export { HumaChallenge, HumaGate, HumaProvider, HumaVerifyButton, useHuma, useHumaContext, useHumaSession };
|
package/dist/react.js
CHANGED
|
@@ -38,6 +38,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
38
38
|
// src/react.tsx
|
|
39
39
|
var react_exports = {};
|
|
40
40
|
__export(react_exports, {
|
|
41
|
+
HumaChallenge: () => HumaChallenge,
|
|
41
42
|
HumaGate: () => HumaGate,
|
|
42
43
|
HumaProvider: () => HumaProvider,
|
|
43
44
|
HumaVerifyButton: () => HumaVerifyButton,
|
|
@@ -73,6 +74,22 @@ function mouseFeatures(pts) {
|
|
|
73
74
|
}
|
|
74
75
|
return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };
|
|
75
76
|
}
|
|
77
|
+
function touchFeatures(taps, moves, multiTouchCount) {
|
|
78
|
+
const tapIntervals = [];
|
|
79
|
+
for (let i = 1; i < taps.length; i++) tapIntervals.push(taps[i].t - taps[i - 1].t);
|
|
80
|
+
const durations = taps.map((t) => t.duration_ms);
|
|
81
|
+
const forces = taps.filter((t) => t.force > 0).map((t) => t.force);
|
|
82
|
+
const speeds = moves.map((m) => m.speed);
|
|
83
|
+
return {
|
|
84
|
+
tap_count: taps.length,
|
|
85
|
+
tap_interval_cv: cv(tapIntervals),
|
|
86
|
+
tap_duration_cv: cv(durations),
|
|
87
|
+
tap_force_cv: cv(forces),
|
|
88
|
+
touch_move_count: moves.length,
|
|
89
|
+
touch_speed_cv: cv(speeds),
|
|
90
|
+
multi_touch_count: multiTouchCount
|
|
91
|
+
};
|
|
92
|
+
}
|
|
76
93
|
var HumaCollector = class {
|
|
77
94
|
constructor() {
|
|
78
95
|
this.signals = {
|
|
@@ -81,9 +98,13 @@ var HumaCollector = class {
|
|
|
81
98
|
scrolls: [],
|
|
82
99
|
clicks: [],
|
|
83
100
|
focusEvents: [],
|
|
101
|
+
taps: [],
|
|
102
|
+
touchMoves: [],
|
|
103
|
+
multiTouchCount: 0,
|
|
84
104
|
startTime: Date.now(),
|
|
85
105
|
lastKeyTime: null,
|
|
86
|
-
lastScrollTime: null
|
|
106
|
+
lastScrollTime: null,
|
|
107
|
+
activeTouches: {}
|
|
87
108
|
};
|
|
88
109
|
this.listening = false;
|
|
89
110
|
this.onMouseMove = (e) => {
|
|
@@ -108,6 +129,46 @@ var HumaCollector = class {
|
|
|
108
129
|
};
|
|
109
130
|
this.onFocus = () => this.signals.focusEvents.push({ type: "focus", t: Date.now() });
|
|
110
131
|
this.onBlur = () => this.signals.focusEvents.push({ type: "blur", t: Date.now() });
|
|
132
|
+
this.onTouchStart = (e) => {
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
if (e.touches.length > 1) this.signals.multiTouchCount++;
|
|
135
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
136
|
+
const t = e.changedTouches[i];
|
|
137
|
+
this.signals.activeTouches[t.identifier] = { t: now, x: t.clientX, y: t.clientY };
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
this.onTouchEnd = (e) => {
|
|
141
|
+
var _a;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
144
|
+
const touch = e.changedTouches[i];
|
|
145
|
+
const start = this.signals.activeTouches[touch.identifier];
|
|
146
|
+
if (!start) continue;
|
|
147
|
+
delete this.signals.activeTouches[touch.identifier];
|
|
148
|
+
if (this.signals.taps.length < 80) {
|
|
149
|
+
this.signals.taps.push({
|
|
150
|
+
t: now,
|
|
151
|
+
duration_ms: now - start.t,
|
|
152
|
+
force: (_a = touch.force) != null ? _a : 0,
|
|
153
|
+
touch_count: e.touches.length + 1
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
this.onTouchMove = (e) => {
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
161
|
+
const touch = e.changedTouches[i];
|
|
162
|
+
const start = this.signals.activeTouches[touch.identifier];
|
|
163
|
+
if (!start) continue;
|
|
164
|
+
const dx = touch.clientX - start.x;
|
|
165
|
+
const dy = touch.clientY - start.y;
|
|
166
|
+
const dt = Math.max(now - start.t, 1);
|
|
167
|
+
if (this.signals.touchMoves.length < 100)
|
|
168
|
+
this.signals.touchMoves.push({ speed: Math.sqrt(dx * dx + dy * dy) / dt, t: now });
|
|
169
|
+
this.signals.activeTouches[touch.identifier] = { t: now, x: touch.clientX, y: touch.clientY };
|
|
170
|
+
}
|
|
171
|
+
};
|
|
111
172
|
}
|
|
112
173
|
start() {
|
|
113
174
|
if (this.listening || typeof window === "undefined") return;
|
|
@@ -116,6 +177,9 @@ var HumaCollector = class {
|
|
|
116
177
|
document.addEventListener("keydown", this.onKeyDown, { passive: true });
|
|
117
178
|
document.addEventListener("scroll", this.onScroll, { passive: true });
|
|
118
179
|
document.addEventListener("click", this.onClick, { passive: true });
|
|
180
|
+
document.addEventListener("touchstart", this.onTouchStart, { passive: true });
|
|
181
|
+
document.addEventListener("touchend", this.onTouchEnd, { passive: true });
|
|
182
|
+
document.addEventListener("touchmove", this.onTouchMove, { passive: true });
|
|
119
183
|
window.addEventListener("focus", this.onFocus, { passive: true });
|
|
120
184
|
window.addEventListener("blur", this.onBlur, { passive: true });
|
|
121
185
|
}
|
|
@@ -126,12 +190,16 @@ var HumaCollector = class {
|
|
|
126
190
|
document.removeEventListener("keydown", this.onKeyDown);
|
|
127
191
|
document.removeEventListener("scroll", this.onScroll);
|
|
128
192
|
document.removeEventListener("click", this.onClick);
|
|
193
|
+
document.removeEventListener("touchstart", this.onTouchStart);
|
|
194
|
+
document.removeEventListener("touchend", this.onTouchEnd);
|
|
195
|
+
document.removeEventListener("touchmove", this.onTouchMove);
|
|
129
196
|
window.removeEventListener("focus", this.onFocus);
|
|
130
197
|
window.removeEventListener("blur", this.onBlur);
|
|
131
198
|
}
|
|
132
199
|
extract() {
|
|
133
|
-
const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;
|
|
200
|
+
const { keys, scrolls, clicks, focusEvents, startTime, mouse, taps, touchMoves, multiTouchCount } = this.signals;
|
|
134
201
|
const m = mouseFeatures(mouse);
|
|
202
|
+
const t = touchFeatures(taps, touchMoves, multiTouchCount);
|
|
135
203
|
const clickIntervals = [];
|
|
136
204
|
for (let i = 1; i < clicks.length; i++)
|
|
137
205
|
clickIntervals.push(clicks[i].t - clicks[i - 1].t);
|
|
@@ -146,7 +214,14 @@ var HumaCollector = class {
|
|
|
146
214
|
mouse_sample_count: m.sample_count,
|
|
147
215
|
click_interval_cv: cv(clickIntervals),
|
|
148
216
|
click_count: clicks.length,
|
|
149
|
-
tab_switches: focusEvents.length
|
|
217
|
+
tab_switches: focusEvents.length,
|
|
218
|
+
tap_count: t.tap_count,
|
|
219
|
+
tap_interval_cv: t.tap_interval_cv,
|
|
220
|
+
tap_duration_cv: t.tap_duration_cv,
|
|
221
|
+
tap_force_cv: t.tap_force_cv,
|
|
222
|
+
touch_move_count: t.touch_move_count,
|
|
223
|
+
touch_speed_cv: t.touch_speed_cv,
|
|
224
|
+
multi_touch_count: t.multi_touch_count
|
|
150
225
|
};
|
|
151
226
|
}
|
|
152
227
|
debug() {
|
|
@@ -299,6 +374,169 @@ function useHumaSession(opts) {
|
|
|
299
374
|
}, [opts.intervalMs]);
|
|
300
375
|
return { lastResult, anomaly, stop };
|
|
301
376
|
}
|
|
377
|
+
var DOT_POSITIONS = {
|
|
378
|
+
1: { x: 16.5, y: 16.5 },
|
|
379
|
+
2: { x: 50, y: 16.5 },
|
|
380
|
+
3: { x: 83.5, y: 16.5 },
|
|
381
|
+
4: { x: 16.5, y: 50 },
|
|
382
|
+
5: { x: 50, y: 50 },
|
|
383
|
+
6: { x: 83.5, y: 50 },
|
|
384
|
+
7: { x: 16.5, y: 83.5 },
|
|
385
|
+
8: { x: 50, y: 83.5 },
|
|
386
|
+
9: { x: 83.5, y: 83.5 }
|
|
387
|
+
};
|
|
388
|
+
function HumaChallenge({
|
|
389
|
+
challengeId,
|
|
390
|
+
type,
|
|
391
|
+
payload,
|
|
392
|
+
apiKey,
|
|
393
|
+
endpoint,
|
|
394
|
+
onPassed,
|
|
395
|
+
onFailed,
|
|
396
|
+
onBlocked
|
|
397
|
+
}) {
|
|
398
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
399
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
400
|
+
const [attemptsLeft, setAttemptsLeft] = (0, import_react.useState)(3);
|
|
401
|
+
const [userSequence, setUserSequence] = (0, import_react.useState)([]);
|
|
402
|
+
const [showingSequence, setShowingSequence] = (0, import_react.useState)(type === "pattern");
|
|
403
|
+
const [highlightedDot, setHighlightedDot] = (0, import_react.useState)(null);
|
|
404
|
+
(0, import_react.useEffect)(() => {
|
|
405
|
+
if (type !== "pattern" || !payload.sequence) return;
|
|
406
|
+
let i = 0;
|
|
407
|
+
const interval = setInterval(() => {
|
|
408
|
+
var _a;
|
|
409
|
+
setHighlightedDot((_a = payload.sequence[i]) != null ? _a : null);
|
|
410
|
+
i++;
|
|
411
|
+
if (i > payload.sequence.length) {
|
|
412
|
+
clearInterval(interval);
|
|
413
|
+
setShowingSequence(false);
|
|
414
|
+
setHighlightedDot(null);
|
|
415
|
+
}
|
|
416
|
+
}, 700);
|
|
417
|
+
return () => clearInterval(interval);
|
|
418
|
+
}, []);
|
|
419
|
+
const submitAnswer = async (answer) => {
|
|
420
|
+
var _a, _b;
|
|
421
|
+
setLoading(true);
|
|
422
|
+
setError(null);
|
|
423
|
+
const verifyEndpoint = (endpoint != null ? endpoint : "https://humaverify.com").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "") + "/api/v1/challenge/verify";
|
|
424
|
+
try {
|
|
425
|
+
const res = await fetch(verifyEndpoint, {
|
|
426
|
+
method: "POST",
|
|
427
|
+
headers: {
|
|
428
|
+
Authorization: `Bearer ${apiKey}`,
|
|
429
|
+
"Content-Type": "application/json"
|
|
430
|
+
},
|
|
431
|
+
body: JSON.stringify({ challenge_id: challengeId, answer })
|
|
432
|
+
});
|
|
433
|
+
const data = await res.json();
|
|
434
|
+
if (data.passed) {
|
|
435
|
+
onPassed(data.token);
|
|
436
|
+
} else {
|
|
437
|
+
const remaining = (_a = data.attempts_remaining) != null ? _a : 0;
|
|
438
|
+
setAttemptsLeft(remaining);
|
|
439
|
+
setError((_b = data.message) != null ? _b : "Incorrect");
|
|
440
|
+
onFailed == null ? void 0 : onFailed(data);
|
|
441
|
+
if (remaining <= 0) onBlocked == null ? void 0 : onBlocked();
|
|
442
|
+
if (type === "pattern") setUserSequence([]);
|
|
443
|
+
}
|
|
444
|
+
} catch (e) {
|
|
445
|
+
setError("Network error \u2014 please try again");
|
|
446
|
+
} finally {
|
|
447
|
+
setLoading(false);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const handleTargetClick = (targetId) => {
|
|
451
|
+
if (loading) return;
|
|
452
|
+
submitAnswer(targetId);
|
|
453
|
+
};
|
|
454
|
+
const handleDotClick = (dot) => {
|
|
455
|
+
if (loading || showingSequence) return;
|
|
456
|
+
const next = [...userSequence, dot];
|
|
457
|
+
setUserSequence(next);
|
|
458
|
+
if (payload.sequence && next.length === payload.sequence.length) {
|
|
459
|
+
submitAnswer(next.join("-"));
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const containerStyle = {
|
|
463
|
+
fontFamily: "'DM Mono', 'Courier New', monospace",
|
|
464
|
+
background: "#0d0d0d",
|
|
465
|
+
border: "1px solid rgba(125,211,168,0.2)",
|
|
466
|
+
padding: "24px",
|
|
467
|
+
maxWidth: 360,
|
|
468
|
+
color: "#e2e8f0"
|
|
469
|
+
};
|
|
470
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: containerStyle, role: "dialog", "aria-label": "Human verification challenge", children: [
|
|
471
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 10, letterSpacing: "0.15em", textTransform: "uppercase", color: "rgba(125,211,168,0.7)", marginBottom: 4 }, children: "HUMA \u2014 Step-up Verification" }),
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 13, color: "#e2e8f0", marginBottom: 20, lineHeight: 1.5 }, children: showingSequence ? "Watch the sequence..." : payload.instruction }),
|
|
473
|
+
type === "click_targets" && payload.targets && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "relative", width: "100%", paddingTop: "80%", background: "#161616", border: "1px solid #2d2d2d", marginBottom: 16 }, children: payload.targets.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
474
|
+
"button",
|
|
475
|
+
{
|
|
476
|
+
onClick: () => handleTargetClick(t.id),
|
|
477
|
+
disabled: loading,
|
|
478
|
+
style: {
|
|
479
|
+
position: "absolute",
|
|
480
|
+
left: `${t.x}%`,
|
|
481
|
+
top: `${t.y}%`,
|
|
482
|
+
transform: "translate(-50%, -50%)",
|
|
483
|
+
fontSize: 28,
|
|
484
|
+
background: "transparent",
|
|
485
|
+
border: "none",
|
|
486
|
+
cursor: loading ? "default" : "pointer",
|
|
487
|
+
padding: 8,
|
|
488
|
+
borderRadius: 8,
|
|
489
|
+
transition: "transform 0.1s"
|
|
490
|
+
},
|
|
491
|
+
"aria-label": `Target ${t.label}`,
|
|
492
|
+
children: t.label
|
|
493
|
+
},
|
|
494
|
+
t.id
|
|
495
|
+
)) }),
|
|
496
|
+
type === "pattern" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "relative", width: "100%", paddingTop: "100%", background: "#161616", border: "1px solid #2d2d2d", borderRadius: 4, marginBottom: 16 }, children: [1, 2, 3, 4, 5, 6, 7, 8, 9].map((dot) => {
|
|
497
|
+
const pos = DOT_POSITIONS[dot];
|
|
498
|
+
const isActive = userSequence.includes(dot);
|
|
499
|
+
const isHl = highlightedDot === dot;
|
|
500
|
+
const size = 18;
|
|
501
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
502
|
+
"button",
|
|
503
|
+
{
|
|
504
|
+
onClick: () => handleDotClick(dot),
|
|
505
|
+
disabled: loading || showingSequence || isActive,
|
|
506
|
+
style: {
|
|
507
|
+
position: "absolute",
|
|
508
|
+
left: `${pos.x}%`,
|
|
509
|
+
top: `${pos.y}%`,
|
|
510
|
+
transform: "translate(-50%, -50%)",
|
|
511
|
+
width: size,
|
|
512
|
+
height: size,
|
|
513
|
+
borderRadius: "50%",
|
|
514
|
+
background: isHl ? "rgba(125,211,168,0.9)" : isActive ? "rgba(125,211,168,0.5)" : "rgba(255,255,255,0.15)",
|
|
515
|
+
border: isHl ? "2px solid #7dd3a8" : "2px solid transparent",
|
|
516
|
+
cursor: loading || showingSequence || isActive ? "default" : "pointer",
|
|
517
|
+
transition: "background 0.15s, border 0.15s"
|
|
518
|
+
},
|
|
519
|
+
"aria-label": `Dot ${dot}`
|
|
520
|
+
},
|
|
521
|
+
dot
|
|
522
|
+
);
|
|
523
|
+
}) }),
|
|
524
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 11, color: "#f87171", marginBottom: 12, letterSpacing: "0.05em" }, children: [
|
|
525
|
+
error,
|
|
526
|
+
attemptsLeft > 0 ? ` \u2014 ${attemptsLeft} attempt${attemptsLeft !== 1 ? "s" : ""} remaining` : ""
|
|
527
|
+
] }),
|
|
528
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 11, color: "rgba(125,211,168,0.7)", letterSpacing: "0.08em" }, children: "Verifying\u2026" }),
|
|
529
|
+
type === "pattern" && !showingSequence && userSequence.length > 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
530
|
+
"button",
|
|
531
|
+
{
|
|
532
|
+
onClick: () => setUserSequence([]),
|
|
533
|
+
style: { fontSize: 10, color: "#64748b", background: "transparent", border: "none", cursor: "pointer", letterSpacing: "0.08em" },
|
|
534
|
+
children: "Reset pattern"
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 9, color: "#334155", marginTop: 16, letterSpacing: "0.05em" }, children: "Secured by HUMA \xB7 humaverify.com" })
|
|
538
|
+
] });
|
|
539
|
+
}
|
|
302
540
|
function HumaVerifyButton({
|
|
303
541
|
apiKey,
|
|
304
542
|
userId,
|
|
@@ -320,6 +558,7 @@ function HumaVerifyButton({
|
|
|
320
558
|
}
|
|
321
559
|
// Annotate the CommonJS export names for ESM import in node:
|
|
322
560
|
0 && (module.exports = {
|
|
561
|
+
HumaChallenge,
|
|
323
562
|
HumaGate,
|
|
324
563
|
HumaProvider,
|
|
325
564
|
HumaVerifyButton,
|