tranfi 0.1.2 → 0.2.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 (132) hide show
  1. package/LICENSE +177 -0
  2. package/NOTICE +8 -0
  3. package/README.md +272 -40
  4. package/app/assets/{index-pDFMluyz.js → index-BIAIKnrp.js} +1 -1
  5. package/app/index.html +1 -1
  6. package/binding.gyp +55 -3
  7. package/csrc/arena.c +7 -5
  8. package/csrc/batch.c +818 -71
  9. package/csrc/buffer.c +84 -8
  10. package/csrc/cJSON.c +262 -19
  11. package/csrc/cJSON.h +17 -1
  12. package/csrc/codec_csv.c +1074 -181
  13. package/csrc/codec_jsonl.c +830 -118
  14. package/csrc/codec_table.c +108 -78
  15. package/csrc/codec_text.c +286 -68
  16. package/csrc/compiler.c +31 -3
  17. package/csrc/config.h +21 -0
  18. package/csrc/dsl.c +4722 -485
  19. package/csrc/expr.c +363 -55
  20. package/csrc/expr.h +2 -0
  21. package/csrc/internal.h +316 -27
  22. package/csrc/ir.c +65 -18
  23. package/csrc/ir.h +41 -0
  24. package/csrc/ir_schema.c +20 -5
  25. package/csrc/ir_serialize.c +68 -6
  26. package/csrc/ir_sql.c +796 -185
  27. package/csrc/ir_validate.c +462 -6
  28. package/csrc/json_path.c +210 -0
  29. package/csrc/main.c +879 -30
  30. package/csrc/memory_estimate.c +477 -0
  31. package/csrc/op_acf.c +171 -21
  32. package/csrc/op_across.c +477 -0
  33. package/csrc/op_anomaly.c +167 -32
  34. package/csrc/op_assert.c +761 -0
  35. package/csrc/op_bin.c +168 -29
  36. package/csrc/op_cast.c +383 -55
  37. package/csrc/op_clip.c +30 -19
  38. package/csrc/op_date_trunc.c +208 -34
  39. package/csrc/op_datetime.c +259 -77
  40. package/csrc/op_derive.c +65 -97
  41. package/csrc/op_diff.c +146 -30
  42. package/csrc/op_ewma.c +149 -30
  43. package/csrc/op_explode.c +124 -26
  44. package/csrc/op_fill_down.c +125 -53
  45. package/csrc/op_fill_null.c +176 -31
  46. package/csrc/op_filter.c +89 -40
  47. package/csrc/op_frequency.c +571 -43
  48. package/csrc/op_grep.c +36 -18
  49. package/csrc/op_group_agg.c +1790 -119
  50. package/csrc/op_hash.c +48 -15
  51. package/csrc/op_head.c +21 -86
  52. package/csrc/op_interpolate.c +268 -62
  53. package/csrc/op_join.c +2700 -182
  54. package/csrc/op_json_extract.c +227 -0
  55. package/csrc/op_json_filter.c +384 -0
  56. package/csrc/op_json_flatten.c +293 -0
  57. package/csrc/op_json_schema.c +503 -0
  58. package/csrc/op_label_encode.c +328 -53
  59. package/csrc/op_lag.c +181 -0
  60. package/csrc/op_lead.c +141 -89
  61. package/csrc/op_normalize.c +363 -79
  62. package/csrc/op_onehot.c +345 -73
  63. package/csrc/op_pivot.c +1546 -162
  64. package/csrc/op_quarantine.c +189 -0
  65. package/csrc/op_registry.c +2062 -166
  66. package/csrc/op_rename.c +41 -50
  67. package/csrc/op_replace.c +270 -118
  68. package/csrc/op_rleid.c +297 -0
  69. package/csrc/op_rowid.c +559 -0
  70. package/csrc/op_sample.c +80 -23
  71. package/csrc/op_schema.c +1341 -0
  72. package/csrc/op_schema_infer.c +252 -0
  73. package/csrc/op_select.c +265 -65
  74. package/csrc/op_set.c +3449 -0
  75. package/csrc/op_skip.c +30 -87
  76. package/csrc/op_sort.c +670 -124
  77. package/csrc/op_source_name.c +120 -0
  78. package/csrc/op_split.c +65 -28
  79. package/csrc/op_split_data.c +41 -9
  80. package/csrc/op_stack.c +178 -222
  81. package/csrc/op_stats.c +206 -110
  82. package/csrc/op_step.c +217 -55
  83. package/csrc/op_tail.c +21 -12
  84. package/csrc/op_tee.c +338 -0
  85. package/csrc/op_top.c +260 -53
  86. package/csrc/op_trim.c +48 -19
  87. package/csrc/op_unique.c +1193 -150
  88. package/csrc/op_unpivot.c +100 -66
  89. package/csrc/op_validate.c +601 -24
  90. package/csrc/op_window.c +492 -51
  91. package/csrc/path_policy.c +85 -0
  92. package/csrc/pipeline.c +872 -99
  93. package/csrc/recipes.c +3 -1
  94. package/csrc/report.c +73 -30
  95. package/csrc/selector.c +1097 -0
  96. package/csrc/size_utils.c +348 -0
  97. package/csrc/spill.c +317 -0
  98. package/csrc/spill.h +21 -0
  99. package/csrc/tranfi.h +169 -1
  100. package/csrc/transform.h +209 -0
  101. package/csrc/transform_api.c +2237 -0
  102. package/csrc/transform_categorical.c +923 -0
  103. package/csrc/transform_internal.h +472 -0
  104. package/csrc/transform_json.c +3812 -0
  105. package/csrc/transform_numeric.c +1966 -0
  106. package/csrc/transform_sha256.c +154 -0
  107. package/csrc/transform_wasm.h +162 -0
  108. package/csrc/transform_wasm_api.c +1373 -0
  109. package/csrc/wasm_api.c +70 -9
  110. package/napi_api.c +219 -11
  111. package/napi_transform.c +1648 -0
  112. package/napi_transform.h +8 -0
  113. package/package.json +27 -11
  114. package/scripts/install-native.js +76 -0
  115. package/scripts/prepack.js +64 -0
  116. package/scripts/sync-csrc.js +23 -0
  117. package/src/cli.js +8 -11
  118. package/src/engines/duckdb.js +45 -12
  119. package/src/index.js +661 -42
  120. package/src/memory_policy.js +411 -0
  121. package/src/native.js +1 -5
  122. package/src/pipeline.js +454 -31
  123. package/src/recipe_json.js +80 -0
  124. package/src/server.js +10 -8
  125. package/src/transform.js +403 -0
  126. package/src/transform_error.js +10 -0
  127. package/src/wasm.js +6 -4
  128. package/wasm/index.js +498 -10
  129. package/wasm/tranfi_core.js +0 -0
  130. package/wasm/transform.js +1156 -0
  131. package/wasm/worker.js +786 -0
  132. package/csrc/plan.c +0 -206
