fix: draftMode() must mark dynamic usage to prevent caching#369
Conversation
draftMode() reads the __prerender_bypass cookie, which is request-specific data. Without calling markDynamicUsage(), a Server Component branching on draftMode().isEnabled could have its output cached by ISR — serving draft content to regular users or stale content to draft users. headers() and cookies() already call markDynamicUsage(); draftMode() was the only request-reading API that didn't.
commit: |
|
/bigbonk review this pr |
There was a problem hiding this comment.
Clean, correct fix. draftMode() reads the __prerender_bypass cookie (request-specific data) and was the only dynamic request API in the headers shim that didn't call markDynamicUsage(). Without this, ISR could cache a response that branched on draftMode().isEnabled, serving draft content to regular users or vice versa.
Verified that the fix is consistent with the existing pattern:
headers()— callsmarkDynamicUsage()(line 276)cookies()— callsmarkDynamicUsage()(line 294)connection()inserver.ts— callsmarkDynamicUsage()(line 459)noStore()incache.ts— calls_markDynamic()(line 365)draftMode()— now callsmarkDynamicUsage()(line 362) ✓
Placement is correct — it's called before _getState() reads the cookie, matching the throwIfInsideCacheScope → markDynamicUsage → read-state ordering used by headers() and cookies().
Test is well-structured: sets up ALS context, resets prior dynamic usage, calls draftMode(), and asserts the flag is set. Good use of the existing runWithHeadersContext / consumeDynamicUsage test utilities.
|
Approved. This is a correct one-line fix that closes a real caching bug. |
Summary
draftMode()reads the__prerender_bypasscookie (request-specific data) but did not callmarkDynamicUsage(), unlikeheaders()andcookies()draftMode().isEnabledcould have its output cached by ISR, serving draft content to regular users or stale content to draft usersmarkDynamicUsage()call and a test verifying the behaviorTest plan
draftMode() marks dynamic usage so the render is uncacheable— callsdraftMode()within a headers context and assertsconsumeDynamicUsage()returnstrue