react-csv-autopilot 1.1.0 → 1.1.1-beta.1

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/index.cjs CHANGED
@@ -378,3 +378,4 @@ function useMessageExportCSV(cb) {
378
378
  }, [cb]);
379
379
  }
380
380
  var useMessageExportCSV_default = useMessageExportCSV;
381
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks/useExportCSV.ts","../src/core/controllers/ExportController.ts","../src/core/contants/index.ts","../src/core/WorkerManager.ts","../src/core/strategy/BolbExportStrategy.ts","../src/core/strategy/FsAccessExportStrategy.ts","../src/core/createExportController.ts","../src/core/ExportControllerSingleton.ts","../src/hooks/useMessageExportCSV.ts"],"sourcesContent":["export type { ExportController } from \"./core/controllers/ExportController\";\nexport { Column, ExportParams } from \"./core/types\";\nexport { useExportCSV, useMessageExportCSV } from \"./hooks\";\n","import { useRef } from \"react\";\nimport { type ExportController, ExportControllerSingleton } from \"../core\";\n\nfunction useExportCSV() {\n const exportCallbackRef = useRef<ExportController>(ExportControllerSingleton.init());\n\n return {\n handler: exportCallbackRef?.current,\n };\n}\n\nexport default useExportCSV;\n","import type BolbExportStrategy from \"../strategy/BolbExportStrategy\";\nimport type FsAccessExportStrategy from \"../strategy/FsAccessExportStrategy\";\nimport type { ExportParams, ExportResponse, ExportStrategy } from \"../types\";\n\ntype ExportControllerDeps = {\n fsAccessStrategy: FsAccessExportStrategy;\n blobExportStrategy: BolbExportStrategy;\n};\n\nexport class ExportController {\n constructor(private readonly deps: ExportControllerDeps) {}\n\n public async start<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const strategy = this.#resolveStrategy();\n\n return strategy.export(params);\n }\n\n #canUseFSAccess() {\n return typeof window.showSaveFilePicker === \"function\";\n }\n\n #resolveStrategy(): ExportStrategy {\n if (this.#canUseFSAccess()) {\n return this.deps.fsAccessStrategy;\n }\n\n return this.deps.blobExportStrategy;\n }\n}\n","export const WEB_WORKER_NAME = \"scv-worker\";\nexport const BROADCAST_CHANNEL_NAME = \"react-csv-exporter\";\n","import { WEB_WORKER_NAME } from \"./contants\";\nimport type { JobId, ToWorkerMessage } from \"./types\";\n\nconst pending = new Map<JobId, { resolve: (value: unknown) => void; reject: (reason?: ErrorEvent) => void }>();\n\nclass WorkerManager {\n #worker: Worker | null;\n\n constructor() {\n const workerUrl = new URL(\"./worker.js\", import.meta.url);\n\n this.#worker = new Worker(workerUrl, {\n name: WEB_WORKER_NAME,\n type: \"module\",\n });\n\n this.#listenerRegistry();\n }\n\n static initialise() {\n return new WorkerManager();\n }\n\n #listenerRegistry() {\n this.#worker?.addEventListener(\"message\", (event) => {\n const { id, result, error } = event.data;\n const entity = pending.get(id);\n\n if (!entity) {\n return;\n }\n\n pending.delete(id);\n\n if (error) {\n entity.reject(error);\n } else {\n entity.resolve(result);\n }\n });\n\n this.#worker?.addEventListener(\"error\", (event) => {\n for (const [, { reject }] of pending) {\n reject(event);\n }\n\n pending.clear();\n });\n }\n\n async triggerWorker(payload: ToWorkerMessage) {\n const id = payload.id ?? Math.random().toString(36).substr(2);\n\n const p = new Promise((resolve, reject) => {\n pending.set(id, { reject, resolve });\n });\n\n this.#worker?.postMessage(payload);\n\n return p;\n }\n\n terminate() {\n if (this.#worker) {\n this.#worker.terminate();\n this.#worker = null;\n }\n }\n}\n\nexport default WorkerManager;\n","import { BROADCAST_CHANNEL_NAME } from \"../contants\";\nimport type { ExportParams, ExportResponse, ExportStrategy, JobId } from \"../types\";\nimport WorkerManager from \"../WorkerManager\";\n\nclass BolbExportStrategy implements ExportStrategy {\n private workerManager: WorkerManager;\n\n constructor() {\n this.workerManager = WorkerManager.initialise();\n }\n\n async export<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const suggestedName = params.fileName ?? \"export\";\n const filename = suggestedName.endsWith(\".csv\") ? suggestedName : `${suggestedName}.csv`;\n\n let iterator: JobId = 0 as JobId;\n let totalRowsLoaded = 0;\n\n const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n const csvParts: BlobPart[] = [];\n\n try {\n while (true) {\n const response = await params.getNextPage(iterator++);\n\n const safeRows = Array.isArray(response.rows) ? response.rows : [];\n const safeTotal = response.total ?? 0;\n\n const isRowsEmpty = safeRows.length === 0;\n\n const nextRowsLoaded = totalRowsLoaded + safeRows.length;\n totalRowsLoaded = isRowsEmpty ? safeTotal : nextRowsLoaded;\n\n const isFinished = safeTotal > 0 ? totalRowsLoaded >= safeTotal : isRowsEmpty;\n\n if (isRowsEmpty) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n break;\n }\n\n const csvChunk = (await this.workerManager.triggerWorker({\n columns: params.columns,\n data: safeRows as Record<string, unknown>[],\n id: iterator,\n type: \"to_csv_chunk\",\n })) as string;\n\n csvParts.push(csvChunk);\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"progress\",\n }),\n );\n\n if (isFinished) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n break;\n }\n }\n\n const blob = new Blob([\"\\uFEFF\", ...csvParts], {\n type: \"text/csv;charset=utf-8\",\n });\n\n this.downloadBlob(blob, filename);\n\n return { finished: true, totalRowsLoaded };\n } catch (error) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: 0,\n type: \"failed\",\n }),\n );\n throw error;\n } finally {\n messaging.close();\n this.workerManager.terminate();\n }\n }\n\n private downloadBlob(blob: Blob, filename: string) {\n const url = URL.createObjectURL(blob);\n\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.rel = \"noopener\";\n\n document.body.appendChild(a);\n a.click();\n a.remove();\n\n URL.revokeObjectURL(url);\n }\n}\n\nexport default BolbExportStrategy;\n","import { BROADCAST_CHANNEL_NAME } from \"../contants\";\nimport type { ExportParams, ExportResponse, ExportStrategy, JobId } from \"../types\";\nimport WorkerManager from \"../WorkerManager\";\n\nclass FsAccessExportStrategy implements ExportStrategy {\n private workerManager: WorkerManager;\n\n constructor() {\n this.workerManager = WorkerManager.initialise();\n }\n\n async export<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const _suggestedName = params?.fileName || \"export\";\n\n const fileHandle = await window.showSaveFilePicker({\n suggestedName: _suggestedName,\n types: [{ accept: { \"text/csv\": [\".csv\"] }, description: \"CSV file\" }],\n });\n\n const writableFileStream = await fileHandle.createWritable();\n let iterator: JobId = 0 as JobId;\n let totalRowsLoaded = 0;\n\n const encoder = new TextEncoder();\n const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n const readable = new ReadableStream({\n pull: async (controller) => {\n try {\n const response = await params.getNextPage(iterator++);\n\n const safeRows = Array.isArray(response.rows) ? response?.rows : [];\n const safeTotal = response.total ?? 0;\n\n const isRowsEmpty = !safeRows || !safeRows.length;\n const nextRowsLoaded = totalRowsLoaded + safeRows.length;\n totalRowsLoaded = isRowsEmpty ? safeTotal : nextRowsLoaded;\n const isFinished = totalRowsLoaded >= safeTotal;\n\n if (isRowsEmpty) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n messaging.close();\n controller.close();\n\n return;\n }\n\n const csvChunks = await this.workerManager.triggerWorker({\n columns: params.columns,\n data: safeRows as Record<string, unknown>[],\n id: iterator,\n type: \"to_csv_chunk\",\n });\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"progress\",\n }),\n );\n\n controller.enqueue(encoder.encode(csvChunks as string));\n\n if (isFinished) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n messaging.close();\n controller.close();\n\n return;\n }\n } catch (error) {\n controller.error(error);\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: 0,\n type: \"failed\",\n }),\n );\n }\n },\n });\n\n try {\n await readable.pipeTo(writableFileStream);\n } catch (err) {\n console.error(\"Export failed:\", err);\n throw err;\n } finally {\n this.workerManager.terminate();\n }\n\n return {\n finished: true,\n totalRowsLoaded,\n };\n }\n}\n\nexport default FsAccessExportStrategy;\n","import { ExportController } from \"./controllers/ExportController\";\nimport BolbExportStrategy from \"./strategy/BolbExportStrategy\";\nimport FsAccessExportStrategy from \"./strategy/FsAccessExportStrategy\";\n\nfunction createExportController(): ExportController {\n return new ExportController({\n blobExportStrategy: new BolbExportStrategy(),\n fsAccessStrategy: new FsAccessExportStrategy(),\n });\n}\n\nexport default createExportController;\n","import type { ExportController } from \"./controllers/ExportController\";\nimport createExportController from \"./createExportController\";\n\n// biome-ignore lint/complexity/noStaticOnlyClass: Note(Pavlo) Prefer to keep as class\nclass ExportControllerSingleton {\n static instance: ExportController | null = null;\n static initialized: boolean = false;\n\n static init() {\n if (ExportControllerSingleton.instance) {\n return ExportControllerSingleton.instance;\n }\n\n ExportControllerSingleton.instance = createExportController();\n ExportControllerSingleton.initialized = true;\n\n return ExportControllerSingleton.instance;\n }\n\n static getInstance(): ExportController {\n if (!ExportControllerSingleton.instance) {\n return ExportControllerSingleton.init();\n }\n\n return ExportControllerSingleton.instance;\n }\n}\n\nexport default ExportControllerSingleton;\n","import { useEffect } from \"react\";\nimport { BROADCAST_CHANNEL_NAME } from \"../core/contants\";\n\ntype Payload = {\n total: number;\n loadedItemsCount: number;\n state: \"progress\" | \"failed\" | \"done\";\n};\n\nfunction useMessageExportCSV(cb: (payload: Payload) => void) {\n useEffect(() => {\n const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n channel.addEventListener(\"message\", (params: MessageEvent) => {\n try {\n const json = JSON.parse(params.data) as Payload;\n\n cb(json);\n } catch (error) {\n console.error({ error });\n }\n });\n\n return () => {\n channel.close();\n };\n }, [cb]);\n}\n\nexport default useMessageExportCSV;\n"],"mappings":"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","names":["import_react"]}
package/dist/index.js CHANGED
@@ -355,3 +355,4 @@ export {
355
355
  useExportCSV_default as useExportCSV,
356
356
  useMessageExportCSV_default as useMessageExportCSV
357
357
  };
