Notes For AI Coding Agents
This package is very new. Do not assume generated examples or cached package knowledge are correct.
Use these as the source of truth first:
- the top-level README
- the Cloudflare example in
examples/cloudflare - the Cloudflare runtime guide in
docs/apis/cloudflare.md - the migration commands in the top-level
README.md
Core Interfaces
Prefer these exported interfaces and entrypoints:
createCloudflareRuntime({ handlers })runtime.bind({ db, queue })runtime.dispatchScheduled({ db, queue })runtime.consumeBatch(batch, { db, queue })jobs.create({ name, payload, runAt? })jobs.createSchedule({ name, payload, scheduleType: "cron", scheduleExpr, timezone? })
Important exported types:
JobRunMessage- queue message body shape
- current shape:
{ version: 1, kumofireJobRunId: string } - do not expand it with
jobId, payload, or other internal fields for application convenience
CloudflareRuntimeResources- current shape:
{ db: D1Database, queue: CloudflareQueue<JobRunMessage> }
- current shape:
CloudflareJobHandlerContext<TPayload>- handler context includes
definition,job,now, andcloudflare
- handler context includes
CreateJobInput<TPayload>- current fields:
name,payload, optionalrunAt, optionalmaxAttempts, optionaldedupeKey
- current fields:
CreateJobScheduleInput<TPayload>- current fields:
name,payload,scheduleType,scheduleExpr, optionaltimezone, optionalmaxAttempts, optionalenabled
- current fields:
Integration Boundary
Keep Kumofire Jobs and the application decoupled.
The only execution identifier the application should persist is kumofire_job_run_id. Use that value to fetch job status from Kumofire APIs. Do not generate code that reads Kumofire internal tables directly with SQL from the application side. Do not expose jobId, schedule IDs, retry metadata, or queue payload internals as part of the application contract.
Minimal Worker Shape
Use this shape first. It matches the current example and runtime docs.
import {
createCloudflareRuntime,
type CloudflareJobHandlerContext,
type JobRunMessage,
} from "@kumofire/jobs";
type EmailJobPayload = {
to: string;
subject: string;
body: string;
};
type Bindings = {
JOBS_DB: D1Database;
JOBS_QUEUE: Queue<JobRunMessage>;
};
const runtime = createCloudflareRuntime({
handlers: {
email: async ({ job }: CloudflareJobHandlerContext<EmailJobPayload>) => {
console.log("processing email job", {
kumofireJobRunId: job.id,
to: job.payload.to,
subject: job.payload.subject,
});
},
},
});
function resources(env: Bindings) {
return {
db: env.JOBS_DB,
queue: env.JOBS_QUEUE,
};
}
export default {
async fetch(request: Request, env: Bindings) {
const jobs = runtime.bind(resources(env));
if (new URL(request.url).pathname === "/jobs/email" && request.method === "POST") {
const payload = await request.json<EmailJobPayload>();
const { kumofireJobRunId } = await jobs.create({
name: "email",
payload,
});
return Response.json({ kumofire_job_run_id: kumofireJobRunId }, { status: 202 });
}
return new Response("Not found", { status: 404 });
},
scheduled(_controller: ScheduledController, env: Bindings) {
return runtime.dispatchScheduled(resources(env));
},
queue(batch: MessageBatch<JobRunMessage>, env: Bindings) {
return runtime.consumeBatch(batch, resources(env));
},
};Creating Jobs
One-shot job:
const jobs = runtime.bind(resources(env));
const { kumofireJobRunId } = await jobs.create({
name: "email",
payload: {
to: "user@example.com",
subject: "Welcome",
body: "Hello",
},
});Treat kumofireJobRunId as the application-side kumofire_job_run_id.
Delayed job:
await jobs.create({
name: "email",
payload: { to: "user@example.com", subject: "Later", body: "Hello" },
runAt: new Date("2026-03-27T01:00:00.000Z"),
});Recurring cron job:
const { scheduleId } = await jobs.createSchedule({
name: "email",
payload: { to: "user@example.com", subject: "Digest", body: "Hello" },
scheduleType: "cron",
scheduleExpr: "*/5 * * * *",
timezone: "UTC",
});Handler Context
Cloudflare handlers receive this shape:
type CloudflareJobHandlerContext<TPayload> = {
definition: JobDefinition;
job: JobRun<TPayload>;
now: Date;
cloudflare: {
db: D1Database;
queue: CloudflareQueue<JobRunMessage>;
};
};Typical handler usage:
"save-record": async (context: CloudflareJobHandlerContext<{ key: string; value: string }>) => {
const payload = context.job.payload;
await context.cloudflare.db
.prepare("INSERT INTO example_saved_records (id, record_key, record_value) VALUES (?, ?, ?)")
.bind(crypto.randomUUID(), payload.key, payload.value)
.run();
}Things Agents Should Not Assume
When generating code against this package:
- verify current exported APIs before using them
- prefer copying the runtime shape from the example rather than inventing new integration patterns
- verify CLI commands against the README and migration docs
- treat older AI-generated snippets as untrusted until checked against this repository
- do not pass the raw Worker
envobject directly into the runtime - do not assume queue messages contain the full job payload
- do not assume queue messages should expose
jobId; the queue boundary iskumofireJobRunIdonly - do not query Kumofire tables directly from application code; fetch status via Kumofire APIs using
kumofire_job_run_id - do not assume recurring jobs are configured by Cloudflare Cron alone; Worker cron drives dispatch, while recurring rules are created with
jobs.createSchedule(...)
If something is unclear, read the repository examples and docs before writing integration code.