Merge branch 'fix/v1437-review' into release/v1.43.7

# Conflicts:
#	src/app/api/analytics/route.ts
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 06:20:42 +02:00
commit 45c027aee2
217 files changed
+3636 -2127

No files matched your search

@@ -35,6 +35,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("@/lib/db", () => ({
prisma: {
measurement: { findMany: vi.fn() },
// The empty recovery page reads the Oura fields for its reason.
user: { findUnique: vi.fn().mockResolvedValue(null) },
},
}));
@@ -0,0 +1,43 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "@/__tests__/helpers/source-files";
/**
* No raw control byte in a source file, apart from tab, newline and carriage
* return.
*
* A control byte typed into a string literal is invisible in an editor, a
* diff and a review, renders as nothing or as a box, and survives copy and
* paste unseen; the same value written as an escape (`"\x01"`) is the same
* string and can be read. Three such bytes had crept in, one of them in a
* production join key.
*/
const CONTROL = /[\x00-\x08\x0b\x0c\x0e-\x1f]/;
describe("source files carry no raw control bytes", () => {
it("every file under src/ is free of them", () => {
const src = join(process.cwd(), "src");
const files = walkSourceFiles(src, {
floor: 3000,
extensions: [".ts", ".tsx", ".js", ".mjs", ".css", ".json", ".md"],
}).filter((file) => !file.startsWith("generated/"));
// A sweep that read nothing would agree with any tree.
expect(files.length).toBeGreaterThan(3000);
const offenders: string[] = [];
for (const file of files) {
const lines = readFileSync(join(src, file), "utf8").split("\n");
lines.forEach((line, i) => {
if (CONTROL.test(line)) offenders.push(`${file}:${i + 1}`);
});
}
expect(offenders).toEqual([]);
});
it("recognises a control byte when one is there", () => {
expect(CONTROL.test("a\u0001b")).toBe(true);
expect(CONTROL.test("tab\tnewline\ncr\r")).toBe(false);
});
});
@@ -0,0 +1,82 @@
/**
* Every read-failure primitive is handed the error it reports.
*
* `QueryErrorRow` and `QueryErrorCard` decide what a failed read offers from
* the error itself: nothing (or the surface's refused state) for a 403, a
* note without a retry for a 429, sign-in for a 401, a retry otherwise. A
* call site that renders one without the error falls back to "failed" for
* every status, which is how a refused share showed an error card and a spent
* budget offered a retry that could only be refused again.
*
* The prop is required in the type, so the compiler already catches a missing
* one. What it cannot catch is a literal that defeats it (`error={null}`,
* `error={undefined}`), so this guard reads every opening tag under `src/`
* and refuses both. The matcher is whitespace-tolerant (tags span lines) and
* the match count is asserted, so a matcher that stops finding the tags fails
* rather than passing over nothing.
*/
import { readdirSync, readFileSync } from "node:fs";
import { join, relative } from "node:path";
import { describe, expect, it } from "vitest";
const SRC = join(__dirname, "..");
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "__tests__" || entry.name === "generated") continue;
const path = join(dir, entry.name);
if (entry.isDirectory()) walk(path, out);
else if (entry.name.endsWith(".tsx") && !entry.name.includes(".test."))
out.push(path);
}
return out;
}
/** The opening tag's attribute text, from the name to its closing `>`. */
function openingTags(source: string): string[] {
const tags: string[] = [];
const open = /<(QueryErrorRow|QueryErrorCard)(?=[\s/>])/g;
for (let m = open.exec(source); m; m = open.exec(source)) {
let depth = 0;
let i = m.index + m[0].length;
for (; i < source.length; i++) {
const ch = source[i];
if (ch === "{") depth++;
else if (ch === "}") depth--;
else if (ch === ">" && depth === 0) break;
}
tags.push(source.slice(m.index, i + 1));
}
return tags;
}
/** Comments mention the primitives by name (`<QueryErrorCard>`); skip them. */
function stripComments(source: string): string {
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
}
const sites = walk(SRC).flatMap((file) =>
openingTags(stripComments(readFileSync(file, "utf8"))).map((tag) => ({
file: relative(SRC, file),
tag,
})),
);
describe("read-failure primitives carry their error", () => {
it("finds the call sites it guards", () => {
// About 120 at the time of writing. A drop to a handful means the
// matcher broke, not that the sites went away.
expect(sites.length).toBeGreaterThan(100);
});
it("passes the query's error at every site", () => {
const offenders = sites
.filter(
({ tag }) =>
!/\serror=\{/.test(tag) ||
/\serror=\{\s*(null|undefined)\s*\}/.test(tag),
)
.map(({ file, tag }) => `${file}: ${tag.split("\n")[0]}`);
expect(offenders).toEqual([]);
});
});
@@ -216,6 +216,78 @@ describe("date formatters name the calendar they render in", () => {
});
});
/**
* A component renders dates through `useFormatters()`, never through the
* shared cache directly.
*
* The two-argument `getDateTimeFormat(locale, options)` from
* `@/lib/intl/formatter-cache` can name a zone and still be wrong: it takes
* the locale and the hour cycle from wherever the caller found them and
* knows nothing of the reader's date-order preference. The workout surfaces
* rebuilt their own formatters this way and showed "Dec 16" to a reader who
* had chosen YYYY-MM-DD. The three-argument `(locale, timeZone, options)`
* form from `@/lib/tz/intl-cache` builds day keys and clock parts, not
* display text, and stays allowed.
*/
describe("components format dates through useFormatters", () => {
const CALL = /\bgetDateTimeFormat\s*\(/g;
function scan() {
const files = execFileSync(
"grep",
[
"-rlE",
"getDateTimeFormat\\s*\\(",
"src/components",
"--include=*.ts",
"--include=*.tsx",
],
{ cwd: REPO_ROOT, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean)
.filter((f) => !f.includes("__tests__"));
const offenders: string[] = [];
let calls = 0;
for (const file of files) {
const source = readFileSync(resolve(REPO_ROOT, file), "utf8");
CALL.lastIndex = 0;
for (let m = CALL.exec(source); m; m = CALL.exec(source)) {
const lineStart = source.lastIndexOf("\n", m.index) + 1;
if (/^\s*(?:\/\/|\*|\/\*)/.test(source.slice(lineStart, m.index)))
continue;
calls += 1;
const args = argumentList(source, m.index + m[0].length - 1);
if (topLevelArgCount(args) < 3) {
offenders.push(
`${file}:${source.slice(0, m.index).split("\n").length}`,
);
}
}
}
return { offenders, calls };
}
it("still sees the cache's calls in components (the matcher matches)", () => {
expect(scan().calls).toBeGreaterThanOrEqual(1);
});
it("has no two-argument getDateTimeFormat in src/components", () => {
expect(scan().offenders).toEqual([]);
});
it("the matcher flags the two-argument form across lines", () => {
const source =
'getDateTimeFormat(\n locale,\n { timeZone, month: "short" },\n)';
CALL.lastIndex = 0;
const m = CALL.exec(source)!;
expect(
topLevelArgCount(argumentList(source, m.index + m[0].length - 1)),
).toBe(2);
});
});
/**
* A stated calendar date must not go through a zone-aware short formatter.
*