import type { Collection } from '../../kernel/collection.js'; import type { TxContext } from '../../with-commit/tx/transaction.js'; import type { MVQueryContext } from './types.js'; import type { RegisteredMV } from './registry.js'; import { type PutDerivedOutputCtx } from '../../kernel/via/dispatch.js'; /** * Accessor shape passed in from the owning Vault. Mirrors v1's * `DerivationStaleAccessor` — provides the per-collection resolver * and the active TxContext so refresh writes/tombstones register on * `_executed` for rollback symmetry. */ export interface MVExecutorAccessor { getCollection(name: string): Collection; getActiveTxContext(): TxContext | null; /** * Vault-shaped accessor passed to the MV's `query()` callback at * each refresh. Same instance the registry used at registration * time; threading through the executor lets the refresh path * re-evaluate the closure against the live vault state. */ getQueryContext(): MVQueryContext; /** * #638 Task 5 — ctx for `putDerivedOutput`'s frozen-period skip+audit. #641: the lazy * resolve-on-read caller (`stale.ts#resolveStaleMVOnRead`) now supplies one too — built at * the `Collection.get()`/`list()` entry point with a `'resolve-on-read'` sentinel id (no * real "reacting write" for a read-triggered materialize, mirroring `refreshView()`'s * `'refreshView'` sentinel). Still declared optional here since `MVExecutorAccessor` is a * general shape and a future caller could reasonably omit it. */ dispatchCtx?: PutDerivedOutputCtx; } export interface RefreshResult { /** Rows newly written / overwritten. */ written: number; /** Rows tombstoned via `_internalDelete` (only when `onEmpty: 'delete'`). */ deleted: number; /** Failed row writes (non-strict mode). */ failed: number; /** #782/#785 — `outputCollection:id` entries from the tombstone pass whose ownership stamp * couldn't be decoded under the collection's default DEK (undecodable, mirroring * `invalidateMVAtRest`'s #776 posture) — ownership UNCONFIRMED. Only populated when * `onEmpty: 'delete'`. */ residueUndecodable: string[]; /** #782/#785 — `outputCollection:id` entries that decoded, stamp-matched this MV, but * `_internalDelete` declined (the #718 tier-elevation gate) — ownership CONFIRMED, a real * silent survival, surfaced rather than dropped. Only populated when `onEmpty: 'delete'`. */ residueDeclined: string[]; } /** * Run an MV's `query()` and write the result rows to the output * collection. Same-DEK encryption: routes through the standard * `Collection.put` pipeline, so the output collection's DEK is what * gets used (matches the v2 spec's "same DEK as the left-most source" * invariant — `Collection.put` looks up the DEK by collection name, * and the output collection IS the MV's owned collection). * * Stamps `_materializedFrom` onto every emitted row. * * **Tombstoning:** when `spec.onEmpty: 'delete'` (default), rows * that existed in a prior refresh but no longer appear in the new * materialized result are deleted via `Collection._internalDelete` — * the housekeeping bypass primitive prevents user * `onDelete` guards on the output collection from firing on these * system-internal deletes. `onEmpty: 'keep'` opts out (rows from * prior refreshes linger even when the new result lacks them). * * **Cost ceiling:** if the materialized row count exceeds * `spec.maxRows` (default 100k), throws `MaterializedViewTooLargeError` * before any writes hit the store — so strict-mode rollback is * clean. * * **Strict mode:** `spec.strict === true` re-throws on any * row-write failure; the active TxContext registration means the * source-write rolls back atomically via `revertExecuted`. * * @internal */ export declare const MaterializedViewExecutor: { refresh(reg: RegisteredMV, accessor: MVExecutorAccessor): Promise; };