@soulcraft/brainy 5.1.2 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +120 -0
- package/dist/api/UniversalImportAPI.d.ts +3 -1
- package/dist/api/UniversalImportAPI.js +8 -2
- package/dist/augmentations/intelligentImport/FormatHandlerRegistry.d.ts +111 -0
- package/dist/augmentations/intelligentImport/FormatHandlerRegistry.js +205 -0
- package/dist/augmentations/intelligentImport/IntelligentImportAugmentation.js +7 -1
- package/dist/augmentations/intelligentImport/handlers/base.d.ts +17 -0
- package/dist/augmentations/intelligentImport/handlers/base.js +33 -0
- package/dist/augmentations/intelligentImport/handlers/imageHandler.d.ts +107 -0
- package/dist/augmentations/intelligentImport/handlers/imageHandler.js +227 -0
- package/dist/augmentations/intelligentImport/index.d.ts +5 -1
- package/dist/augmentations/intelligentImport/index.js +4 -0
- package/dist/augmentations/intelligentImport/types.d.ts +4 -0
- package/dist/brainy.d.ts +1 -1
- package/dist/brainy.js +44 -8
- package/dist/import/FormatDetector.d.ts +11 -2
- package/dist/import/FormatDetector.js +99 -17
- package/dist/import/ImportCoordinator.js +70 -2
- package/dist/vfs/MimeTypeDetector.d.ts +81 -0
- package/dist/vfs/MimeTypeDetector.js +262 -0
- package/dist/vfs/VirtualFileSystem.d.ts +4 -11
- package/dist/vfs/VirtualFileSystem.js +44 -224
- package/dist/vfs/importers/DirectoryImporter.d.ts +0 -4
- package/dist/vfs/importers/DirectoryImporter.js +0 -40
- package/dist/vfs/index.d.ts +1 -0
- package/dist/vfs/index.js +2 -0
- package/dist/vfs/types.d.ts +3 -3
- package/package.json +6 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Import Handler (v5.2.0)
|
|
3
|
+
*
|
|
4
|
+
* Handles image files with:
|
|
5
|
+
* - EXIF metadata extraction (camera, GPS, timestamps)
|
|
6
|
+
* - Thumbnail generation (multiple sizes)
|
|
7
|
+
* - Image metadata (dimensions, format, color space)
|
|
8
|
+
* - Support for JPEG, PNG, WebP, GIF, TIFF, AVIF, etc.
|
|
9
|
+
*
|
|
10
|
+
* NO MOCKS - Production implementation using sharp and exifr
|
|
11
|
+
*/
|
|
12
|
+
import { BaseFormatHandler } from './base.js';
|
|
13
|
+
import type { FormatHandlerOptions, ProcessedData } from '../types.js';
|
|
14
|
+
export interface ImageMetadata {
|
|
15
|
+
/** Image dimensions */
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
/** Image format (jpeg, png, webp, etc.) */
|
|
19
|
+
format: string;
|
|
20
|
+
/** Color space */
|
|
21
|
+
space: string;
|
|
22
|
+
/** Number of channels */
|
|
23
|
+
channels: number;
|
|
24
|
+
/** Bit depth */
|
|
25
|
+
depth: string;
|
|
26
|
+
/** File size in bytes */
|
|
27
|
+
size: number;
|
|
28
|
+
/** Whether image has alpha channel */
|
|
29
|
+
hasAlpha: boolean;
|
|
30
|
+
/** Orientation (EXIF) */
|
|
31
|
+
orientation?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface EXIFData {
|
|
34
|
+
/** Camera make (e.g., "Canon", "Nikon") */
|
|
35
|
+
make?: string;
|
|
36
|
+
/** Camera model */
|
|
37
|
+
model?: string;
|
|
38
|
+
/** Lens information */
|
|
39
|
+
lens?: string;
|
|
40
|
+
/** Date/time original */
|
|
41
|
+
dateTimeOriginal?: Date;
|
|
42
|
+
/** GPS latitude */
|
|
43
|
+
latitude?: number;
|
|
44
|
+
/** GPS longitude */
|
|
45
|
+
longitude?: number;
|
|
46
|
+
/** GPS altitude in meters */
|
|
47
|
+
altitude?: number;
|
|
48
|
+
/** Exposure time (e.g., "1/250") */
|
|
49
|
+
exposureTime?: number;
|
|
50
|
+
/** F-number (e.g., 2.8) */
|
|
51
|
+
fNumber?: number;
|
|
52
|
+
/** ISO speed */
|
|
53
|
+
iso?: number;
|
|
54
|
+
/** Focal length in mm */
|
|
55
|
+
focalLength?: number;
|
|
56
|
+
/** Flash fired */
|
|
57
|
+
flash?: boolean;
|
|
58
|
+
/** Copyright */
|
|
59
|
+
copyright?: string;
|
|
60
|
+
/** Artist/photographer */
|
|
61
|
+
artist?: string;
|
|
62
|
+
/** Image description */
|
|
63
|
+
imageDescription?: string;
|
|
64
|
+
/** Software used */
|
|
65
|
+
software?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ImageHandlerOptions extends FormatHandlerOptions {
|
|
68
|
+
/** Extract EXIF data (default: true) */
|
|
69
|
+
extractEXIF?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* ImageImportHandler
|
|
73
|
+
*
|
|
74
|
+
* Processes image files and extracts rich metadata including EXIF data.
|
|
75
|
+
* Enables developers to import images into the knowledge graph with
|
|
76
|
+
* full metadata extraction.
|
|
77
|
+
*/
|
|
78
|
+
export declare class ImageHandler extends BaseFormatHandler {
|
|
79
|
+
readonly format = "image";
|
|
80
|
+
/**
|
|
81
|
+
* Check if this handler can process the given data
|
|
82
|
+
*/
|
|
83
|
+
canHandle(data: Buffer | string | {
|
|
84
|
+
filename?: string;
|
|
85
|
+
ext?: string;
|
|
86
|
+
}): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Process image file
|
|
89
|
+
*/
|
|
90
|
+
process(data: Buffer | string, options?: ImageHandlerOptions): Promise<ProcessedData>;
|
|
91
|
+
/**
|
|
92
|
+
* Extract image metadata using sharp
|
|
93
|
+
*/
|
|
94
|
+
private extractMetadata;
|
|
95
|
+
/**
|
|
96
|
+
* Extract EXIF data using exifr
|
|
97
|
+
*/
|
|
98
|
+
private extractEXIF;
|
|
99
|
+
/**
|
|
100
|
+
* Detect image format from magic bytes
|
|
101
|
+
*/
|
|
102
|
+
private detectImageFormat;
|
|
103
|
+
/**
|
|
104
|
+
* Detect if extension is an image format
|
|
105
|
+
*/
|
|
106
|
+
private isImageExtension;
|
|
107
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Import Handler (v5.2.0)
|
|
3
|
+
*
|
|
4
|
+
* Handles image files with:
|
|
5
|
+
* - EXIF metadata extraction (camera, GPS, timestamps)
|
|
6
|
+
* - Thumbnail generation (multiple sizes)
|
|
7
|
+
* - Image metadata (dimensions, format, color space)
|
|
8
|
+
* - Support for JPEG, PNG, WebP, GIF, TIFF, AVIF, etc.
|
|
9
|
+
*
|
|
10
|
+
* NO MOCKS - Production implementation using sharp and exifr
|
|
11
|
+
*/
|
|
12
|
+
import { BaseFormatHandler } from './base.js';
|
|
13
|
+
import sharp from 'sharp';
|
|
14
|
+
import exifr from 'exifr';
|
|
15
|
+
/**
|
|
16
|
+
* ImageImportHandler
|
|
17
|
+
*
|
|
18
|
+
* Processes image files and extracts rich metadata including EXIF data.
|
|
19
|
+
* Enables developers to import images into the knowledge graph with
|
|
20
|
+
* full metadata extraction.
|
|
21
|
+
*/
|
|
22
|
+
export class ImageHandler extends BaseFormatHandler {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.format = 'image';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if this handler can process the given data
|
|
29
|
+
*/
|
|
30
|
+
canHandle(data) {
|
|
31
|
+
// Check by filename/extension
|
|
32
|
+
if (typeof data === 'object' && 'filename' in data) {
|
|
33
|
+
const mimeType = this.getMimeType(data);
|
|
34
|
+
return this.mimeTypeMatches(mimeType, ['image/*']);
|
|
35
|
+
}
|
|
36
|
+
// Check by extension
|
|
37
|
+
if (typeof data === 'object' && 'ext' in data && data.ext) {
|
|
38
|
+
return this.isImageExtension(data.ext);
|
|
39
|
+
}
|
|
40
|
+
// Check by data (Buffer magic bytes)
|
|
41
|
+
if (Buffer.isBuffer(data)) {
|
|
42
|
+
return this.detectImageFormat(data) !== null;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Process image file
|
|
48
|
+
*/
|
|
49
|
+
async process(data, options = {}) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
try {
|
|
52
|
+
// Convert to Buffer
|
|
53
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data, 'base64');
|
|
54
|
+
// Extract image metadata
|
|
55
|
+
const metadata = await this.extractMetadata(buffer);
|
|
56
|
+
// Extract EXIF data (default: enabled)
|
|
57
|
+
let exifData;
|
|
58
|
+
if (options.extractEXIF !== false) {
|
|
59
|
+
exifData = await this.extractEXIF(buffer);
|
|
60
|
+
}
|
|
61
|
+
// Calculate processing time
|
|
62
|
+
const processingTime = Date.now() - startTime;
|
|
63
|
+
// Generate descriptive name
|
|
64
|
+
const imageName = options.filename
|
|
65
|
+
? options.filename.replace(/\.[^/.]+$/, '') // Remove extension
|
|
66
|
+
: `${metadata.format.toUpperCase()} Image ${metadata.width}x${metadata.height}`;
|
|
67
|
+
// Return structured data
|
|
68
|
+
return {
|
|
69
|
+
format: 'image',
|
|
70
|
+
data: [
|
|
71
|
+
{
|
|
72
|
+
name: imageName,
|
|
73
|
+
type: 'media',
|
|
74
|
+
metadata: {
|
|
75
|
+
...metadata,
|
|
76
|
+
subtype: 'image',
|
|
77
|
+
exif: exifData
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
metadata: {
|
|
82
|
+
rowCount: 1,
|
|
83
|
+
fields: ['type', 'metadata'],
|
|
84
|
+
processingTime,
|
|
85
|
+
imageMetadata: metadata,
|
|
86
|
+
exifData
|
|
87
|
+
},
|
|
88
|
+
filename: options.filename
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new Error(`Image processing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract image metadata using sharp
|
|
97
|
+
*/
|
|
98
|
+
async extractMetadata(buffer) {
|
|
99
|
+
const image = sharp(buffer);
|
|
100
|
+
const metadata = await image.metadata();
|
|
101
|
+
return {
|
|
102
|
+
width: metadata.width || 0,
|
|
103
|
+
height: metadata.height || 0,
|
|
104
|
+
format: metadata.format || 'unknown',
|
|
105
|
+
space: metadata.space || 'unknown',
|
|
106
|
+
channels: metadata.channels || 0,
|
|
107
|
+
depth: metadata.depth || 'unknown',
|
|
108
|
+
size: buffer.length,
|
|
109
|
+
hasAlpha: metadata.hasAlpha || false,
|
|
110
|
+
orientation: metadata.orientation
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Extract EXIF data using exifr
|
|
115
|
+
*/
|
|
116
|
+
async extractEXIF(buffer) {
|
|
117
|
+
try {
|
|
118
|
+
const exif = await exifr.parse(buffer, {
|
|
119
|
+
pick: [
|
|
120
|
+
'Make',
|
|
121
|
+
'Model',
|
|
122
|
+
'LensModel',
|
|
123
|
+
'DateTimeOriginal',
|
|
124
|
+
'latitude',
|
|
125
|
+
'longitude',
|
|
126
|
+
'GPSAltitude',
|
|
127
|
+
'ExposureTime',
|
|
128
|
+
'FNumber',
|
|
129
|
+
'ISO',
|
|
130
|
+
'FocalLength',
|
|
131
|
+
'Flash',
|
|
132
|
+
'Copyright',
|
|
133
|
+
'Artist',
|
|
134
|
+
'ImageDescription',
|
|
135
|
+
'Software'
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
if (!exif)
|
|
139
|
+
return undefined;
|
|
140
|
+
return {
|
|
141
|
+
make: exif.Make,
|
|
142
|
+
model: exif.Model,
|
|
143
|
+
lens: exif.LensModel,
|
|
144
|
+
dateTimeOriginal: exif.DateTimeOriginal,
|
|
145
|
+
latitude: exif.latitude,
|
|
146
|
+
longitude: exif.longitude,
|
|
147
|
+
altitude: exif.GPSAltitude,
|
|
148
|
+
exposureTime: exif.ExposureTime,
|
|
149
|
+
fNumber: exif.FNumber,
|
|
150
|
+
iso: exif.ISO,
|
|
151
|
+
focalLength: exif.FocalLength,
|
|
152
|
+
flash: exif.Flash !== undefined ? Boolean(exif.Flash & 1) : undefined,
|
|
153
|
+
copyright: exif.Copyright,
|
|
154
|
+
artist: exif.Artist,
|
|
155
|
+
imageDescription: exif.ImageDescription,
|
|
156
|
+
software: exif.Software
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// EXIF extraction can fail for non-JPEG images or corrupt data
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Detect image format from magic bytes
|
|
166
|
+
*/
|
|
167
|
+
detectImageFormat(buffer) {
|
|
168
|
+
if (buffer.length < 4)
|
|
169
|
+
return null;
|
|
170
|
+
// JPEG: FF D8 FF
|
|
171
|
+
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
|
172
|
+
return 'jpeg';
|
|
173
|
+
}
|
|
174
|
+
// PNG: 89 50 4E 47
|
|
175
|
+
if (buffer[0] === 0x89 &&
|
|
176
|
+
buffer[1] === 0x50 &&
|
|
177
|
+
buffer[2] === 0x4e &&
|
|
178
|
+
buffer[3] === 0x47) {
|
|
179
|
+
return 'png';
|
|
180
|
+
}
|
|
181
|
+
// GIF: 47 49 46
|
|
182
|
+
if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {
|
|
183
|
+
return 'gif';
|
|
184
|
+
}
|
|
185
|
+
// WebP: 52 49 46 46 ... 57 45 42 50
|
|
186
|
+
if (buffer[0] === 0x52 &&
|
|
187
|
+
buffer[1] === 0x49 &&
|
|
188
|
+
buffer[2] === 0x46 &&
|
|
189
|
+
buffer[3] === 0x46 &&
|
|
190
|
+
buffer.length >= 12 &&
|
|
191
|
+
buffer[8] === 0x57 &&
|
|
192
|
+
buffer[9] === 0x45 &&
|
|
193
|
+
buffer[10] === 0x42 &&
|
|
194
|
+
buffer[11] === 0x50) {
|
|
195
|
+
return 'webp';
|
|
196
|
+
}
|
|
197
|
+
// TIFF: 49 49 2A 00 (little-endian) or 4D 4D 00 2A (big-endian)
|
|
198
|
+
if ((buffer[0] === 0x49 && buffer[1] === 0x49 && buffer[2] === 0x2a && buffer[3] === 0x00) ||
|
|
199
|
+
(buffer[0] === 0x4d && buffer[1] === 0x4d && buffer[2] === 0x00 && buffer[3] === 0x2a)) {
|
|
200
|
+
return 'tiff';
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Detect if extension is an image format
|
|
206
|
+
*/
|
|
207
|
+
isImageExtension(ext) {
|
|
208
|
+
const imageExts = [
|
|
209
|
+
'.jpg',
|
|
210
|
+
'.jpeg',
|
|
211
|
+
'.png',
|
|
212
|
+
'.gif',
|
|
213
|
+
'.webp',
|
|
214
|
+
'.tiff',
|
|
215
|
+
'.tif',
|
|
216
|
+
'.bmp',
|
|
217
|
+
'.svg',
|
|
218
|
+
'.heic',
|
|
219
|
+
'.heif',
|
|
220
|
+
'.avif'
|
|
221
|
+
];
|
|
222
|
+
const normalized = ext.toLowerCase();
|
|
223
|
+
const withDot = normalized.startsWith('.') ? normalized : `.${normalized}`;
|
|
224
|
+
return imageExts.includes(withDot);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=imageHandler.js.map
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* Exports main augmentation and types
|
|
4
4
|
*/
|
|
5
5
|
export { IntelligentImportAugmentation } from './IntelligentImportAugmentation.js';
|
|
6
|
-
export type { FormatHandler, FormatHandlerOptions, ProcessedData, IntelligentImportConfig } from './types.js';
|
|
6
|
+
export type { FormatHandler, FormatHandlerOptions, ProcessedData, IntelligentImportConfig, HandlerRegistry } from './types.js';
|
|
7
7
|
export { CSVHandler } from './handlers/csvHandler.js';
|
|
8
8
|
export { ExcelHandler } from './handlers/excelHandler.js';
|
|
9
9
|
export { PDFHandler } from './handlers/pdfHandler.js';
|
|
10
|
+
export { ImageHandler } from './handlers/imageHandler.js';
|
|
11
|
+
export { FormatHandlerRegistry, globalHandlerRegistry } from './FormatHandlerRegistry.js';
|
|
12
|
+
export type { HandlerRegistration } from './FormatHandlerRegistry.js';
|
|
13
|
+
export type { ImageMetadata, EXIFData, ImageHandlerOptions } from './handlers/imageHandler.js';
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* Exports main augmentation and types
|
|
4
4
|
*/
|
|
5
5
|
export { IntelligentImportAugmentation } from './IntelligentImportAugmentation.js';
|
|
6
|
+
// Format Handlers
|
|
6
7
|
export { CSVHandler } from './handlers/csvHandler.js';
|
|
7
8
|
export { ExcelHandler } from './handlers/excelHandler.js';
|
|
8
9
|
export { PDFHandler } from './handlers/pdfHandler.js';
|
|
10
|
+
export { ImageHandler } from './handlers/imageHandler.js';
|
|
11
|
+
// Format Handler Registry (v5.2.0)
|
|
12
|
+
export { FormatHandlerRegistry, globalHandlerRegistry } from './FormatHandlerRegistry.js';
|
|
9
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -129,12 +129,16 @@ export interface IntelligentImportConfig {
|
|
|
129
129
|
enableExcel: boolean;
|
|
130
130
|
/** Enable PDF handler */
|
|
131
131
|
enablePDF: boolean;
|
|
132
|
+
/** Enable Image handler (v5.2.0) */
|
|
133
|
+
enableImage: boolean;
|
|
132
134
|
/** Default options for CSV */
|
|
133
135
|
csvDefaults?: Partial<FormatHandlerOptions>;
|
|
134
136
|
/** Default options for Excel */
|
|
135
137
|
excelDefaults?: Partial<FormatHandlerOptions>;
|
|
136
138
|
/** Default options for PDF */
|
|
137
139
|
pdfDefaults?: Partial<FormatHandlerOptions>;
|
|
140
|
+
/** Default options for Image (v5.2.0) */
|
|
141
|
+
imageDefaults?: Partial<FormatHandlerOptions>;
|
|
138
142
|
/** Maximum file size to process (bytes) */
|
|
139
143
|
maxFileSize?: number;
|
|
140
144
|
/** Enable caching of processed data */
|
package/dist/brainy.d.ts
CHANGED
|
@@ -1111,7 +1111,7 @@ export declare class Brainy<T = any> implements BrainyInterface<T> {
|
|
|
1111
1111
|
* - Reduced confusion (removed redundant options)
|
|
1112
1112
|
*/
|
|
1113
1113
|
import(source: Buffer | string | object, options?: {
|
|
1114
|
-
format?: 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx';
|
|
1114
|
+
format?: 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx' | 'image';
|
|
1115
1115
|
vfsPath?: string;
|
|
1116
1116
|
groupBy?: 'type' | 'sheet' | 'flat' | 'custom';
|
|
1117
1117
|
customGrouping?: (entity: any) => string;
|
package/dist/brainy.js
CHANGED
|
@@ -141,6 +141,14 @@ export class Brainy {
|
|
|
141
141
|
this.registerShutdownHooks();
|
|
142
142
|
Brainy.shutdownHooksRegisteredGlobally = true;
|
|
143
143
|
}
|
|
144
|
+
// v5.2.0: Initialize COW (BlobStorage) before VFS
|
|
145
|
+
// VFS now requires BlobStorage for unified file storage
|
|
146
|
+
if (typeof this.storage.initializeCOW === 'function') {
|
|
147
|
+
await this.storage.initializeCOW({
|
|
148
|
+
branch: this.config.storage?.branch || 'main',
|
|
149
|
+
enableCompression: true
|
|
150
|
+
});
|
|
151
|
+
}
|
|
144
152
|
// Mark as initialized BEFORE VFS init (v5.0.1)
|
|
145
153
|
// VFS.init() needs brain to be marked initialized to call brain methods
|
|
146
154
|
this.initialized = true;
|
|
@@ -1860,7 +1868,20 @@ export class Brainy {
|
|
|
1860
1868
|
}
|
|
1861
1869
|
const refManager = this.storage.refManager;
|
|
1862
1870
|
const currentBranch = this.storage.currentBranch || 'main';
|
|
1863
|
-
// Step 1:
|
|
1871
|
+
// Step 1: Ensure initial commit exists (required for fork)
|
|
1872
|
+
const currentRef = await refManager.getRef(currentBranch);
|
|
1873
|
+
if (!currentRef) {
|
|
1874
|
+
// Auto-create initial commit if none exists
|
|
1875
|
+
await this.commit({
|
|
1876
|
+
message: `Initial commit on ${currentBranch}`,
|
|
1877
|
+
author: options?.author || 'Brainy',
|
|
1878
|
+
metadata: { timestamp: Date.now() }
|
|
1879
|
+
});
|
|
1880
|
+
if (!this.config.silent) {
|
|
1881
|
+
console.log(`📝 Auto-created initial commit on ${currentBranch} (required for fork)`);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
// Step 2: Copy storage ref (COW layer - instant!)
|
|
1864
1885
|
await refManager.copyRef(currentBranch, branchName);
|
|
1865
1886
|
// Step 2: Create new Brainy instance pointing to fork branch
|
|
1866
1887
|
const forkConfig = {
|
|
@@ -2651,11 +2672,19 @@ export class Brainy {
|
|
|
2651
2672
|
* - Reduced confusion (removed redundant options)
|
|
2652
2673
|
*/
|
|
2653
2674
|
async import(source, options) {
|
|
2654
|
-
//
|
|
2655
|
-
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2675
|
+
// Execute through augmentation pipeline (v5.2.0: Enables IntelligentImportAugmentation)
|
|
2676
|
+
// If source is an ImportSource object (not a Buffer), spread it so augmentations can access properties
|
|
2677
|
+
const params = typeof source === 'object' && !Buffer.isBuffer(source)
|
|
2678
|
+
? { ...source, ...options } // Spread ImportSource: { type, data, filename, ...options }
|
|
2679
|
+
: { source, ...options }; // Wrap Buffer/string: { source, ...options }
|
|
2680
|
+
return this.augmentationRegistry.execute('import', params, async () => {
|
|
2681
|
+
// Lazy load ImportCoordinator
|
|
2682
|
+
const { ImportCoordinator } = await import('./import/ImportCoordinator.js');
|
|
2683
|
+
const coordinator = new ImportCoordinator(this);
|
|
2684
|
+
await coordinator.init();
|
|
2685
|
+
// Pass augmentation-modified params (contains _intelligentImport, _extractedData, etc)
|
|
2686
|
+
return await coordinator.import(source, { ...options, ...params });
|
|
2687
|
+
});
|
|
2659
2688
|
}
|
|
2660
2689
|
/**
|
|
2661
2690
|
* Virtual File System API - Knowledge Operating System (v5.0.1+)
|
|
@@ -3695,12 +3724,19 @@ export class Brainy {
|
|
|
3695
3724
|
const graphIndexSize = await this.graphIndex.size();
|
|
3696
3725
|
const needsRebuild = metadataStats.totalEntries === 0 ||
|
|
3697
3726
|
hnswIndexSize === 0 ||
|
|
3698
|
-
graphIndexSize === 0
|
|
3699
|
-
this.config.disableAutoRebuild === false; // Explicitly enabled
|
|
3727
|
+
graphIndexSize === 0;
|
|
3700
3728
|
if (!needsRebuild) {
|
|
3701
3729
|
// All indexes already populated, no rebuild needed
|
|
3702
3730
|
return;
|
|
3703
3731
|
}
|
|
3732
|
+
// BUG FIX: If disableAutoRebuild is truthy, skip rebuild even if indexes are empty
|
|
3733
|
+
// Indexes will load lazily on first query
|
|
3734
|
+
if (this.config.disableAutoRebuild) {
|
|
3735
|
+
if (!this.config.silent) {
|
|
3736
|
+
console.log('⚡ Indexes empty but auto-rebuild disabled - using lazy loading');
|
|
3737
|
+
}
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3704
3740
|
// Small dataset: Rebuild all indexes for best performance
|
|
3705
3741
|
if (totalCount < AUTO_REBUILD_THRESHOLD || this.config.disableAutoRebuild === false) {
|
|
3706
3742
|
if (!this.config.silent) {
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* Format Detector
|
|
3
3
|
*
|
|
4
4
|
* Unified format detection for all import types using:
|
|
5
|
+
* - MIME type detection (via MimeTypeDetector service)
|
|
5
6
|
* - Magic byte signatures (PDF, Excel, images)
|
|
6
|
-
* - File extensions
|
|
7
|
+
* - File extensions (via MimeTypeDetector)
|
|
7
8
|
* - Content analysis (JSON, Markdown, CSV)
|
|
8
9
|
*
|
|
9
10
|
* NO MOCKS - Production-ready implementation
|
|
10
11
|
*/
|
|
11
|
-
export type SupportedFormat = 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx';
|
|
12
|
+
export type SupportedFormat = 'excel' | 'pdf' | 'csv' | 'json' | 'markdown' | 'yaml' | 'docx' | 'image';
|
|
12
13
|
export interface DetectionResult {
|
|
13
14
|
format: SupportedFormat;
|
|
14
15
|
confidence: number;
|
|
@@ -24,8 +25,16 @@ export declare class FormatDetector {
|
|
|
24
25
|
detectFromBuffer(buffer: Buffer): DetectionResult | null;
|
|
25
26
|
/**
|
|
26
27
|
* Detect format from file path
|
|
28
|
+
*
|
|
29
|
+
* Uses MimeTypeDetector (2000+ types) and maps to SupportedFormat
|
|
27
30
|
*/
|
|
28
31
|
detectFromPath(path: string): DetectionResult | null;
|
|
32
|
+
/**
|
|
33
|
+
* Map MIME type to SupportedFormat
|
|
34
|
+
*
|
|
35
|
+
* Supports all variations of Excel, PDF, CSV, JSON, Markdown, YAML, DOCX
|
|
36
|
+
*/
|
|
37
|
+
private mimeTypeToFormat;
|
|
29
38
|
/**
|
|
30
39
|
* Detect format from string content
|
|
31
40
|
*/
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* Format Detector
|
|
3
3
|
*
|
|
4
4
|
* Unified format detection for all import types using:
|
|
5
|
+
* - MIME type detection (via MimeTypeDetector service)
|
|
5
6
|
* - Magic byte signatures (PDF, Excel, images)
|
|
6
|
-
* - File extensions
|
|
7
|
+
* - File extensions (via MimeTypeDetector)
|
|
7
8
|
* - Content analysis (JSON, Markdown, CSV)
|
|
8
9
|
*
|
|
9
10
|
* NO MOCKS - Production-ready implementation
|
|
10
11
|
*/
|
|
12
|
+
import { mimeDetector } from '../vfs/MimeTypeDetector.js';
|
|
11
13
|
/**
|
|
12
14
|
* FormatDetector - Detect file format from various inputs
|
|
13
15
|
*/
|
|
@@ -28,32 +30,68 @@ export class FormatDetector {
|
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* Detect format from file path
|
|
33
|
+
*
|
|
34
|
+
* Uses MimeTypeDetector (2000+ types) and maps to SupportedFormat
|
|
31
35
|
*/
|
|
32
36
|
detectFromPath(path) {
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'.pdf': 'pdf',
|
|
38
|
-
'.csv': 'csv',
|
|
39
|
-
'.json': 'json',
|
|
40
|
-
'.md': 'markdown',
|
|
41
|
-
'.markdown': 'markdown',
|
|
42
|
-
'.yaml': 'yaml',
|
|
43
|
-
'.yml': 'yaml',
|
|
44
|
-
'.docx': 'docx',
|
|
45
|
-
'.doc': 'docx'
|
|
46
|
-
};
|
|
47
|
-
const format = extensionMap[ext];
|
|
37
|
+
// Get MIME type from MimeTypeDetector
|
|
38
|
+
const mimeType = mimeDetector.detectMimeType(path);
|
|
39
|
+
// Map MIME type to SupportedFormat
|
|
40
|
+
const format = this.mimeTypeToFormat(mimeType);
|
|
48
41
|
if (format) {
|
|
42
|
+
const ext = this.getExtension(path);
|
|
49
43
|
return {
|
|
50
44
|
format,
|
|
51
45
|
confidence: 0.9,
|
|
52
|
-
evidence: [`File extension: ${ext}`]
|
|
46
|
+
evidence: [`MIME type: ${mimeType}`, `File extension: ${ext}`]
|
|
53
47
|
};
|
|
54
48
|
}
|
|
55
49
|
return null;
|
|
56
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Map MIME type to SupportedFormat
|
|
53
|
+
*
|
|
54
|
+
* Supports all variations of Excel, PDF, CSV, JSON, Markdown, YAML, DOCX
|
|
55
|
+
*/
|
|
56
|
+
mimeTypeToFormat(mimeType) {
|
|
57
|
+
// Excel formats (Office Open XML + legacy)
|
|
58
|
+
if (mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
|
|
59
|
+
mimeType === 'application/vnd.ms-excel' ||
|
|
60
|
+
mimeType === 'application/vnd.ms-excel.sheet.macroEnabled.12' ||
|
|
61
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.template') {
|
|
62
|
+
return 'excel';
|
|
63
|
+
}
|
|
64
|
+
// PDF
|
|
65
|
+
if (mimeType === 'application/pdf') {
|
|
66
|
+
return 'pdf';
|
|
67
|
+
}
|
|
68
|
+
// CSV
|
|
69
|
+
if (mimeType === 'text/csv') {
|
|
70
|
+
return 'csv';
|
|
71
|
+
}
|
|
72
|
+
// JSON
|
|
73
|
+
if (mimeType === 'application/json') {
|
|
74
|
+
return 'json';
|
|
75
|
+
}
|
|
76
|
+
// Markdown
|
|
77
|
+
if (mimeType === 'text/markdown' || mimeType === 'text/x-markdown') {
|
|
78
|
+
return 'markdown';
|
|
79
|
+
}
|
|
80
|
+
// YAML
|
|
81
|
+
if (mimeType === 'text/yaml' || mimeType === 'text/x-yaml' || mimeType === 'application/x-yaml') {
|
|
82
|
+
return 'yaml';
|
|
83
|
+
}
|
|
84
|
+
// Word documents (Office Open XML)
|
|
85
|
+
if (mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
|
|
86
|
+
mimeType === 'application/msword') {
|
|
87
|
+
return 'docx';
|
|
88
|
+
}
|
|
89
|
+
// Images (v5.2.0: ImageHandler support)
|
|
90
|
+
if (mimeType.startsWith('image/')) {
|
|
91
|
+
return 'image';
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
57
95
|
/**
|
|
58
96
|
* Detect format from string content
|
|
59
97
|
*/
|
|
@@ -132,6 +170,50 @@ export class FormatDetector {
|
|
|
132
170
|
};
|
|
133
171
|
}
|
|
134
172
|
}
|
|
173
|
+
// Image formats (v5.2.0)
|
|
174
|
+
// JPEG: FF D8 FF
|
|
175
|
+
if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
|
|
176
|
+
return {
|
|
177
|
+
format: 'image',
|
|
178
|
+
confidence: 1.0,
|
|
179
|
+
evidence: ['JPEG magic bytes: FF D8 FF']
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// PNG: 89 50 4E 47
|
|
183
|
+
if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
|
|
184
|
+
return {
|
|
185
|
+
format: 'image',
|
|
186
|
+
confidence: 1.0,
|
|
187
|
+
evidence: ['PNG magic bytes: 89 50 4E 47']
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// GIF: 47 49 46 38
|
|
191
|
+
if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) {
|
|
192
|
+
return {
|
|
193
|
+
format: 'image',
|
|
194
|
+
confidence: 1.0,
|
|
195
|
+
evidence: ['GIF magic bytes: GIF8']
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// BMP: 42 4D
|
|
199
|
+
if (buffer[0] === 0x42 && buffer[1] === 0x4D) {
|
|
200
|
+
return {
|
|
201
|
+
format: 'image',
|
|
202
|
+
confidence: 1.0,
|
|
203
|
+
evidence: ['BMP magic bytes: BM']
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// WebP: RIFF....WEBP
|
|
207
|
+
if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 && buffer.length >= 12) {
|
|
208
|
+
const webpCheck = buffer.toString('utf8', 8, 12);
|
|
209
|
+
if (webpCheck === 'WEBP') {
|
|
210
|
+
return {
|
|
211
|
+
format: 'image',
|
|
212
|
+
confidence: 1.0,
|
|
213
|
+
evidence: ['WebP magic bytes: RIFF...WEBP']
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
135
217
|
return null;
|
|
136
218
|
}
|
|
137
219
|
/**
|