@inteli.city/node-red-contrib-exec-collection 2.0.0 → 2.0.2
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.
- package/exec.queue.html +6 -2
- package/exec.queue.js +54 -2
- package/node.queue.js +52 -2
- package/package.json +1 -1
- package/python.queue.js +52 -2
package/exec.queue.html
CHANGED
|
@@ -319,9 +319,11 @@ valgrind bash $file
|
|
|
319
319
|
$("#node-input-cmdTemplate").prop("checked",false);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
var initFormat = this.format || "javascript";
|
|
323
|
+
if (initFormat === "nunjucks") initFormat = "twig";
|
|
322
324
|
this.editor = RED.editor.createEditor({
|
|
323
325
|
id: 'node-input-template-editor',
|
|
324
|
-
mode: 'ace/mode/
|
|
326
|
+
mode: 'ace/mode/' + initFormat,
|
|
325
327
|
value: $("#node-input-template").val()
|
|
326
328
|
});
|
|
327
329
|
|
|
@@ -335,7 +337,9 @@ valgrind bash $file
|
|
|
335
337
|
this.editor.focus();
|
|
336
338
|
|
|
337
339
|
$("#node-input-format").on("change", function() {
|
|
338
|
-
var
|
|
340
|
+
var format = $("#node-input-format").val();
|
|
341
|
+
if (format === "nunjucks") format = "twig";
|
|
342
|
+
var mod = "ace/mode/" + format;
|
|
339
343
|
that.editor.getSession().setMode({
|
|
340
344
|
path: mod,
|
|
341
345
|
v: Date.now()
|
package/exec.queue.js
CHANGED
|
@@ -32,7 +32,59 @@ const Queue = require('queue');
|
|
|
32
32
|
const nunjucks = require('nunjucks');
|
|
33
33
|
const yaml = require('js-yaml');
|
|
34
34
|
const convertXML = require('xml-js');
|
|
35
|
-
|
|
35
|
+
|
|
36
|
+
// ── LOCAL TEMPLATE CONTEXT ────────────────────────────────────────────────────
|
|
37
|
+
// Raw context: objects are NOT pre-stringified. Instead, each value is wrapped
|
|
38
|
+
// in a Proxy that serialises itself only when Nunjucks coerces it to a string
|
|
39
|
+
// (i.e. at output time). This means:
|
|
40
|
+
// {{ payload }} → JSON.stringify(payload) (calls toString / toPrimitive)
|
|
41
|
+
// {{ payload.a }} → the value of a (string/number as-is, object → JSON)
|
|
42
|
+
// {{ payload.a.b }} → the value of b
|
|
43
|
+
// No "[object Object]" is ever produced.
|
|
44
|
+
|
|
45
|
+
const _SKIP_KEYS = ['req', 'res', '_req', '_res'];
|
|
46
|
+
|
|
47
|
+
function _safeStringify(obj) {
|
|
48
|
+
const seen = new WeakSet();
|
|
49
|
+
try {
|
|
50
|
+
return JSON.stringify(obj, (key, value) => {
|
|
51
|
+
if (typeof value === 'object' && value !== null) {
|
|
52
|
+
if (seen.has(value)) return '[Circular]';
|
|
53
|
+
seen.add(value);
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
});
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return '[Unserializable]';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _wrapValue(value) {
|
|
63
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
64
|
+
return new Proxy(value, {
|
|
65
|
+
get(target, prop) {
|
|
66
|
+
// String coercion paths used by Nunjucks when outputting a variable
|
|
67
|
+
if (prop === Symbol.toPrimitive) return () => _safeStringify(target);
|
|
68
|
+
if (prop === 'toString') return () => _safeStringify(target);
|
|
69
|
+
const result = Reflect.get(target, prop);
|
|
70
|
+
// Recursively wrap nested objects so their toString also serialises
|
|
71
|
+
if (typeof result === 'object' && result !== null) return _wrapValue(result);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function buildRawContext(msg, node) {
|
|
78
|
+
const ctx = {};
|
|
79
|
+
for (const k of Object.keys(msg || {})) {
|
|
80
|
+
if (_SKIP_KEYS.includes(k)) continue;
|
|
81
|
+
ctx[k] = _wrapValue(msg[k]);
|
|
82
|
+
}
|
|
83
|
+
ctx.flow = { get: (key) => node.context().flow.get(key) };
|
|
84
|
+
ctx.global = { get: (key) => node.context().global.get(key) };
|
|
85
|
+
ctx.env = process.env;
|
|
86
|
+
return ctx;
|
|
87
|
+
}
|
|
36
88
|
|
|
37
89
|
// ── SECTION 1: TEMPLATE RESOLUTION ───────────────────────────────────────────
|
|
38
90
|
|
|
@@ -354,7 +406,7 @@ function updateStatus(node) {
|
|
|
354
406
|
// 4. Build shellcode and execute via exec or spawn.
|
|
355
407
|
// 5. Clean up all temp files created during this execution (finally).
|
|
356
408
|
async function processMessage({ msg, node, send, done, RED, template }) {
|
|
357
|
-
const context =
|
|
409
|
+
const context = buildRawContext(msg, node);
|
|
358
410
|
|
|
359
411
|
// Per-execution temp file list — isolated from other concurrent executions.
|
|
360
412
|
const executionTempFiles = [];
|
package/node.queue.js
CHANGED
|
@@ -6,7 +6,57 @@ const nunjucks = require('nunjucks');
|
|
|
6
6
|
const { spawn } = require('child_process');
|
|
7
7
|
const yaml = require('js-yaml');
|
|
8
8
|
const convertXML = require('xml-js');
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// ── LOCAL TEMPLATE CONTEXT ────────────────────────────────────────────────────
|
|
11
|
+
// Raw context: objects are NOT pre-stringified. Instead, each value is wrapped
|
|
12
|
+
// in a Proxy that serialises itself only when Nunjucks coerces it to a string
|
|
13
|
+
// (i.e. at output time). This means:
|
|
14
|
+
// {{ payload }} → JSON.stringify(payload) (calls toString / toPrimitive)
|
|
15
|
+
// {{ payload.a }} → the value of a (string/number as-is, object → JSON)
|
|
16
|
+
// {{ payload.a.b }} → the value of b
|
|
17
|
+
// No "[object Object]" is ever produced.
|
|
18
|
+
|
|
19
|
+
const _SKIP_KEYS = ['req', 'res', '_req', '_res'];
|
|
20
|
+
|
|
21
|
+
function _safeStringify(obj) {
|
|
22
|
+
const seen = new WeakSet();
|
|
23
|
+
try {
|
|
24
|
+
return JSON.stringify(obj, (key, value) => {
|
|
25
|
+
if (typeof value === 'object' && value !== null) {
|
|
26
|
+
if (seen.has(value)) return '[Circular]';
|
|
27
|
+
seen.add(value);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
});
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return '[Unserializable]';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _wrapValue(value) {
|
|
37
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
38
|
+
return new Proxy(value, {
|
|
39
|
+
get(target, prop) {
|
|
40
|
+
if (prop === Symbol.toPrimitive) return () => _safeStringify(target);
|
|
41
|
+
if (prop === 'toString') return () => _safeStringify(target);
|
|
42
|
+
const result = Reflect.get(target, prop);
|
|
43
|
+
if (typeof result === 'object' && result !== null) return _wrapValue(result);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildRawContext(msg, node) {
|
|
50
|
+
const ctx = {};
|
|
51
|
+
for (const k of Object.keys(msg || {})) {
|
|
52
|
+
if (_SKIP_KEYS.includes(k)) continue;
|
|
53
|
+
ctx[k] = _wrapValue(msg[k]);
|
|
54
|
+
}
|
|
55
|
+
ctx.flow = { get: (key) => node.context().flow.get(key) };
|
|
56
|
+
ctx.global = { get: (key) => node.context().global.get(key) };
|
|
57
|
+
ctx.env = process.env;
|
|
58
|
+
return ctx;
|
|
59
|
+
}
|
|
10
60
|
|
|
11
61
|
// ── BOOTSTRAP ─────────────────────────────────────────────────────────────────
|
|
12
62
|
// Injected into each Node.js worker via node -e.
|
|
@@ -454,7 +504,7 @@ module.exports = function(RED) {
|
|
|
454
504
|
}
|
|
455
505
|
|
|
456
506
|
const template = selectTemplate(msg, node);
|
|
457
|
-
const context =
|
|
507
|
+
const context = buildRawContext(msg, node);
|
|
458
508
|
|
|
459
509
|
let rendered;
|
|
460
510
|
try {
|
package/package.json
CHANGED
package/python.queue.js
CHANGED
|
@@ -6,7 +6,57 @@ const nunjucks = require('nunjucks');
|
|
|
6
6
|
const { spawn } = require('child_process');
|
|
7
7
|
const yaml = require('js-yaml');
|
|
8
8
|
const convertXML = require('xml-js');
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// ── LOCAL TEMPLATE CONTEXT ────────────────────────────────────────────────────
|
|
11
|
+
// Raw context: objects are NOT pre-stringified. Instead, each value is wrapped
|
|
12
|
+
// in a Proxy that serialises itself only when Nunjucks coerces it to a string
|
|
13
|
+
// (i.e. at output time). This means:
|
|
14
|
+
// {{ payload }} → JSON.stringify(payload) (calls toString / toPrimitive)
|
|
15
|
+
// {{ payload.a }} → the value of a (string/number as-is, object → JSON)
|
|
16
|
+
// {{ payload.a.b }} → the value of b
|
|
17
|
+
// No "[object Object]" is ever produced.
|
|
18
|
+
|
|
19
|
+
const _SKIP_KEYS = ['req', 'res', '_req', '_res'];
|
|
20
|
+
|
|
21
|
+
function _safeStringify(obj) {
|
|
22
|
+
const seen = new WeakSet();
|
|
23
|
+
try {
|
|
24
|
+
return JSON.stringify(obj, (key, value) => {
|
|
25
|
+
if (typeof value === 'object' && value !== null) {
|
|
26
|
+
if (seen.has(value)) return '[Circular]';
|
|
27
|
+
seen.add(value);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
});
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return '[Unserializable]';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _wrapValue(value) {
|
|
37
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
38
|
+
return new Proxy(value, {
|
|
39
|
+
get(target, prop) {
|
|
40
|
+
if (prop === Symbol.toPrimitive) return () => _safeStringify(target);
|
|
41
|
+
if (prop === 'toString') return () => _safeStringify(target);
|
|
42
|
+
const result = Reflect.get(target, prop);
|
|
43
|
+
if (typeof result === 'object' && result !== null) return _wrapValue(result);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildRawContext(msg, node) {
|
|
50
|
+
const ctx = {};
|
|
51
|
+
for (const k of Object.keys(msg || {})) {
|
|
52
|
+
if (_SKIP_KEYS.includes(k)) continue;
|
|
53
|
+
ctx[k] = _wrapValue(msg[k]);
|
|
54
|
+
}
|
|
55
|
+
ctx.flow = { get: (key) => node.context().flow.get(key) };
|
|
56
|
+
ctx.global = { get: (key) => node.context().global.get(key) };
|
|
57
|
+
ctx.env = process.env;
|
|
58
|
+
return ctx;
|
|
59
|
+
}
|
|
10
60
|
|
|
11
61
|
// ── BOOTSTRAP ─────────────────────────────────────────────────────────────────
|
|
12
62
|
// Injected into each Python worker via -c.
|
|
@@ -446,7 +496,7 @@ module.exports = function(RED) {
|
|
|
446
496
|
}
|
|
447
497
|
|
|
448
498
|
const template = selectTemplate(msg, node);
|
|
449
|
-
const context =
|
|
499
|
+
const context = buildRawContext(msg, node);
|
|
450
500
|
|
|
451
501
|
let rendered;
|
|
452
502
|
try {
|