Skip to content

Instantly share code, notes, and snippets.

@ericclemmons
Created October 15, 2025 15:20
Show Gist options
  • Select an option

  • Save ericclemmons/22151e28668cc3582a47ad349c4f53fd to your computer and use it in GitHub Desktop.

Select an option

Save ericclemmons/22151e28668cc3582a47ad349c4f53fd to your computer and use it in GitHub Desktop.
Alchemy resource for Worker Builds
import { Resource, type Context } from "alchemy";
import { CloudflareApi, createCloudflareApi } from "alchemy/cloudflare";
const DEFAULT_BUILD_COMMAND = "";
const DEFAULT_PREVIEW_DEPLOY_COMMAND = "npx wrangler versions upload";
const DEFAULT_PRODUCTION_DEPLOY_COMMAND = "npx wrangler deploy";
const DEFAULT_ROOT_DIRECTORY = "/";
interface JsonResponse<T> {
success: boolean;
errors: [unknown[]];
messages: [unknown[]];
result: T;
}
interface PaginatedJsonResponse<T> extends JsonResponse<T[]> {
result_info: {
count: number;
next_page: boolean;
page: number;
per_page: number;
total_count: number;
total_pages: number;
};
}
interface ProviderConnection {
avatar_url: string;
created_on: string;
github_avatar_url: string;
github_username: string;
has_valid_oauth_token: boolean;
owner_display_name: string;
owner: string;
provider_account_id: string;
settings_url: string;
type: string;
}
interface RepoConnection {
default_branch: string;
project_id: string | null;
project_ids: string[] | null;
repo_display_name: string;
repo_id: string;
repo_name: string;
repo_url: string;
}
interface Connection extends Timestamped {
provider_account_id: string;
provider_account_name: string;
provider_type: string;
repo_connection_uuid: string;
repo_id: string;
repo_name: string;
}
interface PutConnection
extends Omit<Connection, keyof Timestamped | "repo_connection_uuid"> {}
interface Token {
build_token_name: string;
build_token_uuid: string;
cloudflare_token_id: string;
owner_type: "user" | unknown;
}
interface Timestamped {
created_on: string;
deleted_on: string | null;
modified_on: string;
}
interface ExternalScript {
/**
* e.g. `props.name`
*/
id: string;
/**
* The actual ID of the worker
*/
tag: string;
entry_point: string;
}
interface Trigger extends Timestamped {
branch_excludes: string[];
branch_includes: string[];
build_caching_enabled: boolean;
build_command: string;
build_token_uuid: string;
deploy_command: string;
external_script_id: string;
path_excludes: string[];
path_includes: string[];
repo_connection_uuid: string;
root_directory: string;
trigger_name: string;
trigger_uuid: string;
}
interface PutTrigger
extends Omit<Trigger, keyof Timestamped | "trigger_uuid"> {}
export interface WorkerProjectProps {
/**
* @default "" (inferrred by Cloudflare)
*/
buildCommand?: string;
/**
* @default "npx wrangler deploy"
*/
deployCommand?: string;
/**
* @default "/"
*/
rootDirectory?: string;
/**
* Should be the name of the Worker (e.g. `wrangler.jsonc#name`)
*/
name: string;
// TODO: Support logs
// PATCH https://dash.cloudflare.com/api/v4/accounts/${app.accountId}/workers/scripts/${app.name}/script-settings
// { "logs": { "enabled": true, "head_sampling_rate": 1, "invocation_logs": true } }
owner: string;
/**
* @default true
*/
preview?: Pick<
WorkerProjectProps,
"buildCommand" | "deployCommand" | "rootDirectory"
>;
/**
* @default "github"
*/
provider?: "github";
repo: string;
}
export interface WorkerProject
extends Resource<"cloudflare::WorkerProject">,
WorkerProjectProps {
/**
* Production and (optional) preview triggers
*/
triggers: [Trigger, Trigger];
}
const getProviderConnection = async (
api: CloudflareApi,
props: WorkerProjectProps
) => {
const { provider = "github", owner, repo } = props;
const response = await api.get(
`/accounts/${api.accountId}/pages/connections`
);
if (!response.ok) {
throw new Error("Could not get Cloudflare connections", {
cause: response,
});
}
const connections = (await response.json()) as JsonResponse<
ProviderConnection[]
>;
if (!connections.success) {
throw new Error(
"Unexpected Error – Cloudflare did not return connections from the API",
{ cause: connections }
);
}
const providerConnection = connections.result.find(
(connection) => connection.type === provider && connection.owner === owner
);
if (!providerConnection) {
throw new Error(
`Cloudflare does not have access to "${owner}/${repo}". Make sure "${owner}/${repo}" (NOT "All Repositories") is selected at:\n\thttps://github.com/apps/cloudflare-workers-and-pages/installations/select_target`,
{ cause: connections }
);
}
return providerConnection;
};
const getRepoConnection = async (
api: CloudflareApi,
props: WorkerProjectProps
) => {
const { provider = "github", owner, repo } = props;
const response = await api.get(
`/accounts/${api.accountId}/pages/connections/${provider}/${owner}/repos/${repo}`
);
if (!response.ok) {
throw new Error(
`Cloudflare does not have access to "${owner}/${repo}". Make sure "${owner}/${repo}" (NOT "All Repositories") is selected at:\n\thttps://github.com/apps/cloudflare-workers-and-pages/installations/select_target`,
{ cause: response }
);
}
const connection = (await response.json()) as JsonResponse<RepoConnection>;
if (!connection.success) {
throw new Error(
`Unexpected Error – Cloudflare has access to "${owner}/${repo}", but did not return it from the API`,
{ cause: connection }
);
}
return connection.result;
};
const getConnection = async (api: CloudflareApi, props: WorkerProjectProps) => {
const [provider, repo] = await Promise.all([
getProviderConnection(api, props),
getRepoConnection(api, props),
]);
return { provider, repo };
};
const getBuildToken = async (api: CloudflareApi, props: WorkerProjectProps) => {
const response = await api.get(`/accounts/${api.accountId}/builds/tokens`);
if (!response.ok) {
throw new Error("Could not get Cloudflare build token", {
cause: response,
});
}
const tokens = (await response.json()) as PaginatedJsonResponse<Token>;
const [token] = tokens.result;
if (!token) {
throw new Error("Unexpected Error – Could not get Cloudflare build token", {
cause: tokens,
});
}
return token;
};
const createExternalScript = async (
api: CloudflareApi,
props: WorkerProjectProps
) => {
const body = new FormData();
// Create a worker stub to satisfy an initial build
body.append(
"worker.js",
new Blob(
[
`export default { async fetch(request, env) { return new Response("Hello world") } }`,
],
{ type: "application/javascript+module" }
),
"worker.js"
);
body.append(
"metadata",
new Blob(
[
JSON.stringify({
compatibility_date: "2025-06-07",
keep_assets: true,
bindings: [],
main_module: "worker.js",
}),
],
{ type: "application/json" }
),
"blob"
);
const response = await api.put(
`/accounts/${api.accountId}/workers/services/${props.name}/environments/production?include_subdomain_availability=true`,
body,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
if (!response.ok) {
throw new Error("Could not create external script", { cause: response });
}
const { result } = (await response.json()) as JsonResponse<ExternalScript>;
return result;
};
const createConnection = async (
api: CloudflareApi,
{ provider, repo }: Awaited<ReturnType<typeof getConnection>>
) => {
const response = await api.put(
`/accounts/${api.accountId}/builds/repos/connections`,
{
provider_account_id: provider.provider_account_id,
provider_account_name: provider.owner,
provider_type: provider.type,
repo_id: repo.repo_id,
repo_name: repo.repo_name,
} satisfies PutConnection
);
if (!response.ok) {
throw new Error(
`Could not Cloudflare connection to ${provider.owner}/${repo.repo_name}`,
{ cause: response }
);
}
const { result } = (await response.json()) as JsonResponse<Connection>;
return result;
};
const createWorkerProject = async (
api: CloudflareApi,
props: WorkerProjectProps
): Promise<WorkerProject["triggers"]> => {
const { provider, repo } = await getConnection(api, props);
const connection = await createConnection(api, { provider, repo });
const buildToken = await getBuildToken(api, props);
const externalScript = await createExternalScript(api, props);
const productionTrigger = (await api
.post(`/accounts/${api.accountId}/builds/triggers`, {
branch_excludes: [],
branch_includes: [repo.default_branch],
build_caching_enabled: true,
build_command: props.buildCommand ?? DEFAULT_BUILD_COMMAND,
build_token_uuid: buildToken.build_token_uuid,
deploy_command: props.deployCommand ?? DEFAULT_PRODUCTION_DEPLOY_COMMAND,
external_script_id: externalScript.tag,
path_excludes: [],
path_includes: ["*"],
repo_connection_uuid: connection.repo_connection_uuid,
root_directory: props.rootDirectory ?? DEFAULT_ROOT_DIRECTORY,
trigger_name: "Deploy default branch",
} satisfies PutTrigger)
.then((response) => response.json())) as JsonResponse<Trigger>;
// Prefer preview values over props values
const { buildCommand, deployCommand, rootDirectory } = {
...props,
...props.preview,
};
const previewResponse = (await api
.post(`/accounts/${api.accountId}/builds/triggers`, {
branch_excludes: [repo.default_branch],
branch_includes: ["*"],
build_caching_enabled: true,
build_command: buildCommand ?? DEFAULT_BUILD_COMMAND,
build_token_uuid: buildToken.build_token_uuid,
deploy_command: deployCommand ?? DEFAULT_PREVIEW_DEPLOY_COMMAND,
external_script_id: externalScript.tag,
path_excludes: [],
path_includes: ["*"],
repo_connection_uuid: connection.repo_connection_uuid,
root_directory: rootDirectory ?? DEFAULT_ROOT_DIRECTORY,
trigger_name: "Deploy non-production branches",
} satisfies PutTrigger)
.then((response) => response.json())) as JsonResponse<Trigger>;
// Enable *.workers.dev
await api.post(
`/accounts/${api.accountId}/workers/services/${props.name}/environments/production/subdomain`,
{ enabled: true, previews_enabled: true }
);
return [productionTrigger.result, previewResponse.result];
};
const updateWorkerProject = async (
api: CloudflareApi,
props: WorkerProjectProps,
output: WorkerProject
): Promise<WorkerProject["triggers"]> => {
const [previousProductionTrigger, previousPreviewTrigger] = output.triggers;
const productionTrigger = (await api
.patch(
`/accounts/${api.accountId}/builds/triggers/${previousProductionTrigger.trigger_uuid}`,
{
build_command: props.buildCommand ?? DEFAULT_BUILD_COMMAND,
deploy_command:
props.deployCommand ?? DEFAULT_PRODUCTION_DEPLOY_COMMAND,
root_directory: props.rootDirectory ?? DEFAULT_ROOT_DIRECTORY,
} satisfies Partial<PutTrigger>
)
.then((response) => response.json())) as JsonResponse<Trigger>;
const previewTrigger = (await api
.patch(
`/accounts/${api.accountId}/builds/triggers/${previousPreviewTrigger.trigger_uuid}`,
{
build_command: props.buildCommand ?? DEFAULT_BUILD_COMMAND,
deploy_command: props.deployCommand ?? DEFAULT_PREVIEW_DEPLOY_COMMAND,
root_directory: props.rootDirectory ?? DEFAULT_ROOT_DIRECTORY,
} satisfies Partial<PutTrigger>
)
.then((response) => response.json())) as JsonResponse<Trigger>;
return [productionTrigger.result, previewTrigger.result];
};
export const WorkerProject = Resource(
"cloudflare::WorkerProject",
async function (
this: Context<WorkerProject>,
id: string,
props: WorkerProjectProps
): Promise<WorkerProject> {
const api = await createCloudflareApi();
switch (this.phase) {
case "create":
return this.create({
...props,
triggers: await createWorkerProject(api, props),
});
case "update":
return this.create({
...props,
triggers: await updateWorkerProject(api, props, this.output),
});
case "delete":
const response = await api.delete(
`/accounts/${api.accountId}/workers/services/${
this.output.name ?? props.name
}?force=false`
);
if (!response.ok) {
console.warn(
`Could not delete Cloudflare Worker Project through API. Do it manually at:\n\thttps://dash.cloudflare.com/${api.accountId}/workers-and-pages`
);
}
return this.destroy();
default:
const phase: never = this.phase;
throw new Error(`Unsupported phase: ${phase}`);
}
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment