node-red-contrib-prib-functions 0.22.0 → 0.23.3

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 (38) hide show
  1. package/README.md +142 -92
  2. package/lib/AlphaBeta.js +32 -0
  3. package/lib/GraphDB.js +40 -9
  4. package/lib/MinMax.js +17 -0
  5. package/lib/Tree.js +64 -0
  6. package/lib/common.js +128 -0
  7. package/lib/objectExtensions.js +213 -80
  8. package/lib/timeDimension.js +36 -0
  9. package/lib/typedInput.js +77 -0
  10. package/matrix/matrixNode.html +2 -1
  11. package/package.json +15 -13
  12. package/test/00-objectExtensions.js +192 -1
  13. package/test/02-graphdb.js +46 -0
  14. package/test/data/.config.nodes.json +3 -3
  15. package/test/data/.config.nodes.json.backup +3 -3
  16. package/test/data/.config.users.json +3 -2
  17. package/test/data/.config.users.json.backup +3 -2
  18. package/test/data/.flow.json.backup +3875 -472
  19. package/test/data/flow.json +3874 -471
  20. package/test/data/package-lock.json +11 -11
  21. package/test/data/shares/.config.nodes.json +589 -0
  22. package/test/data/shares/.config.runtime.json +4 -0
  23. package/test/data/shares/.config.runtime.json.backup +3 -0
  24. package/test/data/shares/.config.users.json +32 -0
  25. package/test/data/shares/.config.users.json.backup +29 -0
  26. package/test/data/shares/.flow.json.backup +230 -0
  27. package/test/data/shares/.flow_cred.json.backup +3 -0
  28. package/test/data/shares/flow.json +267 -0
  29. package/test/data/shares/flow_cred.json +3 -0
  30. package/test/data/shares/package.json +6 -0
  31. package/test/data/shares/settings.js +544 -0
  32. package/testing/test.js +63 -29
  33. package/transform/transform.html +185 -20
  34. package/transform/transform.js +272 -265
  35. package/transform/xlsx2.js +74 -0
  36. package/visual/shapes/base..js +1 -0
  37. package/visual/visual.js +0 -0
  38. package/visual/visualNode.js +45 -0
@@ -1,7 +1,8 @@
1
1
  const logger = new (require("node-red-contrib-logger"))("transform");
2
2
  logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
3
- const CompressionTool = require('compressiontool');
4
3
  const NumPy = require("./NumPy.js")
4
+ const typedInput = require("../lib/typedInput.js")
5
+ const {coalesce,nullif,toDateTypeZulu}=require("../lib/objectExtensions")
5
6
  const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/,
6
7
  Buffer=require('buffer').Buffer,
7
8
  os=require('os'),
@@ -9,6 +10,7 @@ const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/,
9
10
  process=require('process');
10
11
  let avsc,snappy,xmlParser,json2xmlParser,XLSX,compressor;
11
12
  const {ISO8583BitMapId,ISO8583BitMapName}=require("./ISO8583BitMap");
13
+ const { callbackify } = require("util");
12
14
  let ISO8583,ISO8583message;
