@edge-markets/connect-node 1.11.2 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +89 -7
- package/dist/index.mjs +93 -8
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as node_https from 'node:https';
|
|
2
|
-
import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
|
|
2
|
+
import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, IdentityMatchOptions, IdentityMatchResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
|
|
3
3
|
export { ACTIVE_EDGE_SCOPES, ALL_EDGE_SCOPES, Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE, EDGE_SCOPES, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeFeatureUnavailableError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, RESERVED_EDGE_SCOPES, SdkGeolocation, TRANSFER_CATEGORIES, Transfer, TransferCategory, TransferCompletedEventData, TransferExpiredEventData, TransferFailedEventData, TransferList, TransferListItem, TransferProcessingEventData, TransferStatus, TransferType, User, UserAddress, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, VerifyIdentityAddress, VerifyIdentityOptions, VerifyIdentityResult, VerifyIdentityScores, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isFeatureUnavailableError, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
4
4
|
|
|
5
5
|
type RequestEndpoint = 'connect_api' | 'oauth_token' | 'partner_webhook_sync';
|
|
@@ -161,7 +161,13 @@ declare class EdgeUserClient {
|
|
|
161
161
|
readonly accessToken: string;
|
|
162
162
|
constructor(server: EdgeConnectServer, accessToken: string);
|
|
163
163
|
getUser(): Promise<User>;
|
|
164
|
+
/** @deprecated Legacy v1 preview. Request `identity.match` and use {@link matchIdentity} for new integrations. */
|
|
164
165
|
verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
|
|
166
|
+
/**
|
|
167
|
+
* Compare independently collected partner identity data with the EDGE profile.
|
|
168
|
+
* The request body is deliberately excluded from SDK diagnostic hooks.
|
|
169
|
+
*/
|
|
170
|
+
matchIdentity(options: IdentityMatchOptions): Promise<IdentityMatchResult>;
|
|
165
171
|
getBalance(): Promise<Balance>;
|
|
166
172
|
initiateTransfer(options: TransferOptions): Promise<Transfer>;
|
|
167
173
|
startTransferVerification(options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
|
|
@@ -554,7 +560,10 @@ declare class EdgeConnectServer {
|
|
|
554
560
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
555
561
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
556
562
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
557
|
-
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown
|
|
563
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown, requestOptions?: {
|
|
564
|
+
headers?: Record<string, string>;
|
|
565
|
+
redactBodyFromHooks?: boolean;
|
|
566
|
+
}): Promise<T>;
|
|
558
567
|
/**
|
|
559
568
|
* Reconcile webhook events from the partner sync endpoint.
|
|
560
569
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as node_https from 'node:https';
|
|
2
|
-
import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
|
|
2
|
+
import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, IdentityMatchOptions, IdentityMatchResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
|
|
3
3
|
export { ACTIVE_EDGE_SCOPES, ALL_EDGE_SCOPES, Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE, EDGE_SCOPES, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeFeatureUnavailableError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, RESERVED_EDGE_SCOPES, SdkGeolocation, TRANSFER_CATEGORIES, Transfer, TransferCategory, TransferCompletedEventData, TransferExpiredEventData, TransferFailedEventData, TransferList, TransferListItem, TransferProcessingEventData, TransferStatus, TransferType, User, UserAddress, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, VerifyIdentityAddress, VerifyIdentityOptions, VerifyIdentityResult, VerifyIdentityScores, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isFeatureUnavailableError, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
4
4
|
|
|
5
5
|
type RequestEndpoint = 'connect_api' | 'oauth_token' | 'partner_webhook_sync';
|
|
@@ -161,7 +161,13 @@ declare class EdgeUserClient {
|
|
|
161
161
|
readonly accessToken: string;
|
|
162
162
|
constructor(server: EdgeConnectServer, accessToken: string);
|
|
163
163
|
getUser(): Promise<User>;
|
|
164
|
+
/** @deprecated Legacy v1 preview. Request `identity.match` and use {@link matchIdentity} for new integrations. */
|
|
164
165
|
verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
|
|
166
|
+
/**
|
|
167
|
+
* Compare independently collected partner identity data with the EDGE profile.
|
|
168
|
+
* The request body is deliberately excluded from SDK diagnostic hooks.
|
|
169
|
+
*/
|
|
170
|
+
matchIdentity(options: IdentityMatchOptions): Promise<IdentityMatchResult>;
|
|
165
171
|
getBalance(): Promise<Balance>;
|
|
166
172
|
initiateTransfer(options: TransferOptions): Promise<Transfer>;
|
|
167
173
|
startTransferVerification(options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
|
|
@@ -554,7 +560,10 @@ declare class EdgeConnectServer {
|
|
|
554
560
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
555
561
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
556
562
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
557
|
-
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown
|
|
563
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown, requestOptions?: {
|
|
564
|
+
headers?: Record<string, string>;
|
|
565
|
+
redactBodyFromHooks?: boolean;
|
|
566
|
+
}): Promise<T>;
|
|
558
567
|
/**
|
|
559
568
|
* Reconcile webhook events from the partner sync endpoint.
|
|
560
569
|
*
|
package/dist/index.js
CHANGED
|
@@ -306,6 +306,45 @@ function validateVerifyIdentityOptions(options) {
|
|
|
306
306
|
throw new import_connect2.EdgeValidationError(firstMessage, errors);
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
|
+
function validateIdentityMatchOptions(options) {
|
|
310
|
+
const errors = {};
|
|
311
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
312
|
+
throw new import_connect2.EdgeValidationError("Invalid identity match options", { options: ["Options are required"] });
|
|
313
|
+
}
|
|
314
|
+
if (typeof options.idempotencyKey !== "string" || !/^[\x21-\x7e]{16,255}$/.test(options.idempotencyKey)) {
|
|
315
|
+
errors.idempotencyKey = ["Must be 16-255 visible ASCII characters"];
|
|
316
|
+
}
|
|
317
|
+
if (!options.legalName || typeof options.legalName !== "object") {
|
|
318
|
+
errors.legalName = ["legalName is required"];
|
|
319
|
+
} else {
|
|
320
|
+
if (typeof options.legalName.givenName !== "string" || !options.legalName.givenName.trim() || options.legalName.givenName.length > 100) {
|
|
321
|
+
errors["legalName.givenName"] = ["Must be 1-100 characters"];
|
|
322
|
+
}
|
|
323
|
+
if (typeof options.legalName.familyName !== "string" || !options.legalName.familyName.trim() || options.legalName.familyName.length > 100) {
|
|
324
|
+
errors["legalName.familyName"] = ["Must be 1-100 characters"];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (options.address) {
|
|
328
|
+
const required = ["line1", "locality", "region", "postalCode", "country"];
|
|
329
|
+
for (const field of required) {
|
|
330
|
+
if (typeof options.address[field] !== "string" || !options.address[field].trim()) {
|
|
331
|
+
errors[`address.${field}`] = ["Field is required when address is provided"];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (options.address.country !== "US") errors["address.country"] = ["Only US is supported"];
|
|
335
|
+
if (!/^\d{5}(?:-\d{4})?$/.test(options.address.postalCode || "")) {
|
|
336
|
+
errors["address.postalCode"] = ["Must be a valid US ZIP or ZIP+4"];
|
|
337
|
+
}
|
|
338
|
+
const maximums = { line1: 160, line2: 100, locality: 100, region: 50 };
|
|
339
|
+
for (const [field, maximum] of Object.entries(maximums)) {
|
|
340
|
+
const value = options.address[field];
|
|
341
|
+
if (value !== void 0 && (typeof value !== "string" || value.length > maximum)) {
|
|
342
|
+
errors[`address.${field}`] = [`Must be at most ${maximum} characters`];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (Object.keys(errors).length) throw new import_connect2.EdgeValidationError("Invalid identity match options", errors);
|
|
347
|
+
}
|
|
309
348
|
function validateTransferOptions(options) {
|
|
310
349
|
const errors = {};
|
|
311
350
|
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
@@ -345,10 +384,23 @@ var EdgeUserClient = class {
|
|
|
345
384
|
async getUser() {
|
|
346
385
|
return this.server._apiRequest("GET", "/user", this.accessToken);
|
|
347
386
|
}
|
|
387
|
+
/** @deprecated Legacy v1 preview. Request `identity.match` and use {@link matchIdentity} for new integrations. */
|
|
348
388
|
async verifyIdentity(options) {
|
|
349
389
|
validateVerifyIdentityOptions(options);
|
|
350
390
|
return this.server._apiRequest("POST", "/user/verify-identity", this.accessToken, options);
|
|
351
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Compare independently collected partner identity data with the EDGE profile.
|
|
394
|
+
* The request body is deliberately excluded from SDK diagnostic hooks.
|
|
395
|
+
*/
|
|
396
|
+
async matchIdentity(options) {
|
|
397
|
+
validateIdentityMatchOptions(options);
|
|
398
|
+
const { idempotencyKey, ...body } = options;
|
|
399
|
+
return this.server._apiRequest("POST", "/v2/identity/match", this.accessToken, body, {
|
|
400
|
+
headers: { "Idempotency-Key": idempotencyKey },
|
|
401
|
+
redactBodyFromHooks: true
|
|
402
|
+
});
|
|
403
|
+
}
|
|
352
404
|
async getBalance() {
|
|
353
405
|
return this.server._apiRequest("GET", "/balance", this.accessToken);
|
|
354
406
|
}
|
|
@@ -859,7 +911,12 @@ function getInstanceKey(config) {
|
|
|
859
911
|
config.partnerClientId || ""
|
|
860
912
|
]);
|
|
861
913
|
}
|
|
862
|
-
var MTLS_ENFORCED_HOSTS = /* @__PURE__ */ new Set([
|
|
914
|
+
var MTLS_ENFORCED_HOSTS = /* @__PURE__ */ new Set([
|
|
915
|
+
"connect-staging.edgeboost.io",
|
|
916
|
+
"sandbox.connect.staging.edgeboost.io",
|
|
917
|
+
"sandbox.connect.edgeboost.io",
|
|
918
|
+
"connect.edgeboost.io"
|
|
919
|
+
]);
|
|
863
920
|
var CONNECT_INGRESS_REQUIRED_MESSAGE = "Edge Connect API requests must go through the mTLS Connect gateway. Set EDGE_API_BASE_URL and EDGE_OAUTH_BASE_URL to the Connect gateway for this environment and configure EDGE_MTLS_CERT and EDGE_MTLS_KEY.";
|
|
864
921
|
var MTLS_REQUIRED_MESSAGE = "Edge Connect rejected this request because no trusted partner mTLS client certificate was presented. Configure the issued EDGE_MTLS_CERT and EDGE_MTLS_KEY and retry through the Connect gateway.";
|
|
865
922
|
var MTLS_CLIENT_UNREGISTERED_MESSAGE = "Edge Connect accepted the client certificate at the gateway, but the certificate is not registered for this partner. Contact EDGE DevRel to bind the certificate fingerprint to your OAuth client ID.";
|
|
@@ -1043,22 +1100,25 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1043
1100
|
}
|
|
1044
1101
|
}
|
|
1045
1102
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
1046
|
-
async _apiRequest(method, path, accessToken, body) {
|
|
1047
|
-
const url = `${this.apiBaseUrl}${path}`;
|
|
1103
|
+
async _apiRequest(method, path, accessToken, body, requestOptions) {
|
|
1104
|
+
const url = path.startsWith("/v2/") ? `${this.apiBaseUrl.replace(/\/v1\/?$/, "")}${path}` : `${this.apiBaseUrl}${path}`;
|
|
1048
1105
|
let lastError = null;
|
|
1106
|
+
let retryAfterMs;
|
|
1049
1107
|
const mleConfig = this.config.mle;
|
|
1050
1108
|
const mleEnabled = !!mleConfig?.enabled;
|
|
1051
1109
|
for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
|
|
1052
1110
|
const startTime = Date.now();
|
|
1053
1111
|
if (attempt > 0) {
|
|
1054
|
-
await this.sleep(this.getRetryDelay(attempt - 1));
|
|
1112
|
+
await this.sleep(retryAfterMs ?? this.getRetryDelay(attempt - 1));
|
|
1113
|
+
retryAfterMs = void 0;
|
|
1055
1114
|
}
|
|
1056
|
-
this.config.onRequest?.({ method, url, body });
|
|
1115
|
+
this.config.onRequest?.({ method, url, body: requestOptions?.redactBodyFromHooks ? void 0 : body });
|
|
1057
1116
|
try {
|
|
1058
1117
|
const requestHeaders = {
|
|
1059
1118
|
Authorization: `Bearer ${accessToken}`,
|
|
1060
1119
|
"Content-Type": "application/json",
|
|
1061
|
-
"User-Agent": USER_AGENT
|
|
1120
|
+
"User-Agent": USER_AGENT,
|
|
1121
|
+
...requestOptions?.headers
|
|
1062
1122
|
};
|
|
1063
1123
|
let requestBody = body;
|
|
1064
1124
|
if (mleEnabled) {
|
|
@@ -1100,6 +1160,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1100
1160
|
if (!response.ok) {
|
|
1101
1161
|
if (this.retryConfig.retryOn.includes(response.status) && attempt < this.retryConfig.maxRetries) {
|
|
1102
1162
|
lastError = await this.handleApiErrorFromBody(responseBody, response.status, path);
|
|
1163
|
+
const retryAfter = response.headers?.get?.("retry-after");
|
|
1164
|
+
if (retryAfter && /^\d+$/.test(retryAfter)) retryAfterMs = Math.min(Number(retryAfter) * 1e3, 6e4);
|
|
1103
1165
|
continue;
|
|
1104
1166
|
}
|
|
1105
1167
|
throw await this.handleApiErrorFromBody(responseBody, response.status, path);
|
|
@@ -1549,6 +1611,21 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1549
1611
|
error.message
|
|
1550
1612
|
);
|
|
1551
1613
|
}
|
|
1614
|
+
if (status === 409 && ["idempotency_conflict", "idempotency_in_progress", "match_not_eligible"].includes(error.error)) {
|
|
1615
|
+
return new import_connect5.EdgeIdentityMatchConflictError(
|
|
1616
|
+
error.error,
|
|
1617
|
+
error.message
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
if (status === 429 && error.error === "identity_match_attempt_limit") {
|
|
1621
|
+
return new import_connect5.EdgeIdentityMatchAttemptLimitError(
|
|
1622
|
+
typeof error.retry_after === "number" ? error.retry_after : void 0,
|
|
1623
|
+
error.message
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
if (status === 503 && error.error === "identity_match_unavailable") {
|
|
1627
|
+
return new import_connect5.EdgeIdentityMatchUnavailableError(error.message);
|
|
1628
|
+
}
|
|
1552
1629
|
return new import_connect5.EdgeApiError(
|
|
1553
1630
|
error.error || "api_error",
|
|
1554
1631
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
@@ -1666,7 +1743,12 @@ function inferMtlsRequirement(environment, apiBaseUrl) {
|
|
|
1666
1743
|
const resolvedApiBaseUrl = apiBaseUrl || (0, import_connect6.getEnvironmentConfig)(environment).apiBaseUrl;
|
|
1667
1744
|
try {
|
|
1668
1745
|
const host = new URL(resolvedApiBaseUrl).host;
|
|
1669
|
-
return
|
|
1746
|
+
return [
|
|
1747
|
+
"connect-staging.edgeboost.io",
|
|
1748
|
+
"sandbox.connect.staging.edgeboost.io",
|
|
1749
|
+
"sandbox.connect.edgeboost.io",
|
|
1750
|
+
"connect.edgeboost.io"
|
|
1751
|
+
].includes(host);
|
|
1670
1752
|
} catch {
|
|
1671
1753
|
return false;
|
|
1672
1754
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
EdgeAuthenticationError as EdgeAuthenticationError2,
|
|
5
5
|
EdgeConsentRequiredError,
|
|
6
6
|
EdgeError,
|
|
7
|
+
EdgeIdentityMatchAttemptLimitError,
|
|
8
|
+
EdgeIdentityMatchConflictError,
|
|
9
|
+
EdgeIdentityMatchUnavailableError,
|
|
7
10
|
EdgeIdentityVerificationError,
|
|
8
11
|
EdgeInsufficientScopeError,
|
|
9
12
|
EdgeNetworkError,
|
|
@@ -216,6 +219,45 @@ function validateVerifyIdentityOptions(options) {
|
|
|
216
219
|
throw new EdgeValidationError2(firstMessage, errors);
|
|
217
220
|
}
|
|
218
221
|
}
|
|
222
|
+
function validateIdentityMatchOptions(options) {
|
|
223
|
+
const errors = {};
|
|
224
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
225
|
+
throw new EdgeValidationError2("Invalid identity match options", { options: ["Options are required"] });
|
|
226
|
+
}
|
|
227
|
+
if (typeof options.idempotencyKey !== "string" || !/^[\x21-\x7e]{16,255}$/.test(options.idempotencyKey)) {
|
|
228
|
+
errors.idempotencyKey = ["Must be 16-255 visible ASCII characters"];
|
|
229
|
+
}
|
|
230
|
+
if (!options.legalName || typeof options.legalName !== "object") {
|
|
231
|
+
errors.legalName = ["legalName is required"];
|
|
232
|
+
} else {
|
|
233
|
+
if (typeof options.legalName.givenName !== "string" || !options.legalName.givenName.trim() || options.legalName.givenName.length > 100) {
|
|
234
|
+
errors["legalName.givenName"] = ["Must be 1-100 characters"];
|
|
235
|
+
}
|
|
236
|
+
if (typeof options.legalName.familyName !== "string" || !options.legalName.familyName.trim() || options.legalName.familyName.length > 100) {
|
|
237
|
+
errors["legalName.familyName"] = ["Must be 1-100 characters"];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (options.address) {
|
|
241
|
+
const required = ["line1", "locality", "region", "postalCode", "country"];
|
|
242
|
+
for (const field of required) {
|
|
243
|
+
if (typeof options.address[field] !== "string" || !options.address[field].trim()) {
|
|
244
|
+
errors[`address.${field}`] = ["Field is required when address is provided"];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (options.address.country !== "US") errors["address.country"] = ["Only US is supported"];
|
|
248
|
+
if (!/^\d{5}(?:-\d{4})?$/.test(options.address.postalCode || "")) {
|
|
249
|
+
errors["address.postalCode"] = ["Must be a valid US ZIP or ZIP+4"];
|
|
250
|
+
}
|
|
251
|
+
const maximums = { line1: 160, line2: 100, locality: 100, region: 50 };
|
|
252
|
+
for (const [field, maximum] of Object.entries(maximums)) {
|
|
253
|
+
const value = options.address[field];
|
|
254
|
+
if (value !== void 0 && (typeof value !== "string" || value.length > maximum)) {
|
|
255
|
+
errors[`address.${field}`] = [`Must be at most ${maximum} characters`];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (Object.keys(errors).length) throw new EdgeValidationError2("Invalid identity match options", errors);
|
|
260
|
+
}
|
|
219
261
|
function validateTransferOptions(options) {
|
|
220
262
|
const errors = {};
|
|
221
263
|
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
@@ -255,10 +297,23 @@ var EdgeUserClient = class {
|
|
|
255
297
|
async getUser() {
|
|
256
298
|
return this.server._apiRequest("GET", "/user", this.accessToken);
|
|
257
299
|
}
|
|
300
|
+
/** @deprecated Legacy v1 preview. Request `identity.match` and use {@link matchIdentity} for new integrations. */
|
|
258
301
|
async verifyIdentity(options) {
|
|
259
302
|
validateVerifyIdentityOptions(options);
|
|
260
303
|
return this.server._apiRequest("POST", "/user/verify-identity", this.accessToken, options);
|
|
261
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Compare independently collected partner identity data with the EDGE profile.
|
|
307
|
+
* The request body is deliberately excluded from SDK diagnostic hooks.
|
|
308
|
+
*/
|
|
309
|
+
async matchIdentity(options) {
|
|
310
|
+
validateIdentityMatchOptions(options);
|
|
311
|
+
const { idempotencyKey, ...body } = options;
|
|
312
|
+
return this.server._apiRequest("POST", "/v2/identity/match", this.accessToken, body, {
|
|
313
|
+
headers: { "Idempotency-Key": idempotencyKey },
|
|
314
|
+
redactBodyFromHooks: true
|
|
315
|
+
});
|
|
316
|
+
}
|
|
262
317
|
async getBalance() {
|
|
263
318
|
return this.server._apiRequest("GET", "/balance", this.accessToken);
|
|
264
319
|
}
|
|
@@ -769,7 +824,12 @@ function getInstanceKey(config) {
|
|
|
769
824
|
config.partnerClientId || ""
|
|
770
825
|
]);
|
|
771
826
|
}
|
|
772
|
-
var MTLS_ENFORCED_HOSTS = /* @__PURE__ */ new Set([
|
|
827
|
+
var MTLS_ENFORCED_HOSTS = /* @__PURE__ */ new Set([
|
|
828
|
+
"connect-staging.edgeboost.io",
|
|
829
|
+
"sandbox.connect.staging.edgeboost.io",
|
|
830
|
+
"sandbox.connect.edgeboost.io",
|
|
831
|
+
"connect.edgeboost.io"
|
|
832
|
+
]);
|
|
773
833
|
var CONNECT_INGRESS_REQUIRED_MESSAGE = "Edge Connect API requests must go through the mTLS Connect gateway. Set EDGE_API_BASE_URL and EDGE_OAUTH_BASE_URL to the Connect gateway for this environment and configure EDGE_MTLS_CERT and EDGE_MTLS_KEY.";
|
|
774
834
|
var MTLS_REQUIRED_MESSAGE = "Edge Connect rejected this request because no trusted partner mTLS client certificate was presented. Configure the issued EDGE_MTLS_CERT and EDGE_MTLS_KEY and retry through the Connect gateway.";
|
|
775
835
|
var MTLS_CLIENT_UNREGISTERED_MESSAGE = "Edge Connect accepted the client certificate at the gateway, but the certificate is not registered for this partner. Contact EDGE DevRel to bind the certificate fingerprint to your OAuth client ID.";
|
|
@@ -953,22 +1013,25 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
953
1013
|
}
|
|
954
1014
|
}
|
|
955
1015
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
956
|
-
async _apiRequest(method, path, accessToken, body) {
|
|
957
|
-
const url = `${this.apiBaseUrl}${path}`;
|
|
1016
|
+
async _apiRequest(method, path, accessToken, body, requestOptions) {
|
|
1017
|
+
const url = path.startsWith("/v2/") ? `${this.apiBaseUrl.replace(/\/v1\/?$/, "")}${path}` : `${this.apiBaseUrl}${path}`;
|
|
958
1018
|
let lastError = null;
|
|
1019
|
+
let retryAfterMs;
|
|
959
1020
|
const mleConfig = this.config.mle;
|
|
960
1021
|
const mleEnabled = !!mleConfig?.enabled;
|
|
961
1022
|
for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
|
|
962
1023
|
const startTime = Date.now();
|
|
963
1024
|
if (attempt > 0) {
|
|
964
|
-
await this.sleep(this.getRetryDelay(attempt - 1));
|
|
1025
|
+
await this.sleep(retryAfterMs ?? this.getRetryDelay(attempt - 1));
|
|
1026
|
+
retryAfterMs = void 0;
|
|
965
1027
|
}
|
|
966
|
-
this.config.onRequest?.({ method, url, body });
|
|
1028
|
+
this.config.onRequest?.({ method, url, body: requestOptions?.redactBodyFromHooks ? void 0 : body });
|
|
967
1029
|
try {
|
|
968
1030
|
const requestHeaders = {
|
|
969
1031
|
Authorization: `Bearer ${accessToken}`,
|
|
970
1032
|
"Content-Type": "application/json",
|
|
971
|
-
"User-Agent": USER_AGENT
|
|
1033
|
+
"User-Agent": USER_AGENT,
|
|
1034
|
+
...requestOptions?.headers
|
|
972
1035
|
};
|
|
973
1036
|
let requestBody = body;
|
|
974
1037
|
if (mleEnabled) {
|
|
@@ -1010,6 +1073,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1010
1073
|
if (!response.ok) {
|
|
1011
1074
|
if (this.retryConfig.retryOn.includes(response.status) && attempt < this.retryConfig.maxRetries) {
|
|
1012
1075
|
lastError = await this.handleApiErrorFromBody(responseBody, response.status, path);
|
|
1076
|
+
const retryAfter = response.headers?.get?.("retry-after");
|
|
1077
|
+
if (retryAfter && /^\d+$/.test(retryAfter)) retryAfterMs = Math.min(Number(retryAfter) * 1e3, 6e4);
|
|
1013
1078
|
continue;
|
|
1014
1079
|
}
|
|
1015
1080
|
throw await this.handleApiErrorFromBody(responseBody, response.status, path);
|
|
@@ -1459,6 +1524,21 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1459
1524
|
error.message
|
|
1460
1525
|
);
|
|
1461
1526
|
}
|
|
1527
|
+
if (status === 409 && ["idempotency_conflict", "idempotency_in_progress", "match_not_eligible"].includes(error.error)) {
|
|
1528
|
+
return new EdgeIdentityMatchConflictError(
|
|
1529
|
+
error.error,
|
|
1530
|
+
error.message
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
if (status === 429 && error.error === "identity_match_attempt_limit") {
|
|
1534
|
+
return new EdgeIdentityMatchAttemptLimitError(
|
|
1535
|
+
typeof error.retry_after === "number" ? error.retry_after : void 0,
|
|
1536
|
+
error.message
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
if (status === 503 && error.error === "identity_match_unavailable") {
|
|
1540
|
+
return new EdgeIdentityMatchUnavailableError(error.message);
|
|
1541
|
+
}
|
|
1462
1542
|
return new EdgeApiError(
|
|
1463
1543
|
error.error || "api_error",
|
|
1464
1544
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
@@ -1576,7 +1656,12 @@ function inferMtlsRequirement(environment, apiBaseUrl) {
|
|
|
1576
1656
|
const resolvedApiBaseUrl = apiBaseUrl || getEnvironmentConfig2(environment).apiBaseUrl;
|
|
1577
1657
|
try {
|
|
1578
1658
|
const host = new URL(resolvedApiBaseUrl).host;
|
|
1579
|
-
return
|
|
1659
|
+
return [
|
|
1660
|
+
"connect-staging.edgeboost.io",
|
|
1661
|
+
"sandbox.connect.staging.edgeboost.io",
|
|
1662
|
+
"sandbox.connect.edgeboost.io",
|
|
1663
|
+
"connect.edgeboost.io"
|
|
1664
|
+
].includes(host);
|
|
1580
1665
|
} catch {
|
|
1581
1666
|
return false;
|
|
1582
1667
|
}
|
|
@@ -2619,8 +2704,8 @@ import {
|
|
|
2619
2704
|
ACTIVE_EDGE_SCOPES,
|
|
2620
2705
|
ALL_EDGE_SCOPES,
|
|
2621
2706
|
EDGE_SCOPES,
|
|
2622
|
-
RESERVED_EDGE_SCOPES,
|
|
2623
2707
|
EDGE_WEBHOOK_EVENT_TYPES as EDGE_WEBHOOK_EVENT_TYPES2,
|
|
2708
|
+
RESERVED_EDGE_SCOPES,
|
|
2624
2709
|
TRANSFER_CATEGORIES,
|
|
2625
2710
|
getEnvironmentConfig as getEnvironmentConfig3,
|
|
2626
2711
|
isProductionEnvironment
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-markets/connect-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Server SDK for EDGE Connect token exchange and API calls",
|
|
5
5
|
"author": "Edge Markets",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"undici": "^5.29.0",
|
|
25
|
-
"@edge-markets/connect": "^1.
|
|
25
|
+
"@edge-markets/connect": "^1.10.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"tsup": "^8.0.0",
|