@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 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/html',
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 mod = "ace/mode/"+$("#node-input-format").val();
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
- 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.2",
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 {