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

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
@@ -63,18 +63,156 @@ resolveStrategy_fn = function() {
63
63
  var WEB_WORKER_NAME = "scv-worker";
64
64
  var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
65
65
 
66
+ // src/core/workerCode.ts
67
+ var workerCode = `
68
+ const headersWritten = new Map();
69
+
70
+ function getNested(obj, keyPath) {
71
+ return keyPath.split(".").reduce((acc, key) => {
72
+ if (acc && typeof acc === "object") {
73
+ return acc[key];
74
+ }
75
+ return undefined;
76
+ }, obj);
77
+ }
78
+
79
+ function normalisedValue(value) {
80
+ let result = value;
81
+
82
+ if (typeof result === "string") {
83
+ result = '"' + result.replace(/"/g, '""') + '"';
84
+ }
85
+
86
+ if (result === undefined || result === null) {
87
+ result = "";
88
+ }
89
+
90
+ return result;
91
+ }
92
+
93
+ function makeFormatters(locale = "en-US", timeZone = "UTC", currency = "USD") {
94
+ return {
95
+ dateFull: new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone }),
96
+ dateMediumTime: new Intl.DateTimeFormat(locale, {
97
+ dateStyle: "medium",
98
+ timeStyle: "short",
99
+ timeZone,
100
+ }),
101
+ numCompact: new Intl.NumberFormat(locale, {
102
+ maximumFractionDigits: 1,
103
+ notation: "compact",
104
+ }),
105
+ numCurrency: new Intl.NumberFormat(locale, {
106
+ currency,
107
+ maximumFractionDigits: 2,
108
+ style: "currency",
109
+ }),
110
+ numDecimal: new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),
111
+ numPercent: new Intl.NumberFormat(locale, {
112
+ maximumFractionDigits: 1,
113
+ style: "percent",
114
+ }),
115
+ timeShort: new Intl.DateTimeFormat(locale, {
116
+ timeStyle: "short",
117
+ timeZone,
118
+ }),
119
+ };
120
+ }
121
+
122
+ function getFormatter(col, value) {
123
+ const formatters = makeFormatters();
124
+ const availableFormatters = Object.keys(formatters);
125
+
126
+ if ("formatType" in col && availableFormatters.includes(col.formatType)) {
127
+ const enhance = formatters[col.formatType];
128
+ return enhance.format(Number(value));
129
+ } else {
130
+ return value;
131
+ }
132
+ }
133
+
134
+ function objectsToCSV(jsonArray, columns, includeHeaders) {
135
+ if (!jsonArray.length || !columns.length) return "";
136
+
137
+ const rows = [];
138
+
139
+ if (includeHeaders) {
140
+ rows.push(columns.map((col) => col.label).join(","));
141
+ }
142
+
143
+ jsonArray.forEach((row) => {
144
+ rows.push(
145
+ columns
146
+ .map((col) => {
147
+ const val = getNested(row, col.key);
148
+ const normalizedValue = normalisedValue(val);
149
+ const maybeFormattedValue = getFormatter(col, normalizedValue);
150
+ return maybeFormattedValue;
151
+ })
152
+ .join(",")
153
+ );
154
+ });
155
+
156
+ return rows.join("\\n") + "\\n";
157
+ }
158
+
159
+ self.onmessage = (event) => {
160
+ const msg = event.data;
161
+ try {
162
+ switch (msg.type) {
163
+ case "to_csv_chunk": {
164
+ const { columns, data, id } = msg;
165
+ const csvChunk = objectsToCSV(data, columns, !headersWritten.get(id));
166
+ const out = {
167
+ id,
168
+ result: csvChunk,
169
+ type: "csv_chunk",
170
+ };
171
+
172
+ headersWritten.set(id, true);
173
+
174
+ self.postMessage(out);
175
+ break;
176
+ }
177
+
178
+ case "completed": {
179
+ const out = { id: msg.id, type: "done" };
180
+ self.postMessage(out);
181
+ break;
182
+ }
183
+
184
+ default: {
185
+ console.warn("Unsupported worker message: " + JSON.stringify(msg));
186
+ break;
187
+ }
188
+ }
189
+ } catch (error) {
190
+ const _error = error instanceof Error ? error : new Error(String(error));
191
+
192
+ self.postMessage({
193
+ error: { name: _error.name, message: _error.message, stack: _error.stack },
194
+ id: msg.id,
195
+ type: "error",
196
+ });
197
+ }
198
+ };
199
+ `;
200
+
66
201
  // src/core/WorkerManager.ts
67
- var import_meta = {};
68
202
  var pending = /* @__PURE__ */ new Map();
69
- var _worker, _WorkerManager_instances, listenerRegistry_fn;
203
+ function createWorkerBlobUrl() {
204
+ const blob = new Blob([workerCode], { type: "application/javascript" });
205
+ return URL.createObjectURL(blob);
206
+ }
207
+ var _worker, _blobUrl, _WorkerManager_instances, listenerRegistry_fn;
70
208
  var _WorkerManager = class _WorkerManager {
71
209
  constructor() {
72
210
  __privateAdd(this, _WorkerManager_instances);
73
211
  __privateAdd(this, _worker);
74
- const workerUrl = new URL("./worker.js", import_meta.url);
75
- __privateSet(this, _worker, new Worker(workerUrl, {
76
- name: WEB_WORKER_NAME,
77
- type: "module"
212
+ __privateAdd(this, _blobUrl);
213
+ __privateSet(this, _blobUrl, createWorkerBlobUrl());
214
+ __privateSet(this, _worker, new Worker(__privateGet(this, _blobUrl), {
215
+ name: WEB_WORKER_NAME
78
216
  }));
79
217
  __privateMethod(this, _WorkerManager_instances, listenerRegistry_fn).call(this);
80
218
  }
@@ -94,9 +232,14 @@ var _WorkerManager = class _WorkerManager {
94
232
  __privateGet(this, _worker).terminate();
95
233
  __privateSet(this, _worker, null);
96
234
  }
235
+ if (__privateGet(this, _blobUrl)) {
236
+ URL.revokeObjectURL(__privateGet(this, _blobUrl));
237
+ __privateSet(this, _blobUrl, null);
238
+ }
97
239
  }
98
240
  };
99
241
  _worker = new WeakMap();
242
+ _blobUrl = new WeakMap();
100
243
  _WorkerManager_instances = new WeakSet();
101
244
  listenerRegistry_fn = function() {
102
245
  __privateGet(this, _worker)?.addEventListener("message", (event) => {
@@ -378,3 +521,4 @@ function useMessageExportCSV(cb) {
378
521
  }, [cb]);
379
522
  }
380
523
  var useMessageExportCSV_default = useMessageExportCSV;
524
+ //# 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/workerCode.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","export const workerCode = `\nconst headersWritten = new Map();\n\nfunction getNested(obj, keyPath) {\n return keyPath.split(\".\").reduce((acc, key) => {\n if (acc && typeof acc === \"object\") {\n return acc[key];\n }\n return undefined;\n }, obj);\n}\n\nfunction normalisedValue(value) {\n let result = 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 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, value) {\n const formatters = makeFormatters();\n const availableFormatters = Object.keys(formatters);\n\n if (\"formatType\" in col && availableFormatters.includes(col.formatType)) {\n const enhance = formatters[col.formatType];\n return enhance.format(Number(value));\n } else {\n return value;\n }\n}\n\nfunction objectsToCSV(jsonArray, columns, includeHeaders) {\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 = getNested(row, col.key);\n const normalizedValue = normalisedValue(val);\n const maybeFormattedValue = getFormatter(col, normalizedValue);\n return maybeFormattedValue;\n })\n .join(\",\")\n );\n });\n\n return rows.join(\"\\\\n\") + \"\\\\n\";\n}\n\nself.onmessage = (event) => {\n const msg = event.data;\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 = {\n id,\n result: csvChunk,\n type: \"csv_chunk\",\n };\n\n headersWritten.set(id, true);\n\n self.postMessage(out);\n break;\n }\n\n case \"completed\": {\n const out = { id: msg.id, type: \"done\" };\n self.postMessage(out);\n break;\n }\n\n default: {\n console.warn(\"Unsupported 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: { name: _error.name, message: _error.message, stack: _error.stack },\n id: msg.id,\n type: \"error\",\n });\n }\n};\n`;\n","import { WEB_WORKER_NAME } from \"./contants\";\nimport type { JobId, ToWorkerMessage } from \"./types\";\nimport { workerCode } from \"./workerCode\";\n\nconst pending = new Map<JobId, { resolve: (value: unknown) => void; reject: (reason?: ErrorEvent) => void }>();\n\nfunction createWorkerBlobUrl(): string {\n const blob = new Blob([workerCode], { type: \"application/javascript\" });\n return URL.createObjectURL(blob);\n}\n\nclass WorkerManager {\n #worker: Worker | null;\n #blobUrl: string | null;\n\n constructor() {\n this.#blobUrl = createWorkerBlobUrl();\n\n this.#worker = new Worker(this.#blobUrl, {\n name: WEB_WORKER_NAME,\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 if (this.#blobUrl) {\n URL.revokeObjectURL(this.#blobUrl);\n this.#blobUrl = 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
@@ -37,17 +37,156 @@ resolveStrategy_fn = function() {
37
37
  var WEB_WORKER_NAME = "scv-worker";
38
38
  var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
39
39
 
40
+ // src/core/workerCode.ts
41
+ var workerCode = `
42
+ const headersWritten = new Map();
43
+
44
+ function getNested(obj, keyPath) {
45
+ return keyPath.split(".").reduce((acc, key) => {
46
+ if (acc && typeof acc === "object") {
47
+ return acc[key];
48
+ }
49
+ return undefined;
50
+ }, obj);
51
+ }
52
+
53
+ function normalisedValue(value) {
54
+ let result = value;
55
+
56
+ if (typeof result === "string") {
57
+ result = '"' + result.replace(/"/g, '""') + '"';
58
+ }
59
+
60
+ if (result === undefined || result === null) {
61
+ result = "";
62
+ }
63
+
64
+ return result;
65
+ }
66
+
67
+ function makeFormatters(locale = "en-US", timeZone = "UTC", currency = "USD") {
68
+ return {
69
+ dateFull: new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone }),
70
+ dateMediumTime: new Intl.DateTimeFormat(locale, {
71
+ dateStyle: "medium",
72
+ timeStyle: "short",
73
+ timeZone,
74
+ }),
75
+ numCompact: new Intl.NumberFormat(locale, {
76
+ maximumFractionDigits: 1,
77
+ notation: "compact",
78
+ }),
79
+ numCurrency: new Intl.NumberFormat(locale, {
80
+ currency,
81
+ maximumFractionDigits: 2,
82
+ style: "currency",
83
+ }),
84
+ numDecimal: new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),
85
+ numPercent: new Intl.NumberFormat(locale, {
86
+ maximumFractionDigits: 1,
87
+ style: "percent",
88
+ }),
89
+ timeShort: new Intl.DateTimeFormat(locale, {
90
+ timeStyle: "short",
91
+ timeZone,
92
+ }),
93
+ };
94
+ }
95
+
96
+ function getFormatter(col, value) {
97
+ const formatters = makeFormatters();
98
+ const availableFormatters = Object.keys(formatters);
99
+
100
+ if ("formatType" in col && availableFormatters.includes(col.formatType)) {
101
+ const enhance = formatters[col.formatType];
102
+ return enhance.format(Number(value));
103
+ } else {
104
+ return value;
105
+ }
106
+ }
107
+
108
+ function objectsToCSV(jsonArray, columns, includeHeaders) {
109
+ if (!jsonArray.length || !columns.length) return "";
110
+
111
+ const rows = [];
112
+
113
+ if (includeHeaders) {
114
+ rows.push(columns.map((col) => col.label).join(","));
115
+ }
116
+
117
+ jsonArray.forEach((row) => {
118
+ rows.push(
119
+ columns
120
+ .map((col) => {
121
+ const val = getNested(row, col.key);
122
+ const normalizedValue = normalisedValue(val);
123
+ const maybeFormattedValue = getFormatter(col, normalizedValue);
124
+ return maybeFormattedValue;
125
+ })
126
+ .join(",")
127
+ );
128
+ });
129
+
130
+ return rows.join("\\n") + "\\n";
131
+ }
132
+
133
+ self.onmessage = (event) => {
134
+ const msg = event.data;
135
+ try {
136
+ switch (msg.type) {
137
+ case "to_csv_chunk": {
138
+ const { columns, data, id } = msg;
139
+ const csvChunk = objectsToCSV(data, columns, !headersWritten.get(id));
140
+ const out = {
141
+ id,
142
+ result: csvChunk,
143
+ type: "csv_chunk",
144
+ };
145
+
146
+ headersWritten.set(id, true);
147
+
148
+ self.postMessage(out);
149
+ break;
150
+ }
151
+
152
+ case "completed": {
153
+ const out = { id: msg.id, type: "done" };
154
+ self.postMessage(out);
155
+ break;
156
+ }
157
+
158
+ default: {
159
+ console.warn("Unsupported worker message: " + JSON.stringify(msg));
160
+ break;
161
+ }
162
+ }
163
+ } catch (error) {
164
+ const _error = error instanceof Error ? error : new Error(String(error));
165
+
166
+ self.postMessage({
167
+ error: { name: _error.name, message: _error.message, stack: _error.stack },
168
+ id: msg.id,
169
+ type: "error",
170
+ });
171
+ }
172
+ };
173
+ `;
174
+
40
175
  // src/core/WorkerManager.ts
41
176
  var pending = /* @__PURE__ */ new Map();
42
- var _worker, _WorkerManager_instances, listenerRegistry_fn;
177
+ function createWorkerBlobUrl() {
178
+ const blob = new Blob([workerCode], { type: "application/javascript" });
179
+ return URL.createObjectURL(blob);
180
+ }
181
+ var _worker, _blobUrl, _WorkerManager_instances, listenerRegistry_fn;
43
182
  var _WorkerManager = class _WorkerManager {
44
183
  constructor() {
45
184
  __privateAdd(this, _WorkerManager_instances);
46
185
  __privateAdd(this, _worker);
47
- const workerUrl = new URL("./worker.js", import.meta.url);
48
- __privateSet(this, _worker, new Worker(workerUrl, {
49
- name: WEB_WORKER_NAME,
50
- type: "module"
186
+ __privateAdd(this, _blobUrl);
187
+ __privateSet(this, _blobUrl, createWorkerBlobUrl());
188
+ __privateSet(this, _worker, new Worker(__privateGet(this, _blobUrl), {
189
+ name: WEB_WORKER_NAME
51
190
  }));
52
191
  __privateMethod(this, _WorkerManager_instances, listenerRegistry_fn).call(this);
53
192
  }
@@ -67,9 +206,14 @@ var _WorkerManager = class _WorkerManager {
67
206
  __privateGet(this, _worker).terminate();
68
207
  __privateSet(this, _worker, null);
69
208
  }
209
+ if (__privateGet(this, _blobUrl)) {
210
+ URL.revokeObjectURL(__privateGet(this, _blobUrl));
211
+ __privateSet(this, _blobUrl, null);
212
+ }
70
213
  }
71
214
  };
72
215
  _worker = new WeakMap();
216
+ _blobUrl = new WeakMap();
73
217
  _WorkerManager_instances = new WeakSet();
74
218
  listenerRegistry_fn = function() {
75
219
  __privateGet(this, _worker)?.addEventListener("message", (event) => {
@@ -355,3 +499,4 @@ export {
355
499
  useExportCSV_default as useExportCSV,
356
500
  useMessageExportCSV_default as useMessageExportCSV
357
501
  };
502
+ //# 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/workerCode.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","export const workerCode = `\nconst headersWritten = new Map();\n\nfunction getNested(obj, keyPath) {\n return keyPath.split(\".\").reduce((acc, key) => {\n if (acc && typeof acc === \"object\") {\n return acc[key];\n }\n return undefined;\n }, obj);\n}\n\nfunction normalisedValue(value) {\n let result = 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 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, value) {\n const formatters = makeFormatters();\n const availableFormatters = Object.keys(formatters);\n\n if (\"formatType\" in col && availableFormatters.includes(col.formatType)) {\n const enhance = formatters[col.formatType];\n return enhance.format(Number(value));\n } else {\n return value;\n }\n}\n\nfunction objectsToCSV(jsonArray, columns, includeHeaders) {\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 = getNested(row, col.key);\n const normalizedValue = normalisedValue(val);\n const maybeFormattedValue = getFormatter(col, normalizedValue);\n return maybeFormattedValue;\n })\n .join(\",\")\n );\n });\n\n return rows.join(\"\\\\n\") + \"\\\\n\";\n}\n\nself.onmessage = (event) => {\n const msg = event.data;\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 = {\n id,\n result: csvChunk,\n type: \"csv_chunk\",\n };\n\n headersWritten.set(id, true);\n\n self.postMessage(out);\n break;\n }\n\n case \"completed\": {\n const out = { id: msg.id, type: \"done\" };\n self.postMessage(out);\n break;\n }\n\n default: {\n console.warn(\"Unsupported 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: { name: _error.name, message: _error.message, stack: _error.stack },\n id: msg.id,\n type: \"error\",\n });\n }\n};\n`;\n","import { WEB_WORKER_NAME } from \"./contants\";\nimport type { JobId, ToWorkerMessage } from \"./types\";\nimport { workerCode } from \"./workerCode\";\n\nconst pending = new Map<JobId, { resolve: (value: unknown) => void; reject: (reason?: ErrorEvent) => void }>();\n\nfunction createWorkerBlobUrl(): string {\n const blob = new Blob([workerCode], { type: \"application/javascript\" });\n return URL.createObjectURL(blob);\n}\n\nclass WorkerManager {\n #worker: Worker | null;\n #blobUrl: string | null;\n\n constructor() {\n this.#blobUrl = createWorkerBlobUrl();\n\n this.#worker = new Worker(this.#blobUrl, {\n name: WEB_WORKER_NAME,\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 if (this.#blobUrl) {\n URL.revokeObjectURL(this.#blobUrl);\n this.#blobUrl = 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/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.3"
65
65
  }
package/dist/worker.js DELETED
@@ -1,114 +0,0 @@
1
- // src/core/utils.ts
2
- function objectsToCSV(jsonArray, columns, includeHeaders) {
3
- if (!jsonArray.length || !columns.length) return "";
4
- const rows = [];
5
- if (includeHeaders) {
6
- rows.push(columns.map((col) => col.label).join(","));
7
- }
8
- jsonArray.forEach((row) => {
9
- rows.push(
10
- columns.map((col) => {
11
- const val = getNested(row, col.key);
12
- const normalizedValue = normalisedValue(val);
13
- const maybeFormattedValue = getFormatter(col, normalizedValue);
14
- return maybeFormattedValue;
15
- }).join(",")
16
- );
17
- });
18
- return `${rows.join("\n")}
19
- `;
20
- }
21
- function getNested(obj, keyPath) {
22
- return keyPath.split(".").reduce((acc, key) => {
23
- if (acc && typeof acc === "object") {
24
- return acc[key];
25
- }
26
- return void 0;
27
- }, obj);
28
- }
29
- function normalisedValue(value) {
30
- let result = value;
31
- if (typeof result === "string") {
32
- result = `"${result.replace(/"/g, '""')}"`;
33
- }
34
- if (result === void 0 || result === null) {
35
- result = "";
36
- }
37
- return result;
38
- }
39
- function makeFormatters(locale = "en-US", timeZone = "UTC", currency = "USD") {
40
- return {
41
- dateFull: new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone }),
42
- dateMediumTime: new Intl.DateTimeFormat(locale, {
43
- dateStyle: "medium",
44
- timeStyle: "short",
45
- timeZone
46
- }),
47
- numCompact: new Intl.NumberFormat(locale, {
48
- maximumFractionDigits: 1,
49
- notation: "compact"
50
- }),
51
- numCurrency: new Intl.NumberFormat(locale, {
52
- currency,
53
- maximumFractionDigits: 2,
54
- style: "currency"
55
- }),
56
- numDecimal: new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),
57
- numPercent: new Intl.NumberFormat(locale, {
58
- maximumFractionDigits: 1,
59
- style: "percent"
60
- }),
61
- timeShort: new Intl.DateTimeFormat(locale, {
62
- timeStyle: "short",
63
- timeZone
64
- })
65
- };
66
- }
67
- function getFormatter(col, value) {
68
- const formatters = makeFormatters();
69
- const availableFormatters = Object.keys(formatters);
70
- if ("formatType" in col && availableFormatters.includes(col.formatType)) {
71
- const enhance = formatters[col.formatType];
72
- return enhance.format(Number(value));
73
- } else {
74
- return value;
75
- }
76
- }
77
-
78
- // src/core/worker.ts
79
- var headersWritten = /* @__PURE__ */ new Map();
80
- self.onmessage = (event) => {
81
- const msg = event.data;
82
- try {
83
- switch (msg.type) {
84
- case "to_csv_chunk": {
85
- const { columns, data, id } = msg;
86
- const csvChunk = objectsToCSV(data, columns, !headersWritten.get(id));
87
- const out = {
88
- id,
89
- result: csvChunk,
90
- type: "csv_chunk"
91
- };
92
- headersWritten.set(id, true);
93
- self.postMessage(out);
94
- break;
95
- }
96
- case "completed": {
97
- const out = { id: msg.id, type: "done" };
98
- self.postMessage(out);
99
- break;
100
- }
101
- default: {
102
- console.warn(`Unsupported for worker message:: ${JSON.stringify(msg)}`);
103
- break;
104
- }
105
- }
106
- } catch (error) {
107
- const _error = error instanceof Error ? error : new Error(String(error));
108
- self.postMessage({
109
- error: _error,
110
- id: msg.id,
111
- type: "error"
112
- });
113
- }
114
- };