diff --git a/crates/node/src/tests.rs b/crates/node/src/tests.rs index 6e33ddae..246b9f4d 100644 --- a/crates/node/src/tests.rs +++ b/crates/node/src/tests.rs @@ -5718,3 +5718,986 @@ mod config_scenario_tests { assert_eq!(error.code, "ECONFIG"); } } + +// ============================================================================ +// Upgrade Types Tests (Story 8.1) +// ============================================================================ + +/// Tests for upgrade command type definitions. +#[cfg(test)] +mod upgrade_types_tests { + use crate::error::ErrorInfo; + use crate::types::upgrade::{ + AppliedUpgradeInfo, ApplySummaryInfo, BackupCleanApiResponse, BackupCleanData, + BackupCleanParams, BackupInfo, BackupListApiResponse, BackupListData, BackupListParams, + BackupRestoreApiResponse, BackupRestoreData, BackupRestoreParams, DEFAULT_KEEP_COUNT, + DependencyUpgradeInfo, FailedUpgradeInfo, PackageUpgradeInfo, SkippedUpgradeInfo, + UpgradeApplyApiResponse, UpgradeApplyData, UpgradeApplyParams, UpgradeCheckApiResponse, + UpgradeCheckData, UpgradeCheckParams, UpgradeSelectionInfo, UpgradeSummaryInfo, + VALID_DEPENDENCY_TYPES, VALID_UPGRADE_TYPES, + }; + + // ======================================================================== + // Constants Tests + // ======================================================================== + + #[test] + fn test_valid_upgrade_types() { + assert!(VALID_UPGRADE_TYPES.contains(&"major")); + assert!(VALID_UPGRADE_TYPES.contains(&"minor")); + assert!(VALID_UPGRADE_TYPES.contains(&"patch")); + assert_eq!(VALID_UPGRADE_TYPES.len(), 3); + } + + #[test] + fn test_valid_dependency_types() { + assert!(VALID_DEPENDENCY_TYPES.contains(&"regular")); + assert!(VALID_DEPENDENCY_TYPES.contains(&"dev")); + assert!(VALID_DEPENDENCY_TYPES.contains(&"peer")); + assert!(VALID_DEPENDENCY_TYPES.contains(&"optional")); + assert_eq!(VALID_DEPENDENCY_TYPES.len(), 4); + } + + #[test] + fn test_default_keep_count() { + assert_eq!(DEFAULT_KEEP_COUNT, 5); + } + + // ======================================================================== + // UpgradeCheckParams Tests + // ======================================================================== + + #[test] + fn test_upgrade_check_params_new() { + let params = UpgradeCheckParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.include_major.is_none()); + assert!(params.include_minor.is_none()); + assert!(params.include_patch.is_none()); + assert!(params.include_dev_dependencies.is_none()); + assert!(params.include_peer_dependencies.is_none()); + assert!(params.packages.is_none()); + } + + #[test] + fn test_upgrade_check_params_builder_chain() { + let params = UpgradeCheckParams::new("/workspace") + .with_config_path("/workspace/repo.config.json") + .with_include_major(false) + .with_include_minor(true) + .with_include_patch(true) + .with_include_dev_dependencies(true) + .with_include_peer_dependencies(false) + .with_packages(vec!["@scope/core".to_string()]); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.config_path, Some("/workspace/repo.config.json".to_string())); + assert_eq!(params.include_major, Some(false)); + assert_eq!(params.include_minor, Some(true)); + assert_eq!(params.include_patch, Some(true)); + assert_eq!(params.include_dev_dependencies, Some(true)); + assert_eq!(params.include_peer_dependencies, Some(false)); + assert_eq!(params.packages, Some(vec!["@scope/core".to_string()])); + } + + #[test] + fn test_upgrade_check_params_with_upgrade_levels() { + let params = UpgradeCheckParams::new(".").with_upgrade_levels(false, true, true); + + assert_eq!(params.include_major, Some(false)); + assert_eq!(params.include_minor, Some(true)); + assert_eq!(params.include_patch, Some(true)); + } + + #[test] + fn test_upgrade_check_params_clone() { + let params = + UpgradeCheckParams::new("/workspace").with_include_major(true).with_include_minor(true); + let cloned = params.clone(); + + assert_eq!(cloned.root, params.root); + assert_eq!(cloned.include_major, params.include_major); + assert_eq!(cloned.include_minor, params.include_minor); + } + + #[test] + fn test_upgrade_check_params_serialize() { + let params = UpgradeCheckParams::new("/workspace").with_include_patch(true); + let json = serde_json::to_string(¶ms).unwrap_or_default(); + + assert!(json.contains("\"root\":\"/workspace\"")); + assert!(json.contains("\"include_patch\":true")); + assert!(!json.contains("\"config_path\"")); + } + + // ======================================================================== + // UpgradeApplyParams Tests + // ======================================================================== + + #[test] + fn test_upgrade_apply_params_new() { + let params = UpgradeApplyParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.create_backup.is_none()); + assert!(params.create_changeset.is_none()); + assert!(params.selection.is_none()); + assert!(params.dry_run.is_none()); + assert!(params.packages.is_none()); + } + + #[test] + fn test_upgrade_apply_params_builder_chain() { + let params = UpgradeApplyParams::new("/workspace") + .with_config_path("/workspace/repo.config.json") + .with_create_backup(true) + .with_create_changeset(true) + .with_selection(UpgradeSelectionInfo::patch_only()) + .with_dry_run(false) + .with_packages(vec!["@scope/core".to_string()]); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.config_path, Some("/workspace/repo.config.json".to_string())); + assert_eq!(params.create_backup, Some(true)); + assert_eq!(params.create_changeset, Some(true)); + assert!(params.selection.is_some()); + assert_eq!(params.dry_run, Some(false)); + assert_eq!(params.packages, Some(vec!["@scope/core".to_string()])); + } + + #[test] + fn test_upgrade_apply_params_clone() { + let params = UpgradeApplyParams::new("/workspace").with_create_backup(true); + let cloned = params.clone(); + + assert_eq!(cloned.root, params.root); + assert_eq!(cloned.create_backup, params.create_backup); + } + + // ======================================================================== + // BackupListParams Tests + // ======================================================================== + + #[test] + fn test_backup_list_params_new() { + let params = BackupListParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + } + + #[test] + fn test_backup_list_params_with_config() { + let params = BackupListParams::new("/workspace").with_config_path("/config.json"); + + assert_eq!(params.config_path, Some("/config.json".to_string())); + } + + // ======================================================================== + // BackupRestoreParams Tests + // ======================================================================== + + #[test] + fn test_backup_restore_params_new() { + let params = BackupRestoreParams::new("/workspace", "backup-2024-01-15-123456"); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.backup_id, "backup-2024-01-15-123456"); + assert!(params.config_path.is_none()); + } + + #[test] + fn test_backup_restore_params_with_config() { + let params = + BackupRestoreParams::new("/workspace", "backup-id").with_config_path("/config.json"); + + assert_eq!(params.config_path, Some("/config.json".to_string())); + } + + // ======================================================================== + // BackupCleanParams Tests + // ======================================================================== + + #[test] + fn test_backup_clean_params_new() { + let params = BackupCleanParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.keep_count.is_none()); + } + + #[test] + fn test_backup_clean_params_with_keep_count() { + let params = BackupCleanParams::new("/workspace").with_keep_count(3); + + assert_eq!(params.keep_count, Some(3)); + } + + // ======================================================================== + // UpgradeSelectionInfo Tests + // ======================================================================== + + #[test] + fn test_upgrade_selection_info_all() { + let selection = UpgradeSelectionInfo::all(); + + assert_eq!(selection.major, Some(true)); + assert_eq!(selection.minor, Some(true)); + assert_eq!(selection.patch, Some(true)); + assert!(selection.packages.is_none()); + assert!(selection.dependencies.is_none()); + } + + #[test] + fn test_upgrade_selection_info_patch_only() { + let selection = UpgradeSelectionInfo::patch_only(); + + assert_eq!(selection.major, Some(false)); + assert_eq!(selection.minor, Some(false)); + assert_eq!(selection.patch, Some(true)); + } + + #[test] + fn test_upgrade_selection_info_minor_and_patch() { + let selection = UpgradeSelectionInfo::minor_and_patch(); + + assert_eq!(selection.major, Some(false)); + assert_eq!(selection.minor, Some(true)); + assert_eq!(selection.patch, Some(true)); + } + + #[test] + fn test_upgrade_selection_info_for_packages() { + let selection = UpgradeSelectionInfo::for_packages(vec!["@scope/core".to_string()]); + + assert_eq!(selection.packages, Some(vec!["@scope/core".to_string()])); + assert!(selection.dependencies.is_none()); + } + + #[test] + fn test_upgrade_selection_info_for_dependencies() { + let selection = UpgradeSelectionInfo::for_dependencies(vec!["lodash".to_string()]); + + assert!(selection.packages.is_none()); + assert_eq!(selection.dependencies, Some(vec!["lodash".to_string()])); + } + + #[test] + fn test_upgrade_selection_info_default() { + let selection = UpgradeSelectionInfo::default(); + + assert_eq!(selection.major, Some(true)); + assert_eq!(selection.minor, Some(true)); + assert_eq!(selection.patch, Some(true)); + } + + #[test] + fn test_upgrade_selection_info_with_packages() { + let selection = UpgradeSelectionInfo::patch_only().with_packages(vec!["pkg".to_string()]); + + assert_eq!(selection.patch, Some(true)); + assert_eq!(selection.packages, Some(vec!["pkg".to_string()])); + } + + #[test] + fn test_upgrade_selection_info_with_dependencies() { + let selection = + UpgradeSelectionInfo::minor_and_patch().with_dependencies(vec!["lodash".to_string()]); + + assert_eq!(selection.dependencies, Some(vec!["lodash".to_string()])); + } + + // ======================================================================== + // DependencyUpgradeInfo Tests + // ======================================================================== + + #[test] + fn test_dependency_upgrade_info_new() { + let info = DependencyUpgradeInfo::new("lodash", "4.17.20", "4.17.21", "patch", "regular"); + + assert_eq!(info.name, "lodash"); + assert_eq!(info.current_version, "4.17.20"); + assert_eq!(info.latest_version, "4.17.21"); + assert_eq!(info.upgrade_type, "patch"); + assert_eq!(info.dependency_type, "regular"); + } + + #[test] + fn test_dependency_upgrade_info_patch() { + let info = DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21"); + + assert_eq!(info.upgrade_type, "patch"); + assert_eq!(info.dependency_type, "regular"); + assert!(info.is_patch()); + assert!(!info.is_minor()); + assert!(!info.is_major()); + } + + #[test] + fn test_dependency_upgrade_info_minor() { + let info = DependencyUpgradeInfo::minor("lodash", "4.16.0", "4.17.0"); + + assert_eq!(info.upgrade_type, "minor"); + assert!(info.is_minor()); + } + + #[test] + fn test_dependency_upgrade_info_major() { + let info = DependencyUpgradeInfo::major("react", "17.0.0", "18.0.0"); + + assert_eq!(info.upgrade_type, "major"); + assert!(info.is_major()); + } + + #[test] + fn test_dependency_upgrade_info_dev() { + let info = DependencyUpgradeInfo::dev("typescript", "4.9.0", "5.0.0", "major"); + + assert_eq!(info.dependency_type, "dev"); + assert!(info.is_dev_dependency()); + } + + #[test] + fn test_dependency_upgrade_info_clone() { + let info = DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21"); + let cloned = info.clone(); + + assert_eq!(cloned.name, info.name); + assert_eq!(cloned.current_version, info.current_version); + assert_eq!(cloned.latest_version, info.latest_version); + } + + // ======================================================================== + // PackageUpgradeInfo Tests + // ======================================================================== + + #[test] + fn test_package_upgrade_info_new() { + let info = PackageUpgradeInfo::new("@scope/core", "packages/core"); + + assert_eq!(info.package_name, "@scope/core"); + assert_eq!(info.package_path, "packages/core"); + assert!(info.dependencies.is_empty()); + assert_eq!(info.upgrade_count(), 0); + } + + #[test] + fn test_package_upgrade_info_with_dependencies() { + let deps = vec![ + DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21"), + DependencyUpgradeInfo::minor("axios", "0.27.0", "0.28.0"), + ]; + let info = PackageUpgradeInfo::with_dependencies("@scope/core", "packages/core", deps); + + assert_eq!(info.upgrade_count(), 2); + assert_eq!(info.patch_count(), 1); + assert_eq!(info.minor_count(), 1); + assert_eq!(info.major_count(), 0); + } + + #[test] + fn test_package_upgrade_info_with_dependency() { + let info = PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::major("react", "17.0.0", "18.0.0")); + + assert_eq!(info.upgrade_count(), 1); + assert!(info.has_major_upgrades()); + } + + #[test] + fn test_package_upgrade_info_counts() { + let info = PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::major("react", "17.0.0", "18.0.0")) + .with_dependency(DependencyUpgradeInfo::minor("axios", "0.27.0", "0.28.0")) + .with_dependency(DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21")); + + assert_eq!(info.major_count(), 1); + assert_eq!(info.minor_count(), 1); + assert_eq!(info.patch_count(), 1); + assert!(info.has_major_upgrades()); + } + + // ======================================================================== + // UpgradeSummaryInfo Tests + // ======================================================================== + + #[test] + fn test_upgrade_summary_info_new() { + let summary = UpgradeSummaryInfo::new(10, 15, 2, 5, 8); + + assert_eq!(summary.packages_analyzed, 10); + assert_eq!(summary.total_upgrades, 15); + assert_eq!(summary.major_upgrades, 2); + assert_eq!(summary.minor_upgrades, 5); + assert_eq!(summary.patch_upgrades, 8); + } + + #[test] + fn test_upgrade_summary_info_empty() { + let summary = UpgradeSummaryInfo::empty(10); + + assert_eq!(summary.packages_analyzed, 10); + assert_eq!(summary.total_upgrades, 0); + assert!(summary.is_empty()); + assert!(!summary.has_breaking_changes()); + } + + #[test] + fn test_upgrade_summary_info_from_packages() { + let packages = vec![ + PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::major("react", "17.0.0", "18.0.0")) + .with_dependency(DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21")), + PackageUpgradeInfo::new("@scope/utils", "packages/utils") + .with_dependency(DependencyUpgradeInfo::minor("axios", "0.27.0", "0.28.0")), + ]; + + let summary = UpgradeSummaryInfo::from_packages(&packages); + + assert_eq!(summary.packages_analyzed, 2); + assert_eq!(summary.total_upgrades, 3); + assert_eq!(summary.major_upgrades, 1); + assert_eq!(summary.minor_upgrades, 1); + assert_eq!(summary.patch_upgrades, 1); + assert!(summary.has_breaking_changes()); + } + + #[test] + fn test_upgrade_summary_info_default() { + let summary = UpgradeSummaryInfo::default(); + + assert_eq!(summary.packages_analyzed, 0); + assert!(summary.is_empty()); + } + + // ======================================================================== + // AppliedUpgradeInfo Tests + // ======================================================================== + + #[test] + fn test_applied_upgrade_info_new() { + let info = AppliedUpgradeInfo::new("@scope/core", "lodash", "4.17.20", "4.17.21", "patch"); + + assert_eq!(info.package_name, "@scope/core"); + assert_eq!(info.dependency_name, "lodash"); + assert_eq!(info.old_version, "4.17.20"); + assert_eq!(info.new_version, "4.17.21"); + assert_eq!(info.upgrade_type, "patch"); + } + + #[test] + fn test_applied_upgrade_info_from_dependency_upgrade() { + let dep = DependencyUpgradeInfo::minor("axios", "0.27.0", "0.28.0"); + let info = AppliedUpgradeInfo::from_dependency_upgrade("@scope/core", &dep); + + assert_eq!(info.package_name, "@scope/core"); + assert_eq!(info.dependency_name, "axios"); + assert_eq!(info.old_version, "0.27.0"); + assert_eq!(info.new_version, "0.28.0"); + assert_eq!(info.upgrade_type, "minor"); + } + + // ======================================================================== + // SkippedUpgradeInfo Tests + // ======================================================================== + + #[test] + fn test_skipped_upgrade_info_new() { + let info = SkippedUpgradeInfo::new( + "@scope/core", + "react", + "17.0.0", + "18.0.0", + "Major upgrade excluded by selection", + ); + + assert_eq!(info.package_name, "@scope/core"); + assert_eq!(info.dependency_name, "react"); + assert_eq!(info.current_version, "17.0.0"); + assert_eq!(info.available_version, "18.0.0"); + assert!(info.reason.contains("Major upgrade")); + } + + #[test] + fn test_skipped_upgrade_info_filtered() { + let info = SkippedUpgradeInfo::filtered("@scope/core", "react", "17.0.0", "18.0.0"); + + assert!(info.reason.contains("Filtered")); + } + + // ======================================================================== + // FailedUpgradeInfo Tests + // ======================================================================== + + #[test] + fn test_failed_upgrade_info_new() { + let info = FailedUpgradeInfo::new( + "@scope/core", + "lodash", + "4.17.20", + "4.17.21", + "Failed to write package.json", + ); + + assert_eq!(info.package_name, "@scope/core"); + assert_eq!(info.dependency_name, "lodash"); + assert_eq!(info.current_version, "4.17.20"); + assert_eq!(info.target_version, "4.17.21"); + assert!(info.error.contains("Failed to write")); + } + + // ======================================================================== + // ApplySummaryInfo Tests + // ======================================================================== + + #[test] + fn test_apply_summary_info_new() { + let summary = ApplySummaryInfo::new( + 10, + 5, + 2, + vec!["@scope/core".to_string(), "@scope/utils".to_string()], + ); + + assert_eq!(summary.total_applied, 10); + assert_eq!(summary.total_skipped, 5); + assert_eq!(summary.total_failed, 2); + assert_eq!(summary.packages_modified.len(), 2); + assert_eq!(summary.total_processed(), 17); + } + + #[test] + fn test_apply_summary_info_empty() { + let summary = ApplySummaryInfo::empty(); + + assert_eq!(summary.total_applied, 0); + assert_eq!(summary.total_skipped, 0); + assert_eq!(summary.total_failed, 0); + assert!(summary.packages_modified.is_empty()); + } + + #[test] + fn test_apply_summary_info_all_succeeded() { + let summary = ApplySummaryInfo::new(10, 0, 0, vec!["@scope/core".to_string()]); + + assert!(summary.all_succeeded()); + assert!(!summary.has_failures()); + } + + #[test] + fn test_apply_summary_info_has_failures() { + let summary = ApplySummaryInfo::new(8, 0, 2, vec!["@scope/core".to_string()]); + + assert!(!summary.all_succeeded()); + assert!(summary.has_failures()); + } + + #[test] + fn test_apply_summary_info_default() { + let summary = ApplySummaryInfo::default(); + + assert_eq!(summary.total_processed(), 0); + } + + // ======================================================================== + // BackupInfo Tests + // ======================================================================== + + #[test] + fn test_backup_info_new() { + let info = BackupInfo::new( + "backup-2024-01-15-123456", + "2024-01-15T12:34:56Z", + vec!["@scope/core".to_string(), "@scope/utils".to_string()], + 1024, + ); + + assert_eq!(info.id, "backup-2024-01-15-123456"); + assert_eq!(info.created_at, "2024-01-15T12:34:56Z"); + assert_eq!(info.packages.len(), 2); + assert!((info.size_bytes - 1024.0).abs() < f64::EPSILON); + assert_eq!(info.package_count(), 2); + } + + // ======================================================================== + // UpgradeCheckData Tests + // ======================================================================== + + #[test] + fn test_upgrade_check_data_new() { + let packages = vec![ + PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21")), + ]; + let summary = UpgradeSummaryInfo::new(1, 1, 0, 0, 1); + + let data = UpgradeCheckData::new(packages, summary); + + assert_eq!(data.packages.len(), 1); + assert_eq!(data.summary.total_upgrades, 1); + assert!(data.has_upgrades()); + assert!(!data.has_breaking_changes()); + } + + #[test] + fn test_upgrade_check_data_empty() { + let data = UpgradeCheckData::empty(10); + + assert!(data.packages.is_empty()); + assert_eq!(data.summary.packages_analyzed, 10); + assert!(!data.has_upgrades()); + } + + #[test] + fn test_upgrade_check_data_from_packages() { + let packages = vec![ + PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::major("react", "17.0.0", "18.0.0")), + ]; + + let data = UpgradeCheckData::from_packages(packages); + + assert_eq!(data.packages.len(), 1); + assert_eq!(data.summary.major_upgrades, 1); + assert!(data.has_breaking_changes()); + } + + // ======================================================================== + // UpgradeApplyData Tests + // ======================================================================== + + #[test] + fn test_upgrade_apply_data_new() { + let applied = + vec![AppliedUpgradeInfo::new("@scope/core", "lodash", "4.17.20", "4.17.21", "patch")]; + let summary = ApplySummaryInfo::new(1, 0, 0, vec!["@scope/core".to_string()]); + + let data = UpgradeApplyData::new(applied, vec![], vec![], summary); + + assert_eq!(data.applied.len(), 1); + assert!(data.skipped.is_empty()); + assert!(data.failed.is_empty()); + assert!(data.all_succeeded()); + assert!(!data.has_backup()); + assert!(!data.has_changeset()); + } + + #[test] + fn test_upgrade_apply_data_empty() { + let data = UpgradeApplyData::empty(); + + assert!(data.applied.is_empty()); + assert!(data.skipped.is_empty()); + assert!(data.failed.is_empty()); + assert!(data.backup_id.is_none()); + assert!(data.changeset_id.is_none()); + } + + #[test] + fn test_upgrade_apply_data_with_backup() { + let data = UpgradeApplyData::empty().with_backup_id("backup-2024-01-15-123456"); + + assert!(data.has_backup()); + assert_eq!(data.backup_id, Some("backup-2024-01-15-123456".to_string())); + } + + #[test] + fn test_upgrade_apply_data_with_changeset() { + let data = UpgradeApplyData::empty().with_changeset_id("feature/upgrades"); + + assert!(data.has_changeset()); + assert_eq!(data.changeset_id, Some("feature/upgrades".to_string())); + } + + // ======================================================================== + // BackupListData Tests + // ======================================================================== + + #[test] + fn test_backup_list_data_new() { + let backups = vec![BackupInfo::new( + "backup-2024-01-15-123456", + "2024-01-15T12:34:56Z", + vec!["@scope/core".to_string()], + 1024, + )]; + + let data = BackupListData::new(backups); + + assert_eq!(data.count(), 1); + assert!(!data.is_empty()); + } + + #[test] + fn test_backup_list_data_empty() { + let data = BackupListData::empty(); + + assert!(data.is_empty()); + assert_eq!(data.count(), 0); + } + + // ======================================================================== + // BackupRestoreData Tests + // ======================================================================== + + #[test] + fn test_backup_restore_data_new() { + let data = BackupRestoreData::new( + "backup-2024-01-15-123456", + vec!["@scope/core".to_string(), "@scope/utils".to_string()], + ); + + assert_eq!(data.backup_id, "backup-2024-01-15-123456"); + assert_eq!(data.packages_restored, 2); + assert_eq!(data.packages.len(), 2); + } + + // ======================================================================== + // BackupCleanData Tests + // ======================================================================== + + #[test] + fn test_backup_clean_data_new() { + let data = BackupCleanData::new(3, 5, 10240); + + assert_eq!(data.backups_removed, 3); + assert_eq!(data.backups_kept, 5); + assert!((data.bytes_freed - 10240.0).abs() < f64::EPSILON); + } + + #[test] + fn test_backup_clean_data_nothing_to_clean() { + let data = BackupCleanData::nothing_to_clean(5); + + assert_eq!(data.backups_removed, 0); + assert_eq!(data.backups_kept, 5); + assert!((data.bytes_freed - 0.0).abs() < f64::EPSILON); + } + + // ======================================================================== + // API Response Tests + // ======================================================================== + + #[test] + fn test_upgrade_check_api_response_success() { + let data = UpgradeCheckData::empty(10); + let response = UpgradeCheckApiResponse::success(data); + + assert!(response.success); + assert!(response.is_success()); + assert!(!response.is_failure()); + assert!(response.data.is_some()); + assert!(response.error.is_none()); + } + + #[test] + fn test_upgrade_check_api_response_failure() { + let error = ErrorInfo::network("Registry unreachable"); + let response = UpgradeCheckApiResponse::failure(error); + + assert!(!response.success); + assert!(response.is_failure()); + assert!(response.data.is_none()); + assert!(response.error.is_some()); + assert_eq!(response.error.as_ref().map(|e| e.code.as_str()), Some("ENETWORK")); + } + + #[test] + fn test_upgrade_apply_api_response_success() { + let data = UpgradeApplyData::empty(); + let response = UpgradeApplyApiResponse::success(data); + + assert!(response.is_success()); + assert!(response.data.is_some()); + } + + #[test] + fn test_upgrade_apply_api_response_failure() { + let error = ErrorInfo::io("Failed to write file", Some("package.json")); + let response = UpgradeApplyApiResponse::failure(error); + + assert!(response.is_failure()); + assert!(response.error.is_some()); + } + + #[test] + fn test_backup_list_api_response_success() { + let data = BackupListData::empty(); + let response = BackupListApiResponse::success(data); + + assert!(response.is_success()); + } + + #[test] + fn test_backup_list_api_response_failure() { + let error = ErrorInfo::not_found("Backup directory not found", Some("backups")); + let response = BackupListApiResponse::failure(error); + + assert!(response.is_failure()); + assert_eq!(response.error.as_ref().map(|e| e.code.as_str()), Some("ENOENT")); + } + + #[test] + fn test_backup_restore_api_response_success() { + let data = BackupRestoreData::new("backup-id", vec!["@scope/core".to_string()]); + let response = BackupRestoreApiResponse::success(data); + + assert!(response.is_success()); + } + + #[test] + fn test_backup_restore_api_response_failure() { + let error = ErrorInfo::not_found("Backup not found", Some("backup-id")); + let response = BackupRestoreApiResponse::failure(error); + + assert!(response.is_failure()); + } + + #[test] + fn test_backup_clean_api_response_success() { + let data = BackupCleanData::new(2, 3, 5120); + let response = BackupCleanApiResponse::success(data); + + assert!(response.is_success()); + } + + #[test] + fn test_backup_clean_api_response_failure() { + let error = ErrorInfo::io("Permission denied", Some("backups")); + let response = BackupCleanApiResponse::failure(error); + + assert!(response.is_failure()); + } + + // ======================================================================== + // Serialization Tests + // ======================================================================== + + #[test] + fn test_dependency_upgrade_info_serialize() { + let info = DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21"); + let json = serde_json::to_string(&info).unwrap_or_default(); + + assert!(json.contains("\"name\":\"lodash\"")); + assert!(json.contains("\"current_version\":\"4.17.20\"")); + assert!(json.contains("\"latest_version\":\"4.17.21\"")); + assert!(json.contains("\"upgrade_type\":\"patch\"")); + } + + #[test] + fn test_upgrade_check_data_serialize() { + let data = UpgradeCheckData::empty(10); + let json = serde_json::to_string(&data).unwrap_or_default(); + + assert!(json.contains("\"packages\":[]")); + assert!(json.contains("\"packages_analyzed\":10")); + } + + #[test] + fn test_upgrade_apply_data_serialize() { + let data = UpgradeApplyData::empty().with_backup_id("backup-123"); + let json = serde_json::to_string(&data).unwrap_or_default(); + + assert!(json.contains("\"backup_id\":\"backup-123\"")); + assert!(json.contains("\"applied\":[]")); + } + + // ======================================================================== + // Complete Scenario Tests + // ======================================================================== + + #[test] + fn test_complete_upgrade_check_scenario() { + // Simulate checking for upgrades + let packages = vec![ + PackageUpgradeInfo::new("@scope/core", "packages/core") + .with_dependency(DependencyUpgradeInfo::major("react", "17.0.2", "18.2.0")) + .with_dependency(DependencyUpgradeInfo::patch("lodash", "4.17.20", "4.17.21")), + PackageUpgradeInfo::new("@scope/utils", "packages/utils") + .with_dependency(DependencyUpgradeInfo::minor("axios", "0.27.2", "0.28.0")), + ]; + + let data = UpgradeCheckData::from_packages(packages); + let response = UpgradeCheckApiResponse::success(data); + + assert!(response.is_success()); + let data = response.data.unwrap(); + assert_eq!(data.packages.len(), 2); + assert_eq!(data.summary.total_upgrades, 3); + assert_eq!(data.summary.major_upgrades, 1); + assert_eq!(data.summary.minor_upgrades, 1); + assert_eq!(data.summary.patch_upgrades, 1); + assert!(data.has_breaking_changes()); + } + + #[test] + fn test_complete_upgrade_apply_scenario() { + // Simulate applying upgrades with backup + let applied = vec![ + AppliedUpgradeInfo::new("@scope/core", "lodash", "4.17.20", "4.17.21", "patch"), + AppliedUpgradeInfo::new("@scope/utils", "axios", "0.27.2", "0.28.0", "minor"), + ]; + let skipped = + vec![SkippedUpgradeInfo::filtered("@scope/core", "react", "17.0.2", "18.2.0")]; + let summary = ApplySummaryInfo::new( + 2, + 1, + 0, + vec!["@scope/core".to_string(), "@scope/utils".to_string()], + ); + + let data = UpgradeApplyData::new(applied, skipped, vec![], summary) + .with_backup_id("backup-2024-01-15-123456"); + + let response = UpgradeApplyApiResponse::success(data); + + assert!(response.is_success()); + let data = response.data.unwrap(); + assert_eq!(data.applied.len(), 2); + assert_eq!(data.skipped.len(), 1); + assert!(data.failed.is_empty()); + assert!(data.has_backup()); + assert!(data.all_succeeded()); + } + + #[test] + fn test_complete_backup_workflow_scenario() { + // List backups + let backups = vec![ + BackupInfo::new( + "backup-2024-01-15-123456", + "2024-01-15T12:34:56Z", + vec!["@scope/core".to_string()], + 2048, + ), + BackupInfo::new( + "backup-2024-01-14-112233", + "2024-01-14T11:22:33Z", + vec!["@scope/core".to_string(), "@scope/utils".to_string()], + 4096, + ), + ]; + + let list_data = BackupListData::new(backups); + let list_response = BackupListApiResponse::success(list_data); + assert!(list_response.is_success()); + assert_eq!(list_response.data.as_ref().map(BackupListData::count), Some(2)); + + // Restore from backup + let restore_data = + BackupRestoreData::new("backup-2024-01-15-123456", vec!["@scope/core".to_string()]); + let restore_response = BackupRestoreApiResponse::success(restore_data); + assert!(restore_response.is_success()); + assert_eq!(restore_response.data.as_ref().map(|d| d.packages_restored), Some(1)); + + // Clean old backups + let clean_data = BackupCleanData::new(1, 1, 4096); + let clean_response = BackupCleanApiResponse::success(clean_data); + assert!(clean_response.is_success()); + assert_eq!(clean_response.data.as_ref().map(|d| d.backups_removed), Some(1)); + } +} diff --git a/crates/node/src/types/mod.rs b/crates/node/src/types/mod.rs index 0fedcaa1..c2272212 100644 --- a/crates/node/src/types/mod.rs +++ b/crates/node/src/types/mod.rs @@ -186,9 +186,47 @@ pub(crate) use bump::{ VALID_DEPENDENCY_TYPES, }; -// TODO: will be implemented on story 8.1 (upgrade types) +// Upgrade types (Story 8.1 - Implemented) pub(crate) mod upgrade; +// Re-export upgrade types for easier access +// Allow unused imports - these will be used by upgrade commands (Stories 8.2-8.4) +#[allow(unused_imports)] +pub(crate) use upgrade::{ + // Supporting Types + AppliedUpgradeInfo, + ApplySummaryInfo, + // API Responses + BackupCleanApiResponse, + // Response Data + BackupCleanData, + // Input Parameters + BackupCleanParams, + BackupInfo, + BackupListApiResponse, + BackupListData, + BackupListParams, + BackupRestoreApiResponse, + BackupRestoreData, + BackupRestoreParams, + // Constants + DEFAULT_KEEP_COUNT, + DependencyUpgradeInfo, + FailedUpgradeInfo, + PackageUpgradeInfo, + SkippedUpgradeInfo, + UpgradeApplyApiResponse, + UpgradeApplyData, + UpgradeApplyParams, + UpgradeCheckApiResponse, + UpgradeCheckData, + UpgradeCheckParams, + UpgradeSelectionInfo, + UpgradeSummaryInfo, + VALID_DEPENDENCY_TYPES as UPGRADE_VALID_DEPENDENCY_TYPES, + VALID_UPGRADE_TYPES, +}; + // TODO: will be implemented on story 9.1 (audit types) pub(crate) mod audit; diff --git a/crates/node/src/types/upgrade.rs b/crates/node/src/types/upgrade.rs index 2eb54b54..3b12e704 100644 --- a/crates/node/src/types/upgrade.rs +++ b/crates/node/src/types/upgrade.rs @@ -1,41 +1,74 @@ -//! Upgrade command type definitions. +//! Upgrade command type definitions for Node.js bindings. //! //! # What //! -//! This module contains type definitions for upgrade commands (check, apply, -//! backup), including parameter structures and response data types. +//! This module defines all NAPI-compatible type structures for upgrade commands, +//! including input parameters and response data types. Upgrade commands enable +//! detection and application of dependency updates from npm registries, with +//! backup and restore capabilities for safe upgrade workflows. //! //! # How //! -//! Types are defined with `#[napi(object)]` attribute to be exposed as -//! JavaScript objects. The module provides: +//! Types are defined with the `#[napi(object)]` attribute to be automatically +//! exposed as JavaScript objects. The module provides: //! -//! - `UpgradeCheckParams`: Input parameters for checking available upgrades -//! - `UpgradeCheckData`: Response data containing available upgrades -//! - `UpgradeApplyParams`: Input parameters for applying upgrades -//! - `UpgradeApplyData`: Response data containing applied upgrades -//! - `BackupCreateParams`: Input parameters for creating a backup -//! - `BackupCreateData`: Response data containing backup information -//! - `BackupRestoreParams`: Input parameters for restoring from backup -//! - `BackupRestoreData`: Response data containing restore results -//! - `BackupListParams`: Input parameters for listing backups -//! - `BackupListData`: Response data containing backup list +//! - **Input Parameters**: +//! - `UpgradeCheckParams`: Parameters for checking available upgrades +//! - `UpgradeApplyParams`: Parameters for applying upgrades +//! - `BackupListParams`: Parameters for listing backups +//! - `BackupRestoreParams`: Parameters for restoring from backup +//! - `BackupCleanParams`: Parameters for cleaning old backups +//! +//! - **Response Data**: +//! - `UpgradeCheckData`: Response containing available upgrades +//! - `UpgradeApplyData`: Response containing applied upgrades +//! - `BackupListData`: Response containing backup list +//! - `BackupRestoreData`: Response containing restore results +//! - `BackupCleanData`: Response containing cleanup results +//! +//! - **Supporting Types**: +//! - `PackageUpgradeInfo`: Information about upgrades for a single package +//! - `DependencyUpgradeInfo`: Information about a single dependency upgrade +//! - `UpgradeSummaryInfo`: Summary statistics for available upgrades +//! - `UpgradeSelectionInfo`: Selection criteria for which upgrades to apply +//! - `AppliedUpgradeInfo`: Information about a successfully applied upgrade +//! - `ApplySummaryInfo`: Summary of upgrade application results +//! - `BackupInfo`: Information about a single backup +//! +//! - **API Responses**: Type-safe response wrappers for each command +//! +//! All types implement `Clone`, `Debug`, and `Serialize` for flexibility in +//! testing and serialization scenarios. //! //! # Why //! -//! The upgrade commands handle dependency updates from npm registries. -//! They provide controlled upgrade workflows with backup and restore -//! capabilities for safety. +//! Upgrade commands provide controlled dependency update workflows: +//! +//! - **Check**: Detect available upgrades without making changes +//! - **Apply**: Apply selected upgrades with optional backup creation +//! - **Backup List**: View available backups for potential rollback +//! - **Backup Restore**: Rollback to a previous state if upgrades cause issues +//! - **Backup Clean**: Remove old backups to free disk space +//! +//! These types provide: +//! - **Type safety**: Strong typing for JavaScript/TypeScript consumers +//! - **Documentation**: Self-documenting API through TypeScript definitions +//! - **Consistency**: Matches the CLI JSON output structure for compatibility +//! - **Validation**: Enables parameter validation before CLI execution //! //! # Examples //! +//! ## TypeScript Usage +//! //! ```typescript //! import { //! upgradeCheck, //! upgradeApply, -//! backupCreate, +//! backupList, //! backupRestore, -//! UpgradeCheckParams +//! backupClean, +//! UpgradeCheckParams, +//! UpgradeApplyParams //! } from '@websublime/workspace-tools'; //! //! // Check for available upgrades @@ -47,6 +80,7 @@ //! }; //! const checkResult = await upgradeCheck(checkParams); //! if (checkResult.success) { +//! console.log(`Found ${checkResult.data.summary.totalUpgrades} upgrades`); //! for (const pkg of checkResult.data.packages) { //! for (const dep of pkg.dependencies) { //! console.log(`${dep.name}: ${dep.currentVersion} -> ${dep.latestVersion}`); @@ -54,12 +88,27 @@ //! } //! } //! -//! // Apply upgrades with backup -//! const applyResult = await upgradeApply({ +//! // Apply minor and patch upgrades with backup +//! const applyParams: UpgradeApplyParams = { //! root: '.', //! createBackup: true, //! selection: { minor: true, patch: true } -//! }); +//! }; +//! const applyResult = await upgradeApply(applyParams); +//! if (applyResult.success) { +//! console.log(`Applied ${applyResult.data.summary.totalApplied} upgrades`); +//! if (applyResult.data.backupId) { +//! console.log(`Backup created: ${applyResult.data.backupId}`); +//! } +//! } +//! +//! // List available backups +//! const listResult = await backupList({ root: '.' }); +//! if (listResult.success) { +//! for (const backup of listResult.data.backups) { +//! console.log(`${backup.id}: ${backup.createdAt} (${backup.sizeBytes} bytes)`); +//! } +//! } //! //! // Restore from backup if needed //! if (applyResult.success && applyResult.data.backupId) { @@ -67,33 +116,2956 @@ //! root: '.', //! backupId: applyResult.data.backupId //! }); +//! if (restoreResult.success) { +//! console.log(`Restored ${restoreResult.data.packagesRestored} packages`); +//! } +//! } +//! +//! // Clean old backups +//! const cleanResult = await backupClean({ root: '.', keepCount: 3 }); +//! if (cleanResult.success) { +//! console.log(`Cleaned ${cleanResult.data.backupsRemoved} old backups`); //! } //! ``` +//! +//! ## Rust Usage (Internal) +//! +//! ```rust,ignore +//! use sublime_node_tools::types::upgrade::{ +//! UpgradeCheckParams, UpgradeCheckData, PackageUpgradeInfo, +//! DependencyUpgradeInfo, UpgradeSummaryInfo +//! }; +//! +//! // Creating params for validation +//! let params = UpgradeCheckParams::new(".") +//! .with_include_minor(true) +//! .with_include_patch(true); +//! +//! // Constructing response data +//! let dep_upgrade = DependencyUpgradeInfo::new( +//! "lodash", +//! "4.17.20", +//! "4.17.21", +//! "patch", +//! "regular" +//! ); +//! let pkg_upgrade = PackageUpgradeInfo::new( +//! "@scope/pkg1", +//! "packages/pkg1", +//! ).with_dependency(dep_upgrade); +//! ``` + +use napi_derive::napi; +use serde::Serialize; + +use crate::error::ErrorInfo; + +// ============================================================================ +// Constants +// ============================================================================ + +/// Valid upgrade type values for dependency upgrades. +/// +/// These values indicate the type of version change: +/// - `"major"`: Breaking changes (e.g., 1.0.0 → 2.0.0) +/// - `"minor"`: New features, backwards compatible (e.g., 1.0.0 → 1.1.0) +/// - `"patch"`: Bug fixes, backwards compatible (e.g., 1.0.0 → 1.0.1) +#[allow(dead_code)] +pub(crate) const VALID_UPGRADE_TYPES: &[&str] = &["major", "minor", "patch"]; + +/// Valid dependency type values for upgraded dependencies. +/// +/// - `"regular"`: Standard dependencies (dependencies) +/// - `"dev"`: Development dependencies (devDependencies) +/// - `"peer"`: Peer dependencies (peerDependencies) +/// - `"optional"`: Optional dependencies (optionalDependencies) +#[allow(dead_code)] +pub(crate) const VALID_DEPENDENCY_TYPES: &[&str] = &["regular", "dev", "peer", "optional"]; + +/// Default number of backups to keep when cleaning old backups. +#[allow(dead_code)] +pub(crate) const DEFAULT_KEEP_COUNT: u32 = 5; + +// ============================================================================ +// Input Parameters +// ============================================================================ + +/// Input parameters for the upgrade check command. +/// +/// This structure defines the parameters for checking available dependency +/// upgrades in the workspace. The check is a read-only operation that detects +/// which dependencies have newer versions available. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (required) +/// - `config_path`: Optional path to a custom configuration file +/// - `include_major`: Whether to include major version upgrades +/// - `include_minor`: Whether to include minor version upgrades +/// - `include_patch`: Whether to include patch version upgrades +/// - `include_dev_dependencies`: Whether to check devDependencies +/// - `include_peer_dependencies`: Whether to check peerDependencies +/// - `packages`: Optional filter to specific packages +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeCheckParams { +/// root: string; +/// configPath?: string; +/// includeMajor?: boolean; +/// includeMinor?: boolean; +/// includePatch?: boolean; +/// includeDevDependencies?: boolean; +/// includePeerDependencies?: boolean; +/// packages?: string[]; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// // Check all upgrade types +/// const allParams: UpgradeCheckParams = { root: '.' }; +/// +/// // Check only minor and patch upgrades (safer) +/// const safeParams: UpgradeCheckParams = { +/// root: '/path/to/workspace', +/// includeMajor: false, +/// includeMinor: true, +/// includePatch: true +/// }; +/// +/// // Check specific packages only +/// const filteredParams: UpgradeCheckParams = { +/// root: '.', +/// packages: ['@scope/pkg1', '@scope/pkg2'] +/// }; +/// ``` +// Allow dead_code - will be used in story 8.2 (upgradeCheck command) +#[allow(dead_code)] +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeCheckParams { + /// Workspace root directory path. + /// + /// This is the absolute or relative path to the root of the workspace. + /// For monorepos, this should point to the root where the package manager + /// configuration is located. + pub root: String, + + /// Optional custom configuration file path. + /// + /// If not provided, the command will search for configuration files + /// in standard locations (`repo.config.json`, `repo.config.toml`, + /// `repo.config.yaml`) within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, + + /// Whether to include major version upgrades. + /// + /// Major upgrades may contain breaking changes and should be reviewed + /// carefully. Defaults to `true` when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_major: Option, + + /// Whether to include minor version upgrades. + /// + /// Minor upgrades typically add new features while maintaining + /// backwards compatibility. Defaults to `true` when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_minor: Option, + + /// Whether to include patch version upgrades. + /// + /// Patch upgrades typically contain bug fixes and are generally + /// safe to apply. Defaults to `true` when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_patch: Option, + + /// Whether to include development dependencies. + /// + /// When `true`, devDependencies are also checked for upgrades. + /// Defaults to `true` when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_dev_dependencies: Option, + + /// Whether to include peer dependencies. + /// + /// When `true`, peerDependencies are also checked for upgrades. + /// Defaults to `false` when not specified since peer dependency + /// upgrades require careful consideration. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_peer_dependencies: Option, + + /// Filter to specific packages. + /// + /// When provided, only these packages will be checked for upgrades. + /// Package names should include scope if applicable. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, +} + +#[allow(dead_code)] +impl UpgradeCheckParams { + /// Creates a new `UpgradeCheckParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `UpgradeCheckParams` instance with default optional values. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = UpgradeCheckParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { + root: root.into(), + config_path: None, + include_major: None, + include_minor: None, + include_patch: None, + include_dev_dependencies: None, + include_peer_dependencies: None, + packages: None, + } + } + + /// Sets the config path. + /// + /// # Arguments + /// + /// * `config_path` - Path to the configuration file + /// + /// # Returns + /// + /// Self with the config path set. + #[must_use] + pub fn with_config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } + + /// Sets whether to include major upgrades. + /// + /// # Arguments + /// + /// * `include` - Whether to include major upgrades + /// + /// # Returns + /// + /// Self with the include_major flag set. + #[must_use] + pub fn with_include_major(mut self, include: bool) -> Self { + self.include_major = Some(include); + self + } + + /// Sets whether to include minor upgrades. + /// + /// # Arguments + /// + /// * `include` - Whether to include minor upgrades + /// + /// # Returns + /// + /// Self with the include_minor flag set. + #[must_use] + pub fn with_include_minor(mut self, include: bool) -> Self { + self.include_minor = Some(include); + self + } + + /// Sets whether to include patch upgrades. + /// + /// # Arguments + /// + /// * `include` - Whether to include patch upgrades + /// + /// # Returns + /// + /// Self with the include_patch flag set. + #[must_use] + pub fn with_include_patch(mut self, include: bool) -> Self { + self.include_patch = Some(include); + self + } + + /// Sets whether to include development dependencies. + /// + /// # Arguments + /// + /// * `include` - Whether to include dev dependencies + /// + /// # Returns + /// + /// Self with the include_dev_dependencies flag set. + #[must_use] + pub fn with_include_dev_dependencies(mut self, include: bool) -> Self { + self.include_dev_dependencies = Some(include); + self + } + + /// Sets whether to include peer dependencies. + /// + /// # Arguments + /// + /// * `include` - Whether to include peer dependencies + /// + /// # Returns + /// + /// Self with the include_peer_dependencies flag set. + #[must_use] + pub fn with_include_peer_dependencies(mut self, include: bool) -> Self { + self.include_peer_dependencies = Some(include); + self + } + + /// Sets the packages filter. + /// + /// # Arguments + /// + /// * `packages` - List of package names to filter + /// + /// # Returns + /// + /// Self with the packages filter set. + #[must_use] + pub fn with_packages(mut self, packages: Vec) -> Self { + self.packages = Some(packages); + self + } + + /// Convenience method to set all inclusion flags at once. + /// + /// # Arguments + /// + /// * `major` - Whether to include major upgrades + /// * `minor` - Whether to include minor upgrades + /// * `patch` - Whether to include patch upgrades + /// + /// # Returns + /// + /// Self with all inclusion flags set. + /// + /// # Examples + /// + /// ```rust,ignore + /// // Only include minor and patch (safe upgrades) + /// let params = UpgradeCheckParams::new(".") + /// .with_upgrade_levels(false, true, true); + /// ``` + #[must_use] + pub fn with_upgrade_levels(mut self, major: bool, minor: bool, patch: bool) -> Self { + self.include_major = Some(major); + self.include_minor = Some(minor); + self.include_patch = Some(patch); + self + } +} + +/// Input parameters for the upgrade apply command. +/// +/// This structure defines the parameters for applying selected dependency +/// upgrades. The apply operation modifies package.json files and optionally +/// creates backups and changesets. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (required) +/// - `config_path`: Optional path to a custom configuration file +/// - `create_backup`: Whether to create a backup before applying +/// - `create_changeset`: Whether to create a changeset for the upgrades +/// - `selection`: Criteria for which upgrades to apply +/// - `dry_run`: Whether to simulate without making changes +/// - `packages`: Optional filter to specific packages +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeApplyParams { +/// root: string; +/// configPath?: string; +/// createBackup?: boolean; +/// createChangeset?: boolean; +/// selection?: UpgradeSelectionInfo; +/// dryRun?: boolean; +/// packages?: string[]; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// // Apply all upgrades with backup +/// const allParams: UpgradeApplyParams = { +/// root: '.', +/// createBackup: true +/// }; +/// +/// // Apply only patch upgrades (safest) +/// const patchOnly: UpgradeApplyParams = { +/// root: '.', +/// createBackup: true, +/// selection: { major: false, minor: false, patch: true } +/// }; +/// +/// // Dry run to preview changes +/// const preview: UpgradeApplyParams = { +/// root: '.', +/// dryRun: true +/// }; +/// ``` +// Allow dead_code - will be used in story 8.3 (upgradeApply command) +#[allow(dead_code)] +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeApplyParams { + /// Workspace root directory path. + /// + /// This is the absolute or relative path to the root of the workspace. + pub root: String, + + /// Optional custom configuration file path. + /// + /// If not provided, the command will search for configuration files + /// in standard locations within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, + + /// Whether to create a backup before applying upgrades. + /// + /// When `true`, creates a backup of all package.json files that can + /// be restored if the upgrades cause issues. Strongly recommended + /// for production use. + /// + /// Defaults to the value in the configuration file, or `true` if not + /// specified anywhere. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub create_backup: Option, + + /// Whether to create a changeset for the upgrades. + /// + /// When `true`, creates a changeset documenting all the dependency + /// updates. This integrates with the bump workflow to include + /// upgrade information in release notes. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub create_changeset: Option, + + /// Criteria for which upgrades to apply. + /// + /// Allows fine-grained control over which types of upgrades are applied. + /// If not provided, applies all available upgrades. + #[napi(ts_type = "UpgradeSelectionInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub selection: Option, + + /// Whether to perform a dry run. + /// + /// When `true`, simulates the upgrade process without actually + /// modifying any files. Useful for previewing what would change. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub dry_run: Option, + + /// Filter to specific packages. + /// + /// When provided, only upgrades for these packages will be applied. + /// Package names should include scope if applicable. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, +} + +#[allow(dead_code)] +impl UpgradeApplyParams { + /// Creates a new `UpgradeApplyParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `UpgradeApplyParams` instance with default optional values. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = UpgradeApplyParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { + root: root.into(), + config_path: None, + create_backup: None, + create_changeset: None, + selection: None, + dry_run: None, + packages: None, + } + } + + /// Sets the config path. + /// + /// # Arguments + /// + /// * `config_path` - Path to the configuration file + /// + /// # Returns + /// + /// Self with the config path set. + #[must_use] + pub fn with_config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } + + /// Sets whether to create a backup. + /// + /// # Arguments + /// + /// * `create_backup` - Whether to create a backup + /// + /// # Returns + /// + /// Self with the create_backup flag set. + #[must_use] + pub fn with_create_backup(mut self, create_backup: bool) -> Self { + self.create_backup = Some(create_backup); + self + } + + /// Sets whether to create a changeset. + /// + /// # Arguments + /// + /// * `create_changeset` - Whether to create a changeset + /// + /// # Returns + /// + /// Self with the create_changeset flag set. + #[must_use] + pub fn with_create_changeset(mut self, create_changeset: bool) -> Self { + self.create_changeset = Some(create_changeset); + self + } + + /// Sets the upgrade selection criteria. + /// + /// # Arguments + /// + /// * `selection` - The selection criteria + /// + /// # Returns + /// + /// Self with the selection set. + #[must_use] + pub fn with_selection(mut self, selection: UpgradeSelectionInfo) -> Self { + self.selection = Some(selection); + self + } + + /// Sets whether to perform a dry run. + /// + /// # Arguments + /// + /// * `dry_run` - Whether to perform a dry run + /// + /// # Returns + /// + /// Self with the dry_run flag set. + #[must_use] + pub fn with_dry_run(mut self, dry_run: bool) -> Self { + self.dry_run = Some(dry_run); + self + } + + /// Sets the packages filter. + /// + /// # Arguments + /// + /// * `packages` - List of package names to filter + /// + /// # Returns + /// + /// Self with the packages filter set. + #[must_use] + pub fn with_packages(mut self, packages: Vec) -> Self { + self.packages = Some(packages); + self + } +} + +/// Input parameters for the backup list command. +/// +/// This structure defines the parameters for listing available backups +/// in the workspace. Backups are created by the upgrade apply command +/// when `createBackup` is enabled. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (required) +/// - `config_path`: Optional path to a custom configuration file +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupListParams { +/// root: string; +/// configPath?: string; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const params: BackupListParams = { root: '.' }; +/// const result = await backupList(params); +/// ``` +// Allow dead_code - will be used in story 8.4 (backupList command) +#[allow(dead_code)] +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupListParams { + /// Workspace root directory path. + /// + /// This is the absolute or relative path to the root of the workspace. + pub root: String, + + /// Optional custom configuration file path. + /// + /// If not provided, the command will search for configuration files + /// in standard locations within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, +} + +#[allow(dead_code)] +impl BackupListParams { + /// Creates a new `BackupListParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `BackupListParams` instance. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BackupListParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { root: root.into(), config_path: None } + } + + /// Sets the config path. + /// + /// # Arguments + /// + /// * `config_path` - Path to the configuration file + /// + /// # Returns + /// + /// Self with the config path set. + #[must_use] + pub fn with_config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } +} + +/// Input parameters for the backup restore command. +/// +/// This structure defines the parameters for restoring package.json files +/// from a previous backup. This effectively rolls back dependency changes +/// to a known state. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (required) +/// - `backup_id`: The ID of the backup to restore (required) +/// - `config_path`: Optional path to a custom configuration file +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupRestoreParams { +/// root: string; +/// backupId: string; +/// configPath?: string; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const params: BackupRestoreParams = { +/// root: '.', +/// backupId: 'backup-2024-01-15-123456' +/// }; +/// const result = await backupRestore(params); +/// ``` +// Allow dead_code - will be used in story 8.4 (backupRestore command) +#[allow(dead_code)] +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupRestoreParams { + /// Workspace root directory path. + /// + /// This is the absolute or relative path to the root of the workspace. + pub root: String, + + /// The ID of the backup to restore. + /// + /// This should match an ID returned by the backupList command. + /// Backup IDs are typically in the format `backup-YYYY-MM-DD-HHMMSS`. + pub backup_id: String, + + /// Optional custom configuration file path. + /// + /// If not provided, the command will search for configuration files + /// in standard locations within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, +} + +#[allow(dead_code)] +impl BackupRestoreParams { + /// Creates a new `BackupRestoreParams` with required fields. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// * `backup_id` - The ID of the backup to restore + /// + /// # Returns + /// + /// A new `BackupRestoreParams` instance. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BackupRestoreParams::new(".", "backup-2024-01-15-123456"); + /// ``` + #[must_use] + pub fn new(root: impl Into, backup_id: impl Into) -> Self { + Self { root: root.into(), backup_id: backup_id.into(), config_path: None } + } + + /// Sets the config path. + /// + /// # Arguments + /// + /// * `config_path` - Path to the configuration file + /// + /// # Returns + /// + /// Self with the config path set. + #[must_use] + pub fn with_config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } +} + +/// Input parameters for the backup clean command. +/// +/// This structure defines the parameters for cleaning (removing) old backups. +/// This helps manage disk space by removing older backups while keeping +/// the most recent ones. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (required) +/// - `config_path`: Optional path to a custom configuration file +/// - `keep_count`: Number of recent backups to keep (optional, defaults to 5) +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupCleanParams { +/// root: string; +/// configPath?: string; +/// keepCount?: number; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// // Keep last 3 backups, remove older ones +/// const params: BackupCleanParams = { +/// root: '.', +/// keepCount: 3 +/// }; +/// const result = await backupClean(params); +/// ``` +// Allow dead_code - will be used in story 8.4 (backupClean command) +#[allow(dead_code)] +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupCleanParams { + /// Workspace root directory path. + /// + /// This is the absolute or relative path to the root of the workspace. + pub root: String, + + /// Optional custom configuration file path. + /// + /// If not provided, the command will search for configuration files + /// in standard locations within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, + + /// Number of recent backups to keep. + /// + /// Backups are sorted by creation date, and the most recent ones + /// are kept. Older backups beyond this count are removed. + /// Defaults to 5 if not specified. + #[napi(ts_type = "number | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub keep_count: Option, +} + +#[allow(dead_code)] +impl BackupCleanParams { + /// Creates a new `BackupCleanParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `BackupCleanParams` instance with default keep_count. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BackupCleanParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { root: root.into(), config_path: None, keep_count: None } + } + + /// Sets the config path. + /// + /// # Arguments + /// + /// * `config_path` - Path to the configuration file + /// + /// # Returns + /// + /// Self with the config path set. + #[must_use] + pub fn with_config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } + + /// Sets the number of backups to keep. + /// + /// # Arguments + /// + /// * `keep_count` - Number of recent backups to keep + /// + /// # Returns + /// + /// Self with the keep_count set. + #[must_use] + pub fn with_keep_count(mut self, keep_count: u32) -> Self { + self.keep_count = Some(keep_count); + self + } +} + +// ============================================================================ +// Supporting Types +// ============================================================================ + +/// Selection criteria for which upgrades to apply. +/// +/// This structure allows fine-grained control over which types of upgrades +/// are applied during the upgrade apply operation. +/// +/// # Fields +/// +/// - `major`: Whether to apply major version upgrades +/// - `minor`: Whether to apply minor version upgrades +/// - `patch`: Whether to apply patch version upgrades +/// - `packages`: Optional filter to specific packages +/// - `dependencies`: Optional filter to specific dependencies +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeSelectionInfo { +/// major?: boolean; +/// minor?: boolean; +/// patch?: boolean; +/// packages?: string[]; +/// dependencies?: string[]; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// // Apply only patch upgrades (safest) +/// const patchOnly: UpgradeSelectionInfo = { +/// major: false, +/// minor: false, +/// patch: true +/// }; +/// +/// // Apply minor and patch, but not major +/// const safeUpgrades: UpgradeSelectionInfo = { +/// major: false, +/// minor: true, +/// patch: true +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeSelectionInfo { + /// Whether to apply major version upgrades. + /// + /// Major upgrades may contain breaking changes. Defaults to `true` + /// when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub major: Option, + + /// Whether to apply minor version upgrades. + /// + /// Minor upgrades typically add new features. Defaults to `true` + /// when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub minor: Option, + + /// Whether to apply patch version upgrades. + /// + /// Patch upgrades typically contain bug fixes. Defaults to `true` + /// when not specified. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + + /// Filter to specific packages. + /// + /// When provided, only upgrades for these packages will be applied. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, + + /// Filter to specific dependencies. + /// + /// When provided, only these specific dependencies will be upgraded. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub dependencies: Option>, +} + +#[allow(dead_code)] +impl UpgradeSelectionInfo { + /// Creates a new `UpgradeSelectionInfo` that applies all upgrade types. + /// + /// # Returns + /// + /// A new `UpgradeSelectionInfo` with all flags set to `true`. + #[must_use] + pub fn all() -> Self { + Self { + major: Some(true), + minor: Some(true), + patch: Some(true), + packages: None, + dependencies: None, + } + } + + /// Creates a selection for patch upgrades only. + /// + /// This is the safest option as patch upgrades typically only + /// contain bug fixes. + /// + /// # Returns + /// + /// A new `UpgradeSelectionInfo` for patch upgrades only. + #[must_use] + pub fn patch_only() -> Self { + Self { + major: Some(false), + minor: Some(false), + patch: Some(true), + packages: None, + dependencies: None, + } + } + + /// Creates a selection for minor and patch upgrades. + /// + /// This is a moderate option that avoids breaking changes + /// while still getting new features. + /// + /// # Returns + /// + /// A new `UpgradeSelectionInfo` for minor and patch upgrades. + #[must_use] + pub fn minor_and_patch() -> Self { + Self { + major: Some(false), + minor: Some(true), + patch: Some(true), + packages: None, + dependencies: None, + } + } + + /// Creates a selection for specific packages. + /// + /// # Arguments + /// + /// * `packages` - List of package names to include + /// + /// # Returns + /// + /// A new `UpgradeSelectionInfo` filtered to specific packages. + #[must_use] + pub fn for_packages(packages: Vec) -> Self { + Self { + major: Some(true), + minor: Some(true), + patch: Some(true), + packages: Some(packages), + dependencies: None, + } + } + + /// Creates a selection for specific dependencies. + /// + /// # Arguments + /// + /// * `dependencies` - List of dependency names to include + /// + /// # Returns + /// + /// A new `UpgradeSelectionInfo` filtered to specific dependencies. + #[must_use] + pub fn for_dependencies(dependencies: Vec) -> Self { + Self { + major: Some(true), + minor: Some(true), + patch: Some(true), + packages: None, + dependencies: Some(dependencies), + } + } + + /// Sets the packages filter. + /// + /// # Arguments + /// + /// * `packages` - List of package names to filter + /// + /// # Returns + /// + /// Self with the packages filter set. + #[must_use] + pub fn with_packages(mut self, packages: Vec) -> Self { + self.packages = Some(packages); + self + } + + /// Sets the dependencies filter. + /// + /// # Arguments + /// + /// * `dependencies` - List of dependency names to filter + /// + /// # Returns + /// + /// Self with the dependencies filter set. + #[must_use] + pub fn with_dependencies(mut self, dependencies: Vec) -> Self { + self.dependencies = Some(dependencies); + self + } +} + +impl Default for UpgradeSelectionInfo { + fn default() -> Self { + Self::all() + } +} + +/// Information about a single dependency upgrade. +/// +/// This structure contains details about an available or applied upgrade +/// for a specific dependency. +/// +/// # Fields +/// +/// - `name`: The dependency name +/// - `current_version`: The current version in package.json +/// - `latest_version`: The latest available version +/// - `upgrade_type`: The type of upgrade (major, minor, patch) +/// - `dependency_type`: Where the dependency is defined +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface DependencyUpgradeInfo { +/// name: string; +/// currentVersion: string; +/// latestVersion: string; +/// upgradeType: string; +/// dependencyType: string; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct DependencyUpgradeInfo { + /// The name of the dependency. + /// + /// This is the package name as it appears in package.json, + /// including any scope prefix. + pub name: String, + + /// The current version specified in package.json. + /// + /// This is the version range or exact version currently specified. + pub current_version: String, + + /// The latest available version from the registry. + /// + /// This is the exact version that would be installed if the + /// upgrade is applied. + pub latest_version: String, + + /// The type of version upgrade. + /// + /// One of: `"major"`, `"minor"`, `"patch"` + pub upgrade_type: String, + + /// The type of dependency relationship. + /// + /// One of: `"regular"`, `"dev"`, `"peer"`, `"optional"` + pub dependency_type: String, +} + +#[allow(dead_code)] +impl DependencyUpgradeInfo { + /// Creates a new `DependencyUpgradeInfo`. + /// + /// # Arguments + /// + /// * `name` - The dependency name + /// * `current_version` - Current version in package.json + /// * `latest_version` - Latest available version + /// * `upgrade_type` - Type of upgrade (major, minor, patch) + /// * `dependency_type` - Type of dependency (regular, dev, peer, optional) + /// + /// # Returns + /// + /// A new `DependencyUpgradeInfo` instance. + #[must_use] + pub fn new( + name: impl Into, + current_version: impl Into, + latest_version: impl Into, + upgrade_type: impl Into, + dependency_type: impl Into, + ) -> Self { + Self { + name: name.into(), + current_version: current_version.into(), + latest_version: latest_version.into(), + upgrade_type: upgrade_type.into(), + dependency_type: dependency_type.into(), + } + } + + /// Creates a new patch upgrade for a regular dependency. + /// + /// # Arguments + /// + /// * `name` - The dependency name + /// * `current` - Current version + /// * `latest` - Latest version + /// + /// # Returns + /// + /// A new `DependencyUpgradeInfo` for a patch upgrade. + #[must_use] + pub fn patch( + name: impl Into, + current: impl Into, + latest: impl Into, + ) -> Self { + Self::new(name, current, latest, "patch", "regular") + } + + /// Creates a new minor upgrade for a regular dependency. + /// + /// # Arguments + /// + /// * `name` - The dependency name + /// * `current` - Current version + /// * `latest` - Latest version + /// + /// # Returns + /// + /// A new `DependencyUpgradeInfo` for a minor upgrade. + #[must_use] + pub fn minor( + name: impl Into, + current: impl Into, + latest: impl Into, + ) -> Self { + Self::new(name, current, latest, "minor", "regular") + } + + /// Creates a new major upgrade for a regular dependency. + /// + /// # Arguments + /// + /// * `name` - The dependency name + /// * `current` - Current version + /// * `latest` - Latest version + /// + /// # Returns + /// + /// A new `DependencyUpgradeInfo` for a major upgrade. + #[must_use] + pub fn major( + name: impl Into, + current: impl Into, + latest: impl Into, + ) -> Self { + Self::new(name, current, latest, "major", "regular") + } + + /// Creates a dev dependency upgrade. + /// + /// # Arguments + /// + /// * `name` - The dependency name + /// * `current` - Current version + /// * `latest` - Latest version + /// * `upgrade_type` - Type of upgrade + /// + /// # Returns + /// + /// A new `DependencyUpgradeInfo` for a dev dependency. + #[must_use] + pub fn dev( + name: impl Into, + current: impl Into, + latest: impl Into, + upgrade_type: impl Into, + ) -> Self { + Self::new(name, current, latest, upgrade_type, "dev") + } + + /// Returns true if this is a major upgrade. + #[must_use] + pub fn is_major(&self) -> bool { + self.upgrade_type == "major" + } + + /// Returns true if this is a minor upgrade. + #[must_use] + pub fn is_minor(&self) -> bool { + self.upgrade_type == "minor" + } + + /// Returns true if this is a patch upgrade. + #[must_use] + pub fn is_patch(&self) -> bool { + self.upgrade_type == "patch" + } + + /// Returns true if this is a dev dependency. + #[must_use] + pub fn is_dev_dependency(&self) -> bool { + self.dependency_type == "dev" + } +} + +/// Information about available upgrades for a single package. +/// +/// This structure contains all available dependency upgrades for a +/// specific workspace package. +/// +/// # Fields +/// +/// - `package_name`: The package name +/// - `package_path`: The path to the package directory +/// - `dependencies`: List of available dependency upgrades +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface PackageUpgradeInfo { +/// packageName: string; +/// packagePath: string; +/// dependencies: DependencyUpgradeInfo[]; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct PackageUpgradeInfo { + /// The name of the package. + /// + /// This is the package name from package.json, including any scope. + pub package_name: String, + + /// The path to the package directory. + /// + /// This is the relative path from the workspace root to the + /// package directory. + pub package_path: String, + + /// List of available dependency upgrades. + /// + /// Contains information about each dependency that has an + /// upgrade available. + pub dependencies: Vec, +} + +#[allow(dead_code)] +impl PackageUpgradeInfo { + /// Creates a new `PackageUpgradeInfo`. + /// + /// # Arguments + /// + /// * `package_name` - The package name + /// * `package_path` - Path to the package directory + /// + /// # Returns + /// + /// A new `PackageUpgradeInfo` with an empty dependency list. + #[must_use] + pub fn new(package_name: impl Into, package_path: impl Into) -> Self { + Self { + package_name: package_name.into(), + package_path: package_path.into(), + dependencies: Vec::new(), + } + } + + /// Creates a new `PackageUpgradeInfo` with dependencies. + /// + /// # Arguments + /// + /// * `package_name` - The package name + /// * `package_path` - Path to the package directory + /// * `dependencies` - List of dependency upgrades + /// + /// # Returns + /// + /// A new `PackageUpgradeInfo` with the provided dependencies. + #[must_use] + pub fn with_dependencies( + package_name: impl Into, + package_path: impl Into, + dependencies: Vec, + ) -> Self { + Self { package_name: package_name.into(), package_path: package_path.into(), dependencies } + } + + /// Adds a dependency upgrade to this package. + /// + /// # Arguments + /// + /// * `dependency` - The dependency upgrade to add + /// + /// # Returns + /// + /// Self with the dependency added. + #[must_use] + pub fn with_dependency(mut self, dependency: DependencyUpgradeInfo) -> Self { + self.dependencies.push(dependency); + self + } + + /// Returns the number of available upgrades. + #[must_use] + pub fn upgrade_count(&self) -> usize { + self.dependencies.len() + } + + /// Returns the number of major upgrades. + #[must_use] + pub fn major_count(&self) -> usize { + self.dependencies.iter().filter(|d| d.is_major()).count() + } + + /// Returns the number of minor upgrades. + #[must_use] + pub fn minor_count(&self) -> usize { + self.dependencies.iter().filter(|d| d.is_minor()).count() + } + + /// Returns the number of patch upgrades. + #[must_use] + pub fn patch_count(&self) -> usize { + self.dependencies.iter().filter(|d| d.is_patch()).count() + } + + /// Returns true if there are any major upgrades. + #[must_use] + pub fn has_major_upgrades(&self) -> bool { + self.dependencies.iter().any(DependencyUpgradeInfo::is_major) + } +} + +/// Summary of available upgrades. +/// +/// This structure provides aggregate statistics about available upgrades +/// across all packages. +/// +/// # Fields +/// +/// - `packages_analyzed`: Number of packages checked +/// - `total_upgrades`: Total number of available upgrades +/// - `major_upgrades`: Number of major version upgrades +/// - `minor_upgrades`: Number of minor version upgrades +/// - `patch_upgrades`: Number of patch version upgrades +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeSummaryInfo { +/// packagesAnalyzed: number; +/// totalUpgrades: number; +/// majorUpgrades: number; +/// minorUpgrades: number; +/// patchUpgrades: number; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeSummaryInfo { + /// Number of packages that were analyzed. + pub packages_analyzed: u32, + + /// Total number of available upgrades. + pub total_upgrades: u32, + + /// Number of major version upgrades available. + pub major_upgrades: u32, + + /// Number of minor version upgrades available. + pub minor_upgrades: u32, + + /// Number of patch version upgrades available. + pub patch_upgrades: u32, +} + +#[allow(dead_code)] +impl UpgradeSummaryInfo { + /// Creates a new `UpgradeSummaryInfo`. + /// + /// # Arguments + /// + /// * `packages_analyzed` - Number of packages analyzed + /// * `total_upgrades` - Total upgrade count + /// * `major_upgrades` - Major upgrade count + /// * `minor_upgrades` - Minor upgrade count + /// * `patch_upgrades` - Patch upgrade count + /// + /// # Returns + /// + /// A new `UpgradeSummaryInfo` instance. + #[must_use] + pub fn new( + packages_analyzed: u32, + total_upgrades: u32, + major_upgrades: u32, + minor_upgrades: u32, + patch_upgrades: u32, + ) -> Self { + Self { packages_analyzed, total_upgrades, major_upgrades, minor_upgrades, patch_upgrades } + } + + /// Creates an empty summary (no upgrades found). + /// + /// # Arguments + /// + /// * `packages_analyzed` - Number of packages that were analyzed + /// + /// # Returns + /// + /// A new `UpgradeSummaryInfo` with zero upgrades. + #[must_use] + pub fn empty(packages_analyzed: u32) -> Self { + Self { + packages_analyzed, + total_upgrades: 0, + major_upgrades: 0, + minor_upgrades: 0, + patch_upgrades: 0, + } + } + + /// Creates a summary from a list of package upgrades. + /// + /// # Arguments + /// + /// * `packages` - List of package upgrade information + /// + /// # Returns + /// + /// A summary calculated from the provided packages. + /// + /// # Note + /// + /// The package count is truncated to `u32::MAX` if it exceeds that value, + /// which is acceptable since workspaces with over 4 billion packages are + /// not realistic. + #[must_use] + #[allow(clippy::cast_possible_truncation)] + pub fn from_packages(packages: &[PackageUpgradeInfo]) -> Self { + let mut major_upgrades = 0u32; + let mut minor_upgrades = 0u32; + let mut patch_upgrades = 0u32; + + for pkg in packages { + for dep in &pkg.dependencies { + match dep.upgrade_type.as_str() { + "major" => major_upgrades += 1, + "minor" => minor_upgrades += 1, + "patch" => patch_upgrades += 1, + _ => {} + } + } + } + + let total_upgrades = major_upgrades + minor_upgrades + patch_upgrades; + + // Truncation is acceptable: workspaces with >4 billion packages are unrealistic + Self { + packages_analyzed: packages.len() as u32, + total_upgrades, + major_upgrades, + minor_upgrades, + patch_upgrades, + } + } + + /// Returns true if there are any breaking (major) changes. + #[must_use] + pub fn has_breaking_changes(&self) -> bool { + self.major_upgrades > 0 + } + + /// Returns true if there are no upgrades available. + #[must_use] + pub fn is_empty(&self) -> bool { + self.total_upgrades == 0 + } +} + +impl Default for UpgradeSummaryInfo { + fn default() -> Self { + Self::empty(0) + } +} + +/// Information about a successfully applied upgrade. +/// +/// This structure contains details about a single dependency upgrade +/// that was successfully applied. +/// +/// # Fields +/// +/// - `package_name`: The package that was modified +/// - `dependency_name`: The dependency that was upgraded +/// - `old_version`: The previous version +/// - `new_version`: The new version +/// - `upgrade_type`: The type of upgrade +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface AppliedUpgradeInfo { +/// packageName: string; +/// dependencyName: string; +/// oldVersion: string; +/// newVersion: string; +/// upgradeType: string; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct AppliedUpgradeInfo { + /// The name of the package that was modified. + pub package_name: String, + + /// The name of the dependency that was upgraded. + pub dependency_name: String, + + /// The previous version that was replaced. + pub old_version: String, + + /// The new version that was applied. + pub new_version: String, + + /// The type of upgrade that was applied. + /// + /// One of: `"major"`, `"minor"`, `"patch"` + pub upgrade_type: String, +} + +#[allow(dead_code)] +impl AppliedUpgradeInfo { + /// Creates a new `AppliedUpgradeInfo`. + /// + /// # Arguments + /// + /// * `package_name` - The package that was modified + /// * `dependency_name` - The dependency that was upgraded + /// * `old_version` - Previous version + /// * `new_version` - New version + /// * `upgrade_type` - Type of upgrade + /// + /// # Returns + /// + /// A new `AppliedUpgradeInfo` instance. + #[must_use] + pub fn new( + package_name: impl Into, + dependency_name: impl Into, + old_version: impl Into, + new_version: impl Into, + upgrade_type: impl Into, + ) -> Self { + Self { + package_name: package_name.into(), + dependency_name: dependency_name.into(), + old_version: old_version.into(), + new_version: new_version.into(), + upgrade_type: upgrade_type.into(), + } + } + + /// Creates from a `DependencyUpgradeInfo` for a specific package. + /// + /// # Arguments + /// + /// * `package_name` - The package that was modified + /// * `upgrade` - The dependency upgrade info + /// + /// # Returns + /// + /// A new `AppliedUpgradeInfo` instance. + #[must_use] + pub fn from_dependency_upgrade( + package_name: impl Into, + upgrade: &DependencyUpgradeInfo, + ) -> Self { + Self { + package_name: package_name.into(), + dependency_name: upgrade.name.clone(), + old_version: upgrade.current_version.clone(), + new_version: upgrade.latest_version.clone(), + upgrade_type: upgrade.upgrade_type.clone(), + } + } +} + +/// Information about a skipped upgrade with reason. +/// +/// This structure contains details about a dependency upgrade that +/// was not applied, along with the reason it was skipped. +/// +/// # Fields +/// +/// - `package_name`: The package where the upgrade was available +/// - `dependency_name`: The dependency that was skipped +/// - `current_version`: The current version +/// - `available_version`: The available version that was skipped +/// - `reason`: Why the upgrade was skipped +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface SkippedUpgradeInfo { +/// packageName: string; +/// dependencyName: string; +/// currentVersion: string; +/// availableVersion: string; +/// reason: string; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct SkippedUpgradeInfo { + /// The name of the package where the upgrade was available. + pub package_name: String, + + /// The name of the dependency that was skipped. + pub dependency_name: String, + + /// The current version in package.json. + pub current_version: String, + + /// The available version that was not applied. + pub available_version: String, + + /// The reason the upgrade was skipped. + /// + /// Common reasons include: filtered by selection criteria, + /// conflicting requirements, or user exclusion. + pub reason: String, +} + +#[allow(dead_code)] +impl SkippedUpgradeInfo { + /// Creates a new `SkippedUpgradeInfo`. + /// + /// # Arguments + /// + /// * `package_name` - The package name + /// * `dependency_name` - The dependency name + /// * `current_version` - Current version + /// * `available_version` - Available version + /// * `reason` - Reason for skipping + /// + /// # Returns + /// + /// A new `SkippedUpgradeInfo` instance. + #[must_use] + pub fn new( + package_name: impl Into, + dependency_name: impl Into, + current_version: impl Into, + available_version: impl Into, + reason: impl Into, + ) -> Self { + Self { + package_name: package_name.into(), + dependency_name: dependency_name.into(), + current_version: current_version.into(), + available_version: available_version.into(), + reason: reason.into(), + } + } + + /// Creates a skipped upgrade for filtered selection. + /// + /// # Arguments + /// + /// * `package_name` - The package name + /// * `dependency_name` - The dependency name + /// * `current_version` - Current version + /// * `available_version` - Available version + /// + /// # Returns + /// + /// A new `SkippedUpgradeInfo` with a filtered reason. + #[must_use] + pub fn filtered( + package_name: impl Into, + dependency_name: impl Into, + current_version: impl Into, + available_version: impl Into, + ) -> Self { + Self::new( + package_name, + dependency_name, + current_version, + available_version, + "Filtered by selection criteria", + ) + } +} + +/// Information about a failed upgrade attempt. +/// +/// This structure contains details about a dependency upgrade that +/// failed to apply, along with the error message. +/// +/// # Fields +/// +/// - `package_name`: The package where the upgrade was attempted +/// - `dependency_name`: The dependency that failed to upgrade +/// - `current_version`: The current version +/// - `target_version`: The version that was attempted +/// - `error`: The error message +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface FailedUpgradeInfo { +/// packageName: string; +/// dependencyName: string; +/// currentVersion: string; +/// targetVersion: string; +/// error: string; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct FailedUpgradeInfo { + /// The name of the package where the upgrade was attempted. + pub package_name: String, + + /// The name of the dependency that failed to upgrade. + pub dependency_name: String, + + /// The current version in package.json. + pub current_version: String, + + /// The version that was attempted. + pub target_version: String, + + /// The error message describing what went wrong. + pub error: String, +} + +#[allow(dead_code)] +impl FailedUpgradeInfo { + /// Creates a new `FailedUpgradeInfo`. + /// + /// # Arguments + /// + /// * `package_name` - The package name + /// * `dependency_name` - The dependency name + /// * `current_version` - Current version + /// * `target_version` - Target version + /// * `error` - Error message + /// + /// # Returns + /// + /// A new `FailedUpgradeInfo` instance. + #[must_use] + pub fn new( + package_name: impl Into, + dependency_name: impl Into, + current_version: impl Into, + target_version: impl Into, + error: impl Into, + ) -> Self { + Self { + package_name: package_name.into(), + dependency_name: dependency_name.into(), + current_version: current_version.into(), + target_version: target_version.into(), + error: error.into(), + } + } +} + +/// Summary of upgrade application results. +/// +/// This structure provides aggregate statistics about the results +/// of applying upgrades. +/// +/// # Fields +/// +/// - `total_applied`: Number of upgrades successfully applied +/// - `total_skipped`: Number of upgrades that were skipped +/// - `total_failed`: Number of upgrades that failed +/// - `packages_modified`: List of packages that were modified +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface ApplySummaryInfo { +/// totalApplied: number; +/// totalSkipped: number; +/// totalFailed: number; +/// packagesModified: string[]; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct ApplySummaryInfo { + /// Number of upgrades successfully applied. + pub total_applied: u32, + + /// Number of upgrades that were skipped. + pub total_skipped: u32, + + /// Number of upgrades that failed. + pub total_failed: u32, + + /// List of package names that were modified. + pub packages_modified: Vec, +} + +#[allow(dead_code)] +impl ApplySummaryInfo { + /// Creates a new `ApplySummaryInfo`. + /// + /// # Arguments + /// + /// * `total_applied` - Number applied + /// * `total_skipped` - Number skipped + /// * `total_failed` - Number failed + /// * `packages_modified` - List of modified packages + /// + /// # Returns + /// + /// A new `ApplySummaryInfo` instance. + #[must_use] + pub fn new( + total_applied: u32, + total_skipped: u32, + total_failed: u32, + packages_modified: Vec, + ) -> Self { + Self { total_applied, total_skipped, total_failed, packages_modified } + } + + /// Creates an empty summary (nothing happened). + /// + /// # Returns + /// + /// A new `ApplySummaryInfo` with zero counts. + #[must_use] + pub fn empty() -> Self { + Self { total_applied: 0, total_skipped: 0, total_failed: 0, packages_modified: Vec::new() } + } + + /// Returns true if all upgrades were successful. + #[must_use] + pub fn all_succeeded(&self) -> bool { + self.total_failed == 0 && self.total_applied > 0 + } + + /// Returns true if any upgrades failed. + #[must_use] + pub fn has_failures(&self) -> bool { + self.total_failed > 0 + } + + /// Returns the total number of upgrades processed. + #[must_use] + pub fn total_processed(&self) -> u32 { + self.total_applied + self.total_skipped + self.total_failed + } +} + +impl Default for ApplySummaryInfo { + fn default() -> Self { + Self::empty() + } +} + +/// Information about a backup. +/// +/// This structure contains metadata about a backup that was created +/// during upgrade operations. +/// +/// # Fields +/// +/// - `id`: Unique identifier for the backup +/// - `created_at`: When the backup was created (ISO 8601 format) +/// - `packages`: List of packages included in the backup +/// - `size_bytes`: Size of the backup in bytes +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupInfo { +/// id: string; +/// createdAt: string; +/// packages: string[]; +/// sizeBytes: number; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupInfo { + /// Unique identifier for the backup. + /// + /// Typically in the format `backup-YYYY-MM-DD-HHMMSS`. + pub id: String, + + /// When the backup was created. + /// + /// ISO 8601 format (e.g., `2024-01-15T12:34:56Z`). + pub created_at: String, + + /// List of package names included in the backup. + pub packages: Vec, + + /// Size of the backup in bytes. + /// + /// Note: Uses f64 for JavaScript compatibility. f64 can represent + /// integers up to 2^53 without precision loss, which is sufficient + /// for file sizes up to 9 petabytes. + pub size_bytes: f64, +} + +#[allow(dead_code)] +impl BackupInfo { + /// Creates a new `BackupInfo`. + /// + /// # Arguments + /// + /// * `id` - Backup identifier + /// * `created_at` - Creation timestamp + /// * `packages` - List of packages + /// * `size_bytes` - Size in bytes + /// + /// # Returns + /// + /// A new `BackupInfo` instance. + /// + /// # Note + /// + /// The `size_bytes` is converted from `u64` to `f64` for JavaScript compatibility. + /// While f64 can only represent integers exactly up to 2^53, this is sufficient + /// for file sizes up to 9 petabytes. + #[must_use] + #[allow(clippy::cast_precision_loss)] + pub fn new( + id: impl Into, + created_at: impl Into, + packages: Vec, + size_bytes: u64, + ) -> Self { + // Convert u64 to f64 for JavaScript compatibility + // Precision loss is acceptable: files larger than 9 petabytes are unrealistic + Self { + id: id.into(), + created_at: created_at.into(), + packages, + size_bytes: size_bytes as f64, + } + } + + /// Returns the number of packages in the backup. + #[must_use] + pub fn package_count(&self) -> usize { + self.packages.len() + } +} + +// ============================================================================ +// Response Data Types +// ============================================================================ + +/// Response data for the upgrade check command. +/// +/// This structure contains the results of checking for available +/// dependency upgrades. +/// +/// # Fields +/// +/// - `packages`: List of packages with available upgrades +/// - `summary`: Aggregate statistics about available upgrades +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeCheckData { +/// packages: PackageUpgradeInfo[]; +/// summary: UpgradeSummaryInfo; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeCheckData { + /// List of packages with available upgrades. + /// + /// Each entry contains information about the package and its + /// dependencies that have upgrades available. + pub packages: Vec, + + /// Summary statistics about available upgrades. + pub summary: UpgradeSummaryInfo, +} + +#[allow(dead_code)] +impl UpgradeCheckData { + /// Creates a new `UpgradeCheckData`. + /// + /// # Arguments + /// + /// * `packages` - List of package upgrades + /// * `summary` - Upgrade summary + /// + /// # Returns + /// + /// A new `UpgradeCheckData` instance. + #[must_use] + pub fn new(packages: Vec, summary: UpgradeSummaryInfo) -> Self { + Self { packages, summary } + } + + /// Creates an empty result (no upgrades available). + /// + /// # Arguments + /// + /// * `packages_analyzed` - Number of packages that were checked + /// + /// # Returns + /// + /// A new `UpgradeCheckData` with no upgrades. + #[must_use] + pub fn empty(packages_analyzed: u32) -> Self { + Self { packages: Vec::new(), summary: UpgradeSummaryInfo::empty(packages_analyzed) } + } + + /// Creates from a list of packages, calculating the summary. + /// + /// # Arguments + /// + /// * `packages` - List of package upgrades + /// + /// # Returns + /// + /// A new `UpgradeCheckData` with calculated summary. + #[must_use] + pub fn from_packages(packages: Vec) -> Self { + let summary = UpgradeSummaryInfo::from_packages(&packages); + Self { packages, summary } + } + + /// Returns true if there are any upgrades available. + #[must_use] + pub fn has_upgrades(&self) -> bool { + self.summary.total_upgrades > 0 + } + + /// Returns true if there are any breaking changes. + #[must_use] + pub fn has_breaking_changes(&self) -> bool { + self.summary.has_breaking_changes() + } +} + +/// Response data for the upgrade apply command. +/// +/// This structure contains the results of applying dependency upgrades. +/// +/// # Fields +/// +/// - `applied`: List of successfully applied upgrades +/// - `skipped`: List of skipped upgrades with reasons +/// - `failed`: List of failed upgrades with errors +/// - `summary`: Aggregate statistics about the operation +/// - `backup_id`: ID of the backup created (if backup was enabled) +/// - `changeset_id`: ID of the changeset created (if enabled) +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeApplyData { +/// applied: AppliedUpgradeInfo[]; +/// skipped: SkippedUpgradeInfo[]; +/// failed: FailedUpgradeInfo[]; +/// summary: ApplySummaryInfo; +/// backupId?: string; +/// changesetId?: string; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeApplyData { + /// List of successfully applied upgrades. + pub applied: Vec, + + /// List of upgrades that were skipped. + pub skipped: Vec, + + /// List of upgrades that failed. + pub failed: Vec, + + /// Summary statistics about the operation. + pub summary: ApplySummaryInfo, + + /// ID of the backup created, if backup was enabled. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub backup_id: Option, + + /// ID of the changeset created, if changeset creation was enabled. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub changeset_id: Option, +} + +#[allow(dead_code)] +impl UpgradeApplyData { + /// Creates a new `UpgradeApplyData`. + /// + /// # Arguments + /// + /// * `applied` - List of applied upgrades + /// * `skipped` - List of skipped upgrades + /// * `failed` - List of failed upgrades + /// * `summary` - Apply summary + /// + /// # Returns + /// + /// A new `UpgradeApplyData` instance. + #[must_use] + pub fn new( + applied: Vec, + skipped: Vec, + failed: Vec, + summary: ApplySummaryInfo, + ) -> Self { + Self { applied, skipped, failed, summary, backup_id: None, changeset_id: None } + } + + /// Creates an empty result (nothing was applied). + /// + /// # Returns + /// + /// A new `UpgradeApplyData` with empty lists. + #[must_use] + pub fn empty() -> Self { + Self { + applied: Vec::new(), + skipped: Vec::new(), + failed: Vec::new(), + summary: ApplySummaryInfo::empty(), + backup_id: None, + changeset_id: None, + } + } + + /// Sets the backup ID. + /// + /// # Arguments + /// + /// * `backup_id` - The backup ID + /// + /// # Returns + /// + /// Self with the backup ID set. + #[must_use] + pub fn with_backup_id(mut self, backup_id: impl Into) -> Self { + self.backup_id = Some(backup_id.into()); + self + } + + /// Sets the changeset ID. + /// + /// # Arguments + /// + /// * `changeset_id` - The changeset ID + /// + /// # Returns + /// + /// Self with the changeset ID set. + #[must_use] + pub fn with_changeset_id(mut self, changeset_id: impl Into) -> Self { + self.changeset_id = Some(changeset_id.into()); + self + } + + /// Returns true if a backup was created. + #[must_use] + pub fn has_backup(&self) -> bool { + self.backup_id.is_some() + } + + /// Returns true if a changeset was created. + #[must_use] + pub fn has_changeset(&self) -> bool { + self.changeset_id.is_some() + } + + /// Returns true if all upgrades succeeded. + #[must_use] + pub fn all_succeeded(&self) -> bool { + self.summary.all_succeeded() + } + + /// Returns true if any upgrades failed. + #[must_use] + pub fn has_failures(&self) -> bool { + self.summary.has_failures() + } +} + +/// Response data for the backup list command. +/// +/// This structure contains the list of available backups. +/// +/// # Fields +/// +/// - `backups`: List of available backups +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupListData { +/// backups: BackupInfo[]; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupListData { + /// List of available backups, ordered by creation date (newest first). + pub backups: Vec, +} + +#[allow(dead_code)] +impl BackupListData { + /// Creates a new `BackupListData`. + /// + /// # Arguments + /// + /// * `backups` - List of backups + /// + /// # Returns + /// + /// A new `BackupListData` instance. + #[must_use] + pub fn new(backups: Vec) -> Self { + Self { backups } + } + + /// Creates an empty result (no backups available). + /// + /// # Returns + /// + /// A new `BackupListData` with an empty list. + #[must_use] + pub fn empty() -> Self { + Self { backups: Vec::new() } + } + + /// Returns the number of backups. + #[must_use] + pub fn count(&self) -> usize { + self.backups.len() + } + + /// Returns true if there are no backups. + #[must_use] + pub fn is_empty(&self) -> bool { + self.backups.is_empty() + } +} + +/// Response data for the backup restore command. +/// +/// This structure contains the results of restoring from a backup. +/// +/// # Fields +/// +/// - `backup_id`: The ID of the backup that was restored +/// - `packages_restored`: Number of packages restored +/// - `packages`: List of package names that were restored +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupRestoreData { +/// backupId: string; +/// packagesRestored: number; +/// packages: string[]; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupRestoreData { + /// The ID of the backup that was restored. + pub backup_id: String, + + /// Number of packages that were restored. + pub packages_restored: u32, + + /// List of package names that were restored. + pub packages: Vec, +} + +#[allow(dead_code)] +impl BackupRestoreData { + /// Creates a new `BackupRestoreData`. + /// + /// # Arguments + /// + /// * `backup_id` - The backup ID + /// * `packages` - List of restored packages + /// + /// # Returns + /// + /// A new `BackupRestoreData` instance. + /// + /// # Note + /// + /// The package count is truncated to `u32::MAX` if it exceeds that value, + /// which is acceptable since workspaces with over 4 billion packages are + /// not realistic. + #[must_use] + #[allow(clippy::cast_possible_truncation)] + pub fn new(backup_id: impl Into, packages: Vec) -> Self { + // Truncation is acceptable: workspaces with >4 billion packages are unrealistic + let packages_restored = packages.len() as u32; + Self { backup_id: backup_id.into(), packages_restored, packages } + } +} + +/// Response data for the backup clean command. +/// +/// This structure contains the results of cleaning old backups. +/// +/// # Fields +/// +/// - `backups_removed`: Number of backups that were removed +/// - `backups_kept`: Number of backups that were kept +/// - `bytes_freed`: Approximate bytes freed by the cleanup +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupCleanData { +/// backupsRemoved: number; +/// backupsKept: number; +/// bytesFreed: number; +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupCleanData { + /// Number of backups that were removed. + pub backups_removed: u32, + + /// Number of backups that were kept. + pub backups_kept: u32, + + /// Approximate number of bytes freed by the cleanup. + /// + /// Note: Uses f64 for JavaScript compatibility. f64 can represent + /// integers up to 2^53 without precision loss, which is sufficient + /// for file sizes up to 9 petabytes. + pub bytes_freed: f64, +} + +#[allow(dead_code)] +impl BackupCleanData { + /// Creates a new `BackupCleanData`. + /// + /// # Arguments + /// + /// * `backups_removed` - Number removed + /// * `backups_kept` - Number kept + /// * `bytes_freed` - Bytes freed + /// + /// # Returns + /// + /// A new `BackupCleanData` instance. + /// + /// # Note + /// + /// The `bytes_freed` is converted from `u64` to `f64` for JavaScript compatibility. + /// While f64 can only represent integers exactly up to 2^53, this is sufficient + /// for file sizes up to 9 petabytes. + #[must_use] + #[allow(clippy::cast_precision_loss)] + pub fn new(backups_removed: u32, backups_kept: u32, bytes_freed: u64) -> Self { + // Convert u64 to f64 for JavaScript compatibility + // Precision loss is acceptable: files larger than 9 petabytes are unrealistic + Self { backups_removed, backups_kept, bytes_freed: bytes_freed as f64 } + } + + /// Creates a result indicating nothing was cleaned. + /// + /// # Arguments + /// + /// * `backups_kept` - Number of backups that exist + /// + /// # Returns + /// + /// A new `BackupCleanData` with zero removed. + #[must_use] + pub fn nothing_to_clean(backups_kept: u32) -> Self { + Self { backups_removed: 0, backups_kept, bytes_freed: 0.0 } + } +} + +// ============================================================================ +// API Response Types +// ============================================================================ + +/// API response for the upgrade check command. +/// +/// This structure wraps `UpgradeCheckData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeCheckApiResponse { +/// success: boolean; +/// data?: UpgradeCheckData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await upgradeCheck({ root: '.' }); +/// +/// if (result.success) { +/// console.log(`Found ${result.data.summary.totalUpgrades} upgrades`); +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeCheckApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The check result data if successful. + /// + /// Contains the list of available upgrades and summary statistics. + #[napi(ts_type = "UpgradeCheckData | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + /// Error information if the operation failed. + /// + /// Contains the error code, message, and context when the + /// operation fails. + #[napi(ts_type = "ErrorInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +#[allow(dead_code)] +impl UpgradeCheckApiResponse { + /// Creates a successful response with check data. + /// + /// # Arguments + /// + /// * `data` - The upgrade check data + /// + /// # Returns + /// + /// A new successful `UpgradeCheckApiResponse`. + #[must_use] + pub fn success(data: UpgradeCheckData) -> Self { + Self { success: true, data: Some(data), error: None } + } + + /// Creates a failure response with error information. + /// + /// # Arguments + /// + /// * `error` - The error information + /// + /// # Returns + /// + /// A new failed `UpgradeCheckApiResponse`. + #[must_use] + pub fn failure(error: ErrorInfo) -> Self { + Self { success: false, data: None, error: Some(error) } + } + + /// Returns true if the response indicates success. + #[must_use] + pub fn is_success(&self) -> bool { + self.success + } + + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} + +/// API response for the upgrade apply command. +/// +/// This structure wraps `UpgradeApplyData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface UpgradeApplyApiResponse { +/// success: boolean; +/// data?: UpgradeApplyData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await upgradeApply({ +/// root: '.', +/// createBackup: true, +/// selection: { patch: true } +/// }); +/// +/// if (result.success) { +/// console.log(`Applied ${result.data.summary.totalApplied} upgrades`); +/// if (result.data.backupId) { +/// console.log(`Backup: ${result.data.backupId}`); +/// } +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct UpgradeApplyApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The apply result data if successful. + /// + /// Contains information about applied, skipped, and failed upgrades. + #[napi(ts_type = "UpgradeApplyData | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + /// Error information if the operation failed. + /// + /// Contains the error code, message, and context when the + /// operation fails. + #[napi(ts_type = "ErrorInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +#[allow(dead_code)] +impl UpgradeApplyApiResponse { + /// Creates a successful response with apply data. + /// + /// # Arguments + /// + /// * `data` - The upgrade apply data + /// + /// # Returns + /// + /// A new successful `UpgradeApplyApiResponse`. + #[must_use] + pub fn success(data: UpgradeApplyData) -> Self { + Self { success: true, data: Some(data), error: None } + } + + /// Creates a failure response with error information. + /// + /// # Arguments + /// + /// * `error` - The error information + /// + /// # Returns + /// + /// A new failed `UpgradeApplyApiResponse`. + #[must_use] + pub fn failure(error: ErrorInfo) -> Self { + Self { success: false, data: None, error: Some(error) } + } + + /// Returns true if the response indicates success. + #[must_use] + pub fn is_success(&self) -> bool { + self.success + } + + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} + +/// API response for the backup list command. +/// +/// This structure wraps `BackupListData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupListApiResponse { +/// success: boolean; +/// data?: BackupListData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await backupList({ root: '.' }); +/// +/// if (result.success) { +/// for (const backup of result.data.backups) { +/// console.log(`${backup.id}: ${backup.createdAt}`); +/// } +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupListApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The list of backups if successful. + #[napi(ts_type = "BackupListData | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + /// Error information if the operation failed. + #[napi(ts_type = "ErrorInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +#[allow(dead_code)] +impl BackupListApiResponse { + /// Creates a successful response with backup list data. + /// + /// # Arguments + /// + /// * `data` - The backup list data + /// + /// # Returns + /// + /// A new successful `BackupListApiResponse`. + #[must_use] + pub fn success(data: BackupListData) -> Self { + Self { success: true, data: Some(data), error: None } + } + + /// Creates a failure response with error information. + /// + /// # Arguments + /// + /// * `error` - The error information + /// + /// # Returns + /// + /// A new failed `BackupListApiResponse`. + #[must_use] + pub fn failure(error: ErrorInfo) -> Self { + Self { success: false, data: None, error: Some(error) } + } + + /// Returns true if the response indicates success. + #[must_use] + pub fn is_success(&self) -> bool { + self.success + } + + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} + +/// API response for the backup restore command. +/// +/// This structure wraps `BackupRestoreData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupRestoreApiResponse { +/// success: boolean; +/// data?: BackupRestoreData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await backupRestore({ +/// root: '.', +/// backupId: 'backup-2024-01-15-123456' +/// }); +/// +/// if (result.success) { +/// console.log(`Restored ${result.data.packagesRestored} packages`); +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupRestoreApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The restore result data if successful. + #[napi(ts_type = "BackupRestoreData | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + /// Error information if the operation failed. + #[napi(ts_type = "ErrorInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +#[allow(dead_code)] +impl BackupRestoreApiResponse { + /// Creates a successful response with restore data. + /// + /// # Arguments + /// + /// * `data` - The backup restore data + /// + /// # Returns + /// + /// A new successful `BackupRestoreApiResponse`. + #[must_use] + pub fn success(data: BackupRestoreData) -> Self { + Self { success: true, data: Some(data), error: None } + } + + /// Creates a failure response with error information. + /// + /// # Arguments + /// + /// * `error` - The error information + /// + /// # Returns + /// + /// A new failed `BackupRestoreApiResponse`. + #[must_use] + pub fn failure(error: ErrorInfo) -> Self { + Self { success: false, data: None, error: Some(error) } + } + + /// Returns true if the response indicates success. + #[must_use] + pub fn is_success(&self) -> bool { + self.success + } + + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} + +/// API response for the backup clean command. +/// +/// This structure wraps `BackupCleanData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BackupCleanApiResponse { +/// success: boolean; +/// data?: BackupCleanData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await backupClean({ root: '.', keepCount: 3 }); +/// +/// if (result.success) { +/// console.log(`Removed ${result.data.backupsRemoved} backups`); +/// console.log(`Freed ${result.data.bytesFreed} bytes`); +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BackupCleanApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The cleanup result data if successful. + #[napi(ts_type = "BackupCleanData | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + /// Error information if the operation failed. + #[napi(ts_type = "ErrorInfo | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +#[allow(dead_code)] +impl BackupCleanApiResponse { + /// Creates a successful response with cleanup data. + /// + /// # Arguments + /// + /// * `data` - The backup clean data + /// + /// # Returns + /// + /// A new successful `BackupCleanApiResponse`. + #[must_use] + pub fn success(data: BackupCleanData) -> Self { + Self { success: true, data: Some(data), error: None } + } + + /// Creates a failure response with error information. + /// + /// # Arguments + /// + /// * `error` - The error information + /// + /// # Returns + /// + /// A new failed `BackupCleanApiResponse`. + #[must_use] + pub fn failure(error: ErrorInfo) -> Self { + Self { success: false, data: None, error: Some(error) } + } + + /// Returns true if the response indicates success. + #[must_use] + pub fn is_success(&self) -> bool { + self.success + } -// TODO: will be implemented on story 8.1 - Upgrade Types -// This module will contain: -// -// Re-exports from sublime_pkg_tools: -// - pub use sublime_pkg_tools::upgrade::{ -// PackageUpgrades, DependencyUpgrade, UpgradeType, UpgradePreview, -// UpgradeSummary, UpgradeResult, AppliedUpgrade, ApplySummary, -// BackupMetadata, DetectionOptions, UpgradeSelection -// }; -// -// NAPI-specific types: -// - UpgradeCheckParams: { root, includeMajor?, includeMinor?, includePatch?, packages? } -// - UpgradeCheckData: { packages: PackageUpgradeInfo[], summary: UpgradeSummaryInfo } -// - UpgradeApplyParams: { root, createBackup?, selection?, createChangeset? } -// - UpgradeApplyData: { applied, skipped, failed, backupId?, changesetId? } -// - BackupCreateParams: { root } -// - BackupCreateData: { backupId, createdAt, packages } -// - BackupRestoreParams: { root, backupId } -// - BackupRestoreData: { restored, packages } -// - BackupListParams: { root } -// - BackupListData: { backups: BackupInfo[] } -// -// Shared types: -// - PackageUpgradeInfo: { packageName, packagePath, dependencies: DependencyUpgradeInfo[] } -// - DependencyUpgradeInfo: { name, currentVersion, latestVersion, upgradeType, dependencyType } -// - UpgradeSummaryInfo: { totalUpgrades, majorUpgrades, minorUpgrades, patchUpgrades } -// - BackupInfo: { id, createdAt, packages, sizeBytes } + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} diff --git a/packages/workspace-tools/npm/darwin-arm64/package.json b/packages/workspace-tools/npm/darwin-arm64/package.json index 9275ba43..07f6ef55 100644 --- a/packages/workspace-tools/npm/darwin-arm64/package.json +++ b/packages/workspace-tools/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-darwin-arm64", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/darwin-x64/package.json b/packages/workspace-tools/npm/darwin-x64/package.json index 9bdc30e4..1efd44a2 100644 --- a/packages/workspace-tools/npm/darwin-x64/package.json +++ b/packages/workspace-tools/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-darwin-x64", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/npm/linux-arm64-gnu/package.json b/packages/workspace-tools/npm/linux-arm64-gnu/package.json index 600351e0..e0e2ce63 100644 --- a/packages/workspace-tools/npm/linux-arm64-gnu/package.json +++ b/packages/workspace-tools/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-linux-arm64-gnu", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/linux-arm64-musl/package.json b/packages/workspace-tools/npm/linux-arm64-musl/package.json index 4038ffbe..8e296190 100644 --- a/packages/workspace-tools/npm/linux-arm64-musl/package.json +++ b/packages/workspace-tools/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-linux-arm64-musl", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/linux-x64-gnu/package.json b/packages/workspace-tools/npm/linux-x64-gnu/package.json index 3325482a..63270757 100644 --- a/packages/workspace-tools/npm/linux-x64-gnu/package.json +++ b/packages/workspace-tools/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-linux-x64-gnu", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/npm/linux-x64-musl/package.json b/packages/workspace-tools/npm/linux-x64-musl/package.json index f0d963fc..fa71a807 100644 --- a/packages/workspace-tools/npm/linux-x64-musl/package.json +++ b/packages/workspace-tools/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-linux-x64-musl", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/npm/win32-arm64-msvc/package.json b/packages/workspace-tools/npm/win32-arm64-msvc/package.json index 81a8ba5e..50f6301b 100644 --- a/packages/workspace-tools/npm/win32-arm64-msvc/package.json +++ b/packages/workspace-tools/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-win32-arm64-msvc", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/win32-x64-msvc/package.json b/packages/workspace-tools/npm/win32-x64-msvc/package.json index 800819e9..6585bece 100644 --- a/packages/workspace-tools/npm/win32-x64-msvc/package.json +++ b/packages/workspace-tools/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools-win32-x64-msvc", - "version": "2.0.22", + "version": "2.0.23", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/package.json b/packages/workspace-tools/package.json index dbabc0f2..eeda3f2e 100644 --- a/packages/workspace-tools/package.json +++ b/packages/workspace-tools/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools", - "version": "2.0.22", + "version": "2.0.23", "description": "Bindings for node from crate workspace-tools", "main": "./dist/cjs/index.cjs", "types": "./dist/types/index.d.cts", diff --git a/packages/workspace-tools/src/binding.d.ts b/packages/workspace-tools/src/binding.d.ts index 1d77316d..4a694534 100644 --- a/packages/workspace-tools/src/binding.d.ts +++ b/packages/workspace-tools/src/binding.d.ts @@ -1,5 +1,83 @@ /* auto-generated by NAPI-RS */ /* eslint-disable */ +/** + * Information about a successfully applied upgrade. + * + * This structure contains details about a single dependency upgrade + * that was successfully applied. + * + * # Fields + * + * - `package_name`: The package that was modified + * - `dependency_name`: The dependency that was upgraded + * - `old_version`: The previous version + * - `new_version`: The new version + * - `upgrade_type`: The type of upgrade + * + * # TypeScript Definition + * + * ```typescript + * interface AppliedUpgradeInfo { + * packageName: string; + * dependencyName: string; + * oldVersion: string; + * newVersion: string; + * upgradeType: string; + * } + * ``` + */ +export interface AppliedUpgradeInfo { + /** The name of the package that was modified. */ + packageName: string + /** The name of the dependency that was upgraded. */ + dependencyName: string + /** The previous version that was replaced. */ + oldVersion: string + /** The new version that was applied. */ + newVersion: string + /** + * The type of upgrade that was applied. + * + * One of: `"major"`, `"minor"`, `"patch"` + */ + upgradeType: string +} + +/** + * Summary of upgrade application results. + * + * This structure provides aggregate statistics about the results + * of applying upgrades. + * + * # Fields + * + * - `total_applied`: Number of upgrades successfully applied + * - `total_skipped`: Number of upgrades that were skipped + * - `total_failed`: Number of upgrades that failed + * - `packages_modified`: List of packages that were modified + * + * # TypeScript Definition + * + * ```typescript + * interface ApplySummaryInfo { + * totalApplied: number; + * totalSkipped: number; + * totalFailed: number; + * packagesModified: string[]; + * } + * ``` + */ +export interface ApplySummaryInfo { + /** Number of upgrades successfully applied. */ + totalApplied: number + /** Number of upgrades that were skipped. */ + totalSkipped: number + /** Number of upgrades that failed. */ + totalFailed: number + /** List of package names that were modified. */ + packagesModified: Array +} + /** * Archived changeset information. * @@ -128,6 +206,138 @@ export interface AuditSectionsConfigInfo { breakingChanges: boolean } +/** + * API response for the backup clean command. + * + * This structure wraps `BackupCleanData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. + * + * # TypeScript Definition + * + * ```typescript + * interface BackupCleanApiResponse { + * success: boolean; + * data?: BackupCleanData; + * error?: ErrorInfo; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await backupClean({ root: '.', keepCount: 3 }); + * + * if (result.success) { + * console.log(`Removed ${result.data.backupsRemoved} backups`); + * console.log(`Freed ${result.data.bytesFreed} bytes`); + * } else { + * console.error(`Error: ${result.error.message}`); + * } + * ``` + */ +export interface BackupCleanApiResponse { + /** Whether the operation was successful. */ + success: boolean + /** The cleanup result data if successful. */ + data?: BackupCleanData | undefined + /** Error information if the operation failed. */ + error?: ErrorInfo | undefined +} + +/** + * Response data for the backup clean command. + * + * This structure contains the results of cleaning old backups. + * + * # Fields + * + * - `backups_removed`: Number of backups that were removed + * - `backups_kept`: Number of backups that were kept + * - `bytes_freed`: Approximate bytes freed by the cleanup + * + * # TypeScript Definition + * + * ```typescript + * interface BackupCleanData { + * backupsRemoved: number; + * backupsKept: number; + * bytesFreed: number; + * } + * ``` + */ +export interface BackupCleanData { + /** Number of backups that were removed. */ + backupsRemoved: number + /** Number of backups that were kept. */ + backupsKept: number + /** + * Approximate number of bytes freed by the cleanup. + * + * Note: Uses f64 for JavaScript compatibility. f64 can represent + * integers up to 2^53 without precision loss, which is sufficient + * for file sizes up to 9 petabytes. + */ + bytesFreed: number +} + +/** + * Input parameters for the backup clean command. + * + * This structure defines the parameters for cleaning (removing) old backups. + * This helps manage disk space by removing older backups while keeping + * the most recent ones. + * + * # Fields + * + * - `root`: The workspace root directory path (required) + * - `config_path`: Optional path to a custom configuration file + * - `keep_count`: Number of recent backups to keep (optional, defaults to 5) + * + * # TypeScript Definition + * + * ```typescript + * interface BackupCleanParams { + * root: string; + * configPath?: string; + * keepCount?: number; + * } + * ``` + * + * # Examples + * + * ```typescript + * // Keep last 3 backups, remove older ones + * const params: BackupCleanParams = { + * root: '.', + * keepCount: 3 + * }; + * const result = await backupClean(params); + * ``` + */ +export interface BackupCleanParams { + /** + * Workspace root directory path. + * + * This is the absolute or relative path to the root of the workspace. + */ + root: string + /** + * Optional custom configuration file path. + * + * If not provided, the command will search for configuration files + * in standard locations within the workspace root. + */ + configPath?: string | undefined + /** + * Number of recent backups to keep. + * + * Backups are sorted by creation date, and the most recent ones + * are kept. Older backups beyond this count are removed. + * Defaults to 5 if not specified. + */ + keepCount?: number | undefined +} + /** * Backup configuration information. * @@ -176,156 +386,171 @@ export interface BackupConfigInfo { } /** - * Git branch information. + * Information about a backup. * - * Contains the name of the current Git branch, if available. - * This information is useful for determining the context of - * pending changesets and version bumps. + * This structure contains metadata about a backup that was created + * during upgrade operations. * * # Fields * - * - `name`: The branch name + * - `id`: Unique identifier for the backup + * - `created_at`: When the backup was created (ISO 8601 format) + * - `packages`: List of packages included in the backup + * - `size_bytes`: Size of the backup in bytes * * # TypeScript Definition * * ```typescript - * interface BranchInfo { - * Branch name - * name: string; + * interface BackupInfo { + * id: string; + * createdAt: string; + * packages: string[]; + * sizeBytes: number; * } * ``` - * - * # Examples - * - * ```typescript - * const branch: BranchInfo = { name: 'main' }; - * const feature: BranchInfo = { name: 'feature/add-new-api' }; - * ``` */ -export interface BranchInfo { +export interface BackupInfo { /** - * Git branch name. + * Unique identifier for the backup. * - * This is the name of the currently checked-out branch. - * It does not include the `refs/heads/` prefix. + * Typically in the format `backup-YYYY-MM-DD-HHMMSS`. */ - name: string + id: string + /** + * When the backup was created. + * + * ISO 8601 format (e.g., `2024-01-15T12:34:56Z`). + */ + createdAt: string + /** List of package names included in the backup. */ + packages: Array + /** + * Size of the backup in bytes. + * + * Note: Uses f64 for JavaScript compatibility. f64 can represent + * integers up to 2^53 without precision loss, which is sufficient + * for file sizes up to 9 petabytes. + */ + sizeBytes: number } /** - * Apply version bumps to packages. - * - * Applies version changes based on pending changesets. This is the main - * release command that modifies package.json files, generates changelogs, - * archives changesets, and optionally creates Git commits and tags. - * - * This function is the main entry point for Node.js applications to apply - * version bumps. It handles all the complexity of CLI invocation and response - * parsing internally. + * API response for the backup list command. * - * @param params - Apply parameters containing: - * - `root`: Workspace root directory path (required) - * - `configPath`: Optional custom config file path - * - `packages`: Optional filter to specific packages - * - `gitCommit`: Whether to create a Git commit with version changes - * - `gitTag`: Whether to create Git tags for releases - * - `gitPush`: Whether to push Git tags to remote - * - `prerelease`: Prerelease tag (alpha, beta, rc, or custom) - * - `noChangelog`: Whether to skip changelog generation - * - `noArchive`: Whether to keep changesets active after bump - * - `alwaysArchive`: Whether to force archiving for prereleases - * - `force`: Whether to skip confirmation prompts (default: true) + * This structure wraps `BackupListData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. * - * @returns `Promise>` containing: - * - On success: `{ success: true, data: BumpApplyData }` - * - On failure: `{ success: false, error: ErrorInfo }` + * # TypeScript Definition * - * @example Basic usage - apply bumps without Git operations * ```typescript - * const result = await bumpApply({ root: '/path/to/project' }); - * if (result.success) { - * console.log(`Updated ${result.data.packagesUpdated} packages`); - * console.log(`Archived ${result.data.changesetsArchived} changesets`); - * console.log(`Modified files: ${result.data.filesModified.join(', ')}`); - * } else { - * console.error(`Error: ${result.error.code} - ${result.error.message}`); + * interface BackupListApiResponse { + * success: boolean; + * data?: BackupListData; + * error?: ErrorInfo; * } * ``` * - * @example With Git operations + * # Examples + * * ```typescript - * const result = await bumpApply({ - * root: '/path/to/project', - * gitCommit: true, - * gitTag: true, - * gitPush: true - * }); + * const result = await backupList({ root: '.' }); + * * if (result.success) { - * console.log(`Commit SHA: ${result.data.commitSha}`); - * console.log(`Tags created: ${result.data.tagsCreated.join(', ')}`); + * for (const backup of result.data.backups) { + * console.log(`${backup.id}: ${backup.createdAt}`); + * } + * } else { + * console.error(`Error: ${result.error.message}`); * } * ``` + */ +export interface BackupListApiResponse { + /** Whether the operation was successful. */ + success: boolean + /** The list of backups if successful. */ + data?: BackupListData | undefined + /** Error information if the operation failed. */ + error?: ErrorInfo | undefined +} + +/** + * Response data for the backup list command. * - * @example Prerelease version (beta) - * ```typescript - * const result = await bumpApply({ - * root: '/path/to/project', - * prerelease: 'beta', - * gitCommit: true, - * gitTag: true - * }); - * // Creates versions like 1.3.0-beta.0 - * ``` + * This structure contains the list of available backups. * - * @example Skip changelog and archive - * ```typescript - * const result = await bumpApply({ - * root: '/path/to/project', - * noChangelog: true, - * noArchive: true - * }); - * // Updates versions but keeps changesets and skips changelog - * ``` + * # Fields * - * @example Force archive for prerelease - * ```typescript - * const result = await bumpApply({ - * root: '/path/to/project', - * prerelease: 'rc', - * alwaysArchive: true, // Archive changesets even for prerelease - * gitCommit: true, - * gitTag: true - * }); - * ``` + * - `backups`: List of available backups + * + * # TypeScript Definition * - * @example Error handling * ```typescript - * const result = await bumpApply({ root: '/nonexistent' }); - * if (!result.success) { - * if (result.error.code === 'ENOENT') { - * console.error('Path not found'); - * } else if (result.error.code === 'EVALIDATION') { - * console.error('Invalid parameters:', result.error.message); - * } else if (result.error.code === 'EGIT') { - * console.error('Git operation failed:', result.error.message); - * } + * interface BackupListData { + * backups: BackupInfo[]; * } * ``` */ -export declare function bumpApply(params: BumpApplyParams): Promise +export interface BackupListData { + /** List of available backups, ordered by creation date (newest first). */ + backups: Array +} /** - * API response for the bump apply command. + * Input parameters for the backup list command. * - * This structure wraps `BumpApplyData` in the standard `ApiResponse` + * This structure defines the parameters for listing available backups + * in the workspace. Backups are created by the upgrade apply command + * when `createBackup` is enabled. + * + * # Fields + * + * - `root`: The workspace root directory path (required) + * - `config_path`: Optional path to a custom configuration file + * + * # TypeScript Definition + * + * ```typescript + * interface BackupListParams { + * root: string; + * configPath?: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const params: BackupListParams = { root: '.' }; + * const result = await backupList(params); + * ``` + */ +export interface BackupListParams { + /** + * Workspace root directory path. + * + * This is the absolute or relative path to the root of the workspace. + */ + root: string + /** + * Optional custom configuration file path. + * + * If not provided, the command will search for configuration files + * in standard locations within the workspace root. + */ + configPath?: string | undefined +} + +/** + * API response for the backup restore command. + * + * This structure wraps `BackupRestoreData` in the standard `ApiResponse` * format, providing a consistent interface for success and error cases. * * # TypeScript Definition * * ```typescript - * interface BumpApplyApiResponse { + * interface BackupRestoreApiResponse { * success: boolean; - * data?: BumpApplyData; + * data?: BackupRestoreData; * error?: ErrorInfo; * } * ``` @@ -333,197 +558,104 @@ export declare function bumpApply(params: BumpApplyParams): Promise - /** - * List of Git tags that were created. - * - * Format: `{package}@{version}` (e.g., `@scope/core@1.1.0`) - */ - tagsCreated: Array - /** - * Git commit SHA (if gitCommit was true). - * - * The full 40-character SHA of the commit containing - * all version bump changes. - */ - commitSha?: string | undefined +export interface BackupRestoreData { + /** The ID of the backup that was restored. */ + backupId: string + /** Number of packages that were restored. */ + packagesRestored: number + /** List of package names that were restored. */ + packages: Array } /** - * Input parameters for the bump apply command. + * Input parameters for the backup restore command. * - * This structure defines the parameters for applying version bumps to packages. - * Unlike preview, this command actually modifies files and can optionally - * integrate with Git for committing and tagging releases. + * This structure defines the parameters for restoring package.json files + * from a previous backup. This effectively rolls back dependency changes + * to a known state. * * # Fields * * - `root`: The workspace root directory path (required) + * - `backup_id`: The ID of the backup to restore (required) * - `config_path`: Optional path to a custom configuration file - * - `packages`: Optional filter to specific packages - * - `git_commit`: Whether to create a Git commit with version changes - * - `git_tag`: Whether to create Git tags for releases - * - `git_push`: Whether to push Git tags to remote - * - `prerelease`: Prerelease tag for pre-release versions (alpha, beta, rc, or custom) - * - `no_changelog`: Whether to skip changelog generation - * - `no_archive`: Whether to keep changesets active after bump - * - `always_archive`: Whether to force archiving even for prerelease versions - * - `force`: Whether to skip confirmation prompts * * # TypeScript Definition * * ```typescript - * interface BumpApplyParams { + * interface BackupRestoreParams { * root: string; + * backupId: string; * configPath?: string; - * packages?: string[]; - * gitCommit?: boolean; - * gitTag?: boolean; - * gitPush?: boolean; - * prerelease?: string; - * noChangelog?: boolean; - * noArchive?: boolean; - * alwaysArchive?: boolean; - * force?: boolean; * } * ``` * - * # Prerelease Support - * - * The `prerelease` parameter creates semver-compliant pre-release versions: - * - `"alpha"` → `1.2.3 → 1.3.0-alpha.0` - * - `"beta"` → `1.2.3 → 1.3.0-beta.0` - * - `"rc"` → `1.2.3 → 1.3.0-rc.0` - * - Any custom tag → `1.2.3 → 1.3.0-{tag}.0` - * * # Examples * * ```typescript - * // Minimal apply - just bump versions - * const minimal: BumpApplyParams = { root: '.' }; - * - * // Full release with Git integration - * const release: BumpApplyParams = { - * root: '.', - * gitCommit: true, - * gitTag: true, - * gitPush: true, - * force: true - * }; - * - * // Beta prerelease - * const beta: BumpApplyParams = { + * const params: BackupRestoreParams = { * root: '.', - * prerelease: 'beta', - * gitCommit: true, - * gitTag: true + * backupId: 'backup-2024-01-15-123456' * }; + * const result = await backupRestore(params); * ``` */ -export interface BumpApplyParams { +export interface BackupRestoreParams { /** * Workspace root directory path. * * This is the absolute or relative path to the root of the workspace. */ root: string + /** + * The ID of the backup to restore. + * + * This should match an ID returned by the backupList command. + * Backup IDs are typically in the format `backup-YYYY-MM-DD-HHMMSS`. + */ + backupId: string /** * Optional custom configuration file path. * @@ -531,149 +663,159 @@ export interface BumpApplyParams { * in standard locations within the workspace root. */ configPath?: string | undefined - /** - * Filter to specific packages. - * - * When provided, only these packages will be bumped. - * Package names should include scope if applicable. - */ - packages?: string[] | undefined - /** - * Whether to create a Git commit with version changes. - * - * When `true`, creates a commit containing all modified files - * (package.json, CHANGELOG.md, etc.) with a conventional commit message. - */ - gitCommit?: boolean | undefined - /** - * Whether to create Git tags for releases. - * - * When `true`, creates tags in the format `{package}@{version}` for - * each bumped package. Requires `git_commit` to be effective. - */ - gitTag?: boolean | undefined - /** - * Whether to push Git tags to remote. - * - * When `true`, pushes the created tags to the remote repository. - * Requires `git_tag` to be `true` to have any effect. - */ - gitPush?: boolean | undefined - /** - * Prerelease tag for pre-release versions. - * - * Creates semver-compliant pre-release versions. Common values: - * - `"alpha"`: Early development (`1.3.0-alpha.0`) - * - `"beta"`: Feature complete (`1.3.0-beta.0`) - * - `"rc"`: Release candidate (`1.3.0-rc.0`) - * - * Custom tags are also supported. Must contain only ASCII - * alphanumerics and hyphens `[0-9A-Za-z-]`. - */ - prerelease?: string | undefined - /** - * Whether to skip changelog generation. - * - * When `true`, CHANGELOG.md files will not be updated during the bump. - * Useful for quick internal releases or when changelogs are managed - * separately. - */ - noChangelog?: boolean | undefined - /** - * Whether to keep changesets active after bump. - * - * When `true`, changesets are not archived after version bump. - * Useful for partial releases or when you want to accumulate - * more changes before archiving. - */ - noArchive?: boolean | undefined - /** - * Whether to force archiving even for prerelease versions. - * - * By default, prerelease versions don't archive changesets (since - * the final release will archive them). Set this to `true` to - * archive changesets even for prerelease versions. - */ - alwaysArchive?: boolean | undefined - /** - * Whether to skip confirmation prompts. - * - * When `true`, applies changes without asking for confirmation. - * Recommended for CI/CD environments. The NAPI API defaults to - * `true` since it's typically used programmatically. - */ - force?: boolean | undefined } /** - * Preview version bumps without applying changes. - * - * Returns comprehensive information about what versions would change based on - * pending changesets. This is a dry-run operation that does not modify any files. + * Git branch information. * - * This function is the main entry point for Node.js applications to preview + * Contains the name of the current Git branch, if available. + * This information is useful for determining the context of + * pending changesets and version bumps. + * + * # Fields + * + * - `name`: The branch name + * + * # TypeScript Definition + * + * ```typescript + * interface BranchInfo { + * Branch name + * name: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const branch: BranchInfo = { name: 'main' }; + * const feature: BranchInfo = { name: 'feature/add-new-api' }; + * ``` + */ +export interface BranchInfo { + /** + * Git branch name. + * + * This is the name of the currently checked-out branch. + * It does not include the `refs/heads/` prefix. + */ + name: string +} + +/** + * Apply version bumps to packages. + * + * Applies version changes based on pending changesets. This is the main + * release command that modifies package.json files, generates changelogs, + * archives changesets, and optionally creates Git commits and tags. + * + * This function is the main entry point for Node.js applications to apply * version bumps. It handles all the complexity of CLI invocation and response * parsing internally. * - * @param params - Preview parameters containing: + * @param params - Apply parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path * - `packages`: Optional filter to specific packages - * - `showDiff`: Whether to show detailed version diffs + * - `gitCommit`: Whether to create a Git commit with version changes + * - `gitTag`: Whether to create Git tags for releases + * - `gitPush`: Whether to push Git tags to remote + * - `prerelease`: Prerelease tag (alpha, beta, rc, or custom) + * - `noChangelog`: Whether to skip changelog generation + * - `noArchive`: Whether to keep changesets active after bump + * - `alwaysArchive`: Whether to force archiving for prereleases + * - `force`: Whether to skip confirmation prompts (default: true) * - * @returns `Promise>` containing: - * - On success: `{ success: true, data: BumpPreviewData }` + * @returns `Promise>` containing: + * - On success: `{ success: true, data: BumpApplyData }` * - On failure: `{ success: false, error: ErrorInfo }` * - * @example Basic usage + * @example Basic usage - apply bumps without Git operations * ```typescript - * const result = await bumpPreview({ root: '/path/to/project' }); + * const result = await bumpApply({ root: '/path/to/project' }); * if (result.success) { - * console.log(`Strategy: ${result.data.strategy}`); - * console.log(`Packages to bump: ${result.data.packages.length}`); - * for (const pkg of result.data.packages) { - * console.log(` ${pkg.name}: ${pkg.currentVersion} -> ${pkg.nextVersion}`); - * } + * console.log(`Updated ${result.data.packagesUpdated} packages`); + * console.log(`Archived ${result.data.changesetsArchived} changesets`); + * console.log(`Modified files: ${result.data.filesModified.join(', ')}`); * } else { * console.error(`Error: ${result.error.code} - ${result.error.message}`); * } * ``` * - * @example With package filter and diff + * @example With Git operations * ```typescript - * const result = await bumpPreview({ + * const result = await bumpApply({ * root: '/path/to/project', - * packages: ['@scope/core', '@scope/utils'], - * showDiff: true + * gitCommit: true, + * gitTag: true, + * gitPush: true + * }); + * if (result.success) { + * console.log(`Commit SHA: ${result.data.commitSha}`); + * console.log(`Tags created: ${result.data.tagsCreated.join(', ')}`); + * } + * ``` + * + * @example Prerelease version (beta) + * ```typescript + * const result = await bumpApply({ + * root: '/path/to/project', + * prerelease: 'beta', + * gitCommit: true, + * gitTag: true + * }); + * // Creates versions like 1.3.0-beta.0 + * ``` + * + * @example Skip changelog and archive + * ```typescript + * const result = await bumpApply({ + * root: '/path/to/project', + * noChangelog: true, + * noArchive: true + * }); + * // Updates versions but keeps changesets and skips changelog + * ``` + * + * @example Force archive for prerelease + * ```typescript + * const result = await bumpApply({ + * root: '/path/to/project', + * prerelease: 'rc', + * alwaysArchive: true, // Archive changesets even for prerelease + * gitCommit: true, + * gitTag: true * }); * ``` * * @example Error handling * ```typescript - * const result = await bumpPreview({ root: '/nonexistent' }); + * const result = await bumpApply({ root: '/nonexistent' }); * if (!result.success) { * if (result.error.code === 'ENOENT') { * console.error('Path not found'); * } else if (result.error.code === 'EVALIDATION') { - * console.error('Invalid parameters'); + * console.error('Invalid parameters:', result.error.message); + * } else if (result.error.code === 'EGIT') { + * console.error('Git operation failed:', result.error.message); * } * } * ``` */ -export declare function bumpPreview(params: BumpPreviewParams): Promise +export declare function bumpApply(params: BumpApplyParams): Promise /** - * API response for the bump preview command. + * API response for the bump apply command. * - * This structure wraps `BumpPreviewData` in the standard `ApiResponse` + * This structure wraps `BumpApplyData` in the standard `ApiResponse` * format, providing a consistent interface for success and error cases. * * # TypeScript Definition * * ```typescript - * interface BumpPreviewApiResponse { + * interface BumpApplyApiResponse { * success: boolean; - * data?: BumpPreviewData; + * data?: BumpApplyData; * error?: ErrorInfo; * } * ``` @@ -681,25 +823,32 @@ export declare function bumpPreview(params: BumpPreviewParams): Promise + filesModified: Array /** - * Summary statistics of the bump. + * List of Git tags that were created. * - * Aggregated counts of total packages and bump types. + * Format: `{package}@{version}` (e.g., `@scope/core@1.1.0`) */ - summary: BumpSummaryInfo + tagsCreated: Array /** - * IDs of changesets that will be consumed. + * Git commit SHA (if gitCommit was true). * - * These changesets will be archived after the bump is applied. + * The full 40-character SHA of the commit containing + * all version bump changes. */ - changesets: Array + commitSha?: string | undefined } /** - * Input parameters for the bump preview command. + * Input parameters for the bump apply command. * - * This structure defines the parameters for previewing version bumps based on - * pending changesets. The preview is a dry-run operation that shows what would - * change without actually modifying any files. + * This structure defines the parameters for applying version bumps to packages. + * Unlike preview, this command actually modifies files and can optionally + * integrate with Git for committing and tagging releases. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file * - `packages`: Optional filter to specific packages - * - `show_diff`: Whether to show detailed version diffs + * - `git_commit`: Whether to create a Git commit with version changes + * - `git_tag`: Whether to create Git tags for releases + * - `git_push`: Whether to push Git tags to remote + * - `prerelease`: Prerelease tag for pre-release versions (alpha, beta, rc, or custom) + * - `no_changelog`: Whether to skip changelog generation + * - `no_archive`: Whether to keep changesets active after bump + * - `always_archive`: Whether to force archiving even for prerelease versions + * - `force`: Whether to skip confirmation prompts * * # TypeScript Definition * * ```typescript - * interface BumpPreviewParams { + * interface BumpApplyParams { * root: string; * configPath?: string; * packages?: string[]; - * showDiff?: boolean; - * } - * ``` - * + * gitCommit?: boolean; + * gitTag?: boolean; + * gitPush?: boolean; + * prerelease?: string; + * noChangelog?: boolean; + * noArchive?: boolean; + * alwaysArchive?: boolean; + * force?: boolean; + * } + * ``` + * + * # Prerelease Support + * + * The `prerelease` parameter creates semver-compliant pre-release versions: + * - `"alpha"` → `1.2.3 → 1.3.0-alpha.0` + * - `"beta"` → `1.2.3 → 1.3.0-beta.0` + * - `"rc"` → `1.2.3 → 1.3.0-rc.0` + * - Any custom tag → `1.2.3 → 1.3.0-{tag}.0` + * * # Examples * * ```typescript - * // Minimal params - preview all packages - * const minimal: BumpPreviewParams = { root: '.' }; + * // Minimal apply - just bump versions + * const minimal: BumpApplyParams = { root: '.' }; * - * // Preview specific packages with diff - * const filtered: BumpPreviewParams = { - * root: '/path/to/workspace', - * packages: ['@scope/pkg1', '@scope/pkg2'], - * showDiff: true + * // Full release with Git integration + * const release: BumpApplyParams = { + * root: '.', + * gitCommit: true, + * gitTag: true, + * gitPush: true, + * force: true + * }; + * + * // Beta prerelease + * const beta: BumpApplyParams = { + * root: '.', + * prerelease: 'beta', + * gitCommit: true, + * gitTag: true * }; * ``` */ -export interface BumpPreviewParams { +export interface BumpApplyParams { /** * Workspace root directory path. * * This is the absolute or relative path to the root of the workspace. - * For monorepos, this should point to the root where the package manager - * configuration is located. */ root: string /** * Optional custom configuration file path. * * If not provided, the command will search for configuration files - * in standard locations (`repo.config.json`, `repo.config.yaml`, etc.) - * within the workspace root. + * in standard locations within the workspace root. */ configPath?: string | undefined /** * Filter to specific packages. * - * When provided, only these packages will be included in the preview. - * Package names should include scope if applicable (e.g., `@scope/pkg`). + * When provided, only these packages will be bumped. + * Package names should include scope if applicable. */ packages?: string[] | undefined /** - * Whether to show detailed version diffs. + * Whether to create a Git commit with version changes. * - * When `true`, includes detailed information about what changes would - * be made to each package, including dependency updates. + * When `true`, creates a commit containing all modified files + * (package.json, CHANGELOG.md, etc.) with a conventional commit message. */ - showDiff?: boolean | undefined + gitCommit?: boolean | undefined + /** + * Whether to create Git tags for releases. + * + * When `true`, creates tags in the format `{package}@{version}` for + * each bumped package. Requires `git_commit` to be effective. + */ + gitTag?: boolean | undefined + /** + * Whether to push Git tags to remote. + * + * When `true`, pushes the created tags to the remote repository. + * Requires `git_tag` to be `true` to have any effect. + */ + gitPush?: boolean | undefined + /** + * Prerelease tag for pre-release versions. + * + * Creates semver-compliant pre-release versions. Common values: + * - `"alpha"`: Early development (`1.3.0-alpha.0`) + * - `"beta"`: Feature complete (`1.3.0-beta.0`) + * - `"rc"`: Release candidate (`1.3.0-rc.0`) + * + * Custom tags are also supported. Must contain only ASCII + * alphanumerics and hyphens `[0-9A-Za-z-]`. + */ + prerelease?: string | undefined + /** + * Whether to skip changelog generation. + * + * When `true`, CHANGELOG.md files will not be updated during the bump. + * Useful for quick internal releases or when changelogs are managed + * separately. + */ + noChangelog?: boolean | undefined + /** + * Whether to keep changesets active after bump. + * + * When `true`, changesets are not archived after version bump. + * Useful for partial releases or when you want to accumulate + * more changes before archiving. + */ + noArchive?: boolean | undefined + /** + * Whether to force archiving even for prerelease versions. + * + * By default, prerelease versions don't archive changesets (since + * the final release will archive them). Set this to `true` to + * archive changesets even for prerelease versions. + */ + alwaysArchive?: boolean | undefined + /** + * Whether to skip confirmation prompts. + * + * When `true`, applies changes without asking for confirmation. + * Recommended for CI/CD environments. The NAPI API defaults to + * `true` since it's typically used programmatically. + */ + force?: boolean | undefined } /** - * Generate snapshot versions for testing. - * - * Creates temporary, non-persisted versions for branch builds and preview - * deployments. Snapshot versions are NOT SemVer compliant and are intended - * for testing purposes only. + * Preview version bumps without applying changes. * - * **Key characteristics:** - * - Does NOT consume or archive changesets - * - Does NOT create Git commits or tags - * - Does NOT generate changelogs - * - Uses format templates with variables like `{version}`, `{branch}`, `{short_commit}` + * Returns comprehensive information about what versions would change based on + * pending changesets. This is a dry-run operation that does not modify any files. * - * This function is the main entry point for Node.js applications to generate - * snapshot versions. It handles all the complexity of CLI invocation and response + * This function is the main entry point for Node.js applications to preview + * version bumps. It handles all the complexity of CLI invocation and response * parsing internally. * - * @param params - Snapshot parameters containing: + * @param params - Preview parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path * - `packages`: Optional filter to specific packages - * - `format`: Snapshot format template (default: `{version}-snapshot.{short_commit}`) + * - `showDiff`: Whether to show detailed version diffs * - * @returns `Promise>` containing: - * - On success: `{ success: true, data: BumpSnapshotData }` + * @returns `Promise>` containing: + * - On success: `{ success: true, data: BumpPreviewData }` * - On failure: `{ success: false, error: ErrorInfo }` * - * @example Basic usage with default format + * @example Basic usage * ```typescript - * const result = await bumpSnapshot({ root: '/path/to/project' }); + * const result = await bumpPreview({ root: '/path/to/project' }); * if (result.success) { - * console.log(`Format used: ${result.data.format}`); + * console.log(`Strategy: ${result.data.strategy}`); + * console.log(`Packages to bump: ${result.data.packages.length}`); * for (const pkg of result.data.packages) { - * console.log(`${pkg.name}: ${pkg.originalVersion} -> ${pkg.snapshotVersion}`); + * console.log(` ${pkg.name}: ${pkg.currentVersion} -> ${pkg.nextVersion}`); * } * } else { * console.error(`Error: ${result.error.code} - ${result.error.message}`); * } * ``` * - * @example Custom format with branch and commit - * ```typescript - * const result = await bumpSnapshot({ - * root: '/path/to/project', - * format: '{version}-{branch}.{short_commit}' - * }); - * // Generates versions like: 1.2.3-feature-x.abc123f - * ``` - * - * @example Timestamp-based format - * ```typescript - * const result = await bumpSnapshot({ - * root: '/path/to/project', - * format: '{version}-dev.{timestamp}' - * }); - * // Generates versions like: 1.2.3-dev.1699876543 - * ``` - * - * @example Filter to specific packages + * @example With package filter and diff * ```typescript - * const result = await bumpSnapshot({ + * const result = await bumpPreview({ * root: '/path/to/project', * packages: ['@scope/core', '@scope/utils'], - * format: '{version}-snapshot.{short_commit}' + * showDiff: true * }); * ``` * * @example Error handling * ```typescript - * const result = await bumpSnapshot({ - * root: '/path/to/project', - * format: 'invalid-no-variables' - * }); + * const result = await bumpPreview({ root: '/nonexistent' }); * if (!result.success) { - * if (result.error.code === 'EVALIDATION') { - * console.error('Invalid format:', result.error.message); + * if (result.error.code === 'ENOENT') { + * console.error('Path not found'); + * } else if (result.error.code === 'EVALIDATION') { + * console.error('Invalid parameters'); * } * } * ``` */ -export declare function bumpSnapshot(params: BumpSnapshotParams): Promise +export declare function bumpPreview(params: BumpPreviewParams): Promise /** - * API response for the bump snapshot command. + * API response for the bump preview command. * - * This structure wraps `BumpSnapshotData` in the standard `ApiResponse` + * This structure wraps `BumpPreviewData` in the standard `ApiResponse` * format, providing a consistent interface for success and error cases. * * # TypeScript Definition * * ```typescript - * interface BumpSnapshotApiResponse { + * interface BumpPreviewApiResponse { * success: boolean; - * data?: BumpSnapshotData; + * data?: BumpPreviewData; * error?: ErrorInfo; * } * ``` @@ -961,30 +1171,25 @@ export declare function bumpSnapshot(params: BumpSnapshotParams): Promise + packages: Array /** - * The format template that was used. + * Summary statistics of the bump. * - * This is either the user-provided format or the default format - * `{version}-snapshot.{short_commit}`. + * Aggregated counts of total packages and bump types. */ - format: string + summary: BumpSummaryInfo + /** + * IDs of changesets that will be consumed. + * + * These changesets will be archived after the bump is applied. + */ + changesets: Array } /** - * Input parameters for the bump snapshot command. + * Input parameters for the bump preview command. * - * This structure defines the parameters for generating snapshot versions. - * Snapshots are temporary, non-persisted versions used for testing and - * CI/CD preview deployments. Unlike regular bumps, snapshots don't archive - * changesets or create changelogs. + * This structure defines the parameters for previewing version bumps based on + * pending changesets. The preview is a dry-run operation that shows what would + * change without actually modifying any files. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file * - `packages`: Optional filter to specific packages - * - `format`: Snapshot version format template - * - * # Format Template Variables - * - * The `format` parameter supports these variables: - * - `{version}`: Current package version (e.g., `1.2.3`) - * - `{branch}`: Current Git branch name (sanitized, e.g., `feature-x`) - * - `{short_commit}`: Short Git commit hash (7 chars, e.g., `abc123f`) - * - `{commit}`: Full Git commit hash - * - `{timestamp}`: Unix timestamp - * - * Default format: `{version}-snapshot.{short_commit}` + * - `show_diff`: Whether to show detailed version diffs * * # TypeScript Definition * * ```typescript - * interface BumpSnapshotParams { + * interface BumpPreviewParams { * root: string; * configPath?: string; * packages?: string[]; - * format?: string; + * showDiff?: boolean; * } * ``` * - * # Snapshot vs Prerelease - * - * | Aspect | Snapshot | Prerelease | - * |--------|----------|------------| - * | SemVer Compliant | No | Yes | - * | Persisted | No | Yes | - * | Changesets Archived | No | Optional | - * | Use Case | Testing/CI | Staging/Beta | - * | Example | `1.2.3-snapshot.abc123f` | `1.3.0-beta.0` | - * * # Examples * * ```typescript - * // Default format - * const basic: BumpSnapshotParams = { root: '.' }; - * - * // Custom format with branch - * const withBranch: BumpSnapshotParams = { - * root: '.', - * format: '{version}-{branch}.{short_commit}' - * }; + * // Minimal params - preview all packages + * const minimal: BumpPreviewParams = { root: '.' }; * - * // Timestamp-based - * const timestamped: BumpSnapshotParams = { - * root: '.', - * format: '{version}-dev.{timestamp}' + * // Preview specific packages with diff + * const filtered: BumpPreviewParams = { + * root: '/path/to/workspace', + * packages: ['@scope/pkg1', '@scope/pkg2'], + * showDiff: true * }; * ``` */ -export interface BumpSnapshotParams { +export interface BumpPreviewParams { /** * Workspace root directory path. * * This is the absolute or relative path to the root of the workspace. + * For monorepos, this should point to the root where the package manager + * configuration is located. */ root: string /** * Optional custom configuration file path. * * If not provided, the command will search for configuration files - * in standard locations within the workspace root. + * in standard locations (`repo.config.json`, `repo.config.yaml`, etc.) + * within the workspace root. */ configPath?: string | undefined /** * Filter to specific packages. * - * When provided, only these packages will get snapshot versions. - * Package names should include scope if applicable. + * When provided, only these packages will be included in the preview. + * Package names should include scope if applicable (e.g., `@scope/pkg`). */ packages?: string[] | undefined /** - * Snapshot version format template. - * - * Supports the following variables: - * - `{version}`: Current package version - * - `{branch}`: Current Git branch (sanitized) - * - `{short_commit}`: Short Git commit hash (7 chars) - * - `{commit}`: Full Git commit hash - * - `{timestamp}`: Unix timestamp - * - * Default: `{version}-snapshot.{short_commit}` + * Whether to show detailed version diffs. * - * Example: `{version}-{branch}.{short_commit}` → - * `1.2.3-feature-x.abc123f` + * When `true`, includes detailed information about what changes would + * be made to each package, including dependency updates. */ - format?: string | undefined + showDiff?: boolean | undefined } /** - * Summary information for a bump operation. + * Generate snapshot versions for testing. * - * This structure provides aggregated statistics about the version - * bumps that were previewed or applied. + * Creates temporary, non-persisted versions for branch builds and preview + * deployments. Snapshot versions are NOT SemVer compliant and are intended + * for testing purposes only. * - * # Fields + * **Key characteristics:** + * - Does NOT consume or archive changesets + * - Does NOT create Git commits or tags + * - Does NOT generate changelogs + * - Uses format templates with variables like `{version}`, `{branch}`, `{short_commit}` * - * - `total_packages`: Total number of packages affected - * - `major_bumps`: Number of major version bumps - * - `minor_bumps`: Number of minor version bumps - * - `patch_bumps`: Number of patch version bumps + * This function is the main entry point for Node.js applications to generate + * snapshot versions. It handles all the complexity of CLI invocation and response + * parsing internally. * - * # TypeScript Definition + * @param params - Snapshot parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * - `packages`: Optional filter to specific packages + * - `format`: Snapshot format template (default: `{version}-snapshot.{short_commit}`) + * + * @returns `Promise>` containing: + * - On success: `{ success: true, data: BumpSnapshotData }` + * - On failure: `{ success: false, error: ErrorInfo }` * + * @example Basic usage with default format * ```typescript - * interface BumpSummaryInfo { - * totalPackages: number; - * majorBumps: number; - * minorBumps: number; - * patchBumps: number; + * const result = await bumpSnapshot({ root: '/path/to/project' }); + * if (result.success) { + * console.log(`Format used: ${result.data.format}`); + * for (const pkg of result.data.packages) { + * console.log(`${pkg.name}: ${pkg.originalVersion} -> ${pkg.snapshotVersion}`); + * } + * } else { + * console.error(`Error: ${result.error.code} - ${result.error.message}`); * } * ``` * - * # Examples + * @example Custom format with branch and commit + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: '{version}-{branch}.{short_commit}' + * }); + * // Generates versions like: 1.2.3-feature-x.abc123f + * ``` + * + * @example Timestamp-based format + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: '{version}-dev.{timestamp}' + * }); + * // Generates versions like: 1.2.3-dev.1699876543 + * ``` * + * @example Filter to specific packages * ```typescript - * const summary: BumpSummaryInfo = { - * totalPackages: 5, - * majorBumps: 1, - * minorBumps: 3, - * patchBumps: 1 - * }; + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * packages: ['@scope/core', '@scope/utils'], + * format: '{version}-snapshot.{short_commit}' + * }); + * ``` + * + * @example Error handling + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: 'invalid-no-variables' + * }); + * if (!result.success) { + * if (result.error.code === 'EVALIDATION') { + * console.error('Invalid format:', result.error.message); + * } + * } * ``` */ -export interface BumpSummaryInfo { - /** Total number of packages affected by the bump. */ - totalPackages: number - /** Number of major version bumps. */ - majorBumps: number - /** Number of minor version bumps. */ - minorBumps: number - /** Number of patch version bumps. */ - patchBumps: number -} +export declare function bumpSnapshot(params: BumpSnapshotParams): Promise /** - * Changelog configuration information. + * API response for the bump snapshot command. * - * Contains settings for changelog generation. + * This structure wraps `BumpSnapshotData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. * - * # Fields + * # TypeScript Definition * - * - `enabled`: Whether changelog generation is enabled - * - `format`: Changelog format ("keep-a-changelog", "conventional-commits", "custom") - * - `include_commit_links`: Whether to include commit links - * - `repository_url`: Repository URL for generating links - * - `conventional`: Whether to use conventional commits parsing - * - `template`: Custom template path - * - `exclude`: Patterns to exclude from changelog - * - `monorepo_mode`: How to handle changelogs in monorepos + * ```typescript + * interface BumpSnapshotApiResponse { + * success: boolean; + * data?: BumpSnapshotData; + * error?: ErrorInfo; + * } + * ``` * - * # TypeScript Definition + * # Examples * * ```typescript - * interface ChangelogConfigInfo { - * // Whether changelog generation is enabled - * enabled: boolean; - * // Changelog format: "keep-a-changelog", "conventional-commits", or "custom" - * format: string; - * // Whether to include commit links - * includeCommitLinks: boolean; - * // Repository URL for generating links - * repositoryUrl?: string; - * // Whether to use conventional commits parsing - * conventional: boolean; - * // Custom template path - * template?: string; - * // Patterns to exclude from changelog - * exclude: string[]; - * // How to handle changelogs in monorepos: "per-package", "root", or "both" - * monorepoMode: string; + * const result = await bumpSnapshot({ + * root: '.', + * format: '{version}-{branch}.{short_commit}' + * }); + * + * if (result.success) { + * for (const pkg of result.data.packages) { + * console.log(`${pkg.name}: ${pkg.snapshotVersion}`); + * } + * } else { + * console.error(`Error: ${result.error.message}`); + * } + * ``` + */ +export interface BumpSnapshotApiResponse { + /** Whether the operation was successful. */ + success: boolean + /** + * The snapshot result data if successful. + * + * Contains the list of packages with their generated + * snapshot versions. + */ + data?: BumpSnapshotData | undefined + /** + * Error information if the operation failed. + * + * Contains the error code, message, and context when the + * operation fails. + */ + error?: ErrorInfo | undefined +} + +/** + * Response data for the bump snapshot command. + * + * This structure contains the results of generating snapshot versions, + * including the list of packages with their snapshot versions and the + * format template that was used. + * + * # Fields + * + * - `strategy`: Version strategy used (independent or unified) + * - `packages`: List of packages with snapshot versions + * - `format`: The format template that was used + * + * # TypeScript Definition + * + * ```typescript + * interface BumpSnapshotData { + * strategy: 'independent' | 'unified'; + * packages: SnapshotVersionInfo[]; + * format: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const snapshot: BumpSnapshotData = { + * strategy: 'independent', + * packages: [ + * { + * name: '@scope/core', + * path: 'packages/core', + * originalVersion: '1.0.0', + * snapshotVersion: '1.0.0-snapshot.abc123f' + * } + * ], + * format: '{version}-snapshot.{short_commit}' + * }; + * ``` + */ +export interface BumpSnapshotData { + /** + * Version strategy used. + * + * Either `"independent"` or `"unified"`. + */ + strategy: string + /** + * List of packages with snapshot versions. + * + * Contains the original and generated snapshot version for each package. + */ + packages: Array + /** + * The format template that was used. + * + * This is either the user-provided format or the default format + * `{version}-snapshot.{short_commit}`. + */ + format: string +} + +/** + * Input parameters for the bump snapshot command. + * + * This structure defines the parameters for generating snapshot versions. + * Snapshots are temporary, non-persisted versions used for testing and + * CI/CD preview deployments. Unlike regular bumps, snapshots don't archive + * changesets or create changelogs. + * + * # Fields + * + * - `root`: The workspace root directory path (required) + * - `config_path`: Optional path to a custom configuration file + * - `packages`: Optional filter to specific packages + * - `format`: Snapshot version format template + * + * # Format Template Variables + * + * The `format` parameter supports these variables: + * - `{version}`: Current package version (e.g., `1.2.3`) + * - `{branch}`: Current Git branch name (sanitized, e.g., `feature-x`) + * - `{short_commit}`: Short Git commit hash (7 chars, e.g., `abc123f`) + * - `{commit}`: Full Git commit hash + * - `{timestamp}`: Unix timestamp + * + * Default format: `{version}-snapshot.{short_commit}` + * + * # TypeScript Definition + * + * ```typescript + * interface BumpSnapshotParams { + * root: string; + * configPath?: string; + * packages?: string[]; + * format?: string; + * } + * ``` + * + * # Snapshot vs Prerelease + * + * | Aspect | Snapshot | Prerelease | + * |--------|----------|------------| + * | SemVer Compliant | No | Yes | + * | Persisted | No | Yes | + * | Changesets Archived | No | Optional | + * | Use Case | Testing/CI | Staging/Beta | + * | Example | `1.2.3-snapshot.abc123f` | `1.3.0-beta.0` | + * + * # Examples + * + * ```typescript + * // Default format + * const basic: BumpSnapshotParams = { root: '.' }; + * + * // Custom format with branch + * const withBranch: BumpSnapshotParams = { + * root: '.', + * format: '{version}-{branch}.{short_commit}' + * }; + * + * // Timestamp-based + * const timestamped: BumpSnapshotParams = { + * root: '.', + * format: '{version}-dev.{timestamp}' + * }; + * ``` + */ +export interface BumpSnapshotParams { + /** + * Workspace root directory path. + * + * This is the absolute or relative path to the root of the workspace. + */ + root: string + /** + * Optional custom configuration file path. + * + * If not provided, the command will search for configuration files + * in standard locations within the workspace root. + */ + configPath?: string | undefined + /** + * Filter to specific packages. + * + * When provided, only these packages will get snapshot versions. + * Package names should include scope if applicable. + */ + packages?: string[] | undefined + /** + * Snapshot version format template. + * + * Supports the following variables: + * - `{version}`: Current package version + * - `{branch}`: Current Git branch (sanitized) + * - `{short_commit}`: Short Git commit hash (7 chars) + * - `{commit}`: Full Git commit hash + * - `{timestamp}`: Unix timestamp + * + * Default: `{version}-snapshot.{short_commit}` + * + * Example: `{version}-{branch}.{short_commit}` → + * `1.2.3-feature-x.abc123f` + */ + format?: string | undefined +} + +/** + * Summary information for a bump operation. + * + * This structure provides aggregated statistics about the version + * bumps that were previewed or applied. + * + * # Fields + * + * - `total_packages`: Total number of packages affected + * - `major_bumps`: Number of major version bumps + * - `minor_bumps`: Number of minor version bumps + * - `patch_bumps`: Number of patch version bumps + * + * # TypeScript Definition + * + * ```typescript + * interface BumpSummaryInfo { + * totalPackages: number; + * majorBumps: number; + * minorBumps: number; + * patchBumps: number; + * } + * ``` + * + * # Examples + * + * ```typescript + * const summary: BumpSummaryInfo = { + * totalPackages: 5, + * majorBumps: 1, + * minorBumps: 3, + * patchBumps: 1 + * }; + * ``` + */ +export interface BumpSummaryInfo { + /** Total number of packages affected by the bump. */ + totalPackages: number + /** Number of major version bumps. */ + majorBumps: number + /** Number of minor version bumps. */ + minorBumps: number + /** Number of patch version bumps. */ + patchBumps: number +} + +/** + * Changelog configuration information. + * + * Contains settings for changelog generation. + * + * # Fields + * + * - `enabled`: Whether changelog generation is enabled + * - `format`: Changelog format ("keep-a-changelog", "conventional-commits", "custom") + * - `include_commit_links`: Whether to include commit links + * - `repository_url`: Repository URL for generating links + * - `conventional`: Whether to use conventional commits parsing + * - `template`: Custom template path + * - `exclude`: Patterns to exclude from changelog + * - `monorepo_mode`: How to handle changelogs in monorepos + * + * # TypeScript Definition + * + * ```typescript + * interface ChangelogConfigInfo { + * // Whether changelog generation is enabled + * enabled: boolean; + * // Changelog format: "keep-a-changelog", "conventional-commits", or "custom" + * format: string; + * // Whether to include commit links + * includeCommitLinks: boolean; + * // Repository URL for generating links + * repositoryUrl?: string; + * // Whether to use conventional commits parsing + * conventional: boolean; + * // Custom template path + * template?: string; + * // Patterns to exclude from changelog + * exclude: string[]; + * // How to handle changelogs in monorepos: "per-package", "root", or "both" + * monorepoMode: string; * } * ``` */ export interface ChangelogConfigInfo { /** - * Whether changelog generation is enabled. + * Whether changelog generation is enabled. + * + * If `false`, no changelog files are generated or updated. + */ + enabled: boolean + /** + * Changelog format. + * + * The format to use for changelog entries: + * - `"keep-a-changelog"`: Keep a Changelog format + * - `"conventional-commits"`: Conventional Commits format + * - `"custom"`: Custom template-based format + */ + format: string + /** + * Whether to include commit links. + * + * If `true`, changelog entries include links to the relevant commits. + */ + includeCommitLinks: boolean + /** + * Repository URL for generating links. + * + * Used to generate links to commits, comparisons, and issues + * in the changelog. Example: "https://github.com/org/repo". + */ + repositoryUrl?: string | undefined + /** + * Whether to use conventional commits parsing. + * + * If `true`, commit messages are parsed using conventional commits + * specification to categorize changes. + */ + conventional: boolean + /** + * Custom template path. + * + * Path to a custom template file for changelog generation. + * Only used when `format` is `"custom"`. + */ + template?: string | undefined + /** + * Patterns to exclude from changelog. + * + * Commit messages or files matching these patterns are excluded + * from changelog generation. + */ + exclude: Array + /** + * How to handle changelogs in monorepos. + * + * Determines where changelog files are created: + * - `"per-package"`: Each package has its own CHANGELOG.md + * - `"root"`: Single CHANGELOG.md at the repository root + * - `"both"`: Both per-package and root changelogs + */ + monorepoMode: string +} + +/** + * Add a new changeset to the workspace. + * + * Creates a new changeset for the current or specified branch. The changeset + * records which packages are affected, the version bump type, and optionally + * a message describing the changes. + * + * This function always operates in non-interactive mode. If packages are not + * specified, they will be auto-detected from git changes. If bump type is not + * specified, an error will be returned (unlike the CLI which would prompt). + * + * @param params - Changeset add parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * - `bump`: Version bump type (major, minor, patch) + * - `environments`: Optional list of target environments + * - `branch`: Optional branch name (defaults to current git branch) + * - `message`: Optional message describing the changes + * - `packages`: Optional list of packages (auto-detected if not provided) + * - `force`: Optional flag to overwrite existing changeset + * + * @returns `Promise` containing: + * - On success: `{ success: true, data: ChangesetAddData }` + * - On failure: `{ success: false, error: ErrorInfo }` + * + * @example Basic usage with auto-detected packages + * ```typescript + * const result = await changesetAdd({ + * root: '/path/to/workspace', + * bump: 'minor', + * message: 'Add new API endpoints' + * }); + * + * if (result.success) { + * console.log(`Created changeset: ${result.data.id}`); + * console.log(`Packages: ${result.data.packages.join(', ')}`); + * } + * ``` + * + * @example With explicit packages + * ```typescript + * const result = await changesetAdd({ + * root: '/path/to/workspace', + * packages: ['@scope/core', '@scope/utils'], + * bump: 'major', + * message: 'Breaking API changes', + * environments: ['staging', 'production'] + * }); + * ``` + * + * @example Force overwrite existing changeset + * ```typescript + * const result = await changesetAdd({ + * root: '/path/to/workspace', + * packages: ['my-package'], + * bump: 'patch', + * force: true + * }); + * ``` + * + * @example Error handling + * ```typescript + * const result = await changesetAdd({ + * root: '/nonexistent/path', + * bump: 'minor' + * }); + * + * if (!result.success) { + * switch (result.error.code) { + * case 'ENOENT': + * console.error('Path not found'); + * break; + * case 'EVALIDATION': + * console.error('Invalid parameters:', result.error.message); + * break; + * case 'EGIT': + * console.error('Git error:', result.error.message); + * break; + * default: + * console.error(`Error: ${result.error.message}`); + * } + * } + * ``` + */ +export declare function changesetAdd(params: ChangesetAddParams): Promise + +/** + * API response for the changeset add command. + * + * Wraps `ChangesetAddData` with success/error handling. + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetAddApiResponse { + * success: boolean; + * data?: ChangesetAddData; + * error?: ErrorInfo; + * } + * ``` + */ +export interface ChangesetAddApiResponse { + /** Whether the operation succeeded. */ + success: boolean + /** The add data (only present when `success` is `true`). */ + data?: ChangesetAddData | undefined + /** Error information (only present when `success` is `false`). */ + error?: ErrorInfo | undefined +} + +/** + * Response data for the changeset add command. + * + * Contains information about the newly created changeset. + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetAddData { + * id: string; + * branch: string; + * packages: string[]; + * bump: string; + * environments: string[]; + * createdAt: string; + * } + * ``` + */ +export interface ChangesetAddData { + /** Unique changeset identifier. */ + id: string + /** Git branch name. */ + branch: string + /** List of affected packages. */ + packages: Array + /** Version bump type. */ + bump: string + /** Target environments. */ + environments: Array + /** Creation timestamp (ISO 8601 format). */ + createdAt: string +} + +/** + * Input parameters for the changeset add command. + * + * This structure defines the parameters for creating a new changeset. + * The root path is required; all other parameters are optional and will + * use sensible defaults or auto-detection when not provided. + * + * # Fields + * + * - `root`: The workspace root directory path (required) + * - `config_path`: Optional path to a custom configuration file + * - `bump`: The bump type (major, minor, patch) + * - `environments`: List of environments for the changeset + * - `branch`: Branch name (defaults to current Git branch) + * - `message`: Optional description of the changes + * - `packages`: List of packages to include (auto-detected if not provided) + * - `force`: Overwrite existing changeset if one exists + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetAddParams { + * root: string; + * configPath?: string; + * bump?: 'major' | 'minor' | 'patch'; + * environments?: string[]; + * branch?: string; + * message?: string; + * packages?: string[]; + * force?: boolean; + * } + * ``` + * + * # Examples + * + * ```typescript + * // Minimal params - will auto-detect packages and use current branch + * const minimal: ChangesetAddParams = { root: '.' }; + * + * // Full params with all options + * const full: ChangesetAddParams = { + * root: '/path/to/workspace', + * configPath: '/path/to/repo.config.json', + * bump: 'minor', + * environments: ['staging', 'production'], + * branch: 'feature/new-api', + * message: 'Add new REST API endpoints', + * packages: ['@scope/api', '@scope/client'], + * force: true + * }; + * ``` + */ +export interface ChangesetAddParams { + /** + * Workspace root directory path. * - * If `false`, no changelog files are generated or updated. + * This is the absolute or relative path to the root of the workspace. + * For monorepos, this should point to the root where the package manager + * configuration (e.g., `pnpm-workspace.yaml`) is located. */ - enabled: boolean + root: string /** - * Changelog format. + * Optional custom configuration file path. * - * The format to use for changelog entries: - * - `"keep-a-changelog"`: Keep a Changelog format - * - `"conventional-commits"`: Conventional Commits format - * - `"custom"`: Custom template-based format + * If not provided, the command will search for configuration files + * in standard locations (`repo.config.json`, `repo.config.yaml`, etc.) + * within the workspace root. */ - format: string + configPath?: string | undefined /** - * Whether to include commit links. + * Bump type for version changes. * - * If `true`, changelog entries include links to the relevant commits. + * Specifies how the version should be bumped for the affected packages. + * Valid values: `"major"`, `"minor"`, `"patch"`. + * + * If not provided, the command will prompt for selection in interactive + * mode or use the default from configuration. */ - includeCommitLinks: boolean + bump?: string | undefined /** - * Repository URL for generating links. + * List of environments for the changeset. * - * Used to generate links to commits, comparisons, and issues - * in the changelog. Example: "https://github.com/org/repo". + * Environments allow targeting specific release channels (e.g., staging, + * production). If not provided, defaults from configuration are used. + * + * Example: `["staging", "production"]` */ - repositoryUrl?: string | undefined + environments?: string[] | undefined /** - * Whether to use conventional commits parsing. + * Branch name for the changeset. * - * If `true`, commit messages are parsed using conventional commits - * specification to categorize changes. + * If not provided, the current Git branch is used. The branch name + * is used to derive the changeset ID. */ - conventional: boolean + branch?: string | undefined /** - * Custom template path. + * Optional message describing the changes. * - * Path to a custom template file for changelog generation. - * Only used when `format` is `"custom"`. + * A human-readable description of what changes are included in this + * changeset. This message may be included in changelogs. */ - template?: string | undefined + message?: string | undefined /** - * Patterns to exclude from changelog. + * List of packages to include in the changeset. * - * Commit messages or files matching these patterns are excluded - * from changelog generation. + * Package names should match exactly as defined in each package's + * `package.json`. If not provided, packages are auto-detected from + * Git changes. + * + * Example: `["@scope/core", "@scope/utils"]` */ - exclude: Array + packages?: string[] | undefined /** - * How to handle changelogs in monorepos. + * Force overwrite of existing changeset. * - * Determines where changelog files are created: - * - `"per-package"`: Each package has its own CHANGELOG.md - * - `"root"`: Single CHANGELOG.md at the repository root - * - `"both"`: Both per-package and root changelogs + * If `true`, any existing changeset for the branch will be replaced. + * If `false` (default), an error is returned if a changeset already exists. */ - monorepoMode: string + force?: boolean | undefined } /** - * Add a new changeset to the workspace. - * - * Creates a new changeset for the current or specified branch. The changeset - * records which packages are affected, the version bump type, and optionally - * a message describing the changes. + * Check if a changeset exists for a branch. * - * This function always operates in non-interactive mode. If packages are not - * specified, they will be auto-detected from git changes. If bump type is not - * specified, an error will be returned (unlike the CLI which would prompt). + * Verifies whether a changeset exists for the current or specified branch. + * This command is designed for use in Git hooks and CI/CD pipelines to + * enforce changeset creation policies. * - * @param params - Changeset add parameters containing: + * @param params - Changeset check parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path - * - `bump`: Version bump type (major, minor, patch) - * - `environments`: Optional list of target environments - * - `branch`: Optional branch name (defaults to current git branch) - * - `message`: Optional message describing the changes - * - `packages`: Optional list of packages (auto-detected if not provided) - * - `force`: Optional flag to overwrite existing changeset + * - `branch`: Branch name to check (optional, defaults to current Git branch) * - * @returns `Promise` containing: - * - On success: `{ success: true, data: ChangesetAddData }` - * - On failure: `{ success: false, error: ErrorInfo }` + * @returns `Promise` - Response containing: + * - `success`: Whether the operation succeeded + * - `data`: Check result if successful + * - `error`: Error information if failed * - * @example Basic usage with auto-detected packages + * ## Success Response + * + * When successful, `data` contains: + * - `hasChangeset`: Boolean indicating if a changeset exists + * - `branch`: The branch name that was checked (when changeset exists) + * - `packages`: List of packages in the changeset (when available) + * + * ## Error Codes + * + * - `EVALIDATION`: Invalid parameters (empty root) + * - `ENOENT`: Path not found + * - `ECONFIG`: Workspace not initialized + * - `EEXECUTION`: CLI command failed + * + * ## Git Hook Integration + * + * This command is particularly useful in Git hooks: + * - Pre-push hooks to ensure changesets are created + * - Pre-merge hooks to validate release requirements + * - CI/CD pipelines for pull request validation + * + * The response indicates whether a changeset exists, making it easy to + * implement branch protection rules that require changesets. + * + * @example Basic usage - check current branch * ```typescript - * const result = await changesetAdd({ - * root: '/path/to/workspace', - * bump: 'minor', - * message: 'Add new API endpoints' + * const result = await changesetCheck({ + * root: '/path/to/workspace' * }); * * if (result.success) { - * console.log(`Created changeset: ${result.data.id}`); - * console.log(`Packages: ${result.data.packages.join(', ')}`); + * if (result.data.hasChangeset) { + * console.log(`Changeset exists for branch: ${result.data.branch}`); + * } else { + * console.log('No changeset found for current branch'); + * } * } * ``` * - * @example With explicit packages + * @example Check specific branch * ```typescript - * const result = await changesetAdd({ + * const result = await changesetCheck({ * root: '/path/to/workspace', - * packages: ['@scope/core', '@scope/utils'], - * bump: 'major', - * message: 'Breaking API changes', - * environments: ['staging', 'production'] + * branch: 'feature/new-api' * }); + * + * if (result.success && result.data.hasChangeset) { + * console.log('✓ Changeset exists, ready to merge'); + * } else if (result.success && !result.data.hasChangeset) { + * console.log('✗ No changeset found, please create one'); + * process.exit(1); + * } * ``` * - * @example Force overwrite existing changeset + * @example Git pre-push hook * ```typescript - * const result = await changesetAdd({ + * const result = await changesetCheck({ root: '.' }); + * + * if (!result.success) { + * console.error(`Error: ${result.error.message}`); + * process.exit(1); + * } + * + * if (!result.data.hasChangeset) { + * console.error('Push rejected: No changeset found for this branch.'); + * console.error('Run "workspace changeset add" to create a changeset.'); + * process.exit(1); + * } + * + * console.log('Changeset verified, proceeding with push.'); + * ``` + * + * @example With custom config + * ```typescript + * const result = await changesetCheck({ * root: '/path/to/workspace', - * packages: ['my-package'], - * bump: 'patch', - * force: true + * configPath: '/path/to/custom.config.json', + * branch: 'feature/auth-system' * }); * ``` * * @example Error handling * ```typescript - * const result = await changesetAdd({ - * root: '/nonexistent/path', - * bump: 'minor' + * const result = await changesetCheck({ + * root: '/path/to/workspace', + * branch: 'feature/my-branch' * }); * * if (!result.success) { * switch (result.error.code) { * case 'ENOENT': - * console.error('Path not found'); + * console.error('Workspace path not found'); + * break; + * case 'ECONFIG': + * console.error('Workspace not initialized. Run "workspace init" first.'); * break; * case 'EVALIDATION': * console.error('Invalid parameters:', result.error.message); * break; - * case 'EGIT': - * console.error('Git error:', result.error.message); - * break; * default: * console.error(`Error: ${result.error.message}`); * } * } * ``` */ -export declare function changesetAdd(params: ChangesetAddParams): Promise +export declare function changesetCheck(params: ChangesetCheckParams): Promise /** - * API response for the changeset add command. + * API response for the changeset check command. * - * Wraps `ChangesetAddData` with success/error handling. + * Wraps `ChangesetCheckData` with success/error handling. * * # TypeScript Definition * * ```typescript - * interface ChangesetAddApiResponse { + * interface ChangesetCheckApiResponse { * success: boolean; - * data?: ChangesetAddData; + * data?: ChangesetCheckData; * error?: ErrorInfo; * } * ``` */ -export interface ChangesetAddApiResponse { +export interface ChangesetCheckApiResponse { /** Whether the operation succeeded. */ success: boolean - /** The add data (only present when `success` is `true`). */ - data?: ChangesetAddData | undefined + /** The check data (only present when `success` is `true`). */ + data?: ChangesetCheckData | undefined /** Error information (only present when `success` is `false`). */ error?: ErrorInfo | undefined } /** - * Response data for the changeset add command. + * Response data for the changeset check command. * - * Contains information about the newly created changeset. + * Contains the result of the changeset existence check. This matches + * the CLI's `ChangesetCheckResponse` which returns `exists`, `branch`, + * and an optional `message`. * * # TypeScript Definition * * ```typescript - * interface ChangesetAddData { - * id: string; - * branch: string; - * packages: string[]; - * bump: string; - * environments: string[]; - * createdAt: string; + * interface ChangesetCheckData { + * hasChangeset: boolean; + * branch?: string; * } * ``` */ -export interface ChangesetAddData { - /** Unique changeset identifier. */ - id: string - /** Git branch name. */ - branch: string - /** List of affected packages. */ - packages: Array - /** Version bump type. */ - bump: string - /** Target environments. */ - environments: Array - /** Creation timestamp (ISO 8601 format). */ - createdAt: string +export interface ChangesetCheckData { + /** Whether a changeset exists for the branch. */ + hasChangeset: boolean + /** + * Branch name that was checked. + * + * Present when a changeset exists. + */ + branch?: string | undefined } /** - * Input parameters for the changeset add command. + * Input parameters for the changeset check command. * - * This structure defines the parameters for creating a new changeset. - * The root path is required; all other parameters are optional and will - * use sensible defaults or auto-detection when not provided. + * This structure defines the parameters for checking if a changeset exists + * for a specific branch. Useful for Git hooks to enforce changeset creation. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `bump`: The bump type (major, minor, patch) - * - `environments`: List of environments for the changeset - * - `branch`: Branch name (defaults to current Git branch) - * - `message`: Optional description of the changes - * - `packages`: List of packages to include (auto-detected if not provided) - * - `force`: Overwrite existing changeset if one exists + * - `branch`: Branch name to check (defaults to current Git branch) * * # TypeScript Definition * * ```typescript - * interface ChangesetAddParams { + * interface ChangesetCheckParams { * root: string; * configPath?: string; - * bump?: 'major' | 'minor' | 'patch'; - * environments?: string[]; * branch?: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * // Check current branch + * const current: ChangesetCheckParams = { root: '.' }; + * + * // Check specific branch + * const specific: ChangesetCheckParams = { + * root: '.', + * branch: 'feature/new-api' + * }; + * ``` + */ +export interface ChangesetCheckParams { + /** Workspace root directory path. */ + root: string + /** Optional custom configuration file path. */ + configPath?: string | undefined + /** + * Branch name to check. + * + * If not provided, the current Git branch is used. + */ + branch?: string | undefined +} + +/** + * Changeset configuration information. + * + * Contains settings for changeset management, including paths and + * environment configuration. + * + * # Fields + * + * - `path`: Path to store active changesets + * - `history_path`: Path to store archived changesets + * - `available_environments`: List of valid environment names + * - `default_environments`: Default environments for new changesets + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetConfigInfo { + * // Path to store active changesets (default: ".changesets") + * path: string; + * // Path to store archived changesets + * historyPath: string; + * // List of valid environment names + * availableEnvironments: string[]; + * // Default environments for new changesets + * defaultEnvironments: string[]; + * } + * ``` + */ +export interface ChangesetConfigInfo { + /** + * Path to store active changesets. + * + * This is the directory where pending changeset files are stored. + * Default value is `.changesets`. + */ + path: string + /** + * Path to store archived changesets. + * + * This is the directory where consumed changeset files are moved + * after a version bump operation. Typically a subdirectory of `path`. + */ + historyPath: string + /** + * List of valid environment names. + * + * These are the environments that changesets can target. Common + * examples include "production", "staging", "development". + */ + availableEnvironments: Array + /** + * Default environments for new changesets. + * + * These environments are automatically assigned to new changesets + * if not explicitly specified. + */ + defaultEnvironments: Array +} + +/** + * Detailed changeset information. + * + * This structure contains the complete details of a changeset, including + * all packages, commits, environments, and timestamps. Used in list, show, + * and history responses. + * + * # Fields + * + * - `id`: Unique changeset identifier + * - `branch`: Git branch name + * - `bump`: Version bump type + * - `packages`: List of affected packages + * - `environments`: Target environments + * - `commits`: Associated commit hashes + * - `message`: Optional description + * - `created_at`: Creation timestamp (ISO 8601) + * - `updated_at`: Last update timestamp (ISO 8601) + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetDetailInfo { + * id: string; + * branch: string; + * bump: string; + * packages: string[]; + * environments: string[]; + * commits: string[]; * message?: string; - * packages?: string[]; - * force?: boolean; + * createdAt: string; + * updatedAt: string; * } * ``` - * - * # Examples - * - * ```typescript - * // Minimal params - will auto-detect packages and use current branch - * const minimal: ChangesetAddParams = { root: '.' }; - * - * // Full params with all options - * const full: ChangesetAddParams = { - * root: '/path/to/workspace', - * configPath: '/path/to/repo.config.json', - * bump: 'minor', - * environments: ['staging', 'production'], - * branch: 'feature/new-api', - * message: 'Add new REST API endpoints', - * packages: ['@scope/api', '@scope/client'], - * force: true - * }; - * ``` */ -export interface ChangesetAddParams { +export interface ChangesetDetailInfo { /** - * Workspace root directory path. + * Unique changeset identifier. * - * This is the absolute or relative path to the root of the workspace. - * For monorepos, this should point to the root where the package manager - * configuration (e.g., `pnpm-workspace.yaml`) is located. + * This ID is derived from the branch name and uniquely identifies + * the changeset within the workspace. */ - root: string + id: string /** - * Optional custom configuration file path. + * Git branch name. * - * If not provided, the command will search for configuration files - * in standard locations (`repo.config.json`, `repo.config.yaml`, etc.) - * within the workspace root. + * The full branch name associated with this changeset. */ - configPath?: string | undefined + branch: string /** - * Bump type for version changes. - * - * Specifies how the version should be bumped for the affected packages. - * Valid values: `"major"`, `"minor"`, `"patch"`. + * Version bump type. * - * If not provided, the command will prompt for selection in interactive - * mode or use the default from configuration. + * One of: `"major"`, `"minor"`, `"patch"`, `"none"`. */ - bump?: string | undefined + bump: string /** - * List of environments for the changeset. + * List of affected packages. * - * Environments allow targeting specific release channels (e.g., staging, - * production). If not provided, defaults from configuration are used. + * Package names exactly as defined in each package's `package.json`. + */ + packages: Array + /** + * Target environments. * - * Example: `["staging", "production"]` + * List of environments this changeset applies to. */ - environments?: string[] | undefined + environments: Array /** - * Branch name for the changeset. + * Associated commit hashes. * - * If not provided, the current Git branch is used. The branch name - * is used to derive the changeset ID. + * Git commit hashes that are part of this changeset. */ - branch?: string | undefined + commits: Array /** - * Optional message describing the changes. + * Optional description message. * - * A human-readable description of what changes are included in this - * changeset. This message may be included in changelogs. + * Human-readable description of the changes. */ message?: string | undefined /** - * List of packages to include in the changeset. - * - * Package names should match exactly as defined in each package's - * `package.json`. If not provided, packages are auto-detected from - * Git changes. + * Creation timestamp (ISO 8601 format). * - * Example: `["@scope/core", "@scope/utils"]` + * When the changeset was first created. + * Example: `"2024-01-15T10:30:00Z"` */ - packages?: string[] | undefined + createdAt: string /** - * Force overwrite of existing changeset. + * Last update timestamp (ISO 8601 format). * - * If `true`, any existing changeset for the branch will be replaced. - * If `false` (default), an error is returned if a changeset already exists. + * When the changeset was last modified. + * Example: `"2024-01-15T14:45:00Z"` */ - force?: boolean | undefined + updatedAt: string } /** - * Check if a changeset exists for a branch. + * Queries the changeset history with optional filtering. * - * Verifies whether a changeset exists for the current or specified branch. - * This command is designed for use in Git hooks and CI/CD pipelines to - * enforce changeset creation policies. + * This function queries archived changesets from the workspace history, + * supporting various filter options for package, environment, bump type, + * date range, and result limit. * - * @param params - Changeset check parameters containing: - * - `root`: Workspace root directory path (required) - * - `configPath`: Optional custom config file path - * - `branch`: Branch name to check (optional, defaults to current Git branch) + * # Parameters * - * @returns `Promise` - Response containing: - * - `success`: Whether the operation succeeded - * - `data`: Check result if successful - * - `error`: Error information if failed + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional path to custom configuration file + * - `filterPackage`: Filter by package name + * - `filterEnv`: Filter by environment + * - `filterBump`: Filter by bump type (major, minor, patch) + * - `since`: Start date filter (ISO 8601 format) + * - `until`: End date filter (ISO 8601 format) + * - `limit`: Maximum number of results + * + * # Returns + * + * An `ApiResponse` containing `ChangesetHistoryData` with the list of archived + * changesets matching the query, or an error if the operation fails. * * ## Success Response * - * When successful, `data` contains: - * - `hasChangeset`: Boolean indicating if a changeset exists - * - `branch`: The branch name that was checked (when changeset exists) - * - `packages`: List of packages in the changeset (when available) + * ```typescript + * { + * success: true, + * data: { + * archived: [ + * { + * changeset: { + * id: "feature/add-api", + * branch: "feature/add-api", + * bump: "minor", + * packages: ["@scope/core"], + * environments: ["production"], + * commits: ["abc123"], + * createdAt: "2024-01-15T10:30:00Z", + * updatedAt: "2024-01-15T14:45:00Z" + * }, + * releaseInfo: { + * releasedAt: "2024-01-16T10:00:00Z", + * releasedBy: "CI", + * releaseCommit: "def456", + * releasedVersions: [ + * { packageName: "@scope/core", version: "2.0.0" } + * ] + * } + * } + * ], + * count: 1 + * } + * } + * ``` * * ## Error Codes * - * - `EVALIDATION`: Invalid parameters (empty root) + * - `EVALIDATION`: Invalid parameters (empty root or invalid bump type) * - `ENOENT`: Path not found * - `ECONFIG`: Workspace not initialized * - `EEXECUTION`: CLI command failed * - * ## Git Hook Integration - * - * This command is particularly useful in Git hooks: - * - Pre-push hooks to ensure changesets are created - * - Pre-merge hooks to validate release requirements - * - CI/CD pipelines for pull request validation - * - * The response indicates whether a changeset exists, making it easy to - * implement branch protection rules that require changesets. - * - * @example Basic usage - check current branch + * @example Basic usage - get all history * ```typescript - * const result = await changesetCheck({ + * const result = await changesetHistory({ * root: '/path/to/workspace' * }); * * if (result.success) { - * if (result.data.hasChangeset) { - * console.log(`Changeset exists for branch: ${result.data.branch}`); - * } else { - * console.log('No changeset found for current branch'); + * console.log(`Found ${result.data.count} archived changesets`); + * for (const item of result.data.archived) { + * console.log(`- ${item.changeset.branch}: ${item.changeset.bump}`); + * console.log(` Released: ${item.releaseInfo.releasedAt}`); * } * } * ``` * - * @example Check specific branch + * @example Filter by package * ```typescript - * const result = await changesetCheck({ + * const result = await changesetHistory({ * root: '/path/to/workspace', - * branch: 'feature/new-api' + * filterPackage: '@scope/core' * }); * - * if (result.success && result.data.hasChangeset) { - * console.log('✓ Changeset exists, ready to merge'); - * } else if (result.success && !result.data.hasChangeset) { - * console.log('✗ No changeset found, please create one'); - * process.exit(1); + * if (result.success) { + * console.log(`Releases for @scope/core: ${result.data.count}`); * } * ``` * - * @example Git pre-push hook + * @example Filter by date range * ```typescript - * const result = await changesetCheck({ root: '.' }); + * const result = await changesetHistory({ + * root: '/path/to/workspace', + * since: '2024-01-01', + * until: '2024-12-31', + * limit: 10 + * }); + * ``` * - * if (!result.success) { - * console.error(`Error: ${result.error.message}`); - * process.exit(1); - * } + * @example Filter by bump type + * ```typescript + * const result = await changesetHistory({ + * root: '/path/to/workspace', + * filterBump: 'major' + * }); * - * if (!result.data.hasChangeset) { - * console.error('Push rejected: No changeset found for this branch.'); - * console.error('Run "workspace changeset add" to create a changeset.'); - * process.exit(1); + * if (result.success) { + * console.log('Major releases:'); + * result.data.archived.forEach(item => { + * const versions = item.releaseInfo.releasedVersions + * .map(v => `${v.packageName}@${v.version}`) + * .join(', '); + * console.log(` ${item.changeset.branch}: ${versions}`); + * }); * } - * - * console.log('Changeset verified, proceeding with push.'); * ``` * - * @example With custom config + * @example Multiple filters * ```typescript - * const result = await changesetCheck({ + * const result = await changesetHistory({ * root: '/path/to/workspace', - * configPath: '/path/to/custom.config.json', - * branch: 'feature/auth-system' + * filterPackage: '@scope/core', + * filterEnv: 'production', + * filterBump: 'minor', + * since: '2024-06-01', + * limit: 5 * }); * ``` * * @example Error handling * ```typescript - * const result = await changesetCheck({ - * root: '/path/to/workspace', - * branch: 'feature/my-branch' + * const result = await changesetHistory({ + * root: '/nonexistent/path' * }); * * if (!result.success) { * switch (result.error.code) { * case 'ENOENT': - * console.error('Workspace path not found'); - * break; - * case 'ECONFIG': - * console.error('Workspace not initialized. Run "workspace init" first.'); + * console.error('Path not found'); * break; * case 'EVALIDATION': * console.error('Invalid parameters:', result.error.message); * break; + * case 'ECONFIG': + * console.error('Workspace not initialized'); + * break; * default: * console.error(`Error: ${result.error.message}`); * } * } * ``` */ -export declare function changesetCheck(params: ChangesetCheckParams): Promise +export declare function changesetHistory(params: ChangesetHistoryParams): Promise /** - * API response for the changeset check command. + * API response for the changeset history command. * - * Wraps `ChangesetCheckData` with success/error handling. + * Wraps `ChangesetHistoryData` with success/error handling. * * # TypeScript Definition * * ```typescript - * interface ChangesetCheckApiResponse { + * interface ChangesetHistoryApiResponse { * success: boolean; - * data?: ChangesetCheckData; + * data?: ChangesetHistoryData; * error?: ErrorInfo; * } * ``` */ -export interface ChangesetCheckApiResponse { +export interface ChangesetHistoryApiResponse { /** Whether the operation succeeded. */ success: boolean - /** The check data (only present when `success` is `true`). */ - data?: ChangesetCheckData | undefined + /** The history data (only present when `success` is `true`). */ + data?: ChangesetHistoryData | undefined /** Error information (only present when `success` is `false`). */ error?: ErrorInfo | undefined } /** - * Response data for the changeset check command. + * Response data for the changeset history command. * - * Contains the result of the changeset existence check. This matches - * the CLI's `ChangesetCheckResponse` which returns `exists`, `branch`, - * and an optional `message`. + * Contains archived changesets matching the query. * * # TypeScript Definition * * ```typescript - * interface ChangesetCheckData { - * hasChangeset: boolean; - * branch?: string; + * interface ChangesetHistoryData { + * archived: ArchivedChangesetInfo[]; + * count: number; * } * ``` */ -export interface ChangesetCheckData { - /** Whether a changeset exists for the branch. */ - hasChangeset: boolean - /** - * Branch name that was checked. - * - * Present when a changeset exists. - */ - branch?: string | undefined +export interface ChangesetHistoryData { + /** List of archived changesets. */ + archived: Array + /** Total count of results. */ + count: number } /** - * Input parameters for the changeset check command. + * Input parameters for the changeset history command. * - * This structure defines the parameters for checking if a changeset exists - * for a specific branch. Useful for Git hooks to enforce changeset creation. + * This structure defines the parameters for querying archived changesets. + * All filter parameters are optional; when omitted, all archived changesets + * are returned. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `branch`: Branch name to check (defaults to current Git branch) + * - `filter_package`: Filter by package name + * - `filter_env`: Filter by environment + * - `filter_bump`: Filter by bump type + * - `since`: Start date filter (ISO 8601 format) + * - `until`: End date filter (ISO 8601 format) + * - `limit`: Maximum number of results * * # TypeScript Definition * * ```typescript - * interface ChangesetCheckParams { + * interface ChangesetHistoryParams { * root: string; * configPath?: string; - * branch?: string; + * filterPackage?: string; + * filterEnv?: string; + * filterBump?: 'major' | 'minor' | 'patch'; + * since?: string; + * until?: string; + * limit?: number; * } * ``` * * # Examples * * ```typescript - * // Check current branch - * const current: ChangesetCheckParams = { root: '.' }; + * // Get all history + * const all: ChangesetHistoryParams = { root: '.' }; * - * // Check specific branch - * const specific: ChangesetCheckParams = { + * // Get recent major releases for a package + * const filtered: ChangesetHistoryParams = { * root: '.', - * branch: 'feature/new-api' + * filterPackage: '@scope/core', + * filterBump: 'major', + * since: '2024-01-01', + * limit: 10 * }; * ``` */ -export interface ChangesetCheckParams { +export interface ChangesetHistoryParams { /** Workspace root directory path. */ root: string /** Optional custom configuration file path. */ configPath?: string | undefined /** - * Branch name to check. - * - * If not provided, the current Git branch is used. - */ - branch?: string | undefined -} - -/** - * Changeset configuration information. - * - * Contains settings for changeset management, including paths and - * environment configuration. - * - * # Fields - * - * - `path`: Path to store active changesets - * - `history_path`: Path to store archived changesets - * - `available_environments`: List of valid environment names - * - `default_environments`: Default environments for new changesets - * - * # TypeScript Definition - * - * ```typescript - * interface ChangesetConfigInfo { - * // Path to store active changesets (default: ".changesets") - * path: string; - * // Path to store archived changesets - * historyPath: string; - * // List of valid environment names - * availableEnvironments: string[]; - * // Default environments for new changesets - * defaultEnvironments: string[]; - * } - * ``` - */ -export interface ChangesetConfigInfo { - /** - * Path to store active changesets. - * - * This is the directory where pending changeset files are stored. - * Default value is `.changesets`. - */ - path: string - /** - * Path to store archived changesets. - * - * This is the directory where consumed changeset files are moved - * after a version bump operation. Typically a subdirectory of `path`. - */ - historyPath: string - /** - * List of valid environment names. - * - * These are the environments that changesets can target. Common - * examples include "production", "staging", "development". - */ - availableEnvironments: Array - /** - * Default environments for new changesets. - * - * These environments are automatically assigned to new changesets - * if not explicitly specified. - */ - defaultEnvironments: Array -} - -/** - * Detailed changeset information. - * - * This structure contains the complete details of a changeset, including - * all packages, commits, environments, and timestamps. Used in list, show, - * and history responses. - * - * # Fields - * - * - `id`: Unique changeset identifier - * - `branch`: Git branch name - * - `bump`: Version bump type - * - `packages`: List of affected packages - * - `environments`: Target environments - * - `commits`: Associated commit hashes - * - `message`: Optional description - * - `created_at`: Creation timestamp (ISO 8601) - * - `updated_at`: Last update timestamp (ISO 8601) - * - * # TypeScript Definition - * - * ```typescript - * interface ChangesetDetailInfo { - * id: string; - * branch: string; - * bump: string; - * packages: string[]; - * environments: string[]; - * commits: string[]; - * message?: string; - * createdAt: string; - * updatedAt: string; - * } - * ``` - */ -export interface ChangesetDetailInfo { - /** - * Unique changeset identifier. - * - * This ID is derived from the branch name and uniquely identifies - * the changeset within the workspace. - */ - id: string - /** - * Git branch name. - * - * The full branch name associated with this changeset. - */ - branch: string - /** - * Version bump type. - * - * One of: `"major"`, `"minor"`, `"patch"`, `"none"`. - */ - bump: string - /** - * List of affected packages. + * Filter by package name. * - * Package names exactly as defined in each package's `package.json`. + * Only return archived changesets that affected the specified package. */ - packages: Array + filterPackage?: string | undefined /** - * Target environments. + * Filter by environment. * - * List of environments this changeset applies to. + * Only return archived changesets from the specified environment. */ - environments: Array + filterEnv?: string | undefined /** - * Associated commit hashes. + * Filter by bump type. * - * Git commit hashes that are part of this changeset. + * Only return archived changesets with the specified bump type. */ - commits: Array + filterBump?: string | undefined /** - * Optional description message. + * Start date filter (ISO 8601 format). * - * Human-readable description of the changes. + * Only return changesets created on or after this date. + * Example: `"2024-01-01"` or `"2024-01-01T00:00:00Z"` */ - message?: string | undefined + since?: string | undefined /** - * Creation timestamp (ISO 8601 format). + * End date filter (ISO 8601 format). * - * When the changeset was first created. - * Example: `"2024-01-15T10:30:00Z"` + * Only return changesets created on or before this date. + * Example: `"2024-12-31"` or `"2024-12-31T23:59:59Z"` */ - createdAt: string + until?: string | undefined /** - * Last update timestamp (ISO 8601 format). + * Maximum number of results to return. * - * When the changeset was last modified. - * Example: `"2024-01-15T14:45:00Z"` + * Useful for pagination or limiting large result sets. */ - updatedAt: string + limit?: number | undefined } /** - * Queries the changeset history with optional filtering. - * - * This function queries archived changesets from the workspace history, - * supporting various filter options for package, environment, bump type, - * date range, and result limit. - * - * # Parameters + * Changeset information. * - * - `root`: Workspace root directory path (required) - * - `configPath`: Optional path to custom configuration file - * - `filterPackage`: Filter by package name - * - `filterEnv`: Filter by environment - * - `filterBump`: Filter by bump type (major, minor, patch) - * - `since`: Start date filter (ISO 8601 format) - * - `until`: End date filter (ISO 8601 format) - * - `limit`: Maximum number of results + * Represents a pending changeset that has been created but not yet + * consumed by a version bump operation. Each changeset is identified + * by a unique ID derived from the branch name. * - * # Returns + * # Fields * - * An `ApiResponse` containing `ChangesetHistoryData` with the list of archived - * changesets matching the query, or an error if the operation fails. + * - `id`: The unique changeset identifier * - * ## Success Response + * # TypeScript Definition * * ```typescript - * { - * success: true, - * data: { - * archived: [ - * { - * changeset: { - * id: "feature/add-api", - * branch: "feature/add-api", - * bump: "minor", - * packages: ["@scope/core"], - * environments: ["production"], - * commits: ["abc123"], - * createdAt: "2024-01-15T10:30:00Z", - * updatedAt: "2024-01-15T14:45:00Z" - * }, - * releaseInfo: { - * releasedAt: "2024-01-16T10:00:00Z", - * releasedBy: "CI", - * releaseCommit: "def456", - * releasedVersions: [ - * { packageName: "@scope/core", version: "2.0.0" } - * ] - * } - * } - * ], - * count: 1 - * } + * interface ChangesetInfo { + * Changeset ID (derived from branch name) + * id: string; * } * ``` * - * ## Error Codes + * # Examples * - * - `EVALIDATION`: Invalid parameters (empty root or invalid bump type) - * - `ENOENT`: Path not found - * - `ECONFIG`: Workspace not initialized - * - `EEXECUTION`: CLI command failed + * ```typescript + * const changeset: ChangesetInfo = { id: 'feature-add-login' }; + * const fix: ChangesetInfo = { id: 'fix-memory-leak' }; + * ``` + */ +export interface ChangesetInfo { + /** + * Changeset unique identifier. + * + * This ID is typically derived from the Git branch name that + * created the changeset. It uniquely identifies the changeset + * within the workspace. + */ + id: string +} + +/** + * List all pending changesets in the workspace. * - * @example Basic usage - get all history + * Retrieves all pending (not yet released) changesets with optional filtering + * by package, bump type, or environment. Results can be sorted by date, branch + * name, or bump type. + * + * @param params - Changeset list parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * - `filterPackage`: Optional filter by package name + * - `filterBump`: Optional filter by bump type (major, minor, patch) + * - `filterEnv`: Optional filter by environment + * - `sort`: Sort order (date, branch, bump). Defaults to "date" + * + * @returns `Promise` containing: + * - On success: `{ success: true, data: ChangesetListData }` + * - On failure: `{ success: false, error: ErrorInfo }` + * + * @example List all changesets * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/path/to/workspace' * }); * * if (result.success) { - * console.log(`Found ${result.data.count} archived changesets`); - * for (const item of result.data.archived) { - * console.log(`- ${item.changeset.branch}: ${item.changeset.bump}`); - * console.log(` Released: ${item.releaseInfo.releasedAt}`); + * console.log(`Found ${result.data.count} changeset(s)`); + * for (const cs of result.data.changesets) { + * console.log(`- ${cs.branch}: ${cs.bump}`); * } * } * ``` * - * @example Filter by package + * @example Filter by bump type * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/path/to/workspace', - * filterPackage: '@scope/core' + * filterBump: 'major' * }); * * if (result.success) { - * console.log(`Releases for @scope/core: ${result.data.count}`); + * console.log('Major version changesets:'); + * result.data.changesets.forEach(cs => { + * console.log(` ${cs.branch}: ${cs.packages.join(', ')}`); + * }); * } * ``` * - * @example Filter by date range + * @example Filter by package * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/path/to/workspace', - * since: '2024-01-01', - * until: '2024-12-31', - * limit: 10 + * filterPackage: '@scope/core' * }); + * + * if (result.success) { + * console.log(`Changesets affecting @scope/core: ${result.data.count}`); + * } * ``` * - * @example Filter by bump type + * @example Sort by branch name * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/path/to/workspace', - * filterBump: 'major' + * sort: 'branch' * }); - * - * if (result.success) { - * console.log('Major releases:'); - * result.data.archived.forEach(item => { - * const versions = item.releaseInfo.releasedVersions - * .map(v => `${v.packageName}@${v.version}`) - * .join(', '); - * console.log(` ${item.changeset.branch}: ${versions}`); - * }); - * } * ``` * - * @example Multiple filters + * @example Filter by environment * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/path/to/workspace', - * filterPackage: '@scope/core', - * filterEnv: 'production', - * filterBump: 'minor', - * since: '2024-06-01', - * limit: 5 + * filterEnv: 'production' * }); * ``` * * @example Error handling * ```typescript - * const result = await changesetHistory({ + * const result = await changesetList({ * root: '/nonexistent/path' * }); * @@ -2101,103 +2869,215 @@ export interface ChangesetDetailInfo { * } * ``` */ -export declare function changesetHistory(params: ChangesetHistoryParams): Promise +export declare function changesetList(params: ChangesetListParams): Promise /** - * API response for the changeset history command. + * API response for the changeset list command. * - * Wraps `ChangesetHistoryData` with success/error handling. + * Wraps `ChangesetListData` with success/error handling. * * # TypeScript Definition * * ```typescript - * interface ChangesetHistoryApiResponse { + * interface ChangesetListApiResponse { * success: boolean; - * data?: ChangesetHistoryData; + * data?: ChangesetListData; * error?: ErrorInfo; * } * ``` */ -export interface ChangesetHistoryApiResponse { +export interface ChangesetListApiResponse { /** Whether the operation succeeded. */ success: boolean - /** The history data (only present when `success` is `true`). */ - data?: ChangesetHistoryData | undefined + /** The list data (only present when `success` is `true`). */ + data?: ChangesetListData | undefined /** Error information (only present when `success` is `false`). */ error?: ErrorInfo | undefined } /** - * Response data for the changeset history command. + * Response data for the changeset list command. * - * Contains archived changesets matching the query. + * Contains the list of pending changesets with summary information. + * Each changeset item includes `commit_count` rather than full commit + * details - use `changesetShow` for complete commit information. * * # TypeScript Definition * * ```typescript - * interface ChangesetHistoryData { - * archived: ArchivedChangesetInfo[]; + * interface ChangesetListData { + * changesets: ChangesetListItemInfo[]; * count: number; * } * ``` + * + * # Examples + * + * ```typescript + * const result = await changesetList({ root: '.' }); + * if (result.success) { + * console.log(`Found ${result.data.count} changesets`); + * for (const cs of result.data.changesets) { + * console.log(`- ${cs.branch}: ${cs.bump} (${cs.commitCount} commits)`); + * } + * } + * ``` */ -export interface ChangesetHistoryData { - /** List of archived changesets. */ - archived: Array - /** Total count of results. */ +export interface ChangesetListData { + /** + * List of pending changesets. + * + * Each item contains summary information including commit count. + */ + changesets: Array + /** Total count of changesets. */ count: number } /** - * Input parameters for the changeset history command. + * Changeset information for list responses. + * + * This type is specifically designed for the changeset list command output, + * which returns `commit_count` rather than individual commit hashes. For + * full commit details, use `ChangesetDetailInfo` via the `changesetShow` command. + * + * # Fields + * + * - `id`: Unique changeset identifier (derived from branch) + * - `branch`: Git branch name + * - `bump`: Version bump type (major, minor, patch, none) + * - `packages`: List of affected packages + * - `environments`: Target environments + * - `commit_count`: Number of commits in the changeset + * - `created_at`: Creation timestamp (ISO 8601) + * - `updated_at`: Last update timestamp (ISO 8601) + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetListItemInfo { + * id: string; + * branch: string; + * bump: string; + * packages: string[]; + * environments: string[]; + * commitCount: number; + * createdAt: string; + * updatedAt: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await changesetList({ root: '.' }); + * if (result.success) { + * for (const item of result.data.changesets) { + * console.log(`${item.branch}: ${item.bump} (${item.commitCount} commits)`); + * } + * } + * ``` + */ +export interface ChangesetListItemInfo { + /** + * Unique changeset identifier. + * + * This ID is derived from the branch name and uniquely identifies + * the changeset within the workspace. + */ + id: string + /** + * Git branch name. + * + * The full branch name associated with this changeset. + */ + branch: string + /** + * Version bump type. + * + * One of: `"major"`, `"minor"`, `"patch"`, `"none"`. + */ + bump: string + /** + * List of affected packages. + * + * Package names exactly as defined in each package's `package.json`. + */ + packages: Array + /** + * Target environments. + * + * List of environments this changeset applies to. + */ + environments: Array + /** + * Number of commits in the changeset. + * + * The count of git commits associated with this changeset. + * For full commit details, use `changesetShow`. + */ + commitCount: number + /** + * Creation timestamp (ISO 8601 format). + * + * When the changeset was first created. + * Example: `"2024-01-15T10:30:00Z"` + */ + createdAt: string + /** + * Last update timestamp (ISO 8601 format). + * + * When the changeset was last modified. + * Example: `"2024-01-15T14:45:00Z"` + */ + updatedAt: string +} + +/** + * Input parameters for the changeset list command. * - * This structure defines the parameters for querying archived changesets. - * All filter parameters are optional; when omitted, all archived changesets - * are returned. + * This structure defines the parameters for listing pending changesets. + * All parameters are optional; when omitted, all pending changesets are + * returned sorted by date. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `filter_package`: Filter by package name - * - `filter_env`: Filter by environment + * - `filter_package`: Filter changesets containing this package * - `filter_bump`: Filter by bump type - * - `since`: Start date filter (ISO 8601 format) - * - `until`: End date filter (ISO 8601 format) - * - `limit`: Maximum number of results + * - `filter_env`: Filter by environment + * - `sort`: Sort order (date, bump, branch) * * # TypeScript Definition * * ```typescript - * interface ChangesetHistoryParams { + * interface ChangesetListParams { * root: string; * configPath?: string; * filterPackage?: string; - * filterEnv?: string; * filterBump?: 'major' | 'minor' | 'patch'; - * since?: string; - * until?: string; - * limit?: number; + * filterEnv?: string; + * sort?: 'date' | 'bump' | 'branch'; * } * ``` * * # Examples * * ```typescript - * // Get all history - * const all: ChangesetHistoryParams = { root: '.' }; + * // List all changesets + * const all: ChangesetListParams = { root: '.' }; * - * // Get recent major releases for a package - * const filtered: ChangesetHistoryParams = { + * // Filter by package and bump type + * const filtered: ChangesetListParams = { * root: '.', * filterPackage: '@scope/core', * filterBump: 'major', - * since: '2024-01-01', - * limit: 10 + * sort: 'date' * }; * ``` */ -export interface ChangesetHistoryParams { +export interface ChangesetListParams { /** Workspace root directory path. */ root: string /** Optional custom configuration file path. */ @@ -2205,167 +3085,104 @@ export interface ChangesetHistoryParams { /** * Filter by package name. * - * Only return archived changesets that affected the specified package. + * Only return changesets that include the specified package. */ filterPackage?: string | undefined - /** - * Filter by environment. - * - * Only return archived changesets from the specified environment. - */ - filterEnv?: string | undefined /** * Filter by bump type. * - * Only return archived changesets with the specified bump type. + * Only return changesets with the specified bump type. + * Valid values: `"major"`, `"minor"`, `"patch"`. */ filterBump?: string | undefined /** - * Start date filter (ISO 8601 format). - * - * Only return changesets created on or after this date. - * Example: `"2024-01-01"` or `"2024-01-01T00:00:00Z"` - */ - since?: string | undefined - /** - * End date filter (ISO 8601 format). - * - * Only return changesets created on or before this date. - * Example: `"2024-12-31"` or `"2024-12-31T23:59:59Z"` - */ - until?: string | undefined - /** - * Maximum number of results to return. + * Filter by environment. * - * Useful for pagination or limiting large result sets. + * Only return changesets that target the specified environment. */ - limit?: number | undefined -} - -/** - * Changeset information. - * - * Represents a pending changeset that has been created but not yet - * consumed by a version bump operation. Each changeset is identified - * by a unique ID derived from the branch name. - * - * # Fields - * - * - `id`: The unique changeset identifier - * - * # TypeScript Definition - * - * ```typescript - * interface ChangesetInfo { - * Changeset ID (derived from branch name) - * id: string; - * } - * ``` - * - * # Examples - * - * ```typescript - * const changeset: ChangesetInfo = { id: 'feature-add-login' }; - * const fix: ChangesetInfo = { id: 'fix-memory-leak' }; - * ``` - */ -export interface ChangesetInfo { + filterEnv?: string | undefined /** - * Changeset unique identifier. + * Sort order for results. * - * This ID is typically derived from the Git branch name that - * created the changeset. It uniquely identifies the changeset - * within the workspace. + * Valid values: + * - `"date"`: Sort by creation date (default, newest first) + * - `"bump"`: Sort by bump type (major > minor > patch) + * - `"branch"`: Sort alphabetically by branch name */ - id: string + sort?: string | undefined } /** - * List all pending changesets in the workspace. + * Remove a changeset from the workspace. * - * Retrieves all pending (not yet released) changesets with optional filtering - * by package, bump type, or environment. Results can be sorted by date, branch - * name, or bump type. + * Deletes a changeset identified by its branch name. The changeset is archived + * before deletion for recovery purposes. In API mode, the operation always + * proceeds without confirmation (equivalent to `--force` flag in CLI). * - * @param params - Changeset list parameters containing: + * @param params - Changeset remove parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path - * - `filterPackage`: Optional filter by package name - * - `filterBump`: Optional filter by bump type (major, minor, patch) - * - `filterEnv`: Optional filter by environment - * - `sort`: Sort order (date, branch, bump). Defaults to "date" + * - `branch`: Branch name or changeset ID to remove (required) + * - `force`: Ignored in API mode (always treated as true) * - * @returns `Promise` containing: - * - On success: `{ success: true, data: ChangesetListData }` - * - On failure: `{ success: false, error: ErrorInfo }` + * @returns `Promise` - Response containing: + * - `success`: Whether the operation succeeded + * - `data`: Removal confirmation if successful + * - `error`: Error information if failed * - * @example List all changesets - * ```typescript - * const result = await changesetList({ - * root: '/path/to/workspace' - * }); + * ## Success Response * - * if (result.success) { - * console.log(`Found ${result.data.count} changeset(s)`); - * for (const cs of result.data.changesets) { - * console.log(`- ${cs.branch}: ${cs.bump}`); - * } - * } - * ``` + * When successful, `data` contains: + * - `removed`: Boolean indicating the changeset was removed (always true on success) + * - `branch`: The branch name that was removed * - * @example Filter by bump type - * ```typescript - * const result = await changesetList({ - * root: '/path/to/workspace', - * filterBump: 'major' - * }); + * ## Behavior Notes * - * if (result.success) { - * console.log('Major version changesets:'); - * result.data.changesets.forEach(cs => { - * console.log(` ${cs.branch}: ${cs.packages.join(', ')}`); - * }); - * } - * ``` + * - The changeset is archived before deletion for potential recovery + * - The archive includes a marker indicating manual deletion (not a release) + * - In API mode, no confirmation prompt is shown (force mode is implicit) * - * @example Filter by package + * ## Error Codes + * + * - `EVALIDATION`: Invalid parameters (empty root or branch) + * - `ENOENT`: Path or changeset not found + * - `ECONFIG`: Workspace not initialized + * - `EEXECUTION`: CLI command failed + * + * @example Basic usage * ```typescript - * const result = await changesetList({ + * const result = await changesetRemove({ * root: '/path/to/workspace', - * filterPackage: '@scope/core' + * branch: 'feature/abandoned-work' * }); * * if (result.success) { - * console.log(`Changesets affecting @scope/core: ${result.data.count}`); + * console.log(`Removed changeset: ${result.data.branch}`); + * } else { + * console.error(`Error: ${result.error.message}`); * } * ``` * - * @example Sort by branch name - * ```typescript - * const result = await changesetList({ - * root: '/path/to/workspace', - * sort: 'branch' - * }); - * ``` - * - * @example Filter by environment + * @example With custom config * ```typescript - * const result = await changesetList({ + * const result = await changesetRemove({ * root: '/path/to/workspace', - * filterEnv: 'production' + * configPath: '/path/to/custom.config.json', + * branch: 'feature/obsolete' * }); * ``` * * @example Error handling * ```typescript - * const result = await changesetList({ - * root: '/nonexistent/path' + * const result = await changesetRemove({ + * root: '/path/to/workspace', + * branch: 'nonexistent-branch' * }); * * if (!result.success) { * switch (result.error.code) { * case 'ENOENT': - * console.error('Path not found'); + * console.error('Changeset not found'); * break; * case 'EVALIDATION': * console.error('Invalid parameters:', result.error.message); @@ -2378,1355 +3195,1677 @@ export interface ChangesetInfo { * } * } * ``` + * + * @example Cleanup workflow + * ```typescript + * // List all changesets, then remove stale ones + * const listResult = await changesetList({ root: '.' }); + * + * if (listResult.success) { + * for (const changeset of listResult.data.changesets) { + * // Check if changeset is older than 30 days + * const createdAt = new Date(changeset.createdAt); + * const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); + * + * if (createdAt < thirtyDaysAgo) { + * const removeResult = await changesetRemove({ + * root: '.', + * branch: changeset.branch + * }); + * + * if (removeResult.success) { + * console.log(`Removed stale changeset: ${changeset.branch}`); + * } + * } + * } + * } + * ``` */ -export declare function changesetList(params: ChangesetListParams): Promise +export declare function changesetRemove(params: ChangesetRemoveParams): Promise /** - * API response for the changeset list command. + * API response for the changeset remove command. * - * Wraps `ChangesetListData` with success/error handling. + * Wraps `ChangesetRemoveData` with success/error handling. * * # TypeScript Definition * * ```typescript - * interface ChangesetListApiResponse { + * interface ChangesetRemoveApiResponse { * success: boolean; - * data?: ChangesetListData; + * data?: ChangesetRemoveData; * error?: ErrorInfo; * } * ``` */ -export interface ChangesetListApiResponse { +export interface ChangesetRemoveApiResponse { /** Whether the operation succeeded. */ success: boolean - /** The list data (only present when `success` is `true`). */ - data?: ChangesetListData | undefined + /** The remove data (only present when `success` is `true`). */ + data?: ChangesetRemoveData | undefined /** Error information (only present when `success` is `false`). */ error?: ErrorInfo | undefined } /** - * Response data for the changeset list command. + * Response data for the changeset remove command. * - * Contains the list of pending changesets with summary information. - * Each changeset item includes `commit_count` rather than full commit - * details - use `changesetShow` for complete commit information. + * Contains the result of the remove operation. * * # TypeScript Definition * * ```typescript - * interface ChangesetListData { - * changesets: ChangesetListItemInfo[]; - * count: number; + * interface ChangesetRemoveData { + * removed: boolean; + * branch: string; * } * ``` + */ +export interface ChangesetRemoveData { + /** Whether the changeset was removed. */ + removed: boolean + /** Branch name of the removed changeset. */ + branch: string +} + +/** + * Input parameters for the changeset remove command. * - * # Examples + * This structure defines the parameters for removing a changeset. + * The branch is required to identify which changeset to remove. + * + * # Fields + * + * - `root`: The workspace root directory path (required) + * - `config_path`: Optional path to a custom configuration file + * - `branch`: Branch name or changeset ID to remove (required) + * - `force`: Skip confirmation (always true in API mode) + * + * # TypeScript Definition * * ```typescript - * const result = await changesetList({ root: '.' }); - * if (result.success) { - * console.log(`Found ${result.data.count} changesets`); - * for (const cs of result.data.changesets) { - * console.log(`- ${cs.branch}: ${cs.bump} (${cs.commitCount} commits)`); - * } + * interface ChangesetRemoveParams { + * root: string; + * configPath?: string; + * branch: string; + * force?: boolean; * } * ``` + * + * # Examples + * + * ```typescript + * const params: ChangesetRemoveParams = { + * root: '.', + * branch: 'feature/abandoned', + * force: true + * }; + * ``` */ -export interface ChangesetListData { +export interface ChangesetRemoveParams { + /** Workspace root directory path. */ + root: string + /** Optional custom configuration file path. */ + configPath?: string | undefined + /** Branch name or changeset ID to remove. */ + branch: string /** - * List of pending changesets. + * Skip confirmation prompt. * - * Each item contains summary information including commit count. + * In API mode, this is always treated as `true` since there is no + * interactive prompt. Included for consistency with CLI interface. */ - changesets: Array - /** Total count of changesets. */ - count: number + force?: boolean | undefined } /** - * Changeset information for list responses. + * Show details of a specific changeset. * - * This type is specifically designed for the changeset list command output, - * which returns `commit_count` rather than individual commit hashes. For - * full commit details, use `ChangesetDetailInfo` via the `changesetShow` command. + * Retrieves detailed information about a specific changeset identified by + * its branch name or changeset ID. Returns all metadata including packages, + * environments, commits, and timestamps. * - * # Fields + * @param params - Changeset show parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * - `branch`: Branch name or changeset ID (required) * - * - `id`: Unique changeset identifier (derived from branch) - * - `branch`: Git branch name - * - `bump`: Version bump type (major, minor, patch, none) - * - `packages`: List of affected packages - * - `environments`: Target environments - * - `commit_count`: Number of commits in the changeset - * - `created_at`: Creation timestamp (ISO 8601) - * - `updated_at`: Last update timestamp (ISO 8601) + * @returns `Promise` - Response containing: + * - `success`: Whether the operation succeeded + * - `data`: Changeset details if successful + * - `error`: Error information if failed * - * # TypeScript Definition + * ## Success Response + * + * When successful, `data` contains: + * - `changeset.id`: Unique changeset identifier + * - `changeset.branch`: Git branch name + * - `changeset.bump`: Version bump type + * - `changeset.packages`: List of affected packages + * - `changeset.environments`: Target environments + * - `changeset.commits`: Associated commit hashes + * - `changeset.createdAt`: Creation timestamp (ISO 8601) + * - `changeset.updatedAt`: Last update timestamp (ISO 8601) + * + * ## Error Codes + * + * - `EVALIDATION`: Invalid parameters (empty root or branch) + * - `ENOENT`: Path or changeset not found + * - `ECONFIG`: Workspace not initialized + * - `EEXECUTION`: CLI command failed * + * @example Basic usage * ```typescript - * interface ChangesetListItemInfo { - * id: string; - * branch: string; - * bump: string; - * packages: string[]; - * environments: string[]; - * commitCount: number; - * createdAt: string; - * updatedAt: string; + * const result = await changesetShow({ + * root: '/path/to/workspace', + * branch: 'feature/new-api' + * }); + * + * if (result.success) { + * const { changeset } = result.data; + * console.log(`Changeset: ${changeset.branch}`); + * console.log(`Bump: ${changeset.bump}`); + * console.log(`Packages: ${changeset.packages.join(', ')}`); + * console.log(`Created: ${changeset.createdAt}`); * } * ``` * - * # Examples + * @example With custom config + * ```typescript + * const result = await changesetShow({ + * root: '/path/to/workspace', + * configPath: '/path/to/custom.config.json', + * branch: 'feature/auth-system' + * }); + * ``` * + * @example Error handling * ```typescript - * const result = await changesetList({ root: '.' }); - * if (result.success) { - * for (const item of result.data.changesets) { - * console.log(`${item.branch}: ${item.bump} (${item.commitCount} commits)`); + * const result = await changesetShow({ + * root: '/path/to/workspace', + * branch: 'nonexistent-branch' + * }); + * + * if (!result.success) { + * switch (result.error.code) { + * case 'ENOENT': + * console.error('Changeset not found'); + * break; + * case 'EVALIDATION': + * console.error('Invalid parameters:', result.error.message); + * break; + * case 'ECONFIG': + * console.error('Workspace not initialized'); + * break; + * default: + * console.error(`Error: ${result.error.message}`); * } * } * ``` */ -export interface ChangesetListItemInfo { - /** - * Unique changeset identifier. - * - * This ID is derived from the branch name and uniquely identifies - * the changeset within the workspace. - */ - id: string - /** - * Git branch name. - * - * The full branch name associated with this changeset. - */ - branch: string - /** - * Version bump type. - * - * One of: `"major"`, `"minor"`, `"patch"`, `"none"`. - */ - bump: string - /** - * List of affected packages. - * - * Package names exactly as defined in each package's `package.json`. - */ - packages: Array - /** - * Target environments. - * - * List of environments this changeset applies to. - */ - environments: Array - /** - * Number of commits in the changeset. - * - * The count of git commits associated with this changeset. - * For full commit details, use `changesetShow`. - */ - commitCount: number - /** - * Creation timestamp (ISO 8601 format). - * - * When the changeset was first created. - * Example: `"2024-01-15T10:30:00Z"` - */ - createdAt: string - /** - * Last update timestamp (ISO 8601 format). - * - * When the changeset was last modified. - * Example: `"2024-01-15T14:45:00Z"` - */ - updatedAt: string +export declare function changesetShow(params: ChangesetShowParams): Promise + +/** + * API response for the changeset show command. + * + * Wraps `ChangesetShowData` with success/error handling. + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetShowApiResponse { + * success: boolean; + * data?: ChangesetShowData; + * error?: ErrorInfo; + * } + * ``` + */ +export interface ChangesetShowApiResponse { + /** Whether the operation succeeded. */ + success: boolean + /** The show data (only present when `success` is `true`). */ + data?: ChangesetShowData | undefined + /** Error information (only present when `success` is `false`). */ + error?: ErrorInfo | undefined } /** - * Input parameters for the changeset list command. + * Response data for the changeset show command. * - * This structure defines the parameters for listing pending changesets. - * All parameters are optional; when omitted, all pending changesets are - * returned sorted by date. + * Contains the details of a specific changeset. + * + * # TypeScript Definition + * + * ```typescript + * interface ChangesetShowData { + * changeset: ChangesetDetailInfo; + * } + * ``` + */ +export interface ChangesetShowData { + /** The changeset details. */ + changeset: ChangesetDetailInfo +} + +/** + * Input parameters for the changeset show command. + * + * This structure defines the parameters for showing details of a specific + * changeset identified by branch name or changeset ID. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `filter_package`: Filter changesets containing this package - * - `filter_bump`: Filter by bump type - * - `filter_env`: Filter by environment - * - `sort`: Sort order (date, bump, branch) + * - `branch`: Branch name or changeset ID (required) * * # TypeScript Definition * * ```typescript - * interface ChangesetListParams { + * interface ChangesetShowParams { * root: string; * configPath?: string; - * filterPackage?: string; - * filterBump?: 'major' | 'minor' | 'patch'; - * filterEnv?: string; - * sort?: 'date' | 'bump' | 'branch'; + * branch: string; * } * ``` * * # Examples * * ```typescript - * // List all changesets - * const all: ChangesetListParams = { root: '.' }; - * - * // Filter by package and bump type - * const filtered: ChangesetListParams = { + * const params: ChangesetShowParams = { * root: '.', - * filterPackage: '@scope/core', - * filterBump: 'major', - * sort: 'date' + * branch: 'feature/new-api' * }; * ``` */ -export interface ChangesetListParams { +export interface ChangesetShowParams { /** Workspace root directory path. */ root: string /** Optional custom configuration file path. */ configPath?: string | undefined /** - * Filter by package name. - * - * Only return changesets that include the specified package. - */ - filterPackage?: string | undefined - /** - * Filter by bump type. - * - * Only return changesets with the specified bump type. - * Valid values: `"major"`, `"minor"`, `"patch"`. - */ - filterBump?: string | undefined - /** - * Filter by environment. - * - * Only return changesets that target the specified environment. - */ - filterEnv?: string | undefined - /** - * Sort order for results. + * Branch name or changeset ID. * - * Valid values: - * - `"date"`: Sort by creation date (default, newest first) - * - `"bump"`: Sort by bump type (major > minor > patch) - * - `"branch"`: Sort alphabetically by branch name + * The identifier of the changeset to display. This can be either + * the full branch name or the derived changeset ID. */ - sort?: string | undefined + branch: string } /** - * Remove a changeset from the workspace. + * Update an existing changeset in the workspace. * - * Deletes a changeset identified by its branch name. The changeset is archived - * before deletion for recovery purposes. In API mode, the operation always - * proceeds without confirmation (equivalent to `--force` flag in CLI). + * Modifies an existing changeset by adding packages, commits, environments, + * or changing the bump type. The changeset is identified by the `id` parameter, + * which corresponds to the branch name. * - * @param params - Changeset remove parameters containing: + * This function always operates in non-interactive mode. The `id` parameter + * is required since auto-detection of the current git branch is not reliable + * in programmatic contexts. + * + * @param params - Changeset update parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path - * - `branch`: Branch name or changeset ID to remove (required) - * - `force`: Ignored in API mode (always treated as true) - * - * @returns `Promise` - Response containing: - * - `success`: Whether the operation succeeded - * - `data`: Removal confirmation if successful - * - `error`: Error information if failed - * - * ## Success Response - * - * When successful, `data` contains: - * - `removed`: Boolean indicating the changeset was removed (always true on success) - * - `branch`: The branch name that was removed - * - * ## Behavior Notes + * - `id`: Branch name or changeset ID (required) + * - `commit`: Optional commit hash to add + * - `packages`: Optional list of packages to add + * - `bump`: Optional new bump type (major, minor, patch) + * - `environments`: Optional list of environments to add * - * - The changeset is archived before deletion for potential recovery - * - The archive includes a marker indicating manual deletion (not a release) - * - In API mode, no confirmation prompt is shown (force mode is implicit) + * @returns `Promise` containing: + * - On success: `{ success: true, data: ChangesetUpdateData }` + * - On failure: `{ success: false, error: ErrorInfo }` * - * ## Error Codes + * @example Add packages to an existing changeset + * ```typescript + * const result = await changesetUpdate({ + * root: '/path/to/workspace', + * id: 'feature/new-api', + * packages: ['@scope/new-package'] + * }); * - * - `EVALIDATION`: Invalid parameters (empty root or branch) - * - `ENOENT`: Path or changeset not found - * - `ECONFIG`: Workspace not initialized - * - `EEXECUTION`: CLI command failed + * if (result.success) { + * console.log(`Updated: ${result.data.updated}`); + * console.log(`Packages added: ${result.data.summary.packagesAdded}`); + * } + * ``` * - * @example Basic usage + * @example Add a commit and change bump type * ```typescript - * const result = await changesetRemove({ + * const result = await changesetUpdate({ * root: '/path/to/workspace', - * branch: 'feature/abandoned-work' + * id: 'feature/breaking-change', + * commit: 'abc123def456', + * bump: 'major' * }); * * if (result.success) { - * console.log(`Removed changeset: ${result.data.branch}`); - * } else { - * console.error(`Error: ${result.error.message}`); + * console.log(`Bump updated: ${result.data.summary.bumpUpdated}`); + * console.log(`Current bump: ${result.data.changeset.bump}`); * } * ``` * - * @example With custom config + * @example Add environments * ```typescript - * const result = await changesetRemove({ + * const result = await changesetUpdate({ * root: '/path/to/workspace', - * configPath: '/path/to/custom.config.json', - * branch: 'feature/obsolete' + * id: 'feature/deploy', + * environments: ['staging', 'production'] * }); * ``` * * @example Error handling * ```typescript - * const result = await changesetRemove({ + * const result = await changesetUpdate({ * root: '/path/to/workspace', - * branch: 'nonexistent-branch' + * id: 'nonexistent-branch' * }); * * if (!result.success) { * switch (result.error.code) { * case 'ENOENT': - * console.error('Changeset not found'); + * console.error('Path or changeset not found'); * break; * case 'EVALIDATION': * console.error('Invalid parameters:', result.error.message); * break; - * case 'ECONFIG': - * console.error('Workspace not initialized'); + * case 'EEXECUTION': + * console.error('Update failed:', result.error.message); * break; * default: * console.error(`Error: ${result.error.message}`); * } * } * ``` - * - * @example Cleanup workflow - * ```typescript - * // List all changesets, then remove stale ones - * const listResult = await changesetList({ root: '.' }); - * - * if (listResult.success) { - * for (const changeset of listResult.data.changesets) { - * // Check if changeset is older than 30 days - * const createdAt = new Date(changeset.createdAt); - * const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); - * - * if (createdAt < thirtyDaysAgo) { - * const removeResult = await changesetRemove({ - * root: '.', - * branch: changeset.branch - * }); - * - * if (removeResult.success) { - * console.log(`Removed stale changeset: ${changeset.branch}`); - * } - * } - * } - * } - * ``` */ -export declare function changesetRemove(params: ChangesetRemoveParams): Promise +export declare function changesetUpdate(params: ChangesetUpdateParams): Promise /** - * API response for the changeset remove command. + * API response for the changeset update command. * - * Wraps `ChangesetRemoveData` with success/error handling. + * Wraps `ChangesetUpdateData` with success/error handling. * * # TypeScript Definition * * ```typescript - * interface ChangesetRemoveApiResponse { + * interface ChangesetUpdateApiResponse { * success: boolean; - * data?: ChangesetRemoveData; + * data?: ChangesetUpdateData; * error?: ErrorInfo; * } * ``` */ -export interface ChangesetRemoveApiResponse { +export interface ChangesetUpdateApiResponse { /** Whether the operation succeeded. */ success: boolean - /** The remove data (only present when `success` is `true`). */ - data?: ChangesetRemoveData | undefined + /** The update data (only present when `success` is `true`). */ + data?: ChangesetUpdateData | undefined /** Error information (only present when `success` is `false`). */ error?: ErrorInfo | undefined } /** - * Response data for the changeset remove command. + * Response data for the changeset update command. * - * Contains the result of the remove operation. + * Contains the result of the update operation, including a summary of what + * was changed and the current state of the changeset after the update. * * # TypeScript Definition * * ```typescript - * interface ChangesetRemoveData { - * removed: boolean; - * branch: string; + * interface ChangesetUpdateData { + * updated: boolean; + * summary: UpdateSummaryInfo; + * changeset: ChangesetDetailInfo; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await changesetUpdate({ + * root: '.', + * id: 'feature/new-api', + * packages: ['@scope/new-package'], + * bump: 'minor' + * }); + * + * if (result.success) { + * console.log(`Updated: ${result.data.updated}`); + * console.log(`Packages added: ${result.data.summary.packagesAdded}`); + * console.log(`Current packages: ${result.data.changeset.packages.join(', ')}`); * } * ``` */ -export interface ChangesetRemoveData { - /** Whether the changeset was removed. */ - removed: boolean - /** Branch name of the removed changeset. */ - branch: string +export interface ChangesetUpdateData { + /** + * Whether the update was performed. + * + * `true` if at least one change was applied to the changeset, + * `false` if all specified values already existed. + */ + updated: boolean + /** + * Summary of what was updated. + * + * Contains counts of packages, commits, and environments added, + * as well as whether the bump type was changed. + */ + summary: UpdateSummaryInfo + /** + * The updated changeset details. + * + * Contains the complete state of the changeset after the update, + * including all packages, commits, environments, and timestamps. + */ + changeset: ChangesetDetailInfo } /** - * Input parameters for the changeset remove command. + * Input parameters for the changeset update command. * - * This structure defines the parameters for removing a changeset. - * The branch is required to identify which changeset to remove. + * This structure defines the parameters for updating an existing changeset. + * At least one update field (commit, packages, bump, or environments) should + * be provided. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `branch`: Branch name or changeset ID to remove (required) - * - `force`: Skip confirmation (always true in API mode) + * - `id`: Changeset ID or branch name (defaults to current branch) + * - `commit`: Commit hash to add to the changeset + * - `packages`: Additional packages to add + * - `bump`: New bump type to set + * - `environments`: Additional environments to add * * # TypeScript Definition * * ```typescript - * interface ChangesetRemoveParams { + * interface ChangesetUpdateParams { * root: string; * configPath?: string; - * branch: string; - * force?: boolean; + * id?: string; + * commit?: string; + * packages?: string[]; + * bump?: 'major' | 'minor' | 'patch'; + * environments?: string[]; * } * ``` * * # Examples * * ```typescript - * const params: ChangesetRemoveParams = { + * // Add a commit to current branch's changeset + * const addCommit: ChangesetUpdateParams = { * root: '.', - * branch: 'feature/abandoned', - * force: true + * commit: 'abc123def456' + * }; + * + * // Add packages to a specific changeset + * const addPackages: ChangesetUpdateParams = { + * root: '.', + * id: 'feature/new-api', + * packages: ['@scope/new-package'] + * }; + * + * // Upgrade bump type + * const upgradeBump: ChangesetUpdateParams = { + * root: '.', + * bump: 'major' * }; * ``` */ -export interface ChangesetRemoveParams { +export interface ChangesetUpdateParams { /** Workspace root directory path. */ root: string /** Optional custom configuration file path. */ configPath?: string | undefined - /** Branch name or changeset ID to remove. */ - branch: string /** - * Skip confirmation prompt. + * Changeset ID or branch name. * - * In API mode, this is always treated as `true` since there is no - * interactive prompt. Included for consistency with CLI interface. + * If not provided, uses the current Git branch to identify the changeset. */ - force?: boolean | undefined + id?: string | undefined + /** + * Commit hash to add to the changeset. + * + * The full or abbreviated Git commit hash to associate with this changeset. + */ + commit?: string | undefined + /** + * Additional packages to add to the changeset. + * + * These packages will be added to the existing list of packages. + */ + packages?: string[] | undefined + /** + * New bump type to set. + * + * Replaces the current bump type. Valid values: `"major"`, `"minor"`, `"patch"`. + */ + bump?: string | undefined + /** + * Additional environments to add. + * + * These environments will be added to the existing list. + */ + environments?: string[] | undefined } /** - * Show details of a specific changeset. + * Main configuration data structure. * - * Retrieves detailed information about a specific changeset identified by - * its branch name or changeset ID. Returns all metadata including packages, - * environments, commits, and timestamps. + * Contains all configuration sections from the `repo.config` file. + * This is the root structure that holds all workspace tool settings. * - * @param params - Changeset show parameters containing: - * - `root`: Workspace root directory path (required) - * - `configPath`: Optional custom config file path - * - `branch`: Branch name or changeset ID (required) + * # Fields * - * @returns `Promise` - Response containing: - * - `success`: Whether the operation succeeded - * - `data`: Changeset details if successful - * - `error`: Error information if failed + * - `changeset`: Changeset management configuration + * - `version`: Version resolution configuration + * - `dependency`: Dependency propagation configuration + * - `upgrade`: Upgrade detection and application configuration + * - `changelog`: Changelog generation configuration + * - `audit`: Audit and health check configuration + * - `git`: Git integration configuration + * - `execute`: Command execution configuration * - * ## Success Response + * # TypeScript Definition * - * When successful, `data` contains: - * - `changeset.id`: Unique changeset identifier - * - `changeset.branch`: Git branch name - * - `changeset.bump`: Version bump type - * - `changeset.packages`: List of affected packages - * - `changeset.environments`: Target environments - * - `changeset.commits`: Associated commit hashes - * - `changeset.createdAt`: Creation timestamp (ISO 8601) - * - `changeset.updatedAt`: Last update timestamp (ISO 8601) + * ```typescript + * interface ConfigData { + * // Changeset management configuration + * changeset: ChangesetConfigInfo; + * // Version resolution configuration + * version: VersionConfigInfo; + * // Dependency propagation configuration + * dependency: DependencyConfigInfo; + * // Upgrade detection and application configuration + * upgrade: UpgradeConfigInfo; + * // Changelog generation configuration + * changelog: ChangelogConfigInfo; + * // Audit and health check configuration + * audit: AuditConfigInfo; + * // Git integration configuration + * git: GitConfigInfo; + * // Command execution configuration + * execute: ExecuteConfigInfo; + * } + * ``` + */ +export interface ConfigData { + /** + * Changeset management configuration. + * + * Settings for managing changesets including paths and environments. + */ + changeset: ChangesetConfigInfo + /** + * Version resolution configuration. + * + * Settings for version management including strategy and defaults. + */ + version: VersionConfigInfo + /** + * Dependency propagation configuration. + * + * Settings for how dependency updates propagate through the workspace. + */ + dependency: DependencyConfigInfo + /** + * Upgrade detection and application configuration. + * + * Settings for checking and applying dependency upgrades. + */ + upgrade: UpgradeConfigInfo + /** + * Changelog generation configuration. + * + * Settings for generating and formatting changelog files. + */ + changelog: ChangelogConfigInfo + /** + * Audit and health check configuration. + * + * Settings for workspace health auditing. + */ + audit: AuditConfigInfo + /** + * Git integration configuration. + * + * Settings for Git-related operations. + */ + git: GitConfigInfo + /** + * Command execution configuration. + * + * Settings for running commands across packages. + */ + execute: ExecuteConfigInfo +} + +/** + * Show the current workspace configuration. * - * ## Error Codes + * Loads and returns the workspace configuration from the `repo.config` file + * (in JSON, TOML, or YAML format). This command provides access to all + * configuration sections including changeset, version, dependency, upgrade, + * changelog, audit, git, and execute settings. * - * - `EVALIDATION`: Invalid parameters (empty root or branch) - * - `ENOENT`: Path or changeset not found - * - `ECONFIG`: Workspace not initialized - * - `EEXECUTION`: CLI command failed + * @param params - Config show parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * + * @returns `Promise` containing: + * - On success: `{ success: true, data: ConfigShowData }` + * - On failure: `{ success: false, error: ErrorInfo }` * * @example Basic usage * ```typescript - * const result = await changesetShow({ - * root: '/path/to/workspace', - * branch: 'feature/new-api' + * const result = await configShow({ root: '/path/to/project' }); + * if (result.success) { + * console.log(`Config path: ${result.data.configPath}`); + * console.log(`Format: ${result.data.configFormat}`); + * console.log(`Strategy: ${result.data.config.version.strategy}`); + * console.log(`Default bump: ${result.data.config.version.defaultBump}`); + * } else { + * console.error(`Error: ${result.error.code} - ${result.error.message}`); + * } + * ``` + * + * @example With custom config path + * ```typescript + * const result = await configShow({ + * root: '/path/to/project', + * configPath: 'custom/repo.config.json' * }); + * ``` + * + * @example Accessing all configuration sections + * ```typescript + * const result = await configShow({ root: '.' }); + * if (result.success) { + * const { config } = result.data; + * + * // Changeset settings + * console.log(`Changeset path: ${config.changeset.path}`); + * console.log(`History path: ${config.changeset.historyPath}`); + * + * // Version settings + * console.log(`Strategy: ${config.version.strategy}`); + * console.log(`Snapshot format: ${config.version.snapshotFormat}`); * - * if (result.success) { - * const { changeset } = result.data; - * console.log(`Changeset: ${changeset.branch}`); - * console.log(`Bump: ${changeset.bump}`); - * console.log(`Packages: ${changeset.packages.join(', ')}`); - * console.log(`Created: ${changeset.createdAt}`); - * } - * ``` + * // Dependency propagation settings + * console.log(`Propagate deps: ${config.dependency.propagateDependencies}`); + * console.log(`Max depth: ${config.dependency.maxDepth}`); * - * @example With custom config - * ```typescript - * const result = await changesetShow({ - * root: '/path/to/workspace', - * configPath: '/path/to/custom.config.json', - * branch: 'feature/auth-system' - * }); + * // Execute settings + * console.log(`Timeout: ${config.execute.timeoutSecs}s`); + * console.log(`Max parallel: ${config.execute.maxParallel}`); + * } * ``` * * @example Error handling * ```typescript - * const result = await changesetShow({ - * root: '/path/to/workspace', - * branch: 'nonexistent-branch' - * }); - * + * const result = await configShow({ root: '/nonexistent' }); * if (!result.success) { - * switch (result.error.code) { - * case 'ENOENT': - * console.error('Changeset not found'); - * break; - * case 'EVALIDATION': - * console.error('Invalid parameters:', result.error.message); - * break; - * case 'ECONFIG': - * console.error('Workspace not initialized'); - * break; - * default: - * console.error(`Error: ${result.error.message}`); + * if (result.error.code === 'ENOENT') { + * console.error('Path not found'); + * } else if (result.error.code === 'ECONFIG') { + * console.error('Configuration error:', result.error.message); * } * } * ``` */ -export declare function changesetShow(params: ChangesetShowParams): Promise +export declare function configShow(params: ConfigShowParams): Promise /** - * API response for the changeset show command. + * API response wrapper for the `configShow` command. * - * Wraps `ChangesetShowData` with success/error handling. + * This structure wraps the `configShow` response with success/failure status + * and consistent error handling, following the pattern used across all + * NAPI commands. + * + * # Fields + * + * - `success`: Whether the operation succeeded + * - `data`: The config show data (present when success is true) + * - `error`: Error information (present when success is false) * * # TypeScript Definition * * ```typescript - * interface ChangesetShowApiResponse { + * interface ConfigShowApiResponse { + * // Whether the operation succeeded * success: boolean; - * data?: ChangesetShowData; + * // The config show data (present when success is true) + * data?: ConfigShowData; + * // Error information (present when success is false) * error?: ErrorInfo; * } * ``` + * + * # Examples + * + * ```typescript + * const result = await configShow({ root: '.' }); + * + * if (result.success) { + * // result.data is ConfigShowData + * console.log(result.data.config.version.strategy); + * } else { + * // result.error is ErrorInfo + * console.error(`[${result.error.code}] ${result.error.message}`); + * } + * ``` */ -export interface ChangesetShowApiResponse { - /** Whether the operation succeeded. */ +export interface ConfigShowApiResponse { + /** + * Whether the operation succeeded. + * + * - `true`: Operation completed successfully, `data` field will be present + * - `false`: Operation failed, `error` field will be present + */ success: boolean - /** The show data (only present when `success` is `true`). */ - data?: ChangesetShowData | undefined - /** Error information (only present when `success` is `false`). */ + /** + * The config show data (only present when `success` is `true`). + * + * Contains the loaded configuration and its path. + */ + data?: ConfigShowData | undefined + /** + * Error information (only present when `success` is `false`). + * + * Contains structured error information with a Node.js-style error code, + * message, optional context, and error kind. + */ error?: ErrorInfo | undefined } /** - * Response data for the changeset show command. + * Response data for the `configShow` command. * - * Contains the details of a specific changeset. + * Contains the loaded configuration and the path where it was found. + * + * # Fields + * + * - `config_path`: Path to the loaded configuration file + * - `config_format`: Format of the configuration file (json, toml, yaml) + * - `config`: The loaded configuration data * * # TypeScript Definition * * ```typescript - * interface ChangesetShowData { - * changeset: ChangesetDetailInfo; + * interface ConfigShowData { + * // Path to the loaded configuration file + * configPath: string; + * // Format of the configuration file + * configFormat: string; + * // The loaded configuration data + * config: ConfigData; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await configShow({ root: '.' }); + * if (result.success) { + * console.log(`Loaded from: ${result.data.configPath}`); + * console.log(`Format: ${result.data.configFormat}`); + * console.log(`Strategy: ${result.data.config.version.strategy}`); * } * ``` */ -export interface ChangesetShowData { - /** The changeset details. */ - changeset: ChangesetDetailInfo +export interface ConfigShowData { + /** + * Path to the loaded configuration file. + * + * The absolute or relative path where the configuration was found. + * Examples: "repo.config.json", "/path/to/repo.config.toml". + */ + configPath: string + /** + * Format of the configuration file. + * + * The detected format based on file extension: + * - `"json"`: JSON format + * - `"toml"`: TOML format + * - `"yaml"`: YAML format + */ + configFormat: string + /** + * The loaded configuration data. + * + * Contains all configuration sections parsed from the file. + */ + config: ConfigData } /** - * Input parameters for the changeset show command. + * Input parameters for the `configShow` command. * - * This structure defines the parameters for showing details of a specific - * changeset identified by branch name or changeset ID. + * This structure defines the parameters that can be passed to the `configShow` + * function from JavaScript/TypeScript. The root path is required, while + * the config path is optional. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `branch`: Branch name or changeset ID (required) * * # TypeScript Definition * * ```typescript - * interface ChangesetShowParams { + * interface ConfigShowParams { + * // Workspace root directory path * root: string; + * // Optional custom config file path * configPath?: string; - * branch: string; * } * ``` * * # Examples * * ```typescript - * const params: ChangesetShowParams = { - * root: '.', - * branch: 'feature/new-api' + * // Minimal params with just root + * const params: ConfigShowParams = { root: '.' }; + * + * // With custom config path + * const paramsWithConfig: ConfigShowParams = { + * root: '/path/to/workspace', + * configPath: '/path/to/custom/repo.config.json' * }; * ``` */ -export interface ChangesetShowParams { - /** Workspace root directory path. */ +export interface ConfigShowParams { + /** + * Workspace root directory path. + * + * This is the absolute or relative path to the root of the workspace. + * The configuration file will be searched for in this directory unless + * a custom `configPath` is provided. + */ root: string - /** Optional custom configuration file path. */ - configPath?: string | undefined /** - * Branch name or changeset ID. + * Optional custom configuration file path. * - * The identifier of the changeset to display. This can be either - * the full branch name or the derived changeset ID. + * If not provided, the command will search for configuration files + * in standard locations (`repo.config.json`, `repo.config.toml`, + * `repo.config.yaml`) within the workspace root. */ - branch: string + configPath?: string | undefined } /** - * Update an existing changeset in the workspace. + * Validate the workspace configuration. * - * Modifies an existing changeset by adding packages, commits, environments, - * or changing the bump type. The changeset is identified by the `id` parameter, - * which corresponds to the branch name. + * Loads and validates the workspace configuration from the `repo.config` file + * (in JSON, TOML, or YAML format). This command performs both structural + * validation (required fields, valid values) and semantic validation + * (cross-field consistency, potential issues). * - * This function always operates in non-interactive mode. The `id` parameter - * is required since auto-detection of the current git branch is not reliable - * in programmatic contexts. + * The validation returns: + * - `valid: true` if no errors were found (warnings are allowed) + * - `valid: false` if there are validation errors that must be fixed + * - A list of errors (issues that must be fixed) + * - A list of warnings (potential issues that should be reviewed) * - * @param params - Changeset update parameters containing: + * @param params - Config validate parameters containing: * - `root`: Workspace root directory path (required) * - `configPath`: Optional custom config file path - * - `id`: Branch name or changeset ID (required) - * - `commit`: Optional commit hash to add - * - `packages`: Optional list of packages to add - * - `bump`: Optional new bump type (major, minor, patch) - * - `environments`: Optional list of environments to add * - * @returns `Promise` containing: - * - On success: `{ success: true, data: ChangesetUpdateData }` + * @returns `Promise` containing: + * - On success: `{ success: true, data: ConfigValidateData }` * - On failure: `{ success: false, error: ErrorInfo }` * - * @example Add packages to an existing changeset + * @example Basic usage + * ```typescript + * const result = await configValidate({ root: '/path/to/project' }); + * if (result.success) { + * if (result.data.valid) { + * console.log('Configuration is valid!'); + * } else { + * console.error(`Found ${result.data.errors.length} errors`); + * for (const error of result.data.errors) { + * console.error(` [${error.field}]: ${error.message}`); + * if (error.suggestion) { + * console.log(` Suggestion: ${error.suggestion}`); + * } + * } + * } + * + * if (result.data.warnings.length > 0) { + * console.warn(`Found ${result.data.warnings.length} warnings`); + * for (const warning of result.data.warnings) { + * console.warn(` [${warning.field}]: ${warning.message}`); + * } + * } + * } else { + * console.error(`Error: ${result.error.code} - ${result.error.message}`); + * } + * ``` + * + * @example With custom config path * ```typescript - * const result = await changesetUpdate({ - * root: '/path/to/workspace', - * id: 'feature/new-api', - * packages: ['@scope/new-package'] + * const result = await configValidate({ + * root: '/path/to/project', + * configPath: 'custom/repo.config.json' * }); - * - * if (result.success) { - * console.log(`Updated: ${result.data.updated}`); - * console.log(`Packages added: ${result.data.summary.packagesAdded}`); - * } * ``` * - * @example Add a commit and change bump type + * @example CI/CD pipeline validation * ```typescript - * const result = await changesetUpdate({ - * root: '/path/to/workspace', - * id: 'feature/breaking-change', - * commit: 'abc123def456', - * bump: 'major' - * }); + * const result = await configValidate({ root: '.' }); + * if (!result.success) { + * console.error('Failed to load configuration'); + * process.exit(1); + * } * - * if (result.success) { - * console.log(`Bump updated: ${result.data.summary.bumpUpdated}`); - * console.log(`Current bump: ${result.data.changeset.bump}`); + * if (!result.data.valid) { + * console.error('Configuration validation failed:'); + * for (const error of result.data.errors) { + * console.error(` - ${error.field}: ${error.message}`); + * } + * process.exit(1); * } - * ``` * - * @example Add environments - * ```typescript - * const result = await changesetUpdate({ - * root: '/path/to/workspace', - * id: 'feature/deploy', - * environments: ['staging', 'production'] - * }); + * // Optionally fail on warnings in strict mode + * if (process.env.STRICT_CONFIG && result.data.warnings.length > 0) { + * console.error('Configuration has warnings (strict mode):'); + * for (const warning of result.data.warnings) { + * console.error(` - ${warning.field}: ${warning.message}`); + * } + * process.exit(1); + * } + * + * console.log('Configuration is valid'); * ``` * * @example Error handling * ```typescript - * const result = await changesetUpdate({ - * root: '/path/to/workspace', - * id: 'nonexistent-branch' - * }); - * + * const result = await configValidate({ root: '/nonexistent' }); * if (!result.success) { - * switch (result.error.code) { - * case 'ENOENT': - * console.error('Path or changeset not found'); - * break; - * case 'EVALIDATION': - * console.error('Invalid parameters:', result.error.message); - * break; - * case 'EEXECUTION': - * console.error('Update failed:', result.error.message); - * break; - * default: - * console.error(`Error: ${result.error.message}`); + * if (result.error.code === 'ENOENT') { + * console.error('Path not found'); + * } else if (result.error.code === 'ECONFIG') { + * console.error('Configuration error:', result.error.message); * } * } * ``` */ -export declare function changesetUpdate(params: ChangesetUpdateParams): Promise +export declare function configValidate(params: ConfigValidateParams): Promise /** - * API response for the changeset update command. + * API response wrapper for the `configValidate` command. * - * Wraps `ChangesetUpdateData` with success/error handling. + * This structure wraps the `configValidate` response with success/failure status + * and consistent error handling, following the pattern used across all + * NAPI commands. + * + * # Fields + * + * - `success`: Whether the operation succeeded + * - `data`: The config validate data (present when success is true) + * - `error`: Error information (present when success is false) * * # TypeScript Definition * * ```typescript - * interface ChangesetUpdateApiResponse { + * interface ConfigValidateApiResponse { + * // Whether the operation succeeded * success: boolean; - * data?: ChangesetUpdateData; + * // The config validate data (present when success is true) + * data?: ConfigValidateData; + * // Error information (present when success is false) * error?: ErrorInfo; * } * ``` + * + * # Examples + * + * ```typescript + * const result = await configValidate({ root: '.' }); + * + * if (result.success) { + * // result.data is ConfigValidateData + * console.log(`Valid: ${result.data.valid}`); + * console.log(`Errors: ${result.data.errors.length}`); + * } else { + * // result.error is ErrorInfo + * console.error(`[${result.error.code}] ${result.error.message}`); + * } + * ``` */ -export interface ChangesetUpdateApiResponse { - /** Whether the operation succeeded. */ +export interface ConfigValidateApiResponse { + /** + * Whether the operation succeeded. + * + * - `true`: Operation completed successfully, `data` field will be present + * - `false`: Operation failed, `error` field will be present + */ success: boolean - /** The update data (only present when `success` is `true`). */ - data?: ChangesetUpdateData | undefined - /** Error information (only present when `success` is `false`). */ + /** + * The config validate data (only present when `success` is `true`). + * + * Contains validation results including whether the config is valid + * and any issues found. + */ + data?: ConfigValidateData | undefined + /** + * Error information (only present when `success` is `false`). + * + * Contains structured error information with a Node.js-style error code, + * message, optional context, and error kind. + */ error?: ErrorInfo | undefined } /** - * Response data for the changeset update command. + * Response data for the `configValidate` command. * - * Contains the result of the update operation, including a summary of what - * was changed and the current state of the changeset after the update. + * Contains validation results including whether the configuration is valid + * and any issues found. + * + * # Fields + * + * - `valid`: Whether the configuration is valid (no errors) + * - `config_path`: Path to the validated configuration file + * - `errors`: List of validation errors + * - `warnings`: List of validation warnings * * # TypeScript Definition * * ```typescript - * interface ChangesetUpdateData { - * updated: boolean; - * summary: UpdateSummaryInfo; - * changeset: ChangesetDetailInfo; + * interface ConfigValidateData { + * // Whether the configuration is valid (no errors) + * valid: boolean; + * // Path to the validated configuration file + * configPath: string; + * // List of validation errors + * errors: ConfigValidationIssue[]; + * // List of validation warnings + * warnings: ConfigValidationIssue[]; * } * ``` * * # Examples * * ```typescript - * const result = await changesetUpdate({ - * root: '.', - * id: 'feature/new-api', - * packages: ['@scope/new-package'], - * bump: 'minor' - * }); - * + * const result = await configValidate({ root: '.' }); * if (result.success) { - * console.log(`Updated: ${result.data.updated}`); - * console.log(`Packages added: ${result.data.summary.packagesAdded}`); - * console.log(`Current packages: ${result.data.changeset.packages.join(', ')}`); + * if (result.data.valid) { + * console.log('Configuration is valid!'); + * } else { + * console.error(`Found ${result.data.errors.length} errors`); + * for (const error of result.data.errors) { + * console.error(` - [${error.field}]: ${error.message}`); + * } + * } * } * ``` */ -export interface ChangesetUpdateData { +export interface ConfigValidateData { /** - * Whether the update was performed. + * Whether the configuration is valid. * - * `true` if at least one change was applied to the changeset, - * `false` if all specified values already existed. + * `true` if no errors were found (warnings are allowed), + * `false` if there are any validation errors. */ - updated: boolean + valid: boolean /** - * Summary of what was updated. + * Path to the validated configuration file. * - * Contains counts of packages, commits, and environments added, - * as well as whether the bump type was changed. + * The path where the configuration file was found and validated. */ - summary: UpdateSummaryInfo + configPath: string /** - * The updated changeset details. + * List of validation errors. * - * Contains the complete state of the changeset after the update, - * including all packages, commits, environments, and timestamps. + * Critical issues that must be fixed for the configuration to be valid. */ - changeset: ChangesetDetailInfo + errors: Array + /** + * List of validation warnings. + * + * Non-critical issues that should be addressed but don't prevent + * the configuration from being used. + */ + warnings: Array } /** - * Input parameters for the changeset update command. + * Input parameters for the `configValidate` command. * - * This structure defines the parameters for updating an existing changeset. - * At least one update field (commit, packages, bump, or environments) should - * be provided. + * This structure defines the parameters that can be passed to the `configValidate` + * function from JavaScript/TypeScript. The root path is required, while + * the config path is optional. * * # Fields * * - `root`: The workspace root directory path (required) * - `config_path`: Optional path to a custom configuration file - * - `id`: Changeset ID or branch name (defaults to current branch) - * - `commit`: Commit hash to add to the changeset - * - `packages`: Additional packages to add - * - `bump`: New bump type to set - * - `environments`: Additional environments to add * * # TypeScript Definition * * ```typescript - * interface ChangesetUpdateParams { + * interface ConfigValidateParams { + * // Workspace root directory path * root: string; + * // Optional custom config file path * configPath?: string; - * id?: string; - * commit?: string; - * packages?: string[]; - * bump?: 'major' | 'minor' | 'patch'; - * environments?: string[]; * } * ``` * * # Examples * * ```typescript - * // Add a commit to current branch's changeset - * const addCommit: ChangesetUpdateParams = { - * root: '.', - * commit: 'abc123def456' - * }; - * - * // Add packages to a specific changeset - * const addPackages: ChangesetUpdateParams = { - * root: '.', - * id: 'feature/new-api', - * packages: ['@scope/new-package'] - * }; - * - * // Upgrade bump type - * const upgradeBump: ChangesetUpdateParams = { - * root: '.', - * bump: 'major' + * // Minimal params with just root + * const params: ConfigValidateParams = { root: '.' }; + * + * // With custom config path + * const paramsWithConfig: ConfigValidateParams = { + * root: '/path/to/workspace', + * configPath: '/path/to/custom/repo.config.json' * }; * ``` */ -export interface ChangesetUpdateParams { - /** Workspace root directory path. */ - root: string - /** Optional custom configuration file path. */ - configPath?: string | undefined - /** - * Changeset ID or branch name. - * - * If not provided, uses the current Git branch to identify the changeset. - */ - id?: string | undefined - /** - * Commit hash to add to the changeset. - * - * The full or abbreviated Git commit hash to associate with this changeset. - */ - commit?: string | undefined - /** - * Additional packages to add to the changeset. - * - * These packages will be added to the existing list of packages. - */ - packages?: string[] | undefined +export interface ConfigValidateParams { /** - * New bump type to set. + * Workspace root directory path. * - * Replaces the current bump type. Valid values: `"major"`, `"minor"`, `"patch"`. + * This is the absolute or relative path to the root of the workspace. + * The configuration file will be searched for in this directory unless + * a custom `configPath` is provided. */ - bump?: string | undefined + root: string /** - * Additional environments to add. + * Optional custom configuration file path. * - * These environments will be added to the existing list. + * If not provided, the command will search for configuration files + * in standard locations (`repo.config.json`, `repo.config.toml`, + * `repo.config.yaml`) within the workspace root. */ - environments?: string[] | undefined + configPath?: string | undefined } /** - * Main configuration data structure. + * Validation issue information. * - * Contains all configuration sections from the `repo.config` file. - * This is the root structure that holds all workspace tool settings. + * Represents a single validation issue found during configuration validation. * * # Fields * - * - `changeset`: Changeset management configuration - * - `version`: Version resolution configuration - * - `dependency`: Dependency propagation configuration - * - `upgrade`: Upgrade detection and application configuration - * - `changelog`: Changelog generation configuration - * - `audit`: Audit and health check configuration - * - `git`: Git integration configuration - * - `execute`: Command execution configuration + * - `severity`: Issue severity ("error", "warning", or "info") + * - `field`: The configuration field with the issue + * - `message`: Human-readable description of the issue + * - `suggestion`: Optional suggestion for fixing the issue * * # TypeScript Definition * * ```typescript - * interface ConfigData { - * // Changeset management configuration - * changeset: ChangesetConfigInfo; - * // Version resolution configuration - * version: VersionConfigInfo; - * // Dependency propagation configuration - * dependency: DependencyConfigInfo; - * // Upgrade detection and application configuration - * upgrade: UpgradeConfigInfo; - * // Changelog generation configuration - * changelog: ChangelogConfigInfo; - * // Audit and health check configuration - * audit: AuditConfigInfo; - * // Git integration configuration - * git: GitConfigInfo; - * // Command execution configuration - * execute: ExecuteConfigInfo; + * interface ConfigValidationIssue { + * // Issue severity: "error", "warning", or "info" + * severity: string; + * // The configuration field with the issue + * field: string; + * // Human-readable description of the issue + * message: string; + * // Optional suggestion for fixing the issue + * suggestion?: string; * } * ``` */ -export interface ConfigData { - /** - * Changeset management configuration. - * - * Settings for managing changesets including paths and environments. - */ - changeset: ChangesetConfigInfo - /** - * Version resolution configuration. - * - * Settings for version management including strategy and defaults. - */ - version: VersionConfigInfo - /** - * Dependency propagation configuration. - * - * Settings for how dependency updates propagate through the workspace. - */ - dependency: DependencyConfigInfo - /** - * Upgrade detection and application configuration. - * - * Settings for checking and applying dependency upgrades. - */ - upgrade: UpgradeConfigInfo +export interface ConfigValidationIssue { /** - * Changelog generation configuration. + * Issue severity. * - * Settings for generating and formatting changelog files. + * Indicates the importance of the issue: + * - `"error"`: Critical issue that must be fixed + * - `"warning"`: Potential problem that should be addressed + * - `"info"`: Informational note for improvement */ - changelog: ChangelogConfigInfo + severity: string /** - * Audit and health check configuration. + * The configuration field with the issue. * - * Settings for workspace health auditing. + * Dot-notation path to the field, e.g., "version.strategy" or + * "changeset.path". */ - audit: AuditConfigInfo + field: string /** - * Git integration configuration. + * Human-readable description of the issue. * - * Settings for Git-related operations. + * Explains what is wrong with the configuration. */ - git: GitConfigInfo + message: string /** - * Command execution configuration. + * Optional suggestion for fixing the issue. * - * Settings for running commands across packages. + * Provides guidance on how to resolve the issue. */ - execute: ExecuteConfigInfo + suggestion?: string | undefined } /** - * Show the current workspace configuration. - * - * Loads and returns the workspace configuration from the `repo.config` file - * (in JSON, TOML, or YAML format). This command provides access to all - * configuration sections including changeset, version, dependency, upgrade, - * changelog, audit, git, and execute settings. - * - * @param params - Config show parameters containing: - * - `root`: Workspace root directory path (required) - * - `configPath`: Optional custom config file path - * - * @returns `Promise` containing: - * - On success: `{ success: true, data: ConfigShowData }` - * - On failure: `{ success: false, error: ErrorInfo }` - * - * @example Basic usage - * ```typescript - * const result = await configShow({ root: '/path/to/project' }); - * if (result.success) { - * console.log(`Config path: ${result.data.configPath}`); - * console.log(`Format: ${result.data.configFormat}`); - * console.log(`Strategy: ${result.data.config.version.strategy}`); - * console.log(`Default bump: ${result.data.config.version.defaultBump}`); - * } else { - * console.error(`Error: ${result.error.code} - ${result.error.message}`); - * } - * ``` - * - * @example With custom config path - * ```typescript - * const result = await configShow({ - * root: '/path/to/project', - * configPath: 'custom/repo.config.json' - * }); - * ``` - * - * @example Accessing all configuration sections - * ```typescript - * const result = await configShow({ root: '.' }); - * if (result.success) { - * const { config } = result.data; + * Dependency configuration information. * - * // Changeset settings - * console.log(`Changeset path: ${config.changeset.path}`); - * console.log(`History path: ${config.changeset.historyPath}`); + * Contains settings for dependency propagation during version bumps. * - * // Version settings - * console.log(`Strategy: ${config.version.strategy}`); - * console.log(`Snapshot format: ${config.version.snapshotFormat}`); + * # Fields * - * // Dependency propagation settings - * console.log(`Propagate deps: ${config.dependency.propagateDependencies}`); - * console.log(`Max depth: ${config.dependency.maxDepth}`); + * - `propagation_bump`: Version bump type for dependency updates + * - `propagate_dependencies`: Whether to propagate regular dependencies + * - `propagate_dev_dependencies`: Whether to propagate dev dependencies + * - `propagate_peer_dependencies`: Whether to propagate peer dependencies + * - `max_depth`: Maximum propagation depth + * - `fail_on_circular`: Whether to fail on circular dependencies + * - `skip_workspace_protocol`: Skip workspace: protocol dependencies + * - `skip_file_protocol`: Skip file: protocol dependencies + * - `skip_link_protocol`: Skip link: protocol dependencies + * - `skip_portal_protocol`: Skip portal: protocol dependencies * - * // Execute settings - * console.log(`Timeout: ${config.execute.timeoutSecs}s`); - * console.log(`Max parallel: ${config.execute.maxParallel}`); - * } - * ``` + * # TypeScript Definition * - * @example Error handling * ```typescript - * const result = await configShow({ root: '/nonexistent' }); - * if (!result.success) { - * if (result.error.code === 'ENOENT') { - * console.error('Path not found'); - * } else if (result.error.code === 'ECONFIG') { - * console.error('Configuration error:', result.error.message); - * } + * interface DependencyConfigInfo { + * // Version bump type for dependency updates + * propagationBump: string; + * // Whether to propagate regular dependencies + * propagateDependencies: boolean; + * // Whether to propagate dev dependencies + * propagateDevDependencies: boolean; + * // Whether to propagate peer dependencies + * propagatePeerDependencies: boolean; + * // Maximum propagation depth + * maxDepth: number; + * // Whether to fail on circular dependencies + * failOnCircular: boolean; + * // Skip workspace: protocol dependencies + * skipWorkspaceProtocol: boolean; + * // Skip file: protocol dependencies + * skipFileProtocol: boolean; + * // Skip link: protocol dependencies + * skipLinkProtocol: boolean; + * // Skip portal: protocol dependencies + * skipPortalProtocol: boolean; * } * ``` */ -export declare function configShow(params: ConfigShowParams): Promise +export interface DependencyConfigInfo { + /** + * Version bump type for dependency updates. + * + * When a package is updated, this determines how dependent packages + * have their versions bumped. Values: "major", "minor", "patch", "none". + */ + propagationBump: string + /** + * Whether to propagate regular dependencies. + * + * If `true`, packages that depend on updated packages will also + * be considered for version updates. + */ + propagateDependencies: boolean + /** + * Whether to propagate dev dependencies. + * + * If `true`, packages that have the updated package as a dev + * dependency will also be considered for version updates. + */ + propagateDevDependencies: boolean + /** + * Whether to propagate peer dependencies. + * + * If `true`, packages that have the updated package as a peer + * dependency will also be considered for version updates. + */ + propagatePeerDependencies: boolean + /** + * Maximum propagation depth. + * + * Limits how deep dependency propagation can traverse the + * dependency graph. Prevents excessive updates in large monorepos. + */ + maxDepth: number + /** + * Whether to fail on circular dependencies. + * + * If `true`, the operation fails when circular dependencies are + * detected. If `false`, circular dependencies are handled gracefully. + */ + failOnCircular: boolean + /** + * Skip workspace: protocol dependencies. + * + * If `true`, dependencies using `workspace:` protocol are not + * propagated. These are typically handled differently in monorepos. + */ + skipWorkspaceProtocol: boolean + /** + * Skip file: protocol dependencies. + * + * If `true`, dependencies using `file:` protocol are not propagated. + */ + skipFileProtocol: boolean + /** + * Skip link: protocol dependencies. + * + * If `true`, dependencies using `link:` protocol are not propagated. + */ + skipLinkProtocol: boolean + /** + * Skip portal: protocol dependencies. + * + * If `true`, dependencies using `portal:` protocol are not propagated. + */ + skipPortalProtocol: boolean +} /** - * API response wrapper for the `configShow` command. + * Dependency update information for a package version bump. * - * This structure wraps the `configShow` response with success/failure status - * and consistent error handling, following the pattern used across all - * NAPI commands. + * This structure captures information about how a dependency version + * was updated as part of the version bump process. Dependencies are + * updated when the package they depend on is bumped. * * # Fields * - * - `success`: Whether the operation succeeded - * - `data`: The config show data (present when success is true) - * - `error`: Error information (present when success is false) + * - `name`: The dependency package name + * - `dependency_type`: The type of dependency (regular, dev, peer, optional) + * - `old_version`: The previous version specification + * - `new_version`: The new version specification * * # TypeScript Definition * * ```typescript - * interface ConfigShowApiResponse { - * // Whether the operation succeeded - * success: boolean; - * // The config show data (present when success is true) - * data?: ConfigShowData; - * // Error information (present when success is false) - * error?: ErrorInfo; + * interface DependencyUpdateInfo { + * name: string; + * dependencyType: 'regular' | 'dev' | 'peer' | 'optional'; + * oldVersion: string; + * newVersion: string; * } * ``` * * # Examples * * ```typescript - * const result = await configShow({ root: '.' }); - * - * if (result.success) { - * // result.data is ConfigShowData - * console.log(result.data.config.version.strategy); - * } else { - * // result.error is ErrorInfo - * console.error(`[${result.error.code}] ${result.error.message}`); - * } + * const update: DependencyUpdateInfo = { + * name: '@scope/core', + * dependencyType: 'regular', + * oldVersion: '^1.0.0', + * newVersion: '^1.1.0' + * }; * ``` */ -export interface ConfigShowApiResponse { +export interface DependencyUpdateInfo { /** - * Whether the operation succeeded. + * The dependency package name. * - * - `true`: Operation completed successfully, `data` field will be present - * - `false`: Operation failed, `error` field will be present + * This is the name of the package that was updated as a dependency. + * May include scope (e.g., `@scope/package`). */ - success: boolean + name: string /** - * The config show data (only present when `success` is `true`). + * The type of dependency. * - * Contains the loaded configuration and its path. + * One of: `regular`, `dev`, `peer`, `optional` */ - data?: ConfigShowData | undefined + dependencyType: string /** - * Error information (only present when `success` is `false`). + * The previous version specification. * - * Contains structured error information with a Node.js-style error code, - * message, optional context, and error kind. + * This is the version range or exact version that was previously + * specified in package.json (e.g., `^1.0.0`, `~1.0.0`, `1.0.0`). */ - error?: ErrorInfo | undefined + oldVersion: string + /** + * The new version specification. + * + * This is the updated version range or exact version after the bump. + */ + newVersion: string } /** - * Response data for the `configShow` command. + * Information about a single dependency upgrade. * - * Contains the loaded configuration and the path where it was found. + * This structure contains details about an available or applied upgrade + * for a specific dependency. * * # Fields * - * - `config_path`: Path to the loaded configuration file - * - `config_format`: Format of the configuration file (json, toml, yaml) - * - `config`: The loaded configuration data + * - `name`: The dependency name + * - `current_version`: The current version in package.json + * - `latest_version`: The latest available version + * - `upgrade_type`: The type of upgrade (major, minor, patch) + * - `dependency_type`: Where the dependency is defined * * # TypeScript Definition * * ```typescript - * interface ConfigShowData { - * // Path to the loaded configuration file - * configPath: string; - * // Format of the configuration file - * configFormat: string; - * // The loaded configuration data - * config: ConfigData; - * } - * ``` - * - * # Examples - * - * ```typescript - * const result = await configShow({ root: '.' }); - * if (result.success) { - * console.log(`Loaded from: ${result.data.configPath}`); - * console.log(`Format: ${result.data.configFormat}`); - * console.log(`Strategy: ${result.data.config.version.strategy}`); + * interface DependencyUpgradeInfo { + * name: string; + * currentVersion: string; + * latestVersion: string; + * upgradeType: string; + * dependencyType: string; * } * ``` */ -export interface ConfigShowData { +export interface DependencyUpgradeInfo { /** - * Path to the loaded configuration file. + * The name of the dependency. * - * The absolute or relative path where the configuration was found. - * Examples: "repo.config.json", "/path/to/repo.config.toml". + * This is the package name as it appears in package.json, + * including any scope prefix. */ - configPath: string + name: string /** - * Format of the configuration file. + * The current version specified in package.json. * - * The detected format based on file extension: - * - `"json"`: JSON format - * - `"toml"`: TOML format - * - `"yaml"`: YAML format + * This is the version range or exact version currently specified. */ - configFormat: string + currentVersion: string /** - * The loaded configuration data. + * The latest available version from the registry. * - * Contains all configuration sections parsed from the file. + * This is the exact version that would be installed if the + * upgrade is applied. */ - config: ConfigData + latestVersion: string + /** + * The type of version upgrade. + * + * One of: `"major"`, `"minor"`, `"patch"` + */ + upgradeType: string + /** + * The type of dependency relationship. + * + * One of: `"regular"`, `"dev"`, `"peer"`, `"optional"` + */ + dependencyType: string } /** - * Input parameters for the `configShow` command. + * Error information structure for Node.js bindings. * - * This structure defines the parameters that can be passed to the `configShow` - * function from JavaScript/TypeScript. The root path is required, while - * the config path is optional. + * This structure is exposed to JavaScript/TypeScript via napi-rs and provides + * detailed error information in a format familiar to Node.js developers. + * The `#[napi(object)]` attribute enables automatic conversion to JavaScript + * objects and TypeScript type generation. * * # Fields * - * - `root`: The workspace root directory path (required) - * - `config_path`: Optional path to a custom configuration file + * - `code`: Node.js-style error code (e.g., "EVALIDATION", "EGIT") + * - `message`: Human-readable error message + * - `context`: Optional additional context (field name, path, etc.) + * - `kind`: Error category from the CLI layer * * # TypeScript Definition * * ```typescript - * interface ConfigShowParams { - * // Workspace root directory path - * root: string; - * // Optional custom config file path - * configPath?: string; + * export interface ErrorInfo { + * Node.js-style error code (e.g., "EVALIDATION", "EGIT") + * code: string; + * Human-readable error message + * message: string; + * Optional additional context about the error + * context?: string; + * Error category from CLI (for debugging) + * kind: string; * } * ``` * * # Examples * * ```typescript - * // Minimal params with just root - * const params: ConfigShowParams = { root: '.' }; - * - * // With custom config path - * const paramsWithConfig: ConfigShowParams = { - * root: '/path/to/workspace', - * configPath: '/path/to/custom/repo.config.json' - * }; + * // In JavaScript/TypeScript: + * if (!result.success) { + * const { code, message, context, kind } = result.error; + * console.error(`[${code}] ${message}`); + * if (context) { + * console.error(`Context: ${context}`); + * } + * } * ``` */ -export interface ConfigShowParams { +export interface ErrorInfo { /** - * Workspace root directory path. + * Node.js-style error code (e.g., "EVALIDATION", "EGIT"). * - * This is the absolute or relative path to the root of the workspace. - * The configuration file will be searched for in this directory unless - * a custom `configPath` is provided. + * These codes follow Node.js conventions and can be used for + * programmatic error handling in JavaScript/TypeScript. + * + * # Available Codes + * + * - `ECONFIG`: Configuration errors + * - `EVALIDATION`: Validation errors + * - `EEXEC`: Execution errors + * - `EGIT`: Git errors + * - `EPKG`: Package errors + * - `ENOENT`: File/path not found + * - `EIO`: I/O errors + * - `ENETWORK`: Network errors + * - `EUSER`: User errors + * - `ETIMEOUT`: Timeout errors */ - root: string + code: string + /** + * Human-readable error message. + * + * This message is suitable for displaying to end users and + * provides a clear description of what went wrong. + */ + message: string + /** + * Optional additional context for the error. + * + * This may contain the field name that caused a validation error, + * the path that was not found, or other relevant context information. + */ + context?: string /** - * Optional custom configuration file path. + * Error category from the CLI layer. * - * If not provided, the command will search for configuration files - * in standard locations (`repo.config.json`, `repo.config.toml`, - * `repo.config.yaml`) within the workspace root. + * This corresponds to the `CliError` variant name (e.g., "Configuration", + * "Validation", "Git") and can be used for logging and debugging. */ - configPath?: string | undefined + kind: string } /** - * Validate the workspace configuration. + * Execute commands across workspace packages. * - * Loads and validates the workspace configuration from the `repo.config` file - * (in JSON, TOML, or YAML format). This command performs both structural - * validation (required fields, valid values) and semantic validation - * (cross-field consistency, potential issues). + * Runs the specified command on workspace packages with optional filtering, + * parallel execution, and timeout protection. * - * The validation returns: - * - `valid: true` if no errors were found (warnings are allowed) - * - `valid: false` if there are validation errors that must be fixed - * - A list of errors (issues that must be fixed) - * - A list of warnings (potential issues that should be reviewed) + * This function is the main entry point for Node.js applications to execute + * commands across workspace packages. It handles all the complexity of CLI + * invocation, timeout management, and response parsing internally. * - * @param params - Config validate parameters containing: + * @param params - Execute parameters containing: * - `root`: Workspace root directory path (required) - * - `configPath`: Optional custom config file path + * - `cmd`: Command to execute (required, e.g., `npm:test` or `ls -la`) + * - `filterPackage`: Optional filter to specific packages + * - `affected`: Execute only on affected packages + * - `since`: Since commit/branch/tag for affected detection + * - `until`: Until commit/branch/tag for affected detection + * - `branch`: Compare against branch for affected detection + * - `parallel`: Run commands in parallel + * - `args`: Additional arguments to pass to command + * - `timeoutSecs`: Global timeout override (0 = no timeout) + * - `perPackageTimeoutSecs`: Per-package timeout override (0 = no timeout) * - * @returns `Promise` containing: - * - On success: `{ success: true, data: ConfigValidateData }` + * @returns `Promise` containing: + * - On success: `{ success: true, data: ExecuteData }` * - On failure: `{ success: false, error: ErrorInfo }` * * @example Basic usage * ```typescript - * const result = await configValidate({ root: '/path/to/project' }); + * const result = await execute({ + * root: '/path/to/project', + * cmd: 'npm:test' + * }); * if (result.success) { - * if (result.data.valid) { - * console.log('Configuration is valid!'); - * } else { - * console.error(`Found ${result.data.errors.length} errors`); - * for (const error of result.data.errors) { - * console.error(` [${error.field}]: ${error.message}`); - * if (error.suggestion) { - * console.log(` Suggestion: ${error.suggestion}`); - * } - * } - * } - * - * if (result.data.warnings.length > 0) { - * console.warn(`Found ${result.data.warnings.length} warnings`); - * for (const warning of result.data.warnings) { - * console.warn(` [${warning.field}]: ${warning.message}`); - * } - * } + * console.log(`${result.data.summary.succeeded}/${result.data.summary.total} succeeded`); * } else { * console.error(`Error: ${result.error.code} - ${result.error.message}`); * } * ``` * - * @example With custom config path + * @example With timeout and parallel execution * ```typescript - * const result = await configValidate({ + * const result = await execute({ * root: '/path/to/project', - * configPath: 'custom/repo.config.json' + * cmd: 'npm:build', + * parallel: true, + * timeoutSecs: 600, + * perPackageTimeoutSecs: 120 * }); * ``` * - * @example CI/CD pipeline validation - * ```typescript - * const result = await configValidate({ root: '.' }); - * if (!result.success) { - * console.error('Failed to load configuration'); - * process.exit(1); - * } - * - * if (!result.data.valid) { - * console.error('Configuration validation failed:'); - * for (const error of result.data.errors) { - * console.error(` - ${error.field}: ${error.message}`); - * } - * process.exit(1); - * } - * - * // Optionally fail on warnings in strict mode - * if (process.env.STRICT_CONFIG && result.data.warnings.length > 0) { - * console.error('Configuration has warnings (strict mode):'); - * for (const warning of result.data.warnings) { - * console.error(` - ${warning.field}: ${warning.message}`); - * } - * process.exit(1); - * } - * - * console.log('Configuration is valid'); - * ``` - * * @example Error handling * ```typescript - * const result = await configValidate({ root: '/nonexistent' }); + * const result = await execute({ + * root: '/nonexistent', + * cmd: 'npm:test' + * }); * if (!result.success) { * if (result.error.code === 'ENOENT') { * console.error('Path not found'); - * } else if (result.error.code === 'ECONFIG') { - * console.error('Configuration error:', result.error.message); + * } else if (result.error.code === 'ETIMEOUT') { + * console.error('Operation timed out'); + * } else if (result.error.code === 'EVALIDATION') { + * console.error('Invalid parameters'); * } * } * ``` */ -export declare function configValidate(params: ConfigValidateParams): Promise +export declare function execute(params: ExecuteParams): Promise /** - * API response wrapper for the `configValidate` command. + * API response for the execute command. * - * This structure wraps the `configValidate` response with success/failure status - * and consistent error handling, following the pattern used across all - * NAPI commands. + * This is a concrete (non-generic) response type specifically for the execute + * command. It uses `#[napi(object)]` to enable automatic conversion to + * JavaScript objects. + * + * napi-rs cannot use generic types with `#[napi(object)]`, so each command + * that returns structured data needs its own concrete response type. * * # Fields * * - `success`: Whether the operation succeeded - * - `data`: The config validate data (present when success is true) + * - `data`: The execute data (present when success is true) * - `error`: Error information (present when success is false) * * # TypeScript Definition * * ```typescript - * interface ConfigValidateApiResponse { - * // Whether the operation succeeded + * interface ExecuteApiResponse { * success: boolean; - * // The config validate data (present when success is true) - * data?: ConfigValidateData; - * // Error information (present when success is false) + * data?: ExecuteData; * error?: ErrorInfo; * } * ``` @@ -3734,33 +4873,39 @@ export declare function configValidate(params: ConfigValidateParams): Promise` format. */ - configPath: string + command: string /** - * List of validation errors. + * Results for each package. * - * Critical issues that must be fixed for the configuration to be valid. + * Contains execution results for each package that was targeted. + * The order may vary for parallel execution. */ - errors: Array + results: Array /** - * List of validation warnings. + * Execution summary. * - * Non-critical issues that should be addressed but don't prevent - * the configuration from being used. + * Aggregate statistics about the execution across all packages. */ - warnings: Array + summary: ExecuteSummary } /** - * Input parameters for the `configValidate` command. + * Input parameters for the execute command. * - * This structure defines the parameters that can be passed to the `configValidate` - * function from JavaScript/TypeScript. The root path is required, while - * the config path is optional. + * This structure defines the parameters for running commands across workspace + * packages. It supports filtering by package names or affected packages, + * parallel execution, and configurable timeouts. * * # Fields * * - `root`: The workspace root directory path (required) - * - `config_path`: Optional path to a custom configuration file + * - `cmd`: The command to execute (required) + * - `filter_package`: Filter by specific package names + * - `affected`: Execute only on affected packages + * - `since`: Git reference for affected detection start + * - `until`: Git reference for affected detection end + * - `branch`: Base branch for affected comparison + * - `parallel`: Run commands in parallel + * - `args`: Additional arguments to pass to the command + * - `timeout_secs`: Global timeout in seconds + * - `per_package_timeout_secs`: Per-package timeout in seconds + * + * # Mutual Exclusion + * + * `filter_package` and `affected` are mutually exclusive. Only one can be + * specified at a time. Validation should ensure this constraint is enforced. * * # TypeScript Definition * * ```typescript - * interface ConfigValidateParams { - * // Workspace root directory path + * interface ExecuteParams { * root: string; - * // Optional custom config file path - * configPath?: string; + * cmd: string; + * filterPackage?: string[]; + * affected?: boolean; + * since?: string; + * until?: string; + * branch?: string; + * parallel?: boolean; + * args?: string[]; + * timeoutSecs?: number; + * perPackageTimeoutSecs?: number; * } * ``` * * # Examples * * ```typescript - * // Minimal params with just root - * const params: ConfigValidateParams = { root: '.' }; + * // Run tests on affected packages + * const params: ExecuteParams = { + * root: '.', + * cmd: 'npm:test', + * affected: true, + * branch: 'main', + * parallel: true + * }; * - * // With custom config path - * const paramsWithConfig: ConfigValidateParams = { + * // Run build on specific packages with timeout + * const buildParams: ExecuteParams = { * root: '/path/to/workspace', - * configPath: '/path/to/custom/repo.config.json' + * cmd: 'npm:build', + * filterPackage: ['@scope/core', '@scope/utils'], + * timeoutSecs: 600, + * perPackageTimeoutSecs: 120 + * }; + * + * // Run system command with extra arguments + * const systemParams: ExecuteParams = { + * root: '.', + * cmd: 'echo', + * args: ['Hello', 'World'] * }; * ``` */ -export interface ConfigValidateParams { +export interface ExecuteParams { /** * Workspace root directory path. * * This is the absolute or relative path to the root of the workspace. - * The configuration file will be searched for in this directory unless - * a custom `configPath` is provided. + * For monorepos, this should point to the root where the package manager + * configuration is located. */ root: string /** - * Optional custom configuration file path. - * - * If not provided, the command will search for configuration files - * in standard locations (`repo.config.json`, `repo.config.toml`, - * `repo.config.yaml`) within the workspace root. - */ - configPath?: string | undefined -} - -/** - * Validation issue information. - * - * Represents a single validation issue found during configuration validation. - * - * # Fields - * - * - `severity`: Issue severity ("error", "warning", or "info") - * - `field`: The configuration field with the issue - * - `message`: Human-readable description of the issue - * - `suggestion`: Optional suggestion for fixing the issue - * - * # TypeScript Definition - * - * ```typescript - * interface ConfigValidationIssue { - * // Issue severity: "error", "warning", or "info" - * severity: string; - * // The configuration field with the issue - * field: string; - * // Human-readable description of the issue - * message: string; - * // Optional suggestion for fixing the issue - * suggestion?: string; - * } - * ``` - */ -export interface ConfigValidationIssue { - /** - * Issue severity. + * Command to execute. * - * Indicates the importance of the issue: - * - `"error"`: Critical issue that must be fixed - * - `"warning"`: Potential problem that should be addressed - * - `"info"`: Informational note for improvement - */ - severity: string - /** - * The configuration field with the issue. + * Supports two formats: + * - `npm: