@inteli.city/node-red-contrib-exec-collection 2.0.0 → 2.0.1
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.js +54 -2
- package/node.queue.js +52 -2
- package/package.json +1 -1
- package/python.queue.js +52 -2
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 {
|