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
@@ -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