Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions src/adapter/cloudflare-worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,78 @@ export function isCloudflareWorker() {
export const CloudflareAdapter: ElysiaAdapter = {
...WebStandardAdapter,
name: 'cloudflare-worker',
async stop(app, closeActiveConnections) {
if (app.server) {
// Call onStop lifecycle hooks for Cloudflare Workers
if (app.event.stop)
for (let i = 0; i < app.event.stop.length; i++)
app.event.stop[i].fn(app)
} else {
console.log(
"Elysia isn't running. Call `app.listen` to start the server.",
new Error().stack
)
}
},
beforeCompile(app) {
// Polyfill setImmediate for Cloudflare Workers - set it globally
if (typeof globalThis.setImmediate === 'undefined') {
// @ts-ignore - Polyfill for Cloudflare Workers
globalThis.setImmediate = (callback: () => void) => {
// Check if we're in a Cloudflare Worker environment with ctx.waitUntil
if (typeof globalThis !== 'undefined' &&
// @ts-ignore - Check for Cloudflare Workers context
globalThis.ctx &&
// @ts-ignore
typeof globalThis.ctx.waitUntil === 'function') {

// Use ctx.waitUntil to ensure the callback completes
// @ts-ignore
globalThis.ctx.waitUntil(Promise.resolve().then(callback))
} else {
// Fallback to Promise.resolve for non-Cloudflare environments
Promise.resolve().then(callback)
}
}
}

// Also set it on the global object for compatibility
if (typeof global !== 'undefined' && typeof global.setImmediate === 'undefined') {
// @ts-ignore
global.setImmediate = globalThis.setImmediate
}

for (const route of app.routes) route.compile()

// Call onStart lifecycle hooks for Cloudflare Workers
// since they use the compile pattern instead of listen
if (app.event.start)
for (let i = 0; i < app.event.start.length; i++)
app.event.start[i].fn(app)
},
composeHandler: {
...WebStandardAdapter.composeHandler,
inject: {
...WebStandardAdapter.composeHandler.inject,
// Provide setImmediate for composed handlers in Workers
setImmediate: (callback: () => void) => {
// Same logic as above - use ctx.waitUntil if available
if (typeof globalThis !== 'undefined' &&
// @ts-ignore - Checking for Worker context
globalThis.ctx &&
// @ts-ignore
typeof globalThis.ctx.waitUntil === 'function') {

// Let the Worker runtime handle the async work
// @ts-ignore
globalThis.ctx.waitUntil(Promise.resolve().then(callback))
} else {
// Standard promise-based fallback
Promise.resolve().then(callback)
}
}
}
},
composeGeneralHandler: {
...WebStandardAdapter.composeGeneralHandler,
error404(hasEventHook, hasErrorHook, afterHandle) {
Expand All @@ -61,16 +133,19 @@ export const CloudflareAdapter: ElysiaAdapter = {
}
}
},
beforeCompile(app) {
for (const route of app.routes) route.compile()
},

listen(app) {
return (options, callback) => {
console.warn(
'Cloudflare Worker does not support listen method. Please export default Elysia instance instead.'
)

app.compile()

// Call onStart lifecycle hooks
if (app.event.start)
for (let i = 0; i < app.event.start.length; i++)
app.event.start[i].fn(app)
}
}
}
26 changes: 23 additions & 3 deletions src/dynamic-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,11 +663,31 @@ export const createDynamicHandler = (app: AnyElysia) => {
// @ts-expect-error private
return app.handleError(context, reportedError)
} finally {
if (app.event.afterResponse)
setImmediate(async () => {
if (app.event.afterResponse) {
// Use a polyfill that works in Cloudflare Workers
const runAfterResponse = async () => {
for (const afterResponse of app.event.afterResponse!)
await afterResponse.fn(context as any)
})
}

// Check if we're in a Cloudflare Worker environment with ctx.waitUntil
if (typeof globalThis !== 'undefined' &&
// @ts-ignore - Check for Cloudflare Workers context
globalThis.ctx &&
// @ts-ignore
typeof globalThis.ctx.waitUntil === 'function') {

// Use ctx.waitUntil to ensure the hooks complete in Cloudflare Workers
// @ts-ignore
globalThis.ctx.waitUntil(runAfterResponse())
} else if (typeof setImmediate !== 'undefined') {
// Use setImmediate for Node.js environments
setImmediate(runAfterResponse)
} else {
// Fallback to Promise.resolve for other environments
Promise.resolve().then(runAfterResponse)
}
}
}
}
}
Expand Down