react-csv-autopilot 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +19 -155
- package/dist/index.js +19 -155
- package/package.json +1 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -64,152 +64,20 @@ var WEB_WORKER_NAME = "scv-worker";
|
|
|
64
64
|
var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
|
|
65
65
|
|
|
66
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
|
-
`;
|
|
67
|
+
var workerCode = '"use strict";\n(() => {\n // src/core/utils.ts\n function objectsToCSV(jsonArray, columns, includeHeaders) {\n if (!jsonArray.length || !columns.length) return "";\n const rows = [];\n if (includeHeaders) {\n rows.push(columns.map((col) => col.label).join(","));\n }\n jsonArray.forEach((row) => {\n rows.push(\n columns.map((col) => {\n const val = getNested(row, col.key);\n const normalizedValue = normalisedValue(val);\n const maybeFormattedValue = getFormatter(col, normalizedValue);\n return maybeFormattedValue;\n }).join(",")\n );\n });\n return `${rows.join("\\n")}\n`;\n }\n function getNested(obj, keyPath) {\n return keyPath.split(".").reduce((acc, key) => {\n if (acc && typeof acc === "object") {\n return acc[key];\n }\n return void 0;\n }, obj);\n }\n function normalisedValue(value) {\n let result = value;\n if (typeof result === "string") {\n result = `"${result.replace(/"/g, \'""\')}"`;\n }\n if (result === void 0 || result === null) {\n result = "";\n }\n return result;\n }\n function 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 var formatters = makeFormatters();\n var availableFormatters = Object.keys(formatters);\n function getFormatter(col, value) {\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\n // src/core/worker.ts\n var headersWritten = /* @__PURE__ */ new Map();\n self.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 headersWritten.set(id, true);\n self.postMessage(out);\n break;\n }\n case "completed": {\n const out = { id: msg.id, type: "done" };\n self.postMessage(out);\n break;\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 self.postMessage({\n error: _error,\n id: msg.id,\n type: "error"\n });\n }\n };\n})();\n';
|
|
200
68
|
|
|
201
69
|
// src/core/WorkerManager.ts
|
|
202
|
-
var pending = /* @__PURE__ */ new Map();
|
|
203
70
|
function createWorkerBlobUrl() {
|
|
204
71
|
const blob = new Blob([workerCode], { type: "application/javascript" });
|
|
205
72
|
return URL.createObjectURL(blob);
|
|
206
73
|
}
|
|
207
|
-
var _worker, _blobUrl, _WorkerManager_instances, listenerRegistry_fn;
|
|
74
|
+
var _worker, _blobUrl, _pending, _WorkerManager_instances, listenerRegistry_fn;
|
|
208
75
|
var _WorkerManager = class _WorkerManager {
|
|
209
76
|
constructor() {
|
|
210
77
|
__privateAdd(this, _WorkerManager_instances);
|
|
211
78
|
__privateAdd(this, _worker);
|
|
212
79
|
__privateAdd(this, _blobUrl);
|
|
80
|
+
__privateAdd(this, _pending, /* @__PURE__ */ new Map());
|
|
213
81
|
__privateSet(this, _blobUrl, createWorkerBlobUrl());
|
|
214
82
|
__privateSet(this, _worker, new Worker(__privateGet(this, _blobUrl), {
|
|
215
83
|
name: WEB_WORKER_NAME
|
|
@@ -222,7 +90,7 @@ var _WorkerManager = class _WorkerManager {
|
|
|
222
90
|
async triggerWorker(payload) {
|
|
223
91
|
const id = payload.id ?? Math.random().toString(36).substr(2);
|
|
224
92
|
const p = new Promise((resolve, reject) => {
|
|
225
|
-
|
|
93
|
+
__privateGet(this, _pending).set(id, { reject, resolve });
|
|
226
94
|
});
|
|
227
95
|
__privateGet(this, _worker)?.postMessage(payload);
|
|
228
96
|
return p;
|
|
@@ -240,15 +108,16 @@ var _WorkerManager = class _WorkerManager {
|
|
|
240
108
|
};
|
|
241
109
|
_worker = new WeakMap();
|
|
242
110
|
_blobUrl = new WeakMap();
|
|
111
|
+
_pending = new WeakMap();
|
|
243
112
|
_WorkerManager_instances = new WeakSet();
|
|
244
113
|
listenerRegistry_fn = function() {
|
|
245
114
|
__privateGet(this, _worker)?.addEventListener("message", (event) => {
|
|
246
115
|
const { id, result, error } = event.data;
|
|
247
|
-
const entity =
|
|
116
|
+
const entity = __privateGet(this, _pending).get(id);
|
|
248
117
|
if (!entity) {
|
|
249
118
|
return;
|
|
250
119
|
}
|
|
251
|
-
|
|
120
|
+
__privateGet(this, _pending).delete(id);
|
|
252
121
|
if (error) {
|
|
253
122
|
entity.reject(error);
|
|
254
123
|
} else {
|
|
@@ -256,10 +125,10 @@ listenerRegistry_fn = function() {
|
|
|
256
125
|
}
|
|
257
126
|
});
|
|
258
127
|
__privateGet(this, _worker)?.addEventListener("error", (event) => {
|
|
259
|
-
for (const [, { reject }] of
|
|
128
|
+
for (const [, { reject }] of __privateGet(this, _pending)) {
|
|
260
129
|
reject(event);
|
|
261
130
|
}
|
|
262
|
-
|
|
131
|
+
__privateGet(this, _pending).clear();
|
|
263
132
|
});
|
|
264
133
|
};
|
|
265
134
|
var WorkerManager = _WorkerManager;
|
|
@@ -267,10 +136,8 @@ var WorkerManager_default = WorkerManager;
|
|
|
267
136
|
|
|
268
137
|
// src/core/strategy/BolbExportStrategy.ts
|
|
269
138
|
var BolbExportStrategy = class {
|
|
270
|
-
constructor() {
|
|
271
|
-
this.workerManager = WorkerManager_default.initialise();
|
|
272
|
-
}
|
|
273
139
|
async export(params) {
|
|
140
|
+
const workerManager = WorkerManager_default.initialise();
|
|
274
141
|
const suggestedName = params.fileName ?? "export";
|
|
275
142
|
const filename = suggestedName.endsWith(".csv") ? suggestedName : `${suggestedName}.csv`;
|
|
276
143
|
let iterator = 0;
|
|
@@ -294,13 +161,13 @@ var BolbExportStrategy = class {
|
|
|
294
161
|
type: "done"
|
|
295
162
|
})
|
|
296
163
|
);
|
|
297
|
-
await
|
|
164
|
+
await workerManager.triggerWorker({
|
|
298
165
|
id: iterator,
|
|
299
166
|
type: "completed"
|
|
300
167
|
});
|
|
301
168
|
break;
|
|
302
169
|
}
|
|
303
|
-
const csvChunk = await
|
|
170
|
+
const csvChunk = await workerManager.triggerWorker({
|
|
304
171
|
columns: params.columns,
|
|
305
172
|
data: safeRows,
|
|
306
173
|
id: iterator,
|
|
@@ -322,7 +189,7 @@ var BolbExportStrategy = class {
|
|
|
322
189
|
type: "done"
|
|
323
190
|
})
|
|
324
191
|
);
|
|
325
|
-
await
|
|
192
|
+
await workerManager.triggerWorker({
|
|
326
193
|
id: iterator,
|
|
327
194
|
type: "completed"
|
|
328
195
|
});
|
|
@@ -345,7 +212,7 @@ var BolbExportStrategy = class {
|
|
|
345
212
|
throw error;
|
|
346
213
|
} finally {
|
|
347
214
|
messaging.close();
|
|
348
|
-
|
|
215
|
+
workerManager.terminate();
|
|
349
216
|
}
|
|
350
217
|
}
|
|
351
218
|
downloadBlob(blob, filename) {
|
|
@@ -364,10 +231,8 @@ var BolbExportStrategy_default = BolbExportStrategy;
|
|
|
364
231
|
|
|
365
232
|
// src/core/strategy/FsAccessExportStrategy.ts
|
|
366
233
|
var FsAccessExportStrategy = class {
|
|
367
|
-
constructor() {
|
|
368
|
-
this.workerManager = WorkerManager_default.initialise();
|
|
369
|
-
}
|
|
370
234
|
async export(params) {
|
|
235
|
+
const workerManager = WorkerManager_default.initialise();
|
|
371
236
|
const _suggestedName = params?.fileName || "export";
|
|
372
237
|
const fileHandle = await window.showSaveFilePicker({
|
|
373
238
|
suggestedName: _suggestedName,
|
|
@@ -396,7 +261,7 @@ var FsAccessExportStrategy = class {
|
|
|
396
261
|
type: "done"
|
|
397
262
|
})
|
|
398
263
|
);
|
|
399
|
-
await
|
|
264
|
+
await workerManager.triggerWorker({
|
|
400
265
|
id: iterator,
|
|
401
266
|
type: "completed"
|
|
402
267
|
});
|
|
@@ -404,7 +269,7 @@ var FsAccessExportStrategy = class {
|
|
|
404
269
|
controller.close();
|
|
405
270
|
return;
|
|
406
271
|
}
|
|
407
|
-
const csvChunks = await
|
|
272
|
+
const csvChunks = await workerManager.triggerWorker({
|
|
408
273
|
columns: params.columns,
|
|
409
274
|
data: safeRows,
|
|
410
275
|
id: iterator,
|
|
@@ -426,7 +291,7 @@ var FsAccessExportStrategy = class {
|
|
|
426
291
|
type: "done"
|
|
427
292
|
})
|
|
428
293
|
);
|
|
429
|
-
await
|
|
294
|
+
await workerManager.triggerWorker({
|
|
430
295
|
id: iterator,
|
|
431
296
|
type: "completed"
|
|
432
297
|
});
|
|
@@ -452,7 +317,7 @@ var FsAccessExportStrategy = class {
|
|
|
452
317
|
console.error("Export failed:", err);
|
|
453
318
|
throw err;
|
|
454
319
|
} finally {
|
|
455
|
-
|
|
320
|
+
workerManager.terminate();
|
|
456
321
|
}
|
|
457
322
|
return {
|
|
458
323
|
finished: true,
|
|
@@ -521,4 +386,3 @@ function useMessageExportCSV(cb) {
|
|
|
521
386
|
}, [cb]);
|
|
522
387
|
}
|
|
523
388
|
var useMessageExportCSV_default = useMessageExportCSV;
|
|
524
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -38,152 +38,20 @@ var WEB_WORKER_NAME = "scv-worker";
|
|
|
38
38
|
var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
|
|
39
39
|
|
|
40
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
|
-
`;
|
|
41
|
+
var workerCode = '"use strict";\n(() => {\n // src/core/utils.ts\n function objectsToCSV(jsonArray, columns, includeHeaders) {\n if (!jsonArray.length || !columns.length) return "";\n const rows = [];\n if (includeHeaders) {\n rows.push(columns.map((col) => col.label).join(","));\n }\n jsonArray.forEach((row) => {\n rows.push(\n columns.map((col) => {\n const val = getNested(row, col.key);\n const normalizedValue = normalisedValue(val);\n const maybeFormattedValue = getFormatter(col, normalizedValue);\n return maybeFormattedValue;\n }).join(",")\n );\n });\n return `${rows.join("\\n")}\n`;\n }\n function getNested(obj, keyPath) {\n return keyPath.split(".").reduce((acc, key) => {\n if (acc && typeof acc === "object") {\n return acc[key];\n }\n return void 0;\n }, obj);\n }\n function normalisedValue(value) {\n let result = value;\n if (typeof result === "string") {\n result = `"${result.replace(/"/g, \'""\')}"`;\n }\n if (result === void 0 || result === null) {\n result = "";\n }\n return result;\n }\n function 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 var formatters = makeFormatters();\n var availableFormatters = Object.keys(formatters);\n function getFormatter(col, value) {\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\n // src/core/worker.ts\n var headersWritten = /* @__PURE__ */ new Map();\n self.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 headersWritten.set(id, true);\n self.postMessage(out);\n break;\n }\n case "completed": {\n const out = { id: msg.id, type: "done" };\n self.postMessage(out);\n break;\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 self.postMessage({\n error: _error,\n id: msg.id,\n type: "error"\n });\n }\n };\n})();\n';
|
|
174
42
|
|
|
175
43
|
// src/core/WorkerManager.ts
|
|
176
|
-
var pending = /* @__PURE__ */ new Map();
|
|
177
44
|
function createWorkerBlobUrl() {
|
|
178
45
|
const blob = new Blob([workerCode], { type: "application/javascript" });
|
|
179
46
|
return URL.createObjectURL(blob);
|
|
180
47
|
}
|
|
181
|
-
var _worker, _blobUrl, _WorkerManager_instances, listenerRegistry_fn;
|
|
48
|
+
var _worker, _blobUrl, _pending, _WorkerManager_instances, listenerRegistry_fn;
|
|
182
49
|
var _WorkerManager = class _WorkerManager {
|
|
183
50
|
constructor() {
|
|
184
51
|
__privateAdd(this, _WorkerManager_instances);
|
|
185
52
|
__privateAdd(this, _worker);
|
|
186
53
|
__privateAdd(this, _blobUrl);
|
|
54
|
+
__privateAdd(this, _pending, /* @__PURE__ */ new Map());
|
|
187
55
|
__privateSet(this, _blobUrl, createWorkerBlobUrl());
|
|
188
56
|
__privateSet(this, _worker, new Worker(__privateGet(this, _blobUrl), {
|
|
189
57
|
name: WEB_WORKER_NAME
|
|
@@ -196,7 +64,7 @@ var _WorkerManager = class _WorkerManager {
|
|
|
196
64
|
async triggerWorker(payload) {
|
|
197
65
|
const id = payload.id ?? Math.random().toString(36).substr(2);
|
|
198
66
|
const p = new Promise((resolve, reject) => {
|
|
199
|
-
|
|
67
|
+
__privateGet(this, _pending).set(id, { reject, resolve });
|
|
200
68
|
});
|
|
201
69
|
__privateGet(this, _worker)?.postMessage(payload);
|
|
202
70
|
return p;
|
|
@@ -214,15 +82,16 @@ var _WorkerManager = class _WorkerManager {
|
|
|
214
82
|
};
|
|
215
83
|
_worker = new WeakMap();
|
|
216
84
|
_blobUrl = new WeakMap();
|
|
85
|
+
_pending = new WeakMap();
|
|
217
86
|
_WorkerManager_instances = new WeakSet();
|
|
218
87
|
listenerRegistry_fn = function() {
|
|
219
88
|
__privateGet(this, _worker)?.addEventListener("message", (event) => {
|
|
220
89
|
const { id, result, error } = event.data;
|
|
221
|
-
const entity =
|
|
90
|
+
const entity = __privateGet(this, _pending).get(id);
|
|
222
91
|
if (!entity) {
|
|
223
92
|
return;
|
|
224
93
|
}
|
|
225
|
-
|
|
94
|
+
__privateGet(this, _pending).delete(id);
|
|
226
95
|
if (error) {
|
|
227
96
|
entity.reject(error);
|
|
228
97
|
} else {
|
|
@@ -230,10 +99,10 @@ listenerRegistry_fn = function() {
|
|
|
230
99
|
}
|
|
231
100
|
});
|
|
232
101
|
__privateGet(this, _worker)?.addEventListener("error", (event) => {
|
|
233
|
-
for (const [, { reject }] of
|
|
102
|
+
for (const [, { reject }] of __privateGet(this, _pending)) {
|
|
234
103
|
reject(event);
|
|
235
104
|
}
|
|
236
|
-
|
|
105
|
+
__privateGet(this, _pending).clear();
|
|
237
106
|
});
|
|
238
107
|
};
|
|
239
108
|
var WorkerManager = _WorkerManager;
|
|
@@ -241,10 +110,8 @@ var WorkerManager_default = WorkerManager;
|
|
|
241
110
|
|
|
242
111
|
// src/core/strategy/BolbExportStrategy.ts
|
|
243
112
|
var BolbExportStrategy = class {
|
|
244
|
-
constructor() {
|
|
245
|
-
this.workerManager = WorkerManager_default.initialise();
|
|
246
|
-
}
|
|
247
113
|
async export(params) {
|
|
114
|
+
const workerManager = WorkerManager_default.initialise();
|
|
248
115
|
const suggestedName = params.fileName ?? "export";
|
|
249
116
|
const filename = suggestedName.endsWith(".csv") ? suggestedName : `${suggestedName}.csv`;
|
|
250
117
|
let iterator = 0;
|
|
@@ -268,13 +135,13 @@ var BolbExportStrategy = class {
|
|
|
268
135
|
type: "done"
|
|
269
136
|
})
|
|
270
137
|
);
|
|
271
|
-
await
|
|
138
|
+
await workerManager.triggerWorker({
|
|
272
139
|
id: iterator,
|
|
273
140
|
type: "completed"
|
|
274
141
|
});
|
|
275
142
|
break;
|
|
276
143
|
}
|
|
277
|
-
const csvChunk = await
|
|
144
|
+
const csvChunk = await workerManager.triggerWorker({
|
|
278
145
|
columns: params.columns,
|
|
279
146
|
data: safeRows,
|
|
280
147
|
id: iterator,
|
|
@@ -296,7 +163,7 @@ var BolbExportStrategy = class {
|
|
|
296
163
|
type: "done"
|
|
297
164
|
})
|
|
298
165
|
);
|
|
299
|
-
await
|
|
166
|
+
await workerManager.triggerWorker({
|
|
300
167
|
id: iterator,
|
|
301
168
|
type: "completed"
|
|
302
169
|
});
|
|
@@ -319,7 +186,7 @@ var BolbExportStrategy = class {
|
|
|
319
186
|
throw error;
|
|
320
187
|
} finally {
|
|
321
188
|
messaging.close();
|
|
322
|
-
|
|
189
|
+
workerManager.terminate();
|
|
323
190
|
}
|
|
324
191
|
}
|
|
325
192
|
downloadBlob(blob, filename) {
|
|
@@ -338,10 +205,8 @@ var BolbExportStrategy_default = BolbExportStrategy;
|
|
|
338
205
|
|
|
339
206
|
// src/core/strategy/FsAccessExportStrategy.ts
|
|
340
207
|
var FsAccessExportStrategy = class {
|
|
341
|
-
constructor() {
|
|
342
|
-
this.workerManager = WorkerManager_default.initialise();
|
|
343
|
-
}
|
|
344
208
|
async export(params) {
|
|
209
|
+
const workerManager = WorkerManager_default.initialise();
|
|
345
210
|
const _suggestedName = params?.fileName || "export";
|
|
346
211
|
const fileHandle = await window.showSaveFilePicker({
|
|
347
212
|
suggestedName: _suggestedName,
|
|
@@ -370,7 +235,7 @@ var FsAccessExportStrategy = class {
|
|
|
370
235
|
type: "done"
|
|
371
236
|
})
|
|
372
237
|
);
|
|
373
|
-
await
|
|
238
|
+
await workerManager.triggerWorker({
|
|
374
239
|
id: iterator,
|
|
375
240
|
type: "completed"
|
|
376
241
|
});
|
|
@@ -378,7 +243,7 @@ var FsAccessExportStrategy = class {
|
|
|
378
243
|
controller.close();
|
|
379
244
|
return;
|
|
380
245
|
}
|
|
381
|
-
const csvChunks = await
|
|
246
|
+
const csvChunks = await workerManager.triggerWorker({
|
|
382
247
|
columns: params.columns,
|
|
383
248
|
data: safeRows,
|
|
384
249
|
id: iterator,
|
|
@@ -400,7 +265,7 @@ var FsAccessExportStrategy = class {
|
|
|
400
265
|
type: "done"
|
|
401
266
|
})
|
|
402
267
|
);
|
|
403
|
-
await
|
|
268
|
+
await workerManager.triggerWorker({
|
|
404
269
|
id: iterator,
|
|
405
270
|
type: "completed"
|
|
406
271
|
});
|
|
@@ -426,7 +291,7 @@ var FsAccessExportStrategy = class {
|
|
|
426
291
|
console.error("Export failed:", err);
|
|
427
292
|
throw err;
|
|
428
293
|
} finally {
|
|
429
|
-
|
|
294
|
+
workerManager.terminate();
|
|
430
295
|
}
|
|
431
296
|
return {
|
|
432
297
|
finished: true,
|
|
@@ -499,4 +364,3 @@ export {
|
|
|
499
364
|
useExportCSV_default as useExportCSV,
|
|
500
365
|
useMessageExportCSV_default as useMessageExportCSV
|
|
501
366
|
};
|
|
502
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|