tranfi 0.0.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 (151) hide show
  1. package/LICENSE +177 -21
  2. package/NOTICE +8 -0
  3. package/README.md +627 -0
  4. package/app/assets/index-6quYZ5Ap.css +5 -0
  5. package/app/assets/index-BIAIKnrp.js +160 -0
  6. package/app/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
  7. package/app/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
  8. package/app/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
  9. package/app/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
  10. package/app/index.html +13 -0
  11. package/binding.gyp +121 -0
  12. package/csrc/arena.c +93 -0
  13. package/csrc/batch.c +976 -0
  14. package/csrc/buffer.c +154 -0
  15. package/csrc/cJSON.c +3386 -0
  16. package/csrc/cJSON.h +316 -0
  17. package/csrc/codec_csv.c +1951 -0
  18. package/csrc/codec_jsonl.c +1086 -0
  19. package/csrc/codec_table.c +248 -0
  20. package/csrc/codec_text.c +447 -0
  21. package/csrc/compiler.c +130 -0
  22. package/csrc/config.h +21 -0
  23. package/csrc/date_utils.h +94 -0
  24. package/csrc/dsl.c +5417 -0
  25. package/csrc/dsl.h +22 -0
  26. package/csrc/expr.c +1553 -0
  27. package/csrc/expr.h +58 -0
  28. package/csrc/internal.h +539 -0
  29. package/csrc/ir.c +166 -0
  30. package/csrc/ir.h +208 -0
  31. package/csrc/ir_schema.c +75 -0
  32. package/csrc/ir_serialize.c +166 -0
  33. package/csrc/ir_sql.c +1822 -0
  34. package/csrc/ir_validate.c +576 -0
  35. package/csrc/json_path.c +210 -0
  36. package/csrc/main.c +1241 -0
  37. package/csrc/memory_estimate.c +477 -0
  38. package/csrc/op_acf.c +283 -0
  39. package/csrc/op_across.c +477 -0
  40. package/csrc/op_anomaly.c +255 -0
  41. package/csrc/op_assert.c +761 -0
  42. package/csrc/op_bin.c +248 -0
  43. package/csrc/op_cast.c +523 -0
  44. package/csrc/op_clip.c +99 -0
  45. package/csrc/op_date_trunc.c +355 -0
  46. package/csrc/op_datetime.c +394 -0
  47. package/csrc/op_derive.c +216 -0
  48. package/csrc/op_diff.c +250 -0
  49. package/csrc/op_ewma.c +222 -0
  50. package/csrc/op_explode.c +206 -0
  51. package/csrc/op_fill_down.c +235 -0
  52. package/csrc/op_fill_null.c +268 -0
  53. package/csrc/op_filter.c +181 -0
  54. package/csrc/op_frequency.c +721 -0
  55. package/csrc/op_grep.c +181 -0
  56. package/csrc/op_group_agg.c +1956 -0
  57. package/csrc/op_hash.c +159 -0
  58. package/csrc/op_head.c +84 -0
  59. package/csrc/op_interpolate.c +445 -0
  60. package/csrc/op_join.c +2902 -0
  61. package/csrc/op_json_extract.c +227 -0
  62. package/csrc/op_json_filter.c +384 -0
  63. package/csrc/op_json_flatten.c +293 -0
  64. package/csrc/op_json_schema.c +503 -0
  65. package/csrc/op_label_encode.c +419 -0
  66. package/csrc/op_lag.c +181 -0
  67. package/csrc/op_lead.c +242 -0
  68. package/csrc/op_normalize.c +510 -0
  69. package/csrc/op_onehot.c +457 -0
  70. package/csrc/op_pivot.c +1754 -0
  71. package/csrc/op_quarantine.c +189 -0
  72. package/csrc/op_registry.c +3044 -0
  73. package/csrc/op_rename.c +129 -0
  74. package/csrc/op_replace.c +354 -0
  75. package/csrc/op_rleid.c +297 -0
  76. package/csrc/op_rowid.c +559 -0
  77. package/csrc/op_sample.c +158 -0
  78. package/csrc/op_schema.c +1341 -0
  79. package/csrc/op_schema_infer.c +252 -0
  80. package/csrc/op_select.c +340 -0
  81. package/csrc/op_set.c +3449 -0
  82. package/csrc/op_skip.c +95 -0
  83. package/csrc/op_sort.c +819 -0
  84. package/csrc/op_source_name.c +120 -0
  85. package/csrc/op_split.c +151 -0
  86. package/csrc/op_split_data.c +119 -0
  87. package/csrc/op_stack.c +271 -0
  88. package/csrc/op_stats.c +875 -0
  89. package/csrc/op_step.c +333 -0
  90. package/csrc/op_tail.c +105 -0
  91. package/csrc/op_tee.c +338 -0
  92. package/csrc/op_top.c +357 -0
  93. package/csrc/op_trim.c +138 -0
  94. package/csrc/op_unique.c +1343 -0
  95. package/csrc/op_unpivot.c +193 -0
  96. package/csrc/op_validate.c +648 -0
  97. package/csrc/op_window.c +591 -0
  98. package/csrc/path_policy.c +85 -0
  99. package/csrc/pipeline.c +1088 -0
  100. package/csrc/recipes.c +104 -0
  101. package/csrc/recipes.h +27 -0
  102. package/csrc/report.c +506 -0
  103. package/csrc/report.h +22 -0
  104. package/csrc/selector.c +1097 -0
  105. package/csrc/size_utils.c +348 -0
  106. package/csrc/spill.c +317 -0
  107. package/csrc/spill.h +21 -0
  108. package/csrc/tranfi.h +291 -0
  109. package/csrc/transform.h +209 -0
  110. package/csrc/transform_api.c +2237 -0
  111. package/csrc/transform_categorical.c +923 -0
  112. package/csrc/transform_internal.h +472 -0
  113. package/csrc/transform_json.c +3812 -0
  114. package/csrc/transform_numeric.c +1966 -0
  115. package/csrc/transform_sha256.c +154 -0
  116. package/csrc/transform_wasm.h +162 -0
  117. package/csrc/transform_wasm_api.c +1373 -0
  118. package/csrc/wasm_api.c +218 -0
  119. package/napi_api.c +534 -0
  120. package/napi_transform.c +1648 -0
  121. package/napi_transform.h +8 -0
  122. package/package.json +64 -59
  123. package/scripts/install-native.js +76 -0
  124. package/scripts/prepack.js +64 -0
  125. package/scripts/sync-csrc.js +23 -0
  126. package/src/cli.js +190 -0
  127. package/src/engines/duckdb.js +142 -0
  128. package/src/index.js +925 -0
  129. package/src/memory_policy.js +411 -0
  130. package/src/native.js +18 -0
  131. package/src/pipeline.js +709 -0
  132. package/src/recipe_json.js +80 -0
  133. package/src/server.js +279 -0
  134. package/src/transform.js +403 -0
  135. package/src/transform_error.js +10 -0
  136. package/src/wasm.js +21 -0
  137. package/wasm/index.js +732 -0
  138. package/wasm/package.json +1 -0
  139. package/wasm/tranfi_core.js +0 -0
  140. package/wasm/transform.js +1156 -0
  141. package/wasm/worker.js +786 -0
  142. package/dist/bundle.js +0 -1
  143. package/index.html +0 -18
  144. package/src/app.css +0 -169
  145. package/src/app.js +0 -203
  146. package/src/app.vue +0 -250
  147. package/src/bulma-input.vue +0 -110
  148. package/src/common-inputs.js +0 -28
  149. package/src/main.js +0 -20
  150. package/src/transforms.js +0 -166
  151. package/webpack.config.js +0 -108
