mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
Fixes a bug where Route Handlers and Server Actions were prevented from
calling `headers()` (and other request APIs) if
- the call happened after the response is finished
- the call *did not happen* inside an after-callback
generally, this happens when you pass a promise to after.
```ts
const longRunning = () => {
// assume this resolves after the response
await new Promise((resolve) => setTimeout(resolve, 100))
// response is done
after(() => {
await headers() // ❌ should be allowed, but throws
})
}
after(longRunning()) // or waitUntil, doesn't matter
```
The logic we used in the check was incorrect and assumed that an
`afterTaskStore` was present, but we can only wrap callbacks in this ALS
- we cannot change the async context of already-running promises.
One noteworthy change is that i had to exit `actionAsyncStorage` when we
do a render after the server action. Otherwise the checks in
`isRequestApiAllowedInCurrentPhase` might believe that they're still in
a server action when they aren't.
51 lines
1.3 KiB
TypeScript
51 lines
1.3 KiB
TypeScript
import { cookies, headers } from 'next/headers'
|
|
import { after, connection } from 'next/server'
|
|
import { io } from 'next/cache'
|
|
|
|
const apis = {
|
|
headers,
|
|
cookies,
|
|
connection,
|
|
io,
|
|
}
|
|
export const REQUEST_API_NAMES = Object.keys(apis)
|
|
|
|
export function testApiInPromisePassedToAfter(
|
|
context: string,
|
|
apiName: keyof typeof apis | (string & {})
|
|
) {
|
|
if (!(apiName in apis)) {
|
|
throw new Error(`Invalid api: ${apiName}`)
|
|
}
|
|
const apiFn = apis[apiName as keyof typeof apis]
|
|
|
|
const longRunning = async () => {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
console.log(`${context} :: ${apiName} :: after delay`)
|
|
|
|
// Check calling the api directly
|
|
try {
|
|
await apiFn()
|
|
console.log(`${context} :: ${apiName} :: promise :: ok`)
|
|
} catch (err) {
|
|
console.log(`${context} :: ${apiName} :: promise :: error:`, err)
|
|
}
|
|
|
|
// Check calling the api from a nested after
|
|
after(async () => {
|
|
try {
|
|
await apiFn()
|
|
console.log(`${context} :: ${apiName} :: nested after :: ok`)
|
|
} catch (err) {
|
|
console.log(`${context} :: ${apiName} :: nested after :: error:`, err)
|
|
}
|
|
|
|
console.log(`${context} :: ${apiName} :: finished`)
|
|
})
|
|
}
|
|
|
|
console.log(`${context} :: ${apiName} :: starting`)
|
|
const promise = longRunning()
|
|
after(promise)
|
|
}
|