refactor(8.0): rename BackupData → PortableGraph (the type is interchange, not a backup)

The export()/import() document type was named BackupData, but it is not a backup:
it is the portable, versioned, partial-or-whole interchange representation of a
graph (entities + relations + optional vectors/blobs) — the unit Brainy exports
for transport between instances, versions, and products, and the payload other
artifacts embed. The actual backup is persist()/load() (the native whole-brain,
generation-preserving snapshot), so "Backup*" actively collided with that concept.

Rename every developer-visible symbol, file, doc, JSDoc and comment:
- BackupData→PortableGraph, BackupEntity→PortableGraphEntity,
  BackupRelation→PortableGraphRelation, BackupReader/Writer→PortableGraphReader/Writer,
  BackupValidation→PortableGraphValidation, isBackupData→isPortableGraph,
  validateBackup→validatePortableGraph, BACKUP_FORMAT[_VERSION]→PORTABLE_GRAPH_FORMAT[_VERSION].
- src/db/backup.ts → src/db/portableGraph.ts; test → db-portable-graph.test.ts.
- Guide, api/README, RELEASES updated.

The on-the-wire `format` tag is renamed 'brainy-backup' → 'brainy-portable-graph':
the format was introduced in 7.32.0 and has not been adopted by any consumer, so
there are no stored documents to stay compatible with — a clean rename beats
carrying a legacy tag. No deprecated aliases (nothing to alias). 7.x shipped the
same rename as 7.32.2.

1471 unit green; build clean; db-portable-graph.test.ts 17/17.
This commit is contained in:
David Snelling 2026-06-19 12:09:19 -07:00
parent 3a3aa43b3a
commit 373a48122d
8 changed files with 156 additions and 145 deletions

View file

@ -17,10 +17,10 @@ import * as path from 'node:path'
import { Brainy } from '../../../src/brainy'
import { createTestConfig } from '../../helpers/test-factory'
import { NounType, VerbType } from '../../../src/types/graphTypes'
import { validateBackup } from '../../../src/db/backup'
import type { BackupData } from '../../../src/db/backup'
import { validatePortableGraph } from '../../../src/db/portableGraph'
import type { PortableGraph } from '../../../src/db/portableGraph'
describe('8.0 portable graph export/import (BackupData v1)', () => {
describe('8.0 portable graph export/import (PortableGraph v1)', () => {
let brain: Brainy
beforeEach(async () => {
@ -33,13 +33,13 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
})
describe('format + round-trip', () => {
it('brain.export() produces a versioned BackupData document', async () => {
it('brain.export() produces a versioned PortableGraph document', async () => {
const a = await brain.add({ data: 'Alice', type: NounType.Person, subtype: 'employee' })
const b = await brain.add({ data: 'Acme', type: NounType.Organization, subtype: 'vendor' })
await brain.relate({ from: a, to: b, type: VerbType.WorksWith, subtype: 'full-time' })
const backup = await brain.export()
expect(backup.format).toBe('brainy-backup')
expect(backup.format).toBe('brainy-portable-graph')
expect(backup.formatVersion).toBe(1)
expect(backup.entities.map((e) => e.id).sort()).toEqual([a, b].sort())
expect(backup.relations).toHaveLength(1)
@ -169,8 +169,8 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
describe('import validation', () => {
it('rejects a newer formatVersion', async () => {
const future: BackupData = {
format: 'brainy-backup',
const future: PortableGraph = {
format: 'brainy-portable-graph',
formatVersion: 999,
brainyVersion: 'x',
createdAt: new Date().toISOString(),
@ -203,9 +203,9 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
})
})
describe('validateBackup() — dry-run check', () => {
const valid = (): BackupData => ({
format: 'brainy-backup',
describe('validatePortableGraph() — dry-run check', () => {
const valid = (): PortableGraph => ({
format: 'brainy-portable-graph',
formatVersion: 1,
brainyVersion: 'x',
createdAt: new Date().toISOString(),
@ -216,19 +216,19 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
})
it('accepts a well-formed backup', () => {
const r = validateBackup(valid())
const r = validatePortableGraph(valid())
expect(r.valid).toBe(true)
expect(r.errors).toHaveLength(0)
})
it('rejects a non-BackupData value', () => {
const r = validateBackup({ entities: [] })
it('rejects a non-PortableGraph value', () => {
const r = validatePortableGraph({ entities: [] })
expect(r.valid).toBe(false)
expect(r.errors[0]).toMatch(/BackupData/)
expect(r.errors[0]).toMatch(/PortableGraph/)
})
it('rejects a newer formatVersion', () => {
const r = validateBackup({ ...valid(), formatVersion: 999 })
const r = validatePortableGraph({ ...valid(), formatVersion: 999 })
expect(r.valid).toBe(false)
expect(r.errors.some((e) => /formatVersion/.test(e))).toBe(true)
})
@ -239,7 +239,7 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
{ id: 'dup', type: NounType.Thing },
{ id: 'dup', type: NounType.Thing }
]
const r = validateBackup(b)
const r = validatePortableGraph(b)
expect(r.valid).toBe(false)
expect(r.errors.some((e) => /duplicate/.test(e))).toBe(true)
})
@ -247,7 +247,7 @@ describe('8.0 portable graph export/import (BackupData v1)', () => {
it('warns (not errors) on a relation endpoint missing from entities', () => {
const b = valid()
b.relations = [{ id: 'r1', from: 'a', to: 'missing', type: VerbType.RelatedTo }]
const r = validateBackup(b)
const r = validatePortableGraph(b)
expect(r.valid).toBe(true) // a dangling endpoint is a warning, not a hard error
expect(r.warnings.some((w) => /missing/.test(w))).toBe(true)
})