cgs-compliance-sdk 2.0.0

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