@soulcraft/brainy 5.1.1 → 5.2.0

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 CHANGED
@@ -2,6 +2,126 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [5.2.0](https://github.com/soulcraftlabs/brainy/compare/v5.1.2...v5.2.0) (2025-11-03)
6
+
7
+ - fix: update VFS test for v5.2.0 BlobStorage architecture (b3e3e5c)
8
+ - feat: add ImageHandler with EXIF extraction and comprehensive MIME detection (v5.2.0) (1874b77)
9
+
10
+
11
+ ## [5.2.0](https://github.com/soulcraftlabs/brainy/compare/v5.1.0...v5.2.0) (2025-11-03)
12
+
13
+ ### ✨ Features
14
+
15
+ **Format Handler Infrastructure** - Enables developers to create handlers for ANY file type
16
+
17
+ * **feat**: Pluggable format handler system with FormatHandlerRegistry
18
+ - MIME-based automatic format detection and routing
19
+ - Lazy loading support for performance optimization
20
+ - Register handlers dynamically at runtime
21
+ - Type-safe with full TypeScript support
22
+ - Reference: `src/augmentations/intelligentImport/FormatHandlerRegistry.ts:1`
23
+
24
+ * **feat**: Comprehensive MIME type detection with MimeTypeDetector
25
+ - Industry-standard `mime` library integration (2000+ IANA types)
26
+ - 90+ custom developer-specific MIME types (shell scripts, configs, modern languages)
27
+ - Replaces 70+ lines of hardcoded MIME types
28
+ - Single source of truth: `mimeDetector.detectMimeType()`, `mimeDetector.isTextFile()`
29
+ - Reference: `src/vfs/MimeTypeDetector.ts:1`
30
+
31
+ * **feat**: ImageHandler with EXIF extraction (reference implementation)
32
+ - Extract image metadata (dimensions, format, color space, channels)
33
+ - Extract EXIF data (camera, GPS, timestamps, lens, exposure)
34
+ - Supports JPEG, PNG, WebP, GIF, TIFF, BMP, SVG, HEIC, AVIF
35
+ - Magic byte detection for format identification
36
+ - Reference: `src/augmentations/intelligentImport/handlers/imageHandler.ts:1`
37
+
38
+ **Enhanced BaseFormatHandler**
39
+
40
+ * **feat**: Added MIME helper methods to BaseFormatHandler
41
+ - `getMimeType()` - Detect MIME type from filename or buffer
42
+ - `mimeTypeMatches()` - Check MIME type against patterns with wildcard support
43
+ - Reference: `src/augmentations/intelligentImport/handlers/base.ts:39`
44
+
45
+ ### 📚 Documentation
46
+
47
+ * **docs**: Comprehensive format handler documentation
48
+ - [FORMAT_HANDLERS.md](docs/augmentations/FORMAT_HANDLERS.md) - Creating custom format handlers
49
+ - [EXAMPLES.md](docs/augmentations/EXAMPLES.md) - End-to-end workflows (import + store + export)
50
+ - Real-world examples: CAD files, video metadata, Git repos, database schemas, React analyzers
51
+ - Premium augmentation packaging guide
52
+
53
+ ### 🏗️ What This Enables
54
+
55
+ **Custom Format Handlers:**
56
+ - Import ANY file type into knowledge graph (CAD, video, databases, etc.)
57
+ - Automatic MIME-based routing
58
+ - Example: CAD files, Git repos, database schemas
59
+
60
+ **Premium Augmentations:**
61
+ - Package handlers as paid npm products
62
+ - Import + storage + export workflows
63
+ - License-key validation
64
+ - Example: React analyzer, Python project analyzer
65
+
66
+ ### 📦 Dependencies
67
+
68
+ * **added**: `mime@4.1.0` - Industry-standard MIME detection
69
+ * **added**: `sharp@0.33.5` - High-performance image processing
70
+ * **added**: `exifr@7.1.3` - EXIF metadata extraction
71
+
72
+ ### 🔧 Technical Details
73
+
74
+ **Test Coverage:**
75
+ - ✅ 26 MIME detection tests (all passing)
76
+ - ✅ 30 FormatHandlerRegistry tests (all passing)
77
+ - ✅ 27 ImageHandler tests (all passing)
78
+ - ✅ Total: 83/83 tests passing
79
+
80
+ **Modified Files:**
81
+ - `src/vfs/VirtualFileSystem.ts` - Integrated mimeDetector, removed 70 lines of hardcoded MIME types
82
+ - `src/vfs/importers/DirectoryImporter.ts` - Removed duplicate MIME detection
83
+ - `src/import/FormatDetector.ts` - Integrated mimeDetector
84
+ - `src/augmentations/intelligentImport/handlers/base.ts` - Added MIME helpers
85
+ - `src/api/UniversalImportAPI.ts` - Added MIME detection
86
+ - `src/vfs/index.ts` - Exported mimeDetector for augmentations
87
+
88
+ ### 🔄 Backward Compatibility
89
+
90
+ **100% backward compatible** - No breaking changes.
91
+
92
+ - ✅ All existing import flows work unchanged
93
+ - ✅ Existing handlers (CSV, Excel, PDF) unchanged
94
+ - ✅ New functionality is opt-in
95
+
96
+ ### 🚀 Usage
97
+
98
+ ```typescript
99
+ // Register custom handler
100
+ import {
101
+ BaseFormatHandler,
102
+ globalHandlerRegistry
103
+ } from '@soulcraft/brainy/augmentations/intelligentImport'
104
+
105
+ class MyHandler extends BaseFormatHandler {
106
+ readonly format = 'myformat'
107
+ canHandle(data) { return this.mimeTypeMatches(this.getMimeType(data), ['application/x-myformat']) }
108
+ async process(data, options) { /* Parse and return structured data */ }
109
+ }
110
+
111
+ globalHandlerRegistry.registerHandler({
112
+ name: 'myformat',
113
+ mimeTypes: ['application/x-myformat'],
114
+ extensions: ['.myf'],
115
+ loader: async () => new MyHandler()
116
+ })
117
+
118
+ // Now brain.import() automatically handles .myf files!
119
+ ```
120
+
121
+ See [v5.2.0 Summary](.strategy/v5.2.0-SUMMARY.md) for complete details.
122
+
123
+ ---
124
+
5
125
  ## [5.1.0](https://github.com/soulcraftlabs/brainy/compare/v5.0.0...v5.1.0) (2025-11-02)
6
126
 
7
127
  ### ✨ Features
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Handles:
8
8
  * - Strings (text, JSON, CSV, YAML, Markdown)
9
- * - Files (local paths, any format)
9
+ * - Files (local paths, any format) - uses MimeTypeDetector for 2000+ types
10
10
  * - URLs (web pages, APIs, documents)
11
11
  * - Objects (structured data)
12
12
  * - Binary data (images, PDFs via extraction)
@@ -78,6 +78,8 @@ export declare class UniversalImportAPI {
78
78
  /**
79
79
  * Import from file - reads and processes
80
80
  * Note: In browser environment, use File API instead
81
+ *
82
+ * Uses MimeTypeDetector for comprehensive format detection (2000+ types)
81
83
  */
82
84
  importFromFile(filePath: string): Promise<NeuralImportResult>;
83
85
  /**
@@ -6,13 +6,14 @@
6
6
  *
7
7
  * Handles:
8
8
  * - Strings (text, JSON, CSV, YAML, Markdown)
9
- * - Files (local paths, any format)
9
+ * - Files (local paths, any format) - uses MimeTypeDetector for 2000+ types
10
10
  * - URLs (web pages, APIs, documents)
11
11
  * - Objects (structured data)
12
12
  * - Binary data (images, PDFs via extraction)
13
13
  */
14
14
  import { getBrainyTypes } from '../augmentations/typeMatching/brainyTypes.js';
15
15
  import { NeuralImportAugmentation } from '../augmentations/neuralImport.js';
16
+ import { mimeDetector } from '../vfs/MimeTypeDetector.js';
16
17
  export class UniversalImportAPI {
17
18
  constructor(brain) {
18
19
  this.embedCache = new Map();
@@ -89,19 +90,24 @@ export class UniversalImportAPI {
89
90
  /**
90
91
  * Import from file - reads and processes
91
92
  * Note: In browser environment, use File API instead
93
+ *
94
+ * Uses MimeTypeDetector for comprehensive format detection (2000+ types)
92
95
  */
93
96
  async importFromFile(filePath) {
94
97
  // Read the actual file content
95
98
  const { readFileSync } = await import('node:fs');
99
+ // Use MimeTypeDetector for comprehensive format detection
100
+ const mimeType = mimeDetector.detectMimeType(filePath);
96
101
  const ext = filePath.split('.').pop()?.toLowerCase() || 'txt';
97
102
  try {
98
103
  const fileContent = readFileSync(filePath, 'utf-8');
99
104
  return this.import({
100
105
  type: 'file',
101
106
  data: fileContent, // Actual file content
102
- format: ext,
107
+ format: ext, // Keep ext for backward compatibility
103
108
  metadata: {
104
109
  path: filePath,
110
+ mimeType, // Add detected MIME type
105
111
  importedAt: Date.now(),
106
112
  fileSize: fileContent.length
107
113
  }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Format Handler Registry (v5.2.0)
3
+ *
4
+ * Central registry for format handlers with:
5
+ * - MIME type-based routing
6
+ * - Lazy loading support
7
+ * - Pluggable handler registration
8
+ * - Handler lifecycle management
9
+ *
10
+ * NO MOCKS - Production implementation
11
+ */
12
+ import { FormatHandler, HandlerRegistry as IHandlerRegistry } from './types.js';
13
+ export interface HandlerRegistration {
14
+ /** Handler name (e.g., 'csv', 'image', 'pdf') */
15
+ name: string;
16
+ /** MIME types this handler supports */
17
+ mimeTypes: string[];
18
+ /** File extensions (fallback if MIME detection fails) */
19
+ extensions: string[];
20
+ /** Lazy loader function */
21
+ loader: () => Promise<FormatHandler>;
22
+ /** Loaded handler instance (lazy-loaded) */
23
+ instance?: FormatHandler;
24
+ }
25
+ /**
26
+ * FormatHandlerRegistry - Central handler management
27
+ *
28
+ * Implements the HandlerRegistry interface with:
29
+ * - MIME type-based routing
30
+ * - Lazy loading for performance
31
+ * - Multiple handlers per MIME type
32
+ * - Priority-based selection
33
+ */
34
+ export declare class FormatHandlerRegistry implements IHandlerRegistry {
35
+ handlers: Map<string, () => Promise<FormatHandler>>;
36
+ loaded: Map<string, FormatHandler>;
37
+ private registrations;
38
+ private mimeTypeIndex;
39
+ private extensionIndex;
40
+ /**
41
+ * Register a format handler
42
+ *
43
+ * @param registration Handler registration details
44
+ */
45
+ registerHandler(registration: HandlerRegistration): void;
46
+ /**
47
+ * Register a handler (interface compatibility)
48
+ */
49
+ register(extensions: string[], loader: () => Promise<FormatHandler>): void;
50
+ /**
51
+ * Get handler by filename or extension
52
+ *
53
+ * Uses MIME detection first, falls back to extension matching
54
+ *
55
+ * @param filenameOrExt Filename or extension
56
+ * @returns Handler instance or null
57
+ */
58
+ getHandler(filenameOrExt: string): Promise<FormatHandler | null>;
59
+ /**
60
+ * Get handler by MIME type
61
+ *
62
+ * @param mimeType MIME type string
63
+ * @returns Handler instance or null
64
+ */
65
+ getHandlerByMimeType(mimeType: string): Promise<FormatHandler | null>;
66
+ /**
67
+ * Get handler by file extension
68
+ *
69
+ * @param ext File extension (with or without dot)
70
+ * @returns Handler instance or null
71
+ */
72
+ getHandlerByExtension(ext: string): Promise<FormatHandler | null>;
73
+ /**
74
+ * Get handler by name
75
+ *
76
+ * @param name Handler name
77
+ * @returns Handler instance or null
78
+ */
79
+ getHandlerByName(name: string): Promise<FormatHandler | null>;
80
+ /**
81
+ * Load handler (lazy loading)
82
+ *
83
+ * @param name Handler name
84
+ * @returns Loaded handler instance
85
+ */
86
+ private loadHandler;
87
+ /**
88
+ * Get all registered handler names
89
+ */
90
+ getRegisteredHandlers(): string[];
91
+ /**
92
+ * Get handlers for a MIME type
93
+ */
94
+ getHandlersForMimeType(mimeType: string): string[];
95
+ /**
96
+ * Check if handler is registered
97
+ */
98
+ hasHandler(name: string): boolean;
99
+ /**
100
+ * Clear all handlers (for testing)
101
+ */
102
+ clear(): void;
103
+ /**
104
+ * Extract file extension from filename
105
+ */
106
+ private extractExtension;
107
+ }
108
+ /**
109
+ * Global handler registry singleton
110
+ */
111
+ export declare const globalHandlerRegistry: FormatHandlerRegistry;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Format Handler Registry (v5.2.0)
3
+ *
4
+ * Central registry for format handlers with:
5
+ * - MIME type-based routing
6
+ * - Lazy loading support
7
+ * - Pluggable handler registration
8
+ * - Handler lifecycle management
9
+ *
10
+ * NO MOCKS - Production implementation
11
+ */
12
+ import { mimeDetector } from '../../vfs/MimeTypeDetector.js';
13
+ /**
14
+ * FormatHandlerRegistry - Central handler management
15
+ *
16
+ * Implements the HandlerRegistry interface with:
17
+ * - MIME type-based routing
18
+ * - Lazy loading for performance
19
+ * - Multiple handlers per MIME type
20
+ * - Priority-based selection
21
+ */
22
+ export class FormatHandlerRegistry {
23
+ constructor() {
24
+ // Interface compatibility
25
+ this.handlers = new Map();
26
+ this.loaded = new Map();
27
+ // Enhanced registry
28
+ this.registrations = new Map();
29
+ this.mimeTypeIndex = new Map(); // MIME type → handler names
30
+ this.extensionIndex = new Map(); // Extension → handler names
31
+ }
32
+ /**
33
+ * Register a format handler
34
+ *
35
+ * @param registration Handler registration details
36
+ */
37
+ registerHandler(registration) {
38
+ const { name, mimeTypes, extensions, loader } = registration;
39
+ // Store registration
40
+ this.registrations.set(name, registration);
41
+ // Index by MIME types
42
+ for (const mimeType of mimeTypes) {
43
+ const handlers = this.mimeTypeIndex.get(mimeType) || [];
44
+ handlers.push(name);
45
+ this.mimeTypeIndex.set(mimeType, handlers);
46
+ }
47
+ // Index by extensions
48
+ for (const ext of extensions) {
49
+ const normalized = ext.toLowerCase().replace(/^\./, '');
50
+ const handlers = this.extensionIndex.get(normalized) || [];
51
+ handlers.push(name);
52
+ this.extensionIndex.set(normalized, handlers);
53
+ }
54
+ // Interface compatibility
55
+ this.handlers.set(name, loader);
56
+ }
57
+ /**
58
+ * Register a handler (interface compatibility)
59
+ */
60
+ register(extensions, loader) {
61
+ const name = extensions[0].replace(/^\./, '');
62
+ // Auto-detect MIME types from extensions for better routing
63
+ const mimeTypes = [];
64
+ for (const ext of extensions) {
65
+ const mimeType = mimeDetector.detectMimeType(`file${ext}`);
66
+ if (mimeType && mimeType !== 'application/octet-stream' && !mimeTypes.includes(mimeType)) {
67
+ mimeTypes.push(mimeType);
68
+ }
69
+ }
70
+ this.registerHandler({
71
+ name,
72
+ mimeTypes,
73
+ extensions,
74
+ loader
75
+ });
76
+ }
77
+ /**
78
+ * Get handler by filename or extension
79
+ *
80
+ * Uses MIME detection first, falls back to extension matching
81
+ *
82
+ * @param filenameOrExt Filename or extension
83
+ * @returns Handler instance or null
84
+ */
85
+ async getHandler(filenameOrExt) {
86
+ // Try MIME type detection first
87
+ const mimeType = mimeDetector.detectMimeType(filenameOrExt);
88
+ const byMime = await this.getHandlerByMimeType(mimeType);
89
+ if (byMime)
90
+ return byMime;
91
+ // Fallback to extension matching
92
+ const ext = this.extractExtension(filenameOrExt);
93
+ if (ext) {
94
+ return this.getHandlerByExtension(ext);
95
+ }
96
+ return null;
97
+ }
98
+ /**
99
+ * Get handler by MIME type
100
+ *
101
+ * @param mimeType MIME type string
102
+ * @returns Handler instance or null
103
+ */
104
+ async getHandlerByMimeType(mimeType) {
105
+ const handlerNames = this.mimeTypeIndex.get(mimeType);
106
+ if (!handlerNames || handlerNames.length === 0) {
107
+ return null;
108
+ }
109
+ // Return first matching handler (could add priority later)
110
+ return this.loadHandler(handlerNames[0]);
111
+ }
112
+ /**
113
+ * Get handler by file extension
114
+ *
115
+ * @param ext File extension (with or without dot)
116
+ * @returns Handler instance or null
117
+ */
118
+ async getHandlerByExtension(ext) {
119
+ const normalized = ext.toLowerCase().replace(/^\./, '');
120
+ const handlerNames = this.extensionIndex.get(normalized);
121
+ if (!handlerNames || handlerNames.length === 0) {
122
+ return null;
123
+ }
124
+ // Return first matching handler
125
+ return this.loadHandler(handlerNames[0]);
126
+ }
127
+ /**
128
+ * Get handler by name
129
+ *
130
+ * @param name Handler name
131
+ * @returns Handler instance or null
132
+ */
133
+ async getHandlerByName(name) {
134
+ return this.loadHandler(name);
135
+ }
136
+ /**
137
+ * Load handler (lazy loading)
138
+ *
139
+ * @param name Handler name
140
+ * @returns Loaded handler instance
141
+ */
142
+ async loadHandler(name) {
143
+ const registration = this.registrations.get(name);
144
+ if (!registration)
145
+ return null;
146
+ // Return cached instance if available
147
+ if (registration.instance) {
148
+ return registration.instance;
149
+ }
150
+ // Load handler
151
+ try {
152
+ const handler = await registration.loader();
153
+ registration.instance = handler;
154
+ // Interface compatibility
155
+ this.loaded.set(name, handler);
156
+ return handler;
157
+ }
158
+ catch (error) {
159
+ console.error(`Failed to load handler ${name}:`, error);
160
+ return null;
161
+ }
162
+ }
163
+ /**
164
+ * Get all registered handler names
165
+ */
166
+ getRegisteredHandlers() {
167
+ return Array.from(this.registrations.keys());
168
+ }
169
+ /**
170
+ * Get handlers for a MIME type
171
+ */
172
+ getHandlersForMimeType(mimeType) {
173
+ return this.mimeTypeIndex.get(mimeType) || [];
174
+ }
175
+ /**
176
+ * Check if handler is registered
177
+ */
178
+ hasHandler(name) {
179
+ return this.registrations.has(name);
180
+ }
181
+ /**
182
+ * Clear all handlers (for testing)
183
+ */
184
+ clear() {
185
+ this.registrations.clear();
186
+ this.mimeTypeIndex.clear();
187
+ this.extensionIndex.clear();
188
+ this.handlers.clear();
189
+ this.loaded.clear();
190
+ }
191
+ /**
192
+ * Extract file extension from filename
193
+ */
194
+ extractExtension(filename) {
195
+ const lastDot = filename.lastIndexOf('.');
196
+ if (lastDot === -1 || lastDot === 0)
197
+ return null;
198
+ return filename.substring(lastDot + 1).toLowerCase();
199
+ }
200
+ }
201
+ /**
202
+ * Global handler registry singleton
203
+ */
204
+ export const globalHandlerRegistry = new FormatHandlerRegistry();
205
+ //# sourceMappingURL=FormatHandlerRegistry.js.map
@@ -11,6 +11,7 @@ import { BaseAugmentation } from '../brainyAugmentation.js';
11
11
  import { CSVHandler } from './handlers/csvHandler.js';
12
12
  import { ExcelHandler } from './handlers/excelHandler.js';
13
13
  import { PDFHandler } from './handlers/pdfHandler.js';
14
+ import { ImageHandler } from './handlers/imageHandler.js';
14
15
  export class IntelligentImportAugmentation extends BaseAugmentation {
15
16
  constructor(config = {}) {
16
17
  super(config);
@@ -28,6 +29,7 @@ export class IntelligentImportAugmentation extends BaseAugmentation {
28
29
  enableCSV: true,
29
30
  enableExcel: true,
30
31
  enablePDF: true,
32
+ enableImage: true, // v5.2.0: Image handler enabled by default
31
33
  maxFileSize: 100 * 1024 * 1024, // 100MB default
32
34
  enableCache: true,
33
35
  cacheTTL: 24 * 60 * 60 * 1000, // 24 hours
@@ -45,8 +47,11 @@ export class IntelligentImportAugmentation extends BaseAugmentation {
45
47
  if (this.config.enablePDF) {
46
48
  this.handlers.set('pdf', new PDFHandler());
47
49
  }
50
+ if (this.config.enableImage) {
51
+ this.handlers.set('image', new ImageHandler());
52
+ }
48
53
  this.initialized = true;
49
- this.log(`Initialized with ${this.handlers.size} format handlers (CSV: ${this.config.enableCSV}, Excel: ${this.config.enableExcel}, PDF: ${this.config.enablePDF})`);
54
+ this.log(`Initialized with ${this.handlers.size} format handlers (CSV: ${this.config.enableCSV}, Excel: ${this.config.enableExcel}, PDF: ${this.config.enablePDF}, Image: ${this.config.enableImage})`);
50
55
  }
51
56
  async execute(operation, params, next) {
52
57
  // Only process import operations
@@ -78,6 +83,7 @@ export class IntelligentImportAugmentation extends BaseAugmentation {
78
83
  ...this.config.csvDefaults,
79
84
  ...this.config.excelDefaults,
80
85
  ...this.config.pdfDefaults,
86
+ ...this.config.imageDefaults,
81
87
  ...params.options
82
88
  });
83
89
  // Enrich params with processed data
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Base Format Handler
3
3
  * Abstract class providing common functionality for all format handlers
4
+ *
5
+ * Uses MimeTypeDetector for comprehensive file type detection (2000+ types)
4
6
  */
5
7
  import { FormatHandler, FormatHandlerOptions, ProcessedData } from '../types.js';
6
8
  export declare abstract class BaseFormatHandler implements FormatHandler {
@@ -21,6 +23,21 @@ export declare abstract class BaseFormatHandler implements FormatHandler {
21
23
  * Extract extension from filename
22
24
  */
23
25
  protected getExtension(filename: string): string;
26
+ /**
27
+ * Get MIME type using MimeTypeDetector
28
+ *
29
+ * Supports 2000+ file types via mime library + custom developer types
30
+ */
31
+ protected getMimeType(data: Buffer | string | {
32
+ filename?: string;
33
+ }): string;
34
+ /**
35
+ * Check if MIME type matches expected format
36
+ *
37
+ * @param mimeType - MIME type to check
38
+ * @param patterns - Patterns to match (e.g., ['text/csv', 'application/vnd.ms-excel'])
39
+ */
40
+ protected mimeTypeMatches(mimeType: string, patterns: string[]): boolean;
24
41
  /**
25
42
  * Infer field types from data
26
43
  * Analyzes multiple rows to determine the most appropriate type
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Base Format Handler
3
3
  * Abstract class providing common functionality for all format handlers
4
+ *
5
+ * Uses MimeTypeDetector for comprehensive file type detection (2000+ types)
4
6
  */
7
+ import { mimeDetector } from '../../../vfs/MimeTypeDetector.js';
5
8
  export class BaseFormatHandler {
6
9
  /**
7
10
  * Detect file extension from various inputs
@@ -22,6 +25,36 @@ export class BaseFormatHandler {
22
25
  const match = filename.match(/\.([^.]+)$/);
23
26
  return match ? match[1].toLowerCase() : '';
24
27
  }
28
+ /**
29
+ * Get MIME type using MimeTypeDetector
30
+ *
31
+ * Supports 2000+ file types via mime library + custom developer types
32
+ */
33
+ getMimeType(data) {
34
+ if (typeof data === 'object' && 'filename' in data && data.filename) {
35
+ return mimeDetector.detectMimeType(data.filename);
36
+ }
37
+ if (Buffer.isBuffer(data)) {
38
+ // For buffers, we don't have a filename, so return generic
39
+ return 'application/octet-stream';
40
+ }
41
+ return 'text/plain';
42
+ }
43
+ /**
44
+ * Check if MIME type matches expected format
45
+ *
46
+ * @param mimeType - MIME type to check
47
+ * @param patterns - Patterns to match (e.g., ['text/csv', 'application/vnd.ms-excel'])
48
+ */
49
+ mimeTypeMatches(mimeType, patterns) {
50
+ return patterns.some(pattern => {
51
+ if (pattern.endsWith('/*')) {
52
+ const prefix = pattern.slice(0, -2);
53
+ return mimeType.startsWith(prefix);
54
+ }
55
+ return mimeType === pattern;
56
+ });
57
+ }
25
58
  /**
26
59
  * Infer field types from data
27
60
  * Analyzes multiple rows to determine the most appropriate type