jtcsv 3.0.0 → 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 (258) hide show
  1. package/README.md +205 -146
  2. package/bin/jtcsv.ts +280 -202
  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 +336 -7
  23. package/dist/jtcsv-core.cjs.js.map +1 -1
  24. package/dist/jtcsv-core.esm.js +336 -7
  25. package/dist/jtcsv-core.esm.js.map +1 -1
  26. package/dist/jtcsv-core.umd.js +336 -7
  27. package/dist/jtcsv-core.umd.js.map +1 -1
  28. package/dist/jtcsv-full.cjs.js +336 -7
  29. package/dist/jtcsv-full.cjs.js.map +1 -1
  30. package/dist/jtcsv-full.esm.js +336 -7
  31. package/dist/jtcsv-full.esm.js.map +1 -1
  32. package/dist/jtcsv-full.umd.js +336 -7
  33. package/dist/jtcsv-full.umd.js.map +1 -1
  34. package/dist/jtcsv-workers.esm.js +9 -0
  35. package/dist/jtcsv-workers.esm.js.map +1 -1
  36. package/dist/jtcsv-workers.umd.js +9 -0
  37. package/dist/jtcsv-workers.umd.js.map +1 -1
  38. package/dist/jtcsv.cjs.js +1998 -2092
  39. package/dist/jtcsv.cjs.js.map +1 -1
  40. package/dist/jtcsv.esm.js +1994 -2092
  41. package/dist/jtcsv.esm.js.map +1 -1
  42. package/dist/jtcsv.umd.js +2157 -2251
  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/{src → dist/src}/web-server/index.js +251 -286
  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 +2 -2
  131. package/examples/advanced/performance-optimization.ts +2 -2
  132. package/examples/cli-advanced-usage.md +2 -0
  133. package/examples/cli-tool.ts +1 -1
  134. package/examples/large-dataset-example.ts +2 -2
  135. package/examples/simple-usage.ts +2 -2
  136. package/examples/streaming-example.ts +1 -1
  137. package/index.d.ts +186 -15
  138. package/package.json +43 -108
  139. package/plugins.d.ts +37 -0
  140. package/schema.d.ts +103 -0
  141. package/src/browser/csv-to-json-browser.ts +233 -3
  142. package/src/browser/errors-browser.ts +45 -28
  143. package/src/browser/json-to-csv-browser.ts +81 -5
  144. package/src/browser/streams.ts +73 -6
  145. package/src/core/delimiter-cache.ts +21 -11
  146. package/src/core/plugin-system.ts +343 -155
  147. package/src/core/transform-hooks.ts +20 -12
  148. package/src/engines/fast-path-engine.ts +48 -32
  149. package/src/errors.ts +1 -72
  150. package/src/formats/ndjson-parser.ts +6 -0
  151. package/src/formats/tsv-parser.ts +6 -0
  152. package/src/types/index.ts +21 -1
  153. package/src/utils/validators.ts +35 -0
  154. package/src/web-server/index.ts +1 -1
  155. package/bin/jtcsv.js +0 -2532
  156. package/csv-to-json.js +0 -711
  157. package/errors.js +0 -394
  158. package/examples/advanced/conditional-transformations.js +0 -446
  159. package/examples/advanced/csv-parser.worker.js +0 -89
  160. package/examples/advanced/nested-objects-example.js +0 -306
  161. package/examples/advanced/performance-optimization.js +0 -504
  162. package/examples/advanced/run-demo-server.js +0 -116
  163. package/examples/cli-batch-processing.js +0 -38
  164. package/examples/cli-tool.js +0 -183
  165. package/examples/error-handling.js +0 -338
  166. package/examples/express-api.js +0 -164
  167. package/examples/large-dataset-example.js +0 -182
  168. package/examples/ndjson-processing.js +0 -434
  169. package/examples/plugin-excel-exporter.js +0 -406
  170. package/examples/schema-validation.js +0 -640
  171. package/examples/simple-usage.js +0 -282
  172. package/examples/streaming-example.js +0 -418
  173. package/examples/web-workers-advanced.js +0 -28
  174. package/index.js +0 -82
  175. package/json-save.js +0 -255
  176. package/json-to-csv.js +0 -668
  177. package/plugins/README.md +0 -91
  178. package/plugins/express-middleware/README.md +0 -83
  179. package/plugins/express-middleware/example.js +0 -135
  180. package/plugins/express-middleware/example.ts +0 -135
  181. package/plugins/express-middleware/index.d.ts +0 -114
  182. package/plugins/express-middleware/index.js +0 -512
  183. package/plugins/express-middleware/index.ts +0 -557
  184. package/plugins/express-middleware/package.json +0 -52
  185. package/plugins/fastify-plugin/index.js +0 -404
  186. package/plugins/fastify-plugin/index.ts +0 -443
  187. package/plugins/fastify-plugin/package.json +0 -55
  188. package/plugins/hono/README.md +0 -28
  189. package/plugins/hono/index.d.ts +0 -12
  190. package/plugins/hono/index.js +0 -36
  191. package/plugins/hono/index.ts +0 -226
  192. package/plugins/hono/package.json +0 -35
  193. package/plugins/nestjs/README.md +0 -35
  194. package/plugins/nestjs/index.d.ts +0 -25
  195. package/plugins/nestjs/index.js +0 -77
  196. package/plugins/nestjs/index.ts +0 -201
  197. package/plugins/nestjs/package.json +0 -37
  198. package/plugins/nextjs-api/README.md +0 -57
  199. package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
  200. package/plugins/nextjs-api/examples/ConverterComponent.tsx +0 -386
  201. package/plugins/nextjs-api/examples/api-convert.js +0 -67
  202. package/plugins/nextjs-api/examples/api-convert.ts +0 -67
  203. package/plugins/nextjs-api/index.js +0 -387
  204. package/plugins/nextjs-api/index.tsx +0 -339
  205. package/plugins/nextjs-api/package.json +0 -63
  206. package/plugins/nextjs-api/route.js +0 -370
  207. package/plugins/nextjs-api/route.ts +0 -370
  208. package/plugins/nuxt/README.md +0 -24
  209. package/plugins/nuxt/index.js +0 -21
  210. package/plugins/nuxt/index.ts +0 -94
  211. package/plugins/nuxt/package.json +0 -35
  212. package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
  213. package/plugins/nuxt/runtime/composables/useJtcsv.ts +0 -100
  214. package/plugins/nuxt/runtime/plugin.js +0 -6
  215. package/plugins/nuxt/runtime/plugin.ts +0 -71
  216. package/plugins/remix/README.md +0 -26
  217. package/plugins/remix/index.d.ts +0 -16
  218. package/plugins/remix/index.js +0 -62
  219. package/plugins/remix/index.ts +0 -260
  220. package/plugins/remix/package.json +0 -35
  221. package/plugins/sveltekit/README.md +0 -28
  222. package/plugins/sveltekit/index.d.ts +0 -17
  223. package/plugins/sveltekit/index.js +0 -54
  224. package/plugins/sveltekit/index.ts +0 -301
  225. package/plugins/sveltekit/package.json +0 -33
  226. package/plugins/trpc/README.md +0 -25
  227. package/plugins/trpc/index.d.ts +0 -7
  228. package/plugins/trpc/index.js +0 -32
  229. package/plugins/trpc/index.ts +0 -267
  230. package/plugins/trpc/package.json +0 -34
  231. package/src/browser/browser-functions.js +0 -219
  232. package/src/browser/core.js +0 -92
  233. package/src/browser/csv-to-json-browser.js +0 -722
  234. package/src/browser/errors-browser.js +0 -212
  235. package/src/browser/extensions/plugins.js +0 -92
  236. package/src/browser/extensions/workers.js +0 -39
  237. package/src/browser/index.js +0 -113
  238. package/src/browser/json-to-csv-browser.js +0 -319
  239. package/src/browser/streams.js +0 -403
  240. package/src/browser/workers/csv-parser.worker.js +0 -377
  241. package/src/browser/workers/worker-pool.js +0 -527
  242. package/src/core/delimiter-cache.js +0 -200
  243. package/src/core/node-optimizations.js +0 -408
  244. package/src/core/plugin-system.js +0 -494
  245. package/src/core/transform-hooks.js +0 -350
  246. package/src/engines/fast-path-engine-new.js +0 -338
  247. package/src/engines/fast-path-engine.js +0 -844
  248. package/src/errors.js +0 -26
  249. package/src/formats/ndjson-parser.js +0 -467
  250. package/src/formats/tsv-parser.js +0 -339
  251. package/src/index-with-plugins.js +0 -378
  252. package/src/utils/bom-utils.js +0 -259
  253. package/src/utils/encoding-support.js +0 -124
  254. package/src/utils/schema-validator.js +0 -594
  255. package/src/utils/transform-loader.js +0 -205
  256. package/src/utils/zod-adapter.js +0 -170
  257. package/stream-csv-to-json.js +0 -560
  258. package/stream-json-to-csv.js +0 -465
