Runtime and state
Weeble runs each deployed revision inside a managed V8 isolate. Your script registers commands, event handlers, and scheduled tasks; Weeble handles the gateway, routing, Discord rate limits, and isolate lifecycle.
Runtime globals
discord and weeble are global namespaces.
The runtime also provides web-style APIs including fetch, crypto, crypto.subtle, URL, URLSearchParams, Headers, Request, Response, Blob, timers, sleep, TextEncoder, and TextDecoder.
Node.js server APIs and built-in modules are not available. Do not design a deployment around fs, http, process, Buffer, or a process that stays alive forever.
Top-level code
Top-level code runs when Weeble warms a revision. Use it to construct reusable objects and register handlers:
const settings = new weeble.KVNamespace("settings");
discord.on(discord.events.MESSAGE_CREATE, async (message) => {
const prefix = (await settings.get<string>("prefix")) ?? "!";
if (message.content === `${prefix}ping`) {
await message.reply("pong");
}
});
A warm isolate may handle multiple events, but Weeble can evict or recycle it. Values held only in module variables are caches, not durable storage.
Choose where state belongs
State |
Use |
|---|---|
Constants and imported project files |
Configuration shipped with one revision |
Module variables |
Temporary caches that are safe to lose |
|
Durable JSON state that changes while the bot runs |
Scheduled tasks |
Work that must run later, even after an isolate is recycled |
KV persists across publishes and rollbacks until a key expires, is deleted, its namespace is cleared, or the deployment is deleted. See KV storage.
Short delays and durable work
Use sleep() or setTimeout() only for short work that fits inside the current execution deadline.
For a reminder or delayed action, define a task at the top level and schedule it durably:
weeble.tasks.define("send-reminder", async (event) => {
const channelId = String(event.payload?.channelId ?? "");
const text = String(event.payload?.text ?? "");
const channel = await discord.fetchGuildTextChannel(channelId);
if (channel) await channel.send(text);
});
async function scheduleReminder(
instanceId: string,
channelId: string,
text: string,
) {
await weeble.tasks.runAt(
"send-reminder",
instanceId,
Date.now() + 60_000,
{ channelId, text },
);
}
Call scheduleReminder() from a command or event handler. Do not schedule it at the top level, because top-level code can run again whenever Weeble warms the revision. The task handler must be registered by the active revision when the run becomes due.
CPU-heavy work
Normal event handlers have a 500ms CPU budget by default. Use weeble.compute.run() for bounded work that needs more CPU time, such as sorting a large leaderboard or transforming a large data set:
const ranked = await weeble.compute.run(
"rank-scores",
() => scores.toSorted((left, right) => right.score - left.score),
);
console.log(ranked.result, ranked.usedCpuMs, ranked.bucketRemainingMs);
Compute jobs share a separate CPU bucket and still have wall-clock limits. They are not background jobs: await the result in the current command or event. Use a scheduled task when work must survive beyond the current execution.
Execution boundaries
Each event has CPU, wall-time, Discord request, KV, fetch, and response-size limits. Await only the work needed to answer the event, reuse payload data, and move durable delayed work into tasks.
See Limits for the current defaults and Debugging for investigating execution failures.