vesant-sdk 1.7.0-dev.9f7da53 → 1.7.0-dev.ab635a0
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/{client-DoMSYMMR.d.ts → client-0NOPDnT0.d.ts} +29 -26
- package/dist/{client-DMIRx7Tu.d.mts → client-CvWNRwwg.d.mts} +29 -26
- package/dist/{client-ZNdnpWe7.d.mts → client-Dps40EtF.d.mts} +2 -2
- package/dist/{client-C3DCmGe9.d.ts → client-SMxqod4j.d.ts} +2 -2
- package/dist/{client-BolQlL5e.d.mts → client-q9fN8Hhf.d.mts} +103 -1
- package/dist/{client-BolQlL5e.d.ts → client-q9fN8Hhf.d.ts} +103 -1
- package/dist/compliance/index.d.mts +3 -3
- package/dist/compliance/index.d.ts +3 -3
- package/dist/compliance/index.js +133 -22
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +133 -22
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/decisions/index.d.mts +1 -1
- package/dist/decisions/index.d.ts +1 -1
- package/dist/decisions/index.js +68 -7
- package/dist/decisions/index.js.map +1 -1
- package/dist/decisions/index.mjs +68 -7
- package/dist/decisions/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +59 -4
- package/dist/geolocation/index.d.ts +59 -4
- package/dist/geolocation/index.js +136 -19
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +134 -20
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index-C3zUKydT.d.mts +264 -0
- package/dist/index-DSDgS09k.d.ts +264 -0
- package/dist/index.d.mts +27 -10
- package/dist/index.d.ts +27 -10
- package/dist/index.js +343 -69
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +336 -70
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +2 -2
- package/dist/kyc/core.d.ts +2 -2
- package/dist/kyc/core.js +109 -30
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +109 -30
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +164 -61
- package/dist/kyc/index.d.ts +164 -61
- package/dist/kyc/index.js +109 -30
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +109 -30
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +21 -15
- package/dist/react.d.ts +21 -15
- package/dist/react.js +199 -87
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +199 -87
- package/dist/react.mjs.map +1 -1
- package/dist/risk-profile/index.d.mts +1 -1
- package/dist/risk-profile/index.d.ts +1 -1
- package/dist/risk-profile/index.js +68 -7
- package/dist/risk-profile/index.js.map +1 -1
- package/dist/risk-profile/index.mjs +68 -7
- package/dist/risk-profile/index.mjs.map +1 -1
- package/dist/scores/index.d.mts +1 -1
- package/dist/scores/index.d.ts +1 -1
- package/dist/scores/index.js +68 -7
- package/dist/scores/index.js.map +1 -1
- package/dist/scores/index.mjs +68 -7
- package/dist/scores/index.mjs.map +1 -1
- package/dist/tax/index.d.mts +20 -3
- package/dist/tax/index.d.ts +20 -3
- package/dist/tax/index.js +77 -10
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +77 -10
- package/dist/tax/index.mjs.map +1 -1
- package/dist/webhooks/index.d.mts +2 -189
- package/dist/webhooks/index.d.ts +2 -189
- package/dist/webhooks/index.js +85 -21
- package/dist/webhooks/index.js.map +1 -1
- package/dist/webhooks/index.mjs +85 -22
- package/dist/webhooks/index.mjs.map +1 -1
- package/package.json +4 -1
|
@@ -73,6 +73,19 @@ var sdkReasons = {
|
|
|
73
73
|
})
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
// src/core/auth.ts
|
|
77
|
+
var StaticApiKeyProvider = class {
|
|
78
|
+
constructor(apiKey) {
|
|
79
|
+
this.apiKey = apiKey;
|
|
80
|
+
}
|
|
81
|
+
async getCredential() {
|
|
82
|
+
return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
|
|
83
|
+
}
|
|
84
|
+
canRefresh() {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
76
89
|
// src/core/errors.ts
|
|
77
90
|
var VesantError = class _VesantError extends Error {
|
|
78
91
|
constructor(message, code, statusCode, details) {
|
|
@@ -379,6 +392,12 @@ var BaseClient = class {
|
|
|
379
392
|
if (!config.tenantId?.trim()) {
|
|
380
393
|
throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
|
|
381
394
|
}
|
|
395
|
+
if (typeof fetch === "undefined") {
|
|
396
|
+
throw new VesantError(
|
|
397
|
+
"The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
|
|
398
|
+
"RUNTIME_UNSUPPORTED"
|
|
399
|
+
);
|
|
400
|
+
}
|
|
382
401
|
this.interceptors = config.interceptors || [];
|
|
383
402
|
this.logger = config.logger || createConsoleLogger();
|
|
384
403
|
let environment = config.environment;
|
|
@@ -397,11 +416,12 @@ var BaseClient = class {
|
|
|
397
416
|
environment,
|
|
398
417
|
headers: config.headers || {},
|
|
399
418
|
timeout: config.timeout || 1e4,
|
|
400
|
-
retries: config.retries
|
|
419
|
+
retries: config.retries ?? 3,
|
|
401
420
|
debug: config.debug || false,
|
|
402
421
|
interceptors: this.interceptors,
|
|
403
422
|
logger: this.logger
|
|
404
423
|
};
|
|
424
|
+
this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
|
|
405
425
|
if (config.circuitBreaker) {
|
|
406
426
|
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
|
|
407
427
|
}
|
|
@@ -409,6 +429,19 @@ var BaseClient = class {
|
|
|
409
429
|
this.rateLimitTracker = new RateLimitTracker();
|
|
410
430
|
}
|
|
411
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
|
|
434
|
+
* get the caller-supplied key or a freshly minted UUID; non-mutating methods get
|
|
435
|
+
* none. requestWithRetry resolves this once and injects it so every retry reuses
|
|
436
|
+
* one key (letting the server dedup replays); a direct request() resolves per call.
|
|
437
|
+
*/
|
|
438
|
+
resolveIdempotencyKey(method, requestOptions) {
|
|
439
|
+
const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
|
|
440
|
+
if (!isMutating) {
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
return requestOptions?.idempotencyKey ?? generateUUID();
|
|
444
|
+
}
|
|
412
445
|
/**
|
|
413
446
|
* Make an HTTP request with timeout and error handling
|
|
414
447
|
*/
|
|
@@ -434,15 +467,20 @@ var BaseClient = class {
|
|
|
434
467
|
...this.config.headers,
|
|
435
468
|
...options.headers || {}
|
|
436
469
|
};
|
|
437
|
-
|
|
438
|
-
|
|
470
|
+
const credential = await this.authProvider?.getCredential({
|
|
471
|
+
tenantId: this.config.tenantId,
|
|
472
|
+
requestId
|
|
473
|
+
});
|
|
474
|
+
if (credential) {
|
|
475
|
+
headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
|
|
439
476
|
}
|
|
440
477
|
if (this.config.environment === "sandbox") {
|
|
441
478
|
headers["X-Sandbox"] = "true";
|
|
442
479
|
}
|
|
443
480
|
const method = (options.method || "GET").toUpperCase();
|
|
444
|
-
|
|
445
|
-
|
|
481
|
+
const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
|
|
482
|
+
if (idempotencyKey) {
|
|
483
|
+
headers["Idempotency-Key"] = idempotencyKey;
|
|
446
484
|
}
|
|
447
485
|
const controller = new AbortController();
|
|
448
486
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -463,10 +501,28 @@ var BaseClient = class {
|
|
|
463
501
|
if (this.config.debug) {
|
|
464
502
|
this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
|
|
465
503
|
}
|
|
466
|
-
|
|
504
|
+
let response = await fetch(url, {
|
|
467
505
|
...finalOptions,
|
|
468
506
|
signal: controller.signal
|
|
469
507
|
});
|
|
508
|
+
if (response.status === 401 && this.authProvider?.canRefresh?.()) {
|
|
509
|
+
const refreshed = await this.authProvider.getCredential({
|
|
510
|
+
tenantId: this.config.tenantId,
|
|
511
|
+
requestId,
|
|
512
|
+
forceRefresh: true
|
|
513
|
+
});
|
|
514
|
+
if (refreshed) {
|
|
515
|
+
this.config.onCredentialRotated?.({ reason: "unauthorized" });
|
|
516
|
+
finalOptions = {
|
|
517
|
+
...finalOptions,
|
|
518
|
+
headers: {
|
|
519
|
+
...finalOptions.headers,
|
|
520
|
+
Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
response = await fetch(url, { ...finalOptions, signal: controller.signal });
|
|
524
|
+
}
|
|
525
|
+
}
|
|
470
526
|
clearTimeout(timeoutId);
|
|
471
527
|
if (this.rateLimitTracker) {
|
|
472
528
|
this.rateLimitTracker.updateFromHeaders(response.headers);
|
|
@@ -555,9 +611,11 @@ var BaseClient = class {
|
|
|
555
611
|
*/
|
|
556
612
|
async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
|
|
557
613
|
let lastError;
|
|
614
|
+
const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
|
|
615
|
+
const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
|
|
558
616
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
559
617
|
try {
|
|
560
|
-
return await this.request(endpoint, options, serviceURL,
|
|
618
|
+
return await this.request(endpoint, options, serviceURL, attemptOptions);
|
|
561
619
|
} catch (error) {
|
|
562
620
|
lastError = error instanceof Error ? error : new Error("Unknown error");
|
|
563
621
|
if (requestOptions?.signal?.aborted) {
|
|
@@ -654,6 +712,9 @@ var BaseClient = class {
|
|
|
654
712
|
if (config.interceptors) {
|
|
655
713
|
this.interceptors = config.interceptors;
|
|
656
714
|
}
|
|
715
|
+
if (config.authProvider !== void 0 || config.apiKey !== void 0) {
|
|
716
|
+
this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
|
|
717
|
+
}
|
|
657
718
|
}
|
|
658
719
|
/**
|
|
659
720
|
* Get current configuration (readonly)
|
|
@@ -681,6 +742,61 @@ var BaseClient = class {
|
|
|
681
742
|
}
|
|
682
743
|
};
|
|
683
744
|
|
|
745
|
+
// src/geolocation/integrity.ts
|
|
746
|
+
var isNil = (v) => v === null || v === void 0;
|
|
747
|
+
function assessGpsIntegrity(input, opts = {}) {
|
|
748
|
+
const flags = [];
|
|
749
|
+
const now = opts.now ?? (() => Date.now());
|
|
750
|
+
const maxAgeMs = opts.maxAgeMs ?? 6e4;
|
|
751
|
+
const minAccuracy = opts.minAccuracyMeters ?? 1;
|
|
752
|
+
const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
|
|
753
|
+
if (isNil(accuracy)) {
|
|
754
|
+
flags.push("accuracy_missing");
|
|
755
|
+
} else if (accuracy === 0) {
|
|
756
|
+
flags.push("accuracy_zero");
|
|
757
|
+
} else if (accuracy < minAccuracy) {
|
|
758
|
+
flags.push("accuracy_implausibly_small");
|
|
759
|
+
}
|
|
760
|
+
const hasMotion = !isNil(heading) || !isNil(speed);
|
|
761
|
+
if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
|
|
762
|
+
flags.push("motion_without_accuracy");
|
|
763
|
+
}
|
|
764
|
+
if (!isNil(altitude) && isNil(altitudeAccuracy)) {
|
|
765
|
+
flags.push("altitude_without_accuracy");
|
|
766
|
+
}
|
|
767
|
+
if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
|
|
768
|
+
flags.push("stale_timestamp");
|
|
769
|
+
}
|
|
770
|
+
if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
|
|
771
|
+
flags.push(`permission_${opts.permissionState}`);
|
|
772
|
+
}
|
|
773
|
+
return { suspicious: flags.length > 0, flags };
|
|
774
|
+
}
|
|
775
|
+
function gpsIntegrityInputFromPosition(position) {
|
|
776
|
+
const c = position.coords;
|
|
777
|
+
return {
|
|
778
|
+
latitude: c.latitude,
|
|
779
|
+
longitude: c.longitude,
|
|
780
|
+
accuracy: c.accuracy,
|
|
781
|
+
altitude: c.altitude,
|
|
782
|
+
altitudeAccuracy: c.altitudeAccuracy,
|
|
783
|
+
heading: c.heading,
|
|
784
|
+
speed: c.speed,
|
|
785
|
+
timestamp: position.timestamp
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
async function probeGeolocationPermission() {
|
|
789
|
+
try {
|
|
790
|
+
if (typeof navigator === "undefined" || !navigator.permissions?.query) {
|
|
791
|
+
return "unavailable";
|
|
792
|
+
}
|
|
793
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
794
|
+
return status.state;
|
|
795
|
+
} catch {
|
|
796
|
+
return "unavailable";
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
684
800
|
// src/geolocation/ciphertext.ts
|
|
685
801
|
var CIPHER_TEXT_EXPIRY_MINUTES = 5;
|
|
686
802
|
async function computeHMAC(key, message) {
|
|
@@ -733,6 +849,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
733
849
|
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
734
850
|
return null;
|
|
735
851
|
}
|
|
852
|
+
const permissionState = await probeGeolocationPermission();
|
|
736
853
|
return new Promise((resolve) => {
|
|
737
854
|
navigator.geolocation.getCurrentPosition(
|
|
738
855
|
(position) => {
|
|
@@ -749,6 +866,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
749
866
|
resolve(null);
|
|
750
867
|
return;
|
|
751
868
|
}
|
|
869
|
+
const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
|
|
752
870
|
resolve({
|
|
753
871
|
latitude,
|
|
754
872
|
longitude,
|
|
@@ -757,7 +875,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
757
875
|
altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
|
|
758
876
|
heading: position.coords.heading ?? void 0,
|
|
759
877
|
speed: position.coords.speed ?? void 0,
|
|
760
|
-
timestamp: position.timestamp
|
|
878
|
+
timestamp: position.timestamp,
|
|
879
|
+
integrity_flags: flags.length ? flags : void 0
|
|
761
880
|
});
|
|
762
881
|
},
|
|
763
882
|
() => {
|
|
@@ -824,6 +943,9 @@ async function generateCipherText(options, config) {
|
|
|
824
943
|
);
|
|
825
944
|
if (location) {
|
|
826
945
|
locationData = location;
|
|
946
|
+
if (location.integrity_flags?.length) {
|
|
947
|
+
warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
|
|
948
|
+
}
|
|
827
949
|
} else if (gpsRequiredByConfig) {
|
|
828
950
|
throw new VesantError(
|
|
829
951
|
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
|
|
@@ -918,8 +1040,8 @@ var GeolocationClient = class extends BaseClient {
|
|
|
918
1040
|
* ```
|
|
919
1041
|
*/
|
|
920
1042
|
async verifyIP(request, requestOptions) {
|
|
921
|
-
if (!request.ip_address
|
|
922
|
-
throw new ValidationError("ip_address
|
|
1043
|
+
if (request.ip_address !== void 0 && !request.ip_address.trim()) {
|
|
1044
|
+
throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
|
|
923
1045
|
}
|
|
924
1046
|
return this.requestWithRetry("/api/v1/geo/verify", {
|
|
925
1047
|
method: "POST",
|
|
@@ -1269,20 +1391,12 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1269
1391
|
*
|
|
1270
1392
|
* @example
|
|
1271
1393
|
* ```typescript
|
|
1272
|
-
* //
|
|
1394
|
+
* // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
|
|
1273
1395
|
* const result = await client.captureLocation(token, {
|
|
1274
1396
|
* latitude: 37.7749,
|
|
1275
1397
|
* longitude: -122.4194,
|
|
1276
1398
|
* accuracy: 10
|
|
1277
1399
|
* });
|
|
1278
|
-
*
|
|
1279
|
-
* // Using WiFi positioning (fallback)
|
|
1280
|
-
* const result = await client.captureLocation(token, {
|
|
1281
|
-
* wifi_networks: [
|
|
1282
|
-
* { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
|
|
1283
|
-
* { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
|
|
1284
|
-
* ]
|
|
1285
|
-
* });
|
|
1286
1400
|
* ```
|
|
1287
1401
|
*/
|
|
1288
1402
|
async captureLocation(token, capture, requestOptions) {
|
|
@@ -1712,9 +1826,6 @@ var ComplianceClient = class {
|
|
|
1712
1826
|
if (cipherTextResult.risk?.is_blocked) {
|
|
1713
1827
|
blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
|
|
1714
1828
|
}
|
|
1715
|
-
if (cipherTextResult.risk?.location_mismatch) {
|
|
1716
|
-
blockReasons.push(sdkReasons.gpsIPMismatch());
|
|
1717
|
-
}
|
|
1718
1829
|
}
|
|
1719
1830
|
if (geoVerification.gps_required && !cipherTextResult) {
|
|
1720
1831
|
blockReasons.push(sdkReasons.gpsRequired());
|