diff --git a/crates/node/src/commands/bump.rs b/crates/node/src/commands/bump.rs index ea0f827f..4e5e5c37 100644 --- a/crates/node/src/commands/bump.rs +++ b/crates/node/src/commands/bump.rs @@ -127,12 +127,15 @@ use serde::Deserialize; use crate::error::ErrorInfo; use crate::types::bump::{ BumpApplyApiResponse, BumpApplyData, BumpApplyParams, BumpPreviewApiResponse, BumpPreviewData, - BumpPreviewParams, BumpSummaryInfo, PackageVersionInfo, + BumpPreviewParams, BumpSnapshotApiResponse, BumpSnapshotData, BumpSnapshotParams, + BumpSummaryInfo, PackageVersionInfo, SnapshotVersionInfo, }; use crate::validation::validators; use sublime_cli_tools::cli::commands::BumpArgs; -use sublime_cli_tools::commands::bump::{execute_bump_apply, execute_bump_preview}; +use sublime_cli_tools::commands::bump::{ + execute_bump_apply, execute_bump_preview, execute_bump_snapshot, +}; use sublime_cli_tools::output::{Output, OutputFormat}; // ============================================================================ @@ -538,6 +541,46 @@ pub(crate) fn convert_to_napi_apply(cli_data: CliExecuteResult) -> BumpApplyData } } +/// Converts CLI bump snapshot data to NAPI-compatible `BumpSnapshotData`. +/// +/// This function performs a conversion from the CLI's internal types to the +/// NAPI types exposed to JavaScript. For snapshot mode, the `next_version` +/// field contains the generated snapshot version. +/// +/// # Arguments +/// +/// * `cli_data` - The parsed CLI response data +/// * `format` - The snapshot format template that was used +/// +/// # Returns +/// +/// A `BumpSnapshotData` instance suitable for returning to JavaScript. +/// +/// # Conversion Details +/// +/// - `SnapshotVersionInfo.original_version` = `PackageBumpInfo.current_version` +/// - `SnapshotVersionInfo.snapshot_version` = `PackageBumpInfo.next_version` +/// - Only packages where `will_bump` is true are included +pub(crate) fn convert_to_napi_snapshot( + cli_data: CliBumpSnapshot, + format: String, +) -> BumpSnapshotData { + // Convert packages that will be bumped to snapshot version info + let packages: Vec = cli_data + .packages + .into_iter() + .filter(|p| p.will_bump) + .map(|p| SnapshotVersionInfo { + name: p.name, + path: p.path, + original_version: p.current_version, + snapshot_version: p.next_version, + }) + .collect(); + + BumpSnapshotData { strategy: cli_data.strategy.to_lowercase(), packages, format } +} + /// Parses the JSON response from the CLI apply command and converts it to NAPI types. /// /// # Arguments @@ -586,6 +629,63 @@ pub(crate) fn parse_apply_response(json_bytes: &[u8]) -> Result Result { + // Convert bytes to string first for better error messages + let json_str = std::str::from_utf8(json_bytes) + .map_err(|e| ErrorInfo::execution(format!("Invalid UTF-8 in CLI response: {e}")))?; + + // Handle empty response + if json_str.trim().is_empty() { + return Err(ErrorInfo::execution("CLI returned empty response")); + } + + // Parse the JSON response (same structure as preview) + let response: CliJsonResponse = + serde_json::from_str(json_str).map_err(|e| { + ErrorInfo::execution(format!( + "Failed to parse CLI JSON response: {e} (length={})", + json_str.len() + )) + })?; + + // Check for CLI-level errors + if !response.success { + let error_message = response.error.unwrap_or_else(|| "Unknown CLI error".to_string()); + return Err(ErrorInfo::execution(error_message)); + } + + // Extract and convert data + let cli_data = + response.data.ok_or_else(|| ErrorInfo::execution("CLI returned success but no data"))?; + + Ok(convert_to_napi_snapshot(cli_data, format)) +} + // ============================================================================ // Parameter Validation // ============================================================================ @@ -633,6 +733,50 @@ pub(crate) fn validate_apply_params(params: &BumpApplyParams) -> Result Result { + // Validate root path exists and is a directory + validators::root(¶ms.root)?; + + // Validate snapshot format if provided + if let Some(ref format) = params.format { + validators::snapshot_format(format)?; + } + + Ok(PathBuf::from(¶ms.root)) +} + /// Converts `BumpPreviewParams` to CLI `BumpArgs`. /// /// This function sets the appropriate flags for preview mode (dry_run = true) @@ -720,6 +864,72 @@ pub(crate) fn convert_apply_params_to_args(params: &BumpApplyParams) -> BumpArgs } } +/// Default snapshot format template. +/// +/// This is used when no custom format is provided by the user or configuration. +const DEFAULT_SNAPSHOT_FORMAT: &str = "{version}-snapshot.{short_commit}"; + +/// Converts `BumpSnapshotParams` to CLI `BumpArgs`. +/// +/// This function sets the appropriate flags for snapshot mode (snapshot = true) +/// and maps the NAPI parameters to CLI arguments. Snapshot mode generates +/// temporary pre-release versions for testing without consuming changesets. +/// +/// # Arguments +/// +/// * `params` - The NAPI snapshot parameters +/// +/// # Returns +/// +/// A `BumpArgs` struct configured for snapshot mode. +/// +/// # Examples +/// +/// ```rust,ignore +/// use sublime_node_tools::commands::bump::convert_snapshot_params_to_args; +/// use sublime_node_tools::types::bump::BumpSnapshotParams; +/// +/// let params = BumpSnapshotParams::new(".") +/// .with_format("{version}-{branch}.{short_commit}"); +/// +/// let args = convert_snapshot_params_to_args(¶ms); +/// assert!(args.snapshot); +/// assert_eq!(args.snapshot_format, Some("{version}-{branch}.{short_commit}".to_string())); +/// ``` +pub(crate) fn convert_snapshot_params_to_args(params: &BumpSnapshotParams) -> BumpArgs { + BumpArgs { + // Snapshot mode: snapshot = true, no dry run, no execute + dry_run: false, + execute: false, + snapshot: true, + snapshot_format: params.format.clone(), + + // No prerelease in snapshot mode (they are mutually exclusive) + prerelease: None, + + // Package filter from params + packages: params.packages.clone(), + + // No git operations in snapshot mode + git_tag: false, + git_push: false, + git_commit: false, + + // No changelog generation in snapshot mode + no_changelog: true, + + // Changesets are NOT archived in snapshot mode + no_archive: true, + always_archive: false, + + // Skip confirmations (API is non-interactive) + force: true, + + // No diff display in snapshot mode + show_diff: false, + } +} + // ============================================================================ // NAPI Functions // ============================================================================ @@ -993,14 +1203,145 @@ pub async fn bump_apply(params: BumpApplyParams) -> BumpApplyApiResponse { } } -// TODO: will be implemented on story 5.4 - bumpSnapshot -// -// #[napi(js_name = "bumpSnapshot")] -// pub async fn bump_snapshot(params: BumpSnapshotParams) -> BumpSnapshotApiResponse { -// // Implementation will include: -// // 1. Validate parameters including snapshot format if provided -// // 2. Create BumpArgs with snapshot = true -// // 3. Execute CLI command and parse response -// // 4. Return snapshot version information -// todo!() -// } +/// Generate snapshot versions for testing. +/// +/// Creates temporary, non-persisted versions for branch builds and preview +/// deployments. Snapshot versions are NOT SemVer compliant and are intended +/// for testing purposes only. +/// +/// **Key characteristics:** +/// - Does NOT consume or archive changesets +/// - Does NOT create Git commits or tags +/// - Does NOT generate changelogs +/// - Uses format templates with variables like `{version}`, `{branch}`, `{short_commit}` +/// +/// This function is the main entry point for Node.js applications to generate +/// snapshot versions. It handles all the complexity of CLI invocation and response +/// parsing internally. +/// +/// @param params - Snapshot parameters containing: +/// - `root`: Workspace root directory path (required) +/// - `configPath`: Optional custom config file path +/// - `packages`: Optional filter to specific packages +/// - `format`: Snapshot format template (default: `{version}-snapshot.{short_commit}`) +/// +/// @returns `Promise>` containing: +/// - On success: `{ success: true, data: BumpSnapshotData }` +/// - On failure: `{ success: false, error: ErrorInfo }` +/// +/// @example Basic usage with default format +/// ```typescript +/// const result = await bumpSnapshot({ root: '/path/to/project' }); +/// if (result.success) { +/// console.log(`Format used: ${result.data.format}`); +/// for (const pkg of result.data.packages) { +/// console.log(`${pkg.name}: ${pkg.originalVersion} -> ${pkg.snapshotVersion}`); +/// } +/// } else { +/// console.error(`Error: ${result.error.code} - ${result.error.message}`); +/// } +/// ``` +/// +/// @example Custom format with branch and commit +/// ```typescript +/// const result = await bumpSnapshot({ +/// root: '/path/to/project', +/// format: '{version}-{branch}.{short_commit}' +/// }); +/// // Generates versions like: 1.2.3-feature-x.abc123f +/// ``` +/// +/// @example Timestamp-based format +/// ```typescript +/// const result = await bumpSnapshot({ +/// root: '/path/to/project', +/// format: '{version}-dev.{timestamp}' +/// }); +/// // Generates versions like: 1.2.3-dev.1699876543 +/// ``` +/// +/// @example Filter to specific packages +/// ```typescript +/// const result = await bumpSnapshot({ +/// root: '/path/to/project', +/// packages: ['@scope/core', '@scope/utils'], +/// format: '{version}-snapshot.{short_commit}' +/// }); +/// ``` +/// +/// @example Error handling +/// ```typescript +/// const result = await bumpSnapshot({ +/// root: '/path/to/project', +/// format: 'invalid-no-variables' +/// }); +/// if (!result.success) { +/// if (result.error.code === 'EVALIDATION') { +/// console.error('Invalid format:', result.error.message); +/// } +/// } +/// ``` +#[napi(js_name = "bumpSnapshot")] +pub async fn bump_snapshot(params: BumpSnapshotParams) -> BumpSnapshotApiResponse { + // 1. Validate parameters (synchronous validation before spawning) + let root_path = match validate_snapshot_params(¶ms) { + Ok(path) => path, + Err(error) => return BumpSnapshotApiResponse::failure(error), + }; + + // 2. Prepare config path + let config_path: Option = params.config_path.as_ref().map(PathBuf::from); + + // 3. Determine the format to use (user-provided or default) + let format_used = params.format.clone().unwrap_or_else(|| DEFAULT_SNAPSHOT_FORMAT.to_string()); + + // 4. Convert params to CLI args + let args = convert_snapshot_params_to_args(¶ms); + + // Clone format for use inside the blocking task + let format_for_parse = format_used.clone(); + + // 5. Execute CLI command in a blocking task + // The CLI's execute_bump_snapshot uses types that are not Send/Sync (RefCell, git2::Repository), + // so we must run it on a blocking thread via spawn_blocking. + let result = tokio::task::spawn_blocking(move || { + // Create a new tokio runtime for the blocking context + // This is necessary because execute_bump_snapshot is async but we're in a blocking context + let rt = match tokio::runtime::Builder::new_current_thread().enable_all().build() { + Ok(rt) => rt, + Err(e) => { + return Err(ErrorInfo::execution(format!("Failed to create runtime: {e}"))); + } + }; + + rt.block_on(async { + // Create shared buffer for output capture + let buffer = SharedBuffer::new(); + + // Create Output with JSON format + let output = Output::new(OutputFormat::Json, buffer.clone(), true); + + // Execute the CLI command + let config_path_ref: Option<&Path> = config_path.as_deref(); + if let Err(cli_error) = + execute_bump_snapshot(&args, &output, &root_path, config_path_ref).await + { + return Err(ErrorInfo::from(cli_error)); + } + + // Extract and parse JSON + let json_bytes = buffer.take_bytes(); + parse_snapshot_response(&json_bytes, format_for_parse) + }) + }) + .await; + + // 6. Handle spawn_blocking result + match result { + Ok(Ok(data)) => BumpSnapshotApiResponse::success(data), + Ok(Err(error)) => BumpSnapshotApiResponse::failure(error), + Err(join_error) => BumpSnapshotApiResponse::failure(ErrorInfo::execution(format!( + "Task execution failed: {join_error}" + ))), + } +} diff --git a/crates/node/src/commands/mod.rs b/crates/node/src/commands/mod.rs index e935c02f..a4a6cb62 100644 --- a/crates/node/src/commands/mod.rs +++ b/crates/node/src/commands/mod.rs @@ -96,7 +96,8 @@ pub(crate) mod bump; pub use bump::bump_preview; // Story 5.3: bumpApply pub use bump::bump_apply; -// TODO: will be implemented on story 5.4 (bumpSnapshot) +// Story 5.4: bumpSnapshot +pub use bump::bump_snapshot; // TODO: will be implemented on story 8.2-8.4 (upgrade commands) pub(crate) mod upgrade; diff --git a/crates/node/src/commands/tests.rs b/crates/node/src/commands/tests.rs index 74d9aad7..5c447c7e 100644 --- a/crates/node/src/commands/tests.rs +++ b/crates/node/src/commands/tests.rs @@ -5501,3 +5501,670 @@ mod bump_apply_tests { } } } + +// ============================================================================= +// Bump Snapshot Tests (Story 5.4) +// ============================================================================= + +/// Tests for the `bumpSnapshot` command. +/// +/// These tests verify: +/// - `SharedBuffer` functionality for capturing CLI output +/// - JSON response parsing from CLI to NAPI types +/// - Type conversion from CLI structures to NAPI structures +/// - Parameter validation for snapshot format and root path +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod bump_snapshot_tests { + use std::io::Write; + + use tempfile::TempDir; + + use crate::commands::bump::{ + CliBumpSnapshot, CliBumpSummary, CliChangesetInfo, CliPackageBumpInfo, SharedBuffer, + convert_snapshot_params_to_args, convert_to_napi_snapshot, parse_snapshot_response, + validate_snapshot_params, + }; + use crate::types::bump::BumpSnapshotParams; + + // ------------------------------------------------------------------------- + // SharedBuffer Tests (reused pattern, validated for snapshot context) + // ------------------------------------------------------------------------- + + mod shared_buffer_tests { + use super::*; + + #[test] + fn test_shared_buffer_new() { + let buffer = SharedBuffer::new(); + assert!(buffer.take_bytes().is_empty()); + } + + #[test] + fn test_shared_buffer_write() { + let mut buffer = SharedBuffer::new(); + let bytes_written = buffer.write(b"snapshot result").unwrap(); + assert_eq!(bytes_written, 15); + assert_eq!(buffer.take_bytes(), b"snapshot result"); + } + + #[test] + fn test_shared_buffer_multiple_writes() { + let mut buffer = SharedBuffer::new(); + buffer.write_all(b"snapshot ").unwrap(); + buffer.write_all(b"versions").unwrap(); + assert_eq!(buffer.take_bytes(), b"snapshot versions"); + } + + #[test] + fn test_shared_buffer_clone_shares_data() { + let mut buffer = SharedBuffer::new(); + let buffer_clone = buffer.clone(); + buffer.write_all(b"shared snapshot data").unwrap(); + + // Both buffers should see the same data + assert_eq!(buffer.take_bytes(), b"shared snapshot data"); + assert_eq!(buffer_clone.take_bytes(), b"shared snapshot data"); + } + + #[test] + fn test_shared_buffer_flush() { + let mut buffer = SharedBuffer::new(); + assert!(buffer.flush().is_ok()); + } + + #[test] + fn test_shared_buffer_take_bytes_preserves_data() { + let mut buffer = SharedBuffer::new(); + buffer.write_all(b"snapshot test data").unwrap(); + + // Multiple takes should return same data + let first_take = buffer.take_bytes(); + let second_take = buffer.take_bytes(); + assert_eq!(first_take, second_take); + } + } + + // ------------------------------------------------------------------------- + // Parse Response Tests + // ------------------------------------------------------------------------- + + mod parse_response_tests { + use super::*; + + #[test] + fn test_parse_snapshot_response_success() { + let json = r#"{ + "success": true, + "data": { + "strategy": "Independent", + "packages": [ + { + "name": "@scope/core", + "path": "packages/core", + "currentVersion": "1.0.0", + "nextVersion": "1.0.0-snapshot.abc123f", + "bumpType": "Minor", + "willBump": true, + "reason": "Has pending changesets" + }, + { + "name": "@scope/utils", + "path": "packages/utils", + "currentVersion": "2.0.0", + "nextVersion": "2.0.0-snapshot.abc123f", + "bumpType": "Patch", + "willBump": true, + "reason": "Has pending changesets" + } + ], + "changesets": [ + { + "id": "changeset-1", + "branch": "feature/test", + "bumpType": "Minor", + "packages": ["@scope/core"], + "commitCount": 3 + } + ], + "summary": { + "totalPackages": 2, + "packagesToBump": 2, + "packagesUnchanged": 0, + "totalChangesets": 1, + "hasCircularDependencies": false + } + } + }"#; + + let format = "{version}-snapshot.{short_commit}".to_string(); + let result = parse_snapshot_response(json.as_bytes(), format.clone()); + assert!(result.is_ok()); + let data = result.unwrap(); + assert_eq!(data.strategy, "independent"); + assert_eq!(data.format, format); + assert_eq!(data.packages.len(), 2); + + // Verify first package + let pkg1 = &data.packages[0]; + assert_eq!(pkg1.name, "@scope/core"); + assert_eq!(pkg1.path, "packages/core"); + assert_eq!(pkg1.original_version, "1.0.0"); + assert_eq!(pkg1.snapshot_version, "1.0.0-snapshot.abc123f"); + + // Verify second package + let pkg2 = &data.packages[1]; + assert_eq!(pkg2.name, "@scope/utils"); + assert_eq!(pkg2.original_version, "2.0.0"); + assert_eq!(pkg2.snapshot_version, "2.0.0-snapshot.abc123f"); + } + + #[test] + fn test_parse_snapshot_response_filters_non_bumping_packages() { + let json = r#"{ + "success": true, + "data": { + "strategy": "Independent", + "packages": [ + { + "name": "@scope/core", + "path": "packages/core", + "currentVersion": "1.0.0", + "nextVersion": "1.0.0-snapshot.abc123f", + "bumpType": "Minor", + "willBump": true, + "reason": "Has pending changesets" + }, + { + "name": "@scope/unchanged", + "path": "packages/unchanged", + "currentVersion": "1.0.0", + "nextVersion": "1.0.0", + "bumpType": "None", + "willBump": false, + "reason": "No pending changesets" + } + ], + "changesets": [], + "summary": { + "totalPackages": 2, + "packagesToBump": 1, + "packagesUnchanged": 1, + "totalChangesets": 0, + "hasCircularDependencies": false + } + } + }"#; + + let format = "{version}-snapshot.{short_commit}".to_string(); + let result = parse_snapshot_response(json.as_bytes(), format); + assert!(result.is_ok()); + let data = result.unwrap(); + // Only the package with willBump = true should be included + assert_eq!(data.packages.len(), 1); + assert_eq!(data.packages[0].name, "@scope/core"); + } + + #[test] + fn test_parse_snapshot_response_empty_packages() { + let json = r#"{ + "success": true, + "data": { + "strategy": "Unified", + "packages": [], + "changesets": [], + "summary": { + "totalPackages": 0, + "packagesToBump": 0, + "packagesUnchanged": 0, + "totalChangesets": 0, + "hasCircularDependencies": false + } + } + }"#; + + let format = "{version}-dev.{timestamp}".to_string(); + let result = parse_snapshot_response(json.as_bytes(), format.clone()); + assert!(result.is_ok()); + let data = result.unwrap(); + assert_eq!(data.strategy, "unified"); + assert_eq!(data.format, format); + assert!(data.packages.is_empty()); + } + + #[test] + fn test_parse_snapshot_response_cli_error() { + let json = r#"{ + "success": false, + "error": "No Git repository found" + }"#; + + let result = parse_snapshot_response(json.as_bytes(), "{version}-snapshot".to_string()); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!(err.message.contains("No Git repository found")); + } + + #[test] + fn test_parse_snapshot_response_empty() { + let result = parse_snapshot_response(b"", "{version}".to_string()); + assert!(result.is_err()); + } + + #[test] + fn test_parse_snapshot_response_whitespace_only() { + let result = parse_snapshot_response(b" \n\t ", "{version}".to_string()); + assert!(result.is_err()); + } + + #[test] + fn test_parse_snapshot_response_invalid_json() { + let result = parse_snapshot_response(b"not valid json", "{version}".to_string()); + assert!(result.is_err()); + } + + #[test] + fn test_parse_snapshot_response_invalid_utf8() { + let invalid_utf8 = vec![0xff, 0xfe, 0x00, 0x01]; + let result = parse_snapshot_response(&invalid_utf8, "{version}".to_string()); + assert!(result.is_err()); + } + + #[test] + fn test_parse_snapshot_response_success_no_data() { + let json = r#"{"success": true}"#; + let result = parse_snapshot_response(json.as_bytes(), "{version}".to_string()); + assert!(result.is_err()); + } + + #[test] + fn test_parse_snapshot_response_cli_error_no_message() { + let json = r#"{"success": false}"#; + let result = parse_snapshot_response(json.as_bytes(), "{version}".to_string()); + assert!(result.is_err()); + } + } + + // ------------------------------------------------------------------------- + // Conversion Tests + // ------------------------------------------------------------------------- + + mod conversion_tests { + use super::*; + + fn create_test_cli_snapshot() -> CliBumpSnapshot { + CliBumpSnapshot { + strategy: "Independent".to_string(), + packages: vec![ + CliPackageBumpInfo { + name: "@scope/core".to_string(), + path: "packages/core".to_string(), + current_version: "1.0.0".to_string(), + next_version: "1.0.0-snapshot.abc123f".to_string(), + bump_type: "Minor".to_string(), + will_bump: true, + reason: "Has pending changesets".to_string(), + }, + CliPackageBumpInfo { + name: "@scope/utils".to_string(), + path: "packages/utils".to_string(), + current_version: "2.0.0".to_string(), + next_version: "2.0.0-snapshot.abc123f".to_string(), + bump_type: "Patch".to_string(), + will_bump: true, + reason: "Has pending changesets".to_string(), + }, + ], + changesets: vec![CliChangesetInfo { + id: "changeset-1".to_string(), + branch: "feature/test".to_string(), + bump_type: "Minor".to_string(), + packages: vec!["@scope/core".to_string()], + commit_count: 3, + }], + summary: CliBumpSummary { + total_packages: 2, + packages_to_bump: 2, + packages_unchanged: 0, + total_changesets: 1, + has_circular_dependencies: false, + }, + } + } + + #[test] + fn test_convert_to_napi_snapshot_full() { + let cli_data = create_test_cli_snapshot(); + let format = "{version}-snapshot.{short_commit}".to_string(); + let napi_data = convert_to_napi_snapshot(cli_data, format.clone()); + + assert_eq!(napi_data.strategy, "independent"); + assert_eq!(napi_data.format, format); + assert_eq!(napi_data.packages.len(), 2); + + // Verify conversion mapping + let pkg1 = &napi_data.packages[0]; + assert_eq!(pkg1.name, "@scope/core"); + assert_eq!(pkg1.path, "packages/core"); + assert_eq!(pkg1.original_version, "1.0.0"); + assert_eq!(pkg1.snapshot_version, "1.0.0-snapshot.abc123f"); + + let pkg2 = &napi_data.packages[1]; + assert_eq!(pkg2.name, "@scope/utils"); + assert_eq!(pkg2.path, "packages/utils"); + assert_eq!(pkg2.original_version, "2.0.0"); + assert_eq!(pkg2.snapshot_version, "2.0.0-snapshot.abc123f"); + } + + #[test] + fn test_convert_to_napi_snapshot_filters_non_bumping() { + let cli_data = CliBumpSnapshot { + strategy: "Unified".to_string(), + packages: vec![ + CliPackageBumpInfo { + name: "@scope/bumped".to_string(), + path: "packages/bumped".to_string(), + current_version: "1.0.0".to_string(), + next_version: "1.0.0-dev.123".to_string(), + bump_type: "Minor".to_string(), + will_bump: true, + reason: "Has changesets".to_string(), + }, + CliPackageBumpInfo { + name: "@scope/unchanged".to_string(), + path: "packages/unchanged".to_string(), + current_version: "1.0.0".to_string(), + next_version: "1.0.0".to_string(), + bump_type: "None".to_string(), + will_bump: false, + reason: "No changesets".to_string(), + }, + ], + changesets: vec![], + summary: CliBumpSummary { + total_packages: 2, + packages_to_bump: 1, + packages_unchanged: 1, + total_changesets: 0, + has_circular_dependencies: false, + }, + }; + + let format = "{version}-dev.{timestamp}".to_string(); + let napi_data = convert_to_napi_snapshot(cli_data, format); + + assert_eq!(napi_data.packages.len(), 1); + assert_eq!(napi_data.packages[0].name, "@scope/bumped"); + } + + #[test] + fn test_convert_to_napi_snapshot_empty() { + let cli_data = CliBumpSnapshot { + strategy: "Independent".to_string(), + packages: vec![], + changesets: vec![], + summary: CliBumpSummary { + total_packages: 0, + packages_to_bump: 0, + packages_unchanged: 0, + total_changesets: 0, + has_circular_dependencies: false, + }, + }; + + let format = "{version}-snapshot".to_string(); + let napi_data = convert_to_napi_snapshot(cli_data, format.clone()); + + assert_eq!(napi_data.strategy, "independent"); + assert_eq!(napi_data.format, format); + assert!(napi_data.packages.is_empty()); + } + + #[test] + fn test_convert_snapshot_params_to_args_defaults() { + let params = BumpSnapshotParams::new("."); + let args = convert_snapshot_params_to_args(¶ms); + + // Snapshot mode flags + assert!(!args.dry_run); + assert!(!args.execute); + assert!(args.snapshot); + assert!(args.snapshot_format.is_none()); + + // No prerelease in snapshot mode + assert!(args.prerelease.is_none()); + + // No packages filter by default + assert!(args.packages.is_none()); + + // No git operations + assert!(!args.git_tag); + assert!(!args.git_push); + assert!(!args.git_commit); + + // No changelog or archive + assert!(args.no_changelog); + assert!(args.no_archive); + assert!(!args.always_archive); + + // Non-interactive + assert!(args.force); + assert!(!args.show_diff); + } + + #[test] + fn test_convert_snapshot_params_to_args_with_format() { + let params = + BumpSnapshotParams::new(".").with_format("{version}-{branch}.{short_commit}"); + let args = convert_snapshot_params_to_args(¶ms); + + assert!(args.snapshot); + assert_eq!(args.snapshot_format, Some("{version}-{branch}.{short_commit}".to_string())); + } + + #[test] + fn test_convert_snapshot_params_to_args_with_packages() { + let params = BumpSnapshotParams::new(".") + .with_packages(vec!["@scope/core".to_string(), "@scope/utils".to_string()]); + let args = convert_snapshot_params_to_args(¶ms); + + assert!(args.snapshot); + let packages = args.packages.unwrap(); + assert_eq!(packages.len(), 2); + assert!(packages.contains(&"@scope/core".to_string())); + assert!(packages.contains(&"@scope/utils".to_string())); + } + + #[test] + fn test_convert_snapshot_params_to_args_full() { + let params = BumpSnapshotParams::new("/path/to/project") + .with_config_path("/path/to/config.json") + .with_packages(vec!["pkg-a".to_string()]) + .with_format("{version}-dev.{timestamp}"); + let args = convert_snapshot_params_to_args(¶ms); + + assert!(args.snapshot); + assert_eq!(args.snapshot_format, Some("{version}-dev.{timestamp}".to_string())); + assert_eq!(args.packages, Some(vec!["pkg-a".to_string()])); + + // Verify snapshot-specific settings + assert!(!args.dry_run); + assert!(!args.execute); + assert!(args.no_changelog); + assert!(args.no_archive); + } + } + + // ------------------------------------------------------------------------- + // Validation Tests + // ------------------------------------------------------------------------- + + mod validation_tests { + use super::*; + + #[test] + fn test_validate_snapshot_params_valid_directory() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_nonexistent_path() { + let params = BumpSnapshotParams::new("/nonexistent/path/12345"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + } + + #[test] + fn test_validate_snapshot_params_empty_root() { + let params = BumpSnapshotParams::new(""); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + } + + #[test] + fn test_validate_snapshot_params_file_not_directory() { + let temp_dir = TempDir::new().unwrap(); + let file_path = temp_dir.path().join("test.txt"); + std::fs::write(&file_path, "test").unwrap(); + let params = BumpSnapshotParams::new(file_path.to_str().unwrap()); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_default() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = + BumpSnapshotParams::new(path_str).with_format("{version}-snapshot.{short_commit}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_branch() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = + BumpSnapshotParams::new(path_str).with_format("{version}-{branch}.{short_commit}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_timestamp() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format("{version}-dev.{timestamp}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_commit() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format("{version}-{commit}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_version_only() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format("{version}-snapshot"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_valid_format_all_variables() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str) + .with_format("{version}-{branch}-{short_commit}-{commit}-{timestamp}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_invalid_format_empty() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format(""); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!(err.message.contains("empty")); + } + + #[test] + fn test_validate_snapshot_params_invalid_format_no_variables() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format("no-variables-here"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!(err.message.contains("must contain at least one valid variable")); + } + + #[test] + fn test_validate_snapshot_params_invalid_format_invalid_variable() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_format("{invalid}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_err()); + } + + #[test] + fn test_validate_snapshot_params_with_config_path() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str).with_config_path("/path/to/config.json"); + let result = validate_snapshot_params(¶ms); + // Config path is not validated for existence + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_with_packages() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str) + .with_packages(vec!["@scope/core".to_string(), "@scope/utils".to_string()]); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_snapshot_params_returns_correct_path() { + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + let path = result.unwrap(); + assert_eq!(path.to_str().unwrap(), path_str); + } + + #[test] + fn test_validate_snapshot_params_full_workflow() { + // Complete snapshot params with all options + let temp_dir = TempDir::new().unwrap(); + let path_str = temp_dir.path().to_str().unwrap(); + let params = BumpSnapshotParams::new(path_str) + .with_config_path("repo.config.json") + .with_packages(vec!["@scope/core".to_string()]) + .with_format("{version}-{branch}.{short_commit}"); + let result = validate_snapshot_params(¶ms); + assert!(result.is_ok()); + } + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 2c463c30..fdd45a90 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -157,7 +157,8 @@ pub use commands::changeset_check; pub use commands::bump_preview; // Story 5.3: bumpApply pub use commands::bump_apply; -// TODO: will be implemented on story 5.4 (bumpSnapshot) +// Story 5.4: bumpSnapshot +pub use commands::bump_snapshot; // TODO: will be implemented on story 6.3 (execute command) // TODO: will be implemented on story 7.2-7.3 (config commands) diff --git a/packages/workspace-tools/npm/darwin-arm64/package.json b/packages/workspace-tools/npm/darwin-arm64/package.json index d4391876..0bd61ec2 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.13", + "version": "2.0.14", "cpu": [ "arm64" ], diff --git a/packages/workspace-tools/npm/darwin-x64/package.json b/packages/workspace-tools/npm/darwin-x64/package.json index f0c5261a..e3df11b9 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.13", + "version": "2.0.14", "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 12d00313..98d7bfe0 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.13", + "version": "2.0.14", "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 23ffa35c..5299cc6d 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.13", + "version": "2.0.14", "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 f517c038..ed083afb 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.13", + "version": "2.0.14", "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 6b87fef4..5eff3438 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.13", + "version": "2.0.14", "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 5dce4d24..f9dd0e19 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.13", + "version": "2.0.14", "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 cc3c75d1..c140879a 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.13", + "version": "2.0.14", "cpu": [ "x64" ], diff --git a/packages/workspace-tools/package.json b/packages/workspace-tools/package.json index efd19836..f33ac295 100644 --- a/packages/workspace-tools/package.json +++ b/packages/workspace-tools/package.json @@ -1,6 +1,6 @@ { "name": "@websublime/workspace-tools", - "version": "2.0.13", + "version": "2.0.14", "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 e01c5cb6..6dd71715 100644 --- a/packages/workspace-tools/src/binding.d.ts +++ b/packages/workspace-tools/src/binding.d.ts @@ -706,6 +706,88 @@ export interface BumpPreviewParams { showDiff?: boolean | undefined } +/** + * Generate snapshot versions for testing. + * + * Creates temporary, non-persisted versions for branch builds and preview + * deployments. Snapshot versions are NOT SemVer compliant and are intended + * for testing purposes only. + * + * **Key characteristics:** + * - Does NOT consume or archive changesets + * - Does NOT create Git commits or tags + * - Does NOT generate changelogs + * - Uses format templates with variables like `{version}`, `{branch}`, `{short_commit}` + * + * This function is the main entry point for Node.js applications to generate + * snapshot versions. It handles all the complexity of CLI invocation and response + * parsing internally. + * + * @param params - Snapshot parameters containing: + * - `root`: Workspace root directory path (required) + * - `configPath`: Optional custom config file path + * - `packages`: Optional filter to specific packages + * - `format`: Snapshot format template (default: `{version}-snapshot.{short_commit}`) + * + * @returns `Promise>` containing: + * - On success: `{ success: true, data: BumpSnapshotData }` + * - On failure: `{ success: false, error: ErrorInfo }` + * + * @example Basic usage with default format + * ```typescript + * const result = await bumpSnapshot({ root: '/path/to/project' }); + * if (result.success) { + * console.log(`Format used: ${result.data.format}`); + * for (const pkg of result.data.packages) { + * console.log(`${pkg.name}: ${pkg.originalVersion} -> ${pkg.snapshotVersion}`); + * } + * } else { + * console.error(`Error: ${result.error.code} - ${result.error.message}`); + * } + * ``` + * + * @example Custom format with branch and commit + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: '{version}-{branch}.{short_commit}' + * }); + * // Generates versions like: 1.2.3-feature-x.abc123f + * ``` + * + * @example Timestamp-based format + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: '{version}-dev.{timestamp}' + * }); + * // Generates versions like: 1.2.3-dev.1699876543 + * ``` + * + * @example Filter to specific packages + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * packages: ['@scope/core', '@scope/utils'], + * format: '{version}-snapshot.{short_commit}' + * }); + * ``` + * + * @example Error handling + * ```typescript + * const result = await bumpSnapshot({ + * root: '/path/to/project', + * format: 'invalid-no-variables' + * }); + * if (!result.success) { + * if (result.error.code === 'EVALIDATION') { + * console.error('Invalid format:', result.error.message); + * } + * } + * ``` + */ +export declare function bumpSnapshot(params: BumpSnapshotParams): Promise + /** * API response for the bump snapshot command. * diff --git a/packages/workspace-tools/src/binding.js b/packages/workspace-tools/src/binding.js index 732be9c8..58d973c6 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 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.13' && 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.13 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '2.0.14' && 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.14 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -574,6 +574,7 @@ if (!nativeBinding) { module.exports = nativeBinding module.exports.bumpApply = nativeBinding.bumpApply module.exports.bumpPreview = nativeBinding.bumpPreview +module.exports.bumpSnapshot = nativeBinding.bumpSnapshot module.exports.changesetAdd = nativeBinding.changesetAdd module.exports.changesetCheck = nativeBinding.changesetCheck module.exports.changesetHistory = nativeBinding.changesetHistory diff --git a/packages/workspace-tools/src/index.ts b/packages/workspace-tools/src/index.ts index 8e122cfd..734148dc 100644 --- a/packages/workspace-tools/src/index.ts +++ b/packages/workspace-tools/src/index.ts @@ -19,8 +19,9 @@ * - `changesetCheck()` - Check if a changeset exists for a branch (Story 4.8) * - `bumpPreview()` - Preview version bumps without applying changes (Story 5.2) * - `bumpApply()` - Apply version bumps with Git integration and prerelease support (Story 5.3) + * - `bumpSnapshot()` - Generate snapshot versions for testing and CI (Story 5.4) * - * Bump types (Story 5.1 - types only, command bumpSnapshot in Story 5.4): + * Bump types (Story 5.1): * - `BumpPreviewParams`, `BumpPreviewData`, `BumpPreviewApiResponse` * - `BumpApplyParams`, `BumpApplyData`, `BumpApplyApiResponse` * - `BumpSnapshotParams`, `BumpSnapshotData`, `BumpSnapshotApiResponse` @@ -44,9 +45,9 @@ */ // Re-export all functions from bindings -import { bumpApply, bumpPreview, changesetAdd, changesetCheck, changesetHistory, changesetList, changesetRemove, changesetShow, changesetUpdate, getVersion, init, status } from './binding' +import { bumpApply, bumpPreview, bumpSnapshot, changesetAdd, changesetCheck, changesetHistory, changesetList, changesetRemove, changesetShow, changesetUpdate, getVersion, init, status } from './binding' -export { bumpApply, bumpPreview, changesetAdd, changesetCheck, changesetHistory, changesetList, changesetRemove, changesetShow, changesetUpdate, getVersion, init, status } +export { bumpApply, bumpPreview, bumpSnapshot, changesetAdd, changesetCheck, changesetHistory, changesetList, changesetRemove, changesetShow, changesetUpdate, getVersion, init, status } // Re-export all types from bindings export type { @@ -106,7 +107,7 @@ export type { ReleaseInfoData, ReleasedVersionEntry, - // Bump command types (Story 5.1 - types only, commands in Stories 5.2-5.4) + // Bump command types (Stories 5.1-5.4) // Input parameters BumpPreviewParams, BumpApplyParams,