-
Notifications
You must be signed in to change notification settings - Fork 966
Expand file tree
/
Copy pathindex.ts
More file actions
106 lines (93 loc) · 3.3 KB
/
Copy pathindex.ts
File metadata and controls
106 lines (93 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import createClient, { FetchResponse } from 'openapi-fetch'
import type { components, paths } from './schema.gen'
import { defaultHeaders } from './metadata'
import { ConnectionConfig } from '../connectionConfig'
import { AuthenticationError, RateLimitError, SandboxError } from '../errors'
import { createApiLogger } from '../logs'
import { parseRetryAfter } from '../utils'
export function handleApiError(
response: FetchResponse<any, any, any>,
errorClass: new (
message: string,
stackTrace?: string
) => Error = SandboxError,
stackTrace?: string
): Error | undefined {
// openapi-fetch returns empty string for error when response body is empty,
// so we check !== undefined instead of truthiness
if (response.error === undefined) {
return
}
if (response.response.status === 401) {
const message = 'Unauthorized, please check your credentials.'
const content = response.error?.message ?? response.error
if (content) {
return new AuthenticationError(`${message} - ${content}`)
}
return new AuthenticationError(message)
}
if (response.response.status === 429) {
const message = 'Rate limit exceeded, please try again later'
const content = response.error?.message ?? response.error
const retryAfter = parseRetryAfter(
response.response.headers?.get('Retry-After')
)
if (content) {
return new RateLimitError(`${message} - ${content}`, retryAfter)
}
return new RateLimitError(message, retryAfter)
}
const message = response.error?.message ?? response.error
return new errorClass(`${response.response.status}: ${message}`, stackTrace)
}
/**
* Client for interacting with the E2B API.
*/
class ApiClient {
readonly api: ReturnType<typeof createClient<paths>>
constructor(
config: ConnectionConfig,
opts: {
requireAccessToken?: boolean
requireApiKey?: boolean
} = { requireAccessToken: false, requireApiKey: false }
) {
if (opts?.requireApiKey && !config.apiKey) {
throw new AuthenticationError(
'API key is required, please visit the Team tab at https://e2b.dev/dashboard to get your API key. ' +
'You can either set the environment variable `E2B_API_KEY` ' +
"or you can pass it directly to the sandbox like Sandbox.create({ apiKey: 'e2b_...' })"
)
}
if (opts?.requireAccessToken && !config.accessToken) {
throw new AuthenticationError(
'Access token is required, please visit the Personal tab at https://e2b.dev/dashboard to get your access token. ' +
'You can set the environment variable `E2B_ACCESS_TOKEN` or pass the `accessToken` in options.'
)
}
this.api = createClient<paths>({
baseUrl: config.apiUrl,
// In HTTP 1.1, all connections are considered persistent unless declared otherwise
// keepalive: true,
headers: {
...defaultHeaders,
...(config.apiKey && { 'X-API-KEY': config.apiKey }),
...(config.accessToken && {
Authorization: `Bearer ${config.accessToken}`,
}),
...config.headers,
},
querySerializer: {
array: {
style: 'form',
explode: false,
},
},
})
if (config.logger) {
this.api.use(createApiLogger(config.logger))
}
}
}
export type { components, paths }
export { ApiClient }