openredaction 1.0.0 → 1.0.8

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/document/OCRProcessor.ts","../src/document/JsonProcessor.ts","../src/document/CsvProcessor.ts","../src/document/XlsxProcessor.ts","../src/document/DocumentProcessor.ts"],"sourcesContent":["/**\n * OCR (Optical Character Recognition) processor using Tesseract.js\n */\n\nimport type { IOCRProcessor, OCROptions, OCRResult } from './types';\n\n/**\n * OCR processor with optional Tesseract.js support\n * Requires peer dependency: tesseract.js\n */\nexport class OCRProcessor implements IOCRProcessor {\n private tesseract?: any;\n private scheduler?: any;\n\n constructor() {\n // Try to load optional dependency\n try {\n this.tesseract = require('tesseract.js');\n } catch {\n // tesseract.js not installed\n }\n }\n\n /**\n * Extract text from image buffer using OCR\n */\n async recognizeText(buffer: Buffer, options?: OCROptions): Promise<OCRResult> {\n if (!this.tesseract) {\n throw new Error(\n '[OCRProcessor] OCR support requires tesseract.js. Install with: npm install tesseract.js'\n );\n }\n\n const startTime = performance.now();\n\n try {\n // Configure worker\n const language = Array.isArray(options?.language)\n ? options.language.join('+')\n : options?.language || 'eng';\n\n const worker = await this.tesseract.createWorker(language, options?.oem || 3);\n\n // Set page segmentation mode if specified\n if (options?.psm !== undefined) {\n await worker.setParameters({\n tessedit_pageseg_mode: options.psm\n });\n }\n\n // Perform OCR\n const result = await worker.recognize(buffer);\n\n // Clean up worker\n await worker.terminate();\n\n const endTime = performance.now();\n const processingTime = Math.round((endTime - startTime) * 100) / 100;\n\n return {\n text: result.data.text || '',\n confidence: result.data.confidence || 0,\n processingTime\n };\n } catch (error: any) {\n throw new Error(`[OCRProcessor] OCR recognition failed: ${error.message}`);\n }\n }\n\n /**\n * Check if OCR is available (tesseract.js installed)\n */\n isAvailable(): boolean {\n return !!this.tesseract;\n }\n\n /**\n * Create a scheduler for batch OCR processing\n * More efficient for processing multiple images\n */\n async createScheduler(workerCount: number = 4): Promise<any> {\n if (!this.tesseract) {\n throw new Error(\n '[OCRProcessor] OCR support requires tesseract.js. Install with: npm install tesseract.js'\n );\n }\n\n if (this.scheduler) {\n await this.scheduler.terminate();\n }\n\n this.scheduler = this.tesseract.createScheduler();\n\n // Create workers\n const workers = [];\n for (let i = 0; i < workerCount; i++) {\n const worker = await this.tesseract.createWorker('eng');\n this.scheduler.addWorker(worker);\n workers.push(worker);\n }\n\n return this.scheduler;\n }\n\n /**\n * Batch process multiple images\n */\n async recognizeBatch(buffers: Buffer[], _options?: OCROptions): Promise<OCRResult[]> {\n if (!this.tesseract) {\n throw new Error(\n '[OCRProcessor] OCR support requires tesseract.js. Install with: npm install tesseract.js'\n );\n }\n\n // Use scheduler for batch processing\n const scheduler = await this.createScheduler();\n\n try {\n const results = await Promise.all(\n buffers.map(async (buffer) => {\n const startTime = performance.now();\n const result = await scheduler.addJob('recognize', buffer);\n const endTime = performance.now();\n\n return {\n text: result.data.text || '',\n confidence: result.data.confidence || 0,\n processingTime: Math.round((endTime - startTime) * 100) / 100\n };\n })\n );\n\n // Clean up scheduler\n await scheduler.terminate();\n this.scheduler = undefined;\n\n return results;\n } catch (error: any) {\n // Clean up on error\n if (scheduler) {\n await scheduler.terminate();\n this.scheduler = undefined;\n }\n throw new Error(`[OCRProcessor] Batch OCR failed: ${error.message}`);\n }\n }\n\n /**\n * Terminate any running scheduler\n */\n async cleanup(): Promise<void> {\n if (this.scheduler) {\n await this.scheduler.terminate();\n this.scheduler = undefined;\n }\n }\n}\n\n/**\n * Create an OCR processor instance\n */\nexport function createOCRProcessor(): OCRProcessor {\n return new OCRProcessor();\n}\n","/**\n * JSON document processor for PII detection and redaction in structured data\n */\n\nimport type { DetectionResult, PIIDetection } from '../types';\nimport type { OpenRedaction } from '../detector';\n\n/**\n * JSON processing options\n */\nexport interface JsonProcessorOptions {\n /** Maximum depth for nested object traversal (default: 100) */\n maxDepth?: number;\n /** Whether to scan object keys for PII (default: false) */\n scanKeys?: boolean;\n /** Field paths to always redact (e.g., ['user.password', 'auth.token']) */\n alwaysRedact?: string[];\n /** Field paths to never scan (e.g., ['metadata.id', 'timestamp']) */\n skipPaths?: string[];\n /** Field names that indicate PII (boost confidence) */\n piiIndicatorKeys?: string[];\n /** Preserve JSON structure in redacted output (default: true) */\n preserveStructure?: boolean;\n}\n\n/**\n * JSON detection result with path tracking\n */\nexport interface JsonDetectionResult extends DetectionResult {\n /** Paths where PII was detected (e.g., 'user.email', 'contacts[0].phone') */\n pathsDetected: string[];\n /** PII matches with path information */\n matchesByPath: Record<string, PIIDetection[]>;\n}\n\n/**\n * Redacted JSON value types\n */\ntype RedactedValue = string | number | boolean | null | RedactedObject | RedactedValue[];\ninterface RedactedObject {\n [key: string]: RedactedValue;\n}\n\n/**\n * Processor for JSON documents\n */\nexport class JsonProcessor {\n private readonly defaultOptions: Required<JsonProcessorOptions> = {\n maxDepth: 100,\n scanKeys: false,\n alwaysRedact: [],\n skipPaths: [],\n piiIndicatorKeys: [\n 'email', 'e-mail', 'mail',\n 'phone', 'tel', 'telephone', 'mobile',\n 'ssn', 'social_security',\n 'address', 'street', 'city', 'zip', 'postal',\n 'name', 'firstname', 'lastname', 'fullname',\n 'password', 'pwd', 'secret', 'token', 'key',\n 'card', 'credit_card', 'creditcard',\n 'account', 'iban', 'swift',\n 'passport', 'license', 'licence'\n ],\n preserveStructure: true\n };\n\n /**\n * Parse JSON from buffer or string\n */\n parse(input: Buffer | string): any {\n try {\n const text = typeof input === 'string' ? input : input.toString('utf-8');\n return JSON.parse(text);\n } catch (error: any) {\n throw new Error(`[JsonProcessor] Invalid JSON: ${error.message}`);\n }\n }\n\n /**\n * Detect PII in JSON data\n */\n detect(\n data: any,\n detector: OpenRedaction,\n options?: JsonProcessorOptions\n ): JsonDetectionResult {\n const opts = { ...this.defaultOptions, ...options };\n const pathsDetected: string[] = [];\n const matchesByPath: Record<string, PIIDetection[]> = {};\n const allDetections: PIIDetection[] = [];\n\n // Traverse JSON and collect all text values with paths\n this.traverse(data, '', opts, (path, value, key) => {\n // Check if path should be skipped\n if (this.shouldSkip(path, opts.skipPaths)) {\n return;\n }\n\n // Check if path should always be redacted\n if (this.shouldAlwaysRedact(path, opts.alwaysRedact)) {\n // Mark as high-confidence PII without scanning\n const detection: PIIDetection = {\n type: 'SENSITIVE_FIELD',\n value: String(value),\n placeholder: `[SENSITIVE_FIELD]`,\n position: [0, String(value).length],\n severity: 'high',\n confidence: 1.0\n };\n matchesByPath[path] = [detection];\n pathsDetected.push(path);\n allDetections.push(detection);\n return;\n }\n\n // Scan keys if enabled\n if (opts.scanKeys && key) {\n const keyResult = detector.detect(key);\n if (keyResult.detections.length > 0) {\n const keyPath = `${path}.__key__`;\n matchesByPath[keyPath] = keyResult.detections;\n pathsDetected.push(keyPath);\n allDetections.push(...keyResult.detections);\n }\n }\n\n // Scan value\n const valueStr = String(value);\n const result = detector.detect(valueStr);\n\n if (result.detections.length > 0) {\n // Boost confidence if key indicates PII\n const boostedDetections = this.boostConfidenceFromKey(\n result.detections,\n key,\n opts.piiIndicatorKeys\n );\n\n matchesByPath[path] = boostedDetections;\n pathsDetected.push(path);\n allDetections.push(...boostedDetections);\n }\n });\n\n // Build redacted text\n const original = JSON.stringify(data);\n const redacted = this.redact(data, {\n original,\n redacted: original,\n detections: allDetections,\n redactionMap: {},\n stats: { piiCount: allDetections.length },\n pathsDetected,\n matchesByPath\n } as JsonDetectionResult, opts);\n\n // Build redaction map\n const redactionMap: Record<string, string> = {};\n allDetections.forEach(det => {\n redactionMap[det.placeholder] = det.value;\n });\n\n return {\n original,\n redacted: typeof redacted === 'string' ? redacted : JSON.stringify(redacted),\n detections: allDetections,\n redactionMap,\n stats: {\n piiCount: allDetections.length\n },\n pathsDetected,\n matchesByPath\n };\n }\n\n /**\n * Redact PII in JSON data\n */\n redact(\n data: any,\n detectionResult: JsonDetectionResult,\n options?: JsonProcessorOptions\n ): any {\n const opts = { ...this.defaultOptions, ...options };\n\n if (!opts.preserveStructure) {\n // Simple text redaction (convert to JSON string, redact, parse back)\n // This is simpler but loses some structure information\n return this.parse(this.redactText(JSON.stringify(data, null, 2), detectionResult));\n }\n\n // Structure-preserving redaction\n return this.redactPreservingStructure(data, detectionResult.pathsDetected);\n }\n\n /**\n * Redact specific paths in JSON while preserving structure\n */\n private redactPreservingStructure(data: any, pathsToRedact: string[]): any {\n const pathSet = new Set(pathsToRedact);\n\n const redactValue = (value: any, currentPath: string): RedactedValue => {\n // Check if current path should be redacted\n if (pathSet.has(currentPath)) {\n if (typeof value === 'string') {\n return '[REDACTED]';\n } else if (typeof value === 'number') {\n return 0;\n } else if (typeof value === 'boolean') {\n return false;\n } else if (value === null) {\n return null;\n } else if (Array.isArray(value)) {\n return [];\n } else if (typeof value === 'object') {\n return {};\n }\n return '[REDACTED]';\n }\n\n // Recursively process arrays\n if (Array.isArray(value)) {\n return value.map((item, index) =>\n redactValue(item, `${currentPath}[${index}]`)\n );\n }\n\n // Recursively process objects\n if (value !== null && typeof value === 'object') {\n const result: RedactedObject = {};\n for (const [key, val] of Object.entries(value)) {\n const newPath = currentPath ? `${currentPath}.${key}` : key;\n result[key] = redactValue(val, newPath);\n }\n return result;\n }\n\n // Primitive values (not in redaction list)\n return value as RedactedValue;\n };\n\n return redactValue(data, '');\n }\n\n /**\n * Simple text-based redaction (fallback)\n */\n private redactText(text: string, detectionResult: DetectionResult): string {\n let redacted = text;\n const sortedDetections = [...detectionResult.detections].sort((a, b) => b.position[0] - a.position[0]);\n\n for (const detection of sortedDetections) {\n const [start, end] = detection.position;\n redacted = redacted.slice(0, start) + detection.placeholder + redacted.slice(end);\n }\n\n return redacted;\n }\n\n /**\n * Traverse JSON structure and call callback for each value\n */\n private traverse(\n obj: any,\n path: string,\n options: Required<JsonProcessorOptions>,\n callback: (path: string, value: any, key?: string) => void,\n depth = 0\n ): void {\n if (depth > options.maxDepth) {\n throw new Error(`[JsonProcessor] Maximum depth (${options.maxDepth}) exceeded`);\n }\n\n if (obj === null || obj === undefined) {\n return;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n obj.forEach((item, index) => {\n const itemPath = path ? `${path}[${index}]` : `[${index}]`;\n if (this.isPrimitive(item)) {\n callback(itemPath, item);\n }\n this.traverse(item, itemPath, options, callback, depth + 1);\n });\n return;\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n for (const [key, value] of Object.entries(obj)) {\n const valuePath = path ? `${path}.${key}` : key;\n\n if (this.isPrimitive(value)) {\n callback(valuePath, value, key);\n }\n\n this.traverse(value, valuePath, options, callback, depth + 1);\n }\n return;\n }\n\n // Handle primitives at root level\n if (this.isPrimitive(obj)) {\n callback(path, obj);\n }\n }\n\n /**\n * Check if value is primitive (string, number, boolean)\n */\n private isPrimitive(value: any): boolean {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n );\n }\n\n /**\n * Check if path should be skipped\n */\n private shouldSkip(path: string, skipPaths: string[]): boolean {\n return skipPaths.some(skipPath => {\n // Exact match\n if (path === skipPath) return true;\n\n // Wildcard match (e.g., 'users.*.id' matches 'users.123.id')\n const skipRegex = new RegExp('^' + skipPath.replace(/\\*/g, '[^.]+') + '$');\n return skipRegex.test(path);\n });\n }\n\n /**\n * Check if path should always be redacted\n */\n private shouldAlwaysRedact(path: string, alwaysRedact: string[]): boolean {\n return alwaysRedact.some(redactPath => {\n // Exact match\n if (path === redactPath) return true;\n\n // Wildcard match\n const redactRegex = new RegExp('^' + redactPath.replace(/\\*/g, '[^.]+') + '$');\n return redactRegex.test(path);\n });\n }\n\n /**\n * Boost confidence if key name indicates PII\n */\n private boostConfidenceFromKey(\n detections: PIIDetection[],\n key: string | undefined,\n piiIndicatorKeys: string[]\n ): PIIDetection[] {\n if (!key) return detections;\n\n const keyLower = key.toLowerCase();\n const isPiiKey = piiIndicatorKeys.some(indicator =>\n keyLower.includes(indicator.toLowerCase())\n );\n\n if (!isPiiKey) return detections;\n\n // Boost confidence by 20% (capped at 1.0)\n return detections.map(detection => ({\n ...detection,\n confidence: Math.min(1.0, (detection.confidence || 0.5) * 1.2)\n }));\n }\n\n /**\n * Extract all text values from JSON for simple text-based detection\n */\n extractText(data: any, options?: JsonProcessorOptions): string {\n const opts = { ...this.defaultOptions, ...options };\n const textParts: string[] = [];\n\n this.traverse(data, '', opts, (_path, value, key) => {\n if (opts.scanKeys && key) {\n textParts.push(key);\n }\n if (typeof value === 'string') {\n textParts.push(value);\n }\n });\n\n return textParts.join(' ');\n }\n\n /**\n * Validate JSON buffer/string\n */\n isValid(input: Buffer | string): boolean {\n try {\n this.parse(input);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get JSON Lines (JSONL) support - split by newlines and parse each line\n */\n parseJsonLines(input: Buffer | string): any[] {\n const text = typeof input === 'string' ? input : input.toString('utf-8');\n const lines = text.split('\\n').filter(line => line.trim().length > 0);\n\n return lines.map((line, index) => {\n try {\n return JSON.parse(line);\n } catch (error: any) {\n throw new Error(`[JsonProcessor] Invalid JSON at line ${index + 1}: ${error.message}`);\n }\n });\n }\n\n /**\n * Detect PII in JSON Lines format\n */\n detectJsonLines(\n input: Buffer | string,\n detector: OpenRedaction,\n options?: JsonProcessorOptions\n ): JsonDetectionResult[] {\n const documents = this.parseJsonLines(input);\n return documents.map(doc => this.detect(doc, detector, options));\n }\n}\n\n/**\n * Create a JSON processor instance\n */\nexport function createJsonProcessor(): JsonProcessor {\n return new JsonProcessor();\n}\n","/**\n * CSV document processor for PII detection and redaction in tabular data\n */\n\nimport type { DetectionResult, PIIDetection } from '../types';\nimport type { OpenRedaction } from '../detector';\n\n/**\n * CSV processing options\n */\nexport interface CsvProcessorOptions {\n /** CSV delimiter (default: auto-detect from ',', '\\t', ';', '|') */\n delimiter?: string;\n /** Whether CSV has header row (default: auto-detect) */\n hasHeader?: boolean;\n /** Quote character (default: '\"') */\n quote?: string;\n /** Escape character (default: '\"') */\n escape?: string;\n /** Skip empty lines (default: true) */\n skipEmptyLines?: boolean;\n /** Maximum rows to process (default: unlimited) */\n maxRows?: number;\n /** Column indices to always redact (0-indexed) */\n alwaysRedactColumns?: number[];\n /** Column names to always redact (requires hasHeader: true) */\n alwaysRedactColumnNames?: string[];\n /** Column indices to skip scanning (0-indexed) */\n skipColumns?: number[];\n /** Column names that indicate PII (boost confidence) */\n piiIndicatorNames?: string[];\n /** Treat first row as header for detection purposes */\n treatFirstRowAsHeader?: boolean;\n}\n\n/**\n * CSV detection result with column tracking\n */\nexport interface CsvDetectionResult extends DetectionResult {\n /** Total rows processed */\n rowCount: number;\n /** Column count */\n columnCount: number;\n /** Column headers (if detected) */\n headers?: string[];\n /** PII statistics by column index */\n columnStats: Record<number, ColumnStats>;\n /** PII matches by row and column */\n matchesByCell: CellMatch[];\n /** Original text */\n original: string;\n /** Redacted text */\n redacted: string;\n /** Array of detections */\n detections: PIIDetection[];\n /** Redaction map */\n redactionMap: Record<string, string>;\n /** Statistics */\n stats?: {\n processingTime?: number;\n piiCount: number;\n };\n}\n\n/**\n * Column PII statistics\n */\nexport interface ColumnStats {\n /** Column index */\n columnIndex: number;\n /** Column name (if header available) */\n columnName?: string;\n /** Number of PII instances found */\n piiCount: number;\n /** Percentage of rows with PII (0-100) */\n piiPercentage: number;\n /** PII types found in this column */\n piiTypes: string[];\n}\n\n/**\n * Cell-level PII match\n */\nexport interface CellMatch {\n /** Row index (0-indexed, excluding header if present) */\n row: number;\n /** Column index (0-indexed) */\n column: number;\n /** Column name (if header available) */\n columnName?: string;\n /** Cell value */\n value: string;\n /** PII matches in this cell */\n matches: PIIDetection[];\n}\n\n/**\n * Parsed CSV row\n */\ninterface CsvRow {\n /** Row index */\n index: number;\n /** Cell values */\n values: string[];\n}\n\n/**\n * CSV processor for tabular data\n */\nexport class CsvProcessor {\n private readonly defaultOptions: Required<Omit<CsvProcessorOptions, 'delimiter' | 'hasHeader' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns'>> & Partial<Pick<CsvProcessorOptions, 'delimiter' | 'hasHeader' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns'>> = {\n quote: '\"',\n escape: '\"',\n skipEmptyLines: true,\n piiIndicatorNames: [\n 'email', 'e-mail', 'mail', 'email_address',\n 'phone', 'tel', 'telephone', 'mobile', 'phone_number',\n 'ssn', 'social_security', 'social_security_number',\n 'address', 'street', 'street_address', 'city', 'zip', 'zipcode', 'postal', 'postcode',\n 'name', 'firstname', 'first_name', 'lastname', 'last_name', 'fullname', 'full_name',\n 'password', 'pwd', 'secret', 'token', 'api_key',\n 'card', 'credit_card', 'creditcard', 'card_number',\n 'account', 'account_number', 'iban', 'swift',\n 'passport', 'passport_number', 'license', 'licence', 'driver_license',\n 'dob', 'date_of_birth', 'birth_date', 'birthdate'\n ],\n treatFirstRowAsHeader: true\n };\n\n /**\n * Parse CSV from buffer or string\n */\n parse(input: Buffer | string, options?: CsvProcessorOptions): CsvRow[] {\n const opts = { ...this.defaultOptions, ...options };\n const text = typeof input === 'string' ? input : input.toString('utf-8');\n\n // Auto-detect delimiter if not specified\n const delimiter = opts.delimiter || this.detectDelimiter(text);\n\n // Parse CSV\n const lines = text.split(/\\r?\\n/);\n const rows: CsvRow[] = [];\n let rowIndex = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Skip empty lines if configured\n if (opts.skipEmptyLines && line.trim().length === 0) {\n continue;\n }\n\n // Check max rows limit\n if (opts.maxRows !== undefined && rowIndex >= opts.maxRows) {\n break;\n }\n\n // Parse row\n const values = this.parseRow(line, delimiter, opts.quote, opts.escape);\n\n rows.push({\n index: rowIndex,\n values\n });\n\n rowIndex++;\n }\n\n return rows;\n }\n\n /**\n * Detect PII in CSV data\n */\n detect(\n input: Buffer | string,\n detector: OpenRedaction,\n options?: CsvProcessorOptions\n ): CsvDetectionResult {\n const opts = { ...this.defaultOptions, ...options };\n const rows = this.parse(input, options);\n\n if (rows.length === 0) {\n const original = typeof input === 'string' ? input : input.toString('utf-8');\n return {\n original,\n redacted: original,\n detections: [],\n redactionMap: {},\n stats: {\n piiCount: 0\n },\n rowCount: 0,\n columnCount: 0,\n columnStats: {},\n matchesByCell: []\n };\n }\n\n // Determine if first row is header\n const hasHeader = opts.hasHeader !== undefined\n ? opts.hasHeader\n : this.detectHeader(rows);\n\n const headers = hasHeader && rows.length > 0 ? rows[0].values : undefined;\n const dataRows = hasHeader ? rows.slice(1) : rows;\n const columnCount = rows[0].values.length;\n\n // Build column name to index map\n const columnNameToIndex = new Map<string, number>();\n if (headers) {\n headers.forEach((header, index) => {\n columnNameToIndex.set(header.toLowerCase().trim(), index);\n });\n }\n\n // Determine which columns to always redact\n const alwaysRedactCols = new Set<number>(opts.alwaysRedactColumns || []);\n if (opts.alwaysRedactColumnNames && headers) {\n opts.alwaysRedactColumnNames.forEach(name => {\n const index = columnNameToIndex.get(name.toLowerCase().trim());\n if (index !== undefined) {\n alwaysRedactCols.add(index);\n }\n });\n }\n\n // Determine which columns to skip\n const skipCols = new Set<number>(opts.skipColumns || []);\n\n // Track statistics\n const columnStats: Record<number, ColumnStats> = {};\n const matchesByCell: CellMatch[] = [];\n const allDetections: PIIDetection[] = [];\n\n // Initialize column stats\n for (let col = 0; col < columnCount; col++) {\n columnStats[col] = {\n columnIndex: col,\n columnName: headers?.[col],\n piiCount: 0,\n piiPercentage: 0,\n piiTypes: []\n };\n }\n\n // Scan data rows\n for (const row of dataRows) {\n for (let col = 0; col < row.values.length; col++) {\n // Skip if column should be skipped\n if (skipCols.has(col)) {\n continue;\n }\n\n const cellValue = row.values[col];\n\n // Always redact this column?\n if (alwaysRedactCols.has(col)) {\n const detection: PIIDetection = {\n type: 'SENSITIVE_COLUMN',\n value: cellValue,\n placeholder: `[SENSITIVE_COLUMN_${col}]`,\n position: [0, cellValue.length],\n severity: 'high',\n confidence: 1.0\n };\n\n matchesByCell.push({\n row: row.index,\n column: col,\n columnName: headers?.[col],\n value: cellValue,\n matches: [detection]\n });\n\n allDetections.push(detection);\n columnStats[col].piiCount++;\n continue;\n }\n\n // Detect PII\n const result = detector.detect(cellValue);\n\n if (result.detections.length > 0) {\n // Boost confidence if column name indicates PII\n const boostedDetections = this.boostConfidenceFromColumnName(\n result.detections,\n headers?.[col],\n opts.piiIndicatorNames || []\n );\n\n matchesByCell.push({\n row: row.index,\n column: col,\n columnName: headers?.[col],\n value: cellValue,\n matches: boostedDetections\n });\n\n allDetections.push(...boostedDetections);\n columnStats[col].piiCount += boostedDetections.length;\n\n // Track PII types by column\n const columnTypes = new Set(columnStats[col].piiTypes);\n boostedDetections.forEach(d => columnTypes.add(d.type));\n columnStats[col].piiTypes = Array.from(columnTypes);\n }\n }\n }\n\n // Calculate column PII percentages\n for (let col = 0; col < columnCount; col++) {\n const rowsWithPii = matchesByCell.filter(m => m.column === col).length;\n columnStats[col].piiPercentage = dataRows.length > 0\n ? (rowsWithPii / dataRows.length) * 100\n : 0;\n }\n\n // Build redacted text\n const original = typeof input === 'string' ? input : input.toString('utf-8');\n const redacted = this.redact(original, {\n original,\n redacted: original,\n detections: allDetections,\n redactionMap: {},\n stats: { piiCount: allDetections.length },\n rowCount: dataRows.length,\n columnCount,\n headers,\n columnStats,\n matchesByCell\n } as CsvDetectionResult, opts);\n\n // Build redaction map\n const redactionMap: Record<string, string> = {};\n allDetections.forEach((det) => {\n redactionMap[det.placeholder] = det.value;\n });\n\n return {\n original,\n redacted,\n detections: allDetections,\n redactionMap,\n stats: {\n piiCount: allDetections.length\n },\n rowCount: dataRows.length,\n columnCount,\n headers: headers?.filter((h): h is string => h !== undefined),\n columnStats,\n matchesByCell\n };\n }\n\n /**\n * Redact PII in CSV data\n */\n redact(\n input: Buffer | string,\n detectionResult: CsvDetectionResult,\n options?: CsvProcessorOptions\n ): string {\n const opts = { ...this.defaultOptions, ...options };\n const rows = this.parse(input, options);\n\n if (rows.length === 0) {\n return '';\n }\n\n const delimiter = opts.delimiter || this.detectDelimiter(\n typeof input === 'string' ? input : input.toString('utf-8')\n );\n\n const hasHeader = detectionResult.headers !== undefined;\n\n // Build redaction map: row -> column -> redacted value\n const redactionMap = new Map<number, Map<number, string>>();\n\n for (const cellMatch of detectionResult.matchesByCell) {\n if (!redactionMap.has(cellMatch.row)) {\n redactionMap.set(cellMatch.row, new Map());\n }\n redactionMap.get(cellMatch.row)!.set(\n cellMatch.column,\n '[REDACTED]'\n );\n }\n\n // Build redacted CSV\n const outputRows: string[] = [];\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n const isHeaderRow = hasHeader && i === 0;\n\n if (isHeaderRow) {\n // Don't redact header row\n outputRows.push(this.formatRow(row.values, delimiter, opts.quote));\n } else {\n // Apply redactions to data row\n const rowIndex = hasHeader ? i - 1 : i;\n const redactedValues = row.values.map((value, colIndex) => {\n return redactionMap.get(rowIndex)?.get(colIndex) || value;\n });\n outputRows.push(this.formatRow(redactedValues, delimiter, opts.quote));\n }\n }\n\n return outputRows.join('\\n');\n }\n\n /**\n * Parse a single CSV row\n */\n private parseRow(\n line: string,\n delimiter: string,\n quote: string,\n _escape: string\n ): string[] {\n const values: string[] = [];\n let current = '';\n let inQuotes = false;\n let i = 0;\n\n while (i < line.length) {\n const char = line[i];\n const nextChar = line[i + 1];\n\n if (char === quote) {\n if (inQuotes && nextChar === quote) {\n // Escaped quote\n current += quote;\n i += 2;\n } else {\n // Toggle quote state\n inQuotes = !inQuotes;\n i++;\n }\n } else if (char === delimiter && !inQuotes) {\n // End of field\n values.push(current);\n current = '';\n i++;\n } else {\n current += char;\n i++;\n }\n }\n\n // Add last field\n values.push(current);\n\n return values;\n }\n\n /**\n * Format a row as CSV\n */\n private formatRow(values: string[], delimiter: string, quote: string): string {\n return values.map(value => {\n // Quote if contains delimiter, quote, or newline\n if (value.includes(delimiter) || value.includes(quote) || value.includes('\\n')) {\n // Escape quotes\n const escaped = value.replace(new RegExp(quote, 'g'), quote + quote);\n return `${quote}${escaped}${quote}`;\n }\n return value;\n }).join(delimiter);\n }\n\n /**\n * Auto-detect CSV delimiter\n */\n private detectDelimiter(text: string): string {\n const delimiters = [',', '\\t', ';', '|'];\n const lines = text.split(/\\r?\\n/).slice(0, 5); // Check first 5 lines\n\n let bestDelimiter = ',';\n let bestScore = 0;\n\n for (const delimiter of delimiters) {\n const counts = lines.map(line => {\n // Count non-quoted occurrences\n let count = 0;\n let inQuotes = false;\n for (const char of line) {\n if (char === '\"') inQuotes = !inQuotes;\n if (char === delimiter && !inQuotes) count++;\n }\n return count;\n });\n\n // Good delimiter has consistent count across lines\n if (counts.length > 0 && counts[0] > 0) {\n const avg = counts.reduce((a, b) => a + b, 0) / counts.length;\n const variance = counts.reduce((sum, c) => sum + Math.pow(c - avg, 2), 0) / counts.length;\n\n // Score: high count, low variance\n const score = avg / (variance + 1);\n if (score > bestScore) {\n bestScore = score;\n bestDelimiter = delimiter;\n }\n }\n }\n\n return bestDelimiter;\n }\n\n /**\n * Detect if first row is likely a header\n */\n private detectHeader(rows: CsvRow[]): boolean {\n if (rows.length < 2) {\n return false; // Need at least 2 rows to compare\n }\n\n const firstRow = rows[0].values;\n const secondRow = rows[1].values;\n\n // Check if first row values are shorter and more text-like\n const firstRowAvgLen = firstRow.reduce((sum, v) => sum + v.length, 0) / firstRow.length;\n const secondRowAvgLen = secondRow.reduce((sum, v) => sum + v.length, 0) / secondRow.length;\n\n // Headers tend to be shorter\n if (firstRowAvgLen > secondRowAvgLen * 1.5) {\n return false;\n }\n\n // Check if first row contains mostly text (not numbers)\n const firstRowNumeric = firstRow.filter(v => !isNaN(Number(v)) && v.trim() !== '').length;\n const firstRowNonNumeric = firstRow.length - firstRowNumeric;\n\n return firstRowNonNumeric >= firstRowNumeric;\n }\n\n /**\n * Boost confidence if column name indicates PII\n */\n private boostConfidenceFromColumnName(\n detections: PIIDetection[],\n columnName: string | undefined,\n piiIndicatorNames: string[]\n ): PIIDetection[] {\n if (!columnName) return detections;\n\n const nameLower = columnName.toLowerCase().trim();\n const isPiiColumn = piiIndicatorNames.some(indicator =>\n nameLower.includes(indicator.toLowerCase())\n );\n\n if (!isPiiColumn) return detections;\n\n // Boost confidence by 20% (capped at 1.0)\n return detections.map(detection => ({\n ...detection,\n confidence: Math.min(1.0, (detection.confidence || 0.5) * 1.2)\n }));\n }\n\n /**\n * Extract all cell values as text\n */\n extractText(input: Buffer | string, options?: CsvProcessorOptions): string {\n const rows = this.parse(input, options);\n const textParts: string[] = [];\n\n for (const row of rows) {\n for (const value of row.values) {\n if (value.trim().length > 0) {\n textParts.push(value);\n }\n }\n }\n\n return textParts.join(' ');\n }\n\n /**\n * Get column statistics without full PII detection\n */\n getColumnInfo(input: Buffer | string, options?: CsvProcessorOptions): {\n columnCount: number;\n rowCount: number;\n headers?: string[];\n sampleRows: string[][];\n } {\n const rows = this.parse(input, options);\n\n if (rows.length === 0) {\n return {\n columnCount: 0,\n rowCount: 0,\n sampleRows: []\n };\n }\n\n const opts = { ...this.defaultOptions, ...options };\n const hasHeader = opts.hasHeader !== undefined\n ? opts.hasHeader\n : this.detectHeader(rows);\n\n const headers = hasHeader && rows.length > 0 ? rows[0].values : undefined;\n const dataRows = hasHeader ? rows.slice(1) : rows;\n const sampleRows = dataRows.slice(0, 5).map(r => r.values);\n\n return {\n columnCount: rows[0].values.length,\n rowCount: dataRows.length,\n headers,\n sampleRows\n };\n }\n}\n\n/**\n * Create a CSV processor instance\n */\nexport function createCsvProcessor(): CsvProcessor {\n return new CsvProcessor();\n}\n","/**\n * XLSX/Excel document processor for PII detection and redaction in spreadsheets\n */\n\nimport type { DetectionResult, PIIDetection } from '../types';\nimport type { OpenRedaction } from '../detector';\n\n/**\n * XLSX processing options\n */\nexport interface XlsxProcessorOptions {\n /** Sheet names to process (default: all sheets) */\n sheets?: string[];\n /** Sheet indices to process (0-indexed, default: all sheets) */\n sheetIndices?: number[];\n /** Whether to treat first row as header (default: auto-detect) */\n hasHeader?: boolean;\n /** Maximum rows per sheet to process (default: unlimited) */\n maxRows?: number;\n /** Column indices to always redact (0-indexed) */\n alwaysRedactColumns?: number[];\n /** Column names to always redact (requires hasHeader: true) */\n alwaysRedactColumnNames?: string[];\n /** Column indices to skip scanning (0-indexed) */\n skipColumns?: number[];\n /** Column names that indicate PII (boost confidence) */\n piiIndicatorNames?: string[];\n /** Preserve cell formatting (default: true) */\n preserveFormatting?: boolean;\n /** Preserve formulas (default: true, redact values but keep formula) */\n preserveFormulas?: boolean;\n}\n\n/**\n * XLSX detection result with sheet and cell tracking\n */\nexport interface XlsxDetectionResult extends DetectionResult {\n /** Results by sheet */\n sheetResults: SheetDetectionResult[];\n /** Total sheets processed */\n sheetCount: number;\n}\n\n/**\n * Sheet-level detection result\n */\nexport interface SheetDetectionResult {\n /** Sheet name */\n sheetName: string;\n /** Sheet index */\n sheetIndex: number;\n /** Total rows in sheet */\n rowCount: number;\n /** Column count */\n columnCount: number;\n /** Column headers (if detected) */\n headers?: string[];\n /** Column statistics */\n columnStats: Record<number, ColumnStats>;\n /** Cell matches */\n matchesByCell: CellMatch[];\n}\n\n/**\n * Column PII statistics\n */\nexport interface ColumnStats {\n /** Column index */\n columnIndex: number;\n /** Column letter (A, B, C, etc.) */\n columnLetter: string;\n /** Column name (if header available) */\n columnName?: string;\n /** Number of PII instances found */\n piiCount: number;\n /** Percentage of rows with PII (0-100) */\n piiPercentage: number;\n /** PII types found in this column */\n piiTypes: string[];\n}\n\n/**\n * Cell-level PII match\n */\nexport interface CellMatch {\n /** Cell reference (e.g., 'A1', 'B5') */\n cell: string;\n /** Row index (1-indexed, Excel style) */\n row: number;\n /** Column index (0-indexed) */\n column: number;\n /** Column letter */\n columnLetter: string;\n /** Column name (if header available) */\n columnName?: string;\n /** Cell value */\n value: string;\n /** Cell formula (if any) */\n formula?: string;\n /** PII matches in this cell */\n matches: PIIDetection[];\n}\n\n/**\n * XLSX processor for spreadsheet data\n */\nexport class XlsxProcessor {\n private xlsx?: any;\n\n private readonly defaultOptions: Required<Omit<XlsxProcessorOptions, 'sheets' | 'sheetIndices' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns' | 'hasHeader'>> & Partial<Pick<XlsxProcessorOptions, 'sheets' | 'sheetIndices' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns' | 'hasHeader'>> = {\n piiIndicatorNames: [\n 'email', 'e-mail', 'mail', 'email_address',\n 'phone', 'tel', 'telephone', 'mobile', 'phone_number',\n 'ssn', 'social_security', 'social_security_number',\n 'address', 'street', 'street_address', 'city', 'zip', 'zipcode', 'postal', 'postcode',\n 'name', 'firstname', 'first_name', 'lastname', 'last_name', 'fullname', 'full_name',\n 'password', 'pwd', 'secret', 'token', 'api_key',\n 'card', 'credit_card', 'creditcard', 'card_number',\n 'account', 'account_number', 'iban', 'swift',\n 'passport', 'passport_number', 'license', 'licence', 'driver_license',\n 'dob', 'date_of_birth', 'birth_date', 'birthdate'\n ],\n preserveFormatting: true,\n preserveFormulas: true\n };\n\n constructor() {\n // Try to load xlsx dependency\n try {\n this.xlsx = require('xlsx');\n } catch {\n // xlsx not installed\n }\n }\n\n /**\n * Check if XLSX support is available\n */\n isAvailable(): boolean {\n return !!this.xlsx;\n }\n\n /**\n * Parse XLSX from buffer\n */\n parse(buffer: Buffer): any {\n if (!this.xlsx) {\n throw new Error(\n '[XlsxProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n try {\n return this.xlsx.read(buffer, { type: 'buffer', cellFormula: true, cellStyles: true });\n } catch (error: any) {\n throw new Error(`[XlsxProcessor] Failed to parse XLSX: ${error.message}`);\n }\n }\n\n /**\n * Detect PII in XLSX data\n */\n detect(\n buffer: Buffer,\n detector: OpenRedaction,\n options?: XlsxProcessorOptions\n ): XlsxDetectionResult {\n if (!this.xlsx) {\n throw new Error(\n '[XlsxProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n const opts = { ...this.defaultOptions, ...options };\n const workbook = this.parse(buffer);\n\n // Determine which sheets to process\n const sheetNames = this.getSheetNamesToProcess(workbook, opts);\n\n const sheetResults: SheetDetectionResult[] = [];\n const allDetections: PIIDetection[] = [];\n const allTypes = new Set<string>();\n\n for (let sheetIndex = 0; sheetIndex < sheetNames.length; sheetIndex++) {\n const sheetName = sheetNames[sheetIndex];\n const sheet = workbook.Sheets[sheetName];\n\n const sheetResult = this.detectSheet(\n sheet,\n sheetName,\n sheetIndex,\n detector,\n opts\n );\n\n sheetResults.push(sheetResult);\n allDetections.push(...sheetResult.matchesByCell.flatMap(c => c.matches));\n sheetResult.matchesByCell.forEach(cell => {\n cell.matches.forEach(det => allTypes.add(det.type));\n });\n }\n\n const original = this.extractText(buffer, options);\n const redactedBuffer = this.redact(buffer, {\n original,\n redacted: original,\n detections: allDetections,\n redactionMap: {},\n stats: { piiCount: allDetections.length },\n sheetResults,\n sheetCount: sheetResults.length\n } as XlsxDetectionResult, options);\n const redacted = this.extractText(redactedBuffer, options);\n\n const redactionMap: Record<string, string> = {};\n allDetections.forEach(det => {\n redactionMap[det.placeholder] = det.value;\n });\n\n return {\n original,\n redacted,\n detections: allDetections,\n redactionMap,\n stats: {\n piiCount: allDetections.length\n },\n sheetResults,\n sheetCount: sheetResults.length\n };\n }\n\n /**\n * Detect PII in a single sheet\n */\n private detectSheet(\n sheet: any,\n sheetName: string,\n sheetIndex: number,\n detector: OpenRedaction,\n options: Required<Omit<XlsxProcessorOptions, 'sheets' | 'sheetIndices' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns' | 'hasHeader'>> & Partial<Pick<XlsxProcessorOptions, 'sheets' | 'sheetIndices' | 'maxRows' | 'alwaysRedactColumns' | 'alwaysRedactColumnNames' | 'skipColumns' | 'hasHeader'>>\n ): SheetDetectionResult {\n // Get sheet range\n const range = this.xlsx.utils.decode_range(sheet['!ref'] || 'A1');\n const startRow = range.s.r;\n const endRow = options.maxRows !== undefined\n ? Math.min(range.e.r, startRow + options.maxRows - 1)\n : range.e.r;\n const startCol = range.s.c;\n const endCol = range.e.c;\n\n const columnCount = endCol - startCol + 1;\n\n // Detect header\n const hasHeader = options.hasHeader !== undefined\n ? options.hasHeader\n : this.detectHeader(sheet, range);\n\n const headers = hasHeader\n ? this.getRowValues(sheet, startRow, startCol, endCol)\n : undefined;\n\n const dataStartRow = hasHeader ? startRow + 1 : startRow;\n\n // Build column name to index map\n const columnNameToIndex = new Map<string, number>();\n if (headers) {\n headers.forEach((header, index) => {\n if (header) {\n columnNameToIndex.set(header.toLowerCase().trim(), index);\n }\n });\n }\n\n // Determine which columns to always redact\n const alwaysRedactCols = new Set<number>(options.alwaysRedactColumns || []);\n if (options.alwaysRedactColumnNames && headers) {\n options.alwaysRedactColumnNames.forEach(name => {\n const index = columnNameToIndex.get(name.toLowerCase().trim());\n if (index !== undefined) {\n alwaysRedactCols.add(index);\n }\n });\n }\n\n // Determine which columns to skip\n const skipCols = new Set<number>(options.skipColumns || []);\n\n // Initialize column stats\n const columnStats: Record<number, ColumnStats> = {};\n for (let col = 0; col <= endCol - startCol; col++) {\n columnStats[col] = {\n columnIndex: col,\n columnLetter: this.columnToLetter(col),\n columnName: headers?.[col],\n piiCount: 0,\n piiPercentage: 0,\n piiTypes: []\n };\n }\n\n const matchesByCell: CellMatch[] = [];\n\n // Scan data rows\n for (let row = dataStartRow; row <= endRow; row++) {\n for (let col = startCol; col <= endCol; col++) {\n const colIndex = col - startCol;\n\n // Skip if column should be skipped\n if (skipCols.has(colIndex)) {\n continue;\n }\n\n const cellRef = this.xlsx.utils.encode_cell({ r: row, c: col });\n const cell = sheet[cellRef];\n\n if (!cell) continue;\n\n const cellValue = this.getCellValue(cell);\n if (!cellValue) continue;\n\n const cellFormula = cell.f;\n\n // Always redact this column?\n if (alwaysRedactCols.has(colIndex)) {\n const detection: PIIDetection = {\n type: 'SENSITIVE_COLUMN',\n value: cellValue,\n placeholder: `[SENSITIVE_COLUMN_${colIndex}]`,\n position: [0, cellValue.length],\n severity: 'high',\n confidence: 1.0\n };\n\n matchesByCell.push({\n cell: cellRef,\n row: row + 1, // 1-indexed for Excel\n column: colIndex,\n columnLetter: this.columnToLetter(colIndex),\n columnName: headers?.[colIndex],\n value: cellValue,\n formula: cellFormula,\n matches: [detection]\n });\n\n columnStats[colIndex].piiCount++;\n continue;\n }\n\n // Detect PII\n const result = detector.detect(cellValue);\n\n if (result.detections.length > 0) {\n // Boost confidence if column name indicates PII\n const boostedDetections = this.boostConfidenceFromColumnName(\n result.detections,\n headers?.[colIndex],\n options.piiIndicatorNames || []\n );\n\n matchesByCell.push({\n cell: cellRef,\n row: row + 1, // 1-indexed for Excel\n column: colIndex,\n columnLetter: this.columnToLetter(colIndex),\n columnName: headers?.[colIndex],\n value: cellValue,\n formula: cellFormula,\n matches: boostedDetections\n });\n\n columnStats[colIndex].piiCount += boostedDetections.length;\n\n // Track PII types by column\n const columnTypes = new Set(columnStats[colIndex].piiTypes);\n boostedDetections.forEach(d => columnTypes.add(d.type));\n columnStats[colIndex].piiTypes = Array.from(columnTypes);\n }\n }\n }\n\n // Calculate column PII percentages\n const dataRowCount = endRow - dataStartRow + 1;\n for (let col = 0; col <= endCol - startCol; col++) {\n const rowsWithPii = matchesByCell.filter(m => m.column === col).length;\n columnStats[col].piiPercentage = dataRowCount > 0\n ? (rowsWithPii / dataRowCount) * 100\n : 0;\n }\n\n return {\n sheetName,\n sheetIndex,\n rowCount: dataRowCount,\n columnCount,\n headers: headers?.filter((h): h is string => h !== undefined),\n columnStats,\n matchesByCell\n };\n }\n\n /**\n * Redact PII in XLSX data\n */\n redact(\n buffer: Buffer,\n detectionResult: XlsxDetectionResult,\n options?: XlsxProcessorOptions\n ): Buffer {\n if (!this.xlsx) {\n throw new Error(\n '[XlsxProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n const opts = { ...this.defaultOptions, ...options };\n const workbook = this.parse(buffer);\n\n // Apply redactions for each sheet\n for (const sheetResult of detectionResult.sheetResults) {\n const sheet = workbook.Sheets[sheetResult.sheetName];\n\n for (const cellMatch of sheetResult.matchesByCell) {\n const cellRef = cellMatch.cell;\n const cell = sheet[cellRef];\n\n if (!cell) continue;\n\n // Redact value\n cell.v = '[REDACTED]';\n cell.w = '[REDACTED]';\n\n // Preserve formula if configured\n if (!opts.preserveFormulas) {\n delete cell.f;\n }\n\n // Update cell type to string\n cell.t = 's';\n }\n }\n\n // Write back to buffer\n return this.xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });\n }\n\n /**\n * Get cell value as string\n */\n private getCellValue(cell: any): string {\n if (!cell) return '';\n\n // Try formatted value first\n if (cell.w !== undefined) {\n return String(cell.w);\n }\n\n // Fall back to raw value\n if (cell.v !== undefined) {\n return String(cell.v);\n }\n\n return '';\n }\n\n /**\n * Get row values\n */\n private getRowValues(sheet: any, row: number, startCol: number, endCol: number): (string | undefined)[] {\n const values: (string | undefined)[] = [];\n\n for (let col = startCol; col <= endCol; col++) {\n const cellRef = this.xlsx.utils.encode_cell({ r: row, c: col });\n const cell = sheet[cellRef];\n values.push(cell ? this.getCellValue(cell) : undefined);\n }\n\n return values;\n }\n\n /**\n * Detect if first row is likely a header\n */\n private detectHeader(sheet: any, range: any): boolean {\n const firstRow = this.getRowValues(sheet, range.s.r, range.s.c, range.e.c);\n const secondRow = range.s.r + 1 <= range.e.r\n ? this.getRowValues(sheet, range.s.r + 1, range.s.c, range.e.c)\n : null;\n\n if (!secondRow) return false;\n\n // Check if first row values are shorter and more text-like\n const firstRowValues = firstRow.filter(v => v !== undefined) as string[];\n const secondRowValues = secondRow.filter(v => v !== undefined) as string[];\n\n if (firstRowValues.length === 0 || secondRowValues.length === 0) {\n return false;\n }\n\n const firstRowAvgLen = firstRowValues.reduce((sum, v) => sum + v.length, 0) / firstRowValues.length;\n const secondRowAvgLen = secondRowValues.reduce((sum, v) => sum + v.length, 0) / secondRowValues.length;\n\n // Headers tend to be shorter\n if (firstRowAvgLen > secondRowAvgLen * 1.5) {\n return false;\n }\n\n // Check if first row contains mostly text (not numbers)\n const firstRowNumeric = firstRowValues.filter(v => !isNaN(Number(v)) && v.trim() !== '').length;\n const firstRowNonNumeric = firstRowValues.length - firstRowNumeric;\n\n return firstRowNonNumeric >= firstRowNumeric;\n }\n\n /**\n * Convert column index to letter (0 = A, 25 = Z, 26 = AA)\n */\n private columnToLetter(col: number): string {\n let letter = '';\n while (col >= 0) {\n letter = String.fromCharCode((col % 26) + 65) + letter;\n col = Math.floor(col / 26) - 1;\n }\n return letter;\n }\n\n /**\n * Get sheet names to process based on options\n */\n private getSheetNamesToProcess(workbook: any, options: Partial<XlsxProcessorOptions>): string[] {\n const allSheetNames = workbook.SheetNames;\n\n // If specific sheets requested by name\n if (options.sheets && options.sheets.length > 0) {\n return options.sheets.filter(name => allSheetNames.includes(name));\n }\n\n // If specific sheets requested by index\n if (options.sheetIndices && options.sheetIndices.length > 0) {\n return options.sheetIndices\n .filter(index => index >= 0 && index < allSheetNames.length)\n .map(index => allSheetNames[index]);\n }\n\n // Process all sheets\n return allSheetNames;\n }\n\n /**\n * Boost confidence if column name indicates PII\n */\n private boostConfidenceFromColumnName(\n detections: PIIDetection[],\n columnName: string | undefined,\n piiIndicatorNames: string[]\n ): PIIDetection[] {\n if (!columnName) return detections;\n\n const nameLower = columnName.toLowerCase().trim();\n const isPiiColumn = piiIndicatorNames.some(indicator =>\n nameLower.includes(indicator.toLowerCase())\n );\n\n if (!isPiiColumn) return detections;\n\n // Boost confidence by 20% (capped at 1.0)\n return detections.map(detection => ({\n ...detection,\n confidence: Math.min(1.0, (detection.confidence || 0.5) * 1.2)\n }));\n }\n\n /**\n * Extract all cell values as text\n */\n extractText(buffer: Buffer, options?: XlsxProcessorOptions): string {\n if (!this.xlsx) {\n throw new Error(\n '[XlsxProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n const workbook = this.parse(buffer);\n const opts = { ...this.defaultOptions, ...options };\n const sheetNames = this.getSheetNamesToProcess(workbook, opts);\n\n const textParts: string[] = [];\n\n for (const sheetName of sheetNames) {\n const sheet = workbook.Sheets[sheetName];\n const range = this.xlsx.utils.decode_range(sheet['!ref'] || 'A1');\n\n for (let row = range.s.r; row <= range.e.r; row++) {\n for (let col = range.s.c; col <= range.e.c; col++) {\n const cellRef = this.xlsx.utils.encode_cell({ r: row, c: col });\n const cell = sheet[cellRef];\n\n if (cell) {\n const value = this.getCellValue(cell);\n if (value.trim().length > 0) {\n textParts.push(value);\n }\n }\n }\n }\n }\n\n return textParts.join(' ');\n }\n\n /**\n * Get workbook metadata\n */\n getMetadata(buffer: Buffer): {\n sheetNames: string[];\n sheetCount: number;\n } {\n if (!this.xlsx) {\n throw new Error(\n '[XlsxProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n const workbook = this.parse(buffer);\n\n return {\n sheetNames: workbook.SheetNames,\n sheetCount: workbook.SheetNames.length\n };\n }\n}\n\n/**\n * Create an XLSX processor instance\n */\nexport function createXlsxProcessor(): XlsxProcessor {\n return new XlsxProcessor();\n}\n","/**\n * Document text extraction with optional peer dependencies\n */\n\nimport type {\n IDocumentProcessor,\n DocumentFormat,\n DocumentOptions,\n DocumentMetadata\n} from './types';\nimport { OCRProcessor } from './OCRProcessor';\nimport { JsonProcessor } from './JsonProcessor';\nimport { CsvProcessor } from './CsvProcessor';\nimport { XlsxProcessor } from './XlsxProcessor';\n\n/**\n * Document processor with optional PDF, DOCX, OCR, JSON, CSV, and XLSX support\n * Requires peer dependencies:\n * - pdf-parse (for PDF)\n * - mammoth (for DOCX)\n * - tesseract.js (for OCR/images)\n * - xlsx (for Excel/XLSX)\n */\nexport class DocumentProcessor implements IDocumentProcessor {\n private pdfParse?: any;\n private mammoth?: any;\n private ocrProcessor: OCRProcessor;\n private jsonProcessor: JsonProcessor;\n private csvProcessor: CsvProcessor;\n private xlsxProcessor: XlsxProcessor;\n\n constructor() {\n // Try to load optional dependencies\n try {\n this.pdfParse = require('pdf-parse');\n } catch {\n // pdf-parse not installed\n }\n\n try {\n this.mammoth = require('mammoth');\n } catch {\n // mammoth not installed\n }\n\n // Initialize processors\n this.ocrProcessor = new OCRProcessor();\n this.jsonProcessor = new JsonProcessor();\n this.csvProcessor = new CsvProcessor();\n this.xlsxProcessor = new XlsxProcessor();\n }\n\n /**\n * Extract text from document buffer\n */\n async extractText(buffer: Buffer, options?: DocumentOptions): Promise<string> {\n const format = options?.format || this.detectFormat(buffer);\n\n if (!format) {\n throw new Error('[DocumentProcessor] Unable to detect document format. Supported: PDF, DOCX, TXT, images (with OCR)');\n }\n\n // Check size limit\n const maxSize = options?.maxSize || 50 * 1024 * 1024; // 50MB default\n if (buffer.length > maxSize) {\n throw new Error(`[DocumentProcessor] Document size (${buffer.length} bytes) exceeds maximum (${maxSize} bytes)`);\n }\n\n switch (format) {\n case 'pdf':\n return this.extractPdfText(buffer, options);\n case 'docx':\n return this.extractDocxText(buffer, options);\n case 'txt':\n return buffer.toString('utf-8');\n case 'image':\n return this.extractImageText(buffer, options);\n case 'json':\n return this.extractJsonText(buffer, options);\n case 'csv':\n return this.extractCsvText(buffer, options);\n case 'xlsx':\n return this.extractXlsxText(buffer, options);\n default:\n throw new Error(`[DocumentProcessor] Unsupported format: ${format}`);\n }\n }\n\n /**\n * Get document metadata\n */\n async getMetadata(buffer: Buffer, options?: DocumentOptions): Promise<DocumentMetadata> {\n const format = options?.format || this.detectFormat(buffer);\n\n if (!format) {\n throw new Error('[DocumentProcessor] Unable to detect document format');\n }\n\n switch (format) {\n case 'pdf':\n return this.getPdfMetadata(buffer, options);\n case 'docx':\n return this.getDocxMetadata(buffer, options);\n case 'txt':\n return {\n format: 'txt',\n pages: undefined\n };\n case 'image':\n return this.getImageMetadata(buffer, options);\n case 'json':\n return this.getJsonMetadata(buffer, options);\n case 'csv':\n return this.getCsvMetadata(buffer, options);\n case 'xlsx':\n return this.getXlsxMetadata(buffer, options);\n default:\n throw new Error(`[DocumentProcessor] Unsupported format: ${format}`);\n }\n }\n\n /**\n * Detect document format from buffer\n */\n detectFormat(buffer: Buffer): DocumentFormat | null {\n if (buffer.length < 4) {\n return null;\n }\n\n // PDF: starts with %PDF\n if (buffer.toString('utf-8', 0, 4) === '%PDF') {\n return 'pdf';\n }\n\n // PNG: starts with 89 50 4E 47 0D 0A 1A 0A\n if (buffer.length >= 8 &&\n buffer[0] === 0x89 && buffer[1] === 0x50 &&\n buffer[2] === 0x4E && buffer[3] === 0x47) {\n return 'image';\n }\n\n // JPEG: starts with FF D8 FF\n if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {\n return 'image';\n }\n\n // TIFF: starts with 49 49 2A 00 (little-endian) or 4D 4D 00 2A (big-endian)\n if ((buffer[0] === 0x49 && buffer[1] === 0x49 && buffer[2] === 0x2A && buffer[3] === 0x00) ||\n (buffer[0] === 0x4D && buffer[1] === 0x4D && buffer[2] === 0x00 && buffer[3] === 0x2A)) {\n return 'image';\n }\n\n // BMP: starts with 42 4D\n if (buffer[0] === 0x42 && buffer[1] === 0x4D) {\n return 'image';\n }\n\n // WebP: starts with RIFF followed by WEBP at offset 8\n if (buffer.length >= 12 &&\n buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 &&\n buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {\n return 'image';\n }\n\n // DOCX/XLSX: ZIP file starting with PK (Office docs are ZIP archives)\n if (buffer[0] === 0x50 && buffer[1] === 0x4B) {\n // Check for [Content_Types].xml which is specific to Office Open XML\n const zipHeader = buffer.toString('utf-8', 0, Math.min(500, buffer.length));\n if (zipHeader.includes('word/') || zipHeader.includes('[Content_Types].xml')) {\n return 'docx';\n }\n if (zipHeader.includes('xl/')) {\n return 'xlsx';\n }\n }\n\n // JSON: Try to parse as JSON\n const text = buffer.toString('utf-8');\n const trimmed = text.trim();\n if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||\n (trimmed.startsWith('[') && trimmed.endsWith(']'))) {\n if (this.jsonProcessor.isValid(buffer)) {\n return 'json';\n }\n }\n\n // CSV: Check for delimiter patterns (comma, tab, semicolon, pipe)\n // Look for consistent delimiters across multiple lines\n const lines = text.split(/\\r?\\n/).slice(0, 5);\n if (lines.length >= 2) {\n const delimiters = [',', '\\t', ';', '|'];\n for (const delimiter of delimiters) {\n const counts = lines.map(line => (line.match(new RegExp(delimiter, 'g')) || []).length);\n // If we see consistent delimiter counts > 0, likely CSV\n if (counts[0] > 0 && counts.every(c => c === counts[0])) {\n return 'csv';\n }\n }\n }\n\n // TXT: assume plain text if no other format detected\n // Check if buffer contains mostly printable ASCII/UTF-8\n const sample = buffer.slice(0, Math.min(1000, buffer.length));\n const nonPrintable = sample.filter(byte => byte < 32 && byte !== 9 && byte !== 10 && byte !== 13).length;\n if (nonPrintable < sample.length * 0.1) {\n return 'txt';\n }\n\n return null;\n }\n\n /**\n * Check if format is supported\n */\n isFormatSupported(format: DocumentFormat): boolean {\n switch (format) {\n case 'pdf':\n return !!this.pdfParse;\n case 'docx':\n return !!this.mammoth;\n case 'txt':\n return true;\n case 'image':\n return this.ocrProcessor.isAvailable();\n case 'json':\n return true; // Always supported (native)\n case 'csv':\n return true; // Always supported (native)\n case 'xlsx':\n return this.xlsxProcessor.isAvailable();\n default:\n return false;\n }\n }\n\n /**\n * Extract text from PDF\n */\n private async extractPdfText(buffer: Buffer, options?: DocumentOptions): Promise<string> {\n if (!this.pdfParse) {\n throw new Error(\n '[DocumentProcessor] PDF support requires pdf-parse. Install with: npm install pdf-parse'\n );\n }\n\n try {\n const data = await this.pdfParse(buffer, {\n password: options?.password,\n max: options?.pages ? Math.max(...options.pages) : undefined\n });\n\n // If specific pages requested, filter them\n if (options?.pages) {\n // pdf-parse doesn't support per-page text extraction easily\n // Return full text for now (enhancement: implement page filtering)\n return data.text;\n }\n\n return data.text || '';\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] PDF extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Extract text from DOCX\n */\n private async extractDocxText(buffer: Buffer, _options?: DocumentOptions): Promise<string> {\n if (!this.mammoth) {\n throw new Error(\n '[DocumentProcessor] DOCX support requires mammoth. Install with: npm install mammoth'\n );\n }\n\n try {\n const result = await this.mammoth.extractRawText({ buffer });\n return result.value || '';\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] DOCX extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Get PDF metadata\n */\n private async getPdfMetadata(buffer: Buffer, _options?: DocumentOptions): Promise<DocumentMetadata> {\n if (!this.pdfParse) {\n throw new Error(\n '[DocumentProcessor] PDF support requires pdf-parse. Install with: npm install pdf-parse'\n );\n }\n\n try {\n const data = await this.pdfParse(buffer, {\n password: _options?.password\n });\n\n return {\n format: 'pdf',\n pages: data.numpages,\n title: data.info?.Title,\n author: data.info?.Author,\n creationDate: data.info?.CreationDate ? new Date(data.info.CreationDate) : undefined,\n modifiedDate: data.info?.ModDate ? new Date(data.info.ModDate) : undefined,\n custom: data.info\n };\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] PDF metadata extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Get DOCX metadata\n */\n private async getDocxMetadata(_buffer: Buffer, _options?: DocumentOptions): Promise<DocumentMetadata> {\n // mammoth doesn't provide metadata extraction\n // Basic metadata only\n return {\n format: 'docx',\n pages: undefined // Word doesn't have fixed pages\n };\n }\n\n /**\n * Extract text from image using OCR\n */\n private async extractImageText(buffer: Buffer, options?: DocumentOptions): Promise<string> {\n if (!this.ocrProcessor.isAvailable()) {\n throw new Error(\n '[DocumentProcessor] Image/OCR support requires tesseract.js. Install with: npm install tesseract.js'\n );\n }\n\n try {\n const result = await this.ocrProcessor.recognizeText(buffer, options?.ocrOptions);\n return result.text;\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] Image text extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Get image metadata\n */\n private async getImageMetadata(buffer: Buffer, options?: DocumentOptions): Promise<DocumentMetadata> {\n if (!this.ocrProcessor.isAvailable()) {\n return {\n format: 'image',\n pages: undefined,\n usedOCR: false\n };\n }\n\n try {\n // Run OCR to get confidence\n const result = await this.ocrProcessor.recognizeText(buffer, options?.ocrOptions);\n\n return {\n format: 'image',\n pages: undefined,\n usedOCR: true,\n ocrConfidence: result.confidence\n };\n } catch {\n // OCR failed, return basic metadata\n return {\n format: 'image',\n pages: undefined,\n usedOCR: false\n };\n }\n }\n\n /**\n * Extract text from JSON\n */\n private async extractJsonText(buffer: Buffer, _options?: DocumentOptions): Promise<string> {\n try {\n return this.jsonProcessor.extractText(buffer);\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] JSON extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Extract text from CSV\n */\n private async extractCsvText(buffer: Buffer, _options?: DocumentOptions): Promise<string> {\n try {\n return this.csvProcessor.extractText(buffer);\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] CSV extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Extract text from XLSX\n */\n private async extractXlsxText(buffer: Buffer, _options?: DocumentOptions): Promise<string> {\n if (!this.xlsxProcessor.isAvailable()) {\n throw new Error(\n '[DocumentProcessor] XLSX support requires xlsx package. Install with: npm install xlsx'\n );\n }\n\n try {\n return this.xlsxProcessor.extractText(buffer);\n } catch (error: any) {\n throw new Error(`[DocumentProcessor] XLSX extraction failed: ${error.message}`);\n }\n }\n\n /**\n * Get JSON metadata\n */\n private async getJsonMetadata(buffer: Buffer, _options?: DocumentOptions): Promise<DocumentMetadata> {\n try {\n const data = this.jsonProcessor.parse(buffer);\n const isArray = Array.isArray(data);\n\n return {\n format: 'json',\n pages: undefined,\n custom: {\n isArray,\n itemCount: isArray ? data.length : Object.keys(data).length\n }\n };\n } catch {\n return {\n format: 'json',\n pages: undefined\n };\n }\n }\n\n /**\n * Get CSV metadata\n */\n private async getCsvMetadata(buffer: Buffer, _options?: DocumentOptions): Promise<DocumentMetadata> {\n try {\n const info = this.csvProcessor.getColumnInfo(buffer);\n\n return {\n format: 'csv',\n pages: undefined,\n custom: {\n rowCount: info.rowCount,\n columnCount: info.columnCount,\n headers: info.headers\n }\n };\n } catch {\n return {\n format: 'csv',\n pages: undefined\n };\n }\n }\n\n /**\n * Get XLSX metadata\n */\n private async getXlsxMetadata(buffer: Buffer, _options?: DocumentOptions): Promise<DocumentMetadata> {\n if (!this.xlsxProcessor.isAvailable()) {\n return {\n format: 'xlsx',\n pages: undefined\n };\n }\n\n try {\n const metadata = this.xlsxProcessor.getMetadata(buffer);\n\n return {\n format: 'xlsx',\n pages: undefined,\n custom: {\n sheetNames: metadata.sheetNames,\n sheetCount: metadata.sheetCount\n }\n };\n } catch {\n return {\n format: 'xlsx',\n pages: undefined\n };\n }\n }\n\n /**\n * Get OCR processor instance\n */\n getOCRProcessor(): OCRProcessor {\n return this.ocrProcessor;\n }\n\n /**\n * Get JSON processor instance\n */\n getJsonProcessor(): JsonProcessor {\n return this.jsonProcessor;\n }\n\n /**\n * Get CSV processor instance\n */\n getCsvProcessor(): CsvProcessor {\n return this.csvProcessor;\n }\n\n /**\n * Get XLSX processor instance\n */\n getXlsxProcessor(): XlsxProcessor {\n return this.xlsxProcessor;\n }\n}\n\n/**\n * Create a document processor instance\n */\nexport function createDocumentProcessor(): DocumentProcessor {\n return new DocumentProcessor();\n}\n"],"mappings":"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","names":["original"]}
@@ -1,149 +0,0 @@
1
- // src/workers/WorkerPool.ts
2
- import { Worker } from "worker_threads";
3
- import { cpus } from "os";
4
- import { join } from "path";
5
- var WorkerPool = class {
6
- constructor(config = {}) {
7
- this.workers = [];
8
- this.availableWorkers = [];
9
- this.taskQueue = [];
10
- this.totalProcessingTime = 0;
11
- this.config = {
12
- numWorkers: config.numWorkers || cpus().length,
13
- maxQueueSize: config.maxQueueSize || 100,
14
- idleTimeout: config.idleTimeout || 3e4
15
- };
16
- this.stats = {
17
- activeWorkers: 0,
18
- idleWorkers: 0,
19
- queueSize: 0,
20
- totalProcessed: 0,
21
- totalErrors: 0,
22
- avgProcessingTime: 0
23
- };
24
- this.workerPath = join(__dirname, "worker.js");
25
- }
26
- /**
27
- * Initialize worker pool
28
- */
29
- async initialize() {
30
- for (let i = 0; i < this.config.numWorkers; i++) {
31
- await this.createWorker();
32
- }
33
- }
34
- /**
35
- * Create a new worker
36
- */
37
- async createWorker() {
38
- const worker = new Worker(this.workerPath);
39
- worker.on("message", (result) => {
40
- this.handleWorkerResult(worker, result);
41
- });
42
- worker.on("error", (error) => {
43
- console.error("[WorkerPool] Worker error:", error);
44
- this.stats.totalErrors++;
45
- this.removeWorker(worker);
46
- this.createWorker();
47
- });
48
- worker.on("exit", (code) => {
49
- if (code !== 0) {
50
- console.error(`[WorkerPool] Worker exited with code ${code}`);
51
- }
52
- this.removeWorker(worker);
53
- });
54
- this.workers.push(worker);
55
- this.availableWorkers.push(worker);
56
- this.stats.idleWorkers++;
57
- return worker;
58
- }
59
- /**
60
- * Execute a task on the worker pool
61
- */
62
- async execute(task) {
63
- if (this.taskQueue.length >= this.config.maxQueueSize) {
64
- throw new Error(`[WorkerPool] Queue is full (max: ${this.config.maxQueueSize})`);
65
- }
66
- return new Promise((resolve, reject) => {
67
- this.taskQueue.push({ task, resolve, reject });
68
- this.stats.queueSize = this.taskQueue.length;
69
- this.processQueue();
70
- });
71
- }
72
- /**
73
- * Process task queue
74
- */
75
- processQueue() {
76
- while (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
77
- const worker = this.availableWorkers.shift();
78
- const { task, resolve, reject } = this.taskQueue.shift();
79
- this.stats.idleWorkers--;
80
- this.stats.activeWorkers++;
81
- this.stats.queueSize = this.taskQueue.length;
82
- worker.__currentTask = { resolve, reject, startTime: Date.now() };
83
- worker.postMessage(task);
84
- }
85
- }
86
- /**
87
- * Handle worker result
88
- */
89
- handleWorkerResult(worker, result) {
90
- const currentTask = worker.__currentTask;
91
- if (!currentTask) return;
92
- this.stats.activeWorkers--;
93
- this.stats.idleWorkers++;
94
- this.stats.totalProcessed++;
95
- this.totalProcessingTime += result.processingTime;
96
- this.stats.avgProcessingTime = this.totalProcessingTime / this.stats.totalProcessed;
97
- this.availableWorkers.push(worker);
98
- delete worker.__currentTask;
99
- if (result.error) {
100
- this.stats.totalErrors++;
101
- currentTask.reject(new Error(result.error));
102
- } else {
103
- currentTask.resolve(result.result);
104
- }
105
- this.processQueue();
106
- }
107
- /**
108
- * Remove worker from pool
109
- */
110
- removeWorker(worker) {
111
- const index = this.workers.indexOf(worker);
112
- if (index !== -1) {
113
- this.workers.splice(index, 1);
114
- }
115
- const availableIndex = this.availableWorkers.indexOf(worker);
116
- if (availableIndex !== -1) {
117
- this.availableWorkers.splice(availableIndex, 1);
118
- this.stats.idleWorkers--;
119
- }
120
- }
121
- /**
122
- * Get pool statistics
123
- */
124
- getStats() {
125
- return { ...this.stats };
126
- }
127
- /**
128
- * Terminate all workers
129
- */
130
- async terminate() {
131
- const terminatePromises = this.workers.map((worker) => worker.terminate());
132
- await Promise.all(terminatePromises);
133
- this.workers = [];
134
- this.availableWorkers = [];
135
- this.taskQueue = [];
136
- this.stats.activeWorkers = 0;
137
- this.stats.idleWorkers = 0;
138
- this.stats.queueSize = 0;
139
- }
140
- };
141
- function createWorkerPool(config) {
142
- return new WorkerPool(config);
143
- }
144
-
145
- export {
146
- WorkerPool,
147
- createWorkerPool
148
- };
149
- //# sourceMappingURL=chunk-MYYLGNXS.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/workers/WorkerPool.ts"],"sourcesContent":["/**\n * Worker thread pool for parallel processing\n */\n\nimport { Worker } from 'worker_threads';\nimport { cpus } from 'os';\nimport { join } from 'path';\nimport type { WorkerTask, WorkerResult, WorkerPoolConfig, WorkerPoolStats } from './types';\n\n/**\n * Worker pool for parallel text detection and document processing\n */\nexport class WorkerPool {\n private workers: Worker[] = [];\n private availableWorkers: Worker[] = [];\n private taskQueue: Array<{ task: WorkerTask; resolve: Function; reject: Function }> = [];\n private config: Required<WorkerPoolConfig>;\n private stats: WorkerPoolStats;\n private workerPath: string;\n private totalProcessingTime: number = 0;\n\n constructor(config: WorkerPoolConfig = {}) {\n this.config = {\n numWorkers: config.numWorkers || cpus().length,\n maxQueueSize: config.maxQueueSize || 100,\n idleTimeout: config.idleTimeout || 30000\n };\n\n this.stats = {\n activeWorkers: 0,\n idleWorkers: 0,\n queueSize: 0,\n totalProcessed: 0,\n totalErrors: 0,\n avgProcessingTime: 0\n };\n\n // Worker script path (will be in dist after build)\n this.workerPath = join(__dirname, 'worker.js');\n }\n\n /**\n * Initialize worker pool\n */\n async initialize(): Promise<void> {\n for (let i = 0; i < this.config.numWorkers; i++) {\n await this.createWorker();\n }\n }\n\n /**\n * Create a new worker\n */\n private async createWorker(): Promise<Worker> {\n const worker = new Worker(this.workerPath);\n\n worker.on('message', (result: WorkerResult) => {\n this.handleWorkerResult(worker, result);\n });\n\n worker.on('error', (error) => {\n console.error('[WorkerPool] Worker error:', error);\n this.stats.totalErrors++;\n this.removeWorker(worker);\n // Create replacement worker\n this.createWorker();\n });\n\n worker.on('exit', (code) => {\n if (code !== 0) {\n console.error(`[WorkerPool] Worker exited with code ${code}`);\n }\n this.removeWorker(worker);\n });\n\n this.workers.push(worker);\n this.availableWorkers.push(worker);\n this.stats.idleWorkers++;\n\n return worker;\n }\n\n /**\n * Execute a task on the worker pool\n */\n async execute<T = any>(task: WorkerTask): Promise<T> {\n // Check queue size\n if (this.taskQueue.length >= this.config.maxQueueSize) {\n throw new Error(`[WorkerPool] Queue is full (max: ${this.config.maxQueueSize})`);\n }\n\n return new Promise((resolve, reject) => {\n this.taskQueue.push({ task, resolve, reject });\n this.stats.queueSize = this.taskQueue.length;\n this.processQueue();\n });\n }\n\n /**\n * Process task queue\n */\n private processQueue(): void {\n while (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {\n const worker = this.availableWorkers.shift()!;\n const { task, resolve, reject } = this.taskQueue.shift()!;\n\n this.stats.idleWorkers--;\n this.stats.activeWorkers++;\n this.stats.queueSize = this.taskQueue.length;\n\n // Store resolve/reject for this task\n (worker as any).__currentTask = { resolve, reject, startTime: Date.now() };\n\n // Send task to worker\n worker.postMessage(task);\n }\n }\n\n /**\n * Handle worker result\n */\n private handleWorkerResult(worker: Worker, result: WorkerResult): void {\n const currentTask = (worker as any).__currentTask;\n if (!currentTask) return;\n\n this.stats.activeWorkers--;\n this.stats.idleWorkers++;\n this.stats.totalProcessed++;\n\n // Update processing time stats\n this.totalProcessingTime += result.processingTime;\n this.stats.avgProcessingTime = this.totalProcessingTime / this.stats.totalProcessed;\n\n // Return worker to available pool\n this.availableWorkers.push(worker);\n delete (worker as any).__currentTask;\n\n // Resolve or reject the promise\n if (result.error) {\n this.stats.totalErrors++;\n currentTask.reject(new Error(result.error));\n } else {\n currentTask.resolve(result.result);\n }\n\n // Process next task in queue\n this.processQueue();\n }\n\n /**\n * Remove worker from pool\n */\n private removeWorker(worker: Worker): void {\n const index = this.workers.indexOf(worker);\n if (index !== -1) {\n this.workers.splice(index, 1);\n }\n\n const availableIndex = this.availableWorkers.indexOf(worker);\n if (availableIndex !== -1) {\n this.availableWorkers.splice(availableIndex, 1);\n this.stats.idleWorkers--;\n }\n }\n\n /**\n * Get pool statistics\n */\n getStats(): WorkerPoolStats {\n return { ...this.stats };\n }\n\n /**\n * Terminate all workers\n */\n async terminate(): Promise<void> {\n const terminatePromises = this.workers.map(worker => worker.terminate());\n await Promise.all(terminatePromises);\n this.workers = [];\n this.availableWorkers = [];\n this.taskQueue = [];\n this.stats.activeWorkers = 0;\n this.stats.idleWorkers = 0;\n this.stats.queueSize = 0;\n }\n}\n\n/**\n * Create a worker pool instance\n */\nexport function createWorkerPool(config?: WorkerPoolConfig): WorkerPool {\n return new WorkerPool(config);\n}\n"],"mappings":"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","names":[]}
@@ -1,34 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
11
- var __esm = (fn, res) => function __init() {
12
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
- };
14
- var __export = (target, all) => {
15
- for (var name in all)
16
- __defProp(target, name, { get: all[name], enumerable: true });
17
- };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
- }
24
- return to;
25
- };
26
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
-
28
- export {
29
- __require,
30
- __esm,
31
- __export,
32
- __toCommonJS
33
- };
34
- //# sourceMappingURL=chunk-WMJKH4XE.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}