v0 · Developer Preview Frond is under active development. APIs may change between releases.

Dependencies

A node declares the other nodes it needs. Each declaration is a graph edge pointing at a dependency’s node id. The runtime resolves dependencies to ready node instances and passes them to the driver.

Request the leaf resources and readiness loads by layer: auth/session first, then the services that depend on it, then the resources on top. A node cannot become ready until every node it depends on is ready.

Declaring dependencies

Declare dependencies on the spec with Frond.dependencies. Each entry is a Frond.dep(spec, args).

static readonly spec = Frond.facadeSpec<DashboardSpec>({
  tag: Frond.tag("dashboard/summary"),
  key: () => Frond.Key.singleton(),
  dependencies: Frond.dependencies(() => ({
    profile: Frond.dep(CurrentUserProfileNode, Frond.Args.none),
    weather: Frond.dep(WeatherNode, Frond.Args.none),
  })),
  driver: Frond.Driver.Async<DashboardSpec>({
    acquire: Frond.Driver.Acquire((ctx) =>
      buildDashboard(ctx.deps.profile.result, ctx.deps.weather.result)
    ),
  }),
});
  • Frond.dependencies((args) => ({ ... })) takes the node’s args and returns a record of dependencies.
  • Frond.dep(spec, args) names one dependency: which node spec, and the args to resolve its id.
  • Frond.Args.none ({}) is the args for a node that takes none.

The driver receives resolved dependencies as ctx.deps. Each entry is the dependency’s ready node instance, so ctx.deps.profile.result reads the current-user profile node’s result. The keys match the declaration (profile, weather).

Edges

A dependency resolves to a node id (tag:key) the same way any read does. Two nodes that depend on AuthSessionNode with Frond.Args.none point at the same node id, so they share one session node, one fetch, one instance.

dependencies: Frond.dependencies(() => ({
  session: Frond.dep(AuthSessionNode, Frond.Args.none),
})),

See Identity and keys for how ids are computed.

Readiness flows across edges

A node waits for dependency readiness before its own acquire runs. It cannot become Ready until every dependency is Ready.

profile: Ready ─┐
                ├─> dashboard: acquire runs -> Ready
weather: Ready ─┘

If a dependency fails readiness, the dependent enters Error with that failure instead of acquiring. Sibling dependency failures aggregate into a typed dependency failure, so parallel errors are not lost. See Lifecycle.

Static topology

The dependency set is part of the node’s wiring, fixed for a given node id. updateArgs() reconciles args only when they resolve to the same id and the same dependency set. Args that would change the dependency topology fail the update; request the other node instead.

A cycle in the dependency graph, a duplicate tag, or an invalid dependency declaration is a planning failure — the node becomes invalid rather than acquiring.

Eviction cascades to dependents

Evicting a node evicts its reverse dependency closure: every node that depends on it, directly or transitively.

await runtime.client.node(AuthSessionNode, Frond.Args.none).evict();
// evicts the session node and every node that depends on it
ModeEffect
selfAndDependentsEvict the node and its reverse dependency closure. Default.
dependentsEvict the reverse closure, leave the node itself.

Eviction orders deeper dependents before the nodes they depend on. See Eviction and release.


Next: Eviction and release — what cleanup runs, and how the two differ.