/** * Cron Log Page * * Subscribes to the app's CronRoom via `useCronMonitor` and renders live * task state + execution history with the scaffold's ui primitives and * theme tokens. The DO is keyed by `SCOPE_ID` (`app:${APP_ID}`) so a * single shared CronRoom DO instance backs the whole app — same pattern * as RecordRoom. * * Used both as a UI surface for verifying that scheduled tasks are firing * in production, and as the data source for the cron e2e spec at * tests/feature-tests/tests/cron.spec.ts — keep the data-testid and * data-* attributes stable. */ import { useCronMonitor } from 'deepspace' import { Badge, EmptyState } from '@/components/ui' import { SCOPE_ID } from '../constants' const TH = 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground' const TD = 'px-4 py-3 text-sm' export default function CronLogPage() { const { tasks, history, connected, lastError } = useCronMonitor(SCOPE_ID) // Newest first. const sorted = [...history].sort((a, b) => { return new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime() }) return (
Each row is one tick of a scheduled task fired by the AppCronRoom DO. Heartbeat ticks every minute once the DO alarm picks up the registered config.
error: {lastError}
)}| Name | Schedule | Last run | Next run |
|---|---|---|---|
| {task.name} | {task.schedule ? `${task.schedule} (${task.timezone ?? 'UTC'})` : `every ${task.intervalMinutes ?? '?'} min`} | {task.lastRunAt ?? '—'} | {task.nextRunAt ?? '—'} |
| Task | Started (UTC) | Duration | Outcome |
|---|---|---|---|
| {entry.taskName} | {entry.startedAt} | {entry.durationMs} ms |
{entry.success ? (
|