'use strict'; // src/server/handlers/version-helpers.ts var DEFAULT_MAX_VERSIONS = 50; var MCP_CLIENT_SNAPSHOT_CONFIG_FIELDS = ["name", "description", "servers"]; function deepEqual(a, b) { if (a === b) return true; if (a == null || b == null) return a === b; if (typeof a !== typeof b) return false; if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; return a.every((item, index) => deepEqual(item, b[index])); } if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } if (typeof a === "object" && typeof b === "object") { const aObj = a; const bObj = b; const aKeys = Object.keys(aObj); const bKeys = Object.keys(bObj); if (aKeys.length !== bKeys.length) return false; return aKeys.every((key) => Object.prototype.hasOwnProperty.call(bObj, key) && deepEqual(aObj[key], bObj[key])); } return false; } function generateVersionId() { return crypto.randomUUID(); } function extractConfigFromVersion(version, snapshotConfigFields) { const config = {}; for (const field of snapshotConfigFields) { if (field in version) { config[field] = version[field]; } } return config; } function calculateChangedFields(previous, current) { if (!previous) { return Object.keys(current); } const changedFields = []; const allKeys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]); for (const key of allKeys) { if (key === "updatedAt" || key === "createdAt") { continue; } const prevValue = previous[key]; const currValue = current[key]; if (!deepEqual(prevValue, currValue)) { changedFields.push(key); } } return changedFields; } function computeVersionDiffs(fromConfig, toConfig) { const diffs = []; const allKeys = /* @__PURE__ */ new Set([...Object.keys(fromConfig), ...Object.keys(toConfig)]); for (const key of allKeys) { if (key === "updatedAt" || key === "createdAt") { continue; } const prevValue = fromConfig[key]; const currValue = toConfig[key]; if (!deepEqual(prevValue, currValue)) { diffs.push({ field: key, previousValue: prevValue, currentValue: currValue }); } } return diffs; } function isVersionNumberConflictError(error) { if (error instanceof Error) { const message = error.message.toLowerCase(); return message.includes("unique") && message.includes("constraint") || message.includes("duplicate key") || message.includes("unique_violation") || message.includes("sqlite_constraint_unique") || message.includes("versionnumber"); } return false; } async function enforceRetentionLimit(store, parentId, parentIdField, activeVersionId, maxVersions = DEFAULT_MAX_VERSIONS) { const { total } = await store.listVersions({ [parentIdField]: parentId, perPage: 1 }); if (total <= maxVersions) { return { deletedCount: 0 }; } const versionsToDelete = total - maxVersions; const { versions: oldestVersions } = await store.listVersions({ [parentIdField]: parentId, perPage: versionsToDelete + 1, orderBy: { field: "versionNumber", direction: "ASC" } }); let deletedCount = 0; for (const version of oldestVersions) { if (deletedCount >= versionsToDelete) { break; } if (version.id === activeVersionId) { continue; } await store.deleteVersion(version.id); deletedCount++; } return { deletedCount }; } async function createVersionWithRetry(store, parentId, parentIdField, snapshotConfig, changedFields, options = {}) { const { changeMessage, maxRetries = 3 } = options; let lastError; for (let attempt = 0; attempt < maxRetries; attempt++) { try { const latestVersion = await store.getLatestVersion(parentId); const versionNumber = latestVersion ? latestVersion.versionNumber + 1 : 1; const versionId = generateVersionId(); await store.createVersion({ ...snapshotConfig, id: versionId, [parentIdField]: parentId, versionNumber, changedFields, changeMessage }); return { versionId, versionNumber }; } catch (error) { lastError = error; if (isVersionNumberConflictError(error) && attempt < maxRetries - 1) { await new Promise((resolve) => setTimeout(resolve, 10 * (attempt + 1))); continue; } throw error; } } throw lastError; } async function handleAutoVersioning(store, parentId, parentIdField, snapshotConfigFields, existingEntity, updatedEntity, configFields, options) { if (!configFields || Object.keys(configFields).length === 0) { return { entity: updatedEntity, versionCreated: false }; } const versionToCompare = await store.getLatestVersion(parentId); const previousConfig = versionToCompare ? extractConfigFromVersion(versionToCompare, snapshotConfigFields) : null; const changedFields = calculateChangedFields(previousConfig, configFields); if (changedFields.length === 0) { return { entity: updatedEntity, versionCreated: false }; } const fullConfig = previousConfig ? { ...previousConfig } : {}; for (const [key, value] of Object.entries(configFields)) { fullConfig[key] = value === null ? void 0 : value; } const { versionId } = await createVersionWithRetry(store, parentId, parentIdField, fullConfig, changedFields, { changeMessage: options?.changeMessage ?? "Auto-saved after edit" }); const activeVersionId = existingEntity.activeVersionId || versionId; await enforceRetentionLimit(store, parentId, parentIdField, activeVersionId); return { entity: updatedEntity, versionCreated: true }; } exports.DEFAULT_MAX_VERSIONS = DEFAULT_MAX_VERSIONS; exports.MCP_CLIENT_SNAPSHOT_CONFIG_FIELDS = MCP_CLIENT_SNAPSHOT_CONFIG_FIELDS; exports.calculateChangedFields = calculateChangedFields; exports.computeVersionDiffs = computeVersionDiffs; exports.createVersionWithRetry = createVersionWithRetry; exports.deepEqual = deepEqual; exports.enforceRetentionLimit = enforceRetentionLimit; exports.extractConfigFromVersion = extractConfigFromVersion; exports.generateVersionId = generateVersionId; exports.handleAutoVersioning = handleAutoVersioning; //# sourceMappingURL=chunk-2XZ2466F.cjs.map //# sourceMappingURL=chunk-2XZ2466F.cjs.map