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

@ -117,13 +117,13 @@ import * as fs from 'node:fs'
import { Db, type DbHost, type HistoricalQueryHandle } from './db/db.js'
import {
importGraph,
isBackupData,
type BackupData,
isPortableGraph,
type PortableGraph,
type ExportSelector,
type ExportOptions,
type ImportOptions,
type ImportResult
} from './db/backup.js'
} from './db/portableGraph.js'
import { GenerationStore } from './db/generationStore.js'
import { isDeterministicEmbedMode } from './embeddings/deterministicEmbedMode.js'
import { GenerationConflictError } from './db/errors.js'
@ -5074,22 +5074,22 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}
/**
* @description Serialize part or all of the brain into a portable `BackupData`
* @description Serialize part or all of the brain into a portable `PortableGraph`
* document sugar for `brain.now().export(selector, options)` (the current
* generation). For a past generation use `(await brain.asOf(g)).export(...)`;
* for a speculative state use `brain.now().with(ops).export(...)`.
*
* Restore a `BackupData` with {@link Brainy.import} (it dispatches a backup to the
* Restore a `PortableGraph` with {@link Brainy.import} (it dispatches a backup to the
* graph round-trip; a file/buffer to ingestion). Distinct from `db.persist()`
* (native whole-brain snapshot with generation history).
*
* @param selector - WHAT to export (omit for the whole brain). See {@link ExportSelector}.
* @param options - HOW to export (vectors / VFS bytes / edge policy). See {@link ExportOptions}.
* @returns A versioned, portable `BackupData` document.
* @returns A versioned, portable `PortableGraph` document.
* @example
* const backup = await brain.export({ ids }, { includeVectors: true })
*/
async export(selector: ExportSelector = {}, options: ExportOptions = {}): Promise<BackupData> {
async export(selector: ExportSelector = {}, options: ExportOptions = {}): Promise<PortableGraph> {
return this.now().export(selector, options)
}
@ -6743,7 +6743,7 @@ export class Brainy<T = any> implements BrainyInterface<T> {
* - Reduced confusion (removed redundant options)
*/
async import(
source: Buffer | string | object | BackupData,
source: Buffer | string | object | PortableGraph,
options?: {
format?: 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx' | 'image'
vfsPath?: string
@ -6770,9 +6770,9 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}) => void
} & Partial<ImportOptions>
): Promise<ImportResult | any> {
// Portable graph round-trip: a BackupData document (produced by export()) is
// Portable graph round-trip: a PortableGraph document (produced by export()) is
// restored via ONE atomic transaction — distinct from foreign-file ingestion below.
if (isBackupData(source)) {
if (isPortableGraph(source)) {
return importGraph(this, this.storage, source, options as ImportOptions)
}

View file

@ -57,8 +57,8 @@ import type {
Result
} from '../types/brainy.types.js'
import type { StorageAdapter } from '../coreTypes.js'
import { exportGraph } from './backup.js'
import type { ExportSelector, ExportOptions, BackupData } from './backup.js'
import { exportGraph } from './portableGraph.js'
import type { ExportSelector, ExportOptions, PortableGraph } from './portableGraph.js'
import {
splitNounMetadataRecord,
splitVerbMetadataRecord
@ -416,7 +416,7 @@ export class Db<T = any> {
/**
* @description Serialize part or all of this database value into a portable
* `BackupData` document, read **as of this view's generation**. Because `export`
* `PortableGraph` document, read **as of this view's generation**. Because `export`
* lives on the immutable `Db`, it composes with every way of obtaining one:
* `brain.now().export(sel)` (current), `(await brain.asOf(g)).export(sel)`
* (time-travel export), and `brain.now().with(ops).export(sel)` (what-if export).
@ -427,11 +427,11 @@ export class Db<T = any> {
*
* @param selector - WHAT to export (omit for the whole brain). See {@link ExportSelector}.
* @param options - HOW to export (vectors / VFS bytes / edge policy). See {@link ExportOptions}.
* @returns A versioned, portable `BackupData` document.
* @returns A versioned, portable `PortableGraph` document.
* @example
* const backup = await brain.now().export({ collection: id }, { includeVectors: true })
*/
async export(selector: ExportSelector = {}, options: ExportOptions = {}): Promise<BackupData> {
async export(selector: ExportSelector = {}, options: ExportOptions = {}): Promise<PortableGraph> {
this.assertUsable('export')
return exportGraph(this, this.host.storage, selector, options)
}

View file

@ -1,18 +1,24 @@
/**
* @module db/backup
* @description Portable graph backup/restore engine for Brainy 8.0 the
* `BackupData v1` format plus the `export`/`import` engines that power
* @module db/portableGraph
* @description Portable graph export/import engine for Brainy 8.0 the
* `PortableGraph v1` document format plus the `export`/`import` engines that power
* `db.export()` / `brain.export()` (read, at a pinned generation) and
* `brain.import(backup)` (write, applied as one atomic transaction).
* `brain.import(graph)` (write, applied as one atomic transaction).
*
* The wire format is identical to the 7.x line (`formatVersion: 1`), so a
* document written by 7.x imports cleanly here and vice-versa. This is the
* **portable** round-trip (human-readable, partial-or-whole, cross-version),
* distinct from `db.persist()`/`Brainy.load()` (the native whole-brain snapshot,
* which preserves generation history) and from `brain.import(file)` (foreign-file
* ingestion).
* A `PortableGraph` is a self-describing, versioned, **portable** representation
* of a graph (entities + relations, optionally vectors and file blobs) the unit
* Brainy exports for interchange between instances, versions, and products, and the
* payload other artifacts embed (e.g. a workbench file's `graph` field). It is NOT
* a backup: that role belongs to `db.persist()`/`Brainy.load()`, the native
* whole-brain snapshot which preserves generation history. `PortableGraph` is the
* human-readable, partial-or-whole, cross-version round-trip; distinct also from
* `brain.import(file)` (foreign-file ingestion).
*
* **Reserved-field split:** each `BackupEntity` carries Brainy's standard fields
* Every document carries `format: 'brainy-portable-graph'` and `formatVersion: 1`;
* import gates on `formatVersion` for forward migration. The format is shared
* verbatim across the 7.x and 8.0 lines.
*
* **Reserved-field split:** each `PortableGraphEntity` carries Brainy's standard fields
* (`subtype`, `visibility`, `data`, `confidence`, `weight`, `service`, `createdBy`,
* `createdAt`) at the TOP LEVEL and `metadata` holds ONLY custom user fields
* mirroring the in-memory `Entity`, so import maps each to its dedicated `add`/
@ -27,10 +33,10 @@ import { TxOperation } from './types.js'
/** Fixed entity id of the VFS root collection (a `system` entity; excluded unless `includeSystem`). */
const VFS_ROOT_ID = '00000000-0000-0000-0000-000000000000'
/** Magic string identifying a Brainy portable backup document. */
export const BACKUP_FORMAT = 'brainy-backup'
/** Magic string identifying a Brainy `PortableGraph` document (the `format` tag). */
export const PORTABLE_GRAPH_FORMAT = 'brainy-portable-graph'
/** Current portable-format version (import gates on this for cross-version migration). */
export const BACKUP_FORMAT_VERSION = 1
export const PORTABLE_GRAPH_FORMAT_VERSION = 1
/** Default embedding model label (informational; `dimensions` is the real compat gate). */
const DEFAULT_EMBED_MODEL = 'all-MiniLM-L6-v2'
/** Page size for find-based enumeration (whole-brain / predicate selectors). */
@ -92,7 +98,7 @@ export interface ExportOptions {
edges?: 'induced' | 'incident' | 'none'
}
/** Controls how a `BackupData` is applied on `import()`. */
/** Controls how a `PortableGraph` is applied on `import()`. */
export interface ImportOptions {
/** Conflict policy for an existing id (default: `'merge'` — dedup-by-id). */
onConflict?: 'merge' | 'replace' | 'skip'
@ -104,8 +110,8 @@ export interface ImportOptions {
meta?: Record<string, unknown>
}
/** One entity in a `BackupData`. Standard fields top-level; `metadata` is custom-only. */
export interface BackupEntity {
/** One entity in a `PortableGraph`. Standard fields top-level; `metadata` is custom-only. */
export interface PortableGraphEntity {
id: string
type: string
subtype?: string
@ -120,8 +126,8 @@ export interface BackupEntity {
metadata?: any
}
/** One relation (edge) in a `BackupData`. */
export interface BackupRelation {
/** One relation (edge) in a `PortableGraph`. */
export interface PortableGraphRelation {
id: string
from: string
to: string
@ -134,15 +140,15 @@ export interface BackupRelation {
}
/** A self-describing, versioned, portable graph document (identical on 7.x and 8.0). */
export interface BackupData {
format: typeof BACKUP_FORMAT
export interface PortableGraph {
format: typeof PORTABLE_GRAPH_FORMAT
formatVersion: number
brainyVersion: string
createdAt: string
embedding: { model: string; dimensions: number }
selector?: ExportSelector
entities: BackupEntity[]
relations: BackupRelation[]
entities: PortableGraphEntity[]
relations: PortableGraphRelation[]
blobs?: Record<string, string>
danglingIds?: string[]
stats: { entityCount: number; relationCount: number; blobCount: number; vectorDimensions?: number }
@ -159,30 +165,30 @@ export interface ImportResult {
}
/** The minimal read surface the export engine needs — satisfied by `Db` and `Brainy`. */
export interface BackupReader<T = any> {
export interface PortableGraphReader<T = any> {
get(id: string, options?: { includeVectors?: boolean }): Promise<Entity<T> | null>
find(query: any): Promise<Result<T>[]>
related(paramsOrId?: any): Promise<Relation<T>[]>
}
/** The minimal write surface the import engine needs — satisfied by `Brainy`. */
export interface BackupWriter<T = any> {
export interface PortableGraphWriter<T = any> {
get(id: string, options?: { includeVectors?: boolean }): Promise<Entity<T> | null>
transact(ops: TxOperation<T>[], options?: { meta?: Record<string, unknown> }): Promise<unknown>
}
/** Type guard: is this value a Brainy `BackupData` document (vs a file/foreign object)? */
export function isBackupData(value: unknown): value is BackupData {
/** Type guard: is this value a Brainy `PortableGraph` document (vs a file/foreign object)? */
export function isPortableGraph(value: unknown): value is PortableGraph {
return (
typeof value === 'object' &&
value !== null &&
(value as any).format === BACKUP_FORMAT &&
(value as any).format === PORTABLE_GRAPH_FORMAT &&
Array.isArray((value as any).entities)
)
}
/** Result of {@link validateBackup}. `valid` is true iff `errors` is empty. */
export interface BackupValidation {
/** Result of {@link validatePortableGraph}. `valid` is true iff `errors` is empty. */
export interface PortableGraphValidation {
valid: boolean
errors: string[]
warnings: string[]
@ -191,29 +197,29 @@ export interface BackupValidation {
/**
* @description Dry-run check a value before `import()`, without mutating the brain:
* structural validity, a supported `formatVersion`, entity-id uniqueness + required
* fields, and relation-endpoint coverage. `import()` throws on a non-`BackupData` or a
* too-new `formatVersion`; `validateBackup()` lets a consumer (e.g. a serializer checking
* fields, and relation-endpoint coverage. `import()` throws on a non-`PortableGraph` or a
* too-new `formatVersion`; `validatePortableGraph()` lets a consumer (e.g. a serializer checking
* a `.wbench` graph payload) surface issues first.
* @param data - The value to validate (need not be a `BackupData`).
* @param data - The value to validate (need not be a `PortableGraph`).
* @returns `{ valid, errors, warnings }`.
* @example
* const { valid, errors } = validateBackup(payload)
* if (!valid) throw new Error(`invalid backup: ${errors.join('; ')}`)
* const { valid, errors } = validatePortableGraph(payload)
* if (!valid) throw new Error(`invalid PortableGraph: ${errors.join('; ')}`)
* await brain.import(payload)
*/
export function validateBackup(data: unknown): BackupValidation {
export function validatePortableGraph(data: unknown): PortableGraphValidation {
const errors: string[] = []
const warnings: string[] = []
if (!isBackupData(data)) {
errors.push(`not a BackupData document (expected format: '${BACKUP_FORMAT}')`)
if (!isPortableGraph(data)) {
errors.push(`not a PortableGraph document (expected format: '${PORTABLE_GRAPH_FORMAT}')`)
return { valid: false, errors, warnings }
}
if (typeof data.formatVersion !== 'number') {
errors.push('formatVersion is missing or not a number')
} else if (data.formatVersion > BACKUP_FORMAT_VERSION) {
} else if (data.formatVersion > PORTABLE_GRAPH_FORMAT_VERSION) {
errors.push(
`formatVersion ${data.formatVersion} is newer than supported (max ${BACKUP_FORMAT_VERSION})`
`formatVersion ${data.formatVersion} is newer than supported (max ${PORTABLE_GRAPH_FORMAT_VERSION})`
)
}
@ -264,7 +270,7 @@ export function validateBackup(data: unknown): BackupValidation {
/**
* @description Serialize part or all of a graph (read through `reader` at its pinned
* generation) into a portable `BackupData` document.
* generation) into a portable `PortableGraph` document.
* @param reader - Generation-correct read surface (`Db` or `Brainy`).
* @param storage - Storage adapter (used only for VFS blob bytes when `includeContent`).
* @param selector - WHAT to export (omit for the whole brain).
@ -272,11 +278,11 @@ export function validateBackup(data: unknown): BackupValidation {
* @param dimensions - Embedding dimensionality for the manifest.
*/
export async function exportGraph<T = any>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
storage: StorageAdapter | undefined,
selector: ExportSelector,
options: ExportOptions
): Promise<BackupData> {
): Promise<PortableGraph> {
const {
includeVectors = false,
includeContent = false,
@ -290,14 +296,14 @@ export async function exportGraph<T = any>(
// 2. Read canonical entities (reserved fields top-level), applying any predicate filter.
const usePredicate = hasPredicate(selector)
const entityMap = new Map<string, Entity<T>>()
const entities: BackupEntity[] = []
const entities: PortableGraphEntity[] = []
for (const id of idSet) {
const e = await reader.get(id, { includeVectors })
if (!e) continue
if (!includeSystem && (e as any).visibility === 'system') continue
if (usePredicate && !matchesPredicate(e, selector)) continue
entityMap.set(id, e)
entities.push(toBackupEntity(e, includeVectors))
entities.push(toPortableGraphEntity(e, includeVectors))
}
const keptIds = new Set(entityMap.keys())
@ -314,8 +320,8 @@ export async function exportGraph<T = any>(
const dimensions = entities.find((e) => e.vector && e.vector.length)?.vector?.length ?? 384
return {
format: BACKUP_FORMAT,
formatVersion: BACKUP_FORMAT_VERSION,
format: PORTABLE_GRAPH_FORMAT,
formatVersion: PORTABLE_GRAPH_FORMAT_VERSION,
brainyVersion: getBrainyVersion(),
createdAt: new Date().toISOString(),
embedding: { model: DEFAULT_EMBED_MODEL, dimensions },
@ -338,31 +344,31 @@ export async function exportGraph<T = any>(
// ============================================================================
/**
* @description Apply a `BackupData` to the brain as ONE atomic transaction (a single
* @description Apply a `PortableGraph` to the brain as ONE atomic transaction (a single
* new generation). Dedup-by-id merge by default; re-embeds from `data` when no vector
* is carried. VFS blob bytes are written first (content-addressed, idempotent).
* @param writer - The brain (`get` + `transact`).
* @param storage - Storage adapter (for VFS blob bytes).
* @param data - A `BackupData` document.
* @param data - A `PortableGraph` document.
* @param options - Conflict / vector / id-remap policy.
* @throws If `data` is not a `BackupData`, or its `formatVersion` is newer than supported.
* @throws If `data` is not a `PortableGraph`, or its `formatVersion` is newer than supported.
*/
export async function importGraph<T = any>(
writer: BackupWriter<T>,
writer: PortableGraphWriter<T>,
storage: StorageAdapter | undefined,
data: BackupData,
data: PortableGraph,
options: ImportOptions = {}
): Promise<ImportResult> {
if (!isBackupData(data)) {
if (!isPortableGraph(data)) {
throw new Error(
`import() expects a BackupData document (format:'${BACKUP_FORMAT}'). ` +
`import() expects a PortableGraph document (format:'${PORTABLE_GRAPH_FORMAT}'). ` +
`For file ingestion (CSV/PDF/Excel/JSON), pass a file/buffer instead.`
)
}
if (typeof data.formatVersion === 'number' && data.formatVersion > BACKUP_FORMAT_VERSION) {
if (typeof data.formatVersion === 'number' && data.formatVersion > PORTABLE_GRAPH_FORMAT_VERSION) {
throw new Error(
`Backup formatVersion ${data.formatVersion} is newer than this Brainy supports ` +
`(max ${BACKUP_FORMAT_VERSION}). Upgrade Brainy to import this document.`
`PortableGraph formatVersion ${data.formatVersion} is newer than this Brainy supports ` +
`(max ${PORTABLE_GRAPH_FORMAT_VERSION}). Upgrade Brainy to import this document.`
)
}
@ -445,7 +451,7 @@ export async function importGraph<T = any>(
} as TxOperation<T>)
}
// 3. Apply atomically — one generation for the whole backup, or none.
// 3. Apply atomically — one generation for the whole graph, or none.
if (ops.length > 0) {
await writer.transact(ops, meta ? { meta } : undefined)
}
@ -472,7 +478,7 @@ function hasPredicate(s: ExportSelector): boolean {
}
async function resolveSelector<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
s: ExportSelector,
includeSystem: boolean
): Promise<Set<string>> {
@ -493,7 +499,7 @@ async function resolveSelector<T>(
}
/** Whole-brain / predicate enumeration via generation-correct paginated `find()`. */
async function enumerateAll<T>(reader: BackupReader<T>, s: ExportSelector): Promise<Set<string>> {
async function enumerateAll<T>(reader: PortableGraphReader<T>, s: ExportSelector): Promise<Set<string>> {
const params: any = {}
if (s.type !== undefined) params.type = s.type
if (s.subtype !== undefined) params.subtype = s.subtype
@ -512,7 +518,7 @@ async function enumerateAll<T>(reader: BackupReader<T>, s: ExportSelector): Prom
}
async function resolveCollectionSubtree<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
rootId: string,
depth?: number
): Promise<Set<string>> {
@ -538,7 +544,7 @@ async function resolveCollectionSubtree<T>(
}
async function resolveConnected<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
c: NonNullable<ExportSelector['connected']>
): Promise<Set<string>> {
const { from, depth = 1, verbs, direction = 'out' } = c
@ -562,7 +568,7 @@ async function resolveConnected<T>(
}
async function neighboursOf<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
id: string,
direction: 'out' | 'in' | 'both',
verbs?: VerbType[]
@ -580,7 +586,7 @@ async function neighboursOf<T>(
}
async function resolveVfsPath<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
path: string,
recursive: boolean,
depth?: number
@ -596,7 +602,7 @@ async function resolveVfsPath<T>(
return resolveCollectionSubtree(reader, dirId, depth)
}
async function resolveVfsPathToId<T>(reader: BackupReader<T>, path: string): Promise<string | null> {
async function resolveVfsPathToId<T>(reader: PortableGraphReader<T>, path: string): Promise<string | null> {
const segments = path.split('/').filter(Boolean)
let currentId = VFS_ROOT_ID
for (const seg of segments) {
@ -645,8 +651,8 @@ function matchesWhere(metadata: any, where: Record<string, any>): boolean {
// Serialization helpers (private)
// ============================================================================
function toBackupEntity<T>(e: Entity<T>, includeVectors: boolean): BackupEntity {
const be: BackupEntity = { id: e.id, type: e.type as string }
function toPortableGraphEntity<T>(e: Entity<T>, includeVectors: boolean): PortableGraphEntity {
const be: PortableGraphEntity = { id: e.id, type: e.type as string }
if (e.subtype !== undefined) be.subtype = e.subtype
const vis = (e as any).visibility
if (vis !== undefined && vis !== 'public') be.visibility = vis
@ -661,8 +667,8 @@ function toBackupEntity<T>(e: Entity<T>, includeVectors: boolean): BackupEntity
return be
}
function toBackupRelation<T>(r: Relation<T>): BackupRelation {
const br: BackupRelation = { id: r.id, from: r.from, to: r.to, type: r.type as string }
function toPortableGraphRelation<T>(r: Relation<T>): PortableGraphRelation {
const br: PortableGraphRelation = { id: r.id, from: r.from, to: r.to, type: r.type as string }
if (r.subtype !== undefined) br.subtype = r.subtype
const vis = (r as any).visibility
if (vis !== undefined && vis !== 'public') br.visibility = vis
@ -673,13 +679,13 @@ function toBackupRelation<T>(r: Relation<T>): BackupRelation {
}
async function collectEdges<T>(
reader: BackupReader<T>,
reader: PortableGraphReader<T>,
idSet: Set<string>,
edges: 'induced' | 'incident' | 'none'
): Promise<{ relations: BackupRelation[]; danglingIds?: string[] }> {
): Promise<{ relations: PortableGraphRelation[]; danglingIds?: string[] }> {
if (edges === 'none') return { relations: [] }
const relations: BackupRelation[] = []
const relations: PortableGraphRelation[] = []
const dangling = new Set<string>()
const seen = new Set<string>()
@ -691,7 +697,7 @@ async function collectEdges<T>(
if (edges === 'induced' && !toIn) continue
if (!toIn) dangling.add(r.to)
seen.add(r.id)
relations.push(toBackupRelation(r))
relations.push(toPortableGraphRelation(r))
}
}
@ -703,7 +709,7 @@ async function collectEdges<T>(
if (!idSet.has(r.from)) {
dangling.add(r.from)
seen.add(r.id)
relations.push(toBackupRelation(r))
relations.push(toPortableGraphRelation(r))
}
}
}
@ -740,7 +746,7 @@ async function collectBlobs<T>(
// add()/update() param mapping (private)
// ============================================================================
function entityAddFields(be: BackupEntity): Record<string, any> {
function entityAddFields(be: PortableGraphEntity): Record<string, any> {
const fields: Record<string, any> = { data: be.data, type: be.type as NounType }
if (be.subtype !== undefined) fields.subtype = be.subtype
if (be.visibility !== undefined) fields.visibility = be.visibility
@ -751,7 +757,7 @@ function entityAddFields(be: BackupEntity): Record<string, any> {
return fields
}
function entityUpdateFields(be: BackupEntity): Record<string, any> {
function entityUpdateFields(be: PortableGraphEntity): Record<string, any> {
const fields: Record<string, any> = {}
if (be.data !== undefined) fields.data = be.data
if (be.type !== undefined) fields.type = be.type as NounType

View file

@ -142,19 +142,19 @@ export { EntityNotFoundError, RelationNotFoundError } from './errors/notFound.js
// db.with() / db.persist() / Brainy.load(). See src/db/ for the record layer.
export { Db } from './db/db.js'
// Portable graph backup/restore — db.export() / brain.export() / brain.import()
// (BackupData v1; identical wire format to the 7.x line).
export { isBackupData, validateBackup } from './db/backup.js'
// Portable graph export/import — db.export() / brain.export() / brain.import()
// (PortableGraph v1; identical wire format to the 7.x line).
export { isPortableGraph, validatePortableGraph } from './db/portableGraph.js'
export type {
BackupData,
BackupEntity,
BackupRelation,
PortableGraph,
PortableGraphEntity,
PortableGraphRelation,
ExportSelector,
ExportOptions,
ImportOptions,
ImportResult,
BackupValidation
} from './db/backup.js'
PortableGraphValidation
} from './db/portableGraph.js'
export {
GenerationConflictError,
SpeculativeOverlayError,