{"version":3,"sources":["../src/server/handlers/version-helpers.ts"],"names":[],"mappings":"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","file":"chunk-2XZ2466F.cjs","sourcesContent":["/**\n * Generic version management helpers shared across all versioned editor primitives.\n * These utilities are domain-agnostic and work with any VersionedStorageDomain.\n */\n\n// Default maximum versions per entity (can be made configurable in the future)\nexport const DEFAULT_MAX_VERSIONS = 50;\n\n/** Snapshot config fields for MCP client versioning. */\nexport const MCP_CLIENT_SNAPSHOT_CONFIG_FIELDS = ['name', 'description', 'servers'] as const;\n\n/**\n * Deep equality comparison for comparing two values.\n * Handles primitives, arrays, objects, and Date instances.\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return a === b;\n if (typeof a !== typeof b) return false;\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((item, index) => deepEqual(item, b[index]));\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime();\n }\n\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record;\n const bObj = b as Record;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(key => Object.prototype.hasOwnProperty.call(bObj, key) && deepEqual(aObj[key], bObj[key]));\n }\n\n return false;\n}\n\n/**\n * Generates a unique ID for a version using crypto.randomUUID()\n */\nexport function generateVersionId(): string {\n return crypto.randomUUID();\n}\n\n/**\n * Extracts snapshot config fields from a version record.\n * Strips version-metadata fields (id, FK field, versionNumber, changedFields, changeMessage, createdAt).\n */\nexport function extractConfigFromVersion(\n version: Record,\n snapshotConfigFields: readonly string[],\n): Record {\n const config: Record = {};\n for (const field of snapshotConfigFields) {\n if (field in version) {\n config[field] = version[field];\n }\n }\n return config;\n}\n\n/**\n * Compares two snapshots and returns an array of field names that changed.\n */\nexport function calculateChangedFields(\n previous: Record | null | undefined,\n current: Record,\n): string[] {\n if (!previous) {\n return Object.keys(current);\n }\n\n const changedFields: string[] = [];\n const allKeys = new Set([...Object.keys(previous), ...Object.keys(current)]);\n\n for (const key of allKeys) {\n if (key === 'updatedAt' || key === 'createdAt') {\n continue;\n }\n\n const prevValue = previous[key];\n const currValue = current[key];\n\n if (!deepEqual(prevValue, currValue)) {\n changedFields.push(key);\n }\n }\n\n return changedFields;\n}\n\n/**\n * Computes detailed diffs between two config snapshots.\n */\nexport function computeVersionDiffs(\n fromConfig: Record,\n toConfig: Record,\n): Array<{ field: string; previousValue: unknown; currentValue: unknown }> {\n const diffs: Array<{ field: string; previousValue: unknown; currentValue: unknown }> = [];\n const allKeys = new Set([...Object.keys(fromConfig), ...Object.keys(toConfig)]);\n\n for (const key of allKeys) {\n if (key === 'updatedAt' || key === 'createdAt') {\n continue;\n }\n\n const prevValue = fromConfig[key];\n const currValue = toConfig[key];\n\n if (!deepEqual(prevValue, currValue)) {\n diffs.push({\n field: key,\n previousValue: prevValue,\n currentValue: currValue,\n });\n }\n }\n\n return diffs;\n}\n\n/**\n * Generic store interface for version management operations.\n * Works with any versioned storage domain.\n */\nexport interface VersionedStoreInterface {\n getLatestVersion: (parentId: string) => Promise<{ id: string; versionNumber: number } | null>;\n getVersion: (id: string) => Promise<{ id: string; versionNumber: number } | null>;\n createVersion: (params: Record) => Promise;\n update: (params: Record) => Promise;\n listVersions: (params: Record) => Promise<{\n versions: Array<{ id: string; versionNumber: number }>;\n total: number;\n page: number;\n perPage: number | false;\n hasMore: boolean;\n }>;\n deleteVersion: (id: string) => Promise;\n}\n\n/**\n * Determines if an error is a unique constraint violation on versionNumber.\n */\nfunction isVersionNumberConflictError(error: unknown): boolean {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n return (\n (message.includes('unique') && message.includes('constraint')) ||\n message.includes('duplicate key') ||\n message.includes('unique_violation') ||\n message.includes('sqlite_constraint_unique') ||\n message.includes('versionnumber')\n );\n }\n return false;\n}\n\n/**\n * Enforces version retention limit by deleting oldest versions that exceed the maximum.\n * Never deletes the active version.\n */\nexport async function enforceRetentionLimit(\n store: Pick,\n parentId: string,\n parentIdField: string,\n activeVersionId: string | undefined | null,\n maxVersions: number = DEFAULT_MAX_VERSIONS,\n): Promise<{ deletedCount: number }> {\n const { total } = await store.listVersions({ [parentIdField]: parentId, perPage: 1 });\n\n if (total <= maxVersions) {\n return { deletedCount: 0 };\n }\n\n const versionsToDelete = total - maxVersions;\n\n const { versions: oldestVersions } = await store.listVersions({\n [parentIdField]: parentId,\n perPage: versionsToDelete + 1,\n orderBy: { field: 'versionNumber', direction: 'ASC' },\n });\n\n let deletedCount = 0;\n for (const version of oldestVersions) {\n if (deletedCount >= versionsToDelete) {\n break;\n }\n\n if (version.id === activeVersionId) {\n continue;\n }\n\n await store.deleteVersion(version.id);\n deletedCount++;\n }\n\n return { deletedCount };\n}\n\n/**\n * Creates a new version with retry logic for race condition handling.\n */\nexport async function createVersionWithRetry(\n store: Pick,\n parentId: string,\n parentIdField: string,\n snapshotConfig: Record,\n changedFields: string[],\n options: {\n changeMessage?: string;\n maxRetries?: number;\n } = {},\n): Promise<{ versionId: string; versionNumber: number }> {\n const { changeMessage, maxRetries = 3 } = options;\n let lastError: unknown;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const latestVersion = await store.getLatestVersion(parentId);\n const versionNumber = latestVersion ? latestVersion.versionNumber + 1 : 1;\n const versionId = generateVersionId();\n\n await store.createVersion({\n ...snapshotConfig,\n id: versionId,\n [parentIdField]: parentId,\n versionNumber,\n changedFields,\n changeMessage,\n });\n\n return { versionId, versionNumber };\n } catch (error) {\n lastError = error;\n\n if (isVersionNumberConflictError(error) && attempt < maxRetries - 1) {\n await new Promise(resolve => setTimeout(resolve, 10 * (attempt + 1)));\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError;\n}\n\n/**\n * Handles auto-versioning after an entity update.\n * Creates a new version if config fields changed, but does NOT update activeVersionId.\n */\nexport async function handleAutoVersioning(\n store: VersionedStoreInterface,\n parentId: string,\n parentIdField: string,\n snapshotConfigFields: readonly string[],\n existingEntity: TEntity & { activeVersionId?: string },\n updatedEntity: TEntity,\n configFields?: Record,\n options?: { changeMessage?: string },\n): Promise<{ entity: TEntity; versionCreated: boolean }> {\n if (!configFields || Object.keys(configFields).length === 0) {\n return { entity: updatedEntity, versionCreated: false };\n }\n\n // Always compare against the latest version (not the active/published one).\n // This ensures each draft save is diffed against the last edit, so intermediate\n // draft changes are never silently reverted to the published baseline.\n const versionToCompare = await store.getLatestVersion(parentId);\n\n const previousConfig = versionToCompare\n ? extractConfigFromVersion(versionToCompare as unknown as Record, snapshotConfigFields)\n : null;\n\n const changedFields = calculateChangedFields(previousConfig, configFields);\n\n if (changedFields.length === 0) {\n return { entity: updatedEntity, versionCreated: false };\n }\n\n const fullConfig: Record = previousConfig ? { ...previousConfig } : {};\n for (const [key, value] of Object.entries(configFields)) {\n fullConfig[key] = value === null ? undefined : value;\n }\n\n const { versionId } = await createVersionWithRetry(store, parentId, parentIdField, fullConfig, changedFields, {\n changeMessage: options?.changeMessage ?? 'Auto-saved after edit',\n });\n\n // Do NOT update activeVersionId here — the new version stays as a draft.\n // activeVersionId is only updated when the user explicitly publishes/activates a version.\n\n const activeVersionId = existingEntity.activeVersionId || versionId;\n await enforceRetentionLimit(store, parentId, parentIdField, activeVersionId);\n\n return { entity: updatedEntity, versionCreated: true };\n}\n"]}