Versioning API

Entity-level version control: save snapshots, restore previous states, compare changes, and implement undo/redo.

Overview

The Versioning API provides Git-like version control for individual entities. Every change can be tracked, compared, and reverted without affecting other entities.

versioning-quickstart.js
import { Brainy, NounType } from '@soulcraft/brainy'

const brain = new Brainy()
await brain.init()

// Create an entity
const userId = await brain.add({
  data: "John Smith",
  type: NounType.Person,
  metadata: { role: "developer", level: 1 }
})

// Save initial version
await brain.versions.save(userId, { tag: 'v1.0' })

// Make changes
await brain.update({ id: userId, metadata: { level: 2 } })
await brain.versions.save(userId, { tag: 'v1.1' })

// Oops! Restore to v1.0
await brain.versions.restore(userId, 'v1.0')

Methods

save(entityId, options?)

Create a version snapshot of the current entity state.

brain.versions.save(entityId: string, options?: SaveOptions): Promise<EntityVersion>
Option Type Description
tag string Version tag (e.g., 'v1.0', 'release')
description string Description of changes
author string Who made the change
createCommit boolean Also create a workspace commit
save-examples.js
// Simple save
const version = await brain.versions.save(userId)

// Save with tag and description
const tagged = await brain.versions.save(userId, {
  tag: 'v2.0',
  description: 'Promoted to senior developer',
  author: 'admin@example.com'
})

// Save and create workspace commit
const milestone = await brain.versions.save(userId, {
  tag: 'milestone-1',
  createCommit: true,
  commitMessage: 'Milestone 1 complete'
})

list(entityId, options?)

List all versions of an entity, sorted by version number (newest first).

list-examples.js
// Get all versions
const versions = await brain.versions.list(userId)
console.log(`Found ${versions.length} versions`)

// Get last 10 versions
const recent = await brain.versions.list(userId, { limit: 10 })

// Get tagged versions only
const tagged = await brain.versions.list(userId, { tag: 'v*' })

// Get versions from last 30 days
const lastMonth = await brain.versions.list(userId, {
  startDate: Date.now() - 30 * 24 * 60 * 60 * 1000
})

restore(entityId, version, options?)

Restore an entity to a specific version. Overwrites current state.

restore-examples.js
// Restore to version number
await brain.versions.restore(userId, 5)

// Restore by tag
await brain.versions.restore(userId, 'v1.0')

// Restore with safety snapshot (for undo)
await brain.versions.restore(userId, 5, {
  createSnapshot: true,
  snapshotTag: 'before-restore'
})

compare(entityId, fromVersion, toVersion)

Generate a deep diff showing changes between two versions.

compare-examples.js
// Compare version 2 to version 5
const diff = await brain.versions.compare(userId, 2, 5)

console.log(`Added fields: ${diff.added.length}`)
console.log(`Removed fields: ${diff.removed.length}`)
console.log(`Modified fields: ${diff.modified.length}`)

// Diff structure
// {
//   added: [{ path: 'metadata.skills', value: ['typescript'] }],
//   removed: [{ path: 'metadata.temporary', oldValue: true }],
//   modified: [{ path: 'metadata.level', from: 1, to: 3 }],
//   typeChanged: []
// }

// Compare by tags
const tagDiff = await brain.versions.compare(userId, 'v1.0', 'v2.0')

undo(entityId)

Revert to the previous version. Creates a safety snapshot automatically.

undo-example.js
// Made a mistake? Just undo!
await brain.update({ id: userId, metadata: { wrongData: true } })

// Undo the change
const restored = await brain.versions.undo(userId)

if (restored) {
  console.log(`Restored to version ${restored.version}`)
} else {
  console.log('No previous version to restore')
}

prune(entityId, options)

Remove old versions based on retention policy. Preserves tagged versions by default.

prune-examples.js
// Keep last 10 versions, delete rest
const result = await brain.versions.prune(userId, {
  keepRecent: 10
})
console.log(`Deleted ${result.deleted} versions, kept ${result.kept}`)

// Keep last 30 days, preserve tagged versions
await brain.versions.prune(userId, {
  keepAfter: Date.now() - 30 * 24 * 60 * 60 * 1000,
  keepTagged: true
})

// Dry run - preview what would be deleted
const preview = await brain.versions.prune(userId, {
  keepRecent: 5,
  dryRun: true
})
console.log(`Would delete ${preview.deleted} versions`)

Other Methods

other-methods.js
// Get specific version
const v5 = await brain.versions.getVersion(userId, 5)

// Get version by tag
const release = await brain.versions.getVersionByTag(userId, 'v1.0')

// Get latest version
const latest = await brain.versions.getLatest(userId)

// Preview version content without restoring
const oldData = await brain.versions.getContent(userId, 5)

// Get version count
const count = await brain.versions.count(userId)

// Check if entity has versions
if (await brain.versions.hasVersions(userId)) {
  console.log('Entity is versioned')
}

// Get version history summary
const summary = await brain.versions.history(userId)
// { total: 15, oldest: {...}, newest: {...}, tagged: 3 }

// Tag an existing version
await brain.versions.tag(userId, 5, 'stable')

// Compare current state with a version
const changes = await brain.versions.diffWithCurrent(userId, 'v1.0')

// Clear all versions (DANGER!)
const deleted = await brain.versions.clear(userId)

Use Cases

Document Editor with Undo/Redo

document-editor.js
async function saveDocument(docId, content) {
  // Save current state before overwriting
  await brain.versions.save(docId)

  // Update the document
  await brain.update({
    id: docId,
    data: content,
    metadata: { lastModified: Date.now() }
  })
}

async function undoDocument(docId) {
  return brain.versions.undo(docId)
}

async function getDocumentHistory(docId) {
  return brain.versions.list(docId, { limit: 50 })
}

Configuration Management

config-management.js
async function updateConfig(configId, changes, releaseTag) {
  // Save with tag for releases
  await brain.versions.save(configId, {
    tag: releaseTag,
    description: `Config update: ${Object.keys(changes).join(', ')}`
  })

  await brain.update({ id: configId, metadata: changes })
}

async function rollbackConfig(configId, toTag) {
  // Restore with safety snapshot
  await brain.versions.restore(configId, toTag, {
    createSnapshot: true,
    snapshotTag: `rollback-from-${Date.now()}`
  })
}

See Also

Next: Branching & Time Travel →