@edge-markets/connect-node 1.2.0 → 1.4.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 +29 -1
- package/dist/index.d.mts +42 -13
- package/dist/index.d.ts +42 -13
- package/dist/index.js +288 -136
- package/dist/index.mjs +271 -123
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -58,9 +58,38 @@ interface EdgeConnectServerConfig {
|
|
|
58
58
|
apiBaseUrl?: string // Custom API URL (dev only)
|
|
59
59
|
authDomain?: string // Custom Cognito domain (dev only)
|
|
60
60
|
timeout?: number // Request timeout (default: 30000ms)
|
|
61
|
+
|
|
62
|
+
// Optional: Message Level Encryption (Connect endpoints only)
|
|
63
|
+
mle?: {
|
|
64
|
+
enabled: boolean
|
|
65
|
+
edgePublicKey: string // EDGE public encryption key (PEM)
|
|
66
|
+
edgeKeyId: string // EDGE key ID (kid) used for requests
|
|
67
|
+
partnerPrivateKey: string // Your private key (PEM) to decrypt responses
|
|
68
|
+
partnerKeyId: string // Your key ID (kid) expected in response headers
|
|
69
|
+
strictResponseEncryption?: boolean // default true
|
|
70
|
+
}
|
|
61
71
|
}
|
|
62
72
|
```
|
|
63
73
|
|
|
74
|
+
### Message Level Encryption (MLE)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const edge = new EdgeConnectServer({
|
|
78
|
+
clientId: process.env.EDGE_CLIENT_ID!,
|
|
79
|
+
clientSecret: process.env.EDGE_CLIENT_SECRET!,
|
|
80
|
+
environment: 'staging',
|
|
81
|
+
mle: {
|
|
82
|
+
enabled: true,
|
|
83
|
+
edgePublicKey: process.env.EDGE_MLE_EDGE_PUBLIC_KEY!,
|
|
84
|
+
edgeKeyId: process.env.EDGE_MLE_EDGE_KEY_ID!,
|
|
85
|
+
partnerPrivateKey: process.env.EDGE_MLE_PARTNER_PRIVATE_KEY!,
|
|
86
|
+
partnerKeyId: process.env.EDGE_MLE_PARTNER_KEY_ID!,
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When enabled, the SDK sends `X-Edge-MLE: v1`, encrypts request bodies, and decrypts encrypted Connect responses.
|
|
92
|
+
|
|
64
93
|
## Token Exchange
|
|
65
94
|
|
|
66
95
|
After EdgeLink completes, exchange the code for tokens:
|
|
@@ -317,4 +346,3 @@ MIT
|
|
|
317
346
|
|
|
318
347
|
|
|
319
348
|
|
|
320
|
-
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EdgeEnvironment,
|
|
1
|
+
import { EdgeEnvironment, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, Transfer, ListTransfersParams, TransferList, EdgeTokens } from '@edge-markets/connect';
|
|
2
2
|
export { Balance, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, ListTransfersParams, Transfer, TransferList, TransferListItem, TransferStatus, TransferType, User, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
interface RequestInfo {
|
|
@@ -32,12 +32,43 @@ interface EdgeConnectServerConfig {
|
|
|
32
32
|
retry?: RetryConfig;
|
|
33
33
|
onRequest?: (info: RequestInfo) => void;
|
|
34
34
|
onResponse?: (info: ResponseInfo) => void;
|
|
35
|
+
mle?: {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
edgePublicKey: string;
|
|
38
|
+
edgeKeyId: string;
|
|
39
|
+
partnerPrivateKey: string;
|
|
40
|
+
partnerKeyId: string;
|
|
41
|
+
strictResponseEncryption?: boolean;
|
|
42
|
+
};
|
|
35
43
|
}
|
|
36
44
|
interface TransferOptions {
|
|
37
45
|
type: 'debit' | 'credit';
|
|
38
46
|
amount: string;
|
|
39
47
|
idempotencyKey: string;
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A lightweight, user-scoped API client created via {@link EdgeConnectServer.forUser}.
|
|
52
|
+
*
|
|
53
|
+
* Each instance holds a single access token and delegates the actual HTTP work
|
|
54
|
+
* to its parent {@link EdgeConnectServer}.
|
|
55
|
+
*/
|
|
56
|
+
declare class EdgeUserClient {
|
|
57
|
+
private readonly server;
|
|
58
|
+
readonly accessToken: string;
|
|
59
|
+
constructor(server: EdgeConnectServer, accessToken: string);
|
|
60
|
+
getUser(): Promise<User>;
|
|
61
|
+
verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
|
|
62
|
+
getBalance(): Promise<Balance>;
|
|
63
|
+
initiateTransfer(options: TransferOptions): Promise<Transfer>;
|
|
64
|
+
verifyTransfer(transferId: string, otp: string): Promise<Transfer>;
|
|
65
|
+
getTransfer(transferId: string): Promise<Transfer>;
|
|
66
|
+
listTransfers(params?: ListTransfersParams): Promise<TransferList>;
|
|
67
|
+
revokeConsent(): Promise<{
|
|
68
|
+
revoked: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
declare class EdgeConnectServer {
|
|
42
73
|
private readonly config;
|
|
43
74
|
private readonly apiBaseUrl;
|
|
@@ -47,25 +78,23 @@ declare class EdgeConnectServer {
|
|
|
47
78
|
static getInstance(config: EdgeConnectServerConfig): EdgeConnectServer;
|
|
48
79
|
static clearInstances(): void;
|
|
49
80
|
constructor(config: EdgeConnectServerConfig);
|
|
81
|
+
/**
|
|
82
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
83
|
+
*
|
|
84
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
85
|
+
* or per user session and discard it when the token changes.
|
|
86
|
+
*/
|
|
87
|
+
forUser(accessToken: string): EdgeUserClient;
|
|
50
88
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
51
89
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
initiateTransfer(accessToken: string, options: TransferOptions): Promise<Transfer>;
|
|
55
|
-
private validateTransferOptions;
|
|
56
|
-
verifyTransfer(accessToken: string, transferId: string, otp: string): Promise<Transfer>;
|
|
57
|
-
getTransfer(accessToken: string, transferId: string): Promise<Transfer>;
|
|
58
|
-
listTransfers(accessToken: string, params?: ListTransfersParams): Promise<TransferList>;
|
|
59
|
-
revokeConsent(accessToken: string): Promise<{
|
|
60
|
-
revoked: boolean;
|
|
61
|
-
}>;
|
|
90
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
91
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown): Promise<T>;
|
|
62
92
|
private getRetryDelay;
|
|
63
93
|
private sleep;
|
|
64
|
-
private apiRequest;
|
|
65
94
|
private fetchWithTimeout;
|
|
66
95
|
private parseTokenResponse;
|
|
67
96
|
private handleTokenError;
|
|
68
97
|
private handleApiErrorFromBody;
|
|
69
98
|
}
|
|
70
99
|
|
|
71
|
-
export { EdgeConnectServer, type EdgeConnectServerConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
|
100
|
+
export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EdgeEnvironment,
|
|
1
|
+
import { EdgeEnvironment, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, Transfer, ListTransfersParams, TransferList, EdgeTokens } from '@edge-markets/connect';
|
|
2
2
|
export { Balance, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, ListTransfersParams, Transfer, TransferList, TransferListItem, TransferStatus, TransferType, User, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
interface RequestInfo {
|
|
@@ -32,12 +32,43 @@ interface EdgeConnectServerConfig {
|
|
|
32
32
|
retry?: RetryConfig;
|
|
33
33
|
onRequest?: (info: RequestInfo) => void;
|
|
34
34
|
onResponse?: (info: ResponseInfo) => void;
|
|
35
|
+
mle?: {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
edgePublicKey: string;
|
|
38
|
+
edgeKeyId: string;
|
|
39
|
+
partnerPrivateKey: string;
|
|
40
|
+
partnerKeyId: string;
|
|
41
|
+
strictResponseEncryption?: boolean;
|
|
42
|
+
};
|
|
35
43
|
}
|
|
36
44
|
interface TransferOptions {
|
|
37
45
|
type: 'debit' | 'credit';
|
|
38
46
|
amount: string;
|
|
39
47
|
idempotencyKey: string;
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A lightweight, user-scoped API client created via {@link EdgeConnectServer.forUser}.
|
|
52
|
+
*
|
|
53
|
+
* Each instance holds a single access token and delegates the actual HTTP work
|
|
54
|
+
* to its parent {@link EdgeConnectServer}.
|
|
55
|
+
*/
|
|
56
|
+
declare class EdgeUserClient {
|
|
57
|
+
private readonly server;
|
|
58
|
+
readonly accessToken: string;
|
|
59
|
+
constructor(server: EdgeConnectServer, accessToken: string);
|
|
60
|
+
getUser(): Promise<User>;
|
|
61
|
+
verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
|
|
62
|
+
getBalance(): Promise<Balance>;
|
|
63
|
+
initiateTransfer(options: TransferOptions): Promise<Transfer>;
|
|
64
|
+
verifyTransfer(transferId: string, otp: string): Promise<Transfer>;
|
|
65
|
+
getTransfer(transferId: string): Promise<Transfer>;
|
|
66
|
+
listTransfers(params?: ListTransfersParams): Promise<TransferList>;
|
|
67
|
+
revokeConsent(): Promise<{
|
|
68
|
+
revoked: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
declare class EdgeConnectServer {
|
|
42
73
|
private readonly config;
|
|
43
74
|
private readonly apiBaseUrl;
|
|
@@ -47,25 +78,23 @@ declare class EdgeConnectServer {
|
|
|
47
78
|
static getInstance(config: EdgeConnectServerConfig): EdgeConnectServer;
|
|
48
79
|
static clearInstances(): void;
|
|
49
80
|
constructor(config: EdgeConnectServerConfig);
|
|
81
|
+
/**
|
|
82
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
83
|
+
*
|
|
84
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
85
|
+
* or per user session and discard it when the token changes.
|
|
86
|
+
*/
|
|
87
|
+
forUser(accessToken: string): EdgeUserClient;
|
|
50
88
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
51
89
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
initiateTransfer(accessToken: string, options: TransferOptions): Promise<Transfer>;
|
|
55
|
-
private validateTransferOptions;
|
|
56
|
-
verifyTransfer(accessToken: string, transferId: string, otp: string): Promise<Transfer>;
|
|
57
|
-
getTransfer(accessToken: string, transferId: string): Promise<Transfer>;
|
|
58
|
-
listTransfers(accessToken: string, params?: ListTransfersParams): Promise<TransferList>;
|
|
59
|
-
revokeConsent(accessToken: string): Promise<{
|
|
60
|
-
revoked: boolean;
|
|
61
|
-
}>;
|
|
90
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
91
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown): Promise<T>;
|
|
62
92
|
private getRetryDelay;
|
|
63
93
|
private sleep;
|
|
64
|
-
private apiRequest;
|
|
65
94
|
private fetchWithTimeout;
|
|
66
95
|
private parseTokenResponse;
|
|
67
96
|
private handleTokenError;
|
|
68
97
|
private handleApiErrorFromBody;
|
|
69
98
|
}
|
|
70
99
|
|
|
71
|
-
export { EdgeConnectServer, type EdgeConnectServerConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
|
100
|
+
export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
package/dist/index.js
CHANGED
|
@@ -20,27 +20,202 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
EdgeApiError: () =>
|
|
24
|
-
EdgeAuthenticationError: () =>
|
|
23
|
+
EdgeApiError: () => import_connect3.EdgeApiError,
|
|
24
|
+
EdgeAuthenticationError: () => import_connect3.EdgeAuthenticationError,
|
|
25
25
|
EdgeConnectServer: () => EdgeConnectServer,
|
|
26
|
-
EdgeConsentRequiredError: () =>
|
|
27
|
-
EdgeError: () =>
|
|
28
|
-
EdgeInsufficientScopeError: () =>
|
|
29
|
-
EdgeNetworkError: () =>
|
|
30
|
-
EdgeNotFoundError: () =>
|
|
31
|
-
EdgeTokenExchangeError: () =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
EdgeConsentRequiredError: () => import_connect3.EdgeConsentRequiredError,
|
|
27
|
+
EdgeError: () => import_connect3.EdgeError,
|
|
28
|
+
EdgeInsufficientScopeError: () => import_connect3.EdgeInsufficientScopeError,
|
|
29
|
+
EdgeNetworkError: () => import_connect3.EdgeNetworkError,
|
|
30
|
+
EdgeNotFoundError: () => import_connect3.EdgeNotFoundError,
|
|
31
|
+
EdgeTokenExchangeError: () => import_connect3.EdgeTokenExchangeError,
|
|
32
|
+
EdgeUserClient: () => EdgeUserClient,
|
|
33
|
+
getEnvironmentConfig: () => import_connect4.getEnvironmentConfig,
|
|
34
|
+
isApiError: () => import_connect3.isApiError,
|
|
35
|
+
isAuthenticationError: () => import_connect3.isAuthenticationError,
|
|
36
|
+
isConsentRequiredError: () => import_connect3.isConsentRequiredError,
|
|
37
|
+
isEdgeError: () => import_connect3.isEdgeError,
|
|
38
|
+
isNetworkError: () => import_connect3.isNetworkError,
|
|
39
|
+
isProductionEnvironment: () => import_connect4.isProductionEnvironment
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(index_exports);
|
|
41
42
|
|
|
42
43
|
// src/edge-connect-server.ts
|
|
44
|
+
var import_connect2 = require("@edge-markets/connect");
|
|
45
|
+
|
|
46
|
+
// src/validators.ts
|
|
43
47
|
var import_connect = require("@edge-markets/connect");
|
|
48
|
+
function validateVerifyIdentityOptions(options) {
|
|
49
|
+
const errors = {};
|
|
50
|
+
const firstName = typeof options.firstName === "string" ? options.firstName.trim() : "";
|
|
51
|
+
if (!firstName) {
|
|
52
|
+
errors.firstName = ["firstName is required for identity verification"];
|
|
53
|
+
}
|
|
54
|
+
const lastName = typeof options.lastName === "string" ? options.lastName.trim() : "";
|
|
55
|
+
if (!lastName) {
|
|
56
|
+
errors.lastName = ["lastName is required for identity verification"];
|
|
57
|
+
}
|
|
58
|
+
if (!options.address || typeof options.address !== "object" || Array.isArray(options.address)) {
|
|
59
|
+
errors.address = ["address is required for identity verification"];
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(errors).length > 0) {
|
|
62
|
+
const firstMessage = Object.values(errors)[0][0];
|
|
63
|
+
throw new import_connect.EdgeValidationError(firstMessage, errors);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function validateTransferOptions(options) {
|
|
67
|
+
const errors = {};
|
|
68
|
+
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
69
|
+
errors.type = ['Must be "debit" or "credit"'];
|
|
70
|
+
}
|
|
71
|
+
if (!options.amount) {
|
|
72
|
+
errors.amount = ["Amount is required"];
|
|
73
|
+
} else {
|
|
74
|
+
const amount = parseFloat(options.amount);
|
|
75
|
+
if (isNaN(amount)) {
|
|
76
|
+
errors.amount = ["Must be a valid number"];
|
|
77
|
+
} else if (amount <= 0) {
|
|
78
|
+
errors.amount = ["Must be greater than 0"];
|
|
79
|
+
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
80
|
+
errors.amount = ["Must have at most 2 decimal places"];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
84
|
+
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
85
|
+
} else if (options.idempotencyKey.length > 255) {
|
|
86
|
+
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
87
|
+
}
|
|
88
|
+
if (Object.keys(errors).length > 0) {
|
|
89
|
+
throw new import_connect.EdgeValidationError("Invalid transfer options", errors);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/edge-user-client.ts
|
|
94
|
+
var EdgeUserClient = class {
|
|
95
|
+
constructor(server, accessToken) {
|
|
96
|
+
this.server = server;
|
|
97
|
+
this.accessToken = accessToken;
|
|
98
|
+
}
|
|
99
|
+
async getUser() {
|
|
100
|
+
return this.server._apiRequest("GET", "/user", this.accessToken);
|
|
101
|
+
}
|
|
102
|
+
async verifyIdentity(options) {
|
|
103
|
+
validateVerifyIdentityOptions(options);
|
|
104
|
+
return this.server._apiRequest("POST", "/user/verify-identity", this.accessToken, options);
|
|
105
|
+
}
|
|
106
|
+
async getBalance() {
|
|
107
|
+
return this.server._apiRequest("GET", "/balance", this.accessToken);
|
|
108
|
+
}
|
|
109
|
+
async initiateTransfer(options) {
|
|
110
|
+
validateTransferOptions(options);
|
|
111
|
+
const body = {
|
|
112
|
+
type: options.type,
|
|
113
|
+
amount: options.amount,
|
|
114
|
+
idempotencyKey: options.idempotencyKey
|
|
115
|
+
};
|
|
116
|
+
return this.server._apiRequest("POST", "/transfer", this.accessToken, body);
|
|
117
|
+
}
|
|
118
|
+
async verifyTransfer(transferId, otp) {
|
|
119
|
+
return this.server._apiRequest(
|
|
120
|
+
"POST",
|
|
121
|
+
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
122
|
+
this.accessToken,
|
|
123
|
+
{ otp }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
async getTransfer(transferId) {
|
|
127
|
+
return this.server._apiRequest("GET", `/transfer/${encodeURIComponent(transferId)}`, this.accessToken);
|
|
128
|
+
}
|
|
129
|
+
async listTransfers(params) {
|
|
130
|
+
const queryParams = new URLSearchParams();
|
|
131
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
132
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
133
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
134
|
+
const query = queryParams.toString();
|
|
135
|
+
const path = query ? `/transfers?${query}` : "/transfers";
|
|
136
|
+
return this.server._apiRequest("GET", path, this.accessToken);
|
|
137
|
+
}
|
|
138
|
+
async revokeConsent() {
|
|
139
|
+
return this.server._apiRequest("DELETE", "/consent", this.accessToken);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/mle.ts
|
|
144
|
+
var import_crypto = require("crypto");
|
|
145
|
+
function encryptMle(payload, clientId, config) {
|
|
146
|
+
const header = {
|
|
147
|
+
alg: "RSA-OAEP-256",
|
|
148
|
+
enc: "A256GCM",
|
|
149
|
+
kid: config.edgeKeyId,
|
|
150
|
+
typ: "application/json",
|
|
151
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
152
|
+
jti: (0, import_crypto.randomUUID)(),
|
|
153
|
+
clientId
|
|
154
|
+
};
|
|
155
|
+
const protectedHeaderB64 = toBase64Url(Buffer.from(JSON.stringify(header), "utf8"));
|
|
156
|
+
const aad = Buffer.from(protectedHeaderB64, "utf8");
|
|
157
|
+
const cek = (0, import_crypto.randomBytes)(32);
|
|
158
|
+
const iv = (0, import_crypto.randomBytes)(12);
|
|
159
|
+
const cipher = (0, import_crypto.createCipheriv)("aes-256-gcm", cek, iv);
|
|
160
|
+
cipher.setAAD(aad);
|
|
161
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
162
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
163
|
+
const tag = cipher.getAuthTag();
|
|
164
|
+
const encryptedKey = (0, import_crypto.publicEncrypt)(
|
|
165
|
+
{
|
|
166
|
+
key: config.edgePublicKey,
|
|
167
|
+
padding: import_crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
168
|
+
oaepHash: "sha256"
|
|
169
|
+
},
|
|
170
|
+
cek
|
|
171
|
+
);
|
|
172
|
+
return [
|
|
173
|
+
protectedHeaderB64,
|
|
174
|
+
toBase64Url(encryptedKey),
|
|
175
|
+
toBase64Url(iv),
|
|
176
|
+
toBase64Url(ciphertext),
|
|
177
|
+
toBase64Url(tag)
|
|
178
|
+
].join(".");
|
|
179
|
+
}
|
|
180
|
+
function decryptMle(jwe, config) {
|
|
181
|
+
const parts = jwe.split(".");
|
|
182
|
+
if (parts.length !== 5) {
|
|
183
|
+
throw new Error("Invalid MLE payload format");
|
|
184
|
+
}
|
|
185
|
+
const [protectedHeaderB64, encryptedKeyB64, ivB64, ciphertextB64, tagB64] = parts;
|
|
186
|
+
const protectedHeader = JSON.parse(fromBase64Url(protectedHeaderB64).toString("utf8"));
|
|
187
|
+
if (protectedHeader.alg !== "RSA-OAEP-256" || protectedHeader.enc !== "A256GCM") {
|
|
188
|
+
throw new Error("Unsupported MLE algorithms");
|
|
189
|
+
}
|
|
190
|
+
if (protectedHeader.kid && protectedHeader.kid !== config.partnerKeyId) {
|
|
191
|
+
throw new Error(`Unexpected response key id: ${protectedHeader.kid}`);
|
|
192
|
+
}
|
|
193
|
+
const cek = (0, import_crypto.privateDecrypt)(
|
|
194
|
+
{
|
|
195
|
+
key: config.partnerPrivateKey,
|
|
196
|
+
padding: import_crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
197
|
+
oaepHash: "sha256"
|
|
198
|
+
},
|
|
199
|
+
fromBase64Url(encryptedKeyB64)
|
|
200
|
+
);
|
|
201
|
+
const decipher = (0, import_crypto.createDecipheriv)("aes-256-gcm", cek, fromBase64Url(ivB64));
|
|
202
|
+
decipher.setAAD(Buffer.from(protectedHeaderB64, "utf8"));
|
|
203
|
+
decipher.setAuthTag(fromBase64Url(tagB64));
|
|
204
|
+
const plaintext = Buffer.concat([
|
|
205
|
+
decipher.update(fromBase64Url(ciphertextB64)),
|
|
206
|
+
decipher.final()
|
|
207
|
+
]);
|
|
208
|
+
return JSON.parse(plaintext.toString("utf8"));
|
|
209
|
+
}
|
|
210
|
+
function toBase64Url(buffer) {
|
|
211
|
+
return buffer.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
212
|
+
}
|
|
213
|
+
function fromBase64Url(value) {
|
|
214
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
215
|
+
return Buffer.from(normalized, "base64");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/types.ts
|
|
44
219
|
var DEFAULT_TIMEOUT = 3e4;
|
|
45
220
|
var USER_AGENT = "@edge-markets/connect-node/1.0.0";
|
|
46
221
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -49,6 +224,8 @@ var DEFAULT_RETRY_CONFIG = {
|
|
|
49
224
|
backoff: "exponential",
|
|
50
225
|
baseDelayMs: 1e3
|
|
51
226
|
};
|
|
227
|
+
|
|
228
|
+
// src/edge-connect-server.ts
|
|
52
229
|
var instances = /* @__PURE__ */ new Map();
|
|
53
230
|
function getInstanceKey(config) {
|
|
54
231
|
return `${config.clientId}:${config.environment}`;
|
|
@@ -76,7 +253,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
76
253
|
throw new Error("EdgeConnectServer: environment is required");
|
|
77
254
|
}
|
|
78
255
|
this.config = config;
|
|
79
|
-
const envConfig = (0,
|
|
256
|
+
const envConfig = (0, import_connect2.getEnvironmentConfig)(config.environment);
|
|
80
257
|
this.apiBaseUrl = config.apiBaseUrl || envConfig.apiBaseUrl;
|
|
81
258
|
this.oauthBaseUrl = config.oauthBaseUrl || envConfig.oauthBaseUrl;
|
|
82
259
|
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
@@ -85,6 +262,18 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
85
262
|
...config.retry
|
|
86
263
|
};
|
|
87
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
267
|
+
*
|
|
268
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
269
|
+
* or per user session and discard it when the token changes.
|
|
270
|
+
*/
|
|
271
|
+
forUser(accessToken) {
|
|
272
|
+
if (!accessToken) {
|
|
273
|
+
throw new import_connect2.EdgeAuthenticationError("accessToken is required when calling forUser()");
|
|
274
|
+
}
|
|
275
|
+
return new EdgeUserClient(this, accessToken);
|
|
276
|
+
}
|
|
88
277
|
async exchangeCode(code, codeVerifier, redirectUri) {
|
|
89
278
|
const tokenUrl = `${this.oauthBaseUrl}/token`;
|
|
90
279
|
const body = {
|
|
@@ -111,8 +300,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
111
300
|
const data = await response.json();
|
|
112
301
|
return this.parseTokenResponse(data);
|
|
113
302
|
} catch (error) {
|
|
114
|
-
if (error instanceof
|
|
115
|
-
throw new
|
|
303
|
+
if (error instanceof import_connect2.EdgeError) throw error;
|
|
304
|
+
throw new import_connect2.EdgeNetworkError("Failed to exchange code", error);
|
|
116
305
|
}
|
|
117
306
|
}
|
|
118
307
|
async refreshTokens(refreshToken) {
|
|
@@ -134,7 +323,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
134
323
|
});
|
|
135
324
|
if (!response.ok) {
|
|
136
325
|
const error = await response.json().catch(() => ({}));
|
|
137
|
-
throw new
|
|
326
|
+
throw new import_connect2.EdgeAuthenticationError(
|
|
138
327
|
error.message || error.error_description || "Token refresh failed. User may need to reconnect.",
|
|
139
328
|
{ tokenError: error }
|
|
140
329
|
);
|
|
@@ -142,91 +331,16 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
142
331
|
const data = await response.json();
|
|
143
332
|
return this.parseTokenResponse(data, refreshToken);
|
|
144
333
|
} catch (error) {
|
|
145
|
-
if (error instanceof
|
|
146
|
-
throw new
|
|
334
|
+
if (error instanceof import_connect2.EdgeError) throw error;
|
|
335
|
+
throw new import_connect2.EdgeNetworkError("Failed to refresh tokens", error);
|
|
147
336
|
}
|
|
148
337
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
async getBalance(accessToken) {
|
|
153
|
-
return this.apiRequest("GET", "/balance", accessToken);
|
|
154
|
-
}
|
|
155
|
-
async initiateTransfer(accessToken, options) {
|
|
156
|
-
this.validateTransferOptions(options);
|
|
157
|
-
const body = {
|
|
158
|
-
type: options.type,
|
|
159
|
-
amount: options.amount,
|
|
160
|
-
idempotencyKey: options.idempotencyKey
|
|
161
|
-
};
|
|
162
|
-
return this.apiRequest("POST", "/transfer", accessToken, body);
|
|
163
|
-
}
|
|
164
|
-
validateTransferOptions(options) {
|
|
165
|
-
const errors = {};
|
|
166
|
-
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
167
|
-
errors.type = ['Must be "debit" or "credit"'];
|
|
168
|
-
}
|
|
169
|
-
if (!options.amount) {
|
|
170
|
-
errors.amount = ["Amount is required"];
|
|
171
|
-
} else {
|
|
172
|
-
const amount = parseFloat(options.amount);
|
|
173
|
-
if (isNaN(amount)) {
|
|
174
|
-
errors.amount = ["Must be a valid number"];
|
|
175
|
-
} else if (amount <= 0) {
|
|
176
|
-
errors.amount = ["Must be greater than 0"];
|
|
177
|
-
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
178
|
-
errors.amount = ["Must have at most 2 decimal places"];
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
182
|
-
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
183
|
-
} else if (options.idempotencyKey.length > 255) {
|
|
184
|
-
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
185
|
-
}
|
|
186
|
-
if (Object.keys(errors).length > 0) {
|
|
187
|
-
throw new import_connect.EdgeValidationError("Invalid transfer options", errors);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
async verifyTransfer(accessToken, transferId, otp) {
|
|
191
|
-
return this.apiRequest(
|
|
192
|
-
"POST",
|
|
193
|
-
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
194
|
-
accessToken,
|
|
195
|
-
{ otp }
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
async getTransfer(accessToken, transferId) {
|
|
199
|
-
return this.apiRequest(
|
|
200
|
-
"GET",
|
|
201
|
-
`/transfer/${encodeURIComponent(transferId)}`,
|
|
202
|
-
accessToken
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
async listTransfers(accessToken, params) {
|
|
206
|
-
const queryParams = new URLSearchParams();
|
|
207
|
-
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
208
|
-
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
209
|
-
if (params?.status) queryParams.set("status", params.status);
|
|
210
|
-
const query = queryParams.toString();
|
|
211
|
-
const path = query ? `/transfers?${query}` : "/transfers";
|
|
212
|
-
return this.apiRequest("GET", path, accessToken);
|
|
213
|
-
}
|
|
214
|
-
async revokeConsent(accessToken) {
|
|
215
|
-
return this.apiRequest("DELETE", "/consent", accessToken);
|
|
216
|
-
}
|
|
217
|
-
getRetryDelay(attempt) {
|
|
218
|
-
const { backoff, baseDelayMs } = this.retryConfig;
|
|
219
|
-
if (backoff === "exponential") {
|
|
220
|
-
return baseDelayMs * Math.pow(2, attempt);
|
|
221
|
-
}
|
|
222
|
-
return baseDelayMs * (attempt + 1);
|
|
223
|
-
}
|
|
224
|
-
async sleep(ms) {
|
|
225
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
226
|
-
}
|
|
227
|
-
async apiRequest(method, path, accessToken, body) {
|
|
338
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
339
|
+
async _apiRequest(method, path, accessToken, body) {
|
|
228
340
|
const url = `${this.apiBaseUrl}${path}`;
|
|
229
341
|
let lastError = null;
|
|
342
|
+
const mleConfig = this.config.mle;
|
|
343
|
+
const mleEnabled = !!mleConfig?.enabled;
|
|
230
344
|
for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
|
|
231
345
|
const startTime = Date.now();
|
|
232
346
|
if (attempt > 0) {
|
|
@@ -234,16 +348,42 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
234
348
|
}
|
|
235
349
|
this.config.onRequest?.({ method, url, body });
|
|
236
350
|
try {
|
|
351
|
+
const requestHeaders = {
|
|
352
|
+
Authorization: `Bearer ${accessToken}`,
|
|
353
|
+
"Content-Type": "application/json",
|
|
354
|
+
"User-Agent": USER_AGENT
|
|
355
|
+
};
|
|
356
|
+
let requestBody = body;
|
|
357
|
+
if (mleEnabled) {
|
|
358
|
+
requestHeaders["X-Edge-MLE"] = "v1";
|
|
359
|
+
if (body !== void 0) {
|
|
360
|
+
requestBody = { jwe: encryptMle(body, this.config.clientId, mleConfig) };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
237
363
|
const response = await this.fetchWithTimeout(url, {
|
|
238
364
|
method,
|
|
239
|
-
headers:
|
|
240
|
-
|
|
241
|
-
"Content-Type": "application/json",
|
|
242
|
-
"User-Agent": USER_AGENT
|
|
243
|
-
},
|
|
244
|
-
body: body ? JSON.stringify(body) : void 0
|
|
365
|
+
headers: requestHeaders,
|
|
366
|
+
body: requestBody !== void 0 ? JSON.stringify(requestBody) : void 0
|
|
245
367
|
});
|
|
246
|
-
const
|
|
368
|
+
const rawResponseBody = await response.json().catch(() => ({}));
|
|
369
|
+
let responseBody = rawResponseBody;
|
|
370
|
+
if (mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
371
|
+
responseBody = decryptMle(rawResponseBody.jwe, mleConfig);
|
|
372
|
+
} else if (!mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
373
|
+
throw new import_connect2.EdgeApiError(
|
|
374
|
+
"mle_required",
|
|
375
|
+
"The API responded with message-level encryption. Enable MLE in SDK config.",
|
|
376
|
+
response.status,
|
|
377
|
+
rawResponseBody
|
|
378
|
+
);
|
|
379
|
+
} else if (mleEnabled && mleConfig?.strictResponseEncryption !== false && response.ok && typeof rawResponseBody?.jwe !== "string") {
|
|
380
|
+
throw new import_connect2.EdgeApiError(
|
|
381
|
+
"mle_response_missing",
|
|
382
|
+
"Expected encrypted response payload but received plaintext.",
|
|
383
|
+
response.status,
|
|
384
|
+
rawResponseBody
|
|
385
|
+
);
|
|
386
|
+
}
|
|
247
387
|
const durationMs = Date.now() - startTime;
|
|
248
388
|
this.config.onResponse?.({ status: response.status, body: responseBody, durationMs });
|
|
249
389
|
if (!response.ok) {
|
|
@@ -255,8 +395,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
255
395
|
}
|
|
256
396
|
return responseBody;
|
|
257
397
|
} catch (error) {
|
|
258
|
-
if (error instanceof
|
|
259
|
-
if (!(error instanceof
|
|
398
|
+
if (error instanceof import_connect2.EdgeError) {
|
|
399
|
+
if (!(error instanceof import_connect2.EdgeNetworkError) || attempt >= this.retryConfig.maxRetries) {
|
|
260
400
|
throw error;
|
|
261
401
|
}
|
|
262
402
|
lastError = error;
|
|
@@ -264,11 +404,24 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
264
404
|
}
|
|
265
405
|
lastError = error;
|
|
266
406
|
if (attempt >= this.retryConfig.maxRetries) {
|
|
267
|
-
throw new
|
|
407
|
+
throw new import_connect2.EdgeNetworkError(`API request failed: ${method} ${path}`, lastError);
|
|
268
408
|
}
|
|
269
409
|
}
|
|
270
410
|
}
|
|
271
|
-
throw new
|
|
411
|
+
throw new import_connect2.EdgeNetworkError(
|
|
412
|
+
`API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
|
|
413
|
+
lastError
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
getRetryDelay(attempt) {
|
|
417
|
+
const { backoff, baseDelayMs } = this.retryConfig;
|
|
418
|
+
if (backoff === "exponential") {
|
|
419
|
+
return baseDelayMs * Math.pow(2, attempt);
|
|
420
|
+
}
|
|
421
|
+
return baseDelayMs * (attempt + 1);
|
|
422
|
+
}
|
|
423
|
+
async sleep(ms) {
|
|
424
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
272
425
|
}
|
|
273
426
|
async fetchWithTimeout(url, options) {
|
|
274
427
|
const controller = new AbortController();
|
|
@@ -296,45 +449,37 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
296
449
|
const errorCode = error.error;
|
|
297
450
|
const errorMessage = error.message || error.error_description;
|
|
298
451
|
if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
|
|
299
|
-
return new
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
);
|
|
452
|
+
return new import_connect2.EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
|
|
453
|
+
edgeBoostError: error
|
|
454
|
+
});
|
|
303
455
|
}
|
|
304
456
|
if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
|
|
305
|
-
return new
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
);
|
|
457
|
+
return new import_connect2.EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
|
|
458
|
+
edgeBoostError: error
|
|
459
|
+
});
|
|
309
460
|
}
|
|
310
461
|
if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
|
|
311
|
-
return new
|
|
312
|
-
"PKCE verification failed. Please try again.",
|
|
313
|
-
{ edgeBoostError: error }
|
|
314
|
-
);
|
|
462
|
+
return new import_connect2.EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
|
|
315
463
|
}
|
|
316
|
-
return new
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
);
|
|
464
|
+
return new import_connect2.EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
|
|
465
|
+
edgeBoostError: error,
|
|
466
|
+
statusCode: status
|
|
467
|
+
});
|
|
320
468
|
}
|
|
321
469
|
async handleApiErrorFromBody(error, status, path) {
|
|
322
470
|
if (status === 401) {
|
|
323
|
-
return new
|
|
324
|
-
error.message || "Access token is invalid or expired",
|
|
325
|
-
error
|
|
326
|
-
);
|
|
471
|
+
return new import_connect2.EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
|
|
327
472
|
}
|
|
328
473
|
if (status === 403) {
|
|
329
474
|
if (error.error === "consent_required") {
|
|
330
|
-
return new
|
|
475
|
+
return new import_connect2.EdgeConsentRequiredError(
|
|
331
476
|
this.config.clientId,
|
|
332
477
|
error.consentUrl,
|
|
333
478
|
error.message
|
|
334
479
|
);
|
|
335
480
|
}
|
|
336
481
|
if (error.error === "insufficient_scope" || error.error === "insufficient_consent") {
|
|
337
|
-
return new
|
|
482
|
+
return new import_connect2.EdgeInsufficientScopeError(
|
|
338
483
|
error.missing_scopes || error.missingScopes || [],
|
|
339
484
|
error.message
|
|
340
485
|
);
|
|
@@ -343,9 +488,15 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
343
488
|
if (status === 404) {
|
|
344
489
|
const resourceType = path.includes("/transfer") ? "Transfer" : "Resource";
|
|
345
490
|
const resourceId = path.split("/").pop() || "unknown";
|
|
346
|
-
return new
|
|
491
|
+
return new import_connect2.EdgeNotFoundError(resourceType, resourceId);
|
|
347
492
|
}
|
|
348
|
-
|
|
493
|
+
if (status === 422 && error.error === "identity_verification_failed") {
|
|
494
|
+
return new import_connect2.EdgeIdentityVerificationError(
|
|
495
|
+
error.fieldErrors || {},
|
|
496
|
+
error.message
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return new import_connect2.EdgeApiError(
|
|
349
500
|
error.error || "api_error",
|
|
350
501
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
351
502
|
status,
|
|
@@ -355,8 +506,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
355
506
|
};
|
|
356
507
|
|
|
357
508
|
// src/index.ts
|
|
358
|
-
var import_connect2 = require("@edge-markets/connect");
|
|
359
509
|
var import_connect3 = require("@edge-markets/connect");
|
|
510
|
+
var import_connect4 = require("@edge-markets/connect");
|
|
360
511
|
// Annotate the CommonJS export names for ESM import in node:
|
|
361
512
|
0 && (module.exports = {
|
|
362
513
|
EdgeApiError,
|
|
@@ -368,6 +519,7 @@ var import_connect3 = require("@edge-markets/connect");
|
|
|
368
519
|
EdgeNetworkError,
|
|
369
520
|
EdgeNotFoundError,
|
|
370
521
|
EdgeTokenExchangeError,
|
|
522
|
+
EdgeUserClient,
|
|
371
523
|
getEnvironmentConfig,
|
|
372
524
|
isApiError,
|
|
373
525
|
isAuthenticationError,
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,190 @@
|
|
|
1
1
|
// src/edge-connect-server.ts
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
EdgeError,
|
|
3
|
+
EdgeApiError,
|
|
5
4
|
EdgeAuthenticationError,
|
|
6
|
-
EdgeTokenExchangeError,
|
|
7
5
|
EdgeConsentRequiredError,
|
|
6
|
+
EdgeError,
|
|
7
|
+
EdgeIdentityVerificationError,
|
|
8
8
|
EdgeInsufficientScopeError,
|
|
9
|
-
EdgeApiError,
|
|
10
|
-
EdgeNotFoundError,
|
|
11
9
|
EdgeNetworkError,
|
|
12
|
-
|
|
10
|
+
EdgeNotFoundError,
|
|
11
|
+
EdgeTokenExchangeError,
|
|
12
|
+
getEnvironmentConfig
|
|
13
13
|
} from "@edge-markets/connect";
|
|
14
|
+
|
|
15
|
+
// src/validators.ts
|
|
16
|
+
import { EdgeValidationError } from "@edge-markets/connect";
|
|
17
|
+
function validateVerifyIdentityOptions(options) {
|
|
18
|
+
const errors = {};
|
|
19
|
+
const firstName = typeof options.firstName === "string" ? options.firstName.trim() : "";
|
|
20
|
+
if (!firstName) {
|
|
21
|
+
errors.firstName = ["firstName is required for identity verification"];
|
|
22
|
+
}
|
|
23
|
+
const lastName = typeof options.lastName === "string" ? options.lastName.trim() : "";
|
|
24
|
+
if (!lastName) {
|
|
25
|
+
errors.lastName = ["lastName is required for identity verification"];
|
|
26
|
+
}
|
|
27
|
+
if (!options.address || typeof options.address !== "object" || Array.isArray(options.address)) {
|
|
28
|
+
errors.address = ["address is required for identity verification"];
|
|
29
|
+
}
|
|
30
|
+
if (Object.keys(errors).length > 0) {
|
|
31
|
+
const firstMessage = Object.values(errors)[0][0];
|
|
32
|
+
throw new EdgeValidationError(firstMessage, errors);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function validateTransferOptions(options) {
|
|
36
|
+
const errors = {};
|
|
37
|
+
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
38
|
+
errors.type = ['Must be "debit" or "credit"'];
|
|
39
|
+
}
|
|
40
|
+
if (!options.amount) {
|
|
41
|
+
errors.amount = ["Amount is required"];
|
|
42
|
+
} else {
|
|
43
|
+
const amount = parseFloat(options.amount);
|
|
44
|
+
if (isNaN(amount)) {
|
|
45
|
+
errors.amount = ["Must be a valid number"];
|
|
46
|
+
} else if (amount <= 0) {
|
|
47
|
+
errors.amount = ["Must be greater than 0"];
|
|
48
|
+
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
49
|
+
errors.amount = ["Must have at most 2 decimal places"];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
53
|
+
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
54
|
+
} else if (options.idempotencyKey.length > 255) {
|
|
55
|
+
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
56
|
+
}
|
|
57
|
+
if (Object.keys(errors).length > 0) {
|
|
58
|
+
throw new EdgeValidationError("Invalid transfer options", errors);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/edge-user-client.ts
|
|
63
|
+
var EdgeUserClient = class {
|
|
64
|
+
constructor(server, accessToken) {
|
|
65
|
+
this.server = server;
|
|
66
|
+
this.accessToken = accessToken;
|
|
67
|
+
}
|
|
68
|
+
async getUser() {
|
|
69
|
+
return this.server._apiRequest("GET", "/user", this.accessToken);
|
|
70
|
+
}
|
|
71
|
+
async verifyIdentity(options) {
|
|
72
|
+
validateVerifyIdentityOptions(options);
|
|
73
|
+
return this.server._apiRequest("POST", "/user/verify-identity", this.accessToken, options);
|
|
74
|
+
}
|
|
75
|
+
async getBalance() {
|
|
76
|
+
return this.server._apiRequest("GET", "/balance", this.accessToken);
|
|
77
|
+
}
|
|
78
|
+
async initiateTransfer(options) {
|
|
79
|
+
validateTransferOptions(options);
|
|
80
|
+
const body = {
|
|
81
|
+
type: options.type,
|
|
82
|
+
amount: options.amount,
|
|
83
|
+
idempotencyKey: options.idempotencyKey
|
|
84
|
+
};
|
|
85
|
+
return this.server._apiRequest("POST", "/transfer", this.accessToken, body);
|
|
86
|
+
}
|
|
87
|
+
async verifyTransfer(transferId, otp) {
|
|
88
|
+
return this.server._apiRequest(
|
|
89
|
+
"POST",
|
|
90
|
+
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
91
|
+
this.accessToken,
|
|
92
|
+
{ otp }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
async getTransfer(transferId) {
|
|
96
|
+
return this.server._apiRequest("GET", `/transfer/${encodeURIComponent(transferId)}`, this.accessToken);
|
|
97
|
+
}
|
|
98
|
+
async listTransfers(params) {
|
|
99
|
+
const queryParams = new URLSearchParams();
|
|
100
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
101
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
102
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
103
|
+
const query = queryParams.toString();
|
|
104
|
+
const path = query ? `/transfers?${query}` : "/transfers";
|
|
105
|
+
return this.server._apiRequest("GET", path, this.accessToken);
|
|
106
|
+
}
|
|
107
|
+
async revokeConsent() {
|
|
108
|
+
return this.server._apiRequest("DELETE", "/consent", this.accessToken);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/mle.ts
|
|
113
|
+
import { randomUUID, randomBytes, createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from "crypto";
|
|
114
|
+
function encryptMle(payload, clientId, config) {
|
|
115
|
+
const header = {
|
|
116
|
+
alg: "RSA-OAEP-256",
|
|
117
|
+
enc: "A256GCM",
|
|
118
|
+
kid: config.edgeKeyId,
|
|
119
|
+
typ: "application/json",
|
|
120
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
121
|
+
jti: randomUUID(),
|
|
122
|
+
clientId
|
|
123
|
+
};
|
|
124
|
+
const protectedHeaderB64 = toBase64Url(Buffer.from(JSON.stringify(header), "utf8"));
|
|
125
|
+
const aad = Buffer.from(protectedHeaderB64, "utf8");
|
|
126
|
+
const cek = randomBytes(32);
|
|
127
|
+
const iv = randomBytes(12);
|
|
128
|
+
const cipher = createCipheriv("aes-256-gcm", cek, iv);
|
|
129
|
+
cipher.setAAD(aad);
|
|
130
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
131
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
132
|
+
const tag = cipher.getAuthTag();
|
|
133
|
+
const encryptedKey = publicEncrypt(
|
|
134
|
+
{
|
|
135
|
+
key: config.edgePublicKey,
|
|
136
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING,
|
|
137
|
+
oaepHash: "sha256"
|
|
138
|
+
},
|
|
139
|
+
cek
|
|
140
|
+
);
|
|
141
|
+
return [
|
|
142
|
+
protectedHeaderB64,
|
|
143
|
+
toBase64Url(encryptedKey),
|
|
144
|
+
toBase64Url(iv),
|
|
145
|
+
toBase64Url(ciphertext),
|
|
146
|
+
toBase64Url(tag)
|
|
147
|
+
].join(".");
|
|
148
|
+
}
|
|
149
|
+
function decryptMle(jwe, config) {
|
|
150
|
+
const parts = jwe.split(".");
|
|
151
|
+
if (parts.length !== 5) {
|
|
152
|
+
throw new Error("Invalid MLE payload format");
|
|
153
|
+
}
|
|
154
|
+
const [protectedHeaderB64, encryptedKeyB64, ivB64, ciphertextB64, tagB64] = parts;
|
|
155
|
+
const protectedHeader = JSON.parse(fromBase64Url(protectedHeaderB64).toString("utf8"));
|
|
156
|
+
if (protectedHeader.alg !== "RSA-OAEP-256" || protectedHeader.enc !== "A256GCM") {
|
|
157
|
+
throw new Error("Unsupported MLE algorithms");
|
|
158
|
+
}
|
|
159
|
+
if (protectedHeader.kid && protectedHeader.kid !== config.partnerKeyId) {
|
|
160
|
+
throw new Error(`Unexpected response key id: ${protectedHeader.kid}`);
|
|
161
|
+
}
|
|
162
|
+
const cek = privateDecrypt(
|
|
163
|
+
{
|
|
164
|
+
key: config.partnerPrivateKey,
|
|
165
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING,
|
|
166
|
+
oaepHash: "sha256"
|
|
167
|
+
},
|
|
168
|
+
fromBase64Url(encryptedKeyB64)
|
|
169
|
+
);
|
|
170
|
+
const decipher = createDecipheriv("aes-256-gcm", cek, fromBase64Url(ivB64));
|
|
171
|
+
decipher.setAAD(Buffer.from(protectedHeaderB64, "utf8"));
|
|
172
|
+
decipher.setAuthTag(fromBase64Url(tagB64));
|
|
173
|
+
const plaintext = Buffer.concat([
|
|
174
|
+
decipher.update(fromBase64Url(ciphertextB64)),
|
|
175
|
+
decipher.final()
|
|
176
|
+
]);
|
|
177
|
+
return JSON.parse(plaintext.toString("utf8"));
|
|
178
|
+
}
|
|
179
|
+
function toBase64Url(buffer) {
|
|
180
|
+
return buffer.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
181
|
+
}
|
|
182
|
+
function fromBase64Url(value) {
|
|
183
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
184
|
+
return Buffer.from(normalized, "base64");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/types.ts
|
|
14
188
|
var DEFAULT_TIMEOUT = 3e4;
|
|
15
189
|
var USER_AGENT = "@edge-markets/connect-node/1.0.0";
|
|
16
190
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -19,6 +193,8 @@ var DEFAULT_RETRY_CONFIG = {
|
|
|
19
193
|
backoff: "exponential",
|
|
20
194
|
baseDelayMs: 1e3
|
|
21
195
|
};
|
|
196
|
+
|
|
197
|
+
// src/edge-connect-server.ts
|
|
22
198
|
var instances = /* @__PURE__ */ new Map();
|
|
23
199
|
function getInstanceKey(config) {
|
|
24
200
|
return `${config.clientId}:${config.environment}`;
|
|
@@ -55,6 +231,18 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
55
231
|
...config.retry
|
|
56
232
|
};
|
|
57
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
236
|
+
*
|
|
237
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
238
|
+
* or per user session and discard it when the token changes.
|
|
239
|
+
*/
|
|
240
|
+
forUser(accessToken) {
|
|
241
|
+
if (!accessToken) {
|
|
242
|
+
throw new EdgeAuthenticationError("accessToken is required when calling forUser()");
|
|
243
|
+
}
|
|
244
|
+
return new EdgeUserClient(this, accessToken);
|
|
245
|
+
}
|
|
58
246
|
async exchangeCode(code, codeVerifier, redirectUri) {
|
|
59
247
|
const tokenUrl = `${this.oauthBaseUrl}/token`;
|
|
60
248
|
const body = {
|
|
@@ -116,87 +304,12 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
116
304
|
throw new EdgeNetworkError("Failed to refresh tokens", error);
|
|
117
305
|
}
|
|
118
306
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
async getBalance(accessToken) {
|
|
123
|
-
return this.apiRequest("GET", "/balance", accessToken);
|
|
124
|
-
}
|
|
125
|
-
async initiateTransfer(accessToken, options) {
|
|
126
|
-
this.validateTransferOptions(options);
|
|
127
|
-
const body = {
|
|
128
|
-
type: options.type,
|
|
129
|
-
amount: options.amount,
|
|
130
|
-
idempotencyKey: options.idempotencyKey
|
|
131
|
-
};
|
|
132
|
-
return this.apiRequest("POST", "/transfer", accessToken, body);
|
|
133
|
-
}
|
|
134
|
-
validateTransferOptions(options) {
|
|
135
|
-
const errors = {};
|
|
136
|
-
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
137
|
-
errors.type = ['Must be "debit" or "credit"'];
|
|
138
|
-
}
|
|
139
|
-
if (!options.amount) {
|
|
140
|
-
errors.amount = ["Amount is required"];
|
|
141
|
-
} else {
|
|
142
|
-
const amount = parseFloat(options.amount);
|
|
143
|
-
if (isNaN(amount)) {
|
|
144
|
-
errors.amount = ["Must be a valid number"];
|
|
145
|
-
} else if (amount <= 0) {
|
|
146
|
-
errors.amount = ["Must be greater than 0"];
|
|
147
|
-
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
148
|
-
errors.amount = ["Must have at most 2 decimal places"];
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
152
|
-
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
153
|
-
} else if (options.idempotencyKey.length > 255) {
|
|
154
|
-
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
155
|
-
}
|
|
156
|
-
if (Object.keys(errors).length > 0) {
|
|
157
|
-
throw new EdgeValidationError("Invalid transfer options", errors);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async verifyTransfer(accessToken, transferId, otp) {
|
|
161
|
-
return this.apiRequest(
|
|
162
|
-
"POST",
|
|
163
|
-
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
164
|
-
accessToken,
|
|
165
|
-
{ otp }
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
async getTransfer(accessToken, transferId) {
|
|
169
|
-
return this.apiRequest(
|
|
170
|
-
"GET",
|
|
171
|
-
`/transfer/${encodeURIComponent(transferId)}`,
|
|
172
|
-
accessToken
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
async listTransfers(accessToken, params) {
|
|
176
|
-
const queryParams = new URLSearchParams();
|
|
177
|
-
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
178
|
-
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
179
|
-
if (params?.status) queryParams.set("status", params.status);
|
|
180
|
-
const query = queryParams.toString();
|
|
181
|
-
const path = query ? `/transfers?${query}` : "/transfers";
|
|
182
|
-
return this.apiRequest("GET", path, accessToken);
|
|
183
|
-
}
|
|
184
|
-
async revokeConsent(accessToken) {
|
|
185
|
-
return this.apiRequest("DELETE", "/consent", accessToken);
|
|
186
|
-
}
|
|
187
|
-
getRetryDelay(attempt) {
|
|
188
|
-
const { backoff, baseDelayMs } = this.retryConfig;
|
|
189
|
-
if (backoff === "exponential") {
|
|
190
|
-
return baseDelayMs * Math.pow(2, attempt);
|
|
191
|
-
}
|
|
192
|
-
return baseDelayMs * (attempt + 1);
|
|
193
|
-
}
|
|
194
|
-
async sleep(ms) {
|
|
195
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
196
|
-
}
|
|
197
|
-
async apiRequest(method, path, accessToken, body) {
|
|
307
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
308
|
+
async _apiRequest(method, path, accessToken, body) {
|
|
198
309
|
const url = `${this.apiBaseUrl}${path}`;
|
|
199
310
|
let lastError = null;
|
|
311
|
+
const mleConfig = this.config.mle;
|
|
312
|
+
const mleEnabled = !!mleConfig?.enabled;
|
|
200
313
|
for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
|
|
201
314
|
const startTime = Date.now();
|
|
202
315
|
if (attempt > 0) {
|
|
@@ -204,16 +317,42 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
204
317
|
}
|
|
205
318
|
this.config.onRequest?.({ method, url, body });
|
|
206
319
|
try {
|
|
320
|
+
const requestHeaders = {
|
|
321
|
+
Authorization: `Bearer ${accessToken}`,
|
|
322
|
+
"Content-Type": "application/json",
|
|
323
|
+
"User-Agent": USER_AGENT
|
|
324
|
+
};
|
|
325
|
+
let requestBody = body;
|
|
326
|
+
if (mleEnabled) {
|
|
327
|
+
requestHeaders["X-Edge-MLE"] = "v1";
|
|
328
|
+
if (body !== void 0) {
|
|
329
|
+
requestBody = { jwe: encryptMle(body, this.config.clientId, mleConfig) };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
207
332
|
const response = await this.fetchWithTimeout(url, {
|
|
208
333
|
method,
|
|
209
|
-
headers:
|
|
210
|
-
|
|
211
|
-
"Content-Type": "application/json",
|
|
212
|
-
"User-Agent": USER_AGENT
|
|
213
|
-
},
|
|
214
|
-
body: body ? JSON.stringify(body) : void 0
|
|
334
|
+
headers: requestHeaders,
|
|
335
|
+
body: requestBody !== void 0 ? JSON.stringify(requestBody) : void 0
|
|
215
336
|
});
|
|
216
|
-
const
|
|
337
|
+
const rawResponseBody = await response.json().catch(() => ({}));
|
|
338
|
+
let responseBody = rawResponseBody;
|
|
339
|
+
if (mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
340
|
+
responseBody = decryptMle(rawResponseBody.jwe, mleConfig);
|
|
341
|
+
} else if (!mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
342
|
+
throw new EdgeApiError(
|
|
343
|
+
"mle_required",
|
|
344
|
+
"The API responded with message-level encryption. Enable MLE in SDK config.",
|
|
345
|
+
response.status,
|
|
346
|
+
rawResponseBody
|
|
347
|
+
);
|
|
348
|
+
} else if (mleEnabled && mleConfig?.strictResponseEncryption !== false && response.ok && typeof rawResponseBody?.jwe !== "string") {
|
|
349
|
+
throw new EdgeApiError(
|
|
350
|
+
"mle_response_missing",
|
|
351
|
+
"Expected encrypted response payload but received plaintext.",
|
|
352
|
+
response.status,
|
|
353
|
+
rawResponseBody
|
|
354
|
+
);
|
|
355
|
+
}
|
|
217
356
|
const durationMs = Date.now() - startTime;
|
|
218
357
|
this.config.onResponse?.({ status: response.status, body: responseBody, durationMs });
|
|
219
358
|
if (!response.ok) {
|
|
@@ -238,7 +377,20 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
238
377
|
}
|
|
239
378
|
}
|
|
240
379
|
}
|
|
241
|
-
throw new EdgeNetworkError(
|
|
380
|
+
throw new EdgeNetworkError(
|
|
381
|
+
`API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
|
|
382
|
+
lastError
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
getRetryDelay(attempt) {
|
|
386
|
+
const { backoff, baseDelayMs } = this.retryConfig;
|
|
387
|
+
if (backoff === "exponential") {
|
|
388
|
+
return baseDelayMs * Math.pow(2, attempt);
|
|
389
|
+
}
|
|
390
|
+
return baseDelayMs * (attempt + 1);
|
|
391
|
+
}
|
|
392
|
+
async sleep(ms) {
|
|
393
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
242
394
|
}
|
|
243
395
|
async fetchWithTimeout(url, options) {
|
|
244
396
|
const controller = new AbortController();
|
|
@@ -266,34 +418,26 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
266
418
|
const errorCode = error.error;
|
|
267
419
|
const errorMessage = error.message || error.error_description;
|
|
268
420
|
if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
|
|
269
|
-
return new EdgeTokenExchangeError(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
);
|
|
421
|
+
return new EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
|
|
422
|
+
edgeBoostError: error
|
|
423
|
+
});
|
|
273
424
|
}
|
|
274
425
|
if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
|
|
275
|
-
return new EdgeAuthenticationError(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
);
|
|
426
|
+
return new EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
|
|
427
|
+
edgeBoostError: error
|
|
428
|
+
});
|
|
279
429
|
}
|
|
280
430
|
if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
|
|
281
|
-
return new EdgeTokenExchangeError(
|
|
282
|
-
"PKCE verification failed. Please try again.",
|
|
283
|
-
{ edgeBoostError: error }
|
|
284
|
-
);
|
|
431
|
+
return new EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
|
|
285
432
|
}
|
|
286
|
-
return new EdgeTokenExchangeError(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
);
|
|
433
|
+
return new EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
|
|
434
|
+
edgeBoostError: error,
|
|
435
|
+
statusCode: status
|
|
436
|
+
});
|
|
290
437
|
}
|
|
291
438
|
async handleApiErrorFromBody(error, status, path) {
|
|
292
439
|
if (status === 401) {
|
|
293
|
-
return new EdgeAuthenticationError(
|
|
294
|
-
error.message || "Access token is invalid or expired",
|
|
295
|
-
error
|
|
296
|
-
);
|
|
440
|
+
return new EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
|
|
297
441
|
}
|
|
298
442
|
if (status === 403) {
|
|
299
443
|
if (error.error === "consent_required") {
|
|
@@ -315,6 +459,12 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
315
459
|
const resourceId = path.split("/").pop() || "unknown";
|
|
316
460
|
return new EdgeNotFoundError(resourceType, resourceId);
|
|
317
461
|
}
|
|
462
|
+
if (status === 422 && error.error === "identity_verification_failed") {
|
|
463
|
+
return new EdgeIdentityVerificationError(
|
|
464
|
+
error.fieldErrors || {},
|
|
465
|
+
error.message
|
|
466
|
+
);
|
|
467
|
+
}
|
|
318
468
|
return new EdgeApiError(
|
|
319
469
|
error.error || "api_error",
|
|
320
470
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
@@ -326,24 +476,21 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
326
476
|
|
|
327
477
|
// src/index.ts
|
|
328
478
|
import {
|
|
329
|
-
|
|
479
|
+
EdgeApiError as EdgeApiError2,
|
|
330
480
|
EdgeAuthenticationError as EdgeAuthenticationError2,
|
|
331
|
-
EdgeTokenExchangeError as EdgeTokenExchangeError2,
|
|
332
481
|
EdgeConsentRequiredError as EdgeConsentRequiredError2,
|
|
482
|
+
EdgeError as EdgeError2,
|
|
333
483
|
EdgeInsufficientScopeError as EdgeInsufficientScopeError2,
|
|
334
|
-
EdgeApiError as EdgeApiError2,
|
|
335
|
-
EdgeNotFoundError as EdgeNotFoundError2,
|
|
336
484
|
EdgeNetworkError as EdgeNetworkError2,
|
|
337
|
-
|
|
485
|
+
EdgeNotFoundError as EdgeNotFoundError2,
|
|
486
|
+
EdgeTokenExchangeError as EdgeTokenExchangeError2,
|
|
487
|
+
isApiError,
|
|
338
488
|
isAuthenticationError,
|
|
339
489
|
isConsentRequiredError,
|
|
340
|
-
|
|
490
|
+
isEdgeError,
|
|
341
491
|
isNetworkError
|
|
342
492
|
} from "@edge-markets/connect";
|
|
343
|
-
import {
|
|
344
|
-
getEnvironmentConfig as getEnvironmentConfig2,
|
|
345
|
-
isProductionEnvironment
|
|
346
|
-
} from "@edge-markets/connect";
|
|
493
|
+
import { getEnvironmentConfig as getEnvironmentConfig2, isProductionEnvironment } from "@edge-markets/connect";
|
|
347
494
|
export {
|
|
348
495
|
EdgeApiError2 as EdgeApiError,
|
|
349
496
|
EdgeAuthenticationError2 as EdgeAuthenticationError,
|
|
@@ -354,6 +501,7 @@ export {
|
|
|
354
501
|
EdgeNetworkError2 as EdgeNetworkError,
|
|
355
502
|
EdgeNotFoundError2 as EdgeNotFoundError,
|
|
356
503
|
EdgeTokenExchangeError2 as EdgeTokenExchangeError,
|
|
504
|
+
EdgeUserClient,
|
|
357
505
|
getEnvironmentConfig2 as getEnvironmentConfig,
|
|
358
506
|
isApiError,
|
|
359
507
|
isAuthenticationError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-markets/connect-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Server SDK for EDGE Connect token exchange and API calls",
|
|
5
5
|
"author": "Edge Markets",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@edge-markets/connect": "^1.
|
|
24
|
+
"@edge-markets/connect": "^1.3.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsup": "^8.0.0",
|