@edge-markets/connect-node 1.3.0 → 1.5.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 -25
- package/dist/index.d.mts +46 -14
- package/dist/index.d.ts +46 -14
- package/dist/index.js +218 -134
- package/dist/index.mjs +199 -119
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Server SDK for EDGE Connect token exchange and API calls.
|
|
|
6
6
|
|
|
7
7
|
- 🔐 **Secure token exchange** - Exchange codes for tokens with PKCE
|
|
8
8
|
- 🔄 **Token refresh** - Automatic refresh token handling
|
|
9
|
-
- 📡 **Full API client** - User, balance, transfers
|
|
9
|
+
- 📡 **Full API client** - User, balance, transfers via `forUser()` pattern
|
|
10
10
|
- 🛡️ **Typed errors** - Specific error classes for each scenario
|
|
11
11
|
- 📝 **TypeScript first** - Complete type definitions
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@ yarn add @edge-markets/connect-node
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { EdgeConnectServer } from '@
|
|
26
|
+
import { EdgeConnectServer } from '@edge-markets/connect-node'
|
|
27
27
|
|
|
28
28
|
// Create instance once (reuse for all requests)
|
|
29
29
|
const edge = new EdgeConnectServer({
|
|
@@ -35,9 +35,10 @@ const edge = new EdgeConnectServer({
|
|
|
35
35
|
// Exchange code from EdgeLink for tokens
|
|
36
36
|
const tokens = await edge.exchangeCode(code, codeVerifier)
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
const
|
|
38
|
+
// Create a user-scoped client and make API calls
|
|
39
|
+
const client = edge.forUser(tokens.accessToken)
|
|
40
|
+
const user = await client.getUser()
|
|
41
|
+
const balance = await client.getBalance()
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
## ⚠️ Security
|
|
@@ -56,8 +57,11 @@ interface EdgeConnectServerConfig {
|
|
|
56
57
|
|
|
57
58
|
// Optional
|
|
58
59
|
apiBaseUrl?: string // Custom API URL (dev only)
|
|
59
|
-
|
|
60
|
+
oauthBaseUrl?: string // Custom OAuth URL (dev only)
|
|
60
61
|
timeout?: number // Request timeout (default: 30000ms)
|
|
62
|
+
retry?: RetryConfig // Retry configuration
|
|
63
|
+
onRequest?: (info) => void // Hook called before each request
|
|
64
|
+
onResponse?: (info) => void // Hook called after each response
|
|
61
65
|
|
|
62
66
|
// Optional: Message Level Encryption (Connect endpoints only)
|
|
63
67
|
mle?: {
|
|
@@ -152,15 +156,19 @@ async function getValidAccessToken(userId: string): Promise<string> {
|
|
|
152
156
|
|
|
153
157
|
## API Methods
|
|
154
158
|
|
|
159
|
+
All user-scoped API methods live on `EdgeUserClient`, created via `edge.forUser(accessToken)`:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const client = edge.forUser(accessToken)
|
|
163
|
+
```
|
|
164
|
+
|
|
155
165
|
### User & Balance
|
|
156
166
|
|
|
157
167
|
```typescript
|
|
158
|
-
|
|
159
|
-
const user = await edge.getUser(accessToken)
|
|
168
|
+
const user = await client.getUser()
|
|
160
169
|
// Returns: { id, email, firstName, lastName, createdAt }
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
const balance = await edge.getBalance(accessToken)
|
|
171
|
+
const balance = await client.getBalance()
|
|
164
172
|
// Returns: { userId, availableBalance, currency, asOf }
|
|
165
173
|
```
|
|
166
174
|
|
|
@@ -168,7 +176,7 @@ const balance = await edge.getBalance(accessToken)
|
|
|
168
176
|
|
|
169
177
|
```typescript
|
|
170
178
|
// Initiate transfer (requires OTP verification)
|
|
171
|
-
const transfer = await
|
|
179
|
+
const transfer = await client.initiateTransfer({
|
|
172
180
|
type: 'debit', // 'debit' = pull from user, 'credit' = push to user
|
|
173
181
|
amount: '100.00',
|
|
174
182
|
idempotencyKey: `txn_${userId}_${Date.now()}`,
|
|
@@ -176,14 +184,14 @@ const transfer = await edge.initiateTransfer(accessToken, {
|
|
|
176
184
|
// Returns: { transferId, status: 'pending_verification', otpMethod }
|
|
177
185
|
|
|
178
186
|
// Verify with OTP
|
|
179
|
-
const result = await
|
|
187
|
+
const result = await client.verifyTransfer(transfer.transferId, userOtp)
|
|
180
188
|
// Returns: { transferId, status: 'completed' | 'failed' }
|
|
181
189
|
|
|
182
190
|
// Get transfer status
|
|
183
|
-
const status = await
|
|
191
|
+
const status = await client.getTransfer(transferId)
|
|
184
192
|
|
|
185
193
|
// List transfers
|
|
186
|
-
const { transfers, total } = await
|
|
194
|
+
const { transfers, total } = await client.listTransfers({
|
|
187
195
|
status: 'completed',
|
|
188
196
|
limit: 10,
|
|
189
197
|
offset: 0,
|
|
@@ -194,7 +202,7 @@ const { transfers, total } = await edge.listTransfers(accessToken, {
|
|
|
194
202
|
|
|
195
203
|
```typescript
|
|
196
204
|
// Revoke consent (disconnect user)
|
|
197
|
-
await
|
|
205
|
+
await client.revokeConsent()
|
|
198
206
|
|
|
199
207
|
// Clean up stored tokens
|
|
200
208
|
await db.edgeConnections.delete(userId)
|
|
@@ -212,7 +220,8 @@ import {
|
|
|
212
220
|
} from '@edge-markets/connect-node'
|
|
213
221
|
|
|
214
222
|
try {
|
|
215
|
-
const
|
|
223
|
+
const client = edge.forUser(accessToken)
|
|
224
|
+
const balance = await client.getBalance()
|
|
216
225
|
} catch (error) {
|
|
217
226
|
if (error instanceof EdgeAuthenticationError) {
|
|
218
227
|
// Token expired - try refresh or reconnect
|
|
@@ -273,7 +282,8 @@ export class EdgeService {
|
|
|
273
282
|
|
|
274
283
|
async getBalance(accessToken: string) {
|
|
275
284
|
try {
|
|
276
|
-
|
|
285
|
+
const client = this.edge.forUser(accessToken)
|
|
286
|
+
return await client.getBalance()
|
|
277
287
|
} catch (error) {
|
|
278
288
|
if (error instanceof EdgeConsentRequiredError) {
|
|
279
289
|
this.logger.warn('User consent required')
|
|
@@ -323,7 +333,8 @@ app.post('/api/edge/exchange', async (req, res) => {
|
|
|
323
333
|
app.get('/api/edge/balance', async (req, res) => {
|
|
324
334
|
try {
|
|
325
335
|
const accessToken = await getAccessTokenForUser(req.user.id)
|
|
326
|
-
const
|
|
336
|
+
const client = edge.forUser(accessToken)
|
|
337
|
+
const balance = await client.getBalance()
|
|
327
338
|
res.json(balance)
|
|
328
339
|
} catch (error) {
|
|
329
340
|
// Handle errors...
|
|
@@ -339,10 +350,3 @@ app.get('/api/edge/balance', async (req, res) => {
|
|
|
339
350
|
## License
|
|
340
351
|
|
|
341
352
|
MIT
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgeEnvironment,
|
|
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';
|
|
1
|
+
import { EdgeEnvironment, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, Transfer, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSession, VerificationSessionStatusResponse, EdgeTokens } from '@edge-markets/connect';
|
|
2
|
+
export { Balance, CreateVerificationSessionRequest, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, ListTransfersParams, Transfer, TransferList, TransferListItem, TransferStatus, TransferType, User, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
interface RequestInfo {
|
|
5
5
|
method: string;
|
|
@@ -46,6 +46,40 @@ interface TransferOptions {
|
|
|
46
46
|
amount: string;
|
|
47
47
|
idempotencyKey: string;
|
|
48
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
|
+
* Creates an EDGE-hosted transfer verification session.
|
|
72
|
+
* Returns a `verificationUrl` to open in an iframe or popup.
|
|
73
|
+
* The handoff token is single-use and expires in 120 seconds.
|
|
74
|
+
*/
|
|
75
|
+
createVerificationSession(transferId: string, options: CreateVerificationSessionRequest): Promise<VerificationSession>;
|
|
76
|
+
/**
|
|
77
|
+
* Polls the status of a verification session.
|
|
78
|
+
* Use as an alternative to postMessage events.
|
|
79
|
+
*/
|
|
80
|
+
getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
|
|
81
|
+
}
|
|
82
|
+
|
|
49
83
|
declare class EdgeConnectServer {
|
|
50
84
|
private readonly config;
|
|
51
85
|
private readonly apiBaseUrl;
|
|
@@ -55,25 +89,23 @@ declare class EdgeConnectServer {
|
|
|
55
89
|
static getInstance(config: EdgeConnectServerConfig): EdgeConnectServer;
|
|
56
90
|
static clearInstances(): void;
|
|
57
91
|
constructor(config: EdgeConnectServerConfig);
|
|
92
|
+
/**
|
|
93
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
94
|
+
*
|
|
95
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
96
|
+
* or per user session and discard it when the token changes.
|
|
97
|
+
*/
|
|
98
|
+
forUser(accessToken: string): EdgeUserClient;
|
|
58
99
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
59
100
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
initiateTransfer(accessToken: string, options: TransferOptions): Promise<Transfer>;
|
|
63
|
-
private validateTransferOptions;
|
|
64
|
-
verifyTransfer(accessToken: string, transferId: string, otp: string): Promise<Transfer>;
|
|
65
|
-
getTransfer(accessToken: string, transferId: string): Promise<Transfer>;
|
|
66
|
-
listTransfers(accessToken: string, params?: ListTransfersParams): Promise<TransferList>;
|
|
67
|
-
revokeConsent(accessToken: string): Promise<{
|
|
68
|
-
revoked: boolean;
|
|
69
|
-
}>;
|
|
101
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
102
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown): Promise<T>;
|
|
70
103
|
private getRetryDelay;
|
|
71
104
|
private sleep;
|
|
72
|
-
private apiRequest;
|
|
73
105
|
private fetchWithTimeout;
|
|
74
106
|
private parseTokenResponse;
|
|
75
107
|
private handleTokenError;
|
|
76
108
|
private handleApiErrorFromBody;
|
|
77
109
|
}
|
|
78
110
|
|
|
79
|
-
export { EdgeConnectServer, type EdgeConnectServerConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
|
111
|
+
export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgeEnvironment,
|
|
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';
|
|
1
|
+
import { EdgeEnvironment, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, Transfer, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSession, VerificationSessionStatusResponse, EdgeTokens } from '@edge-markets/connect';
|
|
2
|
+
export { Balance, CreateVerificationSessionRequest, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, ListTransfersParams, Transfer, TransferList, TransferListItem, TransferStatus, TransferType, User, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
interface RequestInfo {
|
|
5
5
|
method: string;
|
|
@@ -46,6 +46,40 @@ interface TransferOptions {
|
|
|
46
46
|
amount: string;
|
|
47
47
|
idempotencyKey: string;
|
|
48
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
|
+
* Creates an EDGE-hosted transfer verification session.
|
|
72
|
+
* Returns a `verificationUrl` to open in an iframe or popup.
|
|
73
|
+
* The handoff token is single-use and expires in 120 seconds.
|
|
74
|
+
*/
|
|
75
|
+
createVerificationSession(transferId: string, options: CreateVerificationSessionRequest): Promise<VerificationSession>;
|
|
76
|
+
/**
|
|
77
|
+
* Polls the status of a verification session.
|
|
78
|
+
* Use as an alternative to postMessage events.
|
|
79
|
+
*/
|
|
80
|
+
getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
|
|
81
|
+
}
|
|
82
|
+
|
|
49
83
|
declare class EdgeConnectServer {
|
|
50
84
|
private readonly config;
|
|
51
85
|
private readonly apiBaseUrl;
|
|
@@ -55,25 +89,23 @@ declare class EdgeConnectServer {
|
|
|
55
89
|
static getInstance(config: EdgeConnectServerConfig): EdgeConnectServer;
|
|
56
90
|
static clearInstances(): void;
|
|
57
91
|
constructor(config: EdgeConnectServerConfig);
|
|
92
|
+
/**
|
|
93
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
94
|
+
*
|
|
95
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
96
|
+
* or per user session and discard it when the token changes.
|
|
97
|
+
*/
|
|
98
|
+
forUser(accessToken: string): EdgeUserClient;
|
|
58
99
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
59
100
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
initiateTransfer(accessToken: string, options: TransferOptions): Promise<Transfer>;
|
|
63
|
-
private validateTransferOptions;
|
|
64
|
-
verifyTransfer(accessToken: string, transferId: string, otp: string): Promise<Transfer>;
|
|
65
|
-
getTransfer(accessToken: string, transferId: string): Promise<Transfer>;
|
|
66
|
-
listTransfers(accessToken: string, params?: ListTransfersParams): Promise<TransferList>;
|
|
67
|
-
revokeConsent(accessToken: string): Promise<{
|
|
68
|
-
revoked: boolean;
|
|
69
|
-
}>;
|
|
101
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
102
|
+
_apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown): Promise<T>;
|
|
70
103
|
private getRetryDelay;
|
|
71
104
|
private sleep;
|
|
72
|
-
private apiRequest;
|
|
73
105
|
private fetchWithTimeout;
|
|
74
106
|
private parseTokenResponse;
|
|
75
107
|
private handleTokenError;
|
|
76
108
|
private handleApiErrorFromBody;
|
|
77
109
|
}
|
|
78
110
|
|
|
79
|
-
export { EdgeConnectServer, type EdgeConnectServerConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
|
111
|
+
export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
|
package/dist/index.js
CHANGED
|
@@ -20,27 +20,149 @@ 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
|
+
* Creates an EDGE-hosted transfer verification session.
|
|
143
|
+
* Returns a `verificationUrl` to open in an iframe or popup.
|
|
144
|
+
* The handoff token is single-use and expires in 120 seconds.
|
|
145
|
+
*/
|
|
146
|
+
async createVerificationSession(transferId, options) {
|
|
147
|
+
return this.server._apiRequest(
|
|
148
|
+
"POST",
|
|
149
|
+
`/transfer/${encodeURIComponent(transferId)}/verification-session`,
|
|
150
|
+
this.accessToken,
|
|
151
|
+
{ origin: options.origin }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Polls the status of a verification session.
|
|
156
|
+
* Use as an alternative to postMessage events.
|
|
157
|
+
*/
|
|
158
|
+
async getVerificationSessionStatus(transferId, sessionId) {
|
|
159
|
+
return this.server._apiRequest(
|
|
160
|
+
"GET",
|
|
161
|
+
`/transfer/${encodeURIComponent(transferId)}/verification-session/${encodeURIComponent(sessionId)}`,
|
|
162
|
+
this.accessToken
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
44
166
|
|
|
45
167
|
// src/mle.ts
|
|
46
168
|
var import_crypto = require("crypto");
|
|
@@ -117,7 +239,7 @@ function fromBase64Url(value) {
|
|
|
117
239
|
return Buffer.from(normalized, "base64");
|
|
118
240
|
}
|
|
119
241
|
|
|
120
|
-
// src/
|
|
242
|
+
// src/types.ts
|
|
121
243
|
var DEFAULT_TIMEOUT = 3e4;
|
|
122
244
|
var USER_AGENT = "@edge-markets/connect-node/1.0.0";
|
|
123
245
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -126,6 +248,8 @@ var DEFAULT_RETRY_CONFIG = {
|
|
|
126
248
|
backoff: "exponential",
|
|
127
249
|
baseDelayMs: 1e3
|
|
128
250
|
};
|
|
251
|
+
|
|
252
|
+
// src/edge-connect-server.ts
|
|
129
253
|
var instances = /* @__PURE__ */ new Map();
|
|
130
254
|
function getInstanceKey(config) {
|
|
131
255
|
return `${config.clientId}:${config.environment}`;
|
|
@@ -153,7 +277,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
153
277
|
throw new Error("EdgeConnectServer: environment is required");
|
|
154
278
|
}
|
|
155
279
|
this.config = config;
|
|
156
|
-
const envConfig = (0,
|
|
280
|
+
const envConfig = (0, import_connect2.getEnvironmentConfig)(config.environment);
|
|
157
281
|
this.apiBaseUrl = config.apiBaseUrl || envConfig.apiBaseUrl;
|
|
158
282
|
this.oauthBaseUrl = config.oauthBaseUrl || envConfig.oauthBaseUrl;
|
|
159
283
|
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
@@ -162,6 +286,18 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
162
286
|
...config.retry
|
|
163
287
|
};
|
|
164
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
291
|
+
*
|
|
292
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
293
|
+
* or per user session and discard it when the token changes.
|
|
294
|
+
*/
|
|
295
|
+
forUser(accessToken) {
|
|
296
|
+
if (!accessToken) {
|
|
297
|
+
throw new import_connect2.EdgeAuthenticationError("accessToken is required when calling forUser()");
|
|
298
|
+
}
|
|
299
|
+
return new EdgeUserClient(this, accessToken);
|
|
300
|
+
}
|
|
165
301
|
async exchangeCode(code, codeVerifier, redirectUri) {
|
|
166
302
|
const tokenUrl = `${this.oauthBaseUrl}/token`;
|
|
167
303
|
const body = {
|
|
@@ -188,8 +324,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
188
324
|
const data = await response.json();
|
|
189
325
|
return this.parseTokenResponse(data);
|
|
190
326
|
} catch (error) {
|
|
191
|
-
if (error instanceof
|
|
192
|
-
throw new
|
|
327
|
+
if (error instanceof import_connect2.EdgeError) throw error;
|
|
328
|
+
throw new import_connect2.EdgeNetworkError("Failed to exchange code", error);
|
|
193
329
|
}
|
|
194
330
|
}
|
|
195
331
|
async refreshTokens(refreshToken) {
|
|
@@ -211,7 +347,7 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
211
347
|
});
|
|
212
348
|
if (!response.ok) {
|
|
213
349
|
const error = await response.json().catch(() => ({}));
|
|
214
|
-
throw new
|
|
350
|
+
throw new import_connect2.EdgeAuthenticationError(
|
|
215
351
|
error.message || error.error_description || "Token refresh failed. User may need to reconnect.",
|
|
216
352
|
{ tokenError: error }
|
|
217
353
|
);
|
|
@@ -219,89 +355,12 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
219
355
|
const data = await response.json();
|
|
220
356
|
return this.parseTokenResponse(data, refreshToken);
|
|
221
357
|
} catch (error) {
|
|
222
|
-
if (error instanceof
|
|
223
|
-
throw new
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
async getUser(accessToken) {
|
|
227
|
-
return this.apiRequest("GET", "/user", accessToken);
|
|
228
|
-
}
|
|
229
|
-
async getBalance(accessToken) {
|
|
230
|
-
return this.apiRequest("GET", "/balance", accessToken);
|
|
231
|
-
}
|
|
232
|
-
async initiateTransfer(accessToken, options) {
|
|
233
|
-
this.validateTransferOptions(options);
|
|
234
|
-
const body = {
|
|
235
|
-
type: options.type,
|
|
236
|
-
amount: options.amount,
|
|
237
|
-
idempotencyKey: options.idempotencyKey
|
|
238
|
-
};
|
|
239
|
-
return this.apiRequest("POST", "/transfer", accessToken, body);
|
|
240
|
-
}
|
|
241
|
-
validateTransferOptions(options) {
|
|
242
|
-
const errors = {};
|
|
243
|
-
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
244
|
-
errors.type = ['Must be "debit" or "credit"'];
|
|
245
|
-
}
|
|
246
|
-
if (!options.amount) {
|
|
247
|
-
errors.amount = ["Amount is required"];
|
|
248
|
-
} else {
|
|
249
|
-
const amount = parseFloat(options.amount);
|
|
250
|
-
if (isNaN(amount)) {
|
|
251
|
-
errors.amount = ["Must be a valid number"];
|
|
252
|
-
} else if (amount <= 0) {
|
|
253
|
-
errors.amount = ["Must be greater than 0"];
|
|
254
|
-
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
255
|
-
errors.amount = ["Must have at most 2 decimal places"];
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
259
|
-
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
260
|
-
} else if (options.idempotencyKey.length > 255) {
|
|
261
|
-
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
262
|
-
}
|
|
263
|
-
if (Object.keys(errors).length > 0) {
|
|
264
|
-
throw new import_connect.EdgeValidationError("Invalid transfer options", errors);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
async verifyTransfer(accessToken, transferId, otp) {
|
|
268
|
-
return this.apiRequest(
|
|
269
|
-
"POST",
|
|
270
|
-
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
271
|
-
accessToken,
|
|
272
|
-
{ otp }
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
async getTransfer(accessToken, transferId) {
|
|
276
|
-
return this.apiRequest(
|
|
277
|
-
"GET",
|
|
278
|
-
`/transfer/${encodeURIComponent(transferId)}`,
|
|
279
|
-
accessToken
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
async listTransfers(accessToken, params) {
|
|
283
|
-
const queryParams = new URLSearchParams();
|
|
284
|
-
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
285
|
-
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
286
|
-
if (params?.status) queryParams.set("status", params.status);
|
|
287
|
-
const query = queryParams.toString();
|
|
288
|
-
const path = query ? `/transfers?${query}` : "/transfers";
|
|
289
|
-
return this.apiRequest("GET", path, accessToken);
|
|
290
|
-
}
|
|
291
|
-
async revokeConsent(accessToken) {
|
|
292
|
-
return this.apiRequest("DELETE", "/consent", accessToken);
|
|
293
|
-
}
|
|
294
|
-
getRetryDelay(attempt) {
|
|
295
|
-
const { backoff, baseDelayMs } = this.retryConfig;
|
|
296
|
-
if (backoff === "exponential") {
|
|
297
|
-
return baseDelayMs * Math.pow(2, attempt);
|
|
358
|
+
if (error instanceof import_connect2.EdgeError) throw error;
|
|
359
|
+
throw new import_connect2.EdgeNetworkError("Failed to refresh tokens", error);
|
|
298
360
|
}
|
|
299
|
-
return baseDelayMs * (attempt + 1);
|
|
300
361
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
async apiRequest(method, path, accessToken, body) {
|
|
362
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
363
|
+
async _apiRequest(method, path, accessToken, body) {
|
|
305
364
|
const url = `${this.apiBaseUrl}${path}`;
|
|
306
365
|
let lastError = null;
|
|
307
366
|
const mleConfig = this.config.mle;
|
|
@@ -335,14 +394,14 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
335
394
|
if (mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
336
395
|
responseBody = decryptMle(rawResponseBody.jwe, mleConfig);
|
|
337
396
|
} else if (!mleEnabled && typeof rawResponseBody?.jwe === "string") {
|
|
338
|
-
throw new
|
|
397
|
+
throw new import_connect2.EdgeApiError(
|
|
339
398
|
"mle_required",
|
|
340
399
|
"The API responded with message-level encryption. Enable MLE in SDK config.",
|
|
341
400
|
response.status,
|
|
342
401
|
rawResponseBody
|
|
343
402
|
);
|
|
344
403
|
} else if (mleEnabled && mleConfig?.strictResponseEncryption !== false && response.ok && typeof rawResponseBody?.jwe !== "string") {
|
|
345
|
-
throw new
|
|
404
|
+
throw new import_connect2.EdgeApiError(
|
|
346
405
|
"mle_response_missing",
|
|
347
406
|
"Expected encrypted response payload but received plaintext.",
|
|
348
407
|
response.status,
|
|
@@ -360,8 +419,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
360
419
|
}
|
|
361
420
|
return responseBody;
|
|
362
421
|
} catch (error) {
|
|
363
|
-
if (error instanceof
|
|
364
|
-
if (!(error instanceof
|
|
422
|
+
if (error instanceof import_connect2.EdgeError) {
|
|
423
|
+
if (!(error instanceof import_connect2.EdgeNetworkError) || attempt >= this.retryConfig.maxRetries) {
|
|
365
424
|
throw error;
|
|
366
425
|
}
|
|
367
426
|
lastError = error;
|
|
@@ -369,11 +428,24 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
369
428
|
}
|
|
370
429
|
lastError = error;
|
|
371
430
|
if (attempt >= this.retryConfig.maxRetries) {
|
|
372
|
-
throw new
|
|
431
|
+
throw new import_connect2.EdgeNetworkError(`API request failed: ${method} ${path}`, lastError);
|
|
373
432
|
}
|
|
374
433
|
}
|
|
375
434
|
}
|
|
376
|
-
throw new
|
|
435
|
+
throw new import_connect2.EdgeNetworkError(
|
|
436
|
+
`API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
|
|
437
|
+
lastError
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
getRetryDelay(attempt) {
|
|
441
|
+
const { backoff, baseDelayMs } = this.retryConfig;
|
|
442
|
+
if (backoff === "exponential") {
|
|
443
|
+
return baseDelayMs * Math.pow(2, attempt);
|
|
444
|
+
}
|
|
445
|
+
return baseDelayMs * (attempt + 1);
|
|
446
|
+
}
|
|
447
|
+
async sleep(ms) {
|
|
448
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
377
449
|
}
|
|
378
450
|
async fetchWithTimeout(url, options) {
|
|
379
451
|
const controller = new AbortController();
|
|
@@ -401,56 +473,67 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
401
473
|
const errorCode = error.error;
|
|
402
474
|
const errorMessage = error.message || error.error_description;
|
|
403
475
|
if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
|
|
404
|
-
return new
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
);
|
|
476
|
+
return new import_connect2.EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
|
|
477
|
+
edgeBoostError: error
|
|
478
|
+
});
|
|
408
479
|
}
|
|
409
480
|
if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
|
|
410
|
-
return new
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
);
|
|
481
|
+
return new import_connect2.EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
|
|
482
|
+
edgeBoostError: error
|
|
483
|
+
});
|
|
414
484
|
}
|
|
415
485
|
if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
|
|
416
|
-
return new
|
|
417
|
-
"PKCE verification failed. Please try again.",
|
|
418
|
-
{ edgeBoostError: error }
|
|
419
|
-
);
|
|
486
|
+
return new import_connect2.EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
|
|
420
487
|
}
|
|
421
|
-
return new
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
);
|
|
488
|
+
return new import_connect2.EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
|
|
489
|
+
edgeBoostError: error,
|
|
490
|
+
statusCode: status
|
|
491
|
+
});
|
|
425
492
|
}
|
|
426
493
|
async handleApiErrorFromBody(error, status, path) {
|
|
427
494
|
if (status === 401) {
|
|
428
|
-
return new
|
|
429
|
-
error.message || "Access token is invalid or expired",
|
|
430
|
-
error
|
|
431
|
-
);
|
|
495
|
+
return new import_connect2.EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
|
|
432
496
|
}
|
|
433
497
|
if (status === 403) {
|
|
434
498
|
if (error.error === "consent_required") {
|
|
435
|
-
return new
|
|
499
|
+
return new import_connect2.EdgeConsentRequiredError(
|
|
436
500
|
this.config.clientId,
|
|
437
501
|
error.consentUrl,
|
|
438
502
|
error.message
|
|
439
503
|
);
|
|
440
504
|
}
|
|
441
505
|
if (error.error === "insufficient_scope" || error.error === "insufficient_consent") {
|
|
442
|
-
return new
|
|
506
|
+
return new import_connect2.EdgeInsufficientScopeError(
|
|
443
507
|
error.missing_scopes || error.missingScopes || [],
|
|
444
508
|
error.message
|
|
445
509
|
);
|
|
446
510
|
}
|
|
447
511
|
}
|
|
448
512
|
if (status === 404) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
513
|
+
let resourceType = "Resource";
|
|
514
|
+
let resourceId = path.split("/").pop() || "unknown";
|
|
515
|
+
if (path.includes("/verification-session/")) {
|
|
516
|
+
resourceType = "VerificationSession";
|
|
517
|
+
const parts = path.split("/");
|
|
518
|
+
const vsIdx = parts.indexOf("verification-session");
|
|
519
|
+
resourceId = vsIdx >= 0 && parts[vsIdx + 1] ? parts[vsIdx + 1] : "unknown";
|
|
520
|
+
} else if (path.includes("/verification-session")) {
|
|
521
|
+
resourceType = "Transfer";
|
|
522
|
+
const parts = path.split("/");
|
|
523
|
+
const txIdx = parts.indexOf("transfer");
|
|
524
|
+
resourceId = txIdx >= 0 && parts[txIdx + 1] ? parts[txIdx + 1] : "unknown";
|
|
525
|
+
} else if (path.includes("/transfer")) {
|
|
526
|
+
resourceType = "Transfer";
|
|
527
|
+
}
|
|
528
|
+
return new import_connect2.EdgeNotFoundError(resourceType, resourceId);
|
|
452
529
|
}
|
|
453
|
-
|
|
530
|
+
if (status === 422 && error.error === "identity_verification_failed") {
|
|
531
|
+
return new import_connect2.EdgeIdentityVerificationError(
|
|
532
|
+
error.fieldErrors || {},
|
|
533
|
+
error.message
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
return new import_connect2.EdgeApiError(
|
|
454
537
|
error.error || "api_error",
|
|
455
538
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
456
539
|
status,
|
|
@@ -460,8 +543,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
460
543
|
};
|
|
461
544
|
|
|
462
545
|
// src/index.ts
|
|
463
|
-
var import_connect2 = require("@edge-markets/connect");
|
|
464
546
|
var import_connect3 = require("@edge-markets/connect");
|
|
547
|
+
var import_connect4 = require("@edge-markets/connect");
|
|
465
548
|
// Annotate the CommonJS export names for ESM import in node:
|
|
466
549
|
0 && (module.exports = {
|
|
467
550
|
EdgeApiError,
|
|
@@ -473,6 +556,7 @@ var import_connect3 = require("@edge-markets/connect");
|
|
|
473
556
|
EdgeNetworkError,
|
|
474
557
|
EdgeNotFoundError,
|
|
475
558
|
EdgeTokenExchangeError,
|
|
559
|
+
EdgeUserClient,
|
|
476
560
|
getEnvironmentConfig,
|
|
477
561
|
isApiError,
|
|
478
562
|
isAuthenticationError,
|
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,138 @@
|
|
|
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
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
|
+
* Creates an EDGE-hosted transfer verification session.
|
|
112
|
+
* Returns a `verificationUrl` to open in an iframe or popup.
|
|
113
|
+
* The handoff token is single-use and expires in 120 seconds.
|
|
114
|
+
*/
|
|
115
|
+
async createVerificationSession(transferId, options) {
|
|
116
|
+
return this.server._apiRequest(
|
|
117
|
+
"POST",
|
|
118
|
+
`/transfer/${encodeURIComponent(transferId)}/verification-session`,
|
|
119
|
+
this.accessToken,
|
|
120
|
+
{ origin: options.origin }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Polls the status of a verification session.
|
|
125
|
+
* Use as an alternative to postMessage events.
|
|
126
|
+
*/
|
|
127
|
+
async getVerificationSessionStatus(transferId, sessionId) {
|
|
128
|
+
return this.server._apiRequest(
|
|
129
|
+
"GET",
|
|
130
|
+
`/transfer/${encodeURIComponent(transferId)}/verification-session/${encodeURIComponent(sessionId)}`,
|
|
131
|
+
this.accessToken
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
15
136
|
// src/mle.ts
|
|
16
137
|
import { randomUUID, randomBytes, createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, constants } from "crypto";
|
|
17
138
|
function encryptMle(payload, clientId, config) {
|
|
@@ -87,7 +208,7 @@ function fromBase64Url(value) {
|
|
|
87
208
|
return Buffer.from(normalized, "base64");
|
|
88
209
|
}
|
|
89
210
|
|
|
90
|
-
// src/
|
|
211
|
+
// src/types.ts
|
|
91
212
|
var DEFAULT_TIMEOUT = 3e4;
|
|
92
213
|
var USER_AGENT = "@edge-markets/connect-node/1.0.0";
|
|
93
214
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -96,6 +217,8 @@ var DEFAULT_RETRY_CONFIG = {
|
|
|
96
217
|
backoff: "exponential",
|
|
97
218
|
baseDelayMs: 1e3
|
|
98
219
|
};
|
|
220
|
+
|
|
221
|
+
// src/edge-connect-server.ts
|
|
99
222
|
var instances = /* @__PURE__ */ new Map();
|
|
100
223
|
function getInstanceKey(config) {
|
|
101
224
|
return `${config.clientId}:${config.environment}`;
|
|
@@ -132,6 +255,18 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
132
255
|
...config.retry
|
|
133
256
|
};
|
|
134
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Create a user-scoped client for making authenticated API calls.
|
|
260
|
+
*
|
|
261
|
+
* The returned {@link EdgeUserClient} is lightweight — create one per request
|
|
262
|
+
* or per user session and discard it when the token changes.
|
|
263
|
+
*/
|
|
264
|
+
forUser(accessToken) {
|
|
265
|
+
if (!accessToken) {
|
|
266
|
+
throw new EdgeAuthenticationError("accessToken is required when calling forUser()");
|
|
267
|
+
}
|
|
268
|
+
return new EdgeUserClient(this, accessToken);
|
|
269
|
+
}
|
|
135
270
|
async exchangeCode(code, codeVerifier, redirectUri) {
|
|
136
271
|
const tokenUrl = `${this.oauthBaseUrl}/token`;
|
|
137
272
|
const body = {
|
|
@@ -193,85 +328,8 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
193
328
|
throw new EdgeNetworkError("Failed to refresh tokens", error);
|
|
194
329
|
}
|
|
195
330
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
async getBalance(accessToken) {
|
|
200
|
-
return this.apiRequest("GET", "/balance", accessToken);
|
|
201
|
-
}
|
|
202
|
-
async initiateTransfer(accessToken, options) {
|
|
203
|
-
this.validateTransferOptions(options);
|
|
204
|
-
const body = {
|
|
205
|
-
type: options.type,
|
|
206
|
-
amount: options.amount,
|
|
207
|
-
idempotencyKey: options.idempotencyKey
|
|
208
|
-
};
|
|
209
|
-
return this.apiRequest("POST", "/transfer", accessToken, body);
|
|
210
|
-
}
|
|
211
|
-
validateTransferOptions(options) {
|
|
212
|
-
const errors = {};
|
|
213
|
-
if (!options.type || !["debit", "credit"].includes(options.type)) {
|
|
214
|
-
errors.type = ['Must be "debit" or "credit"'];
|
|
215
|
-
}
|
|
216
|
-
if (!options.amount) {
|
|
217
|
-
errors.amount = ["Amount is required"];
|
|
218
|
-
} else {
|
|
219
|
-
const amount = parseFloat(options.amount);
|
|
220
|
-
if (isNaN(amount)) {
|
|
221
|
-
errors.amount = ["Must be a valid number"];
|
|
222
|
-
} else if (amount <= 0) {
|
|
223
|
-
errors.amount = ["Must be greater than 0"];
|
|
224
|
-
} else if (!/^\d+(\.\d{1,2})?$/.test(options.amount)) {
|
|
225
|
-
errors.amount = ["Must have at most 2 decimal places"];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (!options.idempotencyKey || options.idempotencyKey.trim() === "") {
|
|
229
|
-
errors.idempotencyKey = ["idempotencyKey is required"];
|
|
230
|
-
} else if (options.idempotencyKey.length > 255) {
|
|
231
|
-
errors.idempotencyKey = ["Must be 255 characters or less"];
|
|
232
|
-
}
|
|
233
|
-
if (Object.keys(errors).length > 0) {
|
|
234
|
-
throw new EdgeValidationError("Invalid transfer options", errors);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
async verifyTransfer(accessToken, transferId, otp) {
|
|
238
|
-
return this.apiRequest(
|
|
239
|
-
"POST",
|
|
240
|
-
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
241
|
-
accessToken,
|
|
242
|
-
{ otp }
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
async getTransfer(accessToken, transferId) {
|
|
246
|
-
return this.apiRequest(
|
|
247
|
-
"GET",
|
|
248
|
-
`/transfer/${encodeURIComponent(transferId)}`,
|
|
249
|
-
accessToken
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
async listTransfers(accessToken, params) {
|
|
253
|
-
const queryParams = new URLSearchParams();
|
|
254
|
-
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
255
|
-
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
256
|
-
if (params?.status) queryParams.set("status", params.status);
|
|
257
|
-
const query = queryParams.toString();
|
|
258
|
-
const path = query ? `/transfers?${query}` : "/transfers";
|
|
259
|
-
return this.apiRequest("GET", path, accessToken);
|
|
260
|
-
}
|
|
261
|
-
async revokeConsent(accessToken) {
|
|
262
|
-
return this.apiRequest("DELETE", "/consent", accessToken);
|
|
263
|
-
}
|
|
264
|
-
getRetryDelay(attempt) {
|
|
265
|
-
const { backoff, baseDelayMs } = this.retryConfig;
|
|
266
|
-
if (backoff === "exponential") {
|
|
267
|
-
return baseDelayMs * Math.pow(2, attempt);
|
|
268
|
-
}
|
|
269
|
-
return baseDelayMs * (attempt + 1);
|
|
270
|
-
}
|
|
271
|
-
async sleep(ms) {
|
|
272
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
273
|
-
}
|
|
274
|
-
async apiRequest(method, path, accessToken, body) {
|
|
331
|
+
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
332
|
+
async _apiRequest(method, path, accessToken, body) {
|
|
275
333
|
const url = `${this.apiBaseUrl}${path}`;
|
|
276
334
|
let lastError = null;
|
|
277
335
|
const mleConfig = this.config.mle;
|
|
@@ -343,7 +401,20 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
343
401
|
}
|
|
344
402
|
}
|
|
345
403
|
}
|
|
346
|
-
throw new EdgeNetworkError(
|
|
404
|
+
throw new EdgeNetworkError(
|
|
405
|
+
`API request failed after ${this.retryConfig.maxRetries} retries: ${method} ${path}`,
|
|
406
|
+
lastError
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
getRetryDelay(attempt) {
|
|
410
|
+
const { backoff, baseDelayMs } = this.retryConfig;
|
|
411
|
+
if (backoff === "exponential") {
|
|
412
|
+
return baseDelayMs * Math.pow(2, attempt);
|
|
413
|
+
}
|
|
414
|
+
return baseDelayMs * (attempt + 1);
|
|
415
|
+
}
|
|
416
|
+
async sleep(ms) {
|
|
417
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
347
418
|
}
|
|
348
419
|
async fetchWithTimeout(url, options) {
|
|
349
420
|
const controller = new AbortController();
|
|
@@ -371,34 +442,26 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
371
442
|
const errorCode = error.error;
|
|
372
443
|
const errorMessage = error.message || error.error_description;
|
|
373
444
|
if (errorCode === "invalid_grant" || errorMessage?.includes("Invalid or expired")) {
|
|
374
|
-
return new EdgeTokenExchangeError(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
);
|
|
445
|
+
return new EdgeTokenExchangeError("Authorization code is invalid, expired, or already used. Please try again.", {
|
|
446
|
+
edgeBoostError: error
|
|
447
|
+
});
|
|
378
448
|
}
|
|
379
449
|
if (errorCode === "invalid_client" || errorMessage?.includes("Invalid client")) {
|
|
380
|
-
return new EdgeAuthenticationError(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
);
|
|
450
|
+
return new EdgeAuthenticationError("Invalid client credentials. Check your client ID and secret.", {
|
|
451
|
+
edgeBoostError: error
|
|
452
|
+
});
|
|
384
453
|
}
|
|
385
454
|
if (errorMessage?.includes("code_verifier") || errorMessage?.includes("PKCE")) {
|
|
386
|
-
return new EdgeTokenExchangeError(
|
|
387
|
-
"PKCE verification failed. Please try again.",
|
|
388
|
-
{ edgeBoostError: error }
|
|
389
|
-
);
|
|
455
|
+
return new EdgeTokenExchangeError("PKCE verification failed. Please try again.", { edgeBoostError: error });
|
|
390
456
|
}
|
|
391
|
-
return new EdgeTokenExchangeError(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
);
|
|
457
|
+
return new EdgeTokenExchangeError(errorMessage || "Failed to exchange authorization code", {
|
|
458
|
+
edgeBoostError: error,
|
|
459
|
+
statusCode: status
|
|
460
|
+
});
|
|
395
461
|
}
|
|
396
462
|
async handleApiErrorFromBody(error, status, path) {
|
|
397
463
|
if (status === 401) {
|
|
398
|
-
return new EdgeAuthenticationError(
|
|
399
|
-
error.message || "Access token is invalid or expired",
|
|
400
|
-
error
|
|
401
|
-
);
|
|
464
|
+
return new EdgeAuthenticationError(error.message || "Access token is invalid or expired", error);
|
|
402
465
|
}
|
|
403
466
|
if (status === 403) {
|
|
404
467
|
if (error.error === "consent_required") {
|
|
@@ -416,10 +479,29 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
416
479
|
}
|
|
417
480
|
}
|
|
418
481
|
if (status === 404) {
|
|
419
|
-
|
|
420
|
-
|
|
482
|
+
let resourceType = "Resource";
|
|
483
|
+
let resourceId = path.split("/").pop() || "unknown";
|
|
484
|
+
if (path.includes("/verification-session/")) {
|
|
485
|
+
resourceType = "VerificationSession";
|
|
486
|
+
const parts = path.split("/");
|
|
487
|
+
const vsIdx = parts.indexOf("verification-session");
|
|
488
|
+
resourceId = vsIdx >= 0 && parts[vsIdx + 1] ? parts[vsIdx + 1] : "unknown";
|
|
489
|
+
} else if (path.includes("/verification-session")) {
|
|
490
|
+
resourceType = "Transfer";
|
|
491
|
+
const parts = path.split("/");
|
|
492
|
+
const txIdx = parts.indexOf("transfer");
|
|
493
|
+
resourceId = txIdx >= 0 && parts[txIdx + 1] ? parts[txIdx + 1] : "unknown";
|
|
494
|
+
} else if (path.includes("/transfer")) {
|
|
495
|
+
resourceType = "Transfer";
|
|
496
|
+
}
|
|
421
497
|
return new EdgeNotFoundError(resourceType, resourceId);
|
|
422
498
|
}
|
|
499
|
+
if (status === 422 && error.error === "identity_verification_failed") {
|
|
500
|
+
return new EdgeIdentityVerificationError(
|
|
501
|
+
error.fieldErrors || {},
|
|
502
|
+
error.message
|
|
503
|
+
);
|
|
504
|
+
}
|
|
423
505
|
return new EdgeApiError(
|
|
424
506
|
error.error || "api_error",
|
|
425
507
|
error.message || error.error_description || `Request failed with status ${status}`,
|
|
@@ -431,24 +513,21 @@ var EdgeConnectServer = class _EdgeConnectServer {
|
|
|
431
513
|
|
|
432
514
|
// src/index.ts
|
|
433
515
|
import {
|
|
434
|
-
|
|
516
|
+
EdgeApiError as EdgeApiError2,
|
|
435
517
|
EdgeAuthenticationError as EdgeAuthenticationError2,
|
|
436
|
-
EdgeTokenExchangeError as EdgeTokenExchangeError2,
|
|
437
518
|
EdgeConsentRequiredError as EdgeConsentRequiredError2,
|
|
519
|
+
EdgeError as EdgeError2,
|
|
438
520
|
EdgeInsufficientScopeError as EdgeInsufficientScopeError2,
|
|
439
|
-
EdgeApiError as EdgeApiError2,
|
|
440
|
-
EdgeNotFoundError as EdgeNotFoundError2,
|
|
441
521
|
EdgeNetworkError as EdgeNetworkError2,
|
|
442
|
-
|
|
522
|
+
EdgeNotFoundError as EdgeNotFoundError2,
|
|
523
|
+
EdgeTokenExchangeError as EdgeTokenExchangeError2,
|
|
524
|
+
isApiError,
|
|
443
525
|
isAuthenticationError,
|
|
444
526
|
isConsentRequiredError,
|
|
445
|
-
|
|
527
|
+
isEdgeError,
|
|
446
528
|
isNetworkError
|
|
447
529
|
} from "@edge-markets/connect";
|
|
448
|
-
import {
|
|
449
|
-
getEnvironmentConfig as getEnvironmentConfig2,
|
|
450
|
-
isProductionEnvironment
|
|
451
|
-
} from "@edge-markets/connect";
|
|
530
|
+
import { getEnvironmentConfig as getEnvironmentConfig2, isProductionEnvironment } from "@edge-markets/connect";
|
|
452
531
|
export {
|
|
453
532
|
EdgeApiError2 as EdgeApiError,
|
|
454
533
|
EdgeAuthenticationError2 as EdgeAuthenticationError,
|
|
@@ -459,6 +538,7 @@ export {
|
|
|
459
538
|
EdgeNetworkError2 as EdgeNetworkError,
|
|
460
539
|
EdgeNotFoundError2 as EdgeNotFoundError,
|
|
461
540
|
EdgeTokenExchangeError2 as EdgeTokenExchangeError,
|
|
541
|
+
EdgeUserClient,
|
|
462
542
|
getEnvironmentConfig2 as getEnvironmentConfig,
|
|
463
543
|
isApiError,
|
|
464
544
|
isAuthenticationError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-markets/connect-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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.4.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsup": "^8.0.0",
|