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,408 +1,449 @@
1
- /**
2
- * Node.js Runtime Optimizations
3
- *
4
- * Detects Node.js version and provides optimized implementations
5
- * for modern runtimes while maintaining backward compatibility.
6
- *
7
- * Optimized for: Node 20, 22, 24
8
- * Compatible with: Node 12+
9
- */
10
-
11
- // Parse Node.js version
12
- const nodeVersion = process.versions?.node || '12.0.0';
13
- const [major, minor] = nodeVersion.split('.').map(Number);
14
-
15
- // Feature detection flags
16
- const features = {
17
- // Node 14.17+ / 16+
18
- hasAbortController: typeof AbortController !== 'undefined',
19
-
20
- // Node 15+
21
- hasPromiseAny: typeof Promise.any === 'function',
22
-
23
- // Node 16+
24
- hasArrayAt: typeof Array.prototype.at === 'function',
25
- hasObjectHasOwn: typeof Object.hasOwn === 'function',
26
-
27
- // Node 17+
28
- hasStructuredClone: typeof globalThis.structuredClone === 'function',
29
-
30
- // Node 18+
31
- hasFetch: typeof globalThis.fetch === 'function',
32
-
33
- // Node 20+
34
- hasWebStreams: typeof globalThis.ReadableStream !== 'undefined' && major >= 20,
35
- hasArrayGroup: typeof Array.prototype.group === 'function',
36
-
37
- // Node 21+
38
- hasSetMethods: typeof Set.prototype.union === 'function',
39
-
40
- // Node 22+
41
- hasImportMeta: major >= 22,
42
- hasExplicitResourceManagement: major >= 22,
43
-
44
- // Version checks
45
- isNode20Plus: major >= 20,
46
- isNode22Plus: major >= 22,
47
- isNode24Plus: major >= 24
48
- };
49
-
50
- /**
51
- * Optimized Object.hasOwn polyfill for older Node versions
52
- */
53
- const hasOwn = features.hasObjectHasOwn
54
- ? Object.hasOwn
55
- : (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
56
-
57
- /**
58
- * Optimized deep clone function
59
- * Uses structuredClone on Node 17+ for best performance
60
- */
61
- const deepClone = features.hasStructuredClone
62
- ? (obj) => structuredClone(obj)
63
- : (obj) => JSON.parse(JSON.stringify(obj));
64
-
65
- /**
66
- * Optimized array access with at() method
67
- */
68
- const arrayAt = features.hasArrayAt
69
- ? (arr, index) => arr.at(index)
70
- : (arr, index) => {
71
- const len = arr.length;
72
- const normalizedIndex = index < 0 ? len + index : index;
73
- return normalizedIndex >= 0 && normalizedIndex < len ? arr[normalizedIndex] : undefined;
74
- };
75
-
76
- /**
77
- * High-performance string builder for large CSV generation
78
- * Uses different strategies based on Node version
79
- */
80
- class StringBuilderOptimized {
81
- constructor(initialCapacity = 1024) {
82
- this.parts = [];
83
- this.length = 0;
84
- this.initialCapacity = initialCapacity;
85
-
86
- // Node 20+ uses more aggressive chunking
87
- this.chunkSize = features.isNode20Plus ? 65536 : 16384;
88
- }
89
-
90
- append(str) {
91
- if (str) {
92
- this.parts.push(str);
93
- this.length += str.length;
94
- }
95
- return this;
96
- }
97
-
98
- toString() {
99
- return this.parts.join('');
100
- }
101
-
102
- clear() {
103
- this.parts = [];
104
- this.length = 0;
105
- }
106
- }
107
-
108
- /**
109
- * Optimized row buffer for streaming CSV parsing
110
- * Minimizes allocations on modern Node versions
111
- */
112
- class RowBuffer {
113
- constructor(initialSize = 100) {
114
- this.rows = [];
115
- this.currentRow = [];
116
- this.rowCount = 0;
117
-
118
- // Pre-allocate on Node 20+
119
- if (features.isNode20Plus) {
120
- this.rows = new Array(initialSize);
121
- this.rows.length = 0;
122
- }
123
- }
124
-
125
- addField(field) {
126
- this.currentRow.push(field);
127
- }
128
-
129
- commitRow() {
130
- if (this.currentRow.length > 0) {
131
- this.rows.push(this.currentRow);
132
- this.rowCount++;
133
- this.currentRow = [];
134
- }
135
- }
136
-
137
- getRows() {
138
- return this.rows;
139
- }
140
-
141
- clear() {
142
- this.rows = features.isNode20Plus ? new Array(100) : [];
143
- this.rows.length = 0;
144
- this.currentRow = [];
145
- this.rowCount = 0;
146
- }
147
- }
148
-
149
- /**
150
- * Optimized field parser with char code comparisons
151
- * Faster than string comparisons on all Node versions
152
- */
153
- const CHAR_CODES = {
154
- QUOTE: 34, // "
155
- COMMA: 44, // ,
156
- SEMICOLON: 59, // ;
157
- TAB: 9, // \t
158
- PIPE: 124, // |
159
- NEWLINE: 10, // \n
160
- CARRIAGE: 13, // \r
161
- SPACE: 32, // space
162
- EQUALS: 61, // =
163
- PLUS: 43, // +
164
- MINUS: 45, // -
165
- AT: 64, // @
166
- BACKSLASH: 92, // \
167
- APOSTROPHE: 39 // '
168
- };
169
-
170
- /**
171
- * Fast delimiter detection using char codes
172
- */
173
- function fastDetectDelimiter(sample, candidates = [';', ',', '\t', '|']) {
174
- const firstLineEnd = sample.indexOf('\n');
175
- const firstLine = firstLineEnd > -1 ? sample.slice(0, firstLineEnd) : sample;
176
-
177
- const candidateCodes = candidates.map(c => c.charCodeAt(0));
178
- const counts = new Array(candidateCodes.length).fill(0);
179
-
180
- // Use fast char code iteration on Node 20+
181
- const len = Math.min(firstLine.length, 10000);
182
-
183
- for (let i = 0; i < len; i++) {
184
- const code = firstLine.charCodeAt(i);
185
- for (let j = 0; j < candidateCodes.length; j++) {
186
- if (code === candidateCodes[j]) {
187
- counts[j]++;
188
- }
189
- }
190
- }
191
-
192
- let maxCount = 0;
193
- let maxIndex = 0;
194
-
195
- for (let i = 0; i < counts.length; i++) {
196
- if (counts[i] > maxCount) {
197
- maxCount = counts[i];
198
- maxIndex = i;
199
- }
200
- }
201
-
202
- return candidates[maxIndex];
203
- }
204
-
205
- /**
206
- * Optimized batch processor for large datasets
207
- * Uses different chunk sizes based on Node version
208
- */
209
- function createBatchProcessor(processor, options = {}) {
210
- const batchSize = options.batchSize || (features.isNode20Plus ? 10000 : 5000);
211
- const parallelism = options.parallelism || (features.isNode22Plus ? 4 : 2);
212
-
213
- return async function* processBatches(items) {
214
- const batches = [];
215
-
216
- for (let i = 0; i < items.length; i += batchSize) {
217
- batches.push(items.slice(i, i + batchSize));
218
- }
219
-
220
- // Process batches with limited parallelism
221
- for (let i = 0; i < batches.length; i += parallelism) {
222
- const chunk = batches.slice(i, i + parallelism);
223
- const results = await Promise.all(chunk.map(batch => processor(batch)));
224
-
225
- for (const result of results) {
226
- yield* result;
227
- }
228
- }
229
- };
230
- }
231
-
232
- /**
233
- * Memory-efficient object pool for row objects
234
- * Reduces GC pressure on large CSV files
235
- */
236
- class ObjectPool {
237
- constructor(factory, initialSize = 100) {
238
- this.factory = factory;
239
- this.pool = [];
240
- this.inUse = 0;
241
-
242
- // Pre-warm pool on Node 20+
243
- if (features.isNode20Plus) {
244
- for (let i = 0; i < initialSize; i++) {
245
- this.pool.push(factory());
246
- }
247
- }
248
- }
249
-
250
- acquire() {
251
- this.inUse++;
252
- if (this.pool.length > 0) {
253
- return this.pool.pop();
254
- }
255
- return this.factory();
256
- }
257
-
258
- release(obj) {
259
- this.inUse--;
260
- // Clear object properties before returning to pool
261
- for (const key in obj) {
262
- if (hasOwn(obj, key)) {
263
- delete obj[key];
264
- }
265
- }
266
- this.pool.push(obj);
267
- }
268
-
269
- getStats() {
270
- return {
271
- poolSize: this.pool.length,
272
- inUse: this.inUse
273
- };
274
- }
275
- }
276
-
277
- /**
278
- * Fast string escape for CSV values
279
- * Uses pre-computed regex on all versions
280
- */
281
- const QUOTE_REGEX = /"/g;
282
-
283
- function fastEscapeValue(value, delimiterCode) {
284
- if (value === null || value === undefined || value === '') {
285
- return '';
286
- }
287
-
288
- const str = typeof value === 'string' ? value : String(value);
289
- const len = str.length;
290
-
291
- // Quick scan for special characters using char codes
292
- let needsQuoting = false;
293
- let hasQuote = false;
294
-
295
- for (let i = 0; i < len; i++) {
296
- const code = str.charCodeAt(i);
297
- if (code === CHAR_CODES.QUOTE) {
298
- hasQuote = true;
299
- needsQuoting = true;
300
- } else if (code === delimiterCode || code === CHAR_CODES.NEWLINE || code === CHAR_CODES.CARRIAGE) {
301
- needsQuoting = true;
302
- }
303
- }
304
-
305
- if (!needsQuoting) {
306
- return str;
307
- }
308
-
309
- const escaped = hasQuote ? str.replace(QUOTE_REGEX, '""') : str;
310
- return `"${escaped}"`;
311
- }
312
-
313
- /**
314
- * Async iterator utilities for streaming
315
- */
316
- const asyncIterUtils = {
317
- /**
318
- * Map over async iterator with concurrency control (Node 20+)
319
- */
320
- async *mapConcurrent(iterator, mapper, concurrency = 4) {
321
- const pending = [];
322
-
323
- for await (const item of iterator) {
324
- pending.push(mapper(item));
325
-
326
- if (pending.length >= concurrency) {
327
- const results = await Promise.all(pending.splice(0, concurrency));
328
- for (const result of results) {
329
- yield result;
330
- }
331
- }
332
- }
333
-
334
- if (pending.length > 0) {
335
- const results = await Promise.all(pending);
336
- for (const result of results) {
337
- yield result;
338
- }
339
- }
340
- },
341
-
342
- /**
343
- * Batch items from async iterator
344
- */
345
- async *batch(iterator, size = 1000) {
346
- let batch = [];
347
-
348
- for await (const item of iterator) {
349
- batch.push(item);
350
-
351
- if (batch.length >= size) {
352
- yield batch;
353
- batch = [];
354
- }
355
- }
356
-
357
- if (batch.length > 0) {
358
- yield batch;
359
- }
360
- }
361
- };
362
-
363
- /**
364
- * Get runtime optimization hints
365
- */
366
- function getOptimizationHints() {
367
- return {
368
- nodeVersion: `${major}.${minor}`,
369
- features,
370
- recommendations: {
371
- useWebStreams: features.hasWebStreams,
372
- useStructuredClone: features.hasStructuredClone,
373
- useLargerBatches: features.isNode20Plus,
374
- useHigherParallelism: features.isNode22Plus,
375
- preferredChunkSize: features.isNode24Plus ? 131072 : (features.isNode20Plus ? 65536 : 16384)
376
- }
377
- };
378
- }
379
-
380
- module.exports = {
381
- // Feature detection
382
- features,
383
- nodeVersion: { major, minor },
384
-
385
- // Polyfills and optimized functions
386
- hasOwn,
387
- deepClone,
388
- arrayAt,
389
-
390
- // Classes
391
- StringBuilderOptimized,
392
- RowBuffer,
393
- ObjectPool,
394
-
395
- // Constants
396
- CHAR_CODES,
397
-
398
- // Functions
399
- fastDetectDelimiter,
400
- fastEscapeValue,
401
- createBatchProcessor,
402
-
403
- // Async utilities
404
- asyncIterUtils,
405
-
406
- // Diagnostics
407
- getOptimizationHints
1
+ /**
2
+ * Node.js Runtime Optimizations
3
+ *
4
+ * Detects Node.js version and provides optimized implementations
5
+ * for modern runtimes while maintaining backward compatibility.
6
+ *
7
+ * Optimized for: Node 20, 22, 24
8
+ * Compatible with: Node 12+
9
+ */
10
+
11
+ // Parse Node.js version
12
+ const nodeVersionStr = process.versions?.node || '12.0.0';
13
+ const [major, minor] = nodeVersionStr.split('.').map(Number);
14
+
15
+ // Feature detection flags
16
+ export const features = {
17
+ // Node 14.17+ / 16+
18
+ hasAbortController: typeof AbortController !== 'undefined',
19
+
20
+ // Node 15+
21
+ hasPromiseAny: typeof (Promise as any).any === 'function',
22
+
23
+ // Node 16+
24
+ hasArrayAt: typeof Array.prototype.at === 'function',
25
+ hasObjectHasOwn: typeof (Object as any).hasOwn === 'function',
26
+
27
+ // Node 17+
28
+ hasStructuredClone: typeof globalThis.structuredClone === 'function',
29
+
30
+ // Node 18+
31
+ hasFetch: typeof globalThis.fetch === 'function',
32
+
33
+ // Node 20+
34
+ hasWebStreams: typeof globalThis.ReadableStream !== 'undefined' && major >= 20,
35
+ hasArrayGroup: typeof (Array.prototype as any).group === 'function',
36
+
37
+ // Node 21+
38
+ hasSetMethods: typeof (Set.prototype as any).union === 'function',
39
+
40
+ // Node 22+
41
+ hasImportMeta: major >= 22,
42
+ hasExplicitResourceManagement: major >= 22,
43
+
44
+ // Version checks
45
+ isNode20Plus: major >= 20,
46
+ isNode22Plus: major >= 22,
47
+ isNode24Plus: major >= 24
48
+ };
49
+
50
+ /**
51
+ * Optimized Object.hasOwn polyfill for older Node versions
52
+ */
53
+ export const hasOwn = features.hasObjectHasOwn
54
+ ? (Object as any).hasOwn
55
+ : (obj: object, prop: string | number | symbol): boolean =>
56
+ Object.prototype.hasOwnProperty.call(obj, prop);
57
+
58
+ /**
59
+ * Optimized deep clone function
60
+ * Uses structuredClone on Node 17+ for best performance
61
+ */
62
+ export const deepClone = features.hasStructuredClone
63
+ ? <T>(obj: T): T => structuredClone(obj)
64
+ : <T>(obj: T): T => JSON.parse(JSON.stringify(obj));
65
+
66
+ /**
67
+ * Optimized array access with at() method
68
+ */
69
+ export const arrayAt = features.hasArrayAt
70
+ ? <T>(arr: T[], index: number): T | undefined => arr.at(index)
71
+ : <T>(arr: T[], index: number): T | undefined => {
72
+ const len = arr.length;
73
+ const normalizedIndex = index < 0 ? len + index : index;
74
+ return normalizedIndex >= 0 && normalizedIndex < len ? arr[normalizedIndex] : undefined;
75
+ };
76
+
77
+ /**
78
+ * High-performance string builder for large CSV generation
79
+ * Uses different strategies based on Node version
80
+ */
81
+ export class StringBuilderOptimized {
82
+ private parts: string[];
83
+ private length: number;
84
+ private initialCapacity: number;
85
+ private chunkSize: number;
86
+
87
+ constructor(initialCapacity = 1024) {
88
+ this.parts = [];
89
+ this.length = 0;
90
+ this.initialCapacity = initialCapacity;
91
+
92
+ // Node 20+ uses more aggressive chunking
93
+ this.chunkSize = features.isNode20Plus ? 65536 : 16384;
94
+ }
95
+
96
+ append(str: string): this {
97
+ if (str) {
98
+ this.parts.push(str);
99
+ this.length += str.length;
100
+ }
101
+ return this;
102
+ }
103
+
104
+ toString(): string {
105
+ return this.parts.join('');
106
+ }
107
+
108
+ clear(): void {
109
+ this.parts = [];
110
+ this.length = 0;
111
+ }
112
+
113
+ getLength(): number {
114
+ return this.length;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Optimized row buffer for streaming CSV parsing
120
+ * Minimizes allocations on modern Node versions
121
+ */
122
+ export class RowBuffer<T = any> {
123
+ private rows: T[][];
124
+ private currentRow: T[];
125
+ private rowCount: number;
126
+
127
+ constructor(initialSize = 100) {
128
+ this.rows = [];
129
+ this.currentRow = [];
130
+ this.rowCount = 0;
131
+
132
+ // Pre-allocate on Node 20+
133
+ if (features.isNode20Plus) {
134
+ this.rows = new Array(initialSize);
135
+ this.rows.length = 0;
136
+ }
137
+ }
138
+
139
+ addField(field: T): void {
140
+ this.currentRow.push(field);
141
+ }
142
+
143
+ commitRow(): void {
144
+ if (this.currentRow.length > 0) {
145
+ this.rows.push(this.currentRow);
146
+ this.rowCount++;
147
+ this.currentRow = [];
148
+ }
149
+ }
150
+
151
+ getRows(): T[][] {
152
+ return this.rows;
153
+ }
154
+
155
+ clear(): void {
156
+ this.rows = features.isNode20Plus ? new Array(100) : [];
157
+ this.rows.length = 0;
158
+ this.currentRow = [];
159
+ this.rowCount = 0;
160
+ }
161
+
162
+ getRowCount(): number {
163
+ return this.rowCount;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Optimized field parser with char code comparisons
169
+ * Faster than string comparisons on all Node versions
170
+ */
171
+ export const CHAR_CODES = {
172
+ QUOTE: 34, // "
173
+ COMMA: 44, // ,
174
+ SEMICOLON: 59, // ;
175
+ TAB: 9, // \t
176
+ PIPE: 124, // |
177
+ NEWLINE: 10, // \n
178
+ CARRIAGE: 13, // \r
179
+ SPACE: 32, // space
180
+ EQUALS: 61, // =
181
+ PLUS: 43, // +
182
+ MINUS: 45, // -
183
+ AT: 64, // @
184
+ BACKSLASH: 92, // \
185
+ APOSTROPHE: 39 // '
186
+ } as const;
187
+
188
+ /**
189
+ * Fast delimiter detection using char codes
190
+ */
191
+ export function fastDetectDelimiter(sample: string, candidates = [';', ',', '\t', '|']): string {
192
+ const firstLineEnd = sample.indexOf('\n');
193
+ const firstLine = firstLineEnd > -1 ? sample.slice(0, firstLineEnd) : sample;
194
+
195
+ const candidateCodes = candidates.map(c => c.charCodeAt(0));
196
+ const counts = new Array(candidateCodes.length).fill(0);
197
+
198
+ // Use fast char code iteration on Node 20+
199
+ const len = Math.min(firstLine.length, 10000);
200
+
201
+ for (let i = 0; i < len; i++) {
202
+ const code = firstLine.charCodeAt(i);
203
+ for (let j = 0; j < candidateCodes.length; j++) {
204
+ if (code === candidateCodes[j]) {
205
+ counts[j]++;
206
+ }
207
+ }
208
+ }
209
+
210
+ let maxCount = 0;
211
+ let maxIndex = 0;
212
+
213
+ for (let i = 0; i < counts.length; i++) {
214
+ if (counts[i] > maxCount) {
215
+ maxCount = counts[i];
216
+ maxIndex = i;
217
+ }
218
+ }
219
+
220
+ return candidates[maxIndex];
221
+ }
222
+
223
+ /**
224
+ * Optimized batch processor for large datasets
225
+ * Uses different chunk sizes based on Node version
226
+ */
227
+ export function createBatchProcessor<T, R>(
228
+ processor: (batch: T[]) => Promise<R[]> | R[],
229
+ options: { batchSize?: number; parallelism?: number } = {}
230
+ ): (items: T[]) => AsyncGenerator<R> {
231
+ const batchSize = options.batchSize || (features.isNode20Plus ? 10000 : 5000);
232
+ const parallelism = options.parallelism || (features.isNode22Plus ? 4 : 2);
233
+
234
+ return async function* processBatches(items: T[]): AsyncGenerator<R> {
235
+ const batches: T[][] = [];
236
+
237
+ for (let i = 0; i < items.length; i += batchSize) {
238
+ batches.push(items.slice(i, i + batchSize));
239
+ }
240
+
241
+ // Process batches with limited parallelism
242
+ for (let i = 0; i < batches.length; i += parallelism) {
243
+ const chunk = batches.slice(i, i + parallelism);
244
+ const results = await Promise.all(chunk.map(batch => processor(batch)));
245
+
246
+ for (const result of results) {
247
+ yield* result;
248
+ }
249
+ }
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Memory-efficient object pool for row objects
255
+ * Reduces GC pressure on large CSV files
256
+ */
257
+ export class ObjectPool<T> {
258
+ private factory: () => T;
259
+ private pool: T[];
260
+ private inUse: number;
261
+
262
+ constructor(factory: () => T, initialSize = 100) {
263
+ this.factory = factory;
264
+ this.pool = [];
265
+ this.inUse = 0;
266
+
267
+ // Pre-warm pool on Node 20+
268
+ if (features.isNode20Plus) {
269
+ for (let i = 0; i < initialSize; i++) {
270
+ this.pool.push(factory());
271
+ }
272
+ }
273
+ }
274
+
275
+ acquire(): T {
276
+ this.inUse++;
277
+ if (this.pool.length > 0) {
278
+ return this.pool.pop()!;
279
+ }
280
+ return this.factory();
281
+ }
282
+
283
+ release(obj: T): void {
284
+ this.inUse--;
285
+ // Clear object properties before returning to pool
286
+ for (const key in obj) {
287
+ if (hasOwn(obj, key)) {
288
+ delete (obj as any)[key];
289
+ }
290
+ }
291
+ this.pool.push(obj);
292
+ }
293
+
294
+ getStats(): { poolSize: number; inUse: number } {
295
+ return {
296
+ poolSize: this.pool.length,
297
+ inUse: this.inUse
298
+ };
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Fast string escape for CSV values
304
+ * Uses pre-computed regex on all versions
305
+ */
306
+ const QUOTE_REGEX = /"/g;
307
+
308
+ export function fastEscapeValue(value: any, delimiterCode: number): string {
309
+ if (value === null || value === undefined || value === '') {
310
+ return '';
311
+ }
312
+
313
+ const str = typeof value === 'string' ? value : String(value);
314
+ const len = str.length;
315
+
316
+ // Quick scan for special characters using char codes
317
+ let needsQuoting = false;
318
+ let hasQuote = false;
319
+
320
+ for (let i = 0; i < len; i++) {
321
+ const code = str.charCodeAt(i);
322
+ if (code === CHAR_CODES.QUOTE) {
323
+ hasQuote = true;
324
+ needsQuoting = true;
325
+ } else if (code === delimiterCode || code === CHAR_CODES.NEWLINE || code === CHAR_CODES.CARRIAGE) {
326
+ needsQuoting = true;
327
+ }
328
+ }
329
+
330
+ if (!needsQuoting) {
331
+ return str;
332
+ }
333
+
334
+ const escaped = hasQuote ? str.replace(QUOTE_REGEX, '""') : str;
335
+ return `"${escaped}"`;
336
+ }
337
+
338
+ /**
339
+ * Async iterator utilities for streaming
340
+ */
341
+ export const asyncIterUtils = {
342
+ /**
343
+ * Map over async iterator with concurrency control (Node 20+)
344
+ */
345
+ async *mapConcurrent<T, R>(
346
+ iterator: AsyncIterable<T>,
347
+ mapper: (item: T) => Promise<R> | R,
348
+ concurrency = 4
349
+ ): AsyncGenerator<R> {
350
+ const pending: Promise<R>[] = [];
351
+
352
+ for await (const item of iterator) {
353
+ pending.push(Promise.resolve(mapper(item)));
354
+
355
+ if (pending.length >= concurrency) {
356
+ const results = await Promise.all(pending.splice(0, concurrency));
357
+ for (const result of results) {
358
+ yield result;
359
+ }
360
+ }
361
+ }
362
+
363
+ if (pending.length > 0) {
364
+ const results = await Promise.all(pending);
365
+ for (const result of results) {
366
+ yield result;
367
+ }
368
+ }
369
+ },
370
+
371
+ /**
372
+ * Batch items from async iterator
373
+ */
374
+ async *batch<T>(iterator: AsyncIterable<T>, size = 1000): AsyncGenerator<T[]> {
375
+ let batch: T[] = [];
376
+
377
+ for await (const item of iterator) {
378
+ batch.push(item);
379
+
380
+ if (batch.length >= size) {
381
+ yield batch;
382
+ batch = [];
383
+ }
384
+ }
385
+
386
+ if (batch.length > 0) {
387
+ yield batch;
388
+ }
389
+ }
390
+ };
391
+
392
+ /**
393
+ * Get runtime optimization hints
394
+ */
395
+ export function getOptimizationHints(): {
396
+ nodeVersion: string;
397
+ features: typeof features;
398
+ recommendations: {
399
+ useWebStreams: boolean;
400
+ useStructuredClone: boolean;
401
+ useLargerBatches: boolean;
402
+ useHigherParallelism: boolean;
403
+ preferredChunkSize: number;
404
+ };
405
+ } {
406
+ return {
407
+ nodeVersion: `${major}.${minor}`,
408
+ features,
409
+ recommendations: {
410
+ useWebStreams: features.hasWebStreams,
411
+ useStructuredClone: features.hasStructuredClone,
412
+ useLargerBatches: features.isNode20Plus,
413
+ useHigherParallelism: features.isNode22Plus,
414
+ preferredChunkSize: features.isNode24Plus ? 131072 : (features.isNode20Plus ? 65536 : 16384)
415
+ }
416
+ };
417
+ }
418
+
419
+ export const nodeVersionInfo = { major, minor };
420
+
421
+ export default {
422
+ // Feature detection
423
+ features,
424
+ nodeVersion: { major, minor },
425
+
426
+ // Polyfills and optimized functions
427
+ hasOwn,
428
+ deepClone,
429
+ arrayAt,
430
+
431
+ // Classes
432
+ StringBuilderOptimized,
433
+ RowBuffer,
434
+ ObjectPool,
435
+
436
+ // Constants
437
+ CHAR_CODES,
438
+
439
+ // Functions
440
+ fastDetectDelimiter,
441
+ fastEscapeValue,
442
+ createBatchProcessor,
443
+
444
+ // Async utilities
445
+ asyncIterUtils,
446
+
447
+ // Diagnostics
448
+ getOptimizationHints
408
449
  };