convert-buddy-js 0.10.2 → 0.10.4
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/dist/browser.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +54 -3
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/tests/edge-cases/ndjson-edge-cases.test.js +33 -0
- package/dist/tests/edge-cases/ndjson-edge-cases.test.js.map +1 -1
- package/dist/tests/streaming/large-file-stream.test.js +205 -0
- package/dist/tests/streaming/large-file-stream.test.js.map +1 -0
- package/dist/tests/streaming/stats-object.test.js +216 -0
- package/dist/tests/streaming/stats-object.test.js.map +1 -0
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConvertBuddyOptions, ConvertOptions, Format } from './index.js';
|
|
2
|
-
export { Coerce, ConvertBuddy, ConvertBuddyTransformStream, CsvConfig, CsvDetection, DetectInput, DetectOptions, FieldMap, JsonDetection, NdjsonDetection, ProgressCallback, Stats, StructureDetection, TransformConfig, TransformMode, XmlConfig, XmlDetection, autoDetectConfig, convertAny, convertAnyToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported } from './index.js';
|
|
2
|
+
export { Coerce, ConvertBuddy, ConvertBuddyTransformStream, Converter, CsvConfig, CsvDetection, DetectInput, DetectOptions, FieldMap, JsonDetection, NdjsonDetection, ProgressCallback, Stats, StructureDetection, TransformConfig, TransformMode, XmlConfig, XmlDetection, autoDetectConfig, convertAny, convertAnyToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported } from './index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Stream a File/Blob conversion in a Web Worker with progress updates.
|
package/dist/index.d.ts
CHANGED
|
@@ -262,5 +262,10 @@ declare function getThreadingInfo(): {
|
|
|
262
262
|
currentThreads: number;
|
|
263
263
|
approach: string;
|
|
264
264
|
};
|
|
265
|
+
/**
|
|
266
|
+
* Backward compatibility alias for ConvertBuddy
|
|
267
|
+
* @deprecated Use ConvertBuddy instead
|
|
268
|
+
*/
|
|
269
|
+
declare const Converter: typeof ConvertBuddy;
|
|
265
270
|
|
|
266
|
-
export { type Coerce, ConvertBuddy, type ConvertBuddyOptions, ConvertBuddyTransformStream, type ConvertOptions, type CsvConfig, type CsvDetection, type DetectInput, type DetectOptions, type FieldMap, type Format, type JsonDetection, type NdjsonDetection, type ProgressCallback, type Stats, type StructureDetection, type TransformConfig, type TransformMode, type XmlConfig, type XmlDetection, autoDetectConfig, convert, convertAny, convertAnyToString, convertToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported };
|
|
271
|
+
export { type Coerce, ConvertBuddy, type ConvertBuddyOptions, ConvertBuddyTransformStream, type ConvertOptions, Converter, type CsvConfig, type CsvDetection, type DetectInput, type DetectOptions, type FieldMap, type Format, type JsonDetection, type NdjsonDetection, type ProgressCallback, type Stats, type StructureDetection, type TransformConfig, type TransformMode, type XmlConfig, type XmlDetection, autoDetectConfig, convert, convertAny, convertAnyToString, convertToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported };
|
package/dist/index.js
CHANGED
|
@@ -540,13 +540,14 @@ class ConvertBuddy {
|
|
|
540
540
|
const chunkTargetBytes = opts.chunkTargetBytes || 512 * 1024;
|
|
541
541
|
let converter;
|
|
542
542
|
if (inputFormat && opts.outputFormat) {
|
|
543
|
-
const
|
|
544
|
-
converter =
|
|
543
|
+
const Converter2 = wasmModule.Converter;
|
|
544
|
+
converter = Converter2.withConfig(
|
|
545
545
|
debug,
|
|
546
546
|
inputFormat,
|
|
547
547
|
opts.outputFormat,
|
|
548
548
|
chunkTargetBytes,
|
|
549
549
|
profile,
|
|
550
|
+
// Enable stats tracking when profile is enabled
|
|
550
551
|
csvConfig || null,
|
|
551
552
|
opts.xmlConfig || null,
|
|
552
553
|
opts.transform || null
|
|
@@ -592,7 +593,55 @@ class ConvertBuddy {
|
|
|
592
593
|
return output;
|
|
593
594
|
}
|
|
594
595
|
stats() {
|
|
595
|
-
|
|
596
|
+
if (!this.converter || typeof this.converter.getStats !== "function") {
|
|
597
|
+
return {
|
|
598
|
+
bytesIn: 0,
|
|
599
|
+
bytesOut: 0,
|
|
600
|
+
chunksIn: 0,
|
|
601
|
+
recordsProcessed: 0,
|
|
602
|
+
parseTimeMs: 0,
|
|
603
|
+
transformTimeMs: 0,
|
|
604
|
+
writeTimeMs: 0,
|
|
605
|
+
maxBufferSize: 0,
|
|
606
|
+
currentPartialSize: 0,
|
|
607
|
+
throughputMbPerSec: 0
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const wasmStats = this.converter.getStats();
|
|
612
|
+
const stats = {
|
|
613
|
+
bytesIn: wasmStats.bytes_in,
|
|
614
|
+
bytesOut: wasmStats.bytes_out,
|
|
615
|
+
chunksIn: wasmStats.chunks_in,
|
|
616
|
+
recordsProcessed: wasmStats.records_processed,
|
|
617
|
+
parseTimeMs: wasmStats.parse_time_ms,
|
|
618
|
+
transformTimeMs: wasmStats.transform_time_ms,
|
|
619
|
+
writeTimeMs: wasmStats.write_time_ms,
|
|
620
|
+
maxBufferSize: wasmStats.max_buffer_size,
|
|
621
|
+
currentPartialSize: wasmStats.current_partial_size,
|
|
622
|
+
throughputMbPerSec: wasmStats.throughput_mb_per_sec
|
|
623
|
+
};
|
|
624
|
+
if (!this.profile && stats.bytesIn === 0 && stats.chunksIn === 0) {
|
|
625
|
+
console.warn(
|
|
626
|
+
"[convert-buddy-js] WARNING: stats() called but profiling is disabled. Enable profiling by setting { profile: true } in ConvertBuddy.create() options to get accurate statistics."
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
return stats;
|
|
630
|
+
} catch (e) {
|
|
631
|
+
if (this.debug) console.error("[convert-buddy-js] Error getting stats:", e);
|
|
632
|
+
return {
|
|
633
|
+
bytesIn: 0,
|
|
634
|
+
bytesOut: 0,
|
|
635
|
+
chunksIn: 0,
|
|
636
|
+
recordsProcessed: 0,
|
|
637
|
+
parseTimeMs: 0,
|
|
638
|
+
transformTimeMs: 0,
|
|
639
|
+
writeTimeMs: 0,
|
|
640
|
+
maxBufferSize: 0,
|
|
641
|
+
currentPartialSize: 0,
|
|
642
|
+
throughputMbPerSec: 0
|
|
643
|
+
};
|
|
644
|
+
}
|
|
596
645
|
}
|
|
597
646
|
abort() {
|
|
598
647
|
this.aborted = true;
|
|
@@ -867,9 +916,11 @@ function getThreadingInfo() {
|
|
|
867
916
|
approach: isNodejs ? "nodejs_enhanced_threading" : "browser_custom_threading"
|
|
868
917
|
};
|
|
869
918
|
}
|
|
919
|
+
const Converter = ConvertBuddy;
|
|
870
920
|
export {
|
|
871
921
|
ConvertBuddy,
|
|
872
922
|
ConvertBuddyTransformStream,
|
|
923
|
+
Converter,
|
|
873
924
|
autoDetectConfig,
|
|
874
925
|
convert,
|
|
875
926
|
convertAny,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Format = \"csv\" | \"ndjson\" | \"json\" | \"xml\";\r\nexport type DetectInput =\r\n | Uint8Array\r\n | ArrayBuffer\r\n | string\r\n | ReadableStream<Uint8Array>\r\n | AsyncIterable<Uint8Array>;\r\n\r\nexport type CsvDetection = {\r\n delimiter: string;\r\n fields: string[];\r\n};\r\n\r\nexport type XmlDetection = {\r\n elements: string[];\r\n recordElement?: string;\r\n};\r\n\r\nexport type JsonDetection = {\r\n fields: string[];\r\n};\r\n\r\nexport type NdjsonDetection = {\r\n fields: string[];\r\n};\r\n\r\nexport type StructureDetection = {\r\n format: Format;\r\n fields: string[];\r\n delimiter?: string; // For CSV\r\n recordElement?: string; // For XML\r\n};\r\n\r\nexport type DetectOptions = {\r\n maxBytes?: number;\r\n debug?: boolean;\r\n};\r\n\r\nexport type ProgressCallback = (stats: Stats) => void;\r\n\r\nexport type ConvertBuddyOptions = {\r\n debug?: boolean;\r\n profile?: boolean;\r\n inputFormat?: Format | \"auto\";\r\n outputFormat?: Format;\r\n chunkTargetBytes?: number;\r\n parallelism?: number; // Node only - number of worker threads\r\n maxMemoryMB?: number; // Memory limit for conversions (future use)\r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n transform?: TransformConfig;\r\n onProgress?: ProgressCallback;\r\n progressIntervalBytes?: number; // Trigger progress callback every N bytes (default: 1MB)\r\n};\r\n\r\nexport type ConvertOptions = {\r\n inputFormat?: Format | \"auto\";\r\n outputFormat: Format;\r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n transform?: TransformConfig;\r\n onProgress?: ProgressCallback;\r\n};\r\n\r\nexport type CsvConfig = {\r\n delimiter?: string;\r\n quote?: string;\r\n hasHeaders?: boolean;\r\n trimWhitespace?: boolean;\r\n};\r\n\r\nexport type XmlConfig = {\r\n recordElement?: string;\r\n trimText?: boolean;\r\n includeAttributes?: boolean;\r\n expandEntities?: boolean;\r\n};\r\n\r\nexport type TransformMode = \"replace\" | \"augment\";\r\n\r\nexport type Coerce =\r\n | { type: \"string\" }\r\n | { type: \"i64\" }\r\n | { type: \"f64\" }\r\n | { type: \"bool\" }\r\n | { type: \"timestamp_ms\"; format?: \"iso8601\" | \"unix_ms\" | \"unix_s\" };\r\n\r\nexport type FieldMap = {\r\n targetFieldName: string;\r\n originFieldName?: string;\r\n required?: boolean;\r\n defaultValue?: string | number | boolean | null;\r\n coerce?: Coerce;\r\n compute?: string;\r\n};\r\n\r\nexport type TransformConfig = {\r\n mode?: TransformMode;\r\n fields: FieldMap[];\r\n onMissingField?: \"error\" | \"null\" | \"drop\";\r\n onMissingRequired?: \"error\" | \"abort\";\r\n onCoerceError?: \"error\" | \"null\" | \"dropRecord\";\r\n};\r\n\r\nexport type Stats = {\r\n bytesIn: number;\r\n bytesOut: number;\r\n chunksIn: number;\r\n recordsProcessed: number;\r\n parseTimeMs: number;\r\n transformTimeMs: number;\r\n writeTimeMs: number;\r\n maxBufferSize: number;\r\n currentPartialSize: number;\r\n throughputMbPerSec: number;\r\n};\r\n\r\ntype WasmModule = {\r\n default?: unknown;\r\n init: (debugEnabled: boolean) => void;\r\n Converter: new (debug: boolean) => {\r\n push: (chunk: Uint8Array) => Uint8Array;\r\n finish: () => Uint8Array;\r\n getStats: () => Stats;\r\n };\r\n detectFormat?: (sample: Uint8Array) => string | null | undefined;\r\n detectCsvFields?: (sample: Uint8Array) => CsvDetection | null | undefined;\r\n detectXmlElements?: (sample: Uint8Array) => XmlDetection | null | undefined;\r\n detectJsonFields?: (sample: Uint8Array) => JsonDetection | null | undefined;\r\n detectNdjsonFields?: (sample: Uint8Array) => NdjsonDetection | null | undefined;\r\n detectStructure?: (sample: Uint8Array, formatHint?: string) => StructureDetection | null | undefined;\r\n getSimdEnabled?: () => boolean;\r\n __wbg_set_wasm?: (wasm: unknown) => void;\r\n};\r\n\r\nlet wasmModuleInstance: WasmModule | null = null;\r\nlet wasmModuleLoadPromise: Promise<WasmModule> | null = null;\r\nlet wasmThreadingSupported = false;\r\nlet threadPool: any = null; // Custom WASM thread pool (browser)\r\nlet nodejsThreadPool: any = null; // Node.js specific thread pool\r\nconst utf8Decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true });\r\n\r\nfunction decodeUtf8(bytes: Uint8Array): string {\r\n return utf8Decoder.decode(bytes);\r\n}\r\n\r\n// Detect SharedArrayBuffer support for WASM threading\r\nfunction detectWasmThreadingSupport(): boolean {\r\n if (typeof SharedArrayBuffer === 'undefined') {\r\n return false;\r\n }\r\n \r\n // Test if we can actually create a SharedArrayBuffer\r\n try {\r\n new SharedArrayBuffer(1);\r\n return true;\r\n } catch (e) {\r\n return false;\r\n }\r\n}\r\n\r\nasync function loadWasmModule(): Promise<WasmModule> {\r\n // Return cached instance if already loaded\r\n if (wasmModuleInstance) {\r\n return wasmModuleInstance;\r\n }\r\n\r\n // If load is in progress, wait for it\r\n if (wasmModuleLoadPromise) {\r\n return wasmModuleLoadPromise;\r\n }\r\n\r\n // Start loading\r\n wasmModuleLoadPromise = (async () => {\r\n const isNode =\r\n typeof process !== \"undefined\" &&\r\n !!(process as any).versions?.node;\r\n\r\n // Detect threading support\r\n wasmThreadingSupported = detectWasmThreadingSupport();\r\n \r\n if (isNode) {\r\n const nodeModule: any = await import(/* webpackIgnore: true */ \"node:module\");\r\n const createRequire = nodeModule.createRequire as any;\r\n const requireFn = createRequire ? createRequire(import.meta.url) : (module as any).createRequire?.(import.meta.url);\r\n const require = requireFn ?? (globalThis as any).require;\r\n const mod = require(\"../wasm-node.cjs\");\r\n return mod as WasmModule;\r\n }\r\n\r\n const mod = (await import(\"../wasm/web/convert_buddy.js\")) as unknown as WasmModule;\r\n \r\n // Initialize threading if supported\r\n if (wasmThreadingSupported && (mod as any).initThreadPool) {\r\n try {\r\n const numThreads = Math.min(navigator.hardwareConcurrency || 4, 8);\r\n await (mod as any).initThreadPool(numThreads);\r\n console.log(`[convert-buddy] WASM threading initialized with ${numThreads} threads`);\r\n } catch (e) {\r\n console.warn('[convert-buddy] WASM threading initialization failed, using single-thread:', e);\r\n wasmThreadingSupported = false;\r\n }\r\n }\r\n \r\n return mod;\r\n })();\r\n\r\n try {\r\n wasmModuleInstance = await wasmModuleLoadPromise;\r\n return wasmModuleInstance;\r\n } finally {\r\n wasmModuleLoadPromise = null;\r\n }\r\n}\r\n\r\nlet wasmInitialized = false;\r\nlet wasmInitPromise: Promise<void> | null = null;\r\n\r\nasync function initWasm(debug: boolean): Promise<void> {\r\n // If already initialized, return immediately\r\n if (wasmInitialized) {\r\n return;\r\n }\r\n\r\n // If initialization is in progress, wait for it\r\n if (wasmInitPromise) {\r\n return wasmInitPromise;\r\n }\r\n\r\n // Start initialization\r\n wasmInitPromise = (async () => {\r\n const wasmModule = await loadWasmModule();\r\n\r\n if (typeof wasmModule.default === \"function\") {\r\n await (wasmModule.default as () => Promise<void>)();\r\n }\r\n\r\n wasmModule.init(debug);\r\n \r\n // Note: Node.js enhanced threading is handled at the JavaScript level\r\n // No WASM thread pool initialization needed\r\n \r\n wasmInitialized = true;\r\n })();\r\n\r\n try {\r\n await wasmInitPromise;\r\n } finally {\r\n wasmInitPromise = null;\r\n }\r\n}\r\n\r\nexport class ConvertBuddy {\r\n private converter: any;\r\n private debug: boolean;\r\n private profile: boolean;\r\n private aborted: boolean = false;\r\n private paused: boolean = false;\r\n private onProgress?: ProgressCallback;\r\n private progressIntervalBytes: number;\r\n private lastProgressBytes: number = 0;\r\n private globalConfig: ConvertBuddyOptions;\r\n private initialized: boolean = false;\r\n public simd: boolean;\r\n\r\n /**\r\n * Create a new ConvertBuddy instance with global configuration.\r\n * This is useful when you want to set memory limits, debug mode, or other global settings.\r\n * \r\n * @example\r\n * const buddy = new ConvertBuddy({ maxMemoryMB: 512, debug: true });\r\n * const result = await buddy.convert(input, { outputFormat: \"json\" });\r\n */\r\n constructor(opts: ConvertBuddyOptions = {}) {\r\n // Initialize basic properties\r\n this.debug = !!opts.debug;\r\n this.profile = !!opts.profile;\r\n this.simd = false; // Will be set on first convert\r\n this.globalConfig = opts;\r\n this.progressIntervalBytes = opts.progressIntervalBytes || 1024 * 1024;\r\n this.onProgress = opts.onProgress;\r\n this.converter = null; // Will be initialized lazily\r\n this.initialized = false;\r\n }\r\n\r\n /**\r\n * Convert input (string, Buffer, File, URL, etc.) to the desired output format.\r\n * This is the main method for the new simplified API.\r\n * \r\n * @example\r\n * // Auto-detect everything\r\n * const buddy = new ConvertBuddy();\r\n * const result = await buddy.convert(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * \r\n * @example\r\n * // With configuration\r\n * const buddy = new ConvertBuddy({ maxMemoryMB: 512 });\r\n * const result = await buddy.convert(file, { inputFormat: \"csv\", outputFormat: \"json\" });\r\n */\r\n async convert(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n ): Promise<Uint8Array> {\r\n // Merge global and local options\r\n const mergedOpts: ConvertBuddyOptions = {\r\n ...this.globalConfig,\r\n ...opts,\r\n inputFormat: opts.inputFormat || this.globalConfig.inputFormat || \"auto\",\r\n };\r\n\r\n // Detect input type and convert accordingly\r\n if (typeof input === \"string\") {\r\n // Could be URL or raw data\r\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) {\r\n // Fetch from URL\r\n return this.convertFromUrl(input, mergedOpts);\r\n } else {\r\n // Treat as raw data\r\n return this.convertFromString(input, mergedOpts);\r\n }\r\n } else if (input instanceof Uint8Array) {\r\n return this.convertFromBuffer(input, mergedOpts);\r\n } else if (typeof File !== \"undefined\" && input instanceof File) {\r\n return this.convertFromFile(input as File, mergedOpts);\r\n } else if (typeof Blob !== \"undefined\" && input instanceof Blob) {\r\n return this.convertFromBlob(input as Blob, mergedOpts);\r\n } else if (typeof ReadableStream !== \"undefined\" && input instanceof ReadableStream) {\r\n return this.convertFromStream(input, mergedOpts);\r\n } else {\r\n throw new Error(\"Unsupported input type\");\r\n }\r\n }\r\n\r\n private async convertFromUrl(url: string, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch ${url}: ${response.statusText}`);\r\n }\r\n \r\n const stream = response.body;\r\n if (!stream) {\r\n throw new Error(\"Response body is null\");\r\n }\r\n\r\n return this.convertFromStream(stream, opts);\r\n }\r\n\r\n private async convertFromString(input: string, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n const inputBytes = new TextEncoder().encode(input);\r\n return this.convertFromBuffer(inputBytes, opts);\r\n }\r\n\r\n private async convertFromBuffer(input: Uint8Array, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Use WASM threading for large inputs when available\r\n const useWasmThreading = wasmThreadingSupported && input.length > 256 * 1024; // 256KB threshold\r\n \r\n if (useWasmThreading && opts.debug) {\r\n console.log('[convert-buddy] Using WASM threading for parallel processing');\r\n }\r\n \r\n // Check if we should use JavaScript-level parallelism as fallback\r\n if (!useWasmThreading && opts.parallelism && opts.parallelism > 1 && input.length > 512 * 1024) {\r\n if (opts.debug) {\r\n console.log('[convert-buddy] WASM threading not available, using JavaScript parallelism');\r\n }\r\n return this.convertFromBufferParallel(input, opts);\r\n }\r\n\r\n // Handle auto-detection\r\n let actualOpts = { ...opts };\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const detected = await autoDetectConfig(input, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n const output = buddy.push(input);\r\n const final = buddy.finish();\r\n\r\n // Combine outputs\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n private async convertFromBufferParallel(input: Uint8Array, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Only use parallel processing for large inputs and supported conversions\r\n const parallelThreshold = 512 * 1024; // 512KB (lowered threshold for better parallelism)\r\n const maxConcurrency = Math.min(\r\n typeof navigator !== \"undefined\" && navigator.hardwareConcurrency\r\n ? navigator.hardwareConcurrency\r\n : 4,\r\n 8\r\n );\r\n \r\n if (input.length < parallelThreshold || !opts.parallelism || opts.parallelism < 2) {\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n\r\n // Extended support for parallel processing\r\n const supportedConversions = [\r\n { input: \"csv\", output: \"ndjson\" },\r\n { input: \"csv\", output: \"json\" },\r\n { input: \"ndjson\", output: \"json\" },\r\n { input: \"ndjson\", output: \"csv\" },\r\n { input: \"ndjson\", output: \"ndjson\" }, // passthrough optimization\r\n { input: \"json\", output: \"ndjson\" },\r\n { input: \"json\", output: \"csv\" },\r\n ];\r\n\r\n const isSupported = supportedConversions.some(\r\n conv => conv.input === opts.inputFormat && conv.output === opts.outputFormat\r\n );\r\n\r\n if (!isSupported) {\r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Parallel processing not supported for ${opts.inputFormat} → ${opts.outputFormat}, using sequential`);\r\n }\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n\r\n try {\r\n const actualThreads = Math.min(maxConcurrency, opts.parallelism || maxConcurrency);\r\n const isNodejs = typeof window === \"undefined\" && typeof process !== \"undefined\";\r\n \r\n if (opts.debug) {\r\n const threadingType = isNodejs ? 'Node.js WASM threading' : 'Browser custom threading';\r\n console.log(`[convert-buddy] Using ${threadingType} with ${actualThreads} threads`);\r\n }\r\n\r\n if (isNodejs) {\r\n // Try enhanced Node.js threading first\r\n if (!nodejsThreadPool) {\r\n try {\r\n const { NodejsThreadPool } = await import('./nodejs-thread-pool');\r\n nodejsThreadPool = new NodejsThreadPool({\r\n maxWorkers: actualThreads,\r\n wasmPath: '../wasm-node.cjs'\r\n });\r\n await nodejsThreadPool.initialize();\r\n } catch (error) {\r\n if (opts.debug) {\r\n console.log('[convert-buddy] Node.js thread pool creation failed, using JS parallelism:', error);\r\n }\r\n }\r\n }\r\n\r\n if (nodejsThreadPool) {\r\n return this.convertUsingNodejsThreadPool(input, opts, actualThreads);\r\n } else {\r\n return this.convertUsingJsParallelism(input, opts, actualThreads);\r\n }\r\n } else {\r\n // Browser: Create custom thread pool for WASM-level parallelism\r\n if (!threadPool) {\r\n const { WasmThreadPool } = await import('./thread-pool');\r\n threadPool = new WasmThreadPool({\r\n maxWorkers: actualThreads,\r\n wasmPath: '../wasm/web/convert_buddy.js'\r\n });\r\n await threadPool.initialize();\r\n }\r\n\r\n return this.convertUsingWasmThreadPool(input, opts, actualThreads);\r\n }\r\n \r\n } catch (error) {\r\n if (opts.debug) {\r\n console.warn(`[convert-buddy] Parallel processing failed, falling back to sequential:`, error);\r\n }\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n }\r\n\r\n private async convertUsingNodejsThreadPool(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n const { chunkDataNodejs, mergeResultsNodejs } = await import('./nodejs-thread-pool');\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing with enhanced Node.js threading (${numThreads} workers)`);\r\n }\r\n\r\n // Use optimized chunking for Node.js\r\n const chunks = chunkDataNodejs(input, numThreads);\r\n \r\n // Process chunks using Node.js worker threads\r\n const results = await nodejsThreadPool!.processChunks('convert', chunks, {\r\n outputFormat: opts.outputFormat,\r\n inputFormat: opts.inputFormat,\r\n csvConfig: opts.csvConfig,\r\n xmlConfig: opts.xmlConfig,\r\n debug: false // Disable debug in workers to reduce noise\r\n });\r\n\r\n // Merge results intelligently based on output format\r\n const merged = mergeResultsNodejs(results, opts.outputFormat!);\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Node.js threading completed: ${chunks.length} chunks, ${merged.length} bytes`);\r\n }\r\n\r\n return merged;\r\n }\r\n\r\n private async convertUsingWasmThreadPool(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n const inputStr = decodeUtf8(input);\r\n \r\n // Import chunking utilities\r\n const { chunkData, mergeResults } = await import('./thread-pool');\r\n const chunks = chunkData(inputStr, numThreads);\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing ${chunks.length} chunks with WASM thread pool`);\r\n }\r\n\r\n // Determine method based on conversion type\r\n let method: string;\r\n const conversion = `${opts.inputFormat}_to_${opts.outputFormat}`;\r\n switch (conversion) {\r\n case 'csv_to_ndjson':\r\n case 'csv_to_json':\r\n method = 'parseCSV';\r\n break;\r\n case 'ndjson_to_csv':\r\n case 'ndjson_to_json':\r\n method = 'parseNDJSON';\r\n break;\r\n case 'json_to_csv':\r\n case 'json_to_ndjson':\r\n method = 'parseJSON';\r\n break;\r\n default:\r\n throw new Error(`Unsupported conversion: ${conversion}`);\r\n }\r\n\r\n // Process chunks in parallel using thread pool\r\n const results = await threadPool!.processChunks(method, chunks, {\r\n outputFormat: opts.outputFormat,\r\n csvConfig: opts.csvConfig,\r\n xmlConfig: opts.xmlConfig,\r\n debug: opts.debug\r\n });\r\n\r\n // Merge results intelligently based on output format\r\n const merged = mergeResults(results, opts.outputFormat!);\r\n return new TextEncoder().encode(merged);\r\n }\r\n\r\n private async convertUsingJsParallelism(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n // Split input into chunks based on line boundaries for CSV/NDJSON\r\n const chunks = this.splitIntoChunks(input, Math.max(1, Math.floor(input.length / numThreads)));\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing ${chunks.length} chunks with JS-level parallelism`);\r\n }\r\n \r\n // Process chunks in parallel with improved coordination\r\n const chunkPromises = chunks.map(async (chunk, index) => {\r\n const chunkOpts = { ...opts, parallelism: 1 }; // Disable recursion\r\n \r\n // For CSV with headers, only the first chunk should process headers\r\n if (opts.inputFormat === \"csv\" && index > 0) {\r\n chunkOpts.csvConfig = { ...chunkOpts.csvConfig, hasHeaders: false };\r\n }\r\n \r\n const chunkBuddy = await ConvertBuddy.create(chunkOpts);\r\n const output = chunkBuddy.push(chunk);\r\n const final = chunkBuddy.finish();\r\n \r\n // Combine chunk output\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n \r\n return result;\r\n });\r\n\r\n const chunkResults = await Promise.all(chunkPromises);\r\n\r\n // Combine results more efficiently\r\n const totalLength = chunkResults.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n \r\n let offset = 0;\r\n for (const chunk of chunkResults) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n\r\n if (opts.debug) {\r\n console.log(`[convert-buddy] JS parallel processing completed: ${chunks.length} chunks, ${totalLength} bytes`);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n private splitIntoChunks(input: Uint8Array, targetChunkSize: number): Uint8Array[] {\r\n if (input.length <= targetChunkSize) {\r\n return [input];\r\n }\r\n\r\n const chunks: Uint8Array[] = [];\r\n let start = 0;\r\n\r\n while (start < input.length) {\r\n let end = Math.min(start + targetChunkSize, input.length);\r\n \r\n // Try to find a line boundary within a reasonable range\r\n if (end < input.length) {\r\n const searchStart = Math.max(start + targetChunkSize - 1024, start);\r\n const searchEnd = Math.min(end + 1024, input.length);\r\n \r\n // Look for newline\r\n for (let i = searchEnd - 1; i >= searchStart; i--) {\r\n if (input[i] === 0x0A) { // '\\n'\r\n end = i + 1;\r\n break;\r\n }\r\n }\r\n }\r\n \r\n chunks.push(input.slice(start, end));\r\n start = end;\r\n }\r\n\r\n return chunks;\r\n }\r\n\r\n private async convertFromFile(file: File, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n return this.convertFromBlob(file, opts);\r\n }\r\n\r\n private async convertFromBlob(blob: Blob, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Handle auto-detection\r\n let actualOpts = { ...opts };\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const sampleSize = 256 * 1024; // 256KB\r\n const sampleBlob = blob.slice(0, sampleSize);\r\n const sampleBuffer = await sampleBlob.arrayBuffer();\r\n const sample = new Uint8Array(sampleBuffer as ArrayBuffer);\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n \r\n // Read blob as stream and process\r\n const stream = blob.stream();\r\n const reader = stream.getReader();\r\n \r\n const outputs: Uint8Array[] = [];\r\n \r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n \r\n const output = buddy.push(value);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine all outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n \r\n return result;\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n private async convertFromStream(stream: ReadableStream<Uint8Array>, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Handle auto-detection by reading a sample first\r\n let actualOpts = { ...opts };\r\n let firstChunks: Uint8Array[] = [];\r\n let totalSampleBytes = 0;\r\n const maxSampleSize = 256 * 1024; // 256KB\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const reader = stream.getReader();\r\n \r\n try {\r\n while (totalSampleBytes < maxSampleSize) {\r\n const { done, value } = await reader.read();\r\n if (done || !value) break;\r\n \r\n firstChunks.push(value);\r\n totalSampleBytes += value.length;\r\n }\r\n \r\n // Concatenate sample\r\n const sample = new Uint8Array(totalSampleBytes);\r\n let offset = 0;\r\n for (const chunk of firstChunks) {\r\n sample.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n \r\n // Process buffered chunks from auto-detection\r\n const outputs: Uint8Array[] = [];\r\n for (const chunk of firstChunks) {\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n // Continue with the rest of the stream\r\n const reader = stream.getReader();\r\n \r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n \r\n const output = buddy.push(value);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine all outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n \r\n return result;\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n /**\r\n * Legacy create method for backward compatibility.\r\n * Prefer using the constructor: new ConvertBuddy(opts)\r\n */\r\n static async create(opts: ConvertBuddyOptions = {}): Promise<ConvertBuddy> {\r\n const debug = !!opts.debug;\r\n const profile = !!opts.profile;\r\n\r\n // Initialize WASM once (singleton)\r\n await initWasm(debug);\r\n\r\n const wasmModule = await loadWasmModule();\r\n\r\n // Handle auto-detection\r\n let inputFormat = opts.inputFormat;\r\n let csvConfig = opts.csvConfig;\r\n\r\n // We can't auto-detect without data, so we'll defer this to push()\r\n // For now, just validate the format if it's not \"auto\"\r\n if (inputFormat === \"auto\") {\r\n // Auto-detection will be handled on first push()\r\n inputFormat = undefined;\r\n }\r\n\r\n // Optimize chunk size for better WASM performance\r\n // Larger chunks reduce boundary crossing overhead\r\n // Default: 512KB (was 1MB), but can be customized\r\n const chunkTargetBytes = opts.chunkTargetBytes || (512 * 1024);\r\n\r\n let converter;\r\n if (inputFormat && opts.outputFormat) {\r\n // Use withConfig for custom formats\r\n const Converter = (wasmModule as any).Converter;\r\n converter = Converter.withConfig(\r\n debug,\r\n inputFormat,\r\n opts.outputFormat,\r\n chunkTargetBytes,\r\n profile,\r\n csvConfig || null,\r\n opts.xmlConfig || null,\r\n opts.transform || null\r\n );\r\n } else {\r\n converter = new wasmModule.Converter(debug);\r\n }\r\n\r\n // Check if SIMD is enabled\r\n const simdEnabled = (wasmModule as any).getSimdEnabled?.() ?? false;\r\n\r\n if (debug) console.log(\"[convert-buddy-js] initialized with chunkTargetBytes:\", chunkTargetBytes, \"simd:\", simdEnabled, opts);\r\n \r\n // Create instance using constructor and set internal properties\r\n const instance = new ConvertBuddy(opts);\r\n instance.converter = converter;\r\n instance.simd = simdEnabled;\r\n instance.initialized = true;\r\n \r\n return instance;\r\n }\r\n\r\n push(chunk: Uint8Array): Uint8Array {\r\n if (this.aborted) {\r\n throw new Error(\"Conversion has been aborted\");\r\n }\r\n\r\n if (this.paused) {\r\n throw new Error(\"Conversion is paused. Call resume() before pushing more data.\");\r\n }\r\n\r\n if (this.debug) console.log(\"[convert-buddy-js] push\", chunk.byteLength);\r\n const output = this.converter.push(chunk);\r\n\r\n // Check if we should trigger progress callback\r\n if (this.onProgress) {\r\n const stats = this.stats();\r\n if (stats.bytesIn - this.lastProgressBytes >= this.progressIntervalBytes) {\r\n this.onProgress(stats);\r\n this.lastProgressBytes = stats.bytesIn;\r\n }\r\n }\r\n\r\n return output;\r\n }\r\n\r\n finish(): Uint8Array {\r\n if (this.aborted) {\r\n throw new Error(\"Conversion has been aborted\");\r\n }\r\n\r\n if (this.debug) console.log(\"[convert-buddy-js] finish\");\r\n const output = this.converter.finish();\r\n\r\n // Final progress callback\r\n if (this.onProgress) {\r\n this.onProgress(this.stats());\r\n }\r\n\r\n return output;\r\n }\r\n\r\n stats(): Stats {\r\n return this.converter.getStats();\r\n }\r\n\r\n abort(): void {\r\n this.aborted = true;\r\n if (this.debug) console.log(\"[convert-buddy-js] aborted\");\r\n }\r\n\r\n pause(): void {\r\n this.paused = true;\r\n if (this.debug) console.log(\"[convert-buddy-js] paused\");\r\n }\r\n\r\n resume(): void {\r\n this.paused = false;\r\n if (this.debug) console.log(\"[convert-buddy-js] resumed\");\r\n }\r\n\r\n isAborted(): boolean {\r\n return this.aborted;\r\n }\r\n\r\n isPaused(): boolean {\r\n return this.paused;\r\n }\r\n}\r\n\r\nasync function readSample(\r\n input: DetectInput,\r\n maxBytes = 256 * 1024\r\n): Promise<Uint8Array> {\r\n if (typeof input === \"string\") {\r\n const encoded = new TextEncoder().encode(input);\r\n return encoded.length > maxBytes ? encoded.slice(0, maxBytes) : encoded;\r\n }\r\n\r\n if (input instanceof Uint8Array) {\r\n return input.length > maxBytes ? input.slice(0, maxBytes) : input;\r\n }\r\n\r\n if (input instanceof ArrayBuffer) {\r\n const bytes = new Uint8Array(input as ArrayBuffer);\r\n return bytes.length > maxBytes ? bytes.slice(0, maxBytes) : bytes;\r\n }\r\n\r\n if (isReadableStream(input)) {\r\n const reader = input.getReader();\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n\r\n while (total < maxBytes) {\r\n const { value, done } = await reader.read();\r\n if (done || !value) break;\r\n const slice = total + value.length > maxBytes\r\n ? value.slice(0, maxBytes - total)\r\n : value;\r\n chunks.push(slice);\r\n total += slice.length;\r\n }\r\n\r\n if (total >= maxBytes) {\r\n await reader.cancel();\r\n }\r\n\r\n return concatChunks(chunks, total);\r\n }\r\n\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n\r\n for await (const chunk of input as AsyncIterable<Uint8Array>) {\r\n if (total >= maxBytes) {\r\n break;\r\n }\r\n const data = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\r\n const slice = total + data.length > maxBytes\r\n ? data.slice(0, maxBytes - total)\r\n : data;\r\n chunks.push(slice);\r\n total += slice.length;\r\n }\r\n\r\n return concatChunks(chunks, total);\r\n}\r\n\r\nfunction concatChunks(chunks: Uint8Array[], total: number): Uint8Array {\r\n const result = new Uint8Array(total);\r\n let offset = 0;\r\n for (const chunk of chunks) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction isReadableStream(\r\n input: DetectInput\r\n): input is ReadableStream<Uint8Array> {\r\n return typeof (input as ReadableStream<Uint8Array>)?.getReader === \"function\";\r\n}\r\n\r\nasync function loadDetectionWasm(debug: boolean): Promise<WasmModule> {\r\n const wasmModule = await loadWasmModule();\r\n if (typeof wasmModule.default === \"function\") {\r\n await (wasmModule.default as () => Promise<void>)();\r\n }\r\n wasmModule.init(debug);\r\n return wasmModule;\r\n}\r\n\r\nexport async function detectFormat(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<Format | \"unknown\"> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n const format = wasmModule.detectFormat?.(sample);\r\n return (format as Format) ?? \"unknown\";\r\n}\r\n\r\nexport async function detectStructure(\r\n input: DetectInput,\r\n formatHint?: Format,\r\n opts: DetectOptions = {}\r\n): Promise<StructureDetection | null> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n const result = wasmModule.detectStructure?.(sample, formatHint);\r\n return result ?? null;\r\n}\r\n\r\n// Backward compatibility functions - these now use the unified detectStructure internally\r\nexport async function detectCsvFieldsAndDelimiter(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<CsvDetection | null> {\r\n const structure = await detectStructure(input, \"csv\", opts);\r\n if (structure && structure.format === \"csv\" && structure.delimiter) {\r\n return {\r\n delimiter: structure.delimiter,\r\n fields: structure.fields,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nexport async function detectXmlElements(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<XmlDetection | null> {\r\n const structure = await detectStructure(input, \"xml\", opts);\r\n if (structure && structure.format === \"xml\") {\r\n return {\r\n elements: structure.fields,\r\n recordElement: structure.recordElement,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Helper to auto-detect format and CSV/XML configuration from sample data\r\nexport async function autoDetectConfig(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<{ \r\n format: Format | \"unknown\"; \r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n}> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n \r\n const format = (wasmModule.detectFormat?.(sample) as Format) ?? \"unknown\";\r\n \r\n const result: { format: Format | \"unknown\"; csvConfig?: CsvConfig; xmlConfig?: XmlConfig } = { format };\r\n \r\n if (format === \"csv\") {\r\n const csvDetection = wasmModule.detectCsvFields?.(sample);\r\n if (csvDetection) {\r\n result.csvConfig = {\r\n delimiter: csvDetection.delimiter,\r\n hasHeaders: csvDetection.fields.length > 0,\r\n };\r\n }\r\n } else if (format === \"xml\") {\r\n const xmlDetection = wasmModule.detectXmlElements?.(sample);\r\n if (xmlDetection?.recordElement) {\r\n result.xmlConfig = {\r\n recordElement: xmlDetection.recordElement,\r\n };\r\n }\r\n }\r\n \r\n return result;\r\n}\r\n\r\n// Web Streams TransformStream adapter\r\nexport class ConvertBuddyTransformStream extends TransformStream<Uint8Array, Uint8Array> {\r\n constructor(opts: ConvertBuddyOptions = {}) {\r\n let buddy: ConvertBuddy | null = null;\r\n\r\n super({\r\n async start(controller) {\r\n buddy = await ConvertBuddy.create(opts);\r\n },\r\n\r\n transform(chunk, controller) {\r\n if (!buddy) {\r\n throw new Error(\"ConvertBuddy not initialized\");\r\n }\r\n\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n controller.enqueue(output);\r\n }\r\n },\r\n\r\n flush(controller) {\r\n if (!buddy) {\r\n return;\r\n }\r\n\r\n const output = buddy.finish();\r\n if (output.length > 0) {\r\n controller.enqueue(output);\r\n }\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n },\r\n });\r\n }\r\n}\r\n\r\n// Utility: Convert entire buffer/string\r\nexport async function convert(\r\n input: Uint8Array | string,\r\n opts: ConvertBuddyOptions = {}\r\n): Promise<Uint8Array> {\r\n try {\r\n const inputBytes = typeof input === \"string\" \r\n ? new TextEncoder().encode(input)\r\n : input;\r\n\r\n // If inputFormat is not specified or is \"auto\", perform auto-detection\r\n let actualOpts = opts;\r\n if (!opts.inputFormat || opts.inputFormat === \"auto\") {\r\n const sample = inputBytes.length > 256 * 1024 \r\n ? inputBytes.slice(0, 256 * 1024) \r\n : inputBytes;\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts = { ...opts, inputFormat: detected.format as Format };\r\n \r\n if (detected.csvConfig && !opts.csvConfig) {\r\n actualOpts.csvConfig = detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig && !opts.xmlConfig) {\r\n actualOpts.xmlConfig = detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n\r\n const output = buddy.push(inputBytes);\r\n const final = buddy.finish();\r\n\r\n // Combine outputs\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n\r\n return result;\r\n } catch (err: any) {\r\n // Normalize non-Error throws (e.g., wasm JsValue) into Error with message\r\n if (err instanceof Error) throw err;\r\n try {\r\n // Try to stringify common structures\r\n const msg = typeof err === 'string' ? err : (err && err.message) ? err.message : JSON.stringify(err);\r\n throw new Error(String(msg));\r\n } catch (_) {\r\n throw new Error(String(err));\r\n }\r\n }\r\n}\r\n\r\n// Utility: Convert and return as string\r\nexport async function convertToString(\r\n input: Uint8Array | string,\r\n opts: ConvertBuddyOptions = {}\r\n): Promise<string> {\r\n const result = await convert(input, opts);\r\n return decodeUtf8(result);\r\n}\r\n\r\n/**\r\n * Ultra-simple standalone convert function with auto-detection.\r\n * Accepts any input type (URL, File, Buffer, string, stream) and automatically detects format.\r\n * \r\n * @example\r\n * // From URL\r\n * import { convertAny } from \"convert-buddy-js\";\r\n * const result = await convertAny(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * \r\n * @example\r\n * // From File (browser)\r\n * const file = fileInput.files[0];\r\n * const result = await convertAny(file, { outputFormat: \"ndjson\" });\r\n * \r\n * @example\r\n * // From string data\r\n * const result = await convertAny('{\"name\":\"Ada\"}', { outputFormat: \"csv\" });\r\n */\r\nexport async function convertAny(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n): Promise<Uint8Array> {\r\n const buddy = new ConvertBuddy();\r\n return buddy.convert(input, opts);\r\n}\r\n\r\n/**\r\n * Ultra-simple standalone convert function that returns a string.\r\n * Same as convertAny but decodes the output to a string.\r\n * \r\n * @example\r\n * import { convertAnyToString } from \"convert-buddy-js\";\r\n * const json = await convertAnyToString(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * console.log(JSON.parse(json));\r\n */\r\nexport async function convertAnyToString(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n): Promise<string> {\r\n const result = await convertAny(input, opts);\r\n return decodeUtf8(result);\r\n}\r\n\r\n// ===== Helper Functions =====\r\n// File format utilities for common use cases\r\n\r\n/**\r\n * Get MIME type for a given format\r\n * \r\n * @example\r\n * const mimeType = getMimeType(\"json\"); // \"application/json\"\r\n */\r\nexport function getMimeType(format: Format): string {\r\n switch (format) {\r\n case \"json\":\r\n return \"application/json\";\r\n case \"ndjson\":\r\n return \"application/x-ndjson\";\r\n case \"csv\":\r\n return \"text/csv\";\r\n case \"xml\":\r\n return \"application/xml\";\r\n }\r\n}\r\n\r\n/**\r\n * Get file extension for a given format (without the dot)\r\n * \r\n * @example\r\n * const ext = getExtension(\"json\"); // \"json\"\r\n */\r\nexport function getExtension(format: Format): string {\r\n return format;\r\n}\r\n\r\n/**\r\n * Get suggested filename for a converted file\r\n * \r\n * @param originalName - Original filename\r\n * @param outputFormat - Target format\r\n * @param includeTimestamp - Whether to include a timestamp (default: false)\r\n * \r\n * @example\r\n * const name = getSuggestedFilename(\"data.csv\", \"json\"); // \"data.json\"\r\n * const name = getSuggestedFilename(\"data.csv\", \"json\", true); // \"data_converted_1234567890.json\"\r\n */\r\nexport function getSuggestedFilename(\r\n originalName: string,\r\n outputFormat: Format,\r\n includeTimestamp = false\r\n): string {\r\n const baseName = originalName.replace(/\\.[^/.]+$/, \"\");\r\n const extension = getExtension(outputFormat);\r\n \r\n if (includeTimestamp) {\r\n return `${baseName}_converted_${Date.now()}.${extension}`;\r\n }\r\n \r\n return `${baseName}.${extension}`;\r\n}\r\n\r\n/**\r\n * Get File System Access API file type configuration for showSaveFilePicker\r\n * \r\n * @example\r\n * const types = getFileTypeConfig(\"json\");\r\n * const handle = await showSaveFilePicker({ types });\r\n */\r\nexport function getFileTypeConfig(format: Format): Array<{\r\n description: string;\r\n accept: Record<string, string[]>;\r\n}> {\r\n const mimeType = getMimeType(format);\r\n const extension = `.${getExtension(format)}`;\r\n \r\n return [\r\n {\r\n description: `${format.toUpperCase()} Files`,\r\n accept: { [mimeType]: [extension] },\r\n },\r\n ];\r\n}\r\n\r\n// WASM Threading Capabilities API\r\n/**\r\n * Check if WASM threading is supported in the current environment\r\n * @returns true if SharedArrayBuffer and Atomics are available (required for WASM threads)\r\n */\r\nexport function isWasmThreadingSupported(): boolean {\r\n return detectWasmThreadingSupport();\r\n}\r\n\r\n/**\r\n * Get the optimal number of worker threads based on CPU cores and WASM capabilities\r\n * @returns Recommended thread count for optimal performance\r\n */\r\nexport function getOptimalThreadCount(): number {\r\n const cores = typeof navigator !== 'undefined' \r\n ? (navigator.hardwareConcurrency || 4)\r\n : (process?.env?.UV_THREADPOOL_SIZE ? parseInt(process.env.UV_THREADPOOL_SIZE) : \r\n typeof require !== 'undefined' ? require('os').cpus().length : 4);\r\n \r\n // For current JavaScript-level parallelism, limit to 4 parallel instances\r\n // When true WASM threading is enabled, this can be increased\r\n return Math.min(cores, 4);\r\n}\r\n\r\n/**\r\n * Get current threading capabilities and configuration\r\n * @returns Object with threading information\r\n */\r\nexport function getThreadingInfo(): {\r\n wasmThreadingSupported: boolean;\r\n customThreadPoolAvailable: boolean;\r\n nodejsWasmThreading: boolean;\r\n recommendedThreads: number;\r\n currentThreads: number;\r\n approach: string;\r\n} {\r\n const isNodejs = typeof process !== 'undefined';\r\n \r\n return {\r\n wasmThreadingSupported: detectWasmThreadingSupport(),\r\n customThreadPoolAvailable: typeof window !== 'undefined',\r\n nodejsWasmThreading: isNodejs && !!nodejsThreadPool,\r\n recommendedThreads: getOptimalThreadCount(),\r\n currentThreads: isNodejs ? \r\n (nodejsThreadPool ? nodejsThreadPool.workers?.length || 0 : 0) :\r\n (threadPool ? threadPool.workers?.length || 0 : 0),\r\n approach: isNodejs ? 'nodejs_enhanced_threading' : 'browser_custom_threading'\r\n };\r\n}\r\n"],"mappings":"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","names":["require","mod","result","reader","chunks","total"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Format = \"csv\" | \"ndjson\" | \"json\" | \"xml\";\r\nexport type DetectInput =\r\n | Uint8Array\r\n | ArrayBuffer\r\n | string\r\n | ReadableStream<Uint8Array>\r\n | AsyncIterable<Uint8Array>;\r\n\r\nexport type CsvDetection = {\r\n delimiter: string;\r\n fields: string[];\r\n};\r\n\r\nexport type XmlDetection = {\r\n elements: string[];\r\n recordElement?: string;\r\n};\r\n\r\nexport type JsonDetection = {\r\n fields: string[];\r\n};\r\n\r\nexport type NdjsonDetection = {\r\n fields: string[];\r\n};\r\n\r\nexport type StructureDetection = {\r\n format: Format;\r\n fields: string[];\r\n delimiter?: string; // For CSV\r\n recordElement?: string; // For XML\r\n};\r\n\r\nexport type DetectOptions = {\r\n maxBytes?: number;\r\n debug?: boolean;\r\n};\r\n\r\nexport type ProgressCallback = (stats: Stats) => void;\r\n\r\nexport type ConvertBuddyOptions = {\r\n debug?: boolean;\r\n profile?: boolean;\r\n inputFormat?: Format | \"auto\";\r\n outputFormat?: Format;\r\n chunkTargetBytes?: number;\r\n parallelism?: number; // Node only - number of worker threads\r\n maxMemoryMB?: number; // Memory limit for conversions (future use)\r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n transform?: TransformConfig;\r\n onProgress?: ProgressCallback;\r\n progressIntervalBytes?: number; // Trigger progress callback every N bytes (default: 1MB)\r\n};\r\n\r\nexport type ConvertOptions = {\r\n inputFormat?: Format | \"auto\";\r\n outputFormat: Format;\r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n transform?: TransformConfig;\r\n onProgress?: ProgressCallback;\r\n};\r\n\r\nexport type CsvConfig = {\r\n delimiter?: string;\r\n quote?: string;\r\n hasHeaders?: boolean;\r\n trimWhitespace?: boolean;\r\n};\r\n\r\nexport type XmlConfig = {\r\n recordElement?: string;\r\n trimText?: boolean;\r\n includeAttributes?: boolean;\r\n expandEntities?: boolean;\r\n};\r\n\r\nexport type TransformMode = \"replace\" | \"augment\";\r\n\r\nexport type Coerce =\r\n | { type: \"string\" }\r\n | { type: \"i64\" }\r\n | { type: \"f64\" }\r\n | { type: \"bool\" }\r\n | { type: \"timestamp_ms\"; format?: \"iso8601\" | \"unix_ms\" | \"unix_s\" };\r\n\r\nexport type FieldMap = {\r\n targetFieldName: string;\r\n originFieldName?: string;\r\n required?: boolean;\r\n defaultValue?: string | number | boolean | null;\r\n coerce?: Coerce;\r\n compute?: string;\r\n};\r\n\r\nexport type TransformConfig = {\r\n mode?: TransformMode;\r\n fields: FieldMap[];\r\n onMissingField?: \"error\" | \"null\" | \"drop\";\r\n onMissingRequired?: \"error\" | \"abort\";\r\n onCoerceError?: \"error\" | \"null\" | \"dropRecord\";\r\n};\r\n\r\nexport type Stats = {\r\n bytesIn: number;\r\n bytesOut: number;\r\n chunksIn: number;\r\n recordsProcessed: number;\r\n parseTimeMs: number;\r\n transformTimeMs: number;\r\n writeTimeMs: number;\r\n maxBufferSize: number;\r\n currentPartialSize: number;\r\n throughputMbPerSec: number;\r\n};\r\n\r\ntype WasmModule = {\r\n default?: unknown;\r\n init: (debugEnabled: boolean) => void;\r\n Converter: new (debug: boolean) => {\r\n push: (chunk: Uint8Array) => Uint8Array;\r\n finish: () => Uint8Array;\r\n getStats: () => Stats;\r\n };\r\n detectFormat?: (sample: Uint8Array) => string | null | undefined;\r\n detectCsvFields?: (sample: Uint8Array) => CsvDetection | null | undefined;\r\n detectXmlElements?: (sample: Uint8Array) => XmlDetection | null | undefined;\r\n detectJsonFields?: (sample: Uint8Array) => JsonDetection | null | undefined;\r\n detectNdjsonFields?: (sample: Uint8Array) => NdjsonDetection | null | undefined;\r\n detectStructure?: (sample: Uint8Array, formatHint?: string) => StructureDetection | null | undefined;\r\n getSimdEnabled?: () => boolean;\r\n __wbg_set_wasm?: (wasm: unknown) => void;\r\n};\r\n\r\nlet wasmModuleInstance: WasmModule | null = null;\r\nlet wasmModuleLoadPromise: Promise<WasmModule> | null = null;\r\nlet wasmThreadingSupported = false;\r\nlet threadPool: any = null; // Custom WASM thread pool (browser)\r\nlet nodejsThreadPool: any = null; // Node.js specific thread pool\r\nconst utf8Decoder = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true });\r\n\r\nfunction decodeUtf8(bytes: Uint8Array): string {\r\n return utf8Decoder.decode(bytes);\r\n}\r\n\r\n// Detect SharedArrayBuffer support for WASM threading\r\nfunction detectWasmThreadingSupport(): boolean {\r\n if (typeof SharedArrayBuffer === 'undefined') {\r\n return false;\r\n }\r\n \r\n // Test if we can actually create a SharedArrayBuffer\r\n try {\r\n new SharedArrayBuffer(1);\r\n return true;\r\n } catch (e) {\r\n return false;\r\n }\r\n}\r\n\r\nasync function loadWasmModule(): Promise<WasmModule> {\r\n // Return cached instance if already loaded\r\n if (wasmModuleInstance) {\r\n return wasmModuleInstance;\r\n }\r\n\r\n // If load is in progress, wait for it\r\n if (wasmModuleLoadPromise) {\r\n return wasmModuleLoadPromise;\r\n }\r\n\r\n // Start loading\r\n wasmModuleLoadPromise = (async () => {\r\n const isNode =\r\n typeof process !== \"undefined\" &&\r\n !!(process as any).versions?.node;\r\n\r\n // Detect threading support\r\n wasmThreadingSupported = detectWasmThreadingSupport();\r\n \r\n if (isNode) {\r\n const nodeModule: any = await import(/* webpackIgnore: true */ \"node:module\");\r\n const createRequire = nodeModule.createRequire as any;\r\n const requireFn = createRequire ? createRequire(import.meta.url) : (module as any).createRequire?.(import.meta.url);\r\n const require = requireFn ?? (globalThis as any).require;\r\n const mod = require(\"../wasm-node.cjs\");\r\n return mod as WasmModule;\r\n }\r\n\r\n const mod = (await import(\"../wasm/web/convert_buddy.js\")) as unknown as WasmModule;\r\n \r\n // Initialize threading if supported\r\n if (wasmThreadingSupported && (mod as any).initThreadPool) {\r\n try {\r\n const numThreads = Math.min(navigator.hardwareConcurrency || 4, 8);\r\n await (mod as any).initThreadPool(numThreads);\r\n console.log(`[convert-buddy] WASM threading initialized with ${numThreads} threads`);\r\n } catch (e) {\r\n console.warn('[convert-buddy] WASM threading initialization failed, using single-thread:', e);\r\n wasmThreadingSupported = false;\r\n }\r\n }\r\n \r\n return mod;\r\n })();\r\n\r\n try {\r\n wasmModuleInstance = await wasmModuleLoadPromise;\r\n return wasmModuleInstance;\r\n } finally {\r\n wasmModuleLoadPromise = null;\r\n }\r\n}\r\n\r\nlet wasmInitialized = false;\r\nlet wasmInitPromise: Promise<void> | null = null;\r\n\r\nasync function initWasm(debug: boolean): Promise<void> {\r\n // If already initialized, return immediately\r\n if (wasmInitialized) {\r\n return;\r\n }\r\n\r\n // If initialization is in progress, wait for it\r\n if (wasmInitPromise) {\r\n return wasmInitPromise;\r\n }\r\n\r\n // Start initialization\r\n wasmInitPromise = (async () => {\r\n const wasmModule = await loadWasmModule();\r\n\r\n if (typeof wasmModule.default === \"function\") {\r\n await (wasmModule.default as () => Promise<void>)();\r\n }\r\n\r\n wasmModule.init(debug);\r\n \r\n // Note: Node.js enhanced threading is handled at the JavaScript level\r\n // No WASM thread pool initialization needed\r\n \r\n wasmInitialized = true;\r\n })();\r\n\r\n try {\r\n await wasmInitPromise;\r\n } finally {\r\n wasmInitPromise = null;\r\n }\r\n}\r\n\r\nexport class ConvertBuddy {\r\n private converter: any;\r\n private debug: boolean;\r\n private profile: boolean;\r\n private aborted: boolean = false;\r\n private paused: boolean = false;\r\n private onProgress?: ProgressCallback;\r\n private progressIntervalBytes: number;\r\n private lastProgressBytes: number = 0;\r\n private globalConfig: ConvertBuddyOptions;\r\n private initialized: boolean = false;\r\n public simd: boolean;\r\n\r\n /**\r\n * Create a new ConvertBuddy instance with global configuration.\r\n * This is useful when you want to set memory limits, debug mode, or other global settings.\r\n * \r\n * @example\r\n * const buddy = new ConvertBuddy({ maxMemoryMB: 512, debug: true });\r\n * const result = await buddy.convert(input, { outputFormat: \"json\" });\r\n */\r\n constructor(opts: ConvertBuddyOptions = {}) {\r\n // Initialize basic properties\r\n this.debug = !!opts.debug;\r\n this.profile = !!opts.profile;\r\n this.simd = false; // Will be set on first convert\r\n this.globalConfig = opts;\r\n this.progressIntervalBytes = opts.progressIntervalBytes || 1024 * 1024;\r\n this.onProgress = opts.onProgress;\r\n this.converter = null; // Will be initialized lazily\r\n this.initialized = false;\r\n }\r\n\r\n /**\r\n * Convert input (string, Buffer, File, URL, etc.) to the desired output format.\r\n * This is the main method for the new simplified API.\r\n * \r\n * @example\r\n * // Auto-detect everything\r\n * const buddy = new ConvertBuddy();\r\n * const result = await buddy.convert(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * \r\n * @example\r\n * // With configuration\r\n * const buddy = new ConvertBuddy({ maxMemoryMB: 512 });\r\n * const result = await buddy.convert(file, { inputFormat: \"csv\", outputFormat: \"json\" });\r\n */\r\n async convert(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n ): Promise<Uint8Array> {\r\n // Merge global and local options\r\n const mergedOpts: ConvertBuddyOptions = {\r\n ...this.globalConfig,\r\n ...opts,\r\n inputFormat: opts.inputFormat || this.globalConfig.inputFormat || \"auto\",\r\n };\r\n\r\n // Detect input type and convert accordingly\r\n if (typeof input === \"string\") {\r\n // Could be URL or raw data\r\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) {\r\n // Fetch from URL\r\n return this.convertFromUrl(input, mergedOpts);\r\n } else {\r\n // Treat as raw data\r\n return this.convertFromString(input, mergedOpts);\r\n }\r\n } else if (input instanceof Uint8Array) {\r\n return this.convertFromBuffer(input, mergedOpts);\r\n } else if (typeof File !== \"undefined\" && input instanceof File) {\r\n return this.convertFromFile(input as File, mergedOpts);\r\n } else if (typeof Blob !== \"undefined\" && input instanceof Blob) {\r\n return this.convertFromBlob(input as Blob, mergedOpts);\r\n } else if (typeof ReadableStream !== \"undefined\" && input instanceof ReadableStream) {\r\n return this.convertFromStream(input, mergedOpts);\r\n } else {\r\n throw new Error(\"Unsupported input type\");\r\n }\r\n }\r\n\r\n private async convertFromUrl(url: string, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch ${url}: ${response.statusText}`);\r\n }\r\n \r\n const stream = response.body;\r\n if (!stream) {\r\n throw new Error(\"Response body is null\");\r\n }\r\n\r\n return this.convertFromStream(stream, opts);\r\n }\r\n\r\n private async convertFromString(input: string, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n const inputBytes = new TextEncoder().encode(input);\r\n return this.convertFromBuffer(inputBytes, opts);\r\n }\r\n\r\n private async convertFromBuffer(input: Uint8Array, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Use WASM threading for large inputs when available\r\n const useWasmThreading = wasmThreadingSupported && input.length > 256 * 1024; // 256KB threshold\r\n \r\n if (useWasmThreading && opts.debug) {\r\n console.log('[convert-buddy] Using WASM threading for parallel processing');\r\n }\r\n \r\n // Check if we should use JavaScript-level parallelism as fallback\r\n if (!useWasmThreading && opts.parallelism && opts.parallelism > 1 && input.length > 512 * 1024) {\r\n if (opts.debug) {\r\n console.log('[convert-buddy] WASM threading not available, using JavaScript parallelism');\r\n }\r\n return this.convertFromBufferParallel(input, opts);\r\n }\r\n\r\n // Handle auto-detection\r\n let actualOpts = { ...opts };\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const detected = await autoDetectConfig(input, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n const output = buddy.push(input);\r\n const final = buddy.finish();\r\n\r\n // Combine outputs\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n private async convertFromBufferParallel(input: Uint8Array, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Only use parallel processing for large inputs and supported conversions\r\n const parallelThreshold = 512 * 1024; // 512KB (lowered threshold for better parallelism)\r\n const maxConcurrency = Math.min(\r\n typeof navigator !== \"undefined\" && navigator.hardwareConcurrency\r\n ? navigator.hardwareConcurrency\r\n : 4,\r\n 8\r\n );\r\n \r\n if (input.length < parallelThreshold || !opts.parallelism || opts.parallelism < 2) {\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n\r\n // Extended support for parallel processing\r\n const supportedConversions = [\r\n { input: \"csv\", output: \"ndjson\" },\r\n { input: \"csv\", output: \"json\" },\r\n { input: \"ndjson\", output: \"json\" },\r\n { input: \"ndjson\", output: \"csv\" },\r\n { input: \"ndjson\", output: \"ndjson\" }, // passthrough optimization\r\n { input: \"json\", output: \"ndjson\" },\r\n { input: \"json\", output: \"csv\" },\r\n ];\r\n\r\n const isSupported = supportedConversions.some(\r\n conv => conv.input === opts.inputFormat && conv.output === opts.outputFormat\r\n );\r\n\r\n if (!isSupported) {\r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Parallel processing not supported for ${opts.inputFormat} → ${opts.outputFormat}, using sequential`);\r\n }\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n\r\n try {\r\n const actualThreads = Math.min(maxConcurrency, opts.parallelism || maxConcurrency);\r\n const isNodejs = typeof window === \"undefined\" && typeof process !== \"undefined\";\r\n \r\n if (opts.debug) {\r\n const threadingType = isNodejs ? 'Node.js WASM threading' : 'Browser custom threading';\r\n console.log(`[convert-buddy] Using ${threadingType} with ${actualThreads} threads`);\r\n }\r\n\r\n if (isNodejs) {\r\n // Try enhanced Node.js threading first\r\n if (!nodejsThreadPool) {\r\n try {\r\n const { NodejsThreadPool } = await import('./nodejs-thread-pool');\r\n nodejsThreadPool = new NodejsThreadPool({\r\n maxWorkers: actualThreads,\r\n wasmPath: '../wasm-node.cjs'\r\n });\r\n await nodejsThreadPool.initialize();\r\n } catch (error) {\r\n if (opts.debug) {\r\n console.log('[convert-buddy] Node.js thread pool creation failed, using JS parallelism:', error);\r\n }\r\n }\r\n }\r\n\r\n if (nodejsThreadPool) {\r\n return this.convertUsingNodejsThreadPool(input, opts, actualThreads);\r\n } else {\r\n return this.convertUsingJsParallelism(input, opts, actualThreads);\r\n }\r\n } else {\r\n // Browser: Create custom thread pool for WASM-level parallelism\r\n if (!threadPool) {\r\n const { WasmThreadPool } = await import('./thread-pool');\r\n threadPool = new WasmThreadPool({\r\n maxWorkers: actualThreads,\r\n wasmPath: '../wasm/web/convert_buddy.js'\r\n });\r\n await threadPool.initialize();\r\n }\r\n\r\n return this.convertUsingWasmThreadPool(input, opts, actualThreads);\r\n }\r\n \r\n } catch (error) {\r\n if (opts.debug) {\r\n console.warn(`[convert-buddy] Parallel processing failed, falling back to sequential:`, error);\r\n }\r\n return this.convertFromBuffer(input, { ...opts, parallelism: 1 });\r\n }\r\n }\r\n\r\n private async convertUsingNodejsThreadPool(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n const { chunkDataNodejs, mergeResultsNodejs } = await import('./nodejs-thread-pool');\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing with enhanced Node.js threading (${numThreads} workers)`);\r\n }\r\n\r\n // Use optimized chunking for Node.js\r\n const chunks = chunkDataNodejs(input, numThreads);\r\n \r\n // Process chunks using Node.js worker threads\r\n const results = await nodejsThreadPool!.processChunks('convert', chunks, {\r\n outputFormat: opts.outputFormat,\r\n inputFormat: opts.inputFormat,\r\n csvConfig: opts.csvConfig,\r\n xmlConfig: opts.xmlConfig,\r\n debug: false // Disable debug in workers to reduce noise\r\n });\r\n\r\n // Merge results intelligently based on output format\r\n const merged = mergeResultsNodejs(results, opts.outputFormat!);\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Node.js threading completed: ${chunks.length} chunks, ${merged.length} bytes`);\r\n }\r\n\r\n return merged;\r\n }\r\n\r\n private async convertUsingWasmThreadPool(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n const inputStr = decodeUtf8(input);\r\n \r\n // Import chunking utilities\r\n const { chunkData, mergeResults } = await import('./thread-pool');\r\n const chunks = chunkData(inputStr, numThreads);\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing ${chunks.length} chunks with WASM thread pool`);\r\n }\r\n\r\n // Determine method based on conversion type\r\n let method: string;\r\n const conversion = `${opts.inputFormat}_to_${opts.outputFormat}`;\r\n switch (conversion) {\r\n case 'csv_to_ndjson':\r\n case 'csv_to_json':\r\n method = 'parseCSV';\r\n break;\r\n case 'ndjson_to_csv':\r\n case 'ndjson_to_json':\r\n method = 'parseNDJSON';\r\n break;\r\n case 'json_to_csv':\r\n case 'json_to_ndjson':\r\n method = 'parseJSON';\r\n break;\r\n default:\r\n throw new Error(`Unsupported conversion: ${conversion}`);\r\n }\r\n\r\n // Process chunks in parallel using thread pool\r\n const results = await threadPool!.processChunks(method, chunks, {\r\n outputFormat: opts.outputFormat,\r\n csvConfig: opts.csvConfig,\r\n xmlConfig: opts.xmlConfig,\r\n debug: opts.debug\r\n });\r\n\r\n // Merge results intelligently based on output format\r\n const merged = mergeResults(results, opts.outputFormat!);\r\n return new TextEncoder().encode(merged);\r\n }\r\n\r\n private async convertUsingJsParallelism(input: Uint8Array, opts: ConvertBuddyOptions, numThreads: number): Promise<Uint8Array> {\r\n // Split input into chunks based on line boundaries for CSV/NDJSON\r\n const chunks = this.splitIntoChunks(input, Math.max(1, Math.floor(input.length / numThreads)));\r\n \r\n if (opts.debug) {\r\n console.log(`[convert-buddy] Processing ${chunks.length} chunks with JS-level parallelism`);\r\n }\r\n \r\n // Process chunks in parallel with improved coordination\r\n const chunkPromises = chunks.map(async (chunk, index) => {\r\n const chunkOpts = { ...opts, parallelism: 1 }; // Disable recursion\r\n \r\n // For CSV with headers, only the first chunk should process headers\r\n if (opts.inputFormat === \"csv\" && index > 0) {\r\n chunkOpts.csvConfig = { ...chunkOpts.csvConfig, hasHeaders: false };\r\n }\r\n \r\n const chunkBuddy = await ConvertBuddy.create(chunkOpts);\r\n const output = chunkBuddy.push(chunk);\r\n const final = chunkBuddy.finish();\r\n \r\n // Combine chunk output\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n \r\n return result;\r\n });\r\n\r\n const chunkResults = await Promise.all(chunkPromises);\r\n\r\n // Combine results more efficiently\r\n const totalLength = chunkResults.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n \r\n let offset = 0;\r\n for (const chunk of chunkResults) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n\r\n if (opts.debug) {\r\n console.log(`[convert-buddy] JS parallel processing completed: ${chunks.length} chunks, ${totalLength} bytes`);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n private splitIntoChunks(input: Uint8Array, targetChunkSize: number): Uint8Array[] {\r\n if (input.length <= targetChunkSize) {\r\n return [input];\r\n }\r\n\r\n const chunks: Uint8Array[] = [];\r\n let start = 0;\r\n\r\n while (start < input.length) {\r\n let end = Math.min(start + targetChunkSize, input.length);\r\n \r\n // Try to find a line boundary within a reasonable range\r\n if (end < input.length) {\r\n const searchStart = Math.max(start + targetChunkSize - 1024, start);\r\n const searchEnd = Math.min(end + 1024, input.length);\r\n \r\n // Look for newline\r\n for (let i = searchEnd - 1; i >= searchStart; i--) {\r\n if (input[i] === 0x0A) { // '\\n'\r\n end = i + 1;\r\n break;\r\n }\r\n }\r\n }\r\n \r\n chunks.push(input.slice(start, end));\r\n start = end;\r\n }\r\n\r\n return chunks;\r\n }\r\n\r\n private async convertFromFile(file: File, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n return this.convertFromBlob(file, opts);\r\n }\r\n\r\n private async convertFromBlob(blob: Blob, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Handle auto-detection\r\n let actualOpts = { ...opts };\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const sampleSize = 256 * 1024; // 256KB\r\n const sampleBlob = blob.slice(0, sampleSize);\r\n const sampleBuffer = await sampleBlob.arrayBuffer();\r\n const sample = new Uint8Array(sampleBuffer as ArrayBuffer);\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n \r\n // Read blob as stream and process\r\n const stream = blob.stream();\r\n const reader = stream.getReader();\r\n \r\n const outputs: Uint8Array[] = [];\r\n \r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n \r\n const output = buddy.push(value);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine all outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n \r\n return result;\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n private async convertFromStream(stream: ReadableStream<Uint8Array>, opts: ConvertBuddyOptions): Promise<Uint8Array> {\r\n // Handle auto-detection by reading a sample first\r\n let actualOpts = { ...opts };\r\n let firstChunks: Uint8Array[] = [];\r\n let totalSampleBytes = 0;\r\n const maxSampleSize = 256 * 1024; // 256KB\r\n \r\n if (opts.inputFormat === \"auto\") {\r\n const reader = stream.getReader();\r\n \r\n try {\r\n while (totalSampleBytes < maxSampleSize) {\r\n const { done, value } = await reader.read();\r\n if (done || !value) break;\r\n \r\n firstChunks.push(value);\r\n totalSampleBytes += value.length;\r\n }\r\n \r\n // Concatenate sample\r\n const sample = new Uint8Array(totalSampleBytes);\r\n let offset = 0;\r\n for (const chunk of firstChunks) {\r\n sample.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts.inputFormat = detected.format as Format;\r\n \r\n if (detected.csvConfig) {\r\n actualOpts.csvConfig = opts.csvConfig ? { ...detected.csvConfig, ...opts.csvConfig } : detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig) {\r\n actualOpts.xmlConfig = opts.xmlConfig ? { ...detected.xmlConfig, ...opts.xmlConfig } : detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n \r\n // Process buffered chunks from auto-detection\r\n const outputs: Uint8Array[] = [];\r\n for (const chunk of firstChunks) {\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n // Continue with the rest of the stream\r\n const reader = stream.getReader();\r\n \r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n \r\n const output = buddy.push(value);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine all outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n \r\n return result;\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n /**\r\n * Legacy create method for backward compatibility.\r\n * Prefer using the constructor: new ConvertBuddy(opts)\r\n */\r\n static async create(opts: ConvertBuddyOptions = {}): Promise<ConvertBuddy> {\r\n const debug = !!opts.debug;\r\n const profile = !!opts.profile;\r\n\r\n // Initialize WASM once (singleton)\r\n await initWasm(debug);\r\n\r\n const wasmModule = await loadWasmModule();\r\n\r\n // Handle auto-detection\r\n let inputFormat = opts.inputFormat;\r\n let csvConfig = opts.csvConfig;\r\n\r\n // We can't auto-detect without data, so we'll defer this to push()\r\n // For now, just validate the format if it's not \"auto\"\r\n if (inputFormat === \"auto\") {\r\n // Auto-detection will be handled on first push()\r\n inputFormat = undefined;\r\n }\r\n\r\n // Optimize chunk size for better WASM performance\r\n // Larger chunks reduce boundary crossing overhead\r\n // Default: 512KB (was 1MB), but can be customized\r\n const chunkTargetBytes = opts.chunkTargetBytes || (512 * 1024);\r\n\r\n let converter;\r\n if (inputFormat && opts.outputFormat) {\r\n // Use withConfig for custom formats\r\n const Converter = (wasmModule as any).Converter;\r\n converter = Converter.withConfig(\r\n debug,\r\n inputFormat,\r\n opts.outputFormat,\r\n chunkTargetBytes,\r\n profile, // Enable stats tracking when profile is enabled\r\n csvConfig || null,\r\n opts.xmlConfig || null,\r\n opts.transform || null\r\n );\r\n } else {\r\n converter = new wasmModule.Converter(debug);\r\n }\r\n\r\n // Check if SIMD is enabled\r\n const simdEnabled = (wasmModule as any).getSimdEnabled?.() ?? false;\r\n\r\n if (debug) console.log(\"[convert-buddy-js] initialized with chunkTargetBytes:\", chunkTargetBytes, \"simd:\", simdEnabled, opts);\r\n \r\n // Create instance using constructor and set internal properties\r\n const instance = new ConvertBuddy(opts);\r\n instance.converter = converter;\r\n instance.simd = simdEnabled;\r\n instance.initialized = true;\r\n \r\n return instance;\r\n }\r\n\r\n push(chunk: Uint8Array): Uint8Array {\r\n if (this.aborted) {\r\n throw new Error(\"Conversion has been aborted\");\r\n }\r\n\r\n if (this.paused) {\r\n throw new Error(\"Conversion is paused. Call resume() before pushing more data.\");\r\n }\r\n\r\n if (this.debug) console.log(\"[convert-buddy-js] push\", chunk.byteLength);\r\n const output = this.converter.push(chunk);\r\n\r\n // Check if we should trigger progress callback\r\n if (this.onProgress) {\r\n const stats = this.stats();\r\n if (stats.bytesIn - this.lastProgressBytes >= this.progressIntervalBytes) {\r\n this.onProgress(stats);\r\n this.lastProgressBytes = stats.bytesIn;\r\n }\r\n }\r\n\r\n return output;\r\n }\r\n\r\n finish(): Uint8Array {\r\n if (this.aborted) {\r\n throw new Error(\"Conversion has been aborted\");\r\n }\r\n\r\n if (this.debug) console.log(\"[convert-buddy-js] finish\");\r\n const output = this.converter.finish();\r\n\r\n // Final progress callback\r\n if (this.onProgress) {\r\n this.onProgress(this.stats());\r\n }\r\n\r\n return output;\r\n }\r\n\r\n stats(): Stats {\r\n if (!this.converter || typeof this.converter.getStats !== 'function') {\r\n // Converter not initialized yet\r\n return {\r\n bytesIn: 0,\r\n bytesOut: 0,\r\n chunksIn: 0,\r\n recordsProcessed: 0,\r\n parseTimeMs: 0,\r\n transformTimeMs: 0,\r\n writeTimeMs: 0,\r\n maxBufferSize: 0,\r\n currentPartialSize: 0,\r\n throughputMbPerSec: 0,\r\n };\r\n }\r\n\r\n try {\r\n const wasmStats = this.converter.getStats();\r\n \r\n // WASM object properties are snake_case (Rust convention)\r\n // Access them directly as they're exposed via wasm_bindgen getters\r\n const stats = {\r\n bytesIn: wasmStats.bytes_in,\r\n bytesOut: wasmStats.bytes_out,\r\n chunksIn: wasmStats.chunks_in,\r\n recordsProcessed: wasmStats.records_processed,\r\n parseTimeMs: wasmStats.parse_time_ms,\r\n transformTimeMs: wasmStats.transform_time_ms,\r\n writeTimeMs: wasmStats.write_time_ms,\r\n maxBufferSize: wasmStats.max_buffer_size,\r\n currentPartialSize: wasmStats.current_partial_size,\r\n throughputMbPerSec: wasmStats.throughput_mb_per_sec,\r\n };\r\n\r\n // Warn if stats tracking is not enabled (profile: false)\r\n if (!this.profile && stats.bytesIn === 0 && stats.chunksIn === 0) {\r\n console.warn(\r\n \"[convert-buddy-js] WARNING: stats() called but profiling is disabled. \" +\r\n \"Enable profiling by setting { profile: true } in ConvertBuddy.create() options to get accurate statistics.\"\r\n );\r\n }\r\n\r\n return stats;\r\n } catch (e) {\r\n if (this.debug) console.error(\"[convert-buddy-js] Error getting stats:\", e);\r\n return {\r\n bytesIn: 0,\r\n bytesOut: 0,\r\n chunksIn: 0,\r\n recordsProcessed: 0,\r\n parseTimeMs: 0,\r\n transformTimeMs: 0,\r\n writeTimeMs: 0,\r\n maxBufferSize: 0,\r\n currentPartialSize: 0,\r\n throughputMbPerSec: 0,\r\n };\r\n }\r\n }\r\n\r\n abort(): void {\r\n this.aborted = true;\r\n if (this.debug) console.log(\"[convert-buddy-js] aborted\");\r\n }\r\n\r\n pause(): void {\r\n this.paused = true;\r\n if (this.debug) console.log(\"[convert-buddy-js] paused\");\r\n }\r\n\r\n resume(): void {\r\n this.paused = false;\r\n if (this.debug) console.log(\"[convert-buddy-js] resumed\");\r\n }\r\n\r\n isAborted(): boolean {\r\n return this.aborted;\r\n }\r\n\r\n isPaused(): boolean {\r\n return this.paused;\r\n }\r\n}\r\n\r\nasync function readSample(\r\n input: DetectInput,\r\n maxBytes = 256 * 1024\r\n): Promise<Uint8Array> {\r\n if (typeof input === \"string\") {\r\n const encoded = new TextEncoder().encode(input);\r\n return encoded.length > maxBytes ? encoded.slice(0, maxBytes) : encoded;\r\n }\r\n\r\n if (input instanceof Uint8Array) {\r\n return input.length > maxBytes ? input.slice(0, maxBytes) : input;\r\n }\r\n\r\n if (input instanceof ArrayBuffer) {\r\n const bytes = new Uint8Array(input as ArrayBuffer);\r\n return bytes.length > maxBytes ? bytes.slice(0, maxBytes) : bytes;\r\n }\r\n\r\n if (isReadableStream(input)) {\r\n const reader = input.getReader();\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n\r\n while (total < maxBytes) {\r\n const { value, done } = await reader.read();\r\n if (done || !value) break;\r\n const slice = total + value.length > maxBytes\r\n ? value.slice(0, maxBytes - total)\r\n : value;\r\n chunks.push(slice);\r\n total += slice.length;\r\n }\r\n\r\n if (total >= maxBytes) {\r\n await reader.cancel();\r\n }\r\n\r\n return concatChunks(chunks, total);\r\n }\r\n\r\n const chunks: Uint8Array[] = [];\r\n let total = 0;\r\n\r\n for await (const chunk of input as AsyncIterable<Uint8Array>) {\r\n if (total >= maxBytes) {\r\n break;\r\n }\r\n const data = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);\r\n const slice = total + data.length > maxBytes\r\n ? data.slice(0, maxBytes - total)\r\n : data;\r\n chunks.push(slice);\r\n total += slice.length;\r\n }\r\n\r\n return concatChunks(chunks, total);\r\n}\r\n\r\nfunction concatChunks(chunks: Uint8Array[], total: number): Uint8Array {\r\n const result = new Uint8Array(total);\r\n let offset = 0;\r\n for (const chunk of chunks) {\r\n result.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n return result;\r\n}\r\n\r\nfunction isReadableStream(\r\n input: DetectInput\r\n): input is ReadableStream<Uint8Array> {\r\n return typeof (input as ReadableStream<Uint8Array>)?.getReader === \"function\";\r\n}\r\n\r\nasync function loadDetectionWasm(debug: boolean): Promise<WasmModule> {\r\n const wasmModule = await loadWasmModule();\r\n if (typeof wasmModule.default === \"function\") {\r\n await (wasmModule.default as () => Promise<void>)();\r\n }\r\n wasmModule.init(debug);\r\n return wasmModule;\r\n}\r\n\r\nexport async function detectFormat(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<Format | \"unknown\"> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n const format = wasmModule.detectFormat?.(sample);\r\n return (format as Format) ?? \"unknown\";\r\n}\r\n\r\nexport async function detectStructure(\r\n input: DetectInput,\r\n formatHint?: Format,\r\n opts: DetectOptions = {}\r\n): Promise<StructureDetection | null> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n const result = wasmModule.detectStructure?.(sample, formatHint);\r\n return result ?? null;\r\n}\r\n\r\n// Backward compatibility functions - these now use the unified detectStructure internally\r\nexport async function detectCsvFieldsAndDelimiter(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<CsvDetection | null> {\r\n const structure = await detectStructure(input, \"csv\", opts);\r\n if (structure && structure.format === \"csv\" && structure.delimiter) {\r\n return {\r\n delimiter: structure.delimiter,\r\n fields: structure.fields,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\nexport async function detectXmlElements(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<XmlDetection | null> {\r\n const structure = await detectStructure(input, \"xml\", opts);\r\n if (structure && structure.format === \"xml\") {\r\n return {\r\n elements: structure.fields,\r\n recordElement: structure.recordElement,\r\n };\r\n }\r\n return null;\r\n}\r\n\r\n// Helper to auto-detect format and CSV/XML configuration from sample data\r\nexport async function autoDetectConfig(\r\n input: DetectInput,\r\n opts: DetectOptions = {}\r\n): Promise<{ \r\n format: Format | \"unknown\"; \r\n csvConfig?: CsvConfig;\r\n xmlConfig?: XmlConfig;\r\n}> {\r\n const wasmModule = await loadDetectionWasm(!!opts.debug);\r\n const sample = await readSample(input, opts.maxBytes);\r\n \r\n const format = (wasmModule.detectFormat?.(sample) as Format) ?? \"unknown\";\r\n \r\n const result: { format: Format | \"unknown\"; csvConfig?: CsvConfig; xmlConfig?: XmlConfig } = { format };\r\n \r\n if (format === \"csv\") {\r\n const csvDetection = wasmModule.detectCsvFields?.(sample);\r\n if (csvDetection) {\r\n result.csvConfig = {\r\n delimiter: csvDetection.delimiter,\r\n hasHeaders: csvDetection.fields.length > 0,\r\n };\r\n }\r\n } else if (format === \"xml\") {\r\n const xmlDetection = wasmModule.detectXmlElements?.(sample);\r\n if (xmlDetection?.recordElement) {\r\n result.xmlConfig = {\r\n recordElement: xmlDetection.recordElement,\r\n };\r\n }\r\n }\r\n \r\n return result;\r\n}\r\n\r\n// Web Streams TransformStream adapter\r\nexport class ConvertBuddyTransformStream extends TransformStream<Uint8Array, Uint8Array> {\r\n constructor(opts: ConvertBuddyOptions = {}) {\r\n let buddy: ConvertBuddy | null = null;\r\n\r\n super({\r\n async start(controller) {\r\n buddy = await ConvertBuddy.create(opts);\r\n },\r\n\r\n transform(chunk, controller) {\r\n if (!buddy) {\r\n throw new Error(\"ConvertBuddy not initialized\");\r\n }\r\n\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n controller.enqueue(output);\r\n }\r\n },\r\n\r\n flush(controller) {\r\n if (!buddy) {\r\n return;\r\n }\r\n\r\n const output = buddy.finish();\r\n if (output.length > 0) {\r\n controller.enqueue(output);\r\n }\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n },\r\n });\r\n }\r\n}\r\n\r\n// Utility: Convert entire buffer/string\r\nexport async function convert(\r\n input: Uint8Array | string,\r\n opts: ConvertBuddyOptions = {}\r\n): Promise<Uint8Array> {\r\n try {\r\n const inputBytes = typeof input === \"string\" \r\n ? new TextEncoder().encode(input)\r\n : input;\r\n\r\n // If inputFormat is not specified or is \"auto\", perform auto-detection\r\n let actualOpts = opts;\r\n if (!opts.inputFormat || opts.inputFormat === \"auto\") {\r\n const sample = inputBytes.length > 256 * 1024 \r\n ? inputBytes.slice(0, 256 * 1024) \r\n : inputBytes;\r\n \r\n const detected = await autoDetectConfig(sample, { debug: opts.debug });\r\n \r\n if (detected.format !== \"unknown\") {\r\n actualOpts = { ...opts, inputFormat: detected.format as Format };\r\n \r\n if (detected.csvConfig && !opts.csvConfig) {\r\n actualOpts.csvConfig = detected.csvConfig;\r\n }\r\n \r\n if (detected.xmlConfig && !opts.xmlConfig) {\r\n actualOpts.xmlConfig = detected.xmlConfig;\r\n }\r\n \r\n if (opts.debug) {\r\n console.log(\"[convert-buddy] Auto-detected format:\", detected.format);\r\n }\r\n } else {\r\n throw new Error(\"Could not auto-detect input format. Please specify inputFormat explicitly.\");\r\n }\r\n }\r\n\r\n const buddy = await ConvertBuddy.create(actualOpts);\r\n\r\n const output = buddy.push(inputBytes);\r\n const final = buddy.finish();\r\n\r\n // Combine outputs\r\n const result = new Uint8Array(output.length + final.length);\r\n result.set(output, 0);\r\n result.set(final, output.length);\r\n\r\n if (opts.profile) {\r\n const stats = buddy.stats();\r\n console.log(\"[convert-buddy] Performance Stats:\", stats);\r\n }\r\n\r\n return result;\r\n } catch (err: any) {\r\n // Normalize non-Error throws (e.g., wasm JsValue) into Error with message\r\n if (err instanceof Error) throw err;\r\n try {\r\n // Try to stringify common structures\r\n const msg = typeof err === 'string' ? err : (err && err.message) ? err.message : JSON.stringify(err);\r\n throw new Error(String(msg));\r\n } catch (_) {\r\n throw new Error(String(err));\r\n }\r\n }\r\n}\r\n\r\n// Utility: Convert and return as string\r\nexport async function convertToString(\r\n input: Uint8Array | string,\r\n opts: ConvertBuddyOptions = {}\r\n): Promise<string> {\r\n const result = await convert(input, opts);\r\n return decodeUtf8(result);\r\n}\r\n\r\n/**\r\n * Ultra-simple standalone convert function with auto-detection.\r\n * Accepts any input type (URL, File, Buffer, string, stream) and automatically detects format.\r\n * \r\n * @example\r\n * // From URL\r\n * import { convertAny } from \"convert-buddy-js\";\r\n * const result = await convertAny(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * \r\n * @example\r\n * // From File (browser)\r\n * const file = fileInput.files[0];\r\n * const result = await convertAny(file, { outputFormat: \"ndjson\" });\r\n * \r\n * @example\r\n * // From string data\r\n * const result = await convertAny('{\"name\":\"Ada\"}', { outputFormat: \"csv\" });\r\n */\r\nexport async function convertAny(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n): Promise<Uint8Array> {\r\n const buddy = new ConvertBuddy();\r\n return buddy.convert(input, opts);\r\n}\r\n\r\n/**\r\n * Ultra-simple standalone convert function that returns a string.\r\n * Same as convertAny but decodes the output to a string.\r\n * \r\n * @example\r\n * import { convertAnyToString } from \"convert-buddy-js\";\r\n * const json = await convertAnyToString(\"https://example.com/data.csv\", { outputFormat: \"json\" });\r\n * console.log(JSON.parse(json));\r\n */\r\nexport async function convertAnyToString(\r\n input: string | Uint8Array | File | Blob | ReadableStream<Uint8Array>,\r\n opts: ConvertOptions\r\n): Promise<string> {\r\n const result = await convertAny(input, opts);\r\n return decodeUtf8(result);\r\n}\r\n\r\n// ===== Helper Functions =====\r\n// File format utilities for common use cases\r\n\r\n/**\r\n * Get MIME type for a given format\r\n * \r\n * @example\r\n * const mimeType = getMimeType(\"json\"); // \"application/json\"\r\n */\r\nexport function getMimeType(format: Format): string {\r\n switch (format) {\r\n case \"json\":\r\n return \"application/json\";\r\n case \"ndjson\":\r\n return \"application/x-ndjson\";\r\n case \"csv\":\r\n return \"text/csv\";\r\n case \"xml\":\r\n return \"application/xml\";\r\n }\r\n}\r\n\r\n/**\r\n * Get file extension for a given format (without the dot)\r\n * \r\n * @example\r\n * const ext = getExtension(\"json\"); // \"json\"\r\n */\r\nexport function getExtension(format: Format): string {\r\n return format;\r\n}\r\n\r\n/**\r\n * Get suggested filename for a converted file\r\n * \r\n * @param originalName - Original filename\r\n * @param outputFormat - Target format\r\n * @param includeTimestamp - Whether to include a timestamp (default: false)\r\n * \r\n * @example\r\n * const name = getSuggestedFilename(\"data.csv\", \"json\"); // \"data.json\"\r\n * const name = getSuggestedFilename(\"data.csv\", \"json\", true); // \"data_converted_1234567890.json\"\r\n */\r\nexport function getSuggestedFilename(\r\n originalName: string,\r\n outputFormat: Format,\r\n includeTimestamp = false\r\n): string {\r\n const baseName = originalName.replace(/\\.[^/.]+$/, \"\");\r\n const extension = getExtension(outputFormat);\r\n \r\n if (includeTimestamp) {\r\n return `${baseName}_converted_${Date.now()}.${extension}`;\r\n }\r\n \r\n return `${baseName}.${extension}`;\r\n}\r\n\r\n/**\r\n * Get File System Access API file type configuration for showSaveFilePicker\r\n * \r\n * @example\r\n * const types = getFileTypeConfig(\"json\");\r\n * const handle = await showSaveFilePicker({ types });\r\n */\r\nexport function getFileTypeConfig(format: Format): Array<{\r\n description: string;\r\n accept: Record<string, string[]>;\r\n}> {\r\n const mimeType = getMimeType(format);\r\n const extension = `.${getExtension(format)}`;\r\n \r\n return [\r\n {\r\n description: `${format.toUpperCase()} Files`,\r\n accept: { [mimeType]: [extension] },\r\n },\r\n ];\r\n}\r\n\r\n// WASM Threading Capabilities API\r\n/**\r\n * Check if WASM threading is supported in the current environment\r\n * @returns true if SharedArrayBuffer and Atomics are available (required for WASM threads)\r\n */\r\nexport function isWasmThreadingSupported(): boolean {\r\n return detectWasmThreadingSupport();\r\n}\r\n\r\n/**\r\n * Get the optimal number of worker threads based on CPU cores and WASM capabilities\r\n * @returns Recommended thread count for optimal performance\r\n */\r\nexport function getOptimalThreadCount(): number {\r\n const cores = typeof navigator !== 'undefined' \r\n ? (navigator.hardwareConcurrency || 4)\r\n : (process?.env?.UV_THREADPOOL_SIZE ? parseInt(process.env.UV_THREADPOOL_SIZE) : \r\n typeof require !== 'undefined' ? require('os').cpus().length : 4);\r\n \r\n // For current JavaScript-level parallelism, limit to 4 parallel instances\r\n // When true WASM threading is enabled, this can be increased\r\n return Math.min(cores, 4);\r\n}\r\n\r\n/**\r\n * Get current threading capabilities and configuration\r\n * @returns Object with threading information\r\n */\r\nexport function getThreadingInfo(): {\r\n wasmThreadingSupported: boolean;\r\n customThreadPoolAvailable: boolean;\r\n nodejsWasmThreading: boolean;\r\n recommendedThreads: number;\r\n currentThreads: number;\r\n approach: string;\r\n} {\r\n const isNodejs = typeof process !== 'undefined';\r\n \r\n return {\r\n wasmThreadingSupported: detectWasmThreadingSupport(),\r\n customThreadPoolAvailable: typeof window !== 'undefined',\r\n nodejsWasmThreading: isNodejs && !!nodejsThreadPool,\r\n recommendedThreads: getOptimalThreadCount(),\r\n currentThreads: isNodejs ? \r\n (nodejsThreadPool ? nodejsThreadPool.workers?.length || 0 : 0) :\r\n (threadPool ? threadPool.workers?.length || 0 : 0),\r\n approach: isNodejs ? 'nodejs_enhanced_threading' : 'browser_custom_threading'\r\n };\r\n}\r\n\r\n/**\r\n * Backward compatibility alias for ConvertBuddy\r\n * @deprecated Use ConvertBuddy instead\r\n */\r\nexport const Converter = ConvertBuddy;\r\n"],"mappings":"AAuIA,IAAI,qBAAwC;AAC5C,IAAI,wBAAoD;AACxD,IAAI,yBAAyB;AAC7B,IAAI,aAAkB;AACtB,IAAI,mBAAwB;AAC5B,MAAM,cAAc,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAE7E,SAAS,WAAW,OAA2B;AAC7C,SAAO,YAAY,OAAO,KAAK;AACjC;AAGA,SAAS,6BAAsC;AAC7C,MAAI,OAAO,sBAAsB,aAAa;AAC5C,WAAO;AAAA,EACT;AAGA,MAAI;AACF,QAAI,kBAAkB,CAAC;AACvB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AACF;AAEA,eAAe,iBAAsC;AAEnD,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AAGA,MAAI,uBAAuB;AACzB,WAAO;AAAA,EACT;AAGA,2BAAyB,YAAY;AACnC,UAAM,SACJ,OAAO,YAAY,eACnB,CAAC,CAAE,QAAgB,UAAU;AAG/B,6BAAyB,2BAA2B;AAEpD,QAAI,QAAQ;AACV,YAAM,aAAkB,MAAM;AAAA;AAAA,QAAiC;AAAA,MAAa;AAC5E,YAAM,gBAAgB,WAAW;AACjC,YAAM,YAAY,gBAAgB,cAAc,YAAY,GAAG,IAAK,OAAe,gBAAgB,YAAY,GAAG;AAClH,YAAMA,WAAU,aAAc,WAAmB;AACjD,YAAMC,OAAMD,SAAQ,kBAAkB;AACtC,aAAOC;AAAA,IACT;AAEA,UAAM,MAAO,MAAM,OAAO,8BAA8B;AAGxD,QAAI,0BAA2B,IAAY,gBAAgB;AACzD,UAAI;AACF,cAAM,aAAa,KAAK,IAAI,UAAU,uBAAuB,GAAG,CAAC;AACjE,cAAO,IAAY,eAAe,UAAU;AAC5C,gBAAQ,IAAI,mDAAmD,UAAU,UAAU;AAAA,MACrF,SAAS,GAAG;AACV,gBAAQ,KAAK,8EAA8E,CAAC;AAC5F,iCAAyB;AAAA,MAC3B;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG;AAEH,MAAI;AACF,yBAAqB,MAAM;AAC3B,WAAO;AAAA,EACT,UAAE;AACA,4BAAwB;AAAA,EAC1B;AACF;AAEA,IAAI,kBAAkB;AACtB,IAAI,kBAAwC;AAE5C,eAAe,SAAS,OAA+B;AAErD,MAAI,iBAAiB;AACnB;AAAA,EACF;AAGA,MAAI,iBAAiB;AACnB,WAAO;AAAA,EACT;AAGA,qBAAmB,YAAY;AAC7B,UAAM,aAAa,MAAM,eAAe;AAExC,QAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,YAAO,WAAW,QAAgC;AAAA,IACpD;AAEA,eAAW,KAAK,KAAK;AAKrB,sBAAkB;AAAA,EACpB,GAAG;AAEH,MAAI;AACF,UAAM;AAAA,EACR,UAAE;AACA,sBAAkB;AAAA,EACpB;AACF;AAEO,MAAM,aAAa;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAmB;AAAA,EACnB,SAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA,oBAA4B;AAAA,EAC5B;AAAA,EACA,cAAuB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUP,YAAY,OAA4B,CAAC,GAAG;AAE1C,SAAK,QAAQ,CAAC,CAAC,KAAK;AACpB,SAAK,UAAU,CAAC,CAAC,KAAK;AACtB,SAAK,OAAO;AACZ,SAAK,eAAe;AACpB,SAAK,wBAAwB,KAAK,yBAAyB,OAAO;AAClE,SAAK,aAAa,KAAK;AACvB,SAAK,YAAY;AACjB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,QACJ,OACA,MACqB;AAErB,UAAM,aAAkC;AAAA,MACtC,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,MACH,aAAa,KAAK,eAAe,KAAK,aAAa,eAAe;AAAA,IACpE;AAGA,QAAI,OAAO,UAAU,UAAU;AAE7B,UAAI,MAAM,WAAW,SAAS,KAAK,MAAM,WAAW,UAAU,GAAG;AAE/D,eAAO,KAAK,eAAe,OAAO,UAAU;AAAA,MAC9C,OAAO;AAEL,eAAO,KAAK,kBAAkB,OAAO,UAAU;AAAA,MACjD;AAAA,IACF,WAAW,iBAAiB,YAAY;AACtC,aAAO,KAAK,kBAAkB,OAAO,UAAU;AAAA,IACjD,WAAW,OAAO,SAAS,eAAe,iBAAiB,MAAM;AAC/D,aAAO,KAAK,gBAAgB,OAAe,UAAU;AAAA,IACvD,WAAW,OAAO,SAAS,eAAe,iBAAiB,MAAM;AAC/D,aAAO,KAAK,gBAAgB,OAAe,UAAU;AAAA,IACvD,WAAW,OAAO,mBAAmB,eAAe,iBAAiB,gBAAgB;AACnF,aAAO,KAAK,kBAAkB,OAAO,UAAU;AAAA,IACjD,OAAO;AACL,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,KAAa,MAAgD;AACxF,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,mBAAmB,GAAG,KAAK,SAAS,UAAU,EAAE;AAAA,IAClE;AAEA,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AAEA,WAAO,KAAK,kBAAkB,QAAQ,IAAI;AAAA,EAC5C;AAAA,EAEA,MAAc,kBAAkB,OAAe,MAAgD;AAC7F,UAAM,aAAa,IAAI,YAAY,EAAE,OAAO,KAAK;AACjD,WAAO,KAAK,kBAAkB,YAAY,IAAI;AAAA,EAChD;AAAA,EAEA,MAAc,kBAAkB,OAAmB,MAAgD;AAEjG,UAAM,mBAAmB,0BAA0B,MAAM,SAAS,MAAM;AAExE,QAAI,oBAAoB,KAAK,OAAO;AAClC,cAAQ,IAAI,8DAA8D;AAAA,IAC5E;AAGA,QAAI,CAAC,oBAAoB,KAAK,eAAe,KAAK,cAAc,KAAK,MAAM,SAAS,MAAM,MAAM;AAC9F,UAAI,KAAK,OAAO;AACd,gBAAQ,IAAI,4EAA4E;AAAA,MAC1F;AACA,aAAO,KAAK,0BAA0B,OAAO,IAAI;AAAA,IACnD;AAGA,QAAI,aAAa,EAAE,GAAG,KAAK;AAE3B,QAAI,KAAK,gBAAgB,QAAQ;AAC/B,YAAM,WAAW,MAAM,iBAAiB,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;AAEpE,UAAI,SAAS,WAAW,WAAW;AACjC,mBAAW,cAAc,SAAS;AAElC,YAAI,SAAS,WAAW;AACtB,qBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,QAClG;AAEA,YAAI,SAAS,WAAW;AACtB,qBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,QAClG;AAEA,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,yCAAyC,SAAS,MAAM;AAAA,QACtE;AAAA,MACF,OAAO;AACL,cAAM,IAAI,MAAM,4EAA4E;AAAA,MAC9F;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,OAAO,UAAU;AAClD,UAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,UAAM,QAAQ,MAAM,OAAO;AAG3B,UAAM,SAAS,IAAI,WAAW,OAAO,SAAS,MAAM,MAAM;AAC1D,WAAO,IAAI,QAAQ,CAAC;AACpB,WAAO,IAAI,OAAO,OAAO,MAAM;AAE/B,QAAI,KAAK,SAAS;AAChB,YAAM,QAAQ,MAAM,MAAM;AAC1B,cAAQ,IAAI,sCAAsC,KAAK;AAAA,IACzD;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,0BAA0B,OAAmB,MAAgD;AAEzG,UAAM,oBAAoB,MAAM;AAChC,UAAM,iBAAiB,KAAK;AAAA,MAC1B,OAAO,cAAc,eAAe,UAAU,sBAC1C,UAAU,sBACV;AAAA,MACJ;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,qBAAqB,CAAC,KAAK,eAAe,KAAK,cAAc,GAAG;AACjF,aAAO,KAAK,kBAAkB,OAAO,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;AAAA,IAClE;AAGA,UAAM,uBAAuB;AAAA,MAC3B,EAAE,OAAO,OAAO,QAAQ,SAAS;AAAA,MACjC,EAAE,OAAO,OAAO,QAAQ,OAAO;AAAA,MAC/B,EAAE,OAAO,UAAU,QAAQ,OAAO;AAAA,MAClC,EAAE,OAAO,UAAU,QAAQ,MAAM;AAAA,MACjC,EAAE,OAAO,UAAU,QAAQ,SAAS;AAAA;AAAA,MACpC,EAAE,OAAO,QAAQ,QAAQ,SAAS;AAAA,MAClC,EAAE,OAAO,QAAQ,QAAQ,MAAM;AAAA,IACjC;AAEA,UAAM,cAAc,qBAAqB;AAAA,MACvC,UAAQ,KAAK,UAAU,KAAK,eAAe,KAAK,WAAW,KAAK;AAAA,IAClE;AAEA,QAAI,CAAC,aAAa;AAChB,UAAI,KAAK,OAAO;AACd,gBAAQ,IAAI,yDAAyD,KAAK,WAAW,WAAM,KAAK,YAAY,oBAAoB;AAAA,MAClI;AACA,aAAO,KAAK,kBAAkB,OAAO,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;AAAA,IAClE;AAEA,QAAI;AACF,YAAM,gBAAgB,KAAK,IAAI,gBAAgB,KAAK,eAAe,cAAc;AACjF,YAAM,WAAW,OAAO,WAAW,eAAe,OAAO,YAAY;AAErE,UAAI,KAAK,OAAO;AACd,cAAM,gBAAgB,WAAW,2BAA2B;AAC5D,gBAAQ,IAAI,yBAAyB,aAAa,SAAS,aAAa,UAAU;AAAA,MACpF;AAEA,UAAI,UAAU;AAEZ,YAAI,CAAC,kBAAkB;AACrB,cAAI;AACF,kBAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,sBAAsB;AAChE,+BAAmB,IAAI,iBAAiB;AAAA,cACtC,YAAY;AAAA,cACZ,UAAU;AAAA,YACZ,CAAC;AACD,kBAAM,iBAAiB,WAAW;AAAA,UACpC,SAAS,OAAO;AACd,gBAAI,KAAK,OAAO;AACd,sBAAQ,IAAI,8EAA8E,KAAK;AAAA,YACjG;AAAA,UACF;AAAA,QACF;AAEA,YAAI,kBAAkB;AACpB,iBAAO,KAAK,6BAA6B,OAAO,MAAM,aAAa;AAAA,QACrE,OAAO;AACL,iBAAO,KAAK,0BAA0B,OAAO,MAAM,aAAa;AAAA,QAClE;AAAA,MACF,OAAO;AAEL,YAAI,CAAC,YAAY;AACf,gBAAM,EAAE,eAAe,IAAI,MAAM,OAAO,eAAe;AACvD,uBAAa,IAAI,eAAe;AAAA,YAC9B,YAAY;AAAA,YACZ,UAAU;AAAA,UACZ,CAAC;AACD,gBAAM,WAAW,WAAW;AAAA,QAC9B;AAEA,eAAO,KAAK,2BAA2B,OAAO,MAAM,aAAa;AAAA,MACnE;AAAA,IAEF,SAAS,OAAO;AACd,UAAI,KAAK,OAAO;AACd,gBAAQ,KAAK,2EAA2E,KAAK;AAAA,MAC/F;AACA,aAAO,KAAK,kBAAkB,OAAO,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,MAAc,6BAA6B,OAAmB,MAA2B,YAAyC;AAChI,UAAM,EAAE,iBAAiB,mBAAmB,IAAI,MAAM,OAAO,sBAAsB;AAEnF,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,+DAA+D,UAAU,WAAW;AAAA,IAClG;AAGA,UAAM,SAAS,gBAAgB,OAAO,UAAU;AAGhD,UAAM,UAAU,MAAM,iBAAkB,cAAc,WAAW,QAAQ;AAAA,MACvE,cAAc,KAAK;AAAA,MACnB,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB,OAAO;AAAA;AAAA,IACT,CAAC;AAGD,UAAM,SAAS,mBAAmB,SAAS,KAAK,YAAa;AAE7D,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,gDAAgD,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ;AAAA,IAC5G;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,2BAA2B,OAAmB,MAA2B,YAAyC;AAC9H,UAAM,WAAW,WAAW,KAAK;AAGjC,UAAM,EAAE,WAAW,aAAa,IAAI,MAAM,OAAO,eAAe;AAChE,UAAM,SAAS,UAAU,UAAU,UAAU;AAE7C,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,8BAA8B,OAAO,MAAM,+BAA+B;AAAA,IACxF;AAGA,QAAI;AACJ,UAAM,aAAa,GAAG,KAAK,WAAW,OAAO,KAAK,YAAY;AAC9D,YAAQ,YAAY;AAAA,MAClB,KAAK;AAAA,MACL,KAAK;AACH,iBAAS;AACT;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,iBAAS;AACT;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,iBAAS;AACT;AAAA,MACF;AACE,cAAM,IAAI,MAAM,2BAA2B,UAAU,EAAE;AAAA,IAC3D;AAGA,UAAM,UAAU,MAAM,WAAY,cAAc,QAAQ,QAAQ;AAAA,MAC9D,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK;AAAA,IACd,CAAC;AAGD,UAAM,SAAS,aAAa,SAAS,KAAK,YAAa;AACvD,WAAO,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,EACxC;AAAA,EAEA,MAAc,0BAA0B,OAAmB,MAA2B,YAAyC;AAE7H,UAAM,SAAS,KAAK,gBAAgB,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAE7F,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,8BAA8B,OAAO,MAAM,mCAAmC;AAAA,IAC5F;AAGA,UAAM,gBAAgB,OAAO,IAAI,OAAO,OAAO,UAAU;AACvD,YAAM,YAAY,EAAE,GAAG,MAAM,aAAa,EAAE;AAG5C,UAAI,KAAK,gBAAgB,SAAS,QAAQ,GAAG;AAC3C,kBAAU,YAAY,EAAE,GAAG,UAAU,WAAW,YAAY,MAAM;AAAA,MACpE;AAEA,YAAM,aAAa,MAAM,aAAa,OAAO,SAAS;AACtD,YAAM,SAAS,WAAW,KAAK,KAAK;AACpC,YAAM,QAAQ,WAAW,OAAO;AAGhC,YAAMC,UAAS,IAAI,WAAW,OAAO,SAAS,MAAM,MAAM;AAC1D,MAAAA,QAAO,IAAI,QAAQ,CAAC;AACpB,MAAAA,QAAO,IAAI,OAAO,OAAO,MAAM;AAE/B,aAAOA;AAAA,IACT,CAAC;AAED,UAAM,eAAe,MAAM,QAAQ,IAAI,aAAa;AAGpD,UAAM,cAAc,aAAa,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAC7E,UAAM,SAAS,IAAI,WAAW,WAAW;AAEzC,QAAI,SAAS;AACb,eAAW,SAAS,cAAc;AAChC,aAAO,IAAI,OAAO,MAAM;AACxB,gBAAU,MAAM;AAAA,IAClB;AAEA,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,qDAAqD,OAAO,MAAM,YAAY,WAAW,QAAQ;AAAA,IAC/G;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,OAAmB,iBAAuC;AAChF,QAAI,MAAM,UAAU,iBAAiB;AACnC,aAAO,CAAC,KAAK;AAAA,IACf;AAEA,UAAM,SAAuB,CAAC;AAC9B,QAAI,QAAQ;AAEZ,WAAO,QAAQ,MAAM,QAAQ;AAC3B,UAAI,MAAM,KAAK,IAAI,QAAQ,iBAAiB,MAAM,MAAM;AAGxD,UAAI,MAAM,MAAM,QAAQ;AACtB,cAAM,cAAc,KAAK,IAAI,QAAQ,kBAAkB,MAAM,KAAK;AAClE,cAAM,YAAY,KAAK,IAAI,MAAM,MAAM,MAAM,MAAM;AAGnD,iBAAS,IAAI,YAAY,GAAG,KAAK,aAAa,KAAK;AACjD,cAAI,MAAM,CAAC,MAAM,IAAM;AACrB,kBAAM,IAAI;AACV;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO,KAAK,MAAM,MAAM,OAAO,GAAG,CAAC;AACnC,cAAQ;AAAA,IACV;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBAAgB,MAAY,MAAgD;AACxF,WAAO,KAAK,gBAAgB,MAAM,IAAI;AAAA,EACxC;AAAA,EAEA,MAAc,gBAAgB,MAAY,MAAgD;AAExF,QAAI,aAAa,EAAE,GAAG,KAAK;AAE3B,QAAI,KAAK,gBAAgB,QAAQ;AAC/B,YAAM,aAAa,MAAM;AACzB,YAAM,aAAa,KAAK,MAAM,GAAG,UAAU;AAC3C,YAAM,eAAe,MAAM,WAAW,YAAY;AAClD,YAAM,SAAS,IAAI,WAAW,YAA2B;AAEzD,YAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAErE,UAAI,SAAS,WAAW,WAAW;AACjC,mBAAW,cAAc,SAAS;AAElC,YAAI,SAAS,WAAW;AACtB,qBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,QAClG;AAEA,YAAI,SAAS,WAAW;AACtB,qBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,QAClG;AAEA,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,yCAAyC,SAAS,MAAM;AAAA,QACtE;AAAA,MACF,OAAO;AACL,cAAM,IAAI,MAAM,4EAA4E;AAAA,MAC9F;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,OAAO,UAAU;AAGlD,UAAM,SAAS,KAAK,OAAO;AAC3B,UAAM,SAAS,OAAO,UAAU;AAEhC,UAAM,UAAwB,CAAC;AAE/B,QAAI;AACF,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,cAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,YAAI,OAAO,SAAS,GAAG;AACrB,kBAAQ,KAAK,MAAM;AAAA,QACrB;AAAA,MACF;AAEA,YAAM,QAAQ,MAAM,OAAO;AAC3B,UAAI,MAAM,SAAS,GAAG;AACpB,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAGA,YAAM,cAAc,QAAQ,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;AACpE,YAAM,SAAS,IAAI,WAAW,WAAW;AACzC,UAAI,SAAS;AACb,iBAAW,UAAU,SAAS;AAC5B,eAAO,IAAI,QAAQ,MAAM;AACzB,kBAAU,OAAO;AAAA,MACnB;AAEA,UAAI,KAAK,SAAS;AAChB,cAAM,QAAQ,MAAM,MAAM;AAC1B,gBAAQ,IAAI,sCAAsC,KAAK;AAAA,MACzD;AAEA,aAAO;AAAA,IACT,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,QAAoC,MAAgD;AAElH,QAAI,aAAa,EAAE,GAAG,KAAK;AAC3B,QAAI,cAA4B,CAAC;AACjC,QAAI,mBAAmB;AACvB,UAAM,gBAAgB,MAAM;AAE5B,QAAI,KAAK,gBAAgB,QAAQ;AAC/B,YAAMC,UAAS,OAAO,UAAU;AAEhC,UAAI;AACF,eAAO,mBAAmB,eAAe;AACvC,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAMA,QAAO,KAAK;AAC1C,cAAI,QAAQ,CAAC,MAAO;AAEpB,sBAAY,KAAK,KAAK;AACtB,8BAAoB,MAAM;AAAA,QAC5B;AAGA,cAAM,SAAS,IAAI,WAAW,gBAAgB;AAC9C,YAAI,SAAS;AACb,mBAAW,SAAS,aAAa;AAC/B,iBAAO,IAAI,OAAO,MAAM;AACxB,oBAAU,MAAM;AAAA,QAClB;AAEA,cAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAErE,YAAI,SAAS,WAAW,WAAW;AACjC,qBAAW,cAAc,SAAS;AAElC,cAAI,SAAS,WAAW;AACtB,uBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,UAClG;AAEA,cAAI,SAAS,WAAW;AACtB,uBAAW,YAAY,KAAK,YAAY,EAAE,GAAG,SAAS,WAAW,GAAG,KAAK,UAAU,IAAI,SAAS;AAAA,UAClG;AAEA,cAAI,KAAK,OAAO;AACd,oBAAQ,IAAI,yCAAyC,SAAS,MAAM;AAAA,UACtE;AAAA,QACF,OAAO;AACL,gBAAM,IAAI,MAAM,4EAA4E;AAAA,QAC9F;AAAA,MACF,UAAE;AACA,QAAAA,QAAO,YAAY;AAAA,MACrB;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,OAAO,UAAU;AAGlD,UAAM,UAAwB,CAAC;AAC/B,eAAW,SAAS,aAAa;AAC/B,YAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,UAAI,OAAO,SAAS,GAAG;AACrB,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,UAAU;AAEhC,QAAI;AACF,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,cAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,YAAI,OAAO,SAAS,GAAG;AACrB,kBAAQ,KAAK,MAAM;AAAA,QACrB;AAAA,MACF;AAEA,YAAM,QAAQ,MAAM,OAAO;AAC3B,UAAI,MAAM,SAAS,GAAG;AACpB,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAGA,YAAM,cAAc,QAAQ,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;AACpE,YAAM,SAAS,IAAI,WAAW,WAAW;AACzC,UAAI,SAAS;AACb,iBAAW,UAAU,SAAS;AAC5B,eAAO,IAAI,QAAQ,MAAM;AACzB,kBAAU,OAAO;AAAA,MACnB;AAEA,UAAI,KAAK,SAAS;AAChB,cAAM,QAAQ,MAAM,MAAM;AAC1B,gBAAQ,IAAI,sCAAsC,KAAK;AAAA,MACzD;AAEA,aAAO;AAAA,IACT,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,OAAO,OAA4B,CAAC,GAA0B;AACzE,UAAM,QAAQ,CAAC,CAAC,KAAK;AACrB,UAAM,UAAU,CAAC,CAAC,KAAK;AAGvB,UAAM,SAAS,KAAK;AAEpB,UAAM,aAAa,MAAM,eAAe;AAGxC,QAAI,cAAc,KAAK;AACvB,QAAI,YAAY,KAAK;AAIrB,QAAI,gBAAgB,QAAQ;AAE1B,oBAAc;AAAA,IAChB;AAKA,UAAM,mBAAmB,KAAK,oBAAqB,MAAM;AAEzD,QAAI;AACJ,QAAI,eAAe,KAAK,cAAc;AAEpC,YAAMC,aAAa,WAAmB;AACtC,kBAAYA,WAAU;AAAA,QACpB;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA;AAAA;AAAA,QACA,aAAa;AAAA,QACb,KAAK,aAAa;AAAA,QAClB,KAAK,aAAa;AAAA,MACpB;AAAA,IACF,OAAO;AACL,kBAAY,IAAI,WAAW,UAAU,KAAK;AAAA,IAC5C;AAGA,UAAM,cAAe,WAAmB,iBAAiB,KAAK;AAE9D,QAAI,MAAO,SAAQ,IAAI,yDAAyD,kBAAkB,SAAS,aAAa,IAAI;AAG5H,UAAM,WAAW,IAAI,aAAa,IAAI;AACtC,aAAS,YAAY;AACrB,aAAS,OAAO;AAChB,aAAS,cAAc;AAEvB,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,OAA+B;AAClC,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,+DAA+D;AAAA,IACjF;AAEA,QAAI,KAAK,MAAO,SAAQ,IAAI,2BAA2B,MAAM,UAAU;AACvE,UAAM,SAAS,KAAK,UAAU,KAAK,KAAK;AAGxC,QAAI,KAAK,YAAY;AACnB,YAAM,QAAQ,KAAK,MAAM;AACzB,UAAI,MAAM,UAAU,KAAK,qBAAqB,KAAK,uBAAuB;AACxE,aAAK,WAAW,KAAK;AACrB,aAAK,oBAAoB,MAAM;AAAA,MACjC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,SAAqB;AACnB,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,QAAI,KAAK,MAAO,SAAQ,IAAI,2BAA2B;AACvD,UAAM,SAAS,KAAK,UAAU,OAAO;AAGrC,QAAI,KAAK,YAAY;AACnB,WAAK,WAAW,KAAK,MAAM,CAAC;AAAA,IAC9B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,QAAe;AACb,QAAI,CAAC,KAAK,aAAa,OAAO,KAAK,UAAU,aAAa,YAAY;AAEpE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,UAAU;AAAA,QACV,kBAAkB;AAAA,QAClB,aAAa;AAAA,QACb,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,eAAe;AAAA,QACf,oBAAoB;AAAA,QACpB,oBAAoB;AAAA,MACtB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,KAAK,UAAU,SAAS;AAI1C,YAAM,QAAQ;AAAA,QACZ,SAAS,UAAU;AAAA,QACnB,UAAU,UAAU;AAAA,QACpB,UAAU,UAAU;AAAA,QACpB,kBAAkB,UAAU;AAAA,QAC5B,aAAa,UAAU;AAAA,QACvB,iBAAiB,UAAU;AAAA,QAC3B,aAAa,UAAU;AAAA,QACvB,eAAe,UAAU;AAAA,QACzB,oBAAoB,UAAU;AAAA,QAC9B,oBAAoB,UAAU;AAAA,MAChC;AAGA,UAAI,CAAC,KAAK,WAAW,MAAM,YAAY,KAAK,MAAM,aAAa,GAAG;AAChE,gBAAQ;AAAA,UACN;AAAA,QAEF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,UAAI,KAAK,MAAO,SAAQ,MAAM,2CAA2C,CAAC;AAC1E,aAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,UAAU;AAAA,QACV,kBAAkB;AAAA,QAClB,aAAa;AAAA,QACb,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,eAAe;AAAA,QACf,oBAAoB;AAAA,QACpB,oBAAoB;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU;AACf,QAAI,KAAK,MAAO,SAAQ,IAAI,4BAA4B;AAAA,EAC1D;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,QAAI,KAAK,MAAO,SAAQ,IAAI,2BAA2B;AAAA,EACzD;AAAA,EAEA,SAAe;AACb,SAAK,SAAS;AACd,QAAI,KAAK,MAAO,SAAQ,IAAI,4BAA4B;AAAA,EAC1D;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AACF;AAEA,eAAe,WACb,OACA,WAAW,MAAM,MACI;AACrB,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,IAAI,YAAY,EAAE,OAAO,KAAK;AAC9C,WAAO,QAAQ,SAAS,WAAW,QAAQ,MAAM,GAAG,QAAQ,IAAI;AAAA,EAClE;AAEA,MAAI,iBAAiB,YAAY;AAC/B,WAAO,MAAM,SAAS,WAAW,MAAM,MAAM,GAAG,QAAQ,IAAI;AAAA,EAC9D;AAEA,MAAI,iBAAiB,aAAa;AAChC,UAAM,QAAQ,IAAI,WAAW,KAAoB;AACjD,WAAO,MAAM,SAAS,WAAW,MAAM,MAAM,GAAG,QAAQ,IAAI;AAAA,EAC9D;AAEA,MAAI,iBAAiB,KAAK,GAAG;AAC3B,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAMC,UAAuB,CAAC;AAC9B,QAAIC,SAAQ;AAEZ,WAAOA,SAAQ,UAAU;AACvB,YAAM,EAAE,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,QAAQ,CAAC,MAAO;AACpB,YAAM,QAAQA,SAAQ,MAAM,SAAS,WACjC,MAAM,MAAM,GAAG,WAAWA,MAAK,IAC/B;AACJ,MAAAD,QAAO,KAAK,KAAK;AACjB,MAAAC,UAAS,MAAM;AAAA,IACjB;AAEA,QAAIA,UAAS,UAAU;AACrB,YAAM,OAAO,OAAO;AAAA,IACtB;AAEA,WAAO,aAAaD,SAAQC,MAAK;AAAA,EACnC;AAEA,QAAM,SAAuB,CAAC;AAC9B,MAAI,QAAQ;AAEZ,mBAAiB,SAAS,OAAoC;AAC5D,QAAI,SAAS,UAAU;AACrB;AAAA,IACF;AACA,UAAM,OAAO,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACvE,UAAM,QAAQ,QAAQ,KAAK,SAAS,WAChC,KAAK,MAAM,GAAG,WAAW,KAAK,IAC9B;AACJ,WAAO,KAAK,KAAK;AACjB,aAAS,MAAM;AAAA,EACjB;AAEA,SAAO,aAAa,QAAQ,KAAK;AACnC;AAEA,SAAS,aAAa,QAAsB,OAA2B;AACrE,QAAM,SAAS,IAAI,WAAW,KAAK;AACnC,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AAC1B,WAAO,IAAI,OAAO,MAAM;AACxB,cAAU,MAAM;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,iBACP,OACqC;AACrC,SAAO,OAAQ,OAAsC,cAAc;AACrE;AAEA,eAAe,kBAAkB,OAAqC;AACpE,QAAM,aAAa,MAAM,eAAe;AACxC,MAAI,OAAO,WAAW,YAAY,YAAY;AAC5C,UAAO,WAAW,QAAgC;AAAA,EACpD;AACA,aAAW,KAAK,KAAK;AACrB,SAAO;AACT;AAEA,eAAsB,aACpB,OACA,OAAsB,CAAC,GACM;AAC7B,QAAM,aAAa,MAAM,kBAAkB,CAAC,CAAC,KAAK,KAAK;AACvD,QAAM,SAAS,MAAM,WAAW,OAAO,KAAK,QAAQ;AACpD,QAAM,SAAS,WAAW,eAAe,MAAM;AAC/C,SAAQ,UAAqB;AAC/B;AAEA,eAAsB,gBACpB,OACA,YACA,OAAsB,CAAC,GACa;AACpC,QAAM,aAAa,MAAM,kBAAkB,CAAC,CAAC,KAAK,KAAK;AACvD,QAAM,SAAS,MAAM,WAAW,OAAO,KAAK,QAAQ;AACpD,QAAM,SAAS,WAAW,kBAAkB,QAAQ,UAAU;AAC9D,SAAO,UAAU;AACnB;AAGA,eAAsB,4BACpB,OACA,OAAsB,CAAC,GACO;AAC9B,QAAM,YAAY,MAAM,gBAAgB,OAAO,OAAO,IAAI;AAC1D,MAAI,aAAa,UAAU,WAAW,SAAS,UAAU,WAAW;AAClE,WAAO;AAAA,MACL,WAAW,UAAU;AAAA,MACrB,QAAQ,UAAU;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,kBACpB,OACA,OAAsB,CAAC,GACO;AAC9B,QAAM,YAAY,MAAM,gBAAgB,OAAO,OAAO,IAAI;AAC1D,MAAI,aAAa,UAAU,WAAW,OAAO;AAC3C,WAAO;AAAA,MACL,UAAU,UAAU;AAAA,MACpB,eAAe,UAAU;AAAA,IAC3B;AAAA,EACF;AACA,SAAO;AACT;AAGA,eAAsB,iBACpB,OACA,OAAsB,CAAC,GAKtB;AACD,QAAM,aAAa,MAAM,kBAAkB,CAAC,CAAC,KAAK,KAAK;AACvD,QAAM,SAAS,MAAM,WAAW,OAAO,KAAK,QAAQ;AAEpD,QAAM,SAAU,WAAW,eAAe,MAAM,KAAgB;AAEhE,QAAM,SAAuF,EAAE,OAAO;AAEtG,MAAI,WAAW,OAAO;AACpB,UAAM,eAAe,WAAW,kBAAkB,MAAM;AACxD,QAAI,cAAc;AAChB,aAAO,YAAY;AAAA,QACjB,WAAW,aAAa;AAAA,QACxB,YAAY,aAAa,OAAO,SAAS;AAAA,MAC3C;AAAA,IACF;AAAA,EACF,WAAW,WAAW,OAAO;AAC3B,UAAM,eAAe,WAAW,oBAAoB,MAAM;AAC1D,QAAI,cAAc,eAAe;AAC/B,aAAO,YAAY;AAAA,QACjB,eAAe,aAAa;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGO,MAAM,oCAAoC,gBAAwC;AAAA,EACvF,YAAY,OAA4B,CAAC,GAAG;AAC1C,QAAI,QAA6B;AAEjC,UAAM;AAAA,MACJ,MAAM,MAAM,YAAY;AACtB,gBAAQ,MAAM,aAAa,OAAO,IAAI;AAAA,MACxC;AAAA,MAEA,UAAU,OAAO,YAAY;AAC3B,YAAI,CAAC,OAAO;AACV,gBAAM,IAAI,MAAM,8BAA8B;AAAA,QAChD;AAEA,cAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,YAAI,OAAO,SAAS,GAAG;AACrB,qBAAW,QAAQ,MAAM;AAAA,QAC3B;AAAA,MACF;AAAA,MAEA,MAAM,YAAY;AAChB,YAAI,CAAC,OAAO;AACV;AAAA,QACF;AAEA,cAAM,SAAS,MAAM,OAAO;AAC5B,YAAI,OAAO,SAAS,GAAG;AACrB,qBAAW,QAAQ,MAAM;AAAA,QAC3B;AAEA,YAAI,KAAK,SAAS;AAChB,gBAAM,QAAQ,MAAM,MAAM;AAC1B,kBAAQ,IAAI,sCAAsC,KAAK;AAAA,QACzD;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAGA,eAAsB,QACpB,OACA,OAA4B,CAAC,GACR;AACrB,MAAI;AACF,UAAM,aAAa,OAAO,UAAU,WAChC,IAAI,YAAY,EAAE,OAAO,KAAK,IAC9B;AAGJ,QAAI,aAAa;AACjB,QAAI,CAAC,KAAK,eAAe,KAAK,gBAAgB,QAAQ;AACpD,YAAM,SAAS,WAAW,SAAS,MAAM,OACrC,WAAW,MAAM,GAAG,MAAM,IAAI,IAC9B;AAEJ,YAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAErE,UAAI,SAAS,WAAW,WAAW;AACjC,qBAAa,EAAE,GAAG,MAAM,aAAa,SAAS,OAAiB;AAE/D,YAAI,SAAS,aAAa,CAAC,KAAK,WAAW;AACzC,qBAAW,YAAY,SAAS;AAAA,QAClC;AAEA,YAAI,SAAS,aAAa,CAAC,KAAK,WAAW;AACzC,qBAAW,YAAY,SAAS;AAAA,QAClC;AAEA,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,yCAAyC,SAAS,MAAM;AAAA,QACtE;AAAA,MACF,OAAO;AACL,cAAM,IAAI,MAAM,4EAA4E;AAAA,MAC9F;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,OAAO,UAAU;AAElD,UAAM,SAAS,MAAM,KAAK,UAAU;AACpC,UAAM,QAAQ,MAAM,OAAO;AAG3B,UAAM,SAAS,IAAI,WAAW,OAAO,SAAS,MAAM,MAAM;AAC1D,WAAO,IAAI,QAAQ,CAAC;AACpB,WAAO,IAAI,OAAO,OAAO,MAAM;AAE/B,QAAI,KAAK,SAAS;AAChB,YAAM,QAAQ,MAAM,MAAM;AAC1B,cAAQ,IAAI,sCAAsC,KAAK;AAAA,IACzD;AAEA,WAAO;AAAA,EACT,SAAS,KAAU;AAEjB,QAAI,eAAe,MAAO,OAAM;AAChC,QAAI;AAEF,YAAM,MAAM,OAAO,QAAQ,WAAW,MAAO,OAAO,IAAI,UAAW,IAAI,UAAU,KAAK,UAAU,GAAG;AACnG,YAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,IAC7B,SAAS,GAAG;AACV,YAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,IAC7B;AAAA,EACF;AACF;AAGA,eAAsB,gBACpB,OACA,OAA4B,CAAC,GACZ;AACjB,QAAM,SAAS,MAAM,QAAQ,OAAO,IAAI;AACxC,SAAO,WAAW,MAAM;AAC1B;AAoBA,eAAsB,WACpB,OACA,MACqB;AACrB,QAAM,QAAQ,IAAI,aAAa;AAC/B,SAAO,MAAM,QAAQ,OAAO,IAAI;AAClC;AAWA,eAAsB,mBACpB,OACA,MACiB;AACjB,QAAM,SAAS,MAAM,WAAW,OAAO,IAAI;AAC3C,SAAO,WAAW,MAAM;AAC1B;AAWO,SAAS,YAAY,QAAwB;AAClD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAQO,SAAS,aAAa,QAAwB;AACnD,SAAO;AACT;AAaO,SAAS,qBACd,cACA,cACA,mBAAmB,OACX;AACR,QAAM,WAAW,aAAa,QAAQ,aAAa,EAAE;AACrD,QAAM,YAAY,aAAa,YAAY;AAE3C,MAAI,kBAAkB;AACpB,WAAO,GAAG,QAAQ,cAAc,KAAK,IAAI,CAAC,IAAI,SAAS;AAAA,EACzD;AAEA,SAAO,GAAG,QAAQ,IAAI,SAAS;AACjC;AASO,SAAS,kBAAkB,QAG/B;AACD,QAAM,WAAW,YAAY,MAAM;AACnC,QAAM,YAAY,IAAI,aAAa,MAAM,CAAC;AAE1C,SAAO;AAAA,IACL;AAAA,MACE,aAAa,GAAG,OAAO,YAAY,CAAC;AAAA,MACpC,QAAQ,EAAE,CAAC,QAAQ,GAAG,CAAC,SAAS,EAAE;AAAA,IACpC;AAAA,EACF;AACF;AAOO,SAAS,2BAAoC;AAClD,SAAO,2BAA2B;AACpC;AAMO,SAAS,wBAAgC;AAC9C,QAAM,QAAQ,OAAO,cAAc,cAC9B,UAAU,uBAAuB,IACjC,SAAS,KAAK,qBAAqB,SAAS,QAAQ,IAAI,kBAAkB,IAC1E,OAAO,YAAY,cAAc,QAAQ,IAAI,EAAE,KAAK,EAAE,SAAS;AAIpE,SAAO,KAAK,IAAI,OAAO,CAAC;AAC1B;AAMO,SAAS,mBAOd;AACA,QAAM,WAAW,OAAO,YAAY;AAEpC,SAAO;AAAA,IACL,wBAAwB,2BAA2B;AAAA,IACnD,2BAA2B,OAAO,WAAW;AAAA,IAC7C,qBAAqB,YAAY,CAAC,CAAC;AAAA,IACnC,oBAAoB,sBAAsB;AAAA,IAC1C,gBAAgB,WACb,mBAAmB,iBAAiB,SAAS,UAAU,IAAI,IAC3D,aAAa,WAAW,SAAS,UAAU,IAAI;AAAA,IAClD,UAAU,WAAW,8BAA8B;AAAA,EACrD;AACF;AAMO,MAAM,YAAY;","names":["require","mod","result","reader","Converter","chunks","total"]}
|
package/dist/node.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Transform } from 'node:stream';
|
|
2
2
|
import { PathLike } from 'node:fs';
|
|
3
3
|
import { ConvertOptions, ConvertBuddyOptions } from './index.js';
|
|
4
|
-
export { Coerce, ConvertBuddy, ConvertBuddyTransformStream, CsvConfig, CsvDetection, DetectInput, DetectOptions, FieldMap, Format, JsonDetection, NdjsonDetection, ProgressCallback, Stats, StructureDetection, TransformConfig, TransformMode, XmlConfig, XmlDetection, autoDetectConfig, convertAny, convertAnyToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported } from './index.js';
|
|
4
|
+
export { Coerce, ConvertBuddy, ConvertBuddyTransformStream, Converter, CsvConfig, CsvDetection, DetectInput, DetectOptions, FieldMap, Format, JsonDetection, NdjsonDetection, ProgressCallback, Stats, StructureDetection, TransformConfig, TransformMode, XmlConfig, XmlDetection, autoDetectConfig, convertAny, convertAnyToString, detectCsvFieldsAndDelimiter, detectFormat, detectStructure, detectXmlElements, getExtension, getFileTypeConfig, getMimeType, getOptimalThreadCount, getSuggestedFilename, getThreadingInfo, isWasmThreadingSupported } from './index.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Ultra-simple convert function for Node.js.
|
|
@@ -533,6 +533,39 @@ invalid
|
|
|
533
533
|
assert.strictEqual(parsed.length, 1);
|
|
534
534
|
});
|
|
535
535
|
});
|
|
536
|
+
describe("Real World Data - DND Characters", () => {
|
|
537
|
+
it("should convert DND characters NDJSON to JSON correctly", async () => {
|
|
538
|
+
const ndjsonData = `{"name":"Gorwin \\"Grog\\" Oakenshield","race":"Human","class":"Barbarian","quirk":"Collects spoons from every tavern"}
|
|
539
|
+
{"name":"Zilaen Whisperleaf","race":"Elf","class":"Rogue","quirk":"Talks to shadows, claims they're shy"}
|
|
540
|
+
{"name":"Pip Thistlewhisk","race":"Halfling","class":"Bard","quirk":"Plays the lute with carrots"}
|
|
541
|
+
{"name":"Thraxxus Bonegrinder","race":"Orc","class":"Cleric","quirk":"Prays to a rock named Doris"}
|
|
542
|
+
{"name":"Elaria Moonbeam","race":"Half-Elf","class":"Wizard","quirk":"Writes shopping lists in ancient runes"}`;
|
|
543
|
+
const result = await convertToString(ndjsonData, {
|
|
544
|
+
inputFormat: "ndjson",
|
|
545
|
+
outputFormat: "json"
|
|
546
|
+
});
|
|
547
|
+
const parsed = JSON.parse(result);
|
|
548
|
+
assert.strictEqual(parsed.length, 5, "Should have 5 characters");
|
|
549
|
+
assert.strictEqual(parsed[0].name, 'Gorwin "Grog" Oakenshield');
|
|
550
|
+
assert.strictEqual(parsed[0].race, "Human");
|
|
551
|
+
assert.strictEqual(parsed[0].class, "Barbarian");
|
|
552
|
+
assert.strictEqual(parsed[4].name, "Elaria Moonbeam");
|
|
553
|
+
assert.strictEqual(parsed[4].race, "Half-Elf");
|
|
554
|
+
assert.strictEqual(parsed[4].class, "Wizard");
|
|
555
|
+
assert.strictEqual(parsed[4].quirk, "Writes shopping lists in ancient runes");
|
|
556
|
+
});
|
|
557
|
+
it("should auto-detect DND characters NDJSON format", async () => {
|
|
558
|
+
const ndjsonData = `{"name":"Gorwin \\"Grog\\" Oakenshield","race":"Human","class":"Barbarian","quirk":"Collects spoons from every tavern"}
|
|
559
|
+
{"name":"Zilaen Whisperleaf","race":"Elf","class":"Rogue","quirk":"Talks to shadows, claims they're shy"}
|
|
560
|
+
{"name":"Pip Thistlewhisk","race":"Halfling","class":"Bard","quirk":"Plays the lute with carrots"}`;
|
|
561
|
+
const result = await convertToString(ndjsonData, {
|
|
562
|
+
outputFormat: "json"
|
|
563
|
+
});
|
|
564
|
+
const parsed = JSON.parse(result);
|
|
565
|
+
assert.strictEqual(parsed.length, 3, "Should auto-detect and parse 3 characters");
|
|
566
|
+
assert.strictEqual(parsed[0].name, 'Gorwin "Grog" Oakenshield');
|
|
567
|
+
});
|
|
568
|
+
});
|
|
536
569
|
});
|
|
537
570
|
console.log("\u2713 NDJSON Edge Case Test Suite loaded");
|
|
538
571
|
console.log(" Run with: node --test tests/edge-cases/ndjson-edge-cases.test.ts");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../tests/edge-cases/ndjson-edge-cases.test.ts"],"sourcesContent":["import { describe, it } from \"node:test\";\r\nimport { convertToString, convert } from \"../../index.js\";\r\nimport { strict as assert } from \"node:assert\";\r\n\r\n/**\r\n * Comprehensive NDJSON Edge Case Test Suite\r\n * \r\n * Tests all possible edge cases for NDJSON parsing to ensure maximum safety.\r\n */\r\n\r\ndescribe(\"NDJSON Edge Cases - Comprehensive Safety Tests\", () => {\r\n \r\n // ========== LINE HANDLING ==========\r\n describe(\"Line Handling\", () => {\r\n it(\"should handle empty file\", async () => {\r\n const result = await convertToString(\"\", {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle file with only empty lines\", async () => {\r\n const ndjson = \"\\n\\n\\n\\n\";\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle lines with only whitespace\", async () => {\r\n const ndjson = \" \\n \\n \\n\";\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle empty lines between records\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n\\n{\"name\":\"Bob\"}\\n\\n{\"name\":\"Charlie\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle very long lines (>10MB)\", async () => {\r\n const largeObj = { data: \"A\".repeat(1000000) };\r\n const ndjson = JSON.stringify(largeObj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle missing trailing newline\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle multiple consecutive newlines\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n\\n\\n{\"name\":\"Bob\"}\\n\\n\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle single line NDJSON\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle CRLF line endings\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\r\\n{\"name\":\"Bob\"}\\r\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle mixed line endings (LF and CRLF)\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\r\\n{\"name\":\"Charlie\"}\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n });\r\n\r\n // ========== JSON VALIDITY ==========\r\n describe(\"JSON Validity\", () => {\r\n it(\"should handle valid JSON objects\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30}\\n{\"name\":\"Bob\",\"age\":25}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle JSON arrays as records\", async () => {\r\n const ndjson = `[1,2,3]\\n[4,5,6]\\n[7,8,9]`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (strings)\", async () => {\r\n const ndjson = `\"Alice\"\\n\"Bob\"\\n\"Charlie\"`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (numbers)\", async () => {\r\n const ndjson = `42\\n3.14\\n-100`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (booleans)\", async () => {\r\n const ndjson = `true\\nfalse\\ntrue`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON null values\", async () => {\r\n const ndjson = `null\\nnull\\nnull`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle deeply nested objects (>10 levels)\", async () => {\r\n let obj: any = { value: \"deep\" };\r\n for (let i = 0; i < 15; i++) {\r\n obj = { nested: obj };\r\n }\r\n const ndjson = JSON.stringify(obj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle large arrays (>1000 elements)\", async () => {\r\n const largeArray = Array.from({ length: 2000 }, (_, i) => i);\r\n const ndjson = JSON.stringify(largeArray);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle Unicode escape sequences\", async () => {\r\n const ndjson = `{\"emoji\":\"😀\"}\\n{\"text\":\"Hello\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle special characters in strings\", async () => {\r\n const ndjson = `{\"text\":\"Line 1\\\\nLine 2\"}\\n{\"text\":\"Tab\\\\there\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n });\r\n\r\n // ========== DATA TYPES ==========\r\n describe(\"Data Types\", () => {\r\n it(\"should handle all JSON types in single record\", async () => {\r\n const ndjson = `{\"string\":\"text\",\"number\":42,\"boolean\":true,\"null\":null,\"array\":[1,2,3],\"object\":{\"nested\":\"value\"}}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n assert.strictEqual(parsed[0].string, \"text\");\r\n assert.strictEqual(parsed[0].number, 42);\r\n assert.strictEqual(parsed[0].boolean, true);\r\n assert.strictEqual(parsed[0].null, null);\r\n });\r\n\r\n it(\"should handle mixed types across records\", async () => {\r\n const ndjson = `{\"type\":\"object\"}\\n[1,2,3]\\n\"string\"\\n42\\ntrue\\nnull`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 6);\r\n });\r\n\r\n it(\"should handle empty objects\", async () => {\r\n const ndjson = `{}\\n{}\\n{}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle empty arrays\", async () => {\r\n const ndjson = `[]\\n[]\\n[]`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle very large strings (>1MB)\", async () => {\r\n const largeString = \"A\".repeat(2000000);\r\n const ndjson = JSON.stringify({ data: largeString });\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle numbers at precision limits\", async () => {\r\n const ndjson = `{\"max\":${Number.MAX_SAFE_INTEGER}}\\n{\"min\":${Number.MIN_SAFE_INTEGER}}\\n{\"float\":${Number.MAX_VALUE}}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle objects with many keys (>1000)\", async () => {\r\n const obj: any = {};\r\n for (let i = 0; i < 1500; i++) {\r\n obj[`key${i}`] = i;\r\n }\r\n const ndjson = JSON.stringify(obj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n assert.strictEqual(Object.keys(parsed[0]).length, 1500);\r\n });\r\n\r\n it(\"should handle objects with duplicate keys (last wins)\", async () => {\r\n const ndjson = `{\"key\":\"value1\",\"key\":\"value2\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle objects with special key names\", async () => {\r\n const ndjson = `{\"\":\"empty\",\"123\":\"numeric\",\"@#$\":\"special\",\"__proto__\":\"proto\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n });\r\n\r\n // ========== NDJSON PASSTHROUGH ==========\r\n describe(\"NDJSON Passthrough\", () => {\r\n it(\"should handle NDJSON → NDJSON passthrough\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\n{\"name\":\"Charlie\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n assert.strictEqual(lines.length, 3);\r\n });\r\n\r\n it(\"should preserve record order in passthrough\", async () => {\r\n const ndjson = `{\"id\":1}\\n{\"id\":2}\\n{\"id\":3}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n const first = JSON.parse(lines[0]);\r\n assert.strictEqual(first.id, 1);\r\n });\r\n\r\n it(\"should handle large passthrough (10K records)\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 10000; i++) {\r\n ndjson += `{\"id\":${i}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n assert.strictEqual(lines.length, 10000);\r\n });\r\n });\r\n\r\n // ========== STREAMING AND BOUNDARIES ==========\r\n describe(\"Streaming and Chunk Boundaries\", () => {\r\n it(\"should handle records split across chunks\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\n{\"name\":\"Charlie\"}`;\r\n const data = new TextEncoder().encode(ndjson);\r\n \r\n const result = await convert(data, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n \r\n const resultStr = new TextDecoder().decode(result);\r\n const parsed = JSON.parse(resultStr);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle partial JSON objects across chunks\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30}\\n{\"name\":\"Bob\",\"age\":25}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n });\r\n\r\n // ========== PERFORMANCE EDGE CASES ==========\r\n describe(\"Performance Edge Cases\", () => {\r\n it(\"should handle tiny files (<100 bytes)\", async () => {\r\n const ndjson = `{\"a\":1}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(result.length > 0);\r\n });\r\n\r\n it(\"should handle many small records\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 1000; i++) {\r\n ndjson += `{\"id\":${i}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1000);\r\n });\r\n\r\n it(\"should handle few large records\", async () => {\r\n const largeValue = \"A\".repeat(50000);\r\n const ndjson = `{\"data\":\"${largeValue}\"}\\n{\"data\":\"${largeValue}\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle highly compressible data\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 100; i++) {\r\n ndjson += `{\"name\":\"Alice\",\"value\":100}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 100);\r\n });\r\n\r\n it(\"should handle random data (low compressibility)\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 100; i++) {\r\n ndjson += `{\"id\":\"${Math.random().toString(36)}\",\"value\":${Math.random()}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 100);\r\n });\r\n });\r\n\r\n // ========== MALFORMED INPUT (ERROR HANDLING) ==========\r\n describe(\"Malformed Input Handling\", () => {\r\n it(\"should handle invalid JSON syntax gracefully\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{invalid json}\\n{\"name\":\"Bob\"}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n // If it succeeds, it handled it gracefully (maybe skipped invalid line)\r\n assert.ok(true);\r\n } catch (error) {\r\n // If it fails, it detected the error\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle incomplete JSON objects\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"\\n{\"name\":\"Bob\"}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle truncated JSON\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30,\"city\":\"N`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle mixed valid and invalid lines\", async () => {\r\n const ndjson = `{\"valid\":1}\\ninvalid\\n{\"valid\":2}\\n{broken\\n{\"valid\":3}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n });\r\n\r\n // ========== UNICODE AND ENCODING ==========\r\n describe(\"Unicode and Encoding\", () => {\r\n it(\"should handle Unicode characters\", async () => {\r\n const ndjson = `{\"emoji\":\"😀🎉🚀\"}\\n{\"text\":\"日本語\"}\\n{\"symbols\":\"αβγδ\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n assert.ok(parsed[0].emoji.includes(\"😀\"));\r\n });\r\n\r\n it(\"should handle surrogate pairs\", async () => {\r\n const ndjson = `{\"emoji\":\"𝕳𝖊𝖑𝖑𝖔\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle zero-width characters\", async () => {\r\n const ndjson = `{\"text\":\"Hello\\u200BWorld\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n });\r\n});\r\n\r\nconsole.log(\"✓ NDJSON Edge Case Test Suite loaded\");\r\nconsole.log(\" Run with: node --test tests/edge-cases/ndjson-edge-cases.test.ts\");\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../tests/edge-cases/ndjson-edge-cases.test.ts"],"sourcesContent":["import { describe, it } from \"node:test\";\r\nimport { convertToString, convert } from \"../../index.js\";\r\nimport { strict as assert } from \"node:assert\";\r\n\r\n/**\r\n * Comprehensive NDJSON Edge Case Test Suite\r\n * \r\n * Tests all possible edge cases for NDJSON parsing to ensure maximum safety.\r\n */\r\n\r\ndescribe(\"NDJSON Edge Cases - Comprehensive Safety Tests\", () => {\r\n \r\n // ========== LINE HANDLING ==========\r\n describe(\"Line Handling\", () => {\r\n it(\"should handle empty file\", async () => {\r\n const result = await convertToString(\"\", {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle file with only empty lines\", async () => {\r\n const ndjson = \"\\n\\n\\n\\n\";\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle lines with only whitespace\", async () => {\r\n const ndjson = \" \\n \\n \\n\";\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.strictEqual(result.trim(), \"[]\");\r\n });\r\n\r\n it(\"should handle empty lines between records\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n\\n{\"name\":\"Bob\"}\\n\\n{\"name\":\"Charlie\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle very long lines (>10MB)\", async () => {\r\n const largeObj = { data: \"A\".repeat(1000000) };\r\n const ndjson = JSON.stringify(largeObj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle missing trailing newline\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle multiple consecutive newlines\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n\\n\\n{\"name\":\"Bob\"}\\n\\n\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle single line NDJSON\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle CRLF line endings\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\r\\n{\"name\":\"Bob\"}\\r\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle mixed line endings (LF and CRLF)\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\r\\n{\"name\":\"Charlie\"}\\n`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n });\r\n\r\n // ========== JSON VALIDITY ==========\r\n describe(\"JSON Validity\", () => {\r\n it(\"should handle valid JSON objects\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30}\\n{\"name\":\"Bob\",\"age\":25}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle JSON arrays as records\", async () => {\r\n const ndjson = `[1,2,3]\\n[4,5,6]\\n[7,8,9]`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (strings)\", async () => {\r\n const ndjson = `\"Alice\"\\n\"Bob\"\\n\"Charlie\"`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (numbers)\", async () => {\r\n const ndjson = `42\\n3.14\\n-100`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON primitives (booleans)\", async () => {\r\n const ndjson = `true\\nfalse\\ntrue`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle JSON null values\", async () => {\r\n const ndjson = `null\\nnull\\nnull`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle deeply nested objects (>10 levels)\", async () => {\r\n let obj: any = { value: \"deep\" };\r\n for (let i = 0; i < 15; i++) {\r\n obj = { nested: obj };\r\n }\r\n const ndjson = JSON.stringify(obj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle large arrays (>1000 elements)\", async () => {\r\n const largeArray = Array.from({ length: 2000 }, (_, i) => i);\r\n const ndjson = JSON.stringify(largeArray);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle Unicode escape sequences\", async () => {\r\n const ndjson = `{\"emoji\":\"😀\"}\\n{\"text\":\"Hello\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle special characters in strings\", async () => {\r\n const ndjson = `{\"text\":\"Line 1\\\\nLine 2\"}\\n{\"text\":\"Tab\\\\there\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n });\r\n\r\n // ========== DATA TYPES ==========\r\n describe(\"Data Types\", () => {\r\n it(\"should handle all JSON types in single record\", async () => {\r\n const ndjson = `{\"string\":\"text\",\"number\":42,\"boolean\":true,\"null\":null,\"array\":[1,2,3],\"object\":{\"nested\":\"value\"}}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n assert.strictEqual(parsed[0].string, \"text\");\r\n assert.strictEqual(parsed[0].number, 42);\r\n assert.strictEqual(parsed[0].boolean, true);\r\n assert.strictEqual(parsed[0].null, null);\r\n });\r\n\r\n it(\"should handle mixed types across records\", async () => {\r\n const ndjson = `{\"type\":\"object\"}\\n[1,2,3]\\n\"string\"\\n42\\ntrue\\nnull`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 6);\r\n });\r\n\r\n it(\"should handle empty objects\", async () => {\r\n const ndjson = `{}\\n{}\\n{}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle empty arrays\", async () => {\r\n const ndjson = `[]\\n[]\\n[]`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle very large strings (>1MB)\", async () => {\r\n const largeString = \"A\".repeat(2000000);\r\n const ndjson = JSON.stringify({ data: largeString });\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle numbers at precision limits\", async () => {\r\n const ndjson = `{\"max\":${Number.MAX_SAFE_INTEGER}}\\n{\"min\":${Number.MIN_SAFE_INTEGER}}\\n{\"float\":${Number.MAX_VALUE}}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle objects with many keys (>1000)\", async () => {\r\n const obj: any = {};\r\n for (let i = 0; i < 1500; i++) {\r\n obj[`key${i}`] = i;\r\n }\r\n const ndjson = JSON.stringify(obj);\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n assert.strictEqual(Object.keys(parsed[0]).length, 1500);\r\n });\r\n\r\n it(\"should handle objects with duplicate keys (last wins)\", async () => {\r\n const ndjson = `{\"key\":\"value1\",\"key\":\"value2\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle objects with special key names\", async () => {\r\n const ndjson = `{\"\":\"empty\",\"123\":\"numeric\",\"@#$\":\"special\",\"__proto__\":\"proto\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n });\r\n\r\n // ========== NDJSON PASSTHROUGH ==========\r\n describe(\"NDJSON Passthrough\", () => {\r\n it(\"should handle NDJSON → NDJSON passthrough\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\n{\"name\":\"Charlie\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n assert.strictEqual(lines.length, 3);\r\n });\r\n\r\n it(\"should preserve record order in passthrough\", async () => {\r\n const ndjson = `{\"id\":1}\\n{\"id\":2}\\n{\"id\":3}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n const first = JSON.parse(lines[0]);\r\n assert.strictEqual(first.id, 1);\r\n });\r\n\r\n it(\"should handle large passthrough (10K records)\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 10000; i++) {\r\n ndjson += `{\"id\":${i}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"ndjson\",\r\n });\r\n const lines = result.trim().split(\"\\n\");\r\n assert.strictEqual(lines.length, 10000);\r\n });\r\n });\r\n\r\n // ========== STREAMING AND BOUNDARIES ==========\r\n describe(\"Streaming and Chunk Boundaries\", () => {\r\n it(\"should handle records split across chunks\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{\"name\":\"Bob\"}\\n{\"name\":\"Charlie\"}`;\r\n const data = new TextEncoder().encode(ndjson);\r\n \r\n const result = await convert(data, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n \r\n const resultStr = new TextDecoder().decode(result);\r\n const parsed = JSON.parse(resultStr);\r\n assert.strictEqual(parsed.length, 3);\r\n });\r\n\r\n it(\"should handle partial JSON objects across chunks\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30}\\n{\"name\":\"Bob\",\"age\":25}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n });\r\n\r\n // ========== PERFORMANCE EDGE CASES ==========\r\n describe(\"Performance Edge Cases\", () => {\r\n it(\"should handle tiny files (<100 bytes)\", async () => {\r\n const ndjson = `{\"a\":1}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(result.length > 0);\r\n });\r\n\r\n it(\"should handle many small records\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 1000; i++) {\r\n ndjson += `{\"id\":${i}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1000);\r\n });\r\n\r\n it(\"should handle few large records\", async () => {\r\n const largeValue = \"A\".repeat(50000);\r\n const ndjson = `{\"data\":\"${largeValue}\"}\\n{\"data\":\"${largeValue}\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 2);\r\n });\r\n\r\n it(\"should handle highly compressible data\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 100; i++) {\r\n ndjson += `{\"name\":\"Alice\",\"value\":100}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 100);\r\n });\r\n\r\n it(\"should handle random data (low compressibility)\", async () => {\r\n let ndjson = \"\";\r\n for (let i = 0; i < 100; i++) {\r\n ndjson += `{\"id\":\"${Math.random().toString(36)}\",\"value\":${Math.random()}}\\n`;\r\n }\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 100);\r\n });\r\n });\r\n\r\n // ========== MALFORMED INPUT (ERROR HANDLING) ==========\r\n describe(\"Malformed Input Handling\", () => {\r\n it(\"should handle invalid JSON syntax gracefully\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"}\\n{invalid json}\\n{\"name\":\"Bob\"}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n // If it succeeds, it handled it gracefully (maybe skipped invalid line)\r\n assert.ok(true);\r\n } catch (error) {\r\n // If it fails, it detected the error\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle incomplete JSON objects\", async () => {\r\n const ndjson = `{\"name\":\"Alice\"\\n{\"name\":\"Bob\"}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle truncated JSON\", async () => {\r\n const ndjson = `{\"name\":\"Alice\",\"age\":30,\"city\":\"N`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n\r\n it(\"should handle mixed valid and invalid lines\", async () => {\r\n const ndjson = `{\"valid\":1}\\ninvalid\\n{\"valid\":2}\\n{broken\\n{\"valid\":3}`;\r\n try {\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n assert.ok(true);\r\n } catch (error) {\r\n assert.ok(error);\r\n }\r\n });\r\n });\r\n\r\n // ========== UNICODE AND ENCODING ==========\r\n describe(\"Unicode and Encoding\", () => {\r\n it(\"should handle Unicode characters\", async () => {\r\n const ndjson = `{\"emoji\":\"😀🎉🚀\"}\\n{\"text\":\"日本語\"}\\n{\"symbols\":\"αβγδ\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3);\r\n assert.ok(parsed[0].emoji.includes(\"😀\"));\r\n });\r\n\r\n it(\"should handle surrogate pairs\", async () => {\r\n const ndjson = `{\"emoji\":\"𝕳𝖊𝖑𝖑𝖔\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n\r\n it(\"should handle zero-width characters\", async () => {\r\n const ndjson = `{\"text\":\"Hello\\u200BWorld\"}`;\r\n const result = await convertToString(ndjson, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 1);\r\n });\r\n });\r\n\r\n // ========== REAL WORLD DATA ==========\r\n describe(\"Real World Data - DND Characters\", () => {\r\n it(\"should convert DND characters NDJSON to JSON correctly\", async () => {\r\n const ndjsonData = `{\"name\":\"Gorwin \\\\\"Grog\\\\\" Oakenshield\",\"race\":\"Human\",\"class\":\"Barbarian\",\"quirk\":\"Collects spoons from every tavern\"}\r\n{\"name\":\"Zilaen Whisperleaf\",\"race\":\"Elf\",\"class\":\"Rogue\",\"quirk\":\"Talks to shadows, claims they're shy\"}\r\n{\"name\":\"Pip Thistlewhisk\",\"race\":\"Halfling\",\"class\":\"Bard\",\"quirk\":\"Plays the lute with carrots\"}\r\n{\"name\":\"Thraxxus Bonegrinder\",\"race\":\"Orc\",\"class\":\"Cleric\",\"quirk\":\"Prays to a rock named Doris\"}\r\n{\"name\":\"Elaria Moonbeam\",\"race\":\"Half-Elf\",\"class\":\"Wizard\",\"quirk\":\"Writes shopping lists in ancient runes\"}`;\r\n\r\n const result = await convertToString(ndjsonData, {\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"json\",\r\n });\r\n\r\n // Verify it's valid JSON\r\n const parsed = JSON.parse(result);\r\n \r\n // Verify count\r\n assert.strictEqual(parsed.length, 5, \"Should have 5 characters\");\r\n \r\n // Verify first record\r\n assert.strictEqual(parsed[0].name, 'Gorwin \"Grog\" Oakenshield');\r\n assert.strictEqual(parsed[0].race, 'Human');\r\n assert.strictEqual(parsed[0].class, 'Barbarian');\r\n \r\n // Verify last record\r\n assert.strictEqual(parsed[4].name, 'Elaria Moonbeam');\r\n assert.strictEqual(parsed[4].race, 'Half-Elf');\r\n assert.strictEqual(parsed[4].class, 'Wizard');\r\n assert.strictEqual(parsed[4].quirk, 'Writes shopping lists in ancient runes');\r\n });\r\n\r\n it(\"should auto-detect DND characters NDJSON format\", async () => {\r\n const ndjsonData = `{\"name\":\"Gorwin \\\\\"Grog\\\\\" Oakenshield\",\"race\":\"Human\",\"class\":\"Barbarian\",\"quirk\":\"Collects spoons from every tavern\"}\r\n{\"name\":\"Zilaen Whisperleaf\",\"race\":\"Elf\",\"class\":\"Rogue\",\"quirk\":\"Talks to shadows, claims they're shy\"}\r\n{\"name\":\"Pip Thistlewhisk\",\"race\":\"Halfling\",\"class\":\"Bard\",\"quirk\":\"Plays the lute with carrots\"}`;\r\n\r\n // Don't specify inputFormat - let it auto-detect\r\n const result = await convertToString(ndjsonData, {\r\n outputFormat: \"json\",\r\n });\r\n\r\n const parsed = JSON.parse(result);\r\n assert.strictEqual(parsed.length, 3, \"Should auto-detect and parse 3 characters\");\r\n assert.strictEqual(parsed[0].name, 'Gorwin \"Grog\" Oakenshield');\r\n });\r\n });\r\n});\r\n\r\nconsole.log(\"✓ NDJSON Edge Case Test Suite loaded\");\r\nconsole.log(\" Run with: node --test tests/edge-cases/ndjson-edge-cases.test.ts\");\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { ConvertBuddy } from "../../index.js";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
function generateLargeCsv(rows) {
|
|
6
|
+
const header = "id,name,email,age,city,country,salary,department\n";
|
|
7
|
+
const lines = [header];
|
|
8
|
+
for (let i = 0; i < rows; i++) {
|
|
9
|
+
const line = `${i},User${i},user${i}@example.com,${20 + i % 50},City${i % 100},Country${i % 20},${5e4 + i % 1e5},Dept${i % 10}
|
|
10
|
+
`;
|
|
11
|
+
lines.push(line);
|
|
12
|
+
}
|
|
13
|
+
return lines.join("");
|
|
14
|
+
}
|
|
15
|
+
function generateLargeNdjson(records) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
for (let i = 0; i < records; i++) {
|
|
18
|
+
const obj = {
|
|
19
|
+
id: i,
|
|
20
|
+
name: `User${i}`,
|
|
21
|
+
email: `user${i}@example.com`,
|
|
22
|
+
age: 20 + i % 50,
|
|
23
|
+
city: `City${i % 100}`,
|
|
24
|
+
country: `Country${i % 20}`,
|
|
25
|
+
salary: 5e4 + i % 1e5,
|
|
26
|
+
department: `Dept${i % 10}`
|
|
27
|
+
};
|
|
28
|
+
lines.push(JSON.stringify(obj));
|
|
29
|
+
}
|
|
30
|
+
return lines.join("\n") + "\n";
|
|
31
|
+
}
|
|
32
|
+
function createChunkedStream(data, chunkSize) {
|
|
33
|
+
const buffer = Buffer.from(data, "utf-8");
|
|
34
|
+
const chunks = [];
|
|
35
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
36
|
+
chunks.push(buffer.slice(i, i + chunkSize));
|
|
37
|
+
}
|
|
38
|
+
return Readable.from(chunks);
|
|
39
|
+
}
|
|
40
|
+
describe("Large file streaming tests", () => {
|
|
41
|
+
it("should handle streaming large CSV to JSON with progress tracking", async () => {
|
|
42
|
+
const rowCount = 1e4;
|
|
43
|
+
const csv = generateLargeCsv(rowCount);
|
|
44
|
+
const csvSize = Buffer.byteLength(csv);
|
|
45
|
+
console.log(`Testing with ${rowCount} rows, ~${(csvSize / 1024 / 1024).toFixed(2)}MB`);
|
|
46
|
+
const buddy = await ConvertBuddy.create({
|
|
47
|
+
inputFormat: "csv",
|
|
48
|
+
outputFormat: "json",
|
|
49
|
+
chunkTargetBytes: 64 * 1024,
|
|
50
|
+
progressIntervalBytes: 256 * 1024,
|
|
51
|
+
onProgress: (stats2) => {
|
|
52
|
+
const percentage = stats2.bytesIn / csvSize * 100;
|
|
53
|
+
console.log(`Progress: ${percentage.toFixed(1)}% - ${stats2.recordsProcessed} records - ${stats2.throughputMbPerSec.toFixed(1)} MB/s`);
|
|
54
|
+
},
|
|
55
|
+
profile: true
|
|
56
|
+
});
|
|
57
|
+
const chunkSize = 32 * 1024;
|
|
58
|
+
const stream = createChunkedStream(csv, chunkSize);
|
|
59
|
+
const outputs = [];
|
|
60
|
+
for await (const chunk of stream) {
|
|
61
|
+
const output = buddy.push(chunk);
|
|
62
|
+
if (output.length > 0) {
|
|
63
|
+
outputs.push(output);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const final = buddy.finish();
|
|
67
|
+
if (final.length > 0) {
|
|
68
|
+
outputs.push(final);
|
|
69
|
+
}
|
|
70
|
+
const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);
|
|
71
|
+
const result = new Uint8Array(totalLength);
|
|
72
|
+
let offset = 0;
|
|
73
|
+
for (const output of outputs) {
|
|
74
|
+
result.set(output, offset);
|
|
75
|
+
offset += output.length;
|
|
76
|
+
}
|
|
77
|
+
const jsonString = new TextDecoder().decode(result);
|
|
78
|
+
const parsed = JSON.parse(jsonString);
|
|
79
|
+
assert.strictEqual(parsed.length, rowCount, `Expected ${rowCount} records`);
|
|
80
|
+
assert.strictEqual(parsed[0].id, 0);
|
|
81
|
+
assert.strictEqual(parsed[0].name, "User0");
|
|
82
|
+
const stats = buddy.stats();
|
|
83
|
+
console.log(`Final stats: ${stats.bytesIn} bytes in, ${stats.bytesOut} bytes out, ${stats.recordsProcessed} records`);
|
|
84
|
+
assert.strictEqual(stats.recordsProcessed, rowCount);
|
|
85
|
+
});
|
|
86
|
+
it("should handle streaming large NDJSON to CSV with memory limits", async () => {
|
|
87
|
+
const recordCount = 5e3;
|
|
88
|
+
const ndjson = generateLargeNdjson(recordCount);
|
|
89
|
+
const ndjsonSize = Buffer.byteLength(ndjson);
|
|
90
|
+
console.log(`Testing with ${recordCount} NDJSON records, ~${(ndjsonSize / 1024 / 1024).toFixed(2)}MB`);
|
|
91
|
+
let progressCallCount = 0;
|
|
92
|
+
const buddy = await ConvertBuddy.create({
|
|
93
|
+
inputFormat: "ndjson",
|
|
94
|
+
outputFormat: "csv",
|
|
95
|
+
chunkTargetBytes: 128 * 1024,
|
|
96
|
+
maxMemoryMB: 128,
|
|
97
|
+
progressIntervalBytes: 512 * 1024,
|
|
98
|
+
onProgress: (stats2) => {
|
|
99
|
+
progressCallCount++;
|
|
100
|
+
assert.ok(stats2.maxBufferSize < 128 * 1024 * 1024, "Buffer should stay under memory limit");
|
|
101
|
+
console.log(`Progress: ${stats2.recordsProcessed} records, max buffer: ${(stats2.maxBufferSize / 1024 / 1024).toFixed(2)}MB`);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const chunkSize = 16 * 1024;
|
|
105
|
+
const stream = createChunkedStream(ndjson, chunkSize);
|
|
106
|
+
const outputs = [];
|
|
107
|
+
for await (const chunk of stream) {
|
|
108
|
+
const output = buddy.push(chunk);
|
|
109
|
+
if (output.length > 0) {
|
|
110
|
+
outputs.push(output);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const final = buddy.finish();
|
|
114
|
+
if (final.length > 0) {
|
|
115
|
+
outputs.push(final);
|
|
116
|
+
}
|
|
117
|
+
const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);
|
|
118
|
+
const result = new Uint8Array(totalLength);
|
|
119
|
+
let offset = 0;
|
|
120
|
+
for (const output of outputs) {
|
|
121
|
+
result.set(output, offset);
|
|
122
|
+
offset += output.length;
|
|
123
|
+
}
|
|
124
|
+
const csvString = new TextDecoder().decode(result);
|
|
125
|
+
const lines = csvString.trim().split("\n");
|
|
126
|
+
assert.strictEqual(lines.length, recordCount + 1, "Expected header + records");
|
|
127
|
+
assert.ok(progressCallCount > 0, "Progress callback should have been called");
|
|
128
|
+
const stats = buddy.stats();
|
|
129
|
+
console.log(`Memory stats: max buffer ${(stats.maxBufferSize / 1024).toFixed(2)}KB`);
|
|
130
|
+
});
|
|
131
|
+
it("should handle very large file simulation with proper chunking", async () => {
|
|
132
|
+
const totalRecords = 5e4;
|
|
133
|
+
const batchSize = 5e3;
|
|
134
|
+
const buddy = await ConvertBuddy.create({
|
|
135
|
+
inputFormat: "csv",
|
|
136
|
+
outputFormat: "ndjson",
|
|
137
|
+
chunkTargetBytes: 256 * 1024,
|
|
138
|
+
progressIntervalBytes: 1024 * 1024,
|
|
139
|
+
onProgress: (stats2) => {
|
|
140
|
+
console.log(`Large file: ${(stats2.bytesIn / 1024 / 1024).toFixed(2)}MB processed, ${stats2.throughputMbPerSec.toFixed(1)} MB/s`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const outputs = [];
|
|
144
|
+
let totalBytesProcessed = 0;
|
|
145
|
+
for (let batch = 0; batch < totalRecords / batchSize; batch++) {
|
|
146
|
+
const batchCsv = batch === 0 ? generateLargeCsv(batchSize) : generateLargeCsv(batchSize).split("\n").slice(1).join("\n");
|
|
147
|
+
const batchBuffer = Buffer.from(batchCsv, "utf-8");
|
|
148
|
+
totalBytesProcessed += batchBuffer.length;
|
|
149
|
+
const stream = createChunkedStream(batchCsv, 32 * 1024);
|
|
150
|
+
for await (const chunk of stream) {
|
|
151
|
+
const output = buddy.push(chunk);
|
|
152
|
+
if (output.length > 0) {
|
|
153
|
+
outputs.push(output);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const final = buddy.finish();
|
|
158
|
+
if (final.length > 0) {
|
|
159
|
+
outputs.push(final);
|
|
160
|
+
}
|
|
161
|
+
const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);
|
|
162
|
+
const result = new Uint8Array(totalLength);
|
|
163
|
+
let offset = 0;
|
|
164
|
+
for (const output of outputs) {
|
|
165
|
+
result.set(output, offset);
|
|
166
|
+
offset += output.length;
|
|
167
|
+
}
|
|
168
|
+
const ndjsonString = new TextDecoder().decode(result);
|
|
169
|
+
const lines = ndjsonString.trim().split("\n");
|
|
170
|
+
assert.strictEqual(lines.length, totalRecords, `Expected ${totalRecords} NDJSON records`);
|
|
171
|
+
const stats = buddy.stats();
|
|
172
|
+
console.log(`Processed ${(totalBytesProcessed / 1024 / 1024).toFixed(2)}MB in ${stats.recordsProcessed} records`);
|
|
173
|
+
console.log(`Performance: parse=${stats.parseTimeMs}ms, transform=${stats.transformTimeMs}ms, write=${stats.writeTimeMs}ms`);
|
|
174
|
+
});
|
|
175
|
+
it("should properly track stats during streaming conversion", async () => {
|
|
176
|
+
const rowCount = 1e3;
|
|
177
|
+
const csv = generateLargeCsv(rowCount);
|
|
178
|
+
const buddy = await ConvertBuddy.create({
|
|
179
|
+
inputFormat: "csv",
|
|
180
|
+
outputFormat: "json",
|
|
181
|
+
profile: true
|
|
182
|
+
});
|
|
183
|
+
const initialStats = buddy.stats();
|
|
184
|
+
assert.strictEqual(initialStats.bytesIn, 0);
|
|
185
|
+
assert.strictEqual(initialStats.bytesOut, 0);
|
|
186
|
+
assert.strictEqual(initialStats.recordsProcessed, 0);
|
|
187
|
+
const halfPoint = Math.floor(csv.length / 2);
|
|
188
|
+
const firstHalf = csv.substring(0, halfPoint);
|
|
189
|
+
const secondHalf = csv.substring(halfPoint);
|
|
190
|
+
const firstBuffer = Buffer.from(firstHalf, "utf-8");
|
|
191
|
+
buddy.push(firstBuffer);
|
|
192
|
+
const midStats = buddy.stats();
|
|
193
|
+
assert.ok(midStats.bytesIn > 0, "Should have processed some bytes");
|
|
194
|
+
assert.ok(midStats.parseTimeMs >= 0, "Parse time should be tracked");
|
|
195
|
+
const secondBuffer = Buffer.from(secondHalf, "utf-8");
|
|
196
|
+
buddy.push(secondBuffer);
|
|
197
|
+
buddy.finish();
|
|
198
|
+
const finalStats = buddy.stats();
|
|
199
|
+
assert.ok(finalStats.bytesIn >= Buffer.byteLength(csv), "Should have processed all bytes");
|
|
200
|
+
assert.strictEqual(finalStats.recordsProcessed, rowCount, "Should have processed all records");
|
|
201
|
+
assert.ok(finalStats.throughputMbPerSec > 0, "Throughput should be calculated");
|
|
202
|
+
console.log(`Stats tracking test: ${finalStats.throughputMbPerSec.toFixed(1)} MB/s throughput`);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
//# sourceMappingURL=large-file-stream.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../tests/streaming/large-file-stream.test.ts"],"sourcesContent":["import { describe, it } from \"node:test\";\r\nimport assert from \"node:assert\";\r\nimport { ConvertBuddy } from \"../../index.js\";\r\nimport { Readable } from \"node:stream\";\r\n\r\n/**\r\n * Generate a large CSV dataset for testing\r\n */\r\nfunction generateLargeCsv(rows: number): string {\r\n const header = \"id,name,email,age,city,country,salary,department\\n\";\r\n const lines = [header];\r\n \r\n for (let i = 0; i < rows; i++) {\r\n const line = `${i},User${i},user${i}@example.com,${20 + (i % 50)},City${i % 100},Country${i % 20},${50000 + (i % 100000)},Dept${i % 10}\\n`;\r\n lines.push(line);\r\n }\r\n \r\n return lines.join(\"\");\r\n}\r\n\r\n/**\r\n * Generate a large NDJSON dataset for testing\r\n */\r\nfunction generateLargeNdjson(records: number): string {\r\n const lines: string[] = [];\r\n \r\n for (let i = 0; i < records; i++) {\r\n const obj = {\r\n id: i,\r\n name: `User${i}`,\r\n email: `user${i}@example.com`,\r\n age: 20 + (i % 50),\r\n city: `City${i % 100}`,\r\n country: `Country${i % 20}`,\r\n salary: 50000 + (i % 100000),\r\n department: `Dept${i % 10}`\r\n };\r\n lines.push(JSON.stringify(obj));\r\n }\r\n \r\n return lines.join(\"\\n\") + \"\\n\";\r\n}\r\n\r\n/**\r\n * Create a readable stream from a string, emitting chunks of specified size\r\n */\r\nfunction createChunkedStream(data: string, chunkSize: number): Readable {\r\n const buffer = Buffer.from(data, \"utf-8\");\r\n const chunks: Buffer[] = [];\r\n \r\n for (let i = 0; i < buffer.length; i += chunkSize) {\r\n chunks.push(buffer.slice(i, i + chunkSize));\r\n }\r\n \r\n return Readable.from(chunks);\r\n}\r\n\r\ndescribe(\"Large file streaming tests\", () => {\r\n it(\"should handle streaming large CSV to JSON with progress tracking\", async () => {\r\n const rowCount = 10000;\r\n const csv = generateLargeCsv(rowCount);\r\n const csvSize = Buffer.byteLength(csv);\r\n \r\n console.log(`Testing with ${rowCount} rows, ~${(csvSize / 1024 / 1024).toFixed(2)}MB`);\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n chunkTargetBytes: 64 * 1024,\r\n progressIntervalBytes: 256 * 1024,\r\n onProgress: (stats) => {\r\n const percentage = (stats.bytesIn / csvSize) * 100;\r\n console.log(`Progress: ${percentage.toFixed(1)}% - ${stats.recordsProcessed} records - ${stats.throughputMbPerSec.toFixed(1)} MB/s`);\r\n },\r\n profile: true\r\n });\r\n \r\n // Simulate streaming by chunking the data\r\n const chunkSize = 32 * 1024; // 32KB chunks\r\n const stream = createChunkedStream(csv, chunkSize);\r\n \r\n const outputs: Uint8Array[] = [];\r\n \r\n for await (const chunk of stream) {\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n const jsonString = new TextDecoder().decode(result);\r\n const parsed = JSON.parse(jsonString);\r\n \r\n assert.strictEqual(parsed.length, rowCount, `Expected ${rowCount} records`);\r\n assert.strictEqual(parsed[0].id, 0);\r\n assert.strictEqual(parsed[0].name, \"User0\");\r\n \r\n const stats = buddy.stats();\r\n console.log(`Final stats: ${stats.bytesIn} bytes in, ${stats.bytesOut} bytes out, ${stats.recordsProcessed} records`);\r\n assert.strictEqual(stats.recordsProcessed, rowCount);\r\n });\r\n \r\n it(\"should handle streaming large NDJSON to CSV with memory limits\", async () => {\r\n const recordCount = 5000;\r\n const ndjson = generateLargeNdjson(recordCount);\r\n const ndjsonSize = Buffer.byteLength(ndjson);\r\n \r\n console.log(`Testing with ${recordCount} NDJSON records, ~${(ndjsonSize / 1024 / 1024).toFixed(2)}MB`);\r\n \r\n let progressCallCount = 0;\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"ndjson\",\r\n outputFormat: \"csv\",\r\n chunkTargetBytes: 128 * 1024,\r\n maxMemoryMB: 128,\r\n progressIntervalBytes: 512 * 1024,\r\n onProgress: (stats) => {\r\n progressCallCount++;\r\n assert.ok(stats.maxBufferSize < 128 * 1024 * 1024, \"Buffer should stay under memory limit\");\r\n console.log(`Progress: ${stats.recordsProcessed} records, max buffer: ${(stats.maxBufferSize / 1024 / 1024).toFixed(2)}MB`);\r\n }\r\n });\r\n \r\n // Stream with smaller chunks\r\n const chunkSize = 16 * 1024; // 16KB chunks\r\n const stream = createChunkedStream(ndjson, chunkSize);\r\n \r\n const outputs: Uint8Array[] = [];\r\n \r\n for await (const chunk of stream) {\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n const csvString = new TextDecoder().decode(result);\r\n const lines = csvString.trim().split(\"\\n\");\r\n \r\n // CSV has header + records\r\n assert.strictEqual(lines.length, recordCount + 1, \"Expected header + records\");\r\n assert.ok(progressCallCount > 0, \"Progress callback should have been called\");\r\n \r\n const stats = buddy.stats();\r\n console.log(`Memory stats: max buffer ${(stats.maxBufferSize / 1024).toFixed(2)}KB`);\r\n });\r\n \r\n it(\"should handle very large file simulation with proper chunking\", async () => {\r\n // Simulate a 50MB file by generating data in chunks\r\n const totalRecords = 50000;\r\n const batchSize = 5000;\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"ndjson\",\r\n chunkTargetBytes: 256 * 1024,\r\n progressIntervalBytes: 1024 * 1024,\r\n onProgress: (stats) => {\r\n console.log(`Large file: ${(stats.bytesIn / 1024 / 1024).toFixed(2)}MB processed, ${stats.throughputMbPerSec.toFixed(1)} MB/s`);\r\n }\r\n });\r\n \r\n const outputs: Uint8Array[] = [];\r\n let totalBytesProcessed = 0;\r\n \r\n // Process in batches to simulate very large file\r\n for (let batch = 0; batch < totalRecords / batchSize; batch++) {\r\n const batchCsv = batch === 0 \r\n ? generateLargeCsv(batchSize) \r\n : generateLargeCsv(batchSize).split(\"\\n\").slice(1).join(\"\\n\"); // Skip header for subsequent batches\r\n \r\n const batchBuffer = Buffer.from(batchCsv, \"utf-8\");\r\n totalBytesProcessed += batchBuffer.length;\r\n \r\n // Stream this batch in chunks\r\n const stream = createChunkedStream(batchCsv, 32 * 1024);\r\n \r\n for await (const chunk of stream) {\r\n const output = buddy.push(chunk);\r\n if (output.length > 0) {\r\n outputs.push(output);\r\n }\r\n }\r\n }\r\n \r\n const final = buddy.finish();\r\n if (final.length > 0) {\r\n outputs.push(final);\r\n }\r\n \r\n // Combine outputs\r\n const totalLength = outputs.reduce((sum, arr) => sum + arr.length, 0);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const output of outputs) {\r\n result.set(output, offset);\r\n offset += output.length;\r\n }\r\n \r\n const ndjsonString = new TextDecoder().decode(result);\r\n const lines = ndjsonString.trim().split(\"\\n\");\r\n \r\n assert.strictEqual(lines.length, totalRecords, `Expected ${totalRecords} NDJSON records`);\r\n \r\n const stats = buddy.stats();\r\n console.log(`Processed ${(totalBytesProcessed / 1024 / 1024).toFixed(2)}MB in ${stats.recordsProcessed} records`);\r\n console.log(`Performance: parse=${stats.parseTimeMs}ms, transform=${stats.transformTimeMs}ms, write=${stats.writeTimeMs}ms`);\r\n });\r\n \r\n it(\"should properly track stats during streaming conversion\", async () => {\r\n const rowCount = 1000;\r\n const csv = generateLargeCsv(rowCount);\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n profile: true\r\n });\r\n \r\n // Get initial stats (should be all zeros)\r\n const initialStats = buddy.stats();\r\n assert.strictEqual(initialStats.bytesIn, 0);\r\n assert.strictEqual(initialStats.bytesOut, 0);\r\n assert.strictEqual(initialStats.recordsProcessed, 0);\r\n \r\n // Push half the data\r\n const halfPoint = Math.floor(csv.length / 2);\r\n const firstHalf = csv.substring(0, halfPoint);\r\n const secondHalf = csv.substring(halfPoint);\r\n \r\n const firstBuffer = Buffer.from(firstHalf, \"utf-8\");\r\n buddy.push(firstBuffer);\r\n \r\n const midStats = buddy.stats();\r\n assert.ok(midStats.bytesIn > 0, \"Should have processed some bytes\");\r\n assert.ok(midStats.parseTimeMs >= 0, \"Parse time should be tracked\");\r\n \r\n // Push second half\r\n const secondBuffer = Buffer.from(secondHalf, \"utf-8\");\r\n buddy.push(secondBuffer);\r\n buddy.finish();\r\n \r\n const finalStats = buddy.stats();\r\n assert.ok(finalStats.bytesIn >= Buffer.byteLength(csv), \"Should have processed all bytes\");\r\n assert.strictEqual(finalStats.recordsProcessed, rowCount, \"Should have processed all records\");\r\n assert.ok(finalStats.throughputMbPerSec > 0, \"Throughput should be calculated\");\r\n \r\n console.log(`Stats tracking test: ${finalStats.throughputMbPerSec.toFixed(1)} MB/s throughput`);\r\n });\r\n});\r\n"],"mappings":"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","names":["stats"]}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { ConvertBuddy } from "../../index.js";
|
|
4
|
+
function generateCsv(rows) {
|
|
5
|
+
const header = "id,name,value\n";
|
|
6
|
+
const lines = [header];
|
|
7
|
+
for (let i = 0; i < rows; i++) {
|
|
8
|
+
lines.push(`${i},Item${i},${i * 10}
|
|
9
|
+
`);
|
|
10
|
+
}
|
|
11
|
+
return lines.join("");
|
|
12
|
+
}
|
|
13
|
+
describe("Stats object validation tests", () => {
|
|
14
|
+
it("should provide accurate Stats interface fields", async () => {
|
|
15
|
+
const csv = generateCsv(100);
|
|
16
|
+
const csvBuffer = Buffer.from(csv, "utf-8");
|
|
17
|
+
const buddy = await ConvertBuddy.create({
|
|
18
|
+
inputFormat: "csv",
|
|
19
|
+
outputFormat: "json",
|
|
20
|
+
profile: true
|
|
21
|
+
});
|
|
22
|
+
const initial = buddy.stats();
|
|
23
|
+
assert.strictEqual(initial.bytesIn, 0, "Initial bytesIn should be 0");
|
|
24
|
+
assert.strictEqual(initial.bytesOut, 0, "Initial bytesOut should be 0");
|
|
25
|
+
assert.strictEqual(initial.chunksIn, 0, "Initial chunksIn should be 0");
|
|
26
|
+
assert.strictEqual(initial.recordsProcessed, 0, "Initial recordsProcessed should be 0");
|
|
27
|
+
assert.strictEqual(initial.parseTimeMs, 0, "Initial parseTimeMs should be 0");
|
|
28
|
+
assert.strictEqual(initial.transformTimeMs, 0, "Initial transformTimeMs should be 0");
|
|
29
|
+
assert.strictEqual(initial.writeTimeMs, 0, "Initial writeTimeMs should be 0");
|
|
30
|
+
assert.strictEqual(initial.maxBufferSize, 0, "Initial maxBufferSize should be 0");
|
|
31
|
+
assert.strictEqual(initial.currentPartialSize, 0, "Initial currentPartialSize should be 0");
|
|
32
|
+
assert.strictEqual(initial.throughputMbPerSec, 0, "Initial throughputMbPerSec should be 0");
|
|
33
|
+
buddy.push(csvBuffer);
|
|
34
|
+
const during = buddy.stats();
|
|
35
|
+
assert.ok(during.bytesIn > 0, "bytesIn should increase");
|
|
36
|
+
assert.ok(during.chunksIn > 0, "chunksIn should increase");
|
|
37
|
+
assert.ok(during.recordsProcessed >= 0, "recordsProcessed should be non-negative");
|
|
38
|
+
assert.ok(during.parseTimeMs >= 0, "parseTimeMs should be non-negative");
|
|
39
|
+
assert.ok(during.maxBufferSize >= 0, "maxBufferSize should be non-negative");
|
|
40
|
+
buddy.finish();
|
|
41
|
+
const final = buddy.stats();
|
|
42
|
+
assert.strictEqual(final.bytesIn, csvBuffer.length, "Final bytesIn should match input size");
|
|
43
|
+
assert.strictEqual(final.recordsProcessed, 100, "Should process 100 records");
|
|
44
|
+
assert.ok(final.bytesOut > 0, "Should produce output");
|
|
45
|
+
assert.ok(final.throughputMbPerSec >= 0, "Throughput should be calculated");
|
|
46
|
+
console.log("Final stats:", final);
|
|
47
|
+
});
|
|
48
|
+
it("should track progress via onProgress callback", async () => {
|
|
49
|
+
const csv = generateCsv(1e3);
|
|
50
|
+
const csvSize = Buffer.byteLength(csv);
|
|
51
|
+
const progressUpdates = [];
|
|
52
|
+
const buddy = await ConvertBuddy.create({
|
|
53
|
+
inputFormat: "csv",
|
|
54
|
+
outputFormat: "json",
|
|
55
|
+
progressIntervalBytes: 512,
|
|
56
|
+
// Small interval for testing
|
|
57
|
+
onProgress: (stats) => {
|
|
58
|
+
progressUpdates.push({ ...stats });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const buffer = Buffer.from(csv, "utf-8");
|
|
62
|
+
const chunkSize = 1024;
|
|
63
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
64
|
+
const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));
|
|
65
|
+
buddy.push(chunk);
|
|
66
|
+
}
|
|
67
|
+
buddy.finish();
|
|
68
|
+
assert.ok(progressUpdates.length > 0, "Should have progress updates");
|
|
69
|
+
for (let i = 1; i < progressUpdates.length; i++) {
|
|
70
|
+
assert.ok(
|
|
71
|
+
progressUpdates[i].bytesIn >= progressUpdates[i - 1].bytesIn,
|
|
72
|
+
"bytesIn should increase or stay same"
|
|
73
|
+
);
|
|
74
|
+
assert.ok(
|
|
75
|
+
progressUpdates[i].recordsProcessed >= progressUpdates[i - 1].recordsProcessed,
|
|
76
|
+
"recordsProcessed should increase or stay same"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
console.log(`Progress callback triggered ${progressUpdates.length} times`);
|
|
80
|
+
});
|
|
81
|
+
it("should calculate compression ratio correctly", async () => {
|
|
82
|
+
const csv = generateCsv(100);
|
|
83
|
+
const csvBuffer = Buffer.from(csv, "utf-8");
|
|
84
|
+
const buddy1 = await ConvertBuddy.create({
|
|
85
|
+
inputFormat: "csv",
|
|
86
|
+
outputFormat: "json"
|
|
87
|
+
});
|
|
88
|
+
buddy1.push(csvBuffer);
|
|
89
|
+
buddy1.finish();
|
|
90
|
+
const stats1 = buddy1.stats();
|
|
91
|
+
const ratio1 = stats1.bytesOut / stats1.bytesIn;
|
|
92
|
+
console.log(`CSV \u2192 JSON ratio: ${ratio1.toFixed(2)}x (${stats1.bytesIn} \u2192 ${stats1.bytesOut} bytes)`);
|
|
93
|
+
assert.ok(ratio1 > 1, "CSV to JSON typically expands");
|
|
94
|
+
const jsonData = JSON.stringify([
|
|
95
|
+
{ id: 1, name: "Alice", value: 100 },
|
|
96
|
+
{ id: 2, name: "Bob", value: 200 }
|
|
97
|
+
]);
|
|
98
|
+
const buddy2 = await ConvertBuddy.create({
|
|
99
|
+
inputFormat: "json",
|
|
100
|
+
outputFormat: "csv"
|
|
101
|
+
});
|
|
102
|
+
const jsonBuffer = Buffer.from(jsonData, "utf-8");
|
|
103
|
+
buddy2.push(jsonBuffer);
|
|
104
|
+
buddy2.finish();
|
|
105
|
+
const stats2 = buddy2.stats();
|
|
106
|
+
const ratio2 = stats2.bytesOut / stats2.bytesIn;
|
|
107
|
+
console.log(`JSON \u2192 CSV ratio: ${ratio2.toFixed(2)}x (${stats2.bytesIn} \u2192 ${stats2.bytesOut} bytes)`);
|
|
108
|
+
assert.ok(ratio2 < 1, "JSON to CSV typically shrinks");
|
|
109
|
+
});
|
|
110
|
+
it("should track processing rate (records per second)", async () => {
|
|
111
|
+
const csv = generateCsv(5e3);
|
|
112
|
+
const csvBuffer = Buffer.from(csv, "utf-8");
|
|
113
|
+
const startTime = Date.now();
|
|
114
|
+
const buddy = await ConvertBuddy.create({
|
|
115
|
+
inputFormat: "csv",
|
|
116
|
+
outputFormat: "ndjson",
|
|
117
|
+
profile: true
|
|
118
|
+
});
|
|
119
|
+
buddy.push(csvBuffer);
|
|
120
|
+
buddy.finish();
|
|
121
|
+
const endTime = Date.now();
|
|
122
|
+
const elapsedSec = (endTime - startTime) / 1e3;
|
|
123
|
+
const stats = buddy.stats();
|
|
124
|
+
const recordsPerSec = stats.recordsProcessed / elapsedSec;
|
|
125
|
+
console.log(`Processing rate: ${recordsPerSec.toFixed(0)} records/sec`);
|
|
126
|
+
console.log(`Throughput: ${stats.throughputMbPerSec.toFixed(1)} MB/s`);
|
|
127
|
+
assert.ok(recordsPerSec > 0, "Should have positive records per second");
|
|
128
|
+
assert.strictEqual(stats.recordsProcessed, 5e3);
|
|
129
|
+
});
|
|
130
|
+
it("should provide time breakdown with profile enabled", async () => {
|
|
131
|
+
const csv = generateCsv(1e3);
|
|
132
|
+
const csvBuffer = Buffer.from(csv, "utf-8");
|
|
133
|
+
const buddy = await ConvertBuddy.create({
|
|
134
|
+
inputFormat: "csv",
|
|
135
|
+
outputFormat: "json",
|
|
136
|
+
profile: true
|
|
137
|
+
});
|
|
138
|
+
buddy.push(csvBuffer);
|
|
139
|
+
buddy.finish();
|
|
140
|
+
const stats = buddy.stats();
|
|
141
|
+
console.log("Time breakdown:");
|
|
142
|
+
console.log(` Parse: ${stats.parseTimeMs}ms`);
|
|
143
|
+
console.log(` Transform: ${stats.transformTimeMs}ms`);
|
|
144
|
+
console.log(` Write: ${stats.writeTimeMs}ms`);
|
|
145
|
+
const totalTime = stats.parseTimeMs + stats.transformTimeMs + stats.writeTimeMs;
|
|
146
|
+
assert.ok(stats.parseTimeMs >= 0, "Parse time should be non-negative");
|
|
147
|
+
assert.ok(stats.transformTimeMs >= 0, "Transform time should be non-negative");
|
|
148
|
+
assert.ok(stats.writeTimeMs >= 0, "Write time should be non-negative");
|
|
149
|
+
assert.ok(totalTime > 0, "Total time should be positive");
|
|
150
|
+
console.log(` Parse: ${(stats.parseTimeMs / totalTime * 100).toFixed(1)}%`);
|
|
151
|
+
console.log(` Transform: ${(stats.transformTimeMs / totalTime * 100).toFixed(1)}%`);
|
|
152
|
+
console.log(` Write: ${(stats.writeTimeMs / totalTime * 100).toFixed(1)}%`);
|
|
153
|
+
});
|
|
154
|
+
it("should track memory usage accurately", async () => {
|
|
155
|
+
const csv = generateCsv(2e3);
|
|
156
|
+
const csvBuffer = Buffer.from(csv, "utf-8");
|
|
157
|
+
const buddy = await ConvertBuddy.create({
|
|
158
|
+
inputFormat: "csv",
|
|
159
|
+
outputFormat: "json",
|
|
160
|
+
maxMemoryMB: 64
|
|
161
|
+
});
|
|
162
|
+
const chunkSize = 1024;
|
|
163
|
+
let maxObservedBuffer = 0;
|
|
164
|
+
for (let i = 0; i < csvBuffer.length; i += chunkSize) {
|
|
165
|
+
const chunk = csvBuffer.slice(i, Math.min(i + chunkSize, csvBuffer.length));
|
|
166
|
+
buddy.push(chunk);
|
|
167
|
+
const stats = buddy.stats();
|
|
168
|
+
maxObservedBuffer = Math.max(maxObservedBuffer, stats.maxBufferSize);
|
|
169
|
+
}
|
|
170
|
+
buddy.finish();
|
|
171
|
+
const finalStats = buddy.stats();
|
|
172
|
+
console.log(`Peak memory: ${(finalStats.maxBufferSize / 1024 / 1024).toFixed(2)}MB`);
|
|
173
|
+
console.log(`Current partial: ${(finalStats.currentPartialSize / 1024).toFixed(2)}KB`);
|
|
174
|
+
assert.ok(finalStats.maxBufferSize > 0, "Should track peak memory");
|
|
175
|
+
assert.ok(finalStats.maxBufferSize < 64 * 1024 * 1024, "Should stay under memory limit");
|
|
176
|
+
});
|
|
177
|
+
it("should calculate ETA correctly with progress", async () => {
|
|
178
|
+
const totalRows = 1e4;
|
|
179
|
+
const csv = generateCsv(totalRows);
|
|
180
|
+
const totalBytes = Buffer.byteLength(csv);
|
|
181
|
+
const buffer = Buffer.from(csv, "utf-8");
|
|
182
|
+
const startTime = Date.now();
|
|
183
|
+
const progressPoints = [];
|
|
184
|
+
const buddy = await ConvertBuddy.create({
|
|
185
|
+
inputFormat: "csv",
|
|
186
|
+
outputFormat: "json",
|
|
187
|
+
progressIntervalBytes: totalBytes / 10,
|
|
188
|
+
// ~10 updates
|
|
189
|
+
onProgress: (stats) => {
|
|
190
|
+
const elapsedMs = Date.now() - startTime;
|
|
191
|
+
const bytesPerMs = stats.bytesIn / elapsedMs;
|
|
192
|
+
const remainingBytes = totalBytes - stats.bytesIn;
|
|
193
|
+
const etaMs = remainingBytes / bytesPerMs;
|
|
194
|
+
progressPoints.push({
|
|
195
|
+
elapsed: elapsedMs,
|
|
196
|
+
bytesIn: stats.bytesIn,
|
|
197
|
+
eta: etaMs
|
|
198
|
+
});
|
|
199
|
+
console.log(`Progress: ${(stats.bytesIn / totalBytes * 100).toFixed(1)}%, ETA: ${(etaMs / 1e3).toFixed(1)}s`);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
const chunkSize = 8 * 1024;
|
|
203
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
204
|
+
const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));
|
|
205
|
+
buddy.push(chunk);
|
|
206
|
+
}
|
|
207
|
+
buddy.finish();
|
|
208
|
+
assert.ok(progressPoints.length > 0, "Should have progress points");
|
|
209
|
+
if (progressPoints.length > 2) {
|
|
210
|
+
const firstEta = progressPoints[0].eta;
|
|
211
|
+
const lastEta = progressPoints[progressPoints.length - 1].eta;
|
|
212
|
+
console.log(`First ETA: ${(firstEta / 1e3).toFixed(1)}s, Last ETA: ${(lastEta / 1e3).toFixed(1)}s`);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
//# sourceMappingURL=stats-object.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../tests/streaming/stats-object.test.ts"],"sourcesContent":["import { describe, it } from \"node:test\";\r\nimport assert from \"node:assert\";\r\nimport { ConvertBuddy } from \"../../index.js\";\r\n\r\n/**\r\n * Generate test CSV data\r\n */\r\nfunction generateCsv(rows: number): string {\r\n const header = \"id,name,value\\n\";\r\n const lines = [header];\r\n \r\n for (let i = 0; i < rows; i++) {\r\n lines.push(`${i},Item${i},${i * 10}\\n`);\r\n }\r\n \r\n return lines.join(\"\");\r\n}\r\n\r\ndescribe(\"Stats object validation tests\", () => {\r\n it(\"should provide accurate Stats interface fields\", async () => {\r\n const csv = generateCsv(100);\r\n const csvBuffer = Buffer.from(csv, \"utf-8\");\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n profile: true\r\n });\r\n \r\n // Initial stats\r\n const initial = buddy.stats();\r\n assert.strictEqual(initial.bytesIn, 0, \"Initial bytesIn should be 0\");\r\n assert.strictEqual(initial.bytesOut, 0, \"Initial bytesOut should be 0\");\r\n assert.strictEqual(initial.chunksIn, 0, \"Initial chunksIn should be 0\");\r\n assert.strictEqual(initial.recordsProcessed, 0, \"Initial recordsProcessed should be 0\");\r\n assert.strictEqual(initial.parseTimeMs, 0, \"Initial parseTimeMs should be 0\");\r\n assert.strictEqual(initial.transformTimeMs, 0, \"Initial transformTimeMs should be 0\");\r\n assert.strictEqual(initial.writeTimeMs, 0, \"Initial writeTimeMs should be 0\");\r\n assert.strictEqual(initial.maxBufferSize, 0, \"Initial maxBufferSize should be 0\");\r\n assert.strictEqual(initial.currentPartialSize, 0, \"Initial currentPartialSize should be 0\");\r\n assert.strictEqual(initial.throughputMbPerSec, 0, \"Initial throughputMbPerSec should be 0\");\r\n \r\n // Process data\r\n buddy.push(csvBuffer);\r\n \r\n const during = buddy.stats();\r\n assert.ok(during.bytesIn > 0, \"bytesIn should increase\");\r\n assert.ok(during.chunksIn > 0, \"chunksIn should increase\");\r\n assert.ok(during.recordsProcessed >= 0, \"recordsProcessed should be non-negative\");\r\n assert.ok(during.parseTimeMs >= 0, \"parseTimeMs should be non-negative\");\r\n assert.ok(during.maxBufferSize >= 0, \"maxBufferSize should be non-negative\");\r\n \r\n buddy.finish();\r\n \r\n const final = buddy.stats();\r\n assert.strictEqual(final.bytesIn, csvBuffer.length, \"Final bytesIn should match input size\");\r\n assert.strictEqual(final.recordsProcessed, 100, \"Should process 100 records\");\r\n assert.ok(final.bytesOut > 0, \"Should produce output\");\r\n assert.ok(final.throughputMbPerSec >= 0, \"Throughput should be calculated\");\r\n \r\n console.log(\"Final stats:\", final);\r\n });\r\n \r\n it(\"should track progress via onProgress callback\", async () => {\r\n const csv = generateCsv(1000);\r\n const csvSize = Buffer.byteLength(csv);\r\n const progressUpdates: any[] = [];\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n progressIntervalBytes: 512, // Small interval for testing\r\n onProgress: (stats) => {\r\n progressUpdates.push({ ...stats });\r\n }\r\n });\r\n \r\n // Push in chunks to trigger multiple progress callbacks\r\n const buffer = Buffer.from(csv, \"utf-8\");\r\n const chunkSize = 1024;\r\n \r\n for (let i = 0; i < buffer.length; i += chunkSize) {\r\n const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));\r\n buddy.push(chunk);\r\n }\r\n \r\n buddy.finish();\r\n \r\n assert.ok(progressUpdates.length > 0, \"Should have progress updates\");\r\n \r\n // Verify progress is monotonically increasing\r\n for (let i = 1; i < progressUpdates.length; i++) {\r\n assert.ok(\r\n progressUpdates[i].bytesIn >= progressUpdates[i - 1].bytesIn,\r\n \"bytesIn should increase or stay same\"\r\n );\r\n assert.ok(\r\n progressUpdates[i].recordsProcessed >= progressUpdates[i - 1].recordsProcessed,\r\n \"recordsProcessed should increase or stay same\"\r\n );\r\n }\r\n \r\n console.log(`Progress callback triggered ${progressUpdates.length} times`);\r\n });\r\n \r\n it(\"should calculate compression ratio correctly\", async () => {\r\n const csv = generateCsv(100);\r\n const csvBuffer = Buffer.from(csv, \"utf-8\");\r\n \r\n // CSV to JSON (typically expands)\r\n const buddy1 = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\"\r\n });\r\n \r\n buddy1.push(csvBuffer);\r\n buddy1.finish();\r\n \r\n const stats1 = buddy1.stats();\r\n const ratio1 = stats1.bytesOut / stats1.bytesIn;\r\n \r\n console.log(`CSV → JSON ratio: ${ratio1.toFixed(2)}x (${stats1.bytesIn} → ${stats1.bytesOut} bytes)`);\r\n assert.ok(ratio1 > 1, \"CSV to JSON typically expands\");\r\n \r\n // JSON to CSV (typically shrinks)\r\n const jsonData = JSON.stringify([\r\n { id: 1, name: \"Alice\", value: 100 },\r\n { id: 2, name: \"Bob\", value: 200 }\r\n ]);\r\n \r\n const buddy2 = await ConvertBuddy.create({\r\n inputFormat: \"json\",\r\n outputFormat: \"csv\"\r\n });\r\n \r\n const jsonBuffer = Buffer.from(jsonData, \"utf-8\");\r\n buddy2.push(jsonBuffer);\r\n buddy2.finish();\r\n \r\n const stats2 = buddy2.stats();\r\n const ratio2 = stats2.bytesOut / stats2.bytesIn;\r\n \r\n console.log(`JSON → CSV ratio: ${ratio2.toFixed(2)}x (${stats2.bytesIn} → ${stats2.bytesOut} bytes)`);\r\n assert.ok(ratio2 < 1, \"JSON to CSV typically shrinks\");\r\n });\r\n \r\n it(\"should track processing rate (records per second)\", async () => {\r\n const csv = generateCsv(5000);\r\n const csvBuffer = Buffer.from(csv, \"utf-8\");\r\n \r\n const startTime = Date.now();\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"ndjson\",\r\n profile: true\r\n });\r\n \r\n buddy.push(csvBuffer);\r\n buddy.finish();\r\n \r\n const endTime = Date.now();\r\n const elapsedSec = (endTime - startTime) / 1000;\r\n \r\n const stats = buddy.stats();\r\n const recordsPerSec = stats.recordsProcessed / elapsedSec;\r\n \r\n console.log(`Processing rate: ${recordsPerSec.toFixed(0)} records/sec`);\r\n console.log(`Throughput: ${stats.throughputMbPerSec.toFixed(1)} MB/s`);\r\n \r\n assert.ok(recordsPerSec > 0, \"Should have positive records per second\");\r\n assert.strictEqual(stats.recordsProcessed, 5000);\r\n });\r\n \r\n it(\"should provide time breakdown with profile enabled\", async () => {\r\n const csv = generateCsv(1000);\r\n const csvBuffer = Buffer.from(csv, \"utf-8\");\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n profile: true\r\n });\r\n \r\n buddy.push(csvBuffer);\r\n buddy.finish();\r\n \r\n const stats = buddy.stats();\r\n \r\n console.log(\"Time breakdown:\");\r\n console.log(` Parse: ${stats.parseTimeMs}ms`);\r\n console.log(` Transform: ${stats.transformTimeMs}ms`);\r\n console.log(` Write: ${stats.writeTimeMs}ms`);\r\n \r\n const totalTime = stats.parseTimeMs + stats.transformTimeMs + stats.writeTimeMs;\r\n \r\n assert.ok(stats.parseTimeMs >= 0, \"Parse time should be non-negative\");\r\n assert.ok(stats.transformTimeMs >= 0, \"Transform time should be non-negative\");\r\n assert.ok(stats.writeTimeMs >= 0, \"Write time should be non-negative\");\r\n assert.ok(totalTime > 0, \"Total time should be positive\");\r\n \r\n // Log percentages\r\n console.log(` Parse: ${(stats.parseTimeMs / totalTime * 100).toFixed(1)}%`);\r\n console.log(` Transform: ${(stats.transformTimeMs / totalTime * 100).toFixed(1)}%`);\r\n console.log(` Write: ${(stats.writeTimeMs / totalTime * 100).toFixed(1)}%`);\r\n });\r\n \r\n it(\"should track memory usage accurately\", async () => {\r\n const csv = generateCsv(2000);\r\n const csvBuffer = Buffer.from(csv, \"utf-8\");\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n maxMemoryMB: 64\r\n });\r\n \r\n // Process in chunks to see buffer growth\r\n const chunkSize = 1024;\r\n let maxObservedBuffer = 0;\r\n \r\n for (let i = 0; i < csvBuffer.length; i += chunkSize) {\r\n const chunk = csvBuffer.slice(i, Math.min(i + chunkSize, csvBuffer.length));\r\n buddy.push(chunk);\r\n \r\n const stats = buddy.stats();\r\n maxObservedBuffer = Math.max(maxObservedBuffer, stats.maxBufferSize);\r\n }\r\n \r\n buddy.finish();\r\n \r\n const finalStats = buddy.stats();\r\n \r\n console.log(`Peak memory: ${(finalStats.maxBufferSize / 1024 / 1024).toFixed(2)}MB`);\r\n console.log(`Current partial: ${(finalStats.currentPartialSize / 1024).toFixed(2)}KB`);\r\n \r\n assert.ok(finalStats.maxBufferSize > 0, \"Should track peak memory\");\r\n assert.ok(finalStats.maxBufferSize < 64 * 1024 * 1024, \"Should stay under memory limit\");\r\n });\r\n \r\n it(\"should calculate ETA correctly with progress\", async () => {\r\n const totalRows = 10000;\r\n const csv = generateCsv(totalRows);\r\n const totalBytes = Buffer.byteLength(csv);\r\n const buffer = Buffer.from(csv, \"utf-8\");\r\n \r\n const startTime = Date.now();\r\n const progressPoints: Array<{ elapsed: number; bytesIn: number; eta: number }> = [];\r\n \r\n const buddy = await ConvertBuddy.create({\r\n inputFormat: \"csv\",\r\n outputFormat: \"json\",\r\n progressIntervalBytes: totalBytes / 10, // ~10 updates\r\n onProgress: (stats) => {\r\n const elapsedMs = Date.now() - startTime;\r\n const bytesPerMs = stats.bytesIn / elapsedMs;\r\n const remainingBytes = totalBytes - stats.bytesIn;\r\n const etaMs = remainingBytes / bytesPerMs;\r\n \r\n progressPoints.push({\r\n elapsed: elapsedMs,\r\n bytesIn: stats.bytesIn,\r\n eta: etaMs\r\n });\r\n \r\n console.log(`Progress: ${(stats.bytesIn / totalBytes * 100).toFixed(1)}%, ETA: ${(etaMs / 1000).toFixed(1)}s`);\r\n }\r\n });\r\n \r\n // Push in chunks\r\n const chunkSize = 8 * 1024;\r\n for (let i = 0; i < buffer.length; i += chunkSize) {\r\n const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));\r\n buddy.push(chunk);\r\n }\r\n \r\n buddy.finish();\r\n \r\n assert.ok(progressPoints.length > 0, \"Should have progress points\");\r\n \r\n // ETA should generally decrease over time (or stay relatively stable)\r\n if (progressPoints.length > 2) {\r\n const firstEta = progressPoints[0].eta;\r\n const lastEta = progressPoints[progressPoints.length - 1].eta;\r\n console.log(`First ETA: ${(firstEta / 1000).toFixed(1)}s, Last ETA: ${(lastEta / 1000).toFixed(1)}s`);\r\n }\r\n });\r\n});\r\n"],"mappings":"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","names":[]}
|