diff --git a/.changeset/proud-peaches-watch.md b/.changeset/proud-peaches-watch.md new file mode 100644 index 0000000..9d86c81 --- /dev/null +++ b/.changeset/proud-peaches-watch.md @@ -0,0 +1,5 @@ +--- +"@galaxiajs/cloudflare-kit": patch +--- + +Add method to execute code after request handling diff --git a/packages/cloudflare/src/exports.ts b/packages/cloudflare/src/exports.ts index 19bf817..0b5643e 100644 --- a/packages/cloudflare/src/exports.ts +++ b/packages/cloudflare/src/exports.ts @@ -27,7 +27,7 @@ export { type $Locals as Locals, type $Secrets as Secrets, } from "./modules/env"; -export { executionContext, waitUntil } from "./modules/execution-context"; +export { executionContext, waitUntil, after } from "./modules/execution-context"; export { headers, ip, diff --git a/packages/cloudflare/src/handler.ts b/packages/cloudflare/src/handler.ts index 4c964e6..6f2ad7c 100644 --- a/packages/cloudflare/src/handler.ts +++ b/packages/cloudflare/src/handler.ts @@ -8,6 +8,7 @@ import { } from "./modules/context/context"; import { createContext, createLocals } from "./modules/context/utils"; import type { Env } from "./modules/env"; +import { afters } from "./modules/execution-context"; import type { ExportedWorker, Handler, MakeAsync, Promisable } from "./types"; /** @@ -77,12 +78,14 @@ export function handleRequest(ctx: IHandlerContext, handle: () => Promisable { const result = await handle(); + let response: Response; + if (result instanceof Response) { for (const [key, value] of result.headers.entries()) { ctx.response.headers.append(key, value); } - return new Response(result.body, { + response = new Response(result.body, { statusText: result.statusText, status: result.status, headers: ctx.response.headers as unknown as Headers, @@ -93,12 +96,19 @@ export function handleRequest(ctx: IHandlerContext, handle: () => Promisable r())); +} diff --git a/packages/cloudflare/src/modules/execution-context.ts b/packages/cloudflare/src/modules/execution-context.ts index cfdcf3b..cdae1f8 100644 --- a/packages/cloudflare/src/modules/execution-context.ts +++ b/packages/cloudflare/src/modules/execution-context.ts @@ -22,3 +22,32 @@ export function waitUntil(promise: Promise): void { const ctx = executionContext(); ctx.waitUntil(promise); } + +export const afters: Array<() => any> = []; + +/** + * Allows you to schedule work to be executed after a response is finished. + * This is useful for tasks and other side effects that should not block + * the response, such as logging and analytics. + * + * Unlike {@linkcode waitUntil}, this will run _after_ the response has turned, + * rather than being kicked off concurrently during the request cycle. + * + * `after()` will be executed even if the response didn't complete successfully, + * including when an error is thrown or when `redirect()` is called. + * + * Note that you cannot: + * + * - Set cookies + * - Modify response headers + * - Modify response status + * + * Inside `after()` as the response will have already been sent. Doing any of these + * will throw a Cloudflare error. + * + * @param {() => any} callback A function that will be executed after the response is finished. + * @returns {void} + */ +export function after(callback: () => any): void { + afters.push(callback); +}