@@ -0,0 +1,609 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadSchema = loadSchema;
37
+ exports.createValidationHook = createValidationHook;
38
+ exports.applySchemaValidation = applySchemaValidation;
39
+ exports.createValidationHooks = createValidationHooks;
40
+ exports.createSchemaValidators = createSchemaValidators;
41
+ exports.loadSchemaAsync = loadSchemaAsync;
42
+ exports.applySchemaValidationAsync = applySchemaValidationAsync;
43
+ exports.createAsyncValidationHook = createAsyncValidationHook;
44
+ const fs = __importStar(require("fs"));
45
+ const fsPromises = __importStar(require("fs/promises"));
46
+ const path = __importStar(require("path"));
47
+ const errors_1 = require("../errors");
48
+ function loadSchema(schemaPathOrJson) {
49
+ if (!schemaPathOrJson || typeof schemaPathOrJson !== 'string') {
50
+ throw new errors_1.ValidationError('Schema must be a string (JSON or file path)');
51
+ }
52
+ let schemaString = schemaPathOrJson;
53
+ const isFilePath = schemaPathOrJson.endsWith('.json') ||
54
+ schemaPathOrJson.includes('/') ||
55
+ schemaPathOrJson.includes('\\');
56
+ if (isFilePath) {
57
+ const safePath = path.resolve(schemaPathOrJson);
58
+ const normalizedPath = path.normalize(schemaPathOrJson);
59
+ if (normalizedPath.includes('..') ||
60
+ /\\\.\.\\|\/\.\.\//.test(schemaPathOrJson) ||
61
+ schemaPathOrJson.startsWith('..') ||
62
+ schemaPathOrJson.includes('/..')) {
63
+ throw new errors_1.SecurityError('Directory traversal detected in schema file path');
64
+ }
65
+ if (!fs.existsSync(safePath)) {
66
+ throw new errors_1.ValidationError(`Schema file not found: ${schemaPathOrJson}`);
67
+ }
68
+ if (!safePath.toLowerCase().endsWith('.json')) {
69
+ throw new errors_1.ValidationError('Schema file must have .json extension');
70
+ }
71
+ try {
72
+ schemaString = fs.readFileSync(safePath, 'utf8');
73
+ }
74
+ catch (error) {
75
+ if (error.code === 'EACCES') {
76
+ throw new errors_1.SecurityError(`Permission denied reading schema file: ${schemaPathOrJson}`);
77
+ }
78
+ throw new errors_1.ValidationError(`Failed to read schema file: ${error.message}`);
79
+ }
80
+ }
81
+ try {
82
+ const schema = JSON.parse(schemaString);
83
+ if (typeof schema !== 'object' || schema === null) {
84
+ throw new errors_1.ValidationError('Schema must be a JSON object');
85
+ }
86
+ return schema;
87
+ }
88
+ catch (error) {
89
+ if (error instanceof SyntaxError) {
90
+ throw new errors_1.ValidationError(`Invalid JSON in schema: ${error.message}`);
91
+ }
92
+ throw new errors_1.ValidationError(`Failed to parse schema: ${error.message}`);
93
+ }
94
+ }
95
+ function createSimpleValidator(schema) {
96
+ return {
97
+ validate(data, options = {}) {
98
+ const errors = [];
99
+ const warnings = [];
100
+ if (!Array.isArray(data)) {
101
+ return {
102
+ valid: false,
103
+ errors: [{
104
+ row: 0,
105
+ type: 'INVALID_DATA',
106
+ field: '',
107
+ message: 'Data must be an array'
108
+ }],
109
+ warnings: [],
110
+ summary: {
111
+ totalRows: 0,
112
+ validRows: 0,
113
+ errorCount: 1,
114
+ warningCount: 0
115
+ }
116
+ };
117
+ }
118
+ for (let i = 0; i < data.length; i++) {
119
+ const row = data[i];
120
+ for (const [field, rule] of Object.entries(schema)) {
121
+ const value = row[field];
122
+ if (rule.required && (value === undefined || value === null || value === '')) {
123
+ errors.push({
124
+ row: i + 1,
125
+ type: 'REQUIRED',
126
+ field,
127
+ message: `Field "${field}" is required`,
128
+ value
129
+ });
130
+ continue;
131
+ }
132
+ if (value === undefined || value === null || value === '') {
133
+ continue;
134
+ }
135
+ if (rule.type) {
136
+ const types = Array.isArray(rule.type) ? rule.type : [rule.type];
137
+ let typeValid = false;
138
+ for (const type of types) {
139
+ if (checkType(value, type)) {
140
+ typeValid = true;
141
+ break;
142
+ }
143
+ }
144
+ if (!typeValid) {
145
+ errors.push({
146
+ row: i + 1,
147
+ type: 'TYPE',
148
+ field,
149
+ message: `Field "${field}" must be of type ${types.join(' or ')}`,
150
+ value,
151
+ expected: types
152
+ });
153
+ }
154
+ }
155
+ if (rule.min !== undefined && typeof value === 'string' && value.length < rule.min) {
156
+ errors.push({
157
+ row: i + 1,
158
+ type: 'MIN_LENGTH',
159
+ field,
160
+ message: `Field "${field}" must be at least ${rule.min} characters`,
161
+ value,
162
+ min: rule.min
163
+ });
164
+ }
165
+ if (rule.max !== undefined && typeof value === 'string' && value.length > rule.max) {
166
+ errors.push({
167
+ row: i + 1,
168
+ type: 'MAX_LENGTH',
169
+ field,
170
+ message: `Field "${field}" must be at most ${rule.max} characters`,
171
+ value,
172
+ max: rule.max
173
+ });
174
+ }
175
+ if (rule.min !== undefined && typeof value === 'number' && value < rule.min) {
176
+ errors.push({
177
+ row: i + 1,
178
+ type: 'MIN_VALUE',
179
+ field,
180
+ message: `Field "${field}" must be at least ${rule.min}`,
181
+ value,
182
+ min: rule.min
183
+ });
184
+ }
185
+ if (rule.max !== undefined && typeof value === 'number' && value > rule.max) {
186
+ errors.push({
187
+ row: i + 1,
188
+ type: 'MAX_VALUE',
189
+ field,
190
+ message: `Field "${field}" must be at most ${rule.max}`,
191
+ value,
192
+ max: rule.max
193
+ });
194
+ }
195
+ if (rule.pattern && typeof value === 'string') {
196
+ const pattern = rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern);
197
+ if (!pattern.test(value)) {
198
+ errors.push({
199
+ row: i + 1,
200
+ type: 'PATTERN',
201
+ field,
202
+ message: `Field "${field}" must match pattern`,
203
+ value,
204
+ pattern: pattern.toString()
205
+ });
206
+ }
207
+ }
208
+ if (rule.enum && Array.isArray(rule.enum) && !rule.enum.includes(value)) {
209
+ errors.push({
210
+ row: i + 1,
211
+ type: 'ENUM',
212
+ field,
213
+ message: `Field "${field}" must be one of: ${rule.enum.join(', ')}`,
214
+ value,
215
+ allowed: rule.enum
216
+ });
217
+ }
218
+ }
219
+ }
220
+ return {
221
+ valid: errors.length === 0,
222
+ errors,
223
+ warnings,
224
+ summary: {
225
+ totalRows: data.length,
226
+ validRows: data.length - errors.length,
227
+ errorCount: errors.length,
228
+ warningCount: warnings.length
229
+ }
230
+ };
231
+ }
232
+ };
233
+ }
234
+ function checkType(value, type) {
235
+ switch (type) {
236
+ case 'string':
237
+ return typeof value === 'string';
238
+ case 'number':
239
+ return typeof value === 'number' && !isNaN(value);
240
+ case 'boolean':
241
+ return typeof value === 'boolean';
242
+ case 'integer':
243
+ return Number.isInteger(value);
244
+ case 'float':
245
+ return typeof value === 'number' && !Number.isInteger(value);
246
+ case 'date':
247
+ return value instanceof Date && !isNaN(value.getTime());
248
+ case 'array':
249
+ return Array.isArray(value);
250
+ case 'object':
251
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
252
+ default:
253
+ return false;
254
+ }
255
+ }
256
+ function createValidationHook(schema) {
257
+ let schemaObj;
258
+ if (typeof schema === 'string') {
259
+ schemaObj = loadSchema(schema);
260
+ }
261
+ else if (typeof schema === 'object' && schema !== null) {
262
+ schemaObj = schema;
263
+ }
264
+ else {
265
+ throw new errors_1.ValidationError('Schema must be an object or a path to a JSON file');
266
+ }
267
+ let validator;
268
+ try {
269
+ const JtcsvValidator = require('../../packages/jtcsv-validator/src/index');
270
+ validator = new JtcsvValidator();
271
+ if (schemaObj.fields) {
272
+ validator.schema(schemaObj.fields);
273
+ }
274
+ else {
275
+ Object.entries(schemaObj).forEach(([field, rule]) => {
276
+ if (typeof rule === 'object') {
277
+ validator.field(field, rule);
278
+ }
279
+ });
280
+ }
281
+ }
282
+ catch (error) {
283
+ console.warn('@jtcsv/validator not available, using simple validation');
284
+ validator = createSimpleValidator(schemaObj);
285
+ }
286
+ return function (row, index, context) {
287
+ try {
288
+ const result = validator.validate([row], {
289
+ stopOnFirstError: true,
290
+ transform: false
291
+ });
292
+ if (!result.valid && result.errors.length > 0) {
293
+ const error = result.errors[0];
294
+ throw new errors_1.ValidationError(`Row ${index + 1}: ${error.message} (field: ${error.field})`);
295
+ }
296
+ return row;
297
+ }
298
+ catch (error) {
299
+ if (error instanceof errors_1.ValidationError) {
300
+ throw error;
301
+ }
302
+ console.error(`Validation error at row ${index}: ${error.message}`);
303
+ if (process.env['NODE_ENV'] === 'development') {
304
+ console.error(error.stack);
305
+ }
306
+ return row;
307
+ }
308
+ };
309
+ }
310
+ function applySchemaValidation(data, schema) {
311
+ if (!Array.isArray(data)) {
312
+ throw new errors_1.ValidationError('Data must be an array');
313
+ }
314
+ const validationHook = createValidationHook(schema);
315
+ const errors = [];
316
+ const validatedData = [];
317
+ for (let i = 0; i < data.length; i++) {
318
+ try {
319
+ const validatedRow = validationHook(data[i], i, { operation: 'validate' });
320
+ validatedData.push(validatedRow);
321
+ }
322
+ catch (error) {
323
+ if (error instanceof errors_1.ValidationError) {
324
+ errors.push({
325
+ row: i + 1,
326
+ message: error.message,
327
+ data: data[i]
328
+ });
329
+ }
330
+ else {
331
+ validatedData.push(data[i]);
332
+ }
333
+ }
334
+ }
335
+ return {
336
+ valid: errors.length === 0,
337
+ errors,
338
+ data: validatedData,
339
+ summary: {
340
+ totalRows: data.length,
341
+ validRows: validatedData.length,
342
+ errorCount: errors.length,
343
+ errorRate: data.length > 0 ? (errors.length / data.length) * 100 : 0
344
+ }
345
+ };
346
+ }
347
+ function createValidationHooks(schema) {
348
+ const { TransformHooks } = require('../core/transform-hooks');
349
+ const hooks = new TransformHooks();
350
+ const validationHook = createValidationHook(schema);
351
+ hooks.perRow(validationHook);
352
+ return hooks;
353
+ }
354
+ function createSchemaValidators(schema) {
355
+ const validators = {};
356
+ const properties = schema.properties || schema;
357
+ const requiredFields = schema.required || [];
358
+ if (!properties || typeof properties !== 'object') {
359
+ return validators;
360
+ }
361
+ for (const [key, definition] of Object.entries(properties)) {
362
+ const validator = {
363
+ type: definition.type,
364
+ required: requiredFields.includes(key)
365
+ };
366
+ if (definition.type === 'string' && definition.format) {
367
+ validator.format = (value) => {
368
+ if (definition.format === 'date-time') {
369
+ if (value instanceof Date) {
370
+ return value.toISOString();
371
+ }
372
+ if (typeof value === 'string') {
373
+ const date = new Date(value);
374
+ if (!isNaN(date.getTime())) {
375
+ return date.toISOString();
376
+ }
377
+ }
378
+ }
379
+ if (definition.format === 'email') {
380
+ if (typeof value === 'string') {
381
+ return value.toLowerCase().trim();
382
+ }
383
+ }
384
+ if (definition.format === 'uri') {
385
+ if (typeof value === 'string') {
386
+ return value.trim();
387
+ }
388
+ }
389
+ return value;
390
+ };
391
+ }
392
+ validator.validate = (value) => {
393
+ if (value === null || value === undefined) {
394
+ return !validator.required;
395
+ }
396
+ if (definition.type === 'string' && typeof value !== 'string') {
397
+ if (definition.format === 'date-time' && value instanceof Date) {
398
+ return true;
399
+ }
400
+ return false;
401
+ }
402
+ if (definition.type === 'number' && typeof value !== 'number') {
403
+ return false;
404
+ }
405
+ if (definition.type === 'integer' && (!Number.isInteger(value) || typeof value !== 'number')) {
406
+ return false;
407
+ }
408
+ if (definition.type === 'boolean' && typeof value !== 'boolean') {
409
+ return false;
410
+ }
411
+ if (definition.type === 'array' && !Array.isArray(value)) {
412
+ return false;
413
+ }
414
+ if (definition.type === 'object' && (typeof value !== 'object' || value === null || Array.isArray(value))) {
415
+ return false;
416
+ }
417
+ if (definition.type === 'string') {
418
+ if (definition.minLength !== undefined && value.length < definition.minLength) {
419
+ return false;
420
+ }
421
+ if (definition.maxLength !== undefined && value.length > definition.maxLength) {
422
+ return false;
423
+ }
424
+ if (definition.pattern && !new RegExp(definition.pattern).test(value)) {
425
+ return false;
426
+ }
427
+ if (definition.format === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
428
+ return false;
429
+ }
430
+ if (definition.format === 'uri') {
431
+ try {
432
+ new URL(value);
433
+ }
434
+ catch {
435
+ return false;
436
+ }
437
+ }
438
+ }
439
+ if (definition.type === 'number' || definition.type === 'integer') {
440
+ if (definition.minimum !== undefined && value < definition.minimum) {
441
+ return false;
442
+ }
443
+ if (definition.maximum !== undefined && value > definition.maximum) {
444
+ return false;
445
+ }
446
+ if (definition.exclusiveMinimum !== undefined && value <= definition.exclusiveMinimum) {
447
+ return false;
448
+ }
449
+ if (definition.exclusiveMaximum !== undefined && value >= definition.exclusiveMaximum) {
450
+ return false;
451
+ }
452
+ if (definition.multipleOf !== undefined && value % definition.multipleOf !== 0) {
453
+ return false;
454
+ }
455
+ }
456
+ if (definition.type === 'array') {
457
+ if (definition.minItems !== undefined && value.length < definition.minItems) {
458
+ return false;
459
+ }
460
+ if (definition.maxItems !== undefined && value.length > definition.maxItems) {
461
+ return false;
462
+ }
463
+ if (definition.uniqueItems && new Set(value).size !== value.length) {
464
+ return false;
465
+ }
466
+ if (definition.items) {
467
+ for (const item of value) {
468
+ const itemValidator = createSchemaValidators({ properties: { item: definition.items } });
469
+ if (itemValidator.item && !itemValidator.item.validate(item)) {
470
+ return false;
471
+ }
472
+ }
473
+ }
474
+ }
475
+ if (definition.type === 'object' && definition.properties) {
476
+ const nestedValidators = createSchemaValidators(definition);
477
+ for (const [nestedKey, nestedValidator] of Object.entries(nestedValidators)) {
478
+ if (value[nestedKey] !== undefined && !nestedValidator.validate(value[nestedKey])) {
479
+ return false;
480
+ }
481
+ if (nestedValidator.required && value[nestedKey] === undefined) {
482
+ return false;
483
+ }
484
+ }
485
+ }
486
+ if (definition.enum && !definition.enum.includes(value)) {
487
+ return false;
488
+ }
489
+ return true;
490
+ };
491
+ validators[key] = validator;
492
+ }
493
+ return validators;
494
+ }
495
+ async function loadSchemaAsync(schemaPathOrJson) {
496
+ if (!schemaPathOrJson || typeof schemaPathOrJson !== 'string') {
497
+ throw new errors_1.ValidationError('Schema must be a string (JSON or file path)');
498
+ }
499
+ let schemaString = schemaPathOrJson;
500
+ const isFilePath = schemaPathOrJson.endsWith('.json') ||
501
+ schemaPathOrJson.includes('/') ||
502
+ schemaPathOrJson.includes('\\');
503
+ if (isFilePath) {
504
+ const safePath = path.resolve(schemaPathOrJson);
505
+ const normalizedPath = path.normalize(schemaPathOrJson);
506
+ if (normalizedPath.includes('..') ||
507
+ /\\\.\.\\|\/\.\.\//.test(schemaPathOrJson) ||
508
+ schemaPathOrJson.startsWith('..') ||
509
+ schemaPathOrJson.includes('/..')) {
510
+ throw new errors_1.SecurityError('Directory traversal detected in schema file path');
511
+ }
512
+ try {
513
+ await fsPromises.access(safePath);
514
+ }
515
+ catch {
516
+ throw new errors_1.ValidationError(`Schema file not found: ${schemaPathOrJson}`);
517
+ }
518
+ if (!safePath.toLowerCase().endsWith('.json')) {
519
+ throw new errors_1.ValidationError('Schema file must have .json extension');
520
+ }
521
+ try {
522
+ schemaString = await fsPromises.readFile(safePath, 'utf8');
523
+ }
524
+ catch (error) {
525
+ if (error.code === 'EACCES') {
526
+ throw new errors_1.SecurityError(`Permission denied reading schema file: ${schemaPathOrJson}`);
527
+ }
528
+ throw new errors_1.ValidationError(`Failed to read schema file: ${error.message}`);
529
+ }
530
+ }
531
+ try {
532
+ const schema = JSON.parse(schemaString);
533
+ if (typeof schema !== 'object' || schema === null) {
534
+ throw new errors_1.ValidationError('Schema must be a JSON object');
535
+ }
536
+ return schema;
537
+ }
538
+ catch (error) {
539
+ if (error instanceof SyntaxError) {
540
+ throw new errors_1.ValidationError(`Invalid JSON in schema: ${error.message}`);
541
+ }
542
+ throw new errors_1.ValidationError(`Failed to parse schema: ${error.message}`);
543
+ }
544
+ }
545
+ async function applySchemaValidationAsync(data, schema) {
546
+ if (!Array.isArray(data)) {
547
+ throw new errors_1.ValidationError('Data must be an array');
548
+ }
549
+ const schemaObj = typeof schema === 'string' ? await loadSchemaAsync(schema) : schema;
550
+ const validationHook = createValidationHook(schemaObj);
551
+ if (data.length > 1000) {
552
+ const { createWorkerPool } = require('../workers/worker-pool');
553
+ const pool = createWorkerPool({
554
+ workerCount: Math.min(4, require('os').cpus().length),
555
+ workerScript: require.resolve('./validation-worker.js')
556
+ });
557
+ try {
558
+ const validationPromises = data.map((row, index) => pool.execute({ row, index, schema: schemaObj, operation: 'validate' }));
559
+ const results = await Promise.all(validationPromises);
560
+ const errors = [];
561
+ const validatedData = [];
562
+ results.forEach((result, index) => {
563
+ if (result.error) {
564
+ errors.push({
565
+ row: index + 1,
566
+ message: result.error.message,
567
+ data: data[index]
568
+ });
569
+ }
570
+ else {
571
+ validatedData.push(result.validatedRow);
572
+ }
573
+ });
574
+ return {
575
+ valid: errors.length === 0,
576
+ errors,
577
+ data: validatedData,
578
+ summary: {
579
+ totalRows: data.length,
580
+ validRows: validatedData.length,
581
+ errorCount: errors.length,
582
+ errorRate: data.length > 0 ? (errors.length / data.length) * 100 : 0
583
+ }
584
+ };
585
+ }
586
+ finally {
587
+ await pool.terminate();
588
+ }
589
+ }
590
+ return applySchemaValidation(data, schemaObj);
591
+ }
592
+ function createAsyncValidationHook(schema) {
593
+ const syncHook = createValidationHook(schema);
594
+ return async function (row, index, context) {
595
+ return Promise.resolve(syncHook(row, index, context));
596
+ };
597
+ }
598
+ exports.default = {
599
+ loadSchema,
600
+ loadSchemaAsync,
601
+ createValidationHook,
602
+ createAsyncValidationHook,
603
+ applySchemaValidation,
604
+ applySchemaValidationAsync,
605
+ createValidationHooks,
606
+ checkType,
607
+ createSchemaValidators
608
+ };
609
+ //# sourceMappingURL=schema-validator.js.map