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