vesant-sdk 1.7.0-dev.a85efc5 → 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-DF7hlMEz.d.ts → client-0NOPDnT0.d.ts} +16 -26
- package/dist/{client-DrjgZoH_.d.mts → client-CvWNRwwg.d.mts} +16 -26
- package/dist/{client-B0qhE2kr.d.mts → client-Dps40EtF.d.mts} +2 -2
- package/dist/{client-DtH2RLuy.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 -19
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +133 -19
- 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 +26 -9
- package/dist/index.d.ts +26 -9
- package/dist/index.js +320 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +313 -51
- 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 +90 -15
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +90 -15
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +56 -8
- package/dist/kyc/index.d.ts +56 -8
- package/dist/kyc/index.js +90 -15
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +90 -15
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +7 -3
- package/dist/react.d.ts +7 -3
- package/dist/react.js +68 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +68 -4
- 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 +2 -2
- package/dist/tax/index.d.ts +2 -2
- package/dist/tax/index.js +73 -9
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +73 -9
- 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
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
+
// src/core/auth.ts
|
|
2
|
+
var StaticApiKeyProvider = class {
|
|
3
|
+
constructor(apiKey) {
|
|
4
|
+
this.apiKey = apiKey;
|
|
5
|
+
}
|
|
6
|
+
async getCredential() {
|
|
7
|
+
return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
|
|
8
|
+
}
|
|
9
|
+
canRefresh() {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var RefreshingTokenProvider = class {
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.opts = opts;
|
|
16
|
+
this.cached = null;
|
|
17
|
+
this.inFlight = null;
|
|
18
|
+
this.skewMs = opts.skewMs ?? 3e4;
|
|
19
|
+
this.scheme = opts.scheme ?? "Bearer";
|
|
20
|
+
this.now = opts.now ?? (() => Date.now());
|
|
21
|
+
}
|
|
22
|
+
canRefresh() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
async getCredential(ctx) {
|
|
26
|
+
const forceRefresh = ctx?.forceRefresh ?? false;
|
|
27
|
+
if (!forceRefresh && this.cached && !this.isExpiring(this.cached)) {
|
|
28
|
+
return { scheme: this.scheme, token: this.cached.token };
|
|
29
|
+
}
|
|
30
|
+
const fresh = await this.refresh();
|
|
31
|
+
return { scheme: this.scheme, token: fresh.token };
|
|
32
|
+
}
|
|
33
|
+
isExpiring(token) {
|
|
34
|
+
if (token.expiresAt === void 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return this.now() >= token.expiresAt - this.skewMs;
|
|
38
|
+
}
|
|
39
|
+
refresh() {
|
|
40
|
+
if (this.inFlight) {
|
|
41
|
+
return this.inFlight;
|
|
42
|
+
}
|
|
43
|
+
this.inFlight = (async () => {
|
|
44
|
+
try {
|
|
45
|
+
const fresh = await this.opts.fetchToken();
|
|
46
|
+
this.cached = fresh;
|
|
47
|
+
this.opts.onRotate?.({ expiresAt: fresh.expiresAt });
|
|
48
|
+
return fresh;
|
|
49
|
+
} finally {
|
|
50
|
+
this.inFlight = null;
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
return this.inFlight;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
1
57
|
// src/core/errors.ts
|
|
2
58
|
var VesantError = class _VesantError extends Error {
|
|
3
59
|
constructor(message, code, statusCode, details) {
|
|
@@ -322,6 +378,12 @@ var BaseClient = class {
|
|
|
322
378
|
if (!config.tenantId?.trim()) {
|
|
323
379
|
throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
|
|
324
380
|
}
|
|
381
|
+
if (typeof fetch === "undefined") {
|
|
382
|
+
throw new VesantError(
|
|
383
|
+
"The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
|
|
384
|
+
"RUNTIME_UNSUPPORTED"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
325
387
|
this.interceptors = config.interceptors || [];
|
|
326
388
|
this.logger = config.logger || createConsoleLogger();
|
|
327
389
|
let environment = config.environment;
|
|
@@ -340,11 +402,12 @@ var BaseClient = class {
|
|
|
340
402
|
environment,
|
|
341
403
|
headers: config.headers || {},
|
|
342
404
|
timeout: config.timeout || 1e4,
|
|
343
|
-
retries: config.retries
|
|
405
|
+
retries: config.retries ?? 3,
|
|
344
406
|
debug: config.debug || false,
|
|
345
407
|
interceptors: this.interceptors,
|
|
346
408
|
logger: this.logger
|
|
347
409
|
};
|
|
410
|
+
this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
|
|
348
411
|
if (config.circuitBreaker) {
|
|
349
412
|
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
|
|
350
413
|
}
|
|
@@ -352,6 +415,19 @@ var BaseClient = class {
|
|
|
352
415
|
this.rateLimitTracker = new RateLimitTracker();
|
|
353
416
|
}
|
|
354
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
|
|
420
|
+
* get the caller-supplied key or a freshly minted UUID; non-mutating methods get
|
|
421
|
+
* none. requestWithRetry resolves this once and injects it so every retry reuses
|
|
422
|
+
* one key (letting the server dedup replays); a direct request() resolves per call.
|
|
423
|
+
*/
|
|
424
|
+
resolveIdempotencyKey(method, requestOptions) {
|
|
425
|
+
const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
|
|
426
|
+
if (!isMutating) {
|
|
427
|
+
return void 0;
|
|
428
|
+
}
|
|
429
|
+
return requestOptions?.idempotencyKey ?? generateUUID();
|
|
430
|
+
}
|
|
355
431
|
/**
|
|
356
432
|
* Make an HTTP request with timeout and error handling
|
|
357
433
|
*/
|
|
@@ -377,15 +453,20 @@ var BaseClient = class {
|
|
|
377
453
|
...this.config.headers,
|
|
378
454
|
...options.headers || {}
|
|
379
455
|
};
|
|
380
|
-
|
|
381
|
-
|
|
456
|
+
const credential = await this.authProvider?.getCredential({
|
|
457
|
+
tenantId: this.config.tenantId,
|
|
458
|
+
requestId
|
|
459
|
+
});
|
|
460
|
+
if (credential) {
|
|
461
|
+
headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
|
|
382
462
|
}
|
|
383
463
|
if (this.config.environment === "sandbox") {
|
|
384
464
|
headers["X-Sandbox"] = "true";
|
|
385
465
|
}
|
|
386
466
|
const method = (options.method || "GET").toUpperCase();
|
|
387
|
-
|
|
388
|
-
|
|
467
|
+
const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
|
|
468
|
+
if (idempotencyKey) {
|
|
469
|
+
headers["Idempotency-Key"] = idempotencyKey;
|
|
389
470
|
}
|
|
390
471
|
const controller = new AbortController();
|
|
391
472
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -406,10 +487,28 @@ var BaseClient = class {
|
|
|
406
487
|
if (this.config.debug) {
|
|
407
488
|
this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
|
|
408
489
|
}
|
|
409
|
-
|
|
490
|
+
let response = await fetch(url, {
|
|
410
491
|
...finalOptions,
|
|
411
492
|
signal: controller.signal
|
|
412
493
|
});
|
|
494
|
+
if (response.status === 401 && this.authProvider?.canRefresh?.()) {
|
|
495
|
+
const refreshed = await this.authProvider.getCredential({
|
|
496
|
+
tenantId: this.config.tenantId,
|
|
497
|
+
requestId,
|
|
498
|
+
forceRefresh: true
|
|
499
|
+
});
|
|
500
|
+
if (refreshed) {
|
|
501
|
+
this.config.onCredentialRotated?.({ reason: "unauthorized" });
|
|
502
|
+
finalOptions = {
|
|
503
|
+
...finalOptions,
|
|
504
|
+
headers: {
|
|
505
|
+
...finalOptions.headers,
|
|
506
|
+
Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
response = await fetch(url, { ...finalOptions, signal: controller.signal });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
413
512
|
clearTimeout(timeoutId);
|
|
414
513
|
if (this.rateLimitTracker) {
|
|
415
514
|
this.rateLimitTracker.updateFromHeaders(response.headers);
|
|
@@ -498,9 +597,11 @@ var BaseClient = class {
|
|
|
498
597
|
*/
|
|
499
598
|
async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
|
|
500
599
|
let lastError;
|
|
600
|
+
const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
|
|
601
|
+
const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
|
|
501
602
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
502
603
|
try {
|
|
503
|
-
return await this.request(endpoint, options, serviceURL,
|
|
604
|
+
return await this.request(endpoint, options, serviceURL, attemptOptions);
|
|
504
605
|
} catch (error) {
|
|
505
606
|
lastError = error instanceof Error ? error : new Error("Unknown error");
|
|
506
607
|
if (requestOptions?.signal?.aborted) {
|
|
@@ -597,6 +698,9 @@ var BaseClient = class {
|
|
|
597
698
|
if (config.interceptors) {
|
|
598
699
|
this.interceptors = config.interceptors;
|
|
599
700
|
}
|
|
701
|
+
if (config.authProvider !== void 0 || config.apiKey !== void 0) {
|
|
702
|
+
this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
|
|
703
|
+
}
|
|
600
704
|
}
|
|
601
705
|
/**
|
|
602
706
|
* Get current configuration (readonly)
|
|
@@ -624,8 +728,69 @@ var BaseClient = class {
|
|
|
624
728
|
}
|
|
625
729
|
};
|
|
626
730
|
|
|
731
|
+
// src/core/pagination.ts
|
|
732
|
+
async function* paginate(fetchPage) {
|
|
733
|
+
let page = 1;
|
|
734
|
+
let totalPages;
|
|
735
|
+
do {
|
|
736
|
+
const response = await fetchPage(page);
|
|
737
|
+
const items = response.data ?? [];
|
|
738
|
+
for (const item of items) {
|
|
739
|
+
yield item;
|
|
740
|
+
}
|
|
741
|
+
totalPages = response.total_pages ?? page;
|
|
742
|
+
if (items.length === 0) {
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
page += 1;
|
|
746
|
+
} while (page <= totalPages);
|
|
747
|
+
}
|
|
748
|
+
async function collectAll(fetchPage) {
|
|
749
|
+
const items = [];
|
|
750
|
+
for await (const item of paginate(fetchPage)) {
|
|
751
|
+
items.push(item);
|
|
752
|
+
}
|
|
753
|
+
return items;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/core/dedup.ts
|
|
757
|
+
var InMemoryDedupStore = class {
|
|
758
|
+
constructor(opts = {}) {
|
|
759
|
+
this.expiryById = /* @__PURE__ */ new Map();
|
|
760
|
+
this.maxEntries = opts.maxEntries ?? 1e3;
|
|
761
|
+
this.now = opts.now ?? (() => Date.now());
|
|
762
|
+
}
|
|
763
|
+
async seen(id) {
|
|
764
|
+
return this.expiryById.has(id);
|
|
765
|
+
}
|
|
766
|
+
async mark(id, ttlMs) {
|
|
767
|
+
const now = this.now();
|
|
768
|
+
this.expiryById.set(id, now + ttlMs);
|
|
769
|
+
if (this.expiryById.size > this.maxEntries) {
|
|
770
|
+
for (const [key, expiresAt] of this.expiryById) {
|
|
771
|
+
if (now > expiresAt) {
|
|
772
|
+
this.expiryById.delete(key);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
|
|
627
779
|
// src/core/webhook-utils.ts
|
|
628
|
-
async function verifyWebhookSignature(payload, signature, secret) {
|
|
780
|
+
async function verifyWebhookSignature(payload, signature, secret, opts = {}) {
|
|
781
|
+
const signed = parseSignedTimestamp(signature);
|
|
782
|
+
if (signed) {
|
|
783
|
+
const tolerance = opts.tolerance ?? 3e5;
|
|
784
|
+
const now = opts.now?.() ?? Date.now();
|
|
785
|
+
if (tolerance > 0 && Math.abs(now - signed.t * 1e3) > tolerance) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
const expected = await computeHmacSha256(`${signed.t}.${payload}`, secret);
|
|
789
|
+
return constantTimeEqual(signed.v1, expected);
|
|
790
|
+
}
|
|
791
|
+
if (opts.allowLegacy === false) {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
629
794
|
const hexDigest = await computeHmacSha256(payload, secret);
|
|
630
795
|
const expectedPrefixed = `sha256=${hexDigest}`;
|
|
631
796
|
if (signature.startsWith("sha256=")) {
|
|
@@ -633,6 +798,25 @@ async function verifyWebhookSignature(payload, signature, secret) {
|
|
|
633
798
|
}
|
|
634
799
|
return constantTimeEqual(signature, hexDigest);
|
|
635
800
|
}
|
|
801
|
+
function parseSignedTimestamp(signature) {
|
|
802
|
+
if (!signature.includes("t=") || !signature.includes("v1=")) {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
let t;
|
|
806
|
+
let v1;
|
|
807
|
+
for (const part of signature.split(",")) {
|
|
808
|
+
const eq = part.indexOf("=");
|
|
809
|
+
if (eq === -1) continue;
|
|
810
|
+
const key = part.slice(0, eq).trim();
|
|
811
|
+
const value = part.slice(eq + 1).trim();
|
|
812
|
+
if (key === "t") t = Number(value);
|
|
813
|
+
else if (key === "v1") v1 = value;
|
|
814
|
+
}
|
|
815
|
+
if (t === void 0 || Number.isNaN(t) || !v1) {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
return { t, v1 };
|
|
819
|
+
}
|
|
636
820
|
async function computeHmacSha256(message, secret) {
|
|
637
821
|
if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
|
|
638
822
|
const encoder = new TextEncoder();
|
|
@@ -667,6 +851,61 @@ function constantTimeEqual(a, b) {
|
|
|
667
851
|
return result === 0;
|
|
668
852
|
}
|
|
669
853
|
|
|
854
|
+
// src/geolocation/integrity.ts
|
|
855
|
+
var isNil = (v) => v === null || v === void 0;
|
|
856
|
+
function assessGpsIntegrity(input, opts = {}) {
|
|
857
|
+
const flags = [];
|
|
858
|
+
const now = opts.now ?? (() => Date.now());
|
|
859
|
+
const maxAgeMs = opts.maxAgeMs ?? 6e4;
|
|
860
|
+
const minAccuracy = opts.minAccuracyMeters ?? 1;
|
|
861
|
+
const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
|
|
862
|
+
if (isNil(accuracy)) {
|
|
863
|
+
flags.push("accuracy_missing");
|
|
864
|
+
} else if (accuracy === 0) {
|
|
865
|
+
flags.push("accuracy_zero");
|
|
866
|
+
} else if (accuracy < minAccuracy) {
|
|
867
|
+
flags.push("accuracy_implausibly_small");
|
|
868
|
+
}
|
|
869
|
+
const hasMotion = !isNil(heading) || !isNil(speed);
|
|
870
|
+
if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
|
|
871
|
+
flags.push("motion_without_accuracy");
|
|
872
|
+
}
|
|
873
|
+
if (!isNil(altitude) && isNil(altitudeAccuracy)) {
|
|
874
|
+
flags.push("altitude_without_accuracy");
|
|
875
|
+
}
|
|
876
|
+
if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
|
|
877
|
+
flags.push("stale_timestamp");
|
|
878
|
+
}
|
|
879
|
+
if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
|
|
880
|
+
flags.push(`permission_${opts.permissionState}`);
|
|
881
|
+
}
|
|
882
|
+
return { suspicious: flags.length > 0, flags };
|
|
883
|
+
}
|
|
884
|
+
function gpsIntegrityInputFromPosition(position) {
|
|
885
|
+
const c = position.coords;
|
|
886
|
+
return {
|
|
887
|
+
latitude: c.latitude,
|
|
888
|
+
longitude: c.longitude,
|
|
889
|
+
accuracy: c.accuracy,
|
|
890
|
+
altitude: c.altitude,
|
|
891
|
+
altitudeAccuracy: c.altitudeAccuracy,
|
|
892
|
+
heading: c.heading,
|
|
893
|
+
speed: c.speed,
|
|
894
|
+
timestamp: position.timestamp
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
async function probeGeolocationPermission() {
|
|
898
|
+
try {
|
|
899
|
+
if (typeof navigator === "undefined" || !navigator.permissions?.query) {
|
|
900
|
+
return "unavailable";
|
|
901
|
+
}
|
|
902
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
903
|
+
return status.state;
|
|
904
|
+
} catch {
|
|
905
|
+
return "unavailable";
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
670
909
|
// src/geolocation/ciphertext.ts
|
|
671
910
|
var CIPHER_TEXT_EXPIRY_MINUTES = 5;
|
|
672
911
|
async function computeHMAC(key, message) {
|
|
@@ -719,6 +958,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
719
958
|
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
720
959
|
return null;
|
|
721
960
|
}
|
|
961
|
+
const permissionState = await probeGeolocationPermission();
|
|
722
962
|
return new Promise((resolve) => {
|
|
723
963
|
navigator.geolocation.getCurrentPosition(
|
|
724
964
|
(position) => {
|
|
@@ -735,6 +975,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
735
975
|
resolve(null);
|
|
736
976
|
return;
|
|
737
977
|
}
|
|
978
|
+
const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
|
|
738
979
|
resolve({
|
|
739
980
|
latitude,
|
|
740
981
|
longitude,
|
|
@@ -743,7 +984,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
743
984
|
altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
|
|
744
985
|
heading: position.coords.heading ?? void 0,
|
|
745
986
|
speed: position.coords.speed ?? void 0,
|
|
746
|
-
timestamp: position.timestamp
|
|
987
|
+
timestamp: position.timestamp,
|
|
988
|
+
integrity_flags: flags.length ? flags : void 0
|
|
747
989
|
});
|
|
748
990
|
},
|
|
749
991
|
() => {
|
|
@@ -810,6 +1052,9 @@ async function generateCipherText(options, config) {
|
|
|
810
1052
|
);
|
|
811
1053
|
if (location) {
|
|
812
1054
|
locationData = location;
|
|
1055
|
+
if (location.integrity_flags?.length) {
|
|
1056
|
+
warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
|
|
1057
|
+
}
|
|
813
1058
|
} else if (gpsRequiredByConfig) {
|
|
814
1059
|
throw new VesantError(
|
|
815
1060
|
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
|
|
@@ -929,8 +1174,8 @@ var GeolocationClient = class extends BaseClient {
|
|
|
929
1174
|
* ```
|
|
930
1175
|
*/
|
|
931
1176
|
async verifyIP(request, requestOptions) {
|
|
932
|
-
if (!request.ip_address
|
|
933
|
-
throw new ValidationError("ip_address
|
|
1177
|
+
if (request.ip_address !== void 0 && !request.ip_address.trim()) {
|
|
1178
|
+
throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
|
|
934
1179
|
}
|
|
935
1180
|
return this.requestWithRetry("/api/v1/geo/verify", {
|
|
936
1181
|
method: "POST",
|
|
@@ -1280,20 +1525,12 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1280
1525
|
*
|
|
1281
1526
|
* @example
|
|
1282
1527
|
* ```typescript
|
|
1283
|
-
* //
|
|
1528
|
+
* // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
|
|
1284
1529
|
* const result = await client.captureLocation(token, {
|
|
1285
1530
|
* latitude: 37.7749,
|
|
1286
1531
|
* longitude: -122.4194,
|
|
1287
1532
|
* accuracy: 10
|
|
1288
1533
|
* });
|
|
1289
|
-
*
|
|
1290
|
-
* // Using WiFi positioning (fallback)
|
|
1291
|
-
* const result = await client.captureLocation(token, {
|
|
1292
|
-
* wifi_networks: [
|
|
1293
|
-
* { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
|
|
1294
|
-
* { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
|
|
1295
|
-
* ]
|
|
1296
|
-
* });
|
|
1297
1534
|
* ```
|
|
1298
1535
|
*/
|
|
1299
1536
|
async captureLocation(token, capture, requestOptions) {
|
|
@@ -2707,7 +2944,13 @@ var KycClient = class extends BaseClient {
|
|
|
2707
2944
|
*
|
|
2708
2945
|
* Generates a link that the user can visit to submit their KYC documents.
|
|
2709
2946
|
*
|
|
2710
|
-
*
|
|
2947
|
+
* Optionally pass the customer's registered identity data as
|
|
2948
|
+
* `customer_data` (same shape as the geolocation `customer_data` block).
|
|
2949
|
+
* It seeds the customer's risk profile so document verification can
|
|
2950
|
+
* cross-check the submitted document against trusted reference data;
|
|
2951
|
+
* fields never overwrite data already on the profile.
|
|
2952
|
+
*
|
|
2953
|
+
* @param request - Request containing the user ID, optional redirect URL, optional callback URL (receives POST requests), and optional customer identity data
|
|
2711
2954
|
* @returns Response containing the redirect link and KYC ID
|
|
2712
2955
|
*
|
|
2713
2956
|
* @example
|
|
@@ -2715,19 +2958,27 @@ var KycClient = class extends BaseClient {
|
|
|
2715
2958
|
* const result = await client.requestKycSubmitLink({
|
|
2716
2959
|
* user_id: "user_123",
|
|
2717
2960
|
* redirect_url: "https://merchant.com/kyc-complete", // optional
|
|
2718
|
-
* callback_url: "https://merchant.com/api/kyc-webhook" // optional - receives POST requests on status change
|
|
2961
|
+
* callback_url: "https://merchant.com/api/kyc-webhook", // optional - receives POST requests on status change
|
|
2962
|
+
* customer_data: { // optional - seeds the risk profile for document cross-checks
|
|
2963
|
+
* full_name: "John Doe",
|
|
2964
|
+
* date_of_birth: "1999-06-02", // ISO 8601
|
|
2965
|
+
* email: "john@example.com",
|
|
2966
|
+
* phone: "+94771234567",
|
|
2967
|
+
* address: "12 Main St, Colombo", // used for address verification
|
|
2968
|
+
* country: "LK"
|
|
2969
|
+
* }
|
|
2719
2970
|
* });
|
|
2720
2971
|
*
|
|
2721
2972
|
* console.log(`Redirect user to: ${result.link}`);
|
|
2722
2973
|
* console.log(`KYC ID: ${result.kyc_id}`);
|
|
2723
2974
|
* ```
|
|
2724
2975
|
*/
|
|
2725
|
-
async requestKycSubmitLink(request) {
|
|
2976
|
+
async requestKycSubmitLink(request, requestOptions) {
|
|
2726
2977
|
return this.requestWithRetry("/api/v1/kyc/request", {
|
|
2727
2978
|
method: "POST",
|
|
2728
2979
|
body: JSON.stringify(request),
|
|
2729
2980
|
headers: this.getUserHeaders()
|
|
2730
|
-
});
|
|
2981
|
+
}, void 0, void 0, requestOptions);
|
|
2731
2982
|
}
|
|
2732
2983
|
/**
|
|
2733
2984
|
* Create a Event-Based Face Verification session.
|
|
@@ -2760,12 +3011,12 @@ var KycClient = class extends BaseClient {
|
|
|
2760
3011
|
*
|
|
2761
3012
|
* @param request - Token from `createEventBasedFaceVerificationSession`, plus the base64 selfie.
|
|
2762
3013
|
*/
|
|
2763
|
-
async submitEventBasedFaceVerificationSession(request) {
|
|
3014
|
+
async submitEventBasedFaceVerificationSession(request, requestOptions) {
|
|
2764
3015
|
return this.request("/api/v1/kyc/face/submit", {
|
|
2765
3016
|
method: "POST",
|
|
2766
3017
|
body: JSON.stringify(request),
|
|
2767
3018
|
headers: this.getUserHeaders()
|
|
2768
|
-
});
|
|
3019
|
+
}, void 0, requestOptions);
|
|
2769
3020
|
}
|
|
2770
3021
|
/**
|
|
2771
3022
|
* Look up the current state of a Event-Based Face Verification session by its
|
|
@@ -2881,12 +3132,12 @@ var KycClient = class extends BaseClient {
|
|
|
2881
3132
|
* console.log(`Verification submitted: ${result.reference}`);
|
|
2882
3133
|
* ```
|
|
2883
3134
|
*/
|
|
2884
|
-
async submitVerification(request) {
|
|
3135
|
+
async submitVerification(request, requestOptions) {
|
|
2885
3136
|
return this.requestWithRetry("/api/v1/kyc/submit", {
|
|
2886
3137
|
method: "POST",
|
|
2887
3138
|
body: JSON.stringify(request),
|
|
2888
3139
|
headers: this.getUserHeaders()
|
|
2889
|
-
});
|
|
3140
|
+
}, void 0, void 0, requestOptions);
|
|
2890
3141
|
}
|
|
2891
3142
|
/**
|
|
2892
3143
|
* Get a KYC request by ID
|
|
@@ -3326,10 +3577,13 @@ var TaxClient = class extends BaseClient {
|
|
|
3326
3577
|
{ method: "POST" }
|
|
3327
3578
|
);
|
|
3328
3579
|
}
|
|
3329
|
-
async checkTINStatus(customerID) {
|
|
3580
|
+
async checkTINStatus(customerID, requestOptions) {
|
|
3330
3581
|
return this.requestWithRetry(
|
|
3331
3582
|
`/api/v1/tax/customer-tax-profiles/${customerID}/check-tin`,
|
|
3332
|
-
{ method: "POST" }
|
|
3583
|
+
{ method: "POST" },
|
|
3584
|
+
void 0,
|
|
3585
|
+
void 0,
|
|
3586
|
+
requestOptions
|
|
3333
3587
|
);
|
|
3334
3588
|
}
|
|
3335
3589
|
async reRequestTaxForm(customerID, input) {
|
|
@@ -3410,14 +3664,24 @@ var TaxClient = class extends BaseClient {
|
|
|
3410
3664
|
};
|
|
3411
3665
|
|
|
3412
3666
|
// src/webhooks/handler.ts
|
|
3667
|
+
function resolveEventId(event) {
|
|
3668
|
+
if (event.id) {
|
|
3669
|
+
return event.id;
|
|
3670
|
+
}
|
|
3671
|
+
if (event.event_type === "notification.created") {
|
|
3672
|
+
return event.notification?.id;
|
|
3673
|
+
}
|
|
3674
|
+
return void 0;
|
|
3675
|
+
}
|
|
3413
3676
|
var WebhookHandler = class {
|
|
3414
3677
|
constructor(config) {
|
|
3415
3678
|
this.handlers = /* @__PURE__ */ new Map();
|
|
3416
3679
|
this.anyHandlers = [];
|
|
3417
|
-
this.seenEventIds = /* @__PURE__ */ new Map();
|
|
3418
3680
|
this.secret = config.secret;
|
|
3419
3681
|
this.tolerance = config.tolerance ?? 3e5;
|
|
3420
3682
|
this.replayProtection = config.replayProtection ?? true;
|
|
3683
|
+
this.allowLegacySignature = config.allowLegacySignature ?? true;
|
|
3684
|
+
this.dedupStore = config.dedupStore ?? new InMemoryDedupStore();
|
|
3421
3685
|
}
|
|
3422
3686
|
/**
|
|
3423
3687
|
* Register a handler for a specific event type.
|
|
@@ -3439,7 +3703,10 @@ var WebhookHandler = class {
|
|
|
3439
3703
|
* Verify signature and parse the webhook body.
|
|
3440
3704
|
*/
|
|
3441
3705
|
async verifyAndParse(body, signature) {
|
|
3442
|
-
const isValid = await verifyWebhookSignature(body, signature, this.secret
|
|
3706
|
+
const isValid = await verifyWebhookSignature(body, signature, this.secret, {
|
|
3707
|
+
tolerance: this.tolerance,
|
|
3708
|
+
allowLegacy: this.allowLegacySignature
|
|
3709
|
+
});
|
|
3443
3710
|
if (!isValid) {
|
|
3444
3711
|
throw new ValidationError("Invalid webhook signature", ["signature"]);
|
|
3445
3712
|
}
|
|
@@ -3455,18 +3722,21 @@ var WebhookHandler = class {
|
|
|
3455
3722
|
/**
|
|
3456
3723
|
* Parse an event without signature verification (for testing).
|
|
3457
3724
|
*/
|
|
3458
|
-
parseEvent(body) {
|
|
3725
|
+
async parseEvent(body) {
|
|
3459
3726
|
return this.parseAndValidate(body);
|
|
3460
3727
|
}
|
|
3461
|
-
parseAndValidate(body) {
|
|
3728
|
+
async parseAndValidate(body) {
|
|
3462
3729
|
const event = JSON.parse(body);
|
|
3463
|
-
|
|
3464
|
-
|
|
3730
|
+
const eventId = resolveEventId(event);
|
|
3731
|
+
if (!event.event_type || !eventId || !event.timestamp) {
|
|
3732
|
+
throw new ValidationError(
|
|
3733
|
+
"Invalid webhook event: missing required fields (event_type, id, timestamp)",
|
|
3734
|
+
["event_type", "id", "timestamp"]
|
|
3735
|
+
);
|
|
3465
3736
|
}
|
|
3466
3737
|
if (this.tolerance > 0) {
|
|
3467
3738
|
const eventTime = new Date(event.timestamp).getTime();
|
|
3468
|
-
|
|
3469
|
-
if (Math.abs(now - eventTime) > this.tolerance) {
|
|
3739
|
+
if (Math.abs(Date.now() - eventTime) > this.tolerance) {
|
|
3470
3740
|
throw new ValidationError(
|
|
3471
3741
|
`Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`,
|
|
3472
3742
|
["timestamp"]
|
|
@@ -3474,26 +3744,18 @@ var WebhookHandler = class {
|
|
|
3474
3744
|
}
|
|
3475
3745
|
}
|
|
3476
3746
|
if (this.replayProtection) {
|
|
3477
|
-
if (this.
|
|
3747
|
+
if (await this.dedupStore.seen(eventId)) {
|
|
3478
3748
|
throw new ValidationError(
|
|
3479
|
-
`Duplicate webhook event: ${
|
|
3749
|
+
`Duplicate webhook event: ${eventId} has already been processed`,
|
|
3480
3750
|
["id"]
|
|
3481
3751
|
);
|
|
3482
3752
|
}
|
|
3483
|
-
|
|
3484
|
-
this.seenEventIds.set(event.id, now);
|
|
3485
|
-
if (this.seenEventIds.size > 1e3) {
|
|
3486
|
-
for (const [id, seenAt] of this.seenEventIds) {
|
|
3487
|
-
if (now - seenAt > this.tolerance) {
|
|
3488
|
-
this.seenEventIds.delete(id);
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
3491
|
-
}
|
|
3753
|
+
await this.dedupStore.mark(eventId, this.tolerance);
|
|
3492
3754
|
}
|
|
3493
3755
|
return event;
|
|
3494
3756
|
}
|
|
3495
3757
|
async dispatch(event) {
|
|
3496
|
-
const typeHandlers = this.handlers.get(event.
|
|
3758
|
+
const typeHandlers = this.handlers.get(event.event_type) || [];
|
|
3497
3759
|
const allHandlers = [...typeHandlers, ...this.anyHandlers];
|
|
3498
3760
|
for (const handler of allHandlers) {
|
|
3499
3761
|
await handler(event);
|
|
@@ -3574,6 +3836,6 @@ function buildHandler(options) {
|
|
|
3574
3836
|
return handler;
|
|
3575
3837
|
}
|
|
3576
3838
|
|
|
3577
|
-
export { AuthenticationError, BaseClient, CGSError, CircuitBreaker, CircuitBreakerOpenError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, GeolocationClient, KYC_DECLINED_DESCRIPTIONS, KycClient, NetworkError, RateLimitError, RateLimitTracker, RiskProfileClient, SDK_VERSION, ServiceUnavailableError, TaxClient, TimeoutError, ValidationError, VesantError, WebhookHandler, createConsoleLogger, createNextWebhookHandler, createWebhookMiddleware, decodeCipherText, generateCipherText, isCipherTextExpired, noopLogger, sdkReasons, verifyWebhookSignature };
|
|
3839
|
+
export { AuthenticationError, BaseClient, CGSError, CircuitBreaker, CircuitBreakerOpenError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, GeolocationClient, InMemoryDedupStore, KYC_DECLINED_DESCRIPTIONS, KycClient, NetworkError, RateLimitError, RateLimitTracker, RefreshingTokenProvider, RiskProfileClient, SDK_VERSION, ServiceUnavailableError, StaticApiKeyProvider, TaxClient, TimeoutError, ValidationError, VesantError, WebhookHandler, assessGpsIntegrity, collectAll, createConsoleLogger, createNextWebhookHandler, createWebhookMiddleware, decodeCipherText, generateCipherText, gpsIntegrityInputFromPosition, isCipherTextExpired, noopLogger, paginate, probeGeolocationPermission, sdkReasons, verifyWebhookSignature };
|
|
3578
3840
|
//# sourceMappingURL=index.mjs.map
|
|
3579
3841
|
//# sourceMappingURL=index.mjs.map
|