@bulkimport/core 0.1.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/LICENSE +21 -0
- package/README.md +373 -0
- package/dist/index.cjs +819 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +335 -0
- package/dist/index.d.ts +335 -0
- package/dist/index.js +777 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/model/ImportStatus.ts","../src/domain/model/Batch.ts","../src/domain/model/Record.ts","../src/domain/model/ValidationResult.ts","../src/domain/services/SchemaValidator.ts","../src/application/EventBus.ts","../src/infrastructure/state/InMemoryStateStore.ts","../src/BulkImport.ts","../src/domain/model/BatchStatus.ts","../src/infrastructure/parsers/CsvParser.ts","../src/infrastructure/sources/BufferSource.ts"],"sourcesContent":["export const ImportStatus = {\n CREATED: 'CREATED',\n PREVIEWING: 'PREVIEWING',\n PREVIEWED: 'PREVIEWED',\n PROCESSING: 'PROCESSING',\n PAUSED: 'PAUSED',\n COMPLETED: 'COMPLETED',\n ABORTED: 'ABORTED',\n FAILED: 'FAILED',\n} as const;\n\nexport type ImportStatus = (typeof ImportStatus)[keyof typeof ImportStatus];\n\nconst VALID_TRANSITIONS: Record<ImportStatus, readonly ImportStatus[]> = {\n [ImportStatus.CREATED]: [ImportStatus.PREVIEWING, ImportStatus.PROCESSING],\n [ImportStatus.PREVIEWING]: [ImportStatus.PREVIEWED, ImportStatus.FAILED],\n [ImportStatus.PREVIEWED]: [ImportStatus.PROCESSING],\n [ImportStatus.PROCESSING]: [ImportStatus.PAUSED, ImportStatus.COMPLETED, ImportStatus.ABORTED, ImportStatus.FAILED],\n [ImportStatus.PAUSED]: [ImportStatus.PROCESSING, ImportStatus.ABORTED],\n [ImportStatus.COMPLETED]: [],\n [ImportStatus.ABORTED]: [],\n [ImportStatus.FAILED]: [],\n};\n\nexport function canTransition(from: ImportStatus, to: ImportStatus): boolean {\n return VALID_TRANSITIONS[from].includes(to);\n}\n","import type { BatchStatus } from './BatchStatus.js';\nimport type { ProcessedRecord } from './Record.js';\n\nexport interface Batch {\n readonly id: string;\n readonly index: number;\n readonly status: BatchStatus;\n readonly records: readonly ProcessedRecord[];\n readonly processedCount: number;\n readonly failedCount: number;\n}\n\nexport function createBatch(id: string, index: number, records: readonly ProcessedRecord[]): Batch {\n return {\n id,\n index,\n status: 'PENDING',\n records,\n processedCount: 0,\n failedCount: 0,\n };\n}\n\nexport function updateBatch(batch: Batch, updates: Partial<Pick<Batch, 'status' | 'records' | 'processedCount' | 'failedCount'>>): Batch {\n return { ...batch, ...updates };\n}\n","import type { ValidationError } from './ValidationResult.js';\n\nexport type RecordStatus = 'valid' | 'invalid' | 'processed' | 'failed' | 'pending';\n\nexport interface RawRecord {\n readonly [key: string]: unknown;\n}\n\nexport interface ProcessedRecord {\n readonly index: number;\n readonly raw: RawRecord;\n readonly parsed: RawRecord;\n readonly status: RecordStatus;\n readonly errors: readonly ValidationError[];\n readonly processingError?: string;\n}\n\nexport function createPendingRecord(index: number, raw: RawRecord): ProcessedRecord {\n return {\n index,\n raw,\n parsed: raw,\n status: 'pending',\n errors: [],\n };\n}\n\nexport function markRecordValid(record: ProcessedRecord, parsed: RawRecord): ProcessedRecord {\n return { ...record, parsed, status: 'valid', errors: [] };\n}\n\nexport function markRecordInvalid(record: ProcessedRecord, errors: readonly ValidationError[]): ProcessedRecord {\n return { ...record, status: 'invalid', errors };\n}\n\nexport function markRecordProcessed(record: ProcessedRecord): ProcessedRecord {\n return { ...record, status: 'processed' };\n}\n\nexport function markRecordFailed(record: ProcessedRecord, error: string): ProcessedRecord {\n return { ...record, status: 'failed', processingError: error };\n}\n","export type ValidationErrorCode =\n | 'REQUIRED'\n | 'TYPE_MISMATCH'\n | 'PATTERN_MISMATCH'\n | 'CUSTOM_VALIDATION'\n | 'UNKNOWN_FIELD';\n\nexport interface ValidationError {\n readonly field: string;\n readonly message: string;\n readonly code: ValidationErrorCode;\n readonly value?: unknown;\n}\n\nexport interface ValidationResult {\n readonly isValid: boolean;\n readonly errors: readonly ValidationError[];\n}\n\nexport function validResult(): ValidationResult {\n return { isValid: true, errors: [] };\n}\n\nexport function invalidResult(errors: readonly ValidationError[]): ValidationResult {\n return { isValid: false, errors };\n}\n","import type { SchemaDefinition } from '../model/Schema.js';\nimport type { RawRecord } from '../model/Record.js';\nimport type { ValidationResult, ValidationError } from '../model/ValidationResult.js';\nimport type { FieldDefinition } from '../model/FieldDefinition.js';\nimport { validResult, invalidResult } from '../model/ValidationResult.js';\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nexport class SchemaValidator {\n constructor(private readonly schema: SchemaDefinition) {}\n\n validate(record: RawRecord): ValidationResult {\n const errors: ValidationError[] = [];\n\n for (const field of this.schema.fields) {\n const value = record[field.name];\n const fieldErrors = this.validateField(field, value);\n errors.push(...fieldErrors);\n }\n\n if (this.schema.strict) {\n const definedFields = new Set(this.schema.fields.map((f) => f.name));\n for (const key of Object.keys(record)) {\n if (!definedFields.has(key)) {\n errors.push({\n field: key,\n message: `Unknown field '${key}' is not allowed in strict mode`,\n code: 'UNKNOWN_FIELD',\n value: record[key],\n });\n }\n }\n }\n\n return errors.length === 0 ? validResult() : invalidResult(errors);\n }\n\n applyTransforms(record: RawRecord): RawRecord {\n const transformed: Record<string, unknown> = { ...record };\n\n for (const field of this.schema.fields) {\n if (field.transform && transformed[field.name] !== undefined) {\n transformed[field.name] = field.transform(transformed[field.name]);\n }\n if (transformed[field.name] === undefined && field.defaultValue !== undefined) {\n transformed[field.name] = field.defaultValue;\n }\n }\n\n return transformed as RawRecord;\n }\n\n private validateField(field: FieldDefinition, value: unknown): ValidationError[] {\n const errors: ValidationError[] = [];\n\n if (this.isEmpty(value)) {\n if (field.required) {\n errors.push({\n field: field.name,\n message: `Field '${field.name}' is required`,\n code: 'REQUIRED',\n value,\n });\n }\n return errors;\n }\n\n if (field.type !== 'custom') {\n const typeError = this.validateType(field, value);\n if (typeError) {\n errors.push(typeError);\n return errors;\n }\n }\n\n if (field.pattern) {\n const stringValue = String(value);\n if (!field.pattern.test(stringValue)) {\n errors.push({\n field: field.name,\n message: `Field '${field.name}' does not match pattern ${String(field.pattern)}`,\n code: 'PATTERN_MISMATCH',\n value,\n });\n }\n }\n\n if (field.customValidator) {\n const result = field.customValidator(value);\n if (!result.valid) {\n errors.push({\n field: field.name,\n message: result.message ?? `Custom validation failed for field '${field.name}'`,\n code: 'CUSTOM_VALIDATION',\n value,\n });\n }\n }\n\n return errors;\n }\n\n private validateType(field: FieldDefinition, value: unknown): ValidationError | null {\n const stringValue = String(value);\n\n switch (field.type) {\n case 'number': {\n const num = Number(stringValue);\n if (isNaN(num)) {\n return {\n field: field.name,\n message: `Field '${field.name}' must be a number`,\n code: 'TYPE_MISMATCH',\n value,\n };\n }\n return null;\n }\n case 'boolean': {\n const lower = stringValue.toLowerCase();\n if (!['true', 'false', '1', '0', 'yes', 'no'].includes(lower)) {\n return {\n field: field.name,\n message: `Field '${field.name}' must be a boolean`,\n code: 'TYPE_MISMATCH',\n value,\n };\n }\n return null;\n }\n case 'date': {\n const date = new Date(stringValue);\n if (isNaN(date.getTime())) {\n return {\n field: field.name,\n message: `Field '${field.name}' must be a valid date`,\n code: 'TYPE_MISMATCH',\n value,\n };\n }\n return null;\n }\n case 'email': {\n if (!EMAIL_PATTERN.test(stringValue)) {\n return {\n field: field.name,\n message: `Field '${field.name}' must be a valid email`,\n code: 'TYPE_MISMATCH',\n value,\n };\n }\n return null;\n }\n case 'string':\n return null;\n default:\n return null;\n }\n }\n\n private isEmpty(value: unknown): boolean {\n return value === undefined || value === null || value === '';\n }\n}\n","import type { EventType, EventPayload, DomainEvent } from '../domain/events/DomainEvents.js';\n\ntype EventHandler<T extends EventType> = (event: EventPayload<T>) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyHandler = (event: any) => void;\n\nexport class EventBus {\n private readonly handlers = new Map<string, Set<AnyHandler>>();\n\n on<T extends EventType>(type: T, handler: EventHandler<T>): void {\n const existing = this.handlers.get(type) ?? new Set<AnyHandler>();\n existing.add(handler as AnyHandler);\n this.handlers.set(type, existing);\n }\n\n off<T extends EventType>(type: T, handler: EventHandler<T>): void {\n const existing = this.handlers.get(type);\n if (existing) {\n existing.delete(handler as AnyHandler);\n }\n }\n\n emit(event: DomainEvent): void {\n const handlers = this.handlers.get(event.type);\n if (handlers) {\n for (const handler of handlers) {\n handler(event);\n }\n }\n }\n}\n","import type { StateStore, BatchState } from '../../domain/ports/StateStore.js';\nimport type { ImportJobState, ImportProgress } from '../../domain/model/ImportJob.js';\nimport type { ProcessedRecord } from '../../domain/model/Record.js';\n\nexport class InMemoryStateStore implements StateStore {\n private jobs = new Map<string, ImportJobState>();\n private records = new Map<string, ProcessedRecord[]>();\n\n saveJobState(job: ImportJobState): Promise<void> {\n this.jobs.set(job.id, job);\n return Promise.resolve();\n }\n\n getJobState(jobId: string): Promise<ImportJobState | null> {\n return Promise.resolve(this.jobs.get(jobId) ?? null);\n }\n\n updateBatchState(jobId: string, batchId: string, state: BatchState): Promise<void> {\n const job = this.jobs.get(jobId);\n if (!job) return Promise.resolve();\n\n const batches = job.batches.map((b) =>\n b.id === batchId\n ? { ...b, status: state.status, processedCount: state.processedCount, failedCount: state.failedCount }\n : b,\n );\n\n this.jobs.set(jobId, { ...job, batches });\n return Promise.resolve();\n }\n\n saveProcessedRecord(jobId: string, _batchId: string, record: ProcessedRecord): Promise<void> {\n const key = jobId;\n const existing = this.records.get(key) ?? [];\n const index = existing.findIndex((r) => r.index === record.index);\n if (index >= 0) {\n existing[index] = record;\n } else {\n existing.push(record);\n }\n this.records.set(key, existing);\n return Promise.resolve();\n }\n\n getFailedRecords(jobId: string): Promise<readonly ProcessedRecord[]> {\n const all = this.records.get(jobId) ?? [];\n return Promise.resolve(all.filter((r) => r.status === 'failed' || r.status === 'invalid'));\n }\n\n getPendingRecords(jobId: string): Promise<readonly ProcessedRecord[]> {\n const all = this.records.get(jobId) ?? [];\n return Promise.resolve(all.filter((r) => r.status === 'pending' || r.status === 'valid'));\n }\n\n getProcessedRecords(jobId: string): Promise<readonly ProcessedRecord[]> {\n const all = this.records.get(jobId) ?? [];\n return Promise.resolve(all.filter((r) => r.status === 'processed'));\n }\n\n getProgress(jobId: string): Promise<ImportProgress> {\n const job = this.jobs.get(jobId);\n const all = this.records.get(jobId) ?? [];\n const processed = all.filter((r) => r.status === 'processed').length;\n const failed = all.filter((r) => r.status === 'failed' || r.status === 'invalid').length;\n const total = job?.totalRecords ?? all.length;\n const pending = total - processed - failed;\n const elapsed = job?.startedAt ? Date.now() - job.startedAt : 0;\n\n const currentBatch = job?.batches.filter((b) => b.status === 'COMPLETED').length ?? 0;\n const totalBatches = job?.batches.length ?? 0;\n\n return Promise.resolve({\n totalRecords: total,\n processedRecords: processed,\n failedRecords: failed,\n pendingRecords: pending,\n percentage: total > 0 ? Math.round((processed / total) * 100) : 0,\n currentBatch,\n totalBatches,\n elapsedMs: elapsed,\n });\n }\n}\n","import type { SchemaDefinition } from './domain/model/Schema.js';\nimport type { ImportJobState, ImportProgress, ImportSummary, PreviewResult } from './domain/model/ImportJob.js';\nimport type { ProcessedRecord } from './domain/model/Record.js';\nimport type { Batch } from './domain/model/Batch.js';\nimport type { ImportStatus } from './domain/model/ImportStatus.js';\nimport type { SourceParser } from './domain/ports/SourceParser.js';\nimport type { DataSource } from './domain/ports/DataSource.js';\nimport type { StateStore } from './domain/ports/StateStore.js';\nimport type { RecordProcessorFn, ProcessingContext } from './domain/ports/RecordProcessor.js';\nimport type { EventType, EventPayload } from './domain/events/DomainEvents.js';\nimport { canTransition } from './domain/model/ImportStatus.js';\nimport { createBatch } from './domain/model/Batch.js';\nimport { createPendingRecord, markRecordValid, markRecordInvalid, markRecordProcessed, markRecordFailed } from './domain/model/Record.js';\nimport { SchemaValidator } from './domain/services/SchemaValidator.js';\nimport { EventBus } from './application/EventBus.js';\nimport { InMemoryStateStore } from './infrastructure/state/InMemoryStateStore.js';\n\nexport interface BulkImportConfig {\n readonly schema: SchemaDefinition;\n readonly batchSize?: number;\n readonly maxConcurrentBatches?: number;\n readonly continueOnError?: boolean;\n readonly stateStore?: StateStore;\n}\n\nexport class BulkImport {\n private readonly config: Required<Pick<BulkImportConfig, 'batchSize' | 'continueOnError'>> & BulkImportConfig;\n private readonly validator: SchemaValidator;\n private readonly eventBus: EventBus;\n private readonly stateStore: StateStore;\n\n private source: DataSource | null = null;\n private parser: SourceParser | null = null;\n\n private jobId: string;\n private status: ImportStatus = 'CREATED';\n private batches: Batch[] = [];\n private allRecords: ProcessedRecord[] = [];\n private totalRecords = 0;\n private startedAt?: number;\n\n private abortController: AbortController | null = null;\n private pausePromise: { resolve: () => void; promise: Promise<void> } | null = null;\n\n constructor(config: BulkImportConfig) {\n this.config = {\n ...config,\n batchSize: config.batchSize ?? 100,\n continueOnError: config.continueOnError ?? false,\n };\n this.validator = new SchemaValidator(config.schema);\n this.eventBus = new EventBus();\n this.stateStore = config.stateStore ?? new InMemoryStateStore();\n this.jobId = crypto.randomUUID();\n }\n\n from(source: DataSource, parser: SourceParser): this {\n this.source = source;\n this.parser = parser;\n return this;\n }\n\n on<T extends EventType>(type: T, handler: (event: EventPayload<T>) => void): this {\n this.eventBus.on(type, handler);\n return this;\n }\n\n async preview(maxRecords = 10): Promise<PreviewResult> {\n this.assertSourceConfigured();\n this.transitionTo('PREVIEWING');\n\n const records = await this.parseRecords(maxRecords);\n const validRecords: ProcessedRecord[] = [];\n const invalidRecords: ProcessedRecord[] = [];\n const columns = new Set<string>();\n\n for (const record of records) {\n for (const key of Object.keys(record.raw)) {\n columns.add(key);\n }\n const transformed = this.validator.applyTransforms(record.raw);\n const result = this.validator.validate(transformed);\n\n if (result.isValid) {\n validRecords.push(markRecordValid(record, transformed));\n } else {\n invalidRecords.push(markRecordInvalid(record, result.errors));\n }\n }\n\n this.transitionTo('PREVIEWED');\n\n return {\n validRecords,\n invalidRecords,\n totalSampled: records.length,\n columns: [...columns],\n };\n }\n\n async start(processor: RecordProcessorFn): Promise<void> {\n this.assertSourceConfigured();\n\n this.assertCanStart();\n\n this.transitionTo('PROCESSING');\n this.abortController = new AbortController();\n this.startedAt = Date.now();\n\n const allRawRecords = await this.parseRecords();\n this.totalRecords = allRawRecords.length;\n\n this.batches = this.splitIntoBatches(allRawRecords);\n\n await this.saveState();\n\n this.eventBus.emit({\n type: 'import:started',\n jobId: this.jobId,\n totalRecords: this.totalRecords,\n totalBatches: this.batches.length,\n timestamp: Date.now(),\n });\n\n try {\n for (let i = 0; i < this.batches.length; i++) {\n if (this.abortController.signal.aborted) break;\n await this.checkPause();\n\n const batch = this.batches[i];\n if (!batch) break;\n await this.processBatch(batch, processor);\n }\n\n if (!this.abortController.signal.aborted && this.status !== 'ABORTED') {\n this.transitionTo('COMPLETED');\n const summary = this.buildSummary();\n\n this.eventBus.emit({\n type: 'import:completed',\n jobId: this.jobId,\n summary,\n timestamp: Date.now(),\n });\n }\n } catch (error) {\n if (this.status !== 'ABORTED') {\n this.transitionTo('FAILED');\n this.eventBus.emit({\n type: 'import:failed',\n jobId: this.jobId,\n error: error instanceof Error ? error.message : String(error),\n timestamp: Date.now(),\n });\n }\n }\n\n await this.saveState();\n }\n\n async pause(): Promise<void> {\n if (this.status !== 'PROCESSING') {\n throw new Error(`Cannot pause import from status '${this.status}'`);\n }\n\n this.transitionTo('PAUSED');\n this.pausePromise = this.createPausePromise();\n\n const progress = this.buildProgress();\n this.eventBus.emit({\n type: 'import:paused',\n jobId: this.jobId,\n progress,\n timestamp: Date.now(),\n });\n\n await this.saveState();\n }\n\n resume(): void {\n if (this.status === 'ABORTED') {\n throw new Error('Cannot resume an aborted import');\n }\n if (this.status !== 'PAUSED') {\n throw new Error(`Cannot resume import from status '${this.status}'`);\n }\n\n this.transitionTo('PROCESSING');\n if (this.pausePromise) {\n this.pausePromise.resolve();\n this.pausePromise = null;\n }\n }\n\n async abort(): Promise<void> {\n if (this.status !== 'PROCESSING' && this.status !== 'PAUSED') {\n throw new Error(`Cannot abort import from status '${this.status}'`);\n }\n\n this.transitionTo('ABORTED');\n this.abortController?.abort();\n\n if (this.pausePromise) {\n this.pausePromise.resolve();\n this.pausePromise = null;\n }\n\n const progress = this.buildProgress();\n this.eventBus.emit({\n type: 'import:aborted',\n jobId: this.jobId,\n progress,\n timestamp: Date.now(),\n });\n\n await this.saveState();\n }\n\n getStatus(): { state: ImportStatus; progress: ImportProgress; batches: readonly Batch[] } {\n return {\n state: this.status,\n progress: this.buildProgress(),\n batches: this.batches,\n };\n }\n\n getFailedRecords(): readonly ProcessedRecord[] {\n return this.allRecords.filter((r) => r.status === 'failed' || r.status === 'invalid');\n }\n\n getPendingRecords(): readonly ProcessedRecord[] {\n return this.allRecords.filter((r) => r.status === 'pending' || r.status === 'valid');\n }\n\n getJobId(): string {\n return this.jobId;\n }\n\n // --- Private methods ---\n\n private async processBatch(batch: Batch, processor: RecordProcessorFn): Promise<void> {\n const batchIndex = this.batches.indexOf(batch);\n\n this.updateBatchStatus(batch.id, 'PROCESSING');\n\n this.eventBus.emit({\n type: 'batch:started',\n jobId: this.jobId,\n batchId: batch.id,\n batchIndex,\n recordCount: batch.records.length,\n timestamp: Date.now(),\n });\n\n let processedCount = 0;\n let failedCount = 0;\n\n for (const record of batch.records) {\n if (this.abortController?.signal.aborted) break;\n await this.checkPause();\n\n const transformed = this.validator.applyTransforms(record.raw);\n const validation = this.validator.validate(transformed);\n\n if (!validation.isValid) {\n const invalidRecord = markRecordInvalid(record, validation.errors);\n this.updateRecord(record.index, invalidRecord);\n failedCount++;\n\n this.eventBus.emit({\n type: 'record:failed',\n jobId: this.jobId,\n batchId: batch.id,\n recordIndex: record.index,\n error: validation.errors.map((e) => e.message).join('; '),\n record: invalidRecord,\n timestamp: Date.now(),\n });\n\n if (!this.config.continueOnError) {\n throw new Error(`Validation failed for record ${String(record.index)}`);\n }\n continue;\n }\n\n const validRecord = markRecordValid(record, transformed);\n\n try {\n const context: ProcessingContext = {\n jobId: this.jobId,\n batchId: batch.id,\n batchIndex,\n recordIndex: record.index,\n totalRecords: this.totalRecords,\n signal: this.abortController?.signal ?? new AbortController().signal,\n };\n\n await processor(validRecord.parsed, context);\n\n const processed = markRecordProcessed(validRecord);\n this.updateRecord(record.index, processed);\n processedCount++;\n\n this.eventBus.emit({\n type: 'record:processed',\n jobId: this.jobId,\n batchId: batch.id,\n recordIndex: record.index,\n timestamp: Date.now(),\n });\n } catch (error) {\n const failedRecord = markRecordFailed(validRecord, error instanceof Error ? error.message : String(error));\n this.updateRecord(record.index, failedRecord);\n failedCount++;\n\n this.eventBus.emit({\n type: 'record:failed',\n jobId: this.jobId,\n batchId: batch.id,\n recordIndex: record.index,\n error: error instanceof Error ? error.message : String(error),\n record: failedRecord,\n timestamp: Date.now(),\n });\n\n if (!this.config.continueOnError) {\n throw error;\n }\n }\n }\n\n this.updateBatchStatus(batch.id, 'COMPLETED', processedCount, failedCount);\n\n this.eventBus.emit({\n type: 'batch:completed',\n jobId: this.jobId,\n batchId: batch.id,\n batchIndex,\n processedCount,\n failedCount,\n totalCount: batch.records.length,\n timestamp: Date.now(),\n });\n\n this.emitProgress();\n }\n\n private async parseRecords(maxRecords?: number): Promise<ProcessedRecord[]> {\n const source = this.source;\n const parser = this.parser;\n if (!source || !parser) {\n throw new Error('Source and parser must be configured. Call .from(source, parser) first.');\n }\n\n const records: ProcessedRecord[] = [];\n let index = 0;\n\n for await (const chunk of source.read()) {\n for await (const raw of parser.parse(chunk)) {\n if (maxRecords !== undefined && records.length >= maxRecords) {\n return records;\n }\n records.push(createPendingRecord(index, raw));\n index++;\n }\n }\n\n return records;\n }\n\n private splitIntoBatches(records: ProcessedRecord[]): Batch[] {\n const batches: Batch[] = [];\n const { batchSize } = this.config;\n\n for (let i = 0; i < records.length; i += batchSize) {\n const batchRecords = records.slice(i, i + batchSize);\n const batchId = crypto.randomUUID();\n batches.push(createBatch(batchId, batches.length, batchRecords));\n }\n\n this.allRecords = [...records];\n return batches;\n }\n\n private updateRecord(index: number, record: ProcessedRecord): void {\n const pos = this.allRecords.findIndex((r) => r.index === index);\n if (pos >= 0) {\n this.allRecords[pos] = record;\n }\n }\n\n private updateBatchStatus(batchId: string, status: Batch['status'], processedCount?: number, failedCount?: number): void {\n this.batches = this.batches.map((b) =>\n b.id === batchId\n ? {\n ...b,\n status,\n processedCount: processedCount ?? b.processedCount,\n failedCount: failedCount ?? b.failedCount,\n }\n : b,\n );\n }\n\n private transitionTo(newStatus: ImportStatus): void {\n if (!canTransition(this.status, newStatus)) {\n throw new Error(`Invalid state transition: ${this.status} → ${newStatus}`);\n }\n this.status = newStatus;\n }\n\n private buildProgress(): ImportProgress {\n const processed = this.allRecords.filter((r) => r.status === 'processed').length;\n const failed = this.allRecords.filter((r) => r.status === 'failed' || r.status === 'invalid').length;\n const pending = this.totalRecords - processed - failed;\n const completedBatches = this.batches.filter((b) => b.status === 'COMPLETED').length;\n const elapsed = this.startedAt ? Date.now() - this.startedAt : 0;\n\n return {\n totalRecords: this.totalRecords,\n processedRecords: processed,\n failedRecords: failed,\n pendingRecords: pending,\n percentage: this.totalRecords > 0 ? Math.round((processed / this.totalRecords) * 100) : 0,\n currentBatch: completedBatches,\n totalBatches: this.batches.length,\n elapsedMs: elapsed,\n };\n }\n\n private buildSummary(): ImportSummary {\n const processed = this.allRecords.filter((r) => r.status === 'processed').length;\n const failed = this.allRecords.filter((r) => r.status === 'failed' || r.status === 'invalid').length;\n const skipped = this.totalRecords - processed - failed;\n const elapsed = this.startedAt ? Date.now() - this.startedAt : 0;\n\n return { total: this.totalRecords, processed, failed, skipped, elapsedMs: elapsed };\n }\n\n private emitProgress(): void {\n this.eventBus.emit({\n type: 'import:progress',\n jobId: this.jobId,\n progress: this.buildProgress(),\n timestamp: Date.now(),\n });\n }\n\n private async saveState(): Promise<void> {\n const state: ImportJobState = {\n id: this.jobId,\n config: {\n schema: this.config.schema,\n batchSize: this.config.batchSize,\n continueOnError: this.config.continueOnError,\n },\n status: this.status,\n batches: this.batches,\n totalRecords: this.totalRecords,\n startedAt: this.startedAt,\n };\n await this.stateStore.saveJobState(state);\n }\n\n private async checkPause(): Promise<void> {\n if (this.pausePromise) {\n await this.pausePromise.promise;\n }\n }\n\n private createPausePromise(): { resolve: () => void; promise: Promise<void> } {\n let resolveRef!: () => void;\n const promise = new Promise<void>((resolve) => {\n resolveRef = resolve;\n });\n return { resolve: resolveRef, promise };\n }\n\n private assertSourceConfigured(): void {\n if (!this.source || !this.parser) {\n throw new Error('Source and parser must be configured. Call .from(source, parser) first.');\n }\n }\n\n private assertCanStart(): void {\n if (this.status !== 'PREVIEWED' && this.status !== 'CREATED') {\n throw new Error(`Cannot start import from status '${this.status}'`);\n }\n }\n}\n","export const BatchStatus = {\n PENDING: 'PENDING',\n PROCESSING: 'PROCESSING',\n PAUSED: 'PAUSED',\n COMPLETED: 'COMPLETED',\n FAILED: 'FAILED',\n} as const;\n\nexport type BatchStatus = (typeof BatchStatus)[keyof typeof BatchStatus];\n","import Papa from 'papaparse';\nimport type { SourceParser, ParserOptions } from '../../domain/ports/SourceParser.js';\nimport type { RawRecord } from '../../domain/model/Record.js';\n\nexport class CsvParser implements SourceParser {\n private readonly options: ParserOptions;\n\n constructor(options?: Partial<ParserOptions>) {\n this.options = {\n delimiter: options?.delimiter,\n encoding: options?.encoding ?? 'utf-8',\n hasHeader: options?.hasHeader ?? true,\n };\n }\n\n *parse(data: string | Buffer): Iterable<RawRecord> {\n const content = typeof data === 'string' ? data : data.toString('utf-8');\n\n const result = Papa.parse(content, {\n header: this.options.hasHeader,\n delimiter: this.options.delimiter || undefined,\n skipEmptyLines: true,\n dynamicTyping: false,\n });\n\n for (const row of result.data as Record<string, unknown>[]) {\n if (this.isEmptyRow(row)) continue;\n yield row as RawRecord;\n }\n }\n\n detect(sample: string | Buffer): ParserOptions {\n const content = typeof sample === 'string' ? sample : sample.toString('utf-8');\n const firstLines = content.split('\\n').slice(0, 5).join('\\n');\n\n const delimiters = [',', ';', '\\t', '|'];\n let bestDelimiter = ',';\n let maxColumns = 0;\n\n for (const delimiter of delimiters) {\n const result = Papa.parse(firstLines, { delimiter, header: false });\n const firstRow = result.data[0] as string[] | undefined;\n if (firstRow && firstRow.length > maxColumns) {\n maxColumns = firstRow.length;\n bestDelimiter = delimiter;\n }\n }\n\n return {\n delimiter: bestDelimiter,\n encoding: 'utf-8',\n hasHeader: true,\n };\n }\n\n private isEmptyRow(row: Record<string, unknown>): boolean {\n return Object.values(row).every((v) => v === null || v === undefined || v === '');\n }\n}\n","import type { DataSource, SourceMetadata } from '../../domain/ports/DataSource.js';\n\nexport class BufferSource implements DataSource {\n private readonly content: string;\n private readonly meta: SourceMetadata;\n\n constructor(data: string | Buffer, metadata?: Partial<SourceMetadata>) {\n this.content = typeof data === 'string' ? data : data.toString('utf-8');\n this.meta = {\n fileName: metadata?.fileName ?? 'buffer-input',\n fileSize: this.content.length,\n mimeType: metadata?.mimeType ?? 'text/plain',\n };\n }\n\n async *read(): AsyncIterable<string> {\n yield await Promise.resolve(this.content);\n }\n\n sample(maxBytes?: number): Promise<string> {\n if (maxBytes && maxBytes < this.content.length) {\n return Promise.resolve(this.content.slice(0, maxBytes));\n }\n return Promise.resolve(this.content);\n }\n\n metadata(): SourceMetadata {\n return this.meta;\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bulkimport/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Backend-agnostic bulk data import library with schema validation, batch processing, and state management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.mjs",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "eslint src/ tests/",
|
|
27
|
+
"lint:fix": "eslint src/ tests/ --fix",
|
|
28
|
+
"format": "prettier --write .",
|
|
29
|
+
"format:check": "prettier --check .",
|
|
30
|
+
"prepublishOnly": "npm run typecheck && npm run lint && npm run test && npm run build"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"bulk",
|
|
34
|
+
"import",
|
|
35
|
+
"csv",
|
|
36
|
+
"json",
|
|
37
|
+
"xml",
|
|
38
|
+
"batch",
|
|
39
|
+
"validation",
|
|
40
|
+
"schema",
|
|
41
|
+
"data-import",
|
|
42
|
+
"etl",
|
|
43
|
+
"stream",
|
|
44
|
+
"parser"
|
|
45
|
+
],
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"author": "Víctor Garcia <vgpastor@ingenierosweb.co>",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/vgpastor/bulkimport"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=16.7.0"
|
|
54
|
+
},
|
|
55
|
+
"sideEffects": false,
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"papaparse": "^5.5.2"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^22.13.4",
|
|
61
|
+
"@types/papaparse": "^5.3.15",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.24.1",
|
|
63
|
+
"@typescript-eslint/parser": "^8.24.1",
|
|
64
|
+
"@vitest/coverage-v8": "^3.0.6",
|
|
65
|
+
"eslint": "^9.20.0",
|
|
66
|
+
"eslint-config-prettier": "^10.0.1",
|
|
67
|
+
"prettier": "^3.5.2",
|
|
68
|
+
"tsup": "^8.3.6",
|
|
69
|
+
"typescript": "^5.7.3",
|
|
70
|
+
"vitest": "^3.0.6"
|
|
71
|
+
}
|
|
72
|
+
}
|