/** * 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 (

Cron Log

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.

Connection: {connected ? 'live' : 'connecting…'}
{lastError && (

error: {lastError}

)}

Tasks

{tasks.map((task) => ( ))}
Name Schedule Last run Next run
{task.name} {task.schedule ? `${task.schedule} (${task.timezone ?? 'UTC'})` : `every ${task.intervalMinutes ?? '?'} min`} {task.lastRunAt ?? '—'} {task.nextRunAt ?? '—'}

History

{sorted.length} entries
{sorted.map((entry, idx) => ( ))}
Task Started (UTC) Duration Outcome
{entry.taskName} {entry.startedAt} {entry.durationMs} ms {entry.success ? ( ok ) : ( error: {entry.error ?? '?'} )}
{sorted.length === 0 && connected && ( )}
) }