chore(format): prettier sweep across develop

Mostly table column normalisation under docs/audit/ + planning, object-
literal expansion in src/lib/ai/coach/snapshot.ts after the W4 F6 Apple
Health metric block landed, and small wrap/unwrap line adjustments in
the device + APNs routes / tests touched across W3/W4/W5 sessions.
No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-11 08:27:51 +02:00
1 parent 5dcd3954a0
commit f1e6630958
70 files changed
+899 -660

No files matched your search

@@ -60,22 +60,30 @@ function req(): NextRequest {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(prisma.refreshToken.findMany).mockResolvedValue([]);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({ count: 0 } as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({ count: 0 } as never);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({
count: 0,
} as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({
count: 0,
} as never);
vi.mocked(prisma.$transaction).mockResolvedValue([] as never);
});
describe("DELETE /api/auth/me/devices/[id]", () => {
it("returns 401 when unauthenticated", async () => {
vi.mocked(getSession).mockResolvedValue(null);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-1" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-1" }),
});
expect(res.status).toBe(401);
});
it("returns 404 when the device doesn't exist", async () => {
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
vi.mocked(prisma.device.findUnique).mockResolvedValue(null);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-x" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-x" }),
});
expect(res.status).toBe(404);
});
@@ -87,7 +95,9 @@ describe("DELETE /api/auth/me/devices/[id]", () => {
model: "iPhone",
bundleId: "io.healthlog.app",
} as never);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-x" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-x" }),
});
expect(res.status).toBe(404);
expect(vi.mocked(prisma.device.delete)).not.toHaveBeenCalled();
});
@@ -105,11 +115,17 @@ describe("DELETE /api/auth/me/devices/[id]", () => {
{ accessTokenHash: "hash:a2" },
{ accessTokenHash: null },
] as never);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({ count: 3 } as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({ count: 2 } as never);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({
count: 3,
} as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({
count: 2,
} as never);
vi.mocked(prisma.device.delete).mockResolvedValue({ id: "dev-1" } as never);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-1" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-1" }),
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.data.revoked).toBe(true);
@@ -120,7 +136,8 @@ describe("DELETE /api/auth/me/devices/[id]", () => {
});
// The refresh-token revoke filter MUST scope to the deviceId, not
// every refresh row the user has.
const updateCall = vi.mocked(prisma.refreshToken.updateMany).mock.calls[0]?.[0];
const updateCall = vi.mocked(prisma.refreshToken.updateMany).mock
.calls[0]?.[0];
expect(updateCall?.where).toMatchObject({
userId: "user-1",
deviceId: "dev-1",
+5 -1
View File
@@ -55,7 +55,11 @@ export const DELETE = apiHandler(
},
});
annotate({
action: { name: "auth.me.devices.revoke", entity_type: "device", entity_id: result.id },
action: {
name: "auth.me.devices.revoke",
entity_type: "device",
entity_id: result.id,
},
meta: {
refresh_tokens_revoked: result.refreshTokensRevoked,
access_tokens_revoked: result.accessTokensRevoked,
@@ -60,15 +60,21 @@ function req(): NextRequest {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(prisma.refreshToken.findMany).mockResolvedValue([]);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({ count: 0 } as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({ count: 0 } as never);
vi.mocked(prisma.refreshToken.updateMany).mockResolvedValue({
count: 0,
} as never);
vi.mocked(prisma.apiToken.updateMany).mockResolvedValue({
count: 0,
} as never);
vi.mocked(prisma.$transaction).mockResolvedValue([] as never);
});
describe("DELETE /api/devices/[id] (iOS token-rotation endpoint)", () => {
it("returns 401 when unauthenticated", async () => {
vi.mocked(getSession).mockResolvedValue(null);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-1" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-1" }),
});
expect(res.status).toBe(401);
});
@@ -80,7 +86,9 @@ describe("DELETE /api/devices/[id] (iOS token-rotation endpoint)", () => {
model: "iPhone",
bundleId: "io.healthlog.app",
} as never);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-x" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-x" }),
});
expect(res.status).toBe(404);
});
@@ -97,7 +105,9 @@ describe("DELETE /api/devices/[id] (iOS token-rotation endpoint)", () => {
] as never);
vi.mocked(prisma.device.delete).mockResolvedValue({ id: "dev-1" } as never);
const res = await DELETE(req(), { params: Promise.resolve({ id: "dev-1" }) });
const res = await DELETE(req(), {
params: Promise.resolve({ id: "dev-1" }),
});
expect(res.status).toBe(200);
expect(vi.mocked(prisma.device.delete)).toHaveBeenCalled();
});
+5 -1
View File
@@ -46,7 +46,11 @@ export const DELETE = apiHandler(
},
});
annotate({
action: { name: "devices.revoke", entity_type: "device", entity_id: result.id },
action: {
name: "devices.revoke",
entity_type: "device",
entity_id: result.id,
},
meta: {
refresh_tokens_revoked: result.refreshTokensRevoked,
access_tokens_revoked: result.accessTokensRevoked,
+1 -4
View File
@@ -114,10 +114,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
deviceId: existingApns.id,
},
});
return apiError(
"APNs token already registered to another account",
409,
);
return apiError("APNs token already registered to another account", 409);
}
}
+9 -6
View File
@@ -25,7 +25,12 @@ import { prisma } from "@/lib/db";
import { apiHandler, requireAuth } from "@/lib/api-handler";
import { annotate } from "@/lib/logging/context";
import { auditLog } from "@/lib/auth/audit";
import { apiError, apiSuccess, getClientIp, safeJson } from "@/lib/api-response";
import {
apiError,
apiSuccess,
getClientIp,
safeJson,
} from "@/lib/api-response";
import { withIdempotency } from "@/lib/idempotency";
import { mapAppleHealthEntry } from "@/lib/measurements/apple-health-mapping";
import { validateMeasurementRange } from "@/lib/validations/measurement";
@@ -84,11 +89,9 @@ async function postBatch(request: NextRequest): Promise<Response> {
Array.isArray((rawBody as { entries: unknown }).entries) &&
(rawBody as { entries: unknown[] }).entries.length > MAX_BATCH_ENTRIES
) {
return apiError(
`Batch exceeds the ${MAX_BATCH_ENTRIES}-entry limit`,
422,
{ errorCode: "coach.batch.too_large" },
);
return apiError(`Batch exceeds the ${MAX_BATCH_ENTRIES}-entry limit`, 422, {
errorCode: "coach.batch.too_large",
});
}
const parsed = batchPayloadSchema.safeParse(rawBody);
@@ -97,7 +97,10 @@ describe("<AdminShell>", () => {
// missing from the nav even though the slug + page + i18n shipped.
const navSlugs = new Set(ADMIN_SECTIONS.map((s) => s.slug));
for (const slug of ADMIN_SECTION_SLUGS) {
expect(navSlugs.has(slug), `ADMIN_SECTIONS missing nav entry for ${slug}`).toBe(true);
expect(
navSlugs.has(slug),
`ADMIN_SECTIONS missing nav entry for ${slug}`,
).toBe(true);
}
});
@@ -124,7 +127,9 @@ describe("<AdminShell>", () => {
// filesystem read isn't accidentally noop'd by a future tree-
// shaker pass.
expect(
statSync(join(process.cwd(), "src/app/admin/[section]/page.tsx")).isFile(),
statSync(
join(process.cwd(), "src/app/admin/[section]/page.tsx"),
).isFile(),
).toBe(true);
});
@@ -109,7 +109,9 @@ export function nextResettableValue<T>(
previous: T,
incoming: T,
): { reset: true; value: T } | { reset: false } {
return Object.is(previous, incoming) ? { reset: false } : { reset: true, value: incoming };
return Object.is(previous, incoming)
? { reset: false }
: { reset: true, value: incoming };
}
export function CoachDrawer({
@@ -130,7 +130,7 @@ export function CoachSettingsSheet({
<SheetContent
side="right"
data-slot="coach-settings-sheet"
className="w-full p-0 sm:max-w-[420px] flex h-[100dvh] flex-col gap-0"
className="flex h-[100dvh] w-full flex-col gap-0 p-0 sm:max-w-[420px]"
>
<SheetHeader className="border-border/70 border-b p-4 pr-12">
<SheetTitle className="text-sm font-semibold">
@@ -171,10 +171,7 @@ export function CoachSettingsSheet({
<div className="flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto p-4">
{/* Tone */}
<div className="flex flex-col gap-2">
<Label
htmlFor="coach-prefs-tone"
className="text-xs font-medium"
>
<Label htmlFor="coach-prefs-tone" className="text-xs font-medium">
{t("insights.coach.settingsToneLabel")}
</Label>
<Select
@@ -267,9 +264,7 @@ export function CoachSettingsSheet({
id={id}
data-slot={`coach-prefs-exclude-${metric}`}
checked={checked}
onCheckedChange={(next) =>
toggleExclude(metric, next)
}
onCheckedChange={(next) => toggleExclude(metric, next)}
/>
</li>
);
@@ -323,10 +318,7 @@ export function CoachSettingsSheet({
data-slot="coach-prefs-save"
>
{save.isPending ? (
<Loader2
className="size-3.5 animate-spin"
aria-hidden="true"
/>
<Loader2 className="size-3.5 animate-spin" aria-hidden="true" />
) : null}
{t("insights.coach.settingsSave")}
</Button>
@@ -444,10 +444,7 @@ function CoachMessageFeedback({ messageId }: CoachMessageFeedbackProps) {
}
return (
<div
data-slot="coach-message-feedback"
className="flex items-center gap-2"
>
<div data-slot="coach-message-feedback" className="flex items-center gap-2">
<button
type="button"
data-slot="coach-message-feedback-helpful"
@@ -94,9 +94,7 @@ describe("MeasurementList — APPLE_HEALTH badge", () => {
// duplicate hand-entries on top of synced rows. Both branches must
// carry the badge so the surface is honest at every viewport.
const html = render("en");
const matches = html.match(
/data-testid="measurement-source-badge"/g,
);
const matches = html.match(/data-testid="measurement-source-badge"/g);
expect(matches?.length ?? 0).toBe(2);
});
});
@@ -52,7 +52,9 @@ describe("getStrictInsightsSystemPrompt — EN", () => {
});
it("documents the additive sourceMetric tokens in the OUTPUT FORMAT", () => {
expect(prompt).toMatch(/hrv \| sleep \| resting_hr \| steps \| active_energy/);
expect(prompt).toMatch(
/hrv \| sleep \| resting_hr \| steps \| active_energy/,
);
});
});
@@ -70,6 +72,8 @@ describe("getStrictInsightsSystemPrompt — DE", () => {
});
it("documents the additive sourceMetric tokens in the German OUTPUT FORMAT", () => {
expect(prompt).toMatch(/hrv \| sleep \| resting_hr \| steps \| active_energy/);
expect(prompt).toMatch(
/hrv \| sleep \| resting_hr \| steps \| active_energy/,
);
});
});
+54 -9
View File
@@ -523,15 +523,60 @@ export async function buildCoachSnapshot(
// Apple Health source maps to exactly one MeasurementType (BP is
// the only fan-out and lives in its own legacy block above).
const appleHealthBlocks: AppleHealthBlock[] = [
{ metric: "hrv", snapshotKey: "heartRateVariability", type: "HEART_RATE_VARIABILITY", enabled: sources.has("hrv") },
{ metric: "sleep", snapshotKey: "sleep", type: "SLEEP_DURATION", enabled: sources.has("sleep") },
{ metric: "resting_hr", snapshotKey: "restingHeartRate", type: "RESTING_HEART_RATE", enabled: sources.has("resting_hr") },
{ metric: "steps", snapshotKey: "steps", type: "ACTIVITY_STEPS", enabled: sources.has("steps") },
{ metric: "active_energy", snapshotKey: "activeEnergy", type: "ACTIVE_ENERGY_BURNED", enabled: sources.has("active_energy") },
{ metric: "flights", snapshotKey: "flightsClimbed", type: "FLIGHTS_CLIMBED", enabled: sources.has("flights") },
{ metric: "distance", snapshotKey: "walkingRunningDistance", type: "WALKING_RUNNING_DISTANCE", enabled: sources.has("distance") },
{ metric: "vo2_max", snapshotKey: "vo2Max", type: "VO2_MAX", enabled: sources.has("vo2_max") },
{ metric: "body_temp", snapshotKey: "bodyTemperature", type: "BODY_TEMPERATURE", enabled: sources.has("body_temp") },
{
metric: "hrv",
snapshotKey: "heartRateVariability",
type: "HEART_RATE_VARIABILITY",
enabled: sources.has("hrv"),
},
{
metric: "sleep",
snapshotKey: "sleep",
type: "SLEEP_DURATION",
enabled: sources.has("sleep"),
},
{
metric: "resting_hr",
snapshotKey: "restingHeartRate",
type: "RESTING_HEART_RATE",
enabled: sources.has("resting_hr"),
},
{
metric: "steps",
snapshotKey: "steps",
type: "ACTIVITY_STEPS",
enabled: sources.has("steps"),
},
{
metric: "active_energy",
snapshotKey: "activeEnergy",
type: "ACTIVE_ENERGY_BURNED",
enabled: sources.has("active_energy"),
},
{
metric: "flights",
snapshotKey: "flightsClimbed",
type: "FLIGHTS_CLIMBED",
enabled: sources.has("flights"),
},
{
metric: "distance",
snapshotKey: "walkingRunningDistance",
type: "WALKING_RUNNING_DISTANCE",
enabled: sources.has("distance"),
},
{
metric: "vo2_max",
snapshotKey: "vo2Max",
type: "VO2_MAX",
enabled: sources.has("vo2_max"),
},
{
metric: "body_temp",
snapshotKey: "bodyTemperature",
type: "BODY_TEMPERATURE",
enabled: sources.has("body_temp"),
},
];
for (const block of appleHealthBlocks) {
if (!block.enabled) continue;
+3 -3
View File
@@ -142,8 +142,8 @@ describe("revokeDeviceCascade", () => {
new Error("postgres-blip"),
);
await expect(
revokeDeviceCascade("user-1", "dev-3"),
).rejects.toThrow("postgres-blip");
await expect(revokeDeviceCascade("user-1", "dev-3")).rejects.toThrow(
"postgres-blip",
);
});
});
@@ -38,7 +38,8 @@ describe("APPLE_HEALTH_TYPE_MAP", () => {
});
it("converts oxygen saturation from 0..1 fraction to 0..100 percent", () => {
const mapping = APPLE_HEALTH_TYPE_MAP.HKQuantityTypeIdentifierOxygenSaturation;
const mapping =
APPLE_HEALTH_TYPE_MAP.HKQuantityTypeIdentifierOxygenSaturation;
expect(mapping.convertToDbUnit(0.97)).toBeCloseTo(97);
});
+1 -4
View File
@@ -12,10 +12,7 @@
* DB unit, and the aggregation hint all live alongside the identifier
* so future analytics work has a single discoverable surface.
*/
import type {
MeasurementType,
SleepStage,
} from "@/generated/prisma/client";
import type { MeasurementType, SleepStage } from "@/generated/prisma/client";
/**
* How a batch of HealthKit samples for the same identifier should be
@@ -74,8 +74,7 @@ const VALID_ENV = {
APNS_KEY_ID: "ABCDE12345",
APNS_TEAM_ID: "TEAM123456",
APNS_BUNDLE_ID: "test.healthlog.ios",
APNS_KEY:
"-----BEGIN PRIVATE KEY-----\\nMOCKKEY\\n-----END PRIVATE KEY-----",
APNS_KEY: "-----BEGIN PRIVATE KEY-----\\nMOCKKEY\\n-----END PRIVATE KEY-----",
};
function setEnv(over: Record<string, string | undefined> = {}): void {
@@ -269,7 +268,6 @@ describe("sendApnsPush — failure classification", () => {
expect(r.reason).toBe("apns_network_error");
expect(r.shouldDisable).toBeFalsy();
});
});
describe("sendApnsPush — config disabled paths", () => {
@@ -325,7 +323,11 @@ describe("sendViaApns — dispatcher fan-out", () => {
.mockResolvedValueOnce({
sent: [],
failed: [
{ device: "tok-b", status: 410, response: { reason: "Unregistered" } },
{
device: "tok-b",
status: 410,
response: { reason: "Unregistered" },
},
],
});
const r = await sendViaApns("u-1", {
@@ -349,13 +351,21 @@ describe("sendViaApns — dispatcher fan-out", () => {
.mockResolvedValueOnce({
sent: [],
failed: [
{ device: "tok-a", status: 410, response: { reason: "Unregistered" } },
{
device: "tok-a",
status: 410,
response: { reason: "Unregistered" },
},
],
})
.mockResolvedValueOnce({
sent: [],
failed: [
{ device: "tok-b", status: 400, response: { reason: "BadDeviceToken" } },
{
device: "tok-b",
status: 400,
response: { reason: "BadDeviceToken" },
},
],
});
const r = await sendViaApns("u-1", {
+1 -3
View File
@@ -126,9 +126,7 @@ export function loadApnsConfig(): ApnsConfig | null {
// configuring and stopped — surface that as a warning so the
// half-finished state is visible in logs.
const anySet = Boolean(keyId || teamId || bundleId || inlineKey || keyFile);
const allSet = Boolean(
keyId && teamId && bundleId && (inlineKey || keyFile),
);
const allSet = Boolean(keyId && teamId && bundleId && (inlineKey || keyFile));
if (!anySet) {
cachedConfig = null;
+9 -8
View File
@@ -58,9 +58,7 @@ function dataEnvelope<T extends z.ZodType>(payload: T, id: string) {
.object({
data: payload,
error: z.null(),
meta: z
.object({ requestId: z.string().optional() })
.optional(),
meta: z.object({ requestId: z.string().optional() }).optional(),
})
.meta({ id });
}
@@ -118,7 +116,10 @@ const coachMessageFeedbackBody = z
const passkeyLoginVerifyRequest = z
.object({
challengeId: z.string().min(1).describe("Server-issued WebAuthn challenge id."),
challengeId: z
.string()
.min(1)
.describe("Server-issued WebAuthn challenge id."),
credential: z
.object({
id: z.string(),
@@ -157,9 +158,7 @@ const refreshRequest = z
const accessRefreshBundle = z
.object({
user: z
.object({ id: z.string(), username: z.string() })
.optional(),
user: z.object({ id: z.string(), username: z.string() }).optional(),
token: z.string().describe("Access token (`hlk_<64hex>`)."),
tokenExpiresAt: z.iso.datetime({ offset: true }),
refreshToken: z
@@ -216,7 +215,9 @@ const batchEntrySchema = z
.string()
.min(1)
.max(120)
.describe("HealthKit identifier (e.g. `HKQuantityTypeIdentifierBodyMass`)."),
.describe(
"HealthKit identifier (e.g. `HKQuantityTypeIdentifierBodyMass`).",
),
value: z.number().finite(),
unit: z.string().min(1).max(60),
startDate: z.iso.datetime({ offset: true }),