@steedos/service-package-registry 2.1.10 → 2.1.14

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.
@@ -0,0 +1,987 @@
1
+ /**
2
+ * Copyright JS Foundation and other contributors, http://js.foundation
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ * @ignore
16
+ **/
17
+
18
+ /**
19
+ * @mixin @node-red/util_util
20
+ */
21
+
22
+ const clonedeep = require("lodash.clonedeep");
23
+ const jsonata = require("jsonata");
24
+ const moment = require("moment-timezone");
25
+ const safeJSONStringify = require("json-stringify-safe");
26
+ const util = require("util");
27
+
28
+ /**
29
+ * Generates a psuedo-unique-random id.
30
+ * @return {String} a random-ish id
31
+ * @memberof @node-red/util_util
32
+ */
33
+ function generateId() {
34
+ var bytes = [];
35
+ for (var i=0;i<8;i++) {
36
+ bytes.push(Math.round(0xff*Math.random()).toString(16).padStart(2,'0'));
37
+ }
38
+ return bytes.join("");
39
+ }
40
+
41
+ /**
42
+ * Converts the provided argument to a String, using type-dependent
43
+ * methods.
44
+ *
45
+ * @param {any} o - the property to convert to a String
46
+ * @return {String} the stringified version
47
+ * @memberof @node-red/util_util
48
+ */
49
+ function ensureString(o) {
50
+ if (Buffer.isBuffer(o)) {
51
+ return o.toString();
52
+ } else if (typeof o === "object") {
53
+ return JSON.stringify(o);
54
+ } else if (typeof o === "string") {
55
+ return o;
56
+ }
57
+ return ""+o;
58
+ }
59
+
60
+ /**
61
+ * Converts the provided argument to a Buffer, using type-dependent
62
+ * methods.
63
+ *
64
+ * @param {any} o - the property to convert to a Buffer
65
+ * @return {String} the Buffer version
66
+ * @memberof @node-red/util_util
67
+ */
68
+ function ensureBuffer(o) {
69
+ if (Buffer.isBuffer(o)) {
70
+ return o;
71
+ } else if (typeof o === "object") {
72
+ o = JSON.stringify(o);
73
+ } else if (typeof o !== "string") {
74
+ o = ""+o;
75
+ }
76
+ return Buffer.from(o);
77
+ }
78
+
79
+ /**
80
+ * Safely clones a message object. This handles msg.req/msg.res objects that must
81
+ * not be cloned.
82
+ *
83
+ * @param {any} msg - the message object to clone
84
+ * @return {Object} the cloned message
85
+ * @memberof @node-red/util_util
86
+ */
87
+ function cloneMessage(msg) {
88
+ if (typeof msg !== "undefined" && msg !== null) {
89
+ // Temporary fix for #97
90
+ // TODO: remove this http-node-specific fix somehow
91
+ var req = msg.req;
92
+ var res = msg.res;
93
+ delete msg.req;
94
+ delete msg.res;
95
+ var m = clonedeep(msg);
96
+ if (req) {
97
+ m.req = req;
98
+ msg.req = req;
99
+ }
100
+ if (res) {
101
+ m.res = res;
102
+ msg.res = res;
103
+ }
104
+ return m;
105
+ }
106
+ return msg;
107
+ }
108
+
109
+ /**
110
+ * Compares two objects, handling various JavaScript types.
111
+ *
112
+ * @param {any} obj1
113
+ * @param {any} obj2
114
+ * @return {boolean} whether the two objects are the same
115
+ * @memberof @node-red/util_util
116
+ */
117
+ function compareObjects(obj1,obj2) {
118
+ var i;
119
+ if (obj1 === obj2) {
120
+ return true;
121
+ }
122
+ if (obj1 == null || obj2 == null) {
123
+ return false;
124
+ }
125
+
126
+ var isArray1 = Array.isArray(obj1);
127
+ var isArray2 = Array.isArray(obj2);
128
+ if (isArray1 != isArray2) {
129
+ return false;
130
+ }
131
+ if (isArray1 && isArray2) {
132
+ if (obj1.length !== obj2.length) {
133
+ return false;
134
+ }
135
+ for (i=0;i<obj1.length;i++) {
136
+ if (!compareObjects(obj1[i],obj2[i])) {
137
+ return false;
138
+ }
139
+ }
140
+ return true;
141
+ }
142
+ var isBuffer1 = Buffer.isBuffer(obj1);
143
+ var isBuffer2 = Buffer.isBuffer(obj2);
144
+ if (isBuffer1 != isBuffer2) {
145
+ return false;
146
+ }
147
+ if (isBuffer1 && isBuffer2) {
148
+ if (obj1.equals) {
149
+ // For node 0.12+ - use the native equals
150
+ return obj1.equals(obj2);
151
+ } else {
152
+ if (obj1.length !== obj2.length) {
153
+ return false;
154
+ }
155
+ for (i=0;i<obj1.length;i++) {
156
+ if (obj1.readUInt8(i) !== obj2.readUInt8(i)) {
157
+ return false;
158
+ }
159
+ }
160
+ return true;
161
+ }
162
+ }
163
+
164
+ if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
165
+ return false;
166
+ }
167
+ var keys1 = Object.keys(obj1);
168
+ var keys2 = Object.keys(obj2);
169
+ if (keys1.length != keys2.length) {
170
+ return false;
171
+ }
172
+ for (var k in obj1) {
173
+ /* istanbul ignore else */
174
+ if (obj1.hasOwnProperty(k)) {
175
+ if (!compareObjects(obj1[k],obj2[k])) {
176
+ return false;
177
+ }
178
+ }
179
+ }
180
+ return true;
181
+ }
182
+
183
+ function createError(code, message) {
184
+ var e = new Error(message);
185
+ e.code = code;
186
+ return e;
187
+ }
188
+
189
+ /**
190
+ * Parses a property expression, such as `msg.foo.bar[3]` to validate it
191
+ * and convert it to a canonical version expressed as an Array of property
192
+ * names.
193
+ *
194
+ * For example, `a["b"].c` returns `['a','b','c']`
195
+ *
196
+ * If `msg` is provided, any internal cross-references will be evaluated against that
197
+ * object. Otherwise, it will return a nested set of properties
198
+ *
199
+ * For example, without msg set, 'a[msg.foo]' returns `['a', [ 'msg', 'foo'] ]`
200
+ * But if msg is set to '{"foo": "bar"}', 'a[msg.foo]' returns `['a', 'bar' ]`
201
+ *
202
+ * @param {String} str - the property expression
203
+ * @return {Array} the normalised expression
204
+ * @memberof @node-red/util_util
205
+ */
206
+ function normalisePropertyExpression(str, msg, toString) {
207
+ // This must be kept in sync with validatePropertyExpression
208
+ // in editor/js/ui/utils.js
209
+
210
+ var length = str.length;
211
+ if (length === 0) {
212
+ throw createError("INVALID_EXPR","Invalid property expression: zero-length");
213
+ }
214
+ var parts = [];
215
+ var start = 0;
216
+ var inString = false;
217
+ var inBox = false;
218
+ var boxExpression = false;
219
+ var quoteChar;
220
+ var v;
221
+ for (var i=0;i<length;i++) {
222
+ var c = str[i];
223
+ if (!inString) {
224
+ if (c === "'" || c === '"') {
225
+ if (i != start) {
226
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
227
+ }
228
+ inString = true;
229
+ quoteChar = c;
230
+ start = i+1;
231
+ } else if (c === '.') {
232
+ if (i===0) {
233
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected . at position 0");
234
+ }
235
+ if (start != i) {
236
+ v = str.substring(start,i);
237
+ if (/^\d+$/.test(v)) {
238
+ parts.push(parseInt(v));
239
+ } else {
240
+ parts.push(v);
241
+ }
242
+ }
243
+ if (i===length-1) {
244
+ throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
245
+ }
246
+ // Next char is first char of an identifier: a-z 0-9 $ _
247
+ if (!/[a-z0-9\$\_]/i.test(str[i+1])) {
248
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
249
+ }
250
+ start = i+1;
251
+ } else if (c === '[') {
252
+ if (i === 0) {
253
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
254
+ }
255
+ if (start != i) {
256
+ parts.push(str.substring(start,i));
257
+ }
258
+ if (i===length-1) {
259
+ throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
260
+ }
261
+ // Start of a new expression. If it starts with msg it is a nested expression
262
+ // Need to scan ahead to find the closing bracket
263
+ if (/^msg[.\[]/.test(str.substring(i+1))) {
264
+ var depth = 1;
265
+ var inLocalString = false;
266
+ var localStringQuote;
267
+ for (var j=i+1;j<length;j++) {
268
+ if (/["']/.test(str[j])) {
269
+ if (inLocalString) {
270
+ if (str[j] === localStringQuote) {
271
+ inLocalString = false
272
+ }
273
+ } else {
274
+ inLocalString = true;
275
+ localStringQuote = str[j]
276
+ }
277
+ }
278
+ if (str[j] === '[') {
279
+ depth++;
280
+ } else if (str[j] === ']') {
281
+ depth--;
282
+ }
283
+ if (depth === 0) {
284
+ try {
285
+ if (msg) {
286
+ var crossRefProp = getMessageProperty(msg, str.substring(i+1,j));
287
+ if (crossRefProp === undefined) {
288
+ throw createError("INVALID_EXPR","Invalid expression: undefined reference at position "+(i+1)+" : "+str.substring(i+1,j))
289
+ }
290
+ parts.push(crossRefProp)
291
+ } else {
292
+ parts.push(normalisePropertyExpression(str.substring(i+1,j), msg));
293
+ }
294
+ inBox = false;
295
+ i = j;
296
+ start = j+1;
297
+ break;
298
+ } catch(err) {
299
+ throw createError("INVALID_EXPR","Invalid expression started at position "+(i+1))
300
+ }
301
+ }
302
+ }
303
+ if (depth > 0) {
304
+ throw createError("INVALID_EXPR","Invalid property expression: unmatched '[' at position "+i);
305
+ }
306
+ continue;
307
+ } else if (!/["'\d]/.test(str[i+1])) {
308
+ // Next char is either a quote or a number
309
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
310
+ }
311
+ start = i+1;
312
+ inBox = true;
313
+ } else if (c === ']') {
314
+ if (!inBox) {
315
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
316
+ }
317
+ if (start != i) {
318
+ v = str.substring(start,i);
319
+ if (/^\d+$/.test(v)) {
320
+ parts.push(parseInt(v));
321
+ } else {
322
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
323
+ }
324
+ }
325
+ start = i+1;
326
+ inBox = false;
327
+ } else if (c === ' ') {
328
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected ' ' at position "+i);
329
+ }
330
+ } else {
331
+ if (c === quoteChar) {
332
+ if (i-start === 0) {
333
+ throw createError("INVALID_EXPR","Invalid property expression: zero-length string at position "+start);
334
+ }
335
+ parts.push(str.substring(start,i));
336
+ // If inBox, next char must be a ]. Otherwise it may be [ or .
337
+ if (inBox && !/\]/.test(str[i+1])) {
338
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
339
+ } else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
340
+ throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
341
+ }
342
+ start = i+1;
343
+ inString = false;
344
+ }
345
+ }
346
+
347
+ }
348
+ if (inBox || inString) {
349
+ throw new createError("INVALID_EXPR","Invalid property expression: unterminated expression");
350
+ }
351
+ if (start < length) {
352
+ parts.push(str.substring(start));
353
+ }
354
+
355
+ if (toString) {
356
+ var result = parts.shift();
357
+ while(parts.length > 0) {
358
+ var p = parts.shift();
359
+ if (typeof p === 'string') {
360
+ if (/"/.test(p)) {
361
+ p = "'"+p+"'";
362
+ } else {
363
+ p = '"'+p+'"';
364
+ }
365
+ }
366
+ result = result+"["+p+"]";
367
+ }
368
+ return result;
369
+ }
370
+
371
+ return parts;
372
+ }
373
+
374
+ /**
375
+ * Gets a property of a message object.
376
+ *
377
+ * Unlike {@link @node-red/util-util.getObjectProperty}, this function will strip `msg.` from the
378
+ * front of the property expression if present.
379
+ *
380
+ * @param {Object} msg - the message object
381
+ * @param {String} expr - the property expression
382
+ * @return {any} the message property, or undefined if it does not exist
383
+ * @throws Will throw an error if the *parent* of the property does not exist
384
+ * @memberof @node-red/util_util
385
+ */
386
+ function getMessageProperty(msg,expr) {
387
+ if (expr.indexOf('msg.')===0) {
388
+ expr = expr.substring(4);
389
+ }
390
+ return getObjectProperty(msg,expr);
391
+ }
392
+
393
+ /**
394
+ * Gets a property of an object.
395
+ *
396
+ * Given the object:
397
+ *
398
+ * {
399
+ * "pet": {
400
+ * "type": "cat"
401
+ * }
402
+ * }
403
+ *
404
+ * - `pet.type` will return `"cat"`.
405
+ * - `pet.name` will return `undefined`
406
+ * - `car` will return `undefined`
407
+ * - `car.type` will throw an Error (as `car` does not exist)
408
+ *
409
+ * @param {Object} msg - the object
410
+ * @param {String} expr - the property expression
411
+ * @return {any} the object property, or undefined if it does not exist
412
+ * @throws Will throw an error if the *parent* of the property does not exist
413
+ * @memberof @node-red/util_util
414
+ */
415
+ function getObjectProperty(msg,expr) {
416
+ var result = null;
417
+ var msgPropParts = normalisePropertyExpression(expr,msg);
418
+ msgPropParts.reduce(function(obj, key) {
419
+ result = (typeof obj[key] !== "undefined" ? obj[key] : undefined);
420
+ return result;
421
+ }, msg);
422
+ return result;
423
+ }
424
+
425
+ /**
426
+ * Sets a property of a message object.
427
+ *
428
+ * Unlike {@link @node-red/util-util.setObjectProperty}, this function will strip `msg.` from the
429
+ * front of the property expression if present.
430
+ *
431
+ * @param {Object} msg - the message object
432
+ * @param {String} prop - the property expression
433
+ * @param {any} value - the value to set
434
+ * @param {boolean} createMissing - whether to create missing parent properties
435
+ * @memberof @node-red/util_util
436
+ */
437
+ function setMessageProperty(msg,prop,value,createMissing) {
438
+ if (prop.indexOf('msg.')===0) {
439
+ prop = prop.substring(4);
440
+ }
441
+ return setObjectProperty(msg,prop,value,createMissing);
442
+ }
443
+
444
+ /**
445
+ * Sets a property of an object.
446
+ *
447
+ * @param {Object} msg - the object
448
+ * @param {String} prop - the property expression
449
+ * @param {any} value - the value to set
450
+ * @param {boolean} createMissing - whether to create missing parent properties
451
+ * @memberof @node-red/util_util
452
+ */
453
+ function setObjectProperty(msg,prop,value,createMissing) {
454
+ if (typeof createMissing === 'undefined') {
455
+ createMissing = (typeof value !== 'undefined');
456
+ }
457
+ var msgPropParts = normalisePropertyExpression(prop, msg);
458
+ var depth = 0;
459
+ var length = msgPropParts.length;
460
+ var obj = msg;
461
+ var key;
462
+ for (var i=0;i<length-1;i++) {
463
+ key = msgPropParts[i];
464
+ if (typeof key === 'string' || (typeof key === 'number' && !Array.isArray(obj))) {
465
+ if (obj.hasOwnProperty(key)) {
466
+ if (length > 1 && ((typeof obj[key] !== "object" && typeof obj[key] !== "function") || obj[key] === null)) {
467
+ // Break out early as we cannot create a property beneath
468
+ // this type of value
469
+ return false;
470
+ }
471
+ obj = obj[key];
472
+ } else if (createMissing) {
473
+ if (typeof msgPropParts[i+1] === 'string') {
474
+ obj[key] = {};
475
+ } else {
476
+ obj[key] = [];
477
+ }
478
+ obj = obj[key];
479
+ } else {
480
+ return false;
481
+ }
482
+ } else if (typeof key === 'number') {
483
+ // obj is an array
484
+ if (obj[key] === undefined) {
485
+ if (createMissing) {
486
+ if (typeof msgPropParts[i+1] === 'string') {
487
+ obj[key] = {};
488
+ } else {
489
+ obj[key] = [];
490
+ }
491
+ obj = obj[key];
492
+ } else {
493
+ return false;
494
+ }
495
+ } else {
496
+ obj = obj[key];
497
+ }
498
+ }
499
+ }
500
+ key = msgPropParts[length-1];
501
+ if (typeof value === "undefined") {
502
+ if (typeof key === 'number' && Array.isArray(obj)) {
503
+ obj.splice(key,1);
504
+ } else {
505
+ delete obj[key]
506
+ }
507
+ } else {
508
+ if (typeof obj === "object" && obj !== null) {
509
+ obj[key] = value;
510
+ } else {
511
+ // Cannot set a property of a non-object/array
512
+ return false;
513
+ }
514
+ }
515
+ return true;
516
+ }
517
+
518
+ /*!
519
+ * Get value of environment variable.
520
+ * @param {Node} node - accessing node
521
+ * @param {String} name - name of variable
522
+ * @return {String} value of env var
523
+ */
524
+ function getSetting(node, name, flow_) {
525
+ var flow = (flow_ ? flow_ : (node ? node._flow : null));
526
+ if (flow) {
527
+ if (node && node.g) {
528
+ const group = flow.getGroupNode(node.g);
529
+ const result = flow.getGroupEnvSetting(node, group, name);
530
+ if (result) {
531
+ return result.val;
532
+ }
533
+ }
534
+ return flow.getSetting(name);
535
+ }
536
+ return process.env[name];
537
+ }
538
+
539
+
540
+ /**
541
+ * Checks if a String contains any Environment Variable specifiers and returns
542
+ * it with their values substituted in place.
543
+ *
544
+ * For example, if the env var `WHO` is set to `Joe`, the string `Hello ${WHO}!`
545
+ * will return `Hello Joe!`.
546
+ * @param {String} value - the string to parse
547
+ * @param {Node} node - the node evaluating the property
548
+ * @return {String} The parsed string
549
+ * @memberof @node-red/util_util
550
+ */
551
+ function evaluateEnvProperty(value, node) {
552
+ var flow = (node && node.hasOwnProperty("_flow")) ? node._flow : null;
553
+ var result;
554
+ if (/^\${[^}]+}$/.test(value)) {
555
+ // ${ENV_VAR}
556
+ var name = value.substring(2,value.length-1);
557
+ result = getSetting(node, name, flow);
558
+ } else if (!/\${\S+}/.test(value)) {
559
+ // ENV_VAR
560
+ result = getSetting(node, value, flow);
561
+ } else {
562
+ // FOO${ENV_VAR}BAR
563
+ return value.replace(/\${([^}]+)}/g, function(match, name) {
564
+ var val = getSetting(node, name, flow);
565
+ return (val === undefined)?"":val;
566
+ });
567
+ }
568
+ return (result === undefined)?"":result;
569
+
570
+ }
571
+
572
+
573
+ /**
574
+ * Parses a context property string, as generated by the TypedInput, to extract
575
+ * the store name if present.
576
+ *
577
+ * For example, `#:(file)::foo` results in ` { store: "file", key: "foo" }`.
578
+ *
579
+ * @param {String} key - the context property string to parse
580
+ * @return {Object} The parsed property
581
+ * @memberof @node-red/util_util
582
+ */
583
+ function parseContextStore(key) {
584
+ var parts = {};
585
+ var m = /^#:\((\S+?)\)::(.*)$/.exec(key);
586
+ if (m) {
587
+ parts.store = m[1];
588
+ parts.key = m[2];
589
+ } else {
590
+ parts.key = key;
591
+ }
592
+ return parts;
593
+ }
594
+
595
+
596
+ /**
597
+ * Evaluates a property value according to its type.
598
+ *
599
+ * @param {String} value - the raw value
600
+ * @param {String} type - the type of the value
601
+ * @param {Node} node - the node evaluating the property
602
+ * @param {Object} msg - the message object to evaluate against
603
+ * @param {Function} callback - (optional) called when the property is evaluated
604
+ * @return {any} The evaluted property, if no `callback` is provided
605
+ * @memberof @node-red/util_util
606
+ */
607
+ function evaluateNodeProperty(value, type, node, msg, callback) {
608
+ var result = value;
609
+ if (type === 'str') {
610
+ result = ""+value;
611
+ } else if (type === 'num') {
612
+ result = Number(value);
613
+ } else if (type === 'json') {
614
+ result = JSON.parse(value);
615
+ } else if (type === 're') {
616
+ result = new RegExp(value);
617
+ } else if (type === 'date') {
618
+ result = Date.now();
619
+ } else if (type === 'bin') {
620
+ var data = JSON.parse(value);
621
+ result = Buffer.from(data);
622
+ } else if (type === 'msg' && msg) {
623
+ try {
624
+ result = getMessageProperty(msg,value);
625
+ } catch(err) {
626
+ if (callback) {
627
+ callback(err);
628
+ } else {
629
+ throw err;
630
+ }
631
+ return;
632
+ }
633
+ } else if ((type === 'flow' || type === 'global') && node) {
634
+ var contextKey = parseContextStore(value);
635
+ if (/\[msg/.test(contextKey.key)) {
636
+ // The key has a nest msg. reference to evaluate first
637
+ contextKey.key = normalisePropertyExpression(contextKey.key, msg, true)
638
+ }
639
+ result = node.context()[type].get(contextKey.key,contextKey.store,callback);
640
+ if (callback) {
641
+ return;
642
+ }
643
+ } else if (type === 'bool') {
644
+ result = /^true$/i.test(value);
645
+ } else if (type === 'jsonata') {
646
+ var expr = prepareJSONataExpression(value,node);
647
+ result = evaluateJSONataExpression(expr,msg);
648
+ } else if (type === 'env') {
649
+ result = evaluateEnvProperty(value, node);
650
+ }
651
+ if (callback) {
652
+ callback(null,result);
653
+ } else {
654
+ return result;
655
+ }
656
+ }
657
+
658
+
659
+ /**
660
+ * Prepares a JSONata expression for evaluation.
661
+ * This attaches Node-RED specific functions to the expression.
662
+ *
663
+ * @param {String} value - the JSONata expression
664
+ * @param {Node} node - the node evaluating the property
665
+ * @return {Object} The JSONata expression that can be evaluated
666
+ * @memberof @node-red/util_util
667
+ */
668
+ function prepareJSONataExpression(value,node) {
669
+ var expr = jsonata(value);
670
+ expr.assign('flowContext', function(val, store) {
671
+ return node.context().flow.get(val, store);
672
+ });
673
+ expr.assign('globalContext', function(val, store) {
674
+ return node.context().global.get(val, store);
675
+ });
676
+ expr.assign('env', function(name) {
677
+ var val = getSetting(node, name, node._flow);
678
+ if (typeof val !== 'undefined') {
679
+ return val;
680
+ } else {
681
+ return "";
682
+ }
683
+ });
684
+ expr.assign('moment', function(arg1, arg2, arg3, arg4) {
685
+ return moment(arg1, arg2, arg3, arg4);
686
+ });
687
+ expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
688
+ expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
689
+ expr._node = node;
690
+ return expr;
691
+ }
692
+
693
+ /**
694
+ * Evaluates a JSONata expression.
695
+ * The expression must have been prepared with {@link @node-red/util-util.prepareJSONataExpression}
696
+ * before passing to this function.
697
+ *
698
+ * @param {Object} expr - the prepared JSONata expression
699
+ * @param {Object} msg - the message object to evaluate against
700
+ * @param {Function} callback - (optional) called when the expression is evaluated
701
+ * @return {any} If no callback was provided, the result of the expression
702
+ * @memberof @node-red/util_util
703
+ */
704
+ function evaluateJSONataExpression(expr,msg,callback) {
705
+ var context = msg;
706
+ if (expr._legacyMode) {
707
+ context = {msg:msg};
708
+ }
709
+ var bindings = {};
710
+
711
+ if (callback) {
712
+ // If callback provided, need to override the pre-assigned sync
713
+ // context functions to be their async variants
714
+ bindings.flowContext = function(val, store) {
715
+ return new Promise((resolve,reject) => {
716
+ expr._node.context().flow.get(val, store, function(err,value) {
717
+ if (err) {
718
+ reject(err);
719
+ } else {
720
+ resolve(value);
721
+ }
722
+ })
723
+ });
724
+ }
725
+ bindings.globalContext = function(val, store) {
726
+ return new Promise((resolve,reject) => {
727
+ expr._node.context().global.get(val, store, function(err,value) {
728
+ if (err) {
729
+ reject(err);
730
+ } else {
731
+ resolve(value);
732
+ }
733
+ })
734
+ });
735
+ }
736
+ }
737
+ return expr.evaluate(context, bindings, callback);
738
+ }
739
+
740
+ /**
741
+ * Normalise a node type name to camel case.
742
+ *
743
+ * For example: `a-random node type` will normalise to `aRandomNodeType`
744
+ *
745
+ * @param {String} name - the node type
746
+ * @return {String} The normalised name
747
+ * @memberof @node-red/util_util
748
+ */
749
+ function normaliseNodeTypeName(name) {
750
+ var result = name.replace(/[^a-zA-Z0-9]/g, " ");
751
+ result = result.trim();
752
+ result = result.replace(/ +/g, " ");
753
+ result = result.replace(/ ./g,
754
+ function(s) {
755
+ return s.charAt(1).toUpperCase();
756
+ }
757
+ );
758
+ result = result.charAt(0).toLowerCase() + result.slice(1);
759
+ return result;
760
+ }
761
+
762
+ /**
763
+ * Encode an object to JSON without losing information about non-JSON types
764
+ * such as Buffer and Function.
765
+ *
766
+ * *This function is closely tied to its reverse within the editor*
767
+ *
768
+ * @param {Object} msg
769
+ * @param {Object} opts
770
+ * @return {Object} the encoded object
771
+ * @memberof @node-red/util_util
772
+ */
773
+ function encodeObject(msg,opts) {
774
+ try {
775
+ var debuglength = 1000;
776
+ if (opts && opts.hasOwnProperty('maxLength')) {
777
+ debuglength = opts.maxLength;
778
+ }
779
+ var msgType = typeof msg.msg;
780
+ if (msg.msg instanceof Error) {
781
+ msg.format = "error";
782
+ var errorMsg = {};
783
+ if (msg.msg.name) {
784
+ errorMsg.name = msg.msg.name;
785
+ }
786
+ if (msg.msg.hasOwnProperty('message')) {
787
+ errorMsg.message = msg.msg.message;
788
+ } else {
789
+ errorMsg.message = msg.msg.toString();
790
+ }
791
+ msg.msg = JSON.stringify(errorMsg);
792
+ } else if (msg.msg instanceof Buffer) {
793
+ msg.format = "buffer["+msg.msg.length+"]";
794
+ msg.msg = msg.msg.toString('hex');
795
+ if (msg.msg.length > debuglength) {
796
+ msg.msg = msg.msg.substring(0,debuglength);
797
+ }
798
+ } else if (msg.msg && msgType === 'object') {
799
+ try {
800
+ msg.format = msg.msg.constructor.name || "Object";
801
+ // Handle special case of msg.req/res objects from HTTP In node
802
+ if (msg.format === "IncomingMessage" || msg.format === "ServerResponse") {
803
+ msg.format = "Object";
804
+ }
805
+ } catch(err) {
806
+ msg.format = "Object";
807
+ }
808
+ if (/error/i.test(msg.format)) {
809
+ msg.msg = JSON.stringify({
810
+ name: msg.msg.name,
811
+ message: msg.msg.message
812
+ });
813
+ } else {
814
+ var isArray = util.isArray(msg.msg);
815
+ var needsStringify = isArray;
816
+ if (isArray) {
817
+ msg.format = "array["+msg.msg.length+"]";
818
+ if (msg.msg.length > debuglength) {
819
+ // msg.msg = msg.msg.slice(0,debuglength);
820
+ msg.msg = {
821
+ __enc__: true,
822
+ type: "array",
823
+ data: msg.msg.slice(0,debuglength),
824
+ length: msg.msg.length
825
+ }
826
+ }
827
+ } else if (msg.msg && msg.msg.constructor.name === "Set") {
828
+ msg.format = "set["+msg.msg.size+"]";
829
+ msg.msg = {
830
+ __enc__: true,
831
+ type: "set",
832
+ data: Array.from(msg.msg).slice(0,debuglength),
833
+ length: msg.msg.size
834
+ }
835
+ needsStringify = true;
836
+ } else if (msg.msg && msg.msg.constructor.name === "Map") {
837
+ msg.format = "map";
838
+ msg.msg = {
839
+ __enc__: true,
840
+ type: "map",
841
+ data: Object.fromEntries(Array.from(msg.msg.entries()).slice(0,debuglength)),
842
+ length: msg.msg.size
843
+ }
844
+ needsStringify = true;
845
+ }
846
+ if (needsStringify || (msg.format === "Object")) {
847
+ msg.msg = safeJSONStringify(msg.msg, function(key, value) {
848
+ if (key === '_req' || key === '_res') {
849
+ value = {
850
+ __enc__: true,
851
+ type: "internal"
852
+ }
853
+ } else if (value instanceof Error) {
854
+ value = value.toString()
855
+ } else if (util.isArray(value) && value.length > debuglength) {
856
+ value = {
857
+ __enc__: true,
858
+ type: "array",
859
+ data: value.slice(0,debuglength),
860
+ length: value.length
861
+ }
862
+ } else if (typeof value === 'string') {
863
+ if (value.length > debuglength) {
864
+ value = value.substring(0,debuglength)+"...";
865
+ }
866
+ } else if (typeof value === 'function') {
867
+ value = {
868
+ __enc__: true,
869
+ type: "function"
870
+ }
871
+ } else if (typeof value === 'number') {
872
+ if (isNaN(value) || value === Infinity || value === -Infinity) {
873
+ value = {
874
+ __enc__: true,
875
+ type: "number",
876
+ data: value.toString()
877
+ }
878
+ }
879
+ } else if (typeof value === 'bigint') {
880
+ value = {
881
+ __enc__: true,
882
+ type: 'bigint',
883
+ data: value.toString()
884
+ }
885
+ } else if (value && value.constructor) {
886
+ if (value.type === "Buffer") {
887
+ value.__enc__ = true;
888
+ value.length = value.data.length;
889
+ if (value.length > debuglength) {
890
+ value.data = value.data.slice(0,debuglength);
891
+ }
892
+ } else if (value.constructor.name === "ServerResponse") {
893
+ value = "[internal]"
894
+ } else if (value.constructor.name === "Socket") {
895
+ value = "[internal]"
896
+ } else if (value.constructor.name === "Set") {
897
+ value = {
898
+ __enc__: true,
899
+ type: "set",
900
+ data: Array.from(value).slice(0,debuglength),
901
+ length: value.size
902
+ }
903
+ } else if (value.constructor.name === "Map") {
904
+ value = {
905
+ __enc__: true,
906
+ type: "map",
907
+ data: Object.fromEntries(Array.from(value.entries()).slice(0,debuglength)),
908
+ length: value.size
909
+ }
910
+ }
911
+ } else if (value === undefined) {
912
+ value = {
913
+ __enc__: true,
914
+ type: "undefined",
915
+ }
916
+ }
917
+ return value;
918
+ });
919
+ } else {
920
+ try { msg.msg = msg.msg.toString(); }
921
+ catch(e) { msg.msg = "[Type not printable]" + util.inspect(msg.msg); }
922
+ }
923
+ }
924
+ } else if (msgType === "function") {
925
+ msg.format = "function";
926
+ msg.msg = "[function]"
927
+ } else if (msgType === "boolean") {
928
+ msg.format = "boolean";
929
+ msg.msg = msg.msg.toString();
930
+ } else if (msgType === "number") {
931
+ msg.format = "number";
932
+ msg.msg = msg.msg.toString();
933
+ } else if (msgType === "bigint") {
934
+ msg.format = "bigint";
935
+ msg.msg = {
936
+ __enc__: true,
937
+ type: 'bigint',
938
+ data: msg.msg.toString()
939
+ };
940
+ } else if (msg.msg === null || msgType === "undefined") {
941
+ msg.format = (msg.msg === null)?"null":"undefined";
942
+ msg.msg = "(undefined)";
943
+ } else {
944
+ msg.format = "string["+msg.msg.length+"]";
945
+ if (msg.msg.length > debuglength) {
946
+ msg.msg = msg.msg.substring(0,debuglength)+"...";
947
+ }
948
+ }
949
+ return msg;
950
+ } catch(e) {
951
+ msg.format = "error";
952
+ var errorMsg = {};
953
+ if (e.name) {
954
+ errorMsg.name = e.name;
955
+ }
956
+ if (e.hasOwnProperty('message')) {
957
+ errorMsg.message = 'encodeObject Error: ['+e.message + '] Value: '+util.inspect(msg.msg);
958
+ } else {
959
+ errorMsg.message = 'encodeObject Error: ['+e.toString() + '] Value: '+util.inspect(msg.msg);
960
+ }
961
+ if (errorMsg.message.length > debuglength) {
962
+ errorMsg.message = errorMsg.message.substring(0,debuglength);
963
+ }
964
+ msg.msg = JSON.stringify(errorMsg);
965
+ return msg;
966
+ }
967
+ }
968
+
969
+ module.exports = {
970
+ encodeObject: encodeObject,
971
+ ensureString: ensureString,
972
+ ensureBuffer: ensureBuffer,
973
+ cloneMessage: cloneMessage,
974
+ compareObjects: compareObjects,
975
+ generateId: generateId,
976
+ getMessageProperty: getMessageProperty,
977
+ setMessageProperty: setMessageProperty,
978
+ getObjectProperty: getObjectProperty,
979
+ setObjectProperty: setObjectProperty,
980
+ evaluateNodeProperty: evaluateNodeProperty,
981
+ normalisePropertyExpression: normalisePropertyExpression,
982
+ normaliseNodeTypeName: normaliseNodeTypeName,
983
+ prepareJSONataExpression: prepareJSONataExpression,
984
+ evaluateJSONataExpression: evaluateJSONataExpression,
985
+ parseContextStore: parseContextStore,
986
+ getSetting: getSetting
987
+ };