fix(versioning): VFS file versions now capture actual blob content
VFS files store content in BlobStorage, but versioning was capturing stale embedding text from entity.data instead of actual file content. Changes: - VersionManager.save() now reads fresh content via vfs.readFile() - VersionManager.restore() writes content back via vfs.writeFile() - Text files stored as UTF-8, binary as base64 with encoding flag - Added comprehensive VFS versioning test suite (10 tests) - Updated API docs with VFS file versioning example Fixes: Workshop team bug report where all VFS file versions had identical data despite different metadata.size values.
This commit is contained in:
parent
f3765afb9e
commit
3e0f235f8b
3 changed files with 429 additions and 1 deletions
|
|
@ -139,6 +139,39 @@ export class VersionManager {
|
|||
this.versionIndex = new VersionIndex(brain)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity is a VFS file
|
||||
* VFS files store content in BlobStorage, not in entity.data
|
||||
*
|
||||
* @param entity Entity metadata object
|
||||
* @returns True if entity is a VFS file
|
||||
*/
|
||||
private isVFSFile(entity: any): boolean {
|
||||
return (
|
||||
entity?.isVFS === true &&
|
||||
entity?.vfsType === 'file' &&
|
||||
typeof entity?.path === 'string'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if content is text-based for encoding decisions
|
||||
* @param mimeType MIME type of the content
|
||||
* @returns True if content should be stored as UTF-8 string
|
||||
*/
|
||||
private isTextContent(mimeType?: string): boolean {
|
||||
if (!mimeType) return false
|
||||
return (
|
||||
mimeType.startsWith('text/') ||
|
||||
mimeType === 'application/json' ||
|
||||
mimeType === 'application/javascript' ||
|
||||
mimeType === 'application/typescript' ||
|
||||
mimeType === 'application/xml' ||
|
||||
mimeType.includes('+xml') ||
|
||||
mimeType.includes('+json')
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize versioning system (lazy)
|
||||
*/
|
||||
|
|
@ -173,6 +206,31 @@ export class VersionManager {
|
|||
throw new Error(`Entity ${entityId} not found`)
|
||||
}
|
||||
|
||||
// v6.3.2 FIX: For VFS file entities, fetch current content from blob storage
|
||||
// The entity.data field contains stale embedding text, not actual file content
|
||||
// VFS files store their real content in BlobStorage (content-addressable)
|
||||
if (this.isVFSFile(entity)) {
|
||||
if (!this.brain.vfs) {
|
||||
throw new Error(
|
||||
`Cannot version VFS file ${entityId}: VFS not initialized. ` +
|
||||
`Ensure brain.vfs is available before versioning VFS files.`
|
||||
)
|
||||
}
|
||||
|
||||
// Read fresh content from blob storage via VFS
|
||||
const freshContent: Buffer = await this.brain.vfs.readFile(entity.path)
|
||||
|
||||
// Store content with appropriate encoding
|
||||
// Text files as UTF-8 string (readable, smaller)
|
||||
// Binary files as base64 (safe for JSON serialization)
|
||||
if (this.isTextContent(entity.mimeType)) {
|
||||
entity.data = freshContent.toString('utf-8')
|
||||
} else {
|
||||
entity.data = freshContent.toString('base64')
|
||||
entity._vfsEncoding = 'base64' // Flag for restore to decode
|
||||
}
|
||||
}
|
||||
|
||||
// Get current branch
|
||||
const currentBranch = this.brain.currentBranch
|
||||
|
||||
|
|
@ -358,6 +416,37 @@ export class VersionManager {
|
|||
)
|
||||
}
|
||||
|
||||
// v6.3.2 FIX: For VFS file entities, write content back to blob storage
|
||||
// The versioned data contains the actual file content (not stale embedding text)
|
||||
// Using vfs.writeFile() ensures proper blob creation and metadata update
|
||||
if (this.isVFSFile(versionedEntity)) {
|
||||
if (!this.brain.vfs) {
|
||||
throw new Error(
|
||||
`Cannot restore VFS file ${entityId}: VFS not initialized. ` +
|
||||
`Ensure brain.vfs is available before restoring VFS files.`
|
||||
)
|
||||
}
|
||||
|
||||
// Decode content based on how it was stored
|
||||
let content: Buffer
|
||||
if (versionedEntity._vfsEncoding === 'base64') {
|
||||
// Binary file stored as base64
|
||||
content = Buffer.from(versionedEntity.data as string, 'base64')
|
||||
} else {
|
||||
// Text file stored as UTF-8 string
|
||||
content = Buffer.from(versionedEntity.data as string, 'utf-8')
|
||||
}
|
||||
|
||||
// Write content back to VFS - this handles:
|
||||
// - BlobStorage write (new hash)
|
||||
// - Entity metadata update
|
||||
// - Path resolver cache update
|
||||
await this.brain.vfs.writeFile(versionedEntity.path, content)
|
||||
|
||||
return targetVersion
|
||||
}
|
||||
|
||||
// For non-VFS entities, use existing brain.update() logic
|
||||
// Extract standard fields vs custom metadata
|
||||
// NounMetadata has: noun, data, createdAt, updatedAt, createdBy, service, confidence, weight
|
||||
const {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue