@rosepetal/node-red-contrib-utils 1.1.1 → 1.1.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.
@@ -16,9 +16,14 @@ The `queue` node buffers incoming messages, enforces a minimum interval between
16
16
  - **Incoming Message**: Any Node-RED message to be enqueued.
17
17
 
18
18
  ### Outputs
19
- - **Queued Message**: Messages are forwarded in FIFO order while respecting the configured interval.
20
-
21
- When timeout mode is selected, messages that exceed the configured timeout are silently discarded with a warning in the node's log.
19
+ - **Output 1**: Messages forwarded in FIFO order while respecting the configured interval.
20
+ - **Output 2**: Messages dropped due to timeout or overflow, with metadata:
21
+ - `msg.meta.queueDropped` - Always `true`
22
+ - `msg.meta.queueDropReason` - Either `"timeout"` or `"overflow"`
23
+ - `msg.meta.queuedDuration` - Time spent in queue before timeout (timeout drops only)
24
+ - `msg.meta.queueTimeout` - Configured timeout value in ms (timeout drops only)
25
+ - `msg.meta.queueSize` - Queue size when overflow occurred (overflow drops only)
26
+ - `msg.meta.queueMaxSize` - Configured max queue size (overflow drops only)
22
27
 
23
28
  ## Configuration Options
24
29
 
@@ -47,11 +52,13 @@ When timeout mode is selected, messages that exceed the configured timeout are s
47
52
  - Messages are processed in first-in-first-out order.
48
53
  - The node emits messages immediately when the interval requirement has already been satisfied.
49
54
  - If necessary, the node waits the remaining interval before releasing the next message.
50
- - When messages expire due to timeout (and timeout mode is active) they are removed from the queue and a warning is logged.
55
+ - When messages expire due to timeout (and timeout mode is active) they are sent to output 2 with metadata and a warning is logged.
56
+ - When the queue overflows (queue-size mode) excess messages are sent to output 2 with metadata.
51
57
  - Node status indicates whether the queue is idle, holding messages, or actively sending.
52
58
 
53
59
  ## Best Practices
54
60
 
55
61
  - Pair with Array nodes to control the rate of batch processing pipelines.
56
62
  - Use timeouts to keep sensor readings or camera frames current.
57
- - Combine with Catch or Status nodes to monitor when the queue reaches capacity or drops messages.
63
+ - Connect output 2 to logging, alerting, or fallback processing to handle dropped messages.
64
+ - Use `msg.meta.queueDropReason` to differentiate between timeout and overflow scenarios in downstream logic.
@@ -10,7 +10,8 @@
10
10
  timeout: { value: 0 }
11
11
  },
12
12
  inputs: 1,
13
- outputs: 1,
13
+ outputs: 2,
14
+ outputLabels: ["output", "dropped"],
14
15
  icon: "font-awesome/fa-clock-o",
