import type { ViaGraph } from './graph.js'; import { resolveComputedEdges, type CollectionOpts, type GraphEdge } from '../collection-config.js'; import { type ClassifiedEntry } from '../../port/with/classified-strategy.js'; import { type HasWritableViaPipeline } from './pipeline.js'; type ComputedFieldsParam = NonNullable[1]>; /** * Register one collection's field postures (`binding.posture` + `covers()`) * and computed/via-binding-deps edges onto the vault's shared graph. Called * from `Vault.collection()`'s fresh-construction path, right after the real * `Collection` is built from the SAME `opts` — `resolveCollectionConfig` is a * pure function, so re-running it here (Collection's constructor already ran * it once internally) costs a one-time, side-effect-free recomputation at * collection-declare time, not a per-write cost. */ export declare function registerCollectionGraphSources(graph: ViaGraph, name: string, opts: CollectionOpts): void; /** The slice of `Vault.collection()`'s reconcile-path options relevant to * graph registration — money/computed/classifiedFields are the only * options `Vault.collection()`'s reconcile branches actually attach * (i18nFields/dictKeyFields are construction-only, never reconciled). */ export interface ReconcileGraphOptions { readonly moneyFields?: Record; readonly classifiedFields?: Record; readonly computed?: ComputedFieldsParam; } /** Phase 1's output — what phase 2 (`commitReconcileGraphEdges`) applies once * every `_apply*` mutation for this reconcile call has succeeded. */ export interface ReconcilePlan { readonly edges: readonly GraphEdge[]; readonly depslessComputedFields: readonly string[]; readonly classifiedFieldNames: readonly string[]; } /** * Phase 1 (validate) of the reconcile-path's two-phase graph wiring (#638 * Task 2 fix wave 2). Pure — throws `ValidationError`, never mutates `graph`. * * Evaluates the COMBINED existing+incoming computed/classified state, using * `graph` as the vault-side memory of what a PRIOR, separate * `vault.collection()` call already registered (review Finding I1): a * depsless computed field declared while no classified field existed yet * registers no edge (`registerCollectionGraphSources` marks it instead), so a * LATER call newly introducing a PERSISTABLE (`recoverable`/`digest-only`) * classified field must still see it and refuse, regardless of which call * declared which piece first. `storage: 'never'` fields are exempt from this * specific check: `enforceClassifiedWrite` rejects the whole write BEFORE * computed fields ever evaluate (`Collection._putInternal`'s pipeline order — * enforceWrite, then computed), so a `never`-stored value structurally cannot * reach a computed field's output. (`resolveComputedEdges` below, unchanged, * still blanket-refuses a depsless field THIS SAME call introduces alongside * ANY classified field regardless of storage — same as fresh construction.) * Given that, once a PERSISTABLE classified field is successfully committed * for a collection, no depsless computed field can survive uncaught (this * very check, or the identical fresh-construction one, would already have * refused it) — so this check only needs THIS call's own incoming * `classifiedFields`, never `graph`'s classified memory. * * Each `computed` entry's own `deps` (#638 Task 7 — the reconcile path only ever sees the * `computed:` sugar option, per `ReconcileGraphOptions`'s doc comment; `via(computed(...))` * is construction-only like i18nFields/dictKeyFields) are resolved via `resolveComputedEdges` * exactly like the fresh-construction path — Finding I2ii's original knownFields-sharing * concern is back in play for the Task 7 review's CRITICAL fix: on a collection that * declares classified fields, `resolveComputedEdges` now checks every `deps` entry against * a `knownFields` universe built via the SAME `collectKnownFieldNames` helper the fresh path * uses (never a hand-rolled second universe), scoped to THIS call's own * `moneyFields`/`classifiedFields`/`computed` options (see `resolveComputedEdges`'s own doc * comment for the full rationale and its documented residual limit). On a non-classified * collection a plain, non-via field is still a legal dep, unchanged from Task 7. * * Callers MUST call this BEFORE any `_apply*` mutation runs, and must only * call `commitReconcileGraphEdges` with the result AFTER every `_apply*` for * this call has succeeded (Finding M2 — no partial graph state may survive a * reconcile call whose config is ultimately rejected). */ export declare function validateReconcileGraphEdges(graph: ViaGraph, name: string, options: ReconcileGraphOptions): ReconcilePlan; /** * Phase 2 (commit) — call ONLY after every `_apply*` mutation belonging to * this reconcile call has succeeded (see {@link validateReconcileGraphEdges}'s * doc comment, Finding M2). Registers `plan`'s edges, skipping any target * already registered so `registerDerived`'s at-most-once contract holds * across repeated identical `vault.collection()` calls, not just within one * (Finding I2i); registers the late-attached classified field(s)' sealed * posture (Finding M1 — fresh construction gets this for free from * `registerCollectionGraphSources`'s compiled-binding loop, which the * reconcile path has no equivalent of); and updates the depless-computed / * classified graph memory {@link validateReconcileGraphEdges} reads. */ export declare function commitReconcileGraphEdges(graph: ViaGraph, name: string, plan: ReconcilePlan): void; /** * Rebuild `coll`'s Via pipeline with the graph's taint overlay layered on * (#638 Task 3 — the assignment→enforcement bridge): `postureFor` (query * gate + `redactForExport`, `kernel/via/pipeline.ts`) enforces it with zero * new surface, and any field the overlay resolves to `encryptedAtRest: * 'sealed'` gets the `taint` binding added so it is ACTUALLY sealed at rest * via `ctx.sealedSlots` (the same mechanism classified uses). * * No-op when the collection has no tainted fields — `coll.via` stays exactly * what `resolveCollectionConfig` built, preserving `this.via === undefined` * for an all-plain collection (#553's sync-stack guarantee). Called once * after `registerCollectionGraphSources` (fresh construction) and once after * `commitReconcileGraphEdges` (reconcile) — a late-attached classified/ * computed field can newly taint a field an EARLIER call already built the * pipeline for, so both call sites must refresh. * * Writes through `Collection._setVia` (`kernel/collection.ts`, #666) rather * than the old untyped structural-cast reach-in — `_setVia` does both of the * cast site's old two steps itself: reassigns `this.via` AND resyncs the * codec's OWN `via` snapshot (captured at construction, `collection.ts`'s * `this.codec = new RecordCodec({..., via: this.via})`, which does not * automatically follow a later `this.via` reassignment) so `encryptRecord`/ * `decryptRecord` seal/unseal through the taint binding too, not a stale * pre-taint pipeline. * * #642 — `graph.taintedPostures(name)` also carries the collection's `'*'` * target (a derivation/MV/overlay OUTPUT collection's whole-record fold, * `via/graph.ts`'s `taintedPostures`/`taintSealedFields` already surface it * under the literal key `'*'`, no `via/graph.ts` change needed). Split it off * BEFORE building the field-specific overlay — `'*'` is never a real record * field — and re-derive it as `defaultPosture` through the SAME * `buildTaintOverlay` sealed→`queryable:'none'` clamp (reused, not * duplicated, by feeding it a one-entry map). A sealed default folds into * the SAME single `taintBinding` call via `sealAllFields` — every field * (present-time, at-rest) is covered without a second binding. */ export declare function applyTaintOverlay(coll: HasWritableViaPipeline, graph: ViaGraph, name: string): void; /** * #642 — the cross-collection re-apply gap (seam map finding 4/10): when the * OUTPUT/parent collection was opened (its overlay built) BEFORE the * classified SOURCE collection ever registered a field, the output's `'*'` * fold (or a rollup's real-field fold) was computed against an empty/stale * source posture. `applyTaintOverlay` alone never re-fires for an already- * open dependent — this hook closes that: after `name` registers its OWN * field postures (called right after `applyTaintOverlay(coll, graph, name)` * at each collection-construction call site), re-run `applyTaintOverlay` for * every OPEN collection that graph-depends on `name`. * * Pure wiring — re-applies OVERLAYS only (a pipeline rebuild), never * re-registers a graph edge (`registerDerived`'s at-most-once contract, D-2 * wave-2 law, is untouched). `graph.dependentsOf(name)` already excludes * `'ref'` edges (a referencing field must not seal just because its backing * dimension changed — the phase-D lookup identity contract). A dependent * collection that hasn't been opened yet (`getOpenCollection` returns * `undefined`) is skipped — it will fold correctly on its OWN first-open * `applyTaintOverlay` call, which reads the graph fresh. */ export declare function reapplyDependentOverlays(graph: ViaGraph, name: string, getOpenCollection: (n: string) => HasWritableViaPipeline | undefined): void; export {};