@reelout/core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer.js +38 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/builder.js +52 -0
- package/dist/builder.js.map +1 -0
- package/dist/changelog.js +58 -0
- package/dist/changelog.js.map +1 -0
- package/dist/formatError-BXNdyRfu.js +23 -0
- package/dist/formatError-BXNdyRfu.js.map +1 -0
- package/dist/index.js +175 -1
- package/dist/index.js.map +1 -0
- package/dist/types-la_KkjCS.js +1 -0
- package/dist/types.js +2 -0
- package/dist/workflow-BoVmbvH6.js +2342 -0
- package/dist/workflow-BoVmbvH6.js.map +1 -0
- package/package.json +27 -3
- package/src/analyzer.ts +63 -0
- package/src/builder.ts +104 -0
- package/src/cache/TTLCache.ts +100 -0
- package/src/cache/index.ts +4 -0
- package/src/changelog.ts +97 -0
- package/src/config/const.ts +97 -0
- package/src/config/index.ts +11 -0
- package/src/config/isSemVerGitTag.ts +25 -0
- package/src/config/normalizeCommitFilter.ts +37 -0
- package/src/config/normalizeReleaseRule.ts +42 -0
- package/src/config/normalizeWorkspace.ts +34 -0
- package/src/config/resolvePlugin.ts +56 -0
- package/src/config/resolveReleaseTarget.ts +71 -0
- package/src/config/sanitizePrereleaseLabel.ts +18 -0
- package/src/git/branch/getCurrentGitBranch.ts +18 -0
- package/src/git/branch/index.ts +4 -0
- package/src/git/commit/createGitCommit.ts +29 -0
- package/src/git/commit/extractMentions.ts +34 -0
- package/src/git/commit/getCurrentGitCommit.ts +14 -0
- package/src/git/commit/getParsedCommit.ts +53 -0
- package/src/git/commit/index.ts +9 -0
- package/src/git/commit/parseCommit.ts +99 -0
- package/src/git/commit/parseGitPrettyFormat.ts +76 -0
- package/src/git/history/countGitHistory.ts +28 -0
- package/src/git/history/expandGitHistory.ts +108 -0
- package/src/git/history/index.ts +9 -0
- package/src/git/history/mergeGitHistory.ts +76 -0
- package/src/git/history/restoreFullGitHistory.ts +19 -0
- package/src/git/history/showGitHistory.ts +80 -0
- package/src/git/history/showParsedGitHistory.ts +30 -0
- package/src/git/index.ts +9 -0
- package/src/git/repository/ensureGitUserConfig.ts +31 -0
- package/src/git/repository/getGitConfigValue.ts +27 -0
- package/src/git/repository/getGitRemote.ts +20 -0
- package/src/git/repository/index.ts +10 -0
- package/src/git/repository/isGitShallowRepository.ts +21 -0
- package/src/git/repository/pushGitChanges.ts +25 -0
- package/src/git/repository/resolveGitRepository.ts +40 -0
- package/src/git/repository/withShallowLock.ts +62 -0
- package/src/git/revision/detectGitRelationship.ts +43 -0
- package/src/git/revision/index.ts +7 -0
- package/src/git/revision/isGitAncestor.ts +33 -0
- package/src/git/revision/readGitFile.ts +29 -0
- package/src/git/revision/resolveGitRevision.ts +23 -0
- package/src/git/tag/createGitTag.ts +25 -0
- package/src/git/tag/deleteGitTag.ts +17 -0
- package/src/git/tag/ensureGitTag.ts +63 -0
- package/src/git/tag/generateGitTagName.ts +43 -0
- package/src/git/tag/getCurrentGitTag.ts +23 -0
- package/src/git/tag/getGitTagMessage.ts +35 -0
- package/src/git/tag/getLatestGitTag.ts +22 -0
- package/src/git/tag/index.ts +13 -0
- package/src/git/tag/isGitTagConnected.ts +33 -0
- package/src/git/tag/isGitTagPointed.ts +38 -0
- package/src/git/tag/isGitTagPresent.ts +17 -0
- package/src/index.ts +8 -2
- package/src/types.ts +217 -0
- package/src/util/buildTrailerPattern.ts +20 -0
- package/src/util/compact.ts +14 -0
- package/src/util/execCommand.ts +130 -0
- package/src/util/execGit.ts +18 -0
- package/src/util/formatError.ts +21 -0
- package/src/util/formatFile.ts +41 -0
- package/src/util/index.ts +16 -0
- package/src/util/interpolate.ts +141 -0
- package/src/util/isCI.ts +23 -0
- package/src/util/isDefined.ts +12 -0
- package/src/util/isMatch.ts +43 -0
- package/src/util/readJson.ts +47 -0
- package/src/util/unique.ts +26 -0
- package/src/util/writeJson.ts +28 -0
- package/src/workflow/analysis/analyzePackageChanges.ts +54 -0
- package/src/workflow/analysis/index.ts +9 -0
- package/src/workflow/analysis/isCommitForPackage.ts +23 -0
- package/src/workflow/analysis/promotePrereleaseVersions.ts +50 -0
- package/src/workflow/analysis/propagateDependencyUpdates.ts +71 -0
- package/src/workflow/analysis/sliceUnreleased.ts +36 -0
- package/src/workflow/analysis/streamPackageCommits.ts +34 -0
- package/src/workflow/changelog/buildChangelog.ts +79 -0
- package/src/workflow/changelog/buildReleaseNotes.ts +50 -0
- package/src/workflow/changelog/extractLatestVersion.ts +22 -0
- package/src/workflow/changelog/extractPlainText.ts +38 -0
- package/src/workflow/changelog/extractPreviousVersion.ts +77 -0
- package/src/workflow/changelog/extractSemVer.ts +33 -0
- package/src/workflow/changelog/index.ts +12 -0
- package/src/workflow/changelog/iterateChangelogVersions.ts +70 -0
- package/src/workflow/changelog/prependChangelog.ts +59 -0
- package/src/workflow/changelog/writeChangelog.ts +27 -0
- package/src/workflow/committing/generateReleaseCommitMessage.ts +96 -0
- package/src/workflow/committing/index.ts +5 -0
- package/src/workflow/committing/isReleaseCommit.ts +52 -0
- package/src/workflow/discovery/buildDependencyGraph.ts +43 -0
- package/src/workflow/discovery/discoverPackageTag.ts +98 -0
- package/src/workflow/discovery/index.ts +9 -0
- package/src/workflow/discovery/resolvePackageStableTag.ts +88 -0
- package/src/workflow/discovery/resolvePackageTag.ts +45 -0
- package/src/workflow/discovery/sortPackages.ts +73 -0
- package/src/workflow/discovery/verifyPackageTag.ts +43 -0
- package/src/workflow/index.ts +10 -0
- package/src/workflow/report/generateReleaseReport.ts +76 -0
- package/src/workflow/report/index.ts +5 -0
- package/src/workflow/report/printReleaseReport.ts +97 -0
- package/src/workflow/runner/index.ts +4 -0
- package/src/workflow/runner/runWorkflow.ts +120 -0
- package/src/workflow/versioning/bumpVersion.ts +67 -0
- package/src/workflow/versioning/deriveReleaseType.ts +33 -0
- package/src/workflow/versioning/determineReleaseType.ts +35 -0
- package/src/workflow/versioning/index.ts +8 -0
- package/src/workflow/versioning/isStableVersion.ts +31 -0
- package/src/workflow/versioning/pickPriorityReleaseType.ts +27 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-BoVmbvH6.js","names":[],"sources":["../src/workflow/versioning/bumpVersion.ts","../src/workflow/versioning/determineReleaseType.ts","../src/workflow/versioning/pickPriorityReleaseType.ts","../src/workflow/versioning/deriveReleaseType.ts","../src/workflow/versioning/isStableVersion.ts","../src/config/const.ts","../src/config/isSemVerGitTag.ts","../src/util/buildTrailerPattern.ts","../src/util/isDefined.ts","../src/util/compact.ts","../src/util/execCommand.ts","../src/util/execGit.ts","../src/util/formatFile.ts","../src/util/interpolate.ts","../src/util/isMatch.ts","../src/util/readJson.ts","../src/util/writeJson.ts","../src/config/sanitizePrereleaseLabel.ts","../src/config/resolveReleaseTarget.ts","../src/workflow/analysis/isCommitForPackage.ts","../src/workflow/analysis/analyzePackageChanges.ts","../src/workflow/analysis/promotePrereleaseVersions.ts","../src/git/branch/getCurrentGitBranch.ts","../src/git/commit/createGitCommit.ts","../src/git/commit/extractMentions.ts","../src/git/revision/resolveGitRevision.ts","../src/git/commit/getCurrentGitCommit.ts","../src/cache/TTLCache.ts","../src/git/commit/parseCommit.ts","../src/git/commit/parseGitPrettyFormat.ts","../src/git/commit/getParsedCommit.ts","../src/git/history/countGitHistory.ts","../src/git/repository/isGitShallowRepository.ts","../src/git/repository/withShallowLock.ts","../src/git/revision/isGitAncestor.ts","../src/git/history/expandGitHistory.ts","../src/git/revision/detectGitRelationship.ts","../src/git/history/mergeGitHistory.ts","../src/git/history/restoreFullGitHistory.ts","../src/git/history/showGitHistory.ts","../src/git/history/showParsedGitHistory.ts","../src/git/repository/getGitConfigValue.ts","../src/git/repository/ensureGitUserConfig.ts","../src/git/repository/getGitRemote.ts","../src/git/repository/pushGitChanges.ts","../src/git/repository/resolveGitRepository.ts","../src/git/revision/readGitFile.ts","../src/git/tag/createGitTag.ts","../src/git/tag/deleteGitTag.ts","../src/git/tag/isGitTagPresent.ts","../src/git/tag/ensureGitTag.ts","../src/git/tag/generateGitTagName.ts","../src/git/tag/getCurrentGitTag.ts","../src/git/tag/getGitTagMessage.ts","../src/git/tag/getLatestGitTag.ts","../src/git/tag/isGitTagConnected.ts","../src/git/tag/isGitTagPointed.ts","../src/workflow/discovery/buildDependencyGraph.ts","../src/workflow/changelog/extractPlainText.ts","../src/workflow/changelog/extractSemVer.ts","../src/workflow/changelog/iterateChangelogVersions.ts","../src/workflow/changelog/extractLatestVersion.ts","../src/workflow/discovery/verifyPackageTag.ts","../src/workflow/discovery/discoverPackageTag.ts","../src/workflow/discovery/resolvePackageStableTag.ts","../src/workflow/discovery/resolvePackageTag.ts","../src/workflow/analysis/propagateDependencyUpdates.ts","../src/workflow/committing/isReleaseCommit.ts","../src/workflow/analysis/sliceUnreleased.ts","../src/workflow/analysis/streamPackageCommits.ts","../src/workflow/changelog/buildChangelog.ts","../src/workflow/changelog/buildReleaseNotes.ts","../src/workflow/changelog/extractPreviousVersion.ts","../src/workflow/changelog/prependChangelog.ts","../src/workflow/changelog/writeChangelog.ts","../package.json","../src/workflow/committing/generateReleaseCommitMessage.ts","../src/workflow/report/generateReleaseReport.ts","../src/workflow/report/printReleaseReport.ts","../src/workflow/runner/runWorkflow.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Package, ReleaseType } from '#types'\nimport { colors } from 'consola/utils'\nimport semver from 'semver'\n\n/**\n * Bump a semantic version according to release type and prerelease settings.\n *\n * Increments the version using the provided release type. When a prerelease ID\n * is provided, generates a prerelease version with that identifier. Throws an\n * error when the version is invalid or the initial version is missing.\n *\n * @param pkg Package object containing current version\n * @param releaseType Semantic version component to increment (`'major'`, `'minor'`, `'patch'`)\n * @param prereleaseId Optional prerelease identifier (e.g., `'alpha'`, `'beta'`)\n * @returns Next semantic version string, or `null` when `releaseType` is falsy\n * @throws If the version is invalid or initial version is missing\n */\nexport function bumpVersion(\n pkg: Package,\n releaseType: ReleaseType | null | undefined,\n prereleaseId: string | null | undefined\n): string | null {\n if (!releaseType) {\n return null\n }\n\n if (!pkg.version) {\n if (pkg.private) {\n return null\n }\n\n throw new Error(\n 'An initial version is required to compute the next version.'\n )\n }\n\n const parsed = semver.parse(pkg.version)\n\n if (!parsed) {\n throw new Error(\n `The version \"${colors.bold(pkg.version)}\" is not a valid SemVer.`\n )\n }\n\n prereleaseId ||= undefined\n\n let type: semver.ReleaseType = releaseType\n\n if (prereleaseId) {\n type = `pre${releaseType}` as const\n\n if (parsed.prerelease.length > 0) {\n if (\n (releaseType === 'major' && parsed.minor === 0 && parsed.patch === 0) ||\n (releaseType === 'minor' && parsed.patch === 0) ||\n releaseType === 'patch'\n ) {\n type = 'prerelease'\n }\n }\n }\n\n return semver.inc(parsed, type, { loose: undefined }, prereleaseId, '0')\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit, CommitClassifier, ReleaseType } from '#types'\n\n/**\n * Determine the release type for a single commit.\n *\n * Automatically returns `'major'` if the commit is marked as a breaking change.\n * Otherwise, routes the commit through the custom `classifier` and ensures the\n * resolved value is a valid semantic versioning release type.\n *\n * @param classifier Function mapping commits to release types\n * @param commit Parsed commit to evaluate\n * @returns `ReleaseType` string, or `null` when no matching rule applies\n */\nexport function determineReleaseType(\n classifier: CommitClassifier,\n commit: Commit\n): ReleaseType | null {\n if (commit.notes.length > 0) {\n return 'major'\n }\n\n const type = classifier(commit)\n\n switch (type) {\n case 'major':\n case 'minor':\n case 'patch':\n return type\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleaseType } from '#types'\n\n/**\n * Determine the highest-priority release type between two candidates.\n *\n * Compares release types by precedence order: `major` > `minor` > `patch`.\n * Returns the higher-precedence type, or `null` if neither applies.\n *\n * @param curr Current accumulated release type\n * @param next Candidate release type to consider\n * @returns Highest-precedence `ReleaseType`, or `null` if neither applies\n */\nexport function pickPriorityReleaseType(\n curr: ReleaseType | null | undefined,\n next: ReleaseType | null | undefined\n): ReleaseType | null {\n for (const type of ['major', 'minor', 'patch'] satisfies ReleaseType[]) {\n if (curr === type || next === type) {\n return type\n }\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit, CommitClassifier, ReleaseType } from '#types'\nimport { determineReleaseType } from './determineReleaseType'\nimport { pickPriorityReleaseType } from './pickPriorityReleaseType'\n\n/**\n * Derive the combined release type from a commit history collection.\n *\n * Processes commits sequentially, accumulating the highest-precedence release\n * type found by evaluating each individual commit and comparing it against the\n * current maximum via `pickPriorityReleaseType`.\n *\n * @param classifier Function mapping commits to release types\n * @param history Array of parsed commits to evaluate\n * @returns `ReleaseType` string, or `null` when no matching rule applies\n */\nexport function deriveReleaseType(\n classifier: CommitClassifier,\n history: readonly Commit[]\n): ReleaseType | null {\n let releaseType: ReleaseType | null = null\n\n for (const commit of history) {\n releaseType = pickPriorityReleaseType(\n releaseType,\n determineReleaseType(classifier, commit)\n )\n }\n\n return releaseType\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { colors } from 'consola/utils'\nimport semver from 'semver'\n\n/**\n * Determine whether a version string represents a stable SemVer release.\n *\n * A version is considered stable when:\n * - It is a valid SemVer string.\n * - Its major version is greater than 0 (not 0.x.x).\n * - It contains no prerelease identifiers (no `-alpha`, `-beta`, `-rc`, etc.).\n *\n * Build metadata (the `+` suffix) does not affect stability.\n *\n * @param version Semantic version string to check\n * @returns `true` if `version` is a stable SemVer; otherwise `false`\n * @throws If `version` is not a valid SemVer string\n */\nexport function isStableVersion(version: string): boolean {\n const parsed = semver.parse(version)\n\n if (!parsed) {\n throw new Error(\n `The version \"${colors.bold(version)}\" is not a valid SemVer.`\n )\n }\n\n return parsed.major > 0 && parsed.prerelease.length === 0\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleaseLifecycle } from '#types'\n\nexport const ALL_FILES = '**/*'\n\nexport const DEFAULT_CHANGELOG_FILE = 'CHANGELOG.md'\n\nexport const GIT_TRAILER_RELEASE_AGENT = 'Release-Agent'\n\n/**\n * 1. Primitive Building Blocks\n *\n * Base regular expression patterns for wildcards, alphanumeric strings with hyphens,\n * and standard non-negative integers.\n */\nconst WILDCARD = '\\\\*'\nconst ALPHANUM_DASH = '[0-9a-zA-Z-]+'\nconst NUMERIC_IDENTIFIER = '0|[1-9]\\\\d*'\n\n/**\n * 2. Package Name & Prefix Constructs\n *\n * Matches package name prefixes (unscoped or scoped) or a simple `v` prefix.\n *\n * Capture Groups:\n * - Group 1: Package name (e.g., `my-package` or `@scope/my-package`)\n */\nconst PKG_NAME_PART = `(?:${WILDCARD}|${ALPHANUM_DASH})`\nconst CAPTURED_PACKAGE_NAME = `(${PKG_NAME_PART}|@${PKG_NAME_PART}\\\\/${PKG_NAME_PART})`\nconst OPTIONAL_VERSION_PREFIX = `(?:v|${CAPTURED_PACKAGE_NAME}@)`\n\n/**\n * 3. Core SemVer Components\n *\n * Matches standard `MAJOR.MINOR.PATCH` versions or wildcard variants (e.g., `1.0.0`, `*.*.*`).\n *\n * Capture Groups:\n * - Group 3: Major version\n * - Group 4: Minor version\n * - Group 5: Patch version\n */\nconst CAPTURED_VERSION_DIGIT = `(${WILDCARD}|${NUMERIC_IDENTIFIER})`\nconst SEMVER_CORE = `${CAPTURED_VERSION_DIGIT}\\\\.${CAPTURED_VERSION_DIGIT}\\\\.${CAPTURED_VERSION_DIGIT}`\n\n/**\n * 4. Prerelease & Build Metadata\n *\n * Matches optional prerelease identifiers (e.g., `-alpha.1`) and build metadata (e.g., `+build123`).\n *\n * Capture Groups:\n * - Group 6: Complete prerelease string (without `-`)\n * - Group 7: Non-numeric prerelease token\n * - Group 8: Complete build metadata string (without `+`)\n */\nconst CAPTURED_NON_NUMERIC_PRERELEASE_ID = `(\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)`\nconst PRERELEASE_IDENTIFIER = `(?:${WILDCARD}|${NUMERIC_IDENTIFIER}|${CAPTURED_NON_NUMERIC_PRERELEASE_ID})`\nconst OPTIONAL_PRERELEASE_SECTION = `(?:-(${PRERELEASE_IDENTIFIER}(?:\\\\.${PRERELEASE_IDENTIFIER})*))`\nconst OPTIONAL_BUILD_SECTION = `(?:\\\\+(${ALPHANUM_DASH}(?:\\\\.${ALPHANUM_DASH})*))`\n\n/**\n * 5. Assembled SemVer Pattern\n *\n * Combines core SemVer components with optional prerelease identifiers and build metadata.\n *\n * Capture Groups:\n * - Group 2: Full SemVer version string (excluding package/prefix)\n */\nconst CAPTURED_SEMVER_VERSION = `(${SEMVER_CORE}${OPTIONAL_PRERELEASE_SECTION}?${OPTIONAL_BUILD_SECTION}?)`\nconst FULL_SEMVER_PATTERN = `${OPTIONAL_VERSION_PREFIX}?${CAPTURED_SEMVER_VERSION}`\n\n/**\n * Regular expression to match and capture Semantic Versioning (SemVer) strings,\n * including optional package prefixes (e.g., `@scope/pkg@`) and `v` prefixes.\n *\n * @example\n * ```ts\n * SEMVER_REGEX.test('1.0.0') // true\n * SEMVER_REGEX.test('v1.2.3-beta.1+sha.1234') // true\n * SEMVER_REGEX.test('@scope/package@1.0.0') // true\n * ```\n *\n * @see {@link https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string SemVer Specification}\n */\nexport const SEMVER_REGEX = new RegExp(FULL_SEMVER_PATTERN)\n\nexport const RELEASE_LIFECYCLE_HOOK_LIST = [\n 'configure',\n 'discover',\n 'analyze',\n 'build',\n 'write',\n 'commit',\n 'publish',\n 'finalize',\n] as const satisfies ReleaseLifecycle[]\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { SEMVER_REGEX } from './const'\n\n/**\n * Determine whether a tag string is a valid SemVer-style Git tag.\n *\n * Anchors `SEMVER_REGEX.source` for full-string matching and applies the\n * repository's Git-tag acceptance rules.\n *\n * @param tag Tag string to validate\n * @returns `true` if `tag` is accepted as a SemVer-style Git tag; otherwise `false`\n */\nexport function isSemVerGitTag(tag: string): boolean {\n const match = new RegExp(`^${SEMVER_REGEX.source}$`).exec(tag)\n\n if (!match) return false\n\n const isScopedPackage = Boolean(match[1])\n\n const hasVersionPrefix = tag.startsWith('v')\n\n return isScopedPackage || hasVersionPrefix\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\n/**\n * Build a regular expression to match trailer lines using custom markers.\n *\n * Combines the provided marker strings with a standard alphanumeric pattern\n * to construct a regular expression that matches trailer definitions.\n *\n * @param markers Optional array of custom trailer marker strings\n * @returns A regular expression matching trailer lines\n */\nexport function buildTrailerPattern(markers: readonly string[] = []) {\n return new RegExp(\n `^(${markers\n .map(RegExp.escape)\n .concat('[A-Za-z0-9_-]+')\n .join('|')})[ \\\\t]*:[ \\\\t]*(.*)$`\n )\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\n/**\n * Type guard that asserts a value is neither `null` nor `undefined`.\n *\n * @param value Value to test\n * @returns `true` when `value` is not `null` or `undefined`\n */\nexport function isDefined<T>(value: T): value is NonNullable<T> {\n return value != null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { isDefined } from './isDefined'\n\n/**\n * Remove `null` and `undefined` values from an array.\n *\n * @param array The array to compact\n * @returns A new array that does not contain `null` or `undefined` values\n */\nexport function compact<T>(array: readonly (T | null | undefined)[]): T[] {\n return array.filter(isDefined)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'\n\nexport interface PromiseWithChild<T> extends Promise<T> {\n child: ChildProcessWithoutNullStreams\n}\n\nlet now = new Date('2026-01-01T00:00:00Z').valueOf()\n\nconst MUTATING_GIT_COMMANDS = new Set([\n 'commit',\n 'tag',\n 'cherry-pick',\n 'rebase',\n 'merge',\n 'revert',\n 'commit-tree',\n 'mktag',\n])\n\n/**\n * Execute a command asynchronously and return its standard output.\n *\n * @param cwd Working directory for the command\n * @param command Binary to execute\n * @param args Command-line arguments\n * @returns Promise resolving to the stdout string\n */\nexport function execCommand(\n cwd: string,\n command: string,\n args: readonly string[]\n): PromiseWithChild<string> {\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n }\n\n if (import.meta.env.MODE === 'test' && command === 'git') {\n const primaryArg = args.find((arg) => !arg.startsWith('-'))\n\n if (primaryArg && MUTATING_GIT_COMMANDS.has(primaryArg)) {\n const date = new Date(now).toISOString()\n env.GIT_AUTHOR_DATE = date\n env.GIT_COMMITTER_DATE = date\n now += 1000\n }\n }\n\n let childProcess!: ChildProcessWithoutNullStreams\n\n const promise = new Promise<string>((resolve, reject) => {\n childProcess = spawn(command, args, {\n cwd,\n env,\n })\n\n childProcess.stdout.setEncoding('utf-8')\n childProcess.stderr.setEncoding('utf-8')\n\n let stdout = ''\n let stderr = ''\n\n childProcess.stdout.on('data', (data: string) => {\n stdout += data\n })\n\n childProcess.stderr.on('data', (data: string) => {\n stderr += data\n })\n\n childProcess.on('error', (err) => {\n reject(err)\n })\n\n childProcess.on('close', (code, signal) => {\n if (code === 0) {\n return resolve(stdout)\n }\n\n const quotedCommand = quoteCommandForMessage([command, ...args].join(' '))\n\n /* istanbul ignore next */\n const exitStatus =\n code !== null\n ? `exit code ${code}`\n : signal\n ? `signal ${signal}`\n : 'unknown status'\n\n const errorMessage = `Command ${quotedCommand} failed (${exitStatus})`\n\n const errorDiagnostics = (stderr || stdout).trim()\n\n const err = new Error(\n [errorMessage, errorDiagnostics]\n .filter(Boolean)\n .join(errorDiagnostics.includes('\\n') ? '\\n' : ': '),\n {\n cause: {\n code,\n signal,\n diagnostics: errorDiagnostics,\n },\n }\n )\n\n Object.defineProperty(err, 'code', { value: code })\n\n reject(err)\n })\n }) as PromiseWithChild<string>\n\n promise.child = childProcess\n\n return promise\n}\n\nfunction quoteCommandForMessage(command: string): string {\n if (!command.includes(\"'\")) {\n return `'${command}'`\n }\n\n if (!command.includes('\"')) {\n return `\"${command}\"`\n }\n\n return JSON.stringify(command)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execCommand, type PromiseWithChild } from './execCommand'\n\n/**\n * Run a git command in a working directory and return its standard output.\n *\n * @param cwd Working directory for the git command\n * @param args Git CLI arguments\n * @returns Promise resolving to the stdout string\n */\nexport function execGit(\n cwd: string,\n args: readonly string[]\n): PromiseWithChild<string> {\n return execCommand(cwd, 'git', args)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport prettier from 'prettier'\n\n/**\n * Format `content` for `filePath` using Prettier when possible.\n *\n * Resolves project/editorconfig settings and formats the provided content.\n * Returns the original content on error or when no parser is inferred.\n *\n * @param filePath Path used to infer parser and config\n * @param content Source content to format\n * @returns Formatted content or original content on failure\n */\nexport async function formatFile(\n filePath: string,\n content: string\n): Promise<string> {\n try {\n const info = await prettier.getFileInfo(filePath, {\n resolveConfig: true,\n })\n\n const config = await prettier.resolveConfig(filePath, {\n editorconfig: true,\n })\n\n if (info.inferredParser == null) {\n return content\n }\n\n return await prettier.format(content, {\n ...config,\n parser: info.inferredParser,\n filepath: filePath,\n })\n } catch {\n return content\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nconst BACKSLASH = '\\\\'\nconst LEFT_BRACE = '{'\nconst RIGHT_BRACE = '}'\nconst DOT = '.'\nconst ESCAPE_CHARS = [BACKSLASH, LEFT_BRACE, RIGHT_BRACE]\nconst BANNED_KEYS = ['__proto__', 'constructor', 'prototype']\n\n/**\n * Interpolate placeholders in `template` using values from `data`.\n *\n * - Placeholders use `{path.to.value}` (dot notation).\n * - Backslash escapes are supported for `{`, `}`, `\\`, and `.` inside tokens.\n * - Tokens that reference banned keys (`__proto__`, `constructor`, `prototype`)\n * are left unexpanded to avoid prototype pollution.\n * - Only own properties are followed when resolving paths; if any step is\n * missing, the original token text is preserved.\n * - Resolved non-object, non-null values are stringified and inserted.\n * - Unclosed tokens and a trailing backslash are preserved verbatim.\n *\n * @param template Template string containing placeholders like `{user.name}`\n * @param data Object used to resolve placeholder paths\n * @returns The template with placeholders replaced by corresponding values\n */\nexport function interpolate(\n template: string,\n data: Record<string, unknown>\n): string {\n let result = ''\n let i = 0\n let char: string | undefined\n let nextChar: string | undefined\n\n while ((char = template[i])) {\n // Escape handling: if a backslash precedes an escapable char, emit the\n // escaped character; otherwise emit the backslash itself.\n if (char === BACKSLASH) {\n nextChar = template[i + 1]\n\n if (nextChar !== undefined && ESCAPE_CHARS.includes(nextChar)) {\n result += nextChar\n i += 2\n } else {\n result += char\n i += 1\n }\n }\n\n // Placeholder handling: parse a token starting at '{' until a matching '}'.\n else if (char === LEFT_BRACE) {\n const keys: string[] = []\n let key = ''\n let token = ''\n let closed = false\n i += 1\n\n // Parse token body, supporting escaped characters and escaped dots.\n while ((char = template[i])) {\n if (char === RIGHT_BRACE) {\n closed = true\n keys.push(key)\n i += 1\n break\n }\n\n if (char === BACKSLASH) {\n nextChar = template[i + 1]\n\n // Allow escaping of brace, backslash, and dot inside tokens.\n if (\n nextChar !== undefined &&\n [...ESCAPE_CHARS, DOT].includes(nextChar)\n ) {\n key += nextChar\n token += nextChar\n i += 2\n } else {\n // Treat an isolated backslash as a literal within the token.\n key += char\n token += char\n i += 1\n }\n } else if (char === DOT) {\n // Dot separates path segments; record the current segment.\n keys.push(key)\n key = ''\n token += char\n i += 1\n } else {\n key += char\n token += char\n i += 1\n }\n }\n\n // If token closed with '}', attempt to resolve the path against `data`.\n if (closed) {\n let value: unknown = data\n\n for (const key of keys) {\n const isSafeKey = !BANNED_KEYS.includes(key)\n\n // Only follow own properties on objects and skip banned keys.\n if (\n value !== null &&\n typeof value === 'object' &&\n Object.hasOwn(value, key) &&\n isSafeKey\n ) {\n value = (value as Record<string, unknown>)[key]\n } else {\n value = undefined\n break\n }\n }\n\n // If resolution failed or produced an object/null, preserve the token.\n if (value == null || typeof value === 'object') {\n result += LEFT_BRACE + token + RIGHT_BRACE\n } else {\n result += String(value)\n }\n }\n\n // Unclosed token: preserve the raw token text (including the opening brace).\n else {\n result += LEFT_BRACE + token\n }\n }\n\n // Ordinary characters are appended directly.\n else {\n result += char\n i += 1\n }\n }\n\n return result\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { minimatch } from 'minimatch'\n\n/**\n * Determine whether a target string matches glob patterns.\n *\n * Matches positive and negative glob patterns using dotfile support. Automatically\n * expands a single negative pattern by prepending a global wildcard match.\n *\n * @param target Target string to evaluate against glob patterns\n * @param patterns Single glob pattern or list of patterns to evaluate\n * @returns True if target matches positive patterns and satisfies negative patterns\n */\nexport function isMatch(\n target: string,\n patterns: string | readonly string[]\n): boolean {\n if (typeof patterns === 'string') {\n if (patterns.startsWith('!')) {\n patterns = ['**/*', patterns]\n } else {\n patterns = [patterns]\n }\n }\n\n const positives: string[] = []\n const negatives: string[] = []\n\n for (const pattern of patterns) {\n if (pattern.startsWith('!')) {\n negatives.push(pattern)\n } else {\n positives.push(pattern)\n }\n }\n\n return (\n positives.some((pattern) => minimatch(target, pattern, { dot: true })) &&\n negatives.every((pattern) => minimatch(target, pattern, { dot: true }))\n )\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { colors } from 'consola/utils'\nimport fs from 'node:fs/promises'\n\n/**\n * Read and parse a JSON file.\n *\n * Throws a descriptive error when the file cannot be read or parsed, or when\n * the parsed value is not an object.\n *\n * @param filePath Path to the JSON file\n * @returns Parsed JSON object\n * @throws If the file cannot be read, parsed, or is not an object\n */\nexport async function readJson<T>(filePath: string): Promise<T> {\n let content: string\n\n try {\n content = await fs.readFile(filePath, 'utf-8')\n } catch (err) {\n throw new Error(\n `JSON file at \"${colors.bold(filePath)}\" could not be read.`,\n { cause: err }\n )\n }\n\n let json: T\n\n try {\n json = JSON.parse(content) as T\n } catch (err) {\n throw new Error(\n `JSON file at \"${colors.bold(filePath)}\" could not be parsed.`,\n { cause: err }\n )\n }\n\n if (typeof json !== 'object' || json === null) {\n throw new Error(\n `JSON file at \"${colors.bold(filePath)}\" does not contain a valid object.`\n )\n }\n\n return json\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { formatFile } from './formatFile'\n\n/**\n * Write a JSON `payload` to `filePath`, formatting the content first.\n *\n * Ensures the directory exists and writes the formatted JSON.\n *\n * @param filePath Path to write the JSON file\n * @param payload JSON-serializable object to write\n * @returns Promise that resolves when the file is written\n */\nexport async function writeJson(\n filePath: string,\n payload: object\n): Promise<void> {\n let content = JSON.stringify(payload, null, 2)\n\n content = await formatFile(filePath, content)\n\n await fs.mkdir(path.dirname(filePath), { recursive: true })\n\n await fs.writeFile(filePath, content)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\n/**\n * Sanitize a string into a safe, semver-compliant prerelease label.\n *\n * Replaces sequential non-alphanumeric characters or consecutive hyphens\n * with a single hyphen, and strips leading or trailing hyphens.\n *\n * @param label Raw string to sanitize\n * @returns Safe string for a prerelease label\n */\nexport function sanitizePrereleaseLabel(label: string): string {\n return label\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleaseContext, ReleaseRule } from '#types'\nimport { interpolate, isMatch } from '#util'\nimport { sanitizePrereleaseLabel } from './sanitizePrereleaseLabel'\n\nexport type ResolveReleaseTargetOptions = Record<\n Extract<keyof ReleaseRule, 'prereleaseLabel' | 'releaseChannel'>,\n string | boolean | undefined\n>\n\n/**\n * Resolve prerelease label and release channel for a ref.\n *\n * Resolution priority for each key:\n * 1. Explicit `options` (string values are used verbatim; `false` disables the key).\n * 2. First matching `rule` (rules are checked in order).\n * 3. Fallback: derive and sanitize a label from `ref.name`.\n *\n * Resolved string values are interpolated with `data` before being returned.\n *\n * @param ref Reference context containing `type` and `name`\n * @param rules Ordered list of release rules to match against\n * @param options User overrides for `prereleaseLabel` and `releaseChannel`\n * @param data Values used to interpolate resolved template strings\n * @returns Partial release rule with resolved `prereleaseLabel` and/or `releaseChannel`\n */\nexport function resolveReleaseTarget(\n ref: ReleaseContext['ref'],\n rules: readonly ReleaseRule[],\n options: ResolveReleaseTargetOptions,\n data: Record<string, unknown>\n): Partial<ReleaseRule> {\n const matchedRule = rules.find(\n (rule) => rule.type === ref.type && isMatch(ref.name, rule.pattern)\n )\n\n const resolve = (key: keyof ResolveReleaseTargetOptions) => {\n const value1 = options[key]\n\n if (value1 === false) {\n return undefined\n }\n\n if (value1 && typeof value1 === 'string') {\n return value1\n }\n\n const value2 = matchedRule?.[key] || value1\n\n if (!value2) {\n return undefined\n }\n\n if (typeof value2 === 'string') {\n return value2\n }\n\n return sanitizePrereleaseLabel(ref.name)\n }\n\n const prereleaseLabel = resolve('prereleaseLabel')\n\n const releaseChannel = resolve('releaseChannel')\n\n return {\n prereleaseLabel: prereleaseLabel && interpolate(prereleaseLabel, data),\n releaseChannel: releaseChannel && interpolate(releaseChannel, data),\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { ALL_FILES } from '#config'\nimport type { Commit, Package } from '#types'\nimport { isMatch } from '#util'\nimport path from 'node:path'\n\n/**\n * Test whether a commit modifies files in a package.\n *\n * @param pkg Package with an explicit file pattern or base directory\n * @param commit Parsed commit containing changed file paths\n * @returns `true` if any changed file matches the package pattern\n */\nexport function isCommitForPackage(pkg: Package, commit: Commit): boolean {\n return commit.files.some((file) => {\n const { dir } = pkg\n const files = pkg.files || path.join(dir, ALL_FILES)\n\n return isMatch(file, files)\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit, CommitClassifier, ReleasePlan } from '#types'\nimport { deriveReleaseType, pickPriorityReleaseType } from '../versioning'\nimport { isCommitForPackage } from './isCommitForPackage'\n\n/**\n * Analyze commit history and compute release types for each package plan.\n *\n * Iterates through commits, associates them with affected packages, and\n * calculates each package's release type using the provided classifier.\n *\n * @param classifier Function mapping commits to release types\n * @param plans Array of release plans to update\n * @param history Commit history as iterable or async iterable\n * @returns Updated array of plans with computed `releaseType`\n */\nexport async function analyzePackageChanges(\n classifier: CommitClassifier,\n plans: readonly ReleasePlan[],\n history: Iterable<Commit> | AsyncIterable<Commit>\n): Promise<ReleasePlan[]> {\n for await (const commit of history) {\n plans = plans.map((plan): ReleasePlan => {\n if (!isCommitForPackage(plan.package, commit)) {\n return plan\n }\n\n return {\n ...plan,\n history: plan.history.concat(commit),\n }\n })\n }\n\n return plans.map((plan): ReleasePlan => {\n let { release } = plan\n\n const releaseType = pickPriorityReleaseType(\n release?.type,\n deriveReleaseType(classifier, plan.history)\n )\n\n if (releaseType) {\n release = { ...release, type: releaseType }\n }\n\n return {\n ...plan,\n release,\n }\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleasePlan, ReleaseType } from '#types'\nimport semver from 'semver'\n\n/**\n * Promote prerelease versions when they have no explicit release type.\n *\n * Analyzes prerelease and zero-major versions to determine an appropriate\n * release type for graduation:\n * - Prerelease versions are promoted based on their highest incremented component\n * (patch, minor, or major)\n * - Zero-major versions (0.y.z) are promoted to major\n * - Stable versions (1.0.0+) remain unchanged\n * - Plans with explicit releases are preserved as-is\n *\n * @param plans Release plans to analyze\n * @returns Plans with inferred release types for prerelease versions\n */\nexport async function promotePrereleaseVersions(\n plans: readonly ReleasePlan[]\n): Promise<ReleasePlan[]> {\n return plans.map((plan): ReleasePlan => {\n if (plan.release) return plan\n\n if (!plan.package.tag) return plan\n\n const parsed = semver.parse(plan.package.version)\n\n if (!parsed) return plan\n\n let type: ReleaseType | undefined\n\n if (parsed.prerelease.length) {\n if (parsed.patch !== 0) type = 'patch'\n else if (parsed.minor !== 0) type = 'minor'\n else if (parsed.major !== 0) type = 'major'\n else type = 'major'\n } else if (parsed.major === 0) {\n type = 'major'\n }\n\n if (type) {\n return { ...plan, release: { type } }\n }\n\n return plan\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get the current Git branch name.\n *\n * Uses `execGit` to run `git symbolic-ref -q --short HEAD` in the provided\n * working directory and trims the result. Returns an empty string when the\n * repository is in a detached HEAD state.\n *\n * @param cwd The working directory to run the Git command in\n * @returns The current branch name as a string\n */\nexport async function getCurrentGitBranch(cwd: string): Promise<string> {\n return (await execGit(cwd, ['symbolic-ref', '-q', '--short', 'HEAD'])).trim()\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Stage all changes and create a git commit with the specified message.\n *\n * Validates the commit message and working directory status before staging\n * all changes and executing the commit command.\n *\n * @param cwd The target directory path for executing git commands\n * @param message The message to associate with the commit\n * @returns A promise that resolves when the operation completes\n */\nexport async function createGitCommit(\n cwd: string,\n message: string | null | undefined\n): Promise<void> {\n if (!message) return\n\n await execGit(cwd, ['add', '.'])\n\n const stdout = (await execGit(cwd, ['status', '--porcelain'])).trim()\n\n if (!stdout) return\n\n await execGit(cwd, ['commit', '--message', message])\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nconst ALPHANUMERIC = '[a-zA-Z0-9]'\nconst SEPARATOR = '[-_]'\nconst USERNAME = `${ALPHANUMERIC}+(?:${SEPARATOR}+${ALPHANUMERIC}+)*`\nconst EMAIL_LOCAL = '[a-zA-Z0-9._%+-]'\nconst DOMAIN_SUFFIX = '[a-zA-Z0-9.-]*\\\\.[a-zA-Z]{2,}'\nconst SCOPED_PACKAGE_SPEC_SUFFIX = `/${USERNAME}@[0-9]+\\\\.[0-9]+\\\\.[0-9]+`\n\n/**\n * Extract unique mention handles from the input text string.\n *\n * Evaluates `text` against boundary rules to isolate valid handles while\n * excluding email addresses, domain names, and scoped package specs, then\n * de-duplicates the resulting handles.\n *\n * @param text The input string to parse for mentions\n * @returns An array of unique mention handles\n */\nexport function extractMentions(text: string): string[] {\n if (!text) return []\n\n const regex = new RegExp(\n `(?<!${EMAIL_LOCAL})@(${USERNAME})\\\\b(?!${SEPARATOR}|${DOMAIN_SUFFIX}|${SCOPED_PACKAGE_SPEC_SUFFIX})`,\n 'g'\n )\n\n const matches = text.matchAll(regex)\n\n const mentions = new Set(matches.map((match) => match[1] as string))\n\n return Array.from(mentions)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Resolve a Git revision or commit-ish expression to its full commit hash.\n *\n * Runs `git rev-parse --verify <revision>^{commit}` under the hood. Peels annotated\n * tags, branches, short SHAs, and relative expressions down to a verified commit object.\n *\n * @param cwd The repository working directory\n * @param revision The Git revision or expression to resolve\n * @returns A promise that resolves to the full, trimmed commit SHA-1 string\n */\nexport async function resolveGitRevision(\n cwd: string,\n revision: string\n): Promise<string> {\n return (\n await execGit(cwd, ['rev-parse', '--verify', `${revision}^{commit}`])\n ).trim()\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { resolveGitRevision } from '../revision/resolveGitRevision'\n\n/**\n * Resolve the current commit hash for HEAD in the repository.\n *\n * @param cwd The repository working directory\n * @returns A promise that resolves to the full, trimmed commit SHA-1 string\n */\nexport function getCurrentGitCommit(cwd: string): Promise<string> {\n return resolveGitRevision(cwd, 'HEAD')\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\ninterface Entry<V> {\n value: V\n timer: NodeJS.Timeout\n ttl: number\n}\n\n/**\n * A simple time-to-live cache that automatically evicts entries after a TTL.\n *\n * - Entries are removed automatically when their TTL expires.\n * - Reading an entry resets its TTL to the original value.\n * - Setting an existing key clears its previous timer and resets the TTL.\n *\n * @template K Type of cache keys\n * @template V Type of cache values\n */\nexport class TTLCache<K, V> {\n private cache = new Map<K, Entry<V>>()\n\n /**\n * Store a value under `key` for `ttl` milliseconds.\n *\n * If the key already exists its previous timer is cleared and the TTL is reset.\n *\n * @param key Cache key\n * @param value Value to store\n * @param ttl Time to live in milliseconds (default: 60_000)\n */\n set(key: K, value: V, ttl: number = 60_000): void {\n const ref = this.cache.get(key)\n\n if (ref) {\n clearTimeout(ref.timer)\n }\n\n const timer = setTimeout(() => {\n this.cache.delete(key)\n }, ttl).unref()\n\n this.cache.set(key, { value, timer, ttl })\n }\n\n /**\n * Retrieve the value for `key`, or `undefined` if not present.\n *\n * Reading an entry resets its TTL to the original `ttl` provided when it was set.\n *\n * @param key Cache key\n * @returns The stored value or `undefined` if not present\n */\n get(key: K): V | undefined {\n const ref = this.cache.get(key)\n\n if (!ref) return undefined\n\n this.set(key, ref.value, ref.ttl)\n\n return ref.value\n }\n\n /**\n * Check whether `key` exists in the cache.\n *\n * @param key Cache key\n * @returns `true` if present; otherwise `false`\n */\n has(key: K): boolean {\n return this.cache.has(key)\n }\n\n /**\n * Remove `key` from the cache and clear its eviction timer.\n *\n * @param key Cache key\n * @returns `true` if the key was present and removed; otherwise `false`\n */\n delete(key: K): boolean {\n const ref = this.cache.get(key)\n\n if (!ref) return false\n\n clearTimeout(ref.timer)\n\n return this.cache.delete(key)\n }\n\n /**\n * Clear all entries and their timers.\n */\n clear(): void {\n for (const { timer } of this.cache.values()) {\n clearTimeout(timer)\n }\n\n this.cache.clear()\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit, CommitNote, CommitTrailer, ParsedCommit } from '#types'\nimport { CommitParser } from 'conventional-commits-parser'\nimport { buildTrailerPattern } from '../../util/buildTrailerPattern'\nimport { extractMentions } from './extractMentions'\n\nexport interface GitLogEntry {\n hash: string\n parent: string[]\n authorDate: string\n committerDate: string\n subject: string\n body: string\n files: string[]\n}\n\nconst BREAKING_HEADER_REGEX = /^([\\w-]+)(?:\\(([^)]+)\\))?!: (.*)$/\n\nconst BREAKING_MARKERS: readonly string[] = [\n 'BREAKING CHANGE',\n 'BREAKING-CHANGE',\n]\n\n/**\n * Parse a git log entry into a structured commit object.\n *\n * Combines the commit subject and body, extracts conventional commit metadata\n * using custom trailer markers and breaking header patterns, and normalizes\n * dates to ISO format strings.\n *\n * @param entry The raw git log entry to parse\n * @param markers Optional array of custom trailer marker strings\n * @returns A structured commit object containing parsed details, notes, and mentions\n */\nexport async function parseCommit(\n entry: GitLogEntry,\n markers: readonly string[] = []\n): Promise<Commit> {\n const message = [entry.subject, entry.body].filter(Boolean).join('\\n\\n')\n\n const parser = new CommitParser({\n breakingHeaderPattern: BREAKING_HEADER_REGEX,\n noteKeywords: [],\n notesPattern: () => buildTrailerPattern(BREAKING_MARKERS.concat(markers)),\n })\n\n const commit = parser.parse(message) as object as ParsedCommit\n\n const notes: CommitNote[] = []\n\n const trailers: CommitTrailer[] = []\n\n for (const note of commit.notes) {\n if (BREAKING_MARKERS.includes(note.title.toUpperCase())) {\n notes.push(note)\n }\n\n trailers.push({\n key: note.title,\n value: note.text,\n })\n }\n\n if (notes.length === 0 && BREAKING_HEADER_REGEX.test(commit.header)) {\n const note: CommitNote = {\n title: 'BREAKING CHANGE',\n text: commit.subject,\n }\n\n notes.push(note)\n\n trailers.push({\n key: note.title,\n value: note.text,\n })\n }\n\n const mentions = extractMentions(message)\n\n return {\n ...commit,\n hash: entry.hash,\n parent: entry.parent,\n authorDate: new Date(entry.authorDate).toISOString(),\n committerDate: new Date(entry.committerDate).toISOString(),\n type: commit.type?.toLowerCase() || null,\n scope: commit.scope || null,\n subject: commit.subject || null,\n header: commit.header,\n body: commit.body,\n footer: commit.footer,\n notes,\n mentions,\n trailers,\n files: entry.files,\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { colors } from 'consola/utils'\nimport type { GitLogEntry } from './parseCommit'\n\n/**\n * Field separator used in the git pretty format to produce a single-line\n *, machine-parseable record.\n */\nexport const GIT_PRETTY_FIELD_SEPARATOR = '\\x1f'\n\n/**\n * Pretty format string passed to `git log --pretty=format:`. Produces seven\n * fields separated by `GIT_PRETTY_FIELD_SEPARATOR`:\n * hash, parents, author date, committer date, subject, body, files.\n */\nexport const GIT_PRETTY_FORMAT = [\n '%H',\n '%P',\n '%aI',\n '%cI',\n '%s',\n '%b',\n '',\n].join(GIT_PRETTY_FIELD_SEPARATOR)\n\n/**\n * Parse a single line produced by `git log --pretty=format:${GIT_PRETTY_FORMAT}`.\n *\n * The input is expected to contain seven fields separated by\n * `GIT_PRETTY_FIELD_SEPARATOR`:\n * 1. `hash` (commit hash)\n * 2. `parent` (space-separated parent hashes)\n * 3. `authorDate` (ISO 8601)\n * 4. `committerDate` (ISO 8601)\n * 5. `subject`\n * 6. `body`\n * 7. `files` (newline-separated list)\n *\n * The function trims fields, converts parent hashes into an array, normalizes\n * dates to ISO strings, and splits `files` into an array. Missing optional\n * fields are normalized to sensible defaults.\n *\n * @param raw Raw pretty-format string produced by git\n * @returns Parsed `GitLogEntry` with `hash`, `parent`, `authorDate`, `committerDate`, `subject`, `body`, and `files`\n * @throws If required fields (hash, authorDate, committerDate, subject) are missing or malformed\n */\nexport function parseGitPrettyFormat(raw: string): GitLogEntry {\n let [hash, parentHashes, authorDate, committerDate, subject, body, files] =\n raw.split(GIT_PRETTY_FIELD_SEPARATOR)\n\n hash = hash?.trim()\n parentHashes = parentHashes?.trim()\n authorDate = authorDate?.trim()\n committerDate = committerDate?.trim()\n subject = subject?.trim()\n body = body?.trim()\n files = files?.trim()\n\n if (!hash || !authorDate || !committerDate || typeof subject !== 'string') {\n throw new Error(\n `The input \"${colors.bold(raw)}\" is not in the expected pretty format.`\n )\n }\n\n return {\n hash,\n parent: parentHashes ? parentHashes.split(/\\s+/) : [],\n authorDate: new Date(authorDate).toISOString(),\n committerDate: new Date(committerDate).toISOString(),\n subject,\n body: body ?? '',\n files: files ? files.split('\\n') : [],\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { TTLCache } from '#cache'\nimport type { Commit } from '#types'\nimport { execGit } from '#util'\nimport { parseCommit } from './parseCommit'\nimport { GIT_PRETTY_FORMAT, parseGitPrettyFormat } from './parseGitPrettyFormat'\n\nconst cache = new TTLCache<string, Commit>()\n\n/**\n * Retrieve and parse a commit by its reference.\n *\n * Fetches the raw git log entry for the specified reference, checks the cache\n * for an existing entry, and parses the log data if a cache miss occurs.\n *\n * @param cwd The current working directory path\n * @param ref The git reference to fetch\n * @param markers Optional array of custom trailer marker strings\n * @returns A structured commit object or null if the operation fails\n */\nexport async function getParsedCommit(\n cwd: string,\n ref: string,\n markers: readonly string[] = []\n): Promise<Commit | null> {\n try {\n const stdout = await execGit(cwd, [\n 'log',\n '-1',\n ref,\n '--name-only',\n `--pretty=format:${GIT_PRETTY_FORMAT}`,\n ])\n\n const entry = parseGitPrettyFormat(stdout)\n\n const key = `${entry.hash}:${entry.parent}:${markers}`\n\n const cached = cache.get(key)\n\n if (cached) return cached\n\n const parsed = await parseCommit(entry, markers)\n\n cache.set(key, parsed)\n\n return parsed\n } catch {\n return null\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Count mainline commits reachable from a given reference.\n *\n * Uses `git rev-list --count --first-parent` to calculate the length of the\n * primary history trunk, excluding commits introduced by side-branch merges.\n *\n * @param cwd Repository working directory\n * @param ref Commit reference to count from (defaults to `HEAD`)\n * @returns Number of commits along the primary branch line up to `ref`\n */\nexport async function countGitHistory(\n cwd: string,\n ref: string = 'HEAD'\n): Promise<number> {\n const stdout = await execGit(cwd, [\n 'rev-list',\n '--count',\n '--first-parent',\n ref,\n ])\n\n return Number.parseInt(stdout.trim(), 10)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Determine whether the repository at `cwd` is a shallow clone.\n *\n * Runs `git rev-parse --is-shallow-repository` to detect shallow clones.\n * Useful before operations that require full history.\n *\n * @param cwd Repository working directory\n * @returns `true` if shallow, `false` if full clone, or `null` on error\n */\nexport async function isGitShallowRepository(cwd: string): Promise<boolean> {\n const stdout = (\n await execGit(cwd, ['rev-parse', '--is-shallow-repository'])\n ).trim()\n\n return stdout === 'true'\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { isGitShallowRepository } from './isGitShallowRepository'\n\nconst lockQueue = new Map<string, Promise<void>>()\n\nconst context = new AsyncLocalStorage<Set<string>>()\n\n/**\n * Serialize access to a shallow Git repository while running a callback.\n *\n * If `cwd` is a shallow clone, callers from different async contexts are\n * executed one at a time; nested calls within the same async context run\n * immediately without waiting. If the repository becomes non-shallow before\n * a queued turn runs, downstream waiters are released early.\n *\n * @param cwd Repository working directory path string\n * @param callback Function to execute while the lock is held\n * @returns Promise resolving to the result of `callback`\n */\nexport async function withShallowLock<T>(\n cwd: string,\n callback: () => T | Promise<T>\n): Promise<T> {\n if (!(await isGitShallowRepository(cwd))) {\n return callback()\n }\n\n const active = context.getStore()\n\n if (active?.has(cwd)) {\n return callback()\n }\n\n const { promise, resolve } = Promise.withResolvers<void>()\n const previous = lockQueue.get(cwd) ?? Promise.resolve()\n const queued = previous.then(() => promise)\n\n lockQueue.set(cwd, queued)\n\n // Wait until previous tasks complete.\n await previous\n\n // If an earlier task made the repo non-shallow, release downstream waiters now.\n if (!(await isGitShallowRepository(cwd))) {\n resolve()\n }\n\n try {\n return await context.run(new Set(active).add(cwd), callback)\n } finally {\n // Ensure downstream waiters are unblocked even if callback throws.\n resolve()\n\n // Remove the queue entry only if no newer task has replaced it.\n if (lockQueue.get(cwd) === queued) {\n lockQueue.delete(cwd)\n }\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Determine whether `ancestor` is an ancestor of `descendant`.\n *\n * Runs `git merge-base --is-ancestor <ancestor> <descendant>` in the repository at `cwd`.\n * Resolves to `true` if `ancestor` is an ancestor, `false` if not.\n * Rethrows errors for invalid refs or other failures.\n *\n * @param cwd Repository working directory\n * @param ancestor Candidate ancestor ref\n * @param descendant Candidate descendant ref\n * @returns Resolves to `true` if `ancestor` is an ancestor of `descendant`, `false` otherwise\n * @throws If a ref is invalid or the Git command fails\n */\nexport function isGitAncestor(\n cwd: string,\n ancestor: string,\n descendant: string\n): Promise<boolean> {\n return execGit(cwd, ['merge-base', '--is-ancestor', ancestor, descendant])\n .then(() => true)\n .catch((err) => {\n if ((err as { code?: number })?.code === 1) {\n return false\n }\n\n throw err\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\nimport { isGitShallowRepository } from '../repository/isGitShallowRepository'\nimport { withShallowLock } from '../repository/withShallowLock'\nimport { isGitAncestor } from '../revision/isGitAncestor'\nimport { resolveGitRevision } from '../revision/resolveGitRevision'\nimport { countGitHistory } from './countGitHistory'\n\nexport interface ExpandGitHistoryOptions {\n /**\n * Number of commits to deepen the history by when paging\n * @default 50\n */\n pageSize?: number | undefined\n\n /**\n * Local reference used to measure current shallow depth (for example HEAD)\n * When provided the function accounts for existing depth before fetching more\n */\n from?: string | null | undefined\n\n /**\n * Target boundary reference where history deepening should stop\n * If provided the function will first attempt a single fetch to reach this commit\n */\n until?: string | null | undefined\n}\n\n/**\n * Deepen a shallow clone so additional commits become available.\n *\n * Attempts a single `--shallow-since` fetch to reach `until` (if provided),\n * otherwise deepens by `pageSize`, accounting for existing depth when `from`\n * is supplied. Operation is serialized to avoid concurrent deepens.\n *\n * @param cwd Repository working directory\n * @param options Options: { pageSize?, from?, until? }\n * @returns true if history was deepened or already sufficient, otherwise false\n */\nexport async function expandGitHistory(\n cwd: string,\n options: ExpandGitHistoryOptions = {}\n): Promise<boolean> {\n if (!(await isGitShallowRepository(cwd))) {\n return false\n }\n\n return withShallowLock(cwd, async () => {\n const { until } = options\n let { from } = options\n let { pageSize = 50 } = options\n\n // If an `until` boundary is provided, try a single fetch using the target commit's date\n if (until) {\n if (await isGitAncestor(cwd, until, 'HEAD')) {\n return true\n }\n\n const commitDate = (\n await execGit(cwd, ['show', '-s', '--format=%cI', `${until}^{commit}`])\n ).trim()\n\n try {\n await execGit(cwd, [\n 'fetch',\n `--shallow-since=${commitDate}`,\n '--update-shallow',\n '--filter=blob:none',\n ])\n\n return true\n } catch {}\n }\n\n // Incremental deepening: account for existing local depth when `from` is set\n if (from) {\n from = await resolveGitRevision(cwd, from)\n\n const depth = await countGitHistory(cwd, from)\n\n if (depth > pageSize) {\n return true\n }\n\n // Reduce requested deepen amount by existing depth (exclude the `from` commit)\n pageSize -= depth - 1\n }\n\n // Perform a deepen fetch; if `from` is not an ancestor of HEAD, fetch that ref\n try {\n await execGit(cwd, [\n 'fetch',\n `--deepen=${pageSize}`,\n '--update-shallow',\n '--filter=blob:none',\n ...(from && !(await isGitAncestor(cwd, from, 'HEAD'))\n ? ['origin', from, 'HEAD']\n : []),\n ])\n\n return true\n } catch {\n return false\n }\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { isGitAncestor } from './isGitAncestor'\n\nexport const GIT_REL_FLAGS = {\n NONE: 0,\n IS_ANCESTOR: 1,\n IS_DESCENDANT: 2,\n IS_EQUAL: 3,\n} as const\n\nexport type GitRelFlags = (typeof GIT_REL_FLAGS)[keyof typeof GIT_REL_FLAGS]\n\n/**\n * Determine the relationship between two git refs.\n *\n * Runs `git merge-base --is-ancestor` in both directions and returns a\n * numeric bitmask using `GIT_REL_FLAGS`. A successful check (exit code 0)\n * indicates an ancestor relationship; non-zero exit is treated as \"not ancestor\".\n *\n * @param cwd Repository working directory\n * @param refA First ref (candidate ancestor)\n * @param refB Second ref (candidate descendant)\n * @returns Bitmask of type `GitRelFlags`\n */\nexport async function detectGitRelationship(\n cwd: string,\n refA: string,\n refB: string\n): Promise<GitRelFlags> {\n let flags = GIT_REL_FLAGS.NONE\n\n const [aIsAncestor, bIsAncestor] = await Promise.all([\n isGitAncestor(cwd, refA, refB),\n isGitAncestor(cwd, refB, refA),\n ])\n\n if (aIsAncestor) flags |= GIT_REL_FLAGS.IS_ANCESTOR\n if (bIsAncestor) flags |= GIT_REL_FLAGS.IS_DESCENDANT\n\n return flags as GitRelFlags\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit } from '#types'\nimport {\n detectGitRelationship,\n GIT_REL_FLAGS,\n} from '../revision/detectGitRelationship'\n\n/**\n * Merge two newest-first commit lists into a single deduplicated newest-first list.\n *\n * Compares commits by hash, uses git relationship (ancestor/descendant) when needed,\n * and falls back to committerDate for unrelated commits.\n *\n * @param cwd Repository working directory used for relationship checks\n * @param a Commits from the first source (newest-first)\n * @param b Commits from the second source (newest-first)\n * @returns Merged commits in newest-first order with duplicates removed\n */\nexport async function mergeGitHistory(\n cwd: string,\n a: readonly Commit[],\n b: readonly Commit[]\n): Promise<Commit[]> {\n const merged: Commit[] = []\n const a2 = [...a]\n const b2 = [...b]\n let i = a2.shift()\n let j = b2.shift()\n\n while (i && j) {\n const rel =\n i.hash === j.hash\n ? GIT_REL_FLAGS.IS_EQUAL\n : await detectGitRelationship(cwd, i.hash, j.hash)\n\n switch (rel) {\n case GIT_REL_FLAGS.IS_EQUAL:\n merged.push(i)\n i = a2.shift()\n j = b2.shift()\n break\n\n case GIT_REL_FLAGS.IS_ANCESTOR:\n merged.push(j)\n j = b2.shift()\n break\n\n case GIT_REL_FLAGS.IS_DESCENDANT:\n merged.push(i)\n i = a2.shift()\n break\n\n case GIT_REL_FLAGS.NONE:\n if (i.committerDate < j.committerDate) {\n merged.push(j)\n j = b2.shift()\n } else {\n merged.push(i)\n i = a2.shift()\n }\n break\n }\n }\n\n if (i) {\n merged.push(i)\n }\n\n if (j) {\n merged.push(j)\n }\n\n return merged.concat(a2).concat(b2)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Convert a shallow clone into a full clone by fetching additional history.\n *\n * Runs `git fetch --unshallow`. Returns `true` when the operation succeeds\n * and `false` on error.\n *\n * @param cwd Repository working directory\n * @returns `true` when the operation succeeded, otherwise `false`\n */\nexport function restoreFullGitHistory(cwd: string): Promise<boolean> {\n return execGit(cwd, ['fetch', '--unshallow'])\n .then(() => true)\n .catch(() => false)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\nimport { resolveGitRevision } from '../revision/resolveGitRevision'\nimport {\n expandGitHistory,\n type ExpandGitHistoryOptions,\n} from './expandGitHistory'\n\nexport interface ShowGitHistoryOptions extends ExpandGitHistoryOptions {\n filePath?: string | readonly string[] | undefined\n}\n\n/**\n * Yield commit SHAs from repository history in paginated order.\n *\n * Ensures commits are available via `expandGitHistory`, resolves `until` to a\n * commit id, and pages from `from` toward `until`, yielding each commit SHA.\n * If a git command fails the generator ends silently.\n *\n * @param cwd Repository working directory\n * @param options Supports `from`, `until`, `pageSize`, and `filePath`\n * @yields Commit SHA strings in paginated order\n */\nexport async function* showGitHistory(\n cwd: string,\n options: ShowGitHistoryOptions = {}\n): AsyncGenerator<string> {\n try {\n const { filePath, pageSize = 50 } = options\n let from = options.from || 'HEAD'\n let until = options.until || undefined\n\n if (until) {\n until = await resolveGitRevision(cwd, until)\n }\n\n while (true) {\n await expandGitHistory(cwd, { from, until, pageSize })\n\n const args = ['rev-list', `--max-count=${pageSize}`, from]\n\n if (until) {\n args.push(`^${until}`)\n }\n\n if (filePath) {\n args.push(\n '--',\n ...(Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean)\n )\n }\n\n const stdout = await execGit(cwd, args)\n\n const list = stdout.trim().split('\\n').filter(Boolean)\n\n for (const item of list.slice(0, -1)) {\n yield item\n }\n\n const tail = list.at(-1)\n\n if (!tail) {\n return\n }\n\n if (tail === from) {\n yield tail\n\n from = `${tail}^`\n\n from = await resolveGitRevision(cwd, from)\n } else {\n from = tail\n }\n }\n } catch {}\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Commit } from '#types'\nimport { getParsedCommit } from '../commit/getParsedCommit'\nimport { showGitHistory, type ShowGitHistoryOptions } from './showGitHistory'\n\n/**\n * Yield parsed commits sequentially from repository history.\n *\n * Fetches commits using `showGitHistory` and parses each SHA with\n * `getParsedCommit`. Yields successfully parsed `Commit` objects on demand and\n * omits unparsable commits.\n *\n * @param cwd Repository working directory\n * @param options Filter and pagination options passed to `showGitHistory`\n * @yields Parsed `Commit` objects in historical order\n */\nexport async function* showParsedGitHistory(\n cwd: string,\n options?: ShowGitHistoryOptions\n): AsyncGenerator<Commit> {\n for await (const commit of showGitHistory(cwd, options)) {\n const parsed = await getParsedCommit(cwd, commit)\n\n if (parsed) {\n yield parsed\n }\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get a local git configuration value for `key` in the repository at `cwd`.\n *\n * Returns the config value when present; otherwise `undefined`.\n *\n * @param cwd Repository working directory\n * @param key Git config key to read\n * @returns The config value or `undefined` if not set\n */\nexport async function getGitConfigValue(\n cwd: string,\n key: string\n): Promise<string | undefined> {\n try {\n const stdout = await execGit(cwd, ['config', '--local', '--get', key])\n const value = stdout.trim()\n\n return value ? value : undefined\n } catch {\n return undefined\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\nimport { getGitConfigValue } from './getGitConfigValue'\n\n/**\n * Ensure git `user.name` and `user.email` are configured for the repository at `cwd`.\n *\n * No-op when the values are already present.\n *\n * @param cwd Repository working directory\n * @param user Object containing `name` and `email` values to set when missing\n * @returns Promise that resolves when configuration is ensured\n */\nexport async function ensureGitUserConfig(\n cwd: string,\n user: { name: string; email: string }\n): Promise<void> {\n if (\n (await getGitConfigValue(cwd, 'user.name')) &&\n (await getGitConfigValue(cwd, 'user.email'))\n ) {\n return\n }\n\n // https://github.com/actions/checkout?tab=readme-ov-file#push-a-commit-using-the-built-in-token\n await execGit(cwd, ['config', 'user.name', user.name])\n\n await execGit(cwd, ['config', 'user.email', user.email])\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get the URL for the named remote (defaults to `origin`).\n *\n * @param cwd Repository working directory\n * @param remoteName Remote name (defaults to `'origin'`)\n * @returns Remote URL string or `null` when unavailable\n */\nexport function getGitRemote(\n cwd: string,\n remoteName = 'origin'\n): Promise<string | null> {\n return execGit(cwd, ['remote', 'get-url', remoteName])\n .then((stdout) => stdout.trim())\n .catch(() => null)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { compact, execGit } from '#util'\n\n/**\n * Push a branch and optional tags to the `origin` remote using an atomic push.\n *\n * @param cwd Repository working directory\n * @param branch Branch name to push\n * @param additionalTags Optional list of tag names to push alongside the branch\n * @returns Promise that resolves when the push completes\n */\nexport async function pushGitChanges(\n cwd: string,\n branch: string | null,\n ...additionalTags: readonly string[]\n): Promise<void> {\n await execGit(cwd, [\n 'push',\n 'origin',\n ...compact([branch, ...additionalTags]),\n '--atomic',\n ])\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { GitRepository } from '#types'\nimport GitHost from 'hosted-git-info'\nimport { getGitRemote } from './getGitRemote'\n\n/**\n * Resolve Git repository host, owner, and project metadata from the `origin` remote URL.\n *\n * @param cwd Repository working directory\n * @returns Promise that resolves to a `GitRepository` object, or `null` when unavailable\n */\nexport async function resolveGitRepository(\n cwd: string\n): Promise<GitRepository | null> {\n const origin = await getGitRemote(cwd)\n\n if (!origin) {\n return null\n }\n\n const info = GitHost.fromUrl(origin)\n\n if (\n info &&\n typeof info.type === 'string' &&\n typeof info.user === 'string' &&\n typeof info.project === 'string'\n ) {\n return {\n host: info.type,\n owner: info.user,\n repo: info.project,\n url: info.browse(),\n }\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Read a file's contents from a specific Git revision.\n *\n * Runs `git show <ref>:<filePath>` and returns the command output as a\n * string. If the file does not exist at the given revision or the Git\n * command fails for any reason, the function returns `null`.\n *\n * The returned string is the raw file contents as emitted by Git; callers\n * that need binary-safe handling should decode or process the bytes\n * accordingly. This function intentionally swallows Git errors and signals\n * absence via a `null` result rather than throwing.\n *\n * @param cwd Repository working directory\n * @param ref Commit-ish reference (commit SHA, tag, or branch) to read from\n * @param filePath Path to the file relative to the repository root\n * @returns File contents as a string, or `null` if the file is unavailable at the ref\n */\nexport function readGitFile(\n cwd: string,\n ref: string,\n filePath: string\n): Promise<string | null> {\n return execGit(cwd, ['show', `${ref}:${filePath}`]).catch(() => null)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Create a git tag in the repository at `cwd`.\n *\n * Creates an annotated tag when `message` is provided.\n *\n * @param cwd Repository working directory\n * @param tag Tag name to create\n * @param message Optional tag message (creates an annotated tag when present)\n * @returns Promise that resolves when the tag is created\n */\nexport async function createGitTag(\n cwd: string,\n tag: string,\n message?: string\n): Promise<void> {\n await execGit(cwd, [\n 'tag',\n ...(message ? ['-a', tag, '-m', message, '--cleanup=whitespace'] : [tag]),\n ])\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Delete a local git tag in the repository at `cwd`.\n *\n * @param cwd Repository working directory\n * @param tag Tag name to delete\n * @returns `true` when deletion succeeded, otherwise `false`\n */\nexport function deleteGitTag(cwd: string, tag: string): Promise<boolean> {\n return execGit(cwd, ['tag', '-d', tag])\n .then(() => true)\n .catch(() => false)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { resolveGitRevision } from '../revision/resolveGitRevision'\n\n/**\n * Determine whether a specific git tag exists locally in the repository.\n *\n * @param cwd Working directory for the git command\n * @param tag Name of the tag to evaluate\n * @returns True if the tag reference exists locally, false otherwise\n */\nexport function isGitTagPresent(cwd: string, tag: string): Promise<boolean> {\n return resolveGitRevision(cwd, `refs/tags/${tag}`)\n .then(() => true)\n .catch(() => false)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\nimport { countGitHistory } from '../history/countGitHistory'\nimport { withShallowLock } from '../repository/withShallowLock'\nimport { deleteGitTag } from './deleteGitTag'\nimport { isGitTagPresent } from './isGitTagPresent'\n\nconst MIN_HISTORY_DEPTH = 2\nconst ORIGIN_NOT_REPO = `fatal: 'origin' does not appear to be a git repository`\nconst REMOTE_REF_NOT_FOUND = `fatal: couldn't find remote ref`\n\n/**\n * Ensure a tag exists locally, fetching it shallowly from `origin` if needed.\n *\n * Acquires a shallow-fetch lock, verifies local presence and history depth,\n * attempts a shallow fetch of `refs/tags/<tag>` when required, and removes a\n * local tag if the remote reports the tag is missing.\n *\n * @param cwd Working directory\n * @param tag Tag name (for example `v1.2.0`)\n * @returns `true` if the tag is present or was fetched; otherwise `false`\n */\nexport function ensureGitTag(cwd: string, tag: string): Promise<boolean> {\n return withShallowLock(cwd, async () => {\n const tagRef = `refs/tags/${tag}`\n\n if (\n (await isGitTagPresent(cwd, tag)) &&\n (await countGitHistory(cwd, tagRef)) >= MIN_HISTORY_DEPTH\n ) {\n return true\n }\n\n try {\n await execGit(cwd, [\n 'fetch',\n 'origin',\n `${tagRef}:${tagRef}`,\n `--depth=${MIN_HISTORY_DEPTH}`,\n '--update-shallow',\n '--force',\n ])\n\n return true\n } catch (err) {\n const errorMessage = (err as Error | undefined)?.message\n\n if (errorMessage?.includes(ORIGIN_NOT_REPO)) {\n return await isGitTagPresent(cwd, tag)\n }\n\n if (errorMessage?.includes(REMOTE_REF_NOT_FOUND)) {\n if (await isGitTagPresent(cwd, tag)) {\n await deleteGitTag(cwd, tag)\n }\n }\n }\n\n return false\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\n/**\n * Generate a git tag name for a package.\n *\n * For entry packages the tag is `v<version>`. For non-entry packages the\n * tag is `<name>@<version>`. Returns `null` when `version` is not provided.\n *\n * @param entry Whether the package is the repository entry package\n * @param name Package name (required for non-entry packages)\n * @param version Package version\n * @returns Tag string, or `null` when `version` is absent\n */\nexport function generateGitTagName(\n entry: boolean,\n name: string | null | undefined,\n version: string\n): string\nexport function generateGitTagName(\n entry: boolean,\n name: string | null | undefined,\n version: string | null | undefined\n): string | null\nexport function generateGitTagName(\n entry: boolean,\n name: string | null | undefined,\n version: string | null | undefined\n): string | null {\n if (!version) {\n return null\n }\n\n if (entry) {\n return `v${version}`\n }\n\n if (!name) {\n throw new Error('Package name is required to build a tag name.')\n }\n\n return `${name}@${version}`\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get the tag name that exactly matches the current `HEAD`, if any.\n *\n * @param cwd Repository working directory\n * @returns Tag name or `null` when no exact tag matches HEAD\n */\nexport async function getCurrentGitTag(cwd: string): Promise<string | null> {\n try {\n const stdout = await execGit(cwd, ['describe', '--tags', '--exact-match'])\n\n const tag = stdout.trim()\n\n /* istanbul ignore next */\n return tag ? tag : null\n } catch {\n return null\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get the message of an annotated git tag in the repository at `cwd`.\n *\n * @param cwd Repository working directory\n * @param tag Tag name to query\n * @returns The tag message string when found, otherwise `undefined`\n */\nexport async function getGitTagMessage(\n cwd: string,\n tag: string\n): Promise<string | undefined> {\n try {\n const stdout = await execGit(cwd, [\n 'tag',\n '-l',\n '--format=%(objecttype)%00%(contents)',\n tag,\n ])\n\n const [objectType, message] = stdout.split('\\0')\n\n if (objectType?.trim() !== 'tag') {\n return undefined\n }\n\n return message?.trim() || undefined\n } catch {\n return undefined\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Get the most recent tag reachable from `HEAD`.\n *\n * @param cwd Repository working directory\n * @returns Latest tag string or `null` when none is found\n */\nexport async function getLatestGitTag(cwd: string): Promise<string | null> {\n try {\n const stdout = await execGit(cwd, ['describe', '--tags', '--abbrev=0'])\n const tag = stdout.trim()\n\n /* istanbul ignore next */\n return tag ? tag : null\n } catch {\n return null\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Determine whether a specific git tag commit is reachable from the current HEAD.\n *\n * Uses `git merge-base --is-ancestor refs/tags/<tag>^{commit} HEAD` to verify\n * that the tag's commit is an ancestor of the current reference. Any non-zero\n * exit status or execution error is treated as \"not reachable\".\n *\n * @param cwd Working directory where the git command will run\n * @param tag Tag name to evaluate (for example, \"v1.2.0\")\n * @returns True if the tag commit is reachable from HEAD, false otherwise\n */\nexport async function isGitTagConnected(\n cwd: string,\n tag: string\n): Promise<boolean> {\n try {\n await execGit(cwd, [\n 'merge-base',\n '--is-ancestor',\n `refs/tags/${tag}^{commit}`,\n 'HEAD',\n ])\n\n return true\n } catch {\n return false\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { execGit } from '#util'\n\n/**\n * Check whether a given tag points to the provided commit.\n *\n * @param cwd Repository working directory\n * @param tag Tag name to resolve\n * @param commit Commit hash to compare against\n * @returns `true` when the tag points at `commit`, otherwise `false`\n */\nexport async function isGitTagPointed(\n cwd: string,\n tag: string,\n commit: string\n): Promise<boolean> {\n try {\n await execGit(cwd, [\n 'merge-base',\n '--is-ancestor',\n `refs/tags/${tag}^{commit}`,\n commit,\n ])\n\n await execGit(cwd, [\n 'merge-base',\n '--is-ancestor',\n commit,\n `refs/tags/${tag}^{commit}`,\n ])\n\n return true\n } catch {\n return false\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleasePlan } from '#types'\n\nexport type DependencyGraph = Map<string, Set<ReleasePlan>>\n\n/**\n * Build a dependency graph mapping package names to dependent release plans.\n *\n * Each key is a package name and the value is a set of `ReleasePlan`s that\n * list that package as a dependency.\n *\n * @param plans Array of release plans to analyze\n * @returns Dependency graph mapping package name -> set of dependent plans\n */\nexport function buildDependencyGraph(\n plans: readonly ReleasePlan[]\n): DependencyGraph {\n const graph = new Map<string, Set<ReleasePlan>>()\n\n const packageNames = new Set(\n plans\n .map((plan) => plan.package.name)\n .filter((name): name is string => !!name)\n )\n\n for (const plan of plans) {\n for (const depName of plan.package.dependencies) {\n let set = graph.get(depName)\n\n if (!set && packageNames.has(depName)) {\n graph.set(depName, (set = new Set()))\n }\n\n if (set) {\n set.add(plan)\n }\n }\n }\n\n return graph\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { Heading, Paragraph, PhrasingContent } from 'mdast'\n\n/**\n * Extract trimmed plain text from a Heading, Paragraph, or PhrasingContent node.\n *\n * Performs a depth-first traversal, concatenating string `value` tokens in\n * document order. Non-string values are ignored. Returns `null` for empty or\n * missing input.\n *\n * @param node Node to extract text from\n * @returns The trimmed plain-text string, or null if empty\n */\nexport function extractPlainText(\n node: Heading | Paragraph | PhrasingContent | null | undefined\n): string | null {\n if (!node) {\n return null\n }\n\n const queue = [node]\n let result = ''\n\n while ((node = queue.shift())) {\n if ('value' in node && typeof node.value === 'string') {\n result += node.value\n }\n\n /* istanbul ignore else */\n if ('children' in node && Array.isArray(node.children)) {\n queue.unshift(...node.children)\n }\n }\n\n return result.trim() || null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { SEMVER_REGEX } from '#config'\nimport { compact } from '#util'\n\nconst ANCHORED_SEMVER_REGEX = new RegExp(`^${SEMVER_REGEX.source}$`)\n\nconst BRACKETED_SEMVER_REGEX = new RegExp(`\\\\[${SEMVER_REGEX.source}\\\\]`)\n\n/**\n * Extract SemVer core strings from a line.\n *\n * Splits the line on runs of spaces or tabs and on `..`/`...`. For each token,\n * first attempts an anchored SemVer match (must match the whole token), then\n * a bracketed match that finds a SemVer inside `[...]`. Returns the captured\n * SemVer core (capture group 2) for each successful match; falsy results are\n * filtered out.\n *\n * @param line Line to scan for SemVer cores\n * @returns Array of extracted SemVer core strings; empty if none\n */\nexport function extractSemVer(line: string): string[] {\n const tokens = line.split(/[ \\t]+|\\.{2,3}/)\n\n return compact(\n tokens.map((token) => {\n return (\n ANCHORED_SEMVER_REGEX.exec(token) ?? BRACKETED_SEMVER_REGEX.exec(token)\n )?.at(2)\n })\n )\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { RootContent } from 'mdast'\nimport { fromMarkdown } from 'mdast-util-from-markdown'\nimport semver from 'semver'\nimport { extractPlainText } from './extractPlainText'\nimport { extractSemVer } from './extractSemVer'\n\nconst SENTENCE_TERMINAL_REGEX = /\\p{Sentence_Terminal}$/u\n\n/**\n * Iterate over Semantic Version strings extracted from a Markdown changelog.\n *\n * Parses `changelogContent` into an AST and evaluates nodes sequentially using\n * custom extractors. Evaluates heading nodes first before checking short\n * paragraph nodes. Stops execution after the first strategy yields valid\n * versions.\n *\n * @param changelogContent Raw Markdown text of the changelog\n * @yields Valid SemVer strings found in headings or candidate paragraphs\n */\nexport function* iterateChangelogVersions(\n changelogContent: string\n): Generator<string> {\n const root = fromMarkdown(changelogContent)\n\n const nodeExtractors = [\n (node: RootContent): string | null => {\n return node.type === 'heading' ? extractPlainText(node) : null\n },\n\n (node: RootContent): string | null => {\n if (node.type !== 'paragraph') return null\n\n const paragraphText = extractPlainText(node)\n\n if (\n !paragraphText ||\n paragraphText.length > 48 ||\n SENTENCE_TERMINAL_REGEX.test(paragraphText)\n ) {\n return null\n }\n\n return paragraphText\n },\n ]\n\n for (const extractText of nodeExtractors) {\n let isVersionFound = false\n\n for (const node of root.children) {\n const extractedText = extractText(node)\n\n const candidateVersion = extractedText\n ? semver.sort(extractSemVer(extractedText)).pop()\n : null\n\n if (candidateVersion && semver.valid(candidateVersion)) {\n isVersionFound = true\n yield candidateVersion\n }\n }\n\n if (isVersionFound) {\n return\n }\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { iterateChangelogVersions } from './iterateChangelogVersions'\n\n/**\n * Find the highest semantic version from the first version-bearing element.\n *\n * Pulls the first available version string from the parsed markdown iterator.\n * Short-circuits execution immediately upon locating a match to avoid passing\n * through the remaining document structure.\n *\n * @param changelogContent The raw markdown string representing the changelog\n * @returns The highest valid semantic version string found or null\n */\nexport function extractLatestVersion(changelogContent: string): string | null {\n for (const version of iterateChangelogVersions(changelogContent)) {\n return version\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport {\n ensureGitTag,\n expandGitHistory,\n getParsedCommit,\n isGitTagConnected,\n} from '#git'\nimport type { Package } from '#types'\nimport { isCommitForPackage } from '../analysis/isCommitForPackage'\n\n/**\n * Verify a Git tag against package specifications and history connection.\n *\n * Ensures the target tag exists locally before parsing its commit data. Rejects\n * tags that do not target a commit matching the given `pkg`. Expands the history\n * graph up to the tag reference before verifying structural connectivity.\n *\n * @param cwd Repository working directory\n * @param pkg Package descriptor containing version and location context\n * @param tagName The name of the Git tag to verify\n * @returns A promise resolving to true if the tag is valid and connected\n */\nexport async function verifyPackageTag(\n cwd: string,\n pkg: Package,\n tagName: string\n): Promise<boolean> {\n await ensureGitTag(cwd, tagName)\n\n const tagRef = `refs/tags/${tagName}`\n\n const tagCommit = await getParsedCommit(cwd, tagRef)\n\n if (!tagCommit || !isCommitForPackage(pkg, tagCommit)) {\n return false\n }\n\n await expandGitHistory(cwd, { until: tagRef })\n\n return isGitTagConnected(cwd, tagName)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { DEFAULT_CHANGELOG_FILE } from '#config'\nimport {\n generateGitTagName,\n getGitTagMessage,\n readGitFile,\n resolveGitRevision,\n} from '#git'\nimport type { Package, Tag } from '#types'\nimport { readFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { extractLatestVersion } from '../changelog/extractLatestVersion'\nimport { verifyPackageTag } from './verifyPackageTag'\n\n/**\n * Discover and validate the Git tag for a package.\n *\n * Returns the existing tag if already present on the `pkg` object. Resolves\n * the local changelog file path, extracts the current version, and generates\n * the expected Git tag name. Verifies the tag against the repository and\n * ensures its historical changelog version matches the current local version.\n *\n * @param cwd The current working directory\n * @param pkg The package configuration object\n * @returns The validated `Tag` object or null if validation fails\n */\nexport async function discoverPackageTag(\n cwd: string,\n pkg: Package\n): Promise<Tag | null> {\n if (pkg.tag) {\n return pkg.tag\n }\n\n const changelogFile = pkg.changelogFile || DEFAULT_CHANGELOG_FILE\n\n const changelogFilePath = path.resolve(cwd, pkg.dir, changelogFile)\n\n let localChangelogContent: string | null | undefined\n\n try {\n localChangelogContent = await readFile(changelogFilePath, 'utf-8')\n } catch {}\n\n if (!localChangelogContent) {\n return null\n }\n\n const currentVersion = extractLatestVersion(localChangelogContent)\n\n if (!currentVersion) {\n return null\n }\n\n let expectedTagName: string | null | undefined\n\n try {\n expectedTagName = generateGitTagName(pkg.entry, pkg.name, currentVersion)\n } catch {}\n\n if (!expectedTagName) {\n return null\n }\n\n if (!(await verifyPackageTag(cwd, pkg, expectedTagName))) {\n return null\n }\n\n const tagRef = `refs/tags/${expectedTagName}`\n\n const historicalChangelogContent = await readGitFile(\n cwd,\n await resolveGitRevision(cwd, tagRef),\n path.relative(cwd, changelogFilePath)\n )\n\n if (!historicalChangelogContent) {\n return null\n }\n\n const historicalChangelogVersion = extractLatestVersion(\n historicalChangelogContent\n )\n\n if (\n !historicalChangelogVersion ||\n historicalChangelogVersion !== currentVersion\n ) {\n return null\n }\n\n return {\n name: expectedTagName,\n message: await getGitTagMessage(cwd, expectedTagName),\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { DEFAULT_CHANGELOG_FILE } from '#config'\nimport { generateGitTagName, getGitTagMessage } from '#git'\nimport type { Package, Tag } from '#types'\nimport assert from 'node:assert'\nimport { readFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport semver from 'semver'\nimport { iterateChangelogVersions } from '../changelog/iterateChangelogVersions'\nimport { verifyPackageTag } from './verifyPackageTag'\n\n/**\n * Resolve the latest stable Git tag for a package.\n *\n * Reads changelog release versions for `pkg` and verifies strict descending\n * semver order. Ignores prerelease versions unless transitioning across major\n * or prerelease boundaries, and verifies that the matching Git tag exists.\n *\n * @param cwd Absolute path to the root working directory\n * @param pkg Package configuration object\n * @returns Verified tag metadata object or `null` if no stable tag is resolved\n * @throws If a version is invalid, versions are not in strict descending\n * order, or the corresponding Git tag is missing\n */\nexport async function resolvePackageStableTag(\n cwd: string,\n pkg: Package\n): Promise<Tag | null> {\n const changelogFile = pkg.changelogFile || DEFAULT_CHANGELOG_FILE\n\n const changelogFilePath = path.resolve(cwd, pkg.dir, changelogFile)\n\n let changelogContent: string\n\n try {\n changelogContent = await readFile(changelogFilePath, 'utf-8')\n } catch (error: unknown) {\n /* istanbul ignore else */\n if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') {\n return null\n }\n\n /* istanbul ignore next */\n throw error\n }\n\n let precedingVersion: semver.SemVer | null = null\n\n for (const version of iterateChangelogVersions(changelogContent)) {\n const currentVersion = semver.parse(version)\n\n assert(\n currentVersion,\n `Version \"${version}\" is not a valid semantic version.`\n )\n\n if (precedingVersion && semver.gte(currentVersion, precedingVersion)) {\n throw new Error(\n `Version \"${version}\" must be strictly lower than preceding version \"${precedingVersion.raw}\".`\n )\n }\n\n if (\n !semver.prerelease(currentVersion) &&\n (currentVersion.major >= 1 ||\n (precedingVersion && semver.prerelease(precedingVersion)))\n ) {\n const tagName = generateGitTagName(pkg.entry, pkg.name, version)\n\n if (!(await verifyPackageTag(cwd, pkg, tagName))) {\n throw new Error(\n `Git tag \"${tagName}\" is not associated with package \"${pkg.name ?? ''}\" in directory \"${pkg.dir}\".`\n )\n }\n\n return {\n name: tagName,\n message: await getGitTagMessage(cwd, tagName),\n }\n }\n\n precedingVersion = currentVersion\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { generateGitTagName, getGitTagMessage } from '#git'\nimport type { Package, Tag } from '#types'\nimport { verifyPackageTag } from './verifyPackageTag'\n\n/**\n * Resolve the release tag for a single package version.\n *\n * Returns the cached `pkg.tag` immediately if it already exists. Generates an\n * expected tag name from the package metadata and verifies its integrity using\n * `verifyPackageTag`.\n *\n * @param cwd Repository working directory\n * @param pkg Package descriptor containing version and location context\n * @returns A promise resolving to the tag metadata or null when not found\n */\nexport async function resolvePackageTag(\n cwd: string,\n pkg: Package\n): Promise<Tag | null> {\n if (pkg.tag) {\n return pkg.tag\n }\n\n let expectedTagName: string | null | undefined\n\n try {\n expectedTagName = generateGitTagName(pkg.entry, pkg.name, pkg.version)\n } catch {}\n\n if (!expectedTagName) {\n return null\n }\n\n if (await verifyPackageTag(cwd, pkg, expectedTagName)) {\n return {\n name: expectedTagName,\n message: await getGitTagMessage(cwd, expectedTagName),\n }\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { mergeGitHistory } from '#git'\nimport type { Commit, ReleasePlan } from '#types'\nimport { buildDependencyGraph } from '../discovery'\n\n/**\n * Propagate release updates through package dependencies.\n *\n * Marks packages that depend on a releasing package for a `patch` release\n * when they do not already have a release type.\n *\n * @param plans Array of release plans to update\n * @returns Updated array of release plans\n */\nexport async function propagateDependencyUpdates(\n cwd: string,\n plans: readonly ReleasePlan[]\n): Promise<ReleasePlan[]> {\n const graph = buildDependencyGraph(plans)\n\n const scheduled = new Map<ReleasePlan, Commit[]>()\n\n const stack: ReleasePlan[] = []\n\n for (const plan of plans) {\n if (plan.release?.type) {\n const visited = new Set([plan])\n\n stack.push(plan)\n\n let curr: ReleasePlan | undefined\n\n while ((curr = stack.pop())) {\n if (curr.package.name) {\n for (const dependent of graph.get(curr.package.name) ?? []) {\n if (!visited.has(dependent)) {\n visited.add(dependent)\n\n stack.push(dependent)\n\n scheduled.set(\n dependent,\n await mergeGitHistory(\n cwd,\n scheduled.get(dependent) ?? dependent.history,\n plan.history\n )\n )\n }\n }\n }\n }\n }\n }\n\n return plans.map((plan) => {\n const history = scheduled.get(plan)\n\n if (history) {\n plan = { ...plan, history }\n\n if (!plan.release?.type) {\n plan.release = { type: 'patch' }\n }\n }\n\n return plan\n })\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { GIT_TRAILER_RELEASE_AGENT } from '#config'\nimport { getParsedCommit } from '#git'\nimport type { Commit, CommitTrailer } from '#types'\nimport { execGit } from '#util'\n\nexport type ReleaseMarker = string | ((trailer: CommitTrailer) => boolean)\n\n/**\n * Determine whether a commit represents a release or matches a release marker.\n *\n * Resolves string commit identifiers to parsed commit objects, checks if any\n * associated trailer matches the marker predicate, and falls back to verifying\n * exact tag matches for the `commit` hash.\n *\n * @param cwd The working directory path for Git commands\n * @param commit The commit object, hash string, or null/undefined\n * @param marker The trailer key string or a predicate function to evaluate\n * @returns True if the commit is a release commit, otherwise false\n */\nexport async function isReleaseCommit(\n cwd: string,\n commit: Commit | string | null | undefined,\n marker: ReleaseMarker = GIT_TRAILER_RELEASE_AGENT\n): Promise<boolean> {\n if (typeof commit === 'string') {\n commit = await getParsedCommit(\n cwd,\n commit,\n typeof marker === 'string' ? [marker] : []\n )\n }\n\n if (!commit) {\n return false\n }\n\n const predicate =\n typeof marker === 'string'\n ? (trailer: CommitTrailer) => trailer.key === marker && !!trailer.value\n : marker\n\n if (commit.trailers.some(predicate)) {\n return true\n }\n\n return execGit(cwd, ['describe', '--tags', '--exact-match', commit.hash])\n .then(() => true)\n .catch(() => false)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { showParsedGitHistory, type ShowGitHistoryOptions } from '#git'\nimport type { Commit } from '#types'\nimport {\n isReleaseCommit,\n type ReleaseMarker,\n} from '../committing/isReleaseCommit'\n\nexport interface SliceUnreleasedOptions extends ShowGitHistoryOptions {\n marker?: ReleaseMarker | undefined\n}\n\n/**\n * Yield parsed commits from the workspace until a release commit is found.\n *\n * Iterates through parsed commit history and yields unreleased commits in\n * order. Stops processing upon reaching the first release commit.\n *\n * @param cwd Path to the working directory\n * @param options Options for configuring Git history retrieval\n * @yields Parsed `Commit` objects up to the most recent release commit\n */\nexport async function* sliceUnreleased(\n cwd: string,\n options?: SliceUnreleasedOptions\n): AsyncGenerator<Commit> {\n for await (const commit of showParsedGitHistory(cwd, options)) {\n if (await isReleaseCommit(cwd, commit, options?.marker)) {\n break\n }\n\n yield commit\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { showParsedGitHistory, type ShowGitHistoryOptions } from '#git'\nimport type { Commit, Package } from '#types'\nimport { isCommitForPackage } from './isCommitForPackage'\n\n/**\n * Yield commits from git history associated with a package.\n *\n * Filters git commit history to stream only entries relevant to the target\n * `pkg` directory.\n *\n * @param cwd Path to the working directory for git commands\n * @param pkg Package configuration containing the target directory\n * @param options Options for parsing and filtering git history\n * @yields Commits belonging to the specified package\n */\nexport async function* streamPackageCommits(\n cwd: string,\n pkg: Package,\n options: ShowGitHistoryOptions = {}\n): AsyncGenerator<Commit> {\n options = {\n ...options,\n filePath: options.filePath || pkg.dir,\n }\n\n for await (const commit of showParsedGitHistory(cwd, options)) {\n if (isCommitForPackage(pkg, commit)) {\n yield commit\n }\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { DEFAULT_CHANGELOG_FILE } from '#config'\nimport type { Commit, ReleaseContext } from '#types'\nimport { formatFile } from '#util'\nimport createPreset from 'conventional-changelog-conventionalcommits'\nimport {\n writeChangelogString,\n type CommitKnownProps,\n type FinalTemplateContext,\n type TemplateContext,\n} from 'conventional-changelog-writer'\nimport path from 'node:path'\n\nexport interface BuildChangelogOptions {\n packageDir?: string | undefined\n changelogFile?: string | undefined\n version?: string | null | undefined\n previousTag?: string | null | undefined\n currentTag?: string | null | undefined\n}\n\nconst preset = await createPreset()\n\nconst headerPartial = (context: FinalTemplateContext) => {\n return context.version ? preset.writer.headerPartial?.(context) : undefined\n}\n\n/**\n * Build the changelog string for a release.\n *\n * Compiles commit history and release context into a formatted changelog using\n * conventional commits and writer configurations. Resolves file paths and handles\n * tag comparisons when repository URLs are available.\n *\n * @param context The release context containing repository and working directory details\n * @param history The iterable or async iterable collection of commits to process\n * @param options The build configuration options for the changelog\n * @returns The formatted changelog content string\n */\nexport async function buildChangelog(\n context: ReleaseContext,\n history: Iterable<Commit> | AsyncIterable<Commit>,\n options: BuildChangelogOptions = {}\n): Promise<string> {\n const repository = context.repository?.url || undefined\n\n const version = options.version || undefined\n\n const previousTag = options.previousTag || undefined\n\n const currentTag = options.currentTag || undefined\n\n const filePath = path.join(\n context.cwd,\n options.packageDir || '.',\n options.changelogFile || DEFAULT_CHANGELOG_FILE\n )\n\n const content = await writeChangelogString<CommitKnownProps>(\n history,\n {\n date: context.today,\n version,\n previousTag,\n currentTag,\n linkCompare: !!repository && !!previousTag && !!currentTag,\n linkReferences: !!repository,\n repoUrl: repository,\n commit: 'commit',\n issue: 'issues',\n headerPartial,\n } as TemplateContext,\n preset.writer\n )\n\n return formatFile(filePath, content)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { DEFAULT_CHANGELOG_FILE } from '#config'\nimport type { Commit, ReleaseContext } from '#types'\nimport { formatFile } from '#util'\nimport type { Heading, RootContent } from 'mdast'\nimport { fromMarkdown } from 'mdast-util-from-markdown'\nimport { toMarkdown } from 'mdast-util-to-markdown'\nimport path from 'node:path'\nimport { buildChangelog } from './buildChangelog'\n\n/**\n * Build release notes from a release context and commit history.\n *\n * Generates changelog content from commits, parses it into an abstract syntax\n * tree, shifts heading depths, and formats the resulting markdown file.\n *\n * @param context The release context containing repository and configuration details\n * @param history The iterable or async iterable collection of commits to process\n * @returns The formatted release notes as a markdown string\n */\nexport async function buildReleaseNotes(\n context: ReleaseContext,\n history: Iterable<Commit> | AsyncIterable<Commit>\n): Promise<string> {\n const changelogContent = await buildChangelog(context, history)\n\n const root = fromMarkdown(changelogContent)\n\n root.children = root.children.flatMap<RootContent>((node) => {\n if (node.type === 'heading') {\n /* istanbul ignore next */\n if (node.depth <= 2) {\n return []\n }\n\n node = {\n ...node,\n depth: (node.depth - 1) as Heading['depth'],\n }\n }\n\n return node\n })\n\n const filePath = path.join(context.cwd, DEFAULT_CHANGELOG_FILE)\n\n return formatFile(filePath, toMarkdown(root))\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { colors } from 'consola/utils'\nimport semver from 'semver'\nimport { iterateChangelogVersions } from './iterateChangelogVersions'\n\n// TODO: remove unused\n/**\n * Find the latest stable version that precedes the current version.\n *\n * Parses markdown from top to bottom to extract semantic versions from headings\n * and paragraphs. Enforces that discovered versions appear in strict\n * descending order. Returns the first version that falls outside the current\n * version's major, minor, and patch boundaries if the current version is\n * `v1.0.0` or higher, or the first non-matching version for `v0.x.x` releases.\n *\n * @param currentVersion The version to evaluate against\n * @param changelogContent The raw markdown content to scan for versions\n * @returns The resolved stable version string or null when none is found\n * @throws If `currentVersion` is invalid or already stable, or if versions in the changelog are not listed in strict descending order\n */\nexport async function extractPreviousVersion(\n currentVersion: string | null | undefined,\n changelogContent: string\n): Promise<string | null> {\n const parsedCurrent = semver.parse(currentVersion)\n\n if (!currentVersion || !parsedCurrent) {\n throw new Error(\n `The current version \"${colors.bold(String(currentVersion))}\" is not a valid semantic version.`\n )\n }\n\n const currentMajor = parsedCurrent.major\n\n if (currentMajor >= 1 && parsedCurrent.prerelease.length === 0) {\n throw new Error(\n `The current version \"${colors.bold(currentVersion)}\" is already a stable release.`\n )\n }\n\n const baseVersion = `${parsedCurrent.major}.${parsedCurrent.minor}.${parsedCurrent.patch}`\n\n let priorVersion: semver.SemVer | null = null\n\n for (const version of iterateChangelogVersions(changelogContent)) {\n const parsedVersion = semver.parse(version)\n\n /* istanbul ignore else */\n if (parsedVersion) {\n if (!priorVersion) {\n if (version !== currentVersion) {\n return version\n }\n\n priorVersion = parsedVersion\n } else {\n if (semver.gte(parsedVersion, priorVersion)) {\n throw new Error(\n `Each newer release must have a higher version, but \"${colors.bold(priorVersion.version)}\" appears before \"${colors.bold(version)}\".`\n )\n }\n\n priorVersion = parsedVersion\n\n const { major, minor, patch } = parsedVersion\n\n if (currentMajor >= 1 && `${major}.${minor}.${patch}` !== baseVersion) {\n return version\n }\n }\n }\n }\n\n return null\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { formatFile } from '#util'\nimport { fromMarkdown } from 'mdast-util-from-markdown'\nimport { toMarkdown } from 'mdast-util-to-markdown'\nimport fs from 'node:fs/promises'\n\nconst defaultChangelogTitle = 'Changelog'\n\n/**\n * Prepend changelog content to a markdown file, creating it if necessary.\n *\n * Reads the existing file if present, prepends the new content before the main heading,\n * formats and writes the result back.\n *\n * @param filePath Path to the changelog file\n * @param revision Changelog content to prepend\n * @returns Promise that resolves when the changelog is written\n */\nexport async function prependChangelog(\n filePath: string,\n revision: string\n): Promise<void> {\n let content = ''\n\n try {\n content = await fs.readFile(filePath, 'utf-8')\n } catch (err) {\n /* istanbul ignore next */\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n\n const root = fromMarkdown(content)\n\n let index = root.children.findIndex(\n (node) => node.type === 'heading' && node.depth === 1\n )\n\n if (index === -1) {\n index = 0\n\n root.children.unshift({\n type: 'heading',\n depth: 1,\n children: [{ type: 'text', value: defaultChangelogTitle }],\n })\n }\n\n root.children.splice(index + 1, 0, ...fromMarkdown(revision).children)\n\n content = toMarkdown(root)\n\n content = await formatFile(filePath, content)\n\n await fs.writeFile(filePath, content)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { DEFAULT_CHANGELOG_FILE } from '#config'\nimport type { ReleasePlan } from '#types'\nimport path from 'node:path'\nimport { prependChangelog } from './prependChangelog'\n\n/**\n * Write changelog content to the file system based on the release plan.\n *\n * @param cwd Repository working directory\n * @param plan Release plan containing package and changelog content\n * @returns Promise that resolves when the changelog file has been written\n */\nexport async function writeChangelog(\n cwd: string,\n plan: ReleasePlan\n): Promise<void> {\n if (plan.release?.changelog) {\n const changelogFile = plan.package.changelogFile || DEFAULT_CHANGELOG_FILE\n\n const filePath = path.join(cwd, plan.package.dir, changelogFile)\n\n await prependChangelog(filePath, plan.release.changelog)\n }\n}\n","{\n \"name\": \"@reelout/core\",\n \"version\": \"0.1.1\",\n \"description\": \"Core release lifecycle orchestration.\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"imports\": {\n \"#*\": [\n \"./src/*\",\n \"./src/*.ts\",\n \"./src/*/index.ts\"\n ]\n },\n \"exports\": {\n \"./package.json\": {\n \"default\": \"./package.json\"\n },\n \".\": {\n \"module\": \"./src/index.ts\",\n \"development\": \"./src/index.ts\",\n \"types\": \"./src/index.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"./*\": {\n \"module\": \"./src/*.ts\",\n \"development\": \"./src/*.ts\",\n \"types\": \"./src/*.ts\",\n \"default\": \"./dist/*.js\"\n }\n },\n \"files\": [\n \"./dist\",\n \"./src\"\n ],\n \"scripts\": {\n \"build\": \"vite build --ssr --sourcemap\",\n \"dev\": \"vite build --ssr --watch\"\n },\n \"dependencies\": {\n \"consola\": \"catalog:\",\n \"conventional-changelog-conventionalcommits\": \"catalog:\",\n \"conventional-changelog-writer\": \"catalog:\",\n \"conventional-commits-parser\": \"catalog:\",\n \"hosted-git-info\": \"catalog:\",\n \"mdast-util-from-markdown\": \"catalog:\",\n \"mdast-util-to-markdown\": \"catalog:\",\n \"minimatch\": \"catalog:\",\n \"prettier\": \"catalog:\",\n \"semver\": \"catalog:\"\n },\n \"devDependencies\": {\n \"@types/hosted-git-info\": \"catalog:\",\n \"@types/mdast\": \"catalog:\",\n \"@types/semver\": \"catalog:\"\n },\n \"engines\": {\n \"node\": \">=24.15\"\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { GIT_TRAILER_RELEASE_AGENT } from '#config'\nimport type { ReleasePlan } from '#types'\nimport { version } from '../../../package.json'\n\n/**\n * Generate a formatted release commit message based on release plans and\n * custom trailers.\n *\n * Compiles package version updates and included commit logs into a standardized\n * conventional commit message body, appending configured release agent and\n * custom trailers. Returns null if no valid release subject can be derived.\n *\n * @param plans Readonly array of release plans containing package changes and commit history\n * @param trailers Optional record of custom git trailers to append to the commit\n * @returns The formatted release commit message string or null if the subject is empty\n */\nexport async function generateReleaseCommitMessage(\n plans: readonly ReleasePlan[],\n trailers: Readonly<Record<string, string | number | null | undefined>> = {}\n): Promise<string | null> {\n const standalone = plans.length === 1\n\n trailers = {\n [GIT_TRAILER_RELEASE_AGENT]: `ReelOut/${import.meta.env.MODE === 'test' ? '0.0.0' : /* istanbul ignore next */ version}`,\n ...trailers,\n }\n\n plans = plans\n .filter((plan) => plan.release?.tag?.name)\n .toSorted((a, b) => {\n if (a.package.entry) return -1\n if (b.package.entry) return 1\n /* istanbul ignore next */\n return (a.package.name ?? '').localeCompare(b.package.name ?? '')\n })\n\n let subject: string | null | undefined\n\n if (plans.length === 1) {\n subject = standalone\n ? plans[0]?.release?.version\n : plans[0]?.release?.tag?.name\n } else if (plans.length >= 2 && plans.length <= 3) {\n subject = plans.map((plan) => plan.release?.tag?.name).join(', ')\n } else if (plans.length > 3) {\n subject = `${plans.length} packages (${plans\n .slice(0, 2)\n .map((plan) => plan.release?.tag?.name)\n .join(', ')}, +${plans.length - 2} more)`\n }\n\n if (!subject) {\n return null\n }\n\n const packages = plans.map((plan) => {\n return `- ${plan.package.name || plan.package.dir}: ${plan.package.version} → ${plan.release?.version}`\n })\n\n const commits = Array.from(\n new Map(\n plans\n .flatMap((plan) => plan.history)\n .map((commit) => [commit.hash, commit])\n ).values()\n )\n .toSorted((a, b) => {\n return (\n new Date(a.committerDate).valueOf() -\n new Date(b.committerDate).valueOf()\n )\n })\n .map(({ header }) => `- ${header}`)\n\n return [\n `chore: release ${subject}`,\n\n packages.length >= 2\n ? ['## Version Updates', ...packages].join('\\n')\n : undefined,\n\n commits.length > 0\n ? ['## Changes Included', ...commits].join('\\n')\n : undefined,\n\n Object.entries(trailers)\n .map(([key, value]) => (key && value != null ? `${key}: ${value}` : ''))\n .filter(Boolean)\n .join('\\n'),\n ]\n .filter(Boolean)\n .join('\\n\\n')\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type {\n ReleaseContext,\n ReleaseLifecycle,\n ReleaseReport,\n ReleaseStatus,\n} from '#types'\nimport { compact } from '#util'\n\n/**\n * Generate a release report from the current workflow state.\n *\n * Extracts relevant information from the context and organizes it into a report\n * containing workflow phase, status, plugin metadata, packages, and release plans.\n *\n * @param context Release context with workflow state and plans\n * @param plugin Name of the current plugin executing\n * @param hook Current workflow phase\n * @param status Workflow completion status (`'success'` or `'failure'`)\n * @param reason Error or failure reason (used only when status is `'failure'`)\n * @returns Release report object with structured workflow results\n */\nexport function generateReleaseReport(\n context: ReleaseContext,\n plugin: string | undefined,\n hook: ReleaseLifecycle | undefined,\n status: ReleaseStatus,\n reason: unknown\n): ReleaseReport {\n return {\n phase: plugin && hook ? { plugin, hook } : undefined,\n status,\n reason: Error.isError(reason)\n ? reason.message\n : typeof reason === 'string'\n ? reason\n : null,\n prereleaseLabel: context.prereleaseLabel,\n releaseChannel: context.releaseChannel,\n plugins: context.plugins.map((plugin) => ({\n name: plugin.name,\n version: plugin.version,\n hooks: Object.keys(plugin).filter(\n (key) => key !== 'name' && key !== 'version'\n ),\n })),\n packages: context.packages.map((pkg) => ({\n dir: pkg.dir,\n name: pkg.name,\n version: pkg.version,\n })),\n plans: compact(\n context.plans.map((plan) => {\n if (!plan.release?.version) return null\n\n return {\n package: {\n entry: plan.package.entry,\n dir: plan.package.dir,\n name: plan.package.name,\n version: plan.package.version,\n private: !!plan.package.private,\n },\n release: {\n type: plan.release.type,\n version: plan.release.version,\n tag: plan.release.tag?.name,\n changelog: plan.release.changelog,\n },\n }\n })\n ),\n }\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport type { ReleaseContext, ReleaseReport } from '#types'\nimport { colors } from 'consola/utils'\nimport path from 'node:path'\n\n/**\n * Print a formatted release report to the logger.\n *\n * Logs the workflow status and details about released packages. On success,\n * displays released package names and version changes. On failure, displays\n * the error reason.\n *\n * @param context Release context for logging\n * @param report Release report to display\n * @returns void\n */\nexport function printReleaseReport(\n context: ReleaseContext,\n report: ReleaseReport\n): void {\n const statusLabel = {\n success: colors.green('SUCCESS'),\n failure: colors.red('FAILURE'),\n }[report.status]\n\n context.logger.info(`Status: ${statusLabel}`)\n\n if (report.status === 'failure') {\n return context.logger.error(\n [\n 'Release complete:',\n colors.red(\n report.reason ||\n 'An unexpected error occurred during the release process.'\n ),\n ].join(' ')\n )\n }\n\n if (report.plans.length === 0) {\n return context.logger.info(\n [\n 'Release complete:',\n colors.yellow('No packages are scheduled for release.'),\n ].join(' ')\n )\n }\n\n const header = [\n 'Release complete:',\n colors.bold(colors.green(`${report.plans.length} released`)),\n colors.gray(`(${report.packages.length})`),\n ].join(' ')\n\n const list = report.plans\n .toSorted((a, b) =>\n (a.package.name || '').localeCompare(b.package.name || '')\n )\n .map((plan) => {\n let dir = path.normalize(path.join(plan.package.dir, '.'))\n\n dir = dir === '.' ? dir : `./${dir}`\n\n const name = plan.package.name\n ? colors.bgBlue(` ${plan.package.name} `)\n : !plan.package.entry && colors.gray(` ${dir} `)\n\n let version = [plan.release.version, plan.package.version]\n .filter(Boolean)\n .join(' ← ')\n\n version = ` ${version} `\n\n const type = plan.release.type && colors.gray(` ${plan.release.type} `)\n\n const summary = [\n name,\n plan.release.type === 'major'\n ? colors.bgRed(version)\n : colors.bgGreen(version),\n type,\n ]\n .filter(Boolean)\n .join('')\n\n const changelog = plan.release.changelog\n ?.replace(/^(#+ .+)$/gm, (_, header) => colors.cyan(header))\n .trim()\n\n return [summary, changelog].filter(Boolean).join('\\n\\n')\n })\n .join('\\n\\n')\n\n context.logger.info(`${header}\\n\\n${list}\\n`)\n}\n","// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Fai\n\nimport { RELEASE_LIFECYCLE_HOOK_LIST } from '#config'\nimport type {\n ReleaseContext,\n ReleaseLifecycle,\n ReleasePlugin,\n ReleasePluginHookRollback,\n ReleaseStatus,\n} from '#types'\nimport { compact } from '#util'\nimport { colors } from 'consola/utils'\nimport { generateReleaseReport, printReleaseReport } from '../report'\n\n/**\n * Execute the plugin-driven release workflow using the provided `ReleaseContext`.\n *\n * The workflow runs plugin stages in order: `configure`, `discover`,\n * `analyze`, `build`, `write`, `commit`, and `publish`. After a successful\n * run the function sets `context.status` to `'success'`. The workflow is\n * finalized in all cases; on success a release report is printed.\n *\n * The returned array contains all computed release plans, including plans\n * without releases. Side effects (file writes, commits, tags, publishing)\n * are performed by plugins and may be skipped when `context.dryRun` is true.\n *\n * @returns Promise that resolves when the workflow is complete.\n */\nexport async function runWorkflow(\n createContext: () => ReleaseContext | Promise<ReleaseContext>\n): Promise<void> {\n const isSilent = import.meta.env.MODE === 'test'\n const context = await createContext()\n const stack: ReleasePluginHookRollback[] = []\n let plugin: ReleasePlugin | undefined\n let hook: ReleaseLifecycle | undefined\n let status: ReleaseStatus = 'success'\n let reason: unknown = null\n\n try {\n const ordered = RELEASE_LIFECYCLE_HOOK_LIST.flatMap((hookName) => {\n return compact(\n context.plugins.map((plugin) => {\n let hook = plugin[hookName]\n\n if (!hook) return undefined\n\n hook = typeof hook === 'function' ? { handler: hook } : hook\n\n const { enforce, handler } = hook\n\n const weight = enforce === 'pre' ? -1 : enforce === 'post' ? 1 : 0\n\n return {\n plugin,\n hook: hookName,\n handler,\n weight,\n }\n })\n ).sort((a, b) => a.weight - b.weight)\n })\n\n let handler: (typeof ordered)[number]['handler']\n\n for ({ plugin, hook, handler } of ordered) {\n const label = `${colors.gray(`[${hook}]`)} ${plugin.name}`\n let startedLogging = false\n let timer: NodeJS.Timeout | undefined\n\n /* istanbul ignore next */\n if (!isSilent) {\n timer = setTimeout(() => {\n startedLogging = true\n context.logger.start(label)\n }, 10)\n }\n\n const startTime = performance.now()\n\n try {\n const rollback = await handler(context)\n\n if (typeof rollback === 'function') {\n stack.push(rollback)\n }\n } finally {\n clearTimeout(timer)\n }\n\n const endTime = performance.now()\n const duration = (endTime - startTime) / 1000\n\n /* istanbul ignore next */\n if (startedLogging) {\n const color = duration >= 5 ? colors.red : colors.cyan\n const time = color(`${duration.toFixed(2)}s`)\n\n context.logger.success(`${label} ${time}`)\n }\n }\n } catch (err) {\n status = 'failure'\n reason = err\n\n for (const rollback of stack.toReversed()) {\n try {\n await rollback(reason)\n } catch {}\n }\n\n throw err\n } finally {\n printReleaseReport(\n context,\n generateReleaseReport(context, plugin?.name, hook, status, reason)\n )\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reelout/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Core release lifecycle orchestration.",
|
|
4
5
|
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"imports": {
|
|
7
|
+
"imports": {
|
|
8
|
+
"#*": [
|
|
9
|
+
"./src/*",
|
|
10
|
+
"./src/*.ts",
|
|
11
|
+
"./src/*/index.ts"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
7
14
|
"exports": {
|
|
8
15
|
"./package.json": {
|
|
9
16
|
"default": "./package.json"
|
|
@@ -29,7 +36,24 @@
|
|
|
29
36
|
"build": "vite build --ssr --sourcemap",
|
|
30
37
|
"dev": "vite build --ssr --watch"
|
|
31
38
|
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"consola": "catalog:",
|
|
41
|
+
"conventional-changelog-conventionalcommits": "catalog:",
|
|
42
|
+
"conventional-changelog-writer": "catalog:",
|
|
43
|
+
"conventional-commits-parser": "catalog:",
|
|
44
|
+
"hosted-git-info": "catalog:",
|
|
45
|
+
"mdast-util-from-markdown": "catalog:",
|
|
46
|
+
"mdast-util-to-markdown": "catalog:",
|
|
47
|
+
"minimatch": "catalog:",
|
|
48
|
+
"prettier": "catalog:",
|
|
49
|
+
"semver": "catalog:"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/hosted-git-info": "catalog:",
|
|
53
|
+
"@types/mdast": "catalog:",
|
|
54
|
+
"@types/semver": "catalog:"
|
|
55
|
+
},
|
|
32
56
|
"engines": {
|
|
33
|
-
"node": ">=24.
|
|
57
|
+
"node": ">=24.15"
|
|
34
58
|
}
|
|
35
59
|
}
|
package/src/analyzer.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2026 Fai
|
|
3
|
+
|
|
4
|
+
import type { Package, ReleasePlan, ReleasePlugin } from '#types'
|
|
5
|
+
import {
|
|
6
|
+
analyzePackageChanges,
|
|
7
|
+
promotePrereleaseVersions,
|
|
8
|
+
propagateDependencyUpdates,
|
|
9
|
+
resolvePackageTag,
|
|
10
|
+
sliceUnreleased,
|
|
11
|
+
} from '#workflow'
|
|
12
|
+
import { version } from '../package.json'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Built-in plugin that inspects repository history and produces release plans for packages.
|
|
16
|
+
*
|
|
17
|
+
* Stages:
|
|
18
|
+
* - discover (enforce: post): when promoting, resolve package tags from the repository.
|
|
19
|
+
* - analyze: initialize plans if empty, collect unreleased history, compute package changes,
|
|
20
|
+
* promote prerelease versions when promoting, and propagate dependency updates.
|
|
21
|
+
*/
|
|
22
|
+
const analyzer = (): ReleasePlugin => ({
|
|
23
|
+
name: '@reelout/core/analyzer',
|
|
24
|
+
version,
|
|
25
|
+
discover: {
|
|
26
|
+
enforce: 'post',
|
|
27
|
+
handler: async (context) => {
|
|
28
|
+
if (context.promote) {
|
|
29
|
+
context.packages = await Promise.all(
|
|
30
|
+
context.packages.map(async (pkg): Promise<Package> => ({
|
|
31
|
+
...pkg,
|
|
32
|
+
tag: await resolvePackageTag(context.cwd, pkg),
|
|
33
|
+
}))
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
analyze: async (context) => {
|
|
39
|
+
if (context.plans.length === 0) {
|
|
40
|
+
context.plans = context.packages.map((pkg): ReleasePlan => ({
|
|
41
|
+
package: pkg,
|
|
42
|
+
history: [],
|
|
43
|
+
release: null,
|
|
44
|
+
}))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const history = await sliceUnreleased(context.cwd)
|
|
48
|
+
|
|
49
|
+
context.plans = await analyzePackageChanges(
|
|
50
|
+
context.classifier,
|
|
51
|
+
context.plans,
|
|
52
|
+
history
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if (context.promote) {
|
|
56
|
+
context.plans = await promotePrereleaseVersions(context.plans)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
context.plans = await propagateDependencyUpdates(context.cwd, context.plans)
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
export default analyzer
|
package/src/builder.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2026 Fai
|
|
3
|
+
|
|
4
|
+
import { generateGitTagName } from '#git'
|
|
5
|
+
import type { ReleasePlan, ReleasePlugin } from '#types'
|
|
6
|
+
import { formatError } from '#util'
|
|
7
|
+
import {
|
|
8
|
+
buildReleaseNotes,
|
|
9
|
+
bumpVersion,
|
|
10
|
+
resolvePackageStableTag,
|
|
11
|
+
streamPackageCommits,
|
|
12
|
+
} from '#workflow'
|
|
13
|
+
import { colors } from 'consola/utils'
|
|
14
|
+
import { version } from '../package.json'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Built-in plugin that prepares release metadata for active plans.
|
|
18
|
+
*
|
|
19
|
+
* Stages:
|
|
20
|
+
* - build: compute new package versions, generate git tag names, build changelogs,
|
|
21
|
+
* and attach tag messages (generate from changelog when available).
|
|
22
|
+
*/
|
|
23
|
+
const builder = (): ReleasePlugin => ({
|
|
24
|
+
name: '@reelout/core/builder',
|
|
25
|
+
version,
|
|
26
|
+
build: async (context) => {
|
|
27
|
+
context.plans = await Promise.all(
|
|
28
|
+
context.plans.map(async (plan): Promise<ReleasePlan> => {
|
|
29
|
+
plan = { ...plan }
|
|
30
|
+
|
|
31
|
+
if (!plan.release) return plan
|
|
32
|
+
|
|
33
|
+
plan.release = { ...plan.release }
|
|
34
|
+
|
|
35
|
+
let version: string | null | undefined
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
version = bumpVersion(
|
|
39
|
+
plan.package,
|
|
40
|
+
plan.release.type,
|
|
41
|
+
context.prereleaseLabel
|
|
42
|
+
)
|
|
43
|
+
} catch (err) {
|
|
44
|
+
context.logger.warn(
|
|
45
|
+
colors.yellow(
|
|
46
|
+
`Version could not be determined for package at "${colors.bold(plan.package.dir)}": ${formatError(err)}`
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (version) {
|
|
52
|
+
plan.release.version = version
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let tagName: string | null | undefined
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
tagName = generateGitTagName(
|
|
59
|
+
plan.package.entry,
|
|
60
|
+
plan.package.name,
|
|
61
|
+
plan.release.version
|
|
62
|
+
)
|
|
63
|
+
} catch (err) {
|
|
64
|
+
context.logger.warn(
|
|
65
|
+
colors.yellow(
|
|
66
|
+
`Tag could not be determined for package at "${colors.bold(plan.package.dir)}": ${formatError(err)}`
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let tagMessage: string | undefined
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
if (tagName) {
|
|
75
|
+
if (context.promote && plan.history.length === 0) {
|
|
76
|
+
const previousTag = (
|
|
77
|
+
await resolvePackageStableTag(context.cwd, plan.package)
|
|
78
|
+
)?.name
|
|
79
|
+
|
|
80
|
+
const history = streamPackageCommits(context.cwd, plan.package, {
|
|
81
|
+
until: previousTag,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
tagMessage = await buildReleaseNotes(context, history)
|
|
85
|
+
} else {
|
|
86
|
+
tagMessage = await buildReleaseNotes(context, plan.history)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
|
|
91
|
+
if (tagName) {
|
|
92
|
+
plan.release.tag = {
|
|
93
|
+
name: tagName,
|
|
94
|
+
message: tagMessage,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return plan
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
export default builder
|