15
16
  label: function() {
16
17
  if (this.name) return this.name;
@@ -106,11 +107,28 @@
106
107
  <dd>Minimum time to wait between forwarded messages. Zero means send immediately when data is available.</dd>
107
108
  </dl>
108
109
 
110
+ <h3>Outputs</h3>
111
+ <dl class="message-properties">
112
+ <dt>Output 1 <span class="property-type">message</span></dt>
113
+ <dd>Successfully queued messages released according to the interval setting.</dd>
114
+ <dt>Output 2 <span class="property-type">message</span></dt>
115
+ <dd>Messages dropped due to timeout expiration or queue overflow. Includes metadata:
116
+ <ul>
117
+ <li><code>msg.meta.queueDropped</code> - Always <code>true</code></li>
118
+ <li><code>msg.meta.queueDropReason</code> - Either <code>"timeout"</code> or <code>"overflow"</code></li>
119
+ <li><code>msg.meta.queuedDuration</code> - Time in queue before timeout (timeout only)</li>
120
+ <li><code>msg.meta.queueTimeout</code> - Configured timeout value (timeout only)</li>
121
+ <li><code>msg.meta.queueSize</code> - Current queue size (overflow only)</li>
122
+ <li><code>msg.meta.queueMaxSize</code> - Configured max size (overflow only)</li>
123
+ </ul>
124
+ </dd>
125
+ </dl>
126
+
109
127
  <h3>Behavior</h3>
110
128
  <ul>
111
129
  <li>Messages are processed in FIFO order while respecting the selected mode.</li>
112
- <li>Queue-size mode enforces a hard cap, rejecting excess messages.</li>
113
- <li>Timeout mode keeps buffering but evicts stale entries before sending.</li>
130
+ <li>Queue-size mode enforces a hard cap; excess messages go to output 2 with <code>overflow</code> reason.</li>
131
+ <li>Timeout mode keeps buffering but evicts stale entries to output 2 with <code>timeout</code> reason.</li>
114
132
  <li>The interval ensures a steady pace by adding delays between sends when configured.</li>
115
133
  </ul>
116
134
 
package/nodes/io/queue.js CHANGED
@@ -68,23 +68,32 @@ module.exports = function (RED) {
68
68
  }
69
69
 
70
70
  const now = Date.now();
71
- let dropped = 0;
71
+ const expiredEntries = [];
72
72
 
73
73
  while (state.queue.length > 0) {
74
74
  const oldest = state.queue[0];
75
75
  if (now - oldest.enqueuedAt >= node.timeoutMs) {
76
- state.queue.shift();
77
- dropped += 1;
76
+ expiredEntries.push(state.queue.shift());
78
77
  } else {
79
78
  break;
80
79
  }
81
80
  }
82
81
 
83
- if (dropped > 0) {
84
- node.warn(`Queue node dropped ${dropped} message(s) due to timeout.`);
82
+ if (expiredEntries.length > 0) {
83
+ node.warn(`Queue node dropped ${expiredEntries.length} message(s) due to timeout.`);
84
+ // Send expired messages to output 2 with metadata
85
+ for (const entry of expiredEntries) {
86
+ const droppedMsg = entry.msg;
87
+ droppedMsg.meta = droppedMsg.meta || {};
88
+ droppedMsg.meta.queueDropped = true;
89
+ droppedMsg.meta.queueDropReason = 'timeout';
90
+ droppedMsg.meta.queuedDuration = now - entry.enqueuedAt;
91
+ droppedMsg.meta.queueTimeout = node.timeoutMs;
92
+ node.send([null, droppedMsg]);
93
+ }
85
94
  }
86
95
 
87
- return dropped;
96
+ return expiredEntries.length;
88
97
  }
89
98
 
90
99
  function scheduleNextSend() {
@@ -144,7 +153,7 @@ module.exports = function (RED) {
144
153
  text: `Sending (remaining ${state.queue.length})`
145
154
  });
146
155
 
147
- node.send(entry.msg);
156
+ node.send([entry.msg, null]);
148
157
  scheduleNextSend();
149
158
  }
150
159
 
@@ -161,8 +170,15 @@ module.exports = function (RED) {
161
170
  state.queue.length >= node.maxQueueSize
162
171
  ) {
163
172
  node.warn(
164
- `Queue node at capacity (${node.maxQueueSize}). Incoming message ignored.`
173
+ `Queue node at capacity (${node.maxQueueSize}). Incoming message dropped.`
165
174
  );
175
+ // Send overflow message to output 2 with metadata
176
+ msg.meta = msg.meta || {};
177
+ msg.meta.queueDropped = true;
178
+ msg.meta.queueDropReason = 'overflow';
179
+ msg.meta.queueSize = state.queue.length;
180
+ msg.meta.queueMaxSize = node.maxQueueSize;
181
+ node.send([null, msg]);
166
182
  setStatusQueued();
167
183
  return done?.();
168
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosepetal/node-red-contrib-utils",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Utility and I/O nodes for Node-RED, including array helpers and file saving.",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"