legal-markdown-js
Version:
Node.js implementation of LegalMarkdown for processing legal documents with markdown and YAML - Complete feature parity with Ruby version
204 lines • 7.4 kB
TypeScript
/**
* @fileoverview Frontmatter Merger for Import Processing
*
* This module provides the core functionality for merging YAML frontmatter from
* imported files using the "source always wins" strategy with flattened merging.
* It enables granular merging at the property level while maintaining predictable
* conflict resolution.
*
* Features:
* - Flattened merging for granular property-level control
* - "Source always wins" conflict resolution strategy
* - Type conflict detection and validation
* - Reserved field filtering integration
* - Comprehensive merge statistics and reporting
*
* @example
* ```typescript
* import { mergeFlattened, validateMergeCompatibility } from './frontmatter-merger';
*
* const current = {
* title: "Main Document",
* config: { server: "prod", port: 8080 }
* };
*
* const imported = {
* config: { level: "high", server: "dev" }, // server conflict - current wins
* client: "Acme Corp" // new field - added
* };
*
* const result = mergeFlattened(current, imported);
* // {
* // title: "Main Document",
* // config: { server: "prod", port: 8080, level: "high" },
* // client: "Acme Corp"
* // }
* ```
*/
/**
* Options for frontmatter merging
*/
export interface MergeOptions {
/** Whether to filter reserved fields from imported metadata */
filterReserved?: boolean;
/** Whether to validate type compatibility before merging */
validateTypes?: boolean;
/** Whether to log merge operations for debugging */
logOperations?: boolean;
/** Custom conflict resolution strategy (future extension) */
conflictStrategy?: 'source-wins' | 'import-wins' | 'error';
/** Whether to include merge statistics in result */
includeStats?: boolean;
/** Maximum execution time in milliseconds (default: 10000ms) */
timeoutMs?: number;
}
/**
* Result of a merge operation
*/
export interface MergeResult {
/** Merged metadata object */
metadata: Record<string, any>;
/** Statistics about the merge operation */
stats?: MergeStats;
}
/**
* Statistics about a merge operation
*/
export interface MergeStats {
/** Total properties in current metadata */
currentProperties: number;
/** Total properties in imported metadata */
importedProperties: number;
/** Properties added from imported metadata */
propertiesAdded: number;
/** Properties that had conflicts (current wins) */
conflictsResolved: number;
/** Reserved fields filtered out */
reservedFieldsFiltered: number;
/** List of fields that were added */
addedFields: string[];
/** List of fields that had conflicts */
conflictedFields: string[];
/** List of reserved fields that were filtered */
filteredFields: string[];
}
/**
* Error thrown when merge validation fails
*/
export declare class MergeValidationError extends Error {
field: string;
currentType: string;
importedType: string;
constructor(message: string, field: string, currentType: string, importedType: string);
}
/**
* Merges imported frontmatter into current frontmatter using flattened strategy
*
* Uses the "source always wins" strategy where the current metadata takes precedence
* over imported metadata in case of conflicts. Supports granular merging at the
* property level using dot notation flattening.
*
* @param current - Current metadata (takes precedence)
* @param imported - Imported metadata to merge
* @param options - Merge configuration options
* @returns Merged metadata or MergeResult with statistics
*
* @example
* ```typescript
* const current = {
* document: { title: "Contract", version: "1.0" },
* client: "Main Client"
* };
*
* const imported = {
* document: { title: "Import Doc", author: "John Doe" }, // title conflicts - current wins
* metadata: { created: "@today" } // new field - added
* };
*
* const result = mergeFlattened(current, imported, { includeStats: true });
* // result.metadata = {
* // document: { title: "Contract", version: "1.0", author: "John Doe" },
* // client: "Main Client",
* // metadata: { created: "@today" }
* // }
* // result.stats.propertiesAdded = 2
* // result.stats.conflictsResolved = 1
* ```
*/
export declare function mergeFlattened(current: Record<string, any>, imported: Record<string, any>, options?: MergeOptions): Record<string, any>;
export declare function mergeFlattened(current: Record<string, any>, imported: Record<string, any>, options: MergeOptions & {
includeStats: true;
}): MergeResult;
/**
* Validates that two values are compatible for merging
*
* Checks if the current and imported values have compatible types.
* Throws MergeValidationError if types are incompatible.
*
* @param current - Current value
* @param imported - Imported value to merge
* @param key - Property key for error reporting
* @throws MergeValidationError when types are incompatible
*
* @example
* ```typescript
* // Compatible types - no error
* validateMergeCompatibility("string", "another string", "title");
* validateMergeCompatibility(42, 100, "count");
*
* // Incompatible types - throws error
* try {
* validateMergeCompatibility("string", { object: true }, "config");
* } catch (error) {
* console.log(error.message); // "Type conflict for 'config': current=string, imported=object"
* }
* ```
*/
export declare function validateMergeCompatibility(current: any, imported: any, key: string): void;
/**
* Performs a dry run merge to preview results
*
* Simulates a merge operation without actually performing it.
* Useful for validation and preview purposes.
*
* @param current - Current metadata
* @param imported - Imported metadata
* @param options - Merge options
* @returns Preview of merge results
*
* @example
* ```typescript
* const preview = previewMerge(current, imported, { filterReserved: true });
* console.log(`Would add ${preview.stats.propertiesAdded} properties`);
* console.log(`Would resolve ${preview.stats.conflictsResolved} conflicts`);
* console.log(`Would filter ${preview.stats.reservedFieldsFiltered} reserved fields`);
* ```
*/
export declare function previewMerge(current: Record<string, any>, imported: Record<string, any>, options?: MergeOptions): MergeResult;
/**
* Merges multiple imported metadata objects sequentially
*
* Applies the merge operation sequentially across multiple imports,
* where each result becomes the new "current" for the next merge.
*
* @param initial - Initial metadata (usually from main document)
* @param imports - Array of imported metadata objects to merge
* @param options - Merge options applied to all operations
* @returns Final merged metadata with cumulative statistics
*
* @example
* ```typescript
* const initial = { title: "Main Doc" };
* const imports = [
* { config: { level: "high" } },
* { config: { debug: true }, client: "Acme" },
* { metadata: { version: "1.0" } }
* ];
*
* const result = mergeSequentially(initial, imports, { includeStats: true });
* // result.metadata contains all merged properties
* // result.stats contains cumulative statistics
* ```
*/
export declare function mergeSequentially(initial: Record<string, any>, imports: Record<string, any>[], options?: MergeOptions): MergeResult;
//# sourceMappingURL=frontmatter-merger.d.ts.map