## Demo: cron
### Cron.svelte
```svelte
```
### CronLog.svelte
```svelte
{status}
{#each entries as entry (entry.seq)}
-
#{entry.seq}
{fmtTime(entry.at)}
demo-activity-log ran
{/each}
{#each skeletons as i (i)}
-
{/each}
```
### cron.server.ts
```ts
import { Mochi } from 'mochi-framework';
import type { CronLogEntry } from './types';
const MAX_ENTRIES = 100;
// The cron appends one entry per minute forever; trimming to the last MAX_ENTRIES keeps the in-memory log bounded.
const log: CronLogEntry[] = [];
let seq = 0;
type WsClient = { send: (data: string) => void };
const clients = new Set();
function broadcast(message: string): void {
for (const ws of clients) {
ws.send(message);
}
}
// The site rides the default `cronStorage: 'memory'`, so the demo writes no file into the working dir; set
// `Mochi.serve({ cronStorage })` to make the schedule durable across restarts and multiple nodes.
export const activityLog = Mochi.cron('demo-activity-log', '* * * * *', (run) => {
const entry: CronLogEntry = { seq: ++seq, at: Date.now(), scheduledTime: run.scheduledTime };
log.push(entry);
if (log.length > MAX_ENTRIES) {
log.splice(0, log.length - MAX_ENTRIES);
}
broadcast(JSON.stringify({ type: 'entry', entry }));
});
export function addClient(ws: WsClient): void {
clients.add(ws);
ws.send(JSON.stringify({ type: 'snapshot', entries: [...log].reverse() }));
}
export function removeClient(ws: WsClient): void {
clients.delete(ws);
}
```
### types.ts
```ts
// Components must import types from here, NOT from cron.server.ts: a type import from a side-effectful server
// module still drags that module into the SSR component bundle, re-registering its cron job a second time.
export interface CronLogEntry {
seq: number;
/** Epoch ms at which the cron handler ran. */
at: number;
/** Epoch ms at which the scheduler claimed the firing, which is earlier than `at` by the queue pickup delay. */
scheduledTime: number;
}
export type CronLogMessage = { type: 'snapshot'; entries: CronLogEntry[] } | { type: 'entry'; entry: CronLogEntry };
```
### routes.ts
```ts
import { Mochi } from 'mochi-framework';
import type { MochiRouteValue, MochiCronConfig } from 'mochi-framework';
import { activityLog, addClient, removeClient } from './cron.server';
// Mounted in the site's Mochi.serve({ cron }) call — see src/routes.ts.
export const cron: MochiCronConfig[] = [activityLog];
export const routes: Record = {
'/demos/cron': Mochi.page('./src/demos/cron/Cron.svelte'),
'/ws/cron-log': Mochi.ws({
open(ws) {
addClient(ws);
},
message() {},
close(ws) {
removeClient(ws);
},
}),
};
```
### index.ts
```ts
import { Mochi, logger } from 'mochi-framework';
import { routes, cron } from './routes';
await Mochi.serve({
port: 3333,
development: process.env.MODE === 'development',
routes,
cron,
});
logger.info('Server running at http://localhost:3333');
```