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