or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-builder.mdcommand-system.mdfilesystem-tools.mdhttp-tools.mdindex.mdpackage-manager-tools.mdpatching-tools.mdprint-tools.mdprompt-tools.mdsemver-tools.mdstring-tools.mdsystem-tools.mdtemplate-tools.md
tile.json

semver-tools.mddocs/

Semantic Versioning

Semantic versioning utilities for version comparison, validation, range checking, and version manipulation following semver.org standards.

Capabilities

Version Validation

Functions for validating and cleaning semantic version strings.

/**
 * Validate if string is a valid semantic version
 * @param version - Version string to validate
 * @returns Valid semver string or null if invalid
 */
valid(version: string): string | null;

/**
 * Clean and normalize version string to valid semver
 * @param version - Version string to clean
 * @returns Cleaned semver string or null if invalid
 */
clean(version: string): string | null;

Version Validation Examples:

import { semver } from "gluegun";

// Validate version strings
semver.valid("1.2.3");        // "1.2.3"
semver.valid("v1.2.3");       // "1.2.3"
semver.valid("1.2");          // null (invalid)
semver.valid("1.2.3-alpha");  // "1.2.3-alpha"
semver.valid("invalid");      // null

// Clean version strings
semver.clean("  v1.2.3  ");   // "1.2.3"
semver.clean("=1.2.3");       // "1.2.3"
semver.clean("1.2.3-beta+build"); // "1.2.3-beta+build"
semver.clean("1.2");          // null (cannot be cleaned to valid semver)

// Usage in CLI
const userVersion = "v2.1.0-rc.1";
const cleanVersion = semver.clean(userVersion);

if (cleanVersion) {
  console.log(`Using version: ${cleanVersion}`);
} else {
  console.error(`Invalid version format: ${userVersion}`);
}

Version Comparison

Functions for comparing semantic versions to determine ordering and relationships.

/**
 * Check if first version is greater than second version
 * @param version - First version to compare
 * @param other - Second version to compare
 * @returns True if first version is greater
 */
gt(version: string, other: string): boolean;

/**
 * Check if first version is less than second version
 * @param version - First version to compare
 * @param other - Second version to compare
 * @returns True if first version is less
 */
lt(version: string, other: string): boolean;

Version Comparison Examples:

import { semver } from "gluegun";

// Greater than comparison
semver.gt("2.0.0", "1.9.9");     // true
semver.gt("1.0.0", "1.0.0");     // false
semver.gt("1.0.1", "1.0.0");     // true
semver.gt("2.0.0-alpha", "1.9.9"); // true
semver.gt("2.0.0-alpha", "2.0.0-beta"); // false (alpha < beta)

// Less than comparison
semver.lt("1.0.0", "2.0.0");     // true
semver.lt("1.0.0", "1.0.0");     // false
semver.lt("1.0.0-alpha", "1.0.0"); // true
semver.lt("1.0.0-rc.1", "1.0.0-rc.2"); // true

// Version sorting
const versions = ["2.1.0", "1.0.0", "2.0.0", "1.1.0"];
const sorted = versions.sort((a, b) => {
  if (semver.gt(a, b)) return -1;
  if (semver.lt(a, b)) return 1;
  return 0;
});
console.log(sorted); // ["2.1.0", "2.0.0", "1.1.0", "1.0.0"]

// Find latest version
function findLatestVersion(versions) {
  return versions.reduce((latest, current) => {
    return semver.gt(current, latest) ? current : latest;
  });
}

const latest = findLatestVersion(["1.0.0", "1.2.0", "1.1.5"]);
console.log(latest); // "1.2.0"

Range Validation

Functions for working with semantic version ranges and checking satisfaction.

/**
 * Check if version satisfies a version range
 * @param version - Version to check
 * @param range - Version range pattern
 * @returns True if version satisfies the range
 */
satisfies(version: string, range: string): boolean;

/**
 * Validate if string is a valid version range
 * @param range - Range string to validate
 * @returns True if valid range, false otherwise
 */
validRange(range: string): boolean | null;

Range Validation Examples:

import { semver } from "gluegun";

// Exact version matching
semver.satisfies("1.2.3", "1.2.3");     // true
semver.satisfies("1.2.4", "1.2.3");     // false

// Caret ranges (compatible within major version)
semver.satisfies("1.2.3", "^1.0.0");    // true
semver.satisfies("1.9.9", "^1.0.0");    // true
semver.satisfies("2.0.0", "^1.0.0");    // false

// Tilde ranges (compatible within minor version)
semver.satisfies("1.2.3", "~1.2.0");    // true
semver.satisfies("1.2.9", "~1.2.0");    // true
semver.satisfies("1.3.0", "~1.2.0");    // false

