Skip to content

Commit

Permalink
feat: experimental cloudflare-durable preset (#2801)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Oct 18, 2024
1 parent 9df404a commit 2040cd5
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 2 deletions.
10 changes: 10 additions & 0 deletions playground/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
name = "nitro-test"

compatibility_date = "2024-09-19"

assets = { directory = "./.output/public/", binding = "ASSETS" }

[[durable_objects.bindings]]
name = "$DurableObject"
class_name = "$DurableObject"

[[migrations]]
tag = "v1"
new_classes = ["$DurableObject"]
4 changes: 2 additions & 2 deletions src/presets/_types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export interface PresetOptions {
vercel: VercelOptions;
}

export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "aws-lambda-streaming" | "azure" | "azure-functions" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflare-module-legacy" | "cloudflare-pages" | "cloudflare-pages-static" | "cloudflare-worker" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "edgio" | "firebase" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis" | "iis-handler" | "iis-node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlify-edge" | "netlify-legacy" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-listener" | "node-server" | "platform-sh" | "render-com" | "service-worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";
export type PresetName = "alwaysdata" | "aws-amplify" | "aws-lambda" | "aws-lambda-streaming" | "azure" | "azure-functions" | "azure-swa" | "base-worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-durable" | "cloudflare-module" | "cloudflare-module-legacy" | "cloudflare-pages" | "cloudflare-pages-static" | "cloudflare-worker" | "deno" | "deno-deploy" | "deno-server" | "digital-ocean" | "edgio" | "firebase" | "flight-control" | "genezio" | "github-pages" | "gitlab-pages" | "heroku" | "iis" | "iis-handler" | "iis-node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlify-edge" | "netlify-legacy" | "netlify-static" | "nitro-dev" | "nitro-prerender" | "node" | "node-cluster" | "node-listener" | "node-server" | "platform-sh" | "render-com" | "service-worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercel-static" | "winterjs" | "zeabur" | "zeabur-static" | "zerops" | "zerops-static";

export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "aws-lambda-streaming" | "awsLambdaStreaming" | "aws_lambda_streaming" | "azure" | "azure-functions" | "azureFunctions" | "azure_functions" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-module-legacy" | "cloudflareModuleLegacy" | "cloudflare_module_legacy" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "cloudflare-worker" | "cloudflareWorker" | "cloudflare_worker" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "edgio" | "firebase" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlifyBuilder" | "netlify_builder" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-legacy" | "netlifyLegacy" | "netlify_legacy" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-listener" | "nodeListener" | "node_listener" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "service-worker" | "serviceWorker" | "service_worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercelEdge" | "vercel_edge" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
export type PresetNameInput = "alwaysdata" | "aws-amplify" | "awsAmplify" | "aws_amplify" | "aws-lambda" | "awsLambda" | "aws_lambda" | "aws-lambda-streaming" | "awsLambdaStreaming" | "aws_lambda_streaming" | "azure" | "azure-functions" | "azureFunctions" | "azure_functions" | "azure-swa" | "azureSwa" | "azure_swa" | "base-worker" | "baseWorker" | "base_worker" | "bun" | "cleavr" | "cli" | "cloudflare" | "cloudflare-durable" | "cloudflareDurable" | "cloudflare_durable" | "cloudflare-module" | "cloudflareModule" | "cloudflare_module" | "cloudflare-module-legacy" | "cloudflareModuleLegacy" | "cloudflare_module_legacy" | "cloudflare-pages" | "cloudflarePages" | "cloudflare_pages" | "cloudflare-pages-static" | "cloudflarePagesStatic" | "cloudflare_pages_static" | "cloudflare-worker" | "cloudflareWorker" | "cloudflare_worker" | "deno" | "deno-deploy" | "denoDeploy" | "deno_deploy" | "deno-server" | "denoServer" | "deno_server" | "digital-ocean" | "digitalOcean" | "digital_ocean" | "edgio" | "firebase" | "flight-control" | "flightControl" | "flight_control" | "genezio" | "github-pages" | "githubPages" | "github_pages" | "gitlab-pages" | "gitlabPages" | "gitlab_pages" | "heroku" | "iis" | "iis-handler" | "iisHandler" | "iis_handler" | "iis-node" | "iisNode" | "iis_node" | "koyeb" | "layer0" | "netlify" | "netlify-builder" | "netlifyBuilder" | "netlify_builder" | "netlify-edge" | "netlifyEdge" | "netlify_edge" | "netlify-legacy" | "netlifyLegacy" | "netlify_legacy" | "netlify-static" | "netlifyStatic" | "netlify_static" | "nitro-dev" | "nitroDev" | "nitro_dev" | "nitro-prerender" | "nitroPrerender" | "nitro_prerender" | "node" | "node-cluster" | "nodeCluster" | "node_cluster" | "node-listener" | "nodeListener" | "node_listener" | "node-server" | "nodeServer" | "node_server" | "platform-sh" | "platformSh" | "platform_sh" | "render-com" | "renderCom" | "render_com" | "service-worker" | "serviceWorker" | "service_worker" | "static" | "stormkit" | "vercel" | "vercel-edge" | "vercelEdge" | "vercel_edge" | "vercel-static" | "vercelStatic" | "vercel_static" | "winterjs" | "zeabur" | "zeabur-static" | "zeaburStatic" | "zeabur_static" | "zerops" | "zerops-static" | "zeropsStatic" | "zerops_static" | (string & {});
15 changes: 15 additions & 0 deletions src/presets/cloudflare/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,25 @@ const cloudflareModule = defineNitroPreset(
}
);

