feat: add ImageHandler with EXIF extraction and comprehensive MIME detection (v5.2.0)

Implements Phase 1.5 (Comprehensive MIME Type Detection) and adds built-in image processing support to IntelligentImportAugmentation.

**New Features:**
- ImageHandler: Extracts image metadata (dimensions, format, color space) using sharp
- EXIF extraction: Camera data, GPS, timestamps using exifr library
- Support for JPEG, PNG, WebP, GIF, TIFF, BMP, SVG, HEIC, AVIF formats
- MimeTypeDetector: Unified MIME type detection with magic byte support
- FormatDetector: Enhanced with image format detection via MIME + magic bytes

**Architecture Fixes:**
- Fixed brain.import() augmentation pipeline integration (src/brainy.ts:3140-3154)
- Added parameter spreading for ImportSource objects to enable augmentation access
- Fixed metadata propagation through ImportCoordinator to final results
- Added augmentation data check in ImportCoordinator.extract()

**Integration:**
- ImageHandler registered as built-in handler alongside CSV, Excel, PDF
- Images import as 'media' entities with 'image' subtype
- Full metadata preserved in knowledge graph entities
- Configuration options: enableImage, extractEXIF, imageDefaults

**Test Coverage:**
- 15 integration tests (image-import.test.ts) - 100% passing
- 27 unit tests (image-handler.test.ts) - 100% passing
- Format detection tests for all supported image types
- Error handling and resilience tests

**Breaking Changes:** None - backward compatible

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
David Snelling 2025-11-03 14:06:17 -08:00
parent 6345f87eb2
commit 1874b77896
26 changed files with 5079 additions and 434 deletions

View file

@ -228,6 +228,15 @@ export class Brainy<T = any> implements BrainyInterface<T> {
Brainy.shutdownHooksRegisteredGlobally = true
}
// v5.2.0: Initialize COW (BlobStorage) before VFS
// VFS now requires BlobStorage for unified file storage
if (typeof (this.storage as any).initializeCOW === 'function') {
await (this.storage as any).initializeCOW({
branch: (this.config.storage as any)?.branch || 'main',
enableCompression: true
})
}
// Mark as initialized BEFORE VFS init (v5.0.1)
// VFS.init() needs brain to be marked initialized to call brain methods
this.initialized = true
@ -3103,7 +3112,7 @@ export class Brainy<T = any> implements BrainyInterface<T> {
async import(
source: Buffer | string | object,
options?: {
format?: 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx'
format?: 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx' | 'image'
vfsPath?: string
groupBy?: 'type' | 'sheet' | 'flat' | 'custom'
customGrouping?: (entity: any) => string
@ -3128,12 +3137,21 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}) => void
}
) {
// Lazy load ImportCoordinator
const { ImportCoordinator } = await import('./import/ImportCoordinator.js')
const coordinator = new ImportCoordinator(this)
await coordinator.init()
// Execute through augmentation pipeline (v5.2.0: Enables IntelligentImportAugmentation)
// If source is an ImportSource object (not a Buffer), spread it so augmentations can access properties
const params = typeof source === 'object' && !Buffer.isBuffer(source)
? { ...source as object, ...options } // Spread ImportSource: { type, data, filename, ...options }
: { source, ...options } // Wrap Buffer/string: { source, ...options }
return await coordinator.import(source, options)
return this.augmentationRegistry.execute('import', params, async () => {
// Lazy load ImportCoordinator
const { ImportCoordinator } = await import('./import/ImportCoordinator.js')
const coordinator = new ImportCoordinator(this)
await coordinator.init()
// Pass augmentation-modified params (contains _intelligentImport, _extractedData, etc)
return await coordinator.import(source, { ...options, ...params })
})
}
/**