13
15
  const XMLoptions = {
14
16
  // attributeNamePrefix : "@_",
@@ -28,81 +30,33 @@ const XMLoptions = {
28
30
  // stopNodes: ["parse-me-as-string"]
29
31
  trimValues: false
30
32
  };
31
- function error(node,ex,shortMessage){
33
+ const makeDateType=data=>(data instanceof Date? data:new Date(data))
34
+ const error=(node,ex,shortMessage)=>{
35
+ if(!ex) ex =Error("no exception/error")
32
36
  if(logger.active) logger.send({label:"transformNode catch",shortMessage:shortMessage,error:ex.message,stack:ex.stack});
33
37
  node.error(ex.message);
34
- node.status({fill:"red",shape:"ring",text:(shortMessage||ex.message).substr(0,50)});
38
+ node.status({fill:"red",shape:"ring",text:(shortMessage??ex.message??"unknown error").substr(0,50)});
35
39
  }
36
- function getAvroTransformer(node,schema) {
40
+ const getAvroTransformer=(node,schema)=>{
37
41
  try{
38
- return avsc.Type.forSchema(node.schemas[schema]);
42
+ return avsc.Type.forSchema(node.schemas[schema])
39
43
  } catch(ex){
40
- if(node.schemas.hasOwnProperty(schema)) throw ex;
41
- throw Error("schema not found for "+schema);
44
+ if(node.schemas.hasOwnProperty(schema)) throw ex
45
+ throw Error("schema not found for "+schema)
42
46
  }
43
47
  }
48
+ const xlsx2=require("./xlsx2")
44
49
 
45
- function addWorksheet2JSON(object,worksheet,workbook,options){
46
- object[worksheet]=XLSX.utils.sheet_to_json(workbook.Sheets[worksheet],options);
47
- if(options.header) object[worksheet].shift();
48
- if(logger.active) logger.send({label:"addWorksheet2JSON",object:object,worksheet:worksheet})
49
- return object;
50
+ const ConfluenceToJSON=(RED,node,msg,data,callback)=>{
51
+ if(!Buffer.isBuffer(data)) data=Buffer.from(data)
52
+ const magicByte=data.readUInt8()
53
+ if(magicByte!==0) throw Error("expected magic byte and not found, found "+magicByte)
54
+ if(data.length<5) throw Error("missing schema, data length "+data.length)
55
+ const schema=data.readInt32BE(1)
56
+ const avroTransformer=getAvroTransformer(node,schema)
57
+ callback(RED,node,msg,data,{schema:schema,data:avroTransformer.fromBuffer(data.subarray(5))})
50
58
  }
51
- function XLSXObjectToJSON(RED,node,msg,data){
52
- return data.SheetNames.reduce((a,worksheet)=>addWorksheet2JSON(a,worksheet,data),{})
53
- }
54
- function XLSXToArray(RED,node,msg,data){
55
- return XLSXObjectToArray(RED,node,msg,XLSXToXLSXObject(RED,node,msg,data));
56
- }
57
- function XLSXToJSON(RED,node,msg,data){
58
- return XLSXObjectToJSON(RED,node,msg,XLSXToXLSXObject(RED,node,msg,data));
59
- }
60
- function XLSXToXLSXObject(RED,node,msg,data){
61
- return XLSX.read(data, {raw:true,type: 'buffer' });
62
- }
63
- function XLSXObjectToArray(RED,node,msg,data){
64
- return data.SheetNames.reduce((a,worksheet)=>addWorksheet2JSON(a,worksheet,data,{header:1,raw:true}),{})
65
- }
66
- function JSONToXLSX(RED,node,msg,data){
67
- const workbook=JSONToXLSXObject(RED,node,msg,data);
68
- return XLSX.write(workbook, {bookType:"xlsx", type:'buffer'});
69
- }
70
- function JSONToXLSXObject(RED,node,msg,data){
71
- const workbook = XLSX.utils.book_new();
72
- for(const worksheet in data) {
73
- const ws=XLSX.utils.json_to_sheet(data[worksheet]);
74
- XLSX.utils.book_append_sheet(workbook, ws, worksheet);
75
- }
76
- return workbook;
77
- }
78
- function ArrayToXLSX(RED,node,msg,data){
79
- const workbook=ArrayToXLSXObject(RED,node,msg,data);
80
- return XLSX.write(workbook, {bookType:"xlsx", type:'buffer'});
81
- }
82
- function ArrayToXLSXObject(RED,node,msg,data){
83
- const workbook = XLSX.utils.book_new();
84
- if(Array.isArray(data)) {
85
- const ws=XLSX.utils.aoa_to_sheet(data);
86
- XLSX.utils.book_append_sheet(workbook, ws,"worksheet 1");
87
- return workbook;
88
- }
89
- for(const worksheet in data) {
90
- const ws=XLSX.utils.aoa_to_sheet(data[worksheet]);
91
- XLSX.utils.book_append_sheet(workbook, ws, worksheet);
92
- }
93
- return workbook;
94
- }
95
- function ConfluenceToJSON(RED,node,msg,data){
96
- if(!Buffer.isBuffer(data)) data=Buffer.from(data);
97
- const magicByte=data.readUInt8();
98
- if(magicByte!==0) throw Error("expected magic byte and not found, found "+magicByte);
99
- if(data.length<5) throw Error("missing schema, data length "+data.length);
100
- const schema=data.readInt32BE(1);
101
- const avroTransformer=getAvroTransformer(node,schema);
102
- return {schema:schema,data:avroTransformer.fromBuffer(data.subarray(5))};
103
- }
104
-
105
- function JSONToConfluence(RED,node,msg,data){
59
+ const JSONToConfluence=(RED,node,msg,data,callback)=>{
106
60
  if(!data.schema) throw Error("property schema not defined");
107
61
  if(!data.data) throw Error("property data not defined");
108
62
  const header=Buffer.alloc(5);
@@ -110,9 +64,8 @@ function JSONToConfluence(RED,node,msg,data){
110
64
  header.writeInt32BE(data.schema, 1);
111
65
  const transformer=getAvroTransformer(node,data.schema);
112
66
  const avro=transformer.toBuffer(data.data);
113
- return Buffer.concat([header,avro]);
67
+ callback(RED,node,msg,data,Buffer.concat([header,avro]))
114
68
  }
115
-
116
69
  function SendArray(RED,node,msg,array){
117
70
  if(logger.active) logger.send({label:"SendArray",size:array.length});
118
71
  this.index=0;
@@ -120,7 +73,6 @@ function SendArray(RED,node,msg,array){
120
73
  this.node=node;
121
74
  this.msg=msg;
122
75
  this.array=array;
123
- node.deleteSourceProperty(RED,node,msg);
124
76
  this.next();
125
77
  }
126
78
  SendArray.prototype.next=function() {
@@ -135,6 +87,7 @@ SendArray.prototype.next=function() {
135
87
  this.lastTouchTime=currentTime;
136
88
 
137
89
  let i=cpuUsedRatio>0.9 || memoryUsedRatio>0.9 || heapUsedRatio>0.99?1:100;
90
+ const newMsgs=[]
138
91
  while(--i) {
139
92
  if(this.index>=this.array.length) {
140
93
  delete this;
@@ -144,12 +97,13 @@ SendArray.prototype.next=function() {
144
97
  newMsg._msgid=newMsg._msgid+":"+index;
145
98
  this.node.setData(this.RED,this.node,newMsg,this.array[index],index)
146
99
  this.index++;
147
- this.node.send(newMsg);
100
+ newMsgs.push(newMsg);
148
101
  }
102
+ this.node.send([newMsgs]);
149
103
  const call=this.next.bind(this);
150
104
  this.timeoutID=setTimeout(call, 100);
151
105
  };
152
- function removeQuotes(data){
106
+ const removeQuotes=(data)=>{
153
107
  try{
154
108
  const d=data.trim();
155
109
  if(d.length>1 && d.startsWith('"') && d.endsWith('"')) return d.slice(1,-1);
@@ -159,7 +113,7 @@ function removeQuotes(data){
159
113
  return data;
160
114
  }
161
115
  }
162
- function csvLines(data,skipLeading=0,skipTrailing=0) {
116
+ const csvLines=(data,skipLeading=0,skipTrailing=0)=>{
163
117
  if(logger.active) logger.send({label:"csvLines",skipLeading:skipLeading,skipTrailing:skipTrailing});
164
118
  let lines=data.split(/[\r\n]+/g),skip=skipLeading;
165
119
  while(skip--) lines.shift();
@@ -167,11 +121,11 @@ function csvLines(data,skipLeading=0,skipTrailing=0) {
167
121
  while(skip--) lines.pop();
168
122
  return lines;
169
123
  }
170
- function array2tag(a,t,tf){
124
+ const array2tag=(a,t,tf)=>{
171
125
  const ts="<"+t+">",te="</"+t+">"
172
126
  return a.reduce((a,c)=>a+=ts+tf(c)+te,"");
173
127
  }
174
- function Array2csv(node,data){
128
+ const Array2csv=(node,data)=>{
175
129
  if(!(data instanceof Array)) return JSON.stringify(data);
176
130
  if(data.length==0) return;
177
131
  if(data[0] instanceof Array) {
@@ -198,11 +152,36 @@ function Array2csv(node,data){
198
152
  })
199
153
  return properties.join(node.delimiter)+"\n"+rows.join("\n");
200
154
  }
201
- return data .map(r=>JSON.stringify(r)).join("/n");
155
+ return data.map(r=>JSON.stringify(r)).join("/n");
202
156
  }
157
+ const JSON2Array=data=>{
158
+ if(data instanceof Object){
159
+ let a=[];
160
+ properties=Object.keys(data)
161
+ for(const p of properties) {
162
+ a.push([p,JSON2Array(data[p])]);
163
+ }
164
+ return a;
165
+ }
166
+ return [data]
167
+ }
168
+ const JSON2HTML=(data,level=0)=>{
169
+ if(Array.isArray(data)) {
170
+ return data.length?"<table><tr>"+data.map((r)=>JSON2HTML(r,++level)).join("</tr><tr>")+"</tr><table>":"";
171
+ }
172
+ if(data instanceof Object){
173
+ let a=[];
174
+ for(let p in data) {
175
+ a.push("<td style='vertical-align: top;'>"+escape(p)+":</td><td>"+functions.JSONToHTML(data[p],++level)+"</td>");
176
+ }
177
+ return "<table><tr>"+a.join("</tr><tr>")+"</tr><table>";
178
+ }
179
+ return escape(data);
180
+ }
181
+
203
182
  const functions={
204
- ArrayToCSV: (RED,node,msg,data)=>data.map(c=>JSON.stringify(c)).join("\n"),
205
- ArrayToHTML: (RED,node,msg,data)=>
183
+ ArrayToCSV: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.map(c=>JSON.stringify(c)).join("\n")),
184
+ ArrayToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,
206
185
  "<table>"+ array2tag(data,"tr",(c)=>(
207
186
  Array.isArray(c)?
208
187
  array2tag(c,"td",(cc)=>
@@ -212,96 +191,82 @@ const functions={
212
191
  ):
213
192
  "<![CDATA["+c+"]]>"
214
193
  )
215
- )+"</table>",
216
- ArrayToISO8385: (RED,node,msg,data)=>ISO8583message.packSync(data),
217
- ArrayToMessages: (RED,node,msg,data)=>{
194
+ )+"</table>"),
195
+ ArrayToISO8385: (RED,node,msg,data,callback)=>callback(RED,node,msg,ISO8583message.packSync(data)),
196
+ ArrayToMessages: (RED,node,msg,data,callback)=>{
218
197
  if(logger.active) logger.send({label:"ArrayToMessages",arraySize:data.length});
219
198
  if(data.length>node.maxMessages) throw Error("messages to be created "+data.length +"> max: "+node.maxMessages);
199
+ const newMsgs=[]
220
200
  data.map((row,i)=>{
221
201
  const newMsg=RED.util.cloneMessage(msg);
222
202
  newMsg._msgid=newMsg._msgid+":"+i;
223
203
  if(logger.active) logger.send({label:"ArrayToMessages",row:row,index:i});
224
204
  node.setData(RED,node,newMsg,row,i)
225
- node.send(newMsg);
205
+ newMsgs.push(newMsg);
226
206
  });
207
+ node.send([newMsgs])
227
208
  },
228
- ArrayToXLSX:ArrayToXLSX,
229
- ArrayToXLSXObject:ArrayToXLSXObject,
230
- AVROToJSON: (RED,node,msg,data)=>node.avroTransformer.fromBuffer(data), // = {kind: 'CAT', name: 'Albert'}
231
- BufferToCompressed: (RED,node,msg,data)=>compressor.compress(data,
232
- (compressed)=>{
233
- node.setData(RED,node,msg,compressed);
234
- node.send(msg);
235
- },
236
- (err)=>{
237
- error(node,Error(err));
238
- }
209
+ ArrayToXLSX:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.Array2XLSX(data)),
210
+ ArrayToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.Array2XLSXObject(data)),
211
+ AVROToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.avroTransformer.fromBuffer(data)), // = {kind: 'CAT', name: 'Albert'}
212
+ BigIntToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.rangeLimit(node.minBigIntTyped,node.maxBigIntTyped):node.minBigInt),
213
+ BufferToCompressed: (RED,node,msg,data,callback)=>compressor.compress(data,
214
+ compressed=>node.setData(RED,node,msg,compressed,callback),
215
+ err=>error(node,Error(err))
239
216
  ),
240
- CompressedToBuffer:(RED,node,msg,data)=>compressor.decompress(data,
241
- (uncompressed)=>{
242
- node.setData(RED,node,msg,uncompressed);
243
- node.send(msg);
244
- },
245
- (err)=>{
246
- error(node,Error(err));
247
- }
217
+ CompressedToBuffer:(RED,node,msg,data,callback)=>compressor.decompress(data,
218
+ uncompressed=>node.setData(RED,node,msg,uncompressed,callback),
219
+ err=>error(node,Error(err))
248
220
  ),
249
- CompressedToJSON:(RED,node,msg,data)=>compressor.decompress(data,
250
- (uncompressed)=>{
221
+ CompressedToJSON:(RED,node,msg,data,callback)=>compressor.decompress(data,
222
+ uncompressed=>{
251
223
  try{
252
- node.setData(RED,node,msg,JSON.parse(uncompressed));
253
- node.send(msg);
224
+ node.setData(RED,node,msg,JSON.parse(uncompresse,callback));
254
225
  } catch(ex){
255
226
  msg.error=ex.message
256
- node.setData(RED,node,msg,uncompressed);
227
+ node.setData(RED,node,msg,uncompressed,callback);
257
228
  }
258
229
  },
259
- (err)=>{
260
- error(node,Error(err));
261
- }
230
+ err=>error(node,Error(err))
262
231
  ),
263
- CompressedToString:(RED,node,msg,data)=>compressor.decompress(data,
264
- (uncompressed)=>{
232
+ CompressedToString:(RED,node,msg,data,callback)=>compressor.decompress(data,
233
+ uncompressed=>{
265
234
  try{
266
- node.setData(RED,node,msg,uncompressed.toString());
267
- node.send(msg);
235
+ node.setData(RED,node,msg,uncompressed.toString(),callback)
268
236
  } catch(ex){
269
237
  msg.error=ex.message;
270
- node.setData(RED,node,msg,uncompressed);
238
+ node.setData(RED,node,msg,uncompressed,callback)
271
239
  }
272
240
  },
273
- (err)=>{
274
- error(node,Error(err));
275
- }
241
+ err=>error(node,Error(err))
276
242
  ),
277
243
  ConfluenceToJSON: ConfluenceToJSON,
278
- CSVToArray: (RED,node,msg,data)=>{
244
+ CSVToArray: (RED,node,msg,data,callback)=>{
279
245
  let lines=csvLines(data,node.skipLeading,node.skipTrailing);
280
246
  lines.forEach((value, idx) => {
281
247
  lines[idx]=value.split(regexCSV).map((c)=>removeQuotes(c));
282
248
  });
283
- return lines;
249
+ callback(RED,node,msg,lines)
284
250
  },
285
- CSVToHTML: (RED,node,msg,data)=>
251
+ CSVToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,
286
252
  "<table>"+ array2tag(csvLines(data,node.skipLeading,node.skipTrailing),"tr",(line)=>
287
253
  array2tag(line.split(regexCSV),"td",(c)=>"<![CDATA["+removeQuotes(c)+"]]>")
288
- )+"</table>",
289
- CSVToMessages: (RED,node,msg,data)=>{
290
- functions.ArrayToMessages(RED,node,msg,csvLines(data,this.skipLeading,this.skipTrailing));
291
- },
292
- CSVWithHeaderToArray: (RED,node,msg,data)=>{
254
+ )+"</table>"),
255
+ CSVToMessages: (RED,node,msg,data,callback)=>functions.ArrayToMessages(RED,node,msg,csvLines(data,this.skipLeading,this.skipTrailing),callback),
256
+ CSVWithHeaderToArray: (RED,node,msg,data,callback)=>{
293
257
  let r=functions.CSVToArray(RED,node,msg,data);
294
258
  r.shift();
295
- return r;
259
+ rcallback(RED,node,msg,r)
296
260
  },
297
- CSVWithHeaderToHTML: (RED,node,msg,data)=>{
261
+ CSVWithHeaderToHTML: (RED,node,msg,data,callback)=>{
298
262
  let lines=csvLines(data,node.skipLeading,node.skipTrailing);
299
263
  const header=array2tag(lines.shift().split(regexCSV),"th",(c)=>"<![CDATA["+removeQuotes(c)+"]]>");
300
- return "<table><tr>"+header+"</tr>"+array2tag(lines,"tr",(line)=>
301
- array2tag(line.split(regexCSV),'td',(c)=>"<![CDATA["+removeQuotes(c)+"]]>")
302
- )+"</table>"
264
+ const result= "<table><tr>"+header+"</tr>"+array2tag(lines,"tr",(line)=>
265
+ array2tag(line.split(regexCSV),'td',(c)=>"<![CDATA["+removeQuotes(c)+"]]>")
266
+ )+"</table>"
267
+ callback(RED,node,msg,result)
303
268
  },
304
- CSVWithHeaderToJSON: (RED,node,msg,data)=>{
269
+ CSVWithHeaderToJSON: (RED,node,msg,data,callback)=>{
305
270
  let lines=csvLines(data,node.skipLeading,node.skipTrailing);
306
271
  let header=lines.shift().split(regexCSV);
307
272
  if(logger.active) logger.send({label:"CSVWithHeaderToJSON",header:header});
@@ -312,110 +277,129 @@ const functions={
312
277
  });
313
278
  lines[idx]=o;
314
279
  });
315
- return lines;
280
+ callback(RED,node,msg,lines)
316
281
  },
317
- ISO8385ToArray: (RED,node,msg,data)=>ISO8583message.unpackSync(data, data.length),
318
- ISO8385ToJSON: (RED,node,msg,data)=>{
282
+ DateToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).isBetween(node.minDateTyped,node.maxDateTyped)),
283
+ DateToISODate: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).toISOString().slice(0,10)),
284
+ DateToLocalDate: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).toLocaleDateString().slice(0,10)),
285
+ DateToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,(data? toDateTypeZulu(data).rangeLimit(node.minDateTyped,node.maxDateTyped):node.minDateTyped)),
286
+ ISO8385ToArray: (RED,node,msg,data,callback)=>callback(RED,node,msg,ISO8583message.unpackSync(data, data.length)),
287
+ ISO8385ToJSON: (RED,node,msg,data,callback)=>{
319
288
  let j={},d=ISO8583message.unpackSync(data, data.length);
320
289
  d.forEach((r)=>{
321
290
  j[ISO8583BitMapId(r[0]).name]=r[1];
322
291
  });
323
- return r;
292
+ callback(RED,node,msg,r)
324
293
  },
325
- JSONToArray: (RED,node,msg,data)=>{
326
- if(data instanceof Object){
327
- let a=[];
328
- for(let p in data) {
329
- a.push([p,functions.JSONToArray(RED,node,msg,data[p])]);
330
- }
331
- return a;
332
- }
333
- return data;
334
- },
335
- JSONToAVRO: (RED,node,msg,data)=>node.avroTransformer.toBuffer(data), // Encoded buffer.
336
- JSONToCompressed: (RED,node,msg,data)=>compressor.compress(JSON.stringify(data),
337
- (compressed)=>{
338
- node.setData(RED,node,msg,compressed);
339
- node.send(msg);
340
- },
341
- (err)=>{
342
- error(node,Error(err));
343
- }
294
+ JSONToArray: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON2Array(data)),
295
+ JSONToAVRO: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.avroTransformer.toBuffer(data)), // Encoded buffer.
296
+ JSONToCompressed: (RED,node,msg,data,callback)=>compressor.compress(JSON.stringify(data),
297
+ compressed=>node.setData(RED,node,msg,compressed.callback),
298
+ err=>error(node,Error(err))
344
299
  ),
345
300
  JSONToConfluence:JSONToConfluence,
346
- JSONToCSV: (RED,node,msg,data)=>Array2csv(node,data),
347
- JSONToHTML: (RED,node,msg,data,level=0)=>{
348
- if(Array.isArray(data)) {
349
- return data.length?"<table><tr>"+data.map((r)=>functions.JSONToHTML(RED,node,msg,r,++level)).join("</tr><tr>")+"</tr><table>":"";
350
- }
351
- if(data instanceof Object){
352
- let a=[];
353
- for(let p in data) {
354
- a.push("<td style='vertical-align: top;'>"+escape(p)+":</td><td>"+functions.JSONToHTML(RED,node,msg,data[p],++level)+"</td>");
355
- }
356
- return "<table><tr>"+a.join("</tr><tr>")+"</tr><table>";
357
- }
358
- return escape(data);
359
- },
360
- JSONToISO8385: (RED,node,msg,data)=>{
301
+ JSONToCSV: (RED,node,msg,data,callback)=>callback(RED,node,msg,Array2csv(node,data)),
302
+ JSONToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON2HTML(data)),
303
+ JSONToISO8385: (RED,node,msg,data,callback)=>{
361
304
  var d=[];
362
305
  Object.getOwnPropertyNames(data).forEach((v)=>d.push([ISO8583BitMapName[v].id,data[v]]));
363
306
  d.sort((a, b) => a[0] - b[0]);
364
- return ISO8583message.packSync(d);
307
+ callback(RED,node,msg, ISO8583message.packSync(d))
365
308
  },
366
- JSONToMessages: (RED,node,msg,data)=>{
309
+ JSONToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,data),
310
+ JSONToMessages: (RED,node,msg,data,callback)=>{
367
311
  if(logger.active) logger.send({label:"JSONToMessages",messages:data.length});
368
312
  if(Array.isArray(data)) {
369
313
  new node.SendArray(RED,node,msg,data);
370
- // functions.ArrayToMessages(RED,node,msg,data);
371
314
  } else {
372
315
  const newMsg=RED.util.cloneMessage(msg);
373
316
  newMsg._msgid=newMsg._msgid+":0";
374
- node.setData(RED,node,newMsg,data,0)
375
- node.send(newMsg);
317
+ node.setData(RED,node,newMsg,data,callback)
376
318
  }
377
319
  },
378
- JSONTonpy: (RED,node,msg,data)=>new NumPy(data).toNpyBuffer(),
379
- JSONToNumPyObject: (RED,node,msg,data)=>new NumPy(data),
380
- JSONToString: (RED,node,msg,data)=>JSON.stringify(data),
381
- JSONToXLSX:JSONToXLSX,
382
- JSONToXLSXObject:JSONToXLSXObject,
383
- JSONToXML: (RED,node,msg,data)=>json2xmlParser.parse(data),
384
- npyToJSON: (RED,node,msg,data)=>new NumPy(data).toSerializable(),
385
- npyToNumPyObject: (RED,node,msg,data)=>new NumPy(data),
386
- NumPyObjectToJSON: (RED,node,msg,data)=> data.toSerializable(),
387
- StringToCompressed: (RED,node,msg,data)=>compressor.compress(data,
388
- (compressed)=>{
389
- node.setData(RED,node,msg,compressed);
390
- node.send(msg);
391
- },
392
- (err)=>{
393
- error(node,Error(err));
394
- }
320
+ JSONTonpy: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data).toNpyBuffer()),
321
+ JSONToNumPyObject: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data)),
322
+ JSONToString: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON.stringify(data)),
323
+ JSONToXLSX:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.JSON2XLSX(data)),
324
+ JSONToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.JSON2XLSXObject(data)),
325
+ JSONToXML: (RED,node,msg,data,callback)=>callback(RED,node,msg,json2xmlParser.parse(data)),
326
+ npyToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data).toSerializable()),
327
+ npyToNumPyObject: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data)),
328
+ NumPyObjectToJSON: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toSerializable()),
329
+ NumberToAbbreviated: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.isAbbreviated():data),
330
+ NumberToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.isBetween(node.minNumber,node.maxNumber)),
331
+ NumberToRangeLimit: (RED,node,msg,data)=> callback(RED,node,msg,data?data.rangeLimit(node.minNumber,node.maxNumber):node.minNumber),
332
+ ObjectToCoalesce: (RED,node,msg,data,callback)=>callback(RED,node,msg,coalesce(data,node.value)),
333
+ ObjectToDeepClone: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.deepClone()),
334
+ ObjectToNullif: (RED,node,msg,data,callback)=>callback(RED,node,msg,nullif(data,node.value)),
335
+ StringToAppend: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.concat(node.getString(msg))),
336
+ StringToArrayByDelimiter: (RED,node,msg,data,callback)=>callback(RED,node,msg,data
337
+ .split(node.delimiter??',')
338
+ .map(entry => entry.trim())
339
+ .filter(entry => entry)),
340
+ StringToAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.At(node.index)),
341
+ StringToCapitalize: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.capitalize()),
342
+ StringToCamelize: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase())),
343
+ StringToCharAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.charAt(node.index)),
344
+ StringToCharCodeAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.charCodeAt(node.index)),
345
+ StringToCodePointAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.codePointAt(node.index)),
346
+ StringToCompressed: (RED,node,msg,data,callback)=>compressor.compress(data,
347
+ compressed=>node.setData(RED,node,msg,compressed,callback),
348
+ err=>error(node,Error(err))
349
+ ),
350
+ StringToConcat: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.concat(node.getString(msg))),
351
+ StringToDate: (RED,node,msg,data,callback)=>callback(RED,node,msg,toDateTypeZulu(data)),
352
+ StringToDateLocal: (RED,node,msg,data,callback)=>callback(RED,node,msg,new Date(data)),
353
+ StringToTimestamp: (RED,node,msg,data,callback)=>callback(RED,node,msg,Date.parse(data)),
354
+ StringToDelimiterOnCase:(RED,node,msg,data,callback)=>callback(RED,node,msg,
355
+ data.replace(/[A-Z]/g, (letter, index) => {
356
+ const lcLet = letter.toLowerCase();
357
+ const separator= node.delimiter??"-"
358
+ return index ? separator + lcLet : lcLet;
359
+ })
360
+ .replace(/([-_ ]){1,}/g,node.delimiter??"-")
395
361
  ),
