node-red-contrib-prib-functions 0.23.2 → 0.26.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 (58) hide show
  1. package/.github/copilot-instructions.md +36 -0
  2. package/README.md +153 -140
  3. package/columnar/columnar.html +258 -0
  4. package/columnar/columnar.js +1055 -0
  5. package/columnar/icons/columnar.svg +38 -0
  6. package/fileSystem/filesystem.html +299 -0
  7. package/fileSystem/filesystem.js +170 -0
  8. package/gitlab/gitlab.html +191 -0
  9. package/gitlab/gitlab.js +248 -0
  10. package/gitlab/icons/gitlab.svg +17 -0
  11. package/lib/AlphaBeta.js +32 -0
  12. package/lib/GraphDB.js +40 -9
  13. package/lib/MinMax.js +17 -0
  14. package/lib/Tree.js +64 -0
  15. package/lib/objectExtensions.js +28 -5
  16. package/lib/timeDimension.js +36 -0
  17. package/lib/typedInput.js +18 -2
  18. package/logisticRegression/icons/logisticregression.svg +22 -0
  19. package/logisticRegression/logisticRegression.html +136 -0
  20. package/logisticRegression/logisticRegression.js +83 -0
  21. package/package.json +21 -9
  22. package/test/02-graphdb.js +46 -0
  23. package/test/columnar.js +509 -0
  24. package/test/data/.config.nodes.json +114 -70
  25. package/test/data/.config.nodes.json.backup +104 -71
  26. package/test/data/.config.runtime.json +2 -1
  27. package/test/data/.config.runtime.json.backup +2 -1
  28. package/test/data/.config.users.json +3 -2
  29. package/test/data/.config.users.json.backup +3 -2
  30. package/test/data/.flow.json.backup +1545 -369
  31. package/test/data/flow.json +1457 -270
  32. package/test/data/package-lock.json +11 -11
  33. package/test/data/shares/.config.nodes.json +611 -0
  34. package/test/data/shares/.config.nodes.json.backup +589 -0
  35. package/test/data/shares/.config.runtime.json +5 -0
  36. package/test/data/shares/.config.runtime.json.backup +4 -0
  37. package/test/data/shares/.config.users.json +33 -0
  38. package/test/data/shares/.config.users.json.backup +33 -0
  39. package/test/data/shares/.flow.json.backup +230 -0
  40. package/test/data/shares/.flow_cred.json.backup +3 -0
  41. package/test/data/shares/flow.json +267 -0
  42. package/test/data/shares/flow_cred.json +3 -0
  43. package/test/data/shares/package.json +6 -0
  44. package/test/data/shares/settings.js +544 -0
  45. package/test/dataAnalysisExtensions.js +93 -93
  46. package/test/logisticRegression.js +379 -0
  47. package/test/transform.js +11 -11
  48. package/test/transformConfluence.js +4 -2
  49. package/test/transformNumPy.js +3 -1
  50. package/test/transformXLSX.js +4 -2
  51. package/test/transformXML.js +4 -2
  52. package/test-runner.js +400 -0
  53. package/test.parq +0 -0
  54. package/test_select.js +37 -0
  55. package/testing/test.js +8 -7
  56. package/transform/transform.html +23 -2
  57. package/transform/transform.js +239 -283
  58. package/transform/xlsx2.js +74 -0
