diff --git a/packages/next/src/client/components/draft-mode.ts b/packages/next/src/client/components/draft-mode.ts
new file mode 100644
index 000000000000..b1af9a4c2d7b
--- /dev/null
+++ b/packages/next/src/client/components/draft-mode.ts
@@ -0,0 +1,28 @@
+import { DraftModeProvider } from '../../server/async-storage/draft-mode-provider'
+import { staticGenerationBailout } from './static-generation-bailout'
+
+export class DraftMode {
+ /**
+ * @internal - this declaration is stripped via `tsc --stripInternal`
+ */
+ private readonly _provider: DraftModeProvider
+
+ constructor(provider: DraftModeProvider) {
+ this._provider = provider
+ }
+ get isEnabled() {
+ return this._provider.isEnabled
+ }
+ public enable() {
+ if (staticGenerationBailout('draftMode().enable()')) {
+ return
+ }
+ return this._provider.enable()
+ }
+ public disable() {
+ if (staticGenerationBailout('draftMode().disable()')) {
+ return
+ }
+ return this._provider.disable()
+ }
+}
diff --git a/packages/next/src/client/components/headers.ts b/packages/next/src/client/components/headers.ts
index 388d768d2067..2b239c9b8763 100644
--- a/packages/next/src/client/components/headers.ts
+++ b/packages/next/src/client/components/headers.ts
@@ -7,7 +7,7 @@ import { RequestCookies } from '../../server/web/spec-extension/cookies'
import { requestAsyncStorage } from './request-async-storage'
import { actionAsyncStorage } from './action-async-storage'
import { staticGenerationBailout } from './static-generation-bailout'
-import { DraftMode } from '../../../types'
+import { DraftMode } from './draft-mode'
export function headers() {
if (staticGenerationBailout('headers')) {
@@ -49,28 +49,12 @@ export function cookies() {
return requestStore.cookies
}
-export function draftMode(): DraftMode {
+export function draftMode() {
const requestStore = requestAsyncStorage.getStore()
if (!requestStore) {
throw new Error(
`Invariant: Method expects to have requestAsyncStorage, none available`
)
}
- return {
- get enabled() {
- return requestStore.draftMode.enabled
- },
- enable() {
- if (staticGenerationBailout('draftMode().enable()')) {
- return
- }
- return requestStore.draftMode.enable()
- },
- disable() {
- if (staticGenerationBailout('draftMode().disable()')) {
- return
- }
- return requestStore.draftMode.disable()
- },
- }
+ return new DraftMode(requestStore.draftMode)
}
diff --git a/packages/next/src/client/components/request-async-storage.ts b/packages/next/src/client/components/request-async-storage.ts
index d2879e12c6b0..a8a71c790662 100644
--- a/packages/next/src/client/components/request-async-storage.ts
+++ b/packages/next/src/client/components/request-async-storage.ts
@@ -1,6 +1,6 @@
import type { AsyncLocalStorage } from 'async_hooks'
+import type { DraftModeProvider } from '../../server/async-storage/draft-mode-provider'
import type { ResponseCookies } from '../../server/web/spec-extension/cookies'
-import type { DraftMode } from '../../../types'
import type { ReadonlyHeaders } from '../../server/web/spec-extension/adapters/headers'
import type { ReadonlyRequestCookies } from '../../server/web/spec-extension/adapters/request-cookies'
@@ -10,7 +10,7 @@ export interface RequestStore {
readonly headers: ReadonlyHeaders
readonly cookies: ReadonlyRequestCookies
readonly mutableCookies: ResponseCookies
- readonly draftMode: DraftMode
+ readonly draftMode: DraftModeProvider
}
export type RequestAsyncStorage = AsyncLocalStorage
diff --git a/packages/next/src/server/async-storage/draft-mode.ts b/packages/next/src/server/async-storage/draft-mode-provider.ts
similarity index 70%
rename from packages/next/src/server/async-storage/draft-mode.ts
rename to packages/next/src/server/async-storage/draft-mode-provider.ts
index e3f3f82b8fdc..06138db7dd16 100644
--- a/packages/next/src/server/async-storage/draft-mode.ts
+++ b/packages/next/src/server/async-storage/draft-mode-provider.ts
@@ -10,16 +10,24 @@ import {
__ApiPreviewProps,
} from '../api-utils'
-export class DraftMode {
- public readonly enabled: boolean
+export class DraftModeProvider {
+ public readonly isEnabled: boolean
- private readonly previewModeId: string | undefined
+ /**
+ * @internal - this declaration is stripped via `tsc --stripInternal`
+ */
+ private readonly _previewModeId: string | undefined
+
+ /**
+ * @internal - this declaration is stripped via `tsc --stripInternal`
+ */
+ private readonly _mutableCookies: ResponseCookies
constructor(
previewProps: __ApiPreviewProps | undefined,
req: IncomingMessage | BaseNextRequest | NextRequest,
- private readonly cookies: ReadonlyRequestCookies,
- private readonly mutableCookies: ResponseCookies
+ cookies: ReadonlyRequestCookies,
+ mutableCookies: ResponseCookies
) {
// The logic for draftMode() is very similar to tryGetPreviewData()
// but Draft Mode does not have any data associated with it.
@@ -27,28 +35,29 @@ export class DraftMode {
previewProps &&
checkIsOnDemandRevalidate(req, previewProps).isOnDemandRevalidate
- const cookieValue = this.cookies.get(COOKIE_NAME_PRERENDER_BYPASS)?.value
+ const cookieValue = cookies.get(COOKIE_NAME_PRERENDER_BYPASS)?.value
- this.enabled = Boolean(
+ this.isEnabled = Boolean(
!isOnDemandRevalidate &&
cookieValue &&
previewProps &&
cookieValue === previewProps.previewModeId
)
- this.previewModeId = previewProps?.previewModeId
+ this._previewModeId = previewProps?.previewModeId
+ this._mutableCookies = mutableCookies
}
enable() {
- if (!this.previewModeId) {
+ if (!this._previewModeId) {
throw new Error(
- 'Invariant: previewProps missing previewModeId this should not be hit'
+ 'Invariant: previewProps missing previewModeId this should never happen'
)
}
- this.mutableCookies.set({
+ this._mutableCookies.set({
name: COOKIE_NAME_PRERENDER_BYPASS,
- value: this.previewModeId,
+ value: this._previewModeId,
httpOnly: true,
sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax',
secure: process.env.NODE_ENV !== 'development',
@@ -60,7 +69,7 @@ export class DraftMode {
// To delete a cookie, set `expires` to a date in the past:
// https://tools.ietf.org/html/rfc6265#section-4.1.1
// `Max-Age: 0` is not valid, thus ignored, and the cookie is persisted.
- this.mutableCookies.set({
+ this._mutableCookies.set({
name: COOKIE_NAME_PRERENDER_BYPASS,
value: '',
httpOnly: true,
diff --git a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts
index 860de534258a..4d91f7f77806 100644
--- a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts
+++ b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts
@@ -18,7 +18,7 @@ import {
} from '../web/spec-extension/adapters/request-cookies'
import { RequestCookies, ResponseCookies } from '../web/spec-extension/cookies'
import { __ApiPreviewProps } from '../api-utils'
-import { DraftMode } from './draft-mode'
+import { DraftModeProvider } from './draft-mode-provider'
function getHeaders(headers: Headers | IncomingHttpHeaders): ReadonlyHeaders {
const cleaned = HeadersAdapter.from(headers)
@@ -79,7 +79,7 @@ export const RequestAsyncStorageWrapper: AsyncStorageWrapper<
headers?: ReadonlyHeaders
cookies?: ReadonlyRequestCookies
mutableCookies?: ResponseCookies
- draftMode?: DraftMode
+ draftMode?: DraftModeProvider
} = {}
const store: RequestStore = {
@@ -109,7 +109,7 @@ export const RequestAsyncStorageWrapper: AsyncStorageWrapper<
},
get draftMode() {
if (!cache.draftMode) {
- cache.draftMode = new DraftMode(
+ cache.draftMode = new DraftModeProvider(
previewProps,
req,
this.cookies,
diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts
index 9942b6c11842..297e169cd2b7 100644
--- a/packages/next/types/index.d.ts
+++ b/packages/next/types/index.d.ts
@@ -141,22 +141,6 @@ export {
export type PreviewData = string | false | object | undefined
-export type DraftMode = {
- /**
- * Get the current value of Draft Mode.
- * True when enabled, false when disabled.
- */
- enabled: boolean
- /**
- * Set the value of Draft Mode to true.
- */
- enable: () => void
- /**
- * Set the value of Draft Mode to false.
- */
- disable: () => void
-}
-
/**
* Context object passed into `getStaticProps`.
* @link https://nextjs.org/docs/api-reference/data-fetching/get-static-props#context-parameter
diff --git a/test/e2e/app-dir/app-static/app-static.test.ts b/test/e2e/app-dir/app-static/app-static.test.ts
index 58c6f510c91d..a0e1f8a6759c 100644
--- a/test/e2e/app-dir/app-static/app-static.test.ts
+++ b/test/e2e/app-dir/app-static/app-static.test.ts
@@ -1356,7 +1356,7 @@ createNextDescribe(
.elementByCss('#draft-mode')
.text()
- expect(content).toBe('{"result":{"enabled":false}}')
+ expect(content).toBe('{"isEnabled":false}')
})
it('should force SSR correctly for headers usage', async () => {
diff --git a/test/e2e/app-dir/app-static/app/ssg-draft-mode/[[...route]]/page.js b/test/e2e/app-dir/app-static/app/ssg-draft-mode/[[...route]]/page.js
index 13a7ce2660f6..ad91464d34d6 100644
--- a/test/e2e/app-dir/app-static/app/ssg-draft-mode/[[...route]]/page.js
+++ b/test/e2e/app-dir/app-static/app/ssg-draft-mode/[[...route]]/page.js
@@ -1,11 +1,11 @@
import { draftMode } from 'next/headers'
export default function Page() {
- const result = draftMode()
+ const { isEnabled } = draftMode()
return (
- {JSON.stringify({ result })}
+ {JSON.stringify({ isEnabled })}
)
}
diff --git a/test/e2e/app-dir/draft-mode/app/page.tsx b/test/e2e/app-dir/draft-mode/app/page.tsx
index aa408718d721..65b59b64e32a 100644
--- a/test/e2e/app-dir/draft-mode/app/page.tsx
+++ b/test/e2e/app-dir/draft-mode/app/page.tsx
@@ -2,7 +2,7 @@ import React from 'react'
import { draftMode } from 'next/headers'
export default function Page() {
- const { enabled } = draftMode()
+ const { isEnabled } = draftMode()
return (
<>
@@ -11,7 +11,7 @@ export default function Page() {
Random: {Math.random()}
- State: {enabled ? 'ENABLED' : 'DISABLED'}
+ State: {isEnabled ? 'ENABLED' : 'DISABLED'}
>
)
diff --git a/test/e2e/app-dir/draft-mode/app/state/route.ts b/test/e2e/app-dir/draft-mode/app/state/route.ts
index c4081345262d..c5568ed06b89 100644
--- a/test/e2e/app-dir/draft-mode/app/state/route.ts
+++ b/test/e2e/app-dir/draft-mode/app/state/route.ts
@@ -1,6 +1,6 @@
import { draftMode } from 'next/headers'
export function GET() {
- const { enabled } = draftMode()
- return new Response(enabled ? 'ENABLED' : 'DISABLED')
+ const { isEnabled } = draftMode()
+ return new Response(isEnabled ? 'ENABLED' : 'DISABLED')
}
diff --git a/test/e2e/app-dir/draft-mode/app/with-edge/page.tsx b/test/e2e/app-dir/draft-mode/app/with-edge/page.tsx
index 84b5fb6f099d..bdcd910fa793 100644
--- a/test/e2e/app-dir/draft-mode/app/with-edge/page.tsx
+++ b/test/e2e/app-dir/draft-mode/app/with-edge/page.tsx
@@ -4,7 +4,7 @@ import { draftMode } from 'next/headers'
export const runtime = 'experimental-edge'
export default function Page() {
- const { enabled } = draftMode()
+ const { isEnabled } = draftMode()
return (
<>
@@ -13,7 +13,7 @@ export default function Page() {
Random: {Math.random()}
- State: {enabled ? 'ENABLED' : 'DISABLED'}
+ State: {isEnabled ? 'ENABLED' : 'DISABLED'}
>
)
diff --git a/test/e2e/app-dir/draft-mode/app/with-edge/state/route.ts b/test/e2e/app-dir/draft-mode/app/with-edge/state/route.ts
index 50426a2ec42c..04083518e8b6 100644
--- a/test/e2e/app-dir/draft-mode/app/with-edge/state/route.ts
+++ b/test/e2e/app-dir/draft-mode/app/with-edge/state/route.ts
@@ -3,6 +3,6 @@ import { draftMode } from 'next/headers'
export const runtime = 'edge'
export function GET() {
- const { enabled } = draftMode()
- return new Response(enabled ? 'ENABLED' : 'DISABLED')
+ const { isEnabled } = draftMode()
+ return new Response(isEnabled ? 'ENABLED' : 'DISABLED')
}
diff --git a/test/e2e/app-dir/hooks/app/hooks/use-draft-mode/page.js b/test/e2e/app-dir/hooks/app/hooks/use-draft-mode/page.js
index 141d1afc7320..bf91e2ee399f 100644
--- a/test/e2e/app-dir/hooks/app/hooks/use-draft-mode/page.js
+++ b/test/e2e/app-dir/hooks/app/hooks/use-draft-mode/page.js
@@ -1,7 +1,7 @@
import { draftMode } from 'next/headers'
export default function Page() {
- const { enabled } = draftMode()
+ const { isEnabled } = draftMode()
return (
<>
@@ -9,7 +9,7 @@ export default function Page() {
Rand: {Math.random()}
- {enabled ? 'ENABLED' : 'DISABLED'}
+ {isEnabled ? 'ENABLED' : 'DISABLED'}
>
)
}