package/csrc/op_acf.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * op_acf.c — Autocorrelation function.
3
- * Aggregate op: buffers all values, computes ACF for lags 0..N.
3
+ * Aggregate op: buffers numeric values, computes ACF for lags 0..N.
4
4
  * Output: 2-column table (lag, acf).
5
5
  *
6
6
  * Config: {"column": "price", "lags": 20}
@@ -12,6 +12,18 @@
12
12
  #include <string.h>
13
13
  #include <stdio.h>
14
14
  #include <math.h>
15
+ #include <limits.h>
16
+
17
+ typedef enum {
18
+ ACF_MISSING_ERROR,
19
+ ACF_MISSING_NULL,
20
+ ACF_MISSING_IGNORE
21
+ } acf_missing_policy;
22
+
23
+ typedef enum {
24
+ ACF_TYPE_FAIL,
25
+ ACF_TYPE_NULL
26
+ } acf_type_policy;
15
27
 
16
28
  typedef struct {
17
29
  char *column;
@@ -19,30 +31,112 @@ typedef struct {
19
31
  double *values;
20
32
  size_t n_values;
21
33
  size_t cap_values;
34
+ int checked_schema;
35
+ int col_idx;
36
+ int ignore;
37
+ int force_null;
38
+ acf_missing_policy missing;
39
+ acf_type_policy on_type_error;
22
40
  } acf_state;
23
41
 
42
+ static int acf_is_numeric(tf_type type) {
43
+ return type == TF_TYPE_INT64 || type == TF_TYPE_FLOAT64;
44
+ }
45
+
24
46
  static double get_numeric(const tf_batch *b, size_t r, int ci) {
25
47
  if (b->col_types[ci] == TF_TYPE_INT64) return (double)tf_batch_get_int64(b, r, ci);
26
48
  if (b->col_types[ci] == TF_TYPE_FLOAT64) return tf_batch_get_float64(b, r, ci);
27
49
  return 0;
28
50
  }
29
51
 
52
+ static int acf_parse_missing_policy(const cJSON *args, acf_missing_policy *out) {
53
+ *out = ACF_MISSING_ERROR;
54
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "missing");
55
+ if (!j) return TF_OK;
56
+ if (!cJSON_IsString(j)) {
57
+ tf_set_last_error("acf: missing must be error, null, or ignore");
58
+ return TF_ERROR;
59
+ }
60
+ if (strcmp(j->valuestring, "error") == 0) *out = ACF_MISSING_ERROR;
61
+ else if (strcmp(j->valuestring, "null") == 0) *out = ACF_MISSING_NULL;
62
+ else if (strcmp(j->valuestring, "ignore") == 0) *out = ACF_MISSING_IGNORE;
63
+ else {
64
+ tf_set_last_error("acf: missing must be error, null, or ignore");
65
+ return TF_ERROR;
66
+ }
67
+ return TF_OK;
68
+ }
69
+
70
+ static int acf_parse_type_policy(const cJSON *args, acf_type_policy *out) {
71
+ *out = ACF_TYPE_FAIL;
72
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "on_type_error");
73
+ if (!j) return TF_OK;
74
+ if (!cJSON_IsString(j)) {
75
+ tf_set_last_error("acf: on_type_error must be fail or null");
76
+ return TF_ERROR;
77
+ }
78
+ if (strcmp(j->valuestring, "fail") == 0) *out = ACF_TYPE_FAIL;
79
+ else if (strcmp(j->valuestring, "null") == 0) *out = ACF_TYPE_NULL;
80
+ else {
81
+ tf_set_last_error("acf: on_type_error must be fail or null");
82
+ return TF_ERROR;
83
+ }
84
+ return TF_OK;
85
+ }
86
+
87
+ static int acf_set_col_error(acf_state *st, const tf_batch *in) {
88
+ if (st->col_idx < 0) {
89
+ char msg[256];
90
+ snprintf(msg, sizeof(msg), "acf: column '%s' not found", st->column);
91
+ tf_set_last_error(msg);
92
+ return TF_ERROR;
93
+ }
94
+ if (!acf_is_numeric(in->col_types[st->col_idx])) {
95
+ char msg[256];
96
+ snprintf(msg, sizeof(msg), "acf: column '%s' must be numeric", st->column);
97
+ tf_set_last_error(msg);
98
+ return TF_ERROR;
99
+ }
100
+ return TF_OK;
101
+ }
102
+
103
+ static int acf_check_schema(acf_state *st, const tf_batch *in) {
104
+ if (st->checked_schema) return TF_OK;
105
+ st->checked_schema = 1;
106
+ st->col_idx = tf_batch_col_index(in, st->column);
107
+ if (st->col_idx < 0) {
108
+ if (st->missing == ACF_MISSING_ERROR) return acf_set_col_error(st, in);
109
+ if (st->missing == ACF_MISSING_IGNORE) st->ignore = 1;
110
+ else st->force_null = 1;
111
+ return TF_OK;
112
+ }
113
+ if (!acf_is_numeric(in->col_types[st->col_idx])) {
114
+ if (st->on_type_error == ACF_TYPE_FAIL) return acf_set_col_error(st, in);
115
+ st->force_null = 1;
116
+ }
117
+ return TF_OK;
118
+ }
119
+
30
120
  static int acf_process(tf_step *self, tf_batch *in, tf_batch **out,
31
121
  tf_side_channels *side) {
32
122
  (void)side;
33
123
  acf_state *st = self->state;
34
124
  *out = NULL;
35
125
 
36
- int ci = tf_batch_col_index(in, st->column);
37
- if (ci < 0) return TF_OK;
126
+ if (acf_check_schema(st, in) != TF_OK) return TF_ERROR;
127
+ if (st->ignore || st->force_null || st->col_idx < 0) return TF_OK;
38
128
 
39
129
  for (size_t r = 0; r < in->n_rows; r++) {
40
- if (tf_batch_is_null(in, r, ci)) continue;
41
- double val = get_numeric(in, r, ci);
130
+ if (tf_batch_is_null(in, r, (size_t)st->col_idx)) continue;
131
+ double val = get_numeric(in, r, st->col_idx);
42
132
 
43
133
  if (st->n_values >= st->cap_values) {
44
- size_t newcap = st->cap_values ? st->cap_values * 2 : 256;
45
- double *tmp = realloc(st->values, newcap * sizeof(double));
134
+ size_t min_cap = 0, newcap = 0;
135
+ if (tf_size_add(st->n_values, 1, &min_cap) != TF_OK ||
136
+ tf_size_grow_pow2(st->cap_values, min_cap, 256, &newcap) != TF_OK) {
137
+ return TF_ERROR;
138
+ }
139
+ double *tmp = tf_reallocarray_checked(st->values, newcap, sizeof(double));
46
140
  if (!tmp) return TF_ERROR;
47
141
  st->values = tmp;
48
142
  st->cap_values = newcap;
@@ -53,23 +147,48 @@ static int acf_process(tf_step *self, tf_batch *in, tf_batch **out,
53
147
  return TF_OK;
54
148
  }
55
149
 
150
+ static int acf_emit_nulls(acf_state *st, tf_batch **out) {
151
+ size_t out_rows = 0;
152
+ if (tf_size_add((size_t)st->lags, 1, &out_rows) != TF_OK) return TF_ERROR;
153
+ tf_batch *ob = tf_batch_create(2, out_rows);
154
+ if (!ob) return TF_ERROR;
155
+ if (tf_batch_set_schema(ob, 0, "lag", TF_TYPE_INT64) != TF_OK ||
156
+ tf_batch_set_schema(ob, 1, "acf", TF_TYPE_FLOAT64) != TF_OK) {
157
+ tf_batch_free(ob);
158
+ return TF_ERROR;
159
+ }
160
+ for (int k = 0; k <= st->lags; k++) {
161
+ if (tf_batch_set_int64(ob, (size_t)k, 0, k) != TF_OK) {
162
+ tf_batch_free(ob);
163
+ return TF_ERROR;
164
+ }
165
+ if (tf_batch_expose_row(ob, (size_t)k) != TF_OK) {
166
+ tf_batch_free(ob);
167
+ return TF_ERROR;
168
+ }
169
+ }
170
+ *out = ob;
171
+ return TF_OK;
172
+ }
173
+
56
174
  static int acf_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
57
175
  (void)side;
58
176
  acf_state *st = self->state;
59
177
  *out = NULL;
60
178
 
179
+ if (st->ignore) return TF_OK;
180
+ if (st->force_null) return acf_emit_nulls(st, out);
181
+
61
182
  size_t n = st->n_values;
62
183
  if (n < 2) return TF_OK;
63
184
 
64
185
  int max_lag = st->lags;
65
186
  if ((size_t)max_lag >= n) max_lag = (int)(n - 1);
66
187
 
67
- /* Compute mean */
68
188
  double mean = 0;
69
189
  for (size_t i = 0; i < n; i++) mean += st->values[i];
70
190
  mean /= (double)n;
71
191
 
72
- /* Compute variance (denominator for ACF) */
73
192
  double var = 0;
74
193
  for (size_t i = 0; i < n; i++) {
75
194
  double d = st->values[i] - mean;
@@ -77,12 +196,15 @@ static int acf_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
77
196
  }
78
197
  if (var == 0) return TF_OK;
79
198
 
80
- /* Output: lag+1 rows (lag 0 to max_lag) */
81
- size_t out_rows = (size_t)(max_lag + 1);
199
+ size_t out_rows = 0;
200
+ if (tf_size_add((size_t)max_lag, 1, &out_rows) != TF_OK) return TF_ERROR;
82
201
  tf_batch *ob = tf_batch_create(2, out_rows);
83
202
  if (!ob) return TF_ERROR;
84
- tf_batch_set_schema(ob, 0, "lag", TF_TYPE_INT64);
85
- tf_batch_set_schema(ob, 1, "acf", TF_TYPE_FLOAT64);
203
+ if (tf_batch_set_schema(ob, 0, "lag", TF_TYPE_INT64) != TF_OK ||
204
+ tf_batch_set_schema(ob, 1, "acf", TF_TYPE_FLOAT64) != TF_OK) {
205
+ tf_batch_free(ob);
206
+ return TF_ERROR;
207
+ }
86
208
 
87
209
  for (int k = 0; k <= max_lag; k++) {
88
210
  double cov = 0;
@@ -91,9 +213,15 @@ static int acf_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
91
213
  }
92
214
  double acf_val = cov / var;
93
215
 
94
- tf_batch_set_int64(ob, k, 0, k);
95
- tf_batch_set_float64(ob, k, 1, acf_val);
96
- ob->n_rows = k + 1;
216
+ if (tf_batch_set_int64(ob, (size_t)k, 0, k) != TF_OK ||
217
+ tf_batch_set_float64(ob, (size_t)k, 1, acf_val) != TF_OK) {
218
+ tf_batch_free(ob);
219
+ return TF_ERROR;
220
+ }
221
+ if (tf_batch_expose_row(ob, (size_t)k) != TF_OK) {
222
+ tf_batch_free(ob);
223
+ return TF_ERROR;
224
+ }
97
225
  }
98
226
 
99
227
  *out = ob;
@@ -101,7 +229,7 @@ static int acf_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
101
229
  }
102
230
 
103
231
  static void acf_destroy(tf_step *self) {
104
- acf_state *st = self->state;
232
+ acf_state *st = self ? self->state : NULL;
105
233
  if (st) {
106
234
  free(st->column);
107
235
  free(st->values);
@@ -110,20 +238,42 @@ static void acf_destroy(tf_step *self) {
110
238
  free(self);
111
239
  }
112
240
 
241
+ static int parse_lags(const cJSON *lags_j, int *out) {
242
+ *out = 20;
243
+ if (!lags_j) return TF_OK;
244
+ size_t parsed_lags = 0;
245
+ if (tf_json_size_value(lags_j, "lags", 1, (size_t)INT_MAX,
246
+ &parsed_lags, "acf") < 0) {
247
+ return TF_ERROR;
248
+ }
249
+ *out = (int)parsed_lags;
250
+ return TF_OK;
251
+ }
252
+
113
253
  tf_step *tf_acf_create(const cJSON *args) {
114
254
  if (!args) return NULL;
115
255
  cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
116
- if (!cJSON_IsString(col_j)) return NULL;
256
+ if (!cJSON_IsString(col_j) || !col_j->valuestring[0]) {
257
+ tf_set_last_error("acf: column is required");
258
+ return NULL;
259
+ }
117
260
 
118
261
  acf_state *st = calloc(1, sizeof(acf_state));
119
262
  if (!st) return NULL;
263
+ st->col_idx = -1;
120
264
  st->column = strdup(col_j->valuestring);
265
+ if (!st->column) { free(st); return NULL; }
121
266
 
122
267
  cJSON *lags_j = cJSON_GetObjectItemCaseSensitive(args, "lags");
123
- st->lags = cJSON_IsNumber(lags_j) ? lags_j->valueint : 20;
124
- if (st->lags < 1) st->lags = 1;
268
+ if (parse_lags(lags_j, &st->lags) != TF_OK ||
269
+ acf_parse_missing_policy(args, &st->missing) != TF_OK ||
270
+ acf_parse_type_policy(args, &st->on_type_error) != TF_OK) {
271
+ free(st->column);
272
+ free(st);
273
+ return NULL;
274
+ }
125
275
 
126
- tf_step *step = malloc(sizeof(tf_step));
276
+ tf_step *step = calloc(1, sizeof(tf_step));
127
277
  if (!step) { free(st->column); free(st); return NULL; }
128
278
  step->process = acf_process;
129
279
  step->flush = acf_flush;