Skip to content

Commit 7611b60

Browse files
authored
Merge 90bddbe into 932a5f9
2 parents 932a5f9 + 90bddbe commit 7611b60

File tree

6 files changed

+194
-2
lines changed

6 files changed

+194
-2
lines changed

alchemy-web/src/content/docs/guides/cloudflare-worker.mdx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,40 @@ const worker = await Worker("frontend", {
165165
});
166166
```
167167

168+
## Compatibility Presets
169+
170+
Compatibility presets provide common sets of compatibility flags to avoid having to remember which flags are needed for specific use cases:
171+
172+
```ts
173+
import { Worker } from "alchemy/cloudflare";
174+
175+
const worker = await Worker("nodejs-worker", {
176+
name: "nodejs-worker",
177+
entrypoint: "./src/api.ts",
178+
compatibility: "node", // Automatically includes nodejs_compat flag
179+
});
180+
```
181+
182+
### Available Presets
183+
184+
- **`"node"`**: Enables Node.js compatibility by including the `nodejs_compat` flag
185+
186+
### Combining with Custom Flags
187+
188+
Preset flags are automatically combined with any custom `compatibilityFlags` you provide:
189+
190+
```ts
191+
import { Worker } from "alchemy/cloudflare";
192+
193+
const worker = await Worker("advanced-worker", {
194+
name: "advanced-worker",
195+
entrypoint: "./src/api.ts",
196+
compatibility: "node", // Adds nodejs_compat
197+
compatibilityFlags: ["experimental_feature"], // Additional flags
198+
// Result: ["nodejs_compat", "experimental_feature"]
199+
});
200+
```
201+
168202
## Cron Triggers
169203

170204
Schedule recurring tasks:

alchemy-web/src/content/docs/providers/cloudflare/worker.mdx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,40 @@ const worker = await Worker("frontend", {
163163
});
164164
```
165165

166+
## Compatibility Presets
167+
168+
Compatibility presets provide common sets of compatibility flags to avoid having to remember which flags are needed for specific use cases:
169+
170+
```ts
171+
import { Worker } from "alchemy/cloudflare";
172+
173+
const worker = await Worker("nodejs-worker", {
174+
name: "nodejs-worker",
175+
entrypoint: "./src/api.ts",
176+
compatibility: "node", // Automatically includes nodejs_compat flag
177+
});
178+
```
179+
180+
### Available Presets
181+
182+
- **`"node"`**: Enables Node.js compatibility by including the `nodejs_compat` flag
183+
184+
### Combining with Custom Flags
185+
186+
Preset flags are automatically combined with any custom `compatibilityFlags` you provide:
187+
188+
```ts
189+
import { Worker } from "alchemy/cloudflare";
190+
191+
const worker = await Worker("advanced-worker", {
192+
name: "advanced-worker",
193+
entrypoint: "./src/api.ts",
194+
compatibility: "node", // Adds nodejs_compat
195+
compatibilityFlags: ["experimental_feature"], // Additional flags
196+
// Result: ["nodejs_compat", "experimental_feature"]
197+
});
198+
```
199+
166200
## Cron Triggers
167201

168202
Schedule recurring tasks:
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Compatibility presets for Cloudflare Workers
3+
*
4+
* These presets provide common sets of compatibility flags to avoid
5+
* users having to remember which flags they need for common use cases.
6+
*/
7+
8+
/**
9+
* Mapping of compatibility presets to their respective compatibility flags
10+
*/
11+
export const COMPATIBILITY_PRESETS = {
12+
/**
13+
* Node.js compatibility preset
14+
* Enables Node.js APIs and runtime compatibility
15+
*/
16+
node: ["nodejs_compat"],
17+
} as const;
18+
19+
export type CompatibilityPreset = keyof typeof COMPATIBILITY_PRESETS;
20+
21+
/**
22+
* Union preset compatibility flags with user-provided flags
23+
*/
24+
export function unionCompatibilityFlags(
25+
preset: CompatibilityPreset | undefined,
26+
userFlags: string[] = []
27+
): string[] {
28+
if (!preset) {
29+
return userFlags;
30+
}
31+
32+
const presetFlags = COMPATIBILITY_PRESETS[preset];
33+
return Array.from(new Set([...presetFlags, ...userFlags]));
34+
}

