Skip to content

Commit ffd65fa

Browse files
committed
fix: gracefully handle packages without JSON API
Some packages like getmonero.org, openai.com/codex, and opencode.ai don't have JSON API endpoints. Instead of throwing errors, the scraper now returns null and falls back to other data sources (pantry YAML and versions.txt). This eliminates error logs in GitHub Actions while still successfully processing all packages.
1 parent c9faa02 commit ffd65fa

File tree

1 file changed

+35
-24
lines changed

1 file changed

+35
-24
lines changed

src/pkgx-scraper.ts

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,31 @@ export async function scrapePkgxPackage(
6060

6161
/**
6262
* Fetch basic package info from pkgx.dev JSON API
63+
* Returns null if not available (some packages don't have JSON API data)
6364
*/
64-
async function fetchPkgxJson(packageName: string, timeout: number): Promise<any> {
65+
async function fetchPkgxJson(packageName: string, timeout: number): Promise<any | null> {
6566
const url = `https://pkgx.dev/pkgs/${packageName}.json`
6667

67-
const response = await fetch(url, {
68-
headers: {
69-
'Accept': 'application/json',
70-
'User-Agent': 'ts-pkgx',
71-
},
72-
signal: AbortSignal.timeout(timeout),
73-
})
68+
try {
69+
const response = await fetch(url, {
70+
headers: {
71+
'Accept': 'application/json',
72+
'User-Agent': 'ts-pkgx',
73+
},
74+
signal: AbortSignal.timeout(timeout),
75+
})
7476

75-
if (!response.ok) {
76-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
77-
}
77+
if (!response.ok) {
78+
// JSON API not available for this package (404), fall back to other sources
79+
return null
80+
}
7881

79-
return response.json()
82+
return await response.json()
83+
}
84+
catch (error) {
85+
// Network error or timeout, return null to use other data sources
86+
return null
87+
}
8088
}
8189

8290
/**
@@ -279,7 +287,7 @@ function parseSimpleYaml(yaml: string): PantryData {
279287
* Normalize package data from various sources
280288
*/
281289
function normalizePackageData(
282-
pkgxData: any,
290+
pkgxData: any | null,
283291
pantryData: PantryData | null,
284292
versions: string[],
285293
packageName: string,
@@ -309,22 +317,25 @@ function normalizePackageData(
309317
}
310318
}
311319

320+
// Extract name from package path if not in pkgxData (e.g., "getmonero.org" -> "getmonero")
321+
const fallbackName = packageName.split('/').pop()?.replace(/\.(org|com|io|net|dev)$/, '') || packageName
322+
312323
return {
313-
name: pkgxData.displayName || pkgxData.name || packageName.split('/').pop() || packageName,
314-
domain: pkgxData.project || packageName,
315-
brief: pkgxData.brief || pkgxData.description,
316-
description: pkgxData.description || pkgxData.brief,
317-
displayName: pkgxData.displayName,
318-
homepage: pkgxData.homepage,
319-
github: pkgxData.github,
320-
brew_url: pkgxData.brew_url,
321-
license: pkgxData.license,
322-
provides: pkgxData.provides || [],
324+
name: pkgxData?.displayName || pkgxData?.name || fallbackName,
325+
domain: pkgxData?.project || packageName,
326+
brief: pkgxData?.brief || pkgxData?.description || '',
327+
description: pkgxData?.description || pkgxData?.brief || '',
328+
displayName: pkgxData?.displayName,
329+
homepage: pkgxData?.homepage,
330+
github: pkgxData?.github,
331+
brew_url: pkgxData?.brew_url,
332+
license: pkgxData?.license,
333+
provides: pkgxData?.provides || [],
323334
companions: pantryData?.companions || [],
324335
dependencies,
325336
versions,
326337
build: pantryData?.build,
327-
interprets: pkgxData.interprets,
338+
interprets: pkgxData?.interprets,
328339
}
329340
}
330341

0 commit comments

Comments
 (0)