const cloudflareDurable = defineNitroPreset(
{
extends: "cloudflare-module",
entry: "./runtime/cloudflare-durable",
rollupConfig: {
external: ["cloudflare:workers"],
},
},
{
name: "cloudflare-durable" as const,
url: import.meta.url,
}
);

export default [
cloudflare,
cloudflareModuleLegacy,
cloudflareModule,
cloudflareDurable,
cloudflarePages,
cloudflarePagesStatic,
];
83 changes: 83 additions & 0 deletions src/presets/cloudflare/runtime/cloudflare-durable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import "#nitro-internal-pollyfills";
import type * as CF from "@cloudflare/workers-types";
import { DurableObject } from "cloudflare:workers";
import wsAdapter from "crossws/adapters/cloudflare-durable";
import { useNitroApp } from "nitropack/runtime";
import { isPublicAssetURL } from "#nitro-internal-virtual/public-assets";
import { createHandler } from "./_module-handler";

const nitroApp = useNitroApp();

const ws = import.meta._websocket
? wsAdapter(nitroApp.h3App.websocket)
: undefined;

interface Env {
ASSETS?: { fetch: typeof CF.fetch };
}

export default createHandler<Env>({
fetch(request, env, context, url) {
// Static assets fallback (optional binding)
if (env.ASSETS && isPublicAssetURL(url.pathname)) {
return env.ASSETS.fetch(request);
}

// Websocket upgrade
// https://crossws.unjs.io/adapters/cloudflare#durable-objects
if (
import.meta._websocket &&
request.headers.get("upgrade") === "websocket"
) {
return ws!.handleUpgrade(request, env, context);
}
},
});

export class $DurableObject extends DurableObject {
constructor(state: DurableObjectState, env: Record<string, any>) {
super(state, env);
state.waitUntil(
nitroApp.hooks.callHook("cloudflare:durable:init", this, {
state,
env,
})
);
if (import.meta._websocket) {
ws!.handleDurableInit(this, state, env);
}
}

override fetch(request: Request) {
if (import.meta._websocket) {
return ws!.handleDurableUpgrade(this, request);
}
return new Response("404", { status: 404 });
}

override alarm(): void | Promise<void> {
this.ctx.waitUntil(
nitroApp.hooks.callHook("cloudflare:durable:alarm", this)
);
}

override async webSocketMessage(
client: WebSocket,
message: ArrayBuffer | string
) {
if (import.meta._websocket) {
return ws!.handleDurableMessage(this, client, message);
}
}

override async webSocketClose(
client: WebSocket,
code: number,
reason: string,
wasClean: boolean
) {
if (import.meta._websocket) {
return ws!.handleDurableClose(this, client, code, reason, wasClean);
}
}
}
13 changes: 13 additions & 0 deletions src/presets/cloudflare/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ScheduledController,
TraceItem,
} from "@cloudflare/workers-types";
import type { DurableObject } from "cloudflare:workers";
import type { Config as WranglerConfig } from "./types.wrangler";

/**
Expand Down Expand Up @@ -58,6 +59,8 @@ export interface CloudflareOptions {
};
}

type DurableObjectState = ConstructorParameters<typeof DurableObject>[0];

declare module "nitropack/types" {
export interface NitroRuntimeHooks {
// https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/
Expand Down Expand Up @@ -93,5 +96,15 @@ declare module "nitropack/types" {
env: unknown;
context: ExecutionContext;
}) => void;

"cloudflare:durable:init": (
durable: DurableObject,
_: {
state: DurableObjectState;
env: unknown;
}
) => void;

"cloudflare:durable:alarm": (durable: DurableObject) => void;
}
}
1 change: 1 addition & 0 deletions test/fixture/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"skipLibCheck": true,
"baseUrl": ".",
"strict": false,
"types": ["@cloudflare/workers-types"],
// (currently manually synced with top level tsconfig.json paths)
"paths": {
// CLI
Expand Down

0 comments on commit 2040cd5

Please sign in to comment.