vesant-sdk 1.7.0 → 1.7.1-dev.301c6df
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-DtH2RLuy.d.ts → client-B3DCAHlg.d.ts} +2 -2
- package/dist/{client-DrjgZoH_.d.mts → client-BwXMQA2a.d.mts} +64 -42
- package/dist/{client-B0qhE2kr.d.mts → client-D23KhWeh.d.mts} +2 -2
- package/dist/{client-DF7hlMEz.d.ts → client-fy3trF2K.d.ts} +64 -42
- 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 +181 -91
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +181 -91
- 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 +69 -8
- package/dist/decisions/index.js.map +1 -1
- package/dist/decisions/index.mjs +69 -8
- package/dist/decisions/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +68 -7
- package/dist/geolocation/index.d.ts +68 -7
- package/dist/geolocation/index.js +186 -91
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +183 -92
- 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 +354 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +346 -121
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +1 -1
- package/dist/kyc/core.d.ts +1 -1
- package/dist/kyc/core.js +75 -14
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +75 -14
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +4 -4
- package/dist/kyc/index.d.ts +4 -4
- package/dist/kyc/index.js +75 -14
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +75 -14
- 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 +70 -33
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +70 -33
- 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 +69 -8
- package/dist/risk-profile/index.js.map +1 -1
- package/dist/risk-profile/index.mjs +69 -8
- 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 +69 -8
- package/dist/scores/index.js.map +1 -1
- package/dist/scores/index.mjs +69 -8
- 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 +74 -10
- package/dist/tax/index.js.map +1 -1
- package/dist/tax/index.mjs +74 -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
package/dist/index.js
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/core/auth.ts
|
|
4
|
+
var StaticApiKeyProvider = class {
|
|
5
|
+
constructor(apiKey) {
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
}
|
|
8
|
+
async getCredential() {
|
|
9
|
+
return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
|
|
10
|
+
}
|
|
11
|
+
canRefresh() {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var RefreshingTokenProvider = class {
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
this.cached = null;
|
|
19
|
+
this.inFlight = null;
|
|
20
|
+
this.skewMs = opts.skewMs ?? 3e4;
|
|
21
|
+
this.scheme = opts.scheme ?? "Bearer";
|
|
22
|
+
this.now = opts.now ?? (() => Date.now());
|
|
23
|
+
}
|
|
24
|
+
canRefresh() {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
async getCredential(ctx) {
|
|
28
|
+
const forceRefresh = ctx?.forceRefresh ?? false;
|
|
29
|
+
if (!forceRefresh && this.cached && !this.isExpiring(this.cached)) {
|
|
30
|
+
return { scheme: this.scheme, token: this.cached.token };
|
|
31
|
+
}
|
|
32
|
+
const fresh = await this.refresh();
|
|
33
|
+
return { scheme: this.scheme, token: fresh.token };
|
|
34
|
+
}
|
|
35
|
+
isExpiring(token) {
|
|
36
|
+
if (token.expiresAt === void 0) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return this.now() >= token.expiresAt - this.skewMs;
|
|
40
|
+
}
|
|
41
|
+
refresh() {
|
|
42
|
+
if (this.inFlight) {
|
|
43
|
+
return this.inFlight;
|
|
44
|
+
}
|
|
45
|
+
this.inFlight = (async () => {
|
|
46
|
+
try {
|
|
47
|
+
const fresh = await this.opts.fetchToken();
|
|
48
|
+
this.cached = fresh;
|
|
49
|
+
this.opts.onRotate?.({ expiresAt: fresh.expiresAt });
|
|
50
|
+
return fresh;
|
|
51
|
+
} finally {
|
|
52
|
+
this.inFlight = null;
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
return this.inFlight;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
3
59
|
// src/core/errors.ts
|
|
4
60
|
var VesantError = class _VesantError extends Error {
|
|
5
61
|
constructor(message, code, statusCode, details) {
|
|
@@ -242,7 +298,7 @@ var noopLogger = {
|
|
|
242
298
|
};
|
|
243
299
|
|
|
244
300
|
// src/core/version.ts
|
|
245
|
-
var SDK_VERSION = "1.7.
|
|
301
|
+
var SDK_VERSION = "1.7.1";
|
|
246
302
|
|
|
247
303
|
// src/shared/browser-utils.ts
|
|
248
304
|
function generateUUID() {
|
|
@@ -324,6 +380,12 @@ var BaseClient = class {
|
|
|
324
380
|
if (!config.tenantId?.trim()) {
|
|
325
381
|
throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
|
|
326
382
|
}
|
|
383
|
+
if (typeof fetch === "undefined") {
|
|
384
|
+
throw new VesantError(
|
|
385
|
+
"The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
|
|
386
|
+
"RUNTIME_UNSUPPORTED"
|
|
387
|
+
);
|
|
388
|
+
}
|
|
327
389
|
this.interceptors = config.interceptors || [];
|
|
328
390
|
this.logger = config.logger || createConsoleLogger();
|
|
329
391
|
let environment = config.environment;
|
|
@@ -342,11 +404,12 @@ var BaseClient = class {
|
|
|
342
404
|
environment,
|
|
343
405
|
headers: config.headers || {},
|
|
344
406
|
timeout: config.timeout || 1e4,
|
|
345
|
-
retries: config.retries
|
|
407
|
+
retries: config.retries ?? 3,
|
|
346
408
|
debug: config.debug || false,
|
|
347
409
|
interceptors: this.interceptors,
|
|
348
410
|
logger: this.logger
|
|
349
411
|
};
|
|
412
|
+
this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
|
|
350
413
|
if (config.circuitBreaker) {
|
|
351
414
|
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
|
|
352
415
|
}
|
|
@@ -354,6 +417,19 @@ var BaseClient = class {
|
|
|
354
417
|
this.rateLimitTracker = new RateLimitTracker();
|
|
355
418
|
}
|
|
356
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
|
|
422
|
+
* get the caller-supplied key or a freshly minted UUID; non-mutating methods get
|
|
423
|
+
* none. requestWithRetry resolves this once and injects it so every retry reuses
|
|
424
|
+
* one key (letting the server dedup replays); a direct request() resolves per call.
|
|
425
|
+
*/
|
|
426
|
+
resolveIdempotencyKey(method, requestOptions) {
|
|
427
|
+
const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
|
|
428
|
+
if (!isMutating) {
|
|
429
|
+
return void 0;
|
|
430
|
+
}
|
|
431
|
+
return requestOptions?.idempotencyKey ?? generateUUID();
|
|
432
|
+
}
|
|
357
433
|
/**
|
|
358
434
|
* Make an HTTP request with timeout and error handling
|
|
359
435
|
*/
|
|
@@ -379,15 +455,20 @@ var BaseClient = class {
|
|
|
379
455
|
...this.config.headers,
|
|
380
456
|
...options.headers || {}
|
|
381
457
|
};
|
|
382
|
-
|
|
383
|
-
|
|
458
|
+
const credential = await this.authProvider?.getCredential({
|
|
459
|
+
tenantId: this.config.tenantId,
|
|
460
|
+
requestId
|
|
461
|
+
});
|
|
462
|
+
if (credential) {
|
|
463
|
+
headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
|
|
384
464
|
}
|
|
385
465
|
if (this.config.environment === "sandbox") {
|
|
386
466
|
headers["X-Sandbox"] = "true";
|
|
387
467
|
}
|
|
388
468
|
const method = (options.method || "GET").toUpperCase();
|
|
389
|
-
|
|
390
|
-
|
|
469
|
+
const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
|
|
470
|
+
if (idempotencyKey) {
|
|
471
|
+
headers["Idempotency-Key"] = idempotencyKey;
|
|
391
472
|
}
|
|
392
473
|
const controller = new AbortController();
|
|
393
474
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
@@ -408,10 +489,28 @@ var BaseClient = class {
|
|
|
408
489
|
if (this.config.debug) {
|
|
409
490
|
this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
|
|
410
491
|
}
|
|
411
|
-
|
|
492
|
+
let response = await fetch(url, {
|
|
412
493
|
...finalOptions,
|
|
413
494
|
signal: controller.signal
|
|
414
495
|
});
|
|
496
|
+
if (response.status === 401 && this.authProvider?.canRefresh?.()) {
|
|
497
|
+
const refreshed = await this.authProvider.getCredential({
|
|
498
|
+
tenantId: this.config.tenantId,
|
|
499
|
+
requestId,
|
|
500
|
+
forceRefresh: true
|
|
501
|
+
});
|
|
502
|
+
if (refreshed) {
|
|
503
|
+
this.config.onCredentialRotated?.({ reason: "unauthorized" });
|
|
504
|
+
finalOptions = {
|
|
505
|
+
...finalOptions,
|
|
506
|
+
headers: {
|
|
507
|
+
...finalOptions.headers,
|
|
508
|
+
Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
response = await fetch(url, { ...finalOptions, signal: controller.signal });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
415
514
|
clearTimeout(timeoutId);
|
|
416
515
|
if (this.rateLimitTracker) {
|
|
417
516
|
this.rateLimitTracker.updateFromHeaders(response.headers);
|
|
@@ -500,9 +599,11 @@ var BaseClient = class {
|
|
|
500
599
|
*/
|
|
501
600
|
async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
|
|
502
601
|
let lastError;
|
|
602
|
+
const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
|
|
603
|
+
const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
|
|
503
604
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
504
605
|
try {
|
|
505
|
-
return await this.request(endpoint, options, serviceURL,
|
|
606
|
+
return await this.request(endpoint, options, serviceURL, attemptOptions);
|
|
506
607
|
} catch (error) {
|
|
507
608
|
lastError = error instanceof Error ? error : new Error("Unknown error");
|
|
508
609
|
if (requestOptions?.signal?.aborted) {
|
|
@@ -599,6 +700,9 @@ var BaseClient = class {
|
|
|
599
700
|
if (config.interceptors) {
|
|
600
701
|
this.interceptors = config.interceptors;
|
|
601
702
|
}
|
|
703
|
+
if (config.authProvider !== void 0 || config.apiKey !== void 0) {
|
|
704
|
+
this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
|
|
705
|
+
}
|
|
602
706
|
}
|
|
603
707
|
/**
|
|
604
708
|
* Get current configuration (readonly)
|
|
@@ -626,8 +730,69 @@ var BaseClient = class {
|
|
|
626
730
|
}
|
|
627
731
|
};
|
|
628
732
|
|
|
733
|
+
// src/core/pagination.ts
|
|
734
|
+
async function* paginate(fetchPage) {
|
|
735
|
+
let page = 1;
|
|
736
|
+
let totalPages;
|
|
737
|
+
do {
|
|
738
|
+
const response = await fetchPage(page);
|
|
739
|
+
const items = response.data ?? [];
|
|
740
|
+
for (const item of items) {
|
|
741
|
+
yield item;
|
|
742
|
+
}
|
|
743
|
+
totalPages = response.total_pages ?? page;
|
|
744
|
+
if (items.length === 0) {
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
page += 1;
|
|
748
|
+
} while (page <= totalPages);
|
|
749
|
+
}
|
|
750
|
+
async function collectAll(fetchPage) {
|
|
751
|
+
const items = [];
|
|
752
|
+
for await (const item of paginate(fetchPage)) {
|
|
753
|
+
items.push(item);
|
|
754
|
+
}
|
|
755
|
+
return items;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/core/dedup.ts
|
|
759
|
+
var InMemoryDedupStore = class {
|
|
760
|
+
constructor(opts = {}) {
|
|
761
|
+
this.expiryById = /* @__PURE__ */ new Map();
|
|
762
|
+
this.maxEntries = opts.maxEntries ?? 1e3;
|
|
763
|
+
this.now = opts.now ?? (() => Date.now());
|
|
764
|
+
}
|
|
765
|
+
async seen(id) {
|
|
766
|
+
return this.expiryById.has(id);
|
|
767
|
+
}
|
|
768
|
+
async mark(id, ttlMs) {
|
|
769
|
+
const now = this.now();
|
|
770
|
+
this.expiryById.set(id, now + ttlMs);
|
|
771
|
+
if (this.expiryById.size > this.maxEntries) {
|
|
772
|
+
for (const [key, expiresAt] of this.expiryById) {
|
|
773
|
+
if (now > expiresAt) {
|
|
774
|
+
this.expiryById.delete(key);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
|
|
629
781
|
// src/core/webhook-utils.ts
|
|
630
|
-
async function verifyWebhookSignature(payload, signature, secret) {
|
|
782
|
+
async function verifyWebhookSignature(payload, signature, secret, opts = {}) {
|
|
783
|
+
const signed = parseSignedTimestamp(signature);
|
|
784
|
+
if (signed) {
|
|
785
|
+
const tolerance = opts.tolerance ?? 3e5;
|
|
786
|
+
const now = opts.now?.() ?? Date.now();
|
|
787
|
+
if (tolerance > 0 && Math.abs(now - signed.t * 1e3) > tolerance) {
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
const expected = await computeHmacSha256(`${signed.t}.${payload}`, secret);
|
|
791
|
+
return constantTimeEqual(signed.v1, expected);
|
|
792
|
+
}
|
|
793
|
+
if (opts.allowLegacy === false) {
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
631
796
|
const hexDigest = await computeHmacSha256(payload, secret);
|
|
632
797
|
const expectedPrefixed = `sha256=${hexDigest}`;
|
|
633
798
|
if (signature.startsWith("sha256=")) {
|
|
@@ -635,6 +800,25 @@ async function verifyWebhookSignature(payload, signature, secret) {
|
|
|
635
800
|
}
|
|
636
801
|
return constantTimeEqual(signature, hexDigest);
|
|
637
802
|
}
|
|
803
|
+
function parseSignedTimestamp(signature) {
|
|
804
|
+
if (!signature.includes("t=") || !signature.includes("v1=")) {
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
let t;
|
|
808
|
+
let v1;
|
|
809
|
+
for (const part of signature.split(",")) {
|
|
810
|
+
const eq = part.indexOf("=");
|
|
811
|
+
if (eq === -1) continue;
|
|
812
|
+
const key = part.slice(0, eq).trim();
|
|
813
|
+
const value = part.slice(eq + 1).trim();
|
|
814
|
+
if (key === "t") t = Number(value);
|
|
815
|
+
else if (key === "v1") v1 = value;
|
|
816
|
+
}
|
|
817
|
+
if (t === void 0 || Number.isNaN(t) || !v1) {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
return { t, v1 };
|
|
821
|
+
}
|
|
638
822
|
async function computeHmacSha256(message, secret) {
|
|
639
823
|
if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
|
|
640
824
|
const encoder = new TextEncoder();
|
|
@@ -669,27 +853,63 @@ function constantTimeEqual(a, b) {
|
|
|
669
853
|
return result === 0;
|
|
670
854
|
}
|
|
671
855
|
|
|
672
|
-
// src/geolocation/
|
|
673
|
-
var
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
);
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
856
|
+
// src/geolocation/integrity.ts
|
|
857
|
+
var isNil = (v) => v === null || v === void 0;
|
|
858
|
+
function assessGpsIntegrity(input, opts = {}) {
|
|
859
|
+
const flags = [];
|
|
860
|
+
const now = opts.now ?? (() => Date.now());
|
|
861
|
+
const maxAgeMs = opts.maxAgeMs ?? 6e4;
|
|
862
|
+
const minAccuracy = opts.minAccuracyMeters ?? 1;
|
|
863
|
+
const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
|
|
864
|
+
if (isNil(accuracy)) {
|
|
865
|
+
flags.push("accuracy_missing");
|
|
866
|
+
} else if (accuracy === 0) {
|
|
867
|
+
flags.push("accuracy_zero");
|
|
868
|
+
} else if (accuracy < minAccuracy) {
|
|
869
|
+
flags.push("accuracy_implausibly_small");
|
|
870
|
+
}
|
|
871
|
+
const hasMotion = !isNil(heading) || !isNil(speed);
|
|
872
|
+
if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
|
|
873
|
+
flags.push("motion_without_accuracy");
|
|
874
|
+
}
|
|
875
|
+
if (!isNil(altitude) && isNil(altitudeAccuracy)) {
|
|
876
|
+
flags.push("altitude_without_accuracy");
|
|
877
|
+
}
|
|
878
|
+
if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
|
|
879
|
+
flags.push("stale_timestamp");
|
|
880
|
+
}
|
|
881
|
+
if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
|
|
882
|
+
flags.push(`permission_${opts.permissionState}`);
|
|
883
|
+
}
|
|
884
|
+
return { suspicious: flags.length > 0, flags };
|
|
885
|
+
}
|
|
886
|
+
function gpsIntegrityInputFromPosition(position) {
|
|
887
|
+
const c = position.coords;
|
|
888
|
+
return {
|
|
889
|
+
latitude: c.latitude,
|
|
890
|
+
longitude: c.longitude,
|
|
891
|
+
accuracy: c.accuracy,
|
|
892
|
+
altitude: c.altitude,
|
|
893
|
+
altitudeAccuracy: c.altitudeAccuracy,
|
|
894
|
+
heading: c.heading,
|
|
895
|
+
speed: c.speed,
|
|
896
|
+
timestamp: position.timestamp
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
async function probeGeolocationPermission() {
|
|
900
|
+
try {
|
|
901
|
+
if (typeof navigator === "undefined" || !navigator.permissions?.query) {
|
|
902
|
+
return "unavailable";
|
|
903
|
+
}
|
|
904
|
+
const status = await navigator.permissions.query({ name: "geolocation" });
|
|
905
|
+
return status.state;
|
|
906
|
+
} catch {
|
|
907
|
+
return "unavailable";
|
|
689
908
|
}
|
|
690
|
-
const { createHmac } = await import('crypto');
|
|
691
|
-
return createHmac("sha256", key).update(message).digest("hex");
|
|
692
909
|
}
|
|
910
|
+
|
|
911
|
+
// src/geolocation/ciphertext.ts
|
|
912
|
+
var CIPHER_TEXT_EXPIRY_MINUTES = 5;
|
|
693
913
|
function getWebGLInfo() {
|
|
694
914
|
if (typeof document === "undefined") return null;
|
|
695
915
|
try {
|
|
@@ -721,6 +941,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
721
941
|
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
722
942
|
return null;
|
|
723
943
|
}
|
|
944
|
+
const permissionState = await probeGeolocationPermission();
|
|
724
945
|
return new Promise((resolve) => {
|
|
725
946
|
navigator.geolocation.getCurrentPosition(
|
|
726
947
|
(position) => {
|
|
@@ -737,6 +958,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
737
958
|
resolve(null);
|
|
738
959
|
return;
|
|
739
960
|
}
|
|
961
|
+
const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
|
|
740
962
|
resolve({
|
|
741
963
|
latitude,
|
|
742
964
|
longitude,
|
|
@@ -745,7 +967,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
|
|
|
745
967
|
altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
|
|
746
968
|
heading: position.coords.heading ?? void 0,
|
|
747
969
|
speed: position.coords.speed ?? void 0,
|
|
748
|
-
timestamp: position.timestamp
|
|
970
|
+
timestamp: position.timestamp,
|
|
971
|
+
integrity_flags: flags.length ? flags : void 0
|
|
749
972
|
});
|
|
750
973
|
},
|
|
751
974
|
() => {
|
|
@@ -812,6 +1035,9 @@ async function generateCipherText(options, config) {
|
|
|
812
1035
|
);
|
|
813
1036
|
if (location) {
|
|
814
1037
|
locationData = location;
|
|
1038
|
+
if (location.integrity_flags?.length) {
|
|
1039
|
+
warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
|
|
1040
|
+
}
|
|
815
1041
|
} else if (gpsRequiredByConfig) {
|
|
816
1042
|
throw new VesantError(
|
|
817
1043
|
`GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
|
|
@@ -841,15 +1067,7 @@ async function generateCipherText(options, config) {
|
|
|
841
1067
|
};
|
|
842
1068
|
const encoded = encodePayload(payload);
|
|
843
1069
|
const timestamp = now.getTime().toString(36);
|
|
844
|
-
|
|
845
|
-
const hmacKey = options.signingKey || options.apiKey;
|
|
846
|
-
if (hmacKey) {
|
|
847
|
-
const message = `02.${timestamp}.${encoded}`;
|
|
848
|
-
const hmac = await computeHMAC(hmacKey, message);
|
|
849
|
-
cipherText = `${message}.${hmac}`;
|
|
850
|
-
} else {
|
|
851
|
-
cipherText = `01.${timestamp}.${encoded}`;
|
|
852
|
-
}
|
|
1070
|
+
const cipherText = `01.${timestamp}.${encoded}`;
|
|
853
1071
|
return {
|
|
854
1072
|
cipherText,
|
|
855
1073
|
locationCaptured: !!locationData,
|
|
@@ -858,6 +1076,7 @@ async function generateCipherText(options, config) {
|
|
|
858
1076
|
expiresAt: expiry.toISOString()
|
|
859
1077
|
};
|
|
860
1078
|
}
|
|
1079
|
+
var collectDeviceSignals = generateCipherText;
|
|
861
1080
|
function decodeCipherText(cipherText) {
|
|
862
1081
|
try {
|
|
863
1082
|
const parts = cipherText.split(".");
|
|
@@ -931,8 +1150,8 @@ var GeolocationClient = class extends BaseClient {
|
|
|
931
1150
|
* ```
|
|
932
1151
|
*/
|
|
933
1152
|
async verifyIP(request, requestOptions) {
|
|
934
|
-
if (!request.ip_address
|
|
935
|
-
throw new ValidationError("ip_address
|
|
1153
|
+
if (request.ip_address !== void 0 && !request.ip_address.trim()) {
|
|
1154
|
+
throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
|
|
936
1155
|
}
|
|
937
1156
|
return this.requestWithRetry("/api/v1/geo/verify", {
|
|
938
1157
|
method: "POST",
|
|
@@ -985,37 +1204,50 @@ var GeolocationClient = class extends BaseClient {
|
|
|
985
1204
|
* ```
|
|
986
1205
|
*/
|
|
987
1206
|
async getGPSConfig(requestOptions) {
|
|
988
|
-
|
|
989
|
-
if (config.signing_key) {
|
|
990
|
-
this.cachedSigningKey = config.signing_key;
|
|
991
|
-
}
|
|
992
|
-
return config;
|
|
1207
|
+
return this.requestWithRetry("/api/v1/geo/config", void 0, void 0, void 0, requestOptions);
|
|
993
1208
|
}
|
|
994
1209
|
/**
|
|
995
|
-
*
|
|
1210
|
+
* Mint a short-lived, single-use device-signals nonce.
|
|
996
1211
|
*
|
|
997
|
-
*
|
|
998
|
-
*
|
|
1212
|
+
* The nonce is bound to the tenant, user, and event type and is consumed
|
|
1213
|
+
* server-side during validation, so a captured device-signals envelope cannot
|
|
1214
|
+
* be replayed. `validateCipherText` mints and attaches one automatically; call
|
|
1215
|
+
* this directly only if you manage the nonce yourself.
|
|
999
1216
|
*
|
|
1000
|
-
* @
|
|
1217
|
+
* @param userId - User ID the nonce is bound to
|
|
1218
|
+
* @param eventType - Event type the nonce is bound to (login, registration, ...)
|
|
1219
|
+
* @returns The nonce and its expiry
|
|
1220
|
+
*/
|
|
1221
|
+
async fetchSignalsNonce(userId, eventType, requestOptions) {
|
|
1222
|
+
if (!userId?.trim()) {
|
|
1223
|
+
throw new ValidationError("userId is required and must be a non-empty string", ["userId"]);
|
|
1224
|
+
}
|
|
1225
|
+
const request = { user_id: userId, event_type: eventType };
|
|
1226
|
+
return this.request(
|
|
1227
|
+
"/api/v1/geo/signals-token",
|
|
1228
|
+
{
|
|
1229
|
+
method: "POST",
|
|
1230
|
+
body: JSON.stringify(request)
|
|
1231
|
+
},
|
|
1232
|
+
void 0,
|
|
1233
|
+
requestOptions
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Best-effort nonce mint for validation. Returns undefined if minting fails —
|
|
1238
|
+
* the server is the sole authority and blocks when a nonce is required, so this
|
|
1239
|
+
* never downgrades security on the client's say-so.
|
|
1001
1240
|
*/
|
|
1002
|
-
async
|
|
1241
|
+
async resolveSignalsNonce(userId, eventType, requestOptions) {
|
|
1003
1242
|
try {
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
requestOptions
|
|
1243
|
+
const { nonce } = await this.fetchSignalsNonce(userId, eventType, requestOptions);
|
|
1244
|
+
return nonce;
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
this.logger.warn(
|
|
1247
|
+
`Failed to mint device-signals nonce; proceeding without it: ${err instanceof Error ? err.message : String(err)}`
|
|
1010
1248
|
);
|
|
1011
|
-
|
|
1012
|
-
this.cachedSigningKey = response.signing_key;
|
|
1013
|
-
return response.signing_key;
|
|
1014
|
-
}
|
|
1015
|
-
} catch {
|
|
1249
|
+
return void 0;
|
|
1016
1250
|
}
|
|
1017
|
-
const config = await this.getGPSConfig(requestOptions);
|
|
1018
|
-
return config.signing_key;
|
|
1019
1251
|
}
|
|
1020
1252
|
// ============================================================================
|
|
1021
1253
|
// CipherText Validation
|
|
@@ -1068,12 +1300,14 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1068
1300
|
if (!userId?.trim()) {
|
|
1069
1301
|
throw new ValidationError("userId is required and must be a non-empty string", ["userId"]);
|
|
1070
1302
|
}
|
|
1303
|
+
const nonce = await this.resolveSignalsNonce(userId, eventType, requestOptions);
|
|
1071
1304
|
const request = {
|
|
1072
1305
|
cipher_text: cipherText,
|
|
1073
1306
|
user_id: userId,
|
|
1074
1307
|
event_type: eventType,
|
|
1075
1308
|
expected_ip: expectedIP,
|
|
1076
|
-
customer_data: customerData
|
|
1309
|
+
customer_data: customerData,
|
|
1310
|
+
nonce
|
|
1077
1311
|
};
|
|
1078
1312
|
return this.requestWithRetry(
|
|
1079
1313
|
"/api/v1/geo/validate-ciphertext",
|
|
@@ -1282,20 +1516,12 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1282
1516
|
*
|
|
1283
1517
|
* @example
|
|
1284
1518
|
* ```typescript
|
|
1285
|
-
* //
|
|
1519
|
+
* // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
|
|
1286
1520
|
* const result = await client.captureLocation(token, {
|
|
1287
1521
|
* latitude: 37.7749,
|
|
1288
1522
|
* longitude: -122.4194,
|
|
1289
1523
|
* accuracy: 10
|
|
1290
1524
|
* });
|
|
1291
|
-
*
|
|
1292
|
-
* // Using WiFi positioning (fallback)
|
|
1293
|
-
* const result = await client.captureLocation(token, {
|
|
1294
|
-
* wifi_networks: [
|
|
1295
|
-
* { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
|
|
1296
|
-
* { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
|
|
1297
|
-
* ]
|
|
1298
|
-
* });
|
|
1299
1525
|
* ```
|
|
1300
1526
|
*/
|
|
1301
1527
|
async captureLocation(token, capture, requestOptions) {
|
|
@@ -1308,34 +1534,22 @@ var GeolocationClient = class extends BaseClient {
|
|
|
1308
1534
|
// CipherText Generation
|
|
1309
1535
|
// ============================================================================
|
|
1310
1536
|
/**
|
|
1311
|
-
* Generate a
|
|
1537
|
+
* Generate a device-signals envelope containing device and location data,
|
|
1538
|
+
* validated by the server.
|
|
1312
1539
|
*
|
|
1313
|
-
*
|
|
1314
|
-
* If no API key is configured, falls back to unsigned v01 format.
|
|
1315
|
-
*
|
|
1316
|
-
* @param options - Options for cipherText generation
|
|
1540
|
+
* @param options - Options for envelope generation
|
|
1317
1541
|
* @param gpsConfig - Optional GPS config (from getGPSConfig)
|
|
1318
|
-
* @returns
|
|
1542
|
+
* @returns The device-signals envelope result
|
|
1319
1543
|
*
|
|
1320
1544
|
* @example
|
|
1321
1545
|
* ```typescript
|
|
1322
1546
|
* const result = await client.generateCipherText({ reason: 'login' });
|
|
1323
|
-
* console.log(result.cipherText); //
|
|
1547
|
+
* console.log(result.cipherText); // device-signals envelope
|
|
1324
1548
|
* ```
|
|
1325
1549
|
*/
|
|
1326
1550
|
async generateCipherText(options, gpsConfig) {
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
if (!signingKey) {
|
|
1330
|
-
signingKey = await this.fetchSigningKey();
|
|
1331
|
-
}
|
|
1332
|
-
if (!resolvedGpsConfig) {
|
|
1333
|
-
resolvedGpsConfig = await this.getGPSConfig();
|
|
1334
|
-
}
|
|
1335
|
-
return generateCipherText(
|
|
1336
|
-
{ ...options, signingKey: signingKey || void 0 },
|
|
1337
|
-
resolvedGpsConfig
|
|
1338
|
-
);
|
|
1551
|
+
const resolvedGpsConfig = gpsConfig ?? await this.getGPSConfig();
|
|
1552
|
+
return generateCipherText(options, resolvedGpsConfig);
|
|
1339
1553
|
}
|
|
1340
1554
|
// ============================================================================
|
|
1341
1555
|
// Utility Methods (inherited from BaseClient: healthCheck, updateConfig, getConfig, buildQueryString)
|
|
@@ -2738,12 +2952,12 @@ var KycClient = class extends BaseClient {
|
|
|
2738
2952
|
* console.log(`KYC ID: ${result.kyc_id}`);
|
|
2739
2953
|
* ```
|
|
2740
2954
|
*/
|
|
2741
|
-
async requestKycSubmitLink(request) {
|
|
2955
|
+
async requestKycSubmitLink(request, requestOptions) {
|
|
2742
2956
|
return this.requestWithRetry("/api/v1/kyc/request", {
|
|
2743
2957
|
method: "POST",
|
|
2744
2958
|
body: JSON.stringify(request),
|
|
2745
2959
|
headers: this.getUserHeaders()
|
|
2746
|
-
});
|
|
2960
|
+
}, void 0, void 0, requestOptions);
|
|
2747
2961
|
}
|
|
2748
2962
|
/**
|
|
2749
2963
|
* Create a Event-Based Face Verification session.
|
|
@@ -2776,12 +2990,12 @@ var KycClient = class extends BaseClient {
|
|
|
2776
2990
|
*
|
|
2777
2991
|
* @param request - Token from `createEventBasedFaceVerificationSession`, plus the base64 selfie.
|
|
2778
2992
|
*/
|
|
2779
|
-
async submitEventBasedFaceVerificationSession(request) {
|
|
2993
|
+
async submitEventBasedFaceVerificationSession(request, requestOptions) {
|
|
2780
2994
|
return this.request("/api/v1/kyc/face/submit", {
|
|
2781
2995
|
method: "POST",
|
|
2782
2996
|
body: JSON.stringify(request),
|
|
2783
2997
|
headers: this.getUserHeaders()
|
|
2784
|
-
});
|
|
2998
|
+
}, void 0, requestOptions);
|
|
2785
2999
|
}
|
|
2786
3000
|
/**
|
|
2787
3001
|
* Look up the current state of a Event-Based Face Verification session by its
|
|
@@ -2897,12 +3111,12 @@ var KycClient = class extends BaseClient {
|
|
|
2897
3111
|
* console.log(`Verification submitted: ${result.reference}`);
|
|
2898
3112
|
* ```
|
|
2899
3113
|
*/
|
|
2900
|
-
async submitVerification(request) {
|
|
3114
|
+
async submitVerification(request, requestOptions) {
|
|
2901
3115
|
return this.requestWithRetry("/api/v1/kyc/submit", {
|
|
2902
3116
|
method: "POST",
|
|
2903
3117
|
body: JSON.stringify(request),
|
|
2904
3118
|
headers: this.getUserHeaders()
|
|
2905
|
-
});
|
|
3119
|
+
}, void 0, void 0, requestOptions);
|
|
2906
3120
|
}
|
|
2907
3121
|
/**
|
|
2908
3122
|
* Get a KYC request by ID
|
|
@@ -3342,10 +3556,13 @@ var TaxClient = class extends BaseClient {
|
|
|
3342
3556
|
{ method: "POST" }
|
|
3343
3557
|
);
|
|
3344
3558
|
}
|
|
3345
|
-
async checkTINStatus(customerID) {
|
|
3559
|
+
async checkTINStatus(customerID, requestOptions) {
|
|
3346
3560
|
return this.requestWithRetry(
|
|
3347
3561
|
`/api/v1/tax/customer-tax-profiles/${customerID}/check-tin`,
|
|
3348
|
-
{ method: "POST" }
|
|
3562
|
+
{ method: "POST" },
|
|
3563
|
+
void 0,
|
|
3564
|
+
void 0,
|
|
3565
|
+
requestOptions
|
|
3349
3566
|
);
|
|
3350
3567
|
}
|
|
3351
3568
|
async reRequestTaxForm(customerID, input) {
|
|
@@ -3426,14 +3643,24 @@ var TaxClient = class extends BaseClient {
|
|
|
3426
3643
|
};
|
|
3427
3644
|
|
|
3428
3645
|
// src/webhooks/handler.ts
|
|
3646
|
+
function resolveEventId(event) {
|
|
3647
|
+
if (event.id) {
|
|
3648
|
+
return event.id;
|
|
3649
|
+
}
|
|
3650
|
+
if (event.event_type === "notification.created") {
|
|
3651
|
+
return event.notification?.id;
|
|
3652
|
+
}
|
|
3653
|
+
return void 0;
|
|
3654
|
+
}
|
|
3429
3655
|
var WebhookHandler = class {
|
|
3430
3656
|
constructor(config) {
|
|
3431
3657
|
this.handlers = /* @__PURE__ */ new Map();
|
|
3432
3658
|
this.anyHandlers = [];
|
|
3433
|
-
this.seenEventIds = /* @__PURE__ */ new Map();
|
|
3434
3659
|
this.secret = config.secret;
|
|
3435
3660
|
this.tolerance = config.tolerance ?? 3e5;
|
|
3436
3661
|
this.replayProtection = config.replayProtection ?? true;
|
|
3662
|
+
this.allowLegacySignature = config.allowLegacySignature ?? true;
|
|
3663
|
+
this.dedupStore = config.dedupStore ?? new InMemoryDedupStore();
|
|
3437
3664
|
}
|
|
3438
3665
|
/**
|
|
3439
3666
|
* Register a handler for a specific event type.
|
|
@@ -3455,7 +3682,10 @@ var WebhookHandler = class {
|
|
|
3455
3682
|
* Verify signature and parse the webhook body.
|
|
3456
3683
|
*/
|
|
3457
3684
|
async verifyAndParse(body, signature) {
|
|
3458
|
-
const isValid = await verifyWebhookSignature(body, signature, this.secret
|
|
3685
|
+
const isValid = await verifyWebhookSignature(body, signature, this.secret, {
|
|
3686
|
+
tolerance: this.tolerance,
|
|
3687
|
+
allowLegacy: this.allowLegacySignature
|
|
3688
|
+
});
|
|
3459
3689
|
if (!isValid) {
|
|
3460
3690
|
throw new ValidationError("Invalid webhook signature", ["signature"]);
|
|
3461
3691
|
}
|
|
@@ -3471,18 +3701,21 @@ var WebhookHandler = class {
|
|
|
3471
3701
|
/**
|
|
3472
3702
|
* Parse an event without signature verification (for testing).
|
|
3473
3703
|
*/
|
|
3474
|
-
parseEvent(body) {
|
|
3704
|
+
async parseEvent(body) {
|
|
3475
3705
|
return this.parseAndValidate(body);
|
|
3476
3706
|
}
|
|
3477
|
-
parseAndValidate(body) {
|
|
3707
|
+
async parseAndValidate(body) {
|
|
3478
3708
|
const event = JSON.parse(body);
|
|
3479
|
-
|
|
3480
|
-
|
|
3709
|
+
const eventId = resolveEventId(event);
|
|
3710
|
+
if (!event.event_type || !eventId || !event.timestamp) {
|
|
3711
|
+
throw new ValidationError(
|
|
3712
|
+
"Invalid webhook event: missing required fields (event_type, id, timestamp)",
|
|
3713
|
+
["event_type", "id", "timestamp"]
|
|
3714
|
+
);
|
|
3481
3715
|
}
|
|
3482
3716
|
if (this.tolerance > 0) {
|
|
3483
3717
|
const eventTime = new Date(event.timestamp).getTime();
|
|
3484
|
-
|
|
3485
|
-
if (Math.abs(now - eventTime) > this.tolerance) {
|
|
3718
|
+
if (Math.abs(Date.now() - eventTime) > this.tolerance) {
|
|
3486
3719
|
throw new ValidationError(
|
|
3487
3720
|
`Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`,
|
|
3488
3721
|
["timestamp"]
|
|
@@ -3490,26 +3723,18 @@ var WebhookHandler = class {
|
|
|
3490
3723
|
}
|
|
3491
3724
|
}
|
|
3492
3725
|
if (this.replayProtection) {
|
|
3493
|
-
if (this.
|
|
3726
|
+
if (await this.dedupStore.seen(eventId)) {
|
|
3494
3727
|
throw new ValidationError(
|
|
3495
|
-
`Duplicate webhook event: ${
|
|
3728
|
+
`Duplicate webhook event: ${eventId} has already been processed`,
|
|
3496
3729
|
["id"]
|
|
3497
3730
|
);
|
|
3498
3731
|
}
|
|
3499
|
-
|
|
3500
|
-
this.seenEventIds.set(event.id, now);
|
|
3501
|
-
if (this.seenEventIds.size > 1e3) {
|
|
3502
|
-
for (const [id, seenAt] of this.seenEventIds) {
|
|
3503
|
-
if (now - seenAt > this.tolerance) {
|
|
3504
|
-
this.seenEventIds.delete(id);
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
}
|
|
3732
|
+
await this.dedupStore.mark(eventId, this.tolerance);
|
|
3508
3733
|
}
|
|
3509
3734
|
return event;
|
|
3510
3735
|
}
|
|
3511
3736
|
async dispatch(event) {
|
|
3512
|
-
const typeHandlers = this.handlers.get(event.
|
|
3737
|
+
const typeHandlers = this.handlers.get(event.event_type) || [];
|
|
3513
3738
|
const allHandlers = [...typeHandlers, ...this.anyHandlers];
|
|
3514
3739
|
for (const handler of allHandlers) {
|
|
3515
3740
|
await handler(event);
|
|
@@ -3600,26 +3825,35 @@ exports.ComplianceClient = ComplianceClient;
|
|
|
3600
3825
|
exports.ComplianceError = ComplianceError;
|
|
3601
3826
|
exports.DEFAULT_CURRENCY_RATES = DEFAULT_CURRENCY_RATES;
|
|
3602
3827
|
exports.GeolocationClient = GeolocationClient;
|
|
3828
|
+
exports.InMemoryDedupStore = InMemoryDedupStore;
|
|
3603
3829
|
exports.KYC_DECLINED_DESCRIPTIONS = KYC_DECLINED_DESCRIPTIONS;
|
|
3604
3830
|
exports.KycClient = KycClient;
|
|
3605
3831
|
exports.NetworkError = NetworkError;
|
|
3606
3832
|
exports.RateLimitError = RateLimitError;
|
|
3607
3833
|
exports.RateLimitTracker = RateLimitTracker;
|
|
3834
|
+
exports.RefreshingTokenProvider = RefreshingTokenProvider;
|
|
3608
3835
|
exports.RiskProfileClient = RiskProfileClient;
|
|
3609
3836
|
exports.SDK_VERSION = SDK_VERSION;
|
|
3610
3837
|
exports.ServiceUnavailableError = ServiceUnavailableError;
|
|
3838
|
+
exports.StaticApiKeyProvider = StaticApiKeyProvider;
|
|
3611
3839
|
exports.TaxClient = TaxClient;
|
|
3612
3840
|
exports.TimeoutError = TimeoutError;
|
|
3613
3841
|
exports.ValidationError = ValidationError;
|
|
3614
3842
|
exports.VesantError = VesantError;
|
|
3615
3843
|
exports.WebhookHandler = WebhookHandler;
|
|
3844
|
+
exports.assessGpsIntegrity = assessGpsIntegrity;
|
|
3845
|
+
exports.collectAll = collectAll;
|
|
3846
|
+
exports.collectDeviceSignals = collectDeviceSignals;
|
|
3616
3847
|
exports.createConsoleLogger = createConsoleLogger;
|
|
3617
3848
|
exports.createNextWebhookHandler = createNextWebhookHandler;
|
|
3618
3849
|
exports.createWebhookMiddleware = createWebhookMiddleware;
|
|
3619
3850
|
exports.decodeCipherText = decodeCipherText;
|
|
3620
3851
|
exports.generateCipherText = generateCipherText;
|
|
3852
|
+
exports.gpsIntegrityInputFromPosition = gpsIntegrityInputFromPosition;
|
|
3621
3853
|
exports.isCipherTextExpired = isCipherTextExpired;
|
|
3622
3854
|
exports.noopLogger = noopLogger;
|
|
3855
|
+
exports.paginate = paginate;
|
|
3856
|
+
exports.probeGeolocationPermission = probeGeolocationPermission;
|
|
3623
3857
|
exports.sdkReasons = sdkReasons;
|
|
3624
3858
|
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
3625
3859
|
//# sourceMappingURL=index.js.map
|