/** * @file versioning.ts * @description Version management interfaces * Semantic versioning, change tracking, branches, and diff comparison */ import type { TVersionType, TChangeOperation, TBranchStatus, } from './types.js'; // ============================================================================ // SEMANTIC VERSION // ============================================================================ /** * Semantic version following major.minor.patch pattern * For contracts: major = substantial changes, minor = clause additions, patch = wording fixes */ export interface ISemanticVersion { major: number; minor: number; patch: number; label?: 'alpha' | 'beta' | 'rc' | 'final'; } /** * Version string type */ export type TVersionString = `${number}.${number}.${number}` | `${number}.${number}.${number}-${string}`; // ============================================================================ // VERSION CHANGES // ============================================================================ /** * Represents a single atomic change within a version */ export interface IVersionChange { id: string; operation: TChangeOperation; targetPath: string; targetParagraphId?: string; previousValue?: string | object; newValue?: string | object; characterRange?: { start: number; end: number; }; timestamp: number; userId: string; userDisplayName?: string; changeReason?: string; } // ============================================================================ // AMENDMENT // ============================================================================ /** * Amendment-specific information */ export interface IAmendmentDetails { amendmentNumber: number; amendmentType: 'modification' | 'addendum' | 'rider' | 'waiver' | 'extension'; effectiveDate: number; supersedes: string[]; requiresResigning: boolean; affectedSections: string[]; } // ============================================================================ // VERSION // ============================================================================ /** * A complete version snapshot */ export interface IVersion { id: string; version: ISemanticVersion; versionString: TVersionString; type: TVersionType; createdAt: number; createdBy: string; createdByDisplayName?: string; previousVersionId?: string; changes: IVersionChange[]; contentHash: string; parentHash?: string; title?: string; description?: string; tags: string[]; isAmendment: boolean; amendmentDetails?: IAmendmentDetails; } // ============================================================================ // VERSION BRANCH // ============================================================================ /** * Branch for parallel version development */ export interface IVersionBranch { id: string; name: string; description?: string; createdAt: number; createdBy: string; baseVersionId: string; headVersionId: string; status: TBranchStatus; mergedIntoVersionId?: string; } // ============================================================================ // VERSION HISTORY // ============================================================================ /** * Complete version history for a contract */ export interface IVersionHistory { contractId: string; currentVersionId: string; currentVersion: ISemanticVersion; versions: IVersion[]; branches: IVersionBranch[]; mainBranchId: string; publishedVersions: string[]; amendmentVersions: string[]; } // ============================================================================ // VERSION DIFF // ============================================================================ /** * Diff summary statistics */ export interface IDiffSummary { insertions: number; deletions: number; modifications: number; paragraphsAffected: string[]; } /** * Version comparison result */ export interface IVersionDiff { fromVersionId: string; toVersionId: string; fromVersion: ISemanticVersion; toVersion: ISemanticVersion; changes: IVersionChange[]; summary: IDiffSummary; } // ============================================================================ // ROLLBACK // ============================================================================ /** * Rollback request */ export interface IRollbackRequest { targetVersionId: string; reason: string; requestedBy: string; requestedAt: number; createNewVersion: boolean; } // ============================================================================ // FACTORY FUNCTIONS // ============================================================================ /** * Create initial version */ export function createInitialVersion(userId: string, userDisplayName?: string): IVersion { const now = Date.now(); return { id: crypto.randomUUID(), version: { major: 0, minor: 1, patch: 0 }, versionString: '0.1.0', type: 'draft', createdAt: now, createdBy: userId, createdByDisplayName: userDisplayName, changes: [], contentHash: '', tags: [], isAmendment: false, }; } /** * Create empty version history */ export function createEmptyVersionHistory(contractId: string, initialVersion: IVersion): IVersionHistory { const mainBranchId = crypto.randomUUID(); return { contractId, currentVersionId: initialVersion.id, currentVersion: initialVersion.version, versions: [initialVersion], branches: [ { id: mainBranchId, name: 'main', createdAt: initialVersion.createdAt, createdBy: initialVersion.createdBy, baseVersionId: initialVersion.id, headVersionId: initialVersion.id, status: 'active', }, ], mainBranchId, publishedVersions: [], amendmentVersions: [], }; } /** * Increment semantic version */ export function incrementVersion( current: ISemanticVersion, incrementType: 'major' | 'minor' | 'patch' ): ISemanticVersion { switch (incrementType) { case 'major': return { major: current.major + 1, minor: 0, patch: 0 }; case 'minor': return { major: current.major, minor: current.minor + 1, patch: 0 }; case 'patch': return { major: current.major, minor: current.minor, patch: current.patch + 1 }; } } /** * Convert semantic version to string */ export function versionToString(version: ISemanticVersion): TVersionString { const base = `${version.major}.${version.minor}.${version.patch}` as TVersionString; if (version.label && version.label !== 'final') { return `${base}-${version.label}` as TVersionString; } return base; } /** * Parse version string to semantic version */ export function parseVersionString(versionString: string): ISemanticVersion { const [base, label] = versionString.split('-'); const [major, minor, patch] = base.split('.').map(Number); return { major: major || 0, minor: minor || 0, patch: patch || 0, label: label as ISemanticVersion['label'], }; }