alchemy/src/cloudflare/worker.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ import {
4848
normalizeWorkerBundle,
4949
} from "./bundle/index.ts";
5050
import { wrap } from "./bundle/normalize.ts";
51+
import {
52+
type CompatibilityPreset,
53+
unionCompatibilityFlags,
54+
} from "./compatibility-presets.ts";
5155
import { type Container, ContainerApplication } from "./container.ts";
5256
import { CustomDomain } from "./custom-domain.ts";
5357
import { isD1Database } from "./d1-database.ts";
@@ -215,6 +219,15 @@ export interface BaseWorkerProps<
215219
*/
216220
compatibilityFlags?: string[];
217221

222+
/**
223+
* Compatibility preset to automatically include common compatibility flags
224+
*
225+
* - "node": Includes nodejs_compat flag for Node.js compatibility
226+
*
227+
* @default undefined (no preset)
228+
*/
229+
compatibility?: CompatibilityPreset;
230+
218231
/**
219232
* Configuration for static assets
220233
*/
@@ -834,7 +847,13 @@ export function Worker<const B extends Bindings>(
834847
...(props as any),
835848
url: true,
836849
compatibilityFlags: Array.from(
837-
new Set(["nodejs_compat", ...(props.compatibilityFlags ?? [])]),
850+
new Set([
851+
"nodejs_compat",
852+
...unionCompatibilityFlags(
853+
props.compatibility,
854+
props.compatibilityFlags,
855+
),
856+
]),
838857
),
839858
entrypoint: meta!.filename,
840859
name: workerName,
@@ -987,7 +1006,10 @@ export const _Worker = Resource(
9871006
const workerName = props.name ?? id;
9881007
const compatibilityDate =
9891008
props.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE;
990-
const compatibilityFlags = props.compatibilityFlags ?? [];
1009+
const compatibilityFlags = unionCompatibilityFlags(
1010+
props.compatibility,
1011+
props.compatibilityFlags,
1012+
);
9911013
const dispatchNamespace =
9921014
typeof props.namespace === "string"
9931015
? props.namespace

alchemy/test/cloudflare/worker.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,4 +1876,66 @@ describe("Worker Resource", () => {
18761876
await assertWorkerDoesNotExist(api, workerName);
18771877
}
18781878
});
1879+
1880+
test("create worker with compatibility preset", async (scope) => {
1881+
const workerName = `${BRANCH_PREFIX}-test-worker-compatibility-preset`;
1882+
1883+
let worker: Worker | undefined;
1884+
try {
1885+
// Create a worker with the "node" compatibility preset
1886+
worker = await Worker(workerName, {
1887+
name: workerName,
1888+
adopt: true,
1889+
script: `
1890+
export default {
1891+
async fetch(request, env, ctx) {
1892+
return new Response('Hello from Node.js compatible worker!', {
1893+
status: 200,
1894+
headers: { 'Content-Type': 'text/plain' }
1895+
});
1896+
}
1897+
};
1898+
`,
1899+
format: "esm",
1900+
url: true,
1901+
compatibility: "node", // Use the "node" preset
1902+
});
1903+
1904+
expect(worker.id).toBeTruthy();
1905+
expect(worker.name).toEqual(workerName);
1906+
expect(worker.url).toBeTruthy();
1907+
1908+
// Verify that the "node" preset automatically includes nodejs_compat flag
1909+
expect(worker.compatibilityFlags).toContain("nodejs_compat");
1910+
1911+
// Test that preset flags are combined with user-provided flags
1912+
worker = await Worker(workerName, {
1913+
name: workerName,
1914+
adopt: true,
1915+
script: `
1916+
import crypto from 'node:crypto';
1917+
1918+
export default {
1919+
async fetch(request, env, ctx) {
1920+
return new Response('Hello from Node.js compatible worker with additional flags!', {
1921+
status: 200,
1922+
headers: { 'Content-Type': 'text/plain' }
1923+
});
1924+
}
1925+
};
1926+
`,
1927+
format: "esm",
1928+
url: true,
1929+
compatibility: "node",
1930+
compatibilityFlags: ["nodejs_als"], // Add valid compatibility flag in addition to preset
1931+
});
1932+
1933+
// Verify that both preset flags and user-provided flags are present
1934+
expect(worker.compatibilityFlags).toContain("nodejs_compat"); // From preset
1935+
expect(worker.compatibilityFlags).toContain("nodejs_als"); // From user
1936+
} finally {
1937+
await destroy(scope);
1938+
await assertWorkerDoesNotExist(api, workerName);
1939+
}
1940+
});
18791941
});

vitest.setup.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
import { config } from "dotenv";
22
config({ path: ".env" });
3+
4+
// Polyfill File constructor for Node.js if not available
5+
if (typeof globalThis.File === "undefined") {
6+
const { File } = require("node:buffer");
7+
globalThis.File = File;
8+
}

0 commit comments

Comments
 (0)