// Greater than/less than ranges
semver.satisfies("2.0.0", ">1.0.0");    // true
semver.satisfies("2.0.0", ">=2.0.0");   // true
semver.satisfies("1.9.9", "<2.0.0");    // true
semver.satisfies("2.0.0", "<=2.0.0");   // true

// Complex ranges
semver.satisfies("1.5.0", ">=1.0.0 <2.0.0");   // true
semver.satisfies("1.5.0", "^1.0.0 || ^2.0.0");  // true

// Range validation
semver.validRange("^1.0.0");           // true
semver.validRange(">=1.0.0 <2.0.0");   // true
semver.validRange("invalid-range");     // false

// Dependency checking
function checkDependencyVersion(installedVersion, requiredRange) {
  if (!semver.valid(installedVersion)) {
    return { valid: false, error: "Invalid installed version" };
  }
  
  if (!semver.validRange(requiredRange)) {
    return { valid: false, error: "Invalid version range" };
  }
  
  if (semver.satisfies(installedVersion, requiredRange)) {
    return { valid: true, compatible: true };
  } else {
    return { valid: true, compatible: false, needsUpdate: true };
  }
}

const result = checkDependencyVersion("1.2.3", "^1.0.0");
console.log(result); // { valid: true, compatible: true }

Version Range Patterns

Understanding different semver range patterns and their meanings.

Common Range Patterns:

import { semver } from "gluegun";

// Exact version
semver.satisfies("1.2.3", "1.2.3");     // Only 1.2.3

// Caret ranges (^) - compatible within major version
semver.satisfies("1.2.3", "^1.0.0");    // >=1.0.0 <2.0.0
semver.satisfies("1.9.9", "^1.0.0");    // true
semver.satisfies("2.0.0", "^1.0.0");    // false

// Tilde ranges (~) - compatible within minor version  
semver.satisfies("1.2.3", "~1.2.0");    // >=1.2.0 <1.3.0
semver.satisfies("1.2.9", "~1.2.0");    // true
semver.satisfies("1.3.0", "~1.2.0");    // false

// X-ranges - wildcard matching
semver.satisfies("1.2.3", "1.2.x");     // 1.2.0, 1.2.1, 1.2.999...
semver.satisfies("1.2.3", "1.x");       // 1.0.0, 1.999.999...

// Comparison operators
semver.satisfies("2.0.0", ">1.0.0");    // Greater than
semver.satisfies("2.0.0", ">=1.0.0");   // Greater than or equal
semver.satisfies("1.0.0", "<2.0.0");    // Less than
semver.satisfies("1.0.0", "<=1.0.0");   // Less than or equal

// Hyphen ranges
semver.satisfies("1.5.0", "1.0.0 - 2.0.0");  // >=1.0.0 <=2.0.0

// Multiple ranges (OR)
semver.satisfies("1.5.0", "^1.0.0 || ^2.0.0");  // Either range

// Multiple constraints (AND)
semver.satisfies("1.5.0", ">=1.0.0 <2.0.0");    // Both constraints

Complete Semver Interface

interface GluegunSemver {
  /**
   * Validate semantic version string
   * @param version - Version to validate
   * @returns Valid version or null
   */
  valid(version: string): string | null;

  /**
   * Clean and normalize version string
   * @param version - Version to clean
   * @returns Cleaned version or null
   */
  clean(version: string): string | null;

  /**
   * Check if version satisfies range
   * @param version - Version to check
   * @param range - Range pattern
   * @returns True if version satisfies range
   */
  satisfies(version: string, range: string): boolean;

  /**
   * Check if first version is greater than second
   * @param version - First version
   * @param other - Second version
   * @returns True if first > second
   */
  gt(version: string, other: string): boolean;

  /**
   * Check if first version is less than second
   * @param version - First version
   * @param other - Second version
   * @returns True if first < second
   */
  lt(version: string, other: string): boolean;

  /**
   * Validate version range string
   * @param range - Range to validate
   * @returns True if valid range
   */
  validRange(range: string): boolean | null;
}

Comprehensive Usage Example:

// CLI command for version management
export = {
  name: "version",
  description: "Manage project versions and dependencies",
  run: async (toolbox) => {
    const { semver, print, prompt, filesystem, parameters } = toolbox;
    
    const action = parameters.first;
    
    switch (action) {
      case 'check':
        await checkVersion(toolbox);
        break;
      case 'bump':
        await bumpVersion(toolbox);
        break;
      case 'deps':
        await checkDependencies(toolbox);
        break;
      case 'compare':
        await compareVersions(toolbox);
        break;
      default:
        await showCurrentVersion(toolbox);
    }
  }
};

