@hyperfrontend/versioning/flow

flow/

Composable versioning workflow system for orchestrating release operations.

Overview

This module provides a declarative flow execution engine for versioning workflows. Flows are sequences of steps that perform operations like fetching registry versions, analyzing commits, calculating bumps, generating changelogs, and creating git commits/tags.

Architecture

Execution Options

FlowExecutionOptions

Option Default Description
showDiff false Preview changes before committing to VFS
diffFormat 'unified' Diff format: 'unified' or 'simple'
rollbackOnFailure false Discard all VFS changes if any step fails

WriteChangelogStepOptions

Option Default Description
backupChangelog false Backup existing changelog before writing (uses tree.rename())

Usage

Basic Execution

import { createConventionalFlow, executeFlow } from '@hyperfrontend/versioning/flow'

const flow = createConventionalFlow({ dryRun: true })
const result = await executeFlow(flow, 'lib-utils', '/workspace')

console.log(result.summary)
// "Flow success in 234ms: 8 completed, 0 skipped, 0 failed. Version: 1.2.3 → 1.3.0"

Custom Flow

import {
  createFlow,
  createFetchRegistryStep,
  createAnalyzeCommitsStep,
  createCalculateBumpStep,
  executeFlow,
} from '@hyperfrontend/versioning/flow'

const minimalFlow = createFlow('minimal', 'Minimal Flow', [
  createFetchRegistryStep(),
  createAnalyzeCommitsStep(),
  createCalculateBumpStep(),
])

const result = await executeFlow(minimalFlow, 'lib-utils', '/workspace', {
  dryRun: true,
})

Custom Step

import { createStep, addStep, createConventionalFlow } from '@hyperfrontend/versioning/flow'

const notifyStep = createStep(
  'notify',
  'Send Notification',
  async (ctx) => {
    const { state } = ctx
    console.log(`Released ${state.nextVersion}`)
    return { status: 'success', message: 'Notification sent' }
  },
  { dependsOn: ['create-commit'] }
)

let flow = createConventionalFlow()
flow = addStep(flow, notifyStep)

Flow Execution Lifecycle

Configuration

Option Type Default Description
preset string 'conventional' Flow preset name
releaseTypes string[] ['feat', 'fix', 'perf', 'revert'] Types that trigger releases
minorTypes string[] ['feat'] Types that trigger minor bumps
patchTypes string[] ['fix', 'perf', 'revert'] Types that trigger patch bumps
skipGit boolean false Skip git operations
skipTag boolean true Skip tag creation
skipChangelog boolean false Skip changelog update
dryRun boolean false Preview without changes
commitMessage string 'chore(${projectName}): release...' Commit message template
tagFormat string '${projectName}@${version}' Tag name template
trackDeps boolean false Track dependency bumps
releaseBranch string 'main' Allowed release branch
firstReleaseVersion string '0.1.0' Initial version for new packages
releaseAs string undefined Force bump type: 'major', 'minor', or 'patch'
maxCommitFallback number 500 Max commits to analyze when no base available
repository * undefined Repository config for compare URLs (see below)
changelogFileName string 'CHANGELOG.md' Custom changelog filename
commitTypeToSection object undefined Custom commit type → section mapping (see below)

Repository Configuration

The repository option controls compare URL generation in changelog entries:

// Auto-detect from package.json or git remote
createVersionFlow('conventional', { repository: 'inferred' })

// Disable compare URLs
createVersionFlow('conventional', { repository: 'disabled' })

// Explicit configuration
createVersionFlow('conventional', {
  repository: {
    mode: 'explicit',
    repository: {
      platform: 'github',
      baseUrl: 'https://github.com/owner/repo',
    },
  },
})

// Inferred with custom order
createVersionFlow('conventional', {
  repository: {
    mode: 'inferred',
    inferenceOrder: ['git-remote', 'package-json'], // Try git first
  },
})

When repository is resolved, changelog entries include compare URLs:

## [1.2.0](https://github.com/owner/repo/compare/v1.1.0...v1.2.0) - 2026-03-17

Commit Type to Section Mapping

The commitTypeToSection option customizes how commit types map to changelog sections:

createVersionFlow('conventional', {
  commitTypeToSection: {
    // Override default mapping
    chore: 'other',

    // Add custom commit type
    wip: 'other',

    // Exclude type from changelog
    docs: null,
  },
})

Default mapping:

Commit Type Section
feat features
fix fixes
perf performance
docs documentation
refactor refactoring
revert other
build build
ci ci
test tests
chore chores
style other

Unmapped types fall back to chores. Use null to exclude a type entirely.

Step Dependencies

Steps can declare dependencies using dependsOn:

const tagStep = createStep('create-tag', 'Create Tag', execute, {
  dependsOn: ['create-commit'], // Only runs after create-commit succeeds
})

The executor respects dependencies and skips steps when dependencies fail.

Error Handling

Steps can use continueOnError to allow the flow to continue:

const optionalStep = createStep('optional', 'Optional Step', execute, {
  continueOnError: true, // Flow continues even if this fails
})

Flow results include detailed step-by-step outcomes:

const result = await executeFlow(flow, project, root)

for (const step of result.steps) {
  console.log(`${step.stepName}: ${step.status}`)
  if (step.error) {
    console.error(step.error.message)
  }
}

