Skip to content

Commit 04a2e64

Browse files
committed
Implement custom crates.io client
1 parent 254ca8e commit 04a2e64

8 files changed

Lines changed: 82 additions & 56 deletions

File tree

badgers-web/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
"@types/react-dom": "19.2.3",
2121
"@vercel/analytics": "^1.3.1",
2222
"autoprefixer": "10.4.19",
23-
"crates.io": "^2.4.5",
2423
"eslint": "^9",
2524
"eslint-config-next": "16.1.6",
2625
"next": "16.1.6",

badgers-web/src/app/crates/downloads/[crate]/latest/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ export async function GET(request: NextRequest, props: Params) {
1616
crate
1717
} = params;
1818

19-
const resp = await Crates.wrapRequest(crates =>
20-
crates.api.crates.getVersions(crate),
21-
)
19+
const resp = await Crates.versions(crate)
2220
if (resp === null) return await Badge.error(request, 'crates.io')
2321
const latestVersion = resp.versions
2422
.filter(v => !v.yanked)

badgers-web/src/app/crates/downloads/[crate]/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ export async function GET(request: NextRequest, props: Params) {
1616
crate
1717
} = params;
1818

19-
const resp = await Crates.wrapRequest(crates =>
20-
crates.api.crates.getCrate(crate),
21-
)
19+
const resp = await Crates.crate(crate)
2220
if (resp === null) return await Badge.error(request, 'crates.io')
2321
const downloadCount = Intl.NumberFormat('en-US', {
2422
notation: 'compact',

badgers-web/src/app/crates/info/[crate]/route.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,9 @@ export async function GET(request: NextRequest, props: Params) {
1616
crate
1717
} = params;
1818

19-
const crateResp = await Crates.wrapRequest(crates =>
20-
crates.api.crates.getCrate(crate),
21-
)
19+
const crateResp = await Crates.crate(crate)
2220
if (crateResp === null) return await Badge.error(request, 'crates.io')
23-
const versionsResp = await Crates.wrapRequest(crates =>
24-
crates.api.crates.getVersions(crate),
25-
)
26-
if (versionsResp === null) return await Badge.error(request, 'crates.io')
27-
const latestVersion = versionsResp.versions
21+
const latestVersion = crateResp.versions
2822
.filter(v => !v.yanked)
2923
.sort(
3024
(a, b) =>

badgers-web/src/app/crates/name/[crate]/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ export async function GET(request: NextRequest, props: Params) {
1616
crate
1717
} = params;
1818

19-
const crateResp = await Crates.wrapRequest(crates =>
20-
crates.api.crates.getCrate(crate),
21-
)
19+
const crateResp = await Crates.crate(crate)
2220
if (crateResp === null) return await Badge.error(request, 'crates.io')
2321
return await Badge.generate(request, 'crates.io', `${crateResp.crate.name}`)
2422
}

badgers-web/src/app/crates/version/[crate]/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ export async function GET(request: NextRequest, props: Params) {
1616
crate
1717
} = params;
1818

19-
const resp = await Crates.wrapRequest(crates =>
20-
crates.api.crates.getVersions(crate),
21-
)
19+
const resp = await Crates.versions(crate)
2220
if (resp === null) return await Badge.error(request, 'crates.io')
2321
const latestVersion = resp.versions
2422
.filter(v => !v.yanked)

badgers-web/src/utils/Crates.ts

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,82 @@
1-
import { CratesIO } from 'crates.io'
1+
export type CrateInfo = {
2+
badges: {
3+
[key: string]: {
4+
[key: string]: string
5+
}
6+
} | null
7+
categories: null | string
8+
created_at: string
9+
description: string
10+
documentation: null | string
11+
downloads: number
12+
exact_match: boolean
13+
homepage: null | string
14+
id: string
15+
keywords: null | string
16+
links: {
17+
owner_team: string
18+
owner_user: string
19+
owners: string
20+
reverse_dependencies: string
21+
version_downloads: string
22+
versions: string
23+
}
24+
max_version: string
25+
name: string
26+
recent_downloads: null | number
27+
repository: null | string
28+
updated_at: string
29+
versions: null | number[]
30+
}
231

3-
type WrappedCratesRequest<T> = (arg0: CratesIO) => Promise<T>
32+
export type CrateVersion = {
33+
crate: string
34+
crate_size: number
35+
created_at: string
36+
dl_path: string
37+
downloads: number
38+
features: {
39+
[key: string]: string[]
40+
}
41+
id: number
42+
license: string
43+
links: {
44+
authors: string
45+
dependencies: string
46+
version_downloads: string
47+
}
48+
num: string
49+
published_by: null | string
50+
readme_path: string
51+
updated_at: string
52+
yanked: boolean
53+
}
454

5-
const Crates = {
6-
getCratesClient(): CratesIO {
7-
return new CratesIO()
8-
},
55+
export type CrateInfoResponse = {
56+
crate: CrateInfo
57+
versions: CrateVersion[]
58+
}
959

10-
async wrapRequest<T>(request: WrappedCratesRequest<T>): Promise<T | null> {
11-
try {
12-
return await request(Crates.getCratesClient())
13-
} catch (error) {
14-
return null
15-
}
16-
},
60+
export type CrateVersionResponse = {
61+
versions: CrateVersion[]
1762
}
1863

19-
export default Crates
64+
export default class CratesClient {
65+
static BASE_URL = 'https://crates.io/api/v1'
2066

21-
// Disable Vercel data cache for all requests.
22-
// This is a temporary solution. Once we can serve all routes via fetch,
23-
// we can remove this and use the next revalidate feature.
24-
export const fetchCache = 'force-no-store'
67+
static async crate(crate: string): Promise<CrateInfoResponse | null> {
68+
const url = `${CratesClient.BASE_URL}/crates/${crate}`
69+
const resp = await fetch(url)
70+
71+
if (resp.status !== 200) return null
72+
return await resp.json<CrateInfoResponse>()
73+
}
74+
75+
static async versions(crate: string): Promise<CrateVersionResponse | null> {
76+
const url = `${CratesClient.BASE_URL}/crates/${crate}/versions`
77+
const resp = await fetch(url)
78+
79+
if (resp.status !== 200) return null
80+
return await resp.json<CrateVersionResponse>()
81+
}
82+
}

badgers-web/yarn.lock

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,13 +1738,6 @@ __metadata:
17381738
languageName: node
17391739
linkType: hard
17401740

1741-
"@ffflorian/api-client@npm:2.2.0":
1742-
version: 2.2.0
1743-
resolution: "@ffflorian/api-client@npm:2.2.0"
1744-
checksum: 10c0/70e868fb4ed211ce60bfc5eb2ab27a345cf9562b20d330d132732bfc16248d6040207aa3fa5b773ffe5d58d4a6d58abce0851b89b49c6d2b9ad2f27d99c8d9b1
1745-
languageName: node
1746-
linkType: hard
1747-
17481741
"@humanfs/core@npm:^0.19.1":
17491742
version: 0.19.1
17501743
resolution: "@humanfs/core@npm:0.19.1"
@@ -4057,7 +4050,6 @@ __metadata:
40574050
"@types/react-dom": "npm:19.2.3"
40584051
"@vercel/analytics": "npm:^1.3.1"
40594052
autoprefixer: "npm:10.4.19"
4060-
crates.io: "npm:^2.4.5"
40614053
encoding: "npm:^0.1.13"
40624054
eslint: "npm:^9"
40634055
eslint-config-next: "npm:16.1.6"
@@ -4557,15 +4549,6 @@ __metadata:
45574549
languageName: node
45584550
linkType: hard
45594551

4560-
"crates.io@npm:^2.4.5":
4561-
version: 2.4.5
4562-
resolution: "crates.io@npm:2.4.5"
4563-
dependencies:
4564-
"@ffflorian/api-client": "npm:2.2.0"
4565-
checksum: 10c0/eace8158c8c2422e2dd9d4eba042d576ca91143917708cdd487a32bcce51206a610d5a0a45f7a80383ad7eac051b429aec15e0e8e1952393c4c71ec2b1adf45e
4566-
languageName: node
4567-
linkType: hard
4568-
45694552
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3":
45704553
version: 7.0.3
45714554
resolution: "cross-spawn@npm:7.0.3"

0 commit comments

Comments
 (0)