// Schema for the {{projectName}} collaborative-editing backend. // // Tables are auto-discovered: every exported `table(...)` here (or in any // `*.entity.ts` / `*.schema.ts` file) is registered by `voltro dev` — no manual // barrel. `actors` + `tenants` are the framework's core tables the // `audit()` / `tenant()` mixins reference, so they must be declared. import { crdtText, databaseHandle, id, localFirst, table, text, timestamp, type InferRow, } from '@voltro/database' import { tenant } from '@voltro/plugin-multitenancy' // ---------- Core tables (required by the audit / tenant 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'), }) // ---------- The collaborative document ---------- // `body` is a `crdtText()` column — a CRDT-managed field stored as the encoded // CRDT state (an opaque `bytes` blob; BYTEA / BLOB / VARBINARY per dialect). // There is no special DDL: to the declarative differ it is an ordinary nullable // `bytes` column, so it plans + round-trips like any other. // // The convergence guarantee is AUTHORITATIVE and SERVER-SIDE: when a client // writes an encoded update to `body`, the runtime folds it into the STORED // state with `mergeCrdtStates` on the write path (see // `mutations/documents.setBody.mutation.server.ts`) — so two concurrent edits // both survive, regardless of the order the server processes them. // // `localFirst()` marks the table local-first (client mirror + bi-directional // sync + CRDT convergence for its `crdtText()` fields). It adds no column — it // is a property the framework reflects on (`isLocalFirst` / `hasLocalFirst`). export const documents = table('documents', { id: id({ prefix: 'doc' }), title: text(), body: crdtText(), // CRDT-managed — Uint8Array | null; decode with decodeCrdtText() }) // tenant() pulls audit() transitively → adds tenantId + createdAt / // updatedAt / createdBy / updatedBy (auto-stamped by the runtime), and // auto-scopes every read/write to the caller's tenant. .with(tenant(), localFirst()) export type Document = InferRow export const database = databaseHandle({ actors, tenants, documents })