/** * `immutableGuard` — declarative WORM / append-only sugar over the guard * service. * * Issued fiscal documents (invoices, DDTs) must be immutable after issue. * That is expressible today with a hand-rolled `withGuard` (block on * `check`/`onDelete`, allow an admin `amendment`), but the boilerplate is * repetitive and easy to get subtly wrong. `immutableGuard` generates * exactly that guard from a declarative config, reusing the guard * machinery wholesale — `check`/`onDelete` rejection, the ledgered * `amendment` override, and composition with `periods`/`history`. * * ```ts * createNoydb({ guardStrategies: [ * immutableGuard({ * collection: 'invoices', * after: (r) => r.status === 'issued', // immutable once issued * }), * ] }) * ``` * * A record is mutable until `after(record)` holds; from then on, updates * and deletes throw `RecordLockedError` unless performed inside an * `amendment` transaction by an authorized role (the override is * ledgered by the guard amendment mechanism). `appendOnly: true` is * shorthand for `after: () => true` — immutable from creation. */ import type { GuardStrategyHandle, GuardContext, GuardChange } from './types.js'; export interface ImmutableGuardConfig> { /** The collection to make WORM. */ collection: string; /** * A record becomes immutable once this predicate holds. Evaluated on * the *existing* (already-persisted) record, so the write that first * makes it true is still allowed; subsequent writes are blocked. * Mutually exclusive with `appendOnly`. */ after?: (record: T) => boolean; /** Shorthand for `after: () => true` — immutable from creation. */ appendOnly?: boolean; /** Roles permitted to override via an amendment transaction. Default `['admin', 'owner']`. */ amendmentRoles?: ReadonlyArray<'admin' | 'owner'>; /** * Optional set-level invariant run over the amendment change-set after * the writes execute. Signature matches `GuardStrategy.amendment.invariant` * exactly: it receives every {before, after} pair touching this * collection in the amendment plus the guard context; throwing reverts * the whole amendment and surfaces as `InvariantError`. * * Use this to keep a constraint inviolable EVEN under amendment — e.g. * forbid deleting an issued document by re-throwing on any * `before !== null && after === null` change, or assert a cross-record * sum is preserved. When omitted the amendment is unconditionally * allowed (the amendment itself is the sanctioned, ledgered override) — * this is the backward-compatible default. */ amendmentInvariant?: (changes: ReadonlyArray>, ctx: GuardContext) => Promise | void; } /** * Build an immutability guard. Pass the returned handle to * `createNoydb({ guardStrategies: [...] })`. */ export declare function immutableGuard>(config: ImmutableGuardConfig): GuardStrategyHandle;