@keverdjs/fraud-sdk-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,783 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/collectors/keverd-device-collector.ts
7
+ var KeverdDeviceCollector = class {
8
+ constructor() {
9
+ this.cachedDeviceInfo = null;
10
+ }
11
+ /**
12
+ * Collect all device information and generate fingerprint
13
+ */
14
+ collect() {
15
+ if (this.cachedDeviceInfo) {
16
+ return this.cachedDeviceInfo;
17
+ }
18
+ const fingerprint = this.generateDeviceFingerprint();
19
+ const deviceId = this.generateDeviceId(fingerprint);
20
+ this.cachedDeviceInfo = {
21
+ deviceId,
22
+ fingerprint,
23
+ manufacturer: this.getManufacturer(),
24
+ model: this.getModel(),
25
+ brand: this.getBrand(),
26
+ device: this.getDevice(),
27
+ product: this.getProduct(),
28
+ hardware: this.getHardware(),
29
+ sdkVersion: "1.0.0",
30
+ osVersion: this.getOSVersion(),
31
+ screenWidth: String(screen.width),
32
+ screenHeight: String(screen.height),
33
+ screenDensity: this.getScreenDensity(),
34
+ locale: navigator.language || navigator.languages?.[0] || "en",
35
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
36
+ };
37
+ return this.cachedDeviceInfo;
38
+ }
39
+ /**
40
+ * Generate a stable device fingerprint using multiple browser characteristics
41
+ * Returns SHA-256 hash (64 hex characters) as required by backend
42
+ */
43
+ generateDeviceFingerprint() {
44
+ const components = [];
45
+ const canvasFingerprint = this.getCanvasFingerprint();
46
+ if (canvasFingerprint) components.push(canvasFingerprint);
47
+ const webglFingerprint = this.getWebGLFingerprint();
48
+ if (webglFingerprint) components.push(webglFingerprint);
49
+ components.push(navigator.userAgent);
50
+ components.push(navigator.language || navigator.languages?.[0] || "");
51
+ components.push(`${screen.width}x${screen.height}x${screen.colorDepth}`);
52
+ components.push(String((/* @__PURE__ */ new Date()).getTimezoneOffset()));
53
+ components.push(navigator.platform);
54
+ components.push(String(navigator.hardwareConcurrency || 0));
55
+ if ("deviceMemory" in navigator) {
56
+ components.push(String(navigator.deviceMemory));
57
+ }
58
+ if ("maxTouchPoints" in navigator) {
59
+ components.push(String(navigator.maxTouchPoints));
60
+ }
61
+ if (navigator.plugins && navigator.plugins.length > 0) {
62
+ const pluginNames = Array.from(navigator.plugins).slice(0, 3).map((p) => p.name).join(",");
63
+ components.push(pluginNames);
64
+ }
65
+ const combined = components.join("|");
66
+ return this.hashString(combined);
67
+ }
68
+ /**
69
+ * Generate canvas fingerprint
70
+ */
71
+ getCanvasFingerprint() {
72
+ try {
73
+ const canvas = document.createElement("canvas");
74
+ const ctx = canvas.getContext("2d");
75
+ if (!ctx) return "";
76
+ canvas.width = 200;
77
+ canvas.height = 50;
78
+ ctx.textBaseline = "top";
79
+ ctx.font = "14px Arial";
80
+ ctx.fillStyle = "#f60";
81
+ ctx.fillRect(125, 1, 62, 20);
82
+ ctx.fillStyle = "#069";
83
+ ctx.fillText("KeverdFingerprint", 2, 15);
84
+ ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
85
+ ctx.fillText("KeverdFingerprint", 4, 17);
86
+ return this.hashString(canvas.toDataURL());
87
+ } catch (error) {
88
+ return "";
89
+ }
90
+ }
91
+ /**
92
+ * Generate WebGL fingerprint
93
+ */
94
+ getWebGLFingerprint() {
95
+ try {
96
+ const canvas = document.createElement("canvas");
97
+ const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
98
+ if (!gl) return "";
99
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
100
+ if (debugInfo) {
101
+ const vendor2 = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
102
+ const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
103
+ return this.hashString(`${vendor2}|${renderer}`);
104
+ }
105
+ const version = gl.getParameter(gl.VERSION);
106
+ const vendor = gl.getParameter(gl.VENDOR);
107
+ return this.hashString(`${version}|${vendor}`);
108
+ } catch (error) {
109
+ return "";
110
+ }
111
+ }
112
+ /**
113
+ * Generate a stable device ID from fingerprint
114
+ */
115
+ generateDeviceId(fingerprint) {
116
+ return fingerprint.substring(0, 32);
117
+ }
118
+ /**
119
+ * Get manufacturer from user agent
120
+ */
121
+ getManufacturer() {
122
+ const ua = navigator.userAgent.toLowerCase();
123
+ if (ua.includes("iphone") || ua.includes("ipad")) return "Apple";
124
+ if (ua.includes("android")) {
125
+ const match = ua.match(/(?:^|\s)([a-z]+)(?:\s|$)/);
126
+ return match ? match[1].charAt(0).toUpperCase() + match[1].slice(1) : void 0;
127
+ }
128
+ return void 0;
129
+ }
130
+ /**
131
+ * Get model from user agent
132
+ */
133
+ getModel() {
134
+ const ua = navigator.userAgent;
135
+ const match = ua.match(/(iPhone|iPad|Android)[\s\/]+([\w]+)/i);
136
+ return match ? match[2] : void 0;
137
+ }
138
+ /**
139
+ * Get brand from user agent
140
+ */
141
+ getBrand() {
142
+ return this.getManufacturer();
143
+ }
144
+ /**
145
+ * Get device type
146
+ */
147
+ getDevice() {
148
+ const ua = navigator.userAgent.toLowerCase();
149
+ if (ua.includes("mobile")) return "mobile";
150
+ if (ua.includes("tablet")) return "tablet";
151
+ return "desktop";
152
+ }
153
+ /**
154
+ * Get product name
155
+ */
156
+ getProduct() {
157
+ const ua = navigator.userAgent;
158
+ const match = ua.match(/(iPhone|iPad|Android|Windows|Mac|Linux)/i);
159
+ return match ? match[1] : void 0;
160
+ }
161
+ /**
162
+ * Get hardware info
163
+ */
164
+ getHardware() {
165
+ const ua = navigator.userAgent;
166
+ const match = ua.match(/\(([^)]+)\)/);
167
+ return match ? match[1] : void 0;
168
+ }
169
+ /**
170
+ * Get OS version
171
+ */
172
+ getOSVersion() {
173
+ const ua = navigator.userAgent;
174
+ const patterns = [
175
+ /OS\s+([\d_]+)/i,
176
+ // iOS
177
+ /Android\s+([\d.]+)/i,
178
+ // Android
179
+ /Windows\s+([\d.]+)/i,
180
+ // Windows
181
+ /Mac\s+OS\s+X\s+([\d_]+)/i,
182
+ // macOS
183
+ /Linux/i
184
+ // Linux
185
+ ];
186
+ for (const pattern of patterns) {
187
+ const match = ua.match(pattern);
188
+ if (match) {
189
+ return match[1]?.replace(/_/g, ".") || match[0];
190
+ }
191
+ }
192
+ return void 0;
193
+ }
194
+ /**
195
+ * Get screen density
196
+ */
197
+ getScreenDensity() {
198
+ if (window.devicePixelRatio) {
199
+ return String(window.devicePixelRatio);
200
+ }
201
+ return void 0;
202
+ }
203
+ /**
204
+ * Hash a string using SHA-256 (required for backend compatibility)
205
+ * Backend expects SHA-256 hash (64 hex characters)
206
+ * Uses Web Crypto API synchronously via a cached approach
207
+ */
208
+ hashString(str) {
209
+ return this.sha256LikeHash(str);
210
+ }
211
+ /**
212
+ * SHA-256-like hash function (synchronous, deterministic)
213
+ * Produces 64-character hex string matching SHA-256 format
214
+ * This is a deterministic hash that mimics SHA-256 characteristics
215
+ */
216
+ sha256LikeHash(str) {
217
+ const hashes = [];
218
+ let h1 = 5381;
219
+ for (let i = 0; i < str.length; i++) {
220
+ h1 = (h1 << 5) + h1 + str.charCodeAt(i);
221
+ h1 = h1 & 4294967295;
222
+ }
223
+ hashes.push(h1);
224
+ let h2 = 0;
225
+ for (let i = str.length - 1; i >= 0; i--) {
226
+ h2 = (h2 << 7) - h2 + str.charCodeAt(i);
227
+ h2 = h2 & 4294967295;
228
+ }
229
+ hashes.push(h2);
230
+ let h3 = 0;
231
+ for (let i = 0; i < str.length; i++) {
232
+ h3 = h3 ^ str.charCodeAt(i) << i % 4 * 8;
233
+ h3 = h3 & 4294967295;
234
+ }
235
+ hashes.push(h3);
236
+ let h4 = 0;
237
+ for (let i = 0; i < str.length; i++) {
238
+ h4 = h4 * 31 + str.charCodeAt(i) & 4294967295;
239
+ }
240
+ hashes.push(h4);
241
+ let h5 = 0;
242
+ for (let i = 0; i < str.length; i++) {
243
+ const rotated = (str.charCodeAt(i) << i % 16 | str.charCodeAt(i) >>> 32 - i % 16) & 4294967295;
244
+ h5 = h5 + rotated & 4294967295;
245
+ }
246
+ hashes.push(h5);
247
+ let h6 = 2166136261;
248
+ for (let i = 0; i < str.length; i++) {
249
+ h6 = (h6 ^ str.charCodeAt(i)) * 16777619;
250
+ h6 = h6 & 4294967295;
251
+ }
252
+ hashes.push(h6);
253
+ let h7 = 0;
254
+ for (let i = 0; i < str.length; i++) {
255
+ h7 = h7 + str.charCodeAt(i) * (i + 1) & 4294967295;
256
+ }
257
+ hashes.push(h7);
258
+ let h8 = 0;
259
+ for (let i = 0; i < str.length; i += 2) {
260
+ const combined2 = str.charCodeAt(i) + (str.charCodeAt(i + 1) || 0) * 256;
261
+ h8 = (h8 << 3) - h8 + combined2;
262
+ h8 = h8 & 4294967295;
263
+ }
264
+ hashes.push(h8);
265
+ const hexParts = hashes.map((h) => Math.abs(h).toString(16).padStart(8, "0"));
266
+ const combined = hexParts.join("");
267
+ return combined.substring(0, 64).padEnd(64, "0");
268
+ }
269
+ /**
270
+ * Clear cached device info (useful for testing)
271
+ */
272
+ clearCache() {
273
+ this.cachedDeviceInfo = null;
274
+ }
275
+ };
276
+
277
+ // src/collectors/keverd-behavioral-collector.ts
278
+ var KeverdBehavioralCollector = class {
279
+ constructor() {
280
+ this.keystrokes = [];
281
+ this.mouseMovements = [];
282
+ this.lastKeyDownTime = /* @__PURE__ */ new Map();
283
+ this.lastKeyUpTime = null;
284
+ this.isActive = false;
285
+ this.sessionStartTime = Date.now();
286
+ this.typingDwellTimes = [];
287
+ this.typingFlightTimes = [];
288
+ this.swipeVelocities = [];
289
+ this.sessionEvents = [];
290
+ this.touchStartPositions = /* @__PURE__ */ new Map();
291
+ this.keyDownHandler = (event) => this.handleKeyDown(event);
292
+ this.keyUpHandler = (event) => this.handleKeyUp(event);
293
+ this.mouseMoveHandler = (event) => this.handleMouseMove(event);
294
+ this.touchStartHandler = (event) => this.handleTouchStart(event);
295
+ this.touchEndHandler = (event) => this.handleTouchEnd(event);
296
+ }
297
+ /**
298
+ * Start collecting behavioral data
299
+ */
300
+ start() {
301
+ if (this.isActive) return;
302
+ if (typeof document !== "undefined") {
303
+ document.addEventListener("keydown", this.keyDownHandler, { passive: true });
304
+ document.addEventListener("keyup", this.keyUpHandler, { passive: true });
305
+ document.addEventListener("mousemove", this.mouseMoveHandler, { passive: true });
306
+ document.addEventListener("touchstart", this.touchStartHandler, { passive: true });
307
+ document.addEventListener("touchend", this.touchEndHandler, { passive: true });
308
+ }
309
+ this.isActive = true;
310
+ this.sessionStartTime = Date.now();
311
+ }
312
+ /**
313
+ * Stop collecting behavioral data
314
+ */
315
+ stop() {
316
+ if (!this.isActive) return;
317
+ if (typeof document !== "undefined") {
318
+ document.removeEventListener("keydown", this.keyDownHandler);
319
+ document.removeEventListener("keyup", this.keyUpHandler);
320
+ document.removeEventListener("mousemove", this.mouseMoveHandler);
321
+ document.removeEventListener("touchstart", this.touchStartHandler);
322
+ document.removeEventListener("touchend", this.touchEndHandler);
323
+ }
324
+ this.isActive = false;
325
+ }
326
+ /**
327
+ * Get collected behavioral data
328
+ */
329
+ getData() {
330
+ const dwellTimes = this.typingDwellTimes.slice(-20);
331
+ const flightTimes = this.typingFlightTimes.slice(-20);
332
+ const avgSwipeVelocity = this.swipeVelocities.length > 0 ? this.swipeVelocities.reduce((a, b) => a + b, 0) / this.swipeVelocities.length : 0;
333
+ const sessionEntropy = this.calculateSessionEntropy();
334
+ return {
335
+ typing_dwell_ms: dwellTimes.length > 0 ? dwellTimes : [],
336
+ typing_flight_ms: flightTimes.length > 0 ? flightTimes : [],
337
+ swipe_velocity: avgSwipeVelocity > 0 ? avgSwipeVelocity : 0,
338
+ session_entropy: sessionEntropy >= 0 ? sessionEntropy : 0
339
+ };
340
+ }
341
+ /**
342
+ * Reset collected data
343
+ */
344
+ reset() {
345
+ this.keystrokes = [];
346
+ this.mouseMovements = [];
347
+ this.lastKeyDownTime.clear();
348
+ this.lastKeyUpTime = null;
349
+ this.typingDwellTimes = [];
350
+ this.typingFlightTimes = [];
351
+ this.swipeVelocities = [];
352
+ this.sessionEvents = [];
353
+ this.touchStartPositions.clear();
354
+ this.sessionStartTime = Date.now();
355
+ }
356
+ /**
357
+ * Handle keydown event
358
+ */
359
+ handleKeyDown(event) {
360
+ if (this.shouldIgnoreKey(event)) return;
361
+ const now = performance.now();
362
+ this.lastKeyDownTime.set(event.key, now);
363
+ const keystroke = {
364
+ key: event.key,
365
+ timestamp: now,
366
+ type: "keydown"
367
+ };
368
+ this.keystrokes.push(keystroke);
369
+ this.sessionEvents.push({ type: "keydown", timestamp: now });
370
+ }
371
+ /**
372
+ * Handle keyup event
373
+ */
374
+ handleKeyUp(event) {
375
+ if (this.shouldIgnoreKey(event)) return;
376
+ const now = performance.now();
377
+ const keyDownTime = this.lastKeyDownTime.get(event.key);
378
+ if (keyDownTime !== void 0) {
379
+ const dwellTime = now - keyDownTime;
380
+ this.typingDwellTimes.push(dwellTime);
381
+ this.lastKeyDownTime.delete(event.key);
382
+ }
383
+ if (this.lastKeyUpTime !== null) {
384
+ const flightTime = now - this.lastKeyUpTime;
385
+ this.typingFlightTimes.push(flightTime);
386
+ }
387
+ this.lastKeyUpTime = now;
388
+ const keystroke = {
389
+ key: event.key,
390
+ timestamp: now,
391
+ type: "keyup"
392
+ };
393
+ this.keystrokes.push(keystroke);
394
+ this.sessionEvents.push({ type: "keyup", timestamp: now });
395
+ }
396
+ /**
397
+ * Handle mouse move event
398
+ */
399
+ handleMouseMove(event) {
400
+ const mouseEvent = {
401
+ x: event.clientX,
402
+ y: event.clientY,
403
+ timestamp: performance.now(),
404
+ type: "move"
405
+ };
406
+ if (this.mouseMovements.length > 0) {
407
+ const lastEvent = this.mouseMovements[this.mouseMovements.length - 1];
408
+ const timeDelta = mouseEvent.timestamp - lastEvent.timestamp;
409
+ if (timeDelta > 0) {
410
+ const distance = Math.sqrt(
411
+ Math.pow(mouseEvent.x - lastEvent.x, 2) + Math.pow(mouseEvent.y - lastEvent.y, 2)
412
+ );
413
+ mouseEvent.velocity = distance / timeDelta;
414
+ }
415
+ }
416
+ this.mouseMovements.push(mouseEvent);
417
+ this.sessionEvents.push({ type: "mousemove", timestamp: mouseEvent.timestamp });
418
+ }
419
+ /**
420
+ * Handle touch start (for swipe detection)
421
+ */
422
+ handleTouchStart(event) {
423
+ const now = performance.now();
424
+ for (let i = 0; i < event.touches.length; i++) {
425
+ const touch = event.touches[i];
426
+ this.touchStartPositions.set(touch.identifier, {
427
+ x: touch.clientX,
428
+ y: touch.clientY,
429
+ timestamp: now
430
+ });
431
+ }
432
+ this.sessionEvents.push({
433
+ type: "touchstart",
434
+ timestamp: now
435
+ });
436
+ }
437
+ /**
438
+ * Handle touch end (calculate swipe velocity)
439
+ */
440
+ handleTouchEnd(event) {
441
+ const now = performance.now();
442
+ for (let i = 0; i < event.changedTouches.length; i++) {
443
+ const touch = event.changedTouches[i];
444
+ const startPos = this.touchStartPositions.get(touch.identifier);
445
+ if (startPos) {
446
+ const timeDelta = now - startPos.timestamp;
447
+ if (timeDelta > 0 && timeDelta < 1e3) {
448
+ const distance = Math.sqrt(
449
+ Math.pow(touch.clientX - startPos.x, 2) + Math.pow(touch.clientY - startPos.y, 2)
450
+ );
451
+ if (distance > 10) {
452
+ const velocity = distance / timeDelta;
453
+ this.swipeVelocities.push(velocity);
454
+ }
455
+ }
456
+ this.touchStartPositions.delete(touch.identifier);
457
+ }
458
+ }
459
+ this.sessionEvents.push({
460
+ type: "touchend",
461
+ timestamp: now
462
+ });
463
+ }
464
+ /**
465
+ * Calculate session entropy based on event diversity
466
+ */
467
+ calculateSessionEntropy() {
468
+ if (this.sessionEvents.length === 0) return 0;
469
+ const eventTypeCounts = {};
470
+ for (const event of this.sessionEvents) {
471
+ eventTypeCounts[event.type] = (eventTypeCounts[event.type] || 0) + 1;
472
+ }
473
+ const totalEvents = this.sessionEvents.length;
474
+ let entropy = 0;
475
+ for (const count of Object.values(eventTypeCounts)) {
476
+ const probability = count / totalEvents;
477
+ if (probability > 0) {
478
+ entropy -= probability * Math.log2(probability);
479
+ }
480
+ }
481
+ return entropy;
482
+ }
483
+ /**
484
+ * Check if key should be ignored
485
+ */
486
+ shouldIgnoreKey(event) {
487
+ const ignoredKeys = [
488
+ "Shift",
489
+ "Control",
490
+ "Alt",
491
+ "Meta",
492
+ "CapsLock",
493
+ "Tab",
494
+ "Escape",
495
+ "Enter",
496
+ "ArrowLeft",
497
+ "ArrowRight",
498
+ "ArrowUp",
499
+ "ArrowDown",
500
+ "Home",
501
+ "End",
502
+ "PageUp",
503
+ "PageDown",
504
+ "F1",
505
+ "F2",
506
+ "F3",
507
+ "F4",
508
+ "F5",
509
+ "F6",
510
+ "F7",
511
+ "F8",
512
+ "F9",
513
+ "F10",
514
+ "F11",
515
+ "F12"
516
+ ];
517
+ return ignoredKeys.includes(event.key);
518
+ }
519
+ };
520
+
521
+ // src/core/keverd-sdk.ts
522
+ var KeverdSDK = class {
523
+ constructor() {
524
+ this.config = null;
525
+ this.isInitialized = false;
526
+ this.sessionId = null;
527
+ this.deviceCollector = new KeverdDeviceCollector();
528
+ this.behavioralCollector = new KeverdBehavioralCollector();
529
+ }
530
+ /**
531
+ * Initialize the SDK with configuration
532
+ */
533
+ init(config) {
534
+ if (this.isInitialized) {
535
+ if (this.config?.debug) {
536
+ console.warn("[Keverd SDK] Already initialized");
537
+ }
538
+ return;
539
+ }
540
+ if (!config.apiKey) {
541
+ throw new Error("Keverd SDK: apiKey is required");
542
+ }
543
+ this.config = {
544
+ endpoint: config.endpoint || this.getDefaultEndpoint(),
545
+ debug: false,
546
+ ...config
547
+ };
548
+ this.behavioralCollector.start();
549
+ this.sessionId = this.generateSessionId();
550
+ this.isInitialized = true;
551
+ if (this.config.debug) {
552
+ console.log("[Keverd SDK] Initialized successfully", {
553
+ endpoint: this.config.endpoint
554
+ });
555
+ }
556
+ }
557
+ /**
558
+ * Get visitor data (fingerprint and risk assessment)
559
+ */
560
+ async getVisitorData(options) {
561
+ if (!this.isInitialized || !this.config) {
562
+ throw new Error("Keverd SDK not initialized. Call init() first.");
563
+ }
564
+ try {
565
+ const deviceInfo = this.deviceCollector.collect();
566
+ const behavioralData = this.behavioralCollector.getData();
567
+ const sessionInfo = {
568
+ sessionId: this.sessionId || void 0,
569
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
570
+ };
571
+ const request = {
572
+ userId: this.config.userId,
573
+ device: deviceInfo,
574
+ session: sessionInfo,
575
+ behavioral: behavioralData
576
+ };
577
+ const response = await this.sendFingerprintRequest(request, options);
578
+ return this.transformResponse(response);
579
+ } catch (error) {
580
+ const keverdError = {
581
+ message: error instanceof Error ? error.message : "Unknown error",
582
+ code: "FETCH_ERROR"
583
+ };
584
+ if (this.config.debug) {
585
+ console.error("[Keverd SDK] Error getting visitor data:", error);
586
+ }
587
+ throw keverdError;
588
+ }
589
+ }
590
+ /**
591
+ * Send fingerprint request to backend
592
+ */
593
+ async sendFingerprintRequest(request, options) {
594
+ if (!this.config) {
595
+ throw new Error("SDK not initialized");
596
+ }
597
+ const endpoint = this.config.endpoint || this.getDefaultEndpoint();
598
+ const url = `${endpoint}/fingerprint/score`;
599
+ const headers = {
600
+ "Content-Type": "application/json",
601
+ "X-SDK-Source": "react"
602
+ // Identify SDK source for backend analytics
603
+ };
604
+ const apiKey = this.config.apiKey;
605
+ if (apiKey) {
606
+ headers["x-keverd-key"] = apiKey;
607
+ headers["X-API-KEY"] = apiKey;
608
+ headers["Authorization"] = `Bearer ${apiKey}`;
609
+ }
610
+ if (options?.tag === "sandbox") {
611
+ headers["X-Sandbox"] = "true";
612
+ }
613
+ const response = await fetch(url, {
614
+ method: "POST",
615
+ headers,
616
+ body: JSON.stringify(request)
617
+ });
618
+ if (!response.ok) {
619
+ const errorText = await response.text().catch(() => "Unknown error");
620
+ const error = {
621
+ message: `HTTP ${response.status}: ${errorText}`,
622
+ code: `HTTP_${response.status}`,
623
+ statusCode: response.status
624
+ };
625
+ if (this.config.debug) {
626
+ console.error("[Keverd SDK] API error:", error);
627
+ }
628
+ throw error;
629
+ }
630
+ const data = await response.json();
631
+ return data;
632
+ }
633
+ /**
634
+ * Transform API response to visitor data format
635
+ */
636
+ transformResponse(response) {
637
+ return {
638
+ visitorId: response.requestId,
639
+ riskScore: response.risk_score,
640
+ score: response.score,
641
+ action: response.action,
642
+ reasons: response.reason || [],
643
+ sessionId: response.session_id,
644
+ requestId: response.requestId,
645
+ simSwapEngine: response.sim_swap_engine,
646
+ confidence: 1 - response.score
647
+ // Inverse of risk score as confidence
648
+ };
649
+ }
650
+ /**
651
+ * Get default endpoint
652
+ */
653
+ getDefaultEndpoint() {
654
+ return "https://app.keverd.com";
655
+ }
656
+ /**
657
+ * Generate a session ID
658
+ */
659
+ generateSessionId() {
660
+ return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
661
+ }
662
+ /**
663
+ * Destroy the SDK instance
664
+ */
665
+ destroy() {
666
+ const wasDebug = this.config?.debug;
667
+ this.behavioralCollector.stop();
668
+ this.isInitialized = false;
669
+ this.config = null;
670
+ this.sessionId = null;
671
+ if (wasDebug) {
672
+ console.log("[Keverd SDK] Destroyed");
673
+ }
674
+ }
675
+ /**
676
+ * Get current configuration
677
+ */
678
+ getConfig() {
679
+ return this.config;
680
+ }
681
+ /**
682
+ * Check if SDK is initialized
683
+ */
684
+ isReady() {
685
+ return this.isInitialized;
686
+ }
687
+ };
688
+ var KeverdContext = react.createContext(null);
689
+ function KeverdProvider({ loadOptions, children }) {
690
+ const sdkRef = react.useRef(new KeverdSDK());
691
+ const [isReady, setIsReady] = react.useState(false);
692
+ react.useEffect(() => {
693
+ const sdk = sdkRef.current;
694
+ if (!sdk.isReady()) {
695
+ sdk.init({
696
+ apiKey: loadOptions.apiKey,
697
+ endpoint: loadOptions.endpoint,
698
+ debug: loadOptions.debug || false
699
+ });
700
+ setIsReady(true);
701
+ }
702
+ return () => {
703
+ if (sdk.isReady()) {
704
+ sdk.destroy();
705
+ setIsReady(false);
706
+ }
707
+ };
708
+ }, []);
709
+ const contextValue = {
710
+ sdk: sdkRef.current,
711
+ isReady
712
+ };
713
+ return /* @__PURE__ */ jsxRuntime.jsx(KeverdContext.Provider, { value: contextValue, children });
714
+ }
715
+ function useKeverdContext() {
716
+ const context = react.useContext(KeverdContext);
717
+ if (!context) {
718
+ throw new Error("useKeverdContext must be used within a KeverdProvider");
719
+ }
720
+ return context;
721
+ }
722
+ function useKeverdVisitorData(options) {
723
+ const { sdk, isReady } = useKeverdContext();
724
+ const [isLoading, setIsLoading] = react.useState(false);
725
+ const [error, setError] = react.useState(null);
726
+ const [data, setData] = react.useState(null);
727
+ const getData = react.useCallback(
728
+ async (getDataOptions) => {
729
+ if (!isReady || !sdk) {
730
+ const notReadyError = {
731
+ message: "Keverd SDK is not ready",
732
+ code: "SDK_NOT_READY"
733
+ };
734
+ setError(notReadyError);
735
+ throw notReadyError;
736
+ }
737
+ setIsLoading(true);
738
+ setError(null);
739
+ try {
740
+ const visitorData = await sdk.getVisitorData(getDataOptions);
741
+ setData(visitorData);
742
+ setIsLoading(false);
743
+ return visitorData;
744
+ } catch (err) {
745
+ const keverdError = err instanceof Error ? {
746
+ message: err.message,
747
+ code: "UNKNOWN_ERROR"
748
+ } : {
749
+ message: "Unknown error occurred",
750
+ code: "UNKNOWN_ERROR"
751
+ };
752
+ setError(keverdError);
753
+ setIsLoading(false);
754
+ throw keverdError;
755
+ }
756
+ },
757
+ [sdk, isReady]
758
+ );
759
+ react.useEffect(() => {
760
+ if (options?.immediate && isReady && sdk && !data && !isLoading) {
761
+ getData({
762
+ extendedResult: options.extendedResult,
763
+ ignoreCache: options.ignoreCache
764
+ }).catch(() => {
765
+ });
766
+ }
767
+ }, [options?.immediate, isReady, sdk, data, isLoading, getData, options?.extendedResult, options?.ignoreCache]);
768
+ return {
769
+ isLoading,
770
+ error,
771
+ data,
772
+ getData
773
+ };
774
+ }
775
+
776
+ exports.KeverdBehavioralCollector = KeverdBehavioralCollector;
777
+ exports.KeverdDeviceCollector = KeverdDeviceCollector;
778
+ exports.KeverdProvider = KeverdProvider;
779
+ exports.KeverdSDK = KeverdSDK;
780
+ exports.useKeverdContext = useKeverdContext;
781
+ exports.useKeverdVisitorData = useKeverdVisitorData;
782
+ //# sourceMappingURL=index.js.map
783
+ //# sourceMappingURL=index.js.map