@zuke/core
Zuke — a code-first, strongly-typed build automation system for Deno.
@zuke/core on JSR ↗ 289 symbols
Symbols
function $(strings: TemplateStringsArray, ...values: Interpolatable[]): Command Run an external command, ergonomically.
function absolutePath(first: string, ...rest: string[]): AbsolutePath Build an AbsolutePath from one or more segments. The first segment (after joining) must be absolute — start with / or a drive letter, or build from an absolute base — otherwise an error is thrown.
const root = absolutePath("/app");
root("src", "main.ts").path; // "/app/src/main.ts"
root.join("..", "shared").path; // "/shared"
absolutePath("C:\\repo", "x").path; // "C:/repo/x"interface AbsolutePath An immutable, absolute filesystem path with a fluent API.
Build one with absolutePath. The value itself is callable — path(...segments) returns a new path with those segments appended — and the equivalent AbsolutePath.join method does the same. toString() yields the path string, so an AbsolutePath can be interpolated into the $ shell helper and passed straight to tool args().
| Member | Signature | Doc |
|---|---|---|
join | join(...segments: string[]): AbsolutePath | Append path segments, returning a new path. |
parent | parent(): AbsolutePath | The parent directory; a root is its own parent. |
relativeTo | relativeTo(base: AbsolutePath | string): string | This path expressed relative to base (e.g. "src/main.ts", "../lib"). |
equals | equals(other: AbsolutePath | string): boolean | Whether other resolves to the same normalised path. |
toString | toString(): string | The normalised path string. |
path | path: string | The normalised path string (forward slashes, ./.. resolved). |
name | name: string | The final segment, e.g. "main.ts" (or "" for a root). |
stem | stem: string | The final segment without its extension, e.g. "main" (".gitignore" has none). |
extension | extension: string | The extension including the dot, e.g. ".ts" (or "" if none). |
isRoot | isRoot: boolean | Whether this path is a filesystem root ("/", "C:/"). |
interface AffectedOptions Configure ExecuteOptions.affected: the base revision and diff seam.
| Member | Signature | Doc |
|---|---|---|
base? | base?: string | The git revision to diff against. Defaults to HEAD (uncommitted changes). |
changedFiles? | changedFiles?: ChangedFilesFn | How to list changed files. Defaults to gitChangedFiles. |
function affectedTargets(order: unknown, changed: unknown): Set<TargetBuilder> Compute the set of targets in order affected by the given changed files.
order must be a valid execution order (dependencies before dependents, as produced by plan/planGraph) so each target's dependencies are already decided when it is visited. A target is affected when its own inputs cover a changed file, when it declares no inputs (unprovable — treated as affected), when a dependency is affected, or when an affected target triggers it.
class AlreadyResumedError extends Error Raised when a run has already been resumed by another process.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
class AnnounceError extends Error Raised when an announcement is run before it is fully configured.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
interface Announcement A structured announcement assembled by an AnnouncementSettings.
| Member | Signature | Doc |
|---|---|---|
text | text: string | The main message body. |
title? | title?: string | An optional heading rendered above the message. |
level | level: AnnouncementLevel | The outcome level driving the accent colour and icon. |
fields? | fields?: AnnouncementField[] | Labelled details rendered beside the message. |
link? | link?: AnnouncementLink | A clickable action rendered with the announcement. |
interface AnnouncementField A labelled detail rendered beside the message (e.g. a version or environment).
| Member | Signature | Doc |
|---|---|---|
name | name: string | The field's label. |
value | value: string | The field's value. |
type AnnouncementLevel = success | failure | warning | info The outcome an announcement conveys. It drives the accent colour and the icon prepended to the message; defaults to "info".
interface AnnouncementLink A clickable action rendered with the announcement (e.g. a link to a release).
| Member | Signature | Doc |
|---|---|---|
text | text: string | The link's visible text. |
url | url: string | The link's target URL. |
class AnnouncementSettings Fluent settings shared by every announcement: the message content (a body, an optional title, a level, repeatable detail fields and an action link), an optional display name, the webhook destination, and a fetch seam for tests. All chainers return this. Subclasses add any platform-specific configuration and render the payload.
| Member | Signature | Doc |
|---|---|---|
text | text(text: string): this | Set the main message body. |
title | title(title: string): this | Set an optional heading shown above the body. |
level | level(level: AnnouncementLevel): this | Set the outcome the message conveys (default "info"). |
success | success(): this | Shorthand for .level("success"). |
failure | failure(): this | Shorthand for .level("failure"). |
warning | warning(): this | Shorthand for .level("warning"). |
info | info(): this | Shorthand for .level("info"). |
field | field(name: string, value: string): this | Add a labelled detail rendered beside the body. Repeatable. |
link | link(text: string, url: string): this | Set an action link rendered with the message. |
username | username(name: string): this | Override the display name the message is posted under. Honoured by Slack and Discord; ignored by Teams, which has no equivalent field. |
webhook | webhook(url: string): this | Set the incoming-webhook URL to post to. The URL embeds the secret, so source it from a secret parameter. |
fetch | fetch(impl: fetch): this | The fetch implementation to use. Defaults to the global fetch; override it to unit-test without network access. |
bot | bot(): this | Post through the platform's API with a bot/access token instead of an incoming webhook. Pair with token and channel. |
token | token(token: string): this | Set the bot/access token for bot mode (Slack xoxb-…, a Discord bot token, or a Microsoft Graph bearer token). Source it from a secret parameter; Zuke masks it in CI output. Implies bot. |
channel | channel(channel: string): this | Set the channel (id or name) to post to in bot mode. |
send | send(): Promise<void> | Send the announcement: through the platform's API when bot mode was requested, otherwise by posting the payload to the webhook. |
const AnnounceTasks: AnnounceTasksApi Announcement task functions for posting build status to chat platforms.
interface AnnounceTasksApi The shape of AnnounceTasks.
| Member | Signature | Doc |
|---|---|---|
slack | slack(configure?: Configure<SlackAnnouncementSettings>): Promise<void> | Announce to Slack. Configure a SlackAnnouncementSettings: set a .webhook(url) (or .bot().token(t).channel(c) for the Web API) and the message content. |
teams | teams(configure?: Configure<TeamsAnnouncementSettings>): Promise<void> | Announce to Microsoft Teams. Configure a TeamsAnnouncementSettings: set a .webhook(url) (or .bot().token(t).team(id).channel(c) to post through Microsoft Graph) and the message content. |
discord | discord(configure?: Configure<DiscordAnnouncementSettings>): Promise<void> | Announce to Discord. Configure a DiscordAnnouncementSettings: set a .webhook(url) (or .bot().token(t).channel(c) to post through the REST API with a bot token) and the message content. |
interface AnyParameter The non-generic view of a parameter, used by discovery and resolution.
| Member | Signature | Doc |
|---|---|---|
resolve_ | resolve_(raw: string | undefined): void | Resolve from a raw input (or undefined when none was supplied). |
isSet_ | isSet_(): boolean | Whether the parameter resolved to a defined value (used by .requires()). |
stringValue_ | stringValue_(): string | undefined | The resolved value as a string, or undefined if unset (for masking). |
name_? | name_?: string | Property name, assigned during discovery. Undefined until then. |
description_? | description_?: string | Human-readable description shown in --help/--list. |
kind_ | kind_: ParamKind | The runtime value kind. |
required_ | required_: boolean | Whether a value must be supplied (no default). |
options_? | options_?: unknown | The allowed string choices, if restricted with Parameter.options. |
envName_? | envName_?: string | An explicit environment variable name override. |
hasFallback_ | hasFallback_: boolean | Whether the parameter has a declared default value. |
secret_ | secret_: boolean | Whether the value is sensitive and should be masked in CI output. |
array_ | array_: boolean | Whether the value is a comma-separated / repeatable list (.array()). |
source_? | source_?: SecretSource | A provider that resolves the value when no flag/env supplied one. |
default_? | default_?: string | The declared default rendered as a string (an array default is joined with commas), or undefined when the parameter has no default or an empty-list one. For display in tool schemas and --list; never a secret value. |
type Architecture = x86_64 | aarch64 The CPU architectures Zuke recognises.
async function archiveOutputs(outputs: unknown, host: OutputHost): Promise<Uint8Array> Archive a target's outputs into a gzipped tar of their current contents.
function assert(condition: unknown, message?: string): asserts condition Assert that condition is truthy, narrowing it for the rest of the scope. Throws an AssertionError with message otherwise.
async function assertDirectoryExists(path: PathLike): Promise<void> Assert that path exists and is a directory. Async (stats the filesystem).
function assertExists(value: T, message?: string): NonNullable<T> Assert that value is neither null nor undefined, returning it narrowed to its non-nullable type so it can be used inline.
const token = assertExists(Deno.env.get("TOKEN"), "TOKEN is required");async function assertFileExists(path: PathLike): Promise<void> Assert that path exists and is a file. Async (stats the filesystem).
class AssertionError extends Error Raised by the assertion helpers when an expectation fails.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
function assertSafeEntryName(name: string): void Reject an archive entry whose name would escape the destination directory — an absolute path or one with a .. segment (a "zip slip"). A downloaded or poisoned archive must never place files outside where it is being unpacked.
function assertSafeLinkTarget(entryName: string, target: string): void Reject a symlink whose target would resolve outside the destination directory — an absolute target, or a relative one that climbs (with ..) above the extraction root once resolved against the link's own directory. A file entry's name is bounded by assertSafeEntryName; a symlink adds a second escape vector (its target), so a poisoned tarball can't plant bin/x -> ../../etc.
async function assertWrapperConformance(makeSettings: unknown, tool: string, options: WrapperConformanceOptions): Promise<void> Assert that a tool wrapper conforms: makeSettings() spawns tool, resolves it per options.resolution, and reports a missing binary as a "./tooling.ts".ToolNotFoundError.
makeSettings is called once per check, so each check gets a pristine instance. The resolution check runs against a throwaway temp directory holding a fake node_modules/.bin/<tool> shim, with ZUKE_TOOL_RESOLUTION unset for the duration and restored afterwards; no real subprocess is ever launched.
A wrapper whose run() resolves something at run time must have that pinned inside makeSettings — () => new DockerComposeUpSettings().usePlugin(), say — or the missing-binary check would probe the ambient host. It reports a ToolNotFoundError raised for any binary other than the planted one as a failure, so such a wrapper cannot pass by accident on a host that lacks the real tool.
function box(style: Style, content: string | unknown, options?: BoxOptions): string[] A bordered panel around content (a string, split on newlines, or an array of lines). Content may carry ANSI codes; padding is measured on the visible text so the border stays flush.
interface BoxOptions Options for box.
| Member | Signature | Doc |
|---|---|---|
title? | title?: string | A title embedded in the top border. |
padding? | padding?: number | Horizontal padding inside the border, in spaces. Defaults to 1. |
width? | width?: number | Force an inner width; widened automatically to fit content and title. |
border? | border?: unknown | Styles for the border characters. Defaults to ["dim"]. |
titleStyle? | titleStyle?: unknown | Styles for the title text. Defaults to ["bold"]. |
class Build Base class for user-defined builds. Provides no targets of its own; subclasses declare targets as properties. Optionally override the lifecycle hooks.
| Member | Signature | Doc |
|---|---|---|
onStart | onStart(): void | Promise<void> | Called once before any target runs. |
onFinish | onFinish(_result: BuildResult): void | Promise<void> | Called once after the run completes (success or failure). |
onTargetStart | onTargetStart(_name: string): void | Promise<void> | Called just before a target's body executes (not for skipped/cached). |
onTargetEnd | onTargetEnd(_name: string, _status: TargetStatus): void | Promise<void> | Called after each target settles, with its final status. |
recoverWith | recoverWith(): Remediation[] | Remediations applied to every target, running after each target's own "./target.ts".TargetBuilder.recoverWith when its body fails. Override to attach a global AI fixer once instead of repeating it per target; the default is none. Both styles compose — a target's own remediations run first, then these. ``ts class CI extends Build { key = parameter("OpenAI API key").secret(); override recoverWith() { return [aiFixer((f) => f.provider("openai").apiKey(this.key))]; } lint = target().executes(() => DenoTasks.lint()); // healed globally } `` |
remoteCache | remoteCache(): RemoteCacheStore | undefined | The "./remote_cache.ts".RemoteCacheStore that shares target "./target.ts".TargetBuilder.outputs across machines. Override to declare one in code; the default is none, and — unless overridden — the executor falls back to "./remote_cache.ts".envCacheStore (the ZUKE_REMOTE_CACHE_* environment variables). Applies to targets that declare both inputs and outputs. ``ts class CI extends Build { override remoteCache() { return new HttpCacheStore({ url: this.cacheUrl.value, token: this.cacheToken.value }); } build = target().inputs("src").outputs("dist").executes(...); } `` |
stateStore | stateStore(): StateStore | undefined | The "./state/store.ts".StateStore that persists this build's run records. Override to declare one in code; the default is none, and — unless overridden — the executor falls back to the ZUKE_STATE_URL / ZUKE_STATE_DIR environment variables, then (only when the run opts into durable state) a filesystem store under <root>/.zuke/runs. ``ts class CD extends Build { override stateStore() { return new HttpStateStore({ url: this.stateUrl.value, token: this.stateToken.value }); } deploy = target().executes(async (ctx) => { await ctx.state.set({ at: "sit-7" }); }); } `` |
extraEdges | extraEdges(_targets: Map<string, TargetBuilder>): OrderingEdge[] | Extra soft ordering edges to impose on the plan, beyond the dependsOn / before / after declared on targets. Override to feed an external graph — e.g. a monorepo's dependency-graph.json — into scheduling without wiring every edge by hand. Return [before, after] pairs from the passed targets map (keyed by dotted name); each means before runs before after. Edges whose endpoints are not both in a run's execution set are ignored, and a cycle is reported with the usual friendly error. These are execution-ordering edges. Like .before() / .after(), they are *not* reflected in CI generated by cicd() — a fan-out job's needs: mirrors hard dependsOn only — so an ordering that CI must also honour has to be expressed as a dependsOn, not a soft edge. ``ts class Monorepo extends Build { web = target().executes(...); api = target().executes(...); override extraEdges(t: Map<string, Target>) { // api must build before web, per the external dependency graph. const edges: OrderingEdge[] = []; const api = t.get("api"), web = t.get("web"); if (api && web) edges.push([api, web]); return edges; } } `` |
orderWith | orderWith(_targets: Map<string, TargetBuilder>): OrderingEdge[] | Promise<OrderingEdge[]> | A lazy, per-run provider of soft ordering edges, merged with extraEdges. Unlike extraEdges — synchronous, evaluated at construction — this may be async and is evaluated when a run plans, so it can read an external source (a monorepo's dependency-graph.json, an API) to decide ordering at run time. The consumer keeps ownership of that graph; Zuke only binds it in. Return [before, after] edges over the run's targets; an edge whose endpoints are not both in the execution set is ignored, and cycles are reported with the usual friendly error. Like extraEdges, these are execution-ordering edges only: they are honoured by a run and by zuke cancel (the compensation order), but not by the static graph/--list views (which never run the provider) nor by cicd()-generated CI (whose needs: mirrors hard dependsOn). ``ts override async orderWith(t: Map<string, Target>): Promise<OrderingEdge[]> { const graph = await loadDependencyGraph(); // e.g. dependency-graph.json return graph.edges.flatMap(([before, after]) => { const from = t.get(before), to = t.get(after); return from && to ? [[from, to] as OrderingEdge] : []; }); } `` |
registry | registry(): BuildRegistry | undefined | The "./registry/registry.ts".BuildRegistry this build registers itself in (zuke register) and that a registry-backed zuke mcp server discovers pipelines from. Override to declare one in code; the default is none, and — unless overridden — the resolution falls back to the ZUKE_REGISTRY_URL / ZUKE_REGISTRY_DIR environment variables, then (for zuke register) a filesystem registry under <root>/.zuke/builds. Kept a separate concern from stateStore (a run history and a build catalog are different things), so a consumer can host a richer catalog as a plugin. ``ts class CD extends Build { override registry() { return new HttpBuildRegistry({ url: this.registryUrl.value, token: this.registryToken.value }); } } `` |
mcpIdentity | mcpIdentity(): McpIdentityHook | undefined | A per-request identity hook for zuke mcp — resolve a trusted caller from the request context (an authenticating reverse proxy's header) so a shared, multi-user server attributes each call to the real engineer rather than a client-self-reported label. When set, the resolved actor overrides --actor, the environment, and the client label for that call, and flows to the audit trail, run records, lock holders, and (for a registry-spawned build) the child's ZUKE_ACTOR; a throwing hook rejects the request before anything runs. Default: none — stdio/local use is unchanged. ``ts class ControlPlane extends Build { override mcpIdentity() { return (ctx: McpRequestContext) => { // The proxy strips any client copy of this header and injects its own. const sub = ctx.headers.get("x-forwarded-user"); if (!sub) throw new Error("no identity from proxy"); return { actor: sub, via: "oauth-proxy" }; }; } } `` |
interface BuildCache The incremental cache used by the executor to skip up-to-date targets.
| Member | Signature | Doc |
|---|---|---|
upToDate | upToDate(target: TargetBuilder): Promise<boolean> | Whether target is up-to-date: it declares inputs, their fingerprint matches the last successful run, and every declared output still exists. |
record | record(target: TargetBuilder): Promise<void> | Record target's current fingerprint after a successful run. |
save | save(): Promise<void> | Persist the store if anything changed. |
interface BuildDescriptor A versioned snapshot of one registered build. Persisted as JSON; a registry's opaque version (an ETag / content hash) drives compare-and-swap writes so two registrations racing at the same version cannot both win.
| Member | Signature | Doc |
|---|---|---|
id | id: string | Stable id of the build (its class name, unless overridden). |
name | name: string | Human-facing build name (the build class name). |
location | location: BuildLocation | Where the build lives, so a runner can launch it. |
surface | surface: CliDescription | The build's CLI surface, exactly as "../describe.ts".describeCli produces it. |
actor | actor: string | Who registered the build (a resolved actor; secrets never appear here). |
createdAt | createdAt: string | ISO-8601 timestamp when the build was first registered. |
updatedAt | updatedAt: string | ISO-8601 timestamp of the last registration write. |
type BuildLocation = unknown | unknown Where a registered build lives, so a runner can launch it. Two forms: a module (the entry file deno run executes — the form zuke register writes) or an explicit command (a launch argv, for a build fronted by a wrapper script). Both carry the working directory and, in CI, the repository.
interface BuildQuery Filters for "./registry.ts".BuildRegistry.listBuilds; all fields optional.
| Member | Signature | Doc |
|---|---|---|
name? | name?: string | Keep only builds whose name equals this. |
since? | since?: string | Keep only builds registered at or after this ISO-8601 timestamp. |
interface BuildRegistry Pluggable persistence for BuildDescriptors. version is an opaque token (an ETag or content hash) used for optimistic concurrency: a write only lands if the stored version still matches the one the writer last read, so two registrations racing at the same version cannot both win.
| Member | Signature | Doc |
|---|---|---|
getBuild | getBuild(id: string): Promise<unknown | null> | Fetch a build and its current version, or null if it is not registered. |
register | register(descriptor: BuildDescriptor, expectedVersion: string | null): Promise<PutBuildResult> | Write descriptor only if the stored version equals expectedVersion (null meaning "must not exist yet"). Returns the new version, or a conflict when the stored version has moved on — the caller re-reads and retries. |
deregister | deregister(id: string): Promise<void> | Remove a registered build by id; a missing build is not an error. |
listBuilds | listBuilds(query: BuildQuery): Promise<BuildSummary[]> | List registered builds matching query, newest first (by createdAt, then id). |
type BuildRegistryFactory = unknown A () => factory the kit calls once to obtain the registry under test.
interface BuildResult Result passed to the Build.onFinish lifecycle hook.
| Member | Signature | Doc |
|---|---|---|
ok | ok: boolean | Whether every executed target succeeded (also true for a suspended run). |
executed | executed: string[] | Names of the targets that ran, in execution order. |
error? | error?: unknown | The error that aborted the run, if any. |
suspended? | suspended?: boolean | True when the run suspended at a .waitsFor(...) gate rather than finishing — its state is saved and it can be resumed later. The process still exits 0. |
cancelled? | cancelled?: boolean | True when the run was cancelled (via options.signal / Ctrl-C, or by another process running zuke cancel) rather than failing on its own. Its compensations have run and the record is cancelled. ok is false. |
runId? | runId?: string | The run's id, when a run identity was established (always, in practice — every "./executor.ts".execute generates one). Lets the caller point a follow-up (zuke runs show, zuke cancel) at this run. |
interface BuildSummary A compact registry listing row, returned by "./registry.ts".BuildRegistry.listBuilds.
| Member | Signature | Doc |
|---|---|---|
id | id: string | The build id. |
name | name: string | The build name. |
actor | actor: string | Who last registered the build. |
createdAt | createdAt: string | ISO-8601 first-registration timestamp. |
updatedAt | updatedAt: string | ISO-8601 timestamp of the last registration write. |
interface CancelOptions Options for cancelRun.
| Member | Signature | Doc |
|---|---|---|
runId | runId: string | The id of the run to cancel. |
stateStore? | stateStore?: StateStore | false | Durable store the run lives in. Defaults to the same resolution as a normal run (explicit → stateStore() override → env → .zuke/runs); cancel always needs one. |
actor? | actor?: string | Who to attribute the cancellation to in the audit trail. |
readEnv? | readEnv?: unknown | Reads an environment variable (secrets re-resolve from here for compensations). |
silent? | silent?: boolean | Suppress progress output. |
reporter? | reporter?: Reporter | Custom reporter; overrides silent. |
also? | also?: string[] | Extra compensation target names to run first (a timed-out wait whose onTimeout names a specific compensation target routes through here). |
interface CancelResult The outcome of cancelRun.
| Member | Signature | Doc |
|---|---|---|
runId | runId: string | The run that was cancelled. |
status | status: RunStatus | The run's status after cancelling (cancelled, or the terminal status on a no-op). |
noop | noop: boolean | True when the run was already terminal and nothing was done. |
compensated | compensated: string[] | Names of compensation targets whose bodies ran. |
failures | failures: CompensationFailure[] | Compensations that threw (recorded, non-fatal). |
async function cancelRun(build: Build, options: CancelOptions): Promise<CancelResult> Cancel the run options.runId for build: transition it to cancelling (exactly one canceller drives the walk; a live owning process observes the change and aborts), run the compensations of every succeeded target in reverse order, and settle the record as cancelled. Idempotent — cancelling an already-terminal run is a friendly no-op.
type ChangedFilesFn = unknown Lists the files changed since base (a git revision), each path relative to the repository root. The seam behind ExecuteOptions.affected; defaults to gitChangedFiles and is overridable so the affected plan can be tested without a real git repository.
async function checkBuildRegistry(make: BuildRegistryFactory): Promise<ConformanceResult[]> Run the build-registry conformance scenarios against the registry make builds.
async function checkStateStore(make: StateStoreFactory, options?: ConformanceOptions): Promise<ConformanceResult[]> Run the state-store conformance scenarios against the store make builds.
function cicd(spec: CiFileSpec): CiFile Declare a CI file as a build field. Running the build regenerates it (and the generate-ci command writes it on demand), so the committed configuration is generated from code rather than hand-maintained.
The provider is the only required field: cicd({ provider: "github" }) declares a workflow at .github/workflows/ci.yml that runs the build on push/PR to main. Override only what else you need.
class MyBuild extends Build {
ci = cicd({ provider: "github" }); // sensible default workflow
// …or customise:
gitlab = cicd({ provider: "gitlab", pipeline: { jobs: [{ steps: [...] }] } });
}interface CiConcurrency A concurrency group: at most one run per group, optionally cancelling the prior one.
| Member | Signature | Doc |
|---|---|---|
group | group: string | The group key (often interpolated, e.g. ci-${{ github.ref }}). |
cancelInProgress? | cancelInProgress?: boolean | Cancel an in-progress run in the same group when a new one starts. |
class CiFile A declared CI file. Assign one (via cicd) to a build field and Zuke keeps the file on disk in sync with the definition when the build runs.
| Member | Signature | Doc |
|---|---|---|
pipelineFor | pipelineFor(targets: Map<string, TargetBuilder>): CiPipeline | The pipeline this file renders. With fan-out, the build's targets are expanded into one job per target; otherwise the declared pipeline. |
render | render(): string | Render the file's YAML content (the base pipeline; fan-out is resolved at discovery). |
provider | provider: CiProvider | The provider this file renders for. |
path | path: string | The output path. |
pipeline | pipeline: CiPipeline | The base pipeline (pipeline-level fields, and the jobs unless fanning out). |
fanOut? | fanOut?: FanOutOptions | Fan-out options, when this file expands the build's targets into jobs. |
interface CiFileSpec A CI configuration file declared on a build: a pipeline bound to a path.
| Member | Signature | Doc |
|---|---|---|
provider | provider: CiProvider | The provider to render for — the one field you must choose. |
path? | path?: string | The output path (relative to the working directory). Defaults to the provider's conventional location (.github/workflows/ci.yml, .gitlab-ci.yml, or azure-pipelines.yml). |
pipeline? | pipeline?: CiPipeline | The pipeline to render. Defaults to a single build job that runs the build. |
fanOut? | fanOut?: boolean | FanOutOptions | Fan the build's targets out into one CI job per target, wired by their dependencies (see fanOutPipeline). true uses the defaults; pass FanOutOptions to customise. When set, pipeline supplies the pipeline-level fields (name, triggers, …) and its jobs are ignored. |
function ciHost(): string A short identifier for the detected CI host, or "local" when not on CI. Recognises GitHub Actions, GitLab CI, Azure Pipelines, Bitbucket Pipelines, and the generic CI convention.
Prefer detectCiHost for new code: its values match CiProvider. This function is kept for compatibility and uses longer, host-specific names.
type CiHost = github | gitlab | azure | bitbucket | local The CI host a build is running on, or "local" when not on CI. The names match CiProvider so they compose with CI generation and per-host integrations (e.g. posting a review to the right pull-request API).
interface CiJob A job: a named unit of work with steps, optionally fanned out by a matrix.
| Member | Signature | Doc |
|---|---|---|
id? | id?: string | Stable identifier, used as the job key and as a dependency target. Defaults to "build". |
name? | name?: string | Human-readable job name. |
runsOn? | runsOn?: string | The runner. Interpreted per provider: a GitHub runner label and Azure vmImage (default ubuntu-latest), or a GitLab Docker image (runner default when omitted). Ignored when a matrix defines os on GitHub. |
needs? | needs?: string[] | Other jobs (by id) that must finish before this one. |
matrix? | matrix?: Record<string, Array<string | number>> | A build matrix: each key fans out over its values. |
env? | env?: Record<string, string> | Environment variables for the job. |
if? | if?: string | A condition gating the job. A raw provider expression: GitHub if:, Azure condition:. Ignored on GitLab. Use it to e.g. skip forked pull requests. |
timeoutMinutes? | timeoutMinutes?: number | Fail the job if it runs longer than this many minutes. |
steps? | steps?: CiStep[] | The steps to run, in order. Defaults to a single step that runs the build. |
interface CiPipeline A complete, provider-agnostic CI pipeline.
| Member | Signature | Doc |
|---|---|---|
name? | name?: string | The pipeline name. Defaults to "CI". |
triggers? | triggers?: CiTriggers | When it runs. Defaults to push and pull request on main; pass an empty object ({}) for a pipeline triggered only by external means. |
permissions? | permissions?: Record<string, string> | Workflow-level token permissions (GitHub only), e.g. { contents: "read", "pull-requests": "write" }. Ignored elsewhere. |
concurrency? | concurrency?: CiConcurrency | Limit concurrent runs (GitHub only). Ignored elsewhere. |
jobs? | jobs?: CiJob[] | The jobs to run. Defaults to a single build job that runs the build. |
type CiProvider = github | gitlab | azure | bitbucket The CI providers generateCi can target.
interface CiStep A single step in a job.
| Member | Signature | Doc |
|---|---|---|
name? | name?: string | Human-readable step name. |
run? | run?: string | A shell command to run. Portable across all providers. |
uses? | uses?: string | A GitHub Action reference (e.g. actions/checkout@v4). Rendered only for GitHub; skipped for GitLab and Azure. |
with? | with?: Record<string, string> | Inputs for a uses Action (GitHub only). |
env? | env?: Record<string, string> | Environment variables for this step. Rendered as env: on GitHub Actions and on Azure Pipelines script steps; ignored on GitLab (which sources variables from project settings, not the job YAML). |
interface CiTriggers When the pipeline runs.
| Member | Signature | Doc |
|---|---|---|
push? | push?: string[] | Branches whose pushes trigger the pipeline. An empty array means every branch (no filter); omit the field to disable the push trigger. |
pullRequest? | pullRequest?: string[] | Branches whose pull/merge requests trigger the pipeline. An empty array means every branch (no filter); omit the field to disable the trigger. |
manual? | manual?: boolean | Allow manual runs (workflow dispatch / web). |
schedule? | schedule?: ScheduleEntry[] | Timezone-aware scheduled runs. Each entry is a 5-field cron in an optional IANA timezone ({ cron: "30 9 * * 1-5", tz: "Europe/Sofia" }). Fully supported on GitHub (compiled to UTC crons, with a generated guard step for daylight-saving zones) and Azure (native schedules:, UTC/fixed offset only); ignored on GitLab and Bitbucket, whose schedules are configured in the provider UI, not in-file. See "./ci_schedule.ts". |
interface CliCommandInfo A reserved command (graph, generate-ci, completions).
| Member | Signature | Doc |
|---|---|---|
name | name: string | The command word. |
description | description: string | One-line summary. |
interface CliDescription A build's full CLI surface, suitable for JSON serialization.
| Member | Signature | Doc |
|---|---|---|
commands | commands: CliCommandInfo[] | The reserved positional commands. |
flags | flags: CliFlagInfo[] | The built-in option flags. |
targets | targets: CliTargetInfo[] | The build's targets, in declaration order. |
parameters | parameters: CliParameterInfo[] | The build's declared parameters, in declaration order. |
interface CliFlagInfo A built-in option flag.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The flag, with leading dashes. |
description | description: string | One-line summary. |
interface CliParameterInfo A parameter declared on the build.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The parameter's property name — the key an MCP tool call and execute's params map use (e.g. skipE2e). Distinct from flag, which is its kebab-case form. |
flag | flag: string | The CLI flag (without leading dashes), e.g. skip-e2e. |
description | description: string | The parameter's description, or "" when none was set. |
required | required: boolean | Whether a value is required. |
kind | kind: string | number | boolean | The parameter's value kind. |
boolean | boolean: boolean | Whether the flag is a value-less boolean. |
array | array: boolean | Whether repeated flags accumulate into a list. |
options | options: string[] | The allowed values, when the parameter is constrained to a set. |
default? | default?: string | The declared default rendered as a string, when the parameter has one. |
interface CliTargetInfo A target declared on the build.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The target's name (its field name on the build). |
description | description: string | The target's description, or "" when none was set. |
dependsOn | dependsOn: string[] | The names of its direct dependencies, in declaration order. |
default | default: boolean | Whether this is the conventional default target. |
unlisted | unlisted: boolean | Whether the target is hidden from --list (still runnable by name). |
class Command A lazily-executed command. Built by the $ tagged template. The process does not start until the command is awaited or a terminal method (text, lines, code) is called; the result is memoised so repeated reads are cheap.
| Member | Signature | Doc |
|---|---|---|
env | env(record: Record<string, string>): this | Merge additional environment variables. |
cwd | cwd(path: PathLike): this | Set the working directory for the process. |
noThrow | noThrow(): this | Do not throw on a non-zero exit; combine with code. |
quiet | quiet(): this | Suppress live stdout/stderr streaming to the terminal. |
killAfter | killAfter(ms: number): this | Kill the process if it runs longer than ms milliseconds, raising a CommandTimeoutError. Fires even under noThrow. |
maxCapturedBytes | maxCapturedBytes(bytes: number): this | Cap how much of each captured stream is kept in memory, in bytes (default 8 MiB). Capture keeps the newest bytes: once the cap is reached the oldest are dropped, CommandOutput.truncated is set, and CommandOutput.text prefixes a notice. Raise it for a command whose whole output you must parse; lower it to bound a chatty one. Live streaming to the terminal is never capped — every byte still reaches it. |
signal | signal(signal: AbortSignal): this | Terminate the process (via SIGTERM) when signal aborts — for example when the enclosing run is cancelled. Overrides the executor's ambient run signal for this command. Composes with killAfter: either the timeout or the abort kills the process, whichever fires first. |
commandLine | commandLine(): string | The command line, for diagnostics — argv joined by spaces, with the resolved value of every secret parameter of the enclosing run masked. This is the only rendered form of the command (the echo under --dry-run, a CommandError message), so a secret passed as an argv token cannot leak through one. The argv given to the operating system is unchanged. |
then | then(onfulfilled?: unknown | null, onrejected?: unknown | null): PromiseLike<TResult1 | TResult2> | Await support: run the command and resolve to a CommandOutput. |
text | text(): Promise<string> | Run and resolve to trimmed stdout — prefixed with a truncation notice if the capture cap was hit. Throws on non-zero unless noThrow. |
lines | lines(): Promise<string[]> | Run and resolve to stdout split into lines (trailing blank dropped). |
code | code(): Promise<number> | Run and resolve to the numeric exit code. Never throws on non-zero. |
spawn | spawn(): SpawnedProcess | Start the command as a long-lived process without waiting for it to exit, returning a SpawnedProcess handle. Use this for a service — a dev server, a database, docker compose up — that must keep running while other targets execute; stop it with SpawnedProcess.stop. stdout/stderr are inherited so the process's output is visible. |
class CommandError extends Error Raised when a command exits non-zero and throwing was not suppressed.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
class CommandOutput The resolved result of a command, available when awaiting a Command.
| Member | Signature | Doc |
|---|---|---|
text | text(): string | Trimmed stdout, prefixed with a one-line notice when truncated — so a caller reading the output cannot mistake a tail for the whole of it. |
class CommandTimeoutError extends Error Raised when a command is killed for exceeding its Command.killAfter budget. Thrown regardless of Command.noThrow, since a timeout is a distinct, exceptional outcome from a normal non-zero exit.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
interface CompensationFailure A compensation that threw during the cancel walk (recorded, non-fatal).
| Member | Signature | Doc |
|---|---|---|
target | target: string | The compensation target that failed. |
forTarget | forTarget: string | The original target whose compensation this was. |
error | error: string | The failure message. |
type Condition = unknown A predicate gating whether a target runs; may be synchronous or async.
const CONFIG_FILE: zuke.json The Zuke config file name; its presence marks a repository root.
type Configure = unknown A lambda that configures a settings instance and returns it.
interface ConformanceCliDeps Injectable dependencies for runConformanceCli (tests override them).
| Member | Signature | Doc |
|---|---|---|
makeStateStore? | makeStateStore?: unknown | Build the StateStore for a url/token (default HttpStateStore). |
makeBuildRegistry? | makeBuildRegistry?: unknown | Build the BuildRegistry for a url/token (default HttpBuildRegistry). |
log? | log?: unknown | Emit a line of output (default console.log). |
interface ConformanceOptions Tuning options for the conformance scenarios.
| Member | Signature | Doc |
|---|---|---|
lockTtlMs? | lockTtlMs?: number | The lock TTL (ms) the takeover scenario acquires with; it then waits a bit longer than this for the lock to expire. Raise it for a slow backend. Default 200. |
interface ConformanceResult The outcome of one conformance scenario.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The scenario's name. |
ok | ok: boolean | Whether the backend satisfied it. |
error? | error?: string | The failure detail when ok is false. |
interface CopyOptions Options for FileTasksApi.copy.
| Member | Signature | Doc |
|---|---|---|
overwrite? | overwrite?: boolean | Overwrite an existing destination file (default true). |
interface CreateDirectoryOptions Options for FileTasksApi.createDirectory.
| Member | Signature | Doc |
|---|---|---|
recursive? | recursive?: boolean | Create parent directories as needed (default true). |
async function createTarGzip(files: PathLike[], dest: PathLike, options?: unknown): Promise<void> Read files (relative to cwd), pack them into a tar archive named by their path relative to cwd, gzip it, and write the result to dest.
const DEFAULT_POLL_INTERVAL_MS: 200 How often ServiceBuilder.readyWhen is polled while waiting.
const DEFAULT_READY_TIMEOUT_MS: 30000 The default time a service is given to become ready before it fails.
const DEFAULT_TOOLS_DIR: .zuke/tools The default directory a Toolchain (and ToolTasks) installs into.
const defaultRenderer: Renderer The built-in renderer: Zuke's ruled headers and summary table.
const defaultStateHost: StateHost The real, Deno-backed StateHost.
function defineTool(tool: string, options?: DefineToolOptions): ToolTask Define a fluent task for a CLI that has no dedicated @zuke wrapper. Returns a task that runs the tool, configured through a DynamicToolSettings lambda — the same settings-lambda style as the built-in wrappers, with arg/flag/option for argv and the shared cwd/env/noThrow/… chainers.
import { defineTool } from "jsr:@zuke/core/tooling";
const terraform = defineTool("terraform");
await terraform((s) => s.arg("plan").option("out", "plan.tfplan"));
// → terraform plan --out plan.tfplan
const helmUpgrade = defineTool("helm", { subcommand: "upgrade" });
await helmUpgrade((s) => s.arg("api", "./chart").flag("install"));
// → helm upgrade api ./chart --installinterface DefineToolOptions Options for defineTool.
| Member | Signature | Doc |
|---|---|---|
subcommand? | subcommand?: string | string[] | Leading subcommand token(s) prepended to every invocation. |
function describeCli(build: Build, options?: DescribeCliOptions): CliDescription Describe a build's full CLI surface — reserved commands, option flags, targets (with descriptions and dependencies), and declared parameters — as a plain object ready for JSON. This is the same data zuke --list --json prints, made available to tooling and agents that introspect a build in code.
import { describeCli } from "jsr:@zuke/core";
const surface = describeCli(new MyBuild());
console.log(surface.targets.map((t) => t.name));
Pass { omitSecrets: true } to drop .secret() parameters from the result — the posture the build registry uses, so a secret never becomes a spawnable MCP input or crosses the run boundary (zuke register writes this form).
interface DescribeCliOptions Options for describeCli.
| Member | Signature | Doc |
|---|---|---|
omitSecrets? | omitSecrets?: boolean | Drop .secret() parameters from the surface (used for registry descriptors). |
function detectCiHost(env?: unknown): CiHost Detect the CI host from the environment. Recognises GitHub Actions (GITHUB_ACTIONS), GitLab CI (GITLAB_CI), Azure Pipelines (TF_BUILD), and Bitbucket Pipelines (BITBUCKET_BUILD_NUMBER); anything else is "local". The reader is injectable so detection can be unit-tested hermetically.
function detectWidth(): number Read the terminal width if available, clamped to a sane range.
class DiscordAnnouncementSettings extends AnnouncementSettings Fluent settings for AnnounceTasksApi.discord. Bot mode (.bot().token(t).channel(c)) posts through the REST API with a bot token.
function discoverGroups(build: Build): Map<string, Group> Discover all parallel Group batches declared on a build instance, binding each its property path (for labelling, e.g. in the graph). Groups that are not assigned to a build property simply stay unnamed.
function discoverParameters(build: object): Map<string, AnyParameter> Discover all parameters declared on a build instance: scan its fields (recursing into plain-object component bundles) for Parameter values, bind each its dotted property path, and return a name → parameter map preserving declaration order.
function discoverTargets(build: Build): Map<string, TargetBuilder> Discover all targets declared on a build instance.
Scans the instance's fields (recursing into plain-object component bundles) for TargetBuilder values, assigns each its dotted property path, and returns a name → target map preserving declaration order.
type DownloadFn = unknown A download function: fetch url into the file at dest.
class DynamicToolSettings extends ToolSettings Fluent settings for a defineTool tool: build the argv with DynamicToolSettings.arg/DynamicToolSettings.flag/DynamicToolSettings.option (in call order), plus all the shared chainers (cwd, env, noThrow, quiet, toolPath, args).
| Member | Signature | Doc |
|---|---|---|
arg | arg(...values: Array<string | number>): this | Append raw positional/argument tokens. |
flag | flag(name: string): this | Append a boolean flag, e.g. flag("verbose") → --verbose (or -v). |
option | option(name: string, value: string | number): this | Append a flag and its value as two tokens, e.g. --output dist. |
function envBuildRegistry(readEnv: unknown, host: StateHost): BuildRegistry | undefined Resolve a BuildRegistry from the environment, or undefined when none is configured. ZUKE_REGISTRY_URL (with an optional ZUKE_REGISTRY_TOKEN) selects an HttpBuildRegistry; otherwise ZUKE_REGISTRY_DIR selects a FileSystemBuildRegistry.
function envCacheStore(readEnv: unknown): RemoteCacheStore | undefined Resolve a RemoteCacheStore from the environment, or undefined when none is configured. ZUKE_REMOTE_CACHE_URL (with an optional ZUKE_REMOTE_CACHE_TOKEN) selects an HttpCacheStore; otherwise ZUKE_REMOTE_CACHE_DIR selects a FileSystemCacheStore.
function envStateStore(readEnv: unknown, host: StateHost): StateStore | undefined Resolve a StateStore from the environment, or undefined when none is configured. ZUKE_STATE_URL (with an optional ZUKE_STATE_TOKEN) selects an HttpStateStore; otherwise ZUKE_STATE_DIR selects a FileSystemStateStore.
function envVarName(name: string): string The environment variable for a parameter: its path in SCREAMING_SNAKE_CASE.
function execSecret(configure: Configure<ExecSecretSettings>): SecretSource A SecretSource that runs a command and takes its standard output as the secret value. Configure it through an ExecSecretSettings lambda.
parameter("Vault token").secret().from(
execSecret((s) => s.command("vault").arg("kv", "get", "-field=token", "secret/ci")),
);class ExecSecretSettings Fluent settings for execSecret: a command whose standard output is the secret. Configure the binary with ExecSecretSettings.command, arguments with ExecSecretSettings.arg, and optionally the environment and working directory. Output is trimmed of surrounding whitespace unless ExecSecretSettings.trim is turned off (some values are whitespace-sensitive).
| Member | Signature | Doc |
|---|---|---|
command | command(binary: PathLike): this | The binary to run (e.g. op, vault, gcloud). Required. |
arg | arg(...values: Array<string | number | AbsolutePath>): this | Append one or more arguments to the command. |
env | env(record: Record<string, string>): this | Merge additional environment variables for the process. |
cwd | cwd(path: PathLike): this | Set the working directory for the process. |
trim | trim(on?: boolean): this | Whether to trim surrounding whitespace from stdout (default true). |
resolve_ | resolve_(): Promise<string> | Run the command and return its captured stdout as the secret. Streaming is suppressed (quiet) so the value is never echoed to the terminal, and a non-zero exit throws a SecretError naming the command. |
async function execute(build: Build, root: TargetBuilder, options?: ExecuteOptions): Promise<BuildResult> Execute the requested target and its transitive dependencies.
Runs the build's onStart/onFinish lifecycle hooks around the plan. By default targets run sequentially in deterministic order; with parallel, independent targets run concurrently while dependencies still complete first. Stops launching after the first failure, marks unreached targets as skipped, and returns a failing result.
interface ExecuteOptions Options for execute.
| Member | Signature | Doc |
|---|---|---|
silent? | silent?: boolean | Suppress all banner/summary output (used by tests). |
reporter? | reporter?: Reporter | Custom reporter; overrides silent. |
plugins? | plugins?: Plugin[] | Lifecycle observers invoked alongside the build's own hooks, in order. Lets third-party packages report/time/notify without subclassing the build. |
skip? | skip?: string[] | Target names to skip even if they appear in the plan (CLI --skip). |
parallel? | parallel?: boolean | number | Run independent targets concurrently. false/omitted runs sequentially in deterministic order; true uses the host's CPU count; a number sets the maximum concurrency. Dependencies still complete before their dependents. |
cache? | cache?: boolean | BuildCache | Incremental caching: skip targets whose declared TargetBuilder.inputs are unchanged since the last successful run (and whose outputs still exist). Defaults to on; pass false to disable (CLI --no-cache). A BuildCache may be supplied directly (used in tests). |
remoteCache? | remoteCache?: RemoteCacheStore | false | A RemoteCacheStore that shares target TargetBuilder.outputs across machines: a local cache miss restores outputs from it, and a successful run uploads them. false disables it (CLI --no-remote-cache). When omitted, the build's remoteCache() override is used, falling back to the ZUKE_REMOTE_CACHE_* environment variables. Ignored when cache is a supplied BuildCache or is false. |
params? | params?: Record<string, string> | Raw parameter values from the command line, keyed by parameter (property) name. Each declared Parameter is resolved from this map, then the environment, then its declared default before any target runs. |
readEnv? | readEnv?: unknown | Reads an environment variable as a parameter fallback. Defaults to Deno.env.get (returning undefined when env access is unavailable); overridable so parameter resolution can be tested hermetically. |
prompt? | prompt?: unknown | Prompt for a missing required parameter, returning the entered value (or undefined to leave it unset). Defaults to an interactive terminal prompt when stdin is a TTY and the build is not on CI; overridable for testing. |
dryRun? | dryRun?: boolean | Plan only: resolve and print every target that *would* run (honouring --skip and onlyWhen conditions) without executing any body or touching the cache (CLI --dry-run). |
affected? | affected?: AffectedOptions | Restrict the run to the targets affected by files changed since a base git revision (CLI --affected[=<base>]). A target is affected when a changed file falls inside its declared TargetBuilder.inputs or a dependency is affected; a target that declares no inputs is always considered affected. Unaffected targets are skipped. The base revision defaults to HEAD; supply changedFiles to inject the diff (used in tests). |
github? | github?: boolean | Force GitHub Actions output formatting on or off. Auto-detected from the GITHUB_ACTIONS environment variable when omitted. |
color? | color?: boolean | Force ANSI colour on or off. Auto-detected (a TTY with NO_COLOR unset, outside GitHub Actions) when omitted; off by default with a custom reporter. |
renderer? | renderer?: Renderer | Renderer for the per-target banners and the end-of-build summary. Defaults to Zuke's built-in "./renderer.ts".defaultRenderer; @zuke/console exports an alternative a build can inject to restyle its output. |
signal? | signal?: AbortSignal | Cancel the run when this signal aborts (wired to Ctrl-C/SIGTERM by the CLI, or fired by another process running zuke cancel). Every target body's "./target.ts".TargetContext signal mirrors it, and it is applied as the shell's ambient default so an in-flight $ command is terminated (SIGTERM) on cancellation. When the run is cancelled, the compensations of every target that had succeeded run in reverse order (see "./target.ts".TargetBuilder.onCancel) and the result is a non-ok cancelled outcome. A body that ignores its signal still runs to completion, so promptly-cancellable work should pass ctx.signal to its shell commands. |
stateStore? | stateStore?: StateStore | false | Durable run state (see "./state/store.ts".StateStore). A supplied store is used directly; false disables state entirely. When omitted, the build's stateStore() override is used, falling back to ZUKE_STATE_URL / ZUKE_STATE_DIR, and finally — only when state is set — a filesystem store under <root>/.zuke/runs. |
state? | state?: boolean | Opt a plain build into durable state (CLI --state): fall back to a .zuke/runs filesystem store when nothing else is configured. Ignored when a store is resolved from stateStore, the build, or the environment. |
actor? | actor?: string | Who to attribute the run to in its state record (CLI --actor). Falls back to ZUKE_ACTOR, then the CI actor, then "anonymous". |
resume? | resume?: ResumeState | Continue a suspended run instead of starting a fresh one. Set by "./resume.ts".resumeRun after it has transitioned the run to running; carries the existing record, its store version, and the targets already succeeded (which are not re-run). Not for direct use — call resumeRun. |
function executionSet(root: TargetBuilder): Set<TargetBuilder> Compute the execution set for a requested target: the target plus the transitive closure of its hard dependencies.
function externalSignal(name: string): WaitTrigger A trigger satisfied when a signal named name has been delivered to the run (via zuke resume <id> --signal <name>). The signal's payload is exposed to target bodies through "./target.ts".TargetContext signals.
interface ExtractOptions Options common to extractTarGzip and extractZip.
| Member | Signature | Doc |
|---|---|---|
strip? | strip?: number | Drop this many leading path components from every entry (like tar's --strip-components). An entry left with no path — e.g. the archive's single top-level directory — is skipped. Defaults to 0. Use 1 to unpack a release tarball that wraps everything in a tool-v1.2.3/ directory. |
async function extractTarGzip(src: PathLike, destDir: PathLike, options?: ExtractOptions): Promise<void> Read the .tar.gz at src, gunzip and unpack it, and write each entry under destDir (creating parent directories as needed). Symlink entries are recreated as symlinks and directory entries as directories; pass ExtractOptions.strip to drop leading path components.
async function extractZip(src: PathLike, destDir: PathLike, options?: ExtractOptions): Promise<void> Read the .zip at src, unpack it, and write each entry under destDir (creating parent directories as needed) — the zip counterpart of extractTarGzip. Entry names are validated so a malicious archive cannot escape destDir.
function fail(message: string): never Throw an AssertionError with message. Never returns.
interface FanOutOptions Options for fanOutPipeline: how a build's targets become parallel CI jobs.
| Member | Signature | Doc |
|---|---|---|
command? | command?: unknown | The command a job runs for its target, given the target name. Defaults to the ./zuke <target> launcher (which bootstraps Deno). Each job runs only its own target; its dependencies run in their own jobs and are shared via the remote cache, so pair fan-out with one. |
setupSteps? | setupSteps?: CiStep[] | Steps prepended to every job — checkout, tool setup, cache restore. Defaults to a single actions/checkout (rendered on GitHub; GitLab and Azure check out automatically). Provide env for ZUKE_REMOTE_CACHE_* here or via env. |
runsOn? | runsOn?: string | The runner for every job (see CiJob.runsOn). |
includeUnlisted? | includeUnlisted?: boolean | Include targets hidden from --list via .unlisted(). Defaults to false. |
env? | env?: Record<string, string> | Environment variables set on every job (e.g. the remote-cache config). |
function fanOutPipeline(targets: Map<string, TargetBuilder>, base?: CiPipeline, options?: FanOutOptions): CiPipeline Expand a build's target graph into a fanned-out pipeline: one CI job per runnable target, wired together with needs: edges that mirror the targets' dependsOn dependencies — so independent targets run in parallel and a target's job waits for its prerequisites. Each job runs just its own target; upstream outputs are shared through the remote cache, so configure one (e.g. ZUKE_REMOTE_CACHE_* on the jobs) to avoid rebuilding dependencies in every job.
base contributes the pipeline-level fields (name, triggers, permissions, concurrency); its jobs are ignored in favour of the generated ones. Targets with no body, and (unless FanOutOptions.includeUnlisted) unlisted targets, are omitted, and needs edges to omitted targets are dropped.
function fileSecret(configure: Configure<FileSecretSettings>): SecretSource A SecretSource that reads a file and takes its content as the secret value — for a mounted Kubernetes/Docker secret or a CI-provided file. Configure it through a FileSecretSettings lambda.
parameter("Registry password").secret().from(
fileSecret((s) => s.path("/run/secrets/registry_password")),
);class FileSecretSettings Fluent settings for fileSecret: read a secret from a file. Set the path with FileSecretSettings.path; the content is trimmed of surrounding whitespace unless FileSecretSettings.trim is turned off.
| Member | Signature | Doc |
|---|---|---|
path | path(path: PathLike): this | The file to read the secret from. Required. |
trim | trim(on?: boolean): this | Whether to trim surrounding whitespace from the content (default true). |
resolve_ | resolve_(): Promise<string> | Read the file and return its content as the secret. A missing or unreadable file throws a SecretError naming the path. |
class FileSystemBuildRegistry A BuildRegistry that writes one <id>.json file per build under a directory.
Security. dir is *trusted configuration* — the location you choose to store the build catalog (from ZUKE_REGISTRY_DIR or an explicit registry), the same posture as "../state/fs_store.ts".FileSystemStateStore. The only untrusted value that reaches a path is the build id, validated at every point a path is built, so a traversal cannot be smuggled in via an id.
| Member | Signature | Doc |
|---|---|---|
getBuild | getBuild(id: string): Promise<unknown | null> | Fetch a build and the content-hash version of its stored file. |
register | register(descriptor: BuildDescriptor, expectedVersion: string | null): Promise<PutBuildResult> | Publish descriptor under an exclusive lock, guarding the expected version. |
deregister | deregister(id: string): Promise<void> | Remove a registered build under an exclusive lock; a missing file is a no-op. |
listBuilds | listBuilds(query: BuildQuery): Promise<BuildSummary[]> | List builds matching query, newest first. Unreadable files are skipped. |
class FileSystemCacheStore A RemoteCacheStore backed by a shared or mounted directory.
| Member | Signature | Doc |
|---|---|---|
get | get(key: string): Promise<Uint8Array | null> | Fetch the archived outputs stored under key, or null if there are none. |
put | put(key: string, artifact: Uint8Array): Promise<void> | Store artifact (a gzipped tar of a target's outputs) under key. |
class FileSystemStateStore A StateStore that writes one <id>.json file per run under a directory.
Security. dir is *trusted configuration* — the location you choose to store run state (from ZUKE_STATE_DIR, --state, or an explicit store), the same posture as "../remote_cache.ts".FileSystemCacheStore. The only untrusted value that reaches a path is the run id, which is validated at every point a path is built, so a traversal cannot be smuggled in through an id.
| Member | Signature | Doc |
|---|---|---|
getRun | getRun(id: string): Promise<unknown | null> | Fetch a run and the content-hash version of its stored file. |
putRun | putRun(record: RunRecord, expectedVersion: string | null): Promise<PutResult> | Publish record under an exclusive lock, guarding the expected version. |
listRuns | listRuns(query: RunQuery): Promise<RunSummary[]> | List runs matching query, newest first. Unreadable files are skipped. |
deleteRun | deleteRun(id: string): Promise<void> | Delete a run's file (under its lock); a missing run is a no-op. |
acquireLock | acquireLock(key: string, holder: LockHolder, ttlMs: number): Promise<LockResult> | Atomically acquire the lock key for holder, taking over if expired. |
renewLock | renewLock(key: string, token: string, ttlMs: number): Promise<boolean> | Extend the lock key held under token; false if the token lost it. |
releaseLock | releaseLock(key: string, token: string): Promise<void> | Release the lock key if still held under token; a no-op otherwise. |
const FileTasks: FileTasksApi Filesystem task functions for build scripts.
interface FileTasksApi The shape of FileTasks.
| Member | Signature | Doc |
|---|---|---|
exists | exists(path: PathLike): Promise<boolean> | Whether path exists. |
homeDirectory | homeDirectory(): string | The current user's home directory, read from $HOME (falling back to $USERPROFILE on Windows). Throws a clear error when neither is set or environment access is unavailable, so callers get a path or a useful failure — never an undefined to thread through. |
createDirectory | createDirectory(path: PathLike, options?: CreateDirectoryOptions): Promise<void> | Create the directory at path. Creates parents by default (CreateDirectoryOptions.recursive); a recursive create is a no-op when the directory already exists. |
cleanDirectory | cleanDirectory(path: PathLike): Promise<void> | Remove everything inside the directory at path, leaving an empty directory. A no-op if path does not exist (it is *not* created). |
remove | remove(path: PathLike, options?: RemoveOptions): Promise<boolean> | Remove path, tolerating a missing target the way rm -f does: a NotFound resolves to false instead of throwing. Any other error (e.g. a non-empty directory removed without RemoveOptions.recursive) is rethrown. |
copy | copy(source: PathLike, destination: PathLike, options?: CopyOptions): Promise<void> | Copy a file or directory tree from source to destination (directories are copied recursively). |
move | move(source: PathLike, destination: PathLike): Promise<void> | Move (rename) source to destination. |
readText | readText(path: PathLike): Promise<string> | Read the UTF-8 text content of the file at path. |
writeText | writeText(path: PathLike, content: string): Promise<void> | Write content to the file at path, creating or truncating it. |
readJson | readJson(path: PathLike): Promise<T> | Read and parse the JSON file at path. |
function findCycle(targets: Map<string, TargetBuilder>): string[] | null Detect a cycle in the hard-dependency (dependsOn) graph across all targets.
type ForEachFactory = unknown Builds one item's ordered pipeline of sub-targets for TargetBuilder.forEach. The returned record's keys are stage names and its values are targets; each stage implicitly depends on the one declared before it, so an item's stages run in insertion order.
interface ForEachItem One materialised fan-out item: a unique label plus its pipeline stages.
| Member | Signature | Doc |
|---|---|---|
key | key: string | A label unique within the fan-out, used to name the item's sub-targets. |
stages | stages: Record<string, TargetBuilder> | The item's ordered pipeline stages, keyed by stage name. |
class ForEachSettings Fluent configuration for TargetBuilder.forEach, in the settings-lambda style: .forEach(items, factory, (s) => s.concurrency(3).continueOnItemFailure()). Sets the concurrency cap and whether one item's failure isolates it or stops the whole batch.
| Member | Signature | Doc |
|---|---|---|
concurrency | concurrency(limit: number): this | Cap how many item pipelines run concurrently (default: the host CPU count). Clamped to at least 1; 1 runs items one at a time. |
continueOnItemFailure | continueOnItemFailure(on?: boolean): this | Keep running the other items when one item's pipeline fails (the failed item's later stages are still skipped). The fan-out target still fails at the end if any item failed. Without this, the first item failure stops the batch — the default. |
concurrency_? | concurrency_?: number | Max item pipelines in flight at once; set by concurrency. |
continueOnItemFailure_ | continueOnItemFailure_: boolean | Isolate a failed item from its siblings; set by continueOnItemFailure. |
interface ForEachSpec The internal fan-out spec stored by TargetBuilder.forEach. Its ForEachSpec.materialize closure captures the item type, so the runtime list and factory are erased to concrete ForEachItems the executor can run without knowing the item type.
| Member | Signature | Doc |
|---|---|---|
materialize | materialize: unknown | Produce the per-item sub-target pipelines from the runtime list. |
configure? | configure?: Configure<ForEachSettings> | Optional fan-out settings (concurrency, per-item failure isolation). |
function formatDuration(ms: number): string Format a duration in milliseconds as 1.2s.
function generateCi(pipeline: CiPipeline, provider: CiProvider): string Render pipeline as the YAML configuration for provider: .github/workflows/*.yml, .gitlab-ci.yml, azure-pipelines.yml, or bitbucket-pipelines.yml. The pipeline may be empty ({}) to accept every default.
async function gitChangedFiles(base?: string, run?: unknown): Promise<string[]> List the files changed since base (default HEAD) via git: tracked changes versus base plus untracked files not covered by .gitignore. run invokes git and returns stdout (defaults to a real git subprocess); override it to test without a repository.
async function glob(pattern: string, options?: GlobOptions): Promise<string[]> Expand a glob pattern to the matching paths, relative to cwd, sorted for determinism. The walk starts at the pattern's static prefix, so anchor patterns (e.g. src/**\/*.ts) to avoid scanning the whole tree. Symlinked directories are not followed.
interface GlobOptions Options for glob.
| Member | Signature | Doc |
|---|---|---|
cwd? | cwd?: string | Directory to resolve the pattern against (default: Deno.cwd()). |
function globToRegExp(pattern: string): RegExp Compile a glob pattern into an anchored RegExp that matches a full path. Exposed (and pure) for testing and custom matching.
class GraphError extends Error Raised when the build graph is invalid (cycle or unknown dependency).
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
function group(): Group Create a parallel Group. Targets join it with TargetBuilder.partOf, and a downstream target can depend on the whole batch by passing the group to TargetBuilder.dependsOn.
checks = group();
lint = target().partOf(this.checks).executes(...);
format = target().partOf(this.checks).executes(...);
deploy = target().dependsOn(this.checks).executes(...);class Group A parallel batch of targets, created with group. Targets join it via TargetBuilder.partOf; its members run concurrently with one another (each still awaiting its own dependencies) regardless of the global parallel setting. Passing a group to TargetBuilder.dependsOn depends on every member at once.
| Member | Signature | Doc |
|---|---|---|
members_ | members_: TargetBuilder[] | Members that declared themselves part of this group, in declaration order. |
name_? | name_?: string | Property name, assigned during discovery. Undefined until then. |
async function gunzip(data: Uint8Array): Promise<Uint8Array> Gunzip-decompress data using the platform DecompressionStream.
async function gzip(data: Uint8Array): Promise<Uint8Array> Gzip-compress data using the platform CompressionStream.
function hostPlatform(): Platform The current host's Platform (from Deno.build, with the OS normalised) — the analogue of "./host.ts".isCI for "what machine am I running on". Its os is a Zuke OperatingSystem (macos, not darwin); use the osLabel/archLabel helpers to name it for a download URL.
const p = hostPlatform();
p.os; // "linux" | "macos" | "windows"
const cpu = p.archLabel({ x86_64: "amd64", aarch64: "arm64" });class HttpBuildRegistry A BuildRegistry backed by HTTP.
Security. The url and token are *trusted configuration* — build descriptors (structural CLI metadata plus a launch location) are sent to that host, so point it only at a service you control and prefer a secret parameter or environment variable over a hard-coded value.
| Member | Signature | Doc |
|---|---|---|
getBuild | getBuild(id: string): Promise<unknown | null> | GET /builds/:id → descriptor + ETag; a 404 is a miss. |
register | register(descriptor: BuildDescriptor, expectedVersion: string | null): Promise<PutBuildResult> | PUT /builds/:id guarded by If-Match / If-None-Match; 412 → conflict. |
deregister | deregister(id: string): Promise<void> | DELETE /builds/:id; a missing build (404) is not an error. |
listBuilds | listBuilds(query: BuildQuery): Promise<BuildSummary[]> | GET /builds?name=&since= → an array of BuildSummary. |
interface HttpBuildRegistryOptions Configuration for an HttpBuildRegistry.
| Member | Signature | Doc |
|---|---|---|
url | url: string | The base URL build endpoints are built under (any trailing slash is ignored). |
token? | token?: string | A bearer token sent as Authorization: Bearer <token>, if set. |
fetch? | fetch?: fetch | The fetch implementation; defaults to the global. Overridable for tests. |
class HttpCacheStore A RemoteCacheStore backed by HTTP: GET <url>/<key> fetches an artifact (a 404 means a miss) and PUT <url>/<key> stores one. Works with any object store or cache server that speaks plain HTTP GET/PUT — an S3, GCS, or R2 bucket behind a URL, or a self-hosted cache endpoint.
Security. The url (and token) are *trusted configuration*: outputs are uploaded to that host and archives are extracted from it, so point it only at a cache you control, and prefer a secret parameter or an environment variable over a hard-coded value. On CI, restrict egress to the cache host so a misconfigured or overridden URL can't exfiltrate artifacts. Restored archives are always confined to the workspace (see restoreOutputs), so a poisoned store cannot write outside it.
| Member | Signature | Doc |
|---|---|---|
get | get(key: string): Promise<Uint8Array | null> | Fetch the archived outputs stored under key, or null if there are none. |
put | put(key: string, artifact: Uint8Array): Promise<void> | Store artifact (a gzipped tar of a target's outputs) under key. |
interface HttpCacheStoreOptions Configuration for an HttpCacheStore.
| Member | Signature | Doc |
|---|---|---|
url | url: string | The base URL keys are appended to (any trailing slash is ignored). |
token? | token?: string | A bearer token sent as Authorization: Bearer <token>, if set. |
fetch? | fetch?: fetch | The fetch implementation; defaults to the global. Overridable for tests. |
async function httpDownload(url: string, dest: PathLike, options?: HttpOptions): Promise<void> Download url to dest, streaming the response body to the file. Creates or truncates dest. Throws HttpError on a non-2xx status.
class HttpError extends Error Raised when an HTTP request returns a non-2xx status. The URL appears in the message and on url, so it is passed through redactUrl first — userinfo and credential query params never reach a log.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
status | status: number | The HTTP status code of the failing response. |
url | url: string | The requested URL, with any credentials redacted. |
async function httpJson(url: string, options?: HttpOptions): Promise<T> Fetch url and parse its body as JSON. Throws HttpError on non-2xx.
interface HttpOptions Options shared by the HTTP helpers.
| Member | Signature | Doc |
|---|---|---|
headers? | headers?: Record<string, string> | Extra request headers (e.g. an Authorization token). |
fetch? | fetch?: fetch | The fetch implementation to use. Defaults to the global fetch; override it to unit-test without network access. |
class HttpStateStore A StateStore backed by HTTP.
Security. The url and token are *trusted configuration* — run records (which include resolved non-secret parameters and target metadata) are sent to that host, so point it only at a service you control and prefer a secret parameter or environment variable over a hard-coded value.
| Member | Signature | Doc |
|---|---|---|
getRun | getRun(id: string): Promise<unknown | null> | GET /runs/:id → record + ETag; a 404 is a miss. |
putRun | putRun(record: RunRecord, expectedVersion: string | null): Promise<PutResult> | PUT /runs/:id guarded by If-Match / If-None-Match; 412 → conflict. |
listRuns | listRuns(query: RunQuery): Promise<RunSummary[]> | GET /runs?status=&target=&since= → an array of RunSummary. |
deleteRun | deleteRun(id: string): Promise<void> | DELETE /runs/:id; a missing run (404) is not an error. |
acquireLock | acquireLock(key: string, holder: LockHolder, ttlMs: number): Promise<LockResult> | POST /locks/:key → 201 { token }, or 409 with the current holder. |
renewLock | renewLock(key: string, token: string, ttlMs: number): Promise<boolean> | PUT /locks/:key renews; a 409/404 means the token lost the lock. |
releaseLock | releaseLock(key: string, token: string): Promise<void> | DELETE /locks/:key releases; a missing lock (404) is not an error. |
interface HttpStateStoreOptions Configuration for an HttpStateStore.
| Member | Signature | Doc |
|---|---|---|
url | url: string | The base URL run endpoints are built under (any trailing slash is ignored). |
token? | token?: string | A bearer token sent as Authorization: Bearer <token>, if set. |
fetch? | fetch?: fetch | The fetch implementation; defaults to the global. Overridable for tests. |
async function httpText(url: string, options?: HttpOptions): Promise<string> Fetch url and return its body as text. Throws HttpError on non-2xx.
async function installNpmTool(spec: NpmToolSpec, options?: InstallNpmToolOptions): Promise<AbsolutePath> Provision an npm-registry package as a version-pinned, cached tool and return the installed bin's AbsolutePath — hand it straight to a wrapper's .toolPath(...).
The package installs under <destDir>/npm/<name>@<version> via npm install --prefix <dir> --no-save <name>@<version>; a marker file records the pinned { name, version }, so a later run whose marker matches and whose bin is still present is reused without invoking npm again. npm must be on PATH (it resolves and downloads the package).
Throws — without recording a marker — if spec is malformed (an unsafe name, version, or bin), if npm fails, or if npm succeeds but the expected bin is absent (a typo'd bin, or a package that ships no executable), so a bad install fails loudly here instead of at a later .toolPath(...).
The marker is written only after the bin is verified present, so a matching marker always has its bin — a reader never sees a half-written install. Concurrent installs of the same pin into the same directory are not isolated; they just do redundant work (the documented ceiling — a build resolves its toolchain once, and distinct pins use distinct directories).
interface InstallNpmToolOptions Options for installNpmTool.
| Member | Signature | Doc |
|---|---|---|
destDir? | destDir?: PathLike | The root tools directory; the package installs under <destDir>/npm/<name>@<version>. Defaults to "./tool.ts".DEFAULT_TOOLS_DIR (.zuke/tools). |
run? | run?: NpmRunner | The npm-install runner. Defaults to the ambient npm; a test seam. |
os? | os?: OperatingSystem | The OS whose bin-shim filename to return (.cmd on Windows). Defaults to the host; a test seam for the Windows shim path. |
interface InstallPlatform The host identity: a Zuke OperatingSystem and Architecture.
| Member | Signature | Doc |
|---|---|---|
os | os: OperatingSystem | The operating system (normalised: macos, not darwin). |
arch | arch: Architecture | The CPU architecture. |
async function installRelease(options: InstallReleaseOptions): Promise<AbsolutePath> Download and install a release binary, returning its AbsolutePath. The path is ready to hand to a wrapper's .toolPath(...) (or CmdTasks).
With a InstallReleaseOptions.checksum, the download is verified before anything is installed, and a matching prior install is reused without downloading again — so pinning a checksum makes the install both hermetic (tamper-evident) and cached.
interface InstallReleaseOptions Options for installRelease.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The tool name; also the installed binary's filename (.exe on Windows). |
url | url: unknown | Resolve the download URL for the target Platform. |
destDir | destDir: PathLike | The directory to install the binary into (created if missing). |
archive? | archive?: raw | tar.gz | zip | The download format. "raw" (default) treats the download as the binary itself; "tar.gz" and "zip" unpack it and take binaryPath from inside. Many release assets ship one or the other. |
binaryPath? | binaryPath?: string | For a "tar.gz" or "zip" archive, the binary's path within the archive. Defaults to name. |
platform? | platform?: InstallPlatform | The platform to resolve the URL for. Defaults to hostPlatform. Override it to install a foreign binary or to unit-test URL resolution. |
download? | download?: DownloadFn | The download implementation. Defaults to httpDownload; override it to unit-test without network access. |
checksum? | checksum?: string | unknown | The expected SHA-256 (hex) of the downloaded artifact — the .tar.gz for an archive, or the binary itself for a "raw" download; this is what release pages publish as the checksum. When set, the download is verified against it (a mismatch throws and nothing is installed) and the checksum doubles as a cache key: a prior install whose recorded checksum matches is reused without downloading again. Omit it and the tool is downloaded every time and not verified. Because url resolves a different artifact per platform, each has its own hash — so pass a resolver (platform) => string (like url) to pin a checksum per platform, or a plain string when a single artifact is installed. |
async function installTree(options: InstallTreeOptions): Promise<AbsolutePath> Download and unpack a whole archive tree — a multi-file runtime such as Node.js, which ships bin/node, bin/npm, bin/npx, and lib/node_modules/** in one tarball — and return the AbsolutePath of its (stripped) root. installRelease extracts a single binary; installTree keeps the entire directory, symlinks included.
Because AbsolutePath is callable, the root doubles as an accessor: root("bin", "node") is the node binary and root("bin") is the directory to put on PATH (with prependPath). Declared InstallTreeOptions.bins are marked executable on POSIX. With a InstallTreeOptions.checksum the archive is verified before unpacking and a matching prior install is reused.
interface InstallTreeOptions Options for installTree.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The tool name; the extracted tree lands in <destDir>/<name>. |
url | url: unknown | Resolve the download URL for the target Platform. |
destDir | destDir: PathLike | The directory the tree is installed under (created if missing). |
archive | archive: tar.gz | zip | The archive format — a multi-file runtime always ships packed. |
strip? | strip?: number | Leading path components to drop while unpacking (tar's --strip-components). A release tarball wraps everything in a tool-v1.2.3/ directory, so 1 unwraps it; bins and the returned tree root are then relative to the stripped tree. Defaults to 0. |
bins? | bins?: unknown | Paths (relative to the stripped tree root) to mark executable on POSIX — the tar reader does not preserve mode bits, so a runtime's bin/node, bin/npm, … need it. Chmod follows a symlink to its real target, so listing a symlinked bin makes the script it points at executable too. |
platform? | platform?: InstallPlatform | The platform to resolve for. Defaults to hostPlatform. |
download? | download?: DownloadFn | The download implementation. Defaults to httpDownload; a test seam. |
checksum? | checksum?: string | unknown | The expected SHA-256 (hex) of the downloaded archive — verified before anything is unpacked, and used as the cache key (see InstallReleaseOptions.checksum). Omit it and the tree is downloaded every time and not verified. |
type Interpolatable = string | number | AbsolutePath | Array<string | number | AbsolutePath> A value that may be interpolated into a $ template.
function isCI(): boolean Whether the build appears to be running in a CI environment.
function isStyleName(name: string): name is StyleName Whether a string names one of the SGR styles.
type JsonValue = string | number | boolean | null | JsonValue[] | unknown A JSON-serialisable value — the only thing that may be persisted in a target's TargetStateHandle, since run state is stored as JSON.
function line(style: Style, options?: LineOptions): string A horizontal rule spanning the style's width (dimmed by default).
interface LineOptions Options for line.
| Member | Signature | Doc |
|---|---|---|
char? | char?: string | The character to repeat. Defaults to ═. |
width? | width?: number | The rule width. Defaults to the style's width. |
style? | style?: unknown | Styles applied to the whole rule. Defaults to ["dim"]. |
class LockConflictError extends Error Raised when a target's lock is already held by another run. Its message is the rendered guidance (from the target's onConflict, else a default), so it surfaces verbatim in the CLI failure footer and the run record; holder carries the structured identity for programmatic surfaces (e.g. MCP).
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
interface LockHolder Who holds a lock — surfaced to the loser of a conflict so it can act.
| Member | Signature | Doc |
|---|---|---|
actor | actor: string | The actor that acquired the lock. |
runId | runId: string | The run that holds it (zuke cancel <runId> releases it). |
since | since: string | ISO-8601 timestamp when it was acquired. |
runUrl? | runUrl?: string | A link to the holding run (e.g. its CI job), when known. |
function lockKey(...parts: Array<string | number>): string Join parts into a lock key that is safe to use as a filename and URL segment. Each part is sanitised (non-[A-Za-z0-9._-] runs become _) and empty parts are dropped, so lockKey("deploy", repo) is stable and injection-free.
type LockResult = unknown | unknown The result of StateStore.acquireLock: a token proving ownership, or the current holder when the lock is already held.
class LockSettings Fluent configuration for TargetBuilder.lock, in the settings-lambda style: .lock((s) => s.lockKey("deploy", repo).withTtl("4h")). Set the key (composed from sanitised parts with LockSettings.lockKey, or directly with LockSettings.key), the TTL, and an optional LockSettings.onConflict message. The lambda runs after parameters resolve, so the key may read this.<param>.value.
| Member | Signature | Doc |
|---|---|---|
lockKey | lockKey(...parts: Array<string | number>): this | Set the lock key from parts, sanitised and joined via "./state/lock.ts".lockKey — e.g. s.lockKey("deploy", repo). |
key | key(key: string): this | Set the lock key directly (must be filename-safe; prefer lockKey). |
withTtl | withTtl(ttl: string | number): this | How long the lock survives a killed holder — a duration string like "4h" / "30m" (see the duration parser) or raw milliseconds. A live holder renews it while it runs, so it never expires under it. |
onConflict | onConflict(render: unknown): this | Render the guidance shown to a run that loses the lock. Receives the current "./state/lock.ts".LockHolder; the returned string becomes the failure message. Defaults to a generic "held by … then retry" line. |
key_? | key_?: string | The resolved lock key; set by key or lockKey. |
ttl_? | ttl_?: string | number | The TTL (a duration string or milliseconds); set by withTtl. |
onConflict_? | onConflict_?: unknown | The conflict-guidance renderer; set by onConflict. |
interface McpIdentity A trusted caller identity, resolved per request by a McpIdentityHook (typically from an authenticating reverse proxy's header). Its McpIdentity.actor is the highest-precedence attribution — it overrides --actor, the environment, and the client's self-reported label for the call.
| Member | Signature | Doc |
|---|---|---|
actor | actor: string | The authenticated actor (e.g. an OAuth subject). |
via? | via?: string | How the identity was established (e.g. "oauth-proxy"); informational. |
type McpIdentityHook = unknown Resolve a trusted McpIdentity from a request's context. Invoked once per message, before any dispatch; throwing rejects the whole request with an auth error, so nothing executes and nothing is written to state — the seam a proxy in front of the server uses to inject an authenticated identity.
interface McpRequestContext The per-request context a transport hands the message handler. Carries the request's headers, so a server's identity hook can authenticate the caller from a trusted proxy header. Empty on the stdio transport (no headers).
| Member | Signature | Doc |
|---|---|---|
headers | headers: Headers | The request headers; an empty Headers on stdio. |
function missingTool(settings: S): S Point settings at a binary that cannot exist, so running it raises a "./tooling.ts".ToolNotFoundError without ever launching a real process — the way a wrapper test proves each of its task functions reaches execution.
The platform is pinned to linux because on Windows a missing binary is retried through cmd /c, which exists, so the failure would surface as a command error instead:
await assertRejects(() => BiomeTasks.check(missingTool), ToolNotFoundError);type NpmRunner = unknown Runs npm install <args> — the injectable subprocess seam. Defaults to spawning the ambient npm; a test injects a fake that records the argv and plants the expected bin, so provisioning stays hermetic and network-free.
interface NpmToolSpec A specification of an npm-registry package to provision as a tool.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The npm package to install, e.g. "vitest" or "@nestjs/cli". |
version | version: string | The exact version to pin, e.g. "4.1.9" — installed as name@version. |
bin? | bin?: string | The bin to resolve, when it differs from the package name — @nestjs/cli publishes the nest bin, so { name: "@nestjs/cli", bin: "nest" }. Defaults to name. |
type OnCancel = TargetBuilder | unknown A compensation registered with TargetBuilder.onCancel: either a sibling target directly, or a thunk returning one. The thunk form defers evaluation so a compensation declared *below* the target it cleans up (class fields initialise top-to-bottom) can still be referenced.
type OnTimeout = unknown What a timed-out wait does — resolved from WaitSettings.onTimeout.
interface OpenCacheOptions Optional extras for openCache: a remote store and a warning sink.
| Member | Signature | Doc |
|---|---|---|
remote? | remote?: RemoteCacheStore | A RemoteCacheStore to restore outputs from (on a local miss) and upload them to (after a successful run). Applies only to targets that declare TargetBuilder.outputs. |
warn? | warn?: unknown | Report a non-fatal remote-cache error (a get/put failure never fails the build). |
function operatingSystem(os?: Deno.build.os): OperatingSystem The operating system as a Zuke OperatingSystem: darwin becomes macos, windows stays windows, and every other Unix (linux, the BSDs, solaris, …) is reported as linux. Pass a raw Deno.build.os value to normalise it; defaults to the running host — the platform analogue of isCI.
import { operatingSystem } from "jsr:@zuke/core";
if (operatingSystem() === "macos") { ... }type OperatingSystem = linux | macos | windows The operating systems Zuke recognises — Deno's raw Deno.build.os values normalised to a friendly set (notably darwin → macos). Used across the ecosystem so builds branch on "macos" rather than the surprising "darwin".
type OrderingEdge = unknown A soft ordering edge [before, after]: before must run before after, with no data dependency. Returned by "./build.ts".Build.extraEdges to feed a consumer's dependency graph (e.g. a monorepo's dependency-graph.json) into planning; an edge whose endpoints are not both in the run's execution set is ignored, and cycles are reported like any other.
interface OutputHost Filesystem effects used to archive and restore a target's outputs.
| Member | Signature | Doc |
|---|---|---|
readFile | readFile(path: string): Promise<Uint8Array | null> | File contents, or null if the path does not exist. |
stat | stat(path: string): Promise<unknown | null> | Whether a path exists and is a directory, or null if it is missing. |
readDir | readDir(path: string): Promise<string[]> | The entry names within a directory. |
writeFile | writeFile(path: string, bytes: Uint8Array): Promise<void> | Write a file, creating parent directories as needed. |
function pad(text: string, width: number, align?: left | right): string Pad text to width visible columns, aligning left (default) or right.
function paint(color: boolean, codes: string, text: string): string Wrap text in ANSI codes when colour is enabled, otherwise return it as-is.
function parameter(description?: string): Parameter<string, string | undefined> Create a new build parameter (a string by default). Configure it fluently: .number()/.boolean() change the kind, .options(...) restricts a string, .default(v)/.required() set optionality, and .env(name) overrides the environment variable.
class Parameter A typed build parameter. Declare one with parameter and configure it with the fluent methods; each method returns a new parameter whose value type reflects the configuration (string, number, boolean, and whether it can be undefined).
K is the underlying value kind; T is the exposed value type, which is K for required/defaulted parameters and K | undefined for optional ones.
| Member | Signature | Doc |
|---|---|---|
value | value(): T | The resolved value. Throws if read before the build resolves parameters. |
isSet_ | isSet_(): boolean | Whether the parameter resolved to a defined value (used by .requires()). |
stringValue_ | stringValue_(): string | undefined | The resolved value as a string, or undefined if unset (for masking). |
secret | secret(): Parameter<K, T> | Mark the value as sensitive: it is masked in CI output (::add-mask::) and redacted from all of Zuke's reporter output. Pair with Parameter.from to resolve the value from a secret manager rather than the environment. |
from | from(source: SecretSource): Parameter<K, T> | Resolve the value from a SecretSource (see execSecret / fileSecret) when neither a --flag nor an environment variable supplied one — the source is a fallback provider, consulted before the declared default. Typically paired with Parameter.secret so the resolved value is redacted. |
number | number(this: Parameter<string, string | undefined>): Parameter<number, number | undefined> | Parse the value as a number (e.g. --workers 4). |
boolean | boolean(this: Parameter<string, string | undefined>): Parameter<boolean, boolean> | Treat the parameter as a boolean flag (e.g. --verbose); defaults to false. |
options | options(this: Parameter<string, string | undefined>, ...values: string[]): Parameter<string, string | undefined> | Restrict a string parameter to a fixed set of choices. |
default | default(this: Parameter<K, K | undefined>, value: K): Parameter<K, K> | Provide a default, making value non-optional (K). |
required | required(this: Parameter<K, K | undefined>): Parameter<K, K> | Require a value, making value non-optional (K); errors if unsupplied. |
env | env(name: string): Parameter<K, T> | Override the environment variable read as a fallback for this parameter. |
array | array(this: Parameter<E, E | undefined>): Parameter<E, E[]> | Accept a comma-separated list (or a repeated flag), exposing value as an array. --tags a,b and --tags a --tags b both yield ["a", "b"]; blank entries are dropped, and an unsupplied *optional* list defaults to [] (a required one is reported missing — see below). Each element is parsed by this parameter's own element parser, so it composes: .options("a", "b").array() validates every element against the choices, and .number().array() yields a number[], rejecting a non-numeric entry. (Apply .options()/.number() before .array().) .array() composes last, after .required() too: a .required().array() list stays required, so an unsupplied value is reported as missing rather than silently resolving to the empty-list default. An optional (non-required) list defaults to []. |
resolve_ | resolve_(raw: string | undefined): void | Resolve from a raw input (or undefined when none was supplied). |
name_? | name_?: string | Property name, assigned during discovery. Undefined until then. |
description_? | description_?: string | Human-readable description shown in --help/--list. |
kind_ | kind_: ParamKind | The runtime value kind. |
required_ | required_: boolean | Whether a value must be supplied (no default). |
options_? | options_?: unknown | The allowed string choices, if restricted with Parameter.options. |
envName_? | envName_?: string | An explicit environment variable name override. |
hasFallback_ | hasFallback_: boolean | Whether the parameter has a declared default value. |
secret_ | secret_: boolean | Whether the value is sensitive and should be masked in CI output. |
array_ | array_: boolean | Whether the value is a comma-separated / repeatable list (.array()). |
source_? | source_?: SecretSource | A provider that resolves the value when no flag/env supplied one. |
default_? | default_?: string | The declared default rendered as a string (an array default is joined with commas), or undefined when the parameter has no default or an empty-list one. For display in tool schemas and --list; never a secret value. |
class ParameterError extends Error Raised when a parameter value is invalid or read before resolution.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
type ParamKind = string | number | boolean A parameter's runtime kind tag.
type ParamValue = string | number | boolean The value kinds a parameter can hold.
function parseDuration(value: string | number): number Parse a duration to milliseconds. Accepts a number (already milliseconds) or a string of a non-negative amount and a unit — ms, s, m, h, or d (e.g. "90s", "4h", "1.5h"). Throws a friendly error on anything else.
type PathLike = string | AbsolutePath A filesystem path accepted by Zuke APIs: either a plain string or an AbsolutePath. Anywhere a tool wrapper or build helper takes a path, it accepts a PathLike and coerces it to a string.
function plan(root: TargetBuilder, extra?: unknown): TargetBuilder[] Topologically sort the execution set for root, honouring hard dependencies and the soft before/after ordering hints (the latter only between nodes that are both in the set).
interface Platform A platform with helpers to name it the way a tool's downloads do. osLabel and archLabel map the os/arch to a tool's own naming, falling back to the value itself for anything not in the alias map — so a url callback reads p.osLabel({ macos: "darwin" }) (for a tool that spells macOS "darwin") instead of a hand-written os === … ternary. This is what the InstallReleaseOptions.url and InstallReleaseOptions.checksum callbacks receive.
| Member | Signature | Doc |
|---|---|---|
osLabel | osLabel(aliases?: Partial<Record<OperatingSystem, string>>): string | The OS named for downloads: aliases[os], else the InstallPlatform.os itself. |
archLabel | archLabel(aliases?: Partial<Record<Architecture, string>>): string | The arch named for downloads: aliases[arch], else the InstallPlatform.arch itself. |
interface Plugin A lifecycle observer. Every hook is optional; implement only the ones you need. Hooks may be async — the executor awaits each before continuing.
| Member | Signature | Doc |
|---|---|---|
onStart? | onStart?(run: RunInfo): void | Promise<void> | Called once before any target runs, with the run's RunInfo. |
onTargetStart? | onTargetStart?(target: string, run: RunInfo): void | Promise<void> | Called just before a target's body executes (not for skipped/cached), with the target name and the run's RunInfo. |
onTargetEnd? | onTargetEnd?(target: string, status: TargetStatus, timing: TargetTiming): void | Promise<void> | Called after each target settles, with its final status and its TargetTiming (run id + duration). |
onFinish? | onFinish?(result: BuildResult, run: RunInfo): void | Promise<void> | Called once after the run completes (success or failure), with the result and the run's RunInfo. |
onRunStateChange? | onRunStateChange?(record: RunRecord): void | Promise<void> | Called on each run-level durable status change — the run going running, suspended, succeeded, failed, cancelling, or cancelled — with the current "./state/types.ts".RunRecord. It carries the full record (per-target timings, waits, the audit trail), so a metrics exporter can derive spans, wait durations, and counters from a single source. Only fires when a state store is configured (the record's home); a plain build with no store never produces one, and this hook stays silent. The record is the secret-free projection: secret() parameters are omitted, and ctx.state metadata, target errors, and audit arguments are run through the redactor before they reach it — the same data already persisted to the store and shown by zuke runs show. It is safe to export. A run cancelled in-process (Ctrl-C / its signal) is observed as running → cancelling → cancelled. When another process cancels the run (zuke cancel), this process observes it through cancelling and stops — the canceller's process owns the final cancelled — so treat cancelling as run-ended for the owning process. |
name? | name?: string | A name for diagnostics (optional). |
function prependPath(dir: PathLike, os?: Deno.build.os): string Prepend dir to the process PATH, and return the new value. A tool provisioned into dir (e.g. the bin directory of an "./install.ts".installTree runtime) then resolves for the rest of the build: the shell $, Command, and every tool wrapper spawn subprocesses that inherit Deno.env, so the node_modules/.bin shims and NpmTasks that assume a node/npm on PATH find the provisioned one.
Idempotent — a directory already on PATH is left in place, not duplicated — and uses the platform separator (; on Windows, : elsewhere).
type PutBuildResult = unknown | unknown The result of a BuildRegistry.register compare-and-swap write.
type PutResult = unknown | unknown The result of a StateStore.putRun compare-and-swap write.
const REDACTED: [redacted] The placeholder a Redactor substitutes for each secret value.
class Redactor Collects secret values and masks them in text. Register a value with Redactor.add and rewrite a line with Redactor.redact; empty strings are ignored (they would match everywhere) and duplicates are recorded once. Longer secrets are applied first so a secret that contains another is masked whole rather than partially.
| Member | Signature | Doc |
|---|---|---|
add | add(value: string): void | Register a secret value to mask. Ignores empty strings and duplicates. |
redact | redact(line: string): string | Replace every registered secret in line with REDACTED. |
size | size(): number | The number of distinct secret values registered. |
interface Remediation A recovery step plugged into a target with TargetBuilder.recoverWith. It runs only after the target body fails, receives the failure, and may attempt to repair it — returning { retry: true } to ask the executor to re-run the body (the real build command is the verifier). Implemented, for example, by the AI fixer in @zuke/ai, but any object with a remediate method qualifies.
| Member | Signature | Doc |
|---|---|---|
remediate | remediate(context: RemediationContext): RemediationResult | Promise<RemediationResult> | Inspect (and optionally repair) the failure; report whether to retry. |
name? | name?: string | A name for diagnostics (optional). |
interface RemediationContext Context passed to a Remediation after a target body fails.
| Member | Signature | Doc |
|---|---|---|
target | target: string | The name of the failed target. |
attempt | attempt: number | The 1-based recovery attempt (the body has already failed attempt times). |
error | error: unknown | The failure being remediated. When a target fails through the shell this is a CommandError carrying the failed command and its captured stderr. |
interface RemediationResult The outcome of one Remediation attempt.
| Member | Signature | Doc |
|---|---|---|
retry | retry: boolean | Re-run the target body after this remediation? true asks the executor to retry (the remediation changed something — e.g. applied a fix); false leaves the failure standing (e.g. a diagnose-only remediation that only explained the failure). |
summary? | summary?: string | A one-line description of what was diagnosed or done, for diagnostics. |
function remoteCacheKey(name: string, fingerprint: string): string The store key for a target's outputs: its name and input fingerprint. The name is sanitised so the key is safe as a filename and a URL path segment.
interface RemoteCacheStore A content-addressed store for archived target outputs, keyed by remoteCacheKey. Both operations are best-effort from the build's point of view: the executor never fails a build because the store is unreachable — it just rebuilds and, where it can, re-uploads.
| Member | Signature | Doc |
|---|---|---|
get | get(key: string): Promise<Uint8Array | null> | Fetch the archived outputs stored under key, or null if there are none. |
put | put(key: string, artifact: Uint8Array): Promise<void> | Store artifact (a gzipped tar of a target's outputs) under key. |
interface RemoveOptions Options for FileTasksApi.remove.
| Member | Signature | Doc |
|---|---|---|
recursive? | recursive?: boolean | Remove a directory and its contents recursively, like rm -r. |
interface Renderer How the executor renders a build's output. Each method is pure — it returns the lines to print rather than writing them — so a custom renderer stays unit-testable and the executor keeps control of the output streams.
| Member | Signature | Doc |
|---|---|---|
targetHeader | targetHeader(style: Style, name: string): string[] | The banner that opens a target's section (a ::group:: under Actions). |
targetPassFooter | targetPassFooter(style: Style, name: string, ms: number): string[] | The footer printed after a target body succeeds. |
targetFailFooter | targetFailFooter(style: Style, name: string, ms: number, error: unknown): unknown | The footer printed after a target body fails, split into info (stdout) and error (stderr) so the caller can fan the lines out correctly. |
targetDryRunFooter | targetDryRunFooter(style: Style, name: string): string[] | The footer printed for a dry-run target that was never executed. |
summaryBlock | summaryBlock(style: Style, reports: TargetReport[], totalMs: number, ok: boolean): string[] | The end-of-build summary block: the aligned table and closing verdict. |
jobSummaryMarkdown | jobSummaryMarkdown(reports: TargetReport[], totalMs: number, ok: boolean): string | The GitHub Actions job-summary Markdown mirroring the terminal summary. |
function repoRoot(...segments: string[]): AbsolutePath The absolute path of the repository root — the directory containing CONFIG_FILE — with any segments appended. The returned value is an AbsolutePath, so it is itself callable for further joining.
repoRoot(); // <root>
repoRoot("src", "main.ts"); // <root>/src/main.ts
repoRoot().join("dist"); // <root>/dist
The root is located by walking up from the current working directory, so the path is resolved at runtime and never hard-coded into a committed file.
interface Reporter Sink for executor output, defaulting to the console. Overridable in tests.
| Member | Signature | Doc |
|---|---|---|
info | info(line: string): void | Write an informational line. |
error | error(line: string): void | Write an error line. |
function resolveBuildRegistry(option: BuildRegistry | false | undefined, declared: BuildRegistry | undefined, options: ResolveRegistryOptions): BuildRegistry | undefined Pick the build registry by precedence: an explicit option wins (false disables the registry entirely), then a declared registry (a build's registry() override), then the envBuildRegistry environment fallback, then — only when ResolveRegistryOptions.enableDefault — a filesystem registry under <root>/.zuke/builds.
interface ResolveRegistryOptions Inputs resolveBuildRegistry needs to build the default filesystem registry.
| Member | Signature | Doc |
|---|---|---|
readEnv | readEnv: unknown | Reads an environment variable (injectable for tests). |
host | host: StateHost | Filesystem effects for the default/env filesystem registry. |
defaultDir | defaultDir: string | Directory the default filesystem registry writes to (<root>/.zuke/builds). |
enableDefault | enableDefault: boolean | Fall back to the default filesystem registry when nothing else is configured. zuke register sets this so the command works out of the box. |
function resolveRemoteStore(option: RemoteCacheStore | false | undefined, declared: RemoteCacheStore | undefined, readEnv: unknown): RemoteCacheStore | undefined Pick the remote store for a run by precedence: an explicit option wins (false disables the remote cache entirely), then a declared store (a build's remoteCache() override), then the envCacheStore environment fallback.
interface ResolveStateOptions Inputs resolveStateStore needs to build the default filesystem store.
| Member | Signature | Doc |
|---|---|---|
readEnv | readEnv: unknown | Reads an environment variable (injectable for tests). |
host | host: StateHost | Filesystem effects for the default/env filesystem store. |
defaultDir | defaultDir: string | Directory the default filesystem store writes to (<root>/.zuke/runs). |
enableDefault | enableDefault: boolean | Fall back to the default filesystem store when nothing else is configured. Set when the run opts into durable state (--state, or — from a later milestone — a durable feature like a lock or a wait). |
function resolveStateStore(option: StateStore | false | undefined, declared: StateStore | undefined, options: ResolveStateOptions): StateStore | undefined Pick the state store for a run by precedence: an explicit option wins (false disables state entirely), then a declared store (a build's stateStore() override), then the envStateStore environment fallback, then — only when ResolveStateOptions.enableDefault — a filesystem store under <root>/.zuke/runs. A plain build with no durable feature and no configuration gets undefined, so it carries zero overhead.
async function restoreOutputs(artifact: Uint8Array, host: OutputHost): Promise<string[]> Restore the files in artifact (a gzipped tar produced by archiveOutputs) to disk, returning the paths written. Entry names are validated first: an absolute path or one escaping the workspace (..) is rejected before anything is written, so a malicious archive can't plant files outside the current directory.
async function resumeCheck(build: Build, options: Omit<ResumeOptions, runId | signal | data> & unknown): Promise<unknown> Re-attempt every suspended run in the store (or just runId): predicate-based waits are re-evaluated and expired waits time out. Signal-based waits with no new signal simply re-suspend. Returns the number of runs that ended in failure. This is the sweep a cron or webhook drives (zuke resume --check).
A run whose record is "./state/types.ts".RunRecord.degraded is counted as failed on every sweep until an operator resolves it: it cannot be advanced without deciding whether its targets are safe to repeat, and a non-zero result is the only channel a cron watches. Its refusal is reported through the reporter (the console unless silenced) so the cause is visible, and it stays suspended, so a later sweep with ResumeOptions.resumeDegraded still picks it up.
interface ResumeOptions Options for resumeRun.
| Member | Signature | Doc |
|---|---|---|
runId | runId: string | The id of the suspended run to resume. |
stateStore? | stateStore?: StateStore | false | Durable store the run lives in. Defaults to the same resolution as a normal run (explicit → stateStore() override → env → .zuke/runs); resume always needs one. |
signal? | signal?: string | Deliver a signal by this name before resuming (satisfies externalSignal). |
data? | data?: JsonValue | The signal's JSON payload (defaults to {}); ignored without signal. |
params? | params?: Record<string, string> | Non-secret parameter overrides; the rest come from the record. |
readEnv? | readEnv?: unknown | Reads an environment variable (secrets re-resolve from here). |
actor? | actor?: string | Who to attribute the resumption to (stamped on the run). |
forceGraph? | forceGraph?: boolean | Continue even if the build graph changed since the run was suspended. |
resumeDegraded? | resumeDegraded?: boolean | Resume even though the record is "./state/types.ts".RunRecord.degraded — a state write was permanently lost, so a target that succeeded may still be recorded running or pending. The resume trusts the record as written, which means such a target runs again; passing this accepts that risk, on the grounds that the operator — not Zuke — knows whether the target is safe to repeat. |
silent? | silent?: boolean | Suppress banner/summary output. |
reporter? | reporter?: Reporter | Custom reporter; overrides silent. |
plugins? | plugins?: Plugin[] | Lifecycle observers for the resumed run. Because a resume keeps the original run id, a plugin sees the continuation under the same identity — so an exporter's spans join one trace across the suspend/resume boundary. |
async function resumeRun(build: Build, options: ResumeOptions): Promise<BuildResult> Resume the suspended run options.runId for build. Transitions it to running (exactly one resumer wins), optionally delivers a signal, checks the graph still matches, and continues via "./executor.ts".execute, re-running only the not-yet-succeeded targets.
interface ResumeState The continuation state resumeRun hands to execute on a resume.
| Member | Signature | Doc |
|---|---|---|
record | record: RunRecord | The run being continued (already transitioned to running). |
version | version: string | Its current store version, for the writer to continue from. |
done | done: ReadonlySet<string> | Names of targets recorded succeeded — seeded as done, never re-run. |
function resumeWhen(check: unknown, options?: ResumeWhenOptions): WaitTrigger A trigger satisfied when an async check predicate returns true. Zuke does not poll on its own — the predicate is evaluated when the target is reached and on each zuke resume <id> --check, so a cron or webhook nudging --check drives it. Use it to wait on state Zuke can query (a row, a file, an API).
interface ResumeWhenOptions Options for resumeWhen.
| Member | Signature | Doc |
|---|---|---|
interval? | interval?: string | number | How often zuke resume --check should re-evaluate the predicate. |
async function run(BuildClass: unknown, options?: RunOptions): Promise<void> Public entry point. Instantiate the build, parse arguments, run, and set the process exit code.
Call it at the bottom of your build file — no import.meta.main guard needed. run acts only when its module is the program's entry point; when the file is imported instead (for example under test) it does nothing.
await run(MyBuild);
// …with plugins:
await run(MyBuild, { plugins: [timing] });async function runConformanceCli(args: string[], deps?: ConformanceCliDeps): Promise<number> Run the conformance kit as a CLI: --url <base> (required) and --token <bearer> (optional) name the backend, then both suites run against it. Prints a PASS/FAIL line per scenario and resolves to a process exit code — 0 when every scenario passes, 1 when any fails or --url is missing.
interface RunEvent One entry in a run's audit trail: an MCP tool call, who made it, and how it ended. Appended (never mutated) so the trail is a chronological record. The MCP server records a RunEvent for every mutating or denied tool call; zuke runs show prints them.
| Member | Signature | Doc |
|---|---|---|
at | at: string | ISO-8601 time the call was recorded. |
tool | tool: string | The tool called (e.g. run:deploy, signal_run). |
actor | actor: string | Who made the call (a resolved actor; see "./record.ts".resolveActor). |
outcome | outcome: RunEventOutcome | Whether the call ran, was denied by authorization, or errored. |
args | args: Record<string, string> | The call's arguments, redacted — secret values masked, tokens dropped. |
detail? | detail?: string | A short, redacted human detail (e.g. a denial reason), when present. |
type RunEventOutcome = ok | denied | error The outcome recorded for an audited MCP tool call (see RunEvent).
interface RunGraphNode One entry of a run's graph-shape snapshot.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The target's dotted name. |
dependsOn | dependsOn: string[] | The dotted names of its direct dependencies. |
interface RunInfo Run identity passed to a plugin's lifecycle hooks, so an observer can group a run's events (e.g. under one trace id) — stable across a suspend/resume boundary, since a resumed run keeps the original id.
| Member | Signature | Doc |
|---|---|---|
runId | runId: string | The run id, stable for every target in the run (and across a resume). |
dryRun | dryRun: boolean | True when the run is a dry run (no target body executes). |
interface RunningService A started service the executor holds until it tears it down.
| Member | Signature | Doc |
|---|---|---|
stop | stop(): Promise<void> | Stop the service; never rejects (failures are the registry's concern). |
name | name: string | The service's target name, for diagnostics. |
interface RunOptions Options for run.
| Member | Signature | Doc |
|---|---|---|
args? | args?: string[] | Command-line arguments. Defaults to Deno.args. |
plugins? | plugins?: Plugin[] | Lifecycle observers to run alongside the build's own hooks. |
renderer? | renderer?: Renderer | Renderer for the per-target banners and end-of-build summary. Defaults to Zuke's built-in look; inject consoleRenderer from @zuke/console (or a custom Renderer) to restyle a build's output. |
interface RunQuery Filters for "./store.ts".StateStore.listRuns; all fields are optional.
| Member | Signature | Doc |
|---|---|---|
status? | status?: RunStatus | Keep only runs with this status. |
target? | target?: string | Keep only runs whose graph contains a target with this dotted name. |
since? | since?: string | Keep only runs created at or after this ISO-8601 timestamp. |
limit? | limit?: number | Return at most this many runs (the newest, since listing is newest-first). Applied server-side so a large store stays listable; 0 returns none. |
interface RunRecord A versioned snapshot of one run. Persisted as JSON; a store's opaque version (an ETag / content hash) drives compare-and-swap writes.
| Member | Signature | Doc |
|---|---|---|
id | id: string | Unique run ID (matches "../target.ts".TargetContext runId). |
build | build: string | The build class name. |
rootTarget | rootTarget: string | The dotted name of the requested (root) target. |
status | status: RunStatus | The run's lifecycle status. |
actor | actor: string | Who started the run (resolved from --actor, ZUKE_ACTOR, or CI env). |
createdAt | createdAt: string | ISO-8601 timestamp when the run was created. |
updatedAt | updatedAt: string | ISO-8601 timestamp of the last write. |
graph | graph: RunGraphNode[] | The graph shape the run planned, in declaration order. |
params | params: Record<string, string> | Resolved parameter values, keyed by name. Secrets are always omitted. |
targets | targets: Record<string, TargetRunState> | Per-target progress, keyed by dotted target name. |
signals | signals: Record<string, SignalRecord> | External signals received so far, keyed by name (see .waitsFor(...)). |
events | events: RunEvent[] | Append-only audit trail of MCP tool calls against this run (see RunEvent). |
degraded? | degraded?: boolean | True when at least one state write for this run was permanently lost — a conflicting write from another process could not be re-applied within the writer's retry budget. Writes are best-effort, so the run itself carried on; the flag is how a later reader learns that a transition which really happened may be missing from the record. In particular a target that succeeded can still be recorded running or pending, so a resume would re-run it — which is why a resume refuses a degraded record unless --resume-degraded overrides it (see "../resume.ts".ResumeOptions.resumeDegraded) — and why a cancellation compensates every target whose success the record cannot rule out, rather than only those recorded succeeded (see "../cancel.ts".runCompensations). It is set by the writer when it loses a write and persisted by the next write that lands — the failing one, by definition, could not carry it. A drop that leaves the mutation in memory for a later write to re-persist does *not* set it. Absent (or false) means no write is known to be missing. |
function runSettings(settings: S, configure?: Configure<S>): Promise<CommandOutput> Construct-configure-run: the shared shape of every task function.
export const MyTasks = {
build: (configure?: Configure<MyBuildSettings>) =>
runSettings(new MyBuildSettings(), configure),
};type RunStatus = running | suspended | cancelling | succeeded | failed | cancelled The lifecycle status of a whole run. cancelling is the transient state a cancellation moves through — the run has been asked to stop and its compensations are running — before it settles as cancelled.
interface RunSummary A compact run listing row, returned by "./store.ts".StateStore.listRuns.
| Member | Signature | Doc |
|---|---|---|
id | id: string | The run ID. |
build | build: string | The build class name. |
rootTarget | rootTarget: string | The dotted name of the requested (root) target. |
status | status: RunStatus | The run's lifecycle status. |
actor | actor: string | Who started the run. |
createdAt | createdAt: string | ISO-8601 creation timestamp. |
updatedAt | updatedAt: string | ISO-8601 timestamp of the last write. |
interface ScheduleEntry A scheduled trigger: a 5-field cron expression in an optional IANA timezone.
| Member | Signature | Doc |
|---|---|---|
cron | cron: string | A standard 5-field cron expression (minute hour day-of-month month day-of-week). |
tz? | tz?: string | An IANA timezone (e.g. Europe/Sofia) the cron is expressed in. Omitted (or UTC) means the cron is already UTC and is emitted verbatim. |
class SecretError extends Error Raised when a SecretSource cannot produce a value.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
interface SecretSource A provider that resolves a secret's value on demand. Built by execSecret or fileSecret and attached to a parameter with .from(source); the framework calls SecretSource.resolve during parameter resolution.
| Member | Signature | Doc |
|---|---|---|
resolve | resolve(): Promise<string> | Produce the secret value, or throw SecretError on failure. |
function service(): ServiceBuilder Create a service target — a long-lived process kept running while its dependents execute. Configure it with ServiceBuilder.start / ServiceBuilder.readyWhen and depend on it from a target.
class ServiceBuilder extends TargetBuilder A long-lived target. Configure how it starts (ServiceBuilder.start), how to tell it is ready (ServiceBuilder.readyWhen), and — when the started handle is not self-stopping — how it stops (ServiceBuilder.stop). It inherits the ordering methods (dependsOn, before, after, description) from TargetBuilder; a service has no .executes body.
| Member | Signature | Doc |
|---|---|---|
start | start(fn: unknown): this | How to start the process. Return a ServiceHandle (e.g. $\…\.spawn()) so the service can be stopped on teardown; provide a custom ServiceBuilder.stop if the handle is not self-stopping. |
readyWhen | readyWhen(fn: unknown): this | A readiness probe, polled until it returns true (or the timeout is hit). Without one, the service is considered ready the moment it starts. See tcpReachable for the common "is the port accepting connections?". |
readyTimeout | readyTimeout(ms: number): this | Override how long to wait for ServiceBuilder.readyWhen (default 30s). |
stop | stop(fn: unknown): this | Custom teardown, given the handle ServiceBuilder.start returned. |
launch_ | launch_(name: string): Promise<RunningService> | INTERNAL: start the service and wait until it is ready, returning a handle the executor stops on teardown. Throws ServiceError if no start was configured, or if the service does not become ready in time (the just-started process is stopped first so it is not leaked). |
class ServiceError extends Error Raised when a service cannot start or does not become ready in time.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
interface ServiceHandle A running service — whatever ServiceBuilder.start returns. Its ServiceHandle.stop tears it down; a {@link https://jsr.io/@zuke/core SpawnedProcess} is one, so .start(() => $\…\.spawn()) needs no explicit stop.
| Member | Signature | Doc |
|---|---|---|
stop | stop(): void | Promise<void> | Terminate the service. Called on teardown unless .stop() overrides it. |
class ServiceRegistry Holds the services started during a run and stops them in reverse order on teardown. Stopping never throws — a failure to stop one service is reported and the rest are still stopped.
| Member | Signature | Doc |
|---|---|---|
register | register(running: RunningService): void | Record a started service to stop later. |
size | size(): number | The number of services currently held. |
stopAll | stopAll(report: unknown): Promise<void> | Stop every registered service, newest first, reporting each outcome. |
const SGR: unknown ANSI select-graphic-rendition codes, keyed by style name.
function sgrCodes(names: unknown): string Concatenate the escape codes for names (an unknown name contributes none).
class ShellArgsError extends Error Raised when splitShellArgs reaches the end of the input with a quote still open. Names the offending quote character and the offset at which it was opened so the bad spot in a long command line is findable.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
function shimFallbackArgv(argv: ReadonlyArray<string>, os: Deno.build.os): string[] | null On Windows, wrap an argv in a cmd /c invocation so .cmd/.bat shims (such as npm's) become spawnable; returns null on other platforms.
interface SignalRecord A payload received for an external signal (see RunRecord.signals).
| Member | Signature | Doc |
|---|---|---|
data | data: JsonValue | The signal's JSON payload ({} when none was sent). |
receivedAt | receivedAt: string | ISO-8601 timestamp when the signal was recorded. |
class SlackAnnouncementSettings extends AnnouncementSettings Fluent settings for AnnounceTasksApi.slack. Bot mode (.bot().token(t).channel(c)) posts through the Web API (chat.postMessage).
class SlackApiError extends Error Raised when the Slack Web API accepts the request but reports a logical failure ({ ok: false }), carrying Slack's machine-readable error code (e.g. channel_not_found, not_in_channel, invalid_auth).
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
class SpawnedProcess A long-lived process started with Command.spawn — the handle a {@link https://jsr.io/@zuke/core service} keeps alive. Unlike awaiting a Command, spawning does not wait for the process to exit; call SpawnedProcess.stop to terminate it (which is also the default service teardown). Its stdout/stderr are inherited so the process's own output is visible.
| Member | Signature | Doc |
|---|---|---|
pid | pid(): number | The operating-system process id (-1 for a dry-run stub). |
status | status(): Promise<Deno.CommandStatus> | Resolves when the process exits (immediate success for a dry-run stub). |
stop | stop(signal?: Deno.Signal, graceMs?: number): Promise<void> | Terminate the process and wait for it to exit. Sends signal (default SIGTERM); if the process has not exited within graceMs (default 5s), it escalates to SIGKILL so a process that ignores SIGTERM cannot hang teardown. A process that has already exited is treated as stopped. A dry-run stub (no child) is a no-op. |
function splitShellArgs(input: string): string[] Split input into argv the way a POSIX shell would, honouring the quoting rules only:
- Unquoted runs of whitespace separate arguments; leading, trailing, and repeated whitespace produce no empty arguments. - Single quotes are fully literal — no escape sequences at all, so 'a\b' yields a\b. - Inside double quotes a backslash escapes only ", \, ` `, $, and a newline; before anything else it stays literal, so "\d+" yields \d+ rather than silently losing the backslash. - Outside quotes a backslash escapes the following character, so a\ b is one argument. - A backslash-newline pair is a line continuation and is removed, both unquoted and inside double quotes; a backslash at the very end of the input is a dangling continuation and is dropped. - Adjacent segments concatenate (a"b c"d → ab cd) and a quoted empty string is a real, empty argument ('' → [""]`).
Non-goals, deliberately not implemented — the input is turned into argv, never interpreted: no variable expansion ("$HOME" stays $HOME), no globbing, no tilde expansion, no command substitution, and no operator handling of any kind (|, &&, ;, > are ordinary characters). A caller that needs those must split on them itself, or run a real shell.
\r is treated as a separator alongside space, tab, and newline so a command line read from a CRLF file cannot smuggle an invisible carriage return into an argument.
interface StateHost Injected filesystem effects for "./fs_store.ts".FileSystemStateStore, so it stays unit-testable. The default implementation is defaultStateHost.
| Member | Signature | Doc |
|---|---|---|
readText | readText(path: string): Promise<string | null> | File contents, or null when the file does not exist. |
writeText | writeText(path: string, content: string): Promise<void> | Write a file's contents, creating parent directories as needed. |
rename | rename(from: string, to: string): Promise<void> | Rename a file (used to publish a temp file atomically). |
createExclusive | createExclusive(path: string): Promise<boolean> | Create path exclusively: resolve true if it was created, false if it already existed. Used as an atomic lock marker. |
remove | remove(path: string): Promise<void> | Remove a file; a missing file is not an error. |
listDir | listDir(path: string): Promise<string[]> | The entry names in a directory, or [] when the directory is absent. |
mkdirp | mkdirp(path: string): Promise<void> | Create a directory and any missing parents. |
now | now(): number | The current time in epoch milliseconds — the clock for lock expiry (injectable for tests). |
interface StateStore Pluggable persistence for run records. version is an opaque token (an ETag or content hash) used for optimistic concurrency: a write only lands if the stored version still matches the one the writer last read, so two writers racing at the same version cannot both win.
| Member | Signature | Doc |
|---|---|---|
getRun | getRun(id: string): Promise<unknown | null> | Fetch a run and its current version, or null if it does not exist. |
putRun | putRun(record: RunRecord, expectedVersion: string | null): Promise<PutResult> | Write record only if the stored version equals expectedVersion (null meaning "must not exist yet"). Returns the new version, or a conflict when the stored version has moved on — the caller re-reads and retries. |
listRuns | listRuns(query: RunQuery): Promise<RunSummary[]> | List runs matching query, newest first (by createdAt, then id). |
deleteRun | deleteRun(id: string): Promise<void> | Delete a run permanently. A missing run is not an error (delete is idempotent). Backs zuke runs prune; on the HTTP backend this maps to a DELETE /runs/:id a server may leave unimplemented (retention there is the server's job — see docs/state-api.md). |
acquireLock | acquireLock(key: string, holder: LockHolder, ttlMs: number): Promise<LockResult> | Atomically acquire the lock key for holder, expiring after ttlMs. An expired lock is taken over. Returns a token on success, or the current holder when the lock is live. |
renewLock | renewLock(key: string, token: string, ttlMs: number): Promise<boolean> | Extend the lock key held under token by another ttlMs. Returns false if the token no longer owns it (expired and taken over), so a heartbeat can detect a lost lock. |
releaseLock | releaseLock(key: string, token: string): Promise<void> | Release the lock key if still held under token; a no-op otherwise. |
type StateStoreFactory = unknown A () => factory the kit calls once to obtain the store under test.
function stripAnsi(text: string): string Strip ANSI escape sequences, leaving the visible text.
Style type StyleName = unknown A style name understood by sgrCodes, paint, and markup.
function stylize(color: boolean, names: unknown, text: string): string Paint text in the named styles when color is enabled.
class SubcommandSettings extends ToolSettings Base for a wrapper over a CLI organised into subcommand groups — a command path built with command plus repeatable --flag [value] options built with flag. The agent and cloud wrappers (gh, gcloud, claude, gemini, codex) share this shape; each sets its binary via ToolSettings.defaultTool and, when needed, a fixed prefix via leadingTokens or between-command global flags via middleTokens.
The argv is assembled as [...leadingTokens(), ...command, ...middleTokens(), ...flags], keeping every token a discrete argv entry so command construction stays injection-free.
| Member | Signature | Doc |
|---|---|---|
command | command(...parts: Array<string | number>): this | Append command-path tokens — the group, verb, and operands — in order. |
flag | flag(name: string, value?: string | number): this | Add an arbitrary flag. With a value it renders --name value; without one the bare --name. Repeatable. |
function table(style: Style, columns: unknown, rows: unknown, options?: TableOptions): string[] An aligned text table: a styled header row, an optional dividing rule, then one line per row. Column widths fit the widest visible cell; cells may already carry ANSI colour. Rows shorter than the columns are padded with empty cells.
interface TableColumn One column of a table.
| Member | Signature | Doc |
|---|---|---|
header | header: string | The column header. |
align? | align?: left | right | Cell alignment. Defaults to left. |
interface TableOptions Options for table.
| Member | Signature | Doc |
|---|---|---|
separator? | separator?: string | Column separator. Defaults to two spaces. |
divider? | divider?: boolean | Draw a dividing rule under the header. Defaults to true. |
headerStyle? | headerStyle?: unknown | Styles for the header row. Defaults to ["bold"]. |
dividerStyle? | dividerStyle?: unknown | Styles for the divider rule. Defaults to ["dim"]. |
function tar(entries: TarEntry[]): Uint8Array Create a ustar archive from the given entries (in order).
interface TarEntry A single entry within a tar archive — a regular file or a symbolic link.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The entry's path inside the archive (≤ 100 bytes). |
data | data: Uint8Array | The file contents (empty for a symlink entry). |
linkname? | linkname?: string | For a symbolic-link entry, its target (≤ 100 bytes); absent for a regular file. Node's release tarballs, for one, ship bin/npm/bin/npx as symlinks into lib/node_modules, so extracting a runtime tree must preserve them. |
function target(): TargetBuilder Create a new, empty target builder.
type Target = TargetBuilder A configured target. Alias of TargetBuilder — the same object both builds and represents the target. Exposed as Target for use in signatures.
class TargetBuilder The fluent builder returned by target. All configuration methods are chainable and return this. A body (via TargetBuilder.executes) is required before a target can be executed.
| Member | Signature | Doc |
|---|---|---|
description | description(text: string): this | Set the human-readable description shown in zuke --list. |
dependsOn | dependsOn(...targets: Array<TargetBuilder | Group>): this | Declare hard prerequisites. References sibling targets via this.x, or a group (which expands to every member that has joined it). |
partOf | partOf(group: Group): this | Join a parallel group. Members of the same group run concurrently with one another (each still awaiting its own dependencies) even when the build is otherwise sequential. Declare the group before the targets that join it. |
inputs | inputs(...paths: PathLike[]): this | Declare input files or directories (directories are hashed recursively). A target with inputs is *incremental*: it is skipped (reported cached) when its inputs are unchanged since the last successful run and all its outputs still exist. Repeatable. |
outputs | outputs(...paths: PathLike[]): this | Declare output files or directories. A cache hit also requires every output to still exist, so deleting an output forces a rebuild. Repeatable. |
onlyWhen | onlyWhen(condition: Condition): this | Run only when condition holds; otherwise the target is skipped (and its dependents still run). The predicate may be async and can read resolved parameters or the environment. Repeatable — all conditions must hold. ``ts deploy = target() .onlyWhen(() => this.environment.value === "production") .executes(...); `` |
executes | executes(fn: TargetFn): this | Set the target body. May be async. |
before | before(...targets: TargetBuilder[]): this | Run before the listed targets if both are in the plan (soft ordering). |
after | after(...targets: TargetBuilder[]): this | Run after the listed targets if both are in the plan (soft ordering). |
triggers | triggers(...targets: TargetBuilder[]): this | Pull the listed targets into the plan and run them *after* this one. The inverse of dependsOn: running this target triggers the others. |
dependentFor | dependentFor(...targets: TargetBuilder[]): this | Declare this target as a prerequisite of the listed targets — the reverse of dependsOn: each listed target gains this one as a dependency, so this runs before them. Declare the listed targets above this one. |
requires | requires(...params: AnyParameter[]): this | Require that the given parameters resolve to a value before this target runs; otherwise the target fails with a message naming the missing one. Use it when a target needs a parameter that is optional build-wide. |
proceedAfterFailure | proceedAfterFailure(): this | Keep running the rest of the build even if this target fails. The build still reports failure, and this target's own dependents are skipped. |
unlisted | unlisted(): this | Hide this target from --list and --help (it can still be run by name). |
readOnly | readOnly(): this | Mark this target query-only for MCP: its run: tool advertises MCP's readOnlyHint instead of the default destructiveHint, and it is exempt from --confirm-destructive. A hint about intent only — the target still runs its real body — so declare it on targets that inspect rather than mutate (a status check, a report). |
always | always(): this | Run this target even after the build has failed — for cleanup/teardown that must happen regardless. It still waits for its own dependencies to complete; the build's overall result is unchanged. Repeatable conditions/inputs apply. |
dryRunnable | dryRunnable(): this | Run this target's body under --dry-run instead of skipping it, with the $ shell in echo mode: each command (awaited or .spawn()ed) prints its resolved argv and returns an empty success without starting a process. Opt-in, because Zuke can only intercept $/"./shell.ts".Command — any *other* side effect a body performs (writing a file, calling an API directly) still happens under a dry run. Use it for bodies that are shell-command orchestration, to preview the exact commands a real run would execute. Without it, a dry run skips the body entirely (the default). Because an echoed command returns empty stdout and exit code 0, a body whose *control flow or command arguments* depend on a command's output (await $\git rev-parse HEAD\.text(), a .code() loop) should branch on the "./executor.ts".TargetContext dryRun flag rather than trust the echoed result. |
cacheKey | cacheKey(fn: unknown): this | Contribute an extra value to this target's cache fingerprint, beyond its input files — e.g. a parameter value, tool version, or git commit. The target is up-to-date only when its inputs *and* every cache key are unchanged. The function may be async. Repeatable. ``ts compile = target() .inputs("src") .cacheKey(() => this.configuration.value) .executes(...); `` |
produces | produces(...paths: PathLike[]): this | Declare artifact files/directories this target produces (metadata). |
consumes | consumes(...targets: Array<TargetBuilder | Group>): this | Depend on the listed targets and consume their artifacts: equivalent to dependsOn for ordering, expressing that this target uses what they produces. |
whenSkipped | whenSkipped(behavior: run-dependencies | skip-dependencies): this | When this target is skipped by an onlyWhen condition, also skip its dependencies that no other planned target needs. Because the dependencies would otherwise run first, the condition is evaluated up front, so it must not depend on state produced by other targets during the run. |
timeout | timeout(ms: number): this | Fail the target if its body runs longer than ms milliseconds (per attempt). |
retry | retry(times: number, delayMs?: number): this | Retry the target body up to times more attempts on failure, optionally pausing delayMs between attempts. Combined with timeout, each attempt is bounded by the timeout. |
validateBefore | validateBefore(...validations: Validation[]): this | Run one or more Validations *before* the target body. Each runs in declaration order; the first to throw fails the target and the body never runs. Repeatable. A cached/skipped target runs no validations. ``ts deploy = target() .validateBefore(this.securityReview) // gate before deploying .executes(...); `` |
validateAfter | validateAfter(...validations: Validation[]): this | Run one or more Validations *after* the target body completes successfully. Each runs in declaration order; the first to throw fails the target. Repeatable. |
recoverWith | recoverWith(...remediations: Remediation[]): this | Attach one or more Remediations that run only if the body fails. Each is given the failure; if any returns { retry: true }, the executor re-runs the body and, when it now passes, the target succeeds. This is the hook the AI fixer in @zuke/ai uses for self-healing builds. Repeatable. ``ts test = target() .executes(() => DenoTasks.test((s) => s.allowAll())) .recoverWith(aiFixer((f) => f.provider("claude").apiKey(this.key))); `` |
recoverAttempts | recoverAttempts(times: number): this | The maximum number of fix-then-rerun cycles attempted when the body fails and recoverWith remediations are configured (default 1). Each cycle runs every remediation, then re-runs the body once; the count bounds how many times that repeats before the failure is final. Clamped to at least 1. |
lock | lock(configure: Configure<LockSettings>): this | Hold a cross-run lock while this target runs: only one run may hold key at a time, so a second run that tries to acquire it fails with a "./state/lock.ts".LockConflictError naming the current holder. The lock is released when the target settles — success, failure, or cancellation — and expires after options.ttl as a backstop should the holder be killed (a live holder renews it as it runs). key may be a thunk, evaluated after parameters resolve, so it can depend on this.<param>.value; compose composite keys with "./state/lock.ts".lockKey. Requires a state store (a build that uses .lock() gets a .zuke/runs filesystem store by default). ``ts promote = target() .lock((s) => s.lockKey("deploy", this.repo.value) .withTtl("4h") .onConflict((h) => ${this.repo.value} is being deployed by ${h.actor} (run ${h.runId}).)) .executes(...); `` |
waitsFor | waitsFor(configure: Configure<WaitSettings>): this | Suspend the run at this target until an external event occurs, then let the run be resumed later (in a different process) — a settings lambda in the same style as lock. The target is a gate (no body): when its trigger is already satisfied it passes and dependents run; otherwise the run's state is saved, the run is marked suspended, its independent branches finish, and the process exits 0. Requires a state store. ``ts awaitApproval = target() .dependsOn(this.deploy) .waitsFor((s) => s.on(externalSignal("testing-approved")) .timeout("72h") .onTimeout(() => this.rollback)); `` |
onCancel | onCancel(compensation: OnCancel): this | Register a compensation target that undoes this target's effect when the run is later cancelled (via zuke cancel <run-id>, an MCP cancel_run, or a timed-out wait). The compensation runs iff this target succeeded — a target that never ran, was skipped, or failed has nothing to undo. On cancellation, compensations run in reverse order of the targets that succeeded, so later work is unwound before the work it built on. compensation is a sibling target, or a thunk returning one (use the thunk form to reference a target declared *below* this one — class fields initialise top-to-bottom). The compensation body receives a normal TargetContext whose state exposes this target's persisted metadata, so a deploy that recorded { slot: "sit-7" } in ctx.state can be rolled back from exactly that slot. Compensation failures are recorded but do not stop the walk (cleanup is maximal). Requires a state store. On a forEach sub-target, the compensation is per item: cancel runs it for every item that had succeeded (or was still in-flight), each with its own item-scoped context — see the fan-out section of docs/orchestration.md. ``ts deploy = target() .executes((ctx) => ctx.state.set({ slot: "sit-7" })) .onCancel(() => this.rollback); rollback = target() .executes((ctx) => tearDown(ctx.state.get().slot)); // reads deploy's meta `` |
forEach | forEach(items: unknown, factory: ForEachFactory<Item>, configure?: Configure<ForEachSettings>): this | Fan out over a runtime list: for each item, build an ordered pipeline of sub-targets and run them with per-item failure isolation and bounded concurrency. items is a thunk (evaluated when the target runs, so it can read this.<param>.value); factory returns a record of sub-targets per item, each implicitly depending on the one before it. Items run concurrently, each item's stages sequentially — the pipeline model. The sub-targets are materialised at run time (named parent[item].stage) — --list/graph show only the one fan-out node — and each is a first-class target with its own status in the summary and the run record. The fan-out target fails if any item's pipeline fails. A fan-out cannot contain a wait gate: neither the fan-out target itself nor any stage may use waitsFor — a materialised sub-target has no resume path, so the gate would be silently swallowed. Combining them fails the target with guidance. Gate a fan-out by putting the wait on a separate target that the fan-out .dependsOn(...). ``ts deployBatch = target() .forEach( () => this.repos.value, // string[] (repo) => ({ checks: target().executes(() => checkDeployable(repo)), deploy: target().executes((ctx) => applyToSit(repo, ctx)), }), (s) => s.concurrency(3).continueOnItemFailure(), ); `` |
description_? | description_?: string | Human-readable summary shown in --list. |
dependsOn_ | dependsOn_: TargetBuilder[] | Hard prerequisites: these run (transitively) before this target. |
before_ | before_: TargetBuilder[] | Soft ordering: this runs before the listed targets if both are planned. |
after_ | after_: TargetBuilder[] | Soft ordering: this runs after the listed targets if both are planned. |
fn_? | fn_?: TargetFn | The target body. |
name_? | name_?: string | Property name, assigned during discovery. Undefined until then. |
group_? | group_?: Group | The parallel batch this target belongs to, if any (set by partOf). |
inputs_ | inputs_: string[] | Input files/directories whose contents key the cache (set by inputs). |
outputs_ | outputs_: string[] | Output files/directories that must exist for a cache hit (set by outputs). |
onlyWhen_ | onlyWhen_: Condition[] | Conditions gating execution; all must hold or the target is skipped. |
triggers_ | triggers_: TargetBuilder[] | Targets pulled in and run after this one (set by triggers). |
requires_ | requires_: AnyParameter[] | Parameters that must be set for this target (set by requires). |
proceedAfterFailure_ | proceedAfterFailure_: boolean | Continue the build if this target fails (set by proceedAfterFailure). |
always_ | always_: boolean | Run even after the build has failed (set by always). |
unlisted_ | unlisted_: boolean | Hide this target from --list/--help (set by unlisted). |
readOnly_ | readOnly_: boolean | Advertise this target as query-only over MCP (set by readOnly). |
dryRunnable_ | dryRunnable_: boolean | Run this target's body under --dry-run with $ echoed (set by dryRunnable). |
cacheKeys_ | cacheKeys_: Array<unknown> | Extra cache-key contributors beyond input files (set by cacheKey). |
produces_ | produces_: string[] | Artifact paths this target produces (set by produces). |
skipDependencies_ | skipDependencies_: boolean | When skipped by a condition, also skip dependencies (set by whenSkipped). |
timeout_? | timeout_?: number | Per-attempt timeout in milliseconds, if set by timeout. |
retries_ | retries_: number | Number of extra attempts on failure, set by retry. |
retryDelay_ | retryDelay_: number | Delay between retry attempts in milliseconds. |
validateBefore_ | validateBefore_: Validation[] | Validations run before the body (set by validateBefore). |
validateAfter_ | validateAfter_: Validation[] | Validations run after the body (set by validateAfter). |
recoverWith_ | recoverWith_: Remediation[] | Remediations run after the body fails (set by recoverWith). |
recoverAttempts_ | recoverAttempts_: number | Max fix-then-rerun cycles when the body fails (set by recoverAttempts). |
lock_? | lock_?: Configure<LockSettings> | Cross-run lock settings lambda, set by lock and run after params resolve. |
waitsFor_? | waitsFor_?: Configure<WaitSettings> | External-event wait settings lambda, set by waitsFor and run when reached. |
forEach_? | forEach_?: ForEachSpec | Fan-out spec, set by forEach: materialises per-item sub-target pipelines. |
onCancel_? | onCancel_?: unknown | Compensation thunk, set by onCancel: runs on cancel iff this target succeeded. |
interface TargetContext The context passed to every target body. Optional to receive — an existing zero-argument .executes(() => …) stays valid, since a zero-argument function is assignable to this one-parameter type — but a body that wants the run's identity, a cancellation signal, or durable per-target state reads them here.
| Member | Signature | Doc |
|---|---|---|
stateOf | stateOf(target: string): TargetStateHandle | The durable state handle of another target in this run — the seam a body reads a dependency's published metadata through (e.g. the result a .waitsFor(githubWorkflow(...)) gate recorded to its state). stateOf(this target) is equivalent to state. It reads the run's current record, so it sees writes a dependency made earlier in the run — including across a suspend/resume, since the record is durable. |
runId | runId: string | Unique ID of this run, stable for every target in the run. |
target | target: string | Dotted name of the executing target. |
signal | signal: AbortSignal | Aborted when the run is cancelled (see "./executor.ts".ExecuteOptions signal). Pass it to a shell command's .signal() to have that command terminated on cancellation; the executor also applies it as the shell's ambient default, so a plain $ in the body is terminated too. |
state | state: TargetStateHandle | Durable per-target metadata. Persisted to the run's state store when one is configured (see "./state/store.ts".StateStore), and an in-memory no-op otherwise. The carrier for state that must survive across a suspend/resume boundary — do not put secrets in it. |
signals | signals: ReadonlyMap<string, SignalRecord> | Payloads of the external signals received so far, keyed by name (see .waitsFor(...) and "./wait.ts".externalSignal). Empty until a signal is delivered by zuke resume <id> --signal <name>. |
dryRun | dryRun: boolean | True when the run is a dry run (bodies do not execute under a dry run). |
type TargetFn = unknown The executable body of a target. May be synchronous or asynchronous, and any returned value is ignored — so a body can return a tool-wrapper call directly (.executes(() => DenoTasks.lint()), which resolves to a CommandOutput) without wrapping it in an async block just to discard the result. A single returned promise is awaited before dependents run; a returned *array* of promises is not (it is not a thenable), so await Promise.all([...]) inside the body when you fan work out, rather than returning the array.
interface TargetReport One row of the end-of-build summary.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The target's name. |
status | status: TargetStatus | The target's terminal status. |
ms | ms: number | The target's wall-clock duration in milliseconds. |
interface TargetRunState The recorded progress of a single target.
| Member | Signature | Doc |
|---|---|---|
status | status: TargetRunStatus | The target's current status within the run. |
meta | meta: Record<string, JsonValue> | Durable metadata written via "../target.ts".TargetStateHandle. |
startedAt? | startedAt?: string | ISO-8601 timestamp when the body started, if it has. |
endedAt? | endedAt?: string | ISO-8601 timestamp when the target settled, if it has. |
error? | error?: string | The failure message when status is failed. |
waitingFor? | waitingFor?: WaitState | The pending wait when status is waiting (set by .waitsFor(...)). |
type TargetRunStatus = pending | running | waiting | succeeded | failed | skipped The status of one target within a run record. waiting (a suspended external-event wait) is produced only from a later milestone; the executor records the others.
interface TargetStateHandle A target's durable, per-target metadata, surfaced on TargetContext as state. Writes are persisted to the run's state store (see "./state/store.ts".StateStore) and are visible to later runs — e.g. a resuming process reading what a suspended target recorded. When no store is configured, the handle is an in-memory no-op scoped to the current run.
Never store a secret here — state is persisted in plain JSON and read back by later runs and by zuke runs show.
| Member | Signature | Doc |
|---|---|---|
set | set(patch: Record<string, JsonValue>): Promise<void> | Merge a JSON patch into this target's persisted metadata (awaits the write). |
get | get(): Record<string, JsonValue> | Read this target's persisted metadata (from prior attempts/runs too). |
type TargetStatus = passed | failed | skipped | cached | waiting The outcome of a single target, reported in the summary and lifecycle hooks. waiting marks a .waitsFor(...) gate whose event has not occurred — the run suspends there.
interface TargetTiming Timing for a settled target, passed to Plugin.onTargetEnd.
| Member | Signature | Doc |
|---|---|---|
runId | runId: string | The run id (see RunInfo). |
durationMs | durationMs: number | The target's wall-clock duration in milliseconds (0 for skipped/cached). |
async function tcpReachable(address: string): Promise<boolean> Whether a TCP host:port is accepting connections — the usual readiness probe for a server. Resolves true once a connection succeeds (it is closed immediately), false while the port is still refused/unreachable, so it plugs straight into ServiceBuilder.readyWhen.
.readyWhen(() => tcpReachable("localhost:5432"))class TeamsAnnouncementSettings extends AnnouncementSettings Fluent settings for AnnounceTasksApi.teams. Bot mode (.bot().token(t).team(id).channel(c)) posts through Microsoft Graph with a bearer token.
| Member | Signature | Doc |
|---|---|---|
team | team(team: string): this | Set the Teams team (group) id to post to in bot mode (Microsoft Graph). |
function tokenize(strings: ReadonlyArray<string>, values: ReadonlyArray<Interpolatable>): string[] Tokenise a tagged-template invocation into an argv array.
Literal whitespace separates arguments; interpolated values are appended as atomic tokens (so --flag=${x} and pre${x} work), and arrays expand to one argument per element. Interpolated values are never re-split on whitespace, which is what keeps command construction injection-free.
function toolchain(configure?: unknown): Toolchain Create a Toolchain. Configure it inline with a callback, or chain Toolchain.tool on the returned instance.
const tools = toolchain((t) =>
t.tool((s) => s.name("helm").url(helmUrl))
.tool((s) => s.name("kubectl").url(kubectlUrl))
);class Toolchain A declared set of external tools. Add tools with Toolchain.tool (a ToolInstallSettings lambda) and fetch them all with Toolchain.install. Build one with toolchain.
| Member | Signature | Doc |
|---|---|---|
tool | tool(configure: Configure<ToolInstallSettings>): this | Add a release tool, configured through a settings-lambda. Chainable. |
tree | tree(configure: Configure<ToolInstallSettings>): this | Add a multi-file runtime tree (see ToolTasksApi.installTree), configured through a settings-lambda with .strip(...)/.bins(...). In install's result its entry is the extracted tree's root — a callable AbsolutePath, so root("bin") is the directory to put on PATH. Chainable. |
npm | npm(spec: NpmToolSpec): this | Add an npm-registry package to provision as a version-pinned tool — installed under <destDir>/npm/<name>@<version> and keyed in install's result by its NpmToolSpec.name. See installNpmTool. Chainable. |
tools | tools(): unknown | The configured release tools, in declaration order. |
trees | trees(): unknown | The configured runtime trees, in declaration order. |
npmTools | npmTools(): unknown | The configured npm-package tools, in declaration order. |
install | install(options?: ToolchainInstallOptions): Promise<Map<string, AbsolutePath>> | Install every declared tool concurrently — reusing a cached copy where a release tool's or tree's pinned checksum, or an npm tool's name@version marker, matches — and return a map of tool name to installed AbsolutePath. A tree's entry is its extracted root directory. |
interface ToolchainInstallOptions Options for Toolchain.install.
| Member | Signature | Doc |
|---|---|---|
destDir? | destDir?: PathLike | Where tools without their own destDir install. Defaults to .zuke/tools. |
download? | download?: DownloadFn | The download implementation for every release tool (defaults per installRelease). |
npmRun? | npmRun?: NpmRunner | The npm-install runner for npm-package tools (defaults to the ambient npm; a test seam). |
class ToolInstallSettings Fluent settings for installing a release tool. Configure it in a settings-lambda ((s) => s.name(...).url(...)), the same shape as Zuke's tool wrappers. name and url are required; everything else is optional and mirrors InstallReleaseOptions.
| Member | Signature | Doc |
|---|---|---|
name | name(name: string): this | The tool name; also the installed binary's filename (.exe on Windows). |
url | url(resolve: unknown): this | Resolve the download URL for the target Platform. |
destDir | destDir(dir: PathLike): this | The directory to install the binary into (created if missing). |
archive | archive(format: raw | tar.gz | zip): this | Treat the download as a "tar.gz" or "zip" to unpack (default "raw", the bare binary). Pair with binaryPath for the binary inside. |
binaryPath | binaryPath(path: string): this | For an archive, the binary's path within it (defaults to the name). |
strip | strip(components: number): this | For a tree install (ToolTasksApi.installTree / Toolchain.tree), drop this many leading path components while unpacking — 1 unwraps a release tarball's tool-v1.2.3/ directory. Ignored by a single-binary install. |
bins | bins(...paths: string[]): this | For a tree install, the paths (relative to the stripped root) to mark executable on POSIX — a runtime's bin/node, bin/npm, … Ignored by a single-binary install. |
checksum | checksum(sha256: string | unknown): this | The expected SHA-256 (hex) of the downloaded artifact — verifies and caches the install. Pass a ({ os, arch }) => string resolver to pin it per platform (see InstallReleaseOptions.checksum). |
platform | platform(platform: InstallPlatform): this | Resolve for a specific platform instead of the host (a foreign install). |
download | download(fn: DownloadFn): this | Override the downloader (defaults to an HTTPS download; a test seam). |
options_ | options_(fallbackDestDir: PathLike): InstallReleaseOptions | Build the InstallReleaseOptions, using fallbackDestDir when no destDir was set. Throws if a required field is missing. |
treeOptions_ | treeOptions_(fallbackDestDir: PathLike): InstallTreeOptions | Build the InstallTreeOptions for a tree install, using fallbackDestDir when no destDir was set. A tree always ships packed, so the archive defaults to "tar.gz" and "raw" is rejected. Throws if a required field is missing. |
name_? | name_?: string | The tool name, and the installed filename. Set by name. |
url_? | url_?: unknown | Resolves the per-platform download URL. Set by url. |
destDir_? | destDir_?: PathLike | Install directory (overrides the toolchain's). Set by destDir. |
archive_? | archive_?: raw | tar.gz | zip | Download format. Set by archive. |
binaryPath_? | binaryPath_?: string | The binary's path within a tar.gz. Set by binaryPath. |
strip_? | strip_?: number | Leading path components to strip on a tree install. Set by strip. |
bins_? | bins_?: string[] | Executable bins within a tree install. Set by bins. |
checksum_? | checksum_?: string | unknown | Expected SHA-256 (or a per-platform resolver). Set by checksum. |
platform_? | platform_?: InstallPlatform | The platform to resolve for. Set by platform. |
download_? | download_?: DownloadFn | The download implementation. Set by download. |
class ToolNotFoundError extends Error Raised when a tool's binary cannot be found on the system.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
type ToolResolution = node_modules | path How ToolSettings.run locates a wrapper's binary when no explicit ToolSettings.toolPath is set:
- "path" — spawn the bare tool name and let the OS resolve it on PATH (the default, matching a native/global install); - "node_modules" — npx-style: walk up from the working directory looking for node_modules/.bin/<tool>, falling back to PATH on a miss (so a package hoisted to a monorepo root runs with no .toolPath()).
class ToolSettings Abstract fluent base for tool settings. Subclasses provide the binary (defaultTool) and the pure subcommand argv (buildArgs); the base provides the shared chainers and run.
| Member | Signature | Doc |
|---|---|---|
env | env(record: Record<string, string>): this | Merge additional environment variables for the process. |
cwd | cwd(path: PathLike): this | Set the working directory for the process. |
noThrow | noThrow(): this | Do not throw on a non-zero exit; inspect code on the output instead. |
throwsOnError | throwsOnError(): boolean | Whether a failure should throw — the default, or false after noThrow. A task that layers its own validation on top of the subprocess (e.g. a coverage-threshold gate) reads this to decide whether a gate failure throws or is merely reported. |
quiet | quiet(): this | Suppress live stdout/stderr streaming to the terminal. |
killAfter | killAfter(ms: number): this | Kill the tool if it runs longer than ms milliseconds, raising a CommandTimeoutError. Fires even under noThrow. |
maxCapturedBytes | maxCapturedBytes(bytes: number): this | Cap how much of each captured stream the run keeps in memory, in bytes (default 8 MiB). Once the cap is reached the oldest bytes are dropped, CommandOutput.truncated is set, and CommandOutput.text prefixes a notice. Raise it for a tool whose whole output must be parsed — a coverage report, a --json dump — and lower it to bound a chatty one. Live streaming to the terminal is never capped. |
toolPath | toolPath(path: PathLike): this | Override the binary to run (e.g. an absolute path to the tool). |
fromNodeModules | fromNodeModules(): this | Resolve the binary npx-style: walk up from the working directory looking for node_modules/.bin/<tool>, falling back to PATH on a miss. Overrides both the wrapper default and the ambient ZUKE_TOOL_RESOLUTION. Has no effect once toolPath is set (an explicit path always wins). |
fromPath | fromPath(): this | Resolve the binary from PATH only, ignoring any node_modules/.bin. |
args | args(...extra: Array<string | number | AbsolutePath>): this | Escape hatch: append raw arguments after all typed options. |
argv | argv(): string[] | The full argv (binary first). Pure — useful for tests and diagnostics. |
resolvedArgv | resolvedArgv(): string[] | The argv run will actually spawn — like argv, but with the node_modules/.bin resolution applied (so it performs I/O). Useful for tests and diagnostics: it reveals whether a wrapper resolved to a local shim or fell back to the bare name on PATH. |
run | run(): Promise<CommandOutput> | Run the configured tool. If the binary is missing and the platform is Windows, retry once through cmd /c (covers .cmd/.bat shims); otherwise raise a ToolNotFoundError naming the tool. |
os_ | os_: Deno.build.os | The platform identifier used by run to decide whether to retry a missing binary through the cmd /c shim path (Windows only). In production this is always Deno.build.os. It is exposed as a public field — rather than read from Deno.build.os inline — so that tests can pin a specific platform without spawning a subprocess or touching the environment: ``ts const s = new MyToolSettings(); s.os_ = "windows"; // exercise the cmd /c retry branch on any host `` The trailing underscore signals an internal test seam: do not rely on this field in production code. |
type ToolTask = unknown A ready-to-run task for a defineTool tool.
const ToolTasks: ToolTasksApi Provision external CLIs from a build. ToolTasks.install((s) => …) fetches a single release binary and ToolTasks.npm(...) a single npm package; group several of either with toolchain.
interface ToolTasksApi The task surface of ToolTasks.
| Member | Signature | Doc |
|---|---|---|
install | install(configure: Configure<ToolInstallSettings>): Promise<AbsolutePath> | Install a single release tool, configured through a ToolInstallSettings lambda, and resolve to its installed path. Defaults the install directory to .zuke/tools. |
installTree | installTree(configure: Configure<ToolInstallSettings>): Promise<AbsolutePath> | Install a multi-file runtime tree (Node.js, a JDK, …) from one archive, configured through a ToolInstallSettings lambda, and resolve to the extracted tree's root AbsolutePath. Because that path is callable, root("bin", "node") is a binary and root("bin") a directory to put on PATH. Use .strip(...) and .bins(...); defaults the install directory to .zuke/tools. See installTree; group several with Toolchain.tree. |
npm | npm(spec: NpmToolSpec, options?: InstallNpmToolOptions): Promise<AbsolutePath> | Provision a single npm-registry package as a version-pinned, cached tool and resolve to its installed bin path. Defaults the install root to .zuke/tools. See installNpmTool; group several with Toolchain.npm. |
function untar(archive: Uint8Array): TarEntry[] Extract the entries from a tar archive — regular files, symlinks, and directories. A path longer than the 100-byte name field is reconstructed from whichever long-name form the archive uses: the POSIX ustar prefix split, GNU tar's @LongLink pseudo-entries (typeflags 'L' name / 'K' link target, whose *data* is the following member's value — Node's Linux release tarballs use this), or pax extended headers (typeflag 'x', with path=/linkpath= records — bsdtar/macOS use this). These metadata pseudo-entries accumulate onto the next real member, matching GNU/bsdtar, so a mixed archive is read correctly; a pax record wins over a GNU long name, which wins over the header's own fields.
async function unzip(archive: Uint8Array): Promise<TarEntry[]> Read the entries of a .zip archive, decompressing stored and deflate members. The central directory is the source of truth. Directory entries (a trailing /) are skipped. Encrypted, zip64, or otherwise-compressed entries throw a friendly error naming the offending entry, and a header or data field that runs past the archive is reported as a malformed zip (not a raw out-of-bounds error). Every offset read from the archive is bounds-checked; for integrity against a tampered download, pin a .checksum(...), which is verified before the archive is ever parsed.
function validateGraph(targets: Map<string, TargetBuilder>): void Validate the whole graph: unknown references first, then cycles.
interface Validation A check plugged into a target with TargetBuilder.validateBefore or TargetBuilder.validateAfter. The target decides *when* it runs; the validation decides *what* it checks. Throw from Validation.validate to fail the target (and break the build). Implemented, for example, by the AI reviewers in @zuke/ai, but any object with a validate method qualifies.
| Member | Signature | Doc |
|---|---|---|
validate | validate(context: ValidationContext): void | Promise<void> | Run the check; throw to fail the target. May be async. |
name? | name?: string | A name for diagnostics (optional). |
interface ValidationContext Context passed to a Validation when it runs.
| Member | Signature | Doc |
|---|---|---|
target | target: string | The name of the target the validation is attached to. |
function visibleWidth(text: string): number The printable width of text, ignoring any ANSI colour codes it carries.
interface WaitContext The durable context a WaitTrigger may use while deciding whether its event has occurred. Its WaitContext.state handle is the awaiting target's persisted metadata — it survives a suspend/resume, even across processes — so a stateful trigger (e.g. "dispatch a GitHub workflow, then poll it") can remember what it started and hand a result to the target's body. The built-in triggers ignore it.
| Member | Signature | Doc |
|---|---|---|
state | state: TargetStateHandle | The awaiting target's durable state handle (the same one its body receives as ctx.state). Reads and writes here persist with the run and are visible to a later resume in another process. |
runId | runId: string | The run id — stable across a resume, so a natural correlation key. |
target | target: string | The awaiting target's dotted name. |
type WaitDisposition = fail | cancel-run | unknown What a timed-out wait does: fail, cancel the run, or run a compensation target.
class WaitSettings Fluent configuration for TargetBuilder.waitsFor: .waitsFor((s) => s.on(externalSignal("approved")).timeout("72h")). Set the trigger, an optional WaitSettings.timeout, and an optional WaitSettings.onTimeout disposition. The lambda runs when the target is reached, so the trigger may read this.<param>.value.
| Member | Signature | Doc |
|---|---|---|
on | on(trigger: WaitTrigger): this | Set the "./wait.ts".WaitTrigger the wait is satisfied by. |
timeout | timeout(duration: string | number): this | Give the wait a deadline (a duration like "72h" or milliseconds). |
onTimeout | onTimeout(disposition: OnTimeout): this | What to do when the deadline passes: a thunk returning a sibling compensation target (a thunk, so it can reference a target declared *below* this one), or the string "fail" / "cancel-run". Defaults to "fail". |
trigger_? | trigger_?: WaitTrigger | The trigger deciding when the wait is satisfied; set by on. |
timeout_? | timeout_?: string | number | The deadline duration (string or ms); set by timeout. |
onTimeout_? | onTimeout_?: OnTimeout | The timeout disposition thunk; set by onTimeout. |
interface WaitState The pending wait recorded on a suspended target (see TargetRunState.waitingFor).
| Member | Signature | Doc |
|---|---|---|
trigger | trigger: string | A human-readable descriptor of what is awaited (e.g. signal:approved). |
deadline? | deadline?: string | ISO-8601 deadline after which onTimeout applies, if a timeout was set. |
onTimeout | onTimeout: WaitDisposition | What happens when the deadline passes. |
interface WaitTrigger Decides whether the event a target waits for has occurred. descriptor is a short, JSON-safe label recorded on the suspended target; isSatisfied is evaluated against the run's received signals (and a durable WaitContext) when the target is reached and again on each resume attempt.
| Member | Signature | Doc |
|---|---|---|
isSatisfied | isSatisfied(signals: ReadonlyMap<string, SignalRecord>, context: WaitContext): boolean | Promise<boolean> | Whether the awaited event has occurred, given the run's received signals and a durable WaitContext. The context lets a trigger persist correlation state across a suspend/resume; a trigger that only inspects signals may ignore it (fewer parameters stay assignable). |
descriptor | descriptor: string | A short label recorded on the wait (e.g. signal:approved). |
pollIntervalMs? | pollIntervalMs?: number | Poll interval hint (ms) for predicate triggers driven by zuke resume --check. |
function windowsCmdShim(argv: ReadonlyArray<string>, os: Deno.build.os): string[] On Windows, spawn a resolved .cmd/.bat shim (such as npm's node_modules shims) through cmd /c — a batch shim is not a PE executable, so Deno.Command cannot launch it directly. Returns argv unchanged on other platforms or when the binary is not a batch shim.
interface WrapperConformanceOptions Options for assertWrapperConformance.
| Member | Signature | Doc |
|---|---|---|
resolution | resolution: ToolResolution | The resolution strategy the wrapper must use when nothing overrides it: "node_modules" for a JS-ecosystem tool installed under node_modules, "path" for a natively installed one. Required, with no default: an npm-distributed wrapper that forgot to override defaultResolution() is exactly the bug this kit exists to catch, and a default would let that wrapper's test pass by saying nothing. |