cgs-compliance-sdk 2.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.
@@ -0,0 +1,777 @@
1
+ import { useState, useCallback, useEffect, useRef } from 'react';
2
+
3
+ // src/geolocation/client.ts
4
+ var GeolocationClient = class {
5
+ constructor(config) {
6
+ this.config = {
7
+ baseURL: config.baseURL,
8
+ tenantId: config.tenantId,
9
+ apiKey: config.apiKey || "",
10
+ headers: config.headers || {},
11
+ timeout: config.timeout || 1e4,
12
+ debug: config.debug || false
13
+ };
14
+ }
15
+ // ============================================================================
16
+ // Private Helper Methods
17
+ // ============================================================================
18
+ async request(endpoint, options = {}) {
19
+ const url = `${this.config.baseURL}${endpoint}`;
20
+ const headers = {
21
+ "Content-Type": "application/json",
22
+ "X-Tenant-ID": this.config.tenantId,
23
+ ...this.config.headers,
24
+ ...options.headers || {}
25
+ };
26
+ if (this.config.apiKey) {
27
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
28
+ }
29
+ const controller = new AbortController();
30
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
31
+ try {
32
+ if (this.config.debug) {
33
+ console.log(`[GeolocationSDK] ${options.method || "GET"} ${url}`, {
34
+ headers,
35
+ body: options.body
36
+ });
37
+ }
38
+ const response = await fetch(url, {
39
+ ...options,
40
+ headers,
41
+ signal: controller.signal
42
+ });
43
+ clearTimeout(timeoutId);
44
+ const data = await response.json();
45
+ if (!response.ok) {
46
+ throw new Error(data.error || data.message || `HTTP ${response.status}`);
47
+ }
48
+ if (this.config.debug) {
49
+ console.log(`[GeolocationSDK] Response:`, data);
50
+ }
51
+ return data;
52
+ } catch (error) {
53
+ clearTimeout(timeoutId);
54
+ if (error instanceof Error) {
55
+ if (error.name === "AbortError") {
56
+ throw new Error(`Request timeout after ${this.config.timeout}ms`);
57
+ }
58
+ throw error;
59
+ }
60
+ throw new Error("Unknown error occurred");
61
+ }
62
+ }
63
+ buildQueryString(params) {
64
+ const query = new URLSearchParams();
65
+ Object.entries(params).forEach(([key, value]) => {
66
+ if (value !== void 0 && value !== null) {
67
+ query.append(key, String(value));
68
+ }
69
+ });
70
+ const queryString = query.toString();
71
+ return queryString ? `?${queryString}` : "";
72
+ }
73
+ // ============================================================================
74
+ // IP Verification & Compliance
75
+ // ============================================================================
76
+ /**
77
+ * Verify an IP address and check compliance
78
+ *
79
+ * @param request - Verification request with IP, user ID, event type, and optional device fingerprint
80
+ * @returns Location verification result with risk assessment
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const result = await client.verifyIP({
85
+ * ip_address: "8.8.8.8",
86
+ * user_id: "user_123",
87
+ * event_type: "login",
88
+ * device_fingerprint: {
89
+ * device_id: "device_abc",
90
+ * user_agent: navigator.userAgent,
91
+ * platform: "web"
92
+ * }
93
+ * });
94
+ *
95
+ * if (result.is_blocked) {
96
+ * console.log("Access blocked:", result.risk_reasons);
97
+ * }
98
+ * ```
99
+ */
100
+ async verifyIP(request) {
101
+ return this.request("/api/v1/geo/verify", {
102
+ method: "POST",
103
+ body: JSON.stringify(request)
104
+ });
105
+ }
106
+ /**
107
+ * Check compliance for a specific country
108
+ *
109
+ * @param countryISO - ISO 3166-1 alpha-2 country code (e.g., "US", "GB")
110
+ * @returns Jurisdiction configuration and compliance status
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const compliance = await client.checkCompliance("KP"); // North Korea
115
+ * if (!compliance.is_compliant) {
116
+ * console.log("Country is not allowed:", compliance.jurisdiction?.status);
117
+ * }
118
+ * ```
119
+ */
120
+ async checkCompliance(countryISO) {
121
+ return this.request(
122
+ `/api/v1/geo/compliance?country_iso=${countryISO}`
123
+ );
124
+ }
125
+ /**
126
+ * Get location history for a user
127
+ *
128
+ * @param userId - User ID to retrieve history for
129
+ * @param limit - Maximum number of records to return (default: 100)
130
+ * @returns Array of geolocation records
131
+ */
132
+ async getUserLocationHistory(userId, limit = 100) {
133
+ return this.request(
134
+ `/api/v1/geo/history?user_id=${userId}&limit=${limit}`
135
+ );
136
+ }
137
+ // ============================================================================
138
+ // Alert Management
139
+ // ============================================================================
140
+ /**
141
+ * Get a specific alert by ID
142
+ *
143
+ * @param alertId - Alert ID
144
+ * @returns Alert details
145
+ */
146
+ async getAlert(alertId) {
147
+ return this.request(`/api/v1/alerts/${alertId}`);
148
+ }
149
+ /**
150
+ * List alerts with filters and pagination
151
+ *
152
+ * @param filters - Optional filters (status, severity, type, user, dates)
153
+ * @param pagination - Optional pagination (page, page_size)
154
+ * @returns Paginated list of alerts
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const alerts = await client.listAlerts(
159
+ * { status: "active", severity: "critical" },
160
+ * { page: 1, page_size: 20 }
161
+ * );
162
+ * console.log(`Found ${alerts.total} critical alerts`);
163
+ * ```
164
+ */
165
+ async listAlerts(filters, pagination) {
166
+ const params = {
167
+ ...filters,
168
+ ...pagination
169
+ };
170
+ return this.request(`/api/v1/alerts${this.buildQueryString(params)}`);
171
+ }
172
+ /**
173
+ * Update alert status
174
+ *
175
+ * @param alertId - Alert ID
176
+ * @param status - New status
177
+ */
178
+ async updateAlertStatus(alertId, status) {
179
+ await this.request(`/api/v1/alerts/${alertId}/status`, {
180
+ method: "PUT",
181
+ body: JSON.stringify({ status })
182
+ });
183
+ }
184
+ /**
185
+ * Assign an alert to an analyst
186
+ *
187
+ * @param alertId - Alert ID
188
+ * @param assignedTo - User ID to assign to
189
+ */
190
+ async assignAlert(alertId, assignedTo) {
191
+ await this.request(`/api/v1/alerts/${alertId}/assign`, {
192
+ method: "POST",
193
+ body: JSON.stringify({ assigned_to: assignedTo })
194
+ });
195
+ }
196
+ /**
197
+ * Resolve an alert
198
+ *
199
+ * @param alertId - Alert ID
200
+ * @param resolution - Resolution type
201
+ * @param notes - Optional notes
202
+ */
203
+ async resolveAlert(alertId, resolution, notes) {
204
+ await this.request(`/api/v1/alerts/${alertId}/resolve`, {
205
+ method: "POST",
206
+ body: JSON.stringify({ resolution, notes })
207
+ });
208
+ }
209
+ /**
210
+ * Dismiss an alert as false positive
211
+ *
212
+ * @param alertId - Alert ID
213
+ * @param notes - Optional notes explaining why it's a false positive
214
+ */
215
+ async dismissAlert(alertId, notes) {
216
+ await this.request(`/api/v1/alerts/${alertId}/dismiss`, {
217
+ method: "POST",
218
+ body: JSON.stringify({ notes })
219
+ });
220
+ }
221
+ /**
222
+ * Escalate an alert to higher severity or different assignee
223
+ *
224
+ * @param alertId - Alert ID
225
+ * @param assignedTo - New assignee user ID
226
+ * @param severity - New severity level
227
+ * @param notes - Escalation notes
228
+ */
229
+ async escalateAlert(alertId, assignedTo, severity, notes) {
230
+ await this.request(`/api/v1/alerts/${alertId}/escalate`, {
231
+ method: "POST",
232
+ body: JSON.stringify({ assigned_to: assignedTo, severity, notes })
233
+ });
234
+ }
235
+ /**
236
+ * Block an alert (mark as blocked)
237
+ *
238
+ * @param alertId - Alert ID
239
+ * @param notes - Optional notes
240
+ */
241
+ async blockAlert(alertId, notes) {
242
+ await this.request(`/api/v1/alerts/${alertId}/block`, {
243
+ method: "POST",
244
+ body: JSON.stringify({ notes })
245
+ });
246
+ }
247
+ /**
248
+ * Get dashboard metrics
249
+ *
250
+ * @param timeRangeHours - Time range in hours (default: 24)
251
+ * @returns Dashboard metrics and statistics
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * const metrics = await client.getDashboardMetrics(168); // Last 7 days
256
+ * console.log(`${metrics.critical_alerts} critical alerts in last week`);
257
+ * ```
258
+ */
259
+ async getDashboardMetrics(timeRangeHours = 24) {
260
+ return this.request(
261
+ `/api/v1/alerts/metrics?time_range_hours=${timeRangeHours}`
262
+ );
263
+ }
264
+ // ============================================================================
265
+ // Jurisdiction Configuration
266
+ // ============================================================================
267
+ /**
268
+ * List all jurisdiction configurations
269
+ *
270
+ * @returns Array of jurisdiction configurations
271
+ */
272
+ async listJurisdictions() {
273
+ return this.request("/api/v1/jurisdictions");
274
+ }
275
+ /**
276
+ * Get a jurisdiction configuration by ID
277
+ *
278
+ * @param jurisdictionId - Jurisdiction ID
279
+ * @returns Jurisdiction configuration
280
+ */
281
+ async getJurisdiction(jurisdictionId) {
282
+ return this.request(`/api/v1/jurisdictions/${jurisdictionId}`);
283
+ }
284
+ /**
285
+ * Create a new jurisdiction configuration
286
+ *
287
+ * @param request - Jurisdiction configuration data
288
+ * @returns Created jurisdiction
289
+ *
290
+ * @example
291
+ * ```typescript
292
+ * const jurisdiction = await client.createJurisdiction({
293
+ * country_iso: "US",
294
+ * country_name: "United States",
295
+ * status: "allowed",
296
+ * risk_level: "low",
297
+ * allow_login: true,
298
+ * allow_registration: true,
299
+ * allow_transactions: true,
300
+ * require_kyc: false,
301
+ * require_enhanced_verification: false
302
+ * });
303
+ * ```
304
+ */
305
+ async createJurisdiction(request) {
306
+ return this.request("/api/v1/jurisdictions", {
307
+ method: "POST",
308
+ body: JSON.stringify(request)
309
+ });
310
+ }
311
+ /**
312
+ * Update a jurisdiction configuration
313
+ *
314
+ * @param jurisdictionId - Jurisdiction ID
315
+ * @param request - Updated fields
316
+ * @returns Updated jurisdiction
317
+ */
318
+ async updateJurisdiction(jurisdictionId, request) {
319
+ return this.request(`/api/v1/jurisdictions/${jurisdictionId}`, {
320
+ method: "PUT",
321
+ body: JSON.stringify(request)
322
+ });
323
+ }
324
+ /**
325
+ * Delete a jurisdiction configuration
326
+ *
327
+ * @param jurisdictionId - Jurisdiction ID
328
+ */
329
+ async deleteJurisdiction(jurisdictionId) {
330
+ await this.request(`/api/v1/jurisdictions/${jurisdictionId}`, {
331
+ method: "DELETE"
332
+ });
333
+ }
334
+ // ============================================================================
335
+ // Geofence Rules
336
+ // ============================================================================
337
+ /**
338
+ * List all geofence rules
339
+ *
340
+ * @returns Array of geofence rules
341
+ */
342
+ async listGeofenceRules() {
343
+ return this.request("/api/v1/geofence-rules");
344
+ }
345
+ /**
346
+ * Get a geofence rule by ID
347
+ *
348
+ * @param ruleId - Geofence rule ID
349
+ * @returns Geofence rule
350
+ */
351
+ async getGeofenceRule(ruleId) {
352
+ return this.request(`/api/v1/geofence-rules/${ruleId}`);
353
+ }
354
+ /**
355
+ * Create a new geofence rule
356
+ *
357
+ * @param request - Geofence rule data
358
+ * @returns Created geofence rule
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * const rule = await client.createGeofenceRule({
363
+ * name: "Block High Risk Countries",
364
+ * rule_type: "block_list",
365
+ * action: "block",
366
+ * countries: ["KP", "IR", "SY"],
367
+ * event_types: ["login", "transaction"],
368
+ * priority: 100
369
+ * });
370
+ * ```
371
+ */
372
+ async createGeofenceRule(request) {
373
+ return this.request("/api/v1/geofence-rules", {
374
+ method: "POST",
375
+ body: JSON.stringify(request)
376
+ });
377
+ }
378
+ /**
379
+ * Update a geofence rule
380
+ *
381
+ * @param ruleId - Geofence rule ID
382
+ * @param request - Updated fields
383
+ * @returns Updated geofence rule
384
+ */
385
+ async updateGeofenceRule(ruleId, request) {
386
+ return this.request(`/api/v1/geofence-rules/${ruleId}`, {
387
+ method: "PUT",
388
+ body: JSON.stringify(request)
389
+ });
390
+ }
391
+ /**
392
+ * Delete a geofence rule
393
+ *
394
+ * @param ruleId - Geofence rule ID
395
+ */
396
+ async deleteGeofenceRule(ruleId) {
397
+ await this.request(`/api/v1/geofence-rules/${ruleId}`, {
398
+ method: "DELETE"
399
+ });
400
+ }
401
+ // ============================================================================
402
+ // Device Fingerprinting
403
+ // ============================================================================
404
+ /**
405
+ * Get all devices for a user
406
+ *
407
+ * @param userId - User ID
408
+ * @returns Array of device fingerprints
409
+ */
410
+ async getUserDevices(userId) {
411
+ return this.request(`/api/v1/devices/user/?user_id=${userId}`);
412
+ }
413
+ /**
414
+ * Get a specific device by device ID
415
+ *
416
+ * @param deviceId - Device ID
417
+ * @returns Device fingerprint
418
+ */
419
+ async getDevice(deviceId) {
420
+ return this.request(`/api/v1/devices/?device_id=${deviceId}`);
421
+ }
422
+ /**
423
+ * Update device trust status
424
+ *
425
+ * @param deviceId - Device ID
426
+ * @param action - "trust" or "untrust"
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * // Mark a device as trusted
431
+ * await client.updateDeviceTrust("device_abc", "trust");
432
+ *
433
+ * // Mark a device as untrusted
434
+ * await client.updateDeviceTrust("device_xyz", "untrust");
435
+ * ```
436
+ */
437
+ async updateDeviceTrust(deviceId, action) {
438
+ await this.request(`/api/v1/devices/?device_id=${deviceId}&action=${action}`, {
439
+ method: "POST"
440
+ });
441
+ }
442
+ // ============================================================================
443
+ // Utility Methods
444
+ // ============================================================================
445
+ /**
446
+ * Check service health
447
+ *
448
+ * @returns Health status object
449
+ */
450
+ async healthCheck() {
451
+ return this.request("/api/v1/health");
452
+ }
453
+ /**
454
+ * Update client configuration
455
+ *
456
+ * @param config - Partial configuration to update
457
+ */
458
+ updateConfig(config) {
459
+ this.config = {
460
+ ...this.config,
461
+ ...config,
462
+ headers: {
463
+ ...this.config.headers,
464
+ ...config.headers || {}
465
+ }
466
+ };
467
+ }
468
+ /**
469
+ * Get current configuration (readonly)
470
+ */
471
+ getConfig() {
472
+ return { ...this.config };
473
+ }
474
+ };
475
+ function useGeolocation(client, options = {}) {
476
+ const [verification, setVerification] = useState(null);
477
+ const [loading, setLoading] = useState(false);
478
+ const [error, setError] = useState(null);
479
+ const verifyIP = useCallback(
480
+ async (request) => {
481
+ setLoading(true);
482
+ setError(null);
483
+ try {
484
+ const result = await client.verifyIP(request);
485
+ setVerification(result);
486
+ return result;
487
+ } catch (err) {
488
+ const error2 = err instanceof Error ? err : new Error("Verification failed");
489
+ setError(error2);
490
+ throw error2;
491
+ } finally {
492
+ setLoading(false);
493
+ }
494
+ },
495
+ [client]
496
+ );
497
+ const checkCompliance = useCallback(
498
+ async (countryISO) => {
499
+ setLoading(true);
500
+ setError(null);
501
+ try {
502
+ const result = await client.checkCompliance(countryISO);
503
+ return result;
504
+ } catch (err) {
505
+ const error2 = err instanceof Error ? err : new Error("Compliance check failed");
506
+ setError(error2);
507
+ throw error2;
508
+ } finally {
509
+ setLoading(false);
510
+ }
511
+ },
512
+ [client]
513
+ );
514
+ const refresh = useCallback(async () => {
515
+ if (verification) {
516
+ await verifyIP({
517
+ ip_address: verification.ip_address,
518
+ user_id: "",
519
+ // Will need to be provided
520
+ event_type: options.eventType || "api_access"
521
+ });
522
+ }
523
+ }, [verification, verifyIP, options.eventType]);
524
+ useEffect(() => {
525
+ if (options.autoVerify) {
526
+ const detectAndVerify = async () => {
527
+ try {
528
+ const ipResponse = await fetch("https://api.ipify.org?format=json");
529
+ const { ip } = await ipResponse.json();
530
+ let deviceFingerprint;
531
+ if (options.includeDeviceFingerprint && typeof window !== "undefined") {
532
+ deviceFingerprint = {
533
+ device_id: localStorage.getItem("device_id") || generateDeviceId(),
534
+ user_agent: navigator.userAgent,
535
+ platform: navigator.platform,
536
+ language: navigator.language,
537
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
538
+ screen_resolution: `${window.screen.width}x${window.screen.height}`
539
+ };
540
+ if (!localStorage.getItem("device_id")) {
541
+ localStorage.setItem("device_id", deviceFingerprint.device_id);
542
+ }
543
+ }
544
+ await verifyIP({
545
+ ip_address: ip,
546
+ user_id: "",
547
+ // Should be provided by auth context
548
+ event_type: options.eventType || "page_view",
549
+ device_fingerprint: deviceFingerprint
550
+ });
551
+ } catch (err) {
552
+ console.error("Auto-verification failed:", err);
553
+ }
554
+ };
555
+ detectAndVerify();
556
+ }
557
+ }, [options.autoVerify, options.includeDeviceFingerprint, options.eventType, verifyIP]);
558
+ return {
559
+ verification,
560
+ loading,
561
+ error,
562
+ verifyIP,
563
+ checkCompliance,
564
+ refresh
565
+ };
566
+ }
567
+ function useAlerts(client, options = {}) {
568
+ const [alerts, setAlerts] = useState([]);
569
+ const [total, setTotal] = useState(0);
570
+ const [loading, setLoading] = useState(false);
571
+ const [error, setError] = useState(null);
572
+ const pollIntervalRef = useRef();
573
+ const fetchAlerts = useCallback(
574
+ async (filters, pagination) => {
575
+ setLoading(true);
576
+ setError(null);
577
+ try {
578
+ const result = await client.listAlerts(
579
+ filters || options.filters,
580
+ pagination || options.pagination
581
+ );
582
+ setAlerts(result.alerts);
583
+ setTotal(result.total);
584
+ } catch (err) {
585
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch alerts");
586
+ setError(error2);
587
+ throw error2;
588
+ } finally {
589
+ setLoading(false);
590
+ }
591
+ },
592
+ [client, options.filters, options.pagination]
593
+ );
594
+ const updateStatus = useCallback(
595
+ async (alertId, status) => {
596
+ try {
597
+ await client.updateAlertStatus(alertId, status);
598
+ setAlerts(
599
+ (prev) => prev.map(
600
+ (alert) => alert.id === alertId ? { ...alert, status } : alert
601
+ )
602
+ );
603
+ } catch (err) {
604
+ const error2 = err instanceof Error ? err : new Error("Failed to update status");
605
+ setError(error2);
606
+ throw error2;
607
+ }
608
+ },
609
+ [client]
610
+ );
611
+ const assignAlert = useCallback(
612
+ async (alertId, assignedTo) => {
613
+ try {
614
+ await client.assignAlert(alertId, assignedTo);
615
+ setAlerts(
616
+ (prev) => prev.map(
617
+ (alert) => alert.id === alertId ? { ...alert, assigned_to: assignedTo, status: "under_review" } : alert
618
+ )
619
+ );
620
+ } catch (err) {
621
+ const error2 = err instanceof Error ? err : new Error("Failed to assign alert");
622
+ setError(error2);
623
+ throw error2;
624
+ }
625
+ },
626
+ [client]
627
+ );
628
+ const resolveAlert = useCallback(
629
+ async (alertId, resolution, notes) => {
630
+ try {
631
+ await client.resolveAlert(alertId, resolution, notes);
632
+ setAlerts(
633
+ (prev) => prev.map(
634
+ (alert) => alert.id === alertId ? { ...alert, status: "resolved", resolution, notes } : alert
635
+ )
636
+ );
637
+ } catch (err) {
638
+ const error2 = err instanceof Error ? err : new Error("Failed to resolve alert");
639
+ setError(error2);
640
+ throw error2;
641
+ }
642
+ },
643
+ [client]
644
+ );
645
+ const dismissAlert = useCallback(
646
+ async (alertId, notes) => {
647
+ try {
648
+ await client.dismissAlert(alertId, notes);
649
+ setAlerts(
650
+ (prev) => prev.map(
651
+ (alert) => alert.id === alertId ? { ...alert, status: "false_positive", resolution: "false_positive", notes } : alert
652
+ )
653
+ );
654
+ } catch (err) {
655
+ const error2 = err instanceof Error ? err : new Error("Failed to dismiss alert");
656
+ setError(error2);
657
+ throw error2;
658
+ }
659
+ },
660
+ [client]
661
+ );
662
+ const refresh = useCallback(async () => {
663
+ await fetchAlerts();
664
+ }, [fetchAlerts]);
665
+ useEffect(() => {
666
+ if (options.autoFetch) {
667
+ fetchAlerts();
668
+ }
669
+ }, [options.autoFetch, fetchAlerts]);
670
+ useEffect(() => {
671
+ if (options.pollInterval && options.pollInterval > 0) {
672
+ pollIntervalRef.current = setInterval(() => {
673
+ fetchAlerts();
674
+ }, options.pollInterval);
675
+ return () => {
676
+ if (pollIntervalRef.current) {
677
+ clearInterval(pollIntervalRef.current);
678
+ }
679
+ };
680
+ }
681
+ }, [options.pollInterval, fetchAlerts]);
682
+ return {
683
+ alerts,
684
+ total,
685
+ loading,
686
+ error,
687
+ fetchAlerts,
688
+ updateStatus,
689
+ assignAlert,
690
+ resolveAlert,
691
+ dismissAlert,
692
+ refresh
693
+ };
694
+ }
695
+ function useDashboardMetrics(client, timeRangeHours = 24, autoFetch = true) {
696
+ const [metrics, setMetrics] = useState(null);
697
+ const [loading, setLoading] = useState(false);
698
+ const [error, setError] = useState(null);
699
+ const fetchMetrics = useCallback(async () => {
700
+ setLoading(true);
701
+ setError(null);
702
+ try {
703
+ const result = await client.getDashboardMetrics(timeRangeHours);
704
+ setMetrics(result);
705
+ } catch (err) {
706
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch metrics");
707
+ setError(error2);
708
+ throw error2;
709
+ } finally {
710
+ setLoading(false);
711
+ }
712
+ }, [client, timeRangeHours]);
713
+ useEffect(() => {
714
+ if (autoFetch) {
715
+ fetchMetrics();
716
+ }
717
+ }, [autoFetch, fetchMetrics]);
718
+ return {
719
+ metrics,
720
+ loading,
721
+ error,
722
+ refresh: fetchMetrics
723
+ };
724
+ }
725
+ function generateDeviceId() {
726
+ return `device_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
727
+ }
728
+ function getBrowserInfo() {
729
+ if (typeof window === "undefined") return {};
730
+ const userAgent = navigator.userAgent;
731
+ let browser;
732
+ let browserVersion;
733
+ let os;
734
+ if (userAgent.indexOf("Firefox") > -1) {
735
+ browser = "Firefox";
736
+ browserVersion = userAgent.match(/Firefox\/([0-9.]+)/)?.[1];
737
+ } else if (userAgent.indexOf("Chrome") > -1) {
738
+ browser = "Chrome";
739
+ browserVersion = userAgent.match(/Chrome\/([0-9.]+)/)?.[1];
740
+ } else if (userAgent.indexOf("Safari") > -1) {
741
+ browser = "Safari";
742
+ browserVersion = userAgent.match(/Version\/([0-9.]+)/)?.[1];
743
+ } else if (userAgent.indexOf("Edge") > -1) {
744
+ browser = "Edge";
745
+ browserVersion = userAgent.match(/Edge\/([0-9.]+)/)?.[1];
746
+ }
747
+ if (userAgent.indexOf("Win") > -1) os = "Windows";
748
+ else if (userAgent.indexOf("Mac") > -1) os = "macOS";
749
+ else if (userAgent.indexOf("Linux") > -1) os = "Linux";
750
+ else if (userAgent.indexOf("Android") > -1) os = "Android";
751
+ else if (userAgent.indexOf("iOS") > -1) os = "iOS";
752
+ return {
753
+ browser,
754
+ browser_version: browserVersion,
755
+ os
756
+ };
757
+ }
758
+ function createDeviceFingerprint() {
759
+ const browserInfo = getBrowserInfo();
760
+ const deviceId = localStorage.getItem("device_id") || generateDeviceId();
761
+ if (!localStorage.getItem("device_id")) {
762
+ localStorage.setItem("device_id", deviceId);
763
+ }
764
+ return {
765
+ device_id: deviceId,
766
+ user_agent: navigator.userAgent,
767
+ platform: navigator.platform,
768
+ language: navigator.language,
769
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
770
+ screen_resolution: `${window.screen.width}x${window.screen.height}`,
771
+ ...browserInfo
772
+ };
773
+ }
774
+
775
+ export { GeolocationClient, createDeviceFingerprint, getBrowserInfo, useAlerts, useDashboardMetrics, useGeolocation };
776
+ //# sourceMappingURL=index.mjs.map
777
+ //# sourceMappingURL=index.mjs.map