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/wasm_api.c CHANGED
@@ -10,6 +10,7 @@
10
10
  #endif
11
11
 
12
12
  #include "tranfi.h"
13
+ #include "internal.h"
13
14
  #include "dsl.h"
14
15
  #include "recipes.h"
15
16
  #include <stdlib.h>
@@ -34,15 +35,35 @@ static tf_pipeline *get_handle(int h) {
34
35
  return handles[h];
35
36
  }
36
37
 
38
+ static int wasm_validate_buffer(const void *ptr, int len, const char *what) {
39
+ if (len < 0) {
40
+ tf_set_last_error(what ? what : "invalid WASM buffer length");
41
+ return 0;
42
+ }
43
+ if (len > 0 && !ptr) {
44
+ tf_set_last_error(what ? what : "invalid WASM buffer pointer");
45
+ return 0;
46
+ }
47
+ return 1;
48
+ }
49
+
37
50
  #ifdef __EMSCRIPTEN__
38
51
  #define EXPORT EMSCRIPTEN_KEEPALIVE
39
52
  #else
40
53
  #define EXPORT
41
54
  #endif
42
55
 
43
- EXPORT
44
- int wasm_pipeline_create(const char *json, int len) {
45
- tf_pipeline *p = tf_pipeline_create(json, (size_t)len);
56
+ static int wasm_pipeline_create_policy(const char *json, int len,
57
+ int allow_fs, int allow_spill,
58
+ int allow_rules_file, const char *workspace_root) {
59
+ if (!wasm_validate_buffer(json, len, "invalid WASM plan buffer")) return -1;
60
+ tf_host_policy policy = {0};
61
+ policy.allow_fs = allow_fs != 0;
62
+ policy.allow_spill = allow_spill != 0;
63
+ policy.allow_rules_file = allow_rules_file != 0;
64
+ policy.allow_blocking = true;
65
+ policy.workspace_root = workspace_root && workspace_root[0] ? workspace_root : NULL;
66
+ tf_pipeline *p = tf_pipeline_create_with_host_policy(json, (size_t)len, &policy);
46
67
  if (!p) return -1;
47
68
  int h = alloc_handle(p);
48
69
  if (h < 0) {
@@ -52,13 +73,41 @@ int wasm_pipeline_create(const char *json, int len) {
52
73
  return h;
53
74
  }
54
75
 
76
+ EXPORT
77
+ int wasm_pipeline_create(const char *json, int len) {
78
+ return wasm_pipeline_create_policy(json, len, 0, 0, 0, NULL);
79
+ }
80
+
81
+ EXPORT
82
+ int wasm_pipeline_create_with_policy(const char *json, int len, int allow_fs,
83
+ int allow_spill, int allow_rules_file,
84
+ const char *workspace_root) {
85
+ return wasm_pipeline_create_policy(json, len, allow_fs, allow_spill,
86
+ allow_rules_file, workspace_root);
87
+ }
88
+
55
89
  EXPORT
56
90
  int wasm_pipeline_push(int handle, const uint8_t *data, int len) {
57
91
  tf_pipeline *p = get_handle(handle);
58
92
  if (!p) return -1;
93
+ if (!wasm_validate_buffer(data, len, "invalid WASM input buffer")) return -1;
59
94
  return tf_pipeline_push(p, data, (size_t)len);
60
95
  }
61
96
 
97
+ EXPORT
98
+ int wasm_pipeline_flush_input(int handle) {
99
+ tf_pipeline *p = get_handle(handle);
100
+ if (!p) return -1;
101
+ return tf_pipeline_flush_input(p);
102
+ }
103
+
104
+ EXPORT
105
+ int wasm_pipeline_set_source_name(int handle, const char *name) {
106
+ tf_pipeline *p = get_handle(handle);
107
+ if (!p) return -1;
108
+ return tf_pipeline_set_source_name(p, name);
109
+ }
110
+
62
111
  EXPORT
63
112
  int wasm_pipeline_finish(int handle) {
64
113
  tf_pipeline *p = get_handle(handle);
@@ -66,10 +115,18 @@ int wasm_pipeline_finish(int handle) {
66
115
  return tf_pipeline_finish(p);
67
116
  }
68
117
 
118
+ EXPORT
119
+ int wasm_pipeline_finish_step(int handle) {
120
+ tf_pipeline *p = get_handle(handle);
121
+ if (!p) return -1;
122
+ return tf_pipeline_finish_step(p);
123
+ }
124
+
69
125
  EXPORT
70
126
  int wasm_pipeline_pull(int handle, int channel, uint8_t *buf, int buf_len) {
71
127
  tf_pipeline *p = get_handle(handle);
72
128
  if (!p) return 0;
129
+ if (!wasm_validate_buffer(buf, buf_len, "invalid WASM output buffer")) return -1;
73
130
  return (int)tf_pipeline_pull(p, channel, buf, (size_t)buf_len);
74
131
  }
75
132
 
@@ -98,16 +155,14 @@ void wasm_pipeline_free(int handle) {
98
155
  */
99
156
  EXPORT
100
157
  char *wasm_compile_dsl(const char *dsl, int len) {
158
+ if (!wasm_validate_buffer(dsl, len, "invalid WASM DSL buffer")) return NULL;
101
159
  char *error = NULL;
102
- tf_ir_plan *plan = tf_dsl_parse(dsl, (size_t)len, &error);
103
- if (!plan) {
104
- /* Store error so JS can retrieve it via wasm_pipeline_error(-1) */
105
- (void)error; /* error accessible via tf_last_error() */
160
+ char *json = tf_compile_dsl(dsl, (size_t)len, &error);
161
+ if (!json) {
162
+ tf_set_last_error(error ? error : "DSL compile failed");
106
163
  free(error);
107
164
  return NULL;
108
165
  }
109
- char *json = tf_ir_plan_to_json(plan);
110
- tf_ir_plan_destroy(plan);
111
166
  return json;
112
167
  }
113
168
 
@@ -118,8 +173,14 @@ char *wasm_compile_dsl(const char *dsl, int len) {
118
173
  */
119
174
  EXPORT
120
175
  char *wasm_compile_to_sql(const char *dsl, int len) {
176
+ if (!wasm_validate_buffer(dsl, len, "invalid WASM DSL buffer")) return NULL;
121
177
  char *error = NULL;
122
178
  char *sql = tf_compile_to_sql(dsl, (size_t)len, &error);
179
+ if (!sql) {
180
+ tf_set_last_error(error ? error : "SQL compile failed");
181
+ free(error);
182
+ return NULL;
183
+ }
123
184
  free(error);
124
185
  return sql;
125
186
  }
package/napi_api.c CHANGED
@@ -8,6 +8,7 @@
8
8
  #include "tranfi.h"
9
9
  #include "dsl.h"
10
10
  #include "recipes.h"
11
+ #include "napi_transform.h"
11
12
  #include <stdlib.h>
12
13
  #include <string.h>
13
14
 
@@ -20,10 +21,87 @@
20
21
  } \
21
22
  } while (0)
22
23
 
23
- /*ాcreate_pipeline(planJson: string) → External */
24
+
25
+ static int get_named_bool(napi_env env, napi_value obj, const char *name, bool default_value, bool *out) {
26
+ bool has = false;
27
+ napi_status st = napi_has_named_property(env, obj, name, &has);
28
+ if (st != napi_ok || !has) { *out = default_value; return 0; }
29
+ napi_value value;
30
+ st = napi_get_named_property(env, obj, name, &value);
31
+ if (st != napi_ok) { *out = default_value; return 0; }
32
+ napi_valuetype type;
33
+ st = napi_typeof(env, value, &type);
34
+ if (st != napi_ok || type == napi_undefined || type == napi_null) { *out = default_value; return 0; }
35
+ if (type != napi_boolean) {
36
+ napi_throw_type_error(env, NULL, "host policy option must be boolean");
37
+ return -1;
38
+ }
39
+ bool b = false;
40
+ st = napi_get_value_bool(env, value, &b);
41
+ if (st != napi_ok) return -1;
42
+ *out = b;
43
+ return 0;
44
+ }
45
+
46
+ static int get_named_bool_alias(napi_env env, napi_value obj, const char *camel,
47
+ const char *snake, bool default_value, bool *out) {
48
+ bool has = false;
49
+ if (napi_has_named_property(env, obj, camel, &has) == napi_ok && has)
50
+ return get_named_bool(env, obj, camel, default_value, out);
51
+ return get_named_bool(env, obj, snake, default_value, out);
52
+ }
53
+
54
+ static char *get_named_string(napi_env env, napi_value obj, const char *name) {
55
+ bool has = false;
56
+ if (napi_has_named_property(env, obj, name, &has) != napi_ok || !has) return NULL;
57
+ napi_value value;
58
+ if (napi_get_named_property(env, obj, name, &value) != napi_ok) return NULL;
59
+ napi_valuetype type;
60
+ if (napi_typeof(env, value, &type) != napi_ok || type == napi_undefined || type == napi_null) return NULL;
61
+ if (type != napi_string) {
62
+ napi_throw_type_error(env, NULL, "workspaceRoot must be a string");
63
+ return (char *)-1;
64
+ }
65
+ size_t len = 0;
66
+ if (napi_get_value_string_utf8(env, value, NULL, 0, &len) != napi_ok) return (char *)-1;
67
+ char *s = malloc(len + 1);
68
+ if (!s) return (char *)-1;
69
+ if (napi_get_value_string_utf8(env, value, s, len + 1, &len) != napi_ok) {
70
+ free(s);
71
+ return (char *)-1;
72
+ }
73
+ return s;
74
+ }
75
+
76
+ static int parse_host_policy_options(napi_env env, napi_value value, tf_host_policy *policy,
77
+ char **workspace_root) {
78
+ memset(policy, 0, sizeof(*policy));
79
+ policy->allow_blocking = true;
80
+ *workspace_root = NULL;
81
+ if (!value) return 0;
82
+ napi_valuetype type;
83
+ if (napi_typeof(env, value, &type) != napi_ok || type == napi_undefined || type == napi_null) return 0;
84
+ if (type != napi_object) {
85
+ napi_throw_type_error(env, NULL, "createPipeline options must be an object");
86
+ return -1;
87
+ }
88
+ if (get_named_bool_alias(env, value, "allowFs", "allow_fs", false, &policy->allow_fs) != 0) return -1;
89
+ if (get_named_bool_alias(env, value, "allowNet", "allow_net", false, &policy->allow_net) != 0) return -1;
90
+ if (get_named_bool_alias(env, value, "allowSpill", "allow_spill", false, &policy->allow_spill) != 0) return -1;
91
+ if (get_named_bool_alias(env, value, "allowRulesFile", "allow_rules_file", false, &policy->allow_rules_file) != 0) return -1;
92
+ if (get_named_bool_alias(env, value, "allowBlocking", "allow_blocking", true, &policy->allow_blocking) != 0) return -1;
93
+ *workspace_root = get_named_string(env, value, "workspaceRoot");
94
+ if (*workspace_root == (char *)-1) { *workspace_root = NULL; return -1; }
95
+ if (!*workspace_root) *workspace_root = get_named_string(env, value, "workspace_root");
96
+ if (*workspace_root == (char *)-1) { *workspace_root = NULL; return -1; }
97
+ policy->workspace_root = *workspace_root && (*workspace_root)[0] ? *workspace_root : NULL;
98
+ return 0;
99
+ }
100
+
101
+ /* createPipeline(planJson: string, options?: HostPolicy) → External */
24
102
  static napi_value napi_create_pipeline(napi_env env, napi_callback_info info) {
25
- size_t argc = 1;
26
- napi_value argv[1];
103
+ size_t argc = 2;
104
+ napi_value argv[2] = {0};
27
105
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
28
106
 
29
107
  if (argc < 1) {
@@ -31,13 +109,21 @@ static napi_value napi_create_pipeline(napi_env env, napi_callback_info info) {
31
109
  return NULL;
32
110
  }
33
111
 
112
+ tf_host_policy policy;
113
+ char *workspace_root = NULL;
114
+ if (parse_host_policy_options(env, argc >= 2 ? argv[1] : NULL, &policy, &workspace_root) != 0) {
115
+ free(workspace_root);
116
+ return NULL;
117
+ }
118
+
34
119
  size_t str_len;
35
120
  NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_len));
36
121
  char *json = malloc(str_len + 1);
37
122
  NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], json, str_len + 1, &str_len));
38
123
 
39
- tf_pipeline *p = tf_pipeline_create(json, str_len);
124
+ tf_pipeline *p = tf_pipeline_create_with_host_policy(json, str_len, &policy);
40
125
  free(json);
126
+ free(workspace_root);
41
127
 
42
128
  if (!p) {
43
129
  const char *err = tf_last_error();
@@ -71,6 +157,66 @@ static napi_value napi_push(napi_env env, napi_callback_info info) {
71
157
  return NULL;
72
158
  }
73
159
 
160
+ /* flushInput(pipeline: External) → void */
161
+ static napi_value napi_flush_input(napi_env env, napi_callback_info info) {
162
+ size_t argc = 1;
163
+ napi_value argv[1];
164
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
165
+
166
+ tf_pipeline *p;
167
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
168
+
169
+ int rc = tf_pipeline_flush_input(p);
170
+ if (rc != 0) {
171
+ const char *err = tf_pipeline_error(p);
172
+ napi_throw_error(env, NULL, err ? err : "input flush failed");
173
+ }
174
+ return NULL;
175
+ }
176
+
177
+ /* setSourceName(pipeline: External, name?: string | null) → void */
178
+ static napi_value napi_set_source_name(napi_env env, napi_callback_info info) {
179
+ size_t argc = 2;
180
+ napi_value argv[2];
181
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
182
+
183
+ if (argc < 1) {
184
+ napi_throw_error(env, NULL, "setSourceName requires pipeline");
185
+ return NULL;
186
+ }
187
+
188
+ tf_pipeline *p;
189
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
190
+
191
+ char *name = NULL;
192
+ if (argc >= 2) {
193
+ napi_valuetype type;
194
+ NAPI_CALL(env, napi_typeof(env, argv[1], &type));
195
+ if (type != napi_null && type != napi_undefined) {
196
+ if (type != napi_string) {
197
+ napi_throw_error(env, NULL, "source name must be a string, null, or undefined");
198
+ return NULL;
199
+ }
200
+ size_t str_len = 0;
201
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[1], NULL, 0, &str_len));
202
+ name = malloc(str_len + 1);
203
+ if (!name) {
204
+ napi_throw_error(env, NULL, "out of memory");
205
+ return NULL;
206
+ }
207
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[1], name, str_len + 1, &str_len));
208
+ }
209
+ }
210
+
211
+ int rc = tf_pipeline_set_source_name(p, name);
212
+ free(name);
213
+ if (rc != 0) {
214
+ const char *err = tf_pipeline_error(p);
215
+ napi_throw_error(env, NULL, err ? err : "set source name failed");
216
+ }
217
+ return NULL;
218
+ }
219
+
74
220
  /* finish(pipeline: External) → void */
