(request: Request, env: Env)
| 101 | } satisfies ExportedHandler<Env>; |
| 102 | |
| 103 | async function handleCreate(request: Request, env: Env): Promise<Response> { |
| 104 | if (request.method !== "POST") { |
| 105 | return new Response("method not allowed", { status: 405, headers: { allow: "POST" } }); |
| 106 | } |
| 107 | |
| 108 | let body: CreateRequest; |
| 109 | try { |
| 110 | body = (await request.json()) as CreateRequest; |
| 111 | } catch { |
| 112 | return errorJSON(new Error("invalid JSON body"), 400); |
| 113 | } |
| 114 | |
| 115 | if (typeof body.name !== "string") { |
| 116 | return errorJSON(new Error("name must be a string"), 400); |
| 117 | } |
| 118 | const name = body.name.trim(); |
| 119 | if (!isValidWorkerName(name)) { |
| 120 | return errorJSON( |
| 121 | new Error( |
| 122 | "name must start with a lowercase letter or digit and contain only lowercase letters, digits, and hyphens", |
| 123 | ), |
| 124 | 400, |
| 125 | ); |
| 126 | } |
| 127 | |
| 128 | const stub = env.ArtifactCreator.get(env.ArtifactCreator.idFromName(name)); |
| 129 | // `wrangler types` doesn't surface the `__getWorkspaceStub` accessor |
| 130 | // the `withWorkspace` mixin installs, so cast once at the boundary |
| 131 | // and keep the rest readable. |
| 132 | const ws = await getWorkspace(stub as unknown as Parameters<typeof getWorkspace>[0]); |
| 133 | const sourceDir = `${WORKSPACE_ROOT}/${name}-source`; |
| 134 | const projectDir = `${WORKSPACE_ROOT}/${name}`; |
| 135 | |
| 136 | try { |
| 137 | await exec( |
| 138 | ws, |
| 139 | [ |
| 140 | sh`rm -rf ${sourceDir} ${projectDir}`, |
| 141 | sh`git clone --depth 1 ${SOURCE_REPO} ${sourceDir}`, |
| 142 | sh`mkdir -p ${projectDir}`, |
| 143 | sh`cp -R ${`${sourceDir}/${EXAMPLE_PATH}/.`} ${projectDir}`, |
| 144 | sh`sed -i ${`s/"name"[[:space:]]*:[[:space:]]*"[^"]*"/"name": "${name}"/`} ${`${projectDir}/wrangler.jsonc`}`, |
| 145 | sh`sed -i ${`s/"name"[[:space:]]*:[[:space:]]*"[^"]*"/"name": "@example\\/${name}"/`} ${`${projectDir}/package.json`}`, |
| 146 | sh`git init --initial-branch=main ${projectDir}`, |
| 147 | sh`cat ${`${projectDir}/.git/HEAD`} >/dev/null`, |
| 148 | "git add .", |
| 149 | sh`git commit -m ${`Create ${name} worker example`} --author ${"Cloudflare Computer Artifacts Example <computer-artifacts@example.invalid>"}`, |
| 150 | ].join(" && "), |
| 151 | { cwd: projectDir }, |
| 152 | ); |
| 153 | |
| 154 | // One command does the three setup steps: create the repo, mint |
| 155 | // a write token, and register the credentialed remote as |
| 156 | // `origin` in the project. `--force` makes the demo rerunnable |
| 157 | // with the same name — the repo is session-scoped by |
| 158 | // createArtifact() in the durable object, so this only touches |
| 159 | // the project this endpoint owns. The credentialed remote URL is |
| 160 | // a secret, so it is redacted from any error output. |
no test coverage detected