@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 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
- const { buildContext } = require('./utils/context');
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 = buildContext(msg, node);
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
- const { buildContext } = require('./utils/context');
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 = buildContext(msg, node);
507
+ const context = buildRawContext(msg, node);
458
508
 
459
509
  let rendered;
460
510
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "@inteli.city/node-red-contrib-exec-collection",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "dependencies": {
5
5
  "fs": "*",
6
6
  "tmp-promise": "*",
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
- const { buildContext } = require('./utils/context');
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 = buildContext(msg, node);
499
+ const context = buildRawContext(msg, node);
450
500
 
451
501
  let rendered;
452
502
  try {