fix(nuxt): deeply unwrap headers/query for useFetch key (#24307)

This commit is contained in:
Daniel Roe 2023-11-15 20:40:55 +01:00 committed by GitHub
parent 6ec267be87
commit 562532778b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 4 deletions

View File

@ -1,7 +1,7 @@
import type { FetchError, FetchOptions } from 'ofetch'
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod as _AvailableRouterMethod } from 'nitropack'
import type { MaybeRef, Ref } from 'vue'
import { computed, reactive, unref } from 'vue'
import { computed, reactive, toValue } from 'vue'
import { hash } from 'ohash'
import { useRequestFetch } from './ssr'
@ -86,10 +86,10 @@ export function useFetch<
if (typeof r === 'function') {
r = r()
}
return unref(r)
return toValue(r)
})
const _key = opts.key || hash([autoKey, unref(opts.method as MaybeRef<string | undefined> | undefined)?.toUpperCase() || 'GET', unref(opts.baseURL), typeof _request.value === 'string' ? _request.value : '', unref(opts.params || opts.query), unref(opts.headers)])
const _key = opts.key || hash([autoKey, typeof _request.value === 'string' ? _request.value : '', ...generateOptionSegments(opts)])
if (!_key || typeof _key !== 'string') {
throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key)
}
@ -144,7 +144,7 @@ export function useFetch<
// Use fetch with request context and headers for server direct API calls
if (import.meta.server && !opts.$fetch) {
const isLocalFetch = typeof _request.value === 'string' && _request.value.startsWith('/') && (!unref(opts.baseURL) || unref(opts.baseURL)!.startsWith('/'))
const isLocalFetch = typeof _request.value === 'string' && _request.value.startsWith('/') && (!toValue(opts.baseURL) || toValue(opts.baseURL)!.startsWith('/'))
if (isLocalFetch) {
_$fetch = useRequestFetch()
}
@ -205,3 +205,22 @@ export function useLazyFetch<
// @ts-expect-error we pass an extra argument with the resolved auto-key to prevent another from being injected
autoKey)
}
function generateOptionSegments <_ResT, DataT, DefaultT>(opts: UseFetchOptions<_ResT, DataT, any, DefaultT, any, any>) {
const segments: Array<string | undefined | Record<string, string>> = [
toValue(opts.method as MaybeRef<string | undefined> | undefined)?.toUpperCase() || 'GET',
toValue(opts.baseURL),
]
for (const _obj of [opts.params || opts.query, opts.headers]) {
const obj = toValue(_obj)
if (!obj) { continue }
const unwrapped: Record<string, string> = {}
const iterator = Array.isArray(obj) ? obj : obj instanceof Headers ? obj.entries() : Object.entries(obj)
for (const [key, value] of iterator) {
unwrapped[toValue(key)] = toValue(value)
}
segments.push(unwrapped)
}
return segments
}

View File

@ -38,6 +38,10 @@ registerEndpoint('/_nuxt/builds/meta/override.json', defineEventHandler(() => ({
},
prerendered: ['/specific-prerendered']
})))
registerEndpoint('/api/test', defineEventHandler((event) => ({
method: event.method,
headers: Object.fromEntries(event.headers.entries())
})))
describe('app config', () => {
it('can be updated', () => {
@ -237,6 +241,39 @@ describe('useAsyncData', () => {
})
})
describe('useFetch', () => {
it('should match with/without computed values', async () => {
const nuxtApp = useNuxtApp()
const getPayloadEntries = () => Object.keys(nuxtApp.payload.data).length
const baseCount = getPayloadEntries()
await useFetch('/api/test')
expect(getPayloadEntries()).toBe(baseCount + 1)
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { method: 'POST' }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { method: ref('POST') }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 2)
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: { id: '3' } }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: { id: ref('3') } }, '')
const headers = new Headers()
headers.append('id', '3')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [['id', '3']] }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [['id', ref('3')]] }, '')
/* @ts-expect-error Overriding auto-key */
await useFetch('/api/test', { headers: [[computed(() => 'id'), '3']] }, '')
expect.soft(getPayloadEntries()).toBe(baseCount + 3)
})
})
describe('errors', () => {
it('createError', () => {
expect(createError({ statusCode: 404 }).toJSON()).toMatchInlineSnapshot(`