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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1790 @@
1
+ import { useState, useCallback, useEffect, useRef } from 'react';
2
+
3
+ // src/core/errors.ts
4
+ var CGSError = class _CGSError extends Error {
5
+ constructor(message, code, statusCode, details) {
6
+ super(message);
7
+ this.code = code;
8
+ this.statusCode = statusCode;
9
+ this.details = details;
10
+ this.name = "CGSError";
11
+ Object.setPrototypeOf(this, _CGSError.prototype);
12
+ }
13
+ };
14
+ var NetworkError = class _NetworkError extends CGSError {
15
+ constructor(message, originalError) {
16
+ super(message, "NETWORK_ERROR", void 0, { originalError });
17
+ this.originalError = originalError;
18
+ this.name = "NetworkError";
19
+ Object.setPrototypeOf(this, _NetworkError.prototype);
20
+ }
21
+ };
22
+ var ValidationError = class _ValidationError extends CGSError {
23
+ constructor(message, fields) {
24
+ super(message, "VALIDATION_ERROR", 400, { fields });
25
+ this.name = "ValidationError";
26
+ Object.setPrototypeOf(this, _ValidationError.prototype);
27
+ }
28
+ };
29
+ var ServiceUnavailableError = class _ServiceUnavailableError extends CGSError {
30
+ constructor(service) {
31
+ super(`${service} is unavailable`, "SERVICE_UNAVAILABLE", 503, { service });
32
+ this.name = "ServiceUnavailableError";
33
+ Object.setPrototypeOf(this, _ServiceUnavailableError.prototype);
34
+ }
35
+ };
36
+ var ComplianceBlockedError = class _ComplianceBlockedError extends CGSError {
37
+ constructor(reasons) {
38
+ super("Access blocked due to compliance rules", "COMPLIANCE_BLOCKED", 403, { reasons });
39
+ this.name = "ComplianceBlockedError";
40
+ Object.setPrototypeOf(this, _ComplianceBlockedError.prototype);
41
+ }
42
+ };
43
+ var AuthenticationError = class _AuthenticationError extends CGSError {
44
+ constructor(message = "Authentication failed") {
45
+ super(message, "AUTHENTICATION_ERROR", 401);
46
+ this.name = "AuthenticationError";
47
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
48
+ }
49
+ };
50
+ var RateLimitError = class _RateLimitError extends CGSError {
51
+ constructor(retryAfter) {
52
+ super("Rate limit exceeded", "RATE_LIMIT_EXCEEDED", 429, { retryAfter });
53
+ this.retryAfter = retryAfter;
54
+ this.name = "RateLimitError";
55
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
56
+ }
57
+ };
58
+ var TimeoutError = class _TimeoutError extends CGSError {
59
+ constructor(timeout) {
60
+ super(`Request timeout after ${timeout}ms`, "TIMEOUT", 408, { timeout });
61
+ this.timeout = timeout;
62
+ this.name = "TimeoutError";
63
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
64
+ }
65
+ };
66
+ var ComplianceError = class _ComplianceError extends CGSError {
67
+ constructor(message, originalError, code = "COMPLIANCE_ERROR") {
68
+ super(message, code, void 0, { originalError });
69
+ this.originalError = originalError;
70
+ this.name = "ComplianceError";
71
+ Object.setPrototypeOf(this, _ComplianceError.prototype);
72
+ }
73
+ };
74
+
75
+ // src/core/client.ts
76
+ var BaseClient = class {
77
+ constructor(config) {
78
+ this.config = {
79
+ baseURL: config.baseURL,
80
+ tenantId: config.tenantId,
81
+ apiKey: config.apiKey || "",
82
+ headers: config.headers || {},
83
+ timeout: config.timeout || 1e4,
84
+ retries: config.retries || 3,
85
+ debug: config.debug || false
86
+ };
87
+ }
88
+ /**
89
+ * Make an HTTP request with timeout and error handling
90
+ */
91
+ async request(endpoint, options = {}, serviceURL) {
92
+ const url = `${serviceURL || this.config.baseURL}${endpoint}`;
93
+ const headers = {
94
+ "Content-Type": "application/json",
95
+ "X-Tenant-ID": this.config.tenantId,
96
+ ...this.config.headers,
97
+ ...options.headers || {}
98
+ };
99
+ if (this.config.apiKey) {
100
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
101
+ }
102
+ const controller = new AbortController();
103
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
104
+ try {
105
+ if (this.config.debug) {
106
+ console.log(`[CGS SDK] ${options.method || "GET"} ${url}`, {
107
+ headers,
108
+ body: options.body
109
+ });
110
+ }
111
+ const response = await fetch(url, {
112
+ ...options,
113
+ headers,
114
+ signal: controller.signal
115
+ });
116
+ clearTimeout(timeoutId);
117
+ const data = await response.json();
118
+ if (!response.ok) {
119
+ this.handleErrorResponse(response.status, data);
120
+ }
121
+ if (this.config.debug) {
122
+ console.log(`[CGS SDK] Response:`, data);
123
+ }
124
+ return data;
125
+ } catch (error) {
126
+ clearTimeout(timeoutId);
127
+ if (error instanceof Error) {
128
+ if (error.name === "AbortError") {
129
+ throw new TimeoutError(this.config.timeout);
130
+ }
131
+ if (error instanceof CGSError) {
132
+ throw error;
133
+ }
134
+ }
135
+ throw new NetworkError("Network request failed", error);
136
+ }
137
+ }
138
+ /**
139
+ * Make an HTTP request with retry logic
140
+ */
141
+ async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries) {
142
+ let lastError;
143
+ for (let attempt = 0; attempt <= retries; attempt++) {
144
+ try {
145
+ return await this.request(endpoint, options, serviceURL);
146
+ } catch (error) {
147
+ lastError = error instanceof Error ? error : new Error("Unknown error");
148
+ if (lastError instanceof CGSError && lastError.statusCode && lastError.statusCode >= 400 && lastError.statusCode < 500) {
149
+ throw lastError;
150
+ }
151
+ if (attempt === retries) {
152
+ break;
153
+ }
154
+ const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 1e3, 1e4);
155
+ await new Promise((resolve) => setTimeout(resolve, delay));
156
+ if (this.config.debug) {
157
+ console.log(`[CGS SDK] Retry attempt ${attempt + 1}/${retries} after ${delay.toFixed(0)}ms`);
158
+ }
159
+ }
160
+ }
161
+ throw new NetworkError(`Request failed after ${retries} retries`, lastError);
162
+ }
163
+ /**
164
+ * Handle error responses from API
165
+ */
166
+ handleErrorResponse(status, data) {
167
+ const message = data.error || data.message || `HTTP ${status}`;
168
+ switch (status) {
169
+ case 400:
170
+ throw new CGSError(message, "BAD_REQUEST", 400, data);
171
+ case 401:
172
+ throw new AuthenticationError(message);
173
+ case 403:
174
+ throw new CGSError(message, "FORBIDDEN", 403, data);
175
+ case 404:
176
+ throw new CGSError(message, "NOT_FOUND", 404, data);
177
+ case 429:
178
+ const retryAfter = data.retry_after || data.retryAfter;
179
+ throw new RateLimitError(retryAfter);
180
+ case 500:
181
+ case 502:
182
+ case 503:
183
+ case 504:
184
+ throw new ServiceUnavailableError(message);
185
+ default:
186
+ throw new CGSError(message, "UNKNOWN_ERROR", status, data);
187
+ }
188
+ }
189
+ /**
190
+ * Build query string from parameters
191
+ */
192
+ buildQueryString(params) {
193
+ const query = new URLSearchParams();
194
+ Object.entries(params).forEach(([key, value]) => {
195
+ if (value !== void 0 && value !== null) {
196
+ if (Array.isArray(value)) {
197
+ value.forEach((item) => query.append(key, String(item)));
198
+ } else {
199
+ query.append(key, String(value));
200
+ }
201
+ }
202
+ });
203
+ const queryString = query.toString();
204
+ return queryString ? `?${queryString}` : "";
205
+ }
206
+ /**
207
+ * Update client configuration
208
+ */
209
+ updateConfig(config) {
210
+ this.config = {
211
+ ...this.config,
212
+ ...config,
213
+ headers: {
214
+ ...this.config.headers,
215
+ ...config.headers || {}
216
+ }
217
+ };
218
+ }
219
+ /**
220
+ * Get current configuration (readonly)
221
+ */
222
+ getConfig() {
223
+ return { ...this.config };
224
+ }
225
+ /**
226
+ * Health check endpoint
227
+ */
228
+ async healthCheck() {
229
+ return this.request("/api/v1/health");
230
+ }
231
+ };
232
+
233
+ // src/geolocation/client.ts
234
+ var GeolocationClient = class {
235
+ constructor(config) {
236
+ this.config = {
237
+ baseURL: config.baseURL,
238
+ tenantId: config.tenantId,
239
+ apiKey: config.apiKey || "",
240
+ headers: config.headers || {},
241
+ timeout: config.timeout || 1e4,
242
+ debug: config.debug || false
243
+ };
244
+ }
245
+ // ============================================================================
246
+ // Private Helper Methods
247
+ // ============================================================================
248
+ async request(endpoint, options = {}) {
249
+ const url = `${this.config.baseURL}${endpoint}`;
250
+ const headers = {
251
+ "Content-Type": "application/json",
252
+ "X-Tenant-ID": this.config.tenantId,
253
+ ...this.config.headers,
254
+ ...options.headers || {}
255
+ };
256
+ if (this.config.apiKey) {
257
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
258
+ }
259
+ const controller = new AbortController();
260
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
261
+ try {
262
+ if (this.config.debug) {
263
+ console.log(`[GeolocationSDK] ${options.method || "GET"} ${url}`, {
264
+ headers,
265
+ body: options.body
266
+ });
267
+ }
268
+ const response = await fetch(url, {
269
+ ...options,
270
+ headers,
271
+ signal: controller.signal
272
+ });
273
+ clearTimeout(timeoutId);
274
+ const data = await response.json();
275
+ if (!response.ok) {
276
+ throw new Error(data.error || data.message || `HTTP ${response.status}`);
277
+ }
278
+ if (this.config.debug) {
279
+ console.log(`[GeolocationSDK] Response:`, data);
280
+ }
281
+ return data;
282
+ } catch (error) {
283
+ clearTimeout(timeoutId);
284
+ if (error instanceof Error) {
285
+ if (error.name === "AbortError") {
286
+ throw new Error(`Request timeout after ${this.config.timeout}ms`);
287
+ }
288
+ throw error;
289
+ }
290
+ throw new Error("Unknown error occurred");
291
+ }
292
+ }
293
+ buildQueryString(params) {
294
+ const query = new URLSearchParams();
295
+ Object.entries(params).forEach(([key, value]) => {
296
+ if (value !== void 0 && value !== null) {
297
+ query.append(key, String(value));
298
+ }
299
+ });
300
+ const queryString = query.toString();
301
+ return queryString ? `?${queryString}` : "";
302
+ }
303
+ // ============================================================================
304
+ // IP Verification & Compliance
305
+ // ============================================================================
306
+ /**
307
+ * Verify an IP address and check compliance
308
+ *
309
+ * @param request - Verification request with IP, user ID, event type, and optional device fingerprint
310
+ * @returns Location verification result with risk assessment
311
+ *
312
+ * @example
313
+ * ```typescript
314
+ * const result = await client.verifyIP({
315
+ * ip_address: "8.8.8.8",
316
+ * user_id: "user_123",
317
+ * event_type: "login",
318
+ * device_fingerprint: {
319
+ * device_id: "device_abc",
320
+ * user_agent: navigator.userAgent,
321
+ * platform: "web"
322
+ * }
323
+ * });
324
+ *
325
+ * if (result.is_blocked) {
326
+ * console.log("Access blocked:", result.risk_reasons);
327
+ * }
328
+ * ```
329
+ */
330
+ async verifyIP(request) {
331
+ return this.request("/api/v1/geo/verify", {
332
+ method: "POST",
333
+ body: JSON.stringify(request)
334
+ });
335
+ }
336
+ /**
337
+ * Check compliance for a specific country
338
+ *
339
+ * @param countryISO - ISO 3166-1 alpha-2 country code (e.g., "US", "GB")
340
+ * @returns Jurisdiction configuration and compliance status
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * const compliance = await client.checkCompliance("KP"); // North Korea
345
+ * if (!compliance.is_compliant) {
346
+ * console.log("Country is not allowed:", compliance.jurisdiction?.status);
347
+ * }
348
+ * ```
349
+ */
350
+ async checkCompliance(countryISO) {
351
+ return this.request(
352
+ `/api/v1/geo/compliance?country_iso=${countryISO}`
353
+ );
354
+ }
355
+ /**
356
+ * Get location history for a user
357
+ *
358
+ * @param userId - User ID to retrieve history for
359
+ * @param limit - Maximum number of records to return (default: 100)
360
+ * @returns Array of geolocation records
361
+ */
362
+ async getUserLocationHistory(userId, limit = 100) {
363
+ return this.request(
364
+ `/api/v1/geo/history?user_id=${userId}&limit=${limit}`
365
+ );
366
+ }
367
+ // ============================================================================
368
+ // Alert Management
369
+ // ============================================================================
370
+ /**
371
+ * Get a specific alert by ID
372
+ *
373
+ * @param alertId - Alert ID
374
+ * @returns Alert details
375
+ */
376
+ async getAlert(alertId) {
377
+ return this.request(`/api/v1/alerts/${alertId}`);
378
+ }
379
+ /**
380
+ * List alerts with filters and pagination
381
+ *
382
+ * @param filters - Optional filters (status, severity, type, user, dates)
383
+ * @param pagination - Optional pagination (page, page_size)
384
+ * @returns Paginated list of alerts
385
+ *
386
+ * @example
387
+ * ```typescript
388
+ * const alerts = await client.listAlerts(
389
+ * { status: "active", severity: "critical" },
390
+ * { page: 1, page_size: 20 }
391
+ * );
392
+ * console.log(`Found ${alerts.total} critical alerts`);
393
+ * ```
394
+ */
395
+ async listAlerts(filters, pagination) {
396
+ const params = {
397
+ ...filters,
398
+ ...pagination
399
+ };
400
+ return this.request(`/api/v1/alerts${this.buildQueryString(params)}`);
401
+ }
402
+ /**
403
+ * Update alert status
404
+ *
405
+ * @param alertId - Alert ID
406
+ * @param status - New status
407
+ */
408
+ async updateAlertStatus(alertId, status) {
409
+ await this.request(`/api/v1/alerts/${alertId}/status`, {
410
+ method: "PUT",
411
+ body: JSON.stringify({ status })
412
+ });
413
+ }
414
+ /**
415
+ * Assign an alert to an analyst
416
+ *
417
+ * @param alertId - Alert ID
418
+ * @param assignedTo - User ID to assign to
419
+ */
420
+ async assignAlert(alertId, assignedTo) {
421
+ await this.request(`/api/v1/alerts/${alertId}/assign`, {
422
+ method: "POST",
423
+ body: JSON.stringify({ assigned_to: assignedTo })
424
+ });
425
+ }
426
+ /**
427
+ * Resolve an alert
428
+ *
429
+ * @param alertId - Alert ID
430
+ * @param resolution - Resolution type
431
+ * @param notes - Optional notes
432
+ */
433
+ async resolveAlert(alertId, resolution, notes) {
434
+ await this.request(`/api/v1/alerts/${alertId}/resolve`, {
435
+ method: "POST",
436
+ body: JSON.stringify({ resolution, notes })
437
+ });
438
+ }
439
+ /**
440
+ * Dismiss an alert as false positive
441
+ *
442
+ * @param alertId - Alert ID
443
+ * @param notes - Optional notes explaining why it's a false positive
444
+ */
445
+ async dismissAlert(alertId, notes) {
446
+ await this.request(`/api/v1/alerts/${alertId}/dismiss`, {
447
+ method: "POST",
448
+ body: JSON.stringify({ notes })
449
+ });
450
+ }
451
+ /**
452
+ * Escalate an alert to higher severity or different assignee
453
+ *
454
+ * @param alertId - Alert ID
455
+ * @param assignedTo - New assignee user ID
456
+ * @param severity - New severity level
457
+ * @param notes - Escalation notes
458
+ */
459
+ async escalateAlert(alertId, assignedTo, severity, notes) {
460
+ await this.request(`/api/v1/alerts/${alertId}/escalate`, {
461
+ method: "POST",
462
+ body: JSON.stringify({ assigned_to: assignedTo, severity, notes })
463
+ });
464
+ }
465
+ /**
466
+ * Block an alert (mark as blocked)
467
+ *
468
+ * @param alertId - Alert ID
469
+ * @param notes - Optional notes
470
+ */
471
+ async blockAlert(alertId, notes) {
472
+ await this.request(`/api/v1/alerts/${alertId}/block`, {
473
+ method: "POST",
474
+ body: JSON.stringify({ notes })
475
+ });
476
+ }
477
+ /**
478
+ * Get dashboard metrics
479
+ *
480
+ * @param timeRangeHours - Time range in hours (default: 24)
481
+ * @returns Dashboard metrics and statistics
482
+ *
483
+ * @example
484
+ * ```typescript
485
+ * const metrics = await client.getDashboardMetrics(168); // Last 7 days
486
+ * console.log(`${metrics.critical_alerts} critical alerts in last week`);
487
+ * ```
488
+ */
489
+ async getDashboardMetrics(timeRangeHours = 24) {
490
+ return this.request(
491
+ `/api/v1/alerts/metrics?time_range_hours=${timeRangeHours}`
492
+ );
493
+ }
494
+ // ============================================================================
495
+ // Jurisdiction Configuration
496
+ // ============================================================================
497
+ /**
498
+ * List all jurisdiction configurations
499
+ *
500
+ * @returns Array of jurisdiction configurations
501
+ */
502
+ async listJurisdictions() {
503
+ return this.request("/api/v1/jurisdictions");
504
+ }
505
+ /**
506
+ * Get a jurisdiction configuration by ID
507
+ *
508
+ * @param jurisdictionId - Jurisdiction ID
509
+ * @returns Jurisdiction configuration
510
+ */
511
+ async getJurisdiction(jurisdictionId) {
512
+ return this.request(`/api/v1/jurisdictions/${jurisdictionId}`);
513
+ }
514
+ /**
515
+ * Create a new jurisdiction configuration
516
+ *
517
+ * @param request - Jurisdiction configuration data
518
+ * @returns Created jurisdiction
519
+ *
520
+ * @example
521
+ * ```typescript
522
+ * const jurisdiction = await client.createJurisdiction({
523
+ * country_iso: "US",
524
+ * country_name: "United States",
525
+ * status: "allowed",
526
+ * risk_level: "low",
527
+ * allow_login: true,
528
+ * allow_registration: true,
529
+ * allow_transactions: true,
530
+ * require_kyc: false,
531
+ * require_enhanced_verification: false
532
+ * });
533
+ * ```
534
+ */
535
+ async createJurisdiction(request) {
536
+ return this.request("/api/v1/jurisdictions", {
537
+ method: "POST",
538
+ body: JSON.stringify(request)
539
+ });
540
+ }
541
+ /**
542
+ * Update a jurisdiction configuration
543
+ *
544
+ * @param jurisdictionId - Jurisdiction ID
545
+ * @param request - Updated fields
546
+ * @returns Updated jurisdiction
547
+ */
548
+ async updateJurisdiction(jurisdictionId, request) {
549
+ return this.request(`/api/v1/jurisdictions/${jurisdictionId}`, {
550
+ method: "PUT",
551
+ body: JSON.stringify(request)
552
+ });
553
+ }
554
+ /**
555
+ * Delete a jurisdiction configuration
556
+ *
557
+ * @param jurisdictionId - Jurisdiction ID
558
+ */
559
+ async deleteJurisdiction(jurisdictionId) {
560
+ await this.request(`/api/v1/jurisdictions/${jurisdictionId}`, {
561
+ method: "DELETE"
562
+ });
563
+ }
564
+ // ============================================================================
565
+ // Geofence Rules
566
+ // ============================================================================
567
+ /**
568
+ * List all geofence rules
569
+ *
570
+ * @returns Array of geofence rules
571
+ */
572
+ async listGeofenceRules() {
573
+ return this.request("/api/v1/geofence-rules");
574
+ }
575
+ /**
576
+ * Get a geofence rule by ID
577
+ *
578
+ * @param ruleId - Geofence rule ID
579
+ * @returns Geofence rule
580
+ */
581
+ async getGeofenceRule(ruleId) {
582
+ return this.request(`/api/v1/geofence-rules/${ruleId}`);
583
+ }
584
+ /**
585
+ * Create a new geofence rule
586
+ *
587
+ * @param request - Geofence rule data
588
+ * @returns Created geofence rule
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * const rule = await client.createGeofenceRule({
593
+ * name: "Block High Risk Countries",
594
+ * rule_type: "block_list",
595
+ * action: "block",
596
+ * countries: ["KP", "IR", "SY"],
597
+ * event_types: ["login", "transaction"],
598
+ * priority: 100
599
+ * });
600
+ * ```
601
+ */
602
+ async createGeofenceRule(request) {
603
+ return this.request("/api/v1/geofence-rules", {
604
+ method: "POST",
605
+ body: JSON.stringify(request)
606
+ });
607
+ }
608
+ /**
609
+ * Update a geofence rule
610
+ *
611
+ * @param ruleId - Geofence rule ID
612
+ * @param request - Updated fields
613
+ * @returns Updated geofence rule
614
+ */
615
+ async updateGeofenceRule(ruleId, request) {
616
+ return this.request(`/api/v1/geofence-rules/${ruleId}`, {
617
+ method: "PUT",
618
+ body: JSON.stringify(request)
619
+ });
620
+ }
621
+ /**
622
+ * Delete a geofence rule
623
+ *
624
+ * @param ruleId - Geofence rule ID
625
+ */
626
+ async deleteGeofenceRule(ruleId) {
627
+ await this.request(`/api/v1/geofence-rules/${ruleId}`, {
628
+ method: "DELETE"
629
+ });
630
+ }
631
+ // ============================================================================
632
+ // Device Fingerprinting
633
+ // ============================================================================
634
+ /**
635
+ * Get all devices for a user
636
+ *
637
+ * @param userId - User ID
638
+ * @returns Array of device fingerprints
639
+ */
640
+ async getUserDevices(userId) {
641
+ return this.request(`/api/v1/devices/user/?user_id=${userId}`);
642
+ }
643
+ /**
644
+ * Get a specific device by device ID
645
+ *
646
+ * @param deviceId - Device ID
647
+ * @returns Device fingerprint
648
+ */
649
+ async getDevice(deviceId) {
650
+ return this.request(`/api/v1/devices/?device_id=${deviceId}`);
651
+ }
652
+ /**
653
+ * Update device trust status
654
+ *
655
+ * @param deviceId - Device ID
656
+ * @param action - "trust" or "untrust"
657
+ *
658
+ * @example
659
+ * ```typescript
660
+ * // Mark a device as trusted
661
+ * await client.updateDeviceTrust("device_abc", "trust");
662
+ *
663
+ * // Mark a device as untrusted
664
+ * await client.updateDeviceTrust("device_xyz", "untrust");
665
+ * ```
666
+ */
667
+ async updateDeviceTrust(deviceId, action) {
668
+ await this.request(`/api/v1/devices/?device_id=${deviceId}&action=${action}`, {
669
+ method: "POST"
670
+ });
671
+ }
672
+ // ============================================================================
673
+ // Utility Methods
674
+ // ============================================================================
675
+ /**
676
+ * Check service health
677
+ *
678
+ * @returns Health status object
679
+ */
680
+ async healthCheck() {
681
+ return this.request("/api/v1/health");
682
+ }
683
+ /**
684
+ * Update client configuration
685
+ *
686
+ * @param config - Partial configuration to update
687
+ */
688
+ updateConfig(config) {
689
+ this.config = {
690
+ ...this.config,
691
+ ...config,
692
+ headers: {
693
+ ...this.config.headers,
694
+ ...config.headers || {}
695
+ }
696
+ };
697
+ }
698
+ /**
699
+ * Get current configuration (readonly)
700
+ */
701
+ getConfig() {
702
+ return { ...this.config };
703
+ }
704
+ };
705
+ function useGeolocation(client, options = {}) {
706
+ const [verification, setVerification] = useState(null);
707
+ const [loading, setLoading] = useState(false);
708
+ const [error, setError] = useState(null);
709
+ const verifyIP = useCallback(
710
+ async (request) => {
711
+ setLoading(true);
712
+ setError(null);
713
+ try {
714
+ const result = await client.verifyIP(request);
715
+ setVerification(result);
716
+ return result;
717
+ } catch (err) {
718
+ const error2 = err instanceof Error ? err : new Error("Verification failed");
719
+ setError(error2);
720
+ throw error2;
721
+ } finally {
722
+ setLoading(false);
723
+ }
724
+ },
725
+ [client]
726
+ );
727
+ const checkCompliance = useCallback(
728
+ async (countryISO) => {
729
+ setLoading(true);
730
+ setError(null);
731
+ try {
732
+ const result = await client.checkCompliance(countryISO);
733
+ return result;
734
+ } catch (err) {
735
+ const error2 = err instanceof Error ? err : new Error("Compliance check failed");
736
+ setError(error2);
737
+ throw error2;
738
+ } finally {
739
+ setLoading(false);
740
+ }
741
+ },
742
+ [client]
743
+ );
744
+ const refresh = useCallback(async () => {
745
+ if (verification) {
746
+ await verifyIP({
747
+ ip_address: verification.ip_address,
748
+ user_id: "",
749
+ // Will need to be provided
750
+ event_type: options.eventType || "api_access"
751
+ });
752
+ }
753
+ }, [verification, verifyIP, options.eventType]);
754
+ useEffect(() => {
755
+ if (options.autoVerify) {
756
+ const detectAndVerify = async () => {
757
+ try {
758
+ const ipResponse = await fetch("https://api.ipify.org?format=json");
759
+ const { ip } = await ipResponse.json();
760
+ let deviceFingerprint;
761
+ if (options.includeDeviceFingerprint && typeof window !== "undefined") {
762
+ deviceFingerprint = {
763
+ device_id: localStorage.getItem("device_id") || generateDeviceId(),
764
+ user_agent: navigator.userAgent,
765
+ platform: navigator.platform,
766
+ language: navigator.language,
767
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
768
+ screen_resolution: `${window.screen.width}x${window.screen.height}`
769
+ };
770
+ if (!localStorage.getItem("device_id")) {
771
+ localStorage.setItem("device_id", deviceFingerprint.device_id);
772
+ }
773
+ }
774
+ await verifyIP({
775
+ ip_address: ip,
776
+ user_id: "",
777
+ // Should be provided by auth context
778
+ event_type: options.eventType || "page_view",
779
+ device_fingerprint: deviceFingerprint
780
+ });
781
+ } catch (err) {
782
+ console.error("Auto-verification failed:", err);
783
+ }
784
+ };
785
+ detectAndVerify();
786
+ }
787
+ }, [options.autoVerify, options.includeDeviceFingerprint, options.eventType, verifyIP]);
788
+ return {
789
+ verification,
790
+ loading,
791
+ error,
792
+ verifyIP,
793
+ checkCompliance,
794
+ refresh
795
+ };
796
+ }
797
+ function useAlerts(client, options = {}) {
798
+ const [alerts, setAlerts] = useState([]);
799
+ const [total, setTotal] = useState(0);
800
+ const [loading, setLoading] = useState(false);
801
+ const [error, setError] = useState(null);
802
+ const pollIntervalRef = useRef();
803
+ const fetchAlerts = useCallback(
804
+ async (filters, pagination) => {
805
+ setLoading(true);
806
+ setError(null);
807
+ try {
808
+ const result = await client.listAlerts(
809
+ filters || options.filters,
810
+ pagination || options.pagination
811
+ );
812
+ setAlerts(result.alerts);
813
+ setTotal(result.total);
814
+ } catch (err) {
815
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch alerts");
816
+ setError(error2);
817
+ throw error2;
818
+ } finally {
819
+ setLoading(false);
820
+ }
821
+ },
822
+ [client, options.filters, options.pagination]
823
+ );
824
+ const updateStatus = useCallback(
825
+ async (alertId, status) => {
826
+ try {
827
+ await client.updateAlertStatus(alertId, status);
828
+ setAlerts(
829
+ (prev) => prev.map(
830
+ (alert) => alert.id === alertId ? { ...alert, status } : alert
831
+ )
832
+ );
833
+ } catch (err) {
834
+ const error2 = err instanceof Error ? err : new Error("Failed to update status");
835
+ setError(error2);
836
+ throw error2;
837
+ }
838
+ },
839
+ [client]
840
+ );
841
+ const assignAlert = useCallback(
842
+ async (alertId, assignedTo) => {
843
+ try {
844
+ await client.assignAlert(alertId, assignedTo);
845
+ setAlerts(
846
+ (prev) => prev.map(
847
+ (alert) => alert.id === alertId ? { ...alert, assigned_to: assignedTo, status: "under_review" } : alert
848
+ )
849
+ );
850
+ } catch (err) {
851
+ const error2 = err instanceof Error ? err : new Error("Failed to assign alert");
852
+ setError(error2);
853
+ throw error2;
854
+ }
855
+ },
856
+ [client]
857
+ );
858
+ const resolveAlert = useCallback(
859
+ async (alertId, resolution, notes) => {
860
+ try {
861
+ await client.resolveAlert(alertId, resolution, notes);
862
+ setAlerts(
863
+ (prev) => prev.map(
864
+ (alert) => alert.id === alertId ? { ...alert, status: "resolved", resolution, notes } : alert
865
+ )
866
+ );
867
+ } catch (err) {
868
+ const error2 = err instanceof Error ? err : new Error("Failed to resolve alert");
869
+ setError(error2);
870
+ throw error2;
871
+ }
872
+ },
873
+ [client]
874
+ );
875
+ const dismissAlert = useCallback(
876
+ async (alertId, notes) => {
877
+ try {
878
+ await client.dismissAlert(alertId, notes);
879
+ setAlerts(
880
+ (prev) => prev.map(
881
+ (alert) => alert.id === alertId ? { ...alert, status: "false_positive", resolution: "false_positive", notes } : alert
882
+ )
883
+ );
884
+ } catch (err) {
885
+ const error2 = err instanceof Error ? err : new Error("Failed to dismiss alert");
886
+ setError(error2);
887
+ throw error2;
888
+ }
889
+ },
890
+ [client]
891
+ );
892
+ const refresh = useCallback(async () => {
893
+ await fetchAlerts();
894
+ }, [fetchAlerts]);
895
+ useEffect(() => {
896
+ if (options.autoFetch) {
897
+ fetchAlerts();
898
+ }
899
+ }, [options.autoFetch, fetchAlerts]);
900
+ useEffect(() => {
901
+ if (options.pollInterval && options.pollInterval > 0) {
902
+ pollIntervalRef.current = setInterval(() => {
903
+ fetchAlerts();
904
+ }, options.pollInterval);
905
+ return () => {
906
+ if (pollIntervalRef.current) {
907
+ clearInterval(pollIntervalRef.current);
908
+ }
909
+ };
910
+ }
911
+ }, [options.pollInterval, fetchAlerts]);
912
+ return {
913
+ alerts,
914
+ total,
915
+ loading,
916
+ error,
917
+ fetchAlerts,
918
+ updateStatus,
919
+ assignAlert,
920
+ resolveAlert,
921
+ dismissAlert,
922
+ refresh
923
+ };
924
+ }
925
+ function useDashboardMetrics(client, timeRangeHours = 24, autoFetch = true) {
926
+ const [metrics, setMetrics] = useState(null);
927
+ const [loading, setLoading] = useState(false);
928
+ const [error, setError] = useState(null);
929
+ const fetchMetrics = useCallback(async () => {
930
+ setLoading(true);
931
+ setError(null);
932
+ try {
933
+ const result = await client.getDashboardMetrics(timeRangeHours);
934
+ setMetrics(result);
935
+ } catch (err) {
936
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch metrics");
937
+ setError(error2);
938
+ throw error2;
939
+ } finally {
940
+ setLoading(false);
941
+ }
942
+ }, [client, timeRangeHours]);
943
+ useEffect(() => {
944
+ if (autoFetch) {
945
+ fetchMetrics();
946
+ }
947
+ }, [autoFetch, fetchMetrics]);
948
+ return {
949
+ metrics,
950
+ loading,
951
+ error,
952
+ refresh: fetchMetrics
953
+ };
954
+ }
955
+ function generateDeviceId() {
956
+ return `device_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
957
+ }
958
+ function getBrowserInfo() {
959
+ if (typeof window === "undefined") return {};
960
+ const userAgent = navigator.userAgent;
961
+ let browser;
962
+ let browserVersion;
963
+ let os;
964
+ if (userAgent.indexOf("Firefox") > -1) {
965
+ browser = "Firefox";
966
+ browserVersion = userAgent.match(/Firefox\/([0-9.]+)/)?.[1];
967
+ } else if (userAgent.indexOf("Chrome") > -1) {
968
+ browser = "Chrome";
969
+ browserVersion = userAgent.match(/Chrome\/([0-9.]+)/)?.[1];
970
+ } else if (userAgent.indexOf("Safari") > -1) {
971
+ browser = "Safari";
972
+ browserVersion = userAgent.match(/Version\/([0-9.]+)/)?.[1];
973
+ } else if (userAgent.indexOf("Edge") > -1) {
974
+ browser = "Edge";
975
+ browserVersion = userAgent.match(/Edge\/([0-9.]+)/)?.[1];
976
+ }
977
+ if (userAgent.indexOf("Win") > -1) os = "Windows";
978
+ else if (userAgent.indexOf("Mac") > -1) os = "macOS";
979
+ else if (userAgent.indexOf("Linux") > -1) os = "Linux";
980
+ else if (userAgent.indexOf("Android") > -1) os = "Android";
981
+ else if (userAgent.indexOf("iOS") > -1) os = "iOS";
982
+ return {
983
+ browser,
984
+ browser_version: browserVersion,
985
+ os
986
+ };
987
+ }
988
+ function createDeviceFingerprint() {
989
+ const browserInfo = getBrowserInfo();
990
+ const deviceId = localStorage.getItem("device_id") || generateDeviceId();
991
+ if (!localStorage.getItem("device_id")) {
992
+ localStorage.setItem("device_id", deviceId);
993
+ }
994
+ return {
995
+ device_id: deviceId,
996
+ user_agent: navigator.userAgent,
997
+ platform: navigator.platform,
998
+ language: navigator.language,
999
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1000
+ screen_resolution: `${window.screen.width}x${window.screen.height}`,
1001
+ ...browserInfo
1002
+ };
1003
+ }
1004
+
1005
+ // src/risk-profile/client.ts
1006
+ var RiskProfileClient = class extends BaseClient {
1007
+ constructor(config) {
1008
+ super(config);
1009
+ }
1010
+ // ============================================================================
1011
+ // Profile Management
1012
+ // ============================================================================
1013
+ /**
1014
+ * Create a new customer risk profile
1015
+ *
1016
+ * @param request - Profile creation data
1017
+ * @returns Created customer profile
1018
+ *
1019
+ * @example
1020
+ * ```typescript
1021
+ * const profile = await client.createProfile({
1022
+ * customer_id: 'CUST-12345',
1023
+ * entity_type: 'individual',
1024
+ * customer_status: 'active',
1025
+ * full_name: 'John Doe',
1026
+ * email_address: 'john@example.com',
1027
+ * date_of_birth: '1990-01-15',
1028
+ * country_of_residence: 'US'
1029
+ * });
1030
+ * ```
1031
+ */
1032
+ async createProfile(request) {
1033
+ return this.request("/api/v1/profiles", {
1034
+ method: "POST",
1035
+ body: JSON.stringify(request)
1036
+ });
1037
+ }
1038
+ /**
1039
+ * Get customer profile by customer ID
1040
+ *
1041
+ * Note: This searches for the profile using the customer_id field.
1042
+ * Returns the first matching profile for the tenant.
1043
+ *
1044
+ * @param customerId - Customer ID to search for
1045
+ * @returns Customer profile
1046
+ *
1047
+ * @example
1048
+ * ```typescript
1049
+ * const profile = await client.getProfile('CUST-12345');
1050
+ * console.log('Risk score:', profile.risk_score);
1051
+ * ```
1052
+ */
1053
+ async getProfile(customerId) {
1054
+ const response = await this.queryProfiles({
1055
+ search: customerId,
1056
+ page: 1,
1057
+ page_size: 1
1058
+ });
1059
+ if (response.data.length === 0) {
1060
+ throw new Error(`Profile not found for customer ID: ${customerId}`);
1061
+ }
1062
+ return response.data[0];
1063
+ }
1064
+ /**
1065
+ * Get profile by UUID
1066
+ *
1067
+ * @param profileId - Profile UUID
1068
+ * @returns Customer profile
1069
+ */
1070
+ async getProfileById(profileId) {
1071
+ const details = await this.getProfileDetails(profileId);
1072
+ return details.profile;
1073
+ }
1074
+ /**
1075
+ * Get detailed profile information including risk factors and history
1076
+ *
1077
+ * @param profileId - Profile UUID
1078
+ * @returns Detailed profile response with risk factors and history
1079
+ *
1080
+ * @example
1081
+ * ```typescript
1082
+ * const details = await client.getProfileDetails(profileId);
1083
+ * console.log('Risk factors:', details.risk_factors);
1084
+ * console.log('Risk history:', details.risk_history);
1085
+ * console.log('Alert counts:', {
1086
+ * watchlist: details.watchlist_alert_count,
1087
+ * fraud: details.fraud_alert_count,
1088
+ * geo: details.geolocation_alert_count
1089
+ * });
1090
+ * ```
1091
+ */
1092
+ async getProfileDetails(profileId) {
1093
+ return this.request(
1094
+ `/api/v1/risk-dashboard/profiles/${profileId}`
1095
+ );
1096
+ }
1097
+ /**
1098
+ * Update customer profile
1099
+ *
1100
+ * @param profileId - Profile UUID
1101
+ * @param updates - Fields to update
1102
+ * @returns Updated customer profile
1103
+ *
1104
+ * @example
1105
+ * ```typescript
1106
+ * const updated = await client.updateProfile(profileId, {
1107
+ * location: 'New York, USA',
1108
+ * location_compliance: 'compliant',
1109
+ * last_recorded_activity: new Date().toISOString()
1110
+ * });
1111
+ * ```
1112
+ */
1113
+ async updateProfile(profileId, updates) {
1114
+ return this.request(`/api/v1/profiles/${profileId}`, {
1115
+ method: "PUT",
1116
+ body: JSON.stringify(updates)
1117
+ });
1118
+ }
1119
+ /**
1120
+ * Manually recalculate risk score for a profile
1121
+ *
1122
+ * Triggers immediate risk score recalculation based on current risk factors.
1123
+ *
1124
+ * @param profileId - Profile UUID
1125
+ * @param reason - Optional reason for recalculation
1126
+ *
1127
+ * @example
1128
+ * ```typescript
1129
+ * await client.recalculateRiskScore(profileId, 'Manual review completed');
1130
+ * ```
1131
+ */
1132
+ async recalculateRiskScore(profileId, reason) {
1133
+ const body = reason ? JSON.stringify({ reason }) : void 0;
1134
+ await this.request(`/api/v1/profiles/recalculate/${profileId}`, {
1135
+ method: "POST",
1136
+ body
1137
+ });
1138
+ }
1139
+ // ============================================================================
1140
+ // Query & Search
1141
+ // ============================================================================
1142
+ /**
1143
+ * Query profiles with advanced filters
1144
+ *
1145
+ * @param filters - Filter criteria and pagination
1146
+ * @returns Paginated list of profiles
1147
+ *
1148
+ * @example
1149
+ * ```typescript
1150
+ * const results = await client.queryProfiles({
1151
+ * risk_category: ['high', 'critical'],
1152
+ * kyc_status: ['pending'],
1153
+ * is_pep: true,
1154
+ * page: 1,
1155
+ * page_size: 20,
1156
+ * sort_by: 'risk_score',
1157
+ * sort_order: 'desc'
1158
+ * });
1159
+ *
1160
+ * console.log(`Found ${results.total} high-risk profiles`);
1161
+ * results.data.forEach(profile => {
1162
+ * console.log(`${profile.full_name}: ${profile.risk_score}`);
1163
+ * });
1164
+ * ```
1165
+ */
1166
+ async queryProfiles(filters = {}) {
1167
+ const queryString = this.buildQueryString(filters);
1168
+ return this.request(
1169
+ `/api/v1/risk-dashboard/profiles${queryString}`
1170
+ );
1171
+ }
1172
+ /**
1173
+ * Search profiles by text (searches name, email, customer_id, account_number)
1174
+ *
1175
+ * @param searchText - Text to search for
1176
+ * @param limit - Maximum results to return (default: 10)
1177
+ * @returns Array of matching profiles
1178
+ */
1179
+ async searchProfiles(searchText, limit = 10) {
1180
+ const response = await this.queryProfiles({
1181
+ search: searchText,
1182
+ page: 1,
1183
+ page_size: limit
1184
+ });
1185
+ return response.data;
1186
+ }
1187
+ /**
1188
+ * Get all high-risk profiles (high + critical)
1189
+ *
1190
+ * @param limit - Maximum results to return (default: 50)
1191
+ * @returns Array of high-risk profiles
1192
+ */
1193
+ async getHighRiskProfiles(limit = 50) {
1194
+ const response = await this.queryProfiles({
1195
+ risk_category: ["high", "critical"],
1196
+ page: 1,
1197
+ page_size: limit,
1198
+ sort_by: "risk_score",
1199
+ sort_order: "desc"
1200
+ });
1201
+ return response.data;
1202
+ }
1203
+ /**
1204
+ * Get profiles requiring PEP review
1205
+ *
1206
+ * @param limit - Maximum results to return (default: 50)
1207
+ * @returns Array of PEP profiles
1208
+ */
1209
+ async getPEPProfiles(limit = 50) {
1210
+ const response = await this.queryProfiles({
1211
+ is_pep: true,
1212
+ page: 1,
1213
+ page_size: limit
1214
+ });
1215
+ return response.data;
1216
+ }
1217
+ /**
1218
+ * Get profiles with sanctions matches
1219
+ *
1220
+ * @param limit - Maximum results to return (default: 50)
1221
+ * @returns Array of sanctioned profiles
1222
+ */
1223
+ async getSanctionedProfiles(limit = 50) {
1224
+ const response = await this.queryProfiles({
1225
+ has_sanctions: true,
1226
+ page: 1,
1227
+ page_size: limit
1228
+ });
1229
+ return response.data;
1230
+ }
1231
+ // ============================================================================
1232
+ // Dashboard & Metrics
1233
+ // ============================================================================
1234
+ /**
1235
+ * Get risk dashboard metrics and statistics
1236
+ *
1237
+ * @param startDate - Optional start date for metrics (ISO format)
1238
+ * @param endDate - Optional end date for metrics (ISO format)
1239
+ * @returns Dashboard metrics
1240
+ *
1241
+ * @example
1242
+ * ```typescript
1243
+ * const metrics = await client.getDashboardMetrics();
1244
+ * console.log('Total risky profiles:', metrics.total_risky_profiles);
1245
+ * console.log('Critical profiles:', metrics.total_critical_profiles);
1246
+ * console.log('Average risk score:', metrics.average_risk_score);
1247
+ *
1248
+ * metrics.risk_distribution.forEach(item => {
1249
+ * console.log(`${item.category}: ${item.count} (${item.percentage}%)`);
1250
+ * });
1251
+ * ```
1252
+ */
1253
+ async getDashboardMetrics(startDate, endDate) {
1254
+ const params = {};
1255
+ if (startDate) params.start_date = startDate;
1256
+ if (endDate) params.end_date = endDate;
1257
+ const queryString = this.buildQueryString(params);
1258
+ return this.request(
1259
+ `/api/v1/risk-dashboard/metrics${queryString}`
1260
+ );
1261
+ }
1262
+ // ============================================================================
1263
+ // Bulk Operations
1264
+ // ============================================================================
1265
+ /**
1266
+ * Get or create profile (idempotent operation)
1267
+ *
1268
+ * Attempts to get existing profile by customer_id, creates if not found.
1269
+ *
1270
+ * @param customerId - Customer ID
1271
+ * @param createRequest - Profile data to use if creating new profile
1272
+ * @returns Existing or newly created profile
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * const profile = await client.getOrCreateProfile('CUST-123', {
1277
+ * customer_id: 'CUST-123',
1278
+ * entity_type: 'individual',
1279
+ * full_name: 'John Doe',
1280
+ * email_address: 'john@example.com'
1281
+ * });
1282
+ * ```
1283
+ */
1284
+ async getOrCreateProfile(customerId, createRequest) {
1285
+ try {
1286
+ return await this.getProfile(customerId);
1287
+ } catch (error) {
1288
+ return await this.createProfile(createRequest);
1289
+ }
1290
+ }
1291
+ /**
1292
+ * Batch get profiles by customer IDs
1293
+ *
1294
+ * @param customerIds - Array of customer IDs
1295
+ * @returns Array of profiles (may be less than input if some not found)
1296
+ */
1297
+ async batchGetProfiles(customerIds) {
1298
+ const profiles = [];
1299
+ for (const customerId of customerIds) {
1300
+ try {
1301
+ const profile = await this.getProfile(customerId);
1302
+ profiles.push(profile);
1303
+ } catch (error) {
1304
+ if (this.config.debug) {
1305
+ console.warn(`[RiskProfileClient] Profile not found for: ${customerId}`);
1306
+ }
1307
+ }
1308
+ }
1309
+ return profiles;
1310
+ }
1311
+ // ============================================================================
1312
+ // Configuration
1313
+ // ============================================================================
1314
+ /**
1315
+ * Get risk configuration for tenant
1316
+ *
1317
+ * Returns the risk scoring weights and thresholds configured for the tenant.
1318
+ *
1319
+ * @returns Risk configuration
1320
+ */
1321
+ async getRiskConfiguration() {
1322
+ return this.request("/api/v1/risk-config");
1323
+ }
1324
+ /**
1325
+ * Update risk configuration for tenant
1326
+ *
1327
+ * Updates risk scoring weights and/or thresholds.
1328
+ *
1329
+ * @param config - Configuration updates
1330
+ * @returns Updated risk configuration
1331
+ */
1332
+ async updateRiskConfiguration(config) {
1333
+ return this.request("/api/v1/risk-config", {
1334
+ method: "PUT",
1335
+ body: JSON.stringify(config)
1336
+ });
1337
+ }
1338
+ };
1339
+
1340
+ // src/compliance/client.ts
1341
+ var ComplianceClient = class {
1342
+ constructor(config) {
1343
+ this.config = {
1344
+ apiGatewayURL: config.apiGatewayURL,
1345
+ tenantId: config.tenantId,
1346
+ apiKey: config.apiKey || "",
1347
+ headers: config.headers || {},
1348
+ timeout: config.timeout || 1e4,
1349
+ retries: config.retries || 3,
1350
+ debug: config.debug || false,
1351
+ autoCreateProfiles: config.autoCreateProfiles !== false,
1352
+ // default true
1353
+ syncMode: config.syncMode || "sync"
1354
+ };
1355
+ this.geoClient = new GeolocationClient({
1356
+ baseURL: this.config.apiGatewayURL,
1357
+ tenantId: this.config.tenantId,
1358
+ apiKey: this.config.apiKey,
1359
+ headers: this.config.headers,
1360
+ timeout: this.config.timeout,
1361
+ retries: this.config.retries,
1362
+ debug: this.config.debug
1363
+ });
1364
+ this.riskClient = new RiskProfileClient({
1365
+ baseURL: this.config.apiGatewayURL,
1366
+ tenantId: this.config.tenantId,
1367
+ apiKey: this.config.apiKey,
1368
+ headers: this.config.headers,
1369
+ timeout: this.config.timeout,
1370
+ retries: this.config.retries,
1371
+ debug: this.config.debug
1372
+ });
1373
+ this.currencyRates = DEFAULT_CURRENCY_RATES;
1374
+ }
1375
+ // ============================================================================
1376
+ // Main Verification Methods
1377
+ // ============================================================================
1378
+ /**
1379
+ * Verify customer registration with automatic profile creation
1380
+ *
1381
+ * This is the primary integration point for new customer sign-ups.
1382
+ * Combines geolocation verification with customer risk profile creation.
1383
+ *
1384
+ * @param request - Registration verification request
1385
+ * @returns Verification response with profile and compliance status
1386
+ *
1387
+ * @example
1388
+ * ```typescript
1389
+ * const result = await sdk.verifyAtRegistration({
1390
+ * customerId: 'CUST-12345',
1391
+ * fullName: 'John Doe',
1392
+ * emailAddress: 'john@example.com',
1393
+ * ipAddress: req.ip,
1394
+ * deviceFingerprint: getDeviceFingerprint()
1395
+ * });
1396
+ *
1397
+ * if (result.allowed) {
1398
+ * // Create account
1399
+ * console.log('Profile created:', result.profile);
1400
+ * if (result.requiresKYC) {
1401
+ * // Redirect to KYC flow
1402
+ * }
1403
+ * } else {
1404
+ * // Block registration
1405
+ * console.log('Blocked:', result.blockReasons);
1406
+ * }
1407
+ * ```
1408
+ */
1409
+ async verifyAtRegistration(request) {
1410
+ const startTime = Date.now();
1411
+ try {
1412
+ if (this.config.debug) {
1413
+ console.log("[ComplianceSDK] Starting registration verification for:", request.customerId);
1414
+ }
1415
+ const geoVerification = await this.geoClient.verifyIP({
1416
+ ip_address: request.ipAddress,
1417
+ user_id: request.customerId,
1418
+ event_type: "registration",
1419
+ device_fingerprint: request.deviceFingerprint
1420
+ });
1421
+ const profile = await this.riskClient.createProfile({
1422
+ customer_id: request.customerId,
1423
+ entity_type: request.entityType || "individual",
1424
+ customer_status: "active",
1425
+ full_name: request.fullName,
1426
+ email_address: request.emailAddress,
1427
+ primary_phone_number: request.phoneNumber,
1428
+ date_of_birth: request.dateOfBirth,
1429
+ residential_address: request.address,
1430
+ country_of_residence: geoVerification.location.country_iso,
1431
+ // Geolocation data
1432
+ location: `${geoVerification.location.city}, ${geoVerification.location.country}`,
1433
+ location_compliance: geoVerification.is_compliant ? "compliant" : "non-compliant",
1434
+ // Initial KYC status
1435
+ kyc_status: "pending"
1436
+ });
1437
+ const isAllowed = geoVerification.is_compliant && !geoVerification.is_blocked;
1438
+ const requiresKYC = geoVerification.jurisdiction?.require_kyc || false;
1439
+ const requiresEDD = geoVerification.jurisdiction?.require_enhanced_verification || false;
1440
+ if (this.config.debug) {
1441
+ console.log("[ComplianceSDK] Registration verification complete:", {
1442
+ allowed: isAllowed,
1443
+ riskScore: geoVerification.risk_score,
1444
+ profileId: profile.id
1445
+ });
1446
+ }
1447
+ return {
1448
+ allowed: isAllowed,
1449
+ geolocation: geoVerification,
1450
+ profile,
1451
+ requiresKYC,
1452
+ requiresEDD,
1453
+ blockReasons: !isAllowed ? geoVerification.risk_reasons : [],
1454
+ processingTime: Date.now() - startTime
1455
+ };
1456
+ } catch (error) {
1457
+ if (this.config.debug) {
1458
+ console.error("[ComplianceSDK] Registration verification failed:", error);
1459
+ }
1460
+ throw new ComplianceError("Registration verification failed", error);
1461
+ }
1462
+ }
1463
+ /**
1464
+ * Verify customer login with profile activity update
1465
+ *
1466
+ * Verifies geolocation and updates customer profile with latest activity.
1467
+ *
1468
+ * @param request - Login verification request
1469
+ * @returns Verification response with compliance status
1470
+ *
1471
+ * @example
1472
+ * ```typescript
1473
+ * const result = await sdk.verifyAtLogin({
1474
+ * customerId: 'CUST-12345',
1475
+ * ipAddress: req.ip,
1476
+ * deviceFingerprint: getDeviceFingerprint()
1477
+ * });
1478
+ *
1479
+ * if (result.allowed) {
1480
+ * // Allow login
1481
+ * if (result.requiresStepUp) {
1482
+ * // Trigger MFA or additional verification
1483
+ * }
1484
+ * } else {
1485
+ * // Block login
1486
+ * console.log('Blocked:', result.blockReasons);
1487
+ * }
1488
+ * ```
1489
+ */
1490
+ async verifyAtLogin(request) {
1491
+ const startTime = Date.now();
1492
+ try {
1493
+ if (this.config.debug) {
1494
+ console.log("[ComplianceSDK] Starting login verification for:", request.customerId);
1495
+ }
1496
+ const geoVerification = await this.geoClient.verifyIP({
1497
+ ip_address: request.ipAddress,
1498
+ user_id: request.customerId,
1499
+ event_type: "login",
1500
+ device_fingerprint: request.deviceFingerprint
1501
+ });
1502
+ let profile;
1503
+ try {
1504
+ profile = await this.riskClient.getProfile(request.customerId);
1505
+ if (this.shouldUpdateProfile(profile, geoVerification.location.city)) {
1506
+ await this.riskClient.updateProfile(profile.id, {
1507
+ last_recorded_activity: (/* @__PURE__ */ new Date()).toISOString(),
1508
+ location: `${geoVerification.location.city}, ${geoVerification.location.country}`,
1509
+ location_compliance: geoVerification.is_compliant ? "compliant" : "non-compliant"
1510
+ });
1511
+ profile = await this.riskClient.getProfile(request.customerId);
1512
+ }
1513
+ } catch (error) {
1514
+ if (this.config.autoCreateProfiles && this.config.debug) {
1515
+ console.warn("[ComplianceSDK] Profile not found for login, creating...");
1516
+ }
1517
+ profile = await this.createProfileFromGeo(request.customerId, geoVerification);
1518
+ }
1519
+ const isAllowed = geoVerification.is_compliant && !geoVerification.is_blocked && profile.customer_status !== "suspended";
1520
+ const requiresStepUp = geoVerification.risk_level === "high" || geoVerification.risk_level === "critical";
1521
+ return {
1522
+ allowed: isAllowed,
1523
+ geolocation: geoVerification,
1524
+ profile,
1525
+ requiresStepUp,
1526
+ blockReasons: this.getBlockReasons(geoVerification, profile),
1527
+ processingTime: Date.now() - startTime
1528
+ };
1529
+ } catch (error) {
1530
+ throw new ComplianceError("Login verification failed", error);
1531
+ }
1532
+ }
1533
+ /**
1534
+ * Verify transaction with amount-based risk assessment
1535
+ *
1536
+ * Combines geolocation verification with transaction amount analysis
1537
+ * and customer risk profile for comprehensive transaction screening.
1538
+ *
1539
+ * @param request - Transaction verification request
1540
+ * @returns Verification response with transaction risk assessment
1541
+ *
1542
+ * @example
1543
+ * ```typescript
1544
+ * const result = await sdk.verifyAtTransaction({
1545
+ * customerId: 'CUST-12345',
1546
+ * ipAddress: req.ip,
1547
+ * amount: 5000,
1548
+ * currency: 'USD',
1549
+ * transactionType: 'withdrawal',
1550
+ * deviceFingerprint: getDeviceFingerprint()
1551
+ * });
1552
+ *
1553
+ * if (result.allowed) {
1554
+ * // Process transaction
1555
+ * } else if (result.requiresApproval) {
1556
+ * // Queue for manual review
1557
+ * } else {
1558
+ * // Block transaction
1559
+ * console.log('Blocked:', result.blockReasons);
1560
+ * }
1561
+ * ```
1562
+ */
1563
+ async verifyAtTransaction(request) {
1564
+ const startTime = Date.now();
1565
+ try {
1566
+ if (this.config.debug) {
1567
+ console.log("[ComplianceSDK] Starting transaction verification:", {
1568
+ customerId: request.customerId,
1569
+ amount: request.amount,
1570
+ currency: request.currency
1571
+ });
1572
+ }
1573
+ const geoVerification = await this.geoClient.verifyIP({
1574
+ ip_address: request.ipAddress,
1575
+ user_id: request.customerId,
1576
+ event_type: "transaction",
1577
+ device_fingerprint: request.deviceFingerprint
1578
+ });
1579
+ const profile = await this.riskClient.getProfile(request.customerId);
1580
+ const transactionRisk = this.calculateTransactionRisk(
1581
+ request.amount,
1582
+ request.currency,
1583
+ geoVerification,
1584
+ profile
1585
+ );
1586
+ const jurisdictionAllowed = this.checkJurisdictionLimits(
1587
+ request.amount,
1588
+ request.currency,
1589
+ geoVerification.jurisdiction
1590
+ );
1591
+ const isAllowed = geoVerification.is_compliant && !geoVerification.is_blocked && jurisdictionAllowed && transactionRisk.allowed;
1592
+ return {
1593
+ allowed: isAllowed,
1594
+ geolocation: geoVerification,
1595
+ profile,
1596
+ transactionRisk,
1597
+ requiresApproval: transactionRisk.requiresManualReview,
1598
+ blockReasons: this.getTransactionBlockReasons(
1599
+ geoVerification,
1600
+ transactionRisk,
1601
+ jurisdictionAllowed
1602
+ ),
1603
+ processingTime: Date.now() - startTime
1604
+ };
1605
+ } catch (error) {
1606
+ throw new ComplianceError("Transaction verification failed", error);
1607
+ }
1608
+ }
1609
+ /**
1610
+ * Generic event verification (for other touchpoints)
1611
+ *
1612
+ * @param request - Event verification request
1613
+ * @returns Verification response
1614
+ */
1615
+ async verifyEvent(request) {
1616
+ const startTime = Date.now();
1617
+ const geoVerification = await this.geoClient.verifyIP({
1618
+ ip_address: request.ipAddress,
1619
+ user_id: request.customerId,
1620
+ event_type: request.eventType,
1621
+ device_fingerprint: request.deviceFingerprint
1622
+ });
1623
+ if (this.config.autoCreateProfiles) {
1624
+ try {
1625
+ const profile = await this.riskClient.getProfile(request.customerId);
1626
+ await this.riskClient.updateProfile(profile.id, {
1627
+ last_recorded_activity: (/* @__PURE__ */ new Date()).toISOString()
1628
+ });
1629
+ } catch (error) {
1630
+ if (this.config.debug) {
1631
+ console.warn("[ComplianceSDK] Profile not found for event");
1632
+ }
1633
+ }
1634
+ }
1635
+ return {
1636
+ allowed: geoVerification.is_compliant && !geoVerification.is_blocked,
1637
+ geolocation: geoVerification,
1638
+ blockReasons: geoVerification.risk_reasons,
1639
+ processingTime: Date.now() - startTime
1640
+ };
1641
+ }
1642
+ // ============================================================================
1643
+ // Helper Methods
1644
+ // ============================================================================
1645
+ shouldUpdateProfile(profile, newCity) {
1646
+ return !profile.location || !profile.location.includes(newCity);
1647
+ }
1648
+ async createProfileFromGeo(customerId, geoVerification) {
1649
+ return this.riskClient.createProfile({
1650
+ customer_id: customerId,
1651
+ entity_type: "individual",
1652
+ customer_status: "active",
1653
+ email_address: `${customerId}@placeholder.local`,
1654
+ // Temporary email
1655
+ location: `${geoVerification.location.city}, ${geoVerification.location.country}`,
1656
+ location_compliance: geoVerification.is_compliant ? "compliant" : "non-compliant",
1657
+ country_of_residence: geoVerification.location.country_iso,
1658
+ kyc_status: "pending"
1659
+ });
1660
+ }
1661
+ calculateTransactionRisk(amount, currency, geoVerification, profile) {
1662
+ let riskScore = 0;
1663
+ const factors = [];
1664
+ const normalizedAmount = this.normalizeToUSD(amount, currency);
1665
+ if (normalizedAmount > 1e4) {
1666
+ riskScore += 30;
1667
+ factors.push("high_transaction_amount");
1668
+ } else if (normalizedAmount > 5e3) {
1669
+ riskScore += 15;
1670
+ factors.push("elevated_transaction_amount");
1671
+ }
1672
+ riskScore += geoVerification.risk_score * 0.4;
1673
+ if (geoVerification.risk_level === "high" || geoVerification.risk_level === "critical") {
1674
+ factors.push("high_risk_location");
1675
+ }
1676
+ riskScore += profile.risk_score * 0.3;
1677
+ if (profile.risk_category === "high" || profile.risk_category === "critical") {
1678
+ factors.push("high_risk_customer");
1679
+ }
1680
+ if (geoVerification.location.is_vpn || geoVerification.location.is_proxy) {
1681
+ riskScore += 20;
1682
+ factors.push("anonymization_detected");
1683
+ }
1684
+ if (profile.customer_status === "suspended") {
1685
+ riskScore += 50;
1686
+ factors.push("account_suspended");
1687
+ }
1688
+ return {
1689
+ score: Math.min(riskScore, 100),
1690
+ level: this.getRiskLevel(riskScore),
1691
+ factors,
1692
+ allowed: riskScore < 70,
1693
+ requiresManualReview: riskScore >= 60 && riskScore < 70
1694
+ };
1695
+ }
1696
+ checkJurisdictionLimits(amount, currency, jurisdiction) {
1697
+ if (!jurisdiction || !jurisdiction.max_transaction_amount) {
1698
+ return true;
1699
+ }
1700
+ const normalizedAmount = this.normalizeToUSD(amount, currency);
1701
+ return normalizedAmount <= jurisdiction.max_transaction_amount;
1702
+ }
1703
+ normalizeToUSD(amount, currency) {
1704
+ const rate = this.currencyRates[currency.toUpperCase()] || 1;
1705
+ return amount * rate;
1706
+ }
1707
+ getRiskLevel(score) {
1708
+ if (score >= 80) return "critical";
1709
+ if (score >= 60) return "high";
1710
+ if (score >= 40) return "medium";
1711
+ return "low";
1712
+ }
1713
+ getBlockReasons(geoVerification, profile) {
1714
+ const reasons = [];
1715
+ if (geoVerification.is_blocked) {
1716
+ reasons.push(...geoVerification.risk_reasons);
1717
+ }
1718
+ if (profile.customer_status === "suspended") {
1719
+ reasons.push("account_suspended");
1720
+ }
1721
+ if (profile.has_sanctions) {
1722
+ reasons.push("sanctions_match");
1723
+ }
1724
+ return reasons;
1725
+ }
1726
+ getTransactionBlockReasons(geoVerification, transactionRisk, jurisdictionAllowed) {
1727
+ const reasons = [];
1728
+ if (!geoVerification.is_compliant) {
1729
+ reasons.push("non_compliant_jurisdiction");
1730
+ }
1731
+ if (!jurisdictionAllowed) {
1732
+ reasons.push("exceeds_jurisdiction_limit");
1733
+ }
1734
+ if (!transactionRisk.allowed) {
1735
+ reasons.push(...transactionRisk.factors);
1736
+ }
1737
+ return reasons;
1738
+ }
1739
+ // ============================================================================
1740
+ // Configuration
1741
+ // ============================================================================
1742
+ /**
1743
+ * Update currency exchange rates
1744
+ *
1745
+ * @param rates - Currency to USD exchange rates
1746
+ */
1747
+ updateCurrencyRates(rates) {
1748
+ this.currencyRates = { ...this.currencyRates, ...rates };
1749
+ }
1750
+ /**
1751
+ * Get underlying geolocation client
1752
+ */
1753
+ getGeolocationClient() {
1754
+ return this.geoClient;
1755
+ }
1756
+ /**
1757
+ * Get underlying risk profile client
1758
+ */
1759
+ getRiskProfileClient() {
1760
+ return this.riskClient;
1761
+ }
1762
+ /**
1763
+ * Update configuration
1764
+ */
1765
+ updateConfig(config) {
1766
+ this.config = { ...this.config, ...config };
1767
+ this.geoClient.updateConfig(config);
1768
+ this.riskClient.updateConfig(config);
1769
+ }
1770
+ };
1771
+
1772
+ // src/compliance/types.ts
1773
+ var DEFAULT_CURRENCY_RATES2 = {
1774
+ USD: 1,
1775
+ EUR: 1.1,
1776
+ GBP: 1.27,
1777
+ CAD: 0.74,
1778
+ AUD: 0.66,
1779
+ JPY: 67e-4,
1780
+ CHF: 1.13,
1781
+ CNY: 0.14,
1782
+ INR: 0.012,
1783
+ BRL: 0.2,
1784
+ MXN: 0.058,
1785
+ ZAR: 0.055
1786
+ };
1787
+
1788
+ export { AuthenticationError, BaseClient, CGSError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES2 as DEFAULT_CURRENCY_RATES, GeolocationClient, NetworkError, RateLimitError, RiskProfileClient, ServiceUnavailableError, TimeoutError, ValidationError, createDeviceFingerprint, getBrowserInfo, useAlerts, useDashboardMetrics, useGeolocation };
1789
+ //# sourceMappingURL=index.mjs.map
1790
+ //# sourceMappingURL=index.mjs.map