/** * Shared provider contract for the SQLite-authoritative sync runtime. * * The contract deliberately contains normalized values and stable anchors, * never Google SDK objects or physical row numbers. Both the fake provider and * the Google Sheets API provider implement this boundary so fault tests * exercise the same compare-and-set semantics as a deployed provider. */ import type { CellObservation, NormalizedCell } from "../encoding/types.js"; import type { EffectKind, EffectTargetKind } from "../domain/model/constants.js"; import type { Applicability, Presence } from "../state/types.js"; import type { RegisteredProjection } from "../storage/syncRegistry.js"; import { type SyncEffectResultStatus, type SyncFastAppendStatus, type SyncPostconditionMode, type SyncPostconditionDisposition, type SyncPostconditionStatus, type SyncProtocolVersion, type SyncSnapshotReadMode } from "./constants.js"; import type { SyncProviderTiming as SyncSheetsTiming } from "./timing.js"; /** Projections supported by the v1 sync provider. */ export type SyncProjection = RegisteredProjection; /** Effect classes whose compare-and-set behavior differs at the provider. */ export type SyncEffectKind = EffectKind; /** Literal/formula metadata retained by a normalized Sheet snapshot. */ export interface SyncSnapshotCell extends CellObservation { readonly stableHash: Presence; } /** One physical row read from a registered projection. */ export interface SyncSnapshotRow { readonly rowNumber: number; readonly physicalAnchor: Presence; /** Optional compatibility fields; the real provider leaves visible state to SQLite. */ readonly visibleRevision: Presence; readonly visibleHash: Presence; readonly cells: Readonly>; } /** Lock-free normalized snapshot returned by a provider. */ export interface SyncSheetsSnapshot { readonly protocolVersion: SyncProtocolVersion; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly headers: readonly string[]; readonly rows: readonly SyncSnapshotRow[]; readonly snapshotHash: string; readonly unanchoredRows: readonly number[]; readonly duplicateAnchors: readonly { readonly anchor: string; readonly rowNumbers: readonly number[]; }[]; } /** Request used to assign missing Developer Metadata anchors before a snapshot. */ export interface EnsureSyncRowAnchorsRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; } /** Result of one anchor assignment pass. */ export interface EnsureSyncRowAnchorsResult { readonly assigned: number; readonly existing: number; readonly duplicateAnchors: readonly { readonly anchor: string; readonly rowNumbers: readonly number[]; }[]; } /** Lock-free snapshot request. */ export interface ReadSyncSnapshotRequest extends EnsureSyncRowAnchorsRequest { /** Full metadata for reconciliation, or user-editable values for polling. */ readonly readMode?: SyncSnapshotReadMode; /** * Optional row-level scoping for the check-column polling gate. When * present, a provider that supports it reads ONLY the header row plus * row bands covering these 1-based physical row numbers (multi-range * bands of the registered span) instead of the whole table, and returns * a snapshot containing just the banded nonblank rows. Providers without * the capability ignore the field and return the historical whole-table * snapshot, which stays correct (a superset of rows the caller asked * for); callers only send rowNumbers to providers that expose * {@link isSyncSheetsRowChecksReader}. */ readonly rowNumbers?: readonly number[]; } /** Result of one combined anchor assignment and snapshot read. */ export interface SyncObservedSnapshot { readonly anchors: EnsureSyncRowAnchorsResult; readonly snapshot: SyncSheetsSnapshot; /** Optional diagnostic phases returned by newer observation providers. */ readonly timing?: SyncSheetsTiming; } /** Optional batch observation capability used to share one remote request. */ export interface SyncSheetsObservationBatchProvider extends SyncSheetsObservationProvider { observeSnapshot(request: ReadSyncSnapshotRequest): Promise; observeSnapshots(requests: readonly ReadSyncSnapshotRequest[]): Promise; } /** Returns whether a provider supports the one-request observation path. */ export declare function isSyncSheetsObservationBatchProvider(provider: SyncSheetsObservationProvider): provider is SyncSheetsObservationBatchProvider; /** Reads one snapshot with one combined request when the provider supports it. */ export declare function observeSyncSnapshot(provider: SyncSheetsObservationProvider, request: ReadSyncSnapshotRequest): Promise; /** Reads several snapshots through one remote operation when available. */ export declare function observeSyncSnapshots(provider: SyncSheetsObservationProvider, requests: readonly ReadSyncSnapshotRequest[]): Promise; /** * Serializable projection values written by one outbox effect. * * `targetVisibleHash` is computed over `fields` with * computeSyncVisibleHash(). The anchor is projection-local: User_Input and * System_State may represent the same row binding with different anchors. */ export interface SyncProjectionEffectPayload { readonly sheetName: string; readonly registeredRange: string; readonly schemaVersion: number; readonly targetAnchor: string; readonly fields: Readonly>; readonly targetVisibleHash: string; readonly createIfMissing: boolean; /** A candidate reconcile must fail rather than overwrite an active candidate. */ readonly expectedCandidateHash: Applicability; } /** Provider-ready view of one durable outbox row. */ export interface SyncProjectionEffect { readonly effectId: string; readonly payloadHash: string; readonly effectKind: SyncEffectKind; readonly physicalSheetId: string; readonly projection: SyncProjection; readonly targetKind: EffectTargetKind; readonly targetId: string; readonly rowBindingId: Presence; readonly conflictId: Presence; readonly expectedVisibleRevision: number; readonly expectedVisibleHash: string; readonly repairGuardHash: Presence; readonly payload: SyncProjectionEffectPayload; } /** Per-effect terminal/non-terminal provider result. */ export interface SyncEffectResult { readonly effectId: string; readonly payloadHash: string; readonly status: SyncEffectResultStatus; readonly visibleRevision: Presence; readonly visibleHash: Presence; readonly snapshotHash: Presence; readonly reason: Presence; readonly postcondition: SyncPostconditionStatus; } /** Batch request. All effects must target the same physical sheet. */ export interface ApplySyncEffectsRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly effects: readonly SyncProjectionEffect[]; /** * Defaults to inline verification for compatibility. The worker explicitly * selects deferred verification so recovery/reconciliation owns read-back. */ readonly postconditionMode?: SyncPostconditionMode; /** * Optional same-route delivery-uncertain probe effects absorbed into this * batch's reads (unified read engine Phase 4, design §10.3 D1/D2). * * These effects are NEVER written by this call. The batch's enumeration, * base read, and (where the batch runs one) verification pass double as the * recovery probe's evidence read, and each probe effect is classified from * the absorbed context with the unchanged `classifyPostcondition` rules. * A probe effect whose route does not match one of this request's routes is * left unclassified (absent from `probeResults`) so the caller falls back to * the standalone probe: absorption never widens beyond the same-route gate. */ readonly probeEffects?: readonly SyncProjectionEffect[]; } /** A batch may intentionally return only a prefix when its budget is exhausted. */ export interface ApplySyncEffectsResult { readonly results: readonly SyncEffectResult[]; readonly snapshotHash: Presence; /** True only when the provider intentionally stopped before the supplied suffix. */ readonly hasMore: boolean; /** Optional phase timing returned by newer Code.gs deployments. */ readonly timing?: SyncSheetsTiming; /** * Read-back classifications for the request's absorbed `probeEffects` (one * per probe effect the batch could classify), absent when none were carried * or absorbed. Missing entries keep the caller's standalone-probe fallback. */ readonly probeResults?: readonly SyncEffectPostconditionResult[]; } /** Read-back classification used after a response is lost or a lease expires. */ export interface SyncEffectPostcondition { readonly disposition: SyncPostconditionDisposition; readonly visibleRevision: Presence; readonly visibleHash: Presence; readonly snapshotHash: Presence; /** Stable diagnostic reason for terminal or manual-repair classifications. */ readonly reason?: string; } /** One effect identity paired with its read-back result in a recovery batch. */ export interface SyncEffectPostconditionResult { readonly effectId: string; readonly payloadHash: string; readonly postcondition: SyncEffectPostcondition; } /** One row written through the append-only bulk path. * * The payload hash lets the provider receipt sheet recognize a response-loss * replay without appending the same durable effect twice. The optional shape * keeps older direct adapter fixtures source-compatible; worker-produced rows * always carry the SQLite outbox payload hash. */ export interface FastAppendRow { readonly effectId: string; readonly payloadHash?: string; /** Developer-metadata row anchor written in the same Sheets batch. */ readonly anchor?: string; readonly fields: Readonly>; /** * Optional per-row route so one fast-append request can span multiple tabs. * When absent the provider falls back to the request-level route (single * tab, the legacy shape). The dispatcher populates these from each effect. */ readonly physicalSheetId?: string; readonly projection?: SyncProjection; readonly sheetName?: string; readonly registeredRange?: string; readonly schemaVersion?: number; } /** Per-effect result for one fast-append row. */ export interface FastAppendRowResult { readonly effectId: string; /** The row was included in the provider's bulk write. */ readonly status: SyncFastAppendStatus; /** Receipt-backed evidence returned by the provider append operation. */ readonly visibleHash?: string; readonly visibleRevision?: number; } /** Bounded idempotent append request for one registered projection sheet. */ export interface FastAppendRowsRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly rows: readonly FastAppendRow[]; /** * Optional same-route delivery-uncertain probe effects absorbed into this * append's reads (unified read engine Phase 4; see * `ApplySyncEffectsRequest.probeEffects`). Never appended by this call; * classified from the batch's base + merged verification evidence. */ readonly probeEffects?: readonly SyncProjectionEffect[]; } /** Result of one bounded fast-append batch. */ export interface FastAppendRowsResult { readonly results: readonly FastAppendRowResult[]; /** True when the provider intentionally stopped before the supplied suffix. */ readonly hasMore: boolean; /** Optional phase timing returned by newer Code.gs deployments. */ readonly timing?: SyncSheetsTiming; /** * Read-back classifications for the request's absorbed `probeEffects` * (see `ApplySyncEffectsResult.probeResults`). */ readonly probeResults?: readonly SyncEffectPostconditionResult[]; } /** Request used to classify several response-loss effects with one Sheet read. */ export interface ReadSyncEffectPostconditionsRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly effects: readonly SyncProjectionEffect[]; } /** * Prepared-apply state carried from `preflightApplyEffects` to * `applyPreparedEffects`. * * The worker treats the value as an opaque token and never inspects it; the * provider owns its concrete shape and narrows it back with a runtime `kind` * guard at the `applyPreparedEffects` boundary. Only the shared discriminant * and the request the state was preflighted from are declared here so * unrelated providers cannot invent a conflicting shape across the interface * and so the dispatcher can bind the prepared state to the exact request it * was created for. */ export interface PreparedApplyEffects { readonly kind: "single" | "multi"; /** The exact request this prepared state was preflighted from. */ readonly request: ApplySyncEffectsRequest; } /** * Full effect capability required for fast append, regular updates, deletes, * and recovery. * * `preflightApplyEffects` / `applyPreparedEffects` are an OPTIONAL split of * `applyEffects`: a preflight does the read+plan stage (no remote mutation) * and returns an opaque `PreparedApplyEffects`, which a later * `applyPreparedEffects` consumes for the write+verify stage. Providers that * implement neither optional method keep the single legacy `applyEffects` * path. The worker and dispatcher feature-detect the pair before using it. */ export interface SyncEffectWorkerProvider { fastAppendRows(request: FastAppendRowsRequest): Promise; applyEffects(request: ApplySyncEffectsRequest): Promise; /** Optional read+plan stage that produces opaque prepared state. */ preflightApplyEffects?(request: ApplySyncEffectsRequest): Promise; /** Optional write+verify stage that consumes the prepared state. */ applyPreparedEffects?(prepared: PreparedApplyEffects): Promise; readEffectPostcondition(effect: SyncProjectionEffect): Promise; readEffectPostconditions(request: ReadSyncEffectPostconditionsRequest): Promise; } /** Read-only provider capability used by polling and onEdit observation. */ export interface SyncSheetsObservationProvider { ensureRowAnchors(request: EnsureSyncRowAnchorsRequest): Promise; readSnapshot(request: ReadSyncSnapshotRequest): Promise; } /** Request for a lightweight table read used by simple polling. */ export interface ReadSyncTableRowsRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly headers: readonly string[]; } /** One nonblank row returned by a lightweight table read. */ export interface SyncTableRow { readonly rowNumber: number; readonly fields: Readonly>; } /** Result of a lightweight table read without Sheet metadata or CAS work. */ export interface SyncTableRowsResult { readonly sheetName: string; readonly registeredRange: string; readonly headers: readonly string[]; readonly rows: readonly SyncTableRow[]; readonly timing?: SyncSheetsTiming; } /** Capability for reading literal table values without observation metadata. */ export interface SyncSheetsTableReader { readRows(request: ReadSyncTableRowsRequest): Promise; readRowsBatch(requests: readonly ReadSyncTableRowsRequest[]): Promise; /** * Optional narrow row-check read backing the formula check-column polling * gate (see `sheets/rowCheck.ts`). Providers without the capability (fake * providers, non-Sheets adapters) leave it undefined; polling then keeps * the historical values-only whole-table preflight. */ readRowChecksBatch?: (requests: readonly ReadSyncRowChecksRequest[]) => Promise; } /** * Request for the narrow row-check read of one registered User_Input tab: * ONLY the identity column, the system row-id (anchor) column, and the * check column (three column bands in one ranged read). `identityField` * names the header whose column the provider maps rows with; it must be * one of the route's registered headers. */ export interface ReadSyncRowChecksRequest { readonly physicalSheetId: string; readonly sheetName: string; readonly registeredRange: string; readonly projection: SyncProjection; readonly schemaVersion: number; readonly identityField: string; } /** One visible row of the narrow check read (row mapping + check string). */ export interface SyncRowCheckRow { readonly rowNumber: number; /** Identity cell under values-only (getValues) normalization. */ readonly identity: NormalizedCell; /** * System row-id (anchor) column value, ABSENT when the cell is blank. * The polling gate compares it against the binding's anchor reference * and escalates any deletion/duplication/misplacement to the * whole-table observation. */ readonly anchor: Presence; /** * Computed check-column display string, PRESENT only when the check cell * still carries the EXACT system-generated formula for this row (formula * provenance). A blank cell, a literal replacement, or a foreign formula * yields ABSENT — "no check evidence" — so a pasted stale string can * never pass the gate; callers treat absent as a mismatch. */ readonly check: Presence; } /** * Result of one narrow check read. `status` is `checks_unavailable` when * the tab is not provisioned with the check column (missing or foreign * header cell), so polling falls back to the historical whole-table read * for that tab (mixed mode); rows remain listed so the provider's answer * is still a complete observation of the bands it read. */ export interface SyncRowChecksResult { readonly sheetName: string; readonly registeredRange: string; readonly status: "checks_available" | "checks_unavailable"; readonly rows: readonly SyncRowCheckRow[]; } /** Providers that can answer the narrow check-column read (the polling gate). */ export interface SyncSheetsRowChecksReader { readRowChecksBatch(requests: readonly ReadSyncRowChecksRequest[]): Promise; } /** Returns whether a table reader also exposes the narrow row-check read. */ export declare function isSyncSheetsRowChecksReader(provider: object): provider is SyncSheetsRowChecksReader; /** Returns whether a full observation provider also exposes the values-only reader. */ export declare function isSyncSheetsTableReader(provider: SyncSheetsObservationProvider): provider is SyncSheetsObservationProvider & SyncSheetsTableReader; /** * Full provider boundary used by observation and reconciliation. * * It includes the full effect-worker capabilities plus the metadata/snapshot * reads required to observe user edits and repair projection drift. */ export interface SyncSheetsProvider extends SyncEffectWorkerProvider, SyncSheetsObservationProvider { } /** Computes the stable visible-state hash shared by fake and real providers. */ export declare function computeSyncVisibleHash(fields: Readonly>): string; /** Validates and decodes the projection payload stored in a durable outbox row. */ export declare function parseSyncProjectionEffectPayload(value: string): SyncProjectionEffectPayload; /** Serializes a checked projection payload in a stable key order for outbox use. */ export declare function serializeSyncProjectionEffectPayload(payload: SyncProjectionEffectPayload): string; //# sourceMappingURL=syncSheets.d.ts.map