jtcsv 2.2.8 → 3.1.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.
Files changed (246) hide show
  1. package/README.md +204 -115
  2. package/bin/jtcsv.ts +2612 -0
  3. package/browser.d.ts +142 -0
  4. package/dist/benchmark.js +446 -0
  5. package/dist/benchmark.js.map +1 -0
  6. package/dist/bin/jtcsv.js +1940 -0
  7. package/dist/bin/jtcsv.js.map +1 -0
  8. package/dist/csv-to-json.js +1262 -0
  9. package/dist/csv-to-json.js.map +1 -0
  10. package/dist/errors.js +291 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/eslint.config.js +147 -0
  13. package/dist/eslint.config.js.map +1 -0
  14. package/dist/index-core.js +95 -0
  15. package/dist/index-core.js.map +1 -0
  16. package/dist/index.js +93 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/json-save.js +229 -0
  19. package/dist/json-save.js.map +1 -0
  20. package/dist/json-to-csv.js +576 -0
  21. package/dist/json-to-csv.js.map +1 -0
  22. package/dist/jtcsv-core.cjs.js +1736 -0
  23. package/dist/jtcsv-core.cjs.js.map +1 -0
  24. package/dist/jtcsv-core.esm.js +1708 -0
  25. package/dist/jtcsv-core.esm.js.map +1 -0
  26. package/dist/jtcsv-core.umd.js +1742 -0
  27. package/dist/jtcsv-core.umd.js.map +1 -0
  28. package/dist/jtcsv-full.cjs.js +2241 -0
  29. package/dist/jtcsv-full.cjs.js.map +1 -0
  30. package/dist/jtcsv-full.esm.js +2209 -0
  31. package/dist/jtcsv-full.esm.js.map +1 -0
  32. package/dist/jtcsv-full.umd.js +2247 -0
  33. package/dist/jtcsv-full.umd.js.map +1 -0
  34. package/dist/jtcsv-workers.esm.js +768 -0
  35. package/dist/jtcsv-workers.esm.js.map +1 -0
  36. package/dist/jtcsv-workers.umd.js +782 -0
  37. package/dist/jtcsv-workers.umd.js.map +1 -0
  38. package/dist/jtcsv.cjs.js +1996 -2048
  39. package/dist/jtcsv.cjs.js.map +1 -1
  40. package/dist/jtcsv.esm.js +1992 -2048
  41. package/dist/jtcsv.esm.js.map +1 -1
  42. package/dist/jtcsv.umd.js +2157 -2209
  43. package/dist/jtcsv.umd.js.map +1 -1
  44. package/dist/plugins/express-middleware/index.js +350 -0
  45. package/dist/plugins/express-middleware/index.js.map +1 -0
  46. package/dist/plugins/fastify-plugin/index.js +315 -0
  47. package/dist/plugins/fastify-plugin/index.js.map +1 -0
  48. package/dist/plugins/hono/index.js +111 -0
  49. package/dist/plugins/hono/index.js.map +1 -0
  50. package/dist/plugins/nestjs/index.js +112 -0
  51. package/dist/plugins/nestjs/index.js.map +1 -0
  52. package/dist/plugins/nuxt/index.js +53 -0
  53. package/dist/plugins/nuxt/index.js.map +1 -0
  54. package/dist/plugins/remix/index.js +133 -0
  55. package/dist/plugins/remix/index.js.map +1 -0
  56. package/dist/plugins/sveltekit/index.js +155 -0
  57. package/dist/plugins/sveltekit/index.js.map +1 -0
  58. package/dist/plugins/trpc/index.js +136 -0
  59. package/dist/plugins/trpc/index.js.map +1 -0
  60. package/dist/run-demo.js +49 -0
  61. package/dist/run-demo.js.map +1 -0
  62. package/dist/src/browser/browser-functions.js +193 -0
  63. package/dist/src/browser/browser-functions.js.map +1 -0
  64. package/dist/src/browser/core.js +123 -0
  65. package/dist/src/browser/core.js.map +1 -0
  66. package/dist/src/browser/csv-to-json-browser.js +353 -0
  67. package/dist/src/browser/csv-to-json-browser.js.map +1 -0
  68. package/dist/src/browser/errors-browser.js +219 -0
  69. package/dist/src/browser/errors-browser.js.map +1 -0
  70. package/dist/src/browser/extensions/plugins.js +106 -0
  71. package/dist/src/browser/extensions/plugins.js.map +1 -0
  72. package/dist/src/browser/extensions/workers.js +66 -0
  73. package/dist/src/browser/extensions/workers.js.map +1 -0
  74. package/dist/src/browser/index.js +140 -0
  75. package/dist/src/browser/index.js.map +1 -0
  76. package/dist/src/browser/json-to-csv-browser.js +225 -0
  77. package/dist/src/browser/json-to-csv-browser.js.map +1 -0
  78. package/dist/src/browser/streams.js +340 -0
  79. package/dist/src/browser/streams.js.map +1 -0
  80. package/dist/src/browser/workers/csv-parser.worker.js +264 -0
  81. package/dist/src/browser/workers/csv-parser.worker.js.map +1 -0
  82. package/dist/src/browser/workers/worker-pool.js +338 -0
  83. package/dist/src/browser/workers/worker-pool.js.map +1 -0
  84. package/dist/src/core/delimiter-cache.js +196 -0
  85. package/dist/src/core/delimiter-cache.js.map +1 -0
  86. package/dist/src/core/node-optimizations.js +279 -0
  87. package/dist/src/core/node-optimizations.js.map +1 -0
  88. package/dist/src/core/plugin-system.js +399 -0
  89. package/dist/src/core/plugin-system.js.map +1 -0
  90. package/dist/src/core/transform-hooks.js +348 -0
  91. package/dist/src/core/transform-hooks.js.map +1 -0
  92. package/dist/src/engines/fast-path-engine-new.js +262 -0
  93. package/dist/src/engines/fast-path-engine-new.js.map +1 -0
  94. package/dist/src/engines/fast-path-engine.js +671 -0
  95. package/dist/src/engines/fast-path-engine.js.map +1 -0
  96. package/dist/src/errors.js +18 -0
  97. package/dist/src/errors.js.map +1 -0
  98. package/dist/src/formats/ndjson-parser.js +332 -0
  99. package/dist/src/formats/ndjson-parser.js.map +1 -0
  100. package/dist/src/formats/tsv-parser.js +230 -0
  101. package/dist/src/formats/tsv-parser.js.map +1 -0
  102. package/dist/src/index-with-plugins.js +259 -0
  103. package/dist/src/index-with-plugins.js.map +1 -0
  104. package/dist/src/types/index.js +3 -0
  105. package/dist/src/types/index.js.map +1 -0
  106. package/dist/src/utils/bom-utils.js +267 -0
  107. package/dist/src/utils/bom-utils.js.map +1 -0
  108. package/dist/src/utils/encoding-support.js +77 -0
  109. package/dist/src/utils/encoding-support.js.map +1 -0
  110. package/dist/src/utils/schema-validator.js +609 -0
  111. package/dist/src/utils/schema-validator.js.map +1 -0
  112. package/dist/src/utils/transform-loader.js +281 -0
  113. package/dist/src/utils/transform-loader.js.map +1 -0
  114. package/dist/src/utils/validators.js +40 -0
  115. package/dist/src/utils/validators.js.map +1 -0
  116. package/dist/src/utils/zod-adapter.js +144 -0
  117. package/dist/src/utils/zod-adapter.js.map +1 -0
  118. package/dist/src/web-server/index.js +648 -0
  119. package/dist/src/web-server/index.js.map +1 -0
  120. package/dist/src/workers/csv-multithreaded.js +211 -0
  121. package/dist/src/workers/csv-multithreaded.js.map +1 -0
  122. package/dist/src/workers/csv-parser.worker.js +179 -0
  123. package/dist/src/workers/csv-parser.worker.js.map +1 -0
  124. package/dist/src/workers/worker-pool.js +228 -0
  125. package/dist/src/workers/worker-pool.js.map +1 -0
  126. package/dist/stream-csv-to-json.js +665 -0
  127. package/dist/stream-csv-to-json.js.map +1 -0
  128. package/dist/stream-json-to-csv.js +389 -0
  129. package/dist/stream-json-to-csv.js.map +1 -0
  130. package/examples/advanced/conditional-transformations.ts +446 -0
  131. package/examples/advanced/csv-parser.worker.ts +89 -0
  132. package/examples/advanced/nested-objects-example.ts +306 -0
  133. package/examples/advanced/performance-optimization.ts +504 -0
  134. package/examples/advanced/run-demo-server.ts +116 -0
  135. package/examples/advanced/web-worker-usage.html +874 -0
  136. package/examples/async-multithreaded-example.ts +335 -0
  137. package/examples/cli-advanced-usage.md +290 -0
  138. package/examples/{cli-batch-processing.js → cli-batch-processing.ts} +38 -38
  139. package/examples/{cli-tool.js → cli-tool.ts} +5 -8
  140. package/examples/{error-handling.js → error-handling.ts} +356 -324
  141. package/examples/{express-api.js → express-api.ts} +161 -164
  142. package/examples/{large-dataset-example.js → large-dataset-example.ts} +201 -182
  143. package/examples/{ndjson-processing.js → ndjson-processing.ts} +456 -434
  144. package/examples/{plugin-excel-exporter.js → plugin-excel-exporter.ts} +6 -7
  145. package/examples/react-integration.tsx +637 -0
  146. package/examples/{schema-validation.js → schema-validation.ts} +2 -2
  147. package/examples/simple-usage.ts +194 -0
  148. package/examples/{streaming-example.js → streaming-example.ts} +12 -12
  149. package/index.d.ts +187 -18
  150. package/package.json +75 -81
  151. package/plugins.d.ts +37 -0
  152. package/schema.d.ts +103 -0
  153. package/src/browser/browser-functions.ts +402 -0
  154. package/src/browser/core.ts +152 -0
  155. package/src/browser/csv-to-json-browser.d.ts +3 -0
  156. package/src/browser/csv-to-json-browser.ts +494 -0
  157. package/src/browser/{errors-browser.js → errors-browser.ts} +305 -197
  158. package/src/browser/extensions/plugins.ts +93 -0
  159. package/src/browser/extensions/workers.ts +39 -0
  160. package/src/browser/globals.d.ts +5 -0
  161. package/src/browser/index.ts +192 -0
  162. package/src/browser/json-to-csv-browser.d.ts +3 -0
  163. package/src/browser/json-to-csv-browser.ts +338 -0
  164. package/src/browser/streams.ts +403 -0
  165. package/src/browser/workers/{csv-parser.worker.js → csv-parser.worker.ts} +3 -3
  166. package/src/browser/workers/{worker-pool.js → worker-pool.ts} +51 -30
  167. package/src/core/delimiter-cache.ts +320 -0
  168. package/src/core/{node-optimizations.js → node-optimizations.ts} +448 -407
  169. package/src/core/plugin-system.ts +588 -0
  170. package/src/core/transform-hooks.ts +566 -0
  171. package/src/engines/{fast-path-engine-new.js → fast-path-engine-new.ts} +11 -2
  172. package/src/engines/{fast-path-engine.js → fast-path-engine.ts} +79 -53
  173. package/src/errors.ts +1 -0
  174. package/src/formats/{ndjson-parser.js → ndjson-parser.ts} +24 -16
  175. package/src/formats/{tsv-parser.js → tsv-parser.ts} +18 -17
  176. package/src/{index-with-plugins.js → index-with-plugins.ts} +381 -357
  177. package/src/types/index.ts +275 -0
  178. package/src/utils/bom-utils.ts +373 -0
  179. package/src/utils/encoding-support.ts +155 -0
  180. package/src/utils/{schema-validator.js → schema-validator.ts} +814 -589
  181. package/src/utils/transform-loader.ts +389 -0
  182. package/src/utils/validators.ts +35 -0
  183. package/src/utils/zod-adapter.ts +280 -0
  184. package/src/web-server/{index.js → index.ts} +19 -19
  185. package/src/workers/csv-multithreaded.ts +310 -0
  186. package/src/workers/csv-parser.worker.ts +227 -0
  187. package/src/workers/worker-pool.ts +409 -0
  188. package/bin/jtcsv.js +0 -2462
  189. package/csv-to-json.js +0 -688
  190. package/errors.js +0 -208
  191. package/examples/simple-usage.js +0 -282
  192. package/index.js +0 -68
  193. package/json-save.js +0 -254
  194. package/json-to-csv.js +0 -526
  195. package/plugins/README.md +0 -91
  196. package/plugins/express-middleware/README.md +0 -64
  197. package/plugins/express-middleware/example.js +0 -136
  198. package/plugins/express-middleware/index.d.ts +0 -114
  199. package/plugins/express-middleware/index.js +0 -360
  200. package/plugins/express-middleware/package.json +0 -52
  201. package/plugins/fastify-plugin/index.js +0 -406
  202. package/plugins/fastify-plugin/package.json +0 -55
  203. package/plugins/hono/README.md +0 -28
  204. package/plugins/hono/index.d.ts +0 -12
  205. package/plugins/hono/index.js +0 -36
  206. package/plugins/hono/package.json +0 -35
  207. package/plugins/nestjs/README.md +0 -35
  208. package/plugins/nestjs/index.d.ts +0 -25
  209. package/plugins/nestjs/index.js +0 -77
  210. package/plugins/nestjs/package.json +0 -37
  211. package/plugins/nextjs-api/README.md +0 -57
  212. package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
  213. package/plugins/nextjs-api/examples/api-convert.js +0 -69
  214. package/plugins/nextjs-api/index.js +0 -387
  215. package/plugins/nextjs-api/package.json +0 -63
  216. package/plugins/nextjs-api/route.js +0 -371
  217. package/plugins/nuxt/README.md +0 -24
  218. package/plugins/nuxt/index.js +0 -21
  219. package/plugins/nuxt/package.json +0 -35
  220. package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
  221. package/plugins/nuxt/runtime/plugin.js +0 -6
  222. package/plugins/remix/README.md +0 -26
  223. package/plugins/remix/index.d.ts +0 -16
  224. package/plugins/remix/index.js +0 -62
  225. package/plugins/remix/package.json +0 -35
  226. package/plugins/sveltekit/README.md +0 -28
  227. package/plugins/sveltekit/index.d.ts +0 -17
  228. package/plugins/sveltekit/index.js +0 -54
  229. package/plugins/sveltekit/package.json +0 -33
  230. package/plugins/trpc/README.md +0 -25
  231. package/plugins/trpc/index.d.ts +0 -7
  232. package/plugins/trpc/index.js +0 -32
  233. package/plugins/trpc/package.json +0 -34
  234. package/src/browser/browser-functions.js +0 -219
  235. package/src/browser/csv-to-json-browser.js +0 -700
  236. package/src/browser/index.js +0 -113
  237. package/src/browser/json-to-csv-browser.js +0 -309
  238. package/src/browser/streams.js +0 -393
  239. package/src/core/delimiter-cache.js +0 -186
  240. package/src/core/plugin-system.js +0 -476
  241. package/src/core/transform-hooks.js +0 -350
  242. package/src/errors.js +0 -26
  243. package/src/utils/transform-loader.js +0 -205
  244. package/stream-csv-to-json.js +0 -542
  245. package/stream-json-to-csv.js +0 -464
  246. /package/examples/{web-workers-advanced.js → web-workers-advanced.ts} +0 -0
