import { a as ClivlyEntitiesConfig } from "./entity-config-OhC4Otkz.cjs"; import { CompileOptions, CompiledView } from "./view-compiler.cjs"; //#region src/sync-engine.d.ts /** * Sync engine (Phase 3) — materializes the read-only entity views into Clivly's * own mirror tables (`crm_companies`, `crm_contacts`). * * The reconciliation is the heart of the design: rows present in a view are * created or updated in the mirror; rows that *leave* a view (e.g. an owner * demoted to a member) are **archived, not deleted**, so their deals/activities * survive. Identity fields are host-owned and overwritten each run; Clivly-owned * CRM fields are never touched here. * * `reconcile()` is a pure function and independently testable. `runSync()` * orchestrates: compile views → for each (companies first, so contact * `company_ref`s resolve to a live `crm_companies.id`) read the view, reconcile * against the current mirror, resolve company refs, and persist. All DB I/O is * behind the `SyncStore` port — core stays ORM-agnostic; the SDK/drizzle * package provides the concrete store. */ /** A row read from a compiled view (what Postgres returns). */ export interface SourceRow { /** Host-side company key from `_ref`; resolved before persist. */ companyRef?: string | null; /** Mapped identity fields (clivly field → value). */ fields: Record; /** The source primary key — becomes the mirror row's `source_ref`. */ sourceRef: string; } /** An existing mirror row, as far as reconciliation needs to know. */ export interface MirrorRecord { archivedAt: Date | null; /** Host-side company key; only consulted in composite (join-table) mode. */ companyRef: string | null; id: string; sourceRef: string; } export type SyncAction = { kind: "create"; sourceRef: string; fields: Record; companyRef: string | null; } | { kind: "update"; id: string; sourceRef: string; fields: Record; companyRef: string | null; } | { kind: "restore"; id: string; sourceRef: string; fields: Record; companyRef: string | null; } | { kind: "archive"; id: string; sourceRef: string; }; /** A persist action with the company ref resolved to a `crm_companies.id`. */ export type PersistAction = { kind: "create"; sourceRef: string; fields: Record; companyId: string | null; companySourceRef: string | null; } | { kind: "update"; id: string; sourceRef: string; fields: Record; companyId: string | null; companySourceRef: string | null; } | { kind: "restore"; id: string; sourceRef: string; fields: Record; companyId: string | null; companySourceRef: string | null; } | { kind: "archive"; id: string; sourceRef: string; }; /** * Diff a view's rows against the current mirror population for one entity. * * - source row with no mirror row → create * - source row with a live mirror row → update (overwrite identity fields) * - source row with an archived mirror → restore (re-activate + update) * - live mirror row absent from source → archive (soft delete, keep history) * - archived mirror row absent from source → no-op * * Source rows are de-duplicated by the reconciliation key (first wins). In * simple mode (default) the key is `sourceRef`. In composite mode — used for * `through` (join-table) entities, where the compiled view fans a person out * into one row per membership — the key is `(sourceRef, companyRef)`, so a * person in several companies materializes one mirror row per company and a * single membership leaving archives only that one row. */ export declare function reconcile(source: SourceRow[], mirror: MirrorRecord[], opts?: { composite?: boolean; }): SyncAction[]; /** DB I/O port. The concrete store lives in the SDK/drizzle package. */ export interface SyncStore { /** Current mirror rows for a concept + role (including archived). */ listMirror(input: { concept: string; role: string; /** Target custom-object type id, for `concept: "custom"` entities. */ objectTypeId?: string; }): Promise; /** Apply the resolved actions to the mirror table for this concept + role. */ persist(input: { concept: string; role: string; /** Target custom-object type id, for `concept: "custom"` entities. */ objectTypeId?: string; actions: PersistAction[]; }): Promise; /** * Execute the view's SQL and return its rows. `opts.limit`, when given, * caps the number of rows returned (used by `runSync`'s sample mode). */ readView(view: CompiledView, opts?: { limit?: number; }): Promise; /** Map host-side company refs → `crm_companies.id` (already-synced companies only). */ resolveCompanyRefs(refs: string[]): Promise>; } export interface EntitySyncResult { archived: number; concept: string; created: number; entityKey: string; restored: number; role: string; updated: number; } export interface SyncResult { entities: EntitySyncResult[]; totals: { created: number; updated: number; restored: number; archived: number; }; } /** * Run a full sync: compile the config's views, then reconcile + persist each, * companies first. Returns per-entity and total action counts. */ export declare function runSync(config: ClivlyEntitiesConfig, store: SyncStore, options?: CompileOptions): Promise; export interface PreviewWarning { field?: string; message: string; } export interface PreviewResult { rows: SourceRow[]; sampledCount: number; warnings: PreviewWarning[]; } /** * Sample-scoped data-quality warnings over projected preview rows. Each mapped * (or required-but-unmapped) field yields at most one warning, by precedence: * a required field empty in ≥1 row → `K/N` form; an optional field empty in * ALL rows → all-empty form; zero source rows → a single table-level warning. */ export declare function computeSampleWarnings(rows: SourceRow[], requiredFields: string[], sourceTable: string): PreviewWarning[]; /** * Zero-write preview: read ≤`limit` rows through the compiled view (a pure * SELECT whose SQL already projects each row into its mapped fields) and * compute sample warnings. Calls ONLY `store.readView` — never a write path — * so it can never mutate the mirror. The distinct, write-one-row sibling is * `runSync`'s sample mode (T13). */ export declare function previewSync(store: SyncStore, view: CompiledView, opts: { limit: number; requiredFields: string[]; sourceTable: string; }): Promise; //#endregion