Repository navigation
Releases: hazptr/ember
Release list
v0.40.8 — don't orphan the browser on a failed portal login
v0.40.7 — split layoutEngine + registry-only widgets
Tech-debt batch 5c — final batch of the June 2026 cleanup sweep (epic #161). Behavior-preserving.
- #157 — split
layoutEngine.tsintolib/layout/{placements,merge,pagination}.tsbehind a barrel; lifted Dashboard/WidgetLayout placement transforms into pure testedlib/layout/transforms.ts. Persisted layout shape + merge semantics unchanged (existing layoutEngine.test.ts 70/70 passes unchanged). - #155 —
WidgetDef.defaultVisibleflag; Dashboard derives the interval-widget lists from the registry (renamedavailableChartsAll→fullChartUniverse); "add a widget" is now registry-only.
isPlaced gating byte-identical (the #121/v0.40.2 surface). Verified on staging: saved-layout migration unchanged + force-mount all widgets renders clean (no fit overflow, 0 console errors). Docker test 1005; build green. No schema change.
v0.40.6 — decompose IntervalHistory into hooks
Tech-debt batch 5b (epic #161). Behavior-preserving decomposition.
- #156 —
IntervalHistory.tsx1424→899 LOC; extracteduseIntervalFetch/useOverscanLoad/useChartNavigationhooks (pure window math stays inintervalZoom.ts). Explicitly guarded every payload deref against the loading/null state (the v0.40.2 #150 crash class); preserved fresh-curWindowRef, horizontal-wheel pan + stopPropagation, and the justified exhaustive-deps disable.
Verified: Docker test 983 / build green; staging force-mount probe (cleared layout → all interval widgets mount) renders clean, 0 console errors. No schema change. Gesture interactions (zoom/pan/drag-select) are byte-equivalent code — confirm on the live dashboard.
v0.40.5 — decompose projectSeason + API consistency
Tech-debt batch 5a (epic #161). Behavior-preserving; no schema change.
- #158 — decomposed
projectSeasoninto pure helpers (resolvePricingMode/priceSeasonMonth/combineAnnualBand) + 10 hand-calc tests; existing seasonProjection test unchanged (byte-identical proof). - #159 — uniform error envelope via
withAccount; export routes usewithAccount;bills/[date]/pdfhonors?accountId=. Secrets unaffected.
Verified: Docker test 983; build green;/api/verify50/50 on staging; routes smoke-tested; dashboard renders clean.
v0.40.4 — rename currentCharges fields + hot-path query dedupe
Tech-debt batch 4 (epic #161). Behavior-preserving; money values byte-identical.
- #153 — renamed the in-memory fields that carry
currentChargesbut were misnamedtotalDueAmount(series/queries/csv/notifications + readers). The forbidden APItotalDueAmountis never used for analysis; the?? totalDueAmountPDF-not-yet-parsed fallback and the persistedNotification.payloadkey are deliberately unchanged (append-only). - #154 —
/api/settingsnow uses a lightweightgetAccountMetainstead of the full overview pipeline;getOverviewfetches account+bills once and feedsgetMonthlySeries(was double-querying per/api/overview).
Verified: Docker test 973 / build green; /api/verify 50/50 green on staging's real data; settings + dashboard render clean. No schema change.
v0.40.3 — fix IntervalHeatmap null-payload crash (v0.40.2 regression)
Hotfix release. Fixes a client-side crash introduced in v0.40.2 (#150) and restores the interval-heatmap widget.
- #166 —
IntervalHeatmapdereferencedpayload!.gridin eagerly-evaluatedchildrenduring the loading tick (payload null), throwing an unguarded render-timeTypeErrorthat took down the whole dashboard whenever the heatmap widget was mounted. Now gated on{payload && (…)}. Siblings (LoadShape/History) audited safe.
Verified on staging against the exact crash repro (default layout mounts the heatmap → renders skeleton/data, 0 console errors; previously "Application error"). Docker test 67 files / 973 tests; production build green. No schema change.
Prod was rolled back to v0.40.1 during the incident; this release restores forward to the full batch-1/2/3 cleanup with the fix.
v0.40.2 — tech-debt batches 2 & 3 (shared primitives, lib/stats, scraper dedup, typed ESLint)
Tech-debt batches 2 + 3 of the June 2026 cleanup sweep (epic #161). All behavior-preserving; no feature/visual change. (Batch 2 was build/CI-only with no runtime delta, so it gets its first version label here alongside batch 3.)
Batch 3 — shared primitives + tests (#150/#151/#152):
- #150 — consolidated duplicated UI primitives: one
Segmented,lib/chartTheme.ts,useDismissable,useIntervalPayload+IntervalWidgetBody, pureformatPeakReadout(hand-calc tested). Class/output strings byte-identical. - #151 — extracted pure
lib/stats.ts(median/mad/stdev) replacing ~4 dup median bodies; added missing hand-calc tests (parsePdf fallbacks,ymToLastYmd, interval grain edges). +52 tests (921→973). - #152 — scraper dedup: one
fillEmailAndPassword, typed thecollect.tsGraphQLanycluster (collectRaw.ts, zeroanyleft), sharedGQL_URL_RE/lib/ngrid/dates.ts/errMessage.ts.
Batch 2 — tooling hardening (#149/#160):
- #149 — type-checked
@typescript-eslint(no-floating-promises/no-misused-promiseserrors,no-explicit-anywarn) scoped tosrc/lib/**; enforced bynext build. Zero floating-promise errors in existing code. - #160 — buildx GHA cache on test/smoke-boot, report-only coverage, Node-24 GitHub Actions bumps.
Verified: Docker test 65 files / 973 tests; production build green (lint+typecheck); /api/verify 50/50 green on staging's prod-seeded data; dashboard renders clean (33 chart surfaces, 0 console errors). No schema change.
⚠ #152's auth.ts login extraction is the live login path — to be confirmed against a real account on the next scheduled scrape.
v0.40.1 — tech-debt batch 1 (dead code + build hygiene)
Tech-debt batch 1 of the June 2026 cleanup sweep (epic #161). Low-risk, no runtime behavior change.
- #146 — removed confirmed-dead code:
discoverInterval.ts,layoutEnginecomputeFitRowHeight/placementRows,intervalParamsgrain/IntervalGrain/FIFTEEN_MIN_SECONDS,predictionhorizonK, redundantReconcileRowalias, staleprisma:migrate/prisma:devscripts. - #147 — enabled
tsconfignoUnusedLocals/noUnusedParameters/noImplicitReturns; fixed fallout at the root. - #148 — Dockerfile
npm install→npm ci; un-ignored the trackedpackage-lock.json; documentedDATA_DIR.
Verified: Docker test stage 63 files / 921 tests, tsc clean, production build clean, and /api/verify green (50/50) on staging with prod-seeded data. No schema change.
v0.40.0 — Usage history rework + chart navigation
Usage history: server-side aggregation, energy splice, and focused navigation
A ground-up rework of the Usage history interval widget (#145).
Performance
/api/intervalaggregates in Postgres (reconcile + SUM into the zoom-appropriate bucket), returning ≤600 points instead of hydrating ~20k rows. Wide load ~150–200ms → ~42ms on full-history data; all zoom levels 11–42ms. ShortCache-Control; stale-while-revalidate (no cold-blank).
Energy-correct, finest-grain-by-zoom
- No more 1h/15m toggle — grain is chosen by the zoom span, always by SUMming adjacent intervals (4×15-min == 1h). An "end of 15-min data" marker shows where the 15-min archive begins; a straddle window renders real 15-min plus flat
hourly/4quarter-steps (no cliff). Hourly→15-min extrapolation only in the genuine straddle. Reconcile never double-counts overlapping grains; the line reaches the latest reading at every grain. In-grid grain pill.
Focused mouse navigation
- Click to focus (amber ring). Wheel = zoom on cursor; shift/horizontal-wheel = pan; ctrl+drag = move the viewport. Overscan preloads data left/right for smooth, live panning. Modifier cursors (grab / ew-resize). Fixes for macOS horizontal-wheel shift+scroll, the cockpit paginator stealing the gesture, and pan stability.
Notes
- Display-only: never touches billed numbers;
/api/verifyunaffected. No schema change (additive query). 930 unit tests; verified on a prod-seeded copy.
v0.39.2 — fix: 1h history view clipped at the 15-min-data boundary
Fix (#144): the 1h Usage history chart capped its right edge at the date 15-min recording began (e.g. Jun 7), hiding the most recent week of hourly data.
Root cause was order-of-operations: the 1h view consumed the downsampled all-grain feed and reconciled on the client, so downsampling turned recent 15-min rows into lone slots that reconcileToHourly's "partial 15-min + no hourly → skip" rule discarded.
New server-side ?grain=1h path reconciles raw rows → hourly first, then downsamples (symmetric with ?grain=15m). Legacy 'all' feed unchanged. Display-only — no /api/verify / billed-number surface touched. 830 tests pass (+6, incl. a regression on the ordering).