/** * The Prisma storage driver: implements the storage seam over the structural * delegate surface in delegates.ts, so it runs against any generated Prisma * client (Postgres, MySQL, SQLite, …) without depending on `@prisma/client`. * * Mapping rules, applied uniformly at this boundary: * - Optional core fields (`field?: T | undefined`) ↔ nullable columns: * `null` becomes `undefined` on read, `undefined` becomes `null` (or an * omitted Json field) on write. * - Epoch-ms `number` fields ↔ BigInt columns, converted losslessly with * `BigInt()` / `Number()` (epoch ms sit well inside 2^53). * - Json columns round-trip the core payloads (provenance, patterns, * stages, …) through two typed helpers; no `any` reaches the surface. * - Edge tables (AlfizMembership, AlfizGroupParent) are reconciled on * upsert: rows no longer present are deleted, new ones inserted. * * Partitioned storage (v2): the driver is pinned to exactly ONE partition at * construction (`options.partition`, default `""` — where an unpartitioned * v1 dataset lands after migration) and physically cannot address any * other. The discriminator is threaded through the shared where-builders, * and the delegate types make it REQUIRED, so a query that forgot the * partition fails to compile rather than scanning every tenant. * * Like every driver, this one stores and retrieves; it never interprets. * Ids and semantics are the Application's. */ import type { StorageDriver } from "@alfiz/application"; import type { AlfizPrismaDelegates } from "./delegates.js"; export interface PrismaDriverOptions { /** * Cross-node serialization for graph writes. In-process, the default * promise-chain mutex is enough; a MULTI-NODE deployment must pass a * database advisory lock here (e.g. Postgres: * `SELECT pg_advisory_xact_lock(hashtext($key))` inside a transaction * wrapping `fn`), or two nodes can jointly write a graph cycle that each * would have rejected alone. * * The keys this driver hands to `lock` already carry the partition * (`"docs:groups"` for `partition: "docs"`), so two applications sharing * tables never contend on each other's advisory locks — the deployment * does not have to remember to fold the partition in itself. * * A mesh WRITE edge makes this option mandatory for the target partition * even in single-node deployments: a peer Application is a second process * executing the partition's semantics, and `openPeerApplication` in write * mode refuses a driver whose `runExclusive` cannot serialize across * processes (`crossProcess`). */ lock?: ((key: string, fn: () => Promise) => Promise) | undefined; /** * The sentinel your Prisma client uses to write SQL NULL into a nullable * `Json` column — `Prisma.DbNull`. Needed only to CLEAR a role's * `requestable` policy; every other write passes a real value. * * It is an option rather than an import because this package deliberately * keeps `@prisma/client` out of its dependency graph (see the module * header). Defaults to `null`, which is correct for the bundled memory * driver and for any client that accepts it; pass `Prisma.DbNull` if your * client rejects a bare `null` on a Json column. */ jsonNull?: unknown; /** * Partitioned storage: several Applications sharing one set of Alfiz * tables. Rows are written and read with `app = partition`; the default * `""` is the unpartitioned v1 dataset. The recommended value is the * application's primary catalog namespace (`"docs"`, `"zoom"`), whose * org-wide uniqueness the federation registry already guarantees. * * The rule in shared tables is ALL PARTITIONED OR NONE: an application * that omits the option silently lands in `""` beside any legacy data. * Schema-per-application via the connection string remains the stronger * alternative when adversarial isolation matters. */ partition?: string | undefined; } /** * Build a StorageDriver over a Prisma client (or anything satisfying * {@link AlfizPrismaDelegates} structurally): * * ```ts * const prisma = new PrismaClient(); * const storage = prismaDriver(prisma); // partition "" * const docs = prismaDriver(prisma, { partition: "docs" }); // shared tables * const app = createApplication({ storage, ... }); * ``` * * `runExclusive` uses `options.lock` when provided, else an in-process * promise-chain mutex per key (the memory driver's approach). That mutex * only serializes within one process: multi-node deployments MUST supply a * database advisory lock (e.g. Postgres `pg_advisory_xact_lock`) via * `options.lock` so concurrent graph writes on different nodes cannot * jointly form a cycle. Lock keys are partition-scoped by the driver * (`"docs:groups"`), so co-tenants of one database never falsely contend. */ export declare function prismaDriver(db: AlfizPrismaDelegates, options?: PrismaDriverOptions): StorageDriver; //# sourceMappingURL=driver.d.ts.map