diff --git a/crates/node/src/tests.rs b/crates/node/src/tests.rs index 43e01c7c..4092ecb1 100644 --- a/crates/node/src/tests.rs +++ b/crates/node/src/tests.rs @@ -3348,3 +3348,736 @@ mod changeset_data_tests { assert!(data.branch.is_none()); } } + +// ============================================================================ +// Bump Types Tests (Story 5.1) +// ============================================================================ + +/// Tests for bump command type definitions. +#[cfg(test)] +mod bump_types_tests { + use crate::error::ErrorInfo; + use crate::types::bump::{ + BumpApplyApiResponse, BumpApplyData, BumpApplyParams, BumpPreviewApiResponse, + BumpPreviewData, BumpPreviewParams, BumpSnapshotApiResponse, BumpSnapshotData, + BumpSnapshotParams, BumpSummaryInfo, COMMON_PRERELEASE_TAGS, DEFAULT_SNAPSHOT_FORMAT, + DependencyUpdateInfo, PackageVersionInfo, SnapshotVersionInfo, VALID_DEPENDENCY_TYPES, + }; + + // ======================================================================== + // Constants Tests + // ======================================================================== + + #[test] + fn test_common_prerelease_tags() { + assert!(COMMON_PRERELEASE_TAGS.contains(&"alpha")); + assert!(COMMON_PRERELEASE_TAGS.contains(&"beta")); + assert!(COMMON_PRERELEASE_TAGS.contains(&"rc")); + assert_eq!(COMMON_PRERELEASE_TAGS.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_snapshot_format() { + assert!(DEFAULT_SNAPSHOT_FORMAT.contains("{version}")); + assert!(DEFAULT_SNAPSHOT_FORMAT.contains("{short_commit}")); + assert_eq!(DEFAULT_SNAPSHOT_FORMAT, "{version}-snapshot.{short_commit}"); + } + + // ======================================================================== + // BumpPreviewParams Tests + // ======================================================================== + + #[test] + fn test_bump_preview_params_new() { + let params = BumpPreviewParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.packages.is_none()); + assert!(params.show_diff.is_none()); + } + + #[test] + fn test_bump_preview_params_builder_chain() { + let params = BumpPreviewParams::new("/workspace") + .with_config_path("/workspace/repo.config.json") + .with_packages(vec!["@scope/core".to_string(), "@scope/utils".to_string()]) + .with_show_diff(true); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.config_path, Some("/workspace/repo.config.json".to_string())); + assert_eq!( + params.packages, + Some(vec!["@scope/core".to_string(), "@scope/utils".to_string()]) + ); + assert_eq!(params.show_diff, Some(true)); + } + + #[test] + fn test_bump_preview_params_clone() { + let params = BumpPreviewParams::new("/workspace") + .with_show_diff(true) + .with_packages(vec!["@scope/core".to_string()]); + let cloned = params.clone(); + + assert_eq!(cloned.root, params.root); + assert_eq!(cloned.show_diff, params.show_diff); + assert_eq!(cloned.packages, params.packages); + } + + #[test] + fn test_bump_preview_params_serialize() { + let params = BumpPreviewParams::new("/workspace").with_show_diff(true); + let json = serde_json::to_string(¶ms).unwrap_or_default(); + + assert!(json.contains("\"root\":\"/workspace\"")); + assert!(json.contains("\"show_diff\":true")); + // Optional fields that are None should not be present + assert!(!json.contains("\"config_path\"")); + assert!(!json.contains("\"packages\"")); + } + + // ======================================================================== + // BumpApplyParams Tests + // ======================================================================== + + #[test] + fn test_bump_apply_params_new() { + let params = BumpApplyParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.packages.is_none()); + assert!(params.git_commit.is_none()); + assert!(params.git_tag.is_none()); + assert!(params.git_push.is_none()); + assert!(params.prerelease.is_none()); + assert!(params.no_changelog.is_none()); + assert!(params.no_archive.is_none()); + assert!(params.always_archive.is_none()); + assert!(params.force.is_none()); + } + + #[test] + fn test_bump_apply_params_builder_chain() { + let params = BumpApplyParams::new("/workspace") + .with_config_path("/workspace/repo.config.json") + .with_packages(vec!["@scope/core".to_string()]) + .with_git_commit(true) + .with_git_tag(true) + .with_git_push(false) + .with_prerelease("beta") + .with_no_changelog(false) + .with_no_archive(false) + .with_always_archive(true) + .with_force(true); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.config_path, Some("/workspace/repo.config.json".to_string())); + assert_eq!(params.packages, Some(vec!["@scope/core".to_string()])); + assert_eq!(params.git_commit, Some(true)); + assert_eq!(params.git_tag, Some(true)); + assert_eq!(params.git_push, Some(false)); + assert_eq!(params.prerelease, Some("beta".to_string())); + assert_eq!(params.no_changelog, Some(false)); + assert_eq!(params.no_archive, Some(false)); + assert_eq!(params.always_archive, Some(true)); + assert_eq!(params.force, Some(true)); + } + + #[test] + fn test_bump_apply_params_with_git_options() { + let params = BumpApplyParams::new("/workspace").with_git_options(true, true, false); + + assert_eq!(params.git_commit, Some(true)); + assert_eq!(params.git_tag, Some(true)); + assert_eq!(params.git_push, Some(false)); + } + + #[test] + fn test_bump_apply_params_clone() { + let params = + BumpApplyParams::new("/workspace").with_prerelease("alpha").with_git_commit(true); + let cloned = params.clone(); + + assert_eq!(cloned.root, params.root); + assert_eq!(cloned.prerelease, params.prerelease); + assert_eq!(cloned.git_commit, params.git_commit); + } + + #[test] + fn test_bump_apply_params_serialize() { + let params = BumpApplyParams::new("/workspace").with_git_commit(true).with_prerelease("rc"); + let json = serde_json::to_string(¶ms).unwrap_or_default(); + + assert!(json.contains("\"root\":\"/workspace\"")); + assert!(json.contains("\"git_commit\":true")); + assert!(json.contains("\"prerelease\":\"rc\"")); + // Optional fields that are None should not be present + assert!(!json.contains("\"git_tag\"")); + } + + // ======================================================================== + // BumpSnapshotParams Tests + // ======================================================================== + + #[test] + fn test_bump_snapshot_params_new() { + let params = BumpSnapshotParams::new("/workspace"); + + assert_eq!(params.root, "/workspace"); + assert!(params.config_path.is_none()); + assert!(params.packages.is_none()); + assert!(params.format.is_none()); + } + + #[test] + fn test_bump_snapshot_params_builder_chain() { + let params = BumpSnapshotParams::new("/workspace") + .with_config_path("/workspace/repo.config.json") + .with_packages(vec!["@scope/core".to_string()]) + .with_format("{version}-{branch}.{short_commit}"); + + assert_eq!(params.root, "/workspace"); + assert_eq!(params.config_path, Some("/workspace/repo.config.json".to_string())); + assert_eq!(params.packages, Some(vec!["@scope/core".to_string()])); + assert_eq!(params.format, Some("{version}-{branch}.{short_commit}".to_string())); + } + + #[test] + fn test_bump_snapshot_params_clone() { + let params = + BumpSnapshotParams::new("/workspace").with_format("{version}-snapshot.{timestamp}"); + let cloned = params.clone(); + + assert_eq!(cloned.root, params.root); + assert_eq!(cloned.format, params.format); + } + + #[test] + fn test_bump_snapshot_params_serialize() { + let params = + BumpSnapshotParams::new("/workspace").with_format("{version}-dev.{short_commit}"); + let json = serde_json::to_string(¶ms).unwrap_or_default(); + + assert!(json.contains("\"root\":\"/workspace\"")); + assert!(json.contains("\"format\":\"{version}-dev.{short_commit}\"")); + } + + // ======================================================================== + // DependencyUpdateInfo Tests + // ======================================================================== + + #[test] + fn test_dependency_update_info_new() { + let update = DependencyUpdateInfo::new("@scope/core", "regular", "^1.0.0", "^1.1.0"); + + assert_eq!(update.name, "@scope/core"); + assert_eq!(update.dependency_type, "regular"); + assert_eq!(update.old_version, "^1.0.0"); + assert_eq!(update.new_version, "^1.1.0"); + } + + #[test] + fn test_dependency_update_info_regular() { + let update = DependencyUpdateInfo::regular("@scope/utils", "^2.0.0", "^2.1.0"); + + assert_eq!(update.name, "@scope/utils"); + assert_eq!(update.dependency_type, "regular"); + assert_eq!(update.old_version, "^2.0.0"); + assert_eq!(update.new_version, "^2.1.0"); + } + + #[test] + fn test_dependency_update_info_dev() { + let update = DependencyUpdateInfo::dev("typescript", "^4.0.0", "^5.0.0"); + + assert_eq!(update.name, "typescript"); + assert_eq!(update.dependency_type, "dev"); + } + + #[test] + fn test_dependency_update_info_peer() { + let update = DependencyUpdateInfo::peer("react", "^17.0.0", "^18.0.0"); + + assert_eq!(update.name, "react"); + assert_eq!(update.dependency_type, "peer"); + } + + #[test] + fn test_dependency_update_info_optional() { + let update = DependencyUpdateInfo::optional("lodash", "^4.0.0", "^4.1.0"); + + assert_eq!(update.name, "lodash"); + assert_eq!(update.dependency_type, "optional"); + } + + #[test] + fn test_dependency_update_info_clone() { + let update = DependencyUpdateInfo::regular("@scope/core", "^1.0.0", "^1.1.0"); + let cloned = update.clone(); + + assert_eq!(cloned.name, update.name); + assert_eq!(cloned.dependency_type, update.dependency_type); + assert_eq!(cloned.old_version, update.old_version); + assert_eq!(cloned.new_version, update.new_version); + } + + // ======================================================================== + // PackageVersionInfo Tests + // ======================================================================== + + #[test] + fn test_package_version_info_new() { + let info = + PackageVersionInfo::new("@scope/core", "packages/core", "1.0.0", "1.1.0", "minor"); + + assert_eq!(info.name, "@scope/core"); + assert_eq!(info.path, "packages/core"); + assert_eq!(info.current_version, "1.0.0"); + assert_eq!(info.next_version, "1.1.0"); + assert_eq!(info.bump, "minor"); + assert!(info.dependency_updates.is_empty()); + } + + #[test] + fn test_package_version_info_with_dependency_updates() { + let updates = vec![ + DependencyUpdateInfo::regular("@scope/utils", "^1.0.0", "^1.1.0"), + DependencyUpdateInfo::dev("typescript", "^4.0.0", "^5.0.0"), + ]; + + let info = + PackageVersionInfo::new("@scope/core", "packages/core", "1.0.0", "2.0.0", "major") + .with_dependency_updates(updates); + + assert_eq!(info.dependency_updates.len(), 2); + assert_eq!(info.dependency_updates[0].name, "@scope/utils"); + assert_eq!(info.dependency_updates[1].name, "typescript"); + } + + #[test] + fn test_package_version_info_add_dependency_update() { + let info = + PackageVersionInfo::new("@scope/core", "packages/core", "1.0.0", "1.1.0", "minor") + .add_dependency_update(DependencyUpdateInfo::regular("dep1", "^1.0.0", "^1.1.0")) + .add_dependency_update(DependencyUpdateInfo::dev("dep2", "^2.0.0", "^2.1.0")); + + assert_eq!(info.dependency_updates.len(), 2); + } + + #[test] + fn test_package_version_info_bump_type_checks() { + let major = PackageVersionInfo::new("pkg", "path", "1.0.0", "2.0.0", "major"); + assert!(major.is_major()); + assert!(!major.is_minor()); + assert!(!major.is_patch()); + assert!(!major.is_none()); + + let minor = PackageVersionInfo::new("pkg", "path", "1.0.0", "1.1.0", "minor"); + assert!(!minor.is_major()); + assert!(minor.is_minor()); + assert!(!minor.is_patch()); + assert!(!minor.is_none()); + + let patch = PackageVersionInfo::new("pkg", "path", "1.0.0", "1.0.1", "patch"); + assert!(!patch.is_major()); + assert!(!patch.is_minor()); + assert!(patch.is_patch()); + assert!(!patch.is_none()); + + let none = PackageVersionInfo::new("pkg", "path", "1.0.0", "1.0.0", "none"); + assert!(!none.is_major()); + assert!(!none.is_minor()); + assert!(!none.is_patch()); + assert!(none.is_none()); + } + + #[test] + fn test_package_version_info_clone() { + let info = + PackageVersionInfo::new("@scope/core", "packages/core", "1.0.0", "1.1.0", "minor"); + let cloned = info.clone(); + + assert_eq!(cloned.name, info.name); + assert_eq!(cloned.path, info.path); + assert_eq!(cloned.current_version, info.current_version); + assert_eq!(cloned.next_version, info.next_version); + assert_eq!(cloned.bump, info.bump); + } + + // ======================================================================== + // SnapshotVersionInfo Tests + // ======================================================================== + + #[test] + fn test_snapshot_version_info_new() { + let info = SnapshotVersionInfo::new( + "@scope/core", + "packages/core", + "1.0.0", + "1.0.0-snapshot.abc123f", + ); + + assert_eq!(info.name, "@scope/core"); + assert_eq!(info.path, "packages/core"); + assert_eq!(info.original_version, "1.0.0"); + assert_eq!(info.snapshot_version, "1.0.0-snapshot.abc123f"); + } + + #[test] + fn test_snapshot_version_info_clone() { + let info = SnapshotVersionInfo::new( + "@scope/core", + "packages/core", + "1.0.0", + "1.0.0-feature-x.abc123f", + ); + let cloned = info.clone(); + + assert_eq!(cloned.name, info.name); + assert_eq!(cloned.snapshot_version, info.snapshot_version); + } + + // ======================================================================== + // BumpSummaryInfo Tests + // ======================================================================== + + #[test] + fn test_bump_summary_info_new() { + let summary = BumpSummaryInfo::new(10, 2, 5, 3); + + assert_eq!(summary.total_packages, 10); + assert_eq!(summary.major_bumps, 2); + assert_eq!(summary.minor_bumps, 5); + assert_eq!(summary.patch_bumps, 3); + } + + #[test] + fn test_bump_summary_info_empty() { + let summary = BumpSummaryInfo::empty(); + + assert_eq!(summary.total_packages, 0); + assert_eq!(summary.major_bumps, 0); + assert_eq!(summary.minor_bumps, 0); + assert_eq!(summary.patch_bumps, 0); + } + + #[test] + fn test_bump_summary_info_from_packages() { + let packages = vec![ + PackageVersionInfo::new("pkg1", "path1", "1.0.0", "2.0.0", "major"), + PackageVersionInfo::new("pkg2", "path2", "1.0.0", "1.1.0", "minor"), + PackageVersionInfo::new("pkg3", "path3", "1.0.0", "1.1.0", "minor"), + PackageVersionInfo::new("pkg4", "path4", "1.0.0", "1.0.1", "patch"), + ]; + + let summary = BumpSummaryInfo::from_packages(&packages); + + assert_eq!(summary.total_packages, 4); + assert_eq!(summary.major_bumps, 1); + assert_eq!(summary.minor_bumps, 2); + assert_eq!(summary.patch_bumps, 1); + } + + #[test] + fn test_bump_summary_info_has_breaking_changes() { + let with_major = BumpSummaryInfo::new(5, 1, 2, 2); + assert!(with_major.has_breaking_changes()); + + let without_major = BumpSummaryInfo::new(5, 0, 3, 2); + assert!(!without_major.has_breaking_changes()); + } + + // ======================================================================== + // BumpPreviewData Tests + // ======================================================================== + + #[test] + fn test_bump_preview_data_new() { + let packages = vec![PackageVersionInfo::new( + "@scope/core", + "packages/core", + "1.0.0", + "1.1.0", + "minor", + )]; + let changesets = vec!["feature-api".to_string()]; + + let data = BumpPreviewData::new("independent", packages, changesets); + + assert_eq!(data.strategy, "independent"); + assert_eq!(data.packages.len(), 1); + assert_eq!(data.changesets.len(), 1); + assert_eq!(data.summary.total_packages, 1); + assert_eq!(data.summary.minor_bumps, 1); + } + + #[test] + fn test_bump_preview_data_empty() { + let data = BumpPreviewData::empty("unified"); + + assert_eq!(data.strategy, "unified"); + assert!(data.packages.is_empty()); + assert!(data.changesets.is_empty()); + assert_eq!(data.summary.total_packages, 0); + } + + #[test] + fn test_bump_preview_data_has_packages() { + let empty = BumpPreviewData::empty("independent"); + assert!(!empty.has_packages()); + + let with_packages = BumpPreviewData::new( + "independent", + vec![PackageVersionInfo::new("pkg", "path", "1.0.0", "1.1.0", "minor")], + vec![], + ); + assert!(with_packages.has_packages()); + } + + #[test] + fn test_bump_preview_data_has_breaking_changes() { + let with_major = BumpPreviewData::new( + "independent", + vec![PackageVersionInfo::new("pkg", "path", "1.0.0", "2.0.0", "major")], + vec![], + ); + assert!(with_major.has_breaking_changes()); + + let without_major = BumpPreviewData::new( + "independent", + vec![PackageVersionInfo::new("pkg", "path", "1.0.0", "1.1.0", "minor")], + vec![], + ); + assert!(!without_major.has_breaking_changes()); + } + + // ======================================================================== + // BumpApplyData Tests + // ======================================================================== + + #[test] + fn test_bump_apply_data_new() { + let data = BumpApplyData::new("independent", 5, 2); + + assert_eq!(data.strategy, "independent"); + assert_eq!(data.packages_updated, 5); + assert_eq!(data.changesets_archived, 2); + assert!(data.files_modified.is_empty()); + assert!(data.tags_created.is_empty()); + assert!(data.commit_sha.is_none()); + } + + #[test] + fn test_bump_apply_data_builder_chain() { + let data = BumpApplyData::new("independent", 3, 1) + .with_files_modified(vec![ + "packages/core/package.json".to_string(), + "packages/core/CHANGELOG.md".to_string(), + ]) + .with_tags_created(vec!["@scope/core@1.1.0".to_string()]) + .with_commit_sha("abc123def456789"); + + assert_eq!(data.files_modified.len(), 2); + assert_eq!(data.tags_created.len(), 1); + assert_eq!(data.commit_sha, Some("abc123def456789".to_string())); + } + + #[test] + fn test_bump_apply_data_has_commit() { + let without_commit = BumpApplyData::new("independent", 1, 1); + assert!(!without_commit.has_commit()); + + let with_commit = BumpApplyData::new("independent", 1, 1).with_commit_sha("abc123"); + assert!(with_commit.has_commit()); + } + + #[test] + fn test_bump_apply_data_has_tags() { + let without_tags = BumpApplyData::new("independent", 1, 1); + assert!(!without_tags.has_tags()); + + let with_tags = BumpApplyData::new("independent", 1, 1) + .with_tags_created(vec!["@scope/core@1.0.0".to_string()]); + assert!(with_tags.has_tags()); + } + + // ======================================================================== + // BumpSnapshotData Tests + // ======================================================================== + + #[test] + fn test_bump_snapshot_data_new() { + let packages = vec![SnapshotVersionInfo::new( + "@scope/core", + "packages/core", + "1.0.0", + "1.0.0-snapshot.abc123f", + )]; + + let data = + BumpSnapshotData::new("independent", packages, "{version}-snapshot.{short_commit}"); + + assert_eq!(data.strategy, "independent"); + assert_eq!(data.packages.len(), 1); + assert_eq!(data.format, "{version}-snapshot.{short_commit}"); + } + + #[test] + fn test_bump_snapshot_data_empty() { + let data = BumpSnapshotData::empty("unified", "{version}-dev.{timestamp}"); + + assert_eq!(data.strategy, "unified"); + assert!(data.packages.is_empty()); + assert_eq!(data.format, "{version}-dev.{timestamp}"); + } + + #[test] + fn test_bump_snapshot_data_package_count() { + let empty = BumpSnapshotData::empty("independent", "format"); + assert_eq!(empty.package_count(), 0); + + let with_packages = BumpSnapshotData::new( + "independent", + vec![ + SnapshotVersionInfo::new("pkg1", "path1", "1.0.0", "1.0.0-snapshot"), + SnapshotVersionInfo::new("pkg2", "path2", "2.0.0", "2.0.0-snapshot"), + ], + "format", + ); + assert_eq!(with_packages.package_count(), 2); + } + + // ======================================================================== + // API Response Tests + // ======================================================================== + + #[test] + fn test_bump_preview_api_response_success() { + let data = BumpPreviewData::empty("independent"); + let response = BumpPreviewApiResponse::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_bump_preview_api_response_failure() { + let error = ErrorInfo::validation("Invalid root path", Some("root")); + let response = BumpPreviewApiResponse::failure(error); + + assert!(!response.success); + assert!(!response.is_success()); + assert!(response.is_failure()); + assert!(response.data.is_none()); + assert!(response.error.is_some()); + assert_eq!(response.error.as_ref().unwrap().code, "EVALIDATION"); + } + + #[test] + fn test_bump_apply_api_response_success() { + let data = BumpApplyData::new("independent", 3, 1); + let response = BumpApplyApiResponse::success(data); + + assert!(response.success); + assert!(response.is_success()); + assert!(response.data.is_some()); + assert_eq!(response.data.as_ref().unwrap().packages_updated, 3); + } + + #[test] + fn test_bump_apply_api_response_failure() { + let error = ErrorInfo::git("Failed to create commit"); + let response = BumpApplyApiResponse::failure(error); + + assert!(!response.success); + assert!(response.is_failure()); + assert!(response.error.is_some()); + assert_eq!(response.error.as_ref().unwrap().code, "EGIT"); + } + + #[test] + fn test_bump_snapshot_api_response_success() { + let data = BumpSnapshotData::empty("independent", "format"); + let response = BumpSnapshotApiResponse::success(data); + + assert!(response.success); + assert!(response.is_success()); + assert!(response.data.is_some()); + } + + #[test] + fn test_bump_snapshot_api_response_failure() { + let error = ErrorInfo::validation("Invalid format template", Some("format")); + let response = BumpSnapshotApiResponse::failure(error); + + assert!(!response.success); + assert!(response.is_failure()); + assert!(response.error.is_some()); + } + + // ======================================================================== + // Serialization Tests + // ======================================================================== + + #[test] + fn test_package_version_info_serialize() { + let info = + PackageVersionInfo::new("@scope/core", "packages/core", "1.0.0", "1.1.0", "minor"); + let json = serde_json::to_string(&info).unwrap_or_default(); + + assert!(json.contains("\"name\":\"@scope/core\"")); + assert!(json.contains("\"path\":\"packages/core\"")); + assert!(json.contains("\"current_version\":\"1.0.0\"")); + assert!(json.contains("\"next_version\":\"1.1.0\"")); + assert!(json.contains("\"bump\":\"minor\"")); + } + + #[test] + fn test_bump_preview_data_serialize() { + let packages = vec![PackageVersionInfo::new("pkg", "path", "1.0.0", "1.1.0", "minor")]; + let data = BumpPreviewData::new("independent", packages, vec!["cs1".to_string()]); + let json = serde_json::to_string(&data).unwrap_or_default(); + + assert!(json.contains("\"strategy\":\"independent\"")); + assert!(json.contains("\"packages\"")); + assert!(json.contains("\"summary\"")); + assert!(json.contains("\"changesets\"")); + } + + #[test] + fn test_bump_apply_data_serialize() { + let data = BumpApplyData::new("unified", 5, 2).with_commit_sha("abc123"); + let json = serde_json::to_string(&data).unwrap_or_default(); + + assert!(json.contains("\"strategy\":\"unified\"")); + assert!(json.contains("\"packages_updated\":5")); + assert!(json.contains("\"changesets_archived\":2")); + assert!(json.contains("\"commit_sha\":\"abc123\"")); + } + + #[test] + fn test_bump_snapshot_data_serialize() { + let packages = vec![SnapshotVersionInfo::new("pkg", "path", "1.0.0", "1.0.0-snapshot.abc")]; + let data = + BumpSnapshotData::new("independent", packages, "{version}-snapshot.{short_commit}"); + let json = serde_json::to_string(&data).unwrap_or_default(); + + assert!(json.contains("\"strategy\":\"independent\"")); + assert!(json.contains("\"format\":\"{version}-snapshot.{short_commit}\"")); + assert!(json.contains("\"snapshot_version\":\"1.0.0-snapshot.abc\"")); + } +} diff --git a/crates/node/src/types/bump.rs b/crates/node/src/types/bump.rs index 0a7e940d..90b3c2ba 100644 --- a/crates/node/src/types/bump.rs +++ b/crates/node/src/types/bump.rs @@ -1,39 +1,55 @@ -//! Bump command type definitions. +//! Bump command type definitions for Node.js bindings. //! //! # What //! -//! This module contains type definitions for bump commands (preview, apply, -//! snapshot), including parameter structures and response data types. +//! This module defines all NAPI-compatible type structures for bump commands, +//! including input parameters and response data types. Bump commands are the +//! culmination of the changeset workflow, translating pending changesets into +//! actual version updates for packages. //! //! # 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: //! -//! - `BumpPreviewParams`: Input parameters for previewing version bumps -//! - `BumpPreviewData`: Response data containing preview information -//! - `BumpApplyParams`: Input parameters for applying version bumps -//! - `BumpApplyData`: Response data containing applied changes -//! - `BumpSnapshotParams`: Input parameters for snapshot versioning -//! - `BumpSnapshotData`: Response data containing snapshot versions +//! - **Input Parameters**: `BumpPreviewParams`, `BumpApplyParams`, `BumpSnapshotParams` +//! - **Response Data**: `BumpPreviewData`, `BumpApplyData`, `BumpSnapshotData` +//! - **Supporting Types**: `PackageVersionInfo`, `SnapshotVersionInfo`, +//! `DependencyUpdateInfo`, `BumpSummaryInfo` +//! - **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 bump commands handle version management based on changesets. Preview -//! allows users to see what will change before applying, apply performs the -//! actual version bumps, and snapshot creates pre-release versions. +//! Bump commands provide three distinct workflows: +//! +//! - **Preview**: Dry-run to see what versions would change (no modifications) +//! - **Apply**: Execute version bumps with optional Git integration +//! - **Snapshot**: Generate temporary pre-release versions for testing +//! +//! 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 { //! bumpPreview, //! bumpApply, //! bumpSnapshot, -//! BumpPreviewParams +//! BumpPreviewParams, +//! BumpApplyParams, +//! BumpSnapshotParams //! } from '@websublime/workspace-tools'; //! -//! // Preview version bumps +//! // Preview version bumps (dry-run) //! const previewParams: BumpPreviewParams = { //! root: '.', //! showDiff: true @@ -45,35 +61,2056 @@ //! } //! } //! -//! // Apply version bumps -//! const applyResult = await bumpApply({ +//! // Apply version bumps with Git integration +//! const applyParams: BumpApplyParams = { //! root: '.', -//! execute: true, //! gitCommit: true, -//! gitTag: true -//! }); +//! gitTag: true, +//! gitPush: false +//! }; +//! const applyResult = await bumpApply(applyParams); +//! if (applyResult.success) { +//! console.log(`Updated ${applyResult.data.packagesUpdated} packages`); +//! console.log(`Tags created: ${applyResult.data.tagsCreated.join(', ')}`); +//! } //! -//! // Create snapshot versions -//! const snapshotResult = await bumpSnapshot({ +//! // Generate snapshot versions +//! const snapshotParams: BumpSnapshotParams = { //! root: '.', -//! format: '{version}-snapshot.{timestamp}' -//! }); +//! format: '{version}-snapshot.{short_commit}' +//! }; +//! const snapshotResult = await bumpSnapshot(snapshotParams); +//! if (snapshotResult.success) { +//! for (const pkg of snapshotResult.data.packages) { +//! console.log(`${pkg.name}: ${pkg.snapshotVersion}`); +//! } +//! } //! ``` +//! +//! ## Rust Usage (Internal) +//! +//! ```rust,ignore +//! use sublime_node_tools::types::bump::{ +//! BumpPreviewParams, BumpPreviewData, PackageVersionInfo +//! }; +//! +//! // Creating params for validation +//! let params = BumpPreviewParams::new(".") +//! .with_show_diff(true) +//! .with_packages(vec!["@scope/pkg1".to_string()]); +//! +//! // Constructing response data +//! let version_info = PackageVersionInfo::new( +//! "@scope/pkg1", +//! "packages/pkg1", +//! "1.0.0", +//! "1.1.0", +//! "minor" +//! ); +//! ``` + +use napi_derive::napi; +use serde::Serialize; + +use crate::error::ErrorInfo; + +// ============================================================================ +// Constants +// ============================================================================ + +/// Common prerelease tags used in semantic versioning. +/// +/// These are the standard prerelease identifiers, but any valid tag +/// containing only ASCII alphanumerics and hyphens `[0-9A-Za-z-]` is accepted. +/// +/// - `"alpha"`: Early development, unstable +/// - `"beta"`: Feature complete but may have bugs +/// - `"rc"`: Release candidate, near final release +#[allow(dead_code)] +pub(crate) const COMMON_PRERELEASE_TAGS: &[&str] = &["alpha", "beta", "rc"]; + +/// Valid dependency types for dependency updates. +/// +/// - `"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 snapshot format template. +/// +/// Variables available: +/// - `{version}`: Current package version +/// - `{branch}`: Current Git branch name (sanitized) +/// - `{short_commit}`: Short Git commit hash (7 characters) +/// - `{commit}`: Full Git commit hash +/// - `{timestamp}`: Unix timestamp +#[allow(dead_code)] +pub(crate) const DEFAULT_SNAPSHOT_FORMAT: &str = "{version}-snapshot.{short_commit}"; + +// ============================================================================ +// Input Parameters +// ============================================================================ + +/// Input parameters for the bump preview 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. +/// +/// # 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 +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpPreviewParams { +/// root: string; +/// configPath?: string; +/// packages?: string[]; +/// showDiff?: boolean; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// // Minimal params - preview all packages +/// const minimal: BumpPreviewParams = { root: '.' }; +/// +/// // Preview specific packages with diff +/// const filtered: BumpPreviewParams = { +/// root: '/path/to/workspace', +/// packages: ['@scope/pkg1', '@scope/pkg2'], +/// showDiff: true +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct 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. + 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.yaml`, etc.) + /// within the workspace root. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub config_path: Option, + + /// 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`). + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, + + /// Whether to show detailed version diffs. + /// + /// When `true`, includes detailed information about what changes would + /// be made to each package, including dependency updates. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub show_diff: Option, +} + +#[allow(dead_code)] +impl BumpPreviewParams { + /// Creates a new `BumpPreviewParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `BumpPreviewParams` instance with default optional values. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BumpPreviewParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { root: root.into(), config_path: None, packages: None, show_diff: 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 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 show diff flag. + /// + /// # Arguments + /// + /// * `show_diff` - Whether to show detailed diffs + /// + /// # Returns + /// + /// Self with the show diff flag set. + #[must_use] + pub fn with_show_diff(mut self, show_diff: bool) -> Self { + self.show_diff = Some(show_diff); + self + } +} + +/// Input parameters for the bump apply 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. +/// +/// # Fields +/// +/// - `root`: The workspace root directory path (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 { +/// root: 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 = { +/// root: '.', +/// prerelease: 'beta', +/// gitCommit: true, +/// gitTag: true +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpApplyParams { + /// 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, + + /// Filter to specific packages. + /// + /// When provided, only these packages will be bumped. + /// Package names should include scope if applicable. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub git_commit: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub git_tag: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub git_push: Option, + + /// 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-]`. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub prerelease: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_changelog: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_archive: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub always_archive: Option, + + /// 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. + #[napi(ts_type = "boolean | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub force: Option, +} + +#[allow(dead_code)] +impl BumpApplyParams { + /// Creates a new `BumpApplyParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `BumpApplyParams` instance with default optional values. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BumpApplyParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { + root: root.into(), + config_path: None, + packages: None, + git_commit: None, + git_tag: None, + git_push: None, + prerelease: None, + no_changelog: None, + no_archive: None, + always_archive: None, + force: 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 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 git commit flag. + /// + /// # Arguments + /// + /// * `git_commit` - Whether to create a Git commit + /// + /// # Returns + /// + /// Self with the git commit flag set. + #[must_use] + pub fn with_git_commit(mut self, git_commit: bool) -> Self { + self.git_commit = Some(git_commit); + self + } + + /// Sets the git tag flag. + /// + /// # Arguments + /// + /// * `git_tag` - Whether to create Git tags + /// + /// # Returns + /// + /// Self with the git tag flag set. + #[must_use] + pub fn with_git_tag(mut self, git_tag: bool) -> Self { + self.git_tag = Some(git_tag); + self + } + + /// Sets the git push flag. + /// + /// # Arguments + /// + /// * `git_push` - Whether to push Git tags + /// + /// # Returns + /// + /// Self with the git push flag set. + #[must_use] + pub fn with_git_push(mut self, git_push: bool) -> Self { + self.git_push = Some(git_push); + self + } + + /// Sets the prerelease tag. + /// + /// # Arguments + /// + /// * `prerelease` - The prerelease tag (alpha, beta, rc, or custom) + /// + /// # Returns + /// + /// Self with the prerelease tag set. + #[must_use] + pub fn with_prerelease(mut self, prerelease: impl Into) -> Self { + self.prerelease = Some(prerelease.into()); + self + } + + /// Sets the no changelog flag. + /// + /// # Arguments + /// + /// * `no_changelog` - Whether to skip changelog generation + /// + /// # Returns + /// + /// Self with the no changelog flag set. + #[must_use] + pub fn with_no_changelog(mut self, no_changelog: bool) -> Self { + self.no_changelog = Some(no_changelog); + self + } + + /// Sets the no archive flag. + /// + /// # Arguments + /// + /// * `no_archive` - Whether to keep changesets active + /// + /// # Returns + /// + /// Self with the no archive flag set. + #[must_use] + pub fn with_no_archive(mut self, no_archive: bool) -> Self { + self.no_archive = Some(no_archive); + self + } + + /// Sets the always archive flag. + /// + /// # Arguments + /// + /// * `always_archive` - Whether to force archiving for prereleases + /// + /// # Returns + /// + /// Self with the always archive flag set. + #[must_use] + pub fn with_always_archive(mut self, always_archive: bool) -> Self { + self.always_archive = Some(always_archive); + self + } + + /// Sets the force flag. + /// + /// # Arguments + /// + /// * `force` - Whether to skip confirmation prompts + /// + /// # Returns + /// + /// Self with the force flag set. + #[must_use] + pub fn with_force(mut self, force: bool) -> Self { + self.force = Some(force); + self + } + + /// Convenience method to set all Git options at once. + /// + /// # Arguments + /// + /// * `commit` - Whether to create a Git commit + /// * `tag` - Whether to create Git tags + /// * `push` - Whether to push tags to remote + /// + /// # Returns + /// + /// Self with all Git options set. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BumpApplyParams::new(".") + /// .with_git_options(true, true, false); + /// ``` + #[must_use] + pub fn with_git_options(mut self, commit: bool, tag: bool, push: bool) -> Self { + self.git_commit = Some(commit); + self.git_tag = Some(tag); + self.git_push = Some(push); + self + } +} + +/// 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}' +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpSnapshotParams { + /// 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, + + /// Filter to specific packages. + /// + /// When provided, only these packages will get snapshot versions. + /// Package names should include scope if applicable. + #[napi(ts_type = "string[] | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub packages: Option>, + + /// 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` + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub format: Option, +} + +#[allow(dead_code)] +impl BumpSnapshotParams { + /// Creates a new `BumpSnapshotParams` with the required root path. + /// + /// # Arguments + /// + /// * `root` - The workspace root directory path + /// + /// # Returns + /// + /// A new `BumpSnapshotParams` instance with default optional values. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BumpSnapshotParams::new("/path/to/workspace"); + /// ``` + #[must_use] + pub fn new(root: impl Into) -> Self { + Self { root: root.into(), config_path: None, packages: None, format: 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 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 snapshot format. + /// + /// # Arguments + /// + /// * `format` - The format template for snapshot versions + /// + /// # Returns + /// + /// Self with the format set. + /// + /// # Examples + /// + /// ```rust,ignore + /// let params = BumpSnapshotParams::new(".") + /// .with_format("{version}-{branch}.{short_commit}"); + /// ``` + #[must_use] + pub fn with_format(mut self, format: impl Into) -> Self { + self.format = Some(format.into()); + self + } +} + +// ============================================================================ +// Response Data Types - Supporting Types +// ============================================================================ + +/// Dependency update information for a package version bump. +/// +/// 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 +/// +/// - `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 DependencyUpdateInfo { +/// name: string; +/// dependencyType: 'regular' | 'dev' | 'peer' | 'optional'; +/// oldVersion: string; +/// newVersion: string; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const update: DependencyUpdateInfo = { +/// name: '@scope/core', +/// dependencyType: 'regular', +/// oldVersion: '^1.0.0', +/// newVersion: '^1.1.0' +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct DependencyUpdateInfo { + /// The dependency package name. + /// + /// This is the name of the package that was updated as a dependency. + /// May include scope (e.g., `@scope/package`). + pub name: String, + + /// The type of dependency. + /// + /// One of: `regular`, `dev`, `peer`, `optional` + pub dependency_type: String, + + /// The previous version specification. + /// + /// 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`). + pub old_version: String, + + /// The new version specification. + /// + /// This is the updated version range or exact version after the bump. + pub new_version: String, +} + +#[allow(dead_code)] +impl DependencyUpdateInfo { + /// Creates a new `DependencyUpdateInfo`. + /// + /// # Arguments + /// + /// * `name` - The dependency package name + /// * `dependency_type` - The type of dependency + /// * `old_version` - The previous version specification + /// * `new_version` - The new version specification + /// + /// # Returns + /// + /// A new `DependencyUpdateInfo` instance. + /// + /// # Examples + /// + /// ```rust,ignore + /// let update = DependencyUpdateInfo::new( + /// "@scope/core", + /// "regular", + /// "^1.0.0", + /// "^1.1.0" + /// ); + /// ``` + #[must_use] + pub fn new( + name: impl Into, + dependency_type: impl Into, + old_version: impl Into, + new_version: impl Into, + ) -> Self { + Self { + name: name.into(), + dependency_type: dependency_type.into(), + old_version: old_version.into(), + new_version: new_version.into(), + } + } + + /// Creates a regular (runtime) dependency update. + /// + /// # Arguments + /// + /// * `name` - The dependency package name + /// * `old_version` - The previous version specification + /// * `new_version` - The new version specification + /// + /// # Returns + /// + /// A new `DependencyUpdateInfo` with `dependency_type` set to `"regular"`. + #[must_use] + pub fn regular( + name: impl Into, + old_version: impl Into, + new_version: impl Into, + ) -> Self { + Self::new(name, "regular", old_version, new_version) + } + + /// Creates a dev dependency update. + /// + /// # Arguments + /// + /// * `name` - The dependency package name + /// * `old_version` - The previous version specification + /// * `new_version` - The new version specification + /// + /// # Returns + /// + /// A new `DependencyUpdateInfo` with `dependency_type` set to `"dev"`. + #[must_use] + pub fn dev( + name: impl Into, + old_version: impl Into, + new_version: impl Into, + ) -> Self { + Self::new(name, "dev", old_version, new_version) + } + + /// Creates a peer dependency update. + /// + /// # Arguments + /// + /// * `name` - The dependency package name + /// * `old_version` - The previous version specification + /// * `new_version` - The new version specification + /// + /// # Returns + /// + /// A new `DependencyUpdateInfo` with `dependency_type` set to `"peer"`. + #[must_use] + pub fn peer( + name: impl Into, + old_version: impl Into, + new_version: impl Into, + ) -> Self { + Self::new(name, "peer", old_version, new_version) + } + + /// Creates an optional dependency update. + /// + /// # Arguments + /// + /// * `name` - The dependency package name + /// * `old_version` - The previous version specification + /// * `new_version` - The new version specification + /// + /// # Returns + /// + /// A new `DependencyUpdateInfo` with `dependency_type` set to `"optional"`. + #[must_use] + pub fn optional( + name: impl Into, + old_version: impl Into, + new_version: impl Into, + ) -> Self { + Self::new(name, "optional", old_version, new_version) + } +} + +/// Version information for a package being bumped. +/// +/// This structure captures the full version transition for a package, +/// including the bump type and any dependency updates that resulted +/// from this package being bumped. +/// +/// # Fields +/// +/// - `name`: Package name (may include scope) +/// - `path`: Package path relative to workspace root +/// - `current_version`: Current version before bump +/// - `next_version`: Next version after bump +/// - `bump`: Bump type applied (major, minor, patch, none) +/// - `dependency_updates`: List of dependency updates for this package +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface PackageVersionInfo { +/// name: string; +/// path: string; +/// currentVersion: string; +/// nextVersion: string; +/// bump: 'major' | 'minor' | 'patch' | 'none'; +/// dependencyUpdates: DependencyUpdateInfo[]; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const pkg: PackageVersionInfo = { +/// name: '@scope/core', +/// path: 'packages/core', +/// currentVersion: '1.0.0', +/// nextVersion: '1.1.0', +/// bump: 'minor', +/// dependencyUpdates: [] +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct PackageVersionInfo { + /// Package name. + /// + /// The full package name, including scope if applicable + /// (e.g., `@scope/package` or `package`). + pub name: String, + + /// Package path relative to workspace root. + /// + /// The file system path to the package directory, relative to + /// the workspace root (e.g., `packages/core`). + pub path: String, + + /// Current version before bump. + /// + /// The version string currently in package.json before any + /// changes are applied (e.g., `1.0.0`). + pub current_version: String, + + /// Next version after bump. + /// + /// The version string that will be (or was) written to + /// package.json after the bump (e.g., `1.1.0`). + pub next_version: String, + + /// Bump type applied. + /// + /// One of: `major`, `minor`, `patch`, `none` + pub bump: String, + + /// List of dependency updates for this package. + /// + /// When this package depends on other packages that were bumped, + /// those dependency version specifications are also updated. + pub dependency_updates: Vec, +} + +#[allow(dead_code)] +impl PackageVersionInfo { + /// Creates a new `PackageVersionInfo`. + /// + /// # Arguments + /// + /// * `name` - Package name (may include scope) + /// * `path` - Package path relative to workspace root + /// * `current_version` - Current version before bump + /// * `next_version` - Next version after bump + /// * `bump` - Bump type applied + /// + /// # Returns + /// + /// A new `PackageVersionInfo` with empty dependency updates. + /// + /// # Examples + /// + /// ```rust,ignore + /// let info = PackageVersionInfo::new( + /// "@scope/core", + /// "packages/core", + /// "1.0.0", + /// "1.1.0", + /// "minor" + /// ); + /// ``` + #[must_use] + pub fn new( + name: impl Into, + path: impl Into, + current_version: impl Into, + next_version: impl Into, + bump: impl Into, + ) -> Self { + Self { + name: name.into(), + path: path.into(), + current_version: current_version.into(), + next_version: next_version.into(), + bump: bump.into(), + dependency_updates: Vec::new(), + } + } + + /// Sets the dependency updates for this package. + /// + /// # Arguments + /// + /// * `updates` - List of dependency updates + /// + /// # Returns + /// + /// Self with the dependency updates set. + #[must_use] + pub fn with_dependency_updates(mut self, updates: Vec) -> Self { + self.dependency_updates = updates; + self + } + + /// Adds a single dependency update to this package. + /// + /// # Arguments + /// + /// * `update` - The dependency update to add + /// + /// # Returns + /// + /// Self with the dependency update added. + #[must_use] + pub fn add_dependency_update(mut self, update: DependencyUpdateInfo) -> Self { + self.dependency_updates.push(update); + self + } + + /// Returns true if this package has a major bump. + #[must_use] + pub fn is_major(&self) -> bool { + self.bump == "major" + } + + /// Returns true if this package has a minor bump. + #[must_use] + pub fn is_minor(&self) -> bool { + self.bump == "minor" + } + + /// Returns true if this package has a patch bump. + #[must_use] + pub fn is_patch(&self) -> bool { + self.bump == "patch" + } + + /// Returns true if this package has no bump. + #[must_use] + pub fn is_none(&self) -> bool { + self.bump == "none" + } +} + +/// Snapshot version information for a package. +/// +/// This structure captures the snapshot version generated for a package, +/// including both the original version and the generated snapshot version. +/// +/// # Fields +/// +/// - `name`: Package name (may include scope) +/// - `path`: Package path relative to workspace root +/// - `original_version`: Original version from package.json +/// - `snapshot_version`: Generated snapshot version +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface SnapshotVersionInfo { +/// name: string; +/// path: string; +/// originalVersion: string; +/// snapshotVersion: string; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const snapshot: SnapshotVersionInfo = { +/// name: '@scope/core', +/// path: 'packages/core', +/// originalVersion: '1.0.0', +/// snapshotVersion: '1.0.0-snapshot.abc123f' +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct SnapshotVersionInfo { + /// Package name. + /// + /// The full package name, including scope if applicable. + pub name: String, + + /// Package path relative to workspace root. + /// + /// The file system path to the package directory. + pub path: String, + + /// Original version from package.json. + /// + /// The version before snapshot generation (e.g., `1.0.0`). + pub original_version: String, + + /// Generated snapshot version. + /// + /// The snapshot version generated using the format template + /// (e.g., `1.0.0-snapshot.abc123f`). + pub snapshot_version: String, +} + +#[allow(dead_code)] +impl SnapshotVersionInfo { + /// Creates a new `SnapshotVersionInfo`. + /// + /// # Arguments + /// + /// * `name` - Package name (may include scope) + /// * `path` - Package path relative to workspace root + /// * `original_version` - Original version from package.json + /// * `snapshot_version` - Generated snapshot version + /// + /// # Returns + /// + /// A new `SnapshotVersionInfo` instance. + /// + /// # Examples + /// + /// ```rust,ignore + /// let info = SnapshotVersionInfo::new( + /// "@scope/core", + /// "packages/core", + /// "1.0.0", + /// "1.0.0-snapshot.abc123f" + /// ); + /// ``` + #[must_use] + pub fn new( + name: impl Into, + path: impl Into, + original_version: impl Into, + snapshot_version: impl Into, + ) -> Self { + Self { + name: name.into(), + path: path.into(), + original_version: original_version.into(), + snapshot_version: snapshot_version.into(), + } + } +} + +/// 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 +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpSummaryInfo { + /// Total number of packages affected by the bump. + pub total_packages: u32, + + /// Number of major version bumps. + pub major_bumps: u32, + + /// Number of minor version bumps. + pub minor_bumps: u32, + + /// Number of patch version bumps. + pub patch_bumps: u32, +} + +#[allow(dead_code)] +impl BumpSummaryInfo { + /// Creates a new `BumpSummaryInfo`. + /// + /// # Arguments + /// + /// * `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 + /// + /// # Returns + /// + /// A new `BumpSummaryInfo` instance. + #[must_use] + pub fn new(total_packages: u32, major_bumps: u32, minor_bumps: u32, patch_bumps: u32) -> Self { + Self { total_packages, major_bumps, minor_bumps, patch_bumps } + } + + /// Creates an empty summary (no bumps). + /// + /// # Returns + /// + /// A new `BumpSummaryInfo` with all counts set to zero. + #[must_use] + pub fn empty() -> Self { + Self { total_packages: 0, major_bumps: 0, minor_bumps: 0, patch_bumps: 0 } + } + + /// Creates a summary from a list of package version info. + /// + /// # Arguments + /// + /// * `packages` - List of package version information + /// + /// # Returns + /// + /// A new `BumpSummaryInfo` computed from the packages. + #[must_use] + #[allow(clippy::cast_possible_truncation)] + // Justification: It's practically impossible to have more than 4 billion packages in a + // workspace. The u32 limit of ~4.29 billion packages is sufficient for any real-world + // monorepo. This truncation would only occur in an unrealistic edge case. + pub fn from_packages(packages: &[PackageVersionInfo]) -> Self { + let total_packages = packages.len() as u32; + let major_bumps = packages.iter().filter(|p| p.is_major()).count() as u32; + let minor_bumps = packages.iter().filter(|p| p.is_minor()).count() as u32; + let patch_bumps = packages.iter().filter(|p| p.is_patch()).count() as u32; + + Self { total_packages, major_bumps, minor_bumps, patch_bumps } + } + + /// Returns true if there are any breaking changes (major bumps). + #[must_use] + pub fn has_breaking_changes(&self) -> bool { + self.major_bumps > 0 + } +} + +// ============================================================================ +// Response Data Types - Main Data Structures +// ============================================================================ + +/// Response data for the bump preview command. +/// +/// This structure contains the complete preview of version bumps that +/// would be applied, including all package versions, dependency updates, +/// and a summary of the changes. +/// +/// # Fields +/// +/// - `strategy`: Version strategy used (independent or unified) +/// - `packages`: List of packages that will be bumped +/// - `summary`: Summary statistics of the bump +/// - `changesets`: IDs of changesets that will be consumed +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpPreviewData { +/// strategy: 'independent' | 'unified'; +/// packages: PackageVersionInfo[]; +/// summary: BumpSummaryInfo; +/// changesets: string[]; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const preview: BumpPreviewData = { +/// strategy: 'independent', +/// packages: [ +/// { +/// name: '@scope/core', +/// path: 'packages/core', +/// currentVersion: '1.0.0', +/// nextVersion: '1.1.0', +/// bump: 'minor', +/// dependencyUpdates: [] +/// } +/// ], +/// summary: { +/// totalPackages: 1, +/// majorBumps: 0, +/// minorBumps: 1, +/// patchBumps: 0 +/// }, +/// changesets: ['feature-new-api'] +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpPreviewData { + /// Version strategy used. + /// + /// Either `"independent"` (each package has its own version) or + /// `"unified"` (all packages share the same version). + pub strategy: String, + + /// List of packages that will be bumped. + /// + /// Contains detailed version transition information for each + /// package, including dependency updates. + pub packages: Vec, + + /// Summary statistics of the bump. + /// + /// Aggregated counts of total packages and bump types. + pub summary: BumpSummaryInfo, + + /// IDs of changesets that will be consumed. + /// + /// These changesets will be archived after the bump is applied. + pub changesets: Vec, +} + +#[allow(dead_code)] +impl BumpPreviewData { + /// Creates a new `BumpPreviewData`. + /// + /// # Arguments + /// + /// * `strategy` - Version strategy (independent or unified) + /// * `packages` - List of package version information + /// * `changesets` - List of changeset IDs to be consumed + /// + /// # Returns + /// + /// A new `BumpPreviewData` with summary computed from packages. + #[must_use] + pub fn new( + strategy: impl Into, + packages: Vec, + changesets: Vec, + ) -> Self { + let summary = BumpSummaryInfo::from_packages(&packages); + Self { strategy: strategy.into(), packages, summary, changesets } + } + + /// Creates an empty preview (no bumps). + /// + /// # Arguments + /// + /// * `strategy` - Version strategy (independent or unified) + /// + /// # Returns + /// + /// A new empty `BumpPreviewData`. + #[must_use] + pub fn empty(strategy: impl Into) -> Self { + Self { + strategy: strategy.into(), + packages: Vec::new(), + summary: BumpSummaryInfo::empty(), + changesets: Vec::new(), + } + } + + /// Returns true if there are packages to bump. + #[must_use] + pub fn has_packages(&self) -> bool { + !self.packages.is_empty() + } + + /// Returns true if there are breaking changes. + #[must_use] + pub fn has_breaking_changes(&self) -> bool { + self.summary.has_breaking_changes() + } +} + +/// Response data for the bump apply command. +/// +/// This structure contains the results of applying version bumps, +/// including the number of packages updated, changesets archived, +/// files modified, and Git integration results. +/// +/// # Fields +/// +/// - `strategy`: Version strategy used (independent or unified) +/// - `packages_updated`: Number of packages that were bumped +/// - `changesets_archived`: Number of changesets that were archived +/// - `files_modified`: List of files that were modified +/// - `tags_created`: List of Git tags that were created +/// - `commit_sha`: Git commit SHA (if gitCommit was true) +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpApplyData { +/// strategy: 'independent' | 'unified'; +/// packagesUpdated: number; +/// changesetsArchived: number; +/// filesModified: string[]; +/// tagsCreated: string[]; +/// commitSha?: string; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const apply: BumpApplyData = { +/// strategy: 'independent', +/// packagesUpdated: 3, +/// changesetsArchived: 2, +/// filesModified: [ +/// 'packages/core/package.json', +/// 'packages/core/CHANGELOG.md', +/// 'packages/utils/package.json' +/// ], +/// tagsCreated: ['@scope/core@1.1.0', '@scope/utils@2.0.0'], +/// commitSha: 'abc123def456789' +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpApplyData { + /// Version strategy used. + /// + /// Either `"independent"` or `"unified"`. + pub strategy: String, + + /// Number of packages that were bumped. + pub packages_updated: u32, + + /// Number of changesets that were archived. + pub changesets_archived: u32, + + /// List of files that were modified. + /// + /// Paths are relative to the workspace root. + pub files_modified: Vec, + + /// List of Git tags that were created. + /// + /// Format: `{package}@{version}` (e.g., `@scope/core@1.1.0`) + pub tags_created: Vec, + + /// Git commit SHA (if gitCommit was true). + /// + /// The full 40-character SHA of the commit containing + /// all version bump changes. + #[napi(ts_type = "string | undefined")] + #[serde(skip_serializing_if = "Option::is_none")] + pub commit_sha: Option, +} + +#[allow(dead_code)] +impl BumpApplyData { + /// Creates a new `BumpApplyData`. + /// + /// # Arguments + /// + /// * `strategy` - Version strategy (independent or unified) + /// * `packages_updated` - Number of packages updated + /// * `changesets_archived` - Number of changesets archived + /// + /// # Returns + /// + /// A new `BumpApplyData` with empty lists for files and tags. + #[must_use] + pub fn new( + strategy: impl Into, + packages_updated: u32, + changesets_archived: u32, + ) -> Self { + Self { + strategy: strategy.into(), + packages_updated, + changesets_archived, + files_modified: Vec::new(), + tags_created: Vec::new(), + commit_sha: None, + } + } + + /// Sets the files modified. + /// + /// # Arguments + /// + /// * `files` - List of modified file paths + /// + /// # Returns + /// + /// Self with the files modified set. + #[must_use] + pub fn with_files_modified(mut self, files: Vec) -> Self { + self.files_modified = files; + self + } + + /// Sets the tags created. + /// + /// # Arguments + /// + /// * `tags` - List of Git tags created + /// + /// # Returns + /// + /// Self with the tags created set. + #[must_use] + pub fn with_tags_created(mut self, tags: Vec) -> Self { + self.tags_created = tags; + self + } + + /// Sets the commit SHA. + /// + /// # Arguments + /// + /// * `sha` - The Git commit SHA + /// + /// # Returns + /// + /// Self with the commit SHA set. + #[must_use] + pub fn with_commit_sha(mut self, sha: impl Into) -> Self { + self.commit_sha = Some(sha.into()); + self + } + + /// Returns true if a Git commit was created. + #[must_use] + pub fn has_commit(&self) -> bool { + self.commit_sha.is_some() + } + + /// Returns true if Git tags were created. + #[must_use] + pub fn has_tags(&self) -> bool { + !self.tags_created.is_empty() + } +} + +/// 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}' +/// }; +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpSnapshotData { + /// Version strategy used. + /// + /// Either `"independent"` or `"unified"`. + pub strategy: String, + + /// List of packages with snapshot versions. + /// + /// Contains the original and generated snapshot version for each package. + pub packages: Vec, + + /// The format template that was used. + /// + /// This is either the user-provided format or the default format + /// `{version}-snapshot.{short_commit}`. + pub format: String, +} + +#[allow(dead_code)] +impl BumpSnapshotData { + /// Creates a new `BumpSnapshotData`. + /// + /// # Arguments + /// + /// * `strategy` - Version strategy (independent or unified) + /// * `packages` - List of snapshot version information + /// * `format` - The format template used + /// + /// # Returns + /// + /// A new `BumpSnapshotData` instance. + #[must_use] + pub fn new( + strategy: impl Into, + packages: Vec, + format: impl Into, + ) -> Self { + Self { strategy: strategy.into(), packages, format: format.into() } + } + + /// Creates an empty snapshot data. + /// + /// # Arguments + /// + /// * `strategy` - Version strategy (independent or unified) + /// * `format` - The format template used + /// + /// # Returns + /// + /// A new empty `BumpSnapshotData`. + #[must_use] + pub fn empty(strategy: impl Into, format: impl Into) -> Self { + Self { strategy: strategy.into(), packages: Vec::new(), format: format.into() } + } + + /// Returns the number of packages with snapshot versions. + #[must_use] + pub fn package_count(&self) -> usize { + self.packages.len() + } +} + +// ============================================================================ +// API Response Types +// ============================================================================ + +/// API response for the bump preview command. +/// +/// This structure wraps `BumpPreviewData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpPreviewApiResponse { +/// success: boolean; +/// data?: BumpPreviewData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await bumpPreview({ root: '.' }); +/// +/// if (result.success) { +/// console.log(`Will bump ${result.data.packages.length} packages`); +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpPreviewApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The preview data if successful. + /// + /// Contains the complete preview of version bumps including all + /// packages, dependency updates, and summary. + #[napi(ts_type = "BumpPreviewData | 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 BumpPreviewApiResponse { + /// Creates a successful response with preview data. + /// + /// # Arguments + /// + /// * `data` - The bump preview data + /// + /// # Returns + /// + /// A new successful `BumpPreviewApiResponse`. + #[must_use] + pub fn success(data: BumpPreviewData) -> 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 `BumpPreviewApiResponse`. + #[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 bump apply command. +/// +/// This structure wraps `BumpApplyData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpApplyApiResponse { +/// success: boolean; +/// data?: BumpApplyData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// const result = await bumpApply({ +/// root: '.', +/// gitCommit: true, +/// gitTag: true +/// }); +/// +/// if (result.success) { +/// console.log(`Updated ${result.data.packagesUpdated} packages`); +/// if (result.data.commitSha) { +/// console.log(`Commit: ${result.data.commitSha}`); +/// } +/// } else { +/// console.error(`Error: ${result.error.message}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpApplyApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The apply result data if successful. + /// + /// Contains information about what was updated, including + /// packages, files, and Git integration results. + #[napi(ts_type = "BumpApplyData | 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 BumpApplyApiResponse { + /// Creates a successful response with apply data. + /// + /// # Arguments + /// + /// * `data` - The bump apply data + /// + /// # Returns + /// + /// A new successful `BumpApplyApiResponse`. + #[must_use] + pub fn success(data: BumpApplyData) -> 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 `BumpApplyApiResponse`. + #[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 bump snapshot command. +/// +/// This structure wraps `BumpSnapshotData` in the standard `ApiResponse` +/// format, providing a consistent interface for success and error cases. +/// +/// # TypeScript Definition +/// +/// ```typescript +/// interface BumpSnapshotApiResponse { +/// success: boolean; +/// data?: BumpSnapshotData; +/// error?: ErrorInfo; +/// } +/// ``` +/// +/// # Examples +/// +/// ```typescript +/// 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}`); +/// } +/// ``` +#[napi(object)] +#[derive(Debug, Clone, Serialize)] +pub struct BumpSnapshotApiResponse { + /// Whether the operation was successful. + pub success: bool, + + /// The snapshot result data if successful. + /// + /// Contains the list of packages with their generated + /// snapshot versions. + #[napi(ts_type = "BumpSnapshotData | 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 BumpSnapshotApiResponse { + /// Creates a successful response with snapshot data. + /// + /// # Arguments + /// + /// * `data` - The bump snapshot data + /// + /// # Returns + /// + /// A new successful `BumpSnapshotApiResponse`. + #[must_use] + pub fn success(data: BumpSnapshotData) -> 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 `BumpSnapshotApiResponse`. + #[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 5.1 - Bump Types -// This module will contain: -// -// Re-exports from sublime_pkg_tools: -// - pub use sublime_pkg_tools::version::{VersionResolution, PackageUpdate, ApplyResult, ApplySummary}; -// -// NAPI-specific types: -// - BumpPreviewParams: { root, showDiff?, packages? } -// - BumpPreviewData: { packages: PackageVersionInfo[], dependencyUpdates, summary } -// - BumpApplyParams: { root, execute?, gitCommit?, gitTag?, gitPush?, packages? } -// - BumpApplyData: { applied: PackageVersionInfo[], summary, gitCommitSha?, gitTags? } -// - BumpSnapshotParams: { root, format?, packages? } -// - BumpSnapshotData: { packages: SnapshotVersionInfo[], format } -// -// Shared types: -// - PackageVersionInfo: { name, path, currentVersion, nextVersion, bump, dependencyUpdates } -// - SnapshotVersionInfo: { name, path, originalVersion, snapshotVersion } + /// Returns true if the response indicates failure. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.success + } +} diff --git a/crates/node/src/types/mod.rs b/crates/node/src/types/mod.rs index fe7fdf2f..05e9b190 100644 --- a/crates/node/src/types/mod.rs +++ b/crates/node/src/types/mod.rs @@ -119,9 +119,36 @@ pub(crate) use changeset::{ VALID_SORT_OPTIONS, }; -// TODO: will be implemented on story 5.1 (bump types) +// Bump types (Story 5.1 - Implemented) pub(crate) mod bump; +// Re-export bump types for easier access +// Allow unused imports - these will be used by bump commands (Stories 5.2-5.4) +#[allow(unused_imports)] +pub(crate) use bump::{ + BumpApplyApiResponse, + BumpApplyData, + BumpApplyParams, + // API Responses + BumpPreviewApiResponse, + // Response Data + BumpPreviewData, + // Input Parameters + BumpPreviewParams, + BumpSnapshotApiResponse, + BumpSnapshotData, + BumpSnapshotParams, + BumpSummaryInfo, + // Constants + COMMON_PRERELEASE_TAGS, + DEFAULT_SNAPSHOT_FORMAT, + DependencyUpdateInfo, + // Supporting Types + PackageVersionInfo, + SnapshotVersionInfo, + VALID_DEPENDENCY_TYPES, +}; + // TODO: will be implemented on story 8.1 (upgrade types) pub(crate) mod upgrade; diff --git a/packages/workspace-tools/npm/darwin-arm64/package.json b/packages/workspace-tools/npm/darwin-arm64/package.json index 95fa2928..d76ce2e1 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.10", + "version": "2.0.11", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/darwin-x64/package.json b/packages/workspace-tools/npm/darwin-x64/package.json index dea440d6..e8c24ca8 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.10", + "version": "2.0.11", "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 489c349b..c353ff94 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.10", + "version": "2.0.11", "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 ed5e6938..837e01ae 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.10", + "version": "2.0.11", "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 8c000955..a9facebb 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.10", + "version": "2.0.11", "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 9c76c2f8..fd1e4e57 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.10", + "version": "2.0.11", "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 282e34a2..3d78f5da 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.10", + "version": "2.0.11", "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 f705a433..2eada4c5 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.10", + "version": "2.0.11", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/package.json b/packages/workspace-tools/package.json index 96ecf4d7..f51c831c 100644 --- a/packages/workspace-tools/package.json +++ b/packages/workspace-tools/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools", - "version": "2.0.10", + "version": "2.0.11", "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 be3c5b00..bd6b2f4d 100644 --- a/packages/workspace-tools/src/binding.d.ts +++ b/packages/workspace-tools/src/binding.d.ts @@ -58,6 +58,760 @@ export interface BranchInfo { name: string } +/** + * API response for the bump apply command. + * + * This structure wraps `BumpApplyData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. + * + * # TypeScript Definition + * + * ```typescript + * interface BumpApplyApiResponse { + * success: boolean; + * data?: BumpApplyData; + * error?: ErrorInfo; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await bumpApply({ + * root: '.', + * gitCommit: true, + * gitTag: true + * }); + * + * if (result.success) { + * console.log(`Updated ${result.data.packagesUpdated} packages`); + * if (result.data.commitSha) { + * console.log(`Commit: ${result.data.commitSha}`); + * } + * } else { + * console.error(`Error: ${result.error.message}`); + * } + * ``` + */ +export interface BumpApplyApiResponse { + /** Whether the operation was successful. */ + success: boolean + /** + * The apply result data if successful. + * + * Contains information about what was updated, including + * packages, files, and Git integration results. + */ + data?: BumpApplyData | 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 apply command. + * + * This structure contains the results of applying version bumps, + * including the number of packages updated, changesets archived, + * files modified, and Git integration results. + * + * # Fields + * + * - `strategy`: Version strategy used (independent or unified) + * - `packages_updated`: Number of packages that were bumped + * - `changesets_archived`: Number of changesets that were archived + * - `files_modified`: List of files that were modified + * - `tags_created`: List of Git tags that were created + * - `commit_sha`: Git commit SHA (if gitCommit was true) + * + * # TypeScript Definition + * + * ```typescript + * interface BumpApplyData { + * strategy: 'independent' | 'unified'; + * packagesUpdated: number; + * changesetsArchived: number; + * filesModified: string[]; + * tagsCreated: string[]; + * commitSha?: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const apply: BumpApplyData = { + * strategy: 'independent', + * packagesUpdated: 3, + * changesetsArchived: 2, + * filesModified: [ + * 'packages/core/package.json', + * 'packages/core/CHANGELOG.md', + * 'packages/utils/package.json' + * ], + * tagsCreated: ['@scope/core@1.1.0', '@scope/utils@2.0.0'], + * commitSha: 'abc123def456789' + * }; + * ``` + */ +export interface BumpApplyData { + /** + * Version strategy used. + * + * Either `"independent"` or `"unified"`. + */ + strategy: string + /** Number of packages that were bumped. */ + packagesUpdated: number + /** Number of changesets that were archived. */ + changesetsArchived: number + /** + * List of files that were modified. + * + * Paths are relative to the workspace root. + */ + filesModified: Array + /** + * 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 +} + +/** + * Input parameters for the bump apply 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. + * + * # Fields + * + * - `root`: The workspace root directory path (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 { + * root: 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 = { + * root: '.', + * prerelease: 'beta', + * gitCommit: true, + * gitTag: true + * }; + * ``` + */ +export interface BumpApplyParams { + /** + * 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 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 +} + +/** + * API response for the bump preview command. + * + * This structure wraps `BumpPreviewData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. + * + * # TypeScript Definition + * + * ```typescript + * interface BumpPreviewApiResponse { + * success: boolean; + * data?: BumpPreviewData; + * error?: ErrorInfo; + * } + * ``` + * + * # Examples + * + * ```typescript + * const result = await bumpPreview({ root: '.' }); + * + * if (result.success) { + * console.log(`Will bump ${result.data.packages.length} packages`); + * } else { + * console.error(`Error: ${result.error.message}`); + * } + * ``` + */ +export interface BumpPreviewApiResponse { + /** Whether the operation was successful. */ + success: boolean + /** + * The preview data if successful. + * + * Contains the complete preview of version bumps including all + * packages, dependency updates, and summary. + */ + data?: BumpPreviewData | 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 preview command. + * + * This structure contains the complete preview of version bumps that + * would be applied, including all package versions, dependency updates, + * and a summary of the changes. + * + * # Fields + * + * - `strategy`: Version strategy used (independent or unified) + * - `packages`: List of packages that will be bumped + * - `summary`: Summary statistics of the bump + * - `changesets`: IDs of changesets that will be consumed + * + * # TypeScript Definition + * + * ```typescript + * interface BumpPreviewData { + * strategy: 'independent' | 'unified'; + * packages: PackageVersionInfo[]; + * summary: BumpSummaryInfo; + * changesets: string[]; + * } + * ``` + * + * # Examples + * + * ```typescript + * const preview: BumpPreviewData = { + * strategy: 'independent', + * packages: [ + * { + * name: '@scope/core', + * path: 'packages/core', + * currentVersion: '1.0.0', + * nextVersion: '1.1.0', + * bump: 'minor', + * dependencyUpdates: [] + * } + * ], + * summary: { + * totalPackages: 1, + * majorBumps: 0, + * minorBumps: 1, + * patchBumps: 0 + * }, + * changesets: ['feature-new-api'] + * }; + * ``` + */ +export interface BumpPreviewData { + /** + * Version strategy used. + * + * Either `"independent"` (each package has its own version) or + * `"unified"` (all packages share the same version). + */ + strategy: string + /** + * List of packages that will be bumped. + * + * Contains detailed version transition information for each + * package, including dependency updates. + */ + packages: Array + /** + * Summary statistics of the bump. + * + * Aggregated counts of total packages and bump types. + */ + 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 preview 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. + * + * # 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 + * + * # TypeScript Definition + * + * ```typescript + * interface BumpPreviewParams { + * root: string; + * configPath?: string; + * packages?: string[]; + * showDiff?: boolean; + * } + * ``` + * + * # Examples + * + * ```typescript + * // Minimal params - preview all packages + * const minimal: BumpPreviewParams = { root: '.' }; + * + * // Preview specific packages with diff + * const filtered: BumpPreviewParams = { + * root: '/path/to/workspace', + * packages: ['@scope/pkg1', '@scope/pkg2'], + * showDiff: true + * }; + * ``` + */ +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 (`repo.config.json`, `repo.config.yaml`, etc.) + * 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`). + */ + packages?: string[] | undefined + /** + * Whether to show detailed version diffs. + * + * When `true`, includes detailed information about what changes would + * be made to each package, including dependency updates. + */ + showDiff?: boolean | undefined +} + +/** + * API response for the bump snapshot command. + * + * This structure wraps `BumpSnapshotData` in the standard `ApiResponse` + * format, providing a consistent interface for success and error cases. + * + * # TypeScript Definition + * + * ```typescript + * interface BumpSnapshotApiResponse { + * success: boolean; + * data?: BumpSnapshotData; + * error?: ErrorInfo; + * } + * ``` + * + * # Examples + * + * ```typescript + * 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 +} + /** * Add a new changeset to the workspace. * @@ -1954,6 +2708,71 @@ export interface ChangesetUpdateParams { environments?: string[] | undefined } +/** + * Dependency update information for a package version bump. + * + * 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 + * + * - `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 DependencyUpdateInfo { + * name: string; + * dependencyType: 'regular' | 'dev' | 'peer' | 'optional'; + * oldVersion: string; + * newVersion: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const update: DependencyUpdateInfo = { + * name: '@scope/core', + * dependencyType: 'regular', + * oldVersion: '^1.0.0', + * newVersion: '^1.1.0' + * }; + * ``` + */ +export interface DependencyUpdateInfo { + /** + * The dependency package name. + * + * This is the name of the package that was updated as a dependency. + * May include scope (e.g., `@scope/package`). + */ + name: string + /** + * The type of dependency. + * + * One of: `regular`, `dev`, `peer`, `optional` + */ + dependencyType: string + /** + * The previous version specification. + * + * 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`). + */ + oldVersion: string + /** + * The new version specification. + * + * This is the updated version range or exact version after the bump. + */ + newVersion: string +} + /** * Error information structure for Node.js bindings. * @@ -2612,6 +3431,92 @@ export interface PackageManagerInfo { lockFile: string } +/** + * Version information for a package being bumped. + * + * This structure captures the full version transition for a package, + * including the bump type and any dependency updates that resulted + * from this package being bumped. + * + * # Fields + * + * - `name`: Package name (may include scope) + * - `path`: Package path relative to workspace root + * - `current_version`: Current version before bump + * - `next_version`: Next version after bump + * - `bump`: Bump type applied (major, minor, patch, none) + * - `dependency_updates`: List of dependency updates for this package + * + * # TypeScript Definition + * + * ```typescript + * interface PackageVersionInfo { + * name: string; + * path: string; + * currentVersion: string; + * nextVersion: string; + * bump: 'major' | 'minor' | 'patch' | 'none'; + * dependencyUpdates: DependencyUpdateInfo[]; + * } + * ``` + * + * # Examples + * + * ```typescript + * const pkg: PackageVersionInfo = { + * name: '@scope/core', + * path: 'packages/core', + * currentVersion: '1.0.0', + * nextVersion: '1.1.0', + * bump: 'minor', + * dependencyUpdates: [] + * }; + * ``` + */ +export interface PackageVersionInfo { + /** + * Package name. + * + * The full package name, including scope if applicable + * (e.g., `@scope/package` or `package`). + */ + name: string + /** + * Package path relative to workspace root. + * + * The file system path to the package directory, relative to + * the workspace root (e.g., `packages/core`). + */ + path: string + /** + * Current version before bump. + * + * The version string currently in package.json before any + * changes are applied (e.g., `1.0.0`). + */ + currentVersion: string + /** + * Next version after bump. + * + * The version string that will be (or was) written to + * package.json after the bump (e.g., `1.1.0`). + */ + nextVersion: string + /** + * Bump type applied. + * + * One of: `major`, `minor`, `patch`, `none` + */ + bump: string + /** + * List of dependency updates for this package. + * + * When this package depends on other packages that were bumped, + * those dependency version specifications are also updated. + */ + dependencyUpdates: Array +} + /** * Entry in the released versions map. * @@ -2737,6 +3642,69 @@ export interface RepositoryInfo { monorepoType?: string | undefined } +/** + * Snapshot version information for a package. + * + * This structure captures the snapshot version generated for a package, + * including both the original version and the generated snapshot version. + * + * # Fields + * + * - `name`: Package name (may include scope) + * - `path`: Package path relative to workspace root + * - `original_version`: Original version from package.json + * - `snapshot_version`: Generated snapshot version + * + * # TypeScript Definition + * + * ```typescript + * interface SnapshotVersionInfo { + * name: string; + * path: string; + * originalVersion: string; + * snapshotVersion: string; + * } + * ``` + * + * # Examples + * + * ```typescript + * const snapshot: SnapshotVersionInfo = { + * name: '@scope/core', + * path: 'packages/core', + * originalVersion: '1.0.0', + * snapshotVersion: '1.0.0-snapshot.abc123f' + * }; + * ``` + */ +export interface SnapshotVersionInfo { + /** + * Package name. + * + * The full package name, including scope if applicable. + */ + name: string + /** + * Package path relative to workspace root. + * + * The file system path to the package directory. + */ + path: string + /** + * Original version from package.json. + * + * The version before snapshot generation (e.g., `1.0.0`). + */ + originalVersion: string + /** + * Generated snapshot version. + * + * The snapshot version generated using the format template + * (e.g., `1.0.0-snapshot.abc123f`). + */ + snapshotVersion: string +} + /** * Get workspace status information. * diff --git a/packages/workspace-tools/src/binding.js b/packages/workspace-tools/src/binding.js index 2ffd16cd..00f77418 100644 --- a/packages/workspace-tools/src/binding.js +++ b/packages/workspace-tools/src/binding.js @@ -77,8 +77,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-android-arm64') const bindingPackageVersion = require('@websublime/workspace-tools-android-arm64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -93,8 +93,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-android-arm-eabi') const bindingPackageVersion = require('@websublime/workspace-tools-android-arm-eabi/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -114,8 +114,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-win32-x64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-win32-x64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -130,8 +130,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-win32-x64-msvc') const bindingPackageVersion = require('@websublime/workspace-tools-win32-x64-msvc/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -147,8 +147,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-win32-ia32-msvc') const bindingPackageVersion = require('@websublime/workspace-tools-win32-ia32-msvc/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -163,8 +163,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-win32-arm64-msvc') const bindingPackageVersion = require('@websublime/workspace-tools-win32-arm64-msvc/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -182,8 +182,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-darwin-universal') const bindingPackageVersion = require('@websublime/workspace-tools-darwin-universal/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -198,8 +198,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-darwin-x64') const bindingPackageVersion = require('@websublime/workspace-tools-darwin-x64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -214,8 +214,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-darwin-arm64') const bindingPackageVersion = require('@websublime/workspace-tools-darwin-arm64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -234,8 +234,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-freebsd-x64') const bindingPackageVersion = require('@websublime/workspace-tools-freebsd-x64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -250,8 +250,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-freebsd-arm64') const bindingPackageVersion = require('@websublime/workspace-tools-freebsd-arm64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -271,8 +271,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-x64-musl') const bindingPackageVersion = require('@websublime/workspace-tools-linux-x64-musl/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -287,8 +287,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-x64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-x64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -305,8 +305,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-arm64-musl') const bindingPackageVersion = require('@websublime/workspace-tools-linux-arm64-musl/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -321,8 +321,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-arm64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-arm64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -339,8 +339,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-arm-musleabihf') const bindingPackageVersion = require('@websublime/workspace-tools-linux-arm-musleabihf/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -355,8 +355,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-arm-gnueabihf') const bindingPackageVersion = require('@websublime/workspace-tools-linux-arm-gnueabihf/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -373,8 +373,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-loong64-musl') const bindingPackageVersion = require('@websublime/workspace-tools-linux-loong64-musl/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -389,8 +389,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-loong64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-loong64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -407,8 +407,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-riscv64-musl') const bindingPackageVersion = require('@websublime/workspace-tools-linux-riscv64-musl/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -423,8 +423,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-riscv64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-riscv64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -440,8 +440,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-ppc64-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-ppc64-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -456,8 +456,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-linux-s390x-gnu') const bindingPackageVersion = require('@websublime/workspace-tools-linux-s390x-gnu/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -476,8 +476,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-openharmony-arm64') const bindingPackageVersion = require('@websublime/workspace-tools-openharmony-arm64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -492,8 +492,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-openharmony-x64') const bindingPackageVersion = require('@websublime/workspace-tools-openharmony-x64/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -508,8 +508,8 @@ function requireNative() { try { const binding = require('@websublime/workspace-tools-openharmony-arm') const bindingPackageVersion = require('@websublime/workspace-tools-openharmony-arm/package.json').version - if (bindingPackageVersion !== '2.0.9' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 2.0.9 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 2.0.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { diff --git a/packages/workspace-tools/src/index.ts b/packages/workspace-tools/src/index.ts index f420d386..0acd87b4 100644 --- a/packages/workspace-tools/src/index.ts +++ b/packages/workspace-tools/src/index.ts @@ -18,6 +18,12 @@ * - `changesetHistory()` - Query archived changeset history (Story 4.7) * - `changesetCheck()` - Check if a changeset exists for a branch (Story 4.8) * + * Bump types (Story 5.1 - types only, commands in Stories 5.2-5.4): + * - `BumpPreviewParams`, `BumpPreviewData`, `BumpPreviewApiResponse` + * - `BumpApplyParams`, `BumpApplyData`, `BumpApplyApiResponse` + * - `BumpSnapshotParams`, `BumpSnapshotData`, `BumpSnapshotApiResponse` + * - `PackageVersionInfo`, `SnapshotVersionInfo`, `DependencyUpdateInfo`, `BumpSummaryInfo` + * * ## How * * The native bindings are compiled from Rust using napi-rs and exposed through @@ -97,4 +103,26 @@ export type { ArchivedChangesetInfo, ReleaseInfoData, ReleasedVersionEntry, + + // Bump command types (Story 5.1 - types only, commands in Stories 5.2-5.4) + // Input parameters + BumpPreviewParams, + BumpApplyParams, + BumpSnapshotParams, + + // Response data + BumpPreviewData, + BumpApplyData, + BumpSnapshotData, + + // API responses + BumpPreviewApiResponse, + BumpApplyApiResponse, + BumpSnapshotApiResponse, + + // Supporting types + PackageVersionInfo, + SnapshotVersionInfo, + DependencyUpdateInfo, + BumpSummaryInfo, } from './binding'