@sveltext/cloudflare-platform-runtime
v0.1.0
Published
Lightweight runtime helpers for cron and queues on Cloudflare, remote-functions style.
Downloads
5
Readme
@sveltext/cloudflare-platform-runtime
Tiny helpers that make Cloudflare cron and queue jobs feel like SvelteKit remote functions. Use them in .server.ts / .remote.ts modules and let @sveltext/adapter-cloudflare-platform embed the metadata into your Worker bundle.
Features at a glance
| Helper | Purpose | Notes |
| ------ | ------- | ----- |
| cron(config, handler) | Declare scheduled jobs using Cloudflare cron syntax (CRON_TZ compatible). | Supports manual HTTP triggers (manual: true or manual: { route, headers }). |
| queue(config, consumer?) | Define queue producers and optional push consumers. | Automatically wires producer .send() implementations; supports DLQ + named queues. |
| withEdgeCache(body, opts) | Convenience helper to set public cache headers on responses. | Default s-maxage=300, max-age=0. |
Everything is ESM-friendly and tree-shakeable.
Installation
pnpm add @sveltext/cloudflare-platform-runtime
# plus @sveltext/adapter-cloudflare-platform in your SvelteKit adapter configUsage
Cron jobs
// src/routes/+page.server.ts
import { cron } from '@sveltext/cloudflare-platform-runtime';
export const nightly = cron(
{
schedule: '0 3 * * *', // 03:00 UTC
manual: { route: 'nightly', headers: { 'x-secret': 'dev-token' } },
name: 'nightly-report'
},
async ({ platform, log }) => {
const { DB } = platform.env;
await DB.prepare('DELETE FROM sessions WHERE expires_at < datetime()').run();
log.info('Expired sessions removed');
}
);Queue producers & consumers
import { queue } from '@sveltext/cloudflare-platform-runtime';
type EmailJob = { to: string; subject: string; body: string };
export const sendEmail = queue<EmailJob>(
{
binding: 'MAILS', // matches wrangler queues.producers[].binding
queue: 'project-mails', // optional but validates config
consume: true, // register a consumer in this worker
deadLetter: 'MAILS_DLQ',
name: 'email-dispatch'
},
async (message, { env, log }) => {
log.info('sending email to', message.body.to);
await env.MAIL_SERVICE.fetch('https://api.mail/send', {
method: 'POST',
body: JSON.stringify(message.body)
});
message.ack();
}
);
// Later in your app:
await sendEmail({ to: '[email protected]', subject: 'Hello', body: '…' });Edge caching helper
import { withEdgeCache } from '@sveltext/cloudflare-platform-runtime/cache';
export const GET = () => {
const data = JSON.stringify({ status: 'ok' });
return withEdgeCache(new Response(data, { headers: { 'content-type': 'application/json' } }), {
sMaxAge: 60,
vary: ['authorization']
});
};Wrangler configuration
The runtime reads job metadata from the module system; the adapter ensures Wrangler configuration matches by validating:
triggers.crons– must cover every schedule declared viacron().queues.producers/queues.consumers– must include thebinding,queue, and (if provided)dead_letter_queue.compatibility_date >= 2024-10-22when workflows are present (necessary for remote functions).
Example wrangler.toml excerpt:
[[triggers.crons]]
schedule = "0 3 * * *"
[[queues.producers]]
binding = "MAILS"
queue = "project-mails"
[[queues.consumers]]
queue = "project-mails"
dead_letter_queue = "project-mails-dlq"Migration tips
Coming from manual exports (e.g. export const onRequest = ... or raw queue consumers):
- Replace your functions with
cron()/queue()calls. - Remove manual registration logic in
hooks.server.tsor custom workers – the adapter embeds the metadata automatically. - Update
wrangler.tomlaccording to the validation hints produced duringpnpm build.
The runtime intentionally mirrors SvelteKit remote functions: the default export is callable (producer), and metadata lives on a non-enumerable META symbol.
API reference
cron(config, handler)
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| schedule | string \| string[] | – | CRON expression(s) in Cloudflare syntax. |
| manual | boolean \| { route?: string; headers?: Record<string,string>; allowWhenDisabled?: boolean } | false | Enables manual HTTP triggering via the adapter’s manual.base. |
| manualHeaders | Record<string,string> | – | Deprecated – use manual.headers. |
| name | string | Derived from export identifier | Surfaced in logs and validation hints. |
The handler signature is (ctx: CronCtx) => any, where ctx.platform.env matches your Wrangler bindings.
queue(config, consumer?)
| Option | Type | Description |
| ------ | ---- | ----------- |
| binding | string | Queue binding name (wrangler.toml producers). |
| queue | string? | Explicit queue name (queues.producers[].queue). |
| consume | boolean \| string \| { queue: string } | Register a consumer in this worker; string/object lets you specify the queue name used in wrangler.toml. |
| deadLetter | string? | Dead-letter queue binding. |
| name | string? | Friendly name for logs / hints. |
If consumer is omitted, the helper only registers a producer (useful for remote-only queues).
withEdgeCache(body, opts?)
opts shape:
{
sMaxAge?: number; // default 300s
maxAge?: number; // default 0
vary?: string[]; // adds Vary header
}Typings & ambient declarations
The package ships ESM dist/ bundles with TypeScript declaration files. An ambient definition augments App.Platform and supplies ImportMeta['url'] so import.meta.url works under strict TS configs.
Troubleshooting
| Issue | Explanation |
| ----- | ----------- |
| Missing import.meta.url in types | Ensure your tsconfig extends the ambient declarations (the package ships them automatically). |
| Queue producers throw “producer not initialised” in dev | The adapter stubs producers only when it’s aware of the binding. Check that wrangler.toml defines the binding and re-run pnpm build. |
| Cron job silently skips | Verify triggers.crons covers every schedule. Build output includes hints if something’s missing. |
Need a deeper dive into the worker integration? See the adapter README – it covers job serialisation, manual triggers, validation messages, and the emulate().jobs helper. Happy shipping! 🚀
