Skip to content

Commit 95749a4

Browse files
authored
perf: abort deduped async data handlers (#538)
1 parent 1975cb7 commit 95749a4

5 files changed

Lines changed: 42 additions & 49 deletions

File tree

app/composables/useCachedFetch.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,10 @@ export function useCachedFetch(): CachedFetchFunction {
3434
if (import.meta.client) {
3535
return async <T = unknown>(
3636
url: string,
37-
options: {
38-
method?: string
39-
body?: unknown
40-
headers?: Record<string, string>
41-
} = {},
37+
options: Parameters<typeof $fetch>[1] = {},
4238
_ttl?: number,
4339
): Promise<CachedFetchResult<T>> => {
44-
const data = (await $fetch(url, options as Parameters<typeof $fetch>[1])) as T
40+
const data = (await $fetch<T>(url, options)) as T
4541
return { data, isStale: false, cachedAt: null }
4642
}
4743
}
@@ -59,14 +55,10 @@ export function useCachedFetch(): CachedFetchFunction {
5955
// (shouldn't happen in normal operation)
6056
return async <T = unknown>(
6157
url: string,
62-
options: {
63-
method?: string
64-
body?: unknown
65-
headers?: Record<string, string>
66-
} = {},
58+
options: Parameters<typeof $fetch>[1] = {},
6759
_ttl?: number,
6860
): Promise<CachedFetchResult<T>> => {
69-
const data = (await $fetch(url, options as Parameters<typeof $fetch>[1])) as T
61+
const data = (await $fetch<T>(url, options)) as T
7062
return { data, isStale: false, cachedAt: null }
7163
}
7264
}

app/composables/useNpmRegistry.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,11 @@ export function usePackage(
185185

186186
const asyncData = useLazyAsyncData(
187187
() => `package:${toValue(name)}:${toValue(requestedVersion) ?? ''}`,
188-
async () => {
188+
async (_nuxtApp, { signal }) => {
189189
const encodedName = encodePackageName(toValue(name))
190-
const { data: r, isStale } = await cachedFetch<Packument>(`${NPM_REGISTRY}/${encodedName}`)
190+
const { data: r, isStale } = await cachedFetch<Packument>(`${NPM_REGISTRY}/${encodedName}`, {
191+
signal,
192+
})
191193
const reqVer = toValue(requestedVersion)
192194
const pkg = transformPackument(r, reqVer)
193195
const resolvedVersion = getResolvedVersion(pkg, reqVer)
@@ -233,10 +235,11 @@ export function usePackageDownloads(
233235

234236
const asyncData = useLazyAsyncData(
235237
() => `downloads:${toValue(name)}:${toValue(period)}`,
236-
async () => {
238+
async (_nuxtApp, { signal }) => {
237239
const encodedName = encodePackageName(toValue(name))
238240
const { data, isStale } = await cachedFetch<NpmDownloadCount>(
239241
`${NPM_API}/downloads/point/${toValue(period)}/${encodedName}`,
242+
{ signal },
240243
)
241244
return { ...data, isStale }
242245
},
@@ -306,7 +309,7 @@ export function useNpmSearch(
306309

307310
const asyncData = useLazyAsyncData(
308311
() => `search:incremental:${toValue(query)}`,
309-
async () => {
312+
async (_nuxtApp, { signal }) => {
310313
const q = toValue(query)
311314
if (!q.trim()) {
312315
return emptySearchResponse
@@ -325,7 +328,7 @@ export function useNpmSearch(
325328

326329
const { data: response, isStale } = await cachedFetch<NpmSearchResponse>(
327330
`${NPM_REGISTRY}/-/v1/search?${params.toString()}`,
328-
{},
331+
{ signal },
329332
60,
330333
)
331334

@@ -509,7 +512,7 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
509512

510513
const asyncData = useLazyAsyncData(
511514
() => `org-packages:${toValue(orgName)}`,
512-
async () => {
515+
async (_nuxtApp, { signal }) => {
513516
const org = toValue(orgName)
514517
if (!org) {
515518
return emptySearchResponse
@@ -520,6 +523,7 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
520523
try {
521524
const { data } = await cachedFetch<Record<string, string>>(
522525
`${NPM_REGISTRY}/-/org/${encodeURIComponent(org)}/package`,
526+
{ signal },
523527
)
524528
packageNames = Object.keys(data)
525529
} catch (err) {
@@ -553,6 +557,7 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
553557
const encoded = encodePackageName(name)
554558
const { data: pkg } = await cachedFetch<MinimalPackument>(
555559
`${NPM_REGISTRY}/${encoded}`,
560+
{ signal },
556561
)
557562
return pkg
558563
} catch {

app/composables/useRepoMeta.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ type ProviderAdapter = {
9393
cachedFetch: CachedFetchFunction,
9494
ref: RepoRef,
9595
links: RepoMetaLinks,
96+
options?: Parameters<typeof $fetch>[1],
9697
): Promise<RepoMeta | null>
9798
}
9899

@@ -126,13 +127,13 @@ const githubAdapter: ProviderAdapter = {
126127
}
127128
},
128129

129-
async fetchMeta(cachedFetch, ref, links) {
130+
async fetchMeta(cachedFetch, ref, links, options = {}) {
130131
// Using UNGH to avoid API limitations of the Github API
131132
let res: UnghRepoResponse | null = null
132133
try {
133134
const { data } = await cachedFetch<UnghRepoResponse>(
134135
`https://ungh.cc/repos/${ref.owner}/${ref.repo}`,
135-
{ headers: { 'User-Agent': 'npmx' } },
136+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
136137
REPO_META_TTL,
137138
)
138139
res = data
@@ -191,14 +192,14 @@ const gitlabAdapter: ProviderAdapter = {
191192
}
192193
},
193194

194-
async fetchMeta(cachedFetch, ref, links) {
195+
async fetchMeta(cachedFetch, ref, links, options = {}) {
195196
const baseHost = ref.host ?? 'gitlab.com'
196197
const projectPath = encodeURIComponent(`${ref.owner}/${ref.repo}`)
197198
let res: GitLabProjectResponse | null = null
198199
try {
199200
const { data } = await cachedFetch<GitLabProjectResponse>(
200201
`https://${baseHost}/api/v4/projects/${projectPath}`,
201-
{ headers: { 'User-Agent': 'npmx' } },
202+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
202203
REPO_META_TTL,
203204
)
204205
res = data
@@ -249,12 +250,12 @@ const bitbucketAdapter: ProviderAdapter = {
249250
}
250251
},
251252

252-
async fetchMeta(cachedFetch, ref, links) {
253+
async fetchMeta(cachedFetch, ref, links, options = {}) {
253254
let res: BitbucketRepoResponse | null = null
254255
try {
255256
const { data } = await cachedFetch<BitbucketRepoResponse>(
256257
`https://api.bitbucket.org/2.0/repositories/${ref.owner}/${ref.repo}`,
257-
{ headers: { 'User-Agent': 'npmx' } },
258+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
258259
REPO_META_TTL,
259260
)
260261
res = data
@@ -307,12 +308,12 @@ const codebergAdapter: ProviderAdapter = {
307308
}
308309
},
309310

310-
async fetchMeta(cachedFetch, ref, links) {
311+
async fetchMeta(cachedFetch, ref, links, options = {}) {
311312
let res: GiteaRepoResponse | null = null
312313
try {
313314
const { data } = await cachedFetch<GiteaRepoResponse>(
314315
`https://codeberg.org/api/v1/repos/${ref.owner}/${ref.repo}`,
315-
{ headers: { 'User-Agent': 'npmx' } },
316+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
316317
REPO_META_TTL,
317318
)
318319
res = data
@@ -365,12 +366,12 @@ const giteeAdapter: ProviderAdapter = {
365366
}
366367
},
367368

368-
async fetchMeta(cachedFetch, ref, links) {
369+
async fetchMeta(cachedFetch, ref, links, options = {}) {
369370
let res: GiteeRepoResponse | null = null
370371
try {
371372
const { data } = await cachedFetch<GiteeRepoResponse>(
372373
`https://gitee.com/api/v5/repos/${ref.owner}/${ref.repo}`,
373-
{ headers: { 'User-Agent': 'npmx' } },
374+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
374375
REPO_META_TTL,
375376
)
376377
res = data
@@ -452,7 +453,7 @@ const giteaAdapter: ProviderAdapter = {
452453
}
453454
},
454455

455-
async fetchMeta(cachedFetch, ref, links) {
456+
async fetchMeta(cachedFetch, ref, links, options = {}) {
456457
if (!ref.host) return null
457458

458459
// Note: Generic Gitea instances may not be in the allowlist,
@@ -461,7 +462,7 @@ const giteaAdapter: ProviderAdapter = {
461462
try {
462463
const { data } = await cachedFetch<GiteaRepoResponse>(
463464
`https://${ref.host}/api/v1/repos/${ref.owner}/${ref.repo}`,
464-
{ headers: { 'User-Agent': 'npmx' } },
465+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
465466
REPO_META_TTL,
466467
)
467468
res = data
@@ -564,13 +565,16 @@ const tangledAdapter: ProviderAdapter = {
564565
}
565566
},
566567

567-
async fetchMeta(cachedFetch, ref, links) {
568+
async fetchMeta(cachedFetch, ref, links, options = {}) {
568569
// Tangled doesn't have a public JSON API, but we can scrape the star count
569570
// from the HTML page (it's in the hx-post URL as countHint=N)
570571
try {
571572
const { data: html } = await cachedFetch<string>(
572573
`https://tangled.org/${ref.owner}/${ref.repo}`,
573-
{ headers: { 'User-Agent': 'npmx', 'Accept': 'text/html' } },
574+
{
575+
headers: { 'User-Agent': 'npmx', 'Accept': 'text/html', ...options.headers },
576+
...options,
577+
},
574578
REPO_META_TTL,
575579
)
576580
// Extracts the at-uri used in atproto
@@ -640,12 +644,12 @@ const radicleAdapter: ProviderAdapter = {
640644
}
641645
},
642646

643-
async fetchMeta(cachedFetch, ref, links) {
647+
async fetchMeta(cachedFetch, ref, links, options = {}) {
644648
let res: RadicleProjectResponse | null = null
645649
try {
646650
const { data } = await cachedFetch<RadicleProjectResponse>(
647651
`https://seed.radicle.at/api/v1/projects/${ref.repo}`,
648-
{ headers: { 'User-Agent': 'npmx' } },
652+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
649653
REPO_META_TTL,
650654
)
651655
res = data
@@ -704,14 +708,14 @@ const forgejoAdapter: ProviderAdapter = {
704708
}
705709
},
706710

707-
async fetchMeta(cachedFetch, ref, links) {
711+
async fetchMeta(cachedFetch, ref, links, options = {}) {
708712
if (!ref.host) return null
709713

710714
let res: GiteaRepoResponse | null = null
711715
try {
712716
const { data } = await cachedFetch<GiteaRepoResponse>(
713717
`https://${ref.host}/api/v1/repos/${ref.owner}/${ref.repo}`,
714-
{ headers: { 'User-Agent': 'npmx' } },
718+
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
715719
REPO_META_TTL,
716720
)
717721
res = data
@@ -766,15 +770,15 @@ export function useRepoMeta(repositoryUrl: MaybeRefOrGetter<string | null | unde
766770
repoRef.value
767771
? `repo-meta:${repoRef.value.provider}:${repoRef.value.owner}/${repoRef.value.repo}`
768772
: 'repo-meta:none',
769-
async () => {
773+
async (_nuxtApp, { signal }) => {
770774
const ref = repoRef.value
771775
if (!ref) return null
772776

773777
const adapter = providers.find(provider => provider.id === ref.provider)
774778
if (!adapter) return null
775779

776780
const links = adapter.links(ref)
777-
return await adapter.fetchMeta(cachedFetch, ref, links)
781+
return await adapter.fetchMeta(cachedFetch, ref, links, { signal })
778782
},
779783
)
780784

server/plugins/fetch-cache.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,12 @@ export default defineNitroPlugin(nitroApp => {
6060
function createCachedFetch(event: H3Event): CachedFetchFunction {
6161
return async <T = unknown>(
6262
url: string,
63-
options: {
64-
method?: string
65-
body?: unknown
66-
headers?: Record<string, string>
67-
} = {},
63+
options: Parameters<typeof $fetch>[1] = {},
6864
ttl: number = FETCH_CACHE_DEFAULT_TTL,
6965
): Promise<CachedFetchResult<T>> => {
7066
// Check if this URL should be cached
7167
if (!isAllowedDomain(url)) {
72-
const data = (await $fetch(url, options as Parameters<typeof $fetch>[1])) as T
68+
const data = (await $fetch(url, options)) as T
7369
return { data, isStale: false, cachedAt: null }
7470
}
7571

shared/utils/fetch-cache-config.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,6 @@ export interface CachedFetchResult<T> {
105105
*/
106106
export type CachedFetchFunction = <T = unknown>(
107107
url: string,
108-
options?: {
109-
method?: string
110-
body?: unknown
111-
headers?: Record<string, string>
112-
},
108+
options?: Parameters<typeof $fetch>[1],
113109
ttl?: number,
114110
) => Promise<CachedFetchResult<T>>

0 commit comments

Comments
 (0)