rowpipe 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +401 -0
  2. package/benchmarks/memory-bench.ts +109 -0
  3. package/dist/analytics/reduce.d.ts +33 -0
  4. package/dist/analytics/reduce.d.ts.map +1 -0
  5. package/dist/analytics/reduce.js +318 -0
  6. package/dist/analytics/reduce.js.map +1 -0
  7. package/dist/analytics/schema-inference.d.ts +31 -0
  8. package/dist/analytics/schema-inference.d.ts.map +1 -0
  9. package/dist/analytics/schema-inference.js +166 -0
  10. package/dist/analytics/schema-inference.js.map +1 -0
  11. package/dist/analytics/semantic-types.d.ts +6 -0
  12. package/dist/analytics/semantic-types.d.ts.map +1 -0
  13. package/dist/analytics/semantic-types.js +40 -0
  14. package/dist/analytics/semantic-types.js.map +1 -0
  15. package/dist/analytics/stats.d.ts +102 -0
  16. package/dist/analytics/stats.d.ts.map +1 -0
  17. package/dist/analytics/stats.js +307 -0
  18. package/dist/analytics/stats.js.map +1 -0
  19. package/dist/analytics/validator.d.ts +32 -0
  20. package/dist/analytics/validator.d.ts.map +1 -0
  21. package/dist/analytics/validator.js +96 -0
  22. package/dist/analytics/validator.js.map +1 -0
  23. package/dist/cli/commands/cast.d.ts +13 -0
  24. package/dist/cli/commands/cast.d.ts.map +1 -0
  25. package/dist/cli/commands/cast.js +39 -0
  26. package/dist/cli/commands/cast.js.map +1 -0
  27. package/dist/cli/commands/convert.d.ts +15 -0
  28. package/dist/cli/commands/convert.d.ts.map +1 -0
  29. package/dist/cli/commands/convert.js +89 -0
  30. package/dist/cli/commands/convert.js.map +1 -0
  31. package/dist/cli/commands/filter.d.ts +11 -0
  32. package/dist/cli/commands/filter.d.ts.map +1 -0
  33. package/dist/cli/commands/filter.js +38 -0
  34. package/dist/cli/commands/filter.js.map +1 -0
  35. package/dist/cli/commands/inspect.d.ts +10 -0
  36. package/dist/cli/commands/inspect.d.ts.map +1 -0
  37. package/dist/cli/commands/inspect.js +134 -0
  38. package/dist/cli/commands/inspect.js.map +1 -0
  39. package/dist/cli/commands/map.d.ts +11 -0
  40. package/dist/cli/commands/map.d.ts.map +1 -0
  41. package/dist/cli/commands/map.js +43 -0
  42. package/dist/cli/commands/map.js.map +1 -0
  43. package/dist/cli/commands/reduce.d.ts +12 -0
  44. package/dist/cli/commands/reduce.d.ts.map +1 -0
  45. package/dist/cli/commands/reduce.js +49 -0
  46. package/dist/cli/commands/reduce.js.map +1 -0
  47. package/dist/cli/commands/rename.d.ts +11 -0
  48. package/dist/cli/commands/rename.d.ts.map +1 -0
  49. package/dist/cli/commands/rename.js +39 -0
  50. package/dist/cli/commands/rename.js.map +1 -0
  51. package/dist/cli/commands/sample.d.ts +13 -0
  52. package/dist/cli/commands/sample.d.ts.map +1 -0
  53. package/dist/cli/commands/sample.js +40 -0
  54. package/dist/cli/commands/sample.js.map +1 -0
  55. package/dist/cli/commands/schema.d.ts +13 -0
  56. package/dist/cli/commands/schema.d.ts.map +1 -0
  57. package/dist/cli/commands/schema.js +57 -0
  58. package/dist/cli/commands/schema.js.map +1 -0
  59. package/dist/cli/commands/select.d.ts +11 -0
  60. package/dist/cli/commands/select.d.ts.map +1 -0
  61. package/dist/cli/commands/select.js +39 -0
  62. package/dist/cli/commands/select.js.map +1 -0
  63. package/dist/cli/commands/stats.d.ts +15 -0
  64. package/dist/cli/commands/stats.d.ts.map +1 -0
  65. package/dist/cli/commands/stats.js +75 -0
  66. package/dist/cli/commands/stats.js.map +1 -0
  67. package/dist/cli/commands/validate.d.ts +12 -0
  68. package/dist/cli/commands/validate.d.ts.map +1 -0
  69. package/dist/cli/commands/validate.js +86 -0
  70. package/dist/cli/commands/validate.js.map +1 -0
  71. package/dist/cli/index.d.ts +3 -0
  72. package/dist/cli/index.d.ts.map +1 -0
  73. package/dist/cli/index.js +262 -0
  74. package/dist/cli/index.js.map +1 -0
  75. package/dist/core/batch.d.ts +15 -0
  76. package/dist/core/batch.d.ts.map +1 -0
  77. package/dist/core/batch.js +67 -0
  78. package/dist/core/batch.js.map +1 -0
  79. package/dist/core/errors.d.ts +35 -0
  80. package/dist/core/errors.d.ts.map +1 -0
  81. package/dist/core/errors.js +55 -0
  82. package/dist/core/errors.js.map +1 -0
  83. package/dist/core/pipeline.d.ts +40 -0
  84. package/dist/core/pipeline.d.ts.map +1 -0
  85. package/dist/core/pipeline.js +120 -0
  86. package/dist/core/pipeline.js.map +1 -0
  87. package/dist/core/types.d.ts +73 -0
  88. package/dist/core/types.d.ts.map +1 -0
  89. package/dist/core/types.js +2 -0
  90. package/dist/core/types.js.map +1 -0
  91. package/dist/index.d.ts +22 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +28 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/readers/csv.d.ts +24 -0
  96. package/dist/readers/csv.d.ts.map +1 -0
  97. package/dist/readers/csv.js +251 -0
  98. package/dist/readers/csv.js.map +1 -0
  99. package/dist/readers/index.d.ts +17 -0
  100. package/dist/readers/index.d.ts.map +1 -0
  101. package/dist/readers/index.js +74 -0
  102. package/dist/readers/index.js.map +1 -0
  103. package/dist/readers/json.d.ts +20 -0
  104. package/dist/readers/json.d.ts.map +1 -0
  105. package/dist/readers/json.js +219 -0
  106. package/dist/readers/json.js.map +1 -0
  107. package/dist/readers/jsonl.d.ts +14 -0
  108. package/dist/readers/jsonl.d.ts.map +1 -0
  109. package/dist/readers/jsonl.js +95 -0
  110. package/dist/readers/jsonl.js.map +1 -0
  111. package/dist/readers/xlsx.d.ts +18 -0
  112. package/dist/readers/xlsx.d.ts.map +1 -0
  113. package/dist/readers/xlsx.js +138 -0
  114. package/dist/readers/xlsx.js.map +1 -0
  115. package/dist/transforms/cast.d.ts +21 -0
  116. package/dist/transforms/cast.d.ts.map +1 -0
  117. package/dist/transforms/cast.js +182 -0
  118. package/dist/transforms/cast.js.map +1 -0
  119. package/dist/transforms/expression.d.ts +67 -0
  120. package/dist/transforms/expression.d.ts.map +1 -0
  121. package/dist/transforms/expression.js +943 -0
  122. package/dist/transforms/expression.js.map +1 -0
  123. package/dist/transforms/filter.d.ts +6 -0
  124. package/dist/transforms/filter.d.ts.map +1 -0
  125. package/dist/transforms/filter.js +31 -0
  126. package/dist/transforms/filter.js.map +1 -0
  127. package/dist/transforms/map.d.ts +12 -0
  128. package/dist/transforms/map.d.ts.map +1 -0
  129. package/dist/transforms/map.js +76 -0
  130. package/dist/transforms/map.js.map +1 -0
  131. package/dist/transforms/rename.d.ts +10 -0
  132. package/dist/transforms/rename.d.ts.map +1 -0
  133. package/dist/transforms/rename.js +39 -0
  134. package/dist/transforms/rename.js.map +1 -0
  135. package/dist/transforms/sample.d.ts +10 -0
  136. package/dist/transforms/sample.d.ts.map +1 -0
  137. package/dist/transforms/sample.js +47 -0
  138. package/dist/transforms/sample.js.map +1 -0
  139. package/dist/transforms/select.d.ts +6 -0
  140. package/dist/transforms/select.d.ts.map +1 -0
  141. package/dist/transforms/select.js +63 -0
  142. package/dist/transforms/select.js.map +1 -0
  143. package/dist/utils/compression.d.ts +14 -0
  144. package/dist/utils/compression.d.ts.map +1 -0
  145. package/dist/utils/compression.js +38 -0
  146. package/dist/utils/compression.js.map +1 -0
  147. package/dist/utils/formatting.d.ts +6 -0
  148. package/dist/utils/formatting.d.ts.map +1 -0
  149. package/dist/utils/formatting.js +56 -0
  150. package/dist/utils/formatting.js.map +1 -0
  151. package/dist/utils/progress.d.ts +13 -0
  152. package/dist/utils/progress.d.ts.map +1 -0
  153. package/dist/utils/progress.js +27 -0
  154. package/dist/utils/progress.js.map +1 -0
  155. package/dist/writers/csv.d.ts +18 -0
  156. package/dist/writers/csv.d.ts.map +1 -0
  157. package/dist/writers/csv.js +84 -0
  158. package/dist/writers/csv.js.map +1 -0
  159. package/dist/writers/index.d.ts +17 -0
  160. package/dist/writers/index.d.ts.map +1 -0
  161. package/dist/writers/index.js +73 -0
  162. package/dist/writers/index.js.map +1 -0
  163. package/dist/writers/json.d.ts +12 -0
  164. package/dist/writers/json.d.ts.map +1 -0
  165. package/dist/writers/json.js +42 -0
  166. package/dist/writers/json.js.map +1 -0
  167. package/dist/writers/jsonl.d.ts +12 -0
  168. package/dist/writers/jsonl.d.ts.map +1 -0
  169. package/dist/writers/jsonl.js +41 -0
  170. package/dist/writers/jsonl.js.map +1 -0
  171. package/dist/writers/xlsx.d.ts +13 -0
  172. package/dist/writers/xlsx.d.ts.map +1 -0
  173. package/dist/writers/xlsx.js +65 -0
  174. package/dist/writers/xlsx.js.map +1 -0
  175. package/package.json +44 -0
  176. package/src/analytics/reduce.ts +388 -0
  177. package/src/analytics/schema-inference.ts +217 -0
  178. package/src/analytics/semantic-types.ts +36 -0
  179. package/src/analytics/stats.ts +383 -0
  180. package/src/analytics/validator.ts +134 -0
  181. package/src/cli/commands/cast.ts +60 -0
  182. package/src/cli/commands/convert.ts +128 -0
  183. package/src/cli/commands/filter.ts +58 -0
  184. package/src/cli/commands/inspect.ts +193 -0
  185. package/src/cli/commands/map.ts +66 -0
  186. package/src/cli/commands/reduce.ts +74 -0
  187. package/src/cli/commands/rename.ts +59 -0
  188. package/src/cli/commands/sample.ts +61 -0
  189. package/src/cli/commands/schema.ts +86 -0
  190. package/src/cli/commands/select.ts +59 -0
  191. package/src/cli/commands/stats.ts +100 -0
  192. package/src/cli/commands/validate.ts +123 -0
  193. package/src/cli/index.ts +285 -0
  194. package/src/core/batch.ts +81 -0
  195. package/src/core/errors.ts +81 -0
  196. package/src/core/pipeline.ts +150 -0
  197. package/src/core/types.ts +110 -0
  198. package/src/index.ts +52 -0
  199. package/src/readers/csv.ts +302 -0
  200. package/src/readers/index.ts +88 -0
  201. package/src/readers/json.ts +256 -0
  202. package/src/readers/jsonl.ts +126 -0
  203. package/src/readers/xlsx.ts +182 -0
  204. package/src/transforms/cast.ts +212 -0
  205. package/src/transforms/expression.ts +1114 -0
  206. package/src/transforms/filter.ts +38 -0
  207. package/src/transforms/map.ts +99 -0
  208. package/src/transforms/rename.ts +47 -0
  209. package/src/transforms/sample.ts +57 -0
  210. package/src/transforms/select.ts +64 -0
  211. package/src/utils/compression.ts +43 -0
  212. package/src/utils/formatting.ts +75 -0
  213. package/src/utils/progress.ts +37 -0
  214. package/src/writers/csv.ts +106 -0
  215. package/src/writers/index.ts +87 -0
  216. package/src/writers/json.ts +52 -0
  217. package/src/writers/jsonl.ts +49 -0
  218. package/src/writers/xlsx.ts +87 -0
  219. package/tests/analytics.test.ts +119 -0
  220. package/tests/cli-integration.test.ts +187 -0
  221. package/tests/map-reduce.test.ts +223 -0
  222. package/tests/readers-writers.test.ts +189 -0
  223. package/tests/transforms.test.ts +199 -0
  224. package/tsconfig.json +21 -0
  225. package/vitest.config.ts +9 -0