396
- StringToJSON: (RED,node,msg,data)=>JSON.parse(data),
397
- pathToBasename: (RED,node,msg,data)=>path.basename(data),
398
- pathToDirname: (RED,node,msg,data)=>path.dirname(data),
399
- pathToExtname: (RED,node,msg,data)=>path.extname(data),
400
- pathToFormat: (RED,node,msg,data)=>path.format(data),
401
- pathToIsAbsolute: (RED,node,msg,data)=>path.isAbsolute(data),
402
- pathToJoin: (RED,node,msg,...data)=>path.join(...data),
403
- pathToParse: (RED,node,msg,data)=>path.parse(data),
404
- pathToNormalize: (RED,node,msg,data)=>path.normalize(data),
405
- pathToResolve: (RED,node,msg,data)=>path.resolve(data),
406
- snappyToCompress: (RED,node,msg,data)=>{
362
+ StringToDeunderscore: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.deunderscore()),
363
+ StringToDeunderscoreCapitilize: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.deunderscoreCapitilize()),
364
+ StringToDropSquareBracketPrefix: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.dropSquareBracketPrefix()),
365
+ StringToEndsWith: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.endsWith(node.getString(msg))),
366
+ StringToFloat: (RED,node,msg,data,callback)=>callback(RED,node,msg,parseFloat(data)),
367
+ StringToGetWord: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.getWord(node.index)),
368
+ StringToInteger: (RED,node,msg,data,callback)=>callback(RED,node,msg,parseInt(data, node.radix??10)),
369
+ StringToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.isBetween(node.minString,node.maxString)),
370
+ StringToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON.parse(data)),
371
+ StringToLowerCase: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toLowerCase()),
372
+ StringToNumber: (RED,node,msg,data,callback)=>callback(RED,node,msg,Number(data)),
373
+ StringToPrepend: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.getString(msg).concat(data)),
374
+ StringToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.rangeLimit(node.minString,node.maxString):node.minString),
375
+ StringToReal: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toReal()),
376
+ StringToSplit: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.split(node.getString(msg))),
377
+ StringToStartsWith: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.startsWith(node.getString(msg))),
378
+ StringToTitle: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toTitle()),
379
+ StringTotTitleGrammatical: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toTitleGrammatical()),
380
+ StringToTrim: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trim()),
381
+ StringToTrimEnd: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trimEnd()),
382
+ StringToTrimStart: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trimStart()),
383
+ StringToUpperCase: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toUpperCase()),
384
+ StringToXmlStringEncode: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.xmlStringEncode()),
385
+ pathToBasename: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.basename(data)),
386
+ pathToDirname: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.dirname(data)),
387
+ pathToExtname: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.extname(data)),
388
+ pathToFormat: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.format(data)),
389
+ pathToIsAbsolute: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.isAbsolute(data)),
390
+ pathToJoin: (RED,node,msg,data)=>callback(RED,node,msg,path.join(data)),
391
+ pathToParse: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.parse(data)),
392
+ pathToNormalize: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.normalize(data)),
393
+ pathToResolve: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.resolve(data)),
394
+ snappyToCompress: (RED,node,msg,data,callback)=>{
407
395
  if(logger.active) logger.send({label:"snappyToCompress"});
408
396
  snappy.compress(data, (err, data)=>{
409
397
  if(logger.active) logger.send({label:"snappy.compress",error:err})
410
- if(err) {
411
- error(node,Error(err));
412
- return;
413
- }
414
- node.setData(RED,node,msg,data)
415
- node.send(msg);
398
+ if(err) return error(node,Error(err))
399
+ node.setData(RED,node,msg,data,callback)
416
400
  })
417
401
  },
