Reference · 05

Components, tasks and triggers.

Reusable parts for define({ uses }), background work, and reactions to changed rows.

Components

A component packages parts for define({ uses }). The components guide walks through a complete one.

APIContract
component(parts?: ComponentParts): ComponentFreezes the parts into a component. Every field is optional and must be an array; unknown fields are rejected.
ComponentPartsOptional uses (nested components), collections, definitions, tasks, triggers and keys.
ComponentReadonly kind: "component" and each part as a frozen array.
UsableWhat uses accepts: a Component, or an object with a component property, such as an external value.
A helper that is also a component
import { collection, component, v, type MutationContext } from "@flower-js/sdk";

export function tally(name: string) {
  const counts = collection(name, v.int({ min: 0 }));
  return {
    ...component({ collections: [counts] }),
    add: (ctx: MutationContext, key: string) => ctx.set(counts, key, (ctx.get(counts, key) ?? 0) + 1),
  };
}
// const votes = tally("votes"); define({ uses: [votes], ... }); votes.add(ctx, "yes");
  • define visits nested uses first and includes each component once, however many times it is reached.
  • Names are global across components: two different definitions or two tasks with the same name are rejected, and so is one collection declared with different indexes.
  • Components never add public methods; spread generated ones, like jobs.http("jobs"), into http.

Tasks

APIContract
task(name, { due, run, onError? }): TaskBackground work for the maintenance handler. Names must be unique in the app.
TaskReadonly kind: "task", name, due, run and optional onError.
due(ctx: QueryContext): number | nullThe earliest time the task has work, or null when idle. Must depend only on data and time, and return a finite number.
run(ctx: MutationContext): JsonOne bounded unit of work, committed on its own. The task stays eligible while due is in the past.
onError(ctx, failure: TaskFailure): JsonRuns in place of a failed run, against the failed invocation’s snapshot and time; its writes commit instead. Without it, the task backs off.
TaskFailureerror: Failure, with the real code (yours, COMPUTE_ERROR, EVALUATION_BUDGET…), and failedAt in milliseconds.

define compiles every task, including those of components and of materialize policies, into one maintenance handler pair, $flower.maintenance and $flower.maintenance.error, named in FlowerModule.maintenance.

  • Each run picks the task with the earliest due time and runs it in its own commit. The leader runs maintenance every FLOWER_MAINTENANCE_INTERVAL_MS (250 ms), in bursts of up to FLOWER_MAINTENANCE_BURST_MS (50 ms) while work remains.
  • A failure without onError delays only that task: 1 s after the first failure, doubling up to 60 s. The next success clears it. Backoff state lives in the $flower.tasks collection.
  • A run may be evaluated more than once before it commits. Keep external side effects out of tasks.

Triggers

APIContract
trigger(name, source, run): TriggerRuns run(ctx, change) inside every mutation that changed a row of source, once per changed key, before the commit. The source collection is declared for you.
Trigger<T, K>Readonly kind: "trigger", name, source and run.
Change<T, K>key (decoded for typed-key collections), before and after; null means the row didn’t exist.
  • Triggers see and join the mutation’s writes. A row that ends where it started doesn’t fire.
  • Writes made by triggers can fire more triggers. After 32 rounds the mutation fails with TRIGGER_LOOP.
  • Trigger names must be unique per collection.