Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/consistent-worker-identifiers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"cf": minor
---

Use `--worker` consistently across generated `cf workers` commands

Accept `--worker` for Worker and version operations that previously required
`--worker-id` or a positional Worker identifier. Keep `--worker-id` and
`--script-name` as aliases, and retain positional input for `cf workers get`
and `cf workers delete`.

Keep the Worker flag alongside the other command options in help.
5 changes: 3 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,10 @@ Practical extensions of this rule:
`resolve.ts`, which validates the domain and calls `client.zones.list()` for
the selected account. Keep this exception contained in those global
context/resolution helpers.
- **Worker-name flag** (`--worker`, with `--script-name` as an alias) is a
- **Worker-name flag** (`--worker`, with `--script-name` and `--worker-id` as aliases) is a
generator-side product-scoped affordance, selected by
`resourceName === "workers"` in `isWorkerNameArg`. Add similar scoped checks
`resource === "workers"` in argument derivation. Generated `workers get` and
`workers delete` also accept the Worker as a positional argument. Add similar scoped checks
rather than spreading product names.
- **Product-specific hand-written commands are documented exceptions.** Three
shadow generated leaves. Two exist because the request body's real schema sits behind a sibling
Expand Down
25 changes: 17 additions & 8 deletions packages/cli/generator/plugin/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function multipartFileFlagDescription(field: MultipartField): string {
export const positionals = (d: Derived): Arg[] =>
d.args.filter((a) => a.positional);
export const options = (d: Derived): Arg[] =>
d.args.filter((a) => !a.positional);
d.args.filter((a) => !a.positional || (isPath(a) && a.isWorkerName));
export const bodyOptions = (d: Derived): Arg[] =>
d.args.filter((a) => isBody(a) && !a.positional);
export const queryArgs = (d: Derived): Arg[] =>
Expand Down Expand Up @@ -220,8 +220,10 @@ export function isZonePathParam(name: string): boolean {
return ZONE_ARG_NAMES.includes(flagName(name));
}

export const WORKER_ALIASES = ["script-name", "worker-id"];

export function isWorkerName(name: string): boolean {
return WORKER_NAME_ARGS.includes(name);
return WORKER_NAME_ARGS.includes(name) || kebab(name) === "worker-id";
}

const PAIR = "{account_or_zone}/{account_or_zone_id}";
Expand Down Expand Up @@ -264,7 +266,7 @@ export function accountOrZoneLocal(
/** The argv key that holds the value of the `{wire}` placeholder. */
export function pathParamReadKey(args: readonly Arg[], wire: string): string {
const arg = args.find(
(a) => a.origin.in === "path" && a.origin.name === wire && !a.isWorkerName
(a) => a.origin.in === "path" && a.origin.name === wire
);
return kebab(arg ? arg.name : wire);
}
Expand Down Expand Up @@ -328,10 +330,16 @@ interface Classification {
isWorkerName: boolean;
}

function classify(name: string, workers: boolean): Classification {
function classify(
name: string,
workers: boolean,
pathParam = false
): Classification {
return {
isZone: ZONE_ARG_NAMES.includes(name),
isWorkerName: workers && isWorkerName(name),
isWorkerName:
workers &&
(pathParam ? isWorkerName(name) : WORKER_NAME_ARGS.includes(name)),
};
}

Expand Down Expand Up @@ -404,7 +412,7 @@ function cfOwnedFlags(facts: {
"f",
...(facts.hasFileUpload ? ["file"] : []),
...(facts.binaryOutput ? ["text"] : []),
...(facts.workerArg ? ["script-name"] : []),
...(facts.workerArg ? ["worker", ...WORKER_ALIASES] : []),
];
}

Expand Down Expand Up @@ -729,16 +737,17 @@ function pathParams(
continue;
}
const parameter = op.pathParams.find((a) => a.name === p);
const cls = classify(flagName(p), workers);
const cls = classify(flagName(p), workers, true);
if (cls.isZone) {
names.refuseOverride({ in: "path", name: p }, "--zone");
continue;
}
const defaultValue = o.nullish("default");
const subjectPositional = p === subject;
// Keep the legacy Worker-ID positional alongside the canonical --worker.
// Defaults fill URL segments without consuming a positional slot.
const positional =
!cls.isWorkerName &&
(!cls.isWorkerName || kebab(p) === "worker-id") &&
defaultValue === undefined &&
truthy(o.nullish("positional") ?? subjectPositional);
if (cls.isWorkerName) {
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/generator/plugin/emit/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
optionDefaults,
options as optionArgs,
positionals,
WORKER_ALIASES,
} from "../args.js";
import { kebab, obj, stringify, type Value } from "../js.js";
import { type Ctx, escapeSingle } from "./index.js";
Expand Down Expand Up @@ -87,13 +88,16 @@ export function builderLines(ctx: Ctx): string[] {
option(name, [
["type", type],
["array", scalarArray ? true : undefined],
["alias", arg.isWorkerName ? "script-name" : undefined],
["alias", arg.isWorkerName ? WORKER_ALIASES : undefined],
["description", arg.description],
["choices", arg.choices],
["default", defaults.get(arg.name)],
["demandOption", demand ? true : undefined],
])
);
if (arg.isWorkerName && arg.positional) {
lines.push(`.group(${stringify(name)}, theme.brand('Options'))`);
}
// Resolve `@file` before yargs validates `choices` against the value,
// so the handler gets the file's contents without reading it twice.
if (arg.fromFile !== undefined && hasChoices(arg)) {
Expand Down
23 changes: 13 additions & 10 deletions packages/cli/generator/plugin/emit/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ export function handler(ctx: Ctx): string[] {

function prelude(ctx: Ctx): string[] {
const lines: string[] = [];
if (ctx.needsWorkerName) {
const read = ctx.firstPositionalIsWorkerName
? argvKey(kebab((positionals(ctx.derived)[0] as Arg).name))
: 'argv["worker"]';
lines.push(
` const scriptName = getWorkerName({ scriptName: ${read} });`
);
lines.push(` ${read} = scriptName;`);
}
if (ctx.hasParams) {
lines.push("");
lines.push(" const queryParams: Record<string, unknown> = {");
Expand Down Expand Up @@ -205,7 +214,7 @@ export function dryRun(ctx: Ctx): string[] {
positional,
firstPositionalIsZone: false,
accountId: "${__cfDryRunAccountId ?? '<account-id>'}",
scriptName: "${argv[\"worker\"] ?? '<worker>'}",
scriptName: "${encodeURIComponent(scriptName)}",
zone: "${argv.zone ?? argv.zoneId ?? '<zone>'}",
param,
});
Expand Down Expand Up @@ -405,15 +414,6 @@ function auth(ctx: Ctx): string[] {
lines.push(" argv.zoneId = zoneId;");
}
}
if (ctx.needsWorkerName) {
const read = ctx.firstPositionalIsWorkerName
? argvKey(kebab((positionals(ctx.derived)[0] as Arg).name))
: 'argv["worker"]';
lines.push(
` const scriptName = getWorkerName({ scriptName: ${read} });`
);
lines.push(` ${read} = scriptName;`);
}
return lines;
}

Expand Down Expand Up @@ -1133,6 +1133,9 @@ export function imports(ctx: Ctx): Imports {
}
i.from(`${lib}/output.js`, ["formatOutput"]);
i.from(`${lib}/dry-run.js`, ["formatDryRun"]);
if (positionals(d).some((arg) => arg.isWorkerName)) {
i.from(`${lib}/ui/theme.js`, ["theme"]);
}
if ((ctx.op.bodyUnions?.length ?? 0) > 0 && d.hasBodyParams) {
i.from(`${lib}/body-unions.js`, ["validateBodyUnions"]);
}
Expand Down
13 changes: 4 additions & 9 deletions packages/cli/generator/plugin/emit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
headerArgs,
isBody,
isPath,
isWorkerName,
options,
pathParamReadKey,
positionals,
Expand Down Expand Up @@ -176,10 +177,7 @@ export function substitutePath(path: string, ctx: PathTemplate): string {
if (ACCOUNT_PATH_PARAMS.includes(name)) {
return ctx.accountId;
}
if (
ctx.needsWorkerName &&
(name === "script_name" || name === "scriptName")
) {
if (ctx.needsWorkerName && isWorkerName(name)) {
return ctx.scriptName;
}
if (ZONE_PATH_PARAMS.includes(name)) {
Expand Down Expand Up @@ -253,7 +251,6 @@ export function context(
root: string
): [Ctx, string] {
const kind = outputKind(op);
const workers = resource === "workers";
const path = op.path;
const combined = hasAccountOrZoneScope(path);
const pathNeedsAccount =
Expand All @@ -272,9 +269,7 @@ export function context(
const positional = positionals(derived);
const firstPositionalIsZone = positional[0]?.isZone === true;
const needsZoneId = firstPositionalIsZone || pathNeedsZone || combined;
const pathNeedsWorker =
workers &&
(path.includes("{script_name}") || path.includes("{scriptName}"));
const pathNeedsWorker = derived.args.some((a) => isPath(a) && a.isWorkerName);
const firstPositionalIsWorkerName = positional[0]?.isWorkerName === true;
const needsWorkerName = firstPositionalIsWorkerName || pathNeedsWorker;

Expand Down Expand Up @@ -324,7 +319,7 @@ export function context(
positional,
firstPositionalIsZone,
accountId: "${accountId}",
scriptName: "${scriptName}",
scriptName: "${encodeURIComponent(scriptName)}",
zone: "${argv.zoneId}",
param,
});
Expand Down
6 changes: 2 additions & 4 deletions packages/cli/generator/plugin/emit/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ACCOUNT_PATH_PARAMS,
accountOrZoneLocal,
isBody,
isWorkerName,
pathParamReadKey,
queryArgs,
ZONE_PATH_PARAMS,
Expand Down Expand Up @@ -43,10 +44,7 @@ function pathValue(ctx: Ctx, name: string): string {
if (ZONE_PATH_PARAMS.includes(name)) {
return ctx.firstPositionalIsZone ? "zone_id" : "zoneId";
}
if (
ctx.needsWorkerName &&
(name === "script_name" || name === "scriptName")
) {
if (ctx.needsWorkerName && isWorkerName(name)) {
return "scriptName";
}
return argvKey(pathParamReadKey(ctx.derived.args, name));
Expand Down
35 changes: 6 additions & 29 deletions packages/cli/generator/plugin/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {
derive,
downgradeOptionalParents,
isBody,
isWorkerName,
isZonePathParam,
multipartFileFlagDescription,
optionDefaults,
options as optionArgs,
positionals,
scalar,
WORKER_ALIASES,
} from "./args.js";
import { requiresConfirmation } from "./confirmation.js";
import {
Expand Down Expand Up @@ -251,32 +251,6 @@ class Walk {
const positional = positionals(derived);
const options = optionArgs(derived);
const defaults = optionDefaults(derived, op);
if (resource === "workers") {
for (const p of op.pathParams) {
if (!isWorkerName(p.name.replaceAll("_", "-"))) {
continue;
}
if (options.some((a) => a.name === "worker")) {
continue;
}
options.push({
name: "worker",
description: p.description ?? `The ${p.name.replaceAll("_", " ")}`,
type: "string",
choices: undefined,
default: undefined,
required: true,
positional: false,
origin: { in: "path", name: p.name },
conflicts: undefined,
implies: undefined,
secret: false,
fromFile: undefined,
isZone: false,
isWorkerName: true,
});
}
}
const fullPath = [resource];
if (group !== undefined) {
fullPath.push(...group.split("/"));
Expand All @@ -286,7 +260,7 @@ class Walk {
const positionalUsage = positional
.map((a) => {
const name = a.type === "array" ? `${a.name}...` : a.name;
return a.required ? `<${name}>` : `[${name}]`;
return a.required && !a.isWorkerName ? `<${name}>` : `[${name}]`;
})
.join(" ");
const suffix = options.length === 0 ? "" : " [options]";
Expand All @@ -299,7 +273,7 @@ class Walk {
m["name"] = a.name;
m["position"] = i;
m["type"] = scalar(a.type);
m["required"] = a.required;
m["required"] = a.required && !a.isWorkerName;
m["description"] = a.description;
if (a.choices !== undefined && a.choices.length > 0) {
m["enum"] = a.choices;
Expand All @@ -312,6 +286,9 @@ class Walk {
m["type"] = scalar(a.type);
m["required"] = a.required;
m["description"] = a.description;
if (a.isWorkerName) {
m["alias"] = WORKER_ALIASES;
}
const dflt = defaults.get(a.name);
if (dflt !== undefined) {
m["default"] = dflt;
Expand Down
Loading
Loading