This app marks an invoice ready five seconds after its last update. Save it as examples/invoices.ts and deploy it.
examples/invoices.ts
import { collection, define, mutation, query } from "@flower-js/sdk";
import { scheduler } from "@flower-js/sdk/scheduler";
const invoices = collection<{ total: number; status: string }>("invoices");
const finalize = mutation("internal.invoice.finalize", (ctx, id: string) => {
const invoice = ctx.get(invoices, id);
if (invoice) ctx.set(invoices, id, { ...invoice, status: "ready" });
return null;
});
const timers = scheduler("invoiceTimers", { finalize });
const update = mutation("internal.invoice.update", (ctx, args: { id: string; total: number }) => {
if (!args || typeof args.id !== "string" || !args.id ||
!Number.isSafeInteger(args.total) || args.total < 0) {
throw new TypeError("An invoice ID and nonnegative integer total are required");
}
ctx.set(invoices, args.id, { total: args.total, status: "draft" });
return timers.after(ctx, `finalize:${args.id}`, 5_000, "finalize", args.id);
});
const get = query("internal.invoice.get", (ctx, id: string) => ctx.get(invoices, id));
export default define({
collections: [timers.records],
maintenance: timers.maintenance,
http: { "invoice.update": update, "invoice.get": get },
});Try the delayed update
node sdk/cli.ts deploy examples/invoices.ts
node sdk/cli.ts call invoice.update '{"id":"inv-1","total":2400}' \
--request-id invoice-edit-1
node sdk/cli.ts watch invoice.get '"inv-1"'- Add
timers.recordsto your collections. - Reusing a timer ID replaces its deadline. Use a new ID per callback you want.
timers.attakes an epoch-millisecond deadline.cancel,get,scanandretrymanage timers.
How timers run
- A timer stores a handler name and JSON arguments, not a closure. When renaming a handler, keep the old name or migrate pending timers.
- To repeat, have the callback reschedule itself under the same ID.
- A failed callback’s changes are discarded, then it is retried: three attempts, 1,000 ms first backoff, 60,000 ms maximum.
- The leader checks for due timers every 250 ms (
FLOWER_MAINTENANCE_INTERVAL_MS).
A deadline means “not before.” Load, elections or lost quorum can delay it. Its changes commit once, but it may be evaluated more than once, so send external side effects through a leased worker.
Custom maintenance callbacks
Pass define({ maintenance }) and return { $flower: { continue: true } } to run again right away. A burst stops when nothing changes, at the transaction byte budget, or after FLOWER_MAINTENANCE_BURST_MS (50 ms). See examples/scheduling.ts for a fuller timer app.