@edge-markets/connect-node 1.9.0 → 1.10.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/README.md CHANGED
@@ -92,7 +92,9 @@ Supported environment variables:
92
92
  | `EDGE_PARTNER_CLIENT_SECRET` | Partner machine-to-machine client secret for webhook sync |
93
93
  | `EDGE_MTLS_CERT` / `EDGE_MTLS_CERT_PATH` | Client certificate PEM or file path |
94
94
  | `EDGE_MTLS_KEY` / `EDGE_MTLS_KEY_PATH` | Client private key PEM or file path |
95
- | `EDGE_MTLS_CA` / `EDGE_MTLS_CA_PATH` | Optional extra server trust root PEM or file path |
95
+ | `EDGE_SERVER_CA_PEM` / `EDGE_SERVER_CA_PEM_PATH` | Optional extra server trust root PEM or file path |
96
+ | `EDGE_MTLS_CA` / `EDGE_MTLS_CA_PATH` | Legacy alias for optional extra server trust root PEM or file path |
97
+ | `EDGE_REQUIRE_MTLS` | Set to `true` to fail startup unless client cert and key are configured |
96
98
  | `EDGE_TIMEOUT_MS` | Optional request timeout in milliseconds |
97
99
 
98
100
  Use inline PEMs with escaped newlines (`\n`) or file paths. If both are set,
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as node_https from 'node:https';
2
2
  import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
