From fe1fcf46b32bdb0cd58eadb09a816d89ada05d40 Mon Sep 17 00:00:00 2001 From: SandObserver <260779319+SandObserver@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:27:28 -0400 Subject: [PATCH] Fix: read a JSON field named count in a badge extract path The extractor treated a path segment of `count` as the array-length token wherever it appeared, so a badge pointed at a body such as {"count": 99} resolved to zero. Only $count is the token unconditionally now; `count` counts an array and otherwise reads the field. --- CHANGELOG.md | 3 +++ api/src/badge-extract.js | 5 ++++- api/test/badge-extract.test.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b494bd0..0c3f6cdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A widget settings Fetch marks a blocked private address as a warning, the same as a badge test, instead of a hard error. +- A badge extract path ending in `count` now reads a JSON field of that + name. It only counts an array when the value is an array. + ## [1.6.0] - 2026-08-13 ### Added diff --git a/api/src/badge-extract.js b/api/src/badge-extract.js index e0c6e786..d2e1c858 100644 --- a/api/src/badge-extract.js +++ b/api/src/badge-extract.js @@ -58,7 +58,10 @@ function extractPath(obj, dotPath) { let cur = obj; for (const seg of segments) { if (cur == null) return undefined; - if (seg === '$count' || seg === 'count') return Array.isArray(cur) ? cur.length : undefined; + if (seg === '$count') return Array.isArray(cur) ? cur.length : undefined; + /* `count` is the array-length token only on an array. On an object it is + the field of that name. */ + if (seg === 'count' && Array.isArray(cur)) return cur.length; const fM = seg.match(filterRe); if (fM) { const [, field, rawVal] = fM; diff --git a/api/test/badge-extract.test.js b/api/test/badge-extract.test.js index 3c673839..07d08590 100644 --- a/api/test/badge-extract.test.js +++ b/api/test/badge-extract.test.js @@ -17,6 +17,12 @@ test('extractPath resolves $count and count on arrays', () => { assert.equal(extractPath({ x: 5 }, 'x.count'), undefined); // not an array }); +test('extractPath reads a field named count on an object', () => { + assert.equal(extractPath({ count: 99 }, 'count'), 99); + assert.equal(extractPath({ queue: { count: 4 } }, 'queue.count'), 4); + assert.equal(computeBadgeValue({ count: 99 }, { extract: 'count' }), 99); +}); + test('extractPath filters an array by a boolean field then counts', () => { const data = { list: [{ on: true }, { on: false }, { on: true }] }; assert.equal(extractPath(data, 'list.filter(on==true).count'), 2);