import type { ViaPosture } from './index.js'; /** A (collection, field) node. Artifact-grain targets (rollup field, MV row-class, * overlay output) are modelled as a field node whose `field` is the artifact key. */ export interface FieldRef { readonly collection: string; readonly field: string; } export type EdgeKind = 'computed' | 'derivation' | 'rollup' | 'mv' | 'overlay' | 'ref'; /** `'virtual'` (#638 Task 7) marks a `computed(fn, { mode: 'virtual' })` field's edge — * never stored (rides the `present` phase), so it can never be sealed at rest * ({@link ViaGraph.taintSealedFields} excludes it) and is always non-queryable * regardless of source posture ({@link ViaGraph.effectivePosture} clamps it). */ export type Grain = 'record' | 'aggregate' | 'virtual'; /** Delete/forget-time referential policy for a `'ref'` edge (#650 Task 5, spec §4) — duplicated * (not imported) from `via/lookup/descriptor.ts`'s `OnDelete`: the kernel spine may not * statically import `via/**` (Check 14, `via-layering`). Keep the two in sync by hand. */ export type OnDelete = 'restrict' | 'cascade' | 'nullify'; /** Plain (non-via) field baseline — max-permissive; taint only ever tightens. */ export declare const DEFAULT_POSTURE: ViaPosture; /** The per-axis "most restrictive" fold — pure, exported for direct unit testing. */ export declare function foldPosture(a: ViaPosture, b: ViaPosture): ViaPosture; /** Security-axes-only fold for a wildcard collection node (#642): sealed-wins encryptedAtRest, * AND exportable, OR forgettable — queryable is NOT folded (stays base.queryable; the sealed * clamp to 'none' is buildTaintOverlay's job, so inheriting `sealed` never over-restricts a * blob-adjacent output to unqueryable). Pure; exported for unit testing. Distinct from * {@link foldPosture} (which folds all four axes) — do NOT reuse foldPosture here. */ export declare function foldWildcardSecurity(base: ViaPosture, contributor: ViaPosture): ViaPosture; /** Metadata-only dependency graph, ONE per vault. Never stores values or key material. */ export declare class ViaGraph { /** Declared source-field postures (registerField), keyed by node id. */ private readonly _posture; /** Every derived target's registration, keyed by the target's node id. */ private readonly _in; /** source node id -> derived edges that depend on it (dispatch/erasure), each carrying * its own registration's `kind` (#678 — see {@link OutEdge}). */ private readonly _out; /** Memoized effective posture per derived target node id. */ private readonly _effectiveCache; /** Memoized whole-collection security-axes fold for `'*'` wildcard LEAF nodes (#642), * keyed by collection name — see {@link _wildcardContribution}. Cleared alongside * `_effectiveCache` on every `registerField`/`registerDerived` (a later-registered * field must re-fold on the next read — registration ordering is free). */ private readonly _wildcardCache; /** Collections with at least one classified field (#638 Task 2 fix wave 2, * Finding I1 — the reconcile path's combined-state leak guard memory). */ private readonly _classifiedCollections; /** Computed field names declared with no `deps` entry, per collection * (Finding I1 — such a field registers no edge when no classified field is * present yet, so a LATER, separate reconcile call attaching classifiedFields * still needs to see it regardless of declaration order). */ private readonly _depslessComputed; /** Declare a source field's posture (money/i18n/classified/plain). Plain fields * default to `DEFAULT_POSTURE`; a later declaration for the same node wins-first (idempotent). */ registerField(collection: string, field: string, posture: ViaPosture): void; /** A derived target depends on `sources` (may be cross-collection). `kind`/`grain` * drive dispatch + erasure semantics; sources drive taint. Re-registering the same * target replaces `_in` but does not prune stale `_out` edges — callers must * register each target at most once per graph lifetime. `onDelete`/`keyField` (#650 Task 5) * are meaningful only for `kind === 'ref'` edges — {@link referencingEdgesOf} reads them back. */ registerDerived(target: FieldRef, sources: readonly FieldRef[], kind: EdgeKind, grain: Grain, onDelete?: OnDelete, keyField?: string): void; /** Whether `target` already has a registered in-edge — lets a caller skip * re-registering the same target (#638 Task 2 fix wave 2, Finding I2i: * `registerDerived`'s at-most-once contract must hold across repeated * identical `vault.collection()` calls, not just within a single one). */ hasDerived(target: FieldRef): boolean; /** Mark/query a collection as having declared at least one classified field. */ markClassified(collection: string): void; isClassified(collection: string): boolean; /** Mark/query a collection's depsless (no declared `deps`) computed * field names — see `_depslessComputed`'s doc comment above. */ markDepslessComputed(collection: string, field: string): void; depslessComputedFields(collection: string): ReadonlySet; /** Every field name the graph already has memory of for `collection` — from an * EARLIER, separate `vault.collection()` call (registered postures, derived * targets, depsless-computed names) — regardless of which via feature declared * it. #645 — `via/graph-wiring.ts`'s reconcile-path `knownFields` universe * (`collectKnownFieldNames`) is scoped to THIS call's own options only, so a * computed field attached in a LATER call whose `deps` correctly names a field * declared in an EARLIER call was spuriously refused as "unknown"; this method * lets that call site union in the graph's cross-call memory. Excludes the `'*'` * wildcard target (an overlay/derivation whole-record fold node, never a real * record field — see {@link registerDerived}'s doc comment). */ knownFieldNames(collection: string): ReadonlySet; /** Reject cycles at declare time (vault open). Throws `DerivationCycleError` for a * derivation/rollup/computed cycle, `MaterializedViewCycleError` for an MV cycle — * same classes + message shape the registries throw today (behavior lock). */ assertAcyclic(): void; /** A leaf (non-derived) node's own posture contribution: its declared posture, * or `DEFAULT_POSTURE` if never declared via `registerField`. */ private _declaredPosture; /** A node's posture contribution when folded into a dependent: recurse if it is itself * derived; else its declared/default posture — UNLESS it is a `'*'` LEAF node consumed * by a folded formula kind (#642, `derivation|rollup|mv|overlay`), in which case it * contributes its collection's whole-record security fold ({@link _wildcardContribution}). * `kind` is the CONSUMING edge's kind, threaded from {@link _computeEffective}. `'ref'` * edges (and the provenance-only untyped call, `kind` left `undefined`) keep `'*'` = * identity — the phase-D lookup reliance (`via/lookup/registry.ts:392-397`) that a * referencing field must not seal just because its dimension collection has a classified * column. */ private _contribution; /** Lazy fold (#642) of a collection's REGISTERED field postures on the security axes only * ({@link foldWildcardSecurity} — sealed-wins encryptedAtRest, AND exportable, OR * forgettable; queryable stays at DEFAULT_POSTURE's 'full', never folded), seeded at * DEFAULT_POSTURE. This is the collection-level posture a `'*'` LEAF node contributes to a * folded-kind formula edge (derivation/rollup/mv/overlay) — see {@link _contribution}. A * plain (unregistered) field contributes nothing (the identity), so the fold is strictly * more conservative than deps-based taint and never hits the KNOWN-LIMIT wall (it names no * fields — seam map §1d). Memoized per collection in `_wildcardCache`. */ private _wildcardContribution; private _computeEffective; /** Strictest source posture on every axis, per §2. `undefined` when `target` has no * in-edges (not a derived field). Transitive: folds through chained derivations. */ effectivePosture(target: FieldRef): ViaPosture | undefined; /** Per-collection { field → effectivePosture } overlay the pipeline consumes (Task 3). */ taintedPostures(collection: string): ReadonlyMap; /** Materialized (`grain !== 'virtual'`) derived fields on `collection` whose effective * encryptedAtRest resolves to 'sealed' — the taint-seal set (Task 3). #638 Task 7 * makes the `grain !== 'virtual'` filter meaningful: a `computed(fn, { mode: 'virtual' })` * field is never stored (rides the `present` phase, seam map Part 4's money-Formatted/ * i18n-Label precedent), so it is EXCLUDED here even when its effective `encryptedAtRest` * folds to `'sealed'` — there is no envelope slot to seal. It still surfaces in * {@link taintedPostures} (query refusal via `postureFor`'s `queryable` clamp above, and * export refusal via `exportable`) — only the AT-REST SEALING ACTION is skipped. */ taintSealedFields(collection: string): ReadonlySet; /** Fields on `collection` whose registered edge is `grain === 'virtual'` (#638 Task 7) — * `via/graph-wiring.ts#applyTaintOverlay` intersects this with `exportable === false` * postures to know which virtual fields need PRESENT-TIME (not just export-time) * redaction, since a virtual field's value is only ever materialized inside `present()`. */ virtualFields(collection: string): ReadonlySet; /** Per-collection { field → immediate source field names that forced its * effective posture away from `DEFAULT_POSTURE` } — `describe()`'s * provenance (Task 3). Only the DIRECT declared sources are named (not the * ultimate origin several hops up a transitive chain); a source absent * from the result contributed nothing restrictive. */ taintProvenance(collection: string): ReadonlyMap; private _targetsDependingOn; /** Dispatch (Task 4): every derived target triggered by a write to `collection`. Excludes * `'ref'` edges (#650 Task 5 review, folded Minor) — a `'ref'` edge is consulted only by the * delete/forget-time restrict/cascade/nullify path ({@link referencingEdgesOf}), never by the * sync/cutover/restore dispatch wave (`runGraphDispatchWave`); counting it here defeated that * wave's zero-cost early-continue, costing every write to a referenced backing collection an * unconditional decrypt + two no-op dispatch passes. {@link derivedArtifactsOf} (erasure * fanout) still includes `'ref'` edges — the forget path genuinely needs them. */ dependentsOf(collection: string): ReadonlyArray<{ readonly target: FieldRef; readonly kind: EdgeKind; readonly grain: Grain; }>; /** Erasure (Task 6): derived artifacts of a forgotten record whose source is `collection`. */ derivedArtifactsOf(collection: string): ReadonlyArray<{ readonly target: FieldRef; readonly kind: EdgeKind; readonly grain: Grain; }>; /** Referencing edges pointing AT a backing dimension (delete/forget-time restrict/cascade/ * nullify, #650 Task 5) — an O(1) reverse lookup via the existing `_out` map (keyed at the * dimension's wildcard `field:'*'` node, the same convention every whole-collection source * already uses), NEVER a scan across collections or records. `backing` is the collection name * a lookup field's edge was registered against (a reserved dimension's `_dict_`/ * `_lookup_` collection name, or a matrix dimension's own collection name). */ referencingEdgesOf(backing: string): ReadonlyArray<{ readonly referencing: FieldRef; readonly onDelete: OnDelete; readonly keyField: string; }>; }