See Also

  • commits/ — Commit parsing for analyze-commits step
  • semver/ — Version bumping for calculate-bump step
  • changelog/ — Changelog generation step
  • git/ — Git operations for commit/tag steps
  • registry/ — Registry queries for fetch-registry step
  • workspace/ — Workspace operations for batch flows
  • Main README — Package overview and quick start
  • ARCHITECTURE.md — Design principles and data flow

API Reference

ƒ Functions

§function

createResolveRepositoryStep(): FlowStep

Creates the resolve-repository step.
This step resolves repository configuration for compare URL generation. It supports multiple resolution modes:
  • undefined or 'disabled': No-op, backward compatible default
  • 'inferred': Auto-detect from package.json or git remote
  • RepositoryConfig: Direct repository configuration provided
  • RepositoryResolution: Fine-grained control with mode and options
State updates:
  • repositoryConfig: Resolved repository configuration (if successful)

Returns

FlowStep
A FlowStep that resolves repository configuration

Example

Configuring repository resolution modes

// Auto-detect repository
const flow = createFlow({
  repository: 'inferred'
})

// Explicit repository
const flow = createFlow({
  repository: {
    platform: 'github',
    baseUrl: 'https://github.com/owner/repo'
  }
})
§function

addStep(flow: VersionFlow, step: FlowStep): VersionFlow

Adds a step to a flow. Returns a new flow with the step appended.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to extend
§step
FlowStep
The step to add

Returns

VersionFlow
A new VersionFlow with the step added

Example

Adding a custom step to a flow

import { addStep, createConventionalFlow, createNoopStep } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const extended = addStep(flow, createNoopStep('custom', 'Custom Step'))

console.log(extended.steps.length)
// => original steps + 1
§function

createAnalyzeCommitsStep(): FlowStep

Creates the analyze-commits step.
This step:
  1. Uses publishedCommit from npm registry (set by fetch-registry step)
  2. Verifies the commit is reachable from current HEAD
  3. Gets all commits since that commit (or recent commits if first release/fallback)
  4. Parses each commit using conventional commit format
  5. Classifies commits based on scope filtering strategy
  6. Filters to only release-worthy commits that belong to this project
State updates:
  • effectiveBaseCommit: The verified base commit (null if fallback was used)
  • commits: Array of parsed conventional commits (for backward compatibility)
  • classificationResult: Full classification result with source attribution

Returns

FlowStep
A FlowStep that analyzes commits

Example

Analyzing commits since last release

import { createAnalyzeCommitsStep, executeStep } from '@hyperfrontend/versioning'

const step = createAnalyzeCommitsStep()
const result = await executeStep(step, context)

// Access analyzed commits
console.log(result.stateUpdates?.commits)
// => [{ type: 'feat', subject: 'add feature' }, ...]
§function

createBatchReleaseFlow(config?: Partial<FlowConfig>): VersionFlow

Creates a flow for releasing multiple packages independently.
This is a variant that skips commit/tag creation, intended to be used when releasing multiple packages in sequence with a single commit at the end.

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow for batch independent releases

Example

Releasing multiple packages in batch

import { createBatchReleaseFlow, executeFlow } from '@hyperfrontend/versioning'

// Release multiple packages without individual commits
for (const pkg of ['lib-a', 'lib-b', 'lib-c']) {
  await executeFlow(createBatchReleaseFlow(), pkg, '/workspace')
}
// Then create a single combined commit
§function

createCalculateBumpStep(): FlowStep

Creates the calculate-bump step.
This step:
  1. Analyzes commit types and breaking changes
  2. Determines the appropriate bump level
  3. Calculates the next version
State updates:
  • bumpType: 'major' | 'minor' | 'patch' | 'none'
  • nextVersion: Calculated next version string

Returns

FlowStep
A FlowStep that calculates version bump

Example

Running the calculate-bump step

import { createCalculateBumpStep, executeStep } from '@hyperfrontend/versioning'

const step = createCalculateBumpStep()
const result = await executeStep(step, context)

// Get the calculated bump
console.log(result.stateUpdates?.bumpType)
// => 'minor'
console.log(result.stateUpdates?.nextVersion)
// => '1.2.0'
§function

createCascadeDependenciesStep(): FlowStep

Creates a step that updates dependent packages in a monorepo.
This step cascades version updates to packages that depend on the updated package.

Returns

FlowStep
A FlowStep that cascades dependency updates

Example

Updating dependent packages in a monorepo

import { createCascadeDependenciesStep, executeStep } from '@hyperfrontend/versioning'

const step = createCascadeDependenciesStep()
const result = await executeStep(step, contextWithTrackDeps)

// Dependent packages are updated
console.log(result.message)
§function

createChangelogOnlyFlow(config?: Partial<FlowConfig>): VersionFlow

Creates a changelog-only flow.
Only generates changelog, no version bumps or git operations.

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow that only updates changelog

Example

Creating a changelog-only flow

import { createChangelogOnlyFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createChangelogOnlyFlow()
const result = await executeFlow(flow, 'my-lib', '/workspace')

// Only changelog is updated, no version bump
console.log(result.state.changelogEntry)
§function

createCheckDependentBumpsStep(): FlowStep

Creates a step that checks for dependent package bumps.
In independent versioning, when a dependency is bumped, dependents may also need version bumps.

Returns

FlowStep
A FlowStep that checks dependent bumps

Example

Checking if dependent packages need bumps

import { createCheckDependentBumpsStep, executeStep } from '@hyperfrontend/versioning'

const step = createCheckDependentBumpsStep()
const result = await executeStep(step, contextWithTrackDeps)

// Result indicates if any dependents need bumps
console.log(result.message)
§function

createCheckIdempotencyStep(): FlowStep

Creates a step that checks for idempotency.
This step prevents redundant releases by checking if the calculated version is already published.

Returns

FlowStep
A FlowStep that checks idempotency

Example

Preventing duplicate releases

import { createCheckIdempotencyStep, executeStep } from '@hyperfrontend/versioning'

const step = createCheckIdempotencyStep()
const result = await executeStep(step, context)

// If version already published, step skips with bumpType: 'none'
if (result.status === 'skipped') {
  console.log('Version already published')
}
§function

createCombinedChangelogStep(): FlowStep

Creates a step that generates a combined changelog for all packages.

Returns

FlowStep
A FlowStep that creates a combined changelog

Example

Generating a combined changelog for all packages

import { createCombinedChangelogStep, executeStep } from '@hyperfrontend/versioning'

const step = createCombinedChangelogStep()
const result = await executeStep(step, context)

// Single changelog for all packages
console.log(result.message)
§function

createConventionalFlow(config?: Partial<FlowConfig>): VersionFlow

Creates a conventional flow.
This flow follows the standard conventional commits workflow:
  1. Fetch published version from registry
  2. Resolve repository configuration (for compare URLs)
  3. Analyze commits since last release
  4. Calculate version bump based on commit types
  5. Check if version already published (idempotency)
  6. Generate changelog entry (with compare URL if repository resolved)
  7. Update package.json version
  8. Write changelog to file
  9. Create git commit (optional)
  10. Create git tag (optional, typically after publish)

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow configured for conventional commits

Example

Creating a conventional versioning flow

import { createConventionalFlow, executeFlow } from '@hyperfrontend/versioning'

// Use defaults
const flow = createConventionalFlow()

// With overrides
const customFlow = createConventionalFlow({
  skipTag: false,
  releaseTypes: ['feat', 'fix'],
})

const result = await executeFlow(flow, 'lib-utils', '/workspace')
§function

createDryRunFlow(preset: FlowPreset, config?: Partial<FlowConfig>): VersionFlow

Creates a dry-run version of a flow.
Convenience function that creates a flow with dryRun enabled.

Parameters

NameTypeDescription
§preset
FlowPreset
The preset to use
(default: 'conventional')
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow configured for dry run

Example

Creating a dry-run flow for preview

import { createDryRunFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createDryRunFlow('conventional')
const result = await executeFlow(flow, 'my-lib', '/workspace')

// Preview changes without modifying files
console.log('Would release:', result.state.nextVersion)
§function

createFailedResult(error: Error, message?: string): FlowStepResult

Creates a failed step result.

Parameters

NameTypeDescription
§error
Error
Error that caused the failure
§message?
string
Optional message (defaults to error.message)

Returns

FlowStepResult
A FlowStepResult with 'failed' status

Example

Handling step execution errors

import { createFailedResult } from '@hyperfrontend/versioning'

// In a step handler:
try {
  await doOperation()
} catch (err) {
  return createFailedResult(err as Error, 'Operation failed')
}
§function

createFetchRegistryStep(): FlowStep

Creates the fetch-registry step.
This step:
  1. Queries the registry for the latest published version
  2. Reads the current version from package.json
  3. Determines if this is a first release
State updates:
  • publishedVersion: Latest version on registry (null if not published)
  • currentVersion: Version from local package.json
  • isFirstRelease: True if never published

Returns

FlowStep
A FlowStep that fetches registry information

Example

Fetching published version from registry

import { createFetchRegistryStep, executeStep } from '@hyperfrontend/versioning'

const step = createFetchRegistryStep()
const result = await executeStep(step, context)

// Check if first release
if (result.stateUpdates?.isFirstRelease) {
  console.log('First release detected')
}
§function

createFixedVersionFlow(version: string, config?: Partial<FlowConfig>): VersionFlow

Creates a fixed versioning flow.
Similar to synced, but uses a fixed version scheme where the version is explicitly provided rather than calculated from commits.

Parameters

NameTypeDescription
§version
string
The fixed version to use
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow with a fixed version

Example

Creating a fixed version release flow

import { createFixedVersionFlow, executeFlow } from '@hyperfrontend/versioning'

// Release with explicit version, ignoring commit analysis
const flow = createFixedVersionFlow('2.0.0')
const result = await executeFlow(flow, 'workspace', '/workspace')

console.log(result.state.nextVersion)
// => '2.0.0'
§function

createFlow(id: string, name: string, steps: unknown, options: CreateFlowOptions): VersionFlow

Creates a version flow.

Parameters

NameTypeDescription
§id
string
Flow identifier
§name
string
Human-readable flow name
§steps
unknown
Ordered steps to execute
§options
CreateFlowOptions
Optional flow configuration
(default: {})

Returns

VersionFlow
A VersionFlow object

Example

Creating a custom flow

const myFlow = createFlow(
  'custom',
  'Custom Release Flow',
  [fetchStep, analyzeStep, bumpStep],
  { description: 'My custom versioning workflow' }
)
§function

createGenerateChangelogStep(): FlowStep

Creates the generate-changelog step.
This step:
  1. Groups commits by type/section
  2. Creates changelog items from commits
  3. Assembles a complete changelog entry
State updates:
  • changelogEntry: The generated ChangelogEntry

Returns

FlowStep
A FlowStep that generates changelog

Example

Generating a changelog entry from commits

import { createGenerateChangelogStep, executeStep } from '@hyperfrontend/versioning'

const step = createGenerateChangelogStep()
const result = await executeStep(step, context)

// Access the generated changelog entry
const entry = result.stateUpdates?.changelogEntry
console.log(entry?.version, entry?.sections.length)
// => '1.2.0', 3
§function

createGitCommitStep(): FlowStep

Creates the create-commit step.
This step:
  1. Stages modified files
  2. Creates a commit with the configured message
State updates:
  • commitHash: Hash of the created commit

Returns

FlowStep
A FlowStep that creates a git commit

Example

Creating and staging a release commit

import { createGitCommitStep, executeStep } from '@hyperfrontend/versioning'

const step = createGitCommitStep()
const result = await executeStep(step, context)

// Get the created commit hash
console.log(result.stateUpdates?.commitHash)
// => 'a1b2c3d4e5f6...'
§function

createIndependentFlow(config?: Partial<FlowConfig>): VersionFlow

Creates an independent versioning flow.
This flow is designed for monorepos where each package is versioned independently:
  1. Fetch published version from registry
  2. Analyze commits since last release
  3. Calculate version bump based on commit types
  4. Check for dependent package bumps (cascade)
  5. Check if version already published (idempotency)
  6. Generate changelog entry
  7. Update package.json version
  8. Cascade dependency updates
  9. Write changelog to file
  10. Create git commit
  11. Create git tag

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow configured for independent versioning

Example

Creating an independent versioning flow

import { createIndependentFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createIndependentFlow()
const result = await executeFlow(flow, 'lib-utils', '/workspace')

// Check which dependents were bumped
console.log(result.state.cascadedBumps)
§function

createMinimalFlow(config?: Partial<FlowConfig>): VersionFlow

Creates a minimal flow for quick releases.
Skips changelog and tag creation.

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A minimal VersionFlow

Example

Creating a minimal release flow

import { createMinimalFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createMinimalFlow()
const result = await executeFlow(flow, 'my-lib', '/workspace')

// Quick release without changelog or tags
console.log('Released:', result.state.nextVersion)
§function

createNoopStep(id: string, name: string, message: string): FlowStep

Creates a step that succeeds immediately. Useful for placeholder or conditional steps.

Parameters

NameTypeDescription
§id
string
Unique identifier within the flow
§name
string
Display label shown during execution
§message
string
Success message
(default: 'Step completed (no-op)')

Returns

FlowStep
A FlowStep that always succeeds

Example

Creating a placeholder step

import { createNoopStep, executeStep } from '@hyperfrontend/versioning'

const step = createNoopStep('placeholder', 'Placeholder Step')
const result = await executeStep(step, context)

console.log(result.status)
// => 'success'
§function

createPushTagStep(): FlowStep

Creates a step that pushes the created tag to remote.

Returns

FlowStep
A FlowStep that pushes the git tag

Example

Pushing the tag to remote

import { createPushTagStep, executeStep } from '@hyperfrontend/versioning'

const step = createPushTagStep()
const result = await executeStep(step, context)

// Tag is pushed to remote
console.log(result.message)
// => 'Pushed tag: my-lib@1.2.0'
§function

createSkippedResult(message: string): FlowStepResult

Creates a skipped step result.

Parameters

NameTypeDescription
§message
string
Explanation for why the step was skipped

Returns

FlowStepResult
A FlowStepResult with 'skipped' status

Example

Skipping a step conditionally

import { createSkippedResult } from '@hyperfrontend/versioning'

// In a step handler:
if (!config.enabled) {
  return createSkippedResult('Feature disabled in config')
}
§function

createStep(id: string, name: string, execute: StepExecutor, options: CreateStepOptions): FlowStep

Creates a flow step.

Parameters

NameTypeDescription
§id
string
Unique step identifier
§name
string
Human-readable step name
§execute
StepExecutor
Step executor function
§options
CreateStepOptions
Optional step configuration
(default: {})

Returns

FlowStep
A FlowStep object

Example

Creating a custom fetch step

const fetchStep = createStep(
  'fetch-registry',
  'Fetch Registry Version',
  async (ctx) => {
    const version = await ctx.registry.getLatestVersion(ctx.packageName)
    return {
      status: 'success',
      stateUpdates: { publishedVersion: version },
      message: `Found published version: ${version}`
    }
  }
)
§function

createSuccessResult(message: string, stateUpdates?: Partial<FlowState>): FlowStepResult

Creates a success step result.

Parameters

NameTypeDescription
§message
string
Output text describing what the step accomplished
§stateUpdates?
Partial<FlowState>
Optional state updates to apply after step completion

Returns

FlowStepResult
A FlowStepResult with 'success' status

Example

Returning a success result with state updates

import { createSuccessResult } from '@hyperfrontend/versioning'

// In a step handler:
return createSuccessResult('Updated 3 files', {
  modifiedFiles: ['/path/a.ts', '/path/b.ts', '/path/c.ts']
})
§function

createSyncAllPackagesStep(): FlowStep

Creates a step that updates all workspace packages to the same version.

Returns

FlowStep
A FlowStep that syncs all package versions

Example

Syncing all packages to the same version

import { createSyncAllPackagesStep, executeStep } from '@hyperfrontend/versioning'

const step = createSyncAllPackagesStep()
const result = await executeStep(step, context)

// All packages updated to same version
console.log(result.stateUpdates?.modifiedFiles)
§function

createSyncedFlow(config?: Partial<FlowConfig>): VersionFlow

Creates a synced versioning flow.
This flow maintains the same version across all packages in a monorepo. When any package changes, all packages get the same new version.
Flow steps:
  1. Fetch published version from registry
  2. Analyze commits across all packages
  3. Calculate version bump (highest needed)
  4. Check if version already published
  5. Sync all package versions
  6. Generate combined changelog
  7. Write changelog to root
  8. Create git commit
  9. Create single git tag

Parameters

NameTypeDescription
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow configured for synced versioning

Example

Creating a synced versioning flow

import { createSyncedFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createSyncedFlow()
const result = await executeFlow(flow, 'workspace', '/workspace')

// All packages now share the same version
console.log(`Released v${result.state.nextVersion}`)
§function

createTagStep(): FlowStep

Creates the create-tag step.
This step:
  1. Creates an annotated git tag
  2. Uses the configured tag format
State updates:
  • tagName: Name of the created tag

Returns

FlowStep
A FlowStep that creates a git tag

Example

Creating an annotated version tag

import { createTagStep, executeStep } from '@hyperfrontend/versioning'

const step = createTagStep()
const result = await executeStep(step, context)

// Get the created tag name
console.log(result.stateUpdates?.tagName)
// => 'my-lib@1.2.0'
§function

createUpdatePackageStep(): FlowStep

Creates the update-packages step.
This step:
  1. Updates the version field in package.json
  2. Tracks the modified files
State updates:
  • modifiedFiles: Adds package.json to list

Returns

FlowStep
A FlowStep that updates package.json

Example

Updating the package.json version field

import { createUpdatePackageStep, executeStep } from '@hyperfrontend/versioning'

const step = createUpdatePackageStep()
const result = await executeStep(step, context)

// package.json version field is updated
console.log(result.stateUpdates?.modifiedFiles)
// => ['/workspace/libs/my-lib/package.json']
§function

createVersionFlow(preset: FlowPreset, config?: Partial<FlowConfig>): VersionFlow

Creates a version flow from a preset.
This is the main factory function for creating flows. Use this when you want to create a flow based on a standard preset with optional customization.

Parameters

NameTypeDescription
§preset
FlowPreset
The preset to use (default: 'conventional')
(default: 'conventional')
§config?
Partial<FlowConfig>
Optional configuration overrides

Returns

VersionFlow
A VersionFlow configured for the preset

Example

Creating flows from presets

import { createVersionFlow, executeFlow } from '@hyperfrontend/versioning'

// Create a conventional flow
const flow = createVersionFlow('conventional')

// Create an independent flow with custom config
const customFlow = createVersionFlow('independent', {
  skipTag: true,
  trackDeps: true,
})

const result = await executeFlow(flow, 'lib-utils', '/workspace')
§function

createWriteChangelogStep(): FlowStep

Creates the write-changelog step.
This step writes the generated changelog entry to CHANGELOG.md.

Returns

FlowStep
A FlowStep that writes changelog to file

Example

Writing the changelog entry to CHANGELOG.md

import { createWriteChangelogStep, executeStep } from '@hyperfrontend/versioning'

const step = createWriteChangelogStep()
const result = await executeStep(step, context)

// CHANGELOG.md is updated with the new entry
console.log(result.message)
// => 'Updated CHANGELOG.md with version 1.2.0'
§function

dryRun(flow: VersionFlow, projectName: string, workspaceRoot: string, options: Omit<ExecuteOptions, "dryRun">): Promise<FlowResult>

Executes a flow in dry-run mode.
Convenience wrapper that sets dryRun: true.

Parameters

NameTypeDescription
§flow
VersionFlow
The version flow to execute
§projectName
string
Name of the project to version
§workspaceRoot
string
Absolute path to workspace root
§options
Omit<ExecuteOptions, "dryRun">
Execution options (dryRun forced to true)
(default: {})

Returns

Promise<FlowResult>
Flow execution result (no actual changes made)

Example

Previewing version changes without modifying files

import { dryRun, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const result = await dryRun(flow, 'my-lib', '/workspace')

// Preview what would happen
console.log('Would bump to:', result.state.nextVersion)
// No files modified, no git operations performed
§function

executeFlow(flow: VersionFlow, projectName: string, workspaceRoot: string, options: ExecuteOptions): Promise<FlowResult>

Executes a version flow.
This is the main entry point for running a versioning workflow. Steps are executed in order, with state accumulated between steps.

Parameters

NameTypeDescription
§flow
VersionFlow
The version flow to execute
§projectName
string
Name of the project to version (e.g., 'lib-versioning')
§workspaceRoot
string
Absolute path to workspace root
§options
ExecuteOptions
Execution options
(default: {})

Returns

Promise<FlowResult>
Flow execution result

Example

Executing a conventional version flow

import { createConventionalFlow, executeFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow({ dryRun: true })
const result = await executeFlow(flow, 'lib-utils', '/path/to/workspace')

console.log(result.summary)
// "Flow success in 234ms: 8 completed, 0 skipped, 0 failed. Version: 1.2.3 → 1.3.0"
§function

getAvailablePresets(): unknown

Gets the list of available presets.

Returns

unknown
Array of preset names

Example

Listing available presets

import { getAvailablePresets } from '@hyperfrontend/versioning'

const presets = getAvailablePresets()
// => ['conventional', 'independent', 'synced']
§function

getPresetDescription(preset: FlowPreset): string

Gets the description for a preset.

Parameters

NameTypeDescription
§preset
FlowPreset
The preset name

Returns

string
Human-readable description

Example

Getting a preset description

import { getPresetDescription } from '@hyperfrontend/versioning'

console.log(getPresetDescription('independent'))
// => 'Version packages independently with dependency tracking'
§function

getStep(flow: VersionFlow, stepId: string): FlowStep

Gets a step from a flow by ID.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to search
§stepId
string
The step ID to find

Returns

FlowStep
The step if found, undefined otherwise

Example

Getting a step by ID

import { getStep, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const step = getStep(flow, 'analyze-commits')

console.log(step?.name)
// => 'Analyze Commits'
§function

hasStep(flow: VersionFlow, stepId: string): boolean

Checks if a flow has a specific step.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to check
§stepId
string
The step ID to look for

Returns

boolean
True if the flow contains the step

Example

Checking if a flow has a step

import { hasStep, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()

console.log(hasStep(flow, 'create-commit'))
// => true
console.log(hasStep(flow, 'custom-step'))
// => false
§function

insertStep(flow: VersionFlow, step: FlowStep, index: number): VersionFlow

Inserts a step at a specific position.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to modify
§step
FlowStep
The step to insert
§index
number
Position to insert at (0-based)

Returns

VersionFlow
A new VersionFlow with the step inserted

Example

Inserting a step at a specific position

import { insertStep, createConventionalFlow, createNoopStep } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const modified = insertStep(flow, createNoopStep('early', 'Early Step'), 0)

console.log(modified.steps[0].id)
// => 'early'
§function

insertStepAfter(flow: VersionFlow, step: FlowStep, afterStepId: string): VersionFlow

Inserts a step after another step.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to modify
§step
FlowStep
The step to insert
§afterStepId
string
ID of the step to insert after

Returns

VersionFlow
A new VersionFlow with the step inserted

Example

Inserting a step after another step

import { insertStepAfter, createConventionalFlow, createNoopStep } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const modified = insertStepAfter(flow, createNoopStep('validate', 'Validate'), 'analyze-commits'))

// 'validate' step now follows 'analyze-commits'
§function

insertStepBefore(flow: VersionFlow, step: FlowStep, beforeStepId: string): VersionFlow

Inserts a step before another step.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to modify
§step
FlowStep
The step to insert
§beforeStepId
string
ID of the step to insert before

Returns

VersionFlow
A new VersionFlow with the step inserted

Example

Inserting a step before another step

import { insertStepBefore, createConventionalFlow, createNoopStep } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const modified = insertStepBefore(flow, createNoopStep('prep', 'Prepare'), 'create-commit'))

// 'prep' step now runs before 'create-commit'
§function

removeStep(flow: VersionFlow, stepId: string): VersionFlow

Removes a step from a flow by ID. Returns a new flow without the specified step.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to modify
§stepId
string
The ID of the step to remove

Returns

VersionFlow
A new VersionFlow without the step

Example

Removing a step from a flow

import { removeStep, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const minimal = removeStep(flow, 'generate-changelog')

// Flow no longer has changelog step
console.log(minimal.steps.find(s => s.id === 'generate-changelog'))
// => undefined
§function

replaceStep(flow: VersionFlow, stepId: string, newStep: FlowStep): VersionFlow

Replaces a step in the flow.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to modify
§stepId
string
ID of the step to replace
§newStep
FlowStep
The replacement step

Returns

VersionFlow
A new VersionFlow with the step replaced

Example

Replacing a step with a custom implementation

import { replaceStep, createConventionalFlow, createNoopStep } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const modified = replaceStep(flow, 'create-tag', createNoopStep('create-tag', 'Custom Tag'))

// 'create-tag' now uses custom implementation
§function

validateFlow(flow: VersionFlow): unknown

Validates a flow before execution.
Checks for:
  • Duplicate step IDs
  • Invalid dependency references
  • Circular dependencies

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to validate

Returns

unknown
Array of validation errors (empty if valid)

Example

Validating a flow before execution

import { validateFlow, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const errors = validateFlow(flow)

if (errors.length > 0) {
  console.error('Invalid flow:', errors)
}
// => []
§function

withConfig(flow: VersionFlow, config: Partial<FlowConfig>): VersionFlow

Updates the flow configuration.

Parameters

NameTypeDescription
§flow
VersionFlow
The flow to configure
§config
Partial<FlowConfig>
Configuration updates to merge

Returns

VersionFlow
A new VersionFlow with updated config

Example

Updating flow configuration

import { withConfig, createConventionalFlow } from '@hyperfrontend/versioning'

const flow = createConventionalFlow()
const dryRunFlow = withConfig(flow, { dryRun: true })

// Flow now runs in dry-run mode

Interfaces

§interface

CreateFlowOptions

Options for creating a version flow.

Properties

§config?:FlowConfig
Flow configuration
§description?:string
Flow description
§interface

CreateStepOptions

Options for creating a flow step.

Properties

§continueOnError?:boolean
Whether step failure should fail the flow
§dependsOn?:unknown
Steps that must complete before this one
§description?:string
Step description
§skipIf?:StepCondition
Condition for skipping step
§interface

ExecuteOptions

Options for flow execution.

Properties

§diffFormat?:DiffFormat
Output format for diff: 'unified' (full patch) or 'summary' (stats only)
§dryRun?:boolean
Dry run - don't commit changes to disk
§git?:GitClient
Custom GitClient instance (for testing)
§logger?:Logger
Custom logger (defaults to console)
§projectRoot?:string
Project root path (relative to workspace root, e.g., 'libs/utils/immutable-api'). If provided, this is used directly instead of deriving from project name. This is the recommended approach when calling from the Nx executor.
§registry?:Registry
Custom Registry instance (for testing)
§rollbackOnFailure?:boolean
Whether to rollback all pending changes on step failure.
When true (default), pending VFS changes are discarded when a step fails and continueOnError is false. This ensures no partial state remains.
§showDiff?:boolean
Show unified diff of changes before committing
§tree?:Tree
Custom Tree instance (for testing)
§verbose?:boolean
Verbose logging
§interface

FlowConfig

Flow configuration options.

Properties

§readonly allowPrerelease?:boolean
Allow prerelease versions
§readonly backupChangelog?:boolean
Create a backup of the existing changelog before modification.
When enabled:
  1. Existing CHANGELOG.md is renamed to CHANGELOG.backup.md
  2. New changelog is written
  3. Backup is deleted on success
Useful for safety during changelog regeneration.
§readonly changelogFileName?:string
Changelog file name relative to project root.
§readonly commitMessage?:string
Custom commit message template
§readonly commitTypeToSection?:Partial<Record<string, ChangelogSectionType | null>>
Custom mapping from commit type to changelog section. Merged with defaults; use null to exclude a type from changelog.
§readonly dryRun?:boolean
Dry run mode - preview changes without applying
§readonly firstReleaseVersion?:string
Base version for first release
§readonly maxCommitFallback?:number
Maximum commits to analyze when no base commit is available. Used for first releases, history rewrites, and path-filtered queries.
Set higher if you expect >500 commits between releases.
§readonly minorTypes?:unknown
Commit types that trigger minor bumps
§readonly patchTypes?:unknown
Commit types that trigger patch bumps
§readonly prereleaseId?:string
Prerelease identifier (e.g., 'alpha', 'beta')
§readonly preset?:"conventional" | "independent" | "synced"
Preset name or custom configuration
§readonly releaseAs?:"minor" | "patch" | "major"
Force a specific bump type, bypassing commit analysis
§readonly releaseBranch?:string
Branch allowed for releases
§readonly releaseTypes?:unknown
Commit types that trigger releases
§readonly repository?:RepositoryConfig | "inferred" | "disabled" | RepositoryResolution
Repository resolution configuration for compare URL generation.
Controls how repository information is resolved:
  • 'disabled': No compare URLs generated (default, backward compatible)
  • 'inferred': Auto-detect from package.json or git remote
  • RepositoryResolution: Fine-grained control with explicit mode and options
  • RepositoryConfig: Direct repository configuration
§readonly scopeFiltering?:ScopeFilteringConfig
Commit scope filtering configuration. Controls how commits are attributed to projects in changelogs.
By default, hybrid filtering ensures commits are included based on conventional commit scope OR file changes within the project.
§readonly skipChangelog?:boolean
Skip changelog update
§readonly skipGit?:boolean
Skip git operations
§readonly skipTag?:boolean
Skip tag creation
§readonly tagFormat?:string
Custom tag format
§readonly trackDeps?:boolean
Track dependencies for cascade bumps
§interface

FlowContext

Execution context passed to each step. Contains all resources and accumulated state.
Note: state is mutable within context to allow accumulation, but individual FlowState objects are immutable.

Properties

§readonly config:FlowConfig
Flow configuration
§readonly git:GitClient
Git client
§readonly logger:Logger
Logger instance
§readonly packageName:string
Package name from package.json
§readonly projectName:string
Target project name
§readonly projectRoot:string
Project root path
§readonly registry:Registry
Registry client
§state:FlowState
Accumulated state from previous steps (mutable reference)
§readonly tree:Tree
Virtual file system tree
§readonly workspaceRoot:string
Workspace root path
§interface

FlowResult

Complete result of flow execution.

Properties

§readonly diffs?:unknown
Detailed diffs for pending changes. Populated when showDiff: true is passed to executeFlow. Provides unified diff format for programmatic access.
§readonly duration:number
Duration in milliseconds
§readonly modifiedFiles?:unknown
Files that were modified (or would be in dry-run)
§readonly state:FlowState
Final accumulated state
§readonly status:FlowStatus
Overall flow outcome
§readonly steps:unknown
Results for each step
§readonly summary:string
Summary message
§interface

FlowState

Accumulated state during flow execution. Each step can read previous state and contribute updates.

Properties

§readonly bumpType?:BumpType
Bump type (major/minor/patch/none)
§readonly changelogEntry?:ChangelogEntry
Generated changelog entry
§readonly classificationResult?:ClassificationResult
Classification result with source attribution (when scope filtering enabled)
§readonly commitHash?:string
Created git commit hash
§readonly commits?:unknown
Analyzed commits since last release
§readonly currentVersion?:string
Current/local version from package.json
§readonly effectiveBaseCommit?:string
The verified base commit used for commit scoping and changelog generation. This will be publishedCommit if that commit is reachable from HEAD, or null if a fallback was used (e.g., history was rewritten).
When null, compare URLs are omitted from the changelog.
§readonly isFirstRelease?:boolean
Whether this is a first release (no prior versions)
§readonly isPendingPublication?:boolean
Whether this is a pending publication scenario. True when currentVersion > publishedVersion, meaning the version was already bumped but not yet published to the registry. When true:
  • nextVersion is calculated from publishedVersion (not currentVersion)
  • Changelog entries > publishedVersion should be cleaned up
  • The correct changelog entry replaces any stacked ones
§readonly modifiedFiles?:unknown
Files modified during flow execution
§readonly nextVersion?:string
Calculated next version
§readonly publishedCommit?:string
Git commit hash of the last published version
§readonly publishedVersion?:string
Published version on registry (null if never published)
§readonly repositoryConfig?:RepositoryConfig
Repository configuration for compare URL generation
§readonly tagName?:string
Created git tag name
§interface

FlowStep

A single step in a version flow.
Steps are pure functions that:
  1. Read from context (state, config, services)
  2. Perform work (possibly with side effects via services)
  3. Return state updates

Properties

§readonly continueOnError?:boolean
Whether step failure should fail the flow
§readonly dependsOn?:unknown
Steps that must complete before this one
§readonly description?:string
Optional step description
§readonly execute:StepExecutor
Step function to execute
§readonly id:string
Step identifier (unique within flow)
§readonly name:string
Human-readable step name
§readonly skipIf?:StepCondition
Condition for skipping step
§interface

FlowStepResult

Result of a single step execution.

Properties

§readonly error?:Error
Error if failed
§readonly message?:string
Descriptive message
§readonly stateUpdates?:Partial<FlowState>
State updates from this step
§readonly status:"success" | "skipped" | "failed"
Step outcome
§interface

FlowStepResultWithId

Step result with step identification.

Properties

§readonly error?:Error
Error if failed
§readonly message?:string
Descriptive message
§readonly stateUpdates?:Partial<FlowState>
State updates from this step
§readonly status:"success" | "skipped" | "failed"
Step outcome
§readonly stepId:string
Step identifier
§readonly stepName:string
Step display name
§interface

Logger

Logger interface with level-specific methods and level control

Properties

§debug:DebugLevelFn
Debug-level output
§error:ErrorLevelFn
Error-level output
§getLogLevel:GetLogLevel
Gets the current log level
§info:InfoLevelFn
Info-level output
§log:LogLevelFn
Standard log output
§setLogLevel:SetLogLevel
Sets the current log level
§warn:WarnLevelFn
Warning-level output
§interface

VersionFlow

A complete version flow definition.
Flows are immutable configurations that define:
  1. What steps to execute
  2. In what order
  3. With what configuration

Properties

§readonly config:FlowConfig
Flow-level configuration
§readonly description?:string
Flow description
§readonly id:string
Flow identifier
§readonly name:string
Human-readable flow name
§readonly steps:unknown
Ordered steps to execute

Types

§type

FlowPreset

Supported flow presets.
type FlowPreset = "conventional" | "independent" | "synced"
§type

FlowStatus

Overall flow execution status.
type FlowStatus = "success" | "partial" | "failed" | "skipped"
§type

StepCondition

Step skip condition function type. Returns true if the step should be skipped.
type StepCondition = (context: FlowContext) => boolean
§type

StepExecutor

Step executor function type. Takes flow context and returns a step result promise.
type StepExecutor = (context: FlowContext) => Promise<FlowStepResult>

Variables

§type

RESOLVE_REPOSITORY_STEP_ID

§type

ANALYZE_COMMITS_STEP_ID

§type

CALCULATE_BUMP_STEP_ID

§type

CONVENTIONAL_FLOW_CONFIG

Default configuration for conventional flow.
§type

CREATE_COMMIT_STEP_ID

§type

CREATE_TAG_STEP_ID

§type

DEFAULT_CHANGELOG_FILENAME

Default changelog filename.
§type

DEFAULT_COMMIT_TYPE_TO_SECTION

Maps conventional commit types to changelog section types.
§type

DEFAULT_FLOW_CONFIG

Default flow configuration values.
§type

FETCH_REGISTRY_STEP_ID

§type

GENERATE_CHANGELOG_STEP_ID

§type

INDEPENDENT_FLOW_CONFIG

Default configuration for independent flow.
§type

SYNCED_FLOW_CONFIG

Default configuration for synced flow.
§type

UPDATE_PACKAGES_STEP_ID