hearald 0.1.0 → 0.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.
- package/CHANGELOG.md +26 -1
- package/README.md +5 -0
- package/dist/analytics.html +12 -12
- package/dist/index.js +12 -12
- package/package.json +3 -1
- package/src/public/js/App.js +11 -11
- package/src/public/js/util.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.2] - 2026-10-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- The dashboard crashed (`Cannot read properties of undefined (reading 'map')`) on a `ui` layout written
|
|
15
|
+
the way the README showed it, `{ title, fields }`. Sections now accept `title` or `menuName` and
|
|
16
|
+
`fields` or `stats`, and a section with neither renders empty.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The README now documents the `ui` layout (sections, tiles, `chartContexts`).
|
|
21
|
+
|
|
22
|
+
## [0.1.1] - 2026-10-09
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- The dashboard threw errors the first time it loaded with no data (`Cannot read properties of
|
|
27
|
+
undefined (reading 'map')` and `(reading 'users')`). It now shows zeros and empty menus when there
|
|
28
|
+
are no events, no metrics for the chosen chart context, or no `ui` layout.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Tests that render the dashboard in a fake browser (happy-dom) against a real hearald router.
|
|
33
|
+
|
|
10
34
|
## [0.1.0] - 2026-10-07
|
|
11
35
|
|
|
12
36
|
### Security
|
|
@@ -117,7 +141,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
117
141
|
|
|
118
142
|
- Initial release of Hearald
|
|
119
143
|
|
|
120
|
-
[Unreleased]: https://github.com/pariser/hearald/compare/v0.1.
|
|
144
|
+
[Unreleased]: https://github.com/pariser/hearald/compare/v0.1.1...HEAD
|
|
145
|
+
[0.1.1]: https://github.com/pariser/hearald/compare/v0.1.0...v0.1.1
|
|
121
146
|
[0.1.0]: https://github.com/pariser/hearald/compare/v0.0.5...v0.1.0
|
|
122
147
|
[0.0.5]: https://github.com/pariser/hearald/compare/v0.0.4...v0.0.5
|
|
123
148
|
[0.0.4]: https://github.com/pariser/hearald/compare/v0.0.3...v0.0.4
|
package/README.md
CHANGED
|
@@ -113,6 +113,11 @@ const statDefinitions = {
|
|
|
113
113
|
};
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
`ui` sets the dashboard layout. Each section has a `title` (or `menuName`; `""` for no heading) and a
|
|
117
|
+
list of tiles in `fields` (or `stats`); a tile is `{ key, label, type }` with `type` one of `number`,
|
|
118
|
+
`percent` or `histogram`, and an optional `span`. `chartContexts` (`[{ id, menuName }]`) fills the chart's
|
|
119
|
+
"Context" menu. Both are optional; a dashboard with no `ui` shows only the chart.
|
|
120
|
+
|
|
116
121
|
Names with dots become nested objects in the result. `hidden: true` leaves a metric out of the output
|
|
117
122
|
(useful for sets that only feed a computed metric). A computed metric that names a formula that does
|
|
118
123
|
not exist is an error; one whose inputs are missing is simply left out.
|
package/dist/analytics.html
CHANGED
|
@@ -461,7 +461,7 @@ th {
|
|
|
461
461
|
onLoadProgress(i, n);
|
|
462
462
|
|
|
463
463
|
const json = await getDataFn({ n: 1, d: iso(day) });
|
|
464
|
-
const v = json
|
|
464
|
+
const v = json?.metrics?.[context]?.[field] || 0;
|
|
465
465
|
|
|
466
466
|
data.unshift({ date: iso(day), value: v });
|
|
467
467
|
max = Math.max(max, v);
|
|
@@ -687,14 +687,14 @@ th {
|
|
|
687
687
|
fetchStats: getJson,
|
|
688
688
|
});
|
|
689
689
|
|
|
690
|
-
let UI =
|
|
690
|
+
let UI = {};
|
|
691
691
|
|
|
692
692
|
async function fetchUI() {
|
|
693
693
|
try {
|
|
694
694
|
const res = await fetch(window.location.href + "/layout");
|
|
695
|
-
UI = await res.json();
|
|
695
|
+
UI = (await res.json()) || {};
|
|
696
696
|
} catch (e) {
|
|
697
|
-
UI =
|
|
697
|
+
UI = {};
|
|
698
698
|
}
|
|
699
699
|
}
|
|
700
700
|
|
|
@@ -830,7 +830,7 @@ th {
|
|
|
830
830
|
name="context"
|
|
831
831
|
onchange=${(e) => this.changeChartContext(e)}
|
|
832
832
|
>
|
|
833
|
-
${UI.chartContexts.map(
|
|
833
|
+
${(UI.chartContexts || []).map(
|
|
834
834
|
({ id, menuName }) =>
|
|
835
835
|
m`
|
|
836
836
|
<option
|
|
@@ -881,7 +881,7 @@ th {
|
|
|
881
881
|
!chartData.loading &&
|
|
882
882
|
m`
|
|
883
883
|
<div class="bar-chart__scale">${chartData.max}</div>
|
|
884
|
-
${chartData.data.map(
|
|
884
|
+
${(chartData.data || []).map(
|
|
885
885
|
(v) =>
|
|
886
886
|
m`
|
|
887
887
|
<div
|
|
@@ -899,16 +899,16 @@ th {
|
|
|
899
899
|
`}
|
|
900
900
|
</div>
|
|
901
901
|
</section>
|
|
902
|
-
${UI.sections.map((statsSection) => {
|
|
902
|
+
${(UI.sections || []).map((statsSection) => {
|
|
903
|
+
const sectionTitle =
|
|
904
|
+
statsSection.menuName ?? statsSection.title ?? "";
|
|
903
905
|
return m`
|
|
904
906
|
<section class="analytics analytics--${statsSection.id}">
|
|
905
|
-
${
|
|
907
|
+
${sectionTitle !== "" &&
|
|
906
908
|
m`
|
|
907
|
-
<h2 class="analytics__title">
|
|
908
|
-
${statsSection.menuName}
|
|
909
|
-
</h2>
|
|
909
|
+
<h2 class="analytics__title">${sectionTitle}</h2>
|
|
910
910
|
`}
|
|
911
|
-
${statsSection.stats.map((f) => {
|
|
911
|
+
${(statsSection.stats || statsSection.fields || []).map((f) => {
|
|
912
912
|
return m`
|
|
913
913
|
<${Tile}
|
|
914
914
|
title="${f.label}"
|
package/dist/index.js
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
onLoadProgress(i, n);
|
|
64
64
|
|
|
65
65
|
const json = await getDataFn({ n: 1, d: iso(day) });
|
|
66
|
-
const v = json
|
|
66
|
+
const v = json?.metrics?.[context]?.[field] || 0;
|
|
67
67
|
|
|
68
68
|
data.unshift({ date: iso(day), value: v });
|
|
69
69
|
max = Math.max(max, v);
|
|
@@ -289,14 +289,14 @@
|
|
|
289
289
|
fetchStats: getJson,
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
-
let UI =
|
|
292
|
+
let UI = {};
|
|
293
293
|
|
|
294
294
|
async function fetchUI() {
|
|
295
295
|
try {
|
|
296
296
|
const res = await fetch(window.location.href + "/layout");
|
|
297
|
-
UI = await res.json();
|
|
297
|
+
UI = (await res.json()) || {};
|
|
298
298
|
} catch (e) {
|
|
299
|
-
UI =
|
|
299
|
+
UI = {};
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
@@ -432,7 +432,7 @@
|
|
|
432
432
|
name="context"
|
|
433
433
|
onchange=${(e) => this.changeChartContext(e)}
|
|
434
434
|
>
|
|
435
|
-
${UI.chartContexts.map(
|
|
435
|
+
${(UI.chartContexts || []).map(
|
|
436
436
|
({ id, menuName }) =>
|
|
437
437
|
m`
|
|
438
438
|
<option
|
|
@@ -483,7 +483,7 @@
|
|
|
483
483
|
!chartData.loading &&
|
|
484
484
|
m`
|
|
485
485
|
<div class="bar-chart__scale">${chartData.max}</div>
|
|
486
|
-
${chartData.data.map(
|
|
486
|
+
${(chartData.data || []).map(
|
|
487
487
|
(v) =>
|
|
488
488
|
m`
|
|
489
489
|
<div
|
|
@@ -501,16 +501,16 @@
|
|
|
501
501
|
`}
|
|
502
502
|
</div>
|
|
503
503
|
</section>
|
|
504
|
-
${UI.sections.map((statsSection) => {
|
|
504
|
+
${(UI.sections || []).map((statsSection) => {
|
|
505
|
+
const sectionTitle =
|
|
506
|
+
statsSection.menuName ?? statsSection.title ?? "";
|
|
505
507
|
return m`
|
|
506
508
|
<section class="analytics analytics--${statsSection.id}">
|
|
507
|
-
${
|
|
509
|
+
${sectionTitle !== "" &&
|
|
508
510
|
m`
|
|
509
|
-
<h2 class="analytics__title">
|
|
510
|
-
${statsSection.menuName}
|
|
511
|
-
</h2>
|
|
511
|
+
<h2 class="analytics__title">${sectionTitle}</h2>
|
|
512
512
|
`}
|
|
513
|
-
${statsSection.stats.map((f) => {
|
|
513
|
+
${(statsSection.stats || statsSection.fields || []).map((f) => {
|
|
514
514
|
return m`
|
|
515
515
|
<${Tile}
|
|
516
516
|
title="${f.label}"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hearald",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A single server drop-in file system based metrics and analytics library.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"chalk": "^4.1.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
+
"@happy-dom/global-registrator": "^20.14.6",
|
|
30
31
|
"@rollup/plugin-commonjs": "^15.1.0",
|
|
31
32
|
"@rollup/plugin-json": "^4.1.0",
|
|
32
33
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
40
41
|
"express": "^5.2.1",
|
|
41
42
|
"express4": "npm:express@^4.21.2",
|
|
43
|
+
"happy-dom": "^20.14.6",
|
|
42
44
|
"htm": "^3.0.3",
|
|
43
45
|
"preact": "^10.3.3",
|
|
44
46
|
"prettier": "2.2.1",
|
package/src/public/js/App.js
CHANGED
|
@@ -75,14 +75,14 @@ const getDataFn = ({ n, d, q }) =>
|
|
|
75
75
|
fetchStats: getJson,
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
let UI =
|
|
78
|
+
let UI = {};
|
|
79
79
|
|
|
80
80
|
async function fetchUI() {
|
|
81
81
|
try {
|
|
82
82
|
const res = await fetch(window.location.href + "/layout");
|
|
83
|
-
UI = await res.json();
|
|
83
|
+
UI = (await res.json()) || {};
|
|
84
84
|
} catch (e) {
|
|
85
|
-
UI =
|
|
85
|
+
UI = {};
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -218,7 +218,7 @@ class App extends Component {
|
|
|
218
218
|
name="context"
|
|
219
219
|
onchange=${(e) => this.changeChartContext(e)}
|
|
220
220
|
>
|
|
221
|
-
${UI.chartContexts.map(
|
|
221
|
+
${(UI.chartContexts || []).map(
|
|
222
222
|
({ id, menuName }) =>
|
|
223
223
|
html`
|
|
224
224
|
<option
|
|
@@ -269,7 +269,7 @@ class App extends Component {
|
|
|
269
269
|
!chartData.loading &&
|
|
270
270
|
html`
|
|
271
271
|
<div class="bar-chart__scale">${chartData.max}</div>
|
|
272
|
-
${chartData.data.map(
|
|
272
|
+
${(chartData.data || []).map(
|
|
273
273
|
(v) =>
|
|
274
274
|
html`
|
|
275
275
|
<div
|
|
@@ -287,16 +287,16 @@ class App extends Component {
|
|
|
287
287
|
`}
|
|
288
288
|
</div>
|
|
289
289
|
</section>
|
|
290
|
-
${UI.sections.map((statsSection) => {
|
|
290
|
+
${(UI.sections || []).map((statsSection) => {
|
|
291
|
+
const sectionTitle =
|
|
292
|
+
statsSection.menuName ?? statsSection.title ?? "";
|
|
291
293
|
return html`
|
|
292
294
|
<section class="analytics analytics--${statsSection.id}">
|
|
293
|
-
${
|
|
295
|
+
${sectionTitle !== "" &&
|
|
294
296
|
html`
|
|
295
|
-
<h2 class="analytics__title">
|
|
296
|
-
${statsSection.menuName}
|
|
297
|
-
</h2>
|
|
297
|
+
<h2 class="analytics__title">${sectionTitle}</h2>
|
|
298
298
|
`}
|
|
299
|
-
${statsSection.stats.map((f) => {
|
|
299
|
+
${(statsSection.stats || statsSection.fields || []).map((f) => {
|
|
300
300
|
return html`
|
|
301
301
|
<${Tile}
|
|
302
302
|
title="${f.label}"
|
package/src/public/js/util.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function fetchChartData({
|
|
|
48
48
|
onLoadProgress(i, n);
|
|
49
49
|
|
|
50
50
|
const json = await getDataFn({ n: 1, d: iso(day) });
|
|
51
|
-
const v = json
|
|
51
|
+
const v = json?.metrics?.[context]?.[field] || 0;
|
|
52
52
|
|
|
53
53
|
data.unshift({ date: iso(day), value: v });
|
|
54
54
|
max = Math.max(max, v);
|