/** * Puck Field Classifier * * Puck-aware field classification for conflict resolution. Understands * the Puck component structure (content, zones, root) and classifies * individual prop changes as source-only, target-only, or conflicting * using optional three-way comparison with a base snapshot. */ import type { PuckData } from '@pantheon-systems/css-client'; /** * Classification of a single field (prop) change across source and target. * * - `source-only`: Changed only in source (auto-mergeable from source) * - `target-only`: Changed only in target (auto-mergeable from target) * - `conflicting`: Changed in both source and target (requires resolution) */ export interface PuckFieldClassification { /** How this field change is classified. */ classification: 'source-only' | 'target-only' | 'conflicting'; /** Component ID (or 'root' for root props). */ componentId: string; /** Component type name (or 'root' for root props). */ componentType: string; /** The prop name that changed. */ propName: string; /** Value of this prop in the source snapshot. */ sourceValue: unknown; /** Value of this prop in the target snapshot. */ targetValue: unknown; /** Location path, e.g. 'content', 'zones/sidebar', 'root'. */ path: string; } /** * A group of field classifications for a single component, * with a flag indicating whether any true conflicts exist. */ export interface PuckComponentConflict { /** Component ID (or 'root'). */ componentId: string; /** Component type name (or 'root'). */ componentType: string; /** All classified fields for this component. */ fields: PuckFieldClassification[]; /** True if any field in this component is 'conflicting'. */ hasConflicts: boolean; } /** * Deeply compares two values for equality. * * Handles primitives, null, arrays, and plain objects. Uses the same * approach as diff.ts for consistency. * * @param a - First value * @param b - Second value * @returns True if the values are deeply equal */ export declare function deepEqual(a: unknown, b: unknown): boolean; /** * Detects whether the given data looks like a PuckData structure. * * Checks for the presence of a `content` array and a `root` object, * which are the defining characteristics of Puck page data. * * @param data - The data to check * @returns True if the data has the shape of PuckData * * @example * ```typescript * if (isPuckData(snapshot)) { * const fields = classifyPuckFields(source, target, base); * } * ``` */ export declare function isPuckData(data: unknown): boolean; /** * Classifies all fields across source and target PuckData snapshots. * * Walks content components, zone components, and root props, producing * a classification for each prop that differs. When a base snapshot is * provided, uses three-way comparison to distinguish source-only and * target-only changes from true conflicts. * * @param source - The source PuckData snapshot * @param target - The target PuckData snapshot * @param base - The common ancestor snapshot, or null for two-way comparison * @returns Array of field classifications across all components * * @example * ```typescript * const fields = classifyPuckFields(sourceBranch, targetBranch, commonAncestor); * const conflicts = fields.filter(f => f.classification === 'conflicting'); * ``` */ export declare function classifyPuckFields(source: PuckData, target: PuckData, base: PuckData | null): PuckFieldClassification[]; /** * Returns a human-readable path description for a prop change. * * Formats the path with arrow separators for easy reading in UIs: * - Content components: "Heading -> text" * - Zone components: "sidebar -> Widget -> label" * - Root props: "Page -> title" * * @param path - Location path (e.g. 'content', 'zones/sidebar', 'root') * @param componentType - The component type name * @param propName - The prop name * @returns Human-readable string like "Heading -> text" * * @example * ```typescript * getReadablePropPath('content', 'Heading', 'text'); * // => 'Heading \u2192 text' * * getReadablePropPath('zones/sidebar', 'Widget', 'label'); * // => 'sidebar \u2192 Widget \u2192 label' * * getReadablePropPath('root', 'root', 'title'); * // => 'Page \u2192 title' * ``` */ export declare function getReadablePropPath(path: string, componentType: string, propName: string): string; /** * Groups an array of field classifications by component ID. * * Produces one `PuckComponentConflict` per unique component, collecting * all its fields and determining whether any of them are true conflicts. * * @param fields - Array of field classifications to group * @returns Array of component conflict groups * * @example * ```typescript * const fields = classifyPuckFields(source, target, base); * const groups = groupFieldsByComponent(fields); * for (const group of groups) { * if (group.hasConflicts) { * console.log(`Conflicts in ${group.componentType}`); * } * } * ``` */ export declare function groupFieldsByComponent(fields: PuckFieldClassification[]): PuckComponentConflict[]; /** * Tracks resolution choices keyed by "componentId:propName". */ export type ResolutionMap = Record; /** Suffix used to key a whole-component decision alongside prop keys. */ export declare const COMPONENT_KEY_SUFFIX = "__component"; /** Returns the resolution-map key for a whole-component decision. */ export declare function componentKey(componentId: string): string; /** * Constructs a merged PuckData snapshot from resolution choices. * * Strategy: * - Start with a deep clone of the target snapshot as the base * - Insert workstream-only components chosen as source; remove Live-only ones chosen as source * - Apply source-only changes (overwrite with source values) * - Keep target-only changes (already in target clone) * - Apply user conflict resolutions (source or target) * * @param source - The source PuckData snapshot * @param target - The target PuckData snapshot * @param fields - Classified fields from classifyPuckFields * @param resolutions - Map of "componentId:propName" to 'source' | 'target' * @returns Merged PuckData snapshot */ export declare function buildMergedSnapshot(source: PuckData, target: PuckData, fields: PuckFieldClassification[], resolutions: ResolutionMap): PuckData;