Skip to content

Commit f3b5648

Browse files
committed
fix: separate server handlers
The inner data was ending up with cached params somehow so this seems to solve that by caching the underlying full data set by itself.
1 parent de53377 commit f3b5648

File tree

1 file changed

+67
-69
lines changed

1 file changed

+67
-69
lines changed

server/api/registry/timeline/[...pkg].get.ts

Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,46 @@ export interface TimelineResponse {
1818
total: number
1919
}
2020

21+
const fetchTimeline = defineCachedFunction(
22+
async (packageName: string): Promise<TimelineVersion[]> => {
23+
const packument = await fetchNpmPackage(packageName)
24+
25+
const tagsByVersion = new Map<string, string[]>()
26+
for (const [tag, ver] of Object.entries(packument['dist-tags'] ?? {})) {
27+
const list = tagsByVersion.get(ver)
28+
if (list) list.push(tag)
29+
else tagsByVersion.set(ver, [tag])
30+
}
31+
32+
return Object.keys(packument.versions)
33+
.filter(v => packument.time[v])
34+
.map(v => {
35+
const version = packument.versions[v]!
36+
let license = version.license
37+
if (license && typeof license === 'object' && 'type' in license) {
38+
license = (license as { type: string }).type
39+
}
40+
41+
return {
42+
version: v,
43+
time: packument.time[v]!,
44+
license: typeof license === 'string' ? license : undefined,
45+
type: typeof version.type === 'string' ? version.type : undefined,
46+
hasTypes: hasBuiltInTypes(version) || undefined,
47+
hasTrustedPublisher: version._npmUser?.trustedPublisher ? true : undefined,
48+
hasProvenance: version.dist?.attestations ? true : undefined,
49+
tags: tagsByVersion.get(v) ?? [],
50+
}
51+
})
52+
.sort((a, b) => Date.parse(b.time) - Date.parse(a.time))
53+
},
54+
{
55+
maxAge: CACHE_MAX_AGE_FIVE_MINUTES,
56+
swr: true,
57+
getKey: (packageName: string) => `timeline:v1:${packageName}`,
58+
},
59+
)
60+
2161
/**
2262
* Returns paginated version timeline data for a package.
2363
*
@@ -28,76 +68,34 @@ export interface TimelineResponse {
2868
* - /api/registry/timeline/packageName?offset=0&limit=25
2969
* - /api/registry/timeline/@scope/packageName?offset=0&limit=25
3070
*/
31-
export default defineCachedEventHandler(
32-
async event => {
33-
const pkgParam = getRouterParam(event, 'pkg')
34-
if (!pkgParam) {
35-
throw createError({ statusCode: 404, message: 'Package name is required' })
36-
}
71+
export default defineEventHandler(async event => {
72+
const pkgParam = getRouterParam(event, 'pkg')
73+
if (!pkgParam) {
74+
throw createError({ statusCode: 404, message: 'Package name is required' })
75+
}
3776

38-
let packageName: string
39-
try {
40-
packageName = decodeURIComponent(pkgParam)
41-
} catch {
42-
throw createError({ statusCode: 400, message: 'Invalid package name encoding' })
43-
}
44-
45-
const query = getQuery(event)
46-
const offset = Math.max(0, Number(query.offset) || 0)
47-
const limit = Math.max(1, Math.min(100, Number(query.limit) || DEFAULT_LIMIT))
48-
49-
try {
50-
const packument = await fetchNpmPackage(packageName)
77+
let packageName: string
78+
try {
79+
packageName = decodeURIComponent(pkgParam)
80+
} catch {
81+
throw createError({ statusCode: 400, message: 'Invalid package name encoding' })
82+
}
5183

52-
const tagsByVersion = new Map<string, string[]>()
53-
for (const [tag, ver] of Object.entries(packument['dist-tags'] ?? {})) {
54-
const list = tagsByVersion.get(ver)
55-
if (list) list.push(tag)
56-
else tagsByVersion.set(ver, [tag])
57-
}
84+
const query = getQuery(event)
85+
const offset = Math.max(0, Number(query.offset) || 0)
86+
const limit = Math.max(1, Math.min(100, Number(query.limit) || DEFAULT_LIMIT))
5887

59-
// Build full sorted list
60-
const allVersions = Object.keys(packument.versions)
61-
.filter(v => packument.time[v])
62-
.map(v => {
63-
const version = packument.versions[v]!
64-
let license = version.license
65-
if (license && typeof license === 'object' && 'type' in license) {
66-
license = (license as { type: string }).type
67-
}
88+
try {
89+
const allVersions = await fetchTimeline(packageName)
6890

69-
return {
70-
version: v,
71-
time: packument.time[v]!,
72-
license: typeof license === 'string' ? license : undefined,
73-
type: typeof version.type === 'string' ? version.type : undefined,
74-
hasTypes: hasBuiltInTypes(version) || undefined,
75-
hasTrustedPublisher: version._npmUser?.trustedPublisher ? true : undefined,
76-
hasProvenance: version.dist?.attestations ? true : undefined,
77-
tags: tagsByVersion.get(v) ?? [],
78-
}
79-
})
80-
.sort((a, b) => Date.parse(b.time) - Date.parse(a.time))
81-
82-
return {
83-
versions: allVersions.slice(offset, offset + limit),
84-
total: allVersions.length,
85-
} satisfies TimelineResponse
86-
} catch (error: unknown) {
87-
handleApiError(error, {
88-
statusCode: 502,
89-
message: `Failed to fetch timeline for ${packageName}`,
90-
})
91-
}
92-
},
93-
{
94-
maxAge: CACHE_MAX_AGE_FIVE_MINUTES,
95-
swr: true,
96-
getKey: event => {
97-
const query = getQuery(event)
98-
const offset = Math.max(0, Number(query.offset) || 0)
99-
const limit = Math.max(1, Math.min(100, Number(query.limit) || DEFAULT_LIMIT))
100-
return `timeline:v1:${getRouterParam(event, 'pkg')}:${offset}:${limit}`
101-
},
102-
},
103-
)
91+
return {
92+
versions: allVersions.slice(offset, offset + limit),
93+
total: allVersions.length,
94+
} satisfies TimelineResponse
95+
} catch (error: unknown) {
96+
handleApiError(error, {
97+
statusCode: 502,
98+
message: `Failed to fetch timeline for ${packageName}`,
99+
})
100+
}
101+
})

0 commit comments

Comments
 (0)