diff --git a/.changeset/healthy-bugs-reply.md b/.changeset/healthy-bugs-reply.md new file mode 100644 index 000000000000..f03d1c77c478 --- /dev/null +++ b/.changeset/healthy-bugs-reply.md @@ -0,0 +1,9 @@ +--- +"wrangler": patch +--- + +feat: dev+envs + +This implements service environments + `wrangler dev`. Fairly simple, it just needed the right url when creating the edge preview token. + +I tested this by publishing a service under one env, adding secrets under it in the dashboard, and then trying to dev under another env, and verifying that the secrets didn't leak. diff --git a/packages/wrangler/src/__tests__/dev.test.tsx b/packages/wrangler/src/__tests__/dev.test.tsx index 3e85d0034b12..452065a701e0 100644 --- a/packages/wrangler/src/__tests__/dev.test.tsx +++ b/packages/wrangler/src/__tests__/dev.test.tsx @@ -82,7 +82,8 @@ function renderDev({ usageModel, buildCommand = {}, enableLocalPersistence = false, - env = undefined, + env, + zone, }: Partial) { return render( ); } diff --git a/packages/wrangler/src/api/preview.ts b/packages/wrangler/src/api/preview.ts index 179c0fd9efcd..c2d088f72cd1 100644 --- a/packages/wrangler/src/api/preview.ts +++ b/packages/wrangler/src/api/preview.ts @@ -2,7 +2,7 @@ import { URL } from "node:url"; import { fetch } from "undici"; import { fetchResult } from "../cfetch"; import { toFormData } from "./form_data"; -import type { CfAccount, CfWorkerInit } from "./worker"; +import type { CfAccount, CfWorkerContext, CfWorkerInit } from "./worker"; /** * A preview mode. @@ -55,10 +55,13 @@ export interface CfPreviewToken { /** * Generates a preview session token. */ -async function sessionToken(account: CfAccount): Promise { - const { accountId, zoneId } = account; - const initUrl = zoneId - ? `/zones/${zoneId}/workers/edge-preview` +async function sessionToken( + account: CfAccount, + ctx: CfWorkerContext +): Promise { + const { accountId } = account; + const initUrl = ctx.zone + ? `/zones/${ctx.zone}/workers/edge-preview` : `/accounts/${accountId}/workers/subdomain/edge-preview`; const { exchange_url } = await fetchResult<{ exchange_url: string }>(initUrl); @@ -92,15 +95,22 @@ function randomId(): string { */ export async function previewToken( account: CfAccount, - worker: CfWorkerInit + worker: CfWorkerInit, + ctx: CfWorkerContext ): Promise { - const { value, host, inspectorUrl, prewarmUrl } = await sessionToken(account); + const { value, host, inspectorUrl, prewarmUrl } = await sessionToken( + account, + ctx + ); - const { accountId, zoneId } = account; - const scriptId = zoneId ? randomId() : worker.name || host.split(".")[0]; - const url = `/accounts/${accountId}/workers/scripts/${scriptId}/edge-preview`; + const { accountId } = account; + const scriptId = ctx.zone ? randomId() : worker.name || host.split(".")[0]; + const url = + ctx.env && !ctx.legacyEnv + ? `/accounts/${accountId}/workers/services/${scriptId}/environments/${ctx.env}/edge-preview` + : `/accounts/${accountId}/workers/scripts/${scriptId}/edge-preview`; - const mode: CfPreviewMode = zoneId + const mode: CfPreviewMode = ctx.zone ? { routes: ["*/*"] } : { workers_dev: true }; @@ -119,8 +129,17 @@ export async function previewToken( value: preview_token, // TODO: verify this works with zoned workers host: - worker.name && !zoneId - ? `${worker.name}.${host.split(".").slice(1).join(".")}` + worker.name && !ctx.zone + ? // hrmm this might need change as well + `${ + worker.name + // TODO: this should also probably have the env prefix + // but it doesn't appear to work yet, instead giving us the + // "There is nothing here yet" screen + // ctx.env && !ctx.legacyEnv + // ? `${ctx.env}.${worker.name}` + // : worker.name + }.${host.split(".").slice(1).join(".")}` : host, inspectorUrl, prewarmUrl, diff --git a/packages/wrangler/src/api/worker.ts b/packages/wrangler/src/api/worker.ts index e44dcc57d445..fd2300fc2469 100644 --- a/packages/wrangler/src/api/worker.ts +++ b/packages/wrangler/src/api/worker.ts @@ -16,10 +16,6 @@ export interface CfAccount { * An account ID. */ accountId: string; - /** - * A zone ID, only required if not using `workers.dev`. - */ - zoneId?: string; } /** @@ -164,6 +160,12 @@ export interface CfWorkerInit { usage_model: undefined | "bundled" | "unbound"; } +export interface CfWorkerContext { + env: string | undefined; + legacyEnv: boolean | undefined; + zone: string | undefined; +} + /** * A stub to create a Cloudflare Worker preview. * @@ -172,9 +174,10 @@ export interface CfWorkerInit { */ export async function createWorker( init: CfWorkerInit, - account: CfAccount + account: CfAccount, + ctx: CfWorkerContext ): Promise { - const token = await previewToken(account, init); + const token = await previewToken(account, init, ctx); const response = await fetch(token.prewarmUrl.href, { method: "POST" }); if (!response.ok) { // console.error("worker failed to prewarm: ", response.statusText); diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 8d50df7249e9..464b3e61aa87 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -56,6 +56,7 @@ export type DevProps = { }; env: string | undefined; legacyEnv: boolean; + zone: string | undefined; }; function Dev(props: DevProps): JSX.Element { @@ -142,6 +143,7 @@ function Dev(props: DevProps): JSX.Element { usageModel={props.usageModel} env={props.env} legacyEnv={props.legacyEnv} + zone={props.zone} /> )} @@ -192,6 +194,7 @@ function Remote(props: { usageModel: undefined | "bundled" | "unbound"; env: string | undefined; legacyEnv: boolean | undefined; + zone: string | undefined; }) { assert(props.accountId, "accountId is required"); assert(props.apiToken, "apiToken is required"); @@ -210,6 +213,7 @@ function Remote(props: { usageModel: props.usageModel, env: props.env, legacyEnv: props.legacyEnv, + zone: props.zone, }); usePreviewServer({ @@ -685,6 +689,7 @@ function useWorker(props: { usageModel: undefined | "bundled" | "unbound"; env: string | undefined; legacyEnv: boolean | undefined; + zone: string | undefined; }): CfPreviewToken | undefined { const { name, @@ -769,10 +774,14 @@ function useWorker(props: { usage_model: usageModel, }; setToken( - await createWorker(init, { - accountId, - apiToken, - }) + await createWorker( + init, + { + accountId, + apiToken, + }, + { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone } + ) ); } start().catch((err) => { @@ -795,6 +804,7 @@ function useWorker(props: { modules, props.env, props.legacyEnv, + props.zone, ]); return token; } diff --git a/packages/wrangler/src/index.tsx b/packages/wrangler/src/index.tsx index b5228938889e..60791b983dd0 100644 --- a/packages/wrangler/src/index.tsx +++ b/packages/wrangler/src/index.tsx @@ -707,6 +707,7 @@ export async function main(argv: string[]): Promise { name={getScriptName(args, config)} entry={entry} env={args.env} + zone={undefined} rules={getRules(config)} legacyEnv={isLegacyEnv(args, config)} buildCommand={config.build || {}} @@ -1151,6 +1152,7 @@ export async function main(argv: string[]): Promise { entry={entry} rules={getRules(config)} env={args.env} + zone={undefined} legacyEnv={isLegacyEnv(args, config)} buildCommand={config.build || {}} format={config.build?.upload?.format}