@@ -72,51 +72,93 @@ export function getCloudflareContext<
7272> (
7373 options : GetCloudflareContextOptions = { async : false }
7474) : CloudflareContext < CfProperties , Context > | Promise < CloudflareContext < CfProperties , Context > > {
75+ return options . async ? getCloudflareContextAsync ( ) : getCloudflareContextSync ( ) ;
76+ }
77+
78+ /**
79+ * Get the cloudflare context from the current global scope
80+ */
81+ function getCloudflareContextFromGlobalScope <
82+ CfProperties extends Record < string , unknown > = IncomingRequestCfProperties ,
83+ Context = ExecutionContext ,
84+ > ( ) : CloudflareContext < CfProperties , Context > | undefined {
85+ const global = globalThis as InternalGlobalThis < CfProperties , Context > ;
86+ return global [ cloudflareContextSymbol ] ;
87+ }
88+
89+ /**
90+ * Detects whether the current code is being evaluated in a statically generated route
91+ */
92+ function inSSG <
93+ CfProperties extends Record < string , unknown > = IncomingRequestCfProperties ,
94+ Context = ExecutionContext ,
95+ > ( ) : boolean {
7596 const global = globalThis as InternalGlobalThis < CfProperties , Context > ;
97+ // Note: Next.js sets globalThis.__NEXT_DATA__.nextExport to true for SSG routes
98+ // source: https://github.com/vercel/next.js/blob/4e394608423/packages/next/src/export/worker.ts#L55-L57)
99+ return global . __NEXT_DATA__ ?. nextExport === true ;
100+ }
101+
102+ /**
103+ * Utility to get the current Cloudflare context in sync mode
104+ */
105+ function getCloudflareContextSync <
106+ CfProperties extends Record < string , unknown > = IncomingRequestCfProperties ,
107+ Context = ExecutionContext ,
108+ > ( ) : CloudflareContext < CfProperties , Context > {
109+ const cloudflareContext = getCloudflareContextFromGlobalScope < CfProperties , Context > ( ) ;
76110
77- const cloudflareContext = global [ cloudflareContextSymbol ] ;
111+ if ( cloudflareContext ) {
112+ return cloudflareContext ;
113+ }
114+
115+ // The sync mode of `getCloudflareContext`, relies on the context being set on the global state
116+ // by either the worker entrypoint (in prod) or by `initOpenNextCloudflareForDev` (in dev), neither
117+ // can work during SSG since for SSG Next.js creates (jest) workers that don't get access to the
118+ // normal global state so we throw with a helpful error message.
119+ if ( inSSG ( ) ) {
120+ throw new Error (
121+ `\n\nERROR: \`getCloudflareContext\` has been called in a static route,` +
122+ ` that is not allowed, this can be solved in different ways:\n\n` +
123+ ` - call \`getCloudflareContext({async: true})\` to use the \`async\` mode\n` +
124+ ` - avoid calling \`getCloudflareContext\` in the route\n` +
125+ ` - make the route non static\n`
126+ ) ;
127+ }
128+
129+ throwMissingInitOpenNextCloudflareForDevError ( ) ;
130+ }
78131
79- // whether `getCloudflareContext` is run in "async mode"
80- const asyncMode = options . async ;
132+ /**
133+ * Utility to get the current Cloudflare context in async mode
134+ */
135+ async function getCloudflareContextAsync <
136+ CfProperties extends Record < string , unknown > = IncomingRequestCfProperties ,
137+ Context = ExecutionContext ,
138+ > ( ) : Promise < CloudflareContext < CfProperties , Context > > {
139+ const cloudflareContext = getCloudflareContextFromGlobalScope < CfProperties , Context > ( ) ;
81140
82141 if ( cloudflareContext ) {
83- return asyncMode ? Promise . resolve ( cloudflareContext ) : cloudflareContext ;
142+ return cloudflareContext ;
84143 }
85144
86145 // Note: Next.js sets process.env.NEXT_RUNTIME to 'nodejs' when the runtime in use is the node.js one
87146 // We want to detect when the runtime is the node.js one so that during development (`next dev`) we know wether
88147 // we are or not in a node.js process and that access to wrangler's node.js apis
89148 const inNodejsRuntime = process . env . NEXT_RUNTIME === "nodejs" ;
90149
91- // Note: Next.js sets globalThis.__NEXT_DATA__.nextExport to true for SSG routes
92- // source: https://github.com/vercel/next.js/blob/4e394608423/packages/next/src/export/worker.ts#L55-L57)
93- const inSSG = global . __NEXT_DATA__ ?. nextExport === true ;
94-
95- if ( asyncMode ) {
96- if ( inNodejsRuntime || inSSG ) {
97- // we're in a node.js process and also in "async mode" so we can use wrangler to asynchronously get the context
98- return getCloudflareContextFromWrangler < CfProperties , Context > ( ) . then ( ( context ) => {
99- addCloudflareContextToNodejsGlobal ( context ) ;
100- return context ;
101- } ) ;
102- }
103- } else {
104- // The sync mode of `getCloudflareContext`, relies on the context being set on the global state
105- // by either the worker entrypoint (in prod) or by `initOpenNextCloudflareForDev` (in dev), neither
106- // can work during SSG since for SSG Next.js creates (jest) workers that don't get access to the
107- // normal global state so we throw with a helpful error message.
108- if ( inSSG ) {
109- throw new Error (
110- `\n\nERROR: \`getCloudflareContext\` has been called in a static route,` +
111- ` that is not allowed, this can be solved in different ways:\n\n` +
112- ` - call \`getCloudflareContext({async: true})\` to use the \`async\` mode\n` +
113- ` - avoid calling \`getCloudflareContext\` in the route\n` +
114- ` - make the route non static\n`
115- ) ;
116- }
150+ if ( inNodejsRuntime || inSSG ( ) ) {
151+ // we're in a node.js process and also in "async mode" so we can use wrangler to asynchronously get the context
152+ const cloudflareContext = await getCloudflareContextFromWrangler < CfProperties , Context > ( ) ;
153+ addCloudflareContextToNodejsGlobal ( cloudflareContext ) ;
154+ return cloudflareContext ;
117155 }
118156
119- // The cloudflare context is initialized by the worker so it is always available.
157+ throwMissingInitOpenNextCloudflareForDevError ( ) ;
158+ }
159+
160+ function throwMissingInitOpenNextCloudflareForDevError ( ) : never {
161+ // In production the cloudflare context is initialized by the worker so it is always available.
120162 // During local development (`next dev`) it might be missing only if the developers hasn't called
121163 // the `initOpenNextCloudflareForDev` function in their Next.js config file
122164 throw new Error (
0 commit comments