async function showCurrentVersion(toolbox) {
  const { semver, print, filesystem } = toolbox;
  
  const pkg = filesystem.read('package.json', 'json');
  if (!pkg) {
    print.error('No package.json found');
    return;
  }
  
  const currentVersion = pkg.version;
  if (semver.valid(currentVersion)) {
    print.success(`Current version: ${currentVersion}`);
  } else {
    print.error(`Invalid version in package.json: ${currentVersion}`);
  }
}

async function bumpVersion(toolbox) {
  const { semver, print, prompt, filesystem } = toolbox;
  
  const pkg = filesystem.read('package.json', 'json');
  if (!pkg) {
    print.error('No package.json found');
    return;
  }
  
  const currentVersion = pkg.version;
  if (!semver.valid(currentVersion)) {
    print.error(`Invalid current version: ${currentVersion}`);
    return;
  }
  
  const bumpType = await prompt.ask({
    type: 'select',
    name: 'type',
    message: 'Version bump type:',
    choices: [
      { name: 'patch', message: 'Patch (1.0.0 → 1.0.1)' },
      { name: 'minor', message: 'Minor (1.0.0 → 1.1.0)' },
      { name: 'major', message: 'Major (1.0.0 → 2.0.0)' },
      { name: 'custom', message: 'Custom version' }
    ]
  });
  
  let newVersion;
  if (bumpType.type === 'custom') {
    const input = await prompt.ask({
      type: 'input',
      name: 'version',
      message: 'Enter new version:',
      validate: (v) => semver.valid(v) ? true : 'Invalid semantic version'
    });
    newVersion = input.version;
  } else {
    // Simulate version bumping (normally you'd use semver.inc)
    const parts = currentVersion.split('.');
    if (bumpType.type === 'patch') {
      parts[2] = String(parseInt(parts[2]) + 1);
    } else if (bumpType.type === 'minor') {
      parts[1] = String(parseInt(parts[1]) + 1);
      parts[2] = '0';
    } else if (bumpType.type === 'major') {
      parts[0] = String(parseInt(parts[0]) + 1);
      parts[1] = '0';
      parts[2] = '0';
    }
    newVersion = parts.join('.');
  }
  
  if (semver.gt(newVersion, currentVersion)) {
    pkg.version = newVersion;
    filesystem.write('package.json', pkg);
    print.success(`Version bumped: ${currentVersion} → ${newVersion}`);
  } else {
    print.error('New version must be greater than current version');
  }
}

async function checkDependencies(toolbox) {
  const { semver, print, filesystem } = toolbox;
  
  const pkg = filesystem.read('package.json', 'json');
  if (!pkg) {
    print.error('No package.json found');
    return;
  }
  
  const dependencies = { ...pkg.dependencies, ...pkg.devDependencies };
  const issues = [];
  
  for (const [name, range] of Object.entries(dependencies)) {
    if (!semver.validRange(range)) {
      issues.push({
        package: name,
        range,
        issue: 'Invalid version range'
      });
    }
  }
  
  if (issues.length > 0) {
    print.warning('Dependency issues found:');
    print.table([
      ['Package', 'Range', 'Issue'],
      ...issues.map(i => [i.package, i.range, i.issue])
    ]);
  } else {
    print.success('All dependency ranges are valid');
  }
}

async function compareVersions(toolbox) {
  const { semver, print, prompt } = toolbox;
  
  const versions = await prompt.ask([
    {
      type: 'input',
      name: 'version1',
      message: 'First version:',
      validate: (v) => semver.valid(v) ? true : 'Invalid version'
    },
    {
      type: 'input',
      name: 'version2',
      message: 'Second version:',
      validate: (v) => semver.valid(v) ? true : 'Invalid version'
    }
  ]);
  
  const v1 = versions.version1;
  const v2 = versions.version2;
  
  print.info(`Comparing ${v1} and ${v2}:`);
  
  if (semver.gt(v1, v2)) {
    print.success(`${v1} > ${v2}`);
  } else if (semver.lt(v1, v2)) {
    print.success(`${v1} < ${v2}`);
  } else {
    print.success(`${v1} = ${v2}`);
  }
  
  // Test range satisfaction
  const range = await prompt.ask({
    type: 'input',
    name: 'range',
    message: 'Test against range (optional):',
    validate: (v) => !v || semver.validRange(v) ? true : 'Invalid range'
  });
  
  if (range.range) {
    print.info(`Range satisfaction test: ${range.range}`);
    print.info(`${v1} satisfies range: ${semver.satisfies(v1, range.range)}`);
    print.info(`${v2} satisfies range: ${semver.satisfies(v2, range.range)}`);
  }
}