@@ -1,113 +0,0 @@
1
- // Браузерный entry point для jtcsv
2
- // Экспортирует все функции с поддержкой браузера
3
-
4
- import { jsonToCsv, preprocessData, deepUnwrap } from './json-to-csv-browser.js';
5
- import { csvToJson, csvToJsonIterator, autoDetectDelimiter } from './csv-to-json-browser.js';
6
- import {
7
- downloadAsCsv,
8
- parseCsvFile,
9
- parseCsvFileStream,
10
- jsonToCsvStream,
11
- jsonToNdjsonStream,
12
- csvToJsonStream
13
- } from './browser-functions.js';
14
- import { createWorkerPool, parseCSVWithWorker } from './workers/worker-pool.js';
15
- import {
16
- ValidationError,
17
- SecurityError,
18
- FileSystemError,
19
- ParsingError,
20
- LimitError,
21
- ConfigurationError,
22
- ERROR_CODES
23
- } from './errors-browser.js';
24
-
25
- async function createWorkerPoolLazy(options = {}) {
26
- const mod = await import('./workers/worker-pool.js');
27
- return mod.createWorkerPool(options);
28
- }
29
-
30
- async function parseCSVWithWorkerLazy(csvInput, options = {}, onProgress = null) {
31
- const mod = await import('./workers/worker-pool.js');
32
- return mod.parseCSVWithWorker(csvInput, options, onProgress);
33
- }
34
-
35
- // Основной экспорт
36
- const jtcsv = {
37
- // JSON to CSV функции
38
- jsonToCsv,
39
- preprocessData,
40
- downloadAsCsv,
41
- deepUnwrap,
42
-
43
- // CSV to JSON функции
44
- csvToJson,
45
- csvToJsonIterator,
46
- parseCsvFile,
47
- parseCsvFileStream,
48
- jsonToCsvStream,
49
- jsonToNdjsonStream,
50
- csvToJsonStream,
51
- autoDetectDelimiter,
52
-
53
- // Web Workers функции
54
- createWorkerPool,
55
- parseCSVWithWorker,
56
- createWorkerPoolLazy,
57
- parseCSVWithWorkerLazy,
58
-
59
- // Error classes
60
- ValidationError,
61
- SecurityError,
62
- FileSystemError,
63
- ParsingError,
64
- LimitError,
65
- ConfigurationError,
66
- ERROR_CODES,
67
-
68
- // Удобные алиасы
69
- parse: csvToJson,
70
- unparse: jsonToCsv,
71
-
72
- // Версия
73
- version: '2.0.0-browser'
74
- };
75
-
76
- // Экспорт для разных сред
77
- if (typeof module !== 'undefined' && module.exports) {
78
- // Node.js CommonJS
79
- module.exports = jtcsv;
80
- } else if (typeof define === 'function' && define.amd) {
81
- // AMD
82
- define([], () => jtcsv);
83
- } else if (typeof window !== 'undefined') {
84
- // Браузер (глобальная переменная)
85
- window.jtcsv = jtcsv;
86
- }
87
-
88
- export default jtcsv;
89
- export {
90
- jsonToCsv,
91
- preprocessData,
92
- downloadAsCsv,
93
- deepUnwrap,
94
- csvToJson,
95
- csvToJsonIterator,
96
- parseCsvFile,
97
- parseCsvFileStream,
98
- jsonToCsvStream,
99
- jsonToNdjsonStream,
100
- csvToJsonStream,
101
- autoDetectDelimiter,
102
- createWorkerPool,
103
- parseCSVWithWorker,
104
- createWorkerPoolLazy,
105
- parseCSVWithWorkerLazy,
106
- ValidationError,
107
- SecurityError,
108
- FileSystemError,
109
- ParsingError,
110
- LimitError,
111
- ConfigurationError,
112
- ERROR_CODES
113
- };
@@ -1,309 +0,0 @@
1
- // Браузерная версия JSON to CSV конвертера
2
- // Адаптирована для работы в браузере без Node.js API
3
-
4
- import {
5
- ValidationError,
6
- ConfigurationError,
7
- LimitError,
8
- safeExecute
9
- } from './errors-browser.js';
10
-
11
- /**
12
- * Валидация входных данных и опций
13
- * @private
14
- */
15
- function validateInput(data, options) {
16
- // Validate data
17
- if (!Array.isArray(data)) {
18
- throw new ValidationError('Input data must be an array');
19
- }
20
-
21
- // Validate options
22
- if (options && typeof options !== 'object') {
23
- throw new ConfigurationError('Options must be an object');
24
- }
25
-
26
- // Validate delimiter
27
- if (options?.delimiter && typeof options.delimiter !== 'string') {
28
- throw new ConfigurationError('Delimiter must be a string');
29
- }
30
-
31
- if (options?.delimiter && options.delimiter.length !== 1) {
32
- throw new ConfigurationError('Delimiter must be a single character');
33
- }
34
-
35
- // Validate renameMap
36
- if (options?.renameMap && typeof options.renameMap !== 'object') {
37
- throw new ConfigurationError('renameMap must be an object');
38
- }
39
-
40
- // Validate maxRecords
41
- if (options && options.maxRecords !== undefined) {
42
- if (typeof options.maxRecords !== 'number' || options.maxRecords <= 0) {
43
- throw new ConfigurationError('maxRecords must be a positive number');
44
- }
45
- }
46
-
47
- // Validate preventCsvInjection
48
- if (options?.preventCsvInjection !== undefined && typeof options.preventCsvInjection !== 'boolean') {
49
- throw new ConfigurationError('preventCsvInjection must be a boolean');
50
- }
51
-
52
- // Validate rfc4180Compliant
53
- if (options?.rfc4180Compliant !== undefined && typeof options.rfc4180Compliant !== 'boolean') {
54
- throw new ConfigurationError('rfc4180Compliant must be a boolean');
55
- }
56
-
57
- return true;
58
- }
59
-
60
- /**
61
- * Конвертирует JSON данные в CSV формат
62
- *
63
- * @param {Array<Object>} data - Массив объектов для конвертации в CSV
64
- * @param {Object} [options] - Опции конфигурации
65
- * @param {string} [options.delimiter=';'] - CSV разделитель
66
- * @param {boolean} [options.includeHeaders=true] - Включать ли заголовки
67
- * @param {Object} [options.renameMap={}] - Маппинг переименования заголовков
68
- * @param {Object} [options.template={}] - Шаблон для порядка колонок
69
- * @param {number} [options.maxRecords] - Максимальное количество записей
70
- * @param {boolean} [options.preventCsvInjection=true] - Защита от CSV инъекций
71
- * @param {boolean} [options.rfc4180Compliant=true] - Соответствие RFC 4180
72
- * @returns {string} CSV строка
73
- */
74
- export function jsonToCsv(data, options = {}) {
75
- return safeExecute(() => {
76
- // Валидация входных данных
77
- validateInput(data, options);
78
-
79
- const opts = options && typeof options === 'object' ? options : {};
80
-
81
- const {
82
- delimiter = ';',
83
- includeHeaders = true,
84
- renameMap = {},
85
- template = {},
86
- maxRecords,
87
- preventCsvInjection = true,
88
- rfc4180Compliant = true
89
- } = opts;
90
-
91
- // Обработка пустых данных
92
- if (data.length === 0) {
93
- return '';
94
- }
95
-
96
- // Предупреждение для больших наборов данных
97
- if (data.length > 1000000 && !maxRecords && process.env.NODE_ENV !== 'production') {
98
- console.warn(
99
- '⚠️ Warning: Processing >1M records in memory may be slow.\n' +
100
- '💡 Consider processing data in batches or using Web Workers for large files.\n' +
101
- '📊 Current size: ' + data.length.toLocaleString() + ' records'
102
- );
103
- }
104
-
105
- // Применение ограничения по количеству записей
106
- if (maxRecords && data.length > maxRecords) {
107
- throw new LimitError(
108
- `Data size exceeds maximum limit of ${maxRecords} records`,
109
- maxRecords,
110
- data.length
111
- );
112
- }
113
-
114
- // Получение всех уникальных ключей
115
- const allKeys = new Set();
116
- data.forEach((item) => {
117
- if (!item || typeof item !== 'object') {
118
- return;
119
- }
120
- Object.keys(item).forEach(key => allKeys.add(key));
121
- });
122
-
123
- const originalKeys = Array.from(allKeys);
124
-
125
- // Применение rename map для создания заголовков
126
- const headers = originalKeys.map(key => renameMap[key] || key);
127
-
128
- // Создание обратного маппинга
129
- const reverseRenameMap = {};
130
- originalKeys.forEach((key, index) => {
131
- reverseRenameMap[headers[index]] = key;
132
- });
133
-
134
- // Применение порядка из шаблона
135
- let finalHeaders = headers;
136
- if (Object.keys(template).length > 0) {
137
- const templateHeaders = Object.keys(template).map(key => renameMap[key] || key);
138
- const extraHeaders = headers.filter(h => !templateHeaders.includes(h));
139
- finalHeaders = [...templateHeaders, ...extraHeaders];
140
- }
141
-
142
- /**
143
- * Экранирование значения для CSV с защитой от инъекций
144
- * @private
145
- */
146
- const escapeValue = (value) => {
147
- if (value === null || value === undefined || value === '') {
148
- return '';
149
- }
150
-
151
- const stringValue = String(value);
152
-
153
- // Защита от CSV инъекций
154
- let escapedValue = stringValue;
155
- if (preventCsvInjection && /^[=+\-@]/.test(stringValue)) {
156
- escapedValue = "'" + stringValue;
157
- }
158
-
159
- // Соответствие RFC 4180
160
- const needsQuoting = rfc4180Compliant
161
- ? (escapedValue.includes(delimiter) ||
162
- escapedValue.includes('"') ||
163
- escapedValue.includes('\n') ||
164
- escapedValue.includes('\r'))
165
- : (escapedValue.includes(delimiter) ||
166
- escapedValue.includes('"') ||
167
- escapedValue.includes('\n') ||
168
- escapedValue.includes('\r'));
169
-
170
- if (needsQuoting) {
171
- return `"${escapedValue.replace(/"/g, '""')}"`;
172
- }
173
-
174
- return escapedValue;
175
- };
176
-
177
- // Построение CSV строк
178
- const rows = [];
179
-
180
- // Добавление заголовков
181
- if (includeHeaders && finalHeaders.length > 0) {
182
- rows.push(finalHeaders.join(delimiter));
183
- }
184
-
185
- // Добавление данных
186
- for (const item of data) {
187
- if (!item || typeof item !== 'object') {
188
- continue;
189
- }
190
-
191
- const row = finalHeaders.map(header => {
192
- const originalKey = reverseRenameMap[header] || header;
193
- const value = item[originalKey];
194
- return escapeValue(value);
195
- }).join(delimiter);
196
-
197
- rows.push(row);
198
- }
199
-
200
- // Разделители строк согласно RFC 4180
201
- const lineEnding = rfc4180Compliant ? '\r\n' : '\n';
202
- return rows.join(lineEnding);
203
- }, 'PARSE_FAILED', { function: 'jsonToCsv' });
204
- }
205
-
206
- /**
207
- * Глубокое разворачивание вложенных объектов и массивов
208
- *
209
- * @param {*} value - Значение для разворачивания
210
- * @param {number} [depth=0] - Текущая глубина рекурсии
211
- * @param {number} [maxDepth=5] - Максимальная глубина рекурсии
212
- * @param {Set} [visited=new Set()] - Посещенные объекты для обнаружения циклических ссылок
213
- * @returns {string} Развернутое строковое значение
214
- */
215
- export function deepUnwrap(value, depth = 0, maxDepth = 5, visited = new Set()) {
216
- // Проверка глубины
217
- if (depth >= maxDepth) {
218
- return '[Too Deep]';
219
- }
220
- if (value === null || value === undefined) {
221
- return '';
222
- }
223
-
224
- // Обработка циклических ссылок
225
- if (typeof value === 'object') {
226
- if (visited.has(value)) {
227
- return '[Circular Reference]';
228
- }
229
- visited.add(value);
230
- }
231
-
232
- // Обработка массивов
233
- if (Array.isArray(value)) {
234
- if (value.length === 0) {
235
- return '';
236
- }
237
- const unwrappedItems = value.map(item =>
238
- deepUnwrap(item, depth + 1, maxDepth, visited)
239
- ).filter(item => item !== '');
240
- return unwrappedItems.join(', ');
241
- }
242
-
243
- // Обработка объектов
244
- if (typeof value === 'object') {
245
- const keys = Object.keys(value);
246
- if (keys.length === 0) {
247
- return '';
248
- }
249
-
250
- if (depth + 1 >= maxDepth) {
251
- return '[Too Deep]';
252
- }
253
-
254
- // Сериализация сложных объектов
255
- try {
256
- return JSON.stringify(value);
257
- } catch (error) {
258
- if (error.message.includes('circular') || error.message.includes('Converting circular')) {
259
- return '[Circular Reference]';
260
- }
261
- return '[Unstringifiable Object]';
262
- }
263
- }
264
-
265
- // Примитивные значения
266
- return String(value);
267
- }
268
-
269
- /**
270
- * Предобработка JSON данных путем глубокого разворачивания вложенных структур
271
- *
272
- * @param {Array<Object>} data - Массив объектов для предобработки
273
- * @returns {Array<Object>} Предобработанные данные с развернутыми значениями
274
- */
275
- export function preprocessData(data) {
276
- if (!Array.isArray(data)) {
277
- return [];
278
- }
279
-
280
- return data.map(item => {
281
- if (!item || typeof item !== 'object') {
282
- return {};
283
- }
284
-
285
- const processed = {};
286
-
287
- for (const key in item) {
288
- if (Object.prototype.hasOwnProperty.call(item, key)) {
289
- const value = item[key];
290
- if (value && typeof value === 'object') {
291
- processed[key] = deepUnwrap(value);
292
- } else {
293
- processed[key] = value;
294
- }
295
- }
296
- }
297
-
298
- return processed;
299
- });
300
- }
301
-
302
- // Экспорт для Node.js совместимости
303
- if (typeof module !== 'undefined' && module.exports) {
304
- module.exports = {
305
- jsonToCsv,
306
- preprocessData,
307
- deepUnwrap
308
- };
309
- }