418
- snappyToUncompress: (RED,node,msg,data)=>{
402
+ snappyToUncompress: (RED,node,msg,data,callback)=>{
419
403
  if(logger.active) logger.send({label:"snappyToUncompress"});
420
404
  snappy.uncompress(data, { asBuffer: false }, (err, data)=>{
421
405
  if(logger.active) logger.send({label:"snappy.uncompress",error:err});
@@ -423,16 +407,15 @@ const functions={
423
407
  error(node,Error(err));
424
408
  return;
425
409
  }
426
- node.setData(RED,node,msg,data)
427
- node.send(msg);
410
+ node.setData(RED,node,msg,data,callback)
428
411
  })
429
412
  },
430
- XLSXToArray:XLSXToArray,
431
- XLSXObjectToArray:XLSXObjectToArray,
432
- XLSXToJSON:XLSXToJSON,
433
- XLSXObjectToJSON:XLSXObjectToJSON,
434
- XLSXToXLSXObject:XLSXToXLSXObject,
435
- XMLToJSON: (RED,node,msg,data)=>xmlParser.parse(data,XMLoptions,true),
413
+ XLSXToArray:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2Array(data)),
414
+ XLSXObjectToArray:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSXObject2Array(data)),
415
+ XLSXToJSON:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2JSON(data)),
416
+ XLSXObjectToJSON:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSXObject2JSON(data)),
417
+ XLSXToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2XLSXObject(data)),
418
+ XMLToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,xmlParser.parse(data,XMLoptions,true)),
436
419
  invalidArray:(v=>!Array.isArray(v))
