@yroshcha/node-red-contrib-redis-full 1.0.7 → 1.0.8
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/CHANGELOG.md +6 -0
- package/README.md +2 -1
- package/package.json +1 -1
- package/redis.js +18 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 1.0.8 — 2026-08-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `redis xreadgroup` and `redis xautoclaim` now restore nested object and array fields that were serialised by `redis xadd`, while preserving scalar Redis fields as strings.
|
|
10
|
+
|
|
5
11
|
## 1.0.7 — 2026-08-12
|
|
6
12
|
|
|
7
13
|
### Added
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ For a local archive, use `npm install /path/to/yroshcha-node-red-contrib-redis-f
|
|
|
45
45
|
|
|
46
46
|
## Release status
|
|
47
47
|
|
|
48
|
-
**`1.0.
|
|
48
|
+
**`1.0.8` is the current stable release.** See [CHANGELOG.md](CHANGELOG.md) for release notes and compatibility information.
|
|
49
49
|
|
|
50
50
|
## Production profile
|
|
51
51
|
|
|
@@ -58,6 +58,7 @@ Safe consumer defaults and important settings:
|
|
|
58
58
|
- **Rate limit (msg/s) = 0** is unlimited. For example, `500` limits average downstream delivery to 500 events per second.
|
|
59
59
|
- **Read interval (ms) = 0** reads as fast as Redis responds. `1000` calls `XREADGROUP` no more often than once per second, independent of queue depth.
|
|
60
60
|
- **Batch wait (ms) = 0** emits one event at a time. With a positive value, the node emits an array once it has collected `COUNT` events or the wait expires. `msg.payload` and `msg.streamId` become arrays.
|
|
61
|
+
- Nested object and array fields written by `redis xadd` are automatically decoded from JSON by `redis xreadgroup` and `redis xautoclaim`; scalar Redis fields remain strings.
|
|
61
62
|
- **Batch interval (ms)** sets the minimum delay between emitted batches regardless of queue depth. For one batch of up to 50 entries per second, use `COUNT = 50` and `Batch interval = 1000`.
|
|
62
63
|
- **Max deliveries = 5.** After the limit is exceeded, an entry is atomically moved to `<stream>:dlq` and acknowledged. In Redis Cluster, source and DLQ keys must share a hash tag, for example `orders:{eu}` and `orders:{eu}:dlq`.
|
|
63
64
|
- **Start paused (wait for resume)** makes a consumer create/verify its group without calling `XREADGROUP`. It takes no work until a control-plane `resume` request arrives; use it for controlled pod startup after readiness checks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yroshcha/node-red-contrib-redis-full",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Production-oriented Redis palette for Node-RED: generic commands, Pub/Sub, scans, transactions, and Redis Streams consumer groups.",
|
|
5
5
|
"keywords": ["node-red", "redis", "redis-streams", "pubsub", "xadd", "xreadgroup", "ioredis"],
|
|
6
6
|
"license": "MIT",
|
package/redis.js
CHANGED
|
@@ -173,6 +173,22 @@ module.exports = function (RED) {
|
|
|
173
173
|
return obj;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
function parseStreamFields(flat) {
|
|
177
|
+
const obj = parseFlatFields(flat);
|
|
178
|
+
for (const [field, value] of Object.entries(obj)) {
|
|
179
|
+
// redis xadd serialises nested objects and arrays to JSON. Restore only
|
|
180
|
+
// these compound JSON values; scalar strings such as "true", "42", or
|
|
181
|
+
// "null" remain strings and preserve Redis field semantics.
|
|
182
|
+
if (typeof value !== 'string' || !/^[\[{]/.test(value.trim())) continue;
|
|
183
|
+
try {
|
|
184
|
+
obj[field] = JSON.parse(value);
|
|
185
|
+
} catch (_) {
|
|
186
|
+
// A value that merely looks like JSON is still a valid Redis string.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return obj;
|
|
190
|
+
}
|
|
191
|
+
|
|
176
192
|
function isNoGroupError(err) {
|
|
177
193
|
return String(err && err.message ? err.message : err).includes('NOGROUP');
|
|
178
194
|
}
|
|
@@ -868,7 +884,7 @@ module.exports = function (RED) {
|
|
|
868
884
|
if (!Array.isArray(flat)) {
|
|
869
885
|
throw new Error(`Stream entry ${id} no longer has a payload; do not trim streams below the PEL horizon`);
|
|
870
886
|
}
|
|
871
|
-
const payload =
|
|
887
|
+
const payload = parseStreamFields(flat);
|
|
872
888
|
if (checkDeliveryCount && await shouldDeadLetter(id, payload)) return;
|
|
873
889
|
if (!await waitForDeliverySlot() || paused) return false;
|
|
874
890
|
|
|
@@ -1173,7 +1189,7 @@ module.exports = function (RED) {
|
|
|
1173
1189
|
const [nextCursor, entries] = res;
|
|
1174
1190
|
cursor = nextCursor;
|
|
1175
1191
|
for (const [id, flat] of entries || []) {
|
|
1176
|
-
claimed.push({ streamId: id, payload:
|
|
1192
|
+
claimed.push({ streamId: id, payload: parseStreamFields(flat) });
|
|
1177
1193
|
}
|
|
1178
1194
|
} while (cursor !== '0' && claimed.length < node.maxMessages);
|
|
1179
1195
|
|