1/**
2 * The USER's own worker entry — the nitro entry/exports seam.
3 *
4 * The deploy target maps the configured `main` onto `nitro.options.entry`,
5 * so nitro's rollup bundles THIS module as the worker entry: its exports are
6 * the worker's exports. It wraps nitro's emitted cloudflare-module handler
7 * (every framework route, API route, and asset check still works) and
8 * ADDITIONALLY exports the `Counter` Durable Object class, which must live
9 * on the same worker for the `COUNTER` namespace binding to resolve.
10 */
11import { DurableObject } from "cloudflare:workers";
12// The wrappable-handler seam: nitro's cloudflare_module runtime entry,
13// importable by its public specifier (bundled with the same virtuals and
14// aliases as nitro's own default entry).
15import nitroHandler from "nitropack/presets/cloudflare/runtime/cloudflare-module";
16
17/** A SQLite-backed Durable Object owned by the user app, not the framework. */
18export class Counter extends DurableObject {
19 #ensureTable() {
20 this.ctx.storage.sql.exec(
21 "CREATE TABLE IF NOT EXISTS counter (id INTEGER PRIMARY KEY, value INTEGER NOT NULL)",
22 );
23 }
24
25 async get(): Promise<number> {
26 this.#ensureTable();
27 const rows = this.ctx.storage.sql
28 .exec("SELECT value FROM counter WHERE id = 0")
29 .toArray();
30 return Number(rows[0]?.value ?? 0);
31 }
32
33 async increment(): Promise<number> {
34 this.#ensureTable();
35 const rows = this.ctx.storage.sql
36 .exec(
37 "INSERT INTO counter (id, value) VALUES (0, 1) ON CONFLICT (id) DO UPDATE SET value = value + 1 RETURNING value",
38 )
39 .toArray();
40 return Number(rows[0]!.value);
41 }
42}
43
44export default nitroHandler;