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/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/core.ts"],"sourcesContent":["/**\n * useHUMA — React Integration\n * Hook + Provider + Gate component\n *\n * Usage:\n * import { useHuma, HumaGate } from 'usehuma/react'\n */\n\n\"use client\"; // Next.js App Router compatible\n\nimport {\n createContext, useContext, useEffect, useRef, useState, useCallback,\n type ReactNode,\n} from \"react\";\nimport { HumaCollector, callHumaApi } from \"./core\";\nimport type { HumaOptions, HumaState, HumaVerifyResult } from \"./types\";\n\n/* ─────────────────────────────────────────\n useHuma — core hook\n ───────────────────────────────────────── */\n\n/**\n * Collect behavioral signals and verify a user as human.\n *\n * @example\n * const { isHuman, score, loading, verify } = useHuma({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * });\n */\nexport function useHuma(opts: HumaOptions): HumaState {\n const collectorRef = useRef<HumaCollector | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<HumaVerifyResult | null>(null);\n const [error, setError] = useState<HumaState[\"error\"]>(null);\n\n // Start collecting signals immediately on mount\n useEffect(() => {\n const collector = new HumaCollector();\n collector.start();\n collectorRef.current = collector;\n return () => collector.stop();\n }, []);\n\n const verify = useCallback(async (): Promise<HumaVerifyResult | null> => {\n if (!collectorRef.current) return null;\n setLoading(true);\n setError(null);\n try {\n const sessionData = collectorRef.current.extract();\n const res = await callHumaApi(opts, sessionData);\n setResult(res);\n return res;\n } catch (err) {\n const humaError = err as HumaState[\"error\"];\n setError(humaError);\n return null;\n } finally {\n setLoading(false);\n }\n }, [opts.apiKey, opts.userId, opts.endpoint]);\n\n return {\n loading,\n result,\n error,\n isHuman: result ? result.human : null,\n score: result ? Math.round(result.confidence * 100) : null,\n verify,\n };\n}\n\n/* ─────────────────────────────────────────\n HumaContext — optional global provider\n ───────────────────────────────────────── */\n\ntype HumaContextValue = HumaState & { ready: boolean };\n\nconst HumaContext = createContext<HumaContextValue | null>(null);\n\ntype HumaProviderProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Auto-verify on mount (default: false — call verify() manually) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Wrap your app (or a subtree) with HumaProvider so any child\n * can call useHumaContext() without re-collecting signals.\n *\n * @example\n * <HumaProvider apiKey=\"huma_live_...\" userId={session.userId}>\n * <App />\n * </HumaProvider>\n */\nexport function HumaProvider({ apiKey, userId, endpoint, autoVerify = false, children }: HumaProviderProps) {\n const state = useHuma({ apiKey, userId, endpoint });\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n setReady(true);\n if (autoVerify) state.verify();\n }, []);\n\n return (\n <HumaContext.Provider value={{ ...state, ready }}>\n {children}\n </HumaContext.Provider>\n );\n}\n\n/**\n * Access the shared HUMA state inside a HumaProvider.\n */\nexport function useHumaContext(): HumaContextValue {\n const ctx = useContext(HumaContext);\n if (!ctx) throw new Error(\"useHumaContext must be used inside <HumaProvider>\");\n return ctx;\n}\n\n/* ─────────────────────────────────────────\n HumaGate — conditional render component\n ───────────────────────────────────────── */\n\ntype HumaGateProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Content to show while verifying */\n fallback?: ReactNode;\n /** Content to show if verification fails (bot detected) */\n blocked?: ReactNode;\n /** Auto-verify on mount (default: true) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Only renders children when the user is verified as human.\n * Shows fallback while loading, blocked if bot detected.\n *\n * @example\n * <HumaGate apiKey=\"huma_live_...\" userId={userId} fallback={<Spinner />}>\n * <SensitiveContent />\n * </HumaGate>\n */\nexport function HumaGate({\n apiKey, userId, endpoint,\n fallback = null,\n blocked = null,\n autoVerify = true,\n children,\n}: HumaGateProps) {\n const { isHuman, loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n useEffect(() => {\n if (autoVerify) verify();\n }, []);\n\n if (loading) return <>{fallback}</>;\n if (isHuman === false) return <>{blocked}</>;\n if (isHuman === null) return <>{fallback}</>;\n return <>{children}</>;\n}\n\n/* ─────────────────────────────────────────\n useHumaSession — continuous monitoring\n ───────────────────────────────────────── */\n\ntype SessionResult = {\n score: number;\n confidence: number;\n baseline: number;\n delta: number;\n anomaly: boolean;\n action: \"allow\" | \"flag\" | \"block\";\n notes: string[];\n};\n\ntype UseHumaSessionOptions = {\n apiKey: string;\n userId: string;\n /** The token returned by verify() */\n token: string;\n /** Heartbeat interval in ms (default: 30000) */\n intervalMs?: number;\n /** Called when anomaly detected (score dropped or absolute bot) */\n onAnomaly?: (result: SessionResult) => void;\n endpoint?: string;\n};\n\n/**\n * Continuously monitors an active session for bot behavior / account takeover.\n * Start after a successful verify(). Sends heartbeats every 30s.\n *\n * @example\n * const { lastResult, anomaly, stop } = useHumaSession({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * token: verifyResult.token,\n * onAnomaly: (r) => router.push('/logout'),\n * });\n */\nexport function useHumaSession(opts: UseHumaSessionOptions) {\n const collectorRef = useRef<HumaCollector | null>(null);\n const windowRef = useRef<HumaCollector | null>(null);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const [lastResult, setLastResult] = useState<SessionResult | null>(null);\n const [anomaly, setAnomaly] = useState(false);\n\n const stop = useCallback(() => {\n if (intervalRef.current) clearInterval(intervalRef.current);\n windowRef.current?.stop();\n }, []);\n\n const sendHeartbeat = useCallback(async () => {\n if (!windowRef.current) return;\n const sessionData = windowRef.current.extract();\n\n // Reset rolling window\n windowRef.current.stop();\n const fresh = new HumaCollector();\n fresh.start();\n windowRef.current = fresh;\n\n try {\n const endpoint = opts.endpoint ?? \"https://humaverify.com/api/v1/session\";\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({\n userId: opts.userId,\n sessionToken: opts.token,\n sessionData,\n }),\n });\n if (!res.ok) return;\n const result: SessionResult = await res.json();\n setLastResult(result);\n if (result.anomaly) {\n setAnomaly(true);\n opts.onAnomaly?.(result);\n }\n } catch { /* silent */ }\n }, [opts.apiKey, opts.userId, opts.token, opts.endpoint]);\n\n useEffect(() => {\n // Start rolling window collector\n const w = new HumaCollector();\n w.start();\n windowRef.current = w;\n\n const ms = opts.intervalMs ?? 30_000;\n intervalRef.current = setInterval(sendHeartbeat, ms);\n\n return () => {\n stop();\n };\n }, [opts.intervalMs]);\n\n return { lastResult, anomaly, stop };\n}\n\n/* ─────────────────────────────────────────\n HumaVerifyButton — one-click verify\n ───────────────────────────────────────── */\n\ntype HumaVerifyButtonProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n onVerified?: (result: HumaVerifyResult) => void;\n onBlocked?: () => void;\n children?: ReactNode;\n className?: string;\n style?: React.CSSProperties;\n};\n\n/**\n * A button that triggers behavioral verification on click.\n * Useful for form submissions, checkout flows, sensitive actions.\n *\n * @example\n * <HumaVerifyButton\n * apiKey=\"huma_live_...\"\n * userId={userId}\n * onVerified={(r) => console.log('Human!', r.confidence)}\n * onBlocked={() => console.log('Bot detected')}\n * >\n * Submit\n * </HumaVerifyButton>\n */\nexport function HumaVerifyButton({\n apiKey, userId, endpoint,\n onVerified, onBlocked,\n children = \"Verify\",\n className, style,\n}: HumaVerifyButtonProps) {\n const { loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n const handleClick = async () => {\n const res = await verify();\n if (!res) return;\n if (res.human) onVerified?.(res);\n else onBlocked?.();\n };\n\n return (\n <button onClick={handleClick} disabled={loading} className={className} style={style}>\n {loading ? \"Verifying…\" : children}\n </button>\n );\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/core.ts"],"sourcesContent":["/**\n * useHUMA — React Integration\n * Hook + Provider + Gate component\n *\n * Usage:\n * import { useHuma, HumaGate } from 'usehuma/react'\n */\n\n\"use client\"; // Next.js App Router compatible\n\nimport {\n createContext, useContext, useEffect, useRef, useState, useCallback,\n type ReactNode,\n} from \"react\";\nimport { HumaCollector, callHumaApi } from \"./core\";\nimport type { HumaOptions, HumaState, HumaVerifyResult } from \"./types\";\n\n/* ─────────────────────────────────────────\n useHuma — core hook\n ───────────────────────────────────────── */\n\n/**\n * Collect behavioral signals and verify a user as human.\n *\n * @example\n * const { isHuman, score, loading, verify } = useHuma({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * });\n */\nexport function useHuma(opts: HumaOptions): HumaState {\n const collectorRef = useRef<HumaCollector | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<HumaVerifyResult | null>(null);\n const [error, setError] = useState<HumaState[\"error\"]>(null);\n\n // Start collecting signals immediately on mount\n useEffect(() => {\n const collector = new HumaCollector();\n collector.start();\n collectorRef.current = collector;\n return () => collector.stop();\n }, []);\n\n const verify = useCallback(async (): Promise<HumaVerifyResult | null> => {\n if (!collectorRef.current) return null;\n setLoading(true);\n setError(null);\n try {\n const sessionData = collectorRef.current.extract();\n const res = await callHumaApi(opts, sessionData);\n setResult(res);\n return res;\n } catch (err) {\n const humaError = err as HumaState[\"error\"];\n setError(humaError);\n return null;\n } finally {\n setLoading(false);\n }\n }, [opts.apiKey, opts.userId, opts.endpoint]);\n\n return {\n loading,\n result,\n error,\n isHuman: result ? result.human : null,\n score: result ? Math.round(result.confidence * 100) : null,\n verify,\n };\n}\n\n/* ─────────────────────────────────────────\n HumaContext — optional global provider\n ───────────────────────────────────────── */\n\ntype HumaContextValue = HumaState & { ready: boolean };\n\nconst HumaContext = createContext<HumaContextValue | null>(null);\n\ntype HumaProviderProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Auto-verify on mount (default: false — call verify() manually) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Wrap your app (or a subtree) with HumaProvider so any child\n * can call useHumaContext() without re-collecting signals.\n *\n * @example\n * <HumaProvider apiKey=\"huma_live_...\" userId={session.userId}>\n * <App />\n * </HumaProvider>\n */\nexport function HumaProvider({ apiKey, userId, endpoint, autoVerify = false, children }: HumaProviderProps) {\n const state = useHuma({ apiKey, userId, endpoint });\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n setReady(true);\n if (autoVerify) state.verify();\n }, []);\n\n return (\n <HumaContext.Provider value={{ ...state, ready }}>\n {children}\n </HumaContext.Provider>\n );\n}\n\n/**\n * Access the shared HUMA state inside a HumaProvider.\n */\nexport function useHumaContext(): HumaContextValue {\n const ctx = useContext(HumaContext);\n if (!ctx) throw new Error(\"useHumaContext must be used inside <HumaProvider>\");\n return ctx;\n}\n\n/* ─────────────────────────────────────────\n HumaGate — conditional render component\n ───────────────────────────────────────── */\n\ntype HumaGateProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Content to show while verifying */\n fallback?: ReactNode;\n /** Content to show if verification fails (bot detected) */\n blocked?: ReactNode;\n /** Auto-verify on mount (default: true) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Only renders children when the user is verified as human.\n * Shows fallback while loading, blocked if bot detected.\n *\n * @example\n * <HumaGate apiKey=\"huma_live_...\" userId={userId} fallback={<Spinner />}>\n * <SensitiveContent />\n * </HumaGate>\n */\nexport function HumaGate({\n apiKey, userId, endpoint,\n fallback = null,\n blocked = null,\n autoVerify = true,\n children,\n}: HumaGateProps) {\n const { isHuman, loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n useEffect(() => {\n if (autoVerify) verify();\n }, []);\n\n if (loading) return <>{fallback}</>;\n if (isHuman === false) return <>{blocked}</>;\n if (isHuman === null) return <>{fallback}</>;\n return <>{children}</>;\n}\n\n/* ─────────────────────────────────────────\n useHumaSession — continuous monitoring\n ───────────────────────────────────────── */\n\ntype SessionResult = {\n score: number;\n confidence: number;\n baseline: number;\n delta: number;\n anomaly: boolean;\n action: \"allow\" | \"flag\" | \"block\";\n notes: string[];\n};\n\ntype UseHumaSessionOptions = {\n apiKey: string;\n userId: string;\n /** The token returned by verify() */\n token: string;\n /** Heartbeat interval in ms (default: 30000) */\n intervalMs?: number;\n /** Called when anomaly detected (score dropped or absolute bot) */\n onAnomaly?: (result: SessionResult) => void;\n endpoint?: string;\n};\n\n/**\n * Continuously monitors an active session for bot behavior / account takeover.\n * Start after a successful verify(). Sends heartbeats every 30s.\n *\n * @example\n * const { lastResult, anomaly, stop } = useHumaSession({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * token: verifyResult.token,\n * onAnomaly: (r) => router.push('/logout'),\n * });\n */\nexport function useHumaSession(opts: UseHumaSessionOptions) {\n const collectorRef = useRef<HumaCollector | null>(null);\n const windowRef = useRef<HumaCollector | null>(null);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const [lastResult, setLastResult] = useState<SessionResult | null>(null);\n const [anomaly, setAnomaly] = useState(false);\n\n const stop = useCallback(() => {\n if (intervalRef.current) clearInterval(intervalRef.current);\n windowRef.current?.stop();\n }, []);\n\n const sendHeartbeat = useCallback(async () => {\n if (!windowRef.current) return;\n const sessionData = windowRef.current.extract();\n\n // Reset rolling window\n windowRef.current.stop();\n const fresh = new HumaCollector();\n fresh.start();\n windowRef.current = fresh;\n\n try {\n const endpoint = opts.endpoint ?? \"https://humaverify.com/api/v1/session\";\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({\n userId: opts.userId,\n sessionToken: opts.token,\n sessionData,\n }),\n });\n if (!res.ok) return;\n const result: SessionResult = await res.json();\n setLastResult(result);\n if (result.anomaly) {\n setAnomaly(true);\n opts.onAnomaly?.(result);\n }\n } catch { /* silent */ }\n }, [opts.apiKey, opts.userId, opts.token, opts.endpoint]);\n\n useEffect(() => {\n // Start rolling window collector\n const w = new HumaCollector();\n w.start();\n windowRef.current = w;\n\n const ms = opts.intervalMs ?? 30_000;\n intervalRef.current = setInterval(sendHeartbeat, ms);\n\n return () => {\n stop();\n };\n }, [opts.intervalMs]);\n\n return { lastResult, anomaly, stop };\n}\n\n/* ─────────────────────────────────────────\n HumaChallenge — step-up verification UI\n ───────────────────────────────────────── */\n\ntype ChallengeTarget = { id: string; x: number; y: number; label: string };\n\ntype ChallengePassed = { passed: true; token: string };\ntype ChallengeFailed = { passed: false; message: string; attempts_remaining?: number };\ntype ChallengeResult = ChallengePassed | ChallengeFailed;\n\ntype HumaChallengeProps = {\n /** challenge_id from POST /api/v1/challenge/create */\n challengeId: string;\n /** type from challenge create response */\n type: \"click_targets\" | \"pattern\";\n /** payload from challenge create response */\n payload: {\n instruction: string;\n targets?: ChallengeTarget[];\n sequence?: number[];\n };\n apiKey: string;\n endpoint?: string;\n onPassed: (token: string) => void;\n onFailed?: (result: ChallengeFailed) => void;\n /** Called when user exhausts all attempts */\n onBlocked?: () => void;\n};\n\nconst DOT_POSITIONS: Record<number, { x: number; y: number }> = {\n 1: { x: 16.5, y: 16.5 }, 2: { x: 50, y: 16.5 }, 3: { x: 83.5, y: 16.5 },\n 4: { x: 16.5, y: 50 }, 5: { x: 50, y: 50 }, 6: { x: 83.5, y: 50 },\n 7: { x: 16.5, y: 83.5 }, 8: { x: 50, y: 83.5 }, 9: { x: 83.5, y: 83.5 },\n};\n\n/**\n * Renders a step-up verification challenge when behavioral signals are inconclusive.\n * Supports \"click_targets\" and \"pattern\" challenge types.\n *\n * @example\n * // After verify() returns { human: false, confidence: 0.45 }:\n * const ch = await fetch('/api/v1/challenge/create', { ... });\n * return (\n * <HumaChallenge\n * challengeId={ch.challenge_id}\n * type={ch.type}\n * payload={ch.payload}\n * apiKey=\"huma_live_...\"\n * onPassed={(token) => continueLogin(token)}\n * onBlocked={() => router.push('/blocked')}\n * />\n * );\n */\nexport function HumaChallenge({\n challengeId, type, payload, apiKey, endpoint,\n onPassed, onFailed, onBlocked,\n}: HumaChallengeProps) {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [attemptsLeft, setAttemptsLeft] = useState(3);\n // Pattern state\n const [userSequence, setUserSequence] = useState<number[]>([]);\n const [showingSequence, setShowingSequence] = useState(type === \"pattern\");\n const [highlightedDot, setHighlightedDot] = useState<number | null>(null);\n\n // Show pattern sequence once on mount for pattern challenges\n useEffect(() => {\n if (type !== \"pattern\" || !payload.sequence) return;\n let i = 0;\n const interval = setInterval(() => {\n setHighlightedDot(payload.sequence![i] ?? null);\n i++;\n if (i > payload.sequence!.length) {\n clearInterval(interval);\n setShowingSequence(false);\n setHighlightedDot(null);\n }\n }, 700);\n return () => clearInterval(interval);\n }, []);\n\n const submitAnswer = async (answer: string) => {\n setLoading(true);\n setError(null);\n const verifyEndpoint = (endpoint ?? \"https://humaverify.com\")\n .replace(/\\/api\\/v1\\/verify$/, \"\")\n .replace(/\\/$/, \"\") + \"/api/v1/challenge/verify\";\n\n try {\n const res = await fetch(verifyEndpoint, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ challenge_id: challengeId, answer }),\n });\n const data: ChallengeResult = await res.json();\n if (data.passed) {\n onPassed(data.token);\n } else {\n const remaining = data.attempts_remaining ?? 0;\n setAttemptsLeft(remaining);\n setError(data.message ?? \"Incorrect\");\n onFailed?.(data);\n if (remaining <= 0) onBlocked?.();\n // Reset pattern input\n if (type === \"pattern\") setUserSequence([]);\n }\n } catch {\n setError(\"Network error — please try again\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleTargetClick = (targetId: string) => {\n if (loading) return;\n submitAnswer(targetId);\n };\n\n const handleDotClick = (dot: number) => {\n if (loading || showingSequence) return;\n const next = [...userSequence, dot];\n setUserSequence(next);\n if (payload.sequence && next.length === payload.sequence.length) {\n submitAnswer(next.join(\"-\"));\n }\n };\n\n const containerStyle: React.CSSProperties = {\n fontFamily: \"'DM Mono', 'Courier New', monospace\",\n background: \"#0d0d0d\",\n border: \"1px solid rgba(125,211,168,0.2)\",\n padding: \"24px\",\n maxWidth: 360,\n color: \"#e2e8f0\",\n };\n\n return (\n <div style={containerStyle} role=\"dialog\" aria-label=\"Human verification challenge\">\n {/* Header */}\n <div style={{ fontSize: 10, letterSpacing: \"0.15em\", textTransform: \"uppercase\", color: \"rgba(125,211,168,0.7)\", marginBottom: 4 }}>\n HUMA — Step-up Verification\n </div>\n <div style={{ fontSize: 13, color: \"#e2e8f0\", marginBottom: 20, lineHeight: 1.5 }}>\n {showingSequence ? \"Watch the sequence...\" : payload.instruction}\n </div>\n\n {/* Click Targets */}\n {type === \"click_targets\" && payload.targets && (\n <div style={{ position: \"relative\", width: \"100%\", paddingTop: \"80%\", background: \"#161616\", border: \"1px solid #2d2d2d\", marginBottom: 16 }}>\n {payload.targets.map(t => (\n <button\n key={t.id}\n onClick={() => handleTargetClick(t.id)}\n disabled={loading}\n style={{\n position: \"absolute\",\n left: `${t.x}%`,\n top: `${t.y}%`,\n transform: \"translate(-50%, -50%)\",\n fontSize: 28,\n background: \"transparent\",\n border: \"none\",\n cursor: loading ? \"default\" : \"pointer\",\n padding: 8,\n borderRadius: 8,\n transition: \"transform 0.1s\",\n }}\n aria-label={`Target ${t.label}`}\n >\n {t.label}\n </button>\n ))}\n </div>\n )}\n\n {/* Pattern Grid */}\n {type === \"pattern\" && (\n <div style={{ position: \"relative\", width: \"100%\", paddingTop: \"100%\", background: \"#161616\", border: \"1px solid #2d2d2d\", borderRadius: 4, marginBottom: 16 }}>\n {[1, 2, 3, 4, 5, 6, 7, 8, 9].map(dot => {\n const pos = DOT_POSITIONS[dot];\n const isActive = userSequence.includes(dot);\n const isHl = highlightedDot === dot;\n const size = 18;\n return (\n <button\n key={dot}\n onClick={() => handleDotClick(dot)}\n disabled={loading || showingSequence || isActive}\n style={{\n position: \"absolute\",\n left: `${pos.x}%`,\n top: `${pos.y}%`,\n transform: \"translate(-50%, -50%)\",\n width: size, height: size,\n borderRadius: \"50%\",\n background: isHl\n ? \"rgba(125,211,168,0.9)\"\n : isActive\n ? \"rgba(125,211,168,0.5)\"\n : \"rgba(255,255,255,0.15)\",\n border: isHl ? \"2px solid #7dd3a8\" : \"2px solid transparent\",\n cursor: (loading || showingSequence || isActive) ? \"default\" : \"pointer\",\n transition: \"background 0.15s, border 0.15s\",\n }}\n aria-label={`Dot ${dot}`}\n />\n );\n })}\n </div>\n )}\n\n {/* Status */}\n {error && (\n <div style={{ fontSize: 11, color: \"#f87171\", marginBottom: 12, letterSpacing: \"0.05em\" }}>\n {error}{attemptsLeft > 0 ? ` — ${attemptsLeft} attempt${attemptsLeft !== 1 ? \"s\" : \"\"} remaining` : \"\"}\n </div>\n )}\n {loading && (\n <div style={{ fontSize: 11, color: \"rgba(125,211,168,0.7)\", letterSpacing: \"0.08em\" }}>\n Verifying…\n </div>\n )}\n\n {/* Pattern reset */}\n {type === \"pattern\" && !showingSequence && userSequence.length > 0 && !loading && (\n <button\n onClick={() => setUserSequence([])}\n style={{ fontSize: 10, color: \"#64748b\", background: \"transparent\", border: \"none\", cursor: \"pointer\", letterSpacing: \"0.08em\" }}\n >\n Reset pattern\n </button>\n )}\n\n <div style={{ fontSize: 9, color: \"#334155\", marginTop: 16, letterSpacing: \"0.05em\" }}>\n Secured by HUMA · humaverify.com\n </div>\n </div>\n );\n}\n\n/* ─────────────────────────────────────────\n HumaVerifyButton — one-click verify\n ───────────────────────────────────────── */\n\ntype HumaVerifyButtonProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n onVerified?: (result: HumaVerifyResult) => void;\n onBlocked?: () => void;\n children?: ReactNode;\n className?: string;\n style?: React.CSSProperties;\n};\n\n/**\n * A button that triggers behavioral verification on click.\n * Useful for form submissions, checkout flows, sensitive actions.\n *\n * @example\n * <HumaVerifyButton\n * apiKey=\"huma_live_...\"\n * userId={userId}\n * onVerified={(r) => console.log('Human!', r.confidence)}\n * onBlocked={() => console.log('Bot detected')}\n * >\n * Submit\n * </HumaVerifyButton>\n */\nexport function HumaVerifyButton({\n apiKey, userId, endpoint,\n onVerified, onBlocked,\n children = \"Verify\",\n className, style,\n}: HumaVerifyButtonProps) {\n const { loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n const handleClick = async () => {\n const res = await verify();\n if (!res) return;\n if (res.human) onVerified?.(res);\n else onBlocked?.();\n };\n\n return (\n <button onClick={handleClick} disabled={loading} className={className} style={style}>\n {loading ? \"Verifying…\" : children}\n </button>\n );\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/react.mjs
CHANGED
|
@@ -54,6 +54,22 @@ function mouseFeatures(pts) {
|
|
|
54
54
|
}
|
|
55
55
|
return { speed_cv: cv(speeds), direction_changes: dirChanges, sample_count: pts.length };
|
|
56
56
|
}
|
|
57
|
+
function touchFeatures(taps, moves, multiTouchCount) {
|
|
58
|
+
const tapIntervals = [];
|
|
59
|
+
for (let i = 1; i < taps.length; i++) tapIntervals.push(taps[i].t - taps[i - 1].t);
|
|
60
|
+
const durations = taps.map((t) => t.duration_ms);
|
|
61
|
+
const forces = taps.filter((t) => t.force > 0).map((t) => t.force);
|
|
62
|
+
const speeds = moves.map((m) => m.speed);
|
|
63
|
+
return {
|
|
64
|
+
tap_count: taps.length,
|
|
65
|
+
tap_interval_cv: cv(tapIntervals),
|
|
66
|
+
tap_duration_cv: cv(durations),
|
|
67
|
+
tap_force_cv: cv(forces),
|
|
68
|
+
touch_move_count: moves.length,
|
|
69
|
+
touch_speed_cv: cv(speeds),
|
|
70
|
+
multi_touch_count: multiTouchCount
|
|
71
|
+
};
|
|
72
|
+
}
|
|
57
73
|
var HumaCollector = class {
|
|
58
74
|
constructor() {
|
|
59
75
|
this.signals = {
|
|
@@ -62,9 +78,13 @@ var HumaCollector = class {
|
|
|
62
78
|
scrolls: [],
|
|
63
79
|
clicks: [],
|
|
64
80
|
focusEvents: [],
|
|
81
|
+
taps: [],
|
|
82
|
+
touchMoves: [],
|
|
83
|
+
multiTouchCount: 0,
|
|
65
84
|
startTime: Date.now(),
|
|
66
85
|
lastKeyTime: null,
|
|
67
|
-
lastScrollTime: null
|
|
86
|
+
lastScrollTime: null,
|
|
87
|
+
activeTouches: {}
|
|
68
88
|
};
|
|
69
89
|
this.listening = false;
|
|
70
90
|
this.onMouseMove = (e) => {
|
|
@@ -89,6 +109,46 @@ var HumaCollector = class {
|
|
|
89
109
|
};
|
|
90
110
|
this.onFocus = () => this.signals.focusEvents.push({ type: "focus", t: Date.now() });
|
|
91
111
|
this.onBlur = () => this.signals.focusEvents.push({ type: "blur", t: Date.now() });
|
|
112
|
+
this.onTouchStart = (e) => {
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
if (e.touches.length > 1) this.signals.multiTouchCount++;
|
|
115
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
116
|
+
const t = e.changedTouches[i];
|
|
117
|
+
this.signals.activeTouches[t.identifier] = { t: now, x: t.clientX, y: t.clientY };
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.onTouchEnd = (e) => {
|
|
121
|
+
var _a;
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
124
|
+
const touch = e.changedTouches[i];
|
|
125
|
+
const start = this.signals.activeTouches[touch.identifier];
|
|
126
|
+
if (!start) continue;
|
|
127
|
+
delete this.signals.activeTouches[touch.identifier];
|
|
128
|
+
if (this.signals.taps.length < 80) {
|
|
129
|
+
this.signals.taps.push({
|
|
130
|
+
t: now,
|
|
131
|
+
duration_ms: now - start.t,
|
|
132
|
+
force: (_a = touch.force) != null ? _a : 0,
|
|
133
|
+
touch_count: e.touches.length + 1
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
this.onTouchMove = (e) => {
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
for (let i = 0; i < e.changedTouches.length; i++) {
|
|
141
|
+
const touch = e.changedTouches[i];
|
|
142
|
+
const start = this.signals.activeTouches[touch.identifier];
|
|
143
|
+
if (!start) continue;
|
|
144
|
+
const dx = touch.clientX - start.x;
|
|
145
|
+
const dy = touch.clientY - start.y;
|
|
146
|
+
const dt = Math.max(now - start.t, 1);
|
|
147
|
+
if (this.signals.touchMoves.length < 100)
|
|
148
|
+
this.signals.touchMoves.push({ speed: Math.sqrt(dx * dx + dy * dy) / dt, t: now });
|
|
149
|
+
this.signals.activeTouches[touch.identifier] = { t: now, x: touch.clientX, y: touch.clientY };
|
|
150
|
+
}
|
|
151
|
+
};
|
|
92
152
|
}
|
|
93
153
|
start() {
|
|
94
154
|
if (this.listening || typeof window === "undefined") return;
|
|
@@ -97,6 +157,9 @@ var HumaCollector = class {
|
|
|
97
157
|
document.addEventListener("keydown", this.onKeyDown, { passive: true });
|
|
98
158
|
document.addEventListener("scroll", this.onScroll, { passive: true });
|
|
99
159
|
document.addEventListener("click", this.onClick, { passive: true });
|
|
160
|
+
document.addEventListener("touchstart", this.onTouchStart, { passive: true });
|
|
161
|
+
document.addEventListener("touchend", this.onTouchEnd, { passive: true });
|
|
162
|
+
document.addEventListener("touchmove", this.onTouchMove, { passive: true });
|
|
100
163
|
window.addEventListener("focus", this.onFocus, { passive: true });
|
|
101
164
|
window.addEventListener("blur", this.onBlur, { passive: true });
|
|
102
165
|
}
|
|
@@ -107,12 +170,16 @@ var HumaCollector = class {
|
|
|
107
170
|
document.removeEventListener("keydown", this.onKeyDown);
|
|
108
171
|
document.removeEventListener("scroll", this.onScroll);
|
|
109
172
|
document.removeEventListener("click", this.onClick);
|
|
173
|
+
document.removeEventListener("touchstart", this.onTouchStart);
|
|
174
|
+
document.removeEventListener("touchend", this.onTouchEnd);
|
|
175
|
+
document.removeEventListener("touchmove", this.onTouchMove);
|
|
110
176
|
window.removeEventListener("focus", this.onFocus);
|
|
111
177
|
window.removeEventListener("blur", this.onBlur);
|
|
112
178
|
}
|
|
113
179
|
extract() {
|
|
114
|
-
const { keys, scrolls, clicks, focusEvents, startTime, mouse } = this.signals;
|
|
180
|
+
const { keys, scrolls, clicks, focusEvents, startTime, mouse, taps, touchMoves, multiTouchCount } = this.signals;
|
|
115
181
|
const m = mouseFeatures(mouse);
|
|
182
|
+
const t = touchFeatures(taps, touchMoves, multiTouchCount);
|
|
116
183
|
const clickIntervals = [];
|
|
117
184
|
for (let i = 1; i < clicks.length; i++)
|
|
118
185
|
clickIntervals.push(clicks[i].t - clicks[i - 1].t);
|
|
@@ -127,7 +194,14 @@ var HumaCollector = class {
|
|
|
127
194
|
mouse_sample_count: m.sample_count,
|
|
128
195
|
click_interval_cv: cv(clickIntervals),
|
|
129
196
|
click_count: clicks.length,
|
|
130
|
-
tab_switches: focusEvents.length
|
|
197
|
+
tab_switches: focusEvents.length,
|
|
198
|
+
tap_count: t.tap_count,
|
|
199
|
+
tap_interval_cv: t.tap_interval_cv,
|
|
200
|
+
tap_duration_cv: t.tap_duration_cv,
|
|
201
|
+
tap_force_cv: t.tap_force_cv,
|
|
202
|
+
touch_move_count: t.touch_move_count,
|
|
203
|
+
touch_speed_cv: t.touch_speed_cv,
|
|
204
|
+
multi_touch_count: t.multi_touch_count
|
|
131
205
|
};
|
|
132
206
|
}
|
|
133
207
|
debug() {
|
|
@@ -153,7 +227,7 @@ async function callHumaApi(opts, sessionData) {
|
|
|
153
227
|
}
|
|
154
228
|
|
|
155
229
|
// src/react.tsx
|
|
156
|
-
import { Fragment, jsx } from "react/jsx-runtime";
|
|
230
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
157
231
|
function useHuma(opts) {
|
|
158
232
|
const collectorRef = useRef(null);
|
|
159
233
|
const [loading, setLoading] = useState(false);
|
|
@@ -280,6 +354,169 @@ function useHumaSession(opts) {
|
|
|
280
354
|
}, [opts.intervalMs]);
|
|
281
355
|
return { lastResult, anomaly, stop };
|
|
282
356
|
}
|
|
357
|
+
var DOT_POSITIONS = {
|
|
358
|
+
1: { x: 16.5, y: 16.5 },
|
|
359
|
+
2: { x: 50, y: 16.5 },
|
|
360
|
+
3: { x: 83.5, y: 16.5 },
|
|
361
|
+
4: { x: 16.5, y: 50 },
|
|
362
|
+
5: { x: 50, y: 50 },
|
|
363
|
+
6: { x: 83.5, y: 50 },
|
|
364
|
+
7: { x: 16.5, y: 83.5 },
|
|
365
|
+
8: { x: 50, y: 83.5 },
|
|
366
|
+
9: { x: 83.5, y: 83.5 }
|
|
367
|
+
};
|
|
368
|
+
function HumaChallenge({
|
|
369
|
+
challengeId,
|
|
370
|
+
type,
|
|
371
|
+
payload,
|
|
372
|
+
apiKey,
|
|
373
|
+
endpoint,
|
|
374
|
+
onPassed,
|
|
375
|
+
onFailed,
|
|
376
|
+
onBlocked
|
|
377
|
+
}) {
|
|
378
|
+
const [loading, setLoading] = useState(false);
|
|
379
|
+
const [error, setError] = useState(null);
|
|
380
|
+
const [attemptsLeft, setAttemptsLeft] = useState(3);
|
|
381
|
+
const [userSequence, setUserSequence] = useState([]);
|
|
382
|
+
const [showingSequence, setShowingSequence] = useState(type === "pattern");
|
|
383
|
+
const [highlightedDot, setHighlightedDot] = useState(null);
|
|
384
|
+
useEffect(() => {
|
|
385
|
+
if (type !== "pattern" || !payload.sequence) return;
|
|
386
|
+
let i = 0;
|
|
387
|
+
const interval = setInterval(() => {
|
|
388
|
+
var _a;
|
|
389
|
+
setHighlightedDot((_a = payload.sequence[i]) != null ? _a : null);
|
|
390
|
+
i++;
|
|
391
|
+
if (i > payload.sequence.length) {
|
|
392
|
+
clearInterval(interval);
|
|
393
|
+
setShowingSequence(false);
|
|
394
|
+
setHighlightedDot(null);
|
|
395
|
+
}
|
|
396
|
+
}, 700);
|
|
397
|
+
return () => clearInterval(interval);
|
|
398
|
+
}, []);
|
|
399
|
+
const submitAnswer = async (answer) => {
|
|
400
|
+
var _a, _b;
|
|
401
|
+
setLoading(true);
|
|
402
|
+
setError(null);
|
|
403
|
+
const verifyEndpoint = (endpoint != null ? endpoint : "https://humaverify.com").replace(/\/api\/v1\/verify$/, "").replace(/\/$/, "") + "/api/v1/challenge/verify";
|
|
404
|
+
try {
|
|
405
|
+
const res = await fetch(verifyEndpoint, {
|
|
406
|
+
method: "POST",
|
|
407
|
+
headers: {
|
|
408
|
+
Authorization: `Bearer ${apiKey}`,
|
|
409
|
+
"Content-Type": "application/json"
|
|
410
|
+
},
|
|
411
|
+
body: JSON.stringify({ challenge_id: challengeId, answer })
|
|
412
|
+
});
|
|
413
|
+
const data = await res.json();
|
|
414
|
+
if (data.passed) {
|
|
415
|
+
onPassed(data.token);
|
|
416
|
+
} else {
|
|
417
|
+
const remaining = (_a = data.attempts_remaining) != null ? _a : 0;
|
|
418
|
+
setAttemptsLeft(remaining);
|
|
419
|
+
setError((_b = data.message) != null ? _b : "Incorrect");
|
|
420
|
+
onFailed == null ? void 0 : onFailed(data);
|
|
421
|
+
if (remaining <= 0) onBlocked == null ? void 0 : onBlocked();
|
|
422
|
+
if (type === "pattern") setUserSequence([]);
|
|
423
|
+
}
|
|
424
|
+
} catch (e) {
|
|
425
|
+
setError("Network error \u2014 please try again");
|
|
426
|
+
} finally {
|
|
427
|
+
setLoading(false);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
const handleTargetClick = (targetId) => {
|
|
431
|
+
if (loading) return;
|
|
432
|
+
submitAnswer(targetId);
|
|
433
|
+
};
|
|
434
|
+
const handleDotClick = (dot) => {
|
|
435
|
+
if (loading || showingSequence) return;
|
|
436
|
+
const next = [...userSequence, dot];
|
|
437
|
+
setUserSequence(next);
|
|
438
|
+
if (payload.sequence && next.length === payload.sequence.length) {
|
|
439
|
+
submitAnswer(next.join("-"));
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
const containerStyle = {
|
|
443
|
+
fontFamily: "'DM Mono', 'Courier New', monospace",
|
|
444
|
+
background: "#0d0d0d",
|
|
445
|
+
border: "1px solid rgba(125,211,168,0.2)",
|
|
446
|
+
padding: "24px",
|
|
447
|
+
maxWidth: 360,
|
|
448
|
+
color: "#e2e8f0"
|
|
449
|
+
};
|
|
450
|
+
return /* @__PURE__ */ jsxs("div", { style: containerStyle, role: "dialog", "aria-label": "Human verification challenge", children: [
|
|
451
|
+
/* @__PURE__ */ 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" }),
|
|
452
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "#e2e8f0", marginBottom: 20, lineHeight: 1.5 }, children: showingSequence ? "Watch the sequence..." : payload.instruction }),
|
|
453
|
+
type === "click_targets" && payload.targets && /* @__PURE__ */ jsx("div", { style: { position: "relative", width: "100%", paddingTop: "80%", background: "#161616", border: "1px solid #2d2d2d", marginBottom: 16 }, children: payload.targets.map((t) => /* @__PURE__ */ jsx(
|
|
454
|
+
"button",
|
|
455
|
+
{
|
|
456
|
+
onClick: () => handleTargetClick(t.id),
|
|
457
|
+
disabled: loading,
|
|
458
|
+
style: {
|
|
459
|
+
position: "absolute",
|
|
460
|
+
left: `${t.x}%`,
|
|
461
|
+
top: `${t.y}%`,
|
|
462
|
+
transform: "translate(-50%, -50%)",
|
|
463
|
+
fontSize: 28,
|
|
464
|
+
background: "transparent",
|
|
465
|
+
border: "none",
|
|
466
|
+
cursor: loading ? "default" : "pointer",
|
|
467
|
+
padding: 8,
|
|
468
|
+
borderRadius: 8,
|
|
469
|
+
transition: "transform 0.1s"
|
|
470
|
+
},
|
|
471
|
+
"aria-label": `Target ${t.label}`,
|
|
472
|
+
children: t.label
|
|
473
|
+
},
|
|
474
|
+
t.id
|
|
475
|
+
)) }),
|
|
476
|
+
type === "pattern" && /* @__PURE__ */ 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) => {
|
|
477
|
+
const pos = DOT_POSITIONS[dot];
|
|
478
|
+
const isActive = userSequence.includes(dot);
|
|
479
|
+
const isHl = highlightedDot === dot;
|
|
480
|
+
const size = 18;
|
|
481
|
+
return /* @__PURE__ */ jsx(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
onClick: () => handleDotClick(dot),
|
|
485
|
+
disabled: loading || showingSequence || isActive,
|
|
486
|
+
style: {
|
|
487
|
+
position: "absolute",
|
|
488
|
+
left: `${pos.x}%`,
|
|
489
|
+
top: `${pos.y}%`,
|
|
490
|
+
transform: "translate(-50%, -50%)",
|
|
491
|
+
width: size,
|
|
492
|
+
height: size,
|
|
493
|
+
borderRadius: "50%",
|
|
494
|
+
background: isHl ? "rgba(125,211,168,0.9)" : isActive ? "rgba(125,211,168,0.5)" : "rgba(255,255,255,0.15)",
|
|
495
|
+
border: isHl ? "2px solid #7dd3a8" : "2px solid transparent",
|
|
496
|
+
cursor: loading || showingSequence || isActive ? "default" : "pointer",
|
|
497
|
+
transition: "background 0.15s, border 0.15s"
|
|
498
|
+
},
|
|
499
|
+
"aria-label": `Dot ${dot}`
|
|
500
|
+
},
|
|
501
|
+
dot
|
|
502
|
+
);
|
|
503
|
+
}) }),
|
|
504
|
+
error && /* @__PURE__ */ jsxs("div", { style: { fontSize: 11, color: "#f87171", marginBottom: 12, letterSpacing: "0.05em" }, children: [
|
|
505
|
+
error,
|
|
506
|
+
attemptsLeft > 0 ? ` \u2014 ${attemptsLeft} attempt${attemptsLeft !== 1 ? "s" : ""} remaining` : ""
|
|
507
|
+
] }),
|
|
508
|
+
loading && /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(125,211,168,0.7)", letterSpacing: "0.08em" }, children: "Verifying\u2026" }),
|
|
509
|
+
type === "pattern" && !showingSequence && userSequence.length > 0 && !loading && /* @__PURE__ */ jsx(
|
|
510
|
+
"button",
|
|
511
|
+
{
|
|
512
|
+
onClick: () => setUserSequence([]),
|
|
513
|
+
style: { fontSize: 10, color: "#64748b", background: "transparent", border: "none", cursor: "pointer", letterSpacing: "0.08em" },
|
|
514
|
+
children: "Reset pattern"
|
|
515
|
+
}
|
|
516
|
+
),
|
|
517
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 9, color: "#334155", marginTop: 16, letterSpacing: "0.05em" }, children: "Secured by HUMA \xB7 humaverify.com" })
|
|
518
|
+
] });
|
|
519
|
+
}
|
|
283
520
|
function HumaVerifyButton({
|
|
284
521
|
apiKey,
|
|
285
522
|
userId,
|
|
@@ -300,6 +537,7 @@ function HumaVerifyButton({
|
|
|
300
537
|
return /* @__PURE__ */ jsx("button", { onClick: handleClick, disabled: loading, className, style, children: loading ? "Verifying\u2026" : children });
|
|
301
538
|
}
|
|
302
539
|
export {
|
|
540
|
+
HumaChallenge,
|
|
303
541
|
HumaGate,
|
|
304
542
|
HumaProvider,
|
|
305
543
|
HumaVerifyButton,
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/core.ts"],"sourcesContent":["/**\n * useHUMA — React Integration\n * Hook + Provider + Gate component\n *\n * Usage:\n * import { useHuma, HumaGate } from 'usehuma/react'\n */\n\n\"use client\"; // Next.js App Router compatible\n\nimport {\n createContext, useContext, useEffect, useRef, useState, useCallback,\n type ReactNode,\n} from \"react\";\nimport { HumaCollector, callHumaApi } from \"./core\";\nimport type { HumaOptions, HumaState, HumaVerifyResult } from \"./types\";\n\n/* ─────────────────────────────────────────\n useHuma — core hook\n ───────────────────────────────────────── */\n\n/**\n * Collect behavioral signals and verify a user as human.\n *\n * @example\n * const { isHuman, score, loading, verify } = useHuma({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * });\n */\nexport function useHuma(opts: HumaOptions): HumaState {\n const collectorRef = useRef<HumaCollector | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<HumaVerifyResult | null>(null);\n const [error, setError] = useState<HumaState[\"error\"]>(null);\n\n // Start collecting signals immediately on mount\n useEffect(() => {\n const collector = new HumaCollector();\n collector.start();\n collectorRef.current = collector;\n return () => collector.stop();\n }, []);\n\n const verify = useCallback(async (): Promise<HumaVerifyResult | null> => {\n if (!collectorRef.current) return null;\n setLoading(true);\n setError(null);\n try {\n const sessionData = collectorRef.current.extract();\n const res = await callHumaApi(opts, sessionData);\n setResult(res);\n return res;\n } catch (err) {\n const humaError = err as HumaState[\"error\"];\n setError(humaError);\n return null;\n } finally {\n setLoading(false);\n }\n }, [opts.apiKey, opts.userId, opts.endpoint]);\n\n return {\n loading,\n result,\n error,\n isHuman: result ? result.human : null,\n score: result ? Math.round(result.confidence * 100) : null,\n verify,\n };\n}\n\n/* ─────────────────────────────────────────\n HumaContext — optional global provider\n ───────────────────────────────────────── */\n\ntype HumaContextValue = HumaState & { ready: boolean };\n\nconst HumaContext = createContext<HumaContextValue | null>(null);\n\ntype HumaProviderProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Auto-verify on mount (default: false — call verify() manually) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Wrap your app (or a subtree) with HumaProvider so any child\n * can call useHumaContext() without re-collecting signals.\n *\n * @example\n * <HumaProvider apiKey=\"huma_live_...\" userId={session.userId}>\n * <App />\n * </HumaProvider>\n */\nexport function HumaProvider({ apiKey, userId, endpoint, autoVerify = false, children }: HumaProviderProps) {\n const state = useHuma({ apiKey, userId, endpoint });\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n setReady(true);\n if (autoVerify) state.verify();\n }, []);\n\n return (\n <HumaContext.Provider value={{ ...state, ready }}>\n {children}\n </HumaContext.Provider>\n );\n}\n\n/**\n * Access the shared HUMA state inside a HumaProvider.\n */\nexport function useHumaContext(): HumaContextValue {\n const ctx = useContext(HumaContext);\n if (!ctx) throw new Error(\"useHumaContext must be used inside <HumaProvider>\");\n return ctx;\n}\n\n/* ─────────────────────────────────────────\n HumaGate — conditional render component\n ───────────────────────────────────────── */\n\ntype HumaGateProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Content to show while verifying */\n fallback?: ReactNode;\n /** Content to show if verification fails (bot detected) */\n blocked?: ReactNode;\n /** Auto-verify on mount (default: true) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Only renders children when the user is verified as human.\n * Shows fallback while loading, blocked if bot detected.\n *\n * @example\n * <HumaGate apiKey=\"huma_live_...\" userId={userId} fallback={<Spinner />}>\n * <SensitiveContent />\n * </HumaGate>\n */\nexport function HumaGate({\n apiKey, userId, endpoint,\n fallback = null,\n blocked = null,\n autoVerify = true,\n children,\n}: HumaGateProps) {\n const { isHuman, loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n useEffect(() => {\n if (autoVerify) verify();\n }, []);\n\n if (loading) return <>{fallback}</>;\n if (isHuman === false) return <>{blocked}</>;\n if (isHuman === null) return <>{fallback}</>;\n return <>{children}</>;\n}\n\n/* ─────────────────────────────────────────\n useHumaSession — continuous monitoring\n ───────────────────────────────────────── */\n\ntype SessionResult = {\n score: number;\n confidence: number;\n baseline: number;\n delta: number;\n anomaly: boolean;\n action: \"allow\" | \"flag\" | \"block\";\n notes: string[];\n};\n\ntype UseHumaSessionOptions = {\n apiKey: string;\n userId: string;\n /** The token returned by verify() */\n token: string;\n /** Heartbeat interval in ms (default: 30000) */\n intervalMs?: number;\n /** Called when anomaly detected (score dropped or absolute bot) */\n onAnomaly?: (result: SessionResult) => void;\n endpoint?: string;\n};\n\n/**\n * Continuously monitors an active session for bot behavior / account takeover.\n * Start after a successful verify(). Sends heartbeats every 30s.\n *\n * @example\n * const { lastResult, anomaly, stop } = useHumaSession({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * token: verifyResult.token,\n * onAnomaly: (r) => router.push('/logout'),\n * });\n */\nexport function useHumaSession(opts: UseHumaSessionOptions) {\n const collectorRef = useRef<HumaCollector | null>(null);\n const windowRef = useRef<HumaCollector | null>(null);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const [lastResult, setLastResult] = useState<SessionResult | null>(null);\n const [anomaly, setAnomaly] = useState(false);\n\n const stop = useCallback(() => {\n if (intervalRef.current) clearInterval(intervalRef.current);\n windowRef.current?.stop();\n }, []);\n\n const sendHeartbeat = useCallback(async () => {\n if (!windowRef.current) return;\n const sessionData = windowRef.current.extract();\n\n // Reset rolling window\n windowRef.current.stop();\n const fresh = new HumaCollector();\n fresh.start();\n windowRef.current = fresh;\n\n try {\n const endpoint = opts.endpoint ?? \"https://humaverify.com/api/v1/session\";\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({\n userId: opts.userId,\n sessionToken: opts.token,\n sessionData,\n }),\n });\n if (!res.ok) return;\n const result: SessionResult = await res.json();\n setLastResult(result);\n if (result.anomaly) {\n setAnomaly(true);\n opts.onAnomaly?.(result);\n }\n } catch { /* silent */ }\n }, [opts.apiKey, opts.userId, opts.token, opts.endpoint]);\n\n useEffect(() => {\n // Start rolling window collector\n const w = new HumaCollector();\n w.start();\n windowRef.current = w;\n\n const ms = opts.intervalMs ?? 30_000;\n intervalRef.current = setInterval(sendHeartbeat, ms);\n\n return () => {\n stop();\n };\n }, [opts.intervalMs]);\n\n return { lastResult, anomaly, stop };\n}\n\n/* ─────────────────────────────────────────\n HumaVerifyButton — one-click verify\n ───────────────────────────────────────── */\n\ntype HumaVerifyButtonProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n onVerified?: (result: HumaVerifyResult) => void;\n onBlocked?: () => void;\n children?: ReactNode;\n className?: string;\n style?: React.CSSProperties;\n};\n\n/**\n * A button that triggers behavioral verification on click.\n * Useful for form submissions, checkout flows, sensitive actions.\n *\n * @example\n * <HumaVerifyButton\n * apiKey=\"huma_live_...\"\n * userId={userId}\n * onVerified={(r) => console.log('Human!', r.confidence)}\n * onBlocked={() => console.log('Bot detected')}\n * >\n * Submit\n * </HumaVerifyButton>\n */\nexport function HumaVerifyButton({\n apiKey, userId, endpoint,\n onVerified, onBlocked,\n children = \"Verify\",\n className, style,\n}: HumaVerifyButtonProps) {\n const { loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n const handleClick = async () => {\n const res = await verify();\n if (!res) return;\n if (res.human) onVerified?.(res);\n else onBlocked?.();\n };\n\n return (\n <button onClick={handleClick} disabled={loading} className={className} style={style}>\n {loading ? \"Verifying…\" : children}\n </button>\n );\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/core.ts"],"sourcesContent":["/**\n * useHUMA — React Integration\n * Hook + Provider + Gate component\n *\n * Usage:\n * import { useHuma, HumaGate } from 'usehuma/react'\n */\n\n\"use client\"; // Next.js App Router compatible\n\nimport {\n createContext, useContext, useEffect, useRef, useState, useCallback,\n type ReactNode,\n} from \"react\";\nimport { HumaCollector, callHumaApi } from \"./core\";\nimport type { HumaOptions, HumaState, HumaVerifyResult } from \"./types\";\n\n/* ─────────────────────────────────────────\n useHuma — core hook\n ───────────────────────────────────────── */\n\n/**\n * Collect behavioral signals and verify a user as human.\n *\n * @example\n * const { isHuman, score, loading, verify } = useHuma({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * });\n */\nexport function useHuma(opts: HumaOptions): HumaState {\n const collectorRef = useRef<HumaCollector | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<HumaVerifyResult | null>(null);\n const [error, setError] = useState<HumaState[\"error\"]>(null);\n\n // Start collecting signals immediately on mount\n useEffect(() => {\n const collector = new HumaCollector();\n collector.start();\n collectorRef.current = collector;\n return () => collector.stop();\n }, []);\n\n const verify = useCallback(async (): Promise<HumaVerifyResult | null> => {\n if (!collectorRef.current) return null;\n setLoading(true);\n setError(null);\n try {\n const sessionData = collectorRef.current.extract();\n const res = await callHumaApi(opts, sessionData);\n setResult(res);\n return res;\n } catch (err) {\n const humaError = err as HumaState[\"error\"];\n setError(humaError);\n return null;\n } finally {\n setLoading(false);\n }\n }, [opts.apiKey, opts.userId, opts.endpoint]);\n\n return {\n loading,\n result,\n error,\n isHuman: result ? result.human : null,\n score: result ? Math.round(result.confidence * 100) : null,\n verify,\n };\n}\n\n/* ─────────────────────────────────────────\n HumaContext — optional global provider\n ───────────────────────────────────────── */\n\ntype HumaContextValue = HumaState & { ready: boolean };\n\nconst HumaContext = createContext<HumaContextValue | null>(null);\n\ntype HumaProviderProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Auto-verify on mount (default: false — call verify() manually) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Wrap your app (or a subtree) with HumaProvider so any child\n * can call useHumaContext() without re-collecting signals.\n *\n * @example\n * <HumaProvider apiKey=\"huma_live_...\" userId={session.userId}>\n * <App />\n * </HumaProvider>\n */\nexport function HumaProvider({ apiKey, userId, endpoint, autoVerify = false, children }: HumaProviderProps) {\n const state = useHuma({ apiKey, userId, endpoint });\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n setReady(true);\n if (autoVerify) state.verify();\n }, []);\n\n return (\n <HumaContext.Provider value={{ ...state, ready }}>\n {children}\n </HumaContext.Provider>\n );\n}\n\n/**\n * Access the shared HUMA state inside a HumaProvider.\n */\nexport function useHumaContext(): HumaContextValue {\n const ctx = useContext(HumaContext);\n if (!ctx) throw new Error(\"useHumaContext must be used inside <HumaProvider>\");\n return ctx;\n}\n\n/* ─────────────────────────────────────────\n HumaGate — conditional render component\n ───────────────────────────────────────── */\n\ntype HumaGateProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n /** Content to show while verifying */\n fallback?: ReactNode;\n /** Content to show if verification fails (bot detected) */\n blocked?: ReactNode;\n /** Auto-verify on mount (default: true) */\n autoVerify?: boolean;\n children: ReactNode;\n};\n\n/**\n * Only renders children when the user is verified as human.\n * Shows fallback while loading, blocked if bot detected.\n *\n * @example\n * <HumaGate apiKey=\"huma_live_...\" userId={userId} fallback={<Spinner />}>\n * <SensitiveContent />\n * </HumaGate>\n */\nexport function HumaGate({\n apiKey, userId, endpoint,\n fallback = null,\n blocked = null,\n autoVerify = true,\n children,\n}: HumaGateProps) {\n const { isHuman, loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n useEffect(() => {\n if (autoVerify) verify();\n }, []);\n\n if (loading) return <>{fallback}</>;\n if (isHuman === false) return <>{blocked}</>;\n if (isHuman === null) return <>{fallback}</>;\n return <>{children}</>;\n}\n\n/* ─────────────────────────────────────────\n useHumaSession — continuous monitoring\n ───────────────────────────────────────── */\n\ntype SessionResult = {\n score: number;\n confidence: number;\n baseline: number;\n delta: number;\n anomaly: boolean;\n action: \"allow\" | \"flag\" | \"block\";\n notes: string[];\n};\n\ntype UseHumaSessionOptions = {\n apiKey: string;\n userId: string;\n /** The token returned by verify() */\n token: string;\n /** Heartbeat interval in ms (default: 30000) */\n intervalMs?: number;\n /** Called when anomaly detected (score dropped or absolute bot) */\n onAnomaly?: (result: SessionResult) => void;\n endpoint?: string;\n};\n\n/**\n * Continuously monitors an active session for bot behavior / account takeover.\n * Start after a successful verify(). Sends heartbeats every 30s.\n *\n * @example\n * const { lastResult, anomaly, stop } = useHumaSession({\n * apiKey: 'huma_live_...',\n * userId: 'user_123',\n * token: verifyResult.token,\n * onAnomaly: (r) => router.push('/logout'),\n * });\n */\nexport function useHumaSession(opts: UseHumaSessionOptions) {\n const collectorRef = useRef<HumaCollector | null>(null);\n const windowRef = useRef<HumaCollector | null>(null);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const [lastResult, setLastResult] = useState<SessionResult | null>(null);\n const [anomaly, setAnomaly] = useState(false);\n\n const stop = useCallback(() => {\n if (intervalRef.current) clearInterval(intervalRef.current);\n windowRef.current?.stop();\n }, []);\n\n const sendHeartbeat = useCallback(async () => {\n if (!windowRef.current) return;\n const sessionData = windowRef.current.extract();\n\n // Reset rolling window\n windowRef.current.stop();\n const fresh = new HumaCollector();\n fresh.start();\n windowRef.current = fresh;\n\n try {\n const endpoint = opts.endpoint ?? \"https://humaverify.com/api/v1/session\";\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({\n userId: opts.userId,\n sessionToken: opts.token,\n sessionData,\n }),\n });\n if (!res.ok) return;\n const result: SessionResult = await res.json();\n setLastResult(result);\n if (result.anomaly) {\n setAnomaly(true);\n opts.onAnomaly?.(result);\n }\n } catch { /* silent */ }\n }, [opts.apiKey, opts.userId, opts.token, opts.endpoint]);\n\n useEffect(() => {\n // Start rolling window collector\n const w = new HumaCollector();\n w.start();\n windowRef.current = w;\n\n const ms = opts.intervalMs ?? 30_000;\n intervalRef.current = setInterval(sendHeartbeat, ms);\n\n return () => {\n stop();\n };\n }, [opts.intervalMs]);\n\n return { lastResult, anomaly, stop };\n}\n\n/* ─────────────────────────────────────────\n HumaChallenge — step-up verification UI\n ───────────────────────────────────────── */\n\ntype ChallengeTarget = { id: string; x: number; y: number; label: string };\n\ntype ChallengePassed = { passed: true; token: string };\ntype ChallengeFailed = { passed: false; message: string; attempts_remaining?: number };\ntype ChallengeResult = ChallengePassed | ChallengeFailed;\n\ntype HumaChallengeProps = {\n /** challenge_id from POST /api/v1/challenge/create */\n challengeId: string;\n /** type from challenge create response */\n type: \"click_targets\" | \"pattern\";\n /** payload from challenge create response */\n payload: {\n instruction: string;\n targets?: ChallengeTarget[];\n sequence?: number[];\n };\n apiKey: string;\n endpoint?: string;\n onPassed: (token: string) => void;\n onFailed?: (result: ChallengeFailed) => void;\n /** Called when user exhausts all attempts */\n onBlocked?: () => void;\n};\n\nconst DOT_POSITIONS: Record<number, { x: number; y: number }> = {\n 1: { x: 16.5, y: 16.5 }, 2: { x: 50, y: 16.5 }, 3: { x: 83.5, y: 16.5 },\n 4: { x: 16.5, y: 50 }, 5: { x: 50, y: 50 }, 6: { x: 83.5, y: 50 },\n 7: { x: 16.5, y: 83.5 }, 8: { x: 50, y: 83.5 }, 9: { x: 83.5, y: 83.5 },\n};\n\n/**\n * Renders a step-up verification challenge when behavioral signals are inconclusive.\n * Supports \"click_targets\" and \"pattern\" challenge types.\n *\n * @example\n * // After verify() returns { human: false, confidence: 0.45 }:\n * const ch = await fetch('/api/v1/challenge/create', { ... });\n * return (\n * <HumaChallenge\n * challengeId={ch.challenge_id}\n * type={ch.type}\n * payload={ch.payload}\n * apiKey=\"huma_live_...\"\n * onPassed={(token) => continueLogin(token)}\n * onBlocked={() => router.push('/blocked')}\n * />\n * );\n */\nexport function HumaChallenge({\n challengeId, type, payload, apiKey, endpoint,\n onPassed, onFailed, onBlocked,\n}: HumaChallengeProps) {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [attemptsLeft, setAttemptsLeft] = useState(3);\n // Pattern state\n const [userSequence, setUserSequence] = useState<number[]>([]);\n const [showingSequence, setShowingSequence] = useState(type === \"pattern\");\n const [highlightedDot, setHighlightedDot] = useState<number | null>(null);\n\n // Show pattern sequence once on mount for pattern challenges\n useEffect(() => {\n if (type !== \"pattern\" || !payload.sequence) return;\n let i = 0;\n const interval = setInterval(() => {\n setHighlightedDot(payload.sequence![i] ?? null);\n i++;\n if (i > payload.sequence!.length) {\n clearInterval(interval);\n setShowingSequence(false);\n setHighlightedDot(null);\n }\n }, 700);\n return () => clearInterval(interval);\n }, []);\n\n const submitAnswer = async (answer: string) => {\n setLoading(true);\n setError(null);\n const verifyEndpoint = (endpoint ?? \"https://humaverify.com\")\n .replace(/\\/api\\/v1\\/verify$/, \"\")\n .replace(/\\/$/, \"\") + \"/api/v1/challenge/verify\";\n\n try {\n const res = await fetch(verifyEndpoint, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ challenge_id: challengeId, answer }),\n });\n const data: ChallengeResult = await res.json();\n if (data.passed) {\n onPassed(data.token);\n } else {\n const remaining = data.attempts_remaining ?? 0;\n setAttemptsLeft(remaining);\n setError(data.message ?? \"Incorrect\");\n onFailed?.(data);\n if (remaining <= 0) onBlocked?.();\n // Reset pattern input\n if (type === \"pattern\") setUserSequence([]);\n }\n } catch {\n setError(\"Network error — please try again\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleTargetClick = (targetId: string) => {\n if (loading) return;\n submitAnswer(targetId);\n };\n\n const handleDotClick = (dot: number) => {\n if (loading || showingSequence) return;\n const next = [...userSequence, dot];\n setUserSequence(next);\n if (payload.sequence && next.length === payload.sequence.length) {\n submitAnswer(next.join(\"-\"));\n }\n };\n\n const containerStyle: React.CSSProperties = {\n fontFamily: \"'DM Mono', 'Courier New', monospace\",\n background: \"#0d0d0d\",\n border: \"1px solid rgba(125,211,168,0.2)\",\n padding: \"24px\",\n maxWidth: 360,\n color: \"#e2e8f0\",\n };\n\n return (\n <div style={containerStyle} role=\"dialog\" aria-label=\"Human verification challenge\">\n {/* Header */}\n <div style={{ fontSize: 10, letterSpacing: \"0.15em\", textTransform: \"uppercase\", color: \"rgba(125,211,168,0.7)\", marginBottom: 4 }}>\n HUMA — Step-up Verification\n </div>\n <div style={{ fontSize: 13, color: \"#e2e8f0\", marginBottom: 20, lineHeight: 1.5 }}>\n {showingSequence ? \"Watch the sequence...\" : payload.instruction}\n </div>\n\n {/* Click Targets */}\n {type === \"click_targets\" && payload.targets && (\n <div style={{ position: \"relative\", width: \"100%\", paddingTop: \"80%\", background: \"#161616\", border: \"1px solid #2d2d2d\", marginBottom: 16 }}>\n {payload.targets.map(t => (\n <button\n key={t.id}\n onClick={() => handleTargetClick(t.id)}\n disabled={loading}\n style={{\n position: \"absolute\",\n left: `${t.x}%`,\n top: `${t.y}%`,\n transform: \"translate(-50%, -50%)\",\n fontSize: 28,\n background: \"transparent\",\n border: \"none\",\n cursor: loading ? \"default\" : \"pointer\",\n padding: 8,\n borderRadius: 8,\n transition: \"transform 0.1s\",\n }}\n aria-label={`Target ${t.label}`}\n >\n {t.label}\n </button>\n ))}\n </div>\n )}\n\n {/* Pattern Grid */}\n {type === \"pattern\" && (\n <div style={{ position: \"relative\", width: \"100%\", paddingTop: \"100%\", background: \"#161616\", border: \"1px solid #2d2d2d\", borderRadius: 4, marginBottom: 16 }}>\n {[1, 2, 3, 4, 5, 6, 7, 8, 9].map(dot => {\n const pos = DOT_POSITIONS[dot];\n const isActive = userSequence.includes(dot);\n const isHl = highlightedDot === dot;\n const size = 18;\n return (\n <button\n key={dot}\n onClick={() => handleDotClick(dot)}\n disabled={loading || showingSequence || isActive}\n style={{\n position: \"absolute\",\n left: `${pos.x}%`,\n top: `${pos.y}%`,\n transform: \"translate(-50%, -50%)\",\n width: size, height: size,\n borderRadius: \"50%\",\n background: isHl\n ? \"rgba(125,211,168,0.9)\"\n : isActive\n ? \"rgba(125,211,168,0.5)\"\n : \"rgba(255,255,255,0.15)\",\n border: isHl ? \"2px solid #7dd3a8\" : \"2px solid transparent\",\n cursor: (loading || showingSequence || isActive) ? \"default\" : \"pointer\",\n transition: \"background 0.15s, border 0.15s\",\n }}\n aria-label={`Dot ${dot}`}\n />\n );\n })}\n </div>\n )}\n\n {/* Status */}\n {error && (\n <div style={{ fontSize: 11, color: \"#f87171\", marginBottom: 12, letterSpacing: \"0.05em\" }}>\n {error}{attemptsLeft > 0 ? ` — ${attemptsLeft} attempt${attemptsLeft !== 1 ? \"s\" : \"\"} remaining` : \"\"}\n </div>\n )}\n {loading && (\n <div style={{ fontSize: 11, color: \"rgba(125,211,168,0.7)\", letterSpacing: \"0.08em\" }}>\n Verifying…\n </div>\n )}\n\n {/* Pattern reset */}\n {type === \"pattern\" && !showingSequence && userSequence.length > 0 && !loading && (\n <button\n onClick={() => setUserSequence([])}\n style={{ fontSize: 10, color: \"#64748b\", background: \"transparent\", border: \"none\", cursor: \"pointer\", letterSpacing: \"0.08em\" }}\n >\n Reset pattern\n </button>\n )}\n\n <div style={{ fontSize: 9, color: \"#334155\", marginTop: 16, letterSpacing: \"0.05em\" }}>\n Secured by HUMA · humaverify.com\n </div>\n </div>\n );\n}\n\n/* ─────────────────────────────────────────\n HumaVerifyButton — one-click verify\n ───────────────────────────────────────── */\n\ntype HumaVerifyButtonProps = {\n apiKey: string;\n userId: string;\n endpoint?: string;\n onVerified?: (result: HumaVerifyResult) => void;\n onBlocked?: () => void;\n children?: ReactNode;\n className?: string;\n style?: React.CSSProperties;\n};\n\n/**\n * A button that triggers behavioral verification on click.\n * Useful for form submissions, checkout flows, sensitive actions.\n *\n * @example\n * <HumaVerifyButton\n * apiKey=\"huma_live_...\"\n * userId={userId}\n * onVerified={(r) => console.log('Human!', r.confidence)}\n * onBlocked={() => console.log('Bot detected')}\n * >\n * Submit\n * </HumaVerifyButton>\n */\nexport function HumaVerifyButton({\n apiKey, userId, endpoint,\n onVerified, onBlocked,\n children = \"Verify\",\n className, style,\n}: HumaVerifyButtonProps) {\n const { loading, verify } = useHuma({ apiKey, userId, endpoint });\n\n const handleClick = async () => {\n const res = await verify();\n if (!res) return;\n if (res.human) onVerified?.(res);\n else onBlocked?.();\n };\n\n return (\n <button onClick={handleClick} disabled={loading} className={className} style={style}>\n {loading ? \"Verifying…\" : children}\n </button>\n );\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":[]}
|