import { type ParserOptions, StreamParser, type StreamParserOptions, } from '@json2csv/plainjs'; class JSON2CSVWHATWGTransformer extends StreamParser implements Transformer { private controller!: TransformStreamDefaultController; constructor( opts: ParserOptions = {}, asyncOpts: StreamParserOptions = {}, ) { super(opts, asyncOpts); } override onData(data: string) { this.controller.enqueue(data); } override onError(err: Error) { this.controller.error(err); } override onEnd() { this.controller.terminate(); } start(controller: TransformStreamDefaultController) { this.controller = controller; } transform(chunk: TRaw) { this.tokenizer.write(chunk as any); } flush() { this.end(); } } export interface AwaitableReadableStream extends ReadableStream { promise(): Promise; } export default class JSON2CSVWHATWGTransformStream< TRaw extends object, T extends object, > extends TransformStream implements TransformStream, EventTarget { override readonly readable!: AwaitableReadableStream; private delegate?: DocumentFragment; // TODO should be (event: Event): boolean constructor( opts: ParserOptions = {}, asyncOpts: StreamParserOptions = {}, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy, ) { const transformer = new JSON2CSVWHATWGTransformer(opts, asyncOpts); super(transformer, writableStrategy, readableStrategy); // Implement the EventTarget interface when running on a browser if (typeof document === 'object') { this.delegate = document.createDocumentFragment(); transformer.onHeader = (header) => this.dispatchEvent(new CustomEvent('header', { detail: header })); transformer.onLine = (line) => this.dispatchEvent(new CustomEvent('line', { detail: line })); const origOnData = transformer.onData.bind(transformer); transformer.onData = (data) => { origOnData(data); this.dispatchEvent(new CustomEvent('data', { detail: data })); }; } this.readable.promise = async () => { let csv = ''; const outputStream = new WritableStream({ write(chunk) { csv += chunk; }, }); await this.readable.pipeTo(outputStream); return csv; }; } public addEventListener( type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions | undefined, ): void { this.delegate?.addEventListener(type, callback, options); } public dispatchEvent(event: Event): boolean { return this.delegate?.dispatchEvent(event) ?? false; } public removeEventListener( type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined, ): void { this.delegate?.removeEventListener(type, callback, options); } }