import type { ViaGraph } from './graph.js'; import type { Collection } from '../collection.js'; import type { EncryptedEnvelope } from '../types.js'; /** One deleted child's resolved rollup PARENT intent (#640) — ids + a field name only. A * resolved `parentId` is an id, same class as any touched record id — never a stored value. */ export interface RollupDeleteIntent { readonly into: string; readonly parentId: string; readonly field: string; } /** One collection's batched touches this session (#640 widens the #638 `Set` shape): * `puts` — the original semantics, unchanged; `deletes` — a deleted record's id mapped to its * resolved rollup-parent intents, captured PRE-invalidation by `Collection._onRecordMutated`'s * sync-apply delete case (the FK is only readable there — see `kernel/collection.ts * #_rollupDeleteIntents`). */ export interface GraphTouch { readonly puts: Set; readonly deletes: Map; } /** Per-session touched set — collection → its `GraphTouch`. Metadata only — ids (collection * names, record ids INCLUDING resolved rollup parent ids) and field names; NEVER record payload * or key material. A resolved parentId is an id, same class as the touched ids — not a stored * value. */ export type GraphBatch = Map; /** Get-or-create `batch`'s `GraphTouch` entry for `collection` (#640) — shared by * `Vault._collectGraphTouch`/`_collectGraphDelete` so each stays a one-line call. */ export declare function touchFor(batch: GraphBatch, collection: string): GraphTouch; /** #640 — sync, I/O-free: `deleted`'s resolved rollup PARENT intents (the FK is only readable * NOW, before the record is gone) — a pure function so `Collection._rollupDeleteIntents` stays * a one-line delegator under the kernel-surface ceiling. `registry` is `this.derivationSource * ?.registry()`; `collectionName` is `this.name` (the CHILD/`rollup.from` side). Generic over * the registry's own spec shape so this file never imports a `with-formula` type (port-layering * — via/dispatch.ts must not gain a with-* import). */ export declare function resolveRollupDeleteIntents(registry: { strategiesForSource(name: string): ReadonlyArray<{ spec: S; }>; } | undefined, collectionName: string, deleted: Record): RollupDeleteIntent[]; /** #640 — resolve a `RollupDeleteIntent` back to its registry `spec`. The wave's per-intent * driver needs `spec.rollup.compute`, which `GraphBatch`'s metadata-only pin forbids batching — * so it's re-resolved here, post-boundary, instead of carried across it. `undefined` if the * intent's originating strategy was unregistered between collect time and wave time (residual * gap, freshness-only). */ export declare function findRollupSpecForIntent(registry: { strategiesForSource(name: string): ReadonlyArray<{ spec: S; }>; } | undefined, collectionName: string, intent: RollupDeleteIntent): S | undefined; /** One dedup ledger for a single wave — a target is recomputed at most once (mark-on-check). */ export declare class WaveContext { private readonly _seen; seen(targetKey: string): boolean; } /** The slice of `Vault` the wave needs: the shared graph (the #553 zero-cost skip) and cached- * collection lookup — never constructs, since a touched collection is, by construction, * already open (it just fired `_onRecordMutated`). */ export interface VaultLike { readonly graph: ViaGraph; _getCollection(name: string): Collection> | undefined; /** #640 (#644 item 3) — structured wave-error surfacing, additive to the existing console.warn. */ _emit(event: string, payload: unknown): void; } /** * Run ONE dispatch wave for a completed batch: for each touched (collection, id), decrypt the * applied envelope (id threaded), then run the SAME `dispatchDerivations` + * `dispatchMaterializedViews` the local-write path uses — with a shared `WaveContext` so N * touched records feeding the SAME target recompute once. #553: a collection with no graph * out-edges (e.g. money-only) is skipped before any decrypt/dispatch async work. */ export declare function runGraphDispatchWave(vault: VaultLike, batch: GraphBatch): Promise; /** A record's post-freeze source mutation, for the `'derivation:skipped-frozen'` event * and the optional audit-trail entry. See {@link putDerivedOutput}. */ export interface DerivationSkippedFrozen { readonly source: { readonly collection: string; readonly id: string; }; readonly target: { readonly collection: string; readonly id: string; }; readonly period: string; readonly endDate: string; } /** The minimal put-capable shape `putDerivedOutput` needs. Any `Collection` structurally * satisfies this — its real `options` type is a superset of what's declared here. */ export interface CollectionLike { put(id: string, value: unknown, options?: { readonly source?: string; }): Promise; } export interface PutDerivedOutputCtx { readonly emit: (ev: string, p: unknown) => void; readonly source: { readonly collection: string; readonly id: string; }; readonly audit?: ((e: DerivationSkippedFrozen) => Promise) | undefined; } /** * Attempt a dispatch-driven output write. On `PeriodClosedError` (the closed-period * `beforePut` gate — `freezePeriod`/`archivePeriod` add no separate gate, per seam map * Part 7): SKIP (no `_ts` stamped — the gate throws before any write happens), emit * `'derivation:skipped-frozen'` on the event bus (ALWAYS), and append an audit-trail entry * when the with-history ledger is active. Returns `'written' | 'skipped-frozen'`. Any OTHER * error propagates unchanged — this helper narrows exactly one error class. The SOURCE * write is never wrapped through this helper — only derived-output writes are (§7). */ export declare function putDerivedOutput(outColl: CollectionLike, id: string, value: unknown, ctx: PutDerivedOutputCtx, options?: { readonly source?: string; }): Promise<'written' | 'skipped-frozen'>; /** Structural (no static import — kernel spine may not statically reach a with-* service, * the S4 gate recipe `check-architecture.mjs`'s port-layering check enforces) shape of the * with-history `LedgerStore.append` seam this helper needs. */ interface AuditLedgerLike { append(input: { readonly op: 'lifecycle'; readonly collection: string; readonly id: string; readonly version: number; readonly actor: string; readonly payloadHash: string; readonly reason?: string; }): Promise; } /** @internal — the optional with-history ledger audit hook for `putDerivedOutput`, present * only when the ledger is active (mirrors every other kernel event's `if (this.ledger)` * gate). Encoded as a `'lifecycle'` entry — the existing "non-data audit event" op (the * same convention `forget`'s JSON-summary-in-`reason` entry uses; `ledger/entry.ts:88-104`). */ export declare function ledgerAuditHook(ledger: AuditLedgerLike | undefined, actor: string): ((e: DerivationSkippedFrozen) => Promise) | undefined; /** `recomputeRollup`/`dispatchRollupsOnDelete`'s per-target write outcome (#638 Task 6): * `'written'`/`'skipped-frozen'` mirror {@link putDerivedOutput}'s result; `'noop'` covers * no-parent/no-change/deduped-by-wave — nothing to report either way. */ export type RollupOutcome = 'written' | 'skipped-frozen' | 'noop'; /** Mutable accumulator `forgetDerivedFanout` writes into, one per `Vault.forget()` call — keeps * the per-ref loop's call site to a single line under vault.ts's tight kernel-surface ceiling. * Maps 1:1 onto `ForgetResult`'s additive fields (`with-audit/forget/strategy.ts`). */ export interface ForgetFanoutStats { recordsErased: number; aggregatesRecomputed: number; readonly residueFrozen: string[]; /** #650 Task 5 (#648) — referencing records tombstoned via a `'ref'` edge's `cascade` policy. */ lookupReferencesCascaded: number; /** #650 Task 5 (#648) — referencing fields cleared via a `'ref'` edge's `nullify` policy. */ lookupReferencesNullified: number; /** #650 Task 5 review (Important fix) — `'ref'` edges whose compare-key could NOT be resolved, * even from the LIVE pre-shred row (the backing row itself is unreadable) — propagation was * skipped for these, reported here so the skip is never silent. `backing:key:collection.field` * entries, one per un-propagated edge. */ readonly lookupReferencesResidue: string[]; /** #776/#782/#785 — `outputCollection:id` MV-output rows that survived erasure invalidation * (eager tombstone leg AND lazy/manual `invalidateMVAtRest`) despite belonging to the * forgotten subject, because the ownership stamp could not be decoded (undecodable under * the default DEK — e.g. elevated above tier 0 on a tiered output collection). Ownership * UNCONFIRMED. Never erased, but surfaced here rather than silently skipped. */ readonly derivedResidueUndecodable: string[]; /** #782/#785 — `outputCollection:id` MV-output rows that decoded and stamp-matched but whose * `_internalDelete` declined (the #718 tier-elevation gate). Ownership CONFIRMED, erasure * declined — a real silent survival, surfaced here rather than silently skipped. */ readonly derivedResidueDeclined: string[]; } /** * #622 — after `_writeTombstone(ref.id, actor)` erases the forgotten subject's own record, fan * out to its derived residue via the graph (spec §5): record-grain artifacts (MV rows, * array-shape derivation rows, same-id record-shape derivation copies) are ERASED through the * SAME `!internal` housekeeping-bypass machinery the ordinary delete path uses (no user * `onDelete` re-fires — the shred-is-not-a-domain-delete property `_writeTombstone` protects); * aggregate-grain rollups are RECOMPUTED without the forgotten contribution in open periods, or * skip+audit (via `putDerivedOutput`, already wired into the rollup/MV output paths) in frozen * ones. Mutates `stats` in place. `envelope` is `ref`'s PRE-tombstone envelope, decoded only if * a rollup edge is actually present — the #553 zero-cost-skip discipline: no decrypt for the * common case of a forgotten record with no aggregate-grain consumer, and no work at all when * `ref.collection` has no graph out-edges. `lookupCompareKeys` is the `'ref'` edges' compare-key * map, resolved from the LIVE row BEFORE any shred (`VaultLinks.checkLookupRefsRestrict`'s return * value — `Vault.forget()`'s pre-shred restrict check doubles as the live-resolve pass) — see * {@link applyLookupRefsFanout}. */ export declare function forgetDerivedFanout(vault: VaultLike, ref: { readonly collection: string; readonly id: string; }, envelope: EncryptedEnvelope | null, stats: ForgetFanoutStats, lookupCompareKeys: ReadonlyMap): Promise; export {};