Skip to content

Expressions

Any string scalar in spec (or in needs) may contain ${ expression }. The expression is JavaScript, evaluated by an embedded engine with no access to time, randomness, files or the network, because results are memoized.

A string that is exactly one expression takes the expression’s value with its type: "${ port.web.status.value }" renders to the number 3010. Otherwise each expression is stringified into the string: "http://localhost:${ port.web.status.value }/api".

name value
self { kind, name, spec } of the resource being rendered (spec raw, unrendered)
<kind>.<name> { kind, name, spec, status } of another resource in the same namespace; status is null until the kind publishes it
ns.<namespace>.<kind>.<name> the same for a resource of another namespace
workspace { name, cwd }
env the daemon’s environment variables, snapshotted at start and on workspace restart

A name that is not a JavaScript identifier needs brackets: process["chalk-wasm"].status.exit_code, ns["my-ns"].process["x"]. Without them process.chalk-wasm is a subtraction.

kind: process
name: client
needs: ['${ process["wasm-build"].status.exit_code === 0 }']
spec:
cmd: pnpm
args: [dev, --port, "${ port.web.status.value }"]
env:
API_URL: http://localhost:${ ns.notes.port.api.status.value }
HOME: ${ env.HOME }

A reference is a static member chain rooted at a kind name or ns: port.web.status.value, folder.cache.status.path. They are extracted from the source before evaluation, which is what makes the dependency tracking fine-grained: a resource re-renders when exactly the statuses it reads change. Computed access such as port[name] is not supported.

Every reference is checked against the kinds’ schemas before anything renders, whether the target’s status is published or not. After the resource name comes kind, name, spec or status, then the fields that kind declares. A misspelled field is an Error condition naming the reference and the known fields:

port.web.status.vlaue: unknown field `vlaue` (fields: url, urls, value)

Segments below a scalar (.toString(), .length), the keys of a map such as env, and a process’s restart (whose keys depend on its kind) are not checked.

A read of a status that is null makes the resource Pending with that reference in waiting_on. The reader is applied once the status is published and re-rendered whenever it changes. A status field that does not exist yet (a process’s exit_code while it runs) is undefined.

Reading a resource of a namespace that is not active waits the same way, and waiting_on says so:

notes/port/web.status (namespace `notes` is not active)

A reference cycle (a reads b reads a) is an Error naming the path.

needs is a list of expressions next to kind, name and spec. The resource stays Pending until each evaluates to a truthy value; the ones that do not are listed in waiting_on.

needs:
- ${ process.build.status.exit_code === 0 } # the one-shot build finished cleanly
- ${ process.api.status.health.web.ok } # the API answers its health check

Since a missing field is undefined, the first entry waits for the build to exit and the second for the check to pass. A needs that turns false later (the API went unhealthy) makes the resource Pending again, and it keeps running on its last spec.