// Schema for the {{projectName}} backend. // // Tables are auto-discovered: every exported `table(...)` here (or in any // `*.entity.ts` / `*.schema.ts` file) is registered by `voltro dev`. // `actors` + `tenants` are the framework's core tables the `tenant()` / // `audit()` mixins reference, so they must be declared. // // IMPORTANT contrast (the whole reason this template exists): the ROWS below // live in `ctx.store` — they're re-fetchable from the external source, so a // relational table is the right home. The sync CURSOR and the idempotency // MARKERS do NOT live here — they're state you can't recompute, so they live // in `ctx.kv` (durable, never evicted). Don't model a cursor as a table row // you hand-manage; that's exactly what `ctx.kv` is for. import { databaseHandle, id, integer, table, text, timestamp, type InferRow, } from '@voltro/database' import { tenant } from '@voltro/plugin-multitenancy' // ---------- Core tables (required by the tenant / audit mixins) ---------- export const actors = table('actors', { id: id(), kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']), displayName: text().nullable(), createdAt: timestamp().default('now'), }) export const tenants = table('tenants', { id: id(), name: text(), createdAt: timestamp().default('now'), }) // ---------- Application table — the events we ingest from the source ---------- export const syncedEvents = table('synced_events', { id: id({ prefix: 'evt' }), // The external id we deduped on — carried through so you can trace a row // back to its source record. externalId: text(), kind: text(), payload: text(), // The source sequence number this row came from (the cursor advances past it). sequence: integer(), }) // tenant() pulls audit() transitively → tenantId + created/updated columns, // auto-stamped by the runtime from the caller's subject. .with(tenant()) export type SyncedEvent = InferRow export const database = databaseHandle({ actors, tenants, syncedEvents })