package/test-runner.js ADDED
@@ -0,0 +1,400 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Simple unit test for logistic regression node
4
+ const assert = require('assert');
5
+ const LogisticRegression = require('logisticegression');
6
+
7
+ // counters used by both runners
8
+ let testsPassed = 0;
9
+ let testsFailed = 0;
10
+
11
+ // If the CLI includes the word 'transform' proceed to run the
12
+ // transformation node tests rather than the logistic regression
13
+ // suite. This avoids pulling mocha back in while still exercising
14
+ // the existing mocha-based files under test/transform*.js.
15
+ if (process.argv.includes('transform')) {
16
+ runTransformTests();
17
+ return;
18
+ }
19
+
20
+ // If the CLI includes 'dataanalysis' run dataAnalysis tests
21
+ if (process.argv.includes('dataanalysis')) {
22
+ runDataAnalysisTests();
23
+ return;
24
+ }
25
+
26
+ // If the CLI includes 'columnar' run the columnar-format tests
27
+ if (process.argv.includes('columnar')) {
28
+ runColumnarTests();
29
+ return;
30
+ }
31
+
32
+
33
+ function test(description, fn) {
34
+ try {
35
+ fn();
36
+ console.log(`✓ PASS: ${description}`);
37
+ testsPassed++;
38
+ } catch (err) {
39
+ console.error(`✗ FAIL: ${description}`);
40
+ console.error(` Error: ${err.message}`);
41
+ testsFailed++;
42
+ }
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // transform test emulation
47
+ // ---------------------------------------------------------------------------
48
+ function callMaybeAsync(fn) {
49
+ return new Promise((resolve, reject) => {
50
+ try {
51
+ if (fn.length >= 1) {
52
+ fn(err => (err ? reject(err) : resolve()));
53
+ } else {
54
+ Promise.resolve(fn()).then(resolve, reject);
55
+ }
56
+ } catch (err) {
57
+ reject(err);
58
+ }
59
+ });
60
+ }
61
+
62
+ async function runTransformTests() {
63
+ const path = require('path');
64
+ const fs = require('fs');
65
+
66
+ let beforeEachFns = [];
67
+ let afterEachFns = [];
68
+ let tests = [];
69
+ let currentDescribe = '';
70
+
71
+ global.describe = function(desc, fn) {
72
+ currentDescribe = desc;
73
+ fn();
74
+ };
75
+ global.beforeEach = function(fn) {
76
+ beforeEachFns.push(fn);
77
+ };
78
+ global.afterEach = function(fn) {
79
+ afterEachFns.push(fn);
80
+ };
81
+ global.it = function(desc, fn) {
82
+ const full = currentDescribe ? `${currentDescribe} ${desc}` : desc;
83
+ tests.push({desc: full, fn});
84
+ return { timeout: () => {} };
85
+ };
86
+
87
+ console.log("Transform Node Tests\n" + "=".repeat(60));
88
+
89
+ const dir = path.join(__dirname, 'test');
90
+ const files = fs.readdirSync(dir).filter(f => /^transform.*\.js$/.test(f));
91
+ files.forEach(f => require(path.join(dir, f)));
92
+
93
+ for (const t of tests) {
94
+ for (const bf of beforeEachFns) await callMaybeAsync(bf);
95
+ try {
96
+ await callMaybeAsync(t.fn);
97
+ console.log(`✓ PASS: ${t.desc}`);
98
+ testsPassed++;
99
+ } catch (err) {
100
+ console.error(`✗ FAIL: ${t.desc}`);
101
+ console.error(` Error: ${err.message}`);
102
+ testsFailed++;
103
+ }
104
+ for (const af of afterEachFns) {
105
+ try {
106
+ await callMaybeAsync(af);
107
+ } catch (err) {
108
+ // ignore server-not-running errors which happen when
109
+ // cleanup double-closes the Node-RED test helper server.
110
+ if (err && err.code === 'ERR_SERVER_NOT_RUNNING') {
111
+ // no-op
112
+ } else {
113
+ throw err;
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ console.log('\n' + '='.repeat(60));
120
+ console.log(`Tests passed: ${testsPassed}`);
121
+ console.log(`Tests failed: ${testsFailed}`);
122
+ console.log('='.repeat(60));
123
+ process.exit(testsFailed > 0 ? 1 : 0);
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // dataanalysis test emulation
128
+ // ---------------------------------------------------------------------------
129
+ async function runDataAnalysisTests() {
130
+ const path = require('path');
131
+ const fs = require('fs');
132
+
133
+ let beforeEachFns = [];
134
+ let afterEachFns = [];
135
+ let tests = [];
136
+ let currentDescribe = '';
137
+
138
+ global.describe = function(desc, fn) {
139
+ currentDescribe = desc;
140
+ fn();
141
+ };
142
+ global.beforeEach = function(fn) {
143
+ beforeEachFns.push(fn);
144
+ };
145
+ global.afterEach = function(fn) {
146
+ afterEachFns.push(fn);
147
+ };
148
+ global.it = function(desc, fn) {
149
+ const full = currentDescribe ? `${currentDescribe} ${desc}` : desc;
150
+ tests.push({desc: full, fn});
151
+ return { timeout: () => {} };
152
+ };
153
+
154
+ console.log("Data Analysis Extension Tests\n" + "=".repeat(60));
155
+
156
+ const dir = path.join(__dirname, 'test');
157
+ const files = fs.readdirSync(dir).filter(f => /^dataAnalysisE.*\.js$/.test(f));
158
+ files.forEach(f => require(path.join(dir, f)));
159
+
160
+ for (const t of tests) {
161
+ for (const bf of beforeEachFns) await callMaybeAsync(bf);
162
+ try {
163
+ await callMaybeAsync(t.fn);
164
+ console.log(`✓ PASS: ${t.desc}`);
165
+ testsPassed++;
166
+ } catch (err) {
167
+ console.error(`✗ FAIL: ${t.desc}`);
168
+ console.error(` Error: ${err.message}`);
169
+ testsFailed++;
170
+ }
171
+ for (const af of afterEachFns) await callMaybeAsync(af);
172
+ }
173
+
174
+ console.log('\n' + '='.repeat(60));
175
+ console.log(`Tests passed: ${testsPassed}`);
176
+ console.log(`Tests failed: ${testsFailed}`);
177
+ console.log('='.repeat(60));
178
+ process.exit(testsFailed > 0 ? 1 : 0);
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // columnar test emulation
183
+ // ---------------------------------------------------------------------------
184
+ async function runColumnarTests() {
185
+ const path = require('path');
186
+ const fs = require('fs');
187
+
188
+ let beforeEachFns = [];
189
+ let afterEachFns = [];
190
+ let tests = [];
191
+ let currentDescribe = '';
192
+
193
+ global.describe = function(desc, fn) {
194
+ currentDescribe = desc;
195
+ fn();
196
+ };
197
+ global.beforeEach = function(fn) {
198
+ beforeEachFns.push(fn);
199
+ };
200
+ global.afterEach = function(fn) {
201
+ afterEachFns.push(fn);
202
+ };
203
+ global.it = function(desc, fn) {
204
+ const full = currentDescribe ? `${currentDescribe} ${desc}` : desc;
205
+ tests.push({desc: full, fn});
206
+ return { timeout: () => {} };
207
+ };
208
+
209
+ console.log("Columnar File Tests\n" + "=".repeat(60));
210
+
211
+ const dir = path.join(__dirname, 'test');
212
+ const files = fs.readdirSync(dir).filter(f => /^columnar.*\.js$/.test(f));
213
+ files.forEach(f => require(path.join(dir, f)));
214
+
215
+ for (const t of tests) {
216
+ for (const bf of beforeEachFns) await callMaybeAsync(bf);
217
+ try {
218
+ await callMaybeAsync(t.fn);
219
+ console.log(`✓ PASS: ${t.desc}`);
220
+ testsPassed++;
221
+ } catch (err) {
222
+ console.error(`✗ FAIL: ${t.desc}`);
223
+ console.error(` Error: ${err.message}`);
224
+ testsFailed++;
225
+ }
226
+ for (const af of afterEachFns) await callMaybeAsync(af);
227
+ }
228
+
229
+ console.log('\n' + '='.repeat(60));
230
+ console.log(`Tests passed: ${testsPassed}`);
231
+ console.log(`Tests failed: ${testsFailed}`);
232
+ console.log('='.repeat(60));
233
+ process.exit(testsFailed > 0 ? 1 : 0);
234
+ }
235
+
236
+ console.log("Logistic Regression Unit Tests\n" + "=".repeat(60));
237
+
238
+ // Test 1: Model instantiation
239
+ test("Should create a LogisticRegression model instance", () => {
240
+ const model = new LogisticRegression({
241
+ learningRate: 0.1,
242
+ iterations: 100
243
+ });
244
+ assert(model instanceof LogisticRegression);
245
+ assert.strictEqual(model.learningRate, 0.1);
246
+ assert.strictEqual(model.iterations, 100);
247
+ });
248
+
249
+ // Test 2: Model fit
250
+ test("Should fit a model with training data", () => {
251
+ const model = new LogisticRegression({
252
+ learningRate: 0.1,
253
+ iterations: 100
254
+ });
255
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
256
+ const y = [0, 1, 1, 1];
257
+
258
+ const result = model.fit(X, y);
259
+ assert(result instanceof LogisticRegression);
260
+ assert(model.weights !== null);
261
+ assert.strictEqual(model.weights.length, 3); // 2 features + 1 intercept
262
+ });
263
+
264
+ // Test 3: Model predict
265
+ test("Should predict class labels", () => {
266
+ const model = new LogisticRegression({
267
+ learningRate: 0.1,
268
+ iterations: 100
269
+ });
270
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
271
+ const y = [0, 1, 1, 1];
272
+
273
+ model.fit(X, y);
274
+ const predictions = model.predict([[0, 0], [1, 1], [0, 1]]);
275
+
276
+ assert(Array.isArray(predictions));
277
+ assert.strictEqual(predictions.length, 3);
278
+ predictions.forEach(pred => {
279
+ assert([0, 1].includes(pred));
280
+ });
281
+ });
282
+
283
+ // Test 4: Model predictProba
284
+ test("Should predict probabilities", () => {
285
+ const model = new LogisticRegression({
286
+ learningRate: 0.1,
287
+ iterations: 100
288
+ });
289
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
290
+ const y = [0, 1, 1, 1];
291
+
292
+ model.fit(X, y);
293
+ const probs = model.predictProba([[0, 0], [1, 1], [0.5, 0.5]]);
294
+
295
+ assert(Array.isArray(probs));
296
+ assert.strictEqual(probs.length, 3);
297
+ probs.forEach(prob => {
298
+ assert(typeof prob === 'number');
299
+ assert(prob >= 0 && prob <= 1);
300
+ });
301
+ });
302
+
303
+ // Test 5: Model decision function
304
+ test("Should compute decision function (logits)", () => {
305
+ const model = new LogisticRegression({
306
+ learningRate: 0.1,
307
+ iterations: 100
308
+ });
309
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
310
+ const y = [0, 1, 1, 1];
311
+
312
+ model.fit(X, y);
313
+ const decisions = model.decisionFunction([[0, 0], [1, 1]]);
314
+
315
+ assert(Array.isArray(decisions));
316
+ assert.strictEqual(decisions.length, 2);
317
+ decisions.forEach(d => {
318
+ assert(typeof d === 'number');
319
+ });
320
+ });
321
+
322
+ // Test 6: Fit without data
323
+ test("Should throw error when fitting without data", () => {
324
+ const model = new LogisticRegression();
325
+ try {
326
+ model.fit([], []);
327
+ throw new Error("Should have thrown an error");
328
+ } catch (err) {
329
+ assert(err.message.includes("non-empty"));
330
+ }
331
+ });
332
+
333
+ // Test 7: Mismatched dimensions
334
+ test("Should throw error when X and y have different lengths", () => {
335
+ const model = new LogisticRegression();
336
+ try {
337
+ model.fit([[0, 0], [1, 1]], [0, 1, 1]);
338
+ throw new Error("Should have thrown an error");
339
+ } catch (err) {
340
+ assert(err.message.includes("same number"));
341
+ }
342
+ });
343
+
344
+ // Test 8: Predict before fit
345
+ test("Should throw error when predicting before fitting", () => {
346
+ const model = new LogisticRegression();
347
+ try {
348
+ model.predict([[0, 0]]);
349
+ throw new Error("Should have thrown an error");
350
+ } catch (err) {
351
+ assert(err.message.includes("not fitted"));
352
+ }
353
+ });
354
+
355
+ // Test 9: L2 regularization
356
+ test("Should support L2 regularization", () => {
357
+ const model = new LogisticRegression({
358
+ learningRate: 0.1,
359
+ iterations: 100,
360
+ l2: 0.01
361
+ });
362
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
363
+ const y = [0, 1, 1, 1];
364
+
365
+ model.fit(X, y);
366
+ const predictions = model.predict([[0.5, 0.5]]);
367
+
368
+ assert(Array.isArray(predictions));
369
+ assert.strictEqual(predictions.length, 1);
370
+ });
371
+
372
+ // Test 10: Custom threshold
373
+ test("Should use custom threshold for prediction", () => {
374
+ const model = new LogisticRegression({
375
+ learningRate: 0.1,
376
+ iterations: 100
377
+ });
378
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
379
+ const y = [0, 1, 1, 1];
380
+
381
+ model.fit(X, y);
382
+ const predictions = model.predict([[0.5, 0.5]], 0.7);
383
+
384
+ assert(Array.isArray(predictions));
385
+ assert([0, 1].includes(predictions[0]));
386
+ });
387
+
388
+ // Summary
389
+ console.log("\n" + "=".repeat(60));
390
+ console.log(`Tests passed: ${testsPassed}`);
391
+ console.log(`Tests failed: ${testsFailed}`);
392
+ console.log("=".repeat(60));
393
+
394
+ if (testsFailed > 0) {
395
+ console.log("\nSome tests failed!");
396
+ process.exit(1);
397
+ } else {
398
+ console.log("\nAll tests passed! ✓");
399
+ process.exit(0);
400
+ }
package/test.parq ADDED
Binary file
package/test_select.js ADDED
@@ -0,0 +1,37 @@
1
+ const { SimpleColumnarStore } = require('./columnar/columnar.js');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ (async () => {
7
+ const tmp = path.join(os.tmpdir(), 'test-select-issue.columnar');
8
+ if (fs.existsSync(tmp)) fs.unlinkSync(tmp);
9
+
10
+ const records = [
11
+ { id: 1, name: 'Alice', age: 25 },
12
+ { id: 2, name: 'Bob', age: 30 },
13
+ { id: 3, name: 'Charlie', age: 35 },
14
+ { id: 4, name: 'Diana', age: 28 },
15
+ { id: 5, name: 'Eve', age: 32 },
16
+ { id: 6, name: 'Frank', age: 29 },
17
+ ];
18
+
19
+ await SimpleColumnarStore.writeRecords(records, tmp);
20
+
21
+ // Test the exact SQL from Node-RED
22
+ const sql = 'SELECT "a1" as test,:msg.payload as name,COUNT(*) AS cnt1 FROM ? a where name= :msg.payload';
23
+ console.log('Testing SQL:', sql);
24
+
25
+ const result = await SimpleColumnarStore.sqlQuery(
26
+ tmp,
27
+ sql,
28
+ null,
29
+ { msg: { payload: 'Alice' }, flow: {}, global: {} }
30
+ );
31
+
32
+ console.log('Result:', JSON.stringify(result, null, 2));
33
+ console.log('Number of columns in first record:', Object.keys(result[0]).length);
34
+ console.log('Column names:', Object.keys(result[0]));
35
+
36
+ if (fs.existsSync(tmp)) fs.unlinkSync(tmp);
37
+ })();
package/testing/test.js CHANGED
@@ -74,7 +74,7 @@ function equalObjects(obj1,obj2,errorFactor,callEquals=()=>true,callNotEquals=()
74
74
  if( keys1.length !== keys2.length ) return callNotEquals("different number of properties")
75
75
  for(const key1 of keys1){
76
76
  try{
77
- if( !equalObjects(obj1[key1],obj2[key1],errorFactor) ) return callNotEquals()
77
+ if( !equalObjects(obj1[key1],obj2[key1],errorFactor,undefined,err=>{throw Error(err)}) ) return callNotEquals()
78
78
  } catch(ex){
79
79
  return callNotEquals(ex.message)
80
80
  }
@@ -123,17 +123,18 @@ module.exports = function(RED) {
123
123
  node.on("input",function(msg) {
124
124
  if(msg._test) {
125
125
  try{
126
+ const testData=node.getData(msg,node)
126
127
  if(msg._test.id!==node.id) return setError(msg,node,"Sent by another test "+msg._test.id);
127
-
128
128
  if(node.isJSONata)
129
- return RED.util.evaluateJSONataExpression(node.resultExpression,msg,(err,data)=>{
130
- if(err) testedFailed(node,msg,err)
131
- else return data?testedOK(node,msg):testedFailed(node,msg,"no data")
129
+ return RED.util.evaluateJSONataExpression(node.resultExpression,testData,(err,data)=>{
130
+ if(err) return testedFailed(node,msg,err)
131
+ if(data==null) testedFailed(node,msg,"no data, expecting boolean")
132
+ return data==true ? testedOK(node,msg) : testedFailed(node,msg,"jsonata not true")
132
133
  })
133
134
 
134
- node.equalObjects(node.getData(msg,node),msg._test.result,node.errorFactor,
135
+ node.equalObjects(testData,msg._test.result,node.errorFactor,
135
136
  ()=>testedOK(node,msg),
136
- (err)=>testedFailed(node,msg,err)
137
+ err=>testedFailed(node,msg,"equal object test failure "+err)
137
138
  );
138
139
 
139
140
  } catch(ex){
@@ -27,6 +27,10 @@
27
27
  sourceProperty:{value:"msg.payload"},
28
28
  targetProperty:{value:"msg.payload"},
29
29
  topicProperty:{value:"msg.topic"},
30
+ JSONataSource:{value:null,validate:v=>v==null||RED.validators.typedInput("JSONataSourceType")},
31
+ JSONataSourceType:{value:"jsonata"},
32
+ JSONataTarget:{value:null,validate:v=>v==null||RED.validators.typedInput("JSONataTargetType")},
33
+ JSONataTargetType:{value:"jsonata"},
30
34
  index: {value:0},
31
35
  maxMessages: {value:1000},
32
36
  maxDate: {value:null},
@@ -104,6 +108,7 @@
104
108
  $(".form-row-http-in-skip").hide();
105
109
  $(".form-row-http-in-schema").hide();
106
110
  $(".form-row-http-in-compressionType").hide();
111
+ $(".form-row-http-in-JSONataSource").hide();
107
112
  if(!['CSV','CSVWithHeader'].includes(actionSource)) {
108
113
  $(".form-row-http-in-csv").hide();
109
114
  }
@@ -151,6 +156,7 @@
151
156
  options["Array"]="Array";
152
157
  break;
153
158
  case 'JSON':
159
+ $(".form-row-http-in-JSONataSource").show();
154
160
  options["Array"]="Array";
155
161
  options["AVRO"]="AVRO";
156
162
  options["Compressed"]="Compressed";
@@ -277,7 +283,7 @@
277
283
  $(".form-row-http-in-radix").hide();
278
284
  $(".form-row-http-in-schema").hide();
279
285
  $(".form-row-http-in-string").hide();
280
-
286
+ $(".form-row-http-in-JSONataTarget").hide();
281
287
  switch (actionTarget) {
282
288
  case 'AVRO':
283
289
  case 'Confluence':
@@ -317,8 +323,14 @@
317
323
  $(".form-row-http-in-max"+actionSource).show();
318
324
  $(".form-row-http-in-min"+actionSource).show();
319
325
  break;
326
+ case 'JSON':
327
+ $(".form-row-http-in-max"+actionSource).show();
328
+ $(".form-row-http-in-JSONataTarget").show();
329
+ break;
320
330
  }
321
331
  }).change();
332
+ setInput.apply(this,["JSONataSource",["jsonata"]]);
333
+ setInput.apply(this,["JSONataTarget",["jsonata"]]);
322
334
  setInput.apply(this,["deleteSource",["bool"]]);
323
335
  setInput.apply(this,["schema"]);
324
336
  setInput.apply(this,["string",["str","msg","flow","global","env","node"]]);
@@ -357,11 +369,21 @@
357
369
  <label for="node-input-actionSource"><i class="fa fa-list-ul"></i> Source Type </label>
358
370
  <input type="text" id="node-input-actionSource">
359
371
  </div>
372
+ <div class="form-row form-row-http-in-JSONataSource hide">
373
+ <label for="node-input-JSONataSource"><i class="fa fa-bookmark"></i> <span data-i18n="common.label.string"> JSONata Source</span></label>
374
+ <input type="text" id="node-input-JSONataSource" style="width:70%">
375
+ <input type="hidden" id="node-input-JSONataSourceType">
376
+ </div>
360
377
  <div class="form-row">
361
378
  <label for="node-input-actionTarget"><i class="fa fa-list-ul"></i> Target Type </label>
362
379
  <select id="node-input-actionTarget" placeholder="actionTarget">
363
380
  </select>
364
381
  </div>
382
+ <div class="form-row form-row-http-in-JSONataTarget hide">
383
+ <label for="node-input-JSONataTarget"><i class="fa fa-bookmark"></i> <span data-i18n="common.label.string"> JSONata Target</span></label>
384
+ <input type="text" id="node-input-JSONataTarget" style="width:70%">
385
+ <input type="hidden" id="node-input-JSONataTargetType">
386
+ </div>
365
387
  <div class="form-row form-row-http-in-delimiter hide">
366
388
  <label for="node-input-delimiter"><i class="icon-bookmark"></i> Delimiter</label>
367
389
  <input type="text" id="node-input-delimiter" placeholder="delimiter" size="1" minlength="1">
@@ -426,7 +448,6 @@
426
448
  <input type="text" id="node-input-string" style="width:70%">
427
449
  <input type="hidden" id="node-input-stringType">
428
450
  </div>
429
-
430
451
  </script>
431
452
 
432
453
  <script type="text/x-red" data-help-name="transform">