@edge-markets/connect-node 1.9.0 → 1.10.1
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 +37 -1
- package/dist/index.d.mts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.js +184 -134
- package/dist/index.mjs +62 -17
- package/package.json +4 -3
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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: () =>
|
|
39
|
-
EdgeError: () =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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: () =>
|
|
51
|
+
EdgeValidationError: () => import_connect15.EdgeValidationError,
|
|
49
52
|
EdgeWebhookInbox: () => EdgeWebhookInbox,
|
|
50
53
|
EdgeWebhookReconciler: () => EdgeWebhookReconciler,
|
|
51
|
-
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: () =>
|
|
72
|
-
isApiError: () =>
|
|
73
|
-
isAuthenticationError: () =>
|
|
74
|
-
isConsentRequiredError: () =>
|
|
75
|
-
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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
|
-
*
|
|
369
|
-
*
|
|
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
|
-
*
|
|
383
|
-
*
|
|
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");
|
|
@@ -471,6 +500,7 @@ function fromBase64Url(value) {
|
|
|
471
500
|
var import_node_fs = __toESM(require("fs"));
|
|
472
501
|
var import_node_https = __toESM(require("https"));
|
|
473
502
|
var import_node_tls = __toESM(require("tls"));
|
|
503
|
+
var import_undici = require("undici");
|
|
474
504
|
var CERT_PATTERN = /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g;
|
|
475
505
|
function normalizeCaBundle(ca) {
|
|
476
506
|
if (!ca) return [];
|
|
@@ -508,9 +538,7 @@ function createHttpsAgent(config) {
|
|
|
508
538
|
}
|
|
509
539
|
function createUndiciDispatcher(config) {
|
|
510
540
|
const ca = buildServerCaBundle(config.ca);
|
|
511
|
-
|
|
512
|
-
const { Agent } = require(moduleName);
|
|
513
|
-
return new Agent({
|
|
541
|
+
return new import_undici.Agent({
|
|
514
542
|
connect: {
|
|
515
543
|
cert: config.cert,
|
|
516
544
|
key: config.key,
|
|
@@ -529,28 +557,28 @@ function validateMtlsConfig(config) {
|
|
|
529
557
|
}
|
|
530
558
|
|
|
531
559
|
// src/session.ts
|
|
532
|
-
var
|
|
560
|
+
var import_connect4 = require("@edge-markets/connect");
|
|
533
561
|
var EdgeUserSession = class {
|
|
534
562
|
constructor(options) {
|
|
535
563
|
this.refreshPromise = null;
|
|
536
564
|
if (!options.edge?.forUser || !options.edge?.refreshTokens) {
|
|
537
|
-
throw new
|
|
565
|
+
throw new import_connect4.EdgeValidationError("EdgeUserSession requires an EdgeConnectServer instance", {
|
|
538
566
|
edge: ["Provide edge.forUser and edge.refreshTokens"]
|
|
539
567
|
});
|
|
540
568
|
}
|
|
541
569
|
const subjectId = typeof options.subjectId === "string" ? options.subjectId.trim() : "";
|
|
542
570
|
if (!subjectId) {
|
|
543
|
-
throw new
|
|
571
|
+
throw new import_connect4.EdgeValidationError("EdgeUserSession subjectId is required", {
|
|
544
572
|
subjectId: ["Required"]
|
|
545
573
|
});
|
|
546
574
|
}
|
|
547
575
|
if (!options.tokenStore?.load || !options.tokenStore?.save) {
|
|
548
|
-
throw new
|
|
576
|
+
throw new import_connect4.EdgeValidationError("EdgeUserSession tokenStore is required", {
|
|
549
577
|
tokenStore: ["Provide load(subjectId) and save(subjectId, value, context)"]
|
|
550
578
|
});
|
|
551
579
|
}
|
|
552
580
|
if (options.refreshSkewMs !== void 0 && (!Number.isFinite(options.refreshSkewMs) || options.refreshSkewMs < 0)) {
|
|
553
|
-
throw new
|
|
581
|
+
throw new import_connect4.EdgeValidationError("refreshSkewMs must be a non-negative number", {
|
|
554
582
|
refreshSkewMs: ["Must be greater than or equal to 0"]
|
|
555
583
|
});
|
|
556
584
|
}
|
|
@@ -603,13 +631,13 @@ var EdgeUserSession = class {
|
|
|
603
631
|
async loadTokens() {
|
|
604
632
|
const value = await this.tokenStore.load(this.subjectId);
|
|
605
633
|
if (!value) {
|
|
606
|
-
throw new
|
|
634
|
+
throw new import_connect4.EdgeAuthenticationError("No EDGE tokens are stored for this subject", {
|
|
607
635
|
subjectId: this.subjectId
|
|
608
636
|
});
|
|
609
637
|
}
|
|
610
638
|
if (typeof value === "string") {
|
|
611
639
|
if (!this.tokenVault) {
|
|
612
|
-
throw new
|
|
640
|
+
throw new import_connect4.EdgeAuthenticationError("Encrypted EDGE token envelope loaded but no tokenVault was configured", {
|
|
613
641
|
subjectId: this.subjectId
|
|
614
642
|
});
|
|
615
643
|
}
|
|
@@ -695,7 +723,7 @@ function validateTokens(tokens) {
|
|
|
695
723
|
}
|
|
696
724
|
}
|
|
697
725
|
if (Object.keys(errors).length > 0) {
|
|
698
|
-
throw new
|
|
726
|
+
throw new import_connect4.EdgeValidationError("Invalid EDGE token payload", errors);
|
|
699
727
|
}
|
|
700
728
|
}
|
|
701
729
|
|
|
@@ -886,7 +914,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
886
914
|
throw new Error("EdgeConnectServer: environment is required");
|
|
887
915
|
}
|
|
888
916
|
this.config = config;
|
|
889
|
-
const envConfig = (0,
|
|
917
|
+
const envConfig = (0, import_connect5.getEnvironmentConfig)(config.environment);
|
|
890
918
|
this.apiBaseUrl = config.apiBaseUrl || envConfig.apiBaseUrl;
|
|
891
919
|
this.partnerApiBaseUrl = stripTrailingSlash(config.partnerApiBaseUrl || derivePartnerApiBaseUrl(this.apiBaseUrl));
|
|
892
920
|
this.oauthBaseUrl = config.oauthBaseUrl || envConfig.oauthBaseUrl;
|
|
@@ -931,7 +959,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
931
959
|
*/
|
|
932
960
|
forUser(accessToken) {
|
|
933
961
|
if (!accessToken) {
|
|
934
|
-
throw new
|
|
962
|
+
throw new import_connect5.EdgeAuthenticationError("accessToken is required when calling forUser()");
|
|
935
963
|
}
|
|
936
964
|
return new EdgeUserClient(this, accessToken);
|
|
937
965
|
}
|
|
@@ -968,8 +996,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
968
996
|
const data = await response.json();
|
|
969
997
|
return this.parseTokenResponse(data);
|
|
970
998
|
} catch (error) {
|
|
971
|
-
if (error instanceof
|
|
972
|
-
throw new
|
|
999
|
+
if (error instanceof import_connect5.EdgeError) throw error;
|
|
1000
|
+
throw new import_connect5.EdgeNetworkError("Failed to exchange code", error);
|
|
973
1001
|
}
|
|
974
1002
|
}
|
|
975
1003
|
async refreshTokens(refreshToken) {
|
|
@@ -995,7 +1023,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
995
1023
|
);
|
|
996
1024
|
if (!response.ok) {
|
|
997
1025
|
const error = await response.json().catch(() => ({}));
|
|
998
|
-
throw new
|
|
1026
|
+
throw new import_connect5.EdgeAuthenticationError(
|
|
999
1027
|
error.message || error.error_description || "Token refresh failed. User may need to reconnect.",
|
|
1000
1028
|
{ tokenError: error }
|
|
1001
1029
|
);
|
|
@@ -1003,8 +1031,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1003
1031
|
const data = await response.json();
|
|
1004
1032
|
return this.parseTokenResponse(data, refreshToken);
|
|
1005
1033
|
} catch (error) {
|
|
1006
|
-
if (error instanceof
|
|
1007
|
-
throw new
|
|
1034
|
+
if (error instanceof import_connect5.EdgeError) throw error;
|
|
1035
|
+
throw new import_connect5.EdgeNetworkError("Failed to refresh tokens", error);
|
|
1008
1036
|
}
|
|
1009
1037
|
}
|
|
1010
1038
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
@@ -1046,14 +1074,14 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1046
1074
|
if (mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
1047
1075
|
responseBody = decryptMle(rawResponseBody.jwe, mleConfig);
|
|
1048
1076
|
} else if (!mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
1049
|
-
throw new
|
|
1077
|
+
throw new import_connect5.EdgeApiError(
|
|
1050
1078
|
"mle_required",
|
|
1051
1079
|
"The API responded with message-level encryption. Enable MLE in SDK config.",
|
|
1052
1080
|
response.status,
|
|
1053
1081
|
rawResponseBody
|
|
1054
1082
|
);
|
|
1055
1083
|
} else if (mleEnabled && mleConfig?.strictResponseEncryption !== false && response.ok && typeof rawResponseBody?.jwe !== "string") {
|
|
1056
|
-
throw new
|
|
1084
|
+
throw new import_connect5.EdgeApiError(
|
|
1057
1085
|
"mle_response_missing",
|
|
1058
1086
|
"Expected encrypted response payload but received plaintext.",
|
|
1059
1087
|
response.status,
|
|
@@ -1071,8 +1099,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1071
1099
|
}
|
|
1072
1100
|
return responseBody;
|
|
1073
1101
|
} catch (error) {
|
|
1074
|
-
if (error instanceof
|
|
1075
|
-
if (!(error instanceof
|
|
1102
|
+
if (error instanceof import_connect5.EdgeError) {
|
|
1103
|
+
if (!(error instanceof import_connect5.EdgeNetworkError) || attempt >= this.retryConfig.maxRetries) {
|
|
1076
1104
|
throw error;
|
|
1077
1105
|
}
|
|
1078
1106
|
lastError = error;
|
|
@@ -1080,11 +1108,11 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1080
1108
|
}
|
|
1081
1109
|
lastError = error;
|
|
1082
1110
|
if (attempt >= this.retryConfig.maxRetries) {
|
|
1083
|
-
throw new
|
|
1111
|
+
throw new import_connect5.EdgeNetworkError(`API request failed: ${method} ${path}`, lastError);
|
|
1084
1112
|
}
|
|
1085
1113
|
}
|
|
1086
1114
|
}
|
|
1087
|
-
throw new
|
|
1115
|
+
throw new import_connect5.EdgeNetworkError(
|
|
1088
1116
|
`API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
|
|
1089
1117
|
lastError
|
|
1090
1118
|
);
|
|
@@ -1128,10 +1156,10 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1128
1156
|
*/
|
|
1129
1157
|
async syncWebhookEvents(options = {}) {
|
|
1130
1158
|
if (!this.config.partnerClientId) {
|
|
1131
|
-
throw new
|
|
1159
|
+
throw new import_connect5.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientId` in EdgeConnectServer config");
|
|
1132
1160
|
}
|
|
1133
1161
|
if (!this.config.partnerClientSecret) {
|
|
1134
|
-
throw new
|
|
1162
|
+
throw new import_connect5.EdgeAuthenticationError("syncWebhookEvents requires `partnerClientSecret` in EdgeConnectServer config");
|
|
1135
1163
|
}
|
|
1136
1164
|
const normalizedOptions = this.normalizeSyncWebhookEventsOptions(options);
|
|
1137
1165
|
const url = this.buildSyncUrl(normalizedOptions);
|
|
@@ -1162,7 +1190,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1162
1190
|
endpoint: "partner_webhook_sync",
|
|
1163
1191
|
attempt
|
|
1164
1192
|
});
|
|
1165
|
-
throw new
|
|
1193
|
+
throw new import_connect5.EdgeAuthenticationError(
|
|
1166
1194
|
body.message || "Partner token rejected after refresh \u2014 check partnerClientId/Secret",
|
|
1167
1195
|
body
|
|
1168
1196
|
);
|
|
@@ -1179,7 +1207,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1179
1207
|
endpoint: "partner_webhook_sync",
|
|
1180
1208
|
attempt
|
|
1181
1209
|
});
|
|
1182
|
-
throw new
|
|
1210
|
+
throw new import_connect5.EdgeApiError(
|
|
1183
1211
|
body.error || "sync_failed",
|
|
1184
1212
|
body.message || `syncWebhookEvents failed with status ${response.status}`,
|
|
1185
1213
|
response.status,
|
|
@@ -1225,7 +1253,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1225
1253
|
}
|
|
1226
1254
|
if (options.limit !== void 0) {
|
|
1227
1255
|
if (!Number.isInteger(options.limit) || options.limit < SYNC_WEBHOOK_EVENTS_MIN_LIMIT || options.limit > SYNC_WEBHOOK_EVENTS_MAX_LIMIT) {
|
|
1228
|
-
throw new
|
|
1256
|
+
throw new import_connect5.EdgeApiError(
|
|
1229
1257
|
"invalid_sync_options",
|
|
1230
1258
|
`syncWebhookEvents limit must be an integer between ${SYNC_WEBHOOK_EVENTS_MIN_LIMIT} and ${SYNC_WEBHOOK_EVENTS_MAX_LIMIT}`,
|
|
1231
1259
|
400,
|
|
@@ -1295,11 +1323,11 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1295
1323
|
} catch (error) {
|
|
1296
1324
|
lastError = error;
|
|
1297
1325
|
if (attempt >= this.retryConfig.maxRetries) {
|
|
1298
|
-
throw new
|
|
1326
|
+
throw new import_connect5.EdgeNetworkError("syncWebhookEvents network failure", lastError);
|
|
1299
1327
|
}
|
|
1300
1328
|
}
|
|
1301
1329
|
}
|
|
1302
|
-
throw new
|
|
1330
|
+
throw new import_connect5.EdgeNetworkError(
|
|
1303
1331
|
`syncWebhookEvents network failure after ${this.retryConfig.maxRetries} retries`,
|
|
1304
1332
|
lastError
|
|
1305
1333
|
);
|
|
@@ -1347,7 +1375,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1347
1375
|
this.dispatcher
|
|
1348
1376
|
);
|
|
1349
1377
|
} catch (error) {
|
|
1350
|
-
throw new
|
|
1378
|
+
throw new import_connect5.EdgeNetworkError("Partner token request network failure", error);
|
|
1351
1379
|
}
|
|
1352
1380
|
const durationMs = Date.now() - startTime;
|
|
1353
1381
|
const data = await response.json().catch(() => ({}));
|
|
@@ -1360,13 +1388,13 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1360
1388
|
endpoint: "oauth_token"
|
|
1361
1389
|
});
|
|
1362
1390
|
if (!response.ok) {
|
|
1363
|
-
throw new
|
|
1391
|
+
throw new import_connect5.EdgeAuthenticationError(
|
|
1364
1392
|
data.message || data.error_description || `Partner token request failed with status ${response.status}`,
|
|
1365
1393
|
data
|
|
1366
1394
|
);
|
|
1367
1395
|
}
|
|
1368
1396
|
if (!data.access_token || typeof data.expires_in !== "number") {
|
|
1369
|
-
throw new
|
|
1397
|
+
throw new import_connect5.EdgeAuthenticationError("Partner token response missing access_token or expires_in");
|
|
1370
1398
|
}
|
|
1371
1399
|
this.partnerTokenCache = {
|
|
1372
1400
|
token: data.access_token,
|
|
@@ -1441,37 +1469,37 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1441
1469
|
const errorCode = error.error;
|
|
1442
1470
|
const errorMessage = error.message || error.error_description;
|
|
1443
1471
|
if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
|
|
1444
|
-
return new
|
|
1472
|
+
return new import_connect5.EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
|
|
1445
1473
|
edgeBoostError: error
|
|
1446
1474
|
});
|
|
1447
1475
|
}
|
|
1448
1476
|
if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
|
|
1449
|
-
return new
|
|
1477
|
+
return new import_connect5.EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
|
|
1450
1478
|
edgeBoostError: error
|
|
1451
1479
|
});
|
|
1452
1480
|
}
|
|
1453
1481
|
if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
|
|
1454
|
-
return new
|
|
1482
|
+
return new import_connect5.EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
|
|
1455
1483
|
}
|
|
1456
|
-
return new
|
|
1484
|
+
return new import_connect5.EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
|
|
1457
1485
|
edgeBoostError: error,
|
|
1458
1486
|
statusCode: status
|
|
1459
1487
|
});
|
|
1460
1488
|
}
|
|
1461
1489
|
async handleApiErrorFromBody(error, status, path) {
|
|
1462
1490
|
if (status === 401) {
|
|
1463
|
-
return new
|
|
1491
|
+
return new import_connect5.EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
|
|
1464
1492
|
}
|
|
1465
1493
|
if (status === 403) {
|
|
1466
1494
|
if (error.error === "consent_required") {
|
|
1467
|
-
return new
|
|
1495
|
+
return new import_connect5.EdgeConsentRequiredError(
|
|
1468
1496
|
this.config.clientId,
|
|
1469
1497
|
error.consentUrl,
|
|
1470
1498
|
error.message
|
|
1471
1499
|
);
|
|
1472
1500
|
}
|
|
1473
1501
|
if (error.error === "insufficient_scope" || error.error === "insufficient_consent") {
|
|
1474
|
-
return new
|
|
1502
|
+
return new import_connect5.EdgeInsufficientScopeError(
|
|
1475
1503
|
error.missing_scopes || error.missingScopes || [],
|
|
1476
1504
|
error.message
|
|
1477
1505
|
);
|
|
@@ -1493,15 +1521,15 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1493
1521
|
} else if (path.includes("/transfer")) {
|
|
1494
1522
|
resourceType = "Transfer";
|
|
1495
1523
|
}
|
|
1496
|
-
return new
|
|
1524
|
+
return new import_connect5.EdgeNotFoundError(resourceType, resourceId);
|
|
1497
1525
|
}
|
|
1498
1526
|
if (status === 422 && error.error === "identity_verification_failed") {
|
|
1499
|
-
return new
|
|
1527
|
+
return new import_connect5.EdgeIdentityVerificationError(
|
|
1500
1528
|
error.fieldErrors || {},
|
|
1501
1529
|
error.message
|
|
1502
1530
|
);
|
|
1503
1531
|
}
|
|
1504
|
-
return new
|
|
1532
|
+
return new import_connect5.EdgeApiError(
|
|
1505
1533
|
error.error || "api_error",
|
|
1506
1534
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
1507
1535
|
status,
|
|
@@ -1511,9 +1539,9 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
1511
1539
|
};
|
|
1512
1540
|
|
|
1513
1541
|
// src/env.ts
|
|
1514
|
-
var
|
|
1542
|
+
var import_connect6 = require("@edge-markets/connect");
|
|
1515
1543
|
var import_node_fs2 = __toESM(require("fs"));
|
|
1516
|
-
var ENVIRONMENT_NAMES = new Set(Object.keys(
|
|
1544
|
+
var ENVIRONMENT_NAMES = new Set(Object.keys(import_connect6.EDGE_ENVIRONMENTS));
|
|
1517
1545
|
function readEnv(env, name) {
|
|
1518
1546
|
const value = env[name];
|
|
1519
1547
|
if (typeof value !== "string") return void 0;
|
|
@@ -1546,7 +1574,7 @@ function readPemValue(env, valueName, pathName, warnings) {
|
|
|
1546
1574
|
return normalizePem(import_node_fs2.default.readFileSync(filePath, "utf8"));
|
|
1547
1575
|
} catch (error) {
|
|
1548
1576
|
const message = error instanceof Error ? error.message : String(error);
|
|
1549
|
-
throw new
|
|
1577
|
+
throw new import_connect6.EdgeValidationError(`Failed to read ${pathName}`, {
|
|
1550
1578
|
[pathName]: [`Unable to read PEM file "${filePath}": ${message}`]
|
|
1551
1579
|
});
|
|
1552
1580
|
}
|
|
@@ -1554,19 +1582,29 @@ function readPemValue(env, valueName, pathName, warnings) {
|
|
|
1554
1582
|
function readEnvironment(env) {
|
|
1555
1583
|
const value = readEnv(env, "EDGE_ENVIRONMENT") || "staging";
|
|
1556
1584
|
if (!ENVIRONMENT_NAMES.has(value)) {
|
|
1557
|
-
throw new
|
|
1585
|
+
throw new import_connect6.EdgeValidationError("Invalid EDGE_ENVIRONMENT", {
|
|
1558
1586
|
EDGE_ENVIRONMENT: [`Must be one of: ${Array.from(ENVIRONMENT_NAMES).join(", ")}`]
|
|
1559
1587
|
});
|
|
1560
1588
|
}
|
|
1561
1589
|
return value;
|
|
1562
1590
|
}
|
|
1591
|
+
function readBooleanEnv(env, name) {
|
|
1592
|
+
const value = readEnv(env, name);
|
|
1593
|
+
if (!value) return void 0;
|
|
1594
|
+
const normalized = value.toLowerCase();
|
|
1595
|
+
if (["true", "1", "yes"].includes(normalized)) return true;
|
|
1596
|
+
if (["false", "0", "no"].includes(normalized)) return false;
|
|
1597
|
+
throw new import_connect6.EdgeValidationError(`Invalid ${name}`, {
|
|
1598
|
+
[name]: ["Must be one of: true, false, 1, 0, yes, no"]
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1563
1601
|
function readTimeout(env, override) {
|
|
1564
1602
|
if (override !== void 0) return override;
|
|
1565
1603
|
const value = readFirstEnv(env, ["EDGE_TIMEOUT_MS", "EDGE_REQUEST_TIMEOUT_MS"]);
|
|
1566
1604
|
if (!value) return void 0;
|
|
1567
1605
|
const timeout = Number(value);
|
|
1568
1606
|
if (!Number.isInteger(timeout) || timeout <= 0) {
|
|
1569
|
-
throw new
|
|
1607
|
+
throw new import_connect6.EdgeValidationError("Invalid EDGE_TIMEOUT_MS", {
|
|
1570
1608
|
EDGE_TIMEOUT_MS: ["Must be a positive integer number of milliseconds"]
|
|
1571
1609
|
});
|
|
1572
1610
|
}
|
|
@@ -1575,10 +1613,15 @@ function readTimeout(env, override) {
|
|
|
1575
1613
|
function buildMtlsConfig(env, requireMtls, warnings) {
|
|
1576
1614
|
const cert = readPemValue(env, "EDGE_MTLS_CERT", "EDGE_MTLS_CERT_PATH", warnings);
|
|
1577
1615
|
const key = readPemValue(env, "EDGE_MTLS_KEY", "EDGE_MTLS_KEY_PATH", warnings);
|
|
1578
|
-
const
|
|
1616
|
+
const serverCa = readPemValue(env, "EDGE_SERVER_CA_PEM", "EDGE_SERVER_CA_PEM_PATH", warnings);
|
|
1617
|
+
const hasLegacyCa = !!readFirstEnv(env, ["EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH"]);
|
|
1618
|
+
if (serverCa && hasLegacyCa) {
|
|
1619
|
+
warnings.push("EDGE_SERVER_CA_PEM and EDGE_MTLS_CA are both set; using EDGE_SERVER_CA_PEM");
|
|
1620
|
+
}
|
|
1621
|
+
const ca = serverCa || readPemValue(env, "EDGE_MTLS_CA", "EDGE_MTLS_CA_PATH", warnings);
|
|
1579
1622
|
if (!cert && !key && !ca) {
|
|
1580
1623
|
if (requireMtls) {
|
|
1581
|
-
throw new
|
|
1624
|
+
throw new import_connect6.EdgeValidationError("mTLS is required but no EDGE_MTLS_CERT or EDGE_MTLS_KEY was provided", {
|
|
1582
1625
|
EDGE_MTLS_CERT: ["Required when requireMtls is true"],
|
|
1583
1626
|
EDGE_MTLS_KEY: ["Required when requireMtls is true"]
|
|
1584
1627
|
});
|
|
@@ -1586,7 +1629,7 @@ function buildMtlsConfig(env, requireMtls, warnings) {
|
|
|
1586
1629
|
return void 0;
|
|
1587
1630
|
}
|
|
1588
1631
|
if (!cert || !key) {
|
|
1589
|
-
throw new
|
|
1632
|
+
throw new import_connect6.EdgeValidationError("Incomplete mTLS configuration", {
|
|
1590
1633
|
EDGE_MTLS_CERT: cert ? [] : ["Required when mTLS is configured"],
|
|
1591
1634
|
EDGE_MTLS_KEY: key ? [] : ["Required when mTLS is configured"]
|
|
1592
1635
|
});
|
|
@@ -1600,7 +1643,7 @@ function buildMtlsConfig(env, requireMtls, warnings) {
|
|
|
1600
1643
|
}
|
|
1601
1644
|
function inferMtlsRequirement(environment, apiBaseUrl) {
|
|
1602
1645
|
if (environment === "staging" || environment === "production") return true;
|
|
1603
|
-
const resolvedApiBaseUrl = apiBaseUrl || (0,
|
|
1646
|
+
const resolvedApiBaseUrl = apiBaseUrl || (0, import_connect6.getEnvironmentConfig)(environment).apiBaseUrl;
|
|
1604
1647
|
try {
|
|
1605
1648
|
const host = new URL(resolvedApiBaseUrl).host;
|
|
1606
1649
|
return host === "connect-staging.edgeboost.io" || host === "connect.edgeboost.io";
|
|
@@ -1614,7 +1657,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
|
|
|
1614
1657
|
const environment = readEnvironment(env);
|
|
1615
1658
|
const apiBaseUrl = readEnv(env, "EDGE_API_BASE_URL");
|
|
1616
1659
|
const mtlsRequiredByTarget = inferMtlsRequirement(environment, apiBaseUrl);
|
|
1617
|
-
const requireMtls = options.requireMtls ?? false;
|
|
1660
|
+
const requireMtls = options.requireMtls ?? readBooleanEnv(env, "EDGE_REQUIRE_MTLS") ?? false;
|
|
1618
1661
|
const mtls = buildMtlsConfig(env, requireMtls, warnings);
|
|
1619
1662
|
const timeout = readTimeout(env, options.timeout);
|
|
1620
1663
|
const clientId = readFirstEnv(env, ["EDGE_CLIENT_ID", "EDGE_CONNECT_CLIENT_ID"]) || "";
|
|
@@ -1627,7 +1670,7 @@ function createEdgeConnectServerFromEnv(options = {}) {
|
|
|
1627
1670
|
if (!clientId) validationErrors.EDGE_CLIENT_ID = ["Required"];
|
|
1628
1671
|
if (!clientSecret) validationErrors.EDGE_CLIENT_SECRET = ["Required"];
|
|
1629
1672
|
if (Object.keys(validationErrors).length > 0) {
|
|
1630
|
-
throw new
|
|
1673
|
+
throw new import_connect6.EdgeValidationError("Missing required EDGE Connect environment variables", validationErrors);
|
|
1631
1674
|
}
|
|
1632
1675
|
if (mtlsRequiredByTarget && !mtls) {
|
|
1633
1676
|
warnings.push(
|
|
@@ -1666,32 +1709,35 @@ function createEdgeConnectServerFromEnv(options = {}) {
|
|
|
1666
1709
|
}
|
|
1667
1710
|
|
|
1668
1711
|
// src/error-adapter.ts
|
|
1669
|
-
var
|
|
1712
|
+
var import_connect7 = require("@edge-markets/connect");
|
|
1670
1713
|
function toEdgeHttpError(error) {
|
|
1671
1714
|
const body = toEdgeProblemJson(error);
|
|
1672
1715
|
return { status: body.status, body };
|
|
1673
1716
|
}
|
|
1674
1717
|
function toEdgeProblemJson(error) {
|
|
1675
|
-
if (error instanceof
|
|
1718
|
+
if (error instanceof import_connect7.EdgeConsentRequiredError) {
|
|
1676
1719
|
return problem(error, "Consent required", error.statusCode ?? 403, {
|
|
1677
1720
|
clientId: error.clientId,
|
|
1678
1721
|
consentUrl: error.consentUrl
|
|
1679
1722
|
});
|
|
1680
1723
|
}
|
|
1681
|
-
if (error instanceof
|
|
1724
|
+
if (error instanceof import_connect7.EdgeValidationError) {
|
|
1682
1725
|
return problem(error, "Validation failed", error.statusCode ?? 400, {
|
|
1683
1726
|
validationErrors: error.validationErrors
|
|
1684
1727
|
});
|
|
1685
1728
|
}
|
|
1686
|
-
if (error instanceof
|
|
1729
|
+
if (error instanceof import_connect7.EdgeIdentityVerificationError) {
|
|
1687
1730
|
return problem(error, "Identity verification failed", error.statusCode ?? 422, {
|
|
1688
1731
|
fieldErrors: error.fieldErrors
|
|
1689
1732
|
});
|
|
1690
1733
|
}
|
|
1691
|
-
if (error instanceof
|
|
1734
|
+
if (error instanceof import_connect7.EdgeFeatureUnavailableError) {
|
|
1735
|
+
return problem(error, "Feature unavailable", error.statusCode ?? 501);
|
|
1736
|
+
}
|
|
1737
|
+
if (error instanceof import_connect7.EdgeNetworkError) {
|
|
1692
1738
|
return problem(error, "EDGE network error", error.statusCode ?? 502);
|
|
1693
1739
|
}
|
|
1694
|
-
if (error instanceof
|
|
1740
|
+
if (error instanceof import_connect7.EdgeError) {
|
|
1695
1741
|
return problem(error, edgeTitle(error), error.statusCode ?? 500, error.details);
|
|
1696
1742
|
}
|
|
1697
1743
|
const message = error instanceof Error ? error.message : "Unexpected error";
|
|
@@ -1718,10 +1764,10 @@ function edgeTitle(error) {
|
|
|
1718
1764
|
}
|
|
1719
1765
|
|
|
1720
1766
|
// src/http-helpers.ts
|
|
1721
|
-
var
|
|
1767
|
+
var import_connect9 = require("@edge-markets/connect");
|
|
1722
1768
|
|
|
1723
1769
|
// src/webhook-parser.ts
|
|
1724
|
-
var
|
|
1770
|
+
var import_connect8 = require("@edge-markets/connect");
|
|
1725
1771
|
|
|
1726
1772
|
// src/webhook-signing.ts
|
|
1727
1773
|
var import_node_crypto2 = __toESM(require("crypto"));
|
|
@@ -1753,23 +1799,23 @@ function verifyWebhookSignature(header, body, secret, options = {}) {
|
|
|
1753
1799
|
}
|
|
1754
1800
|
|
|
1755
1801
|
// src/webhook-parser.ts
|
|
1756
|
-
var EVENT_TYPES = new Set(
|
|
1802
|
+
var EVENT_TYPES = new Set(import_connect8.EDGE_WEBHOOK_EVENT_TYPES);
|
|
1757
1803
|
var TRANSFER_TYPES = /* @__PURE__ */ new Set(["debit", "credit"]);
|
|
1758
1804
|
function parseAndVerifyWebhook(options) {
|
|
1759
1805
|
const body = normalizeRawBody(options.rawBody);
|
|
1760
1806
|
const header = normalizeSignatureHeader(options.signatureHeader);
|
|
1761
1807
|
const signatureTimestamp = extractWebhookSignatureTimestamp(header);
|
|
1762
1808
|
if (!header || !signatureTimestamp) {
|
|
1763
|
-
throw new
|
|
1809
|
+
throw new import_connect8.EdgeAuthenticationError("Missing or malformed EDGE webhook signature");
|
|
1764
1810
|
}
|
|
1765
1811
|
if (!verifyWebhookSignature(header, body, options.secret, { toleranceSeconds: options.toleranceSeconds })) {
|
|
1766
|
-
throw new
|
|
1812
|
+
throw new import_connect8.EdgeAuthenticationError("Invalid EDGE webhook signature");
|
|
1767
1813
|
}
|
|
1768
1814
|
let parsed;
|
|
1769
1815
|
try {
|
|
1770
1816
|
parsed = JSON.parse(body);
|
|
1771
1817
|
} catch {
|
|
1772
|
-
throw new
|
|
1818
|
+
throw new import_connect8.EdgeValidationError("EDGE webhook body must be valid JSON", {
|
|
1773
1819
|
rawBody: ["Unable to parse JSON after signature verification"]
|
|
1774
1820
|
});
|
|
1775
1821
|
}
|
|
@@ -1802,7 +1848,7 @@ function isEdgeWebhookEvent(value) {
|
|
|
1802
1848
|
function validateEdgeWebhookEvent(value) {
|
|
1803
1849
|
const errors = {};
|
|
1804
1850
|
if (!isRecord(value)) {
|
|
1805
|
-
throw new
|
|
1851
|
+
throw new import_connect8.EdgeValidationError("EDGE webhook event must be an object", {
|
|
1806
1852
|
event: ["Expected JSON object"]
|
|
1807
1853
|
});
|
|
1808
1854
|
}
|
|
@@ -1814,7 +1860,7 @@ function validateEdgeWebhookEvent(value) {
|
|
|
1814
1860
|
errors.id = ["Required string"];
|
|
1815
1861
|
}
|
|
1816
1862
|
if (typeof type !== "string" || !EVENT_TYPES.has(type)) {
|
|
1817
|
-
errors.type = [`Must be one of: ${
|
|
1863
|
+
errors.type = [`Must be one of: ${import_connect8.EDGE_WEBHOOK_EVENT_TYPES.join(", ")}`];
|
|
1818
1864
|
}
|
|
1819
1865
|
if (typeof createdAt !== "string" || !createdAt.trim() || Number.isNaN(Date.parse(createdAt))) {
|
|
1820
1866
|
errors.created_at = ["Required ISO 8601 timestamp string"];
|
|
@@ -1826,7 +1872,7 @@ function validateEdgeWebhookEvent(value) {
|
|
|
1826
1872
|
validateKnownEventData(type, data, errors);
|
|
1827
1873
|
}
|
|
1828
1874
|
if (Object.keys(errors).length > 0) {
|
|
1829
|
-
throw new
|
|
1875
|
+
throw new import_connect8.EdgeValidationError("Invalid EDGE webhook event", errors);
|
|
1830
1876
|
}
|
|
1831
1877
|
return value;
|
|
1832
1878
|
}
|
|
@@ -1854,7 +1900,7 @@ function validateKnownEventData(type, data, errors) {
|
|
|
1854
1900
|
return;
|
|
1855
1901
|
default: {
|
|
1856
1902
|
const exhaustive = type;
|
|
1857
|
-
throw new
|
|
1903
|
+
throw new import_connect8.EdgeValidationError("Unsupported EDGE webhook event type", {
|
|
1858
1904
|
type: [`Unsupported event type: ${exhaustive}`]
|
|
1859
1905
|
});
|
|
1860
1906
|
}
|
|
@@ -1887,7 +1933,7 @@ function normalizeRawBody(rawBody) {
|
|
|
1887
1933
|
if (Buffer.isBuffer(rawBody) || rawBody instanceof Uint8Array) {
|
|
1888
1934
|
return Buffer.from(rawBody).toString("utf8");
|
|
1889
1935
|
}
|
|
1890
|
-
throw new
|
|
1936
|
+
throw new import_connect8.EdgeValidationError("EDGE webhook raw body is required", {
|
|
1891
1937
|
rawBody: ["Pass the raw UTF-8 request body, not a parsed JSON object"]
|
|
1892
1938
|
});
|
|
1893
1939
|
}
|
|
@@ -1908,7 +1954,7 @@ function getEdgeWebhookRawBody(request) {
|
|
|
1908
1954
|
if (typeof request.rawBody === "string" || Buffer.isBuffer(request.rawBody) || request.rawBody instanceof Uint8Array) {
|
|
1909
1955
|
return request.rawBody;
|
|
1910
1956
|
}
|
|
1911
|
-
throw new
|
|
1957
|
+
throw new import_connect9.EdgeValidationError("EDGE webhook raw body is required", {
|
|
1912
1958
|
rawBody: [
|
|
1913
1959
|
"Configure your framework to expose the raw UTF-8 request body before JSON parsing. Parsed JSON bodies cannot be used for HMAC verification."
|
|
1914
1960
|
]
|
|
@@ -1935,7 +1981,7 @@ function parseWebhookHttpRequest(options) {
|
|
|
1935
1981
|
}
|
|
1936
1982
|
|
|
1937
1983
|
// src/identity.ts
|
|
1938
|
-
var
|
|
1984
|
+
var import_connect10 = require("@edge-markets/connect");
|
|
1939
1985
|
var DEFAULT_IDENTITY_POLICY = {
|
|
1940
1986
|
minNameScore: 70,
|
|
1941
1987
|
minAddressScore: 65,
|
|
@@ -1963,7 +2009,7 @@ function buildVerifyIdentityPayload(profile) {
|
|
|
1963
2009
|
errors.address = ["At least one address field is required"];
|
|
1964
2010
|
}
|
|
1965
2011
|
if (Object.keys(errors).length > 0) {
|
|
1966
|
-
throw new
|
|
2012
|
+
throw new import_connect10.EdgeValidationError("Partner identity profile is incomplete", errors);
|
|
1967
2013
|
}
|
|
1968
2014
|
return payload;
|
|
1969
2015
|
}
|
|
@@ -2000,7 +2046,7 @@ function validatePolicy(policy) {
|
|
|
2000
2046
|
if (!isScore(policy.minNameScore)) errors.minNameScore = ["Must be between 0 and 100"];
|
|
2001
2047
|
if (!isScore(policy.minAddressScore)) errors.minAddressScore = ["Must be between 0 and 100"];
|
|
2002
2048
|
if (Object.keys(errors).length > 0) {
|
|
2003
|
-
throw new
|
|
2049
|
+
throw new import_connect10.EdgeValidationError("Invalid identity policy", errors);
|
|
2004
2050
|
}
|
|
2005
2051
|
}
|
|
2006
2052
|
function isScore(value) {
|
|
@@ -2017,7 +2063,7 @@ function firstNonEmpty(...values) {
|
|
|
2017
2063
|
}
|
|
2018
2064
|
|
|
2019
2065
|
// src/session-store.ts
|
|
2020
|
-
var
|
|
2066
|
+
var import_connect11 = require("@edge-markets/connect");
|
|
2021
2067
|
function createSessionTokenRecord(subjectId, tokens, tokenVault, options = {}) {
|
|
2022
2068
|
const normalizedSubjectId = normalizeRequiredString(subjectId, "subjectId");
|
|
2023
2069
|
validateTokens2(tokens);
|
|
@@ -2033,7 +2079,7 @@ function createSessionTokenRecord(subjectId, tokens, tokenVault, options = {}) {
|
|
|
2033
2079
|
}
|
|
2034
2080
|
function parseSessionTokenRecord(record) {
|
|
2035
2081
|
if (!isRecord2(record)) {
|
|
2036
|
-
throw new
|
|
2082
|
+
throw new import_connect11.EdgeValidationError("EDGE session token record must be an object", {
|
|
2037
2083
|
record: ["Expected object"]
|
|
2038
2084
|
});
|
|
2039
2085
|
}
|
|
@@ -2056,7 +2102,7 @@ function encryptLegacySessionTokens(legacyRecord, tokenVault, options) {
|
|
|
2056
2102
|
const accessToken = normalizeRequiredString(legacyRecord.accessToken, "accessToken");
|
|
2057
2103
|
const refreshToken = normalizeRequiredString(legacyRecord.refreshToken, "refreshToken");
|
|
2058
2104
|
if (!options.allowPlaintext && !tokenVault.isEncrypted(accessToken) && !tokenVault.isEncrypted(refreshToken)) {
|
|
2059
|
-
throw new
|
|
2105
|
+
throw new import_connect11.EdgeValidationError("Plaintext token migration requires allowPlaintext: true", {
|
|
2060
2106
|
allowPlaintext: ["Set allowPlaintext only inside an explicit migration path"]
|
|
2061
2107
|
});
|
|
2062
2108
|
}
|
|
@@ -2080,7 +2126,7 @@ function normalizeSessionExpiresAt(value) {
|
|
|
2080
2126
|
if (value instanceof Date) {
|
|
2081
2127
|
const time = value.getTime();
|
|
2082
2128
|
if (!Number.isFinite(time) || time <= 0) {
|
|
2083
|
-
throw new
|
|
2129
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
|
|
2084
2130
|
expiresAt: ["Date must be valid"]
|
|
2085
2131
|
});
|
|
2086
2132
|
}
|
|
@@ -2089,7 +2135,7 @@ function normalizeSessionExpiresAt(value) {
|
|
|
2089
2135
|
if (typeof value === "string") {
|
|
2090
2136
|
const trimmed = value.trim();
|
|
2091
2137
|
if (!trimmed) {
|
|
2092
|
-
throw new
|
|
2138
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
|
|
2093
2139
|
expiresAt: ["Required"]
|
|
2094
2140
|
});
|
|
2095
2141
|
}
|
|
@@ -2101,18 +2147,18 @@ function normalizeSessionExpiresAt(value) {
|
|
|
2101
2147
|
}
|
|
2102
2148
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2103
2149
|
if (!Number.isInteger(value) || value <= 0) {
|
|
2104
|
-
throw new
|
|
2150
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
|
|
2105
2151
|
expiresAt: ["Must be a positive integer timestamp in milliseconds"]
|
|
2106
2152
|
});
|
|
2107
2153
|
}
|
|
2108
2154
|
if (value < 1e10) {
|
|
2109
|
-
throw new
|
|
2155
|
+
throw new import_connect11.EdgeValidationError("Ambiguous EDGE session expiresAt", {
|
|
2110
2156
|
expiresAt: ["Use Unix milliseconds, not seconds"]
|
|
2111
2157
|
});
|
|
2112
2158
|
}
|
|
2113
2159
|
return value;
|
|
2114
2160
|
}
|
|
2115
|
-
throw new
|
|
2161
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session expiresAt", {
|
|
2116
2162
|
expiresAt: ["Use Unix milliseconds, ISO string, or Date"]
|
|
2117
2163
|
});
|
|
2118
2164
|
}
|
|
@@ -2136,7 +2182,7 @@ function normalizeScopes(value) {
|
|
|
2136
2182
|
if (typeof value === "string") {
|
|
2137
2183
|
return splitScopes(value);
|
|
2138
2184
|
}
|
|
2139
|
-
throw new
|
|
2185
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session scopes", {
|
|
2140
2186
|
scopes: ["Expected array or space-delimited string"]
|
|
2141
2187
|
});
|
|
2142
2188
|
}
|
|
@@ -2145,7 +2191,7 @@ function splitScopes(value) {
|
|
|
2145
2191
|
}
|
|
2146
2192
|
function normalizeRequiredString(value, field) {
|
|
2147
2193
|
if (typeof value !== "string" || !value.trim()) {
|
|
2148
|
-
throw new
|
|
2194
|
+
throw new import_connect11.EdgeValidationError("Invalid EDGE session token record", {
|
|
2149
2195
|
[field]: ["Required string"]
|
|
2150
2196
|
});
|
|
2151
2197
|
}
|
|
@@ -2163,7 +2209,7 @@ function isRecord2(value) {
|
|
|
2163
2209
|
}
|
|
2164
2210
|
|
|
2165
2211
|
// src/token-vault.ts
|
|
2166
|
-
var
|
|
2212
|
+
var import_connect12 = require("@edge-markets/connect");
|
|
2167
2213
|
var import_node_crypto3 = __toESM(require("crypto"));
|
|
2168
2214
|
var TOKEN_VAULT_VERSION = "edge_vault_v1";
|
|
2169
2215
|
var IV_BYTES = 12;
|
|
@@ -2176,7 +2222,7 @@ var EdgeTokenVault = class {
|
|
|
2176
2222
|
for (const [index, key] of (options.previousKeys ?? []).entries()) {
|
|
2177
2223
|
const normalized = normalizeVaultKey(key, `previousKeys[${index}]`);
|
|
2178
2224
|
if (this.keysById.has(normalized.id)) {
|
|
2179
|
-
throw new
|
|
2225
|
+
throw new import_connect12.EdgeValidationError("Duplicate token vault key ID", {
|
|
2180
2226
|
keyId: [`Duplicate key ID "${normalized.id}"`]
|
|
2181
2227
|
});
|
|
2182
2228
|
}
|
|
@@ -2204,7 +2250,7 @@ var EdgeTokenVault = class {
|
|
|
2204
2250
|
const parsed = parseEnvelope(envelope);
|
|
2205
2251
|
const key = this.keysById.get(parsed.keyId);
|
|
2206
2252
|
if (!key) {
|
|
2207
|
-
throw new
|
|
2253
|
+
throw new import_connect12.EdgeValidationError("Unknown token vault key ID", {
|
|
2208
2254
|
keyId: [`No key configured for "${parsed.keyId}"`]
|
|
2209
2255
|
});
|
|
2210
2256
|
}
|
|
@@ -2217,8 +2263,8 @@ var EdgeTokenVault = class {
|
|
|
2217
2263
|
validateEdgeTokens(tokens);
|
|
2218
2264
|
return tokens;
|
|
2219
2265
|
} catch (error) {
|
|
2220
|
-
if (error instanceof
|
|
2221
|
-
throw new
|
|
2266
|
+
if (error instanceof import_connect12.EdgeValidationError) throw error;
|
|
2267
|
+
throw new import_connect12.EdgeValidationError("Failed to decrypt EDGE tokens", {
|
|
2222
2268
|
tokenEnvelope: ["Envelope is malformed, corrupted, or encrypted with a different key"]
|
|
2223
2269
|
});
|
|
2224
2270
|
}
|
|
@@ -2236,7 +2282,7 @@ function isEdgeTokenVaultEnvelope(value) {
|
|
|
2236
2282
|
function normalizeVaultKey(input, fieldName) {
|
|
2237
2283
|
const id = typeof input.id === "string" ? input.id.trim() : "";
|
|
2238
2284
|
if (!id) {
|
|
2239
|
-
throw new
|
|
2285
|
+
throw new import_connect12.EdgeValidationError("Token vault key ID is required", {
|
|
2240
2286
|
[`${fieldName}.id`]: ["Required"]
|
|
2241
2287
|
});
|
|
2242
2288
|
}
|
|
@@ -2267,7 +2313,7 @@ function normalizeKeyMaterial(input, fieldName) {
|
|
|
2267
2313
|
}
|
|
2268
2314
|
const key = candidates.find((candidate) => candidate.length === KEY_BYTES);
|
|
2269
2315
|
if (!key) {
|
|
2270
|
-
throw new
|
|
2316
|
+
throw new import_connect12.EdgeValidationError("Token vault key must decode to 32 bytes", {
|
|
2271
2317
|
[fieldName]: ["Provide a 32-byte key as base64, base64url, hex, Buffer, or Uint8Array"]
|
|
2272
2318
|
});
|
|
2273
2319
|
}
|
|
@@ -2298,18 +2344,18 @@ function validateEdgeTokens(tokens) {
|
|
|
2298
2344
|
}
|
|
2299
2345
|
}
|
|
2300
2346
|
if (Object.keys(errors).length > 0) {
|
|
2301
|
-
throw new
|
|
2347
|
+
throw new import_connect12.EdgeValidationError("Invalid EDGE token payload", errors);
|
|
2302
2348
|
}
|
|
2303
2349
|
}
|
|
2304
2350
|
function parseEnvelope(envelope) {
|
|
2305
2351
|
if (typeof envelope !== "string") {
|
|
2306
|
-
throw new
|
|
2352
|
+
throw new import_connect12.EdgeValidationError("Token envelope must be a string", {
|
|
2307
2353
|
tokenEnvelope: ["Expected string"]
|
|
2308
2354
|
});
|
|
2309
2355
|
}
|
|
2310
2356
|
const parts = envelope.split(".");
|
|
2311
2357
|
if (parts.length !== 5 || parts[0] !== TOKEN_VAULT_VERSION) {
|
|
2312
|
-
throw new
|
|
2358
|
+
throw new import_connect12.EdgeValidationError("Invalid EDGE token vault envelope", {
|
|
2313
2359
|
tokenEnvelope: [`Expected ${TOKEN_VAULT_VERSION}.<keyId>.<iv>.<tag>.<ciphertext>`]
|
|
2314
2360
|
});
|
|
2315
2361
|
}
|
|
@@ -2324,7 +2370,7 @@ function parseEnvelope(envelope) {
|
|
|
2324
2370
|
if (tag.length !== TAG_BYTES) errors.tag = [`Must be ${TAG_BYTES} bytes`];
|
|
2325
2371
|
if (ciphertext.length === 0) errors.ciphertext = ["Required"];
|
|
2326
2372
|
if (Object.keys(errors).length > 0) {
|
|
2327
|
-
throw new
|
|
2373
|
+
throw new import_connect12.EdgeValidationError("Invalid EDGE token vault envelope", errors);
|
|
2328
2374
|
}
|
|
2329
2375
|
return { keyId, iv, tag, ciphertext };
|
|
2330
2376
|
}
|
|
@@ -2338,13 +2384,13 @@ function fromBase64Url2(value) {
|
|
|
2338
2384
|
}
|
|
2339
2385
|
|
|
2340
2386
|
// src/webhook-guards.ts
|
|
2341
|
-
var
|
|
2387
|
+
var import_connect13 = require("@edge-markets/connect");
|
|
2342
2388
|
function isTransferWebhookEvent(event) {
|
|
2343
2389
|
return event.type.startsWith("transfer.");
|
|
2344
2390
|
}
|
|
2345
2391
|
function assertTransferEventMatchesIntent(event, intent) {
|
|
2346
2392
|
if (!isTransferWebhookEvent(event)) {
|
|
2347
|
-
throw new
|
|
2393
|
+
throw new import_connect13.EdgeValidationError("Webhook event is not a transfer event", {
|
|
2348
2394
|
type: [`Received ${event.type}`]
|
|
2349
2395
|
});
|
|
2350
2396
|
}
|
|
@@ -2367,34 +2413,34 @@ function assertTransferEventMatchesIntent(event, intent) {
|
|
|
2367
2413
|
}
|
|
2368
2414
|
}
|
|
2369
2415
|
if (Object.keys(errors).length > 0) {
|
|
2370
|
-
throw new
|
|
2416
|
+
throw new import_connect13.EdgeValidationError("Webhook transfer event does not match expected transfer intent", errors);
|
|
2371
2417
|
}
|
|
2372
2418
|
}
|
|
2373
2419
|
var assertWebhookMatchesTransfer = assertTransferEventMatchesIntent;
|
|
2374
2420
|
|
|
2375
2421
|
// src/webhook-inbox.ts
|
|
2376
|
-
var
|
|
2422
|
+
var import_connect14 = require("@edge-markets/connect");
|
|
2377
2423
|
var import_node_crypto4 = __toESM(require("crypto"));
|
|
2378
2424
|
var DEFAULT_PROCESSING_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2379
2425
|
var EdgeWebhookInbox = class {
|
|
2380
2426
|
constructor(options) {
|
|
2381
2427
|
if (!options.store?.insert || !options.store?.find || !options.store?.markProcessing) {
|
|
2382
|
-
throw new
|
|
2428
|
+
throw new import_connect14.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
|
|
2383
2429
|
store: ["Provide insert, find, markProcessing, markProcessed, and markFailed callbacks"]
|
|
2384
2430
|
});
|
|
2385
2431
|
}
|
|
2386
2432
|
if (!options.store.markProcessed || !options.store.markFailed) {
|
|
2387
|
-
throw new
|
|
2433
|
+
throw new import_connect14.EdgeValidationError("EdgeWebhookInbox store is incomplete", {
|
|
2388
2434
|
store: ["Provide markProcessed and markFailed callbacks"]
|
|
2389
2435
|
});
|
|
2390
2436
|
}
|
|
2391
2437
|
if (!options.processEvent) {
|
|
2392
|
-
throw new
|
|
2438
|
+
throw new import_connect14.EdgeValidationError("EdgeWebhookInbox processEvent callback is required", {
|
|
2393
2439
|
processEvent: ["Required"]
|
|
2394
2440
|
});
|
|
2395
2441
|
}
|
|
2396
2442
|
if (options.processingTimeoutMs !== void 0 && (!Number.isFinite(options.processingTimeoutMs) || options.processingTimeoutMs <= 0)) {
|
|
2397
|
-
throw new
|
|
2443
|
+
throw new import_connect14.EdgeValidationError("processingTimeoutMs must be positive", {
|
|
2398
2444
|
processingTimeoutMs: ["Must be greater than 0"]
|
|
2399
2445
|
});
|
|
2400
2446
|
}
|
|
@@ -2554,7 +2600,7 @@ var EdgeWebhookInbox = class {
|
|
|
2554
2600
|
status: result.record.status,
|
|
2555
2601
|
source: options.source
|
|
2556
2602
|
});
|
|
2557
|
-
throw new
|
|
2603
|
+
throw new import_connect14.EdgeValidationError("Duplicate webhook event payload does not match stored event", {
|
|
2558
2604
|
eventId: [event.id]
|
|
2559
2605
|
});
|
|
2560
2606
|
}
|
|
@@ -2590,7 +2636,7 @@ function fingerprintWebhookEvent(event) {
|
|
|
2590
2636
|
}
|
|
2591
2637
|
function normalizeEventId(eventId) {
|
|
2592
2638
|
if (typeof eventId !== "string" || !eventId.trim()) {
|
|
2593
|
-
throw new
|
|
2639
|
+
throw new import_connect14.EdgeValidationError("Webhook event ID is required", {
|
|
2594
2640
|
eventId: ["Required"]
|
|
2595
2641
|
});
|
|
2596
2642
|
}
|
|
@@ -2607,17 +2653,20 @@ function stableStringify(value) {
|
|
|
2607
2653
|
}
|
|
2608
2654
|
|
|
2609
2655
|
// src/index.ts
|
|
2610
|
-
var import_connect14 = require("@edge-markets/connect");
|
|
2611
2656
|
var import_connect15 = require("@edge-markets/connect");
|
|
2657
|
+
var import_connect16 = require("@edge-markets/connect");
|
|
2612
2658
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2613
2659
|
0 && (module.exports = {
|
|
2660
|
+
ACTIVE_EDGE_SCOPES,
|
|
2614
2661
|
CONNECT_TRANSFER_DIRECTIONS,
|
|
2662
|
+
EDGE_CONNECT_FEATURE_UNAVAILABLE_MESSAGE,
|
|
2615
2663
|
EDGE_WEBHOOK_EVENT_TYPES,
|
|
2616
2664
|
EdgeApiError,
|
|
2617
2665
|
EdgeAuthenticationError,
|
|
2618
2666
|
EdgeConnectServer,
|
|
2619
2667
|
EdgeConsentRequiredError,
|
|
2620
2668
|
EdgeError,
|
|
2669
|
+
EdgeFeatureUnavailableError,
|
|
2621
2670
|
EdgeIdentityVerificationError,
|
|
2622
2671
|
EdgeInsufficientScopeError,
|
|
2623
2672
|
EdgeNetworkError,
|
|
@@ -2656,6 +2705,7 @@ var import_connect15 = require("@edge-markets/connect");
|
|
|
2656
2705
|
isEdgeError,
|
|
2657
2706
|
isEdgeTokenVaultEnvelope,
|
|
2658
2707
|
isEdgeWebhookEvent,
|
|
2708
|
+
isFeatureUnavailableError,
|
|
2659
2709
|
isIdentityVerificationError,
|
|
2660
2710
|
isNetworkError,
|
|
2661
2711
|
isProductionEnvironment,
|