/** * Observation model: row-anchor planning and full snapshot building. * * These helpers port the Apps Script observation operation's semantics to * the REST wire shapes: nonblank rows are detected with the checkbox rule, * anchors come from the User_Input tab's LAST system column (cell values, * never developer metadata), and every cell is classified merged / error / * formula / literal / blank with the same precedence and the same stableHash * evidence as the Apps Script source, so snapshot hashes are byte-compatible * across providers. The system column is excluded from user-field cells, * blank-row detection, and visible hashes. * * Every untrusted SDK payload is validated by the preflight guards before it * reaches this module; all functions here fail closed on drift (header * mismatch, duplicate anchors) exactly like the Apps Script source throws. */ import { type SyncSnapshotReadMode } from "../../../../contracts/sheets/constants.js"; import type { SyncSheetsSnapshot } from "../../../../contracts/sheets/syncSheets.js"; import { type EngineRuntime } from "@hikoutei/ikisaki"; import type { ParsedGridData, ParsedMergedCell, ParsedSheet } from "./preflightTypes.js"; /** One tab requested by an observation/table-read batch. */ export interface ObservationGridTarget { readonly sheetName: string; readonly registeredRange: string; /** * Optional row-level scoping (the check-column polling gate). When * present, the tab is read as the header row plus row bands covering * ONLY these 1-based physical rows (full registered width), and the * returned grid is the geometric synthesis of those bands: rows outside * the bands resolve blank and are skipped by every downstream rule. An * oversized band plan expands into additional sequential band requests * (unified read engine); the whole-table degradation is removed. */ readonly rowNumbers?: readonly number[]; } /** Validated grid plus sheet identity for one observed tab. */ export interface ObservedTab { readonly sheetId: number; readonly title: string; readonly grid: ParsedGridData; /** Merged regions of this tab (sheet-level `merges` GridRange entries). */ readonly merges: readonly ParsedMergedCell[]; } /** Anchor planning inputs shared by ensureRowAnchors and observation. */ export interface AnchorPlanningTarget { readonly registeredRange: string; readonly headers: readonly string[]; /** §12 columnMap: adopted-route physical headers (see the definition type). */ readonly physicalHeaders?: readonly string[]; readonly checkboxHeaders: readonly string[]; /** * 1-based absolute column of the system row-id column; `undefined` for * projections without one (no anchors are planned or read). */ readonly anchorColumn: number | undefined; } /** One anchor write planned for a missing anchor. */ export interface PlannedAnchorWrite { /** 0-based row index for the updateCells request. */ readonly rowIndex: number; readonly anchor: string; } /** Result of one anchor-planning pass over a tab grid. */ export interface AnchorPlanResult { readonly assigned: number; readonly existing: number; readonly duplicateAnchors: readonly { readonly anchor: string; readonly rowNumbers: readonly number[]; }[]; readonly planned: readonly PlannedAnchorWrite[]; } /** Full snapshot inputs; the projection and schemaVersion are wire fields. */ export interface SnapshotBuildTarget extends AnchorPlanningTarget { readonly sheetName: string; readonly projection: string; readonly schemaVersion: number; readonly readMode: SyncSnapshotReadMode; /** * §12 columnMap: adopted-route physical headers, positionally parallel to * `headers`. When present the grid's header row is validated against them * while cells stay keyed by the canonical `headers` (field names). */ readonly physicalHeaders?: readonly string[]; } /** * Reads the grids of several registered tabs through the unified read * engine: the bands planned for the batch are packed into as FEW paced * requests as the shared range/byte budget allows and executed SEQUENTIALLY * on the runtime's lane, then reassembled per sheet (one ordered GridData * list per requested range, in request order). * * Every requested tab must exist and return grid data; the result is keyed * by tab name. The `fields` mask decides which metadata comes back (full * observation mask or the lighter user_input mask). The historical * "band plan > 40 ranges degrades to ONE whole-table request" rung is * REMOVED: an oversized band plan expands into additional sequential * requests (the polling lane has no lease, so multi-request reads are its * steady state at scale), and a whole-table read chunks against the tab's * authoritative row bound (`engine.rowBounds`, fed by the enumeration / * the polling lane's cold-title metadata enumeration) with the last band * open-ended, so no single request grows with the accumulated data while * coverage is never truncated. */ export declare function readTabGrids(engine: EngineRuntime, targets: readonly ObservationGridTarget[], fields: string): Promise>; /** * Plans anchors for every nonblank data row of one tab grid. Rows with more * than one anchor fail closed; missing anchors get a fresh * `sync-anchor:` value; duplicate anchors across rows are reported as * evidence (never rewritten). Blank rows are skipped, matching the Apps * Script ensureAnchorsFromValues_ behavior. The system column is validated * (fail-closed on legacy tabs) and its cell value is the anchor source. */ export declare function planRowAnchors(tab: ObservedTab, target: AnchorPlanningTarget): AnchorPlanResult; /** * Builds one full snapshot from a validated tab grid with EXACT parity to * the Apps Script observation source: per-cell precedence merged -> error -> * formula -> literal/blank, stableHash evidence (including stableHash(null) * for blank cells), and a snapshotHash over the wire-shaped snapshot object * with presence fields serialized as null. */ export declare function buildSnapshotFromTab(tab: ObservedTab, target: SnapshotBuildTarget): SyncSheetsSnapshot; //# sourceMappingURL=observation.d.ts.map