69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import type { VariableConfig } from "../../lib/config/schema";
|
|||
|
|
|
||
|
|
/** Splits each `name=value` pair and groups by name - repeats accumulate into an array, which is
|
||
|
|
* how a `multiselect` variable is given more than one selection on the command line. */
|
||
|
|
export function parseVarFlags(pairs: string[]): Record<string, string[]> {
|
||
|
|
const grouped: Record<string, string[]> = {};
|
||
|
|
for (const pair of pairs) {
|
||
|
|
const eq = pair.indexOf("=");
|
||
|
|
if (eq === -1) {
|
||
|
|
throw new Error(`--var ${pair} is missing '=' - expected --var name=value`);
|
||
|
|
}
|
||
|
|
const name = pair.slice(0, eq);
|
||
|
|
const value = pair.slice(eq + 1);
|
||
|
|
(grouped[name] ??= []).push(value);
|
||
|
|
}
|
||
|
|
return grouped;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Coerces raw `--var` strings into the JS type each variable expects, ready for
|
||
|
|
* `buildVariableSchema(script).safeParse(...)` - the same schema the web form and the
|
||
|
|
* `/api/scripts/:id/runs` route already validate against. Variables not present in `grouped`
|
||
|
|
* are left out entirely (the caller fills those from `defaultValuesForScript` first). */
|
||
|
|
export function coerceVariables(
|
||
|
|
variables: readonly VariableConfig[],
|
||
|
|
grouped: Record<string, string[]>,
|
||
|
|
): Record<string, unknown> {
|
||
|
|
const values: Record<string, unknown> = {};
|
||
|
|
const known = new Set(variables.map((v) => v.name));
|
||
|
|
|
||
|
|
for (const name of Object.keys(grouped)) {
|
||
|
|
if (!known.has(name)) {
|
||
|
|
throw new Error(`--var ${name}=... does not match any variable on this script`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const variable of variables) {
|
||
|
|
const raw = grouped[variable.name];
|
||
|
|
if (!raw) continue;
|
||
|
|
|
||
|
|
if (variable.type === "multiselect") {
|
||
|
|
values[variable.name] = raw;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (raw.length > 1) {
|
||
|
|
throw new Error(
|
||
|
|
`--var ${variable.name}=... was given ${raw.length} times, but '${variable.name}' is not a multiselect variable`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const value = raw[0];
|
||
|
|
|
||
|
|
if (variable.type === "boolean") {
|
||
|
|
if (value === "true") values[variable.name] = true;
|
||
|
|
else if (value === "false") values[variable.name] = false;
|
||
|
|
else throw new Error(`--var ${variable.name}=${value} must be 'true' or 'false'`);
|
||
|
|
} else if (variable.type === "number") {
|
||
|
|
const n = Number(value);
|
||
|
|
if (Number.isNaN(n)) {
|
||
|
|
throw new Error(`--var ${variable.name}=${value} is not a valid number`);
|
||
|
|
}
|
||
|
|
values[variable.name] = n;
|
||
|
|
} else {
|
||
|
|
values[variable.name] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return values;
|
||
|
|
}
|