jtcsv 2.2.7 → 3.0.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/README.md +31 -1
- package/bin/jtcsv.js +891 -821
- package/bin/jtcsv.ts +2534 -0
- package/csv-to-json.js +168 -145
- package/dist/jtcsv-core.cjs.js +1407 -0
- package/dist/jtcsv-core.cjs.js.map +1 -0
- package/dist/jtcsv-core.esm.js +1379 -0
- package/dist/jtcsv-core.esm.js.map +1 -0
- package/dist/jtcsv-core.umd.js +1413 -0
- package/dist/jtcsv-core.umd.js.map +1 -0
- package/dist/jtcsv-full.cjs.js +1912 -0
- package/dist/jtcsv-full.cjs.js.map +1 -0
- package/dist/jtcsv-full.esm.js +1880 -0
- package/dist/jtcsv-full.esm.js.map +1 -0
- package/dist/jtcsv-full.umd.js +1918 -0
- package/dist/jtcsv-full.umd.js.map +1 -0
- package/dist/jtcsv-workers.esm.js +759 -0
- package/dist/jtcsv-workers.esm.js.map +1 -0
- package/dist/jtcsv-workers.umd.js +773 -0
- package/dist/jtcsv-workers.umd.js.map +1 -0
- package/dist/jtcsv.cjs.js +61 -19
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +61 -19
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +61 -19
- package/dist/jtcsv.umd.js.map +1 -1
- package/errors.js +188 -2
- package/examples/advanced/conditional-transformations.js +446 -0
- package/examples/advanced/conditional-transformations.ts +446 -0
- package/examples/advanced/csv-parser.worker.js +89 -0
- package/examples/advanced/csv-parser.worker.ts +89 -0
- package/examples/advanced/nested-objects-example.js +306 -0
- package/examples/advanced/nested-objects-example.ts +306 -0
- package/examples/advanced/performance-optimization.js +504 -0
- package/examples/advanced/performance-optimization.ts +504 -0
- package/examples/advanced/run-demo-server.js +116 -0
- package/examples/advanced/run-demo-server.ts +116 -0
- package/examples/advanced/web-worker-usage.html +874 -0
- package/examples/async-multithreaded-example.ts +335 -0
- package/examples/cli-advanced-usage.md +288 -0
- package/examples/cli-batch-processing.ts +38 -0
- package/examples/cli-tool.js +0 -3
- package/examples/cli-tool.ts +183 -0
- package/examples/error-handling.js +21 -7
- package/examples/error-handling.ts +356 -0
- package/examples/express-api.js +0 -3
- package/examples/express-api.ts +164 -0
- package/examples/large-dataset-example.js +0 -3
- package/examples/large-dataset-example.ts +204 -0
- package/examples/ndjson-processing.js +1 -1
- package/examples/ndjson-processing.ts +456 -0
- package/examples/plugin-excel-exporter.js +3 -4
- package/examples/plugin-excel-exporter.ts +406 -0
- package/examples/react-integration.tsx +637 -0
- package/examples/schema-validation.ts +640 -0
- package/examples/simple-usage.js +254 -254
- package/examples/simple-usage.ts +194 -0
- package/examples/streaming-example.js +4 -5
- package/examples/streaming-example.ts +419 -0
- package/examples/web-workers-advanced.ts +28 -0
- package/index.d.ts +1 -3
- package/index.js +15 -1
- package/json-save.js +9 -3
- package/json-to-csv.js +168 -21
- package/package.json +69 -10
- package/plugins/express-middleware/README.md +21 -2
- package/plugins/express-middleware/example.js +3 -4
- package/plugins/express-middleware/example.ts +135 -0
- package/plugins/express-middleware/index.d.ts +1 -1
- package/plugins/express-middleware/index.js +270 -118
- package/plugins/express-middleware/index.ts +557 -0
- package/plugins/fastify-plugin/index.js +2 -4
- package/plugins/fastify-plugin/index.ts +443 -0
- package/plugins/hono/index.ts +226 -0
- package/plugins/nestjs/index.ts +201 -0
- package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
- package/plugins/nextjs-api/examples/api-convert.js +0 -2
- package/plugins/nextjs-api/examples/api-convert.ts +67 -0
- package/plugins/nextjs-api/index.tsx +339 -0
- package/plugins/nextjs-api/route.js +2 -3
- package/plugins/nextjs-api/route.ts +370 -0
- package/plugins/nuxt/index.ts +94 -0
- package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
- package/plugins/nuxt/runtime/plugin.ts +71 -0
- package/plugins/remix/index.js +1 -1
- package/plugins/remix/index.ts +260 -0
- package/plugins/sveltekit/index.js +1 -1
- package/plugins/sveltekit/index.ts +301 -0
- package/plugins/trpc/index.ts +267 -0
- package/src/browser/browser-functions.ts +402 -0
- package/src/browser/core.js +92 -0
- package/src/browser/core.ts +152 -0
- package/src/browser/csv-to-json-browser.d.ts +3 -0
- package/src/browser/csv-to-json-browser.js +36 -14
- package/src/browser/csv-to-json-browser.ts +264 -0
- package/src/browser/errors-browser.ts +303 -0
- package/src/browser/extensions/plugins.js +92 -0
- package/src/browser/extensions/plugins.ts +93 -0
- package/src/browser/extensions/workers.js +39 -0
- package/src/browser/extensions/workers.ts +39 -0
- package/src/browser/globals.d.ts +5 -0
- package/src/browser/index.ts +192 -0
- package/src/browser/json-to-csv-browser.d.ts +3 -0
- package/src/browser/json-to-csv-browser.js +13 -3
- package/src/browser/json-to-csv-browser.ts +262 -0
- package/src/browser/streams.js +12 -2
- package/src/browser/streams.ts +336 -0
- package/src/browser/workers/csv-parser.worker.ts +377 -0
- package/src/browser/workers/worker-pool.ts +548 -0
- package/src/core/delimiter-cache.js +22 -8
- package/src/core/delimiter-cache.ts +310 -0
- package/src/core/node-optimizations.ts +449 -0
- package/src/core/plugin-system.js +29 -11
- package/src/core/plugin-system.ts +400 -0
- package/src/core/transform-hooks.ts +558 -0
- package/src/engines/fast-path-engine-new.ts +347 -0
- package/src/engines/fast-path-engine.ts +854 -0
- package/src/errors.ts +72 -0
- package/src/formats/ndjson-parser.ts +469 -0
- package/src/formats/tsv-parser.ts +334 -0
- package/src/index-with-plugins.js +16 -9
- package/src/index-with-plugins.ts +395 -0
- package/src/types/index.ts +255 -0
- package/src/utils/bom-utils.js +259 -0
- package/src/utils/bom-utils.ts +373 -0
- package/src/utils/encoding-support.js +124 -0
- package/src/utils/encoding-support.ts +155 -0
- package/src/utils/schema-validator.js +19 -19
- package/src/utils/schema-validator.ts +819 -0
- package/src/utils/transform-loader.js +1 -1
- package/src/utils/transform-loader.ts +389 -0
- package/src/utils/zod-adapter.js +170 -0
- package/src/utils/zod-adapter.ts +280 -0
- package/src/web-server/index.js +10 -10
- package/src/web-server/index.ts +683 -0
- package/src/workers/csv-multithreaded.ts +310 -0
- package/src/workers/csv-parser.worker.ts +227 -0
- package/src/workers/worker-pool.ts +409 -0
- package/stream-csv-to-json.js +26 -8
- package/stream-json-to-csv.js +1 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tRPC plugin for jtcsv
|
|
3
|
+
* Provides utilities for CSV parsing and generation in tRPC applications
|
|
4
|
+
* @module plugins/trpc
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { csvToJson, jsonToCsv } from '../../index-core';
|
|
8
|
+
import type { CsvToJsonOptions, JsonToCsvOptions } from '../../src/types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* tRPC context type (simplified)
|
|
12
|
+
*/
|
|
13
|
+
interface TRPCContext {
|
|
14
|
+
// tRPC context properties
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* tRPC procedure type (simplified)
|
|
19
|
+
*/
|
|
20
|
+
interface TRPCProcedure {
|
|
21
|
+
input(schema: any): any;
|
|
22
|
+
use(middleware: any): any;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* tRPC instance type (simplified)
|
|
27
|
+
*/
|
|
28
|
+
interface TRPCInstance {
|
|
29
|
+
procedure: TRPCProcedure;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for CSV parsing in tRPC procedures
|
|
34
|
+
*/
|
|
35
|
+
export interface CsvProcedureOptions extends CsvToJsonOptions {
|
|
36
|
+
/** Whether to return raw CSV text instead of parsed JSON */
|
|
37
|
+
raw?: boolean;
|
|
38
|
+
/** Whether to use async parsing */
|
|
39
|
+
async?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Options for CSV generation in tRPC procedures
|
|
44
|
+
*/
|
|
45
|
+
export interface CsvResponseOptions extends JsonToCsvOptions {
|
|
46
|
+
/** Filename for download (default: 'export.csv') */
|
|
47
|
+
filename?: string;
|
|
48
|
+
/** Whether to return as Response object */
|
|
49
|
+
asResponse?: boolean;
|
|
50
|
+
/** Whether to use async generation */
|
|
51
|
+
async?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extract CSV text from various input formats
|
|
56
|
+
*/
|
|
57
|
+
function extractCsvText(input: any): string | null {
|
|
58
|
+
if (typeof input === 'string') {
|
|
59
|
+
return input;
|
|
60
|
+
}
|
|
61
|
+
if (input && typeof input === 'object' && typeof input.csv === 'string') {
|
|
62
|
+
return input.csv;
|
|
63
|
+
}
|
|
64
|
+
if (input && typeof input === 'object' && input.file && typeof input.file.text === 'function') {
|
|
65
|
+
return input.file.text();
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a tRPC procedure for CSV parsing
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // In your tRPC router:
|
|
75
|
+
* import { createCsvProcedure } from 'jtcsv/plugins/trpc';
|
|
76
|
+
*
|
|
77
|
+
* export const csvRouter = t.router({
|
|
78
|
+
* parse: createCsvProcedure(t, z.string(), { delimiter: ',' })
|
|
79
|
+
* });
|
|
80
|
+
*/
|
|
81
|
+
export function createCsvProcedure(
|
|
82
|
+
t: TRPCInstance,
|
|
83
|
+
schema: any,
|
|
84
|
+
options: CsvProcedureOptions = {}
|
|
85
|
+
): any {
|
|
86
|
+
if (!t || !t.procedure) {
|
|
87
|
+
throw new Error('createCsvProcedure expects initTRPC instance');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return t.procedure
|
|
91
|
+
.input(schema)
|
|
92
|
+
.use(async ({ input, next }: { input: any; next: any }) => {
|
|
93
|
+
const csvText = extractCsvText(input);
|
|
94
|
+
if (!csvText) {
|
|
95
|
+
throw new Error('CSV input must be a string or { csv: string }');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.async) {
|
|
99
|
+
const parsed = await csvToJson(csvText, options);
|
|
100
|
+
return next({ input: parsed });
|
|
101
|
+
} else {
|
|
102
|
+
const parsed = csvToJson(csvText, options);
|
|
103
|
+
return next({ input: parsed });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Async version of createCsvProcedure
|
|
110
|
+
*/
|
|
111
|
+
export function createAsyncCsvProcedure(
|
|
112
|
+
t: TRPCInstance,
|
|
113
|
+
schema: any,
|
|
114
|
+
options: CsvProcedureOptions = {}
|
|
115
|
+
): any {
|
|
116
|
+
return createCsvProcedure(t, schema, { ...options, async: true });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create a tRPC procedure for CSV generation
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // In your tRPC router:
|
|
124
|
+
* import { createCsvResponseProcedure } from 'jtcsv/plugins/trpc';
|
|
125
|
+
*
|
|
126
|
+
* export const csvRouter = t.router({
|
|
127
|
+
* export: createCsvResponseProcedure(t, z.array(z.any()), { filename: 'data.csv' })
|
|
128
|
+
* });
|
|
129
|
+
*/
|
|
130
|
+
export function createCsvResponseProcedure(
|
|
131
|
+
t: TRPCInstance,
|
|
132
|
+
schema: any,
|
|
133
|
+
options: CsvResponseOptions = {}
|
|
134
|
+
): any {
|
|
135
|
+
if (!t || !t.procedure) {
|
|
136
|
+
throw new Error('createCsvResponseProcedure expects initTRPC instance');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return t.procedure
|
|
140
|
+
.input(schema)
|
|
141
|
+
.use(async ({ input, next }: { input: any; next: any }) => {
|
|
142
|
+
const { filename = 'export.csv', asResponse = false, ...csvOptions } = options;
|
|
143
|
+
const rows = Array.isArray(input) ? input : [input];
|
|
144
|
+
|
|
145
|
+
if (options.async) {
|
|
146
|
+
const csv = await jsonToCsv(rows, csvOptions);
|
|
147
|
+
|
|
148
|
+
if (asResponse) {
|
|
149
|
+
const response = new Response(csv, {
|
|
150
|
+
headers: {
|
|
151
|
+
'Content-Type': 'text/csv; charset=utf-8',
|
|
152
|
+
'Content-Disposition': `attachment; filename="${filename}"`
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return next({ input: response });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return next({ input: csv });
|
|
159
|
+
} else {
|
|
160
|
+
const csv = jsonToCsv(rows, csvOptions);
|
|
161
|
+
|
|
162
|
+
if (asResponse) {
|
|
163
|
+
const response = new Response(csv, {
|
|
164
|
+
headers: {
|
|
165
|
+
'Content-Type': 'text/csv; charset=utf-8',
|
|
166
|
+
'Content-Disposition': `attachment; filename="${filename}"`
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return next({ input: response });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return next({ input: csv });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Async version of createCsvResponseProcedure
|
|
179
|
+
*/
|
|
180
|
+
export function createAsyncCsvResponseProcedure(
|
|
181
|
+
t: TRPCInstance,
|
|
182
|
+
schema: any,
|
|
183
|
+
options: CsvResponseOptions = {}
|
|
184
|
+
): any {
|
|
185
|
+
return createCsvResponseProcedure(t, schema, { ...options, async: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Create a complete tRPC router for CSV operations
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* // In your tRPC setup:
|
|
193
|
+
* import { createCsvRouter } from 'jtcsv/plugins/trpc';
|
|
194
|
+
*
|
|
195
|
+
* export const csvRouter = createCsvRouter(t);
|
|
196
|
+
*/
|
|
197
|
+
export function createCsvRouter(t: TRPCInstance, options: {
|
|
198
|
+
parseOptions?: CsvProcedureOptions;
|
|
199
|
+
responseOptions?: CsvResponseOptions;
|
|
200
|
+
} = {}) {
|
|
201
|
+
if (!t || !t.procedure) {
|
|
202
|
+
throw new Error('createCsvRouter expects initTRPC instance');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
parse: createCsvProcedure(t, z.string(), options.parseOptions),
|
|
207
|
+
parseAsync: createAsyncCsvProcedure(t, z.string(), options.parseOptions),
|
|
208
|
+
export: createCsvResponseProcedure(t, z.array(z.any()), options.responseOptions),
|
|
209
|
+
exportAsync: createAsyncCsvResponseProcedure(t, z.array(z.any()), options.responseOptions),
|
|
210
|
+
batch: t.procedure
|
|
211
|
+
.input(z.array(z.string()))
|
|
212
|
+
.use(async ({ input, next }: { input: string[]; next: any }) => {
|
|
213
|
+
const results = await Promise.all(
|
|
214
|
+
input.map((csv: string) => csvToJson(csv, options.parseOptions))
|
|
215
|
+
);
|
|
216
|
+
return next({ input: results });
|
|
217
|
+
}),
|
|
218
|
+
batchAsync: t.procedure
|
|
219
|
+
.input(z.array(z.string()))
|
|
220
|
+
.use(async ({ input, next }: { input: string[]; next: any }) => {
|
|
221
|
+
const results = await Promise.all(
|
|
222
|
+
input.map((csv: string) => csvToJson(csv, options.parseOptions))
|
|
223
|
+
);
|
|
224
|
+
return next({ input: results });
|
|
225
|
+
}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* tRPC middleware for CSV processing
|
|
231
|
+
* Adds CSV utilities to tRPC context
|
|
232
|
+
*/
|
|
233
|
+
export function createCsvMiddleware(options: CsvProcedureOptions = {}) {
|
|
234
|
+
return async ({ ctx, next }: { ctx: TRPCContext; next: any }) => {
|
|
235
|
+
const enhancedCtx = {
|
|
236
|
+
...ctx,
|
|
237
|
+
csv: {
|
|
238
|
+
parse: (csvText: string) => csvToJson(csvText, options),
|
|
239
|
+
parseAsync: async (csvText: string) => await csvToJson(csvText, options),
|
|
240
|
+
generate: (data: any, opts?: JsonToCsvOptions) => jsonToCsv(data, { ...options, ...opts }),
|
|
241
|
+
generateAsync: async (data: any, opts?: JsonToCsvOptions) =>
|
|
242
|
+
await jsonToCsv(data, { ...options, ...opts }),
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
return next({ ctx: enhancedCtx });
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Zod schema for CSV input validation
|
|
252
|
+
* Note: This is a placeholder - in real usage, import zod
|
|
253
|
+
*/
|
|
254
|
+
const z = {
|
|
255
|
+
string: () => ({ _type: 'string' }),
|
|
256
|
+
array: (schema: any) => ({ _type: 'array', schema }),
|
|
257
|
+
any: () => ({ _type: 'any' }),
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export default {
|
|
261
|
+
createCsvProcedure,
|
|
262
|
+
createAsyncCsvProcedure,
|
|
263
|
+
createCsvResponseProcedure,
|
|
264
|
+
createAsyncCsvResponseProcedure,
|
|
265
|
+
createCsvRouter,
|
|
266
|
+
createCsvMiddleware,
|
|
267
|
+
};
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// Браузерные специфичные функции для jtcsv
|
|
2
|
+
// Функции, которые работают только в браузере
|
|
3
|
+
|
|
4
|
+
import { jsonToCsv } from './json-to-csv-browser';
|
|
5
|
+
import { csvToJson, csvToJsonIterator } from './csv-to-json-browser';
|
|
6
|
+
import {
|
|
7
|
+
csvToJsonStream as createCsvToJsonStream,
|
|
8
|
+
jsonToCsvStream as createJsonToCsvStream,
|
|
9
|
+
jsonToNdjsonStream as createJsonToNdjsonStream
|
|
10
|
+
} from './streams';
|
|
11
|
+
import { ValidationError } from './errors-browser';
|
|
12
|
+
|
|
13
|
+
import type { JsonToCsvOptions, CsvToJsonOptions } from '../types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Скачивает JSON данные как CSV файл
|
|
17
|
+
*
|
|
18
|
+
* @param data - Массив объектов для конвертации
|
|
19
|
+
* @param filename - Имя файла для скачивания (по умолчанию 'data.csv')
|
|
20
|
+
* @param options - Опции для jsonToCsv
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const data = [
|
|
24
|
+
* { id: 1, name: 'John' },
|
|
25
|
+
* { id: 2, name: 'Jane' }
|
|
26
|
+
* ];
|
|
27
|
+
* downloadAsCsv(data, 'users.csv', { delimiter: ',' });
|
|
28
|
+
*/
|
|
29
|
+
export function downloadAsCsv(
|
|
30
|
+
data: any[],
|
|
31
|
+
filename: string = 'data.csv',
|
|
32
|
+
options: JsonToCsvOptions = {}
|
|
33
|
+
): void {
|
|
34
|
+
// Проверка что мы в браузере
|
|
35
|
+
if (typeof window === 'undefined') {
|
|
36
|
+
throw new ValidationError('downloadAsCsv() работает только в браузере. Используйте saveAsCsv() в Node.js');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Валидация имени файла
|
|
40
|
+
if (typeof filename !== 'string' || filename.trim() === '') {
|
|
41
|
+
throw new ValidationError('Filename must be a non-empty string');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Добавление расширения .csv если его нет
|
|
45
|
+
if (!filename.toLowerCase().endsWith('.csv')) {
|
|
46
|
+
filename += '.csv';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Конвертация в CSV
|
|
50
|
+
const csv = jsonToCsv(data, options);
|
|
51
|
+
|
|
52
|
+
// Создание Blob
|
|
53
|
+
const blob = new Blob([csv], {
|
|
54
|
+
type: 'text/csv;charset=utf-8;'
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Создание ссылки для скачивания
|
|
58
|
+
const link = document.createElement('a');
|
|
59
|
+
const url = URL.createObjectURL(blob);
|
|
60
|
+
|
|
61
|
+
link.setAttribute('href', url);
|
|
62
|
+
link.setAttribute('download', filename);
|
|
63
|
+
link.style.visibility = 'hidden';
|
|
64
|
+
|
|
65
|
+
document.body.appendChild(link);
|
|
66
|
+
link.click();
|
|
67
|
+
document.body.removeChild(link);
|
|
68
|
+
|
|
69
|
+
// Освобождение URL
|
|
70
|
+
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Асинхронная версия downloadAsCsv
|
|
75
|
+
*/
|
|
76
|
+
export async function downloadAsCsvAsync(
|
|
77
|
+
data: any[],
|
|
78
|
+
filename: string = 'data.csv',
|
|
79
|
+
options: JsonToCsvOptions = {}
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
return downloadAsCsv(data, filename, options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Парсит CSV файл из input[type="file"]
|
|
86
|
+
*
|
|
87
|
+
* @param file - File объект из input
|
|
88
|
+
* @param options - Опции для csvToJson
|
|
89
|
+
* @returns Promise с распарсенными данными
|
|
90
|
+
*/
|
|
91
|
+
export async function parseCsvFile(
|
|
92
|
+
file: File,
|
|
93
|
+
options: CsvToJsonOptions = {}
|
|
94
|
+
): Promise<any[]> {
|
|
95
|
+
if (!(file instanceof File)) {
|
|
96
|
+
throw new ValidationError('parseCsvFile() ожидает объект File');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Чтение файла как текст
|
|
100
|
+
const text = await file.text();
|
|
101
|
+
|
|
102
|
+
// Парсинг CSV
|
|
103
|
+
return csvToJson(text, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Парсит CSV файл потоково
|
|
108
|
+
*
|
|
109
|
+
* @param file - File объект
|
|
110
|
+
* @param options - Опции для потокового парсинга
|
|
111
|
+
* @returns AsyncIterator с данными
|
|
112
|
+
*/
|
|
113
|
+
export function parseCsvFileStream(
|
|
114
|
+
file: File,
|
|
115
|
+
options: CsvToJsonOptions = {}
|
|
116
|
+
): AsyncIterator<any> {
|
|
117
|
+
if (!(file instanceof File)) {
|
|
118
|
+
throw new ValidationError('parseCsvFileStream() ожидает объект File');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Используем csvToJsonIterator из импортированного модуля
|
|
122
|
+
return csvToJsonIterator(file, options);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Создает поток для конвертации JSON в CSV
|
|
127
|
+
*
|
|
128
|
+
* @param options - Опции для jsonToCsv
|
|
129
|
+
* @returns ReadableStream
|
|
130
|
+
*/
|
|
131
|
+
export function jsonToCsvStream(options: JsonToCsvOptions = {}): ReadableStream {
|
|
132
|
+
return createJsonToCsvStream(options);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Создает поток для конвертации JSON в NDJSON
|
|
137
|
+
*
|
|
138
|
+
* @param options - Опции для конвертации
|
|
139
|
+
* @returns ReadableStream
|
|
140
|
+
*/
|
|
141
|
+
export function jsonToNdjsonStream(options: any = {}): ReadableStream {
|
|
142
|
+
return createJsonToNdjsonStream(options);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Создает поток для парсинга CSV в JSON
|
|
147
|
+
*
|
|
148
|
+
* @param options - Опции для csvToJson
|
|
149
|
+
* @returns ReadableStream
|
|
150
|
+
*/
|
|
151
|
+
export function csvToJsonStream(options: CsvToJsonOptions = {}): ReadableStream {
|
|
152
|
+
return createCsvToJsonStream(options);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Загружает CSV файл по URL
|
|
157
|
+
*
|
|
158
|
+
* @param url - URL CSV файла
|
|
159
|
+
* @param options - Опции для csvToJson
|
|
160
|
+
* @returns Promise с распарсенными данными
|
|
161
|
+
*/
|
|
162
|
+
export async function loadCsvFromUrl(
|
|
163
|
+
url: string,
|
|
164
|
+
options: CsvToJsonOptions = {}
|
|
165
|
+
): Promise<any[]> {
|
|
166
|
+
if (typeof window === 'undefined') {
|
|
167
|
+
throw new ValidationError('loadCsvFromUrl() работает только в браузере');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const response = await fetch(url);
|
|
171
|
+
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
throw new ValidationError(`Failed to load CSV from URL: ${response.status} ${response.statusText}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const text = await response.text();
|
|
177
|
+
return csvToJson(text, options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Асинхронная версия loadCsvFromUrl
|
|
182
|
+
*/
|
|
183
|
+
export async function loadCsvFromUrlAsync(
|
|
184
|
+
url: string,
|
|
185
|
+
options: CsvToJsonOptions = {}
|
|
186
|
+
): Promise<any[]> {
|
|
187
|
+
return loadCsvFromUrl(url, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Экспортирует данные в CSV и открывает в новой вкладке
|
|
192
|
+
*
|
|
193
|
+
* @param data - Данные для экспорта
|
|
194
|
+
* @param options - Опции для jsonToCsv
|
|
195
|
+
*/
|
|
196
|
+
export function openCsvInNewTab(
|
|
197
|
+
data: any[],
|
|
198
|
+
options: JsonToCsvOptions = {}
|
|
199
|
+
): void {
|
|
200
|
+
if (typeof window === 'undefined') {
|
|
201
|
+
throw new ValidationError('openCsvInNewTab() работает только в браузере');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const csv = jsonToCsv(data, options);
|
|
205
|
+
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
|
|
206
|
+
const url = URL.createObjectURL(blob);
|
|
207
|
+
|
|
208
|
+
window.open(url, '_blank');
|
|
209
|
+
|
|
210
|
+
// Освобождение URL через некоторое время
|
|
211
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Асинхронная версия openCsvInNewTab
|
|
216
|
+
*/
|
|
217
|
+
export async function openCsvInNewTabAsync(
|
|
218
|
+
data: any[],
|
|
219
|
+
options: JsonToCsvOptions = {}
|
|
220
|
+
): Promise<void> {
|
|
221
|
+
return openCsvInNewTab(data, options);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Копирует CSV в буфер обмена
|
|
226
|
+
*
|
|
227
|
+
* @param data - Данные для копирования
|
|
228
|
+
* @param options - Опции для jsonToCsv
|
|
229
|
+
* @returns Promise с результатом копирования
|
|
230
|
+
*/
|
|
231
|
+
export async function copyCsvToClipboard(
|
|
232
|
+
data: any[],
|
|
233
|
+
options: JsonToCsvOptions = {}
|
|
234
|
+
): Promise<boolean> {
|
|
235
|
+
if (typeof window === 'undefined' || !navigator.clipboard) {
|
|
236
|
+
throw new ValidationError('copyCsvToClipboard() требует поддержки Clipboard API');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const csv = jsonToCsv(data, options);
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
await navigator.clipboard.writeText(csv);
|
|
243
|
+
return true;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error('Failed to copy to clipboard:', error);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Сохраняет CSV в localStorage
|
|
252
|
+
*
|
|
253
|
+
* @param key - Ключ для сохранения
|
|
254
|
+
* @param data - Данные для сохранения
|
|
255
|
+
* @param options - Опции для jsonToCsv
|
|
256
|
+
*/
|
|
257
|
+
export function saveCsvToLocalStorage(
|
|
258
|
+
key: string,
|
|
259
|
+
data: any[],
|
|
260
|
+
options: JsonToCsvOptions = {}
|
|
261
|
+
): void {
|
|
262
|
+
if (typeof window === 'undefined' || !localStorage) {
|
|
263
|
+
throw new ValidationError('saveCsvToLocalStorage() требует localStorage');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const csv = jsonToCsv(data, options);
|
|
267
|
+
localStorage.setItem(key, csv);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Загружает CSV из localStorage
|
|
272
|
+
*
|
|
273
|
+
* @param key - Ключ для загрузки
|
|
274
|
+
* @param options - Опции для csvToJson
|
|
275
|
+
* @returns Распарсенные данные или null
|
|
276
|
+
*/
|
|
277
|
+
export function loadCsvFromLocalStorage(
|
|
278
|
+
key: string,
|
|
279
|
+
options: CsvToJsonOptions = {}
|
|
280
|
+
): any[] | null {
|
|
281
|
+
if (typeof window === 'undefined' || !localStorage) {
|
|
282
|
+
throw new ValidationError('loadCsvFromLocalStorage() требует localStorage');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const csv = localStorage.getItem(key);
|
|
286
|
+
|
|
287
|
+
if (!csv) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return csvToJson(csv, options);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Асинхронная версия loadCsvFromLocalStorage
|
|
296
|
+
*/
|
|
297
|
+
export async function loadCsvFromLocalStorageAsync(
|
|
298
|
+
key: string,
|
|
299
|
+
options: CsvToJsonOptions = {}
|
|
300
|
+
): Promise<any[] | null> {
|
|
301
|
+
return loadCsvFromLocalStorage(key, options);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Создает CSV файл из JSON данных (альтернатива downloadAsCsv)
|
|
306
|
+
* Возвращает Blob вместо автоматического скачивания
|
|
307
|
+
*
|
|
308
|
+
* @param data - Массив объектов
|
|
309
|
+
* @param options - Опции для jsonToCsv
|
|
310
|
+
* @returns CSV Blob
|
|
311
|
+
*/
|
|
312
|
+
export function createCsvBlob(
|
|
313
|
+
data: any[],
|
|
314
|
+
options: JsonToCsvOptions = {}
|
|
315
|
+
): Blob {
|
|
316
|
+
const csv = jsonToCsv(data, options);
|
|
317
|
+
return new Blob([csv], {
|
|
318
|
+
type: 'text/csv;charset=utf-8;'
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Асинхронная версия createCsvBlob
|
|
324
|
+
*/
|
|
325
|
+
export async function createCsvBlobAsync(
|
|
326
|
+
data: any[],
|
|
327
|
+
options: JsonToCsvOptions = {}
|
|
328
|
+
): Promise<Blob> {
|
|
329
|
+
return createCsvBlob(data, options);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Парсит CSV строку из Blob
|
|
334
|
+
*
|
|
335
|
+
* @param blob - CSV Blob
|
|
336
|
+
* @param options - Опции для csvToJson
|
|
337
|
+
* @returns Promise с JSON данными
|
|
338
|
+
*/
|
|
339
|
+
export async function parseCsvBlob(
|
|
340
|
+
blob: Blob,
|
|
341
|
+
options: CsvToJsonOptions = {}
|
|
342
|
+
): Promise<any[]> {
|
|
343
|
+
if (!(blob instanceof Blob)) {
|
|
344
|
+
throw new ValidationError('Input must be a Blob object');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return new Promise((resolve, reject) => {
|
|
348
|
+
const reader = new FileReader();
|
|
349
|
+
|
|
350
|
+
reader.onload = function (event: ProgressEvent<FileReader>) {
|
|
351
|
+
try {
|
|
352
|
+
const csvText = event.target?.result as string;
|
|
353
|
+
const json = csvToJson(csvText, options);
|
|
354
|
+
resolve(json);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
reject(error);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
reader.onerror = function () {
|
|
361
|
+
reject(new ValidationError('Ошибка чтения Blob'));
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
reader.readAsText(blob, 'UTF-8');
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Асинхронная версия parseCsvBlob
|
|
370
|
+
*/
|
|
371
|
+
export async function parseCsvBlobAsync(
|
|
372
|
+
blob: Blob,
|
|
373
|
+
options: CsvToJsonOptions = {}
|
|
374
|
+
): Promise<any[]> {
|
|
375
|
+
return parseCsvBlob(blob, options);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Экспорт для Node.js совместимости
|
|
379
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
380
|
+
module.exports = {
|
|
381
|
+
downloadAsCsv,
|
|
382
|
+
downloadAsCsvAsync,
|
|
383
|
+
parseCsvFile,
|
|
384
|
+
parseCsvFileStream,
|
|
385
|
+
createCsvBlob,
|
|
386
|
+
createCsvBlobAsync,
|
|
387
|
+
parseCsvBlob,
|
|
388
|
+
parseCsvBlobAsync,
|
|
389
|
+
jsonToCsvStream,
|
|
390
|
+
jsonToNdjsonStream,
|
|
391
|
+
csvToJsonStream,
|
|
392
|
+
loadCsvFromUrl,
|
|
393
|
+
loadCsvFromUrlAsync,
|
|
394
|
+
openCsvInNewTab,
|
|
395
|
+
openCsvInNewTabAsync,
|
|
396
|
+
copyCsvToClipboard,
|
|
397
|
+
saveCsvToLocalStorage,
|
|
398
|
+
loadCsvFromLocalStorage,
|
|
399
|
+
loadCsvFromLocalStorageAsync
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|