mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
v1.4.33 — polish and reliability
Quality-leap release between two HealthKit milestones. The headline is a P0 hotfix for the `/api/analytics` 500 that broke the Insights mother page for any account with more than a few thousand measurements: the six per-metric status helpers were spreading the entire numeric history into `Math.min` / `Math.max`, which trips a stack overflow once a single argument list exceeds the V8 spread cap. Both the surfaced symptom and the latent risk in five sibling helpers were folded onto a reduce-based min/max path. Around that hotfix landed a deep polish pass: the Insights mother page now defers three below-the-fold blocks behind `next/dynamic` to trim the cold mount cost, the analytics endpoint gained a `?slice=summaries` slim slice that the per-metric sub-pages now ride, the Coach snapshot builder picked up a 60 s LRU keyed on `(userId, scope)`, the Settings shell consolidated three duplicate copies of the about / notifications / about-dropdown surfaces, the navigation strip dropped a redundant "Home" group label and a colliding "Notifications" sibling, every icon-only button on the medications and admin surfaces gained an accessible name, every Progress bar gained one too, three pages had their heading hierarchy repaired to a sequential h1 → h2 → h3, the mobile Coach FAB now hides while a chart tooltip is open so it cannot occlude the read-out, and the auth shell normalised every authenticated route to a single `max-w-screen-xl` container. Also lands the Apple App Site Association handler at `/.well-known/apple-app-site-association` on every host that fronts the app, advertising the iOS bundle's App ID prefix (S8WDX4W5KX.dev.healthlog.app) under `webcredentials.apps` so the passkey ceremony shares cleanly between the web origin and the iOS app. The release closes nineteen issues from a five-surface audit + a runtime bug-hunt pass, plus six follow-up tracks of polish across the affected surfaces.
This commit is contained in:
1 parent
c7a0ff214b
commit
7bef199039
186 files changed
+9358
-946
No files matched your search
@@ -1,24 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useReportWebVitals } from "next/web-vitals";
|
||||
|
||||
/**
|
||||
* v1.4.28 R3d — client-side mount that pipes every Web Vitals
|
||||
* measurement to `/api/internal/web-vitals`.
|
||||
* v1.4.28 R3d — client-side mount that pipes Web Vitals measurements
|
||||
* to `/api/internal/web-vitals`.
|
||||
*
|
||||
* Next 16 ships `useReportWebVitals` as the supported hook for
|
||||
* capturing CLS / FCP / FID / LCP / TTFB / INP from the live
|
||||
* navigation. The hook fires once per metric per page load, so even a
|
||||
* heavy navigation produces at most six beacons.
|
||||
* navigation. The hook fires once per metric per page load, so a
|
||||
* heavy navigation can produce up to six beacons per route change.
|
||||
*
|
||||
* v1.4.33 F19 — the reporter previously posted EVERY measurement on
|
||||
* EVERY page-load. In production the runtime audit captured
|
||||
* `POST /api/internal/web-vitals 429` for almost every metric after
|
||||
* the first navigation: six beacons × N navigations burned through
|
||||
* the per-IP 60/min route-side rate-limit in seconds, so most samples
|
||||
* were dropped before they reached the wide-event sink. Telemetry
|
||||
* was self-throttled.
|
||||
*
|
||||
* Switch to client-side sampling at SAMPLE_RATE: each page-load
|
||||
* decides ONCE (memoised via `useMemo`) whether to report and either
|
||||
* sends every metric for that load or sends none. Per-load all-or-
|
||||
* none keeps a sampled session's vitals coherent (the LCP from one
|
||||
* page must be comparable to the CLS from the same page) while
|
||||
* cutting traffic by an order of magnitude. The route-side rate-limit
|
||||
* stays in place as a backstop against runaway sampling.
|
||||
*
|
||||
* Delivery transport: `navigator.sendBeacon` when available (queues
|
||||
* the POST without blocking the page lifecycle, survives the
|
||||
* `pagehide` event) with a `fetch({ keepalive: true })` fallback.
|
||||
* The route accepts the payload best-effort and returns 200 even on
|
||||
* The route accepts the payload best-effort and returns 204 even on
|
||||
* malformed bodies — beacons must never delay or fail the navigation.
|
||||
*/
|
||||
const SAMPLE_RATE = 0.1;
|
||||
|
||||
export function WebVitalsReporter() {
|
||||
// Decide once per mount whether this page-load reports its vitals.
|
||||
// Math.random() is impure, so we deferred the draw to `useEffect`
|
||||
// (React's purity contract forbids running impure work inside
|
||||
// useMemo / the render body). The ref keeps the decision stable
|
||||
// across every metric callback inside the same navigation —
|
||||
// partial sampling would mix LCP-without-CLS noise into the wide-
|
||||
// event aggregates. SSR initialises the ref to `null` (no sample
|
||||
// decision yet); the first reportWebVitals callback fires post-
|
||||
// hydration once the effect has filled the slot.
|
||||
const sampledRef = useRef<boolean | null>(null);
|
||||
useEffect(() => {
|
||||
sampledRef.current = Math.random() < SAMPLE_RATE;
|
||||
}, []);
|
||||
|
||||
useReportWebVitals((metric) => {
|
||||
// If the effect hasn't yet seeded the sample decision (very first
|
||||
// pre-hydration measurement), drop the beacon — over-sampling at
|
||||
// first paint would defeat the rate-limit relief.
|
||||
if (sampledRef.current !== true) return;
|
||||
|
||||
const body = JSON.stringify({
|
||||
id: metric.id,
|
||||
name: metric.name,
|
||||
|
||||
Reference in new issue
Block a user