2025-09-11 16:23:32 -07:00
|
|
|
/**
|
|
|
|
|
* Universal Neural Import API
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
*
|
2025-09-11 16:23:32 -07:00
|
|
|
* ALWAYS uses neural matching to map ANY data to our strict NounTypes and VerbTypes
|
|
|
|
|
* Never falls back to rules - neural matching is MANDATORY
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
*
|
2025-09-11 16:23:32 -07:00
|
|
|
* Handles:
|
|
|
|
|
* - Strings (text, JSON, CSV, YAML, Markdown)
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
* - Files (local paths, any format) - uses MimeTypeDetector for 2000+ types
|
2025-09-11 16:23:32 -07:00
|
|
|
* - URLs (web pages, APIs, documents)
|
|
|
|
|
* - Objects (structured data)
|
|
|
|
|
* - Binary data (images, PDFs via extraction)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { NounType, VerbType } from '../types/graphTypes.js'
|
|
|
|
|
import { Vector } from '../coreTypes.js'
|
|
|
|
|
import type { Brainy } from '../brainy.js'
|
|
|
|
|
import type { Entity, Relation } from '../types/brainy.types.js'
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
import { NeuralImportAugmentation } from '../cortex/neuralImportAugmentation.js'
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
import { mimeDetector } from '../vfs/MimeTypeDetector.js'
|
2025-09-11 16:23:32 -07:00
|
|
|
|
|
|
|
|
export interface ImportSource {
|
|
|
|
|
type: 'string' | 'file' | 'url' | 'object' | 'binary'
|
|
|
|
|
data: any
|
|
|
|
|
format?: string // Optional hint about format
|
|
|
|
|
metadata?: any // Additional context
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface NeuralImportResult {
|
|
|
|
|
entities: Array<{
|
|
|
|
|
id: string
|
|
|
|
|
type: NounType
|
|
|
|
|
data: any
|
|
|
|
|
vector: Vector
|
|
|
|
|
confidence: number
|
|
|
|
|
metadata: any
|
|
|
|
|
}>
|
|
|
|
|
relationships: Array<{
|
|
|
|
|
id: string
|
|
|
|
|
from: string
|
|
|
|
|
to: string
|
|
|
|
|
type: VerbType
|
|
|
|
|
weight: number
|
|
|
|
|
confidence: number
|
|
|
|
|
metadata?: any
|
|
|
|
|
}>
|
|
|
|
|
stats: {
|
|
|
|
|
totalProcessed: number
|
|
|
|
|
entitiesCreated: number
|
|
|
|
|
relationshipsCreated: number
|
|
|
|
|
averageConfidence: number
|
|
|
|
|
processingTimeMs: number
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:08:46 -07:00
|
|
|
export interface NeuralImportProgress {
|
|
|
|
|
phase: 'extracting' | 'storing-entities' | 'storing-relationships' | 'complete'
|
|
|
|
|
message: string
|
|
|
|
|
current: number
|
|
|
|
|
total: number
|
|
|
|
|
entities?: number
|
|
|
|
|
relationships?: number
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
export class UniversalImportAPI {
|
|
|
|
|
private brain: Brainy<any>
|
|
|
|
|
private neuralImport: NeuralImportAugmentation
|
|
|
|
|
private embedCache = new Map<string, Vector>()
|
|
|
|
|
|
|
|
|
|
constructor(brain: Brainy<any>) {
|
|
|
|
|
this.brain = brain
|
|
|
|
|
this.neuralImport = new NeuralImportAugmentation({
|
|
|
|
|
confidenceThreshold: 0.0, // Accept ALL confidence levels - never reject
|
|
|
|
|
enableWeights: true,
|
|
|
|
|
skipDuplicates: false // Process everything
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the neural import system
|
|
|
|
|
*/
|
|
|
|
|
async init(): Promise<void> {
|
|
|
|
|
// Neural import initializes itself
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Universal import - handles ANY data source
|
|
|
|
|
* ALWAYS uses neural matching, NEVER falls back
|
|
|
|
|
*/
|
2025-10-16 12:08:46 -07:00
|
|
|
async import(
|
|
|
|
|
source: ImportSource | string | any,
|
|
|
|
|
options?: { onProgress?: (progress: NeuralImportProgress) => void }
|
|
|
|
|
): Promise<NeuralImportResult> {
|
2025-09-11 16:23:32 -07:00
|
|
|
const startTime = Date.now()
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Normalize source
|
|
|
|
|
const normalizedSource = this.normalizeSource(source)
|
2025-10-16 12:08:46 -07:00
|
|
|
|
|
|
|
|
options?.onProgress?.({
|
|
|
|
|
phase: 'extracting',
|
|
|
|
|
message: 'Extracting data from source...',
|
|
|
|
|
current: 0,
|
|
|
|
|
total: 0
|
|
|
|
|
})
|
|
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Extract data based on source type
|
|
|
|
|
const extractedData = await this.extractData(normalizedSource)
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Neural processing - MANDATORY
|
|
|
|
|
const neuralResults = await this.neuralProcess(extractedData)
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Store in brain
|
2025-10-16 12:08:46 -07:00
|
|
|
const result = await this.storeInBrain(neuralResults, options?.onProgress)
|
|
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
result.stats.processingTimeMs = Date.now() - startTime
|
2025-10-16 12:08:46 -07:00
|
|
|
|
|
|
|
|
options?.onProgress?.({
|
|
|
|
|
phase: 'complete',
|
|
|
|
|
message: 'Import complete',
|
|
|
|
|
current: result.stats.entitiesCreated + result.stats.relationshipsCreated,
|
|
|
|
|
total: result.stats.totalProcessed,
|
|
|
|
|
entities: result.stats.entitiesCreated,
|
|
|
|
|
relationships: result.stats.relationshipsCreated
|
|
|
|
|
})
|
|
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import from URL - fetches and processes
|
|
|
|
|
*/
|
|
|
|
|
async importFromURL(url: string): Promise<NeuralImportResult> {
|
|
|
|
|
const response = await fetch(url)
|
|
|
|
|
const contentType = response.headers.get('content-type') || 'text/plain'
|
|
|
|
|
|
|
|
|
|
let data: any
|
|
|
|
|
if (contentType.includes('json')) {
|
|
|
|
|
data = await response.json()
|
|
|
|
|
} else if (contentType.includes('text') || contentType.includes('html')) {
|
|
|
|
|
data = await response.text()
|
|
|
|
|
} else {
|
|
|
|
|
// Binary data
|
|
|
|
|
const buffer = await response.arrayBuffer()
|
|
|
|
|
data = new Uint8Array(buffer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.import({
|
|
|
|
|
type: 'url',
|
|
|
|
|
data,
|
|
|
|
|
format: contentType,
|
|
|
|
|
metadata: { url, fetchedAt: Date.now() }
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import from file - reads and processes
|
|
|
|
|
* Note: In browser environment, use File API instead
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
*
|
|
|
|
|
* Uses MimeTypeDetector for comprehensive format detection (2000+ types)
|
2025-09-11 16:23:32 -07:00
|
|
|
*/
|
|
|
|
|
async importFromFile(filePath: string): Promise<NeuralImportResult> {
|
|
|
|
|
// Read the actual file content
|
feat: add node: protocol to all Node.js built-in imports for bundler compatibility
- Updated all fs, path, crypto, os, url, util, events, http, https, net, child_process, stream, and zlib imports
- Changed both static imports and dynamic imports to use node: protocol
- This makes Brainy more bundler-friendly by explicitly marking Node.js built-ins
- Prevents bundlers from attempting to polyfill or bundle these modules
- Reduces bundle size for web applications using Brainy
- Improves tree-shaking and dead code elimination
Benefits for external bundlers:
- Clear distinction between Node.js built-ins and external dependencies
- No ambiguity about what needs polyfilling
- Smaller bundles for browser builds
- Better compatibility with modern bundlers (Webpack 5, Vite, Rollup, esbuild)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-17 14:20:21 -07:00
|
|
|
const { readFileSync } = await import('node:fs')
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
|
|
|
|
|
// Use MimeTypeDetector for comprehensive format detection
|
|
|
|
|
const mimeType = mimeDetector.detectMimeType(filePath)
|
2025-09-11 16:23:32 -07:00
|
|
|
const ext = filePath.split('.').pop()?.toLowerCase() || 'txt'
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
try {
|
|
|
|
|
const fileContent = readFileSync(filePath, 'utf-8')
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
return this.import({
|
|
|
|
|
type: 'file',
|
|
|
|
|
data: fileContent, // Actual file content
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
format: ext, // Keep ext for backward compatibility
|
|
|
|
|
metadata: {
|
2025-09-11 16:23:32 -07:00
|
|
|
path: filePath,
|
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>
2025-11-03 14:06:17 -08:00
|
|
|
mimeType, // Add detected MIME type
|
2025-09-11 16:23:32 -07:00
|
|
|
importedAt: Date.now(),
|
|
|
|
|
fileSize: fileContent.length
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(`Failed to read file ${filePath}: ${(error as Error).message}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Normalize any input to ImportSource
|
|
|
|
|
*/
|
|
|
|
|
private normalizeSource(source: any): ImportSource {
|
|
|
|
|
// Already normalized
|
|
|
|
|
if (source && typeof source === 'object' && 'type' in source && 'data' in source) {
|
|
|
|
|
return source as ImportSource
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String input
|
|
|
|
|
if (typeof source === 'string') {
|
|
|
|
|
// Check if it's a URL
|
|
|
|
|
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
|
|
|
return { type: 'url', data: source }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if it looks like a file path
|
|
|
|
|
if (source.includes('/') || source.includes('\\') || source.includes('.')) {
|
|
|
|
|
// Assume it's a file path reference
|
|
|
|
|
return { type: 'file', data: source }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Treat as raw string data
|
|
|
|
|
return { type: 'string', data: source }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Object/Array input
|
|
|
|
|
if (typeof source === 'object') {
|
|
|
|
|
return { type: 'object', data: source }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default to string
|
|
|
|
|
return { type: 'string', data: String(source) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract structured data from source
|
|
|
|
|
*/
|
|
|
|
|
private async extractData(source: ImportSource): Promise<any[]> {
|
|
|
|
|
switch (source.type) {
|
|
|
|
|
case 'url':
|
|
|
|
|
// URL is in data field, need to fetch
|
|
|
|
|
return this.extractFromURL(source.data)
|
|
|
|
|
|
|
|
|
|
case 'file':
|
|
|
|
|
// File path is in data field, need to read
|
|
|
|
|
return this.extractFromFile(source.data)
|
|
|
|
|
|
|
|
|
|
case 'string':
|
|
|
|
|
return this.extractFromString(source.data, source.format)
|
|
|
|
|
|
|
|
|
|
case 'object':
|
|
|
|
|
return Array.isArray(source.data) ? source.data : [source.data]
|
|
|
|
|
|
|
|
|
|
case 'binary':
|
|
|
|
|
return this.extractFromBinary(source.data, source.format)
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// Unknown type, treat as object
|
|
|
|
|
return [source.data]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract data from URL
|
|
|
|
|
*/
|
|
|
|
|
private async extractFromURL(url: string): Promise<any[]> {
|
|
|
|
|
const result = await this.importFromURL(url)
|
|
|
|
|
return result.entities.map(e => e.data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract data from file
|
|
|
|
|
*/
|
|
|
|
|
private async extractFromFile(filePath: string): Promise<any[]> {
|
|
|
|
|
const result = await this.importFromFile(filePath)
|
|
|
|
|
return result.entities.map(e => e.data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract data from string based on format
|
|
|
|
|
*/
|
|
|
|
|
private extractFromString(data: string, format?: string): any[] {
|
|
|
|
|
// Try to detect format if not provided
|
|
|
|
|
const detectedFormat = format || this.detectFormat(data)
|
|
|
|
|
|
|
|
|
|
switch (detectedFormat) {
|
|
|
|
|
case 'json':
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(data)
|
|
|
|
|
return Array.isArray(parsed) ? parsed : [parsed]
|
|
|
|
|
} catch {
|
|
|
|
|
// Not valid JSON, treat as text
|
|
|
|
|
return this.extractFromText(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'csv':
|
|
|
|
|
return this.parseCSV(data)
|
|
|
|
|
|
|
|
|
|
case 'yaml':
|
|
|
|
|
case 'yml':
|
|
|
|
|
return this.parseYAML(data)
|
|
|
|
|
|
|
|
|
|
case 'markdown':
|
|
|
|
|
case 'md':
|
|
|
|
|
return this.parseMarkdown(data)
|
|
|
|
|
|
|
|
|
|
case 'xml':
|
|
|
|
|
case 'html':
|
|
|
|
|
return this.parseHTML(data)
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return this.extractFromText(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract from binary data (images, PDFs, etc)
|
|
|
|
|
*/
|
|
|
|
|
private async extractFromBinary(data: Uint8Array, format?: string): Promise<any[]> {
|
|
|
|
|
// For now, create a single entity representing the binary data
|
|
|
|
|
// In production, would use OCR, image recognition, PDF extraction, etc.
|
|
|
|
|
return [{
|
|
|
|
|
type: 'binary',
|
|
|
|
|
format: format || 'unknown',
|
|
|
|
|
size: data.length,
|
|
|
|
|
hash: await this.hashBinary(data),
|
|
|
|
|
extractedAt: Date.now()
|
|
|
|
|
}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract entities from plain text
|
|
|
|
|
*/
|
|
|
|
|
private extractFromText(text: string): any[] {
|
|
|
|
|
// Split into meaningful chunks
|
|
|
|
|
const chunks: any[] = []
|
|
|
|
|
|
|
|
|
|
// Split by paragraphs
|
|
|
|
|
const paragraphs = text.split(/\n\n+/)
|
|
|
|
|
for (const para of paragraphs) {
|
|
|
|
|
if (para.trim()) {
|
|
|
|
|
chunks.push({
|
|
|
|
|
text: para.trim(),
|
|
|
|
|
type: 'paragraph',
|
|
|
|
|
length: para.length
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no paragraphs, split by sentences
|
|
|
|
|
if (chunks.length === 0) {
|
|
|
|
|
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text]
|
|
|
|
|
for (const sentence of sentences) {
|
|
|
|
|
if (sentence.trim()) {
|
|
|
|
|
chunks.push({
|
|
|
|
|
text: sentence.trim(),
|
|
|
|
|
type: 'sentence',
|
|
|
|
|
length: sentence.length
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chunks
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Neural processing - CORE of the system
|
|
|
|
|
* ALWAYS uses embeddings and neural matching
|
|
|
|
|
*/
|
|
|
|
|
private async neuralProcess(data: any[]): Promise<{
|
|
|
|
|
entities: Map<string, any>
|
|
|
|
|
relationships: Map<string, any>
|
|
|
|
|
}> {
|
|
|
|
|
const entities = new Map<string, any>()
|
|
|
|
|
const relationships = new Map<string, any>()
|
|
|
|
|
|
|
|
|
|
for (const item of data) {
|
|
|
|
|
// Generate embedding for the item
|
|
|
|
|
const embedding = await this.generateEmbedding(item)
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
|
|
|
|
|
// Determine noun type from data
|
|
|
|
|
const nounType = this.inferNounType(item)
|
2025-09-11 16:23:32 -07:00
|
|
|
const entityId = this.generateId(item)
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
entities.set(entityId, {
|
|
|
|
|
id: entityId,
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
type: nounType,
|
2025-09-11 16:23:32 -07:00
|
|
|
data: item,
|
|
|
|
|
vector: embedding,
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
confidence: 1.0,
|
2025-09-11 16:23:32 -07:00
|
|
|
metadata: {
|
|
|
|
|
...item,
|
|
|
|
|
_importedAt: Date.now()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Detect relationships using neural matching
|
|
|
|
|
await this.detectNeuralRelationships(item, entityId, entities, relationships)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { entities, relationships }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate embedding for any data
|
|
|
|
|
*/
|
|
|
|
|
private async generateEmbedding(data: any): Promise<Vector> {
|
|
|
|
|
// Convert to string for embedding
|
|
|
|
|
const text = this.dataToText(data)
|
|
|
|
|
|
|
|
|
|
// Check cache
|
|
|
|
|
if (this.embedCache.has(text)) {
|
|
|
|
|
return this.embedCache.get(text)!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate new embedding
|
|
|
|
|
const embedding = await (this.brain as any).embed(text)
|
|
|
|
|
|
|
|
|
|
// Cache it
|
|
|
|
|
this.embedCache.set(text, embedding)
|
|
|
|
|
|
|
|
|
|
return embedding
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert any data to text for embedding
|
|
|
|
|
*/
|
|
|
|
|
private dataToText(data: any): string {
|
|
|
|
|
if (typeof data === 'string') return data
|
|
|
|
|
|
|
|
|
|
if (typeof data === 'object') {
|
|
|
|
|
// Extract meaningful text from object
|
|
|
|
|
const parts: string[] = []
|
|
|
|
|
|
|
|
|
|
// Priority fields
|
|
|
|
|
const priorityFields = ['name', 'title', 'description', 'text', 'content', 'label', 'value']
|
|
|
|
|
for (const field of priorityFields) {
|
|
|
|
|
if (data[field]) {
|
|
|
|
|
parts.push(String(data[field]))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add other fields
|
|
|
|
|
for (const [key, value] of Object.entries(data)) {
|
|
|
|
|
if (!priorityFields.includes(key) && value) {
|
|
|
|
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
|
|
|
parts.push(`${key}: ${value}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parts.join(' ')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return JSON.stringify(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Detect relationships using neural matching
|
|
|
|
|
*/
|
|
|
|
|
private async detectNeuralRelationships(
|
|
|
|
|
item: any,
|
|
|
|
|
sourceId: string,
|
|
|
|
|
entities: Map<string, any>,
|
|
|
|
|
relationships: Map<string, any>
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
if (typeof item !== 'object') return
|
|
|
|
|
|
|
|
|
|
// Look for references to other entities
|
|
|
|
|
for (const [key, value] of Object.entries(item)) {
|
|
|
|
|
// Check if this looks like a reference
|
|
|
|
|
if (this.looksLikeReference(key, value)) {
|
|
|
|
|
const targetId = String(value)
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
const verbType = this.inferVerbType(key)
|
|
|
|
|
|
|
|
|
|
const relationId = `${sourceId}_${verbType}_${targetId}`
|
2025-09-11 16:23:32 -07:00
|
|
|
relationships.set(relationId, {
|
|
|
|
|
id: relationId,
|
|
|
|
|
from: sourceId,
|
|
|
|
|
to: targetId,
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
type: verbType,
|
|
|
|
|
weight: 1.0,
|
|
|
|
|
confidence: 1.0,
|
2025-09-11 16:23:32 -07:00
|
|
|
metadata: {
|
|
|
|
|
field: key,
|
|
|
|
|
_importedAt: Date.now()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Handle arrays of references
|
|
|
|
|
if (Array.isArray(value)) {
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
for (const arrayItem of value) {
|
|
|
|
|
if (this.looksLikeReference(key, arrayItem)) {
|
|
|
|
|
const targetId = String(arrayItem)
|
|
|
|
|
const verbType = this.inferVerbType(key)
|
|
|
|
|
|
|
|
|
|
const relationId = `${sourceId}_${verbType}_${targetId}`
|
2025-09-11 16:23:32 -07:00
|
|
|
relationships.set(relationId, {
|
|
|
|
|
id: relationId,
|
|
|
|
|
from: sourceId,
|
|
|
|
|
to: targetId,
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
type: verbType,
|
|
|
|
|
weight: 1.0,
|
|
|
|
|
confidence: 1.0,
|
2025-09-11 16:23:32 -07:00
|
|
|
metadata: {
|
|
|
|
|
field: key,
|
|
|
|
|
array: true,
|
|
|
|
|
_importedAt: Date.now()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if a field looks like a reference
|
|
|
|
|
*/
|
|
|
|
|
private looksLikeReference(key: string, value: any): boolean {
|
|
|
|
|
// Field name patterns that suggest references
|
|
|
|
|
const refPatterns = [
|
|
|
|
|
/[Ii]d$/, // ends with Id or id
|
|
|
|
|
/_id$/, // ends with _id
|
|
|
|
|
/^parent/i, // starts with parent
|
|
|
|
|
/^child/i, // starts with child
|
|
|
|
|
/^related/i, // starts with related
|
|
|
|
|
/^ref/i, // starts with ref
|
|
|
|
|
/^link/i, // starts with link
|
|
|
|
|
/^target/i, // starts with target
|
|
|
|
|
/^source/i, // starts with source
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// Check if field name matches patterns
|
|
|
|
|
const fieldLooksLikeRef = refPatterns.some(pattern => pattern.test(key))
|
|
|
|
|
|
|
|
|
|
// Check if value looks like an ID
|
|
|
|
|
const valueLooksLikeId = (
|
|
|
|
|
typeof value === 'string' ||
|
|
|
|
|
typeof value === 'number'
|
|
|
|
|
) && String(value).length > 0
|
|
|
|
|
|
|
|
|
|
return fieldLooksLikeRef && valueLooksLikeId
|
|
|
|
|
}
|
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Infer noun type from object structure using field heuristics
|
|
|
|
|
*/
|
|
|
|
|
private inferNounType(obj: any): NounType {
|
|
|
|
|
if (typeof obj !== 'object' || obj === null) return NounType.Thing
|
|
|
|
|
|
|
|
|
|
// Check for explicit type field
|
|
|
|
|
if (obj.type && typeof obj.type === 'string') {
|
|
|
|
|
const normalized = obj.type.charAt(0).toUpperCase() + obj.type.slice(1)
|
|
|
|
|
if (Object.values(NounType).includes(normalized as NounType)) {
|
|
|
|
|
return normalized as NounType
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Person heuristics
|
|
|
|
|
if (obj.email || obj.firstName || obj.lastName || obj.username || obj.age) {
|
|
|
|
|
return NounType.Person
|
|
|
|
|
}
|
|
|
|
|
// Organization heuristics
|
|
|
|
|
if (obj.companyName || obj.organizationId || obj.employees || obj.industry) {
|
|
|
|
|
return NounType.Organization
|
|
|
|
|
}
|
|
|
|
|
// Location heuristics
|
|
|
|
|
if (obj.latitude || obj.longitude || obj.address || obj.city || obj.country) {
|
|
|
|
|
return NounType.Location
|
|
|
|
|
}
|
|
|
|
|
// Document heuristics
|
|
|
|
|
if ((obj.content && (obj.title || obj.author)) || obj.documentType || obj.pages) {
|
|
|
|
|
return NounType.Document
|
|
|
|
|
}
|
|
|
|
|
// Event heuristics
|
|
|
|
|
if (obj.startTime || obj.endTime || obj.date || obj.eventType || obj.attendees) {
|
|
|
|
|
return NounType.Event
|
|
|
|
|
}
|
|
|
|
|
// Product heuristics
|
|
|
|
|
if (obj.price || obj.sku || obj.inventory || obj.productId) {
|
|
|
|
|
return NounType.Product
|
|
|
|
|
}
|
|
|
|
|
// Task heuristics
|
|
|
|
|
if ((obj.status && (obj.assignee || obj.dueDate)) || obj.priority || obj.completed !== undefined) {
|
|
|
|
|
return NounType.Task
|
|
|
|
|
}
|
|
|
|
|
// Dataset heuristics
|
|
|
|
|
if (Array.isArray(obj.data) || obj.rows || obj.columns || obj.schema) {
|
|
|
|
|
return NounType.Dataset
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NounType.Thing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Infer verb type from field name using common patterns
|
|
|
|
|
*/
|
|
|
|
|
private inferVerbType(fieldName: string): VerbType {
|
|
|
|
|
const field = fieldName.toLowerCase()
|
|
|
|
|
|
|
|
|
|
if (field.includes('parent') || field.includes('child') || field.includes('contain')) {
|
|
|
|
|
return VerbType.Contains
|
|
|
|
|
}
|
|
|
|
|
if (field.includes('owner') || field.includes('created') || field.includes('author')) {
|
|
|
|
|
return VerbType.Creates
|
|
|
|
|
}
|
|
|
|
|
if (field.includes('member') || field.includes('belong')) {
|
|
|
|
|
return VerbType.MemberOf
|
|
|
|
|
}
|
|
|
|
|
if (field.includes('depend') || field.includes('require')) {
|
|
|
|
|
return VerbType.DependsOn
|
|
|
|
|
}
|
|
|
|
|
if (field.includes('ref') || field.includes('link') || field.includes('source')) {
|
|
|
|
|
return VerbType.References
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VerbType.RelatedTo
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
/**
|
|
|
|
|
* Store processed data in brain
|
|
|
|
|
*/
|
2025-10-16 12:08:46 -07:00
|
|
|
private async storeInBrain(
|
|
|
|
|
neuralResults: {
|
|
|
|
|
entities: Map<string, any>
|
|
|
|
|
relationships: Map<string, any>
|
|
|
|
|
},
|
|
|
|
|
onProgress?: (progress: NeuralImportProgress) => void
|
|
|
|
|
): Promise<NeuralImportResult> {
|
2025-09-11 16:23:32 -07:00
|
|
|
const result: NeuralImportResult = {
|
|
|
|
|
entities: [],
|
|
|
|
|
relationships: [],
|
|
|
|
|
stats: {
|
|
|
|
|
totalProcessed: neuralResults.entities.size + neuralResults.relationships.size,
|
|
|
|
|
entitiesCreated: 0,
|
|
|
|
|
relationshipsCreated: 0,
|
|
|
|
|
averageConfidence: 0,
|
|
|
|
|
processingTimeMs: 0
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
let totalConfidence = 0
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Store entities
|
2025-10-16 12:08:46 -07:00
|
|
|
onProgress?.({
|
|
|
|
|
phase: 'storing-entities',
|
|
|
|
|
message: 'Storing entities...',
|
|
|
|
|
current: 0,
|
|
|
|
|
total: neuralResults.entities.size
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let entitiesProcessed = 0
|
2025-09-11 16:23:32 -07:00
|
|
|
for (const entity of neuralResults.entities.values()) {
|
|
|
|
|
const id = await this.brain.add({
|
|
|
|
|
data: entity.data,
|
|
|
|
|
type: entity.type,
|
|
|
|
|
metadata: entity.metadata,
|
|
|
|
|
vector: entity.vector
|
|
|
|
|
})
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Update entity ID for relationship mapping
|
|
|
|
|
entity.id = id
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
result.entities.push({
|
|
|
|
|
...entity,
|
|
|
|
|
id
|
|
|
|
|
})
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
result.stats.entitiesCreated++
|
|
|
|
|
totalConfidence += entity.confidence
|
2025-10-16 12:08:46 -07:00
|
|
|
entitiesProcessed++
|
|
|
|
|
|
|
|
|
|
// Report progress periodically
|
|
|
|
|
if (entitiesProcessed % 10 === 0 || entitiesProcessed === neuralResults.entities.size) {
|
|
|
|
|
onProgress?.({
|
|
|
|
|
phase: 'storing-entities',
|
|
|
|
|
message: `Storing entities: ${entitiesProcessed}/${neuralResults.entities.size}`,
|
|
|
|
|
current: entitiesProcessed,
|
|
|
|
|
total: neuralResults.entities.size,
|
|
|
|
|
entities: entitiesProcessed
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-09-11 16:23:32 -07:00
|
|
|
}
|
2025-10-16 12:08:46 -07:00
|
|
|
|
|
|
|
|
// Store relationships using batch processing
|
|
|
|
|
if (neuralResults.relationships.size > 0) {
|
|
|
|
|
onProgress?.({
|
|
|
|
|
phase: 'storing-relationships',
|
|
|
|
|
message: 'Preparing relationships...',
|
|
|
|
|
current: 0,
|
|
|
|
|
total: neuralResults.relationships.size
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Collect all relationship parameters
|
|
|
|
|
const relationshipParams: Array<{from: string; to: string; type: VerbType; weight?: number; metadata?: any}> = []
|
|
|
|
|
|
|
|
|
|
for (const relation of neuralResults.relationships.values()) {
|
|
|
|
|
// Map to actual entity IDs
|
|
|
|
|
const sourceEntity = Array.from(neuralResults.entities.values())
|
|
|
|
|
.find(e => e.id === relation.from)
|
|
|
|
|
const targetEntity = Array.from(neuralResults.entities.values())
|
|
|
|
|
.find(e => e.id === relation.to)
|
|
|
|
|
|
|
|
|
|
if (sourceEntity && targetEntity) {
|
|
|
|
|
relationshipParams.push({
|
|
|
|
|
from: sourceEntity.id,
|
|
|
|
|
to: targetEntity.id,
|
|
|
|
|
type: relation.type,
|
|
|
|
|
weight: relation.weight,
|
|
|
|
|
metadata: relation.metadata
|
|
|
|
|
})
|
|
|
|
|
totalConfidence += relation.confidence
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Batch create relationships with progress
|
|
|
|
|
if (relationshipParams.length > 0) {
|
|
|
|
|
const relationshipIds = await this.brain.relateMany({
|
|
|
|
|
items: relationshipParams,
|
|
|
|
|
parallel: true,
|
|
|
|
|
chunkSize: 100,
|
|
|
|
|
continueOnError: true,
|
|
|
|
|
onProgress: (done, total) => {
|
|
|
|
|
onProgress?.({
|
|
|
|
|
phase: 'storing-relationships',
|
|
|
|
|
message: `Building relationships: ${done}/${total}`,
|
|
|
|
|
current: done,
|
|
|
|
|
total: total,
|
|
|
|
|
entities: result.stats.entitiesCreated,
|
|
|
|
|
relationships: done
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-09-11 16:23:32 -07:00
|
|
|
})
|
2025-10-16 12:08:46 -07:00
|
|
|
|
|
|
|
|
// Map results back
|
|
|
|
|
relationshipIds.forEach((id, index) => {
|
|
|
|
|
if (id && relationshipParams[index]) {
|
|
|
|
|
result.relationships.push({
|
|
|
|
|
id,
|
|
|
|
|
from: relationshipParams[index].from,
|
|
|
|
|
to: relationshipParams[index].to,
|
|
|
|
|
type: relationshipParams[index].type,
|
|
|
|
|
weight: relationshipParams[index].weight || 1,
|
|
|
|
|
confidence: 0.5, // Default confidence
|
|
|
|
|
metadata: relationshipParams[index].metadata
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-09-11 16:23:32 -07:00
|
|
|
})
|
2025-10-16 12:08:46 -07:00
|
|
|
|
|
|
|
|
result.stats.relationshipsCreated = relationshipIds.length
|
2025-09-11 16:23:32 -07:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
// Calculate average confidence
|
|
|
|
|
const totalItems = result.stats.entitiesCreated + result.stats.relationshipsCreated
|
|
|
|
|
result.stats.averageConfidence = totalItems > 0 ? totalConfidence / totalItems : 0
|
2025-10-16 12:08:46 -07:00
|
|
|
|
2025-09-11 16:23:32 -07:00
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper methods for parsing different formats
|
|
|
|
|
|
|
|
|
|
private detectFormat(data: string): string {
|
|
|
|
|
const trimmed = data.trim()
|
|
|
|
|
|
|
|
|
|
// JSON
|
|
|
|
|
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
|
|
|
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
|
|
|
return 'json'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSV (has commas and newlines)
|
|
|
|
|
if (trimmed.includes(',') && trimmed.includes('\n')) {
|
|
|
|
|
return 'csv'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// YAML (has colons and indentation)
|
|
|
|
|
if (trimmed.includes(':') && (trimmed.includes('\n ') || trimmed.includes('\n\t'))) {
|
|
|
|
|
return 'yaml'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Markdown (has headers)
|
|
|
|
|
if (trimmed.includes('#') || trimmed.includes('```')) {
|
|
|
|
|
return 'markdown'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HTML/XML
|
|
|
|
|
if (trimmed.includes('<') && trimmed.includes('>')) {
|
|
|
|
|
return trimmed.toLowerCase().includes('<!doctype html') ? 'html' : 'xml'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'text'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseCSV(data: string): any[] {
|
|
|
|
|
// Reuse the CSV parser from neural import
|
|
|
|
|
const lines = data.split('\n').filter(l => l.trim())
|
|
|
|
|
if (lines.length === 0) return []
|
|
|
|
|
|
|
|
|
|
const headers = lines[0].split(',').map(h => h.trim())
|
|
|
|
|
const results = []
|
|
|
|
|
|
|
|
|
|
for (let i = 1; i < lines.length; i++) {
|
|
|
|
|
const values = lines[i].split(',').map(v => v.trim())
|
|
|
|
|
const obj: any = {}
|
|
|
|
|
headers.forEach((header, index) => {
|
|
|
|
|
obj[header] = values[index] || ''
|
|
|
|
|
})
|
|
|
|
|
results.push(obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseYAML(data: string): any[] {
|
|
|
|
|
// Simple YAML parser
|
|
|
|
|
const results = []
|
|
|
|
|
const lines = data.split('\n')
|
|
|
|
|
let current: any = null
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
const trimmed = line.trim()
|
|
|
|
|
if (!trimmed || trimmed.startsWith('#')) continue
|
|
|
|
|
|
|
|
|
|
if (trimmed.startsWith('- ')) {
|
|
|
|
|
// Array item
|
|
|
|
|
const value = trimmed.substring(2)
|
|
|
|
|
if (!current) {
|
|
|
|
|
results.push(value)
|
|
|
|
|
} else {
|
|
|
|
|
if (!current._items) current._items = []
|
|
|
|
|
current._items.push(value)
|
|
|
|
|
}
|
|
|
|
|
} else if (trimmed.includes(':')) {
|
|
|
|
|
// Key-value
|
|
|
|
|
const [key, ...valueParts] = trimmed.split(':')
|
|
|
|
|
const value = valueParts.join(':').trim()
|
|
|
|
|
|
|
|
|
|
if (!current) {
|
|
|
|
|
current = {}
|
|
|
|
|
results.push(current)
|
|
|
|
|
}
|
|
|
|
|
current[key.trim()] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results.length > 0 ? results : [{ text: data }]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseMarkdown(data: string): any[] {
|
|
|
|
|
const results = []
|
|
|
|
|
const lines = data.split('\n')
|
|
|
|
|
|
|
|
|
|
let current: any = null
|
|
|
|
|
let inCodeBlock = false
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
if (line.startsWith('```')) {
|
|
|
|
|
inCodeBlock = !inCodeBlock
|
|
|
|
|
if (inCodeBlock && current) {
|
|
|
|
|
current.code = ''
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inCodeBlock && current) {
|
|
|
|
|
current.code += line + '\n'
|
|
|
|
|
} else if (line.startsWith('#')) {
|
|
|
|
|
// Header
|
|
|
|
|
const level = line.match(/^#+/)?.[0].length || 1
|
|
|
|
|
const text = line.replace(/^#+\s*/, '')
|
|
|
|
|
current = {
|
|
|
|
|
type: 'heading',
|
|
|
|
|
level,
|
|
|
|
|
text
|
|
|
|
|
}
|
|
|
|
|
results.push(current)
|
|
|
|
|
} else if (line.trim()) {
|
|
|
|
|
// Paragraph
|
|
|
|
|
if (!current || current.type !== 'paragraph') {
|
|
|
|
|
current = {
|
|
|
|
|
type: 'paragraph',
|
|
|
|
|
text: ''
|
|
|
|
|
}
|
|
|
|
|
results.push(current)
|
|
|
|
|
}
|
|
|
|
|
current.text += line + ' '
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseHTML(data: string): any[] {
|
|
|
|
|
// Simple HTML text extraction
|
|
|
|
|
const text = data
|
|
|
|
|
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') // Remove scripts
|
|
|
|
|
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '') // Remove styles
|
|
|
|
|
.replace(/<[^>]+>/g, ' ') // Remove tags
|
|
|
|
|
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
|
|
|
.trim()
|
|
|
|
|
|
|
|
|
|
return this.extractFromText(text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private generateId(data: any): string {
|
|
|
|
|
// Generate deterministic ID based on content
|
|
|
|
|
const text = this.dataToText(data)
|
|
|
|
|
const hash = this.simpleHash(text)
|
|
|
|
|
return `import_${hash}_${Date.now()}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private simpleHash(text: string): string {
|
|
|
|
|
let hash = 0
|
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
|
|
|
const char = text.charCodeAt(i)
|
|
|
|
|
hash = ((hash << 5) - hash) + char
|
|
|
|
|
hash = hash & hash
|
|
|
|
|
}
|
|
|
|
|
return Math.abs(hash).toString(36)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async hashBinary(data: Uint8Array): Promise<string> {
|
|
|
|
|
// Simple binary hash
|
|
|
|
|
let hash = 0
|
|
|
|
|
for (let i = 0; i < Math.min(data.length, 1000); i++) {
|
|
|
|
|
hash = ((hash << 5) - hash) + data[i]
|
|
|
|
|
hash = hash & hash
|
|
|
|
|
}
|
|
|
|
|
return Math.abs(hash).toString(36)
|
|
|
|
|
}
|
|
|
|
|
}
|