Skip to content

Commit c7d51b9

Browse files
ckorhonendevin-ai-integration[bot]claude
authored
feat: add User-Agent header to all HTTP requests (DIS-41) (#34)
* feat: add User-Agent header to all HTTP requests (DIS-41) Add User-Agent: opensea-cli/<version> header to all requests made by OpenSeaClient. Version is dynamically read from package.json using createRequire. Updates both get() and post() methods and corresponding test assertions. Co-Authored-By: Chris K <ckorhonen@gmail.com> * fix: use stricter semver regex in User-Agent test assertions Co-Authored-By: Chris K <ckorhonen@gmail.com> * refactor: use build-time version injection instead of createRequire Replace the runtime createRequire/package.json pattern with tsup's define option for compile-time version injection. Also anchors the test regex with $ to prevent false-positive matches. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: address review feedback (DIS-41) - Replace createRequire with build-time __VERSION__ via tsup define - Add __VERSION__ define to vitest config for test compatibility - Anchor test regex with $ to prevent matching malformed versions - Extract private defaultHeaders getter to reduce header duplication Co-Authored-By: Chris K <ckorhonen@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7a06fc4 commit c7d51b9

4 files changed

Lines changed: 41 additions & 14 deletions

File tree

src/client.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { OpenSeaClientConfig } from "./types/index.js"
22

3+
declare const __VERSION__: string
4+
35
const DEFAULT_BASE_URL = "https://api.opensea.io"
46
const DEFAULT_TIMEOUT_MS = 30_000
7+
const USER_AGENT = `opensea-cli/${__VERSION__}`
58

69
export class OpenSeaClient {
710
private apiKey: string
@@ -18,6 +21,14 @@ export class OpenSeaClient {
1821
this.verbose = config.verbose ?? false
1922
}
2023

24+
private get defaultHeaders(): Record<string, string> {
25+
return {
26+
Accept: "application/json",
27+
"User-Agent": USER_AGENT,
28+
"x-api-key": this.apiKey,
29+
}
30+
}
31+
2132
async get<T>(path: string, params?: Record<string, unknown>): Promise<T> {
2233
const url = new URL(`${this.baseUrl}${path}`)
2334

@@ -35,10 +46,7 @@ export class OpenSeaClient {
3546

3647
const response = await fetch(url.toString(), {
3748
method: "GET",
38-
headers: {
39-
Accept: "application/json",
40-
"x-api-key": this.apiKey,
41-
},
49+
headers: this.defaultHeaders,
4250
signal: AbortSignal.timeout(this.timeoutMs),
4351
})
4452

@@ -69,10 +77,7 @@ export class OpenSeaClient {
6977
}
7078
}
7179

72-
const headers: Record<string, string> = {
73-
Accept: "application/json",
74-
"x-api-key": this.apiKey,
75-
}
80+
const headers: Record<string, string> = { ...this.defaultHeaders }
7681

7782
if (body) {
7883
headers["Content-Type"] = "application/json"

test/client.test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ describe("OpenSeaClient", () => {
3939
"https://api.opensea.io/api/v2/test",
4040
expect.objectContaining({
4141
method: "GET",
42-
headers: {
42+
headers: expect.objectContaining({
4343
Accept: "application/json",
44+
"User-Agent": expect.stringMatching(/^opensea-cli\/\d+\.\d+\.\d+$/),
4445
"x-api-key": "test-key",
45-
},
46+
}),
4647
}),
4748
)
4849
expect(result).toEqual(mockResponse)
@@ -93,10 +94,11 @@ describe("OpenSeaClient", () => {
9394
"https://api.opensea.io/api/v2/refresh",
9495
expect.objectContaining({
9596
method: "POST",
96-
headers: {
97+
headers: expect.objectContaining({
9798
Accept: "application/json",
99+
"User-Agent": expect.stringMatching(/^opensea-cli\/\d+\.\d+\.\d+$/),
98100
"x-api-key": "test-key",
99-
},
101+
}),
100102
}),
101103
)
102104
expect(result).toEqual(mockResponse)
@@ -111,11 +113,12 @@ describe("OpenSeaClient", () => {
111113
"https://api.opensea.io/api/v2/create",
112114
expect.objectContaining({
113115
method: "POST",
114-
headers: {
116+
headers: expect.objectContaining({
115117
Accept: "application/json",
116118
"Content-Type": "application/json",
119+
"User-Agent": expect.stringMatching(/^opensea-cli\/\d+\.\d+\.\d+$/),
117120
"x-api-key": "test-key",
118-
},
121+
}),
119122
body: JSON.stringify({ name: "test" }),
120123
}),
121124
)

tsup.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1+
import { readFileSync } from "node:fs"
12
import { defineConfig } from "tsup"
23

4+
const pkg = JSON.parse(readFileSync("./package.json", "utf-8")) as {
5+
version: string
6+
}
7+
38
export default defineConfig([
49
{
510
entry: { cli: "src/cli.ts" },
611
format: ["esm"],
712
clean: true,
813
sourcemap: true,
914
target: "node18",
15+
define: {
16+
__VERSION__: JSON.stringify(pkg.version),
17+
},
1018
banner: {
1119
js: "#!/usr/bin/env node",
1220
},
@@ -17,5 +25,8 @@ export default defineConfig([
1725
dts: true,
1826
sourcemap: true,
1927
target: "node18",
28+
define: {
29+
__VERSION__: JSON.stringify(pkg.version),
30+
},
2031
},
2132
])

vitest.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
import { readFileSync } from "node:fs"
12
import { defineConfig } from "vitest/config"
23

4+
const pkg = JSON.parse(readFileSync("./package.json", "utf-8")) as {
5+
version: string
6+
}
7+
38
export default defineConfig({
9+
define: {
10+
__VERSION__: JSON.stringify(pkg.version),
11+
},
412
test: {
513
coverage: {
614
provider: "v8",

0 commit comments

Comments
 (0)