forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import type { CachedFetchResult } from '#shared/utils/fetch-cache-config'
2import { defu } from 'defu'
3
4/**
5 * Get the cachedFetch function from the current request context.
6 *
7 * IMPORTANT: This must be called in the composable setup context (outside of
8 * useAsyncData handlers). The returned function can then be used inside handlers.
9 *
10 * The returned function returns a wrapper object with staleness metadata:
11 * - `data`: The response data
12 * - `isStale`: Whether the data came from stale cache
13 * - `cachedAt`: Unix timestamp when cached, or null if fresh fetch
14 *
15 * @example
16 * ```ts
17 * export function usePackage(name: MaybeRefOrGetter<string>) {
18 * // Get cachedFetch in setup context
19 * const cachedFetch = useCachedFetch()
20 *
21 * return useLazyAsyncData(
22 * () => `package:${toValue(name)}`,
23 * // Use it inside the handler - destructure { data } or { data, isStale }
24 * async () => {
25 * const { data } = await cachedFetch<Packument>(`https://registry.npmjs.org/${toValue(name)}`)
26 * return data
27 * }
28 * )
29 * }
30 * ```
31 */
32export function useCachedFetch(): CachedFetchFunction {
33 // On client, return a function that just uses $fetch (no caching, not stale)
34 if (import.meta.client) {
35 return async <T = unknown>(
36 url: string,
37 options: Parameters<typeof $fetch>[1] = {},
38 _ttl: number = FETCH_CACHE_DEFAULT_TTL,
39 ): Promise<CachedFetchResult<T>> => {
40 const defaultFetchOptions: Parameters<typeof $fetch>[1] = {
41 cache: 'force-cache',
42 }
43 const data = (await $fetch<T>(url, defu(options, defaultFetchOptions))) as T
44 return { data, isStale: false, cachedAt: null }
45 }
46 }
47
48 // On server, get the cachedFetch from request context
49 const event = useRequestEvent()
50 const serverCachedFetch = event?.context?.cachedFetch
51
52 // If cachedFetch is available from middleware, return it
53 if (serverCachedFetch) {
54 return serverCachedFetch as CachedFetchFunction
55 }
56
57 // Fallback: return a function that uses regular $fetch
58 // (shouldn't happen in normal operation)
59 return async <T = unknown>(
60 url: string,
61 options: Parameters<typeof $fetch>[1] = {},
62 _ttl: number = FETCH_CACHE_DEFAULT_TTL,
63 ): Promise<CachedFetchResult<T>> => {
64 const defaultFetchOptions: Parameters<typeof $fetch>[1] = {
65 cache: 'force-cache',
66 }
67 const data = (await $fetch<T>(url, defu(options, defaultFetchOptions))) as T
68 return { data, isStale: false, cachedAt: null }
69 }
70}