75
221
  static napi_value napi_finish(napi_env env, napi_callback_info info) {
76
222
  size_t argc = 1;
@@ -88,6 +234,26 @@ static napi_value napi_finish(napi_env env, napi_callback_info info) {
88
234
  return NULL;
89
235
  }
90
236
 
237
+ /* finishStep(pipeline: External) → boolean done */
238
+ static napi_value napi_finish_step(napi_env env, napi_callback_info info) {
239
+ size_t argc = 1;
240
+ napi_value argv[1];
241
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
242
+
243
+ tf_pipeline *p;
244
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
245
+
246
+ int rc = tf_pipeline_finish_step(p);
247
+ if (rc == TF_ERROR) {
248
+ const char *err = tf_pipeline_error(p);
249
+ napi_throw_error(env, NULL, err ? err : "finish failed");
250
+ return NULL;
251
+ }
252
+ napi_value result;
253
+ NAPI_CALL(env, napi_get_boolean(env, rc == TF_DONE, &result));
254
+ return result;
255
+ }
256
+
91
257
  /* pull(pipeline: External, channel: number) → Buffer */
92
258
  static napi_value napi_pull(napi_env env, napi_callback_info info) {
93
259
  size_t argc = 2;
@@ -124,6 +290,46 @@ static napi_value napi_pull(napi_env env, napi_callback_info info) {
124
290
  return buffer;
125
291
  }
126
292
 
293
+ /* pullChunk(pipeline: External, channel: number, maxBytes?: number) → Buffer */
294
+ static napi_value napi_pull_chunk(napi_env env, napi_callback_info info) {
295
+ size_t argc = 3;
296
+ napi_value argv[3];
297
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
298
+
299
+ if (argc < 2) {
300
+ napi_throw_error(env, NULL, "pullChunk requires pipeline and channel");
301
+ return NULL;
302
+ }
303
+
304
+ tf_pipeline *p;
305
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
306
+
307
+ int32_t channel;
308
+ NAPI_CALL(env, napi_get_value_int32(env, argv[1], &channel));
309
+
310
+ uint32_t max_bytes = 65536;
311
+ if (argc >= 3) {
312
+ NAPI_CALL(env, napi_get_value_uint32(env, argv[2], &max_bytes));
313
+ if (max_bytes == 0) {
314
+ napi_throw_error(env, NULL, "pullChunk maxBytes must be positive");
315
+ return NULL;
316
+ }
317
+ }
318
+
319
+ uint8_t *tmp = malloc((size_t)max_bytes);
320
+ if (!tmp) {
321
+ napi_throw_error(env, NULL, "out of memory");
322
+ return NULL;
323
+ }
324
+
325
+ size_t n = tf_pipeline_pull(p, channel, tmp, (size_t)max_bytes);
326
+ napi_value buffer;
327
+ void *buf_data;
328
+ NAPI_CALL(env, napi_create_buffer_copy(env, n, tmp, &buf_data, &buffer));
329
+ free(tmp);
330
+ return buffer;
331
+ }
332
+
127
333
  /* free(pipeline: External) → void */
128
334
  static napi_value napi_free_pipeline(napi_env env, napi_callback_info info) {
129
335
  size_t argc = 1;
@@ -184,18 +390,15 @@ static napi_value napi_compile_dsl(napi_env env, napi_callback_info info) {
184
390
  NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], dsl, str_len + 1, &str_len));
185
391
 
186
392
  char *error = NULL;
187
- tf_ir_plan *plan = tf_dsl_parse(dsl, str_len, &error);
393
+ char *json = tf_compile_dsl(dsl, str_len, &error);
188
394
  free(dsl);
189
395
 
190
- if (!plan) {
191
- napi_throw_error(env, NULL, error ? error : "DSL parse failed");
396
+ if (!json) {
397
+ napi_throw_error(env, NULL, error ? error : "DSL compile failed");
192
398
  free(error);
193
399
  return NULL;
194
400
  }
195
401
 
196
- char *json = tf_ir_plan_to_json(plan);
197
- tf_ir_plan_destroy(plan);
198
-
199
402
  napi_value result;
200
403
  NAPI_CALL(env, napi_create_string_utf8(env, json, strlen(json), &result));
201
404
  free(json);
@@ -306,8 +509,12 @@ static napi_value init(napi_env env, napi_value exports) {
306
509
  napi_property_descriptor props[] = {
307
510
  {"createPipeline", NULL, napi_create_pipeline, NULL, NULL, NULL, napi_default, NULL},
308
511
  {"push", NULL, napi_push, NULL, NULL, NULL, napi_default, NULL},
512
+ {"flushInput", NULL, napi_flush_input, NULL, NULL, NULL, napi_default, NULL},
513
+ {"setSourceName", NULL, napi_set_source_name, NULL, NULL, NULL, napi_default, NULL},
309
514
  {"finish", NULL, napi_finish, NULL, NULL, NULL, napi_default, NULL},
515
+ {"finishStep", NULL, napi_finish_step, NULL, NULL, NULL, napi_default, NULL},
310
516
  {"pull", NULL, napi_pull, NULL, NULL, NULL, napi_default, NULL},
517
+ {"pullChunk", NULL, napi_pull_chunk, NULL, NULL, NULL, napi_default, NULL},
311
518
  {"free", NULL, napi_free_pipeline, NULL, NULL, NULL, napi_default, NULL},
312
519
  {"version", NULL, napi_tranfi_version, NULL, NULL, NULL, napi_default, NULL},
313
520
  {"error", NULL, napi_tranfi_error, NULL, NULL, NULL, napi_default, NULL},
@@ -319,7 +526,8 @@ static napi_value init(napi_env env, napi_value exports) {
319
526
  {"recipeDescription", NULL, napi_recipe_description, NULL, NULL, NULL, napi_default, NULL},
320
527
  {"recipeFindDsl", NULL, napi_recipe_find_dsl, NULL, NULL, NULL, napi_default, NULL},
321
528
  };
322
- NAPI_CALL(env, napi_define_properties(env, exports, 14, props));
529
+ NAPI_CALL(env, napi_define_properties(env, exports, 18, props));
530
+ NAPI_CALL(env, tranfi_napi_define_transform(env, exports));
323
531
  return exports;
324
532
  }
325
533