doba

Path Finding

Automatic path discovery between schemas using BFS and Dijkstra.

Path Finding

When no direct migration exists between two schemas, doba automatically finds the optimal path through the migration graph.

How it works

Given these migrations:

const registry = createRegistry({
  schemas: { legacy, database, frontend, ai },
  migrations: {
    'legacy->database': (data) => {
      /* upgrade */
    },
    'database->frontend': (data) => {
      /* strip sensitive */
    },
    'frontend->ai': (data) => {
      /* flatten */
    },
  },
})

Transforming from legacy to ai automatically chains through every intermediate schema:

legacy
upgrade
database
strip
frontend
flatten
ai
const result = await registry.transform(legacyData, 'legacy', 'ai')

if (result.ok) {
  console.log(result.meta.path)
  // ["legacy", "database", "frontend", "ai"]
}
ok: true
value: { id: "...", email: "...", isAdmin: true }
meta: {
path: ["legacy", "database", "frontend", "ai"]
steps: 3
}

Algorithm selection

doba automatically picks the right algorithm based on your migrations:

  • BFS: all migrations have equal cost (no cost, preferred, or deprecated options). Finds the shortest path by hop count.
  • Dijkstra: any migration has a custom cost, is marked preferred (cost 0), or deprecated (cost 1000). Finds the lowest-cost path.

You don't need to choose. The registry detects whether weighted edges exist at construction time.

Path strategies

shortest (default)

Finds the optimal path using BFS or Dijkstra. Handles graphs with 10k+ edges efficiently.

createRegistry({
  pathStrategy: 'shortest',
  // ...
})

direct

Only uses direct migrations. Returns an error if no direct source->target migration exists.

createRegistry({
  pathStrategy: 'direct',
  // ...
})

You can also override the strategy per-transform:

await registry.transform(data, 'a', 'b', {
  pathStrategy: 'direct',
})

Explicit paths

Override automatic path finding by specifying the exact route:

await registry.transform(data, 'a', 'd', {
  path: ['a', 'b', 'c', 'd'],
})

Inspecting paths

Use findPath to preview what route doba would take without running a transform:

const path = registry.findPath('legacy', 'ai')
// ["legacy", "database", "frontend", "ai"] or null

Returns null if no path exists between the two schemas.

Cost system

Migration costs control which paths the Dijkstra algorithm prefers:

OptionCostEffect
(default)1Standard edge weight
preferred: true0Always chosen over alternatives
deprecated: true1000Avoided unless no other path exists
cost: NNExplicit weight (overrides above)

See Weighted Migrations for a full example.

Path finding runs in O(V + E) time (BFS) or O((V + E) log V) time (Dijkstra) where V is the number of schemas and E is the number of migrations.

On this page