437
420
  };
438
421
 
@@ -443,26 +426,10 @@ function evalFunction(id,mapping){
443
426
  throw Error(id+" "+ex.message);
444
427
  }
445
428
  }
446
- function is(node,value){
429
+ const is=(node,value)=>{
447
430
  return node.actionSource==value||node.actionTarget==value;
448
431
  }
449
432
  let jsonata;
450
- function JSONataTransform(data,ok,error){
451
- /*
452
- (async () => {
453
- return result = await expression.evaluate(data);
454
- })()
455
- */
456
-
457
- this.transformFuncionCompiled.evalFunction(data,{},(error, result) => {
458
- if(error) {
459
- console.error(error);
460
- return;
461
- }
462
- console.log("Finished with", result);
463
- });
464
- }
465
-
466
433
  module.exports = function (RED) {
467
434
  function transformNode(n) {
468
435
  RED.nodes.createNode(this,n);
@@ -489,41 +456,50 @@ module.exports = function (RED) {
489
456
  }
490
457
  if(logger.active) logger.send({label:"load xml",xmlParserKeys:Object.keys(xmlParser),json2xmlParser:Object.keys(json2xmlParser)});
491
458
  }
492
- node.sendInFunction=["snappy","Compressed"].includes(node.actionSource)||["Messages","Compressed"].includes(node.actionTarget);
493
- node.hasNewTopic=![null,""].includes(node.topicProperty);
494
- const sourceMap="(RED,node,msg)=>"+(node.sourceProperty||"msg.payload"),
495
- sourceDelete="(RED,node,msg)=>{delete "+(node.sourceProperty||"msg.payload")+";}",
496
- targetMap="(RED,node,msg,data,index)=>{"+(node.targetProperty||"msg.payload")+"=data;"+
497
- (node.sendInFunction && node.hasNewTopic? "msg.topic=node.topicFunction(RED,node,msg,data,index);":"")+
498
- (node.sendInFunction ? "" : "node.send(msg);" )+
459
+ if(['Append','Concat','EndsWith','Prepend','Split','StartsWith'].includes(node.actionTarget)) {
460
+ typedInput.setGetFunction(RED,node,"string")
461
+ }
462
+ const source=(node.sourceProperty||"msg.payload")
463
+ const target=(node.targetProperty||"msg.payload")
464
+ const sourceMap="(RED,node,msg)=>"+source,
465
+ deleteSource=typedInput.getValue(RED,node,"deleteSource",true)
466
+ targetMap="(RED,node,msg,data,callback)=>{"+target+"=data;"+
467
+ (node.topicProperty? "msg.topic=node.topicFunction(RED,node,msg,data);":"")+
468
+ (deleteSource&&source!==target?"delete "+source+";"+"delete "+source+";":"")+
499
469
  "}",
500
470
  topicMap="(RED,node,msg,data,index)=>"+(node.topicProperty||"msg.topic");
501
- logger.sendInfo({label:"mappings",source:sourceMap,target:targetMap,topicMap:topicMap});
502
- node.getData=evalFunction("source",sourceMap);
503
- node.deleteSourceProperty=evalFunction("source delete",sourceDelete);
504
- node.setData=evalFunction("target",targetMap);
471
+ logger.sendInfo({label:"mappings",source:sourceMap,deleteSource:deleteSource,target:targetMap,topicMap:topicMap});
472
+ const getData=evalFunction("source",sourceMap);
473
+ node.getData=(RED,node,msg,callback)=>callback(RED,node,msg,getData(RED,node,msg))
474
+ const setData1=evalFunction("target",targetMap);
475
+ node.setData=(RED,node,msg,data,callback)=>{
476
+ setData1(RED,node,msg,data)
477
+ callback(RED,node,msg,data)
478
+ }
505
479
  node.topicFunction=evalFunction("topic",topicMap);
506
480
  if(is(node,"AVRO")) {
507
481
  node.avroTransformer=avsc.Type.forSchema(node.schemaValid);
508
- } else if(is(node,"Compressed")) {
509
- compressor=new CompressionTool();
510
- compressor[node.compressionType]();
511
- } else if(is(node,"Confluence")) {
482
+ } else if(is(node,"Compressed")) {
483
+ if(compressor==null) {
484
+ const CompressionTool = require('compressiontool')
485
+ compressor=new CompressionTool();
486
+ compressor[node.compressionType]();
487
+ }
488
+ } else if(is(node,"Confluence")) {
512
489
  node.schemas={};
513
490
  for(const schema in node.schemaValid )
514
491
  node.schemas[schema]=avsc.Type.forSchema(node.schemaValid[schema]);
515
492
  logger.info({label:"confluence",schemas:Object.keys(node.schemas)});
516
- }
517
- if(node.actionTarget=="Compressed"){
518
- compressor=new CompressionTool();
519
- compressor[node.compressionType]();
493
+ } else if(node.actionSource=="Date") {
494
+ if(node.maxDate) node.maxDateTyped=toDateTypeZulu(node.maxDate)
495
+ if(node.minDate) node.minDateTyped=toDateTypeZulu(node.minDate)
520
496
  }
521
497
  } catch(ex) {
522
498
  logger.error(n);
523
499
  error(node,ex,"Invalid setup "+ex.message);
524
500
  return;
525
501
  }
526
- if(node.actionSource=="ISO8583" || node.actionTarget=="ISO8583") {
502
+ if(is(node,"ISO8583")) {
527
503
  if(!ISO8583) {
528
504
  try{
529
505
  ISO8583=require('iso-8583');
@@ -535,16 +511,42 @@ module.exports = function (RED) {
535
511
  }
536
512
  }
537
513
  }
538
- if(node.transformFuncion && ( node.actionSource=="JSON" || node.actionTarget=="JSON" )) {
514
+ if(node.actionSource=="JSON" && node.JSONataSource){
539
515
  try{
540
516
  if(!jsonata) jsonata=require('jsonata')
541
- node.transformFuncionCompiled = jsonata(node.transformFuncion);
517
+ node.JSONataSourceExpression = jsonata(node.JSONataSource)
518
+ node.getData1=node.getData
519
+ node.getData=(RED,node,msg,callback)=>
520
+ node.getData1(RED,node,msg,(RED,node,msg,data)=>
521
+ node.JSONataSourceExpression.evaluate(data,
522
+ {msg:msg,RED:RED,node:node},
523
+ (err,response)=>err?error(node,Error(err),err):callback(RED,node,msg,response))
524
+ )
542
525
  } catch (ex) {
543
- error(node,ex,"Transform function error");
544
- return;
526
+ console.log("JSONata source:",node.JSONataSource)
527
+ error(node,ex,"JSONata source function error")
528
+ return
529
+ }
530
+ }
531
+ if(node.actionTarget=="JSON"&& node.JSONataTarget){
532
+ try{
533
+ if(!jsonata) jsonata=require('jsonata')
534
+ node.JSONataTargetExpression = jsonata(node.JSONataTarget)
535
+ node.setData1=node.setData
536
+ node.setData=(RED,node,msg,data,callback)=>{
537
+ node.JSONataTargetExpression.evaluate(data,{msg:msg,RED:RED,node:node},
538
+ (err,data)=>{
539
+ if(err) error(node,ex,"JSONata target evaluate error")
540
+ node.setData1(RED,node,msg,data,callback)
541
+ }
542
+ )
543
+ }
544
+ } catch (ex) {
545
+ console.log("JSONata Target:",node.JSONataTarget)
546
+ error(node,ex,"JSONata target function error")
547
+ return
545
548
  }
546
549
  }
547
-
548
550
  const typeValidate="invalid"+node.actionSource;
549
551
  node.invalidSourceType=typeValidate in functions &! ["XLSX","XLSXObject"].includes(node.actionTarget)?functions[typeValidate]:(()=>false);
550
552
  try {
@@ -554,17 +556,22 @@ module.exports = function (RED) {
554
556
  error(node,ex,node.actionSource+"\nto "+node.actionTarget + " not implemented")
555
557
  return;
556
558
  }
557
- node.processMsg=node.sendInFunction?this.transform
558
- :(RED,node,msg,data)=>node.setData(RED,node,msg,node.transform(RED,node,msg,data));
559
+ // node.processMsg=node.sendInFunction?this.transform
560
+ // :(RED,node,msg,data,callback)=>node.setData(RED,node,msg,node.transform(RED,node,msg,data),callback);
559
561
  this.status({fill:"green",shape:"ring",text:"Ready"});
560
562
  node.on("input", function(msg) {
561
563
  if(logger.active) logger.send({label:"input",msgid:msg._msgid,topic:msg.topic});
562
564
  try{
563
- const data=node.getData(RED,node,msg);
564
- if(node.invalidSourceType(data)) throw Error("expected source data type "+node.actionSource);
565
- node.processMsg(RED,node,msg,data);
565
+ node.getData(RED,node,msg,(RED,node,msg,data)=>{
566
+ if(node.invalidSourceType(data)) {
567
+ msg.error=node.actionSource+" to "+node.actionTarget + " expected source data type "+node.actionSource;
568
+ error(node,Error(msg.error),"Error(s)");
569
+ node.send([null,msg]);
570
+ }
571
+ node.transform(RED,node,msg,data,(RED,node,msg,dataTransformed)=>node.setData(RED,node,msg,dataTransformed,()=>node.send([msg]) ))
572
+ })
566
573
  } catch (ex) {
567
- if(logger.active) logger.sendErrorAndDump("on input error",ex)
574
+ logger.sendErrorAndDump("on input error",ex)
568
575
  msg.error=node.actionSource+" to "+node.actionTarget + " " +ex.message;
569
576
  error(node,Error(msg.error),"Error(s)");
570
577
  node.send([null,msg]);