-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathclient.ts
More file actions
123 lines (101 loc) · 3.07 KB
/
client.ts
File metadata and controls
123 lines (101 loc) · 3.07 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import type { OpenSeaClientConfig } from "./types/index.js"
const DEFAULT_BASE_URL = "https://api.opensea.io"
const DEFAULT_TIMEOUT_MS = 30_000
export class OpenSeaClient {
private apiKey: string
private baseUrl: string
private defaultChain: string
private timeoutMs: number
private verbose: boolean
constructor(config: OpenSeaClientConfig) {
this.apiKey = config.apiKey
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL
this.defaultChain = config.chain ?? "ethereum"
this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS
this.verbose = config.verbose ?? false
}
async get<T>(path: string, params?: Record<string, unknown>): Promise<T> {
const url = new URL(`${this.baseUrl}${path}`)
if (params) {
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value))
}
}
}
if (this.verbose) {
console.error(`[verbose] GET ${url.toString()}`)
}
const response = await fetch(url.toString(), {
method: "GET",
headers: {
Accept: "application/json",
"x-api-key": this.apiKey,
},
signal: AbortSignal.timeout(this.timeoutMs),
})
if (this.verbose) {
console.error(`[verbose] ${response.status} ${response.statusText}`)
}
if (!response.ok) {
const body = await response.text()
throw new OpenSeaAPIError(response.status, body, path)
}
return response.json() as Promise<T>
}
async post<T>(
path: string,
body?: Record<string, unknown>,
params?: Record<string, unknown>,
): Promise<T> {
const url = new URL(`${this.baseUrl}${path}`)
if (params) {
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value))
}
}
}
const headers: Record<string, string> = {
Accept: "application/json",
"x-api-key": this.apiKey,
}
if (body) {
headers["Content-Type"] = "application/json"
}
if (this.verbose) {
console.error(`[verbose] POST ${url.toString()}`)
}
const response = await fetch(url.toString(), {
method: "POST",
headers,
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(this.timeoutMs),
})
if (this.verbose) {
console.error(`[verbose] ${response.status} ${response.statusText}`)
}
if (!response.ok) {
const text = await response.text()
throw new OpenSeaAPIError(response.status, text, path)
}
return response.json() as Promise<T>
}
getDefaultChain(): string {
return this.defaultChain
}
getApiKeyPrefix(): string {
if (this.apiKey.length < 8) return "***"
return `${this.apiKey.slice(0, 4)}...`
}
}
export class OpenSeaAPIError extends Error {
constructor(
public statusCode: number,
public responseBody: string,
public path: string,
) {
super(`OpenSea API error ${statusCode} on ${path}: ${responseBody}`)
this.name = "OpenSeaAPIError"
}
}