From 4613118b607d50f5b8300c69803937090f479035 Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 18:04:59 +0700 Subject: [PATCH 1/8] feat(cloudflare): add queue and scheduled support --- src/runtime/entries/cloudflare-module.ts | 22 ++++++++++++++- src/runtime/entries/cloudflare.ts | 9 ++++++ src/runtime/types.ts | 35 ++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/runtime/entries/cloudflare-module.ts b/src/runtime/entries/cloudflare-module.ts index 53db2c6010..88820aee61 100644 --- a/src/runtime/entries/cloudflare-module.ts +++ b/src/runtime/entries/cloudflare-module.ts @@ -1,6 +1,10 @@ import "#internal/nitro/virtual/polyfill"; import { withoutBase } from "ufo"; -import type { ExecutionContext } from "@cloudflare/workers-types"; +import type { + ExecutionContext, + MessageBatch, + ScheduledEvent, +} from "@cloudflare/workers-types"; import { getAssetFromKV, mapRequestToAsset, @@ -69,6 +73,22 @@ export default { body, }); }, + // eslint-disable-next-line require-await + async queue( + batch: MessageBatch, + env: CFModuleEnv, + context: ExecutionContext + ) { + return nitroApp.hooks.callHook("cloudflare:queue", batch, env, context); + }, + // eslint-disable-next-line require-await + async scheduled( + event: ScheduledEvent, + env: CFModuleEnv, + context: ExecutionContext + ) { + return nitroApp.hooks.callHook("cloudflare:scheduled", event, env, context); + }, }; function assetsCacheControl(_request) { diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index 5277b5e141..b8d6c093eb 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -3,12 +3,21 @@ import { getAssetFromKV, mapRequestToAsset, } from "@cloudflare/kv-asset-handler"; +import { addEventListener } from "@cloudflare/workers-types"; import { withoutBase } from "ufo"; import { requestHasBody } from "../utils"; import { nitroApp } from "#internal/nitro/app"; import { useRuntimeConfig } from "#internal/nitro"; import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets"; +addEventListener("scheduled", (event) => { + return nitroApp.hooks.callHook("cloudflare:scheduled", event); +}); + +addEventListener("queue", (event) => { + return nitroApp.hooks.callHook("cloudflare:queue", event); +}); + addEventListener("fetch", (event: any) => { event.respondWith(handleEvent(event)); }); diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 41642cfd8a..e50ce048a4 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -1,3 +1,9 @@ +import { + ScheduledEvent, + ExecutionContext, + MessageBatch, + QueueEvent, +} from "@cloudflare/workers-types"; import type { H3Event, AppOptions } from "h3"; import type { RenderResponse } from "./renderer"; @@ -21,6 +27,32 @@ export type CaptureError = ( context: CapturedErrorContext ) => void; +interface CloudflareModuleEnv { + [key: string]: any; +} + +interface CloudflareModuleRuntimeHooks { + "cloudflare:scheduled": ( + event: ScheduledEvent, + env: CloudflareModuleEnv, + context: ExecutionContext + ) => any; + "cloudflare:queue": ( + event: MessageBatch, + env: CloudflareModuleEnv, + context: ExecutionContext + ) => any; +} + +interface CloudflareSWRuntimeHooks { + "cloudflare:scheduled": (event: ScheduledEvent) => any; + "cloudflare:queue": (event: QueueEvent) => any; +} + +type CloudflareRuntimeOptions = + | CloudflareModuleRuntimeHooks + | CloudflareSWRuntimeHooks; + export interface NitroRuntimeHooks { close: () => void; error: CaptureError; @@ -29,6 +61,9 @@ export interface NitroRuntimeHooks { beforeResponse: AppOptions["onBeforeResponse"]; afterResponse: AppOptions["onAfterResponse"]; + "cloudflare:scheduled": CloudflareRuntimeOptions["cloudflare:scheduled"]; + "cloudflare:queue": CloudflareRuntimeOptions["cloudflare:queue"]; + "render:response": ( response: Partial, context: { event: H3Event } From aa5b9c33aaa7130eb00385ffe12421871a218bb5 Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 19:30:17 +0700 Subject: [PATCH 2/8] feat: improve typings --- src/runtime/entries/cloudflare-module.ts | 18 ++++++++++++---- src/runtime/entries/cloudflare.ts | 14 +++++++++++- src/runtime/types.ts | 27 +++++++++++++----------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/runtime/entries/cloudflare-module.ts b/src/runtime/entries/cloudflare-module.ts index 88820aee61..412a89d909 100644 --- a/src/runtime/entries/cloudflare-module.ts +++ b/src/runtime/entries/cloudflare-module.ts @@ -1,9 +1,11 @@ +/* eslint-disable require-await */ import "#internal/nitro/virtual/polyfill"; import { withoutBase } from "ufo"; import type { ExecutionContext, MessageBatch, ScheduledEvent, + ExportedHandler, } from "@cloudflare/workers-types"; import { getAssetFromKV, @@ -73,21 +75,29 @@ export default { body, }); }, - // eslint-disable-next-line require-await + // https://developers.cloudflare.com/queues/get-started/#5-create-your-consumer-worker async queue( batch: MessageBatch, env: CFModuleEnv, context: ExecutionContext ) { - return nitroApp.hooks.callHook("cloudflare:queue", batch, env, context); + return nitroApp.hooks.callHook("cloudflare-module:queue", { + batch, + env, + context, + }); }, - // eslint-disable-next-line require-await + // https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/ async scheduled( event: ScheduledEvent, env: CFModuleEnv, context: ExecutionContext ) { - return nitroApp.hooks.callHook("cloudflare:scheduled", event, env, context); + return nitroApp.hooks.callHook("cloudflare-module:scheduled", { + event, + env, + context, + }); }, }; diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index b8d6c093eb..52f94a831d 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -3,13 +3,25 @@ import { getAssetFromKV, mapRequestToAsset, } from "@cloudflare/kv-asset-handler"; -import { addEventListener } from "@cloudflare/workers-types"; +import type { + WorkerGlobalScopeEventMap, + EventListenerOrEventListenerObject, + EventTargetAddEventListenerOptions, +} from "@cloudflare/workers-types"; import { withoutBase } from "ufo"; import { requestHasBody } from "../utils"; import { nitroApp } from "#internal/nitro/app"; import { useRuntimeConfig } from "#internal/nitro"; import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets"; +export declare function addEventListener< + Type extends keyof WorkerGlobalScopeEventMap, +>( + type: Type, + handler: EventListenerOrEventListenerObject, + options?: EventTargetAddEventListenerOptions | boolean +): void; + addEventListener("scheduled", (event) => { return nitroApp.hooks.callHook("cloudflare:scheduled", event); }); diff --git a/src/runtime/types.ts b/src/runtime/types.ts index e50ce048a4..63cf0c88dc 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -32,16 +32,16 @@ interface CloudflareModuleEnv { } interface CloudflareModuleRuntimeHooks { - "cloudflare:scheduled": ( - event: ScheduledEvent, - env: CloudflareModuleEnv, - context: ExecutionContext - ) => any; - "cloudflare:queue": ( - event: MessageBatch, - env: CloudflareModuleEnv, - context: ExecutionContext - ) => any; + "cloudflare:scheduled": (moduleArgs: { + event: ScheduledEvent; + env: CloudflareModuleEnv; + context: ExecutionContext; + }) => any; + "cloudflare:queue": (moduleArgs: { + batch: MessageBatch; + env: CloudflareModuleEnv; + context: ExecutionContext; + }) => any; } interface CloudflareSWRuntimeHooks { @@ -61,8 +61,11 @@ export interface NitroRuntimeHooks { beforeResponse: AppOptions["onBeforeResponse"]; afterResponse: AppOptions["onAfterResponse"]; - "cloudflare:scheduled": CloudflareRuntimeOptions["cloudflare:scheduled"]; - "cloudflare:queue": CloudflareRuntimeOptions["cloudflare:queue"]; + "cloudflare-module:scheduled": CloudflareModuleRuntimeHooks["cloudflare:scheduled"]; + "cloudflare-module:queue": CloudflareModuleRuntimeHooks["cloudflare:queue"]; + + "cloudflare:scheduled": CloudflareSWRuntimeHooks["cloudflare:scheduled"]; + "cloudflare:queue": CloudflareSWRuntimeHooks["cloudflare:queue"]; "render:response": ( response: Partial, From 32ffd79537bfaca76add5691a887b86ea3d5dd9d Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 19:59:40 +0700 Subject: [PATCH 3/8] fix: scheduled types --- src/runtime/entries/cloudflare-module.ts | 33 +++++++----------------- src/runtime/entries/cloudflare.ts | 15 ++++++----- src/runtime/types.ts | 3 ++- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/runtime/entries/cloudflare-module.ts b/src/runtime/entries/cloudflare-module.ts index 412a89d909..c6798ec7d8 100644 --- a/src/runtime/entries/cloudflare-module.ts +++ b/src/runtime/entries/cloudflare-module.ts @@ -2,10 +2,8 @@ import "#internal/nitro/virtual/polyfill"; import { withoutBase } from "ufo"; import type { - ExecutionContext, - MessageBatch, - ScheduledEvent, ExportedHandler, + Response as CFResponse, } from "@cloudflare/workers-types"; import { getAssetFromKV, @@ -23,12 +21,9 @@ interface CFModuleEnv { [key: string]: any; } -export default { - async fetch( - request: Request, // CFRequest, - env: CFModuleEnv, - context: ExecutionContext - ) { +export default { + async fetch(cfRequest, env: CFModuleEnv, context) { + const request = cfRequest as unknown as Request; try { // https://github.com/cloudflare/kv-asset-handler#es-modules return await getAssetFromKV( @@ -73,28 +68,20 @@ export default { method: request.method, headers: request.headers, body, - }); + }) as unknown as CFResponse; }, // https://developers.cloudflare.com/queues/get-started/#5-create-your-consumer-worker - async queue( - batch: MessageBatch, - env: CFModuleEnv, - context: ExecutionContext - ) { - return nitroApp.hooks.callHook("cloudflare-module:queue", { + async queue(batch, env: CFModuleEnv, context) { + await nitroApp.hooks.callHook("cloudflare-module:queue", { batch, env, context, }); }, // https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/ - async scheduled( - event: ScheduledEvent, - env: CFModuleEnv, - context: ExecutionContext - ) { - return nitroApp.hooks.callHook("cloudflare-module:scheduled", { - event, + async scheduled(controller, env: CFModuleEnv, context) { + await nitroApp.hooks.callHook("cloudflare-module:scheduled", { + controller, env, context, }); diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index 52f94a831d..926e975b31 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -7,6 +7,7 @@ import type { WorkerGlobalScopeEventMap, EventListenerOrEventListenerObject, EventTargetAddEventListenerOptions, + Response as CFResponse, } from "@cloudflare/workers-types"; import { withoutBase } from "ufo"; import { requestHasBody } from "../utils"; @@ -22,16 +23,18 @@ export declare function addEventListener< options?: EventTargetAddEventListenerOptions | boolean ): void; -addEventListener("scheduled", (event) => { - return nitroApp.hooks.callHook("cloudflare:scheduled", event); +addEventListener("scheduled", async (event) => { + await nitroApp.hooks.callHook("cloudflare:scheduled", event); }); -addEventListener("queue", (event) => { - return nitroApp.hooks.callHook("cloudflare:queue", event); +addEventListener("queue", async (event) => { + await nitroApp.hooks.callHook("cloudflare:queue", event); }); -addEventListener("fetch", (event: any) => { - event.respondWith(handleEvent(event)); +addEventListener("fetch", (event) => { + event.respondWith( + handleEvent(event as unknown as FetchEvent) as unknown as CFResponse + ); }); async function handleEvent(event: FetchEvent) { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 63cf0c88dc..e8bde4e0cd 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -3,6 +3,7 @@ import { ExecutionContext, MessageBatch, QueueEvent, + ScheduledController, } from "@cloudflare/workers-types"; import type { H3Event, AppOptions } from "h3"; import type { RenderResponse } from "./renderer"; @@ -33,7 +34,7 @@ interface CloudflareModuleEnv { interface CloudflareModuleRuntimeHooks { "cloudflare:scheduled": (moduleArgs: { - event: ScheduledEvent; + controller: ScheduledController; env: CloudflareModuleEnv; context: ExecutionContext; }) => any; From 1448389e4b750583af89db92f6bac6be40322c12 Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 20:39:51 +0700 Subject: [PATCH 4/8] feat: add sendResponse callback --- src/runtime/entries/cloudflare-module.ts | 14 ++++++++++++-- src/runtime/entries/cloudflare.ts | 15 ++++++++++----- src/runtime/types.ts | 18 ++++++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/runtime/entries/cloudflare-module.ts b/src/runtime/entries/cloudflare-module.ts index c6798ec7d8..7644cca1a5 100644 --- a/src/runtime/entries/cloudflare-module.ts +++ b/src/runtime/entries/cloudflare-module.ts @@ -2,6 +2,7 @@ import "#internal/nitro/virtual/polyfill"; import { withoutBase } from "ufo"; import type { + ExecutionContext as CFExecutionContext, ExportedHandler, Response as CFResponse, } from "@cloudflare/workers-types"; @@ -13,6 +14,7 @@ import { // See https://github.com/cloudflare/kv-asset-handler#asset_manifest-required-for-es-modules import manifest from "__STATIC_CONTENT_MANIFEST"; import { requestHasBody } from "../utils"; +import type { ExtendedExecutionContext } from "../types"; import { nitroApp } from "#internal/nitro/app"; import { useRuntimeConfig } from "#internal/nitro"; import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets"; @@ -71,20 +73,28 @@ export default { }) as unknown as CFResponse; }, // https://developers.cloudflare.com/queues/get-started/#5-create-your-consumer-worker - async queue(batch, env: CFModuleEnv, context) { + async queue(batch, env: CFModuleEnv, context: ExtendedExecutionContext) { + context.sendResponse = () => null; await nitroApp.hooks.callHook("cloudflare-module:queue", { batch, env, context, }); + return context.sendResponse(); }, // https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/ - async scheduled(controller, env: CFModuleEnv, context) { + async scheduled( + controller, + env: CFModuleEnv, + context: ExtendedExecutionContext + ) { + context.sendResponse = () => null; await nitroApp.hooks.callHook("cloudflare-module:scheduled", { controller, env, context, }); + return context.sendResponse(); }, }; diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index 926e975b31..dca7b8a259 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -8,27 +8,32 @@ import type { EventListenerOrEventListenerObject, EventTargetAddEventListenerOptions, Response as CFResponse, + ScheduledEvent, + QueueEvent, } from "@cloudflare/workers-types"; import { withoutBase } from "ufo"; import { requestHasBody } from "../utils"; +import type { ExtendedEvent } from "../types"; import { nitroApp } from "#internal/nitro/app"; import { useRuntimeConfig } from "#internal/nitro"; import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets"; -export declare function addEventListener< - Type extends keyof WorkerGlobalScopeEventMap, ->( +declare function addEventListener( type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean ): void; -addEventListener("scheduled", async (event) => { +addEventListener("scheduled", async (event: ExtendedEvent) => { + event.sendResponse = () => null; await nitroApp.hooks.callHook("cloudflare:scheduled", event); + return event.sendResponse(); }); -addEventListener("queue", async (event) => { +addEventListener("queue", async (event: ExtendedEvent) => { + event.sendResponse = () => null; await nitroApp.hooks.callHook("cloudflare:queue", event); + return event.sendResponse(); }); addEventListener("fetch", (event) => { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index e8bde4e0cd..1ecaeb02ac 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -32,22 +32,28 @@ interface CloudflareModuleEnv { [key: string]: any; } +type SendResponse = { sendResponse: () => any }; + +export type ExtendedExecutionContext = ExecutionContext & SendResponse; + interface CloudflareModuleRuntimeHooks { - "cloudflare:scheduled": (moduleArgs: { + "cloudflare:scheduled": (e: { controller: ScheduledController; env: CloudflareModuleEnv; - context: ExecutionContext; + context: ExtendedExecutionContext; }) => any; - "cloudflare:queue": (moduleArgs: { + "cloudflare:queue": (e: { batch: MessageBatch; env: CloudflareModuleEnv; - context: ExecutionContext; + context: ExtendedExecutionContext; }) => any; } +export type ExtendedEvent = Event & SendResponse; + interface CloudflareSWRuntimeHooks { - "cloudflare:scheduled": (event: ScheduledEvent) => any; - "cloudflare:queue": (event: QueueEvent) => any; + "cloudflare:scheduled": (event: ExtendedEvent) => any; + "cloudflare:queue": (event: ExtendedEvent) => any; } type CloudflareRuntimeOptions = From cb095e367bb33a42a49a53d6c6e6a8d47f6e9e5f Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 21:17:57 +0700 Subject: [PATCH 5/8] feat: SW syntax can't return directly --- src/runtime/entries/cloudflare.ts | 13 +++------- src/runtime/types.ts | 10 ++------ test/fixture/plugins/hooks.ts | 24 ++++++++++++++++++ test/presets/cloudflare-module.test.ts | 34 +++++++++++++++----------- 4 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 test/fixture/plugins/hooks.ts diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index dca7b8a259..2eb9df014d 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -8,12 +8,9 @@ import type { EventListenerOrEventListenerObject, EventTargetAddEventListenerOptions, Response as CFResponse, - ScheduledEvent, - QueueEvent, } from "@cloudflare/workers-types"; import { withoutBase } from "ufo"; import { requestHasBody } from "../utils"; -import type { ExtendedEvent } from "../types"; import { nitroApp } from "#internal/nitro/app"; import { useRuntimeConfig } from "#internal/nitro"; import { getPublicAssetMeta } from "#internal/nitro/virtual/public-assets"; @@ -24,16 +21,14 @@ declare function addEventListener( options?: EventTargetAddEventListenerOptions | boolean ): void; -addEventListener("scheduled", async (event: ExtendedEvent) => { - event.sendResponse = () => null; +// https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/ +addEventListener("scheduled", async (event) => { await nitroApp.hooks.callHook("cloudflare:scheduled", event); - return event.sendResponse(); }); -addEventListener("queue", async (event: ExtendedEvent) => { - event.sendResponse = () => null; +// https://developers.cloudflare.com/queues/get-started/#5-create-your-consumer-worker +addEventListener("queue", async (event) => { await nitroApp.hooks.callHook("cloudflare:queue", event); - return event.sendResponse(); }); addEventListener("fetch", (event) => { diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 1ecaeb02ac..841ec86e14 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -49,17 +49,11 @@ interface CloudflareModuleRuntimeHooks { }) => any; } -export type ExtendedEvent = Event & SendResponse; - interface CloudflareSWRuntimeHooks { - "cloudflare:scheduled": (event: ExtendedEvent) => any; - "cloudflare:queue": (event: ExtendedEvent) => any; + "cloudflare:scheduled": (event: ScheduledEvent) => any; + "cloudflare:queue": (event: QueueEvent) => any; } -type CloudflareRuntimeOptions = - | CloudflareModuleRuntimeHooks - | CloudflareSWRuntimeHooks; - export interface NitroRuntimeHooks { close: () => void; error: CaptureError; diff --git a/test/fixture/plugins/hooks.ts b/test/fixture/plugins/hooks.ts new file mode 100644 index 0000000000..4d660d6de1 --- /dev/null +++ b/test/fixture/plugins/hooks.ts @@ -0,0 +1,24 @@ +import { defineNitroPlugin } from "../../../src/runtime/plugin"; + +export default defineNitroPlugin((app) => { + app.hooks.hook("cloudflare:scheduled", (event) => { + event.waitUntil(Promise.resolve(event.scheduledTime)); + event.waitUntil(Promise.resolve(event.cron)); + event.waitUntil(Promise.resolve("scheduled:cloudflare")); + }); + + app.hooks.hook( + "cloudflare-module:scheduled", + ({ controller, env, context }) => { + context.waitUntil(Promise.resolve(controller.scheduledTime)); + context.waitUntil(Promise.resolve(controller.cron)); + context.sendResponse = () => "scheduled:cloudflare-module"; + } + ); + + app.hooks.hook("cloudflare:queue", (event) => {}); + + app.hooks.hook("cloudflare-module:queue", ({ batch, env, context }) => { + context.sendResponse = () => "queued:cloudflare-module"; + }); +}); diff --git a/test/presets/cloudflare-module.test.ts b/test/presets/cloudflare-module.test.ts index eae3eb289e..a7d53328e4 100644 --- a/test/presets/cloudflare-module.test.ts +++ b/test/presets/cloudflare-module.test.ts @@ -9,23 +9,23 @@ import { setupTest, testNitro } from "../tests"; describe("nitro:preset:cloudflare-module", async () => { const ctx = await setupTest("cloudflare-module"); - testNitro(ctx, () => { - const mf = new Miniflare({ - modules: true, - scriptPath: resolve(ctx.outDir, "server/index.mjs"), - sitePath: resolve(ctx.outDir, "public"), - bindings: { - ASSETS: { - fetch: async (request) => { - const contents = await fsp.readFile( - join(ctx.outDir, new URL(request.url).pathname) - ); - return new _Response(contents); - }, + const mf = new Miniflare({ + modules: true, + scriptPath: resolve(ctx.outDir, "server/index.mjs"), + sitePath: resolve(ctx.outDir, "public"), + bindings: { + ASSETS: { + fetch: async (request) => { + const contents = await fsp.readFile( + join(ctx.outDir, new URL(request.url).pathname) + ); + return new _Response(contents); }, }, - }); + }, + }); + testNitro(ctx, () => { return async ({ url, headers, method, body }) => { const res = await mf.dispatchFetch("http://localhost" + url, { headers: headers || {}, @@ -35,4 +35,10 @@ describe("nitro:preset:cloudflare-module", async () => { return res as unknown as Response; }; }); + + it("should handle scheduled events", async () => { + const waitUntil = await mf.dispatchScheduled(); + console.log(waitUntil); + expect(waitUntil[0]).toBe("scheduled:cloudflare-module"); + }); }); From 210cac1ee553e53086572ef0039e00d5116fa8d5 Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 21:34:54 +0700 Subject: [PATCH 6/8] docs: add better api docs --- src/runtime/entries/cloudflare.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runtime/entries/cloudflare.ts b/src/runtime/entries/cloudflare.ts index 2eb9df014d..38d70e6c13 100644 --- a/src/runtime/entries/cloudflare.ts +++ b/src/runtime/entries/cloudflare.ts @@ -21,12 +21,14 @@ declare function addEventListener( options?: EventTargetAddEventListenerOptions | boolean ): void; +// https://developers.cloudflare.com/workers/runtime-apis/add-event-listener // https://developers.cloudflare.com/workers/runtime-apis/scheduled-event/ addEventListener("scheduled", async (event) => { await nitroApp.hooks.callHook("cloudflare:scheduled", event); }); -// https://developers.cloudflare.com/queues/get-started/#5-create-your-consumer-worker +// https://developers.cloudflare.com/workers/runtime-apis/add-event-listener +// https://developers.cloudflare.com/queues/platform/javascript-apis/#consumer addEventListener("queue", async (event) => { await nitroApp.hooks.callHook("cloudflare:queue", event); }); From df36d8dba8eb1a1029cfb4259e353d67eb94f332 Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Tue, 15 Aug 2023 21:35:36 +0700 Subject: [PATCH 7/8] chore: remove logs --- test/presets/cloudflare-module.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/presets/cloudflare-module.test.ts b/test/presets/cloudflare-module.test.ts index a7d53328e4..c1eeae40c4 100644 --- a/test/presets/cloudflare-module.test.ts +++ b/test/presets/cloudflare-module.test.ts @@ -38,7 +38,6 @@ describe("nitro:preset:cloudflare-module", async () => { it("should handle scheduled events", async () => { const waitUntil = await mf.dispatchScheduled(); - console.log(waitUntil); expect(waitUntil[0]).toBe("scheduled:cloudflare-module"); }); }); From 7214a9a0f92836d04d3f7062cb9a31c2079e5bbe Mon Sep 17 00:00:00 2001 From: Hebilicious Date: Sun, 27 Aug 2023 12:01:43 +0700 Subject: [PATCH 8/8] test: add cloudflare test --- test/fixture/plugins/hooks.ts | 9 +++++++++ test/presets/cloudflare-module.test.ts | 1 + test/presets/cloudflare.test.ts | 14 ++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/test/fixture/plugins/hooks.ts b/test/fixture/plugins/hooks.ts index 4d660d6de1..e1ca82b840 100644 --- a/test/fixture/plugins/hooks.ts +++ b/test/fixture/plugins/hooks.ts @@ -2,6 +2,8 @@ import { defineNitroPlugin } from "../../../src/runtime/plugin"; export default defineNitroPlugin((app) => { app.hooks.hook("cloudflare:scheduled", (event) => { + console.log("SCHEDULED", event, event.scheduledTime, event.cron); + event.waitUntil(Promise.resolve(0)); event.waitUntil(Promise.resolve(event.scheduledTime)); event.waitUntil(Promise.resolve(event.cron)); event.waitUntil(Promise.resolve("scheduled:cloudflare")); @@ -10,6 +12,13 @@ export default defineNitroPlugin((app) => { app.hooks.hook( "cloudflare-module:scheduled", ({ controller, env, context }) => { + console.log( + "SCHEDULED:M", + controller, + controller.scheduledTime, + controller.cron + ); + context.waitUntil(Promise.resolve(0)); context.waitUntil(Promise.resolve(controller.scheduledTime)); context.waitUntil(Promise.resolve(controller.cron)); context.sendResponse = () => "scheduled:cloudflare-module"; diff --git a/test/presets/cloudflare-module.test.ts b/test/presets/cloudflare-module.test.ts index c1eeae40c4..27701a3342 100644 --- a/test/presets/cloudflare-module.test.ts +++ b/test/presets/cloudflare-module.test.ts @@ -38,6 +38,7 @@ describe("nitro:preset:cloudflare-module", async () => { it("should handle scheduled events", async () => { const waitUntil = await mf.dispatchScheduled(); + // console.log("WAIT:M", waitUntil); // Event is sent and hook is called, but waitUntil doesn't work. expect(waitUntil[0]).toBe("scheduled:cloudflare-module"); }); }); diff --git a/test/presets/cloudflare.test.ts b/test/presets/cloudflare.test.ts index 25d62c0e20..8eb7391c02 100644 --- a/test/presets/cloudflare.test.ts +++ b/test/presets/cloudflare.test.ts @@ -1,4 +1,3 @@ -import { promises as fsp } from "node:fs"; import { resolve } from "pathe"; import { Miniflare } from "miniflare"; import { describe, it, expect } from "vitest"; @@ -7,10 +6,11 @@ import { setupTest, testNitro } from "../tests"; describe("nitro:preset:cloudflare", async () => { const ctx = await setupTest("cloudflare"); + const mf = new Miniflare({ + scriptPath: resolve(ctx.outDir, "server/index.mjs"), + }); + testNitro(ctx, () => { - const mf = new Miniflare({ - scriptPath: resolve(ctx.outDir, "server/index.mjs"), - }); return async ({ url, headers, method, body }) => { const res = await mf.dispatchFetch("http://localhost" + url, { headers: headers || {}, @@ -20,4 +20,10 @@ describe("nitro:preset:cloudflare", async () => { return res as unknown as Response; }; }); + + it("should handle scheduled events", async () => { + const waitUntil = await mf.dispatchScheduled(); + // console.log("WAIT", waitUntil); // Event is sent and hook is called, but waitUntil doesn't work. + expect(waitUntil[0]).toBe(undefined); + }); });