358
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useExportCSV.ts","../src/core/controllers/ExportController.ts","../src/core/contants/index.ts","../src/core/WorkerManager.ts","../src/core/strategy/BolbExportStrategy.ts","../src/core/strategy/FsAccessExportStrategy.ts","../src/core/createExportController.ts","../src/core/ExportControllerSingleton.ts","../src/hooks/useMessageExportCSV.ts"],"sourcesContent":["import { useRef } from \"react\";\nimport { type ExportController, ExportControllerSingleton } from \"../core\";\n\nfunction useExportCSV() {\n const exportCallbackRef = useRef<ExportController>(ExportControllerSingleton.init());\n\n return {\n handler: exportCallbackRef?.current,\n };\n}\n\nexport default useExportCSV;\n","import type BolbExportStrategy from \"../strategy/BolbExportStrategy\";\nimport type FsAccessExportStrategy from \"../strategy/FsAccessExportStrategy\";\nimport type { ExportParams, ExportResponse, ExportStrategy } from \"../types\";\n\ntype ExportControllerDeps = {\n fsAccessStrategy: FsAccessExportStrategy;\n blobExportStrategy: BolbExportStrategy;\n};\n\nexport class ExportController {\n constructor(private readonly deps: ExportControllerDeps) {}\n\n public async start<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const strategy = this.#resolveStrategy();\n\n return strategy.export(params);\n }\n\n #canUseFSAccess() {\n return typeof window.showSaveFilePicker === \"function\";\n }\n\n #resolveStrategy(): ExportStrategy {\n if (this.#canUseFSAccess()) {\n return this.deps.fsAccessStrategy;\n }\n\n return this.deps.blobExportStrategy;\n }\n}\n","export const WEB_WORKER_NAME = \"scv-worker\";\nexport const BROADCAST_CHANNEL_NAME = \"react-csv-exporter\";\n","import { WEB_WORKER_NAME } from \"./contants\";\nimport type { JobId, ToWorkerMessage } from \"./types\";\n\nconst pending = new Map<JobId, { resolve: (value: unknown) => void; reject: (reason?: ErrorEvent) => void }>();\n\nclass WorkerManager {\n #worker: Worker | null;\n\n constructor() {\n const workerUrl = new URL(\"./worker.js\", import.meta.url);\n\n this.#worker = new Worker(workerUrl, {\n name: WEB_WORKER_NAME,\n type: \"module\",\n });\n\n this.#listenerRegistry();\n }\n\n static initialise() {\n return new WorkerManager();\n }\n\n #listenerRegistry() {\n this.#worker?.addEventListener(\"message\", (event) => {\n const { id, result, error } = event.data;\n const entity = pending.get(id);\n\n if (!entity) {\n return;\n }\n\n pending.delete(id);\n\n if (error) {\n entity.reject(error);\n } else {\n entity.resolve(result);\n }\n });\n\n this.#worker?.addEventListener(\"error\", (event) => {\n for (const [, { reject }] of pending) {\n reject(event);\n }\n\n pending.clear();\n });\n }\n\n async triggerWorker(payload: ToWorkerMessage) {\n const id = payload.id ?? Math.random().toString(36).substr(2);\n\n const p = new Promise((resolve, reject) => {\n pending.set(id, { reject, resolve });\n });\n\n this.#worker?.postMessage(payload);\n\n return p;\n }\n\n terminate() {\n if (this.#worker) {\n this.#worker.terminate();\n this.#worker = null;\n }\n }\n}\n\nexport default WorkerManager;\n","import { BROADCAST_CHANNEL_NAME } from \"../contants\";\nimport type { ExportParams, ExportResponse, ExportStrategy, JobId } from \"../types\";\nimport WorkerManager from \"../WorkerManager\";\n\nclass BolbExportStrategy implements ExportStrategy {\n private workerManager: WorkerManager;\n\n constructor() {\n this.workerManager = WorkerManager.initialise();\n }\n\n async export<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const suggestedName = params.fileName ?? \"export\";\n const filename = suggestedName.endsWith(\".csv\") ? suggestedName : `${suggestedName}.csv`;\n\n let iterator: JobId = 0 as JobId;\n let totalRowsLoaded = 0;\n\n const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n const csvParts: BlobPart[] = [];\n\n try {\n while (true) {\n const response = await params.getNextPage(iterator++);\n\n const safeRows = Array.isArray(response.rows) ? response.rows : [];\n const safeTotal = response.total ?? 0;\n\n const isRowsEmpty = safeRows.length === 0;\n\n const nextRowsLoaded = totalRowsLoaded + safeRows.length;\n totalRowsLoaded = isRowsEmpty ? safeTotal : nextRowsLoaded;\n\n const isFinished = safeTotal > 0 ? totalRowsLoaded >= safeTotal : isRowsEmpty;\n\n if (isRowsEmpty) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n break;\n }\n\n const csvChunk = (await this.workerManager.triggerWorker({\n columns: params.columns,\n data: safeRows as Record<string, unknown>[],\n id: iterator,\n type: \"to_csv_chunk\",\n })) as string;\n\n csvParts.push(csvChunk);\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"progress\",\n }),\n );\n\n if (isFinished) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n break;\n }\n }\n\n const blob = new Blob([\"\\uFEFF\", ...csvParts], {\n type: \"text/csv;charset=utf-8\",\n });\n\n this.downloadBlob(blob, filename);\n\n return { finished: true, totalRowsLoaded };\n } catch (error) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: 0,\n type: \"failed\",\n }),\n );\n throw error;\n } finally {\n messaging.close();\n this.workerManager.terminate();\n }\n }\n\n private downloadBlob(blob: Blob, filename: string) {\n const url = URL.createObjectURL(blob);\n\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.rel = \"noopener\";\n\n document.body.appendChild(a);\n a.click();\n a.remove();\n\n URL.revokeObjectURL(url);\n }\n}\n\nexport default BolbExportStrategy;\n","import { BROADCAST_CHANNEL_NAME } from \"../contants\";\nimport type { ExportParams, ExportResponse, ExportStrategy, JobId } from \"../types\";\nimport WorkerManager from \"../WorkerManager\";\n\nclass FsAccessExportStrategy implements ExportStrategy {\n private workerManager: WorkerManager;\n\n constructor() {\n this.workerManager = WorkerManager.initialise();\n }\n\n async export<T>(params: ExportParams<T>): Promise<ExportResponse> {\n const _suggestedName = params?.fileName || \"export\";\n\n const fileHandle = await window.showSaveFilePicker({\n suggestedName: _suggestedName,\n types: [{ accept: { \"text/csv\": [\".csv\"] }, description: \"CSV file\" }],\n });\n\n const writableFileStream = await fileHandle.createWritable();\n let iterator: JobId = 0 as JobId;\n let totalRowsLoaded = 0;\n\n const encoder = new TextEncoder();\n const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n const readable = new ReadableStream({\n pull: async (controller) => {\n try {\n const response = await params.getNextPage(iterator++);\n\n const safeRows = Array.isArray(response.rows) ? response?.rows : [];\n const safeTotal = response.total ?? 0;\n\n const isRowsEmpty = !safeRows || !safeRows.length;\n const nextRowsLoaded = totalRowsLoaded + safeRows.length;\n totalRowsLoaded = isRowsEmpty ? safeTotal : nextRowsLoaded;\n const isFinished = totalRowsLoaded >= safeTotal;\n\n if (isRowsEmpty) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n messaging.close();\n controller.close();\n\n return;\n }\n\n const csvChunks = await this.workerManager.triggerWorker({\n columns: params.columns,\n data: safeRows as Record<string, unknown>[],\n id: iterator,\n type: \"to_csv_chunk\",\n });\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"progress\",\n }),\n );\n\n controller.enqueue(encoder.encode(csvChunks as string));\n\n if (isFinished) {\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: safeTotal,\n type: \"done\",\n }),\n );\n\n await this.workerManager.triggerWorker({\n id: iterator,\n type: \"completed\",\n });\n\n messaging.close();\n controller.close();\n\n return;\n }\n } catch (error) {\n controller.error(error);\n\n messaging.postMessage(\n JSON.stringify({\n loadedItemsCount: totalRowsLoaded,\n total: 0,\n type: \"failed\",\n }),\n );\n }\n },\n });\n\n try {\n await readable.pipeTo(writableFileStream);\n } catch (err) {\n console.error(\"Export failed:\", err);\n throw err;\n } finally {\n this.workerManager.terminate();\n }\n\n return {\n finished: true,\n totalRowsLoaded,\n };\n }\n}\n\nexport default FsAccessExportStrategy;\n","import { ExportController } from \"./controllers/ExportController\";\nimport BolbExportStrategy from \"./strategy/BolbExportStrategy\";\nimport FsAccessExportStrategy from \"./strategy/FsAccessExportStrategy\";\n\nfunction createExportController(): ExportController {\n return new ExportController({\n blobExportStrategy: new BolbExportStrategy(),\n fsAccessStrategy: new FsAccessExportStrategy(),\n });\n}\n\nexport default createExportController;\n","import type { ExportController } from \"./controllers/ExportController\";\nimport createExportController from \"./createExportController\";\n\n// biome-ignore lint/complexity/noStaticOnlyClass: Note(Pavlo) Prefer to keep as class\nclass ExportControllerSingleton {\n static instance: ExportController | null = null;\n static initialized: boolean = false;\n\n static init() {\n if (ExportControllerSingleton.instance) {\n return ExportControllerSingleton.instance;\n }\n\n ExportControllerSingleton.instance = createExportController();\n ExportControllerSingleton.initialized = true;\n\n return ExportControllerSingleton.instance;\n }\n\n static getInstance(): ExportController {\n if (!ExportControllerSingleton.instance) {\n return ExportControllerSingleton.init();\n }\n\n return ExportControllerSingleton.instance;\n }\n}\n\nexport default ExportControllerSingleton;\n","import { useEffect } from \"react\";\nimport { BROADCAST_CHANNEL_NAME } from \"../core/contants\";\n\ntype Payload = {\n total: number;\n loadedItemsCount: number;\n state: \"progress\" | \"failed\" | \"done\";\n};\n\nfunction useMessageExportCSV(cb: (payload: Payload) => void) {\n useEffect(() => {\n const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\n channel.addEventListener(\"message\", (params: MessageEvent) => {\n try {\n const json = JSON.parse(params.data) as Payload;\n\n cb(json);\n } catch (error) {\n console.error({ error });\n }\n });\n\n return () => {\n channel.close();\n };\n }, [cb]);\n}\n\nexport default useMessageExportCSV;\n"],"mappings":"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","names":[]}
package/dist/worker.js CHANGED
@@ -112,3 +112,4 @@ self.onmessage = (event) => {
112
112
  });
