// 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) {
  if (!configFields || Object.keys(configFields).length === 0) {
    return { entity: updatedEntity, versionCreated: false };
  }
  const activeVersion = existingEntity.activeVersionId ? await store.getVersion(existingEntity.activeVersionId) : null;
  const versionToCompare = activeVersion || 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: "Auto-saved after edit"
  });
  const activeVersionId = existingEntity.activeVersionId || versionId;
  await enforceRetentionLimit(store, parentId, parentIdField, activeVersionId);
  return { entity: updatedEntity, versionCreated: true };
}

export { DEFAULT_MAX_VERSIONS, MCP_CLIENT_SNAPSHOT_CONFIG_FIELDS, calculateChangedFields, computeVersionDiffs, createVersionWithRetry, deepEqual, enforceRetentionLimit, extractConfigFromVersion, generateVersionId, handleAutoVersioning };
//# sourceMappingURL=chunk-Z2BWJX7H.js.map
//# sourceMappingURL=chunk-Z2BWJX7H.js.map