@edge-markets/connect-node 1.0.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 +314 -0
- package/dist/index.d.mts +387 -0
- package/dist/index.d.ts +387 -0
- package/dist/index.js +543 -0
- package/dist/index.mjs +528 -0
- package/package.json +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
EdgeApiError: () => import_connect2.EdgeApiError,
|
|
24
|
+
EdgeAuthenticationError: () => import_connect2.EdgeAuthenticationError,
|
|
25
|
+
EdgeConnectServer: () => EdgeConnectServer,
|
|
26
|
+
EdgeConsentRequiredError: () => import_connect2.EdgeConsentRequiredError,
|
|
27
|
+
EdgeError: () => import_connect2.EdgeError,
|
|
28
|
+
EdgeInsufficientScopeError: () => import_connect2.EdgeInsufficientScopeError,
|
|
29
|
+
EdgeNetworkError: () => import_connect2.EdgeNetworkError,
|
|
30
|
+
EdgeNotFoundError: () => import_connect2.EdgeNotFoundError,
|
|
31
|
+
EdgeTokenExchangeError: () => import_connect2.EdgeTokenExchangeError,
|
|
32
|
+
getEnvironmentConfig: () => import_connect3.getEnvironmentConfig,
|
|
33
|
+
isApiError: () => import_connect2.isApiError,
|
|
34
|
+
isAuthenticationError: () => import_connect2.isAuthenticationError,
|
|
35
|
+
isConsentRequiredError: () => import_connect2.isConsentRequiredError,
|
|
36
|
+
isEdgeError: () => import_connect2.isEdgeError,
|
|
37
|
+
isNetworkError: () => import_connect2.isNetworkError,
|
|
38
|
+
isProductionEnvironment: () => import_connect3.isProductionEnvironment
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
|
|
42
|
+
// src/edge-connect-server.ts
|
|
43
|
+
var import_connect = require("@edge-markets/connect");
|
|
44
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
45
|
+
var USER_AGENT = "@edge-markets/connect-node/1.0.0";
|
|
46
|
+
var EdgeConnectServer = class {
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new EdgeConnectServer instance.
|
|
49
|
+
*
|
|
50
|
+
* @param config - Server configuration
|
|
51
|
+
* @throws Error if required config is missing
|
|
52
|
+
*/
|
|
53
|
+
constructor(config) {
|
|
54
|
+
if (!config.clientId) {
|
|
55
|
+
throw new Error("EdgeConnectServer: clientId is required");
|
|
56
|
+
}
|
|
57
|
+
if (!config.clientSecret) {
|
|
58
|
+
throw new Error("EdgeConnectServer: clientSecret is required");
|
|
59
|
+
}
|
|
60
|
+
if (!config.environment) {
|
|
61
|
+
throw new Error("EdgeConnectServer: environment is required");
|
|
62
|
+
}
|
|
63
|
+
this.config = config;
|
|
64
|
+
const envConfig = (0, import_connect.getEnvironmentConfig)(config.environment);
|
|
65
|
+
this.apiBaseUrl = config.apiBaseUrl || envConfig.apiBaseUrl;
|
|
66
|
+
this.oauthBaseUrl = envConfig.oauthBaseUrl;
|
|
67
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
68
|
+
}
|
|
69
|
+
// ===========================================================================
|
|
70
|
+
// TOKEN OPERATIONS
|
|
71
|
+
// ===========================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Exchanges an authorization code for tokens.
|
|
74
|
+
*
|
|
75
|
+
* Call this after receiving the code from EdgeLink's `onSuccess` callback.
|
|
76
|
+
* The code is single-use and expires in ~10 minutes.
|
|
77
|
+
*
|
|
78
|
+
* @param code - Authorization code from EdgeLink
|
|
79
|
+
* @param codeVerifier - PKCE code verifier from EdgeLink
|
|
80
|
+
* @returns Access token, refresh token, and metadata
|
|
81
|
+
* @throws EdgeTokenExchangeError if exchange fails
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // In your /api/edge/exchange endpoint
|
|
86
|
+
* const { code, codeVerifier } = req.body
|
|
87
|
+
*
|
|
88
|
+
* try {
|
|
89
|
+
* const tokens = await edge.exchangeCode(code, codeVerifier)
|
|
90
|
+
*
|
|
91
|
+
* // Store tokens securely
|
|
92
|
+
* await db.edgeConnections.upsert({
|
|
93
|
+
* userId: req.user.id,
|
|
94
|
+
* accessToken: encrypt(tokens.accessToken),
|
|
95
|
+
* refreshToken: encrypt(tokens.refreshToken),
|
|
96
|
+
* expiresAt: new Date(tokens.expiresAt),
|
|
97
|
+
* })
|
|
98
|
+
*
|
|
99
|
+
* return { success: true }
|
|
100
|
+
* } catch (error) {
|
|
101
|
+
* if (error instanceof EdgeTokenExchangeError) {
|
|
102
|
+
* // Code expired or already used
|
|
103
|
+
* return { error: 'Please try connecting again' }
|
|
104
|
+
* }
|
|
105
|
+
* throw error
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
async exchangeCode(code, codeVerifier) {
|
|
110
|
+
const tokenUrl = `${this.oauthBaseUrl}/oauth/token`;
|
|
111
|
+
const body = {
|
|
112
|
+
grant_type: "authorization_code",
|
|
113
|
+
code,
|
|
114
|
+
code_verifier: codeVerifier,
|
|
115
|
+
client_id: this.config.clientId,
|
|
116
|
+
client_secret: this.config.clientSecret
|
|
117
|
+
};
|
|
118
|
+
try {
|
|
119
|
+
const response = await this.fetchWithTimeout(tokenUrl, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
"User-Agent": USER_AGENT,
|
|
124
|
+
// Required for ngrok tunnels in local development
|
|
125
|
+
"ngrok-skip-browser-warning": "true"
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify(body)
|
|
128
|
+
});
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
const error = await response.json().catch(() => ({}));
|
|
131
|
+
throw this.handleTokenError(error, response.status);
|
|
132
|
+
}
|
|
133
|
+
const data = await response.json();
|
|
134
|
+
return this.parseTokenResponse(data);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error instanceof import_connect.EdgeError) throw error;
|
|
137
|
+
throw new import_connect.EdgeNetworkError("Failed to exchange code", error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Refreshes an access token using a refresh token.
|
|
142
|
+
*
|
|
143
|
+
* Call this when the access token is expired or about to expire.
|
|
144
|
+
* Check `tokens.expiresAt` to know when to refresh.
|
|
145
|
+
*
|
|
146
|
+
* @param refreshToken - Refresh token from previous exchange
|
|
147
|
+
* @returns New tokens (refresh token may or may not change)
|
|
148
|
+
* @throws EdgeAuthenticationError if refresh fails
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // Check if token needs refresh (with 5 minute buffer)
|
|
153
|
+
* const BUFFER_MS = 5 * 60 * 1000
|
|
154
|
+
*
|
|
155
|
+
* async function getValidAccessToken(userId: string): Promise<string> {
|
|
156
|
+
* const connection = await db.edgeConnections.get(userId)
|
|
157
|
+
*
|
|
158
|
+
* if (Date.now() > connection.expiresAt.getTime() - BUFFER_MS) {
|
|
159
|
+
* // Token expired or expiring soon - refresh it
|
|
160
|
+
* const newTokens = await edge.refreshTokens(decrypt(connection.refreshToken))
|
|
161
|
+
*
|
|
162
|
+
* // Update stored tokens
|
|
163
|
+
* await db.edgeConnections.update(userId, {
|
|
164
|
+
* accessToken: encrypt(newTokens.accessToken),
|
|
165
|
+
* refreshToken: encrypt(newTokens.refreshToken),
|
|
166
|
+
* expiresAt: new Date(newTokens.expiresAt),
|
|
167
|
+
* })
|
|
168
|
+
*
|
|
169
|
+
* return newTokens.accessToken
|
|
170
|
+
* }
|
|
171
|
+
*
|
|
172
|
+
* return decrypt(connection.accessToken)
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
async refreshTokens(refreshToken) {
|
|
177
|
+
const tokenUrl = `${this.oauthBaseUrl}/oauth/token`;
|
|
178
|
+
const body = {
|
|
179
|
+
grant_type: "refresh_token",
|
|
180
|
+
refresh_token: refreshToken,
|
|
181
|
+
client_id: this.config.clientId,
|
|
182
|
+
client_secret: this.config.clientSecret
|
|
183
|
+
};
|
|
184
|
+
try {
|
|
185
|
+
const response = await this.fetchWithTimeout(tokenUrl, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
"User-Agent": USER_AGENT,
|
|
190
|
+
"ngrok-skip-browser-warning": "true"
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify(body)
|
|
193
|
+
});
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
const error = await response.json().catch(() => ({}));
|
|
196
|
+
throw new import_connect.EdgeAuthenticationError(
|
|
197
|
+
error.error_description || "Token refresh failed. User may need to reconnect.",
|
|
198
|
+
{ tokenError: error }
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const data = await response.json();
|
|
202
|
+
return this.parseTokenResponse(data, refreshToken);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (error instanceof import_connect.EdgeError) throw error;
|
|
205
|
+
throw new import_connect.EdgeNetworkError("Failed to refresh tokens", error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ===========================================================================
|
|
209
|
+
// USER & BALANCE
|
|
210
|
+
// ===========================================================================
|
|
211
|
+
/**
|
|
212
|
+
* Gets the connected user's profile.
|
|
213
|
+
*
|
|
214
|
+
* Requires scope: `user.read`
|
|
215
|
+
*
|
|
216
|
+
* @param accessToken - Valid access token
|
|
217
|
+
* @returns User profile information
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const user = await edge.getUser(accessToken)
|
|
222
|
+
* console.log(`Connected: ${user.firstName} ${user.lastName}`)
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
async getUser(accessToken) {
|
|
226
|
+
return this.apiRequest("GET", "/user", accessToken);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Gets the connected user's EdgeBoost balance.
|
|
230
|
+
*
|
|
231
|
+
* Requires scope: `balance.read`
|
|
232
|
+
*
|
|
233
|
+
* @param accessToken - Valid access token
|
|
234
|
+
* @returns Balance information
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* const balance = await edge.getBalance(accessToken)
|
|
239
|
+
* console.log(`Balance: $${balance.availableBalance.toFixed(2)} ${balance.currency}`)
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
async getBalance(accessToken) {
|
|
243
|
+
return this.apiRequest("GET", "/balance", accessToken);
|
|
244
|
+
}
|
|
245
|
+
// ===========================================================================
|
|
246
|
+
// TRANSFERS
|
|
247
|
+
// ===========================================================================
|
|
248
|
+
/**
|
|
249
|
+
* Initiates a fund transfer.
|
|
250
|
+
*
|
|
251
|
+
* Requires scope: `transfer.write`
|
|
252
|
+
*
|
|
253
|
+
* **Transfer Types:**
|
|
254
|
+
* - `debit`: Pull funds FROM user's EdgeBoost TO your platform
|
|
255
|
+
* - `credit`: Push funds FROM your platform TO user's EdgeBoost
|
|
256
|
+
*
|
|
257
|
+
* **Idempotency:** Using the same `idempotencyKey` returns the existing
|
|
258
|
+
* transfer instead of creating a duplicate. Use a unique key per transaction.
|
|
259
|
+
*
|
|
260
|
+
* **OTP Verification:** Transfers require OTP verification before completion.
|
|
261
|
+
* The response includes `otpMethod` indicating how the user will receive the code.
|
|
262
|
+
*
|
|
263
|
+
* @param accessToken - Valid access token
|
|
264
|
+
* @param options - Transfer options
|
|
265
|
+
* @returns Transfer with status and OTP method
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const transfer = await edge.initiateTransfer(accessToken, {
|
|
270
|
+
* type: 'debit',
|
|
271
|
+
* amount: '100.00',
|
|
272
|
+
* idempotencyKey: `withdraw_${userId}_${Date.now()}`,
|
|
273
|
+
* })
|
|
274
|
+
*
|
|
275
|
+
* if (transfer.status === 'pending_verification') {
|
|
276
|
+
* // Show OTP input to user
|
|
277
|
+
* console.log(`Enter code sent via ${transfer.otpMethod}`)
|
|
278
|
+
* }
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
async initiateTransfer(accessToken, options) {
|
|
282
|
+
const body = {
|
|
283
|
+
type: options.type,
|
|
284
|
+
amount: options.amount,
|
|
285
|
+
idempotencyKey: options.idempotencyKey
|
|
286
|
+
};
|
|
287
|
+
return this.apiRequest("POST", "/transfer", accessToken, body);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Verifies a pending transfer with OTP.
|
|
291
|
+
*
|
|
292
|
+
* Call this after the user enters the OTP code they received.
|
|
293
|
+
* The OTP is valid for ~5 minutes.
|
|
294
|
+
*
|
|
295
|
+
* @param accessToken - Valid access token
|
|
296
|
+
* @param transferId - Transfer ID from initiateTransfer
|
|
297
|
+
* @param otp - 6-digit OTP code from user
|
|
298
|
+
* @returns Updated transfer (status will be 'completed' or 'failed')
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```typescript
|
|
302
|
+
* const result = await edge.verifyTransfer(accessToken, transferId, userOtp)
|
|
303
|
+
*
|
|
304
|
+
* if (result.status === 'completed') {
|
|
305
|
+
* console.log('Transfer successful!')
|
|
306
|
+
* } else if (result.status === 'failed') {
|
|
307
|
+
* console.log('Transfer failed - possibly wrong OTP')
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
async verifyTransfer(accessToken, transferId, otp) {
|
|
312
|
+
return this.apiRequest(
|
|
313
|
+
"POST",
|
|
314
|
+
`/transfer/${encodeURIComponent(transferId)}/verify`,
|
|
315
|
+
accessToken,
|
|
316
|
+
{ otp }
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Gets the status of a transfer.
|
|
321
|
+
*
|
|
322
|
+
* Use for polling after initiating a transfer.
|
|
323
|
+
*
|
|
324
|
+
* @param accessToken - Valid access token
|
|
325
|
+
* @param transferId - Transfer ID
|
|
326
|
+
* @returns Current transfer status
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```typescript
|
|
330
|
+
* const transfer = await edge.getTransfer(accessToken, transferId)
|
|
331
|
+
* console.log(`Status: ${transfer.status}`)
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
async getTransfer(accessToken, transferId) {
|
|
335
|
+
return this.apiRequest(
|
|
336
|
+
"GET",
|
|
337
|
+
`/transfer/${encodeURIComponent(transferId)}`,
|
|
338
|
+
accessToken
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Lists transfers for the connected user.
|
|
343
|
+
*
|
|
344
|
+
* Useful for reconciliation and showing transfer history.
|
|
345
|
+
*
|
|
346
|
+
* @param accessToken - Valid access token
|
|
347
|
+
* @param params - Pagination and filter options
|
|
348
|
+
* @returns Paginated list of transfers
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```typescript
|
|
352
|
+
* // Get first page of completed transfers
|
|
353
|
+
* const { transfers, total } = await edge.listTransfers(accessToken, {
|
|
354
|
+
* status: 'completed',
|
|
355
|
+
* limit: 10,
|
|
356
|
+
* offset: 0,
|
|
357
|
+
* })
|
|
358
|
+
*
|
|
359
|
+
* console.log(`Showing ${transfers.length} of ${total} transfers`)
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
async listTransfers(accessToken, params) {
|
|
363
|
+
const queryParams = new URLSearchParams();
|
|
364
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
365
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
366
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
367
|
+
const query = queryParams.toString();
|
|
368
|
+
const path = query ? `/transfers?${query}` : "/transfers";
|
|
369
|
+
return this.apiRequest("GET", path, accessToken);
|
|
370
|
+
}
|
|
371
|
+
// ===========================================================================
|
|
372
|
+
// CONSENT
|
|
373
|
+
// ===========================================================================
|
|
374
|
+
/**
|
|
375
|
+
* Revokes the user's consent (disconnects their account).
|
|
376
|
+
*
|
|
377
|
+
* After revocation:
|
|
378
|
+
* - All API calls will fail with `consent_required` error
|
|
379
|
+
* - User must go through EdgeLink again to reconnect
|
|
380
|
+
* - Stored tokens become invalid
|
|
381
|
+
*
|
|
382
|
+
* Use this for "Disconnect" or "Unlink" features in your app.
|
|
383
|
+
*
|
|
384
|
+
* @param accessToken - Valid access token
|
|
385
|
+
* @returns Confirmation of revocation
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* // Disconnect user's EdgeBoost account
|
|
390
|
+
* await edge.revokeConsent(accessToken)
|
|
391
|
+
*
|
|
392
|
+
* // Clean up stored tokens
|
|
393
|
+
* await db.edgeConnections.delete(userId)
|
|
394
|
+
*
|
|
395
|
+
* console.log('EdgeBoost disconnected')
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
async revokeConsent(accessToken) {
|
|
399
|
+
return this.apiRequest("DELETE", "/consent", accessToken);
|
|
400
|
+
}
|
|
401
|
+
// ===========================================================================
|
|
402
|
+
// PRIVATE HELPERS
|
|
403
|
+
// ===========================================================================
|
|
404
|
+
/**
|
|
405
|
+
* Makes an authenticated API request.
|
|
406
|
+
*/
|
|
407
|
+
async apiRequest(method, path, accessToken, body) {
|
|
408
|
+
const url = `${this.apiBaseUrl}${path}`;
|
|
409
|
+
try {
|
|
410
|
+
const response = await this.fetchWithTimeout(url, {
|
|
411
|
+
method,
|
|
412
|
+
headers: {
|
|
413
|
+
Authorization: `Bearer ${accessToken}`,
|
|
414
|
+
"Content-Type": "application/json",
|
|
415
|
+
"User-Agent": USER_AGENT,
|
|
416
|
+
// For ngrok during development
|
|
417
|
+
"ngrok-skip-browser-warning": "true"
|
|
418
|
+
},
|
|
419
|
+
body: body ? JSON.stringify(body) : void 0
|
|
420
|
+
});
|
|
421
|
+
if (!response.ok) {
|
|
422
|
+
throw await this.handleApiError(response, path);
|
|
423
|
+
}
|
|
424
|
+
return response.json();
|
|
425
|
+
} catch (error) {
|
|
426
|
+
if (error instanceof import_connect.EdgeError) throw error;
|
|
427
|
+
throw new import_connect.EdgeNetworkError(`API request failed: ${method} ${path}`, error);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Fetch with timeout support.
|
|
432
|
+
*/
|
|
433
|
+
async fetchWithTimeout(url, options) {
|
|
434
|
+
const controller = new AbortController();
|
|
435
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
436
|
+
try {
|
|
437
|
+
return await fetch(url, {
|
|
438
|
+
...options,
|
|
439
|
+
signal: controller.signal
|
|
440
|
+
});
|
|
441
|
+
} finally {
|
|
442
|
+
clearTimeout(timeoutId);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Parses token response from Cognito.
|
|
447
|
+
*/
|
|
448
|
+
parseTokenResponse(data, existingRefreshToken) {
|
|
449
|
+
return {
|
|
450
|
+
accessToken: data.access_token,
|
|
451
|
+
refreshToken: data.refresh_token || existingRefreshToken || "",
|
|
452
|
+
idToken: data.id_token,
|
|
453
|
+
expiresIn: data.expires_in,
|
|
454
|
+
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
455
|
+
scope: data.scope || ""
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Handles token exchange errors.
|
|
460
|
+
*/
|
|
461
|
+
handleTokenError(error, status) {
|
|
462
|
+
const errorCode = error.error;
|
|
463
|
+
const errorDescription = error.error_description;
|
|
464
|
+
if (errorCode === "invalid_grant") {
|
|
465
|
+
return new import_connect.EdgeTokenExchangeError(
|
|
466
|
+
"Authorization code is invalid, expired, or already used. Please try again.",
|
|
467
|
+
{ cognitoError: error }
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
if (errorCode === "invalid_client") {
|
|
471
|
+
return new import_connect.EdgeAuthenticationError(
|
|
472
|
+
"Invalid client credentials. Check your client ID and secret.",
|
|
473
|
+
{ cognitoError: error }
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
return new import_connect.EdgeTokenExchangeError(
|
|
477
|
+
errorDescription || "Failed to exchange authorization code",
|
|
478
|
+
{ cognitoError: error, statusCode: status }
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Handles API errors.
|
|
483
|
+
*/
|
|
484
|
+
async handleApiError(response, path) {
|
|
485
|
+
const error = await response.json().catch(() => ({}));
|
|
486
|
+
const status = response.status;
|
|
487
|
+
if (status === 401) {
|
|
488
|
+
return new import_connect.EdgeAuthenticationError(
|
|
489
|
+
error.message || "Access token is invalid or expired",
|
|
490
|
+
error
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
if (status === 403) {
|
|
494
|
+
if (error.error === "consent_required") {
|
|
495
|
+
return new import_connect.EdgeConsentRequiredError(
|
|
496
|
+
this.config.clientId,
|
|
497
|
+
error.consentUrl,
|
|
498
|
+
error.message
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
if (error.error === "insufficient_scope" || error.error === "insufficient_consent") {
|
|
502
|
+
return new import_connect.EdgeInsufficientScopeError(
|
|
503
|
+
error.missing_scopes || error.missingScopes || [],
|
|
504
|
+
error.message
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (status === 404) {
|
|
509
|
+
const resourceType = path.includes("/transfer") ? "Transfer" : "Resource";
|
|
510
|
+
const resourceId = path.split("/").pop() || "unknown";
|
|
511
|
+
return new import_connect.EdgeNotFoundError(resourceType, resourceId);
|
|
512
|
+
}
|
|
513
|
+
return new import_connect.EdgeApiError(
|
|
514
|
+
error.error || "api_error",
|
|
515
|
+
error.message || error.error_description || `Request failed with status ${status}`,
|
|
516
|
+
status,
|
|
517
|
+
error
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
// src/index.ts
|
|
523
|
+
var import_connect2 = require("@edge-markets/connect");
|
|
524
|
+
var import_connect3 = require("@edge-markets/connect");
|
|
525
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
526
|
+
0 && (module.exports = {
|
|
527
|
+
EdgeApiError,
|
|
528
|
+
EdgeAuthenticationError,
|
|
529
|
+
EdgeConnectServer,
|
|
530
|
+
EdgeConsentRequiredError,
|
|
531
|
+
EdgeError,
|
|
532
|
+
EdgeInsufficientScopeError,
|
|
533
|
+
EdgeNetworkError,
|
|
534
|
+
EdgeNotFoundError,
|
|
535
|
+
EdgeTokenExchangeError,
|
|
536
|
+
getEnvironmentConfig,
|
|
537
|
+
isApiError,
|
|
538
|
+
isAuthenticationError,
|
|
539
|
+
isConsentRequiredError,
|
|
540
|
+
isEdgeError,
|
|
541
|
+
isNetworkError,
|
|
542
|
+
isProductionEnvironment
|
|
543
|
+
});
|