113
113
  }
114
114
  };
115
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/utils.ts","../src/core/worker.ts"],"sourcesContent":["import type { Column, ToCSVChunkMessage } from \"./types\";\n\nexport function objectsToCSV(\n jsonArray: ToCSVChunkMessage[\"data\"],\n columns: ToCSVChunkMessage[\"columns\"],\n includeHeaders: boolean,\n) {\n if (!jsonArray.length || !columns.length) return \"\";\n\n const rows = [];\n\n if (includeHeaders) {\n rows.push(columns.map((col) => col.label).join(\",\"));\n }\n\n jsonArray.forEach((row) => {\n rows.push(\n columns\n .map((col) => {\n const val: unknown = getNested(row, col.key);\n const normalizedValue = normalisedValue(val);\n const maybeFormattedValue = getFormatter(col, normalizedValue);\n\n return maybeFormattedValue;\n })\n .join(\",\"),\n );\n });\n\n return `${rows.join(\"\\n\")}\\n`;\n}\n\nfunction getNested(obj: Record<string, unknown>, keyPath: string): unknown {\n return keyPath.split(\".\").reduce<unknown>((acc, key) => {\n if (acc && typeof acc === \"object\") {\n return (acc as Record<string, unknown>)[key];\n }\n\n return undefined;\n }, obj);\n}\n\nfunction normalisedValue<T>(value: T): string | T {\n let result: string | T = value;\n\n if (typeof result === \"string\") {\n result = `\"${result.replace(/\"/g, '\"\"')}\"`;\n }\n\n if (result === undefined || result === null) {\n result = \"\";\n }\n\n return result;\n}\n\nfunction makeFormatters(locale = \"en-US\", timeZone = \"UTC\", currency = \"USD\") {\n return {\n dateFull: new Intl.DateTimeFormat(locale, { dateStyle: \"full\", timeZone }),\n dateMediumTime: new Intl.DateTimeFormat(locale, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n timeZone,\n }),\n numCompact: new Intl.NumberFormat(locale, {\n maximumFractionDigits: 1,\n notation: \"compact\",\n }),\n numCurrency: new Intl.NumberFormat(locale, {\n currency,\n maximumFractionDigits: 2,\n style: \"currency\",\n }),\n\n numDecimal: new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),\n numPercent: new Intl.NumberFormat(locale, {\n maximumFractionDigits: 1,\n style: \"percent\",\n }),\n timeShort: new Intl.DateTimeFormat(locale, {\n timeStyle: \"short\",\n timeZone,\n }),\n };\n}\n\nfunction getFormatter(col: Column, value: unknown) {\n const formatters = makeFormatters();\n const availableFormatters = Object.keys(formatters);\n\n if (\"formatType\" in col && availableFormatters.includes(col.formatType as string)) {\n const enhance = formatters[col.formatType as keyof typeof formatters];\n\n return enhance.format(Number(value));\n } else {\n return value;\n }\n}\n","import type { FromWorkerMessage, JobId, ToWorkerMessage } from \"./types\";\nimport { objectsToCSV } from \"./utils\";\n\nconst headersWritten = new Map<JobId, boolean>();\n\nself.onmessage = (event: MessageEvent<ToWorkerMessage>) => {\n const msg = event.data;\n\n try {\n switch (msg.type) {\n case \"to_csv_chunk\": {\n const { columns, data, id } = msg;\n const csvChunk = objectsToCSV(data, columns, !headersWritten.get(id));\n const out: FromWorkerMessage = {\n id,\n result: csvChunk,\n type: \"csv_chunk\",\n };\n\n headersWritten.set(id, true);\n\n self.postMessage(out);\n\n break;\n }\n\n case \"completed\": {\n const out: FromWorkerMessage = { id: msg.id, type: \"done\" };\n self.postMessage(out);\n\n break;\n }\n\n default: {\n console.warn(`Unsupported for worker message:: ${JSON.stringify(msg)}`);\n break;\n }\n }\n } catch (error) {\n const _error = error instanceof Error ? error : new Error(String(error));\n\n self.postMessage({\n error: _error,\n id: msg.id,\n type: \"error\",\n });\n }\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -61,5 +61,5 @@
61
61
  "sideEffects": false,
62
62
  "type": "module",
63
63
  "types": "dist/index.d.ts",
64
- "version": "1.1.0"
64
+ "version": "1.1.1-beta.1"
65
65
  }