3
- export { Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, 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, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
3
+ export { ACTIVE_EDGE_SCOPES, Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeFeatureUnavailableError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, 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';
6
6
  interface RequestInfo {
@@ -175,14 +175,13 @@ declare class EdgeUserClient {
175
175
  revoked: boolean;
176
176
  }>;
177
177
  /**
178
- * Creates an EDGE-hosted transfer verification session.
179
- * Returns a `verificationUrl` to open in an iframe or popup.
180
- * The handoff token is single-use and expires in 120 seconds.
178
+ * Reserved for a future transfer release.
179
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
181
180
  */
182
181
  createVerificationSession(transferId: string, options: CreateVerificationSessionRequest): Promise<VerificationSession>;
183
182
  /**
184
- * Polls the status of a verification session.
185
- * Use as an alternative to postMessage events.
183
+ * Reserved for a future transfer release.
184
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
186
185
  */
187
186
  getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
188
187
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as node_https from 'node:https';
2
2
  import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, EdgeWebhookEventType } from '@edge-markets/connect';
3
- export { Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, 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, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
3
+ export { ACTIVE_EDGE_SCOPES, Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeFeatureUnavailableError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, 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';
6
6
  interface RequestInfo {
@@ -175,14 +175,13 @@ declare class EdgeUserClient {
175
175
  revoked: boolean;
176
176
  }>;
177
177
  /**
178
- * Creates an EDGE-hosted transfer verification session.
179
- * Returns a `verificationUrl` to open in an iframe or popup.
180
- * The handoff token is single-use and expires in 120 seconds.
178
+ * Reserved for a future transfer release.
179
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
181
180
  */
182
181
  createVerificationSession(transferId: string, options: CreateVerificationSessionRequest): Promise<VerificationSession>;
183
182
  /**
184
- * Polls the status of a verification session.
185
- * Use as an alternative to postMessage events.
183
+ * Reserved for a future transfer release.
184
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
186
185
  */
187
186
  getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
188
187
  }
package/dist/index.js CHANGED
@@ -30,25 +30,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ACTIVE_EDGE_SCOPES: () => import_connect16.ACTIVE_EDGE_SCOPES,
33
34
  CONNECT_TRANSFER_DIRECTIONS: () => CONNECT_TRANSFER_DIRECTIONS,
34
- EDGE_WEBHOOK_EVENT_TYPES: () => import_connect15.EDGE_WEBHOOK_EVENT_TYPES,
35
- EdgeApiError: () => import_connect14.EdgeApiError,
36
- EdgeAuthenticationError: () => import_connect14.EdgeAuthenticationError,
35
+ EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE: () => import_connect15.EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE,
36
+ EDGE_WEBHOOK_EVENT_TYPES: () => import_connect16.EDGE_WEBHOOK_EVENT_TYPES,
37
+ EdgeApiError: () => import_connect15.EdgeApiError,
38
+ EdgeAuthenticationError: () => import_connect15.EdgeAuthenticationError,
37
39
  EdgeConnectServer: () => EdgeConnectServer,
38
- EdgeConsentRequiredError: () => import_connect14.EdgeConsentRequiredError,
39
- EdgeError: () => import_connect14.EdgeError,
40
- EdgeIdentityVerificationError: () => import_connect14.EdgeIdentityVerificationError,
41
- EdgeInsufficientScopeError: () => import_connect14.EdgeInsufficientScopeError,
42
- EdgeNetworkError: () => import_connect14.EdgeNetworkError,
43
- EdgeNotFoundError: () => import_connect14.EdgeNotFoundError,
44
- EdgeTokenExchangeError: () => import_connect14.EdgeTokenExchangeError,
40
+ EdgeConsentRequiredError: () => import_connect15.EdgeConsentRequiredError,
41
+ EdgeError: () => import_connect15.EdgeError,
42
+ EdgeFeatureUnavailableError: () => import_connect15.EdgeFeatureUnavailableError,
43
+ EdgeIdentityVerificationError: () => import_connect15.EdgeIdentityVerificationError,
44
+ EdgeInsufficientScopeError: () => import_connect15.EdgeInsufficientScopeError,
45
+ EdgeNetworkError: () => import_connect15.EdgeNetworkError,
46
+ EdgeNotFoundError: () => import_connect15.EdgeNotFoundError,
47
+ EdgeTokenExchangeError: () => import_connect15.EdgeTokenExchangeError,
45
48
  EdgeTokenVault: () => EdgeTokenVault,
46
49
  EdgeUserClient: () => EdgeUserClient,
47
50
  EdgeUserSession: () => EdgeUserSession,
48
- EdgeValidationError: () => import_connect14.EdgeValidationError,
51
+ EdgeValidationError: () => import_connect15.EdgeValidationError,
49
52
  EdgeWebhookInbox: () => EdgeWebhookInbox,
50
53
  EdgeWebhookReconciler: () => EdgeWebhookReconciler,
51
- TRANSFER_CATEGORIES: () => import_connect15.TRANSFER_CATEGORIES,
54
+ TRANSFER_CATEGORIES: () => import_connect16.TRANSFER_CATEGORIES,
52
55
  assertSameTransferIntent: () => assertSameTransferIntent,
53
56
  assertTransferEventMatchesIntent: () => assertTransferEventMatchesIntent,
54
57
  assertWebhookMatchesTransfer: () => assertWebhookMatchesTransfer,
@@ -68,16 +71,17 @@ __export(index_exports, {
68
71
  getConnectTransferDirection: () => getConnectTransferDirection,
69
72
  getEdgeWebhookRawBody: () => getEdgeWebhookRawBody,
70
73
  getEdgeWebhookSignatureHeader: () => getEdgeWebhookSignatureHeader,
71
- getEnvironmentConfig: () => import_connect15.getEnvironmentConfig,
72
- isApiError: () => import_connect14.isApiError,
73
- isAuthenticationError: () => import_connect14.isAuthenticationError,
74
- isConsentRequiredError: () => import_connect14.isConsentRequiredError,
75
- isEdgeError: () => import_connect14.isEdgeError,
74
+ getEnvironmentConfig: () => import_connect16.getEnvironmentConfig,
75
+ isApiError: () => import_connect15.isApiError,
76
+ isAuthenticationError: () => import_connect15.isAuthenticationError,
77
+ isConsentRequiredError: () => import_connect15.isConsentRequiredError,
78
+ isEdgeError: () => import_connect15.isEdgeError,
76
79
  isEdgeTokenVaultEnvelope: () => isEdgeTokenVaultEnvelope,
77
80
  isEdgeWebhookEvent: () => isEdgeWebhookEvent,
78
- isIdentityVerificationError: () => import_connect14.isIdentityVerificationError,
79
- isNetworkError: () => import_connect14.isNetworkError,
80
- isProductionEnvironment: () => import_connect15.isProductionEnvironment,
81
+ isFeatureUnavailableError: () => import_connect15.isFeatureUnavailableError,
82
+ isIdentityVerificationError: () => import_connect15.isIdentityVerificationError,
83
+ isNetworkError: () => import_connect15.isNetworkError,
84
+ isProductionEnvironment: () => import_connect16.isProductionEnvironment,
81
85
  isTransferWebhookEvent: () => isTransferWebhookEvent,
82
86
  mapTransferStatusToPartnerState: () => mapTransferStatusToPartnerState,
83
87
  moneyAmountsEqual: () => moneyAmountsEqual,
@@ -96,7 +100,10 @@ __export(index_exports, {
96
100
  module.exports = __toCommonJS(index_exports);
97
101
 
98
102
  // src/edge-connect-server.ts
99
- var import_connect4 = require("@edge-markets/connect");
103
+ var import_connect5 = require("@edge-markets/connect");
104
+
105
+ // src/edge-user-client.ts
106
+ var import_connect3 = require("@edge-markets/connect");
100
107
 
101
108
  // src/transfer-helpers.ts
102
109
  var import_connect = require("@edge-markets/connect");
@@ -194,6 +201,7 @@ function mapTransferStatusToPartnerState(status, mapping) {
194
201
  return partnerState;
195
202
  }
196
203
  async function startTransferVerification(client, options) {
204
+ assertTransferFeatureAvailable();
197
205
  const origin = typeof options.origin === "string" ? options.origin.trim() : "";
198
206
  if (!origin) {
199
207
  throw new import_connect.EdgeValidationError("Verification origin is required", {
@@ -213,6 +221,14 @@ async function startTransferVerification(client, options) {
213
221
  const verificationSession = await client.createVerificationSession(transferId, { origin });
214
222
  return { transfer, verificationSession };
215
223
  }
224
+ function assertTransferFeatureAvailable() {
225
+ if (!isTransferFeatureAvailable()) {
226
+ throw new import_connect.EdgeFeatureUnavailableError();
227
+ }
228
+ }
229
+ function isTransferFeatureAvailable() {
230
+ return false;
231
+ }
216
232
  function parseMoneyToCents(value, field, options = {}) {
217
233
  let input;
218
234
  if (typeof value === "number") {
@@ -334,6 +350,7 @@ var EdgeUserClient = class {
334
350
  return this.server._apiRequest("GET", "/balance", this.accessToken);
335
351
  }
336
352
  async initiateTransfer(options) {
353
+ assertTransferFeatureAvailable2();
337
354
  validateTransferOptions(options);
338
355
  const body = {
339
356
  type: options.type,
@@ -346,13 +363,16 @@ var EdgeUserClient = class {
346
363
  return this.server._apiRequest("POST", "/transfer", this.accessToken, body);
347
364
  }
348
365
  async startTransferVerification(options) {
366
+ assertTransferFeatureAvailable2();
349
367
  return startTransferVerification(this, options);
350
368
  }
351
369
  async getTransfer(transferId) {
370
+ assertTransferFeatureAvailable2();
352
371
  validateTransferId(transferId);
353
372
  return this.server._apiRequest("GET", `/transfer/${encodeURIComponent(transferId)}`, this.accessToken);
354
373
  }
355
374
  async listTransfers(params) {
375
+ assertTransferFeatureAvailable2();
356
376
  const queryParams = new URLSearchParams();
357
377
  if (params?.limit) queryParams.set("limit", String(params.limit));
358
378
  if (params?.offset) queryParams.set("offset", String(params.offset));
@@ -365,11 +385,11 @@ var EdgeUserClient = class {
365
385
  return this.server._apiRequest("DELETE", "/consent", this.accessToken);
366
386
  }
367
387
  /**
368
- * Creates an EDGE-hosted transfer verification session.
369
- * Returns a `verificationUrl` to open in an iframe or popup.
370
- * The handoff token is single-use and expires in 120 seconds.
388
+ * Reserved for a future transfer release.
389
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
371
390
  */
372
391
  async createVerificationSession(transferId, options) {
392
+ assertTransferFeatureAvailable2();
373
393
  validateTransferId(transferId);
374
394
  return this.server._apiRequest(
375
395
  "POST",
@@ -379,10 +399,11 @@ var EdgeUserClient = class {
379
399
  );
380
400
  }
381
401
  /**
382
- * Polls the status of a verification session.
383
- * Use as an alternative to postMessage events.
402
+ * Reserved for a future transfer release.
403
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
384
404
  */
385
405
  async getVerificationSessionStatus(transferId, sessionId) {
406
+ assertTransferFeatureAvailable2();
386
407
  validateTransferId(transferId);
387
408
  return this.server._apiRequest(
388
409
  "GET",
@@ -391,6 +412,14 @@ var EdgeUserClient = class {
391
412
  );
392
413
  }
393
414
  };
415
+ function assertTransferFeatureAvailable2() {
416
+ if (!isTransferFeatureAvailable2()) {
417
+ throw new import_connect3.EdgeFeatureUnavailableError();
418
+ }
419
+ }
420
+ function isTransferFeatureAvailable2() {
421
+ return false;
422
+ }
394
423
 
395
424
  // src/mle.ts
396
425
  var import_crypto = require("crypto");
@@ -529,28 +558,28 @@ function validateMtlsConfig(config) {
529
558
  }
530
559
 
531
560
  // src/session.ts
532
- var import_connect3 = require("@edge-markets/connect");
561
+ var import_connect4 = require("@edge-markets/connect");
533
562
  var EdgeUserSession = class {
534
563
  constructor(options) {
535
564
  this.refreshPromise = null;
536
565
  if (!options.edge?.forUser || !options.edge?.refreshTokens) {
537
- throw new import_connect3.EdgeValidationError("EdgeUserSession requires an EdgeConnectServer instance", {
566
+ throw new import_connect4.EdgeValidationError("EdgeUserSession requires an EdgeConnectServer instance", {
538
567
  edge: ["Provide edge.forUser and edge.refreshTokens"]
539
568
  });
540
569
  }
541
570
  const subjectId = typeof options.subjectId === "string" ? options.subjectId.trim() : "";
542
571
  if (!subjectId) {
543
- throw new import_connect3.EdgeValidationError("EdgeUserSession subjectId is required", {
572
+ throw new import_connect4.EdgeValidationError("EdgeUserSession subjectId is required", {
544
573
  subjectId: ["Required"]
545
574
  });
546
575
  }
547
576
  if (!options.tokenStore?.load || !options.tokenStore?.save) {
548
- throw new import_connect3.EdgeValidationError("EdgeUserSession tokenStore is required", {
577
+ throw new import_connect4.EdgeValidationError("EdgeUserSession tokenStore is required", {
549
578
  tokenStore: ["Provide load(subjectId) and save(subjectId, value, context)"]
550
579
  });
551
580
  }
552
581
  if (options.refreshSkewMs !== void 0 && (!Number.isFinite(options.refreshSkewMs) || options.refreshSkewMs < 0)) {
553
- throw new import_connect3.EdgeValidationError("refreshSkewMs must be a non-negative number", {
582
+ throw new import_connect4.EdgeValidationError("refreshSkewMs must be a non-negative number", {
554
583
  refreshSkewMs: ["Must be greater than or equal to 0"]
555
584
  });
556
585
  }
@@ -603,13 +632,13 @@ var EdgeUserSession = class {
603
632
  async loadTokens() {
604
633
  const value = await this.tokenStore.load(this.subjectId);
605
634
  if (!value) {
606
- throw new import_connect3.EdgeAuthenticationError("No EDGE tokens are stored for this subject", {
635
+ throw new import_connect4.EdgeAuthenticationError("No EDGE tokens are stored for this subject", {
607
636
  subjectId: this.subjectId
608
637
  });
609
638
  }
610
639
  if (typeof value === "string") {
611
640
  if (!this.tokenVault) {
612
- throw new import_connect3.EdgeAuthenticationError("Encrypted EDGE token envelope loaded but no tokenVault was configured", {
641
+ throw new import_connect4.EdgeAuthenticationError("Encrypted EDGE token envelope loaded but no tokenVault was configured", {
613
642
  subjectId: this.subjectId
614
643
  });
615
644
  }
@@ -695,7 +724,7 @@ function validateTokens(tokens) {
695
724
  }
696
725
  }
697
726
  if (Object.keys(errors).length > 0) {
698
- throw new import_connect3.EdgeValidationError("Invalid EDGE token payload", errors);
727
+ throw new import_connect4.EdgeValidationError("Invalid EDGE token payload", errors);
699
728
  }
700
729
  }
701
730
 
@@ -886,7 +915,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
886
915
  throw new Error("EdgeConnectServer: environment is required");
887
916
  }
888
917
  this.config = config;
889
- const envConfig = (0, import_connect4.getEnvironmentConfig)(config.environment);
918
+ const envConfig = (0, import_connect5.getEnvironmentConfig)(config.environment);
890
919
  this.apiBaseUrl = config.apiBaseUrl || envConfig.apiBaseUrl;
891
920
  this.partnerApiBaseUrl = stripTrailingSlash(config.partnerApiBaseUrl || derivePartnerApiBaseUrl(this.apiBaseUrl));
892
921
  this.oauthBaseUrl = config.oauthBaseUrl || envConfig.oauthBaseUrl;
@@ -931,7 +960,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
931
960
  */
932
961
  forUser(accessToken) {
933
962
  if (!accessToken) {
934
- throw new import_connect4.EdgeAuthenticationError("accessToken is required when calling forUser()");
963
+ throw new import_connect5.EdgeAuthenticationError("accessToken is required when calling forUser()");
935
964
  }
936
965
  return new EdgeUserClient(this, accessToken);
937
966
  }
@@ -968,8 +997,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
968
997
  const data = await response.json();
969
998
  return this.parseTokenResponse(data);
970
999
  } catch (error) {
971
- if (error instanceof import_connect4.EdgeError) throw error;
972
- throw new import_connect4.EdgeNetworkError("Failed to exchange code", error);
1000
+ if (error instanceof import_connect5.EdgeError) throw error;
1001
+ throw new import_connect5.EdgeNetworkError("Failed to exchange code", error);
973
1002
  }
974
1003
  }
975
1004
  async refreshTokens(refreshToken) {
@@ -995,7 +1024,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
995
1024
  );
996
1025
  if (!response.ok) {
997
1026
  const error = await response.json().catch(() => ({}));
998
- throw new import_connect4.EdgeAuthenticationError(
1027
+ throw new import_connect5.EdgeAuthenticationError(
999
1028
  error.message || error.error_description || "Token refresh failed. User may need to reconnect.",
1000
1029
  { tokenError: error }
1001
1030
  );
@@ -1003,8 +1032,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
1003
1032
  const data = await response.json();
1004
1033
  return this.parseTokenResponse(data, refreshToken);
1005
1034
  } catch (error) {
1006
- if (error instanceof import_connect4.EdgeError) throw error;
1007
- throw new import_connect4.EdgeNetworkError("Failed to refresh tokens", error);
1035
+ if (error instanceof import_connect5.EdgeError) throw error;
1036
+ throw new import_connect5.EdgeNetworkError("Failed to refresh tokens", error);
1008
1037
  }
1009
1038
  }
1010
1039
  /** @internal Called by {@link EdgeUserClient} — not part of the public API. */
@@ -1046,14 +1075,14 @@ var EdgeConnectServer = class _EdgeConnectServer {
1046
1075
  if (mleEnabled && typeof rawResponseBody?.jwe === "string") {
1047
1076
  responseBody = decryptMle(rawResponseBody.jwe, mleConfig);
1048
1077
  } else if (!mleEnabled && typeof rawResponseBody?.jwe === "string") {
1049
- throw new import_connect4.EdgeApiError(
1078
+ throw new import_connect5.EdgeApiError(
1050
1079
  "mle_required",
1051
1080
  "The API responded with message-level encryption. Enable MLE in SDK config.",
1052
1081
  response.status,
1053
1082
  rawResponseBody
1054
1083
  );
1055
1084
  } else if (mleEnabled && mleConfig?.strictResponseEncryption !== false && response.ok && typeof rawResponseBody?.jwe !== "string") {
1056
- throw new import_connect4.EdgeApiError(
1085
+ throw new import_connect5.EdgeApiError(
1057
1086
  "mle_response_missing",
1058
1087
  "Expected encrypted response payload but received plaintext.",
1059
1088
  response.status,
@@ -1071,8 +1100,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
1071
1100
  }
1072
1101
  return responseBody;
1073
1102
  } catch (error) {
1074
- if (error instanceof import_connect4.EdgeError) {
1075
- if (!(error instanceof import_connect4.EdgeNetworkError) || attempt >= this.retryConfig.maxRetries) {
1103
+ if (error instanceof import_connect5.EdgeError) {
1104
+ if (!(error instanceof import_connect5.EdgeNetworkError) || attempt >= this.retryConfig.maxRetries) {
1076
1105
  throw error;
1077
1106
  }
1078
1107
  lastError = error;
@@ -1080,11 +1109,11 @@ var EdgeConnectServer = class _EdgeConnectServer {
1080
1109
  }
1081
1110
  lastError = error;
1082
1111
  if (attempt >= this.retryConfig.maxRetries) {
1083
- throw new import_connect4.EdgeNetworkError(`API request failed: ${method} ${path}`, lastError);
1112
+ throw new import_connect5.EdgeNetworkError(`API request failed: ${method} ${path}`, lastError);
1084
1113
  }
1085
1114
  }
1086
1115
  }
1087
- throw new import_connect4.EdgeNetworkError(
1116
+ throw new import_connect5.EdgeNetworkError(
1088
1117
  `API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
1089
1118
  lastError
1090
1119
  );
@@ -1128,10 +1157,10 @@ var EdgeConnectServer = class _EdgeConnectServer {
1128
1157
  */
1129
1158
  async syncWebhookEvents(options = {}) {
1130
1159
  if (!this.config.partnerClientId) {
1131
- throw new import_connect4.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientId` in EdgeConnectServer config");
1160
+ throw new import_connect5.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientId` in EdgeConnectServer config");
1132
1161
  }
1133
1162
  if (!this.config.partnerClientSecret) {
1134
- throw new import_connect4.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientSecret` in EdgeConnectServer config");
1163
+ throw new import_connect5.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientSecret` in EdgeConnectServer config");
1135
1164
  }
1136
1165
  const normalizedOptions = this.normalizeSyncWebhookEventsOptions(options);
1137
1166
  const url = this.buildSyncUrl(normalizedOptions);
@@ -1162,7 +1191,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
1162
1191
  endpoint: "partner_webhook_sync",
1163
1192
  attempt
1164
1193
  });
1165
- throw new import_connect4.EdgeAuthenticationError(
1194
+ throw new import_connect5.EdgeAuthenticationError(
1166
1195
  body.message || "Partner token rejected after refresh \u2014 check partnerClientId/Secret",
1167
1196
  body
1168
1197
  );
@@ -1179,7 +1208,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
1179
1208
  endpoint: "partner_webhook_sync",
1180
1209
  attempt
1181
1210
  });
1182
- throw new import_connect4.EdgeApiError(
1211
+ throw new import_connect5.EdgeApiError(
1183
1212
  body.error || "sync_failed",
1184
1213
  body.message || `syncWebhookEvents failed with status ${response.status}`,
1185
1214
  response.status,
@@ -1225,7 +1254,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
1225
1254
  }
1226
1255
  if (options.limit !== void 0) {
1227
1256
  if (!Number.isInteger(options.limit) || options.limit < SYNC_WEBHOOK_EVENTS_MIN_LIMIT || options.limit > SYNC_WEBHOOK_EVENTS_MAX_LIMIT) {
1228
- throw new import_connect4.EdgeApiError(
1257
+ throw new import_connect5.EdgeApiError(
1229
1258
  "invalid_sync_options",
1230
1259
  `syncWebhookEvents limit must be an integer between ${SYNC_WEBHOOK_EVENTS_MIN_LIMIT} and ${SYNC_WEBHOOK_EVENTS_MAX_LIMIT}`,
1231
1260
  400,
@@ -1295,11 +1324,11 @@ var EdgeConnectServer = class _EdgeConnectServer {
1295
1324
  } catch (error) {
1296
1325
  lastError = error;
1297
1326
  if (attempt >= this.retryConfig.maxRetries) {
1298
- throw new import_connect4.EdgeNetworkError("syncWebhookEvents network failure", lastError);
1327
+ throw new import_connect5.EdgeNetworkError("syncWebhookEvents network failure", lastError);
1299
1328
  }
1300
1329
  }
1301
1330
  }
1302
- throw new import_connect4.EdgeNetworkError(
1331
+ throw new import_connect5.EdgeNetworkError(
1303
1332
  `syncWebhookEvents network failure after ${this.retryConfig.maxRetries} retries`,
1304
1333
  lastError
1305
1334
  );
@@ -1347,7 +1376,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
1347
1376
  this.dispatcher
1348
1377
  );
1349
1378
  } catch (error) {
1350
- throw new import_connect4.EdgeNetworkError("Partner token request network failure", error);
1379
+ throw new import_connect5.EdgeNetworkError("Partner token request network failure", error);
1351
1380
  }
1352
1381
  const durationMs = Date.now() - startTime;
1353
1382
  const data = await response.json().catch(() => ({}));
@@ -1360,13 +1389,13 @@ var EdgeConnectServer = class _EdgeConnectServer {
1360
1389
  endpoint: "oauth_token"
1361
1390
  });
1362
1391
  if (!response.ok) {
1363
- throw new import_connect4.EdgeAuthenticationError(
1392
+ throw new import_connect5.EdgeAuthenticationError(
1364
1393
  data.message || data.error_description || `Partner token request failed with status ${response.status}`,
1365
1394
  data
1366
1395
  );
1367
1396
  }
1368
1397
  if (!data.access_token || typeof data.expires_in !== "number") {
1369
- throw new import_connect4.EdgeAuthenticationError("Partner token response missing access_token or expires_in");
1398
+ throw new import_connect5.EdgeAuthenticationError("Partner token response missing access_token or expires_in");
1370
1399
  }
1371
1400
  this.partnerTokenCache = {
1372
1401
  token: data.access_token,
@@ -1441,37 +1470,37 @@ var EdgeConnectServer = class _EdgeConnectServer {
1441
1470
  const errorCode = error.error;
1442
1471
  const errorMessage = error.message || error.error_description;
1443
1472
  if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
1444
- return new import_connect4.EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
1473
+ return new import_connect5.EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
1445
1474
  edgeBoostError: error
1446
1475
  });
1447
1476
  }
1448
1477
  if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
1449
- return new import_connect4.EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
1478
+ return new import_connect5.EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
1450
1479
  edgeBoostError: error
1451
1480
  });
1452
1481
  }
1453
1482
  if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
1454
- return new import_connect4.EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
1483
+ return new import_connect5.EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
1455
1484
  }
1456
- return new import_connect4.EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
1485
+ return new import_connect5.EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
1457
1486
  edgeBoostError: error,
1458
1487
  statusCode: status
1459
1488
  });
1460
1489
  }
1461
1490
  async handleApiErrorFromBody(error, status, path) {
1462
1491
  if (status === 401) {
1463
- return new import_connect4.EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
1492
+ return new import_connect5.EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
1464
1493
  }
1465
1494
  if (status === 403) {
1466
1495
  if (error.error === "consent_required") {
1467
- return new import_connect4.EdgeConsentRequiredError(
1496
+ return new import_connect5.EdgeConsentRequiredError(
1468
1497
  this.config.clientId,
1469
1498
  error.consentUrl,
1470
1499
  error.message
1471
1500
  );
1472
1501
  }
1473
1502
  if (error.error === "insufficient_scope" || error.error === "insufficient_consent") {
1474
- return new import_connect4.EdgeInsufficientScopeError(
1503
+ return new import_connect5.EdgeInsufficientScopeError(
1475
1504
  error.missing_scopes || error.missingScopes || [],
1476
1505
  error.message
1477
1506
  );
@@ -1493,15 +1522,15 @@ var EdgeConnectServer = class _EdgeConnectServer {
1493
1522
  } else if (path.includes("/transfer")) {
1494
1523
  resourceType = "Transfer";
1495
1524
  }
1496
- return new import_connect4.EdgeNotFoundError(resourceType, resourceId);
1525
+ return new import_connect5.EdgeNotFoundError(resourceType, resourceId);
1497
1526
  }
1498
1527
  if (status === 422 && error.error === "identity_verification_failed") {
1499
- return new import_connect4.EdgeIdentityVerificationError(
1528
+ return new import_connect5.EdgeIdentityVerificationError(
1500
1529
  error.fieldErrors || {},
1501
1530
  error.message
1502
1531
  );
1503
1532
  }
1504
- return new import_connect4.EdgeApiError(
1533
+ return new import_connect5.EdgeApiError(
1505
1534
  error.error || "api_error",
1506
1535
  error.message || error.error_description || `Request failed with status ${status}`,
1507
1536
  status,
@@ -1511,9 +1540,9 @@ var EdgeConnectServer = class _EdgeConnectServer {
1511
1540
  };
1512
1541
 
1513
1542
  // src/env.ts
1514
- var import_connect5 = require("@edge-markets/connect");
1543
+ var import_connect6 = require("@edge-markets/connect");
1515
1544
  var import_node_fs2 = __toESM(require("fs"));
1516
- var ENVIRONMENT_NAMES = new Set(Object.keys(import_connect5.EDGE_ENVIRONMENTS));
1545
+ var ENVIRONMENT_NAMES = new Set(Object.keys(import_connect6.EDGE_ENVIRONMENTS));
1517
1546
  function readEnv(env, name) {
1518
1547
  const value = env[name];
1519
1548
  if (typeof value !== "string") return void 0;
@@ -1546,7 +1575,7 @@ function readPemValue(env, valueName, pathName, warnings) {
1546
1575
  return normalizePem(import_node_fs2.default.readFileSync(filePath, "utf8"));
1547
1576
  } catch (error) {
1548
1577
  const message = error instanceof Error ? error.message : String(error);
1549
- throw new import_connect5.EdgeValidationError(`Failed to read ${pathName}`, {
1578
+ throw new import_connect6.EdgeValidationError(`Failed to read ${pathName}`, {
1550
1579
  [pathName]: [`Unable to read PEM file "${filePath}": ${message}`]
1551
1580
  });
1552
1581
  }
@@ -1554,19 +1583,29 @@ function readPemValue(env, valueName, pathName, warnings) {
1554
1583
  function readEnvironment(env) {
1555
1584
  const value = readEnv(env, "EDGE_ENVIRONMENT") || "staging";
1556
1585
  if (!ENVIRONMENT_NAMES.has(value)) {
1557
- throw new import_connect5.EdgeValidationError("Invalid EDGE_ENVIRONMENT", {
1586
+ throw new import_connect6.EdgeValidationError("Invalid EDGE_ENVIRONMENT", {
1558
1587
  EDGE_ENVIRONMENT: [`Must be one of: ${Array.from(ENVIRONMENT_NAMES).join(", ")}`]
1559
1588
  });
1560
1589
  }
1561
1590
  return value;
1562
1591
  }
1592
+ function readBooleanEnv(env, name) {
1593
+ const value = readEnv(env, name);
1594
+ if (!value) return void 0;
1595
+ const normalized = value.toLowerCase();
1596
+ if (["true", "1", "yes"].includes(normalized)) return true;
1597
+ if (["false", "0", "no"].includes(normalized)) return false;
1598
+ throw new import_connect6.EdgeValidationError(`Invalid ${name}`, {
1599
+ [name]: ["Must be one of: true, false, 1, 0, yes, no"]
1600
+ });
1601
+ }
1563
1602
  function readTimeout(env, override) {
1564
1603
  if (override !== void 0) return override;
1565
1604
  const value = readFirstEnv(env, ["EDGE_TIMEOUT_MS", "EDGE_REQUEST_TIMEOUT_MS"]);
1566
1605
  if (!value) return void 0;
1567
1606
  const timeout = Number(value);
1568
1607
  if (!Number.isInteger(timeout) || timeout <= 0) {
1569
- throw new import_connect5.EdgeValidationError("Invalid EDGE_TIMEOUT_MS", {
1608
+ throw new import_connect6.EdgeValidationError("Invalid EDGE_TIMEOUT_MS", {
1570
1609
  EDGE_TIMEOUT_MS: ["Must be a positive integer number of milliseconds"]
1571
1610
  });
1572
1611
  }
@@ -1575,10 +1614,15 @@ function readTimeout(env, override) {
1575
1614
  function buildMtlsConfig(env, requireMtls, warnings) {
1576
1615
  const cert = readPemValue(env, "EDGE_MTLS_CERT", "EDGE_MTLS_CERT_PATH", warnings);
1577
1616
  const key = readPemValue(env, "EDGE_MTLS_KEY", "EDGE_MTLS_KEY_PATH", warnings);
1578
- const ca = readPemValue(env, "EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH", warnings);
1617
+ const serverCa = readPemValue(env, "EDGE_SERVER_CA_PEM", "EDGE_SERVER_CA_PEM_PATH", warnings);
1618
+ const hasLegacyCa = !!readFirstEnv(env, ["EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH"]);
1619
+ if (serverCa && hasLegacyCa) {
1620
+ warnings.push("EDGE_SERVER_CA_PEM and EDGE_MTLS_CA are both set; using EDGE_SERVER_CA_PEM");
1621
+ }
1622
+ const ca = serverCa || readPemValue(env, "EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH", warnings);
1579
1623
  if (!cert && !key && !ca) {
1580
1624
  if (requireMtls) {
1581
- throw new import_connect5.EdgeValidationError("mTLS is required but no EDGE_MTLS_CERT or EDGE_MTLS_KEY was provided", {
1625
+ throw new import_connect6.EdgeValidationError("mTLS is required but no EDGE_MTLS_CERT or EDGE_MTLS_KEY was provided", {
1582
1626
  EDGE_MTLS_CERT: ["Required when requireMtls is true"],
1583
1627
  EDGE_MTLS_KEY: ["Required when requireMtls is true"]
1584
1628
  });
@@ -1586,7 +1630,7 @@ function buildMtlsConfig(env, requireMtls, warnings) {
1586
1630
  return void 0;
1587
1631
  }
1588
1632
  if (!cert || !key) {
1589
- throw new import_connect5.EdgeValidationError("Incomplete mTLS configuration", {
1633
+ throw new import_connect6.EdgeValidationError("Incomplete mTLS configuration", {
1590
1634
  EDGE_MTLS_CERT: cert ? [] : ["Required when mTLS is configured"],
1591
1635
  EDGE_MTLS_KEY: key ? [] : ["Required when mTLS is configured"]
1592
1636
  });
@@ -1600,7 +1644,7 @@ function buildMtlsConfig(env, requireMtls, warnings) {
1600
1644
  }
1601
1645
  function inferMtlsRequirement(environment, apiBaseUrl) {
1602
1646
  if (environment === "staging" || environment === "production") return true;
1603
- const resolvedApiBaseUrl = apiBaseUrl || (0, import_connect5.getEnvironmentConfig)(environment).apiBaseUrl;
1647
+ const resolvedApiBaseUrl = apiBaseUrl || (0, import_connect6.getEnvironmentConfig)(environment).apiBaseUrl;
1604
1648
  try {
1605
1649
  const host = new URL(resolvedApiBaseUrl).host;
1606
1650
  return host === "connect-staging.edgeboost.io" || host === "connect.edgeboost.io";
@@ -1614,7 +1658,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
1614
1658
  const environment = readEnvironment(env);
1615
1659
  const apiBaseUrl = readEnv(env, "EDGE_API_BASE_URL");
1616
1660
  const mtlsRequiredByTarget = inferMtlsRequirement(environment, apiBaseUrl);
1617
- const requireMtls = options.requireMtls ?? false;
1661
+ const requireMtls = options.requireMtls ?? readBooleanEnv(env, "EDGE_REQUIRE_MTLS") ?? false;
1618
1662
  const mtls = buildMtlsConfig(env, requireMtls, warnings);
1619
1663
  const timeout = readTimeout(env, options.timeout);
1620
1664
  const clientId = readFirstEnv(env, ["EDGE_CLIENT_ID", "EDGE_CONNECT_CLIENT_ID"]) || "";
@@ -1627,7 +1671,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
1627
1671
  if (!clientId) validationErrors.EDGE_CLIENT_ID = ["Required"];
1628
1672
  if (!clientSecret) validationErrors.EDGE_CLIENT_SECRET = ["Required"];
1629
1673
  if (Object.keys(validationErrors).length > 0) {
1630
- throw new import_connect5.EdgeValidationError("Missing required EDGE Connect environment variables", validationErrors);
1674
+ throw new import_connect6.EdgeValidationError("Missing required EDGE Connect environment variables", validationErrors);
1631
1675
  }
1632
1676
  if (mtlsRequiredByTarget && !mtls) {
1633
1677
  warnings.push(
@@ -1666,32 +1710,35 @@ function createEdgeConnectServerFromEnv(options = {}) {
1666
1710
  }
1667
1711
 
1668
1712
  // src/error-adapter.ts
1669
- var import_connect6 = require("@edge-markets/connect");
1713
+ var import_connect7 = require("@edge-markets/connect");
1670
1714
  function toEdgeHttpError(error) {
1671
1715
  const body = toEdgeProblemJson(error);
1672
1716
  return { status: body.status, body };
1673
1717
  }
1674
1718
  function toEdgeProblemJson(error) {
1675
- if (error instanceof import_connect6.EdgeConsentRequiredError) {
1719
+ if (error instanceof import_connect7.EdgeConsentRequiredError) {
1676
1720
  return problem(error, "Consent required", error.statusCode ?? 403, {
1677
1721
  clientId: error.clientId,
1678
1722
  consentUrl: error.consentUrl
1679
1723
  });
1680
1724
  }
1681
- if (error instanceof import_connect6.EdgeValidationError) {
1725
+ if (error instanceof import_connect7.EdgeValidationError) {
1682
1726
  return problem(error, "Validation failed", error.statusCode ?? 400, {
1683
1727
  validationErrors: error.validationErrors
1684
1728
  });
1685
1729
  }
1686
- if (error instanceof import_connect6.EdgeIdentityVerificationError) {
1730
+ if (error instanceof import_connect7.EdgeIdentityVerificationError) {
1687
1731
  return problem(error, "Identity verification failed", error.statusCode ?? 422, {
1688
1732
  fieldErrors: error.fieldErrors
1689
1733
  });
1690
1734
  }
1691
- if (error instanceof import_connect6.EdgeNetworkError) {
1735
+ if (error instanceof import_connect7.EdgeFeatureUnavailableError) {
1736
+ return problem(error, "Feature unavailable", error.statusCode ?? 501);
1737
+ }
1738
+ if (error instanceof import_connect7.EdgeNetworkError) {
1692
1739
  return problem(error, "EDGE network error", error.statusCode ?? 502);
1693
1740
  }
1694
- if (error instanceof import_connect6.EdgeError) {
1741
+ if (error instanceof import_connect7.EdgeError) {
1695
1742
  return problem(error, edgeTitle(error), error.statusCode ?? 500, error.details);
1696
1743
  }
1697
1744
  const message = error instanceof Error ? error.message : "Unexpected error";
@@ -1718,10 +1765,10 @@ function edgeTitle(error) {
1718
1765
  }
1719
1766
 
1720
1767
  // src/http-helpers.ts
1721
- var import_connect8 = require("@edge-markets/connect");
1768
+ var import_connect9 = require("@edge-markets/connect");
1722
1769
 
1723
1770
  // src/webhook-parser.ts
1724
- var import_connect7 = require("@edge-markets/connect");
1771
+ var import_connect8 = require("@edge-markets/connect");
1725
1772
 
1726
1773
  // src/webhook-signing.ts
1727
1774
  var import_node_crypto2 = __toESM(require("crypto"));
@@ -1753,23 +1800,23 @@ function verifyWebhookSignature(header, body, secret, options = {}) {
1753
1800
  }
1754
1801
 
1755
1802
  // src/webhook-parser.ts
1756
- var EVENT_TYPES = new Set(import_connect7.EDGE_WEBHOOK_EVENT_TYPES);
1803
+ var EVENT_TYPES = new Set(import_connect8.EDGE_WEBHOOK_EVENT_TYPES);
1757
1804
  var TRANSFER_TYPES = /* @__PURE__ */ new Set(["debit", "credit"]);
1758
1805
  function parseAndVerifyWebhook(options) {
1759
1806
  const body = normalizeRawBody(options.rawBody);
1760
1807
  const header = normalizeSignatureHeader(options.signatureHeader);
1761
1808
  const signatureTimestamp = extractWebhookSignatureTimestamp(header);
1762
1809
  if (!header || !signatureTimestamp) {
1763
- throw new import_connect7.EdgeAuthenticationError("Missing or malformed EDGE webhook signature");
1810
+ throw new import_connect8.EdgeAuthenticationError("Missing or malformed EDGE webhook signature");
1764
1811
  }
1765
1812
  if (!verifyWebhookSignature(header, body, options.secret, { toleranceSeconds: options.toleranceSeconds })) {
1766
- throw new import_connect7.EdgeAuthenticationError("Invalid EDGE webhook signature");
1813
+ throw new import_connect8.EdgeAuthenticationError("Invalid EDGE webhook signature");
1767
1814
  }
1768
1815
  let parsed;
1769
1816
  try {
1770
1817
  parsed = JSON.parse(body);
1771
1818
  } catch {
1772
- throw new import_connect7.EdgeValidationError("EDGE webhook body must be valid JSON", {
1819
+ throw new import_connect8.EdgeValidationError("EDGE webhook body must be valid JSON", {
1773
1820
  rawBody: ["Unable to parse JSON after signature verification"]
1774
1821
  });
1775
1822
  }
@@ -1802,7 +1849,7 @@ function isEdgeWebhookEvent(value) {
1802
1849
  function validateEdgeWebhookEvent(value) {
1803
1850
  const errors = {};
1804
1851
  if (!isRecord(value)) {
1805
- throw new import_connect7.EdgeValidationError("EDGE webhook event must be an object", {
1852
+ throw new import_connect8.EdgeValidationError("EDGE webhook event must be an object", {
1806
1853
  event: ["Expected JSON object"]
1807
1854
  });
1808
1855
  }
@@ -1814,7 +1861,7 @@ function validateEdgeWebhookEvent(value) {
1814
1861
  errors.id = ["Required string"];
1815
1862
  }
1816
1863
  if (typeof type !== "string" || !EVENT_TYPES.has(type)) {
1817
- errors.type = [`Must be one of: ${import_connect7.EDGE_WEBHOOK_EVENT_TYPES.join(", ")}`];
1864
+ errors.type = [`Must be one of: ${import_connect8.EDGE_WEBHOOK_EVENT_TYPES.join(", ")}`];
1818
1865
  }
1819
1866
  if (typeof createdAt !== "string" || !createdAt.trim() || Number.isNaN(Date.parse(createdAt))) {
1820
1867
  errors.created_at = ["Required ISO 8601 timestamp string"];
@@ -1826,7 +1873,7 @@ function validateEdgeWebhookEvent(value) {
1826
1873
  validateKnownEventData(type, data, errors);
1827
1874
  }
1828
1875
  if (Object.keys(errors).length > 0) {
1829
- throw new import_connect7.EdgeValidationError("Invalid EDGE webhook event", errors);
1876
+ throw new import_connect8.EdgeValidationError("Invalid EDGE webhook event", errors);
1830
1877
  }
1831
1878
  return value;
1832
1879
  }
@@ -1854,7 +1901,7 @@ function validateKnownEventData(type, data, errors) {
1854
1901
  return;
1855
1902
  default: {
1856
1903
  const exhaustive = type;
1857
- throw new import_connect7.EdgeValidationError("Unsupported EDGE webhook event type", {
1904
+ throw new import_connect8.EdgeValidationError("Unsupported EDGE webhook event type", {
1858
1905
  type: [`Unsupported event type: ${exhaustive}`]
1859
1906
  });
1860
1907
  }
@@ -1887,7 +1934,7 @@ function normalizeRawBody(rawBody) {
1887
1934
  if (Buffer.isBuffer(rawBody) || rawBody instanceof Uint8Array) {
1888
1935
  return Buffer.from(rawBody).toString("utf8");
1889
1936
  }
1890
- throw new import_connect7.EdgeValidationError("EDGE webhook raw body is required", {
1937
+ throw new import_connect8.EdgeValidationError("EDGE webhook raw body is required", {
1891
1938
  rawBody: ["Pass the raw UTF-8 request body, not a parsed JSON object"]
1892
1939
  });
1893
1940
  }
@@ -1908,7 +1955,7 @@ function getEdgeWebhookRawBody(request) {
1908
1955
  if (typeof request.rawBody === "string" || Buffer.isBuffer(request.rawBody) || request.rawBody instanceof Uint8Array) {
1909
1956
  return request.rawBody;
1910
1957
  }
1911
- throw new import_connect8.EdgeValidationError("EDGE webhook raw body is required", {
1958
+ throw new import_connect9.EdgeValidationError("EDGE webhook raw body is required", {
1912
1959
  rawBody: [
1913
1960
  "Configure your framework to expose the raw UTF-8 request body before JSON parsing. Parsed JSON bodies cannot be used for HMAC verification."
1914
1961
  ]
@@ -1935,7 +1982,7 @@ function parseWebhookHttpRequest(options) {
1935
1982
  }
1936
1983
 
1937
1984
  // src/identity.ts
1938
- var import_connect9 = require("@edge-markets/connect");
1985
+ var import_connect10 = require("@edge-markets/connect");
1939
1986
  var DEFAULT_IDENTITY_POLICY = {
1940
1987
  minNameScore: 70,
1941
1988
  minAddressScore: 65,
@@ -1963,7 +2010,7 @@ function buildVerifyIdentityPayload(profile) {
1963
2010
  errors.address = ["At least one address field is required"];
1964
2011
  }
1965
2012
  if (Object.keys(errors).length > 0) {
1966
- throw new import_connect9.EdgeValidationError("Partner identity profile is incomplete", errors);
2013
+ throw new import_connect10.EdgeValidationError("Partner identity profile is incomplete", errors);
1967
2014
  }
1968
2015
  return payload;
1969
2016
  }
@@ -2000,7 +2047,7 @@ function validatePolicy(policy) {
2000
2047
  if (!isScore(policy.minNameScore)) errors.minNameScore = ["Must be between 0 and 100"];
2001
2048
  if (!isScore(policy.minAddressScore)) errors.minAddressScore = ["Must be between 0 and 100"];
2002
2049
  if (Object.keys(errors).length > 0) {
2003
- throw new import_connect9.EdgeValidationError("Invalid identity policy", errors);
2050
+ throw new import_connect10.EdgeValidationError("Invalid identity policy", errors);
2004
2051
  }
2005
2052
  }
2006
2053
  function isScore(value) {
@@ -2017,7 +2064,7 @@ function firstNonEmpty(...values) {
2017
2064
  }
2018
2065
 
2019
2066
  // src/session-store.ts
2020
- var import_connect10 = require("@edge-markets/connect");
2067
+ var import_connect11 = require("@edge-markets/connect");
2021
2068
  function createSessionTokenRecord(subjectId, tokens, tokenVault, options = {}) {
2022
2069
  const normalizedSubjectId = normalizeRequiredString(subjectId, "subjectId");
2023
2070
  validateTokens2(tokens);
@@ -2033,7 +2080,7 @@ function createSessionTokenRecord(subjectId, tokens, tokenVault, options = {}) {
2033
2080
  }
2034
2081
  function parseSessionTokenRecord(record) {
2035
2082
  if (!isRecord2(record)) {
2036
- throw new import_connect10.EdgeValidationError("EDGE session token record must be an object", {
2083
+ throw new import_connect11.EdgeValidationError("EDGE session token record must be an object", {
2037
2084
  record: ["Expected object"]
2038
2085
  });
2039
2086
  }
@@ -2056,7 +2103,7 @@ function encryptLegacySessionTokens(legacyRecord, tokenVault, options) {
2056
2103
  const accessToken = normalizeRequiredString(legacyRecord.accessToken, "accessToken");
2057
2104
  const refreshToken = normalizeRequiredString(legacyRecord.refreshToken, "refreshToken");
2058
2105
  if (!options.allowPlaintext && !tokenVault.isEncrypted(accessToken) && !tokenVault.isEncrypted(refreshToken)) {
2059
- throw new import_connect10.EdgeValidationError("Plaintext token migration requires allowPlaintext: true", {
2106
+ throw new import_connect11.EdgeValidationError("Plaintext token migration requires allowPlaintext: true", {
2060
2107
  allowPlaintext: ["Set allowPlaintext only inside an explicit migration path"]
2061
2108
  });
2062
2109
  }
@@ -2080,7 +2127,7 @@ function normalizeSessionExpiresAt(value) {
2080
2127
  if (value instanceof Date) {
2081
2128
  const time = value.getTime();
2082
2129
  if (!Number.isFinite(time) || time <= 0) {
2083
- throw new import_connect10.EdgeValidationError("Invalid EDGE session expiresAt", {
2130
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
2084
2131
  expiresAt: ["Date must be valid"]
2085
2132
  });
2086
2133
  }
@@ -2089,7 +2136,7 @@ function normalizeSessionExpiresAt(value) {
2089
2136
  if (typeof value === "string") {
2090
2137
  const trimmed = value.trim();
2091
2138
  if (!trimmed) {
2092
- throw new import_connect10.EdgeValidationError("Invalid EDGE session expiresAt", {
2139
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
2093
2140
  expiresAt: ["Required"]
2094
2141
  });
2095
2142
  }
@@ -2101,18 +2148,18 @@ function normalizeSessionExpiresAt(value) {
2101
2148
  }
2102
2149
  if (typeof value === "number" && Number.isFinite(value)) {
2103
2150
  if (!Number.isInteger(value) || value <= 0) {
2104
- throw new import_connect10.EdgeValidationError("Invalid EDGE session expiresAt", {
2151
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
2105
2152
  expiresAt: ["Must be a positive integer timestamp in milliseconds"]
2106
2153
  });
2107
2154
  }
2108
2155
  if (value < 1e10) {
2109
- throw new import_connect10.EdgeValidationError("Ambiguous EDGE session expiresAt", {
2156
+ throw new import_connect11.EdgeValidationError("Ambiguous EDGE session expiresAt", {
2110
2157
  expiresAt: ["Use Unix milliseconds, not seconds"]
2111
2158
  });
2112
2159
  }
2113
2160
  return value;
2114
2161
  }
2115
- throw new import_connect10.EdgeValidationError("Invalid EDGE session expiresAt", {
2162
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
2116
2163
  expiresAt: ["Use Unix milliseconds, ISO string, or Date"]
2117
2164
  });
2118
2165
  }
@@ -2136,7 +2183,7 @@ function normalizeScopes(value) {
2136
2183
  if (typeof value === "string") {
2137
2184
  return splitScopes(value);
2138
2185
  }
2139
- throw new import_connect10.EdgeValidationError("Invalid EDGE session scopes", {
2186
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session scopes", {
2140
2187
  scopes: ["Expected array or space-delimited string"]
2141
2188
  });
2142
2189
  }
@@ -2145,7 +2192,7 @@ function splitScopes(value) {
2145
2192
  }
2146
2193
  function normalizeRequiredString(value, field) {
2147
2194
  if (typeof value !== "string" || !value.trim()) {
2148
- throw new import_connect10.EdgeValidationError("Invalid EDGE session token record", {
2195
+ throw new import_connect11.EdgeValidationError("Invalid EDGE session token record", {
2149
2196
  [field]: ["Required string"]
2150
2197
  });
2151
2198
  }
@@ -2163,7 +2210,7 @@ function isRecord2(value) {
2163
2210
  }
2164
2211
 
2165
2212
  // src/token-vault.ts
2166
- var import_connect11 = require("@edge-markets/connect");
2213
+ var import_connect12 = require("@edge-markets/connect");
2167
2214
  var import_node_crypto3 = __toESM(require("crypto"));
2168
2215
  var TOKEN_VAULT_VERSION = "edge_vault_v1";
2169
2216
  var IV_BYTES = 12;
@@ -2176,7 +2223,7 @@ var EdgeTokenVault = class {
2176
2223
  for (const [index, key] of (options.previousKeys ?? []).entries()) {
2177
2224
  const normalized = normalizeVaultKey(key, `previousKeys[${index}]`);
2178
2225
  if (this.keysById.has(normalized.id)) {
2179
- throw new import_connect11.EdgeValidationError("Duplicate token vault key ID", {
2226
+ throw new import_connect12.EdgeValidationError("Duplicate token vault key ID", {
2180
2227
  keyId: [`Duplicate key ID "${normalized.id}"`]
2181
2228
  });
2182
2229
  }
@@ -2204,7 +2251,7 @@ var EdgeTokenVault = class {
2204
2251
  const parsed = parseEnvelope(envelope);
2205
2252
  const key = this.keysById.get(parsed.keyId);
2206
2253
  if (!key) {
2207
- throw new import_connect11.EdgeValidationError("Unknown token vault key ID", {
2254
+ throw new import_connect12.EdgeValidationError("Unknown token vault key ID", {
2208
2255
  keyId: [`No key configured for "${parsed.keyId}"`]
2209
2256
  });
2210
2257
  }
@@ -2217,8 +2264,8 @@ var EdgeTokenVault = class {
2217
2264
  validateEdgeTokens(tokens);
2218
2265
  return tokens;
2219
2266
  } catch (error) {
2220
- if (error instanceof import_connect11.EdgeValidationError) throw error;
2221
- throw new import_connect11.EdgeValidationError("Failed to decrypt EDGE tokens", {
2267
+ if (error instanceof import_connect12.EdgeValidationError) throw error;
2268
+ throw new import_connect12.EdgeValidationError("Failed to decrypt EDGE tokens", {
2222
2269
  tokenEnvelope: ["Envelope is malformed, corrupted, or encrypted with a different key"]
2223
2270
  });
2224
2271
  }
@@ -2236,7 +2283,7 @@ function isEdgeTokenVaultEnvelope(value) {
2236
2283
  function normalizeVaultKey(input, fieldName) {
2237
2284
  const id = typeof input.id === "string" ? input.id.trim() : "";
2238
2285
  if (!id) {
2239
- throw new import_connect11.EdgeValidationError("Token vault key ID is required", {
2286
+ throw new import_connect12.EdgeValidationError("Token vault key ID is required", {
2240
2287
  [`${fieldName}.id`]: ["Required"]
2241
2288
  });
2242
2289
  }
@@ -2267,7 +2314,7 @@ function normalizeKeyMaterial(input, fieldName) {
2267
2314
  }
2268
2315
  const key = candidates.find((candidate) => candidate.length === KEY_BYTES);
2269
2316
  if (!key) {
2270
- throw new import_connect11.EdgeValidationError("Token vault key must decode to 32 bytes", {
2317
+ throw new import_connect12.EdgeValidationError("Token vault key must decode to 32 bytes", {
2271
2318
  [fieldName]: ["Provide a 32-byte key as base64, base64url, hex, Buffer, or Uint8Array"]
2272
2319
  });
2273
2320
  }
@@ -2298,18 +2345,18 @@ function validateEdgeTokens(tokens) {
2298
2345
  }
2299
2346
  }
2300
2347
  if (Object.keys(errors).length > 0) {
2301
- throw new import_connect11.EdgeValidationError("Invalid EDGE token payload", errors);
2348
+ throw new import_connect12.EdgeValidationError("Invalid EDGE token payload", errors);
2302
2349
  }
2303
2350
  }
2304
2351
  function parseEnvelope(envelope) {
2305
2352
  if (typeof envelope !== "string") {
2306
- throw new import_connect11.EdgeValidationError("Token envelope must be a string", {
2353
+ throw new import_connect12.EdgeValidationError("Token envelope must be a string", {
2307
2354
  tokenEnvelope: ["Expected string"]
2308
2355
  });
2309
2356
  }
2310
2357
  const parts = envelope.split(".");
2311
2358
  if (parts.length !== 5 || parts[0] !== TOKEN_VAULT_VERSION) {
2312
- throw new import_connect11.EdgeValidationError("Invalid EDGE token vault envelope", {
2359
+ throw new import_connect12.EdgeValidationError("Invalid EDGE token vault envelope", {
2313
2360
  tokenEnvelope: [`Expected ${TOKEN_VAULT_VERSION}.<keyId>.<iv>.<tag>.<ciphertext>`]
2314
2361
  });
2315
2362
  }
@@ -2324,7 +2371,7 @@ function parseEnvelope(envelope) {
2324
2371
  if (tag.length !== TAG_BYTES) errors.tag = [`Must be ${TAG_BYTES} bytes`];
2325
2372
  if (ciphertext.length === 0) errors.ciphertext = ["Required"];
2326
2373
  if (Object.keys(errors).length > 0) {
2327
- throw new import_connect11.EdgeValidationError("Invalid EDGE token vault envelope", errors);
2374
+ throw new import_connect12.EdgeValidationError("Invalid EDGE token vault envelope", errors);
2328
2375
  }
2329
2376
  return { keyId, iv, tag, ciphertext };
2330
2377
  }
@@ -2338,13 +2385,13 @@ function fromBase64Url2(value) {
2338
2385
  }
2339
2386
 
2340
2387
  // src/webhook-guards.ts
2341
- var import_connect12 = require("@edge-markets/connect");
2388
+ var import_connect13 = require("@edge-markets/connect");
2342
2389
  function isTransferWebhookEvent(event) {
2343
2390
  return event.type.startsWith("transfer.");
2344
2391
  }
2345
2392
  function assertTransferEventMatchesIntent(event, intent) {
2346
2393
  if (!isTransferWebhookEvent(event)) {
2347
- throw new import_connect12.EdgeValidationError("Webhook event is not a transfer event", {
2394
+ throw new import_connect13.EdgeValidationError("Webhook event is not a transfer event", {
2348
2395
  type: [`Received ${event.type}`]
2349
2396
  });
2350
2397
  }
@@ -2367,34 +2414,34 @@ function assertTransferEventMatchesIntent(event, intent) {
2367
2414
  }
2368
2415
  }
2369
2416
  if (Object.keys(errors).length > 0) {
2370
- throw new import_connect12.EdgeValidationError("Webhook transfer event does not match expected transfer intent", errors);
2417
+ throw new import_connect13.EdgeValidationError("Webhook transfer event does not match expected transfer intent", errors);
2371
2418
  }
2372
2419
  }
2373
2420
  var assertWebhookMatchesTransfer = assertTransferEventMatchesIntent;
2374
2421
 
2375
2422
  // src/webhook-inbox.ts
2376
- var import_connect13 = require("@edge-markets/connect");
2423
+ var import_connect14 = require("@edge-markets/connect");
2377
2424
  var import_node_crypto4 = __toESM(require("crypto"));
2378
2425
  var DEFAULT_PROCESSING_TIMEOUT_MS = 10 * 60 * 1e3;
2379
2426
  var EdgeWebhookInbox = class {
2380
2427
  constructor(options) {
2381
2428
  if (!options.store?.insert || !options.store?.find || !options.store?.markProcessing) {
2382
- throw new import_connect13.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
2429
+ throw new import_connect14.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
2383
2430
  store: ["Provide insert, find, markProcessing, markProcessed, and markFailed callbacks"]
2384
2431
  });
2385
2432
  }
2386
2433
  if (!options.store.markProcessed || !options.store.markFailed) {
2387
- throw new import_connect13.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
2434
+ throw new import_connect14.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
2388
2435
  store: ["Provide markProcessed and markFailed callbacks"]
2389
2436
  });
2390
2437
  }
2391
2438
  if (!options.processEvent) {
2392
- throw new import_connect13.EdgeValidationError("EdgeWebhookInbox processEvent callback is required", {
2439
+ throw new import_connect14.EdgeValidationError("EdgeWebhookInbox processEvent callback is required", {
2393
2440
  processEvent: ["Required"]
2394
2441
  });
2395
2442
  }
2396
2443
  if (options.processingTimeoutMs !== void 0 && (!Number.isFinite(options.processingTimeoutMs) || options.processingTimeoutMs <= 0)) {
2397
- throw new import_connect13.EdgeValidationError("processingTimeoutMs must be positive", {
2444
+ throw new import_connect14.EdgeValidationError("processingTimeoutMs must be positive", {
2398
2445
  processingTimeoutMs: ["Must be greater than 0"]
2399
2446
  });
2400
2447
  }
@@ -2554,7 +2601,7 @@ var EdgeWebhookInbox = class {
2554
2601
  status: result.record.status,
2555
2602
  source: options.source
2556
2603
  });
2557
- throw new import_connect13.EdgeValidationError("Duplicate webhook event payload does not match stored event", {
2604
+ throw new import_connect14.EdgeValidationError("Duplicate webhook event payload does not match stored event", {
2558
2605
  eventId: [event.id]
2559
2606
  });
2560
2607
  }
@@ -2590,7 +2637,7 @@ function fingerprintWebhookEvent(event) {
2590
2637
  }
2591
2638
  function normalizeEventId(eventId) {
2592
2639
  if (typeof eventId !== "string" || !eventId.trim()) {
2593
- throw new import_connect13.EdgeValidationError("Webhook event ID is required", {
2640
+ throw new import_connect14.EdgeValidationError("Webhook event ID is required", {
2594
2641
  eventId: ["Required"]
2595
2642
  });
2596
2643
  }
@@ -2607,17 +2654,20 @@ function stableStringify(value) {
2607
2654
  }
2608
2655
 
2609
2656
  // src/index.ts
2610
- var import_connect14 = require("@edge-markets/connect");
2611
2657
  var import_connect15 = require("@edge-markets/connect");
2658
+ var import_connect16 = require("@edge-markets/connect");
2612
2659
  // Annotate the CommonJS export names for ESM import in node:
2613
2660
  0 && (module.exports = {
2661
+ ACTIVE_EDGE_SCOPES,
2614
2662
  CONNECT_TRANSFER_DIRECTIONS,
2663
+ EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE,
2615
2664
  EDGE_WEBHOOK_EVENT_TYPES,
2616
2665
  EdgeApiError,
2617
2666
  EdgeAuthenticationError,
2618
2667
  EdgeConnectServer,
2619
2668
  EdgeConsentRequiredError,
2620
2669
  EdgeError,
2670
+ EdgeFeatureUnavailableError,
2621
2671
  EdgeIdentityVerificationError,
2622
2672
  EdgeInsufficientScopeError,
2623
2673
  EdgeNetworkError,
@@ -2656,6 +2706,7 @@ var import_connect15 = require("@edge-markets/connect");
2656
2706
  isEdgeError,
2657
2707
  isEdgeTokenVaultEnvelope,
2658
2708
  isEdgeWebhookEvent,
2709
+ isFeatureUnavailableError,
2659
2710
  isIdentityVerificationError,
2660
2711
  isNetworkError,
2661
2712
  isProductionEnvironment,
package/dist/index.mjs CHANGED
@@ -19,8 +19,12 @@ import {
19
19
  getEnvironmentConfig
20
20
  } from "@edge-markets/connect";
21
21
 
22
+ // src/edge-user-client.ts
23
+ import { EdgeFeatureUnavailableError as EdgeFeatureUnavailableError2 } from "@edge-markets/connect";
24
+
22
25
  // src/transfer-helpers.ts
23
26
  import {
27
+ EdgeFeatureUnavailableError,
24
28
  EdgeValidationError
25
29
  } from "@edge-markets/connect";
26
30
  import crypto from "crypto";
@@ -117,6 +121,7 @@ function mapTransferStatusToPartnerState(status, mapping) {
117
121
  return partnerState;
118
122
  }
119
123
  async function startTransferVerification(client, options) {
124
+ assertTransferFeatureAvailable();
120
125
  const origin = typeof options.origin === "string" ? options.origin.trim() : "";
121
126
  if (!origin) {
122
127
  throw new EdgeValidationError("Verification origin is required", {
@@ -136,6 +141,14 @@ async function startTransferVerification(client, options) {
136
141
  const verificationSession = await client.createVerificationSession(transferId, { origin });
137
142
  return { transfer, verificationSession };
138
143
  }
144
+ function assertTransferFeatureAvailable() {
145
+ if (!isTransferFeatureAvailable()) {
146
+ throw new EdgeFeatureUnavailableError();
147
+ }
148
+ }
149
+ function isTransferFeatureAvailable() {
150
+ return false;
151
+ }
139
152
  function parseMoneyToCents(value, field, options = {}) {
140
153
  let input;
141
154
  if (typeof value === "number") {
@@ -257,6 +270,7 @@ var EdgeUserClient = class {
257
270
  return this.server._apiRequest("GET", "/balance", this.accessToken);
258
271
  }
259
272
  async initiateTransfer(options) {
273
+ assertTransferFeatureAvailable2();
260
274
  validateTransferOptions(options);
261
275
  const body = {
262
276
  type: options.type,
@@ -269,13 +283,16 @@ var EdgeUserClient = class {
269
283
  return this.server._apiRequest("POST", "/transfer", this.accessToken, body);
270
284
  }
271
285
  async startTransferVerification(options) {
286
+ assertTransferFeatureAvailable2();
272
287
  return startTransferVerification(this, options);
273
288
  }
274
289
  async getTransfer(transferId) {
290
+ assertTransferFeatureAvailable2();
275
291
  validateTransferId(transferId);
276
292
  return this.server._apiRequest("GET", `/transfer/${encodeURIComponent(transferId)}`, this.accessToken);
277
293
  }
278
294
  async listTransfers(params) {
295
+ assertTransferFeatureAvailable2();
279
296
  const queryParams = new URLSearchParams();
280
297
  if (params?.limit) queryParams.set("limit", String(params.limit));
281
298
  if (params?.offset) queryParams.set("offset", String(params.offset));
@@ -288,11 +305,11 @@ var EdgeUserClient = class {
288
305
  return this.server._apiRequest("DELETE", "/consent", this.accessToken);
289
306
  }
290
307
  /**
291
- * Creates an EDGE-hosted transfer verification session.
292
- * Returns a `verificationUrl` to open in an iframe or popup.
293
- * The handoff token is single-use and expires in 120 seconds.
308
+ * Reserved for a future transfer release.
309
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
294
310
  */
295
311
  async createVerificationSession(transferId, options) {
312
+ assertTransferFeatureAvailable2();
296
313
  validateTransferId(transferId);
297
314
  return this.server._apiRequest(
298
315
  "POST",
@@ -302,10 +319,11 @@ var EdgeUserClient = class {
302
319
  );
303
320
  }
304
321
  /**
305
- * Polls the status of a verification session.
306
- * Use as an alternative to postMessage events.
322
+ * Reserved for a future transfer release.
323
+ * Currently throws EdgeFeatureUnavailableError before validation or network I/O.
307
324
  */
308
325
  async getVerificationSessionStatus(transferId, sessionId) {
326
+ assertTransferFeatureAvailable2();
309
327
  validateTransferId(transferId);
310
328
  return this.server._apiRequest(
311
329
  "GET",
@@ -314,6 +332,14 @@ var EdgeUserClient = class {
314
332
  );
315
333
  }
316
334
  };
335
+ function assertTransferFeatureAvailable2() {
336
+ if (!isTransferFeatureAvailable2()) {
337
+ throw new EdgeFeatureUnavailableError2();
338
+ }
339
+ }
340
+ function isTransferFeatureAvailable2() {
341
+ return false;
342
+ }
317
343
 
318
344
  // src/mle.ts
319
345
  import { randomUUID, randomBytes, createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from "crypto";
@@ -1483,6 +1509,16 @@ function readEnvironment(env) {
1483
1509
  }
1484
1510
  return value;
1485
1511
  }
1512
+ function readBooleanEnv(env, name) {
1513
+ const value = readEnv(env, name);
1514
+ if (!value) return void 0;
1515
+ const normalized = value.toLowerCase();
1516
+ if (["true", "1", "yes"].includes(normalized)) return true;
1517
+ if (["false", "0", "no"].includes(normalized)) return false;
1518
+ throw new EdgeValidationError4(`Invalid ${name}`, {
1519
+ [name]: ["Must be one of: true, false, 1, 0, yes, no"]
1520
+ });
1521
+ }
1486
1522
  function readTimeout(env, override) {
1487
1523
  if (override !== void 0) return override;
1488
1524
  const value = readFirstEnv(env, ["EDGE_TIMEOUT_MS", "EDGE_REQUEST_TIMEOUT_MS"]);
@@ -1498,7 +1534,12 @@ function readTimeout(env, override) {
1498
1534
  function buildMtlsConfig(env, requireMtls, warnings) {
1499
1535
  const cert = readPemValue(env, "EDGE_MTLS_CERT", "EDGE_MTLS_CERT_PATH", warnings);
1500
1536
  const key = readPemValue(env, "EDGE_MTLS_KEY", "EDGE_MTLS_KEY_PATH", warnings);
1501
- const ca = readPemValue(env, "EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH", warnings);
1537
+ const serverCa = readPemValue(env, "EDGE_SERVER_CA_PEM", "EDGE_SERVER_CA_PEM_PATH", warnings);
1538
+ const hasLegacyCa = !!readFirstEnv(env, ["EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH"]);
1539
+ if (serverCa && hasLegacyCa) {
1540
+ warnings.push("EDGE_SERVER_CA_PEM and EDGE_MTLS_CA are both set; using EDGE_SERVER_CA_PEM");
1541
+ }
1542
+ const ca = serverCa || readPemValue(env, "EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH", warnings);
1502
1543
  if (!cert && !key && !ca) {
1503
1544
  if (requireMtls) {
1504
1545
  throw new EdgeValidationError4("mTLS is required but no EDGE_MTLS_CERT or EDGE_MTLS_KEY was provided", {
@@ -1537,7 +1578,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
1537
1578
  const environment = readEnvironment(env);
1538
1579
  const apiBaseUrl = readEnv(env, "EDGE_API_BASE_URL");
1539
1580
  const mtlsRequiredByTarget = inferMtlsRequirement(environment, apiBaseUrl);
1540
- const requireMtls = options.requireMtls ?? false;
1581
+ const requireMtls = options.requireMtls ?? readBooleanEnv(env, "EDGE_REQUIRE_MTLS") ?? false;
1541
1582
  const mtls = buildMtlsConfig(env, requireMtls, warnings);
1542
1583
  const timeout = readTimeout(env, options.timeout);
1543
1584
  const clientId = readFirstEnv(env, ["EDGE_CLIENT_ID", "EDGE_CONNECT_CLIENT_ID"]) || "";
@@ -1592,6 +1633,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
1592
1633
  import {
1593
1634
  EdgeConsentRequiredError as EdgeConsentRequiredError2,
1594
1635
  EdgeError as EdgeError2,
1636
+ EdgeFeatureUnavailableError as EdgeFeatureUnavailableError3,
1595
1637
  EdgeIdentityVerificationError as EdgeIdentityVerificationError2,
1596
1638
  EdgeNetworkError as EdgeNetworkError2,
1597
1639
  EdgeValidationError as EdgeValidationError5
@@ -1617,6 +1659,9 @@ function toEdgeProblemJson(error) {
1617
1659
  fieldErrors: error.fieldErrors
1618
1660
  });
1619
1661
  }
1662
+ if (error instanceof EdgeFeatureUnavailableError3) {
1663
+ return problem(error, "Feature unavailable", error.statusCode ?? 501);
1664
+ }
1620
1665
  if (error instanceof EdgeNetworkError2) {
1621
1666
  return problem(error, "EDGE network error", error.statusCode ?? 502);
1622
1667
  }
@@ -2541,10 +2586,12 @@ function stableStringify(value) {
2541
2586
 
2542
2587
  // src/index.ts
2543
2588
  import {
2589
+ EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE,
2544
2590
  EdgeApiError as EdgeApiError2,
2545
2591
  EdgeAuthenticationError as EdgeAuthenticationError4,
2546
2592
  EdgeConsentRequiredError as EdgeConsentRequiredError3,
2547
2593
  EdgeError as EdgeError3,
2594
+ EdgeFeatureUnavailableError as EdgeFeatureUnavailableError4,
2548
2595
  EdgeIdentityVerificationError as EdgeIdentityVerificationError3,
2549
2596
  EdgeInsufficientScopeError as EdgeInsufficientScopeError2,
2550
2597
  EdgeNetworkError as EdgeNetworkError3,
@@ -2555,23 +2602,28 @@ import {
2555
2602
  isAuthenticationError,
2556
2603
  isConsentRequiredError,
2557
2604
  isEdgeError,
2605
+ isFeatureUnavailableError,
2558
2606
  isIdentityVerificationError,
2559
2607
  isNetworkError
2560
2608
  } from "@edge-markets/connect";
2561
2609
  import {
2610
+ ACTIVE_EDGE_SCOPES,
2562
2611
  EDGE_WEBHOOK_EVENT_TYPES as EDGE_WEBHOOK_EVENT_TYPES2,
2563
2612
  TRANSFER_CATEGORIES,
2564
2613
  getEnvironmentConfig as getEnvironmentConfig3,
2565
2614
  isProductionEnvironment
2566
2615
  } from "@edge-markets/connect";
2567
2616
  export {
2617
+ ACTIVE_EDGE_SCOPES,
2568
2618
  CONNECT_TRANSFER_DIRECTIONS,
2619
+ EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE,
2569
2620
  EDGE_WEBHOOK_EVENT_TYPES2 as EDGE_WEBHOOK_EVENT_TYPES,
2570
2621
  EdgeApiError2 as EdgeApiError,
2571
2622
  EdgeAuthenticationError4 as EdgeAuthenticationError,
2572
2623
  EdgeConnectServer,
2573
2624
  EdgeConsentRequiredError3 as EdgeConsentRequiredError,
2574
2625
  EdgeError3 as EdgeError,
2626
+ EdgeFeatureUnavailableError4 as EdgeFeatureUnavailableError,
2575
2627
  EdgeIdentityVerificationError3 as EdgeIdentityVerificationError,
2576
2628
  EdgeInsufficientScopeError2 as EdgeInsufficientScopeError,
2577
2629
  EdgeNetworkError3 as EdgeNetworkError,
@@ -2610,6 +2662,7 @@ export {
2610
2662
  isEdgeError,
2611
2663
  isEdgeTokenVaultEnvelope,
2612
2664
  isEdgeWebhookEvent,
2665
+ isFeatureUnavailableError,
2613
2666
  isIdentityVerificationError,
2614
2667
  isNetworkError,
2615
2668
  isProductionEnvironment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edge-markets/connect-node",
3
- "version": "1.9.0",
3
+ "version": "1.10.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.7.0"
25
+ "@edge-markets/connect": "^1.8.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "tsup": "^8.0.0",