@uniflowed/config
type
TaskArgument
export type TaskArgument = {
// What `--name` and the picker call it: letters, digits, `-` and `_`.
readonly name: string,
// One line saying what it is for, shown in the picker.
readonly description?: string,
// The only values it may take. Anything else is refused, and at a terminal
// these are the list to pick from.
readonly choices?: $ReadOnlyArray<string>,
// Used when it is not given. An argument with a default is never asked for.
readonly default?: string,
// Whether leaving it out is an error. Defaults to `true` unless there is a
// `default`; an optional argument with no default must come last.
readonly required?: boolean,
};
One argument a task takes, filled by uf run <task>.
Given after the task's name, in the order declared, or as --name value; the values are appended to command in that order either way. At a terminal, a required argument that was not given is picked from choices, or typed; in CI and pipelines it is an error that names it.
type
RuntimeSpec
export type RuntimeSpec = string;
A JavaScript runtime, and optionally which release of it: "node", "node@26", "bun@1.3.5".
The names are node, bun and deno. No version is the one on PATH; a prefix is the newest release that starts with it, locked in uf.lock; a full version is exactly that release. A range or a tag is refused.
The type of runtime, build.runtime and test.runtime.
type
PackageManagerSpec
export type PackageManagerSpec = string;
A package manager, and optionally which release of it: "pnpm", "pnpm@10", "pnpm@12.0.0".
The names are npm, pnpm, yarn and bun. Yarn's edition is its major version — "yarn@1" is Classic. No version is the one on PATH; a prefix is the newest release that starts with it, locked in uf.lock; a full version is exactly that release. A range or a tag is refused.
The type of packageManager.
type
TestRunnerSpec
export type TestRunnerSpec = string;
What runs the test suite: "uf" or "bun[@version]".
"uf" is the runner built into uf, and the default; it takes no version, because it is the binary that is running. "bun" is bun test, on the Bun it names — so it also decides the test runtime when test.runtime is absent, and a test.runtime naming anything else is an error. The version follows the same grammar as a runtime's. Under a Bun runner uf test hands the files its discovery found to bun test, with uf's Flow preload, and @uniflowed/test resolves to bun:test; what Bun has no equivalent for raises UnsupportedError by name. See guide/testing, "Choosing a runner".
The type of test.runner.
type
BuilderSpec
export type BuilderSpec = string;
Which builder uf dev, uf build, uf preview and uf start drive: "vite", or a module specifier.
"vite" is @uniflowed/vite, the builder uf ships and the default. Any other string is a module specifier — a package found up node_modules, or a path starting with . or / that must stay inside the project — whose driver satisfies the contract in docs/architecture.md.
The type of build.builder.
type
PluginEntry
export type PluginEntry =
| string
| {
readonly name: string,
readonly order?: "pre" | "normal" | "post",
readonly apply?: "build" | "serve" | "always",
};
One entry of plugins: [...].
A bare name takes the default band and applies to every pipeline; the long form says otherwise. Declaration order decides within a band, so the resolved pipeline is a function of this file alone.
type
SizeBudget
export type SizeBudget = {
readonly max: number | string,
readonly metric?: "raw" | "gzip" | "brotli",
};
A ceiling uf build fails over, and what it is measured on.
max accepts a byte count or a size a person would write — "180kb" — because a budget is written by hand and read back by a report.
type
Permissions
export type Permissions = {
readonly read?: $ReadOnlyArray<string>,
readonly write?: $ReadOnlyArray<string>,
readonly net?: $ReadOnlyArray<string>,
readonly env?: $ReadOnlyArray<string>,
readonly run?: $ReadOnlyArray<string>,
};
What the project's own code may reach.
Absent from uf.config.js means no permission model: the toolchain starts its host the way it always has. Present means **deny by default** — the project's code gets what is listed and nothing else — and permissions: {} is a legitimate thing to write, meaning "nothing beyond what uf itself needs to load and transform the project". There is deliberately no way to spell "everything"; a project that wants everything does not declare a set.
Every field is a list of literal strings, because uf.config.js is read as text and parsed as JSON5 rather than executed: a computed path or a process.env read here would not parse.
The set is uf's, not a runtime's. Node.js enforces read and write, Deno enforces all five, and Bun has no permission model at all — a host that cannot enforce what is declared **refuses the run** rather than applying part of it. docs/hosts.md is the table.
The declared paths are *added to* the ones uf needs to run the project, so what a set denies is the rest of the machine — ~/.ssh, the network, the environment — and not the project's own files.
type
CoverageThresholds
export type CoverageThresholds = {
readonly lines?: number,
readonly functions?: number,
readonly branches?: number,
};
Percentages a coverage gate requires, as whole numbers between 0 and 100.
A metric nobody names is not checked, which is not the same as requiring zero: { lines: 0 } says "every line, and I mean it" in a way that reads wrong, so the absent case is the one that means nothing is required.
function
defineConfig
export function defineConfig(config: UniflowedConfig): UniflowedConfig { ... }
Identity function that pins uf.config.js to UniflowedConfig.
This is the one binding in the package that is not a native call: a config module evaluates defineConfig({...}) at its top level, so raising here would make every config file unloadable. Its whole job is to give Flow a type to check the literal against.
Without a doc comment