@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 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): Promise<T>;
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): Promise<T>;
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(["sandbox.connect.staging.edgeboost.io", "connect.edgeboost.io"]);
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 host === "sandbox.connect.staging.edgeboost.io" || host === "connect.edgeboost.io";
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(["sandbox.connect.staging.edgeboost.io", "connect.edgeboost.io"]);
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 host === "sandbox.connect.staging.edgeboost.io" || host === "connect.edgeboost.io";
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.11.2",
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.9.2"
25
+ "@edge-markets/connect": "^1.10.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "tsup": "^8.0.0",