@@ -0,0 +1,212 @@
1
+ import { InvalidArgumentError } from "../core/errors.js";
2
+ import type { DataStream, Row, TransformFunction } from "../core/types.js";
3
+
4
+ export type CastOnError = "fail" | "null" | "keep" | "skip-row";
5
+
6
+ export interface CastOptions {
7
+ onError?: CastOnError;
8
+ }
9
+
10
+ /**
11
+ * Casts a single value to the target type.
12
+ */
13
+ export function castValue(
14
+ value: unknown,
15
+ targetType: string,
16
+ onError: CastOnError = "null"
17
+ ): { value: unknown; success: boolean } {
18
+ if (value === null || value === undefined || value === "") {
19
+ return { value: null, success: true };
20
+ }
21
+
22
+ const str = String(value).trim();
23
+ const lowerType = targetType.toLowerCase();
24
+
25
+ try {
26
+ switch (lowerType) {
27
+ case "string":
28
+ return { value: String(value), success: true };
29
+
30
+ case "integer":
31
+ case "int": {
32
+ if (!/^-?\d+$/.test(str)) {
33
+ throw new Error(`Cannot cast "${str}" to integer`);
34
+ }
35
+ const parsedInt = Number.parseInt(str, 10);
36
+ if (Number.isNaN(parsedInt)) {
37
+ throw new Error(`Cannot cast "${str}" to integer`);
38
+ }
39
+ return { value: parsedInt, success: true };
40
+ }
41
+
42
+ case "number":
43
+ case "float":
44
+ case "double": {
45
+ const parsedNum = Number.parseFloat(str);
46
+ if (Number.isNaN(parsedNum)) {
47
+ throw new Error(`Cannot cast "${str}" to number`);
48
+ }
49
+ return { value: parsedNum, success: true };
50
+ }
51
+
52
+ case "boolean":
53
+ case "bool": {
54
+ const lower = str.toLowerCase();
55
+ if (["true", "1", "yes", "t", "y"].includes(lower)) {
56
+ return { value: true, success: true };
57
+ }
58
+ if (["false", "0", "no", "f", "n"].includes(lower)) {
59
+ return { value: false, success: true };
60
+ }
61
+ throw new Error(`Cannot cast "${str}" to boolean`);
62
+ }
63
+
64
+ case "date": {
65
+ const d = new Date(str);
66
+ if (Number.isNaN(d.getTime())) {
67
+ throw new Error(`Cannot cast "${str}" to date`);
68
+ }
69
+ return { value: d.toISOString().split("T")[0], success: true };
70
+ }
71
+
72
+ case "datetime": {
73
+ const d = new Date(str);
74
+ if (Number.isNaN(d.getTime())) {
75
+ throw new Error(`Cannot cast "${str}" to datetime`);
76
+ }
77
+ return { value: d.toISOString(), success: true };
78
+ }
79
+
80
+ case "json": {
81
+ const parsed = JSON.parse(str);
82
+ return { value: parsed, success: true };
83
+ }
84
+
85
+ default:
86
+ throw new Error(`Unknown target type "${targetType}"`);
87
+ }
88
+ } catch (err) {
89
+ if (onError === "fail") {
90
+ throw new InvalidArgumentError(
91
+ `Failed to cast value "${str}" to type "${targetType}": ${(err as Error).message}`
92
+ );
93
+ }
94
+ if (onError === "keep") {
95
+ return { value, success: false };
96
+ }
97
+ // "null" or "skip-row"
98
+ return { value: null, success: false };
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Creates a transform that casts specified columns to target types.
104
+ */
105
+ export function castColumns(
106
+ typeSpecs: Record<string, string>,
107
+ options: CastOptions = {}
108
+ ): TransformFunction {
109
+ const onError = options.onError || "null";
110
+
111
+ const compiled = Object.entries(typeSpecs).map(([col, targetType]) => {
112
+ const lower = targetType.toLowerCase();
113
+
114
+ // Fast-path specialized caster functions
115
+ if (lower === "number" || lower === "float" || lower === "double") {
116
+ return {
117
+ col,
118
+ cast: (val: unknown) => {
119
+ if (val === null || val === undefined || val === "") return { value: null, success: true };
120
+ const num = typeof val === "number" ? val : Number(val);
121
+ if (!Number.isNaN(num)) return { value: num, success: true };
122
+ if (onError === "fail") throw new InvalidArgumentError(`Cannot cast "${String(val)}" to number`);
123
+ if (onError === "keep") return { value: val, success: false };
124
+ return { value: null, success: false };
125
+ },
126
+ };
127
+ }
128
+
129
+ if (lower === "integer" || lower === "int") {
130
+ return {
131
+ col,
132
+ cast: (val: unknown) => {
133
+ if (val === null || val === undefined || val === "") return { value: null, success: true };
134
+ const str = String(val).trim();
135
+ if (!/^-?\d+$/.test(str)) {
136
+ if (onError === "fail") throw new InvalidArgumentError(`Cannot cast "${str}" to integer`);
137
+ if (onError === "keep") return { value: val, success: false };
138
+ return { value: null, success: false };
139
+ }
140
+ const num = Number.parseInt(str, 10);
141
+ return { value: num, success: true };
142
+ },
143
+ };
144
+ }
145
+
146
+ if (lower === "string") {
147
+ return {
148
+ col,
149
+ cast: (val: unknown) => ({ value: val !== null && val !== undefined ? String(val) : "", success: true }),
150
+ };
151
+ }
152
+
153
+ return {
154
+ col,
155
+ cast: (val: unknown) => castValue(val, targetType, onError),
156
+ };
157
+ });
158
+
159
+ const compiledLen = compiled.length;
160
+
161
+ return function (stream: DataStream): DataStream {
162
+ return (async function* () {
163
+ for await (const batch of stream) {
164
+ const rows = batch.rows;
165
+ const rowCount = rows.length;
166
+ const transformedRows: Row[] = [];
167
+
168
+ for (let i = 0; i < rowCount; i++) {
169
+ const row = rows[i]!;
170
+ let skipRow = false;
171
+ const newRow: Row = { ...row };
172
+
173
+ for (let j = 0; j < compiledLen; j++) {
174
+ const entry = compiled[j]!;
175
+ const col = entry.col;
176
+ if (col in newRow) {
177
+ const res = entry.cast(newRow[col]);
178
+ if (!res.success && onError === "skip-row") {
179
+ skipRow = true;
180
+ break;
181
+ }
182
+ newRow[col] = res.value;
183
+ }
184
+ }
185
+
186
+ if (!skipRow) {
187
+ transformedRows.push(newRow);
188
+ }
189
+ }
190
+
191
+ yield {
192
+ rows: transformedRows,
193
+ offset: batch.offset,
194
+ };
195
+ }
196
+ })();
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Parses command line cast specs like ["age:integer", "revenue:number"].
202
+ */
203
+ export function parseCastSpecs(specs: string[]): Record<string, string> {
204
+ const mapping: Record<string, string> = {};
205
+ for (const spec of specs) {
206
+ const parts = spec.split(":");
207
+ if (parts.length === 2 && parts[0] && parts[1]) {
208
+ mapping[parts[0].trim()] = parts[1].trim();
209
+ }
210
+ }
211
+ return mapping;
212
+ }