marketing-post-generator-mcp
Version:
A powerful MCP server for AI-powered marketing blog post generation with Claude integration
290 lines • 10.6 kB
JavaScript
import { VersionError, } from './types.js';
/**
* Manages versioning for registry entries with semantic versioning support
*/
export class VersionManager {
migrationHandlers = new Map();
compatibilityRules = new Map();
constructor() {
// Set up default compatibility rules
this.setupDefaultCompatibilityRules();
}
/**
* Parse version string to VersionInfo object
*/
parseVersion(versionString) {
const versionPattern = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?$/;
const match = versionString.match(versionPattern);
if (!match) {
throw new VersionError(`Invalid version format: ${versionString}. Expected format: x.y.z or x.y.z-prerelease`);
}
const result = {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
};
if (match[4]) {
result.prerelease = match[4];
}
return result;
}
/**
* Format VersionInfo object to version string
*/
formatVersion(version) {
let versionString = `${version.major}.${version.minor}.${version.patch}`;
if (version.prerelease) {
versionString += `-${version.prerelease}`;
}
return versionString;
}
/**
* Compare two versions
* Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
*/
compareVersions(v1, v2) {
// Compare major version
if (v1.major !== v2.major) {
return v1.major - v2.major;
}
// Compare minor version
if (v1.minor !== v2.minor) {
return v1.minor - v2.minor;
}
// Compare patch version
if (v1.patch !== v2.patch) {
return v1.patch - v2.patch;
}
// Compare prerelease
if (v1.prerelease && v2.prerelease) {
return v1.prerelease.localeCompare(v2.prerelease);
}
else if (v1.prerelease && !v2.prerelease) {
return -1; // prerelease is less than release
}
else if (!v1.prerelease && v2.prerelease) {
return 1; // release is greater than prerelease
}
return 0; // versions are equal
}
/**
* Check if a version satisfies a range requirement
*/
satisfiesRange(version, range) {
try {
const rangePattern = /^([~^]?)(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?$/;
const match = range.match(rangePattern);
if (!match) {
throw new VersionError(`Invalid version range: ${range}`);
}
const operator = match[1] || '';
const requiredVersion = {
major: parseInt(match[2], 10),
minor: parseInt(match[3], 10),
patch: parseInt(match[4], 10),
prerelease: match[5] || undefined,
};
switch (operator) {
case '~': // Compatible within patch version
return this.isCompatiblePatch(version, requiredVersion);
case '^': // Compatible within minor version
return this.isCompatibleMinor(version, requiredVersion);
default: // Exact version match
return this.compareVersions(version, requiredVersion) === 0;
}
}
catch (error) {
throw new VersionError(`Error checking version range: ${error}`);
}
}
/**
* Check compatibility between two versions
*/
checkCompatibility(current, required) {
const compatible = this.isCompatible(current, required);
const migrationRequired = !compatible && this.isMigrationPossible(current, required);
const result = {
compatible,
requiredVersion: required,
currentVersion: current,
migrationRequired,
};
if (migrationRequired) {
result.migrationPath = this.generateMigrationPath(current, required);
}
return result;
}
/**
* Increment version based on change type
*/
incrementVersion(currentVersion, changeType, prereleaseIdentifier) {
const newVersion = { ...currentVersion };
switch (changeType) {
case 'major':
newVersion.major += 1;
newVersion.minor = 0;
newVersion.patch = 0;
delete newVersion.prerelease;
break;
case 'minor':
newVersion.minor += 1;
newVersion.patch = 0;
delete newVersion.prerelease;
break;
case 'patch':
newVersion.patch += 1;
delete newVersion.prerelease;
break;
case 'prerelease':
if (!prereleaseIdentifier) {
throw new VersionError('Prerelease identifier is required for prerelease version increment');
}
newVersion.prerelease = prereleaseIdentifier;
break;
default:
throw new VersionError(`Invalid change type: ${changeType}`);
}
return newVersion;
}
/**
* Register a migration handler for a specific version transition
*/
registerMigrationHandler(fromVersion, toVersion, handler) {
const key = `${fromVersion}->${toVersion}`;
this.migrationHandlers.set(key, handler);
}
/**
* Execute migration for a registry entry
*/
async migrateEntry(entry, targetVersion) {
const currentVersionString = this.formatVersion(entry.version);
const targetVersionString = this.formatVersion(targetVersion);
const migrationKey = `${currentVersionString}->${targetVersionString}`;
const handler = this.migrationHandlers.get(migrationKey);
if (!handler) {
throw new VersionError(`No migration handler found for ${migrationKey}`);
}
try {
const migratedEntry = await handler(entry);
migratedEntry.version = targetVersion;
migratedEntry.updatedAt = new Date();
return migratedEntry;
}
catch (error) {
throw new VersionError(`Migration failed from ${currentVersionString} to ${targetVersionString}: ${error}`);
}
}
/**
* Register custom compatibility rule
*/
registerCompatibilityRule(ruleKey, rule) {
this.compatibilityRules.set(ruleKey, rule);
}
/**
* Apply custom compatibility rule
*/
applyCompatibilityRule(ruleKey, v1, v2) {
const rule = this.compatibilityRules.get(ruleKey);
if (!rule) {
throw new VersionError(`Compatibility rule '${ruleKey}' not found`);
}
return rule(v1, v2);
}
/**
* Get the latest version from an array of versions
*/
getLatestVersion(versions) {
if (versions.length === 0) {
return null;
}
return versions.reduce((latest, current) => {
return this.compareVersions(current, latest) > 0 ? current : latest;
});
}
/**
* Filter versions by compatibility with a base version
*/
getCompatibleVersions(baseVersion, versions) {
return versions.filter((version) => this.isCompatible(version, baseVersion));
}
/**
* Check if version is stable (no prerelease)
*/
isStableVersion(version) {
return !version.prerelease;
}
/**
* Check if version is prerelease
*/
isPrereleaseVersion(version) {
return !!version.prerelease;
}
// Private helper methods
setupDefaultCompatibilityRules() {
// Backward compatibility rule
this.compatibilityRules.set('backward', (current, required) => {
return this.compareVersions(current, required) >= 0;
});
// Strict compatibility rule
this.compatibilityRules.set('strict', (current, required) => {
return this.compareVersions(current, required) === 0;
});
// Semantic compatibility rule
this.compatibilityRules.set('semantic', (current, required) => {
return this.isSemanticCompatible(current, required);
});
}
isCompatible(current, required) {
// Apply semantic versioning compatibility rules
return this.isSemanticCompatible(current, required);
}
isSemanticCompatible(current, required) {
// Major version must match for compatibility
if (current.major !== required.major) {
return false;
}
// Current version must be greater than or equal to required
return this.compareVersions(current, required) >= 0;
}
isCompatiblePatch(version, required) {
return (version.major === required.major &&
version.minor === required.minor &&
version.patch >= required.patch);
}
isCompatibleMinor(version, required) {
if (version.major !== required.major) {
return false;
}
if (version.minor > required.minor) {
return true;
}
if (version.minor === required.minor) {
return version.patch >= required.patch;
}
return false;
}
isMigrationPossible(current, required) {
// Migration is possible if versions are in the same major version line
// or if there's a registered migration handler
const currentVersionString = this.formatVersion(current);
const requiredVersionString = this.formatVersion(required);
const migrationKey = `${currentVersionString}->${requiredVersionString}`;
return (this.migrationHandlers.has(migrationKey) ||
(current.major === required.major && this.compareVersions(current, required) < 0));
}
generateMigrationPath(current, target) {
const path = [];
const currentVersionString = this.formatVersion(current);
const targetVersionString = this.formatVersion(target);
// Simple migration path - direct migration if possible
if (this.migrationHandlers.has(`${currentVersionString}->${targetVersionString}`)) {
path.push(`${currentVersionString}->${targetVersionString}`);
}
else {
// Multi-step migration would require more complex logic
path.push(`Complex migration required from ${currentVersionString} to ${targetVersionString}`);
}
return path;
}
}
//# sourceMappingURL=VersionManager.js.map