package/napi_api.c ADDED
@@ -0,0 +1,534 @@
1
+ /*
2
+ * napi_api.c — Node.js N-API addon for Tranfi.
3
+ *
4
+ * Exposes the C pipeline API as native Node functions.
5
+ */
6
+
7
+ #include <node_api.h>
8
+ #include "tranfi.h"
9
+ #include "dsl.h"
10
+ #include "recipes.h"
11
+ #include "napi_transform.h"
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+
15
+ #define NAPI_CALL(env, call) \
16
+ do { \
17
+ napi_status status = (call); \
18
+ if (status != napi_ok) { \
19
+ napi_throw_error(env, NULL, "N-API call failed"); \
20
+ return NULL; \
21
+ } \
22
+ } while (0)
23
+
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 */
102
+ static napi_value napi_create_pipeline(napi_env env, napi_callback_info info) {
103
+ size_t argc = 2;
104
+ napi_value argv[2] = {0};
105
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
106
+
107
+ if (argc < 1) {
108
+ napi_throw_error(env, NULL, "createPipeline requires plan JSON string");
109
+ return NULL;
110
+ }
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
+
119
+ size_t str_len;
120
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_len));
121
+ char *json = malloc(str_len + 1);
122
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], json, str_len + 1, &str_len));
123
+
124
+ tf_pipeline *p = tf_pipeline_create_with_host_policy(json, str_len, &policy);
125
+ free(json);
126
+ free(workspace_root);
127
+
128
+ if (!p) {
129
+ const char *err = tf_last_error();
130
+ napi_throw_error(env, NULL, err ? err : "failed to create pipeline");
131
+ return NULL;
132
+ }
133
+
134
+ napi_value external;
135
+ NAPI_CALL(env, napi_create_external(env, p, NULL, NULL, &external));
136
+ return external;
137
+ }
138
+
139
+ /* push(pipeline: External, data: Buffer) → void */
140
+ static napi_value napi_push(napi_env env, napi_callback_info info) {
141
+ size_t argc = 2;
142
+ napi_value argv[2];
143
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
144
+
145
+ tf_pipeline *p;
146
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
147
+
148
+ uint8_t *data;
149
+ size_t len;
150
+ NAPI_CALL(env, napi_get_buffer_info(env, argv[1], (void **)&data, &len));
151
+
152
+ int rc = tf_pipeline_push(p, data, len);
153
+ if (rc != 0) {
154
+ const char *err = tf_pipeline_error(p);
155
+ napi_throw_error(env, NULL, err ? err : "push failed");
156
+ }
157
+ return NULL;
158
+ }
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
+
220
+ /* finish(pipeline: External) → void */
221
+ static napi_value napi_finish(napi_env env, napi_callback_info info) {
222
+ size_t argc = 1;
223
+ napi_value argv[1];
224
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
225
+
226
+ tf_pipeline *p;
227
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
228
+
229
+ int rc = tf_pipeline_finish(p);
230
+ if (rc != 0) {
231
+ const char *err = tf_pipeline_error(p);
232
+ napi_throw_error(env, NULL, err ? err : "finish failed");
233
+ }
234
+ return NULL;
235
+ }
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
+
257
+ /* pull(pipeline: External, channel: number) → Buffer */
258
+ static napi_value napi_pull(napi_env env, napi_callback_info info) {
259
+ size_t argc = 2;
260
+ napi_value argv[2];
261
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
262
+
263
+ tf_pipeline *p;
264
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
265
+
266
+ int32_t channel;
267
+ NAPI_CALL(env, napi_get_value_int32(env, argv[1], &channel));
268
+
269
+ /* Pull all available data */
270
+ size_t total = 0;
271
+ size_t cap = 65536;
272
+ uint8_t *result = malloc(cap);
273
+ uint8_t buf[65536];
274
+
275
+ for (;;) {
276
+ size_t n = tf_pipeline_pull(p, channel, buf, sizeof(buf));
277
+ if (n == 0) break;
278
+ if (total + n > cap) {
279
+ while (cap < total + n) cap *= 2;
280
+ result = realloc(result, cap);
281
+ }
282
+ memcpy(result + total, buf, n);
283
+ total += n;
284
+ }
285
+
286
+ napi_value buffer;
287
+ void *buf_data;
288
+ NAPI_CALL(env, napi_create_buffer_copy(env, total, result, &buf_data, &buffer));
289
+ free(result);
290
+ return buffer;
291
+ }
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
+
333
+ /* free(pipeline: External) → void */
334
+ static napi_value napi_free_pipeline(napi_env env, napi_callback_info info) {
335
+ size_t argc = 1;
336
+ napi_value argv[1];
337
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
338
+
339
+ tf_pipeline *p;
340
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
341
+
342
+ tf_pipeline_free(p);
343
+ return NULL;
344
+ }
345
+
346
+ /* version() → string */
347
+ static napi_value napi_tranfi_version(napi_env env, napi_callback_info info) {
348
+ (void)info;
349
+ const char *v = tf_version();
350
+ napi_value result;
351
+ NAPI_CALL(env, napi_create_string_utf8(env, v, strlen(v), &result));
352
+ return result;
353
+ }
354
+
355
+ /* error(pipeline: External) → string | null */
356
+ static napi_value napi_tranfi_error(napi_env env, napi_callback_info info) {
357
+ size_t argc = 1;
358
+ napi_value argv[1];
359
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
360
+
361
+ tf_pipeline *p;
362
+ NAPI_CALL(env, napi_get_value_external(env, argv[0], (void **)&p));
363
+
364
+ const char *err = tf_pipeline_error(p);
365
+ if (!err) {
366
+ napi_value null_val;
367
+ napi_get_null(env, &null_val);
368
+ return null_val;
369
+ }
370
+
371
+ napi_value result;
372
+ NAPI_CALL(env, napi_create_string_utf8(env, err, strlen(err), &result));
373
+ return result;
374
+ }
375
+
376
+ /* compileDsl(dslString: string) → string (plan JSON) */
377
+ static napi_value napi_compile_dsl(napi_env env, napi_callback_info info) {
378
+ size_t argc = 1;
379
+ napi_value argv[1];
380
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
381
+
382
+ if (argc < 1) {
383
+ napi_throw_error(env, NULL, "compileDsl requires DSL string");
384
+ return NULL;
385
+ }
386
+
387
+ size_t str_len;
388
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_len));
389
+ char *dsl = malloc(str_len + 1);
390
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], dsl, str_len + 1, &str_len));
391
+
392
+ char *error = NULL;
393
+ char *json = tf_compile_dsl(dsl, str_len, &error);
394
+ free(dsl);
395
+
396
+ if (!json) {
397
+ napi_throw_error(env, NULL, error ? error : "DSL compile failed");
398
+ free(error);
399
+ return NULL;
400
+ }
401
+
402
+ napi_value result;
403
+ NAPI_CALL(env, napi_create_string_utf8(env, json, strlen(json), &result));
404
+ free(json);
405
+ return result;
406
+ }
407
+
408
+ /* compileToSql(dslString: string) → string (SQL query) */
409
+ static napi_value napi_compile_to_sql(napi_env env, napi_callback_info info) {
410
+ size_t argc = 1;
411
+ napi_value argv[1];
412
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
413
+
414
+ if (argc < 1) {
415
+ napi_throw_error(env, NULL, "compileToSql requires DSL string");
416
+ return NULL;
417
+ }
418
+
419
+ size_t str_len;
420
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_len));
421
+ char *dsl = malloc(str_len + 1);
422
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], dsl, str_len + 1, &str_len));
423
+
424
+ char *error = NULL;
425
+ char *sql = tf_compile_to_sql(dsl, str_len, &error);
426
+ free(dsl);
427
+
428
+ if (!sql) {
429
+ napi_throw_error(env, NULL, error ? error : "SQL compilation failed");
430
+ free(error);
431
+ return NULL;
432
+ }
433
+
434
+ napi_value result;
435
+ NAPI_CALL(env, napi_create_string_utf8(env, sql, strlen(sql), &result));
436
+ tf_string_free(sql);
437
+ return result;
438
+ }
439
+
440
+ /* recipeCount() → number */
441
+ static napi_value napi_recipe_count(napi_env env, napi_callback_info info) {
442
+ (void)info;
443
+ napi_value result;
444
+ NAPI_CALL(env, napi_create_uint32(env, (uint32_t)tf_recipe_count(), &result));
445
+ return result;
446
+ }
447
+
448
+ /* recipeName(index: number) → string | null */
449
+ static napi_value napi_recipe_name(napi_env env, napi_callback_info info) {
450
+ size_t argc = 1;
451
+ napi_value argv[1];
452
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
453
+ uint32_t index;
454
+ NAPI_CALL(env, napi_get_value_uint32(env, argv[0], &index));
455
+ const char *s = tf_recipe_name(index);
456
+ if (!s) { napi_value n; napi_get_null(env, &n); return n; }
457
+ napi_value result;
458
+ NAPI_CALL(env, napi_create_string_utf8(env, s, strlen(s), &result));
459
+ return result;
460
+ }
461
+
462
+ /* recipeDsl(index: number) → string | null */
463
+ static napi_value napi_recipe_dsl(napi_env env, napi_callback_info info) {
464
+ size_t argc = 1;
465
+ napi_value argv[1];
466
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
467
+ uint32_t index;
468
+ NAPI_CALL(env, napi_get_value_uint32(env, argv[0], &index));
469
+ const char *s = tf_recipe_dsl(index);
470
+ if (!s) { napi_value n; napi_get_null(env, &n); return n; }
471
+ napi_value result;
472
+ NAPI_CALL(env, napi_create_string_utf8(env, s, strlen(s), &result));
473
+ return result;
474
+ }
475
+
476
+ /* recipeDescription(index: number) → string | null */
477
+ static napi_value napi_recipe_description(napi_env env, napi_callback_info info) {
478
+ size_t argc = 1;
479
+ napi_value argv[1];
480
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
481
+ uint32_t index;
482
+ NAPI_CALL(env, napi_get_value_uint32(env, argv[0], &index));
483
+ const char *s = tf_recipe_description(index);
484
+ if (!s) { napi_value n; napi_get_null(env, &n); return n; }
485
+ napi_value result;
486
+ NAPI_CALL(env, napi_create_string_utf8(env, s, strlen(s), &result));
487
+ return result;
488
+ }
489
+
490
+ /* recipeFindDsl(name: string) → string | null */
491
+ static napi_value napi_recipe_find_dsl(napi_env env, napi_callback_info info) {
492
+ size_t argc = 1;
493
+ napi_value argv[1];
494
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
495
+ size_t str_len;
496
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_len));
497
+ char *name = malloc(str_len + 1);
498
+ NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], name, str_len + 1, &str_len));
499
+ const char *dsl = tf_recipe_find_dsl(name);
500
+ free(name);
501
+ if (!dsl) { napi_value n; napi_get_null(env, &n); return n; }
502
+ napi_value result;
503
+ NAPI_CALL(env, napi_create_string_utf8(env, dsl, strlen(dsl), &result));
504
+ return result;
505
+ }
506
+
507
+ /* Module init */
508
+ static napi_value init(napi_env env, napi_value exports) {
509
+ napi_property_descriptor props[] = {
510
+ {"createPipeline", NULL, napi_create_pipeline, NULL, NULL, NULL, napi_default, NULL},
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},
514
+ {"finish", NULL, napi_finish, NULL, NULL, NULL, napi_default, NULL},
515
+ {"finishStep", NULL, napi_finish_step, NULL, NULL, NULL, napi_default, NULL},
516
+ {"pull", NULL, napi_pull, NULL, NULL, NULL, napi_default, NULL},
517
+ {"pullChunk", NULL, napi_pull_chunk, NULL, NULL, NULL, napi_default, NULL},
518
+ {"free", NULL, napi_free_pipeline, NULL, NULL, NULL, napi_default, NULL},
519
+ {"version", NULL, napi_tranfi_version, NULL, NULL, NULL, napi_default, NULL},
520
+ {"error", NULL, napi_tranfi_error, NULL, NULL, NULL, napi_default, NULL},
521
+ {"compileDsl", NULL, napi_compile_dsl, NULL, NULL, NULL, napi_default, NULL},
522
+ {"compileToSql", NULL, napi_compile_to_sql, NULL, NULL, NULL, napi_default, NULL},
523
+ {"recipeCount", NULL, napi_recipe_count, NULL, NULL, NULL, napi_default, NULL},
524
+ {"recipeName", NULL, napi_recipe_name, NULL, NULL, NULL, napi_default, NULL},
525
+ {"recipeDsl", NULL, napi_recipe_dsl, NULL, NULL, NULL, napi_default, NULL},
526
+ {"recipeDescription", NULL, napi_recipe_description, NULL, NULL, NULL, napi_default, NULL},
527
+ {"recipeFindDsl", NULL, napi_recipe_find_dsl, NULL, NULL, NULL, napi_default, NULL},
528
+ };
529
+ NAPI_CALL(env, napi_define_properties(env, exports, 18, props));
530
+ NAPI_CALL(env, tranfi_napi_define_transform(env, exports));
531
+ return exports;
532
+ }
533
+
534
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, init)