chore(style): repo-wide prettier sweep before v1.4.6 tag

Long-overdue prettier sweep; prettier had drifted vs. checked-in code
across ~130 files (mostly long-argument line-break wrapping in TS and
table-padding in markdown). Pure formatting — no semantic changes —
and required to clear the v1.4.6 release gate's `pnpm format:check`.

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-09 02:27:17 +02:00
1 parent 67575180b8
commit 02b99553b9
135 files changed
+2159 -1583

No files matched your search

+69 -17
View File
@@ -8,7 +8,9 @@ const DE_PATH = join(ROOT, "messages/de.json");
// JSON.parse silently keeps the last value when an object has duplicate keys,
// which lets shadowed keys hide bugs. Walk the source manually to surface them.
function findDuplicateKeys(src: string): { path: string; line: number; firstLine: number }[] {
function findDuplicateKeys(
src: string,
): { path: string; line: number; firstLine: number }[] {
const dups: { path: string; line: number; firstLine: number }[] = [];
const pathStack: string[] = [];
let i = 0;
@@ -22,8 +24,10 @@ function findDuplicateKeys(src: string): { path: string; line: number; firstLine
while (i < src.length) {
const c = src[i];
if (c === " " || c === "\t" || c === "\r") i++;
else if (c === "\n") { i++; line++; }
else break;
else if (c === "\n") {
i++;
line++;
} else break;
}
}
@@ -55,23 +59,46 @@ function findDuplicateKeys(src: string): { path: string; line: number; firstLine
const c = src[i];
if (c === "{") return parseObject();
if (c === "[") return parseArray();
if (c === '"') { readString(); return; }
if (c === '"') {
readString();
return;
}
while (i < src.length) {
const ch = src[i];
if (ch === "," || ch === "}" || ch === "]" || ch === " " || ch === "\n" || ch === "\t" || ch === "\r") break;
if (
ch === "," ||
ch === "}" ||
ch === "]" ||
ch === " " ||
ch === "\n" ||
ch === "\t" ||
ch === "\r"
)
break;
if (ch === "\n") line++;
i++;
}
}
function parseArray() {
i++; skipWs();
if (src[i] === "]") { i++; return; }
i++;
skipWs();
if (src[i] === "]") {
i++;
return;
}
while (true) {
parseValue();
skipWs();
if (src[i] === ",") { i++; skipWs(); continue; }
if (src[i] === "]") { i++; return; }
if (src[i] === ",") {
i++;
skipWs();
continue;
}
if (src[i] === "]") {
i++;
return;
}
err("expected , or ] in array");
}
}
@@ -80,13 +107,20 @@ function findDuplicateKeys(src: string): { path: string; line: number; firstLine
i++;
const seen = new Map<string, number>();
skipWs();
if (src[i] === "}") { i++; return; }
if (src[i] === "}") {
i++;
return;
}
while (true) {
skipWs();
const { value: key, line: keyLine } = readString();
const seenAt = seen.get(key);
if (seenAt != null) {
dups.push({ path: [...pathStack, key].join("."), line: keyLine, firstLine: seenAt });
dups.push({
path: [...pathStack, key].join("."),
line: keyLine,
firstLine: seenAt,
});
} else {
seen.set(key, keyLine);
}
@@ -97,8 +131,14 @@ function findDuplicateKeys(src: string): { path: string; line: number; firstLine
parseValue();
pathStack.pop();
skipWs();
if (src[i] === ",") { i++; continue; }
if (src[i] === "}") { i++; return; }
if (src[i] === ",") {
i++;
continue;
}
if (src[i] === "}") {
i++;
return;
}
err("expected , or } in object");
}
}
@@ -114,7 +154,10 @@ describe("i18n locale file integrity", () => {
dups,
`Duplicate keys silently shadow earlier definitions:\n` +
dups
.map((d) => ` ${d.path} at line ${d.line} (first defined at line ${d.firstLine})`)
.map(
(d) =>
` ${d.path} at line ${d.line} (first defined at line ${d.firstLine})`,
)
.join("\n"),
).toEqual([]);
});
@@ -125,14 +168,23 @@ describe("i18n locale file integrity", () => {
dups,
`Duplicate keys silently shadow earlier definitions:\n` +
dups
.map((d) => ` ${d.path} at line ${d.line} (first defined at line ${d.firstLine})`)
.map(
(d) =>
` ${d.path} at line ${d.line} (first defined at line ${d.firstLine})`,
)
.join("\n"),
).toEqual([]);
});
it("en and de share the same key shape (locale parity)", () => {
const en = JSON.parse(readFileSync(EN_PATH, "utf8")) as Record<string, unknown>;
const de = JSON.parse(readFileSync(DE_PATH, "utf8")) as Record<string, unknown>;
const en = JSON.parse(readFileSync(EN_PATH, "utf8")) as Record<
string,
unknown
>;
const de = JSON.parse(readFileSync(DE_PATH, "utf8")) as Record<
string,
unknown
>;
function flatten(obj: unknown, prefix: string, out: string[]) {
if (obj == null || typeof obj !== "object") return;
+10 -9
View File
@@ -205,9 +205,11 @@ describe("withIdempotency", () => {
describe("defaultUserIdResolver (audit C-4)", () => {
function mockHeader(value: string | null) {
vi.mocked(headers).mockResolvedValue({
get: vi.fn().mockImplementation((name: string) =>
name.toLowerCase() === "authorization" ? value : null,
),
get: vi
.fn()
.mockImplementation((name: string) =>
name.toLowerCase() === "authorization" ? value : null,
),
} as unknown as ReturnType<typeof headers> extends Promise<infer T>
? T
: never);
@@ -291,9 +293,7 @@ describe("withIdempotency body-content exclusion (P12)", () => {
'{"data":{"echoed":"sk-ant-api03-xyz"},"error":null}',
],
])("does NOT cache responses containing %s", async (_label, body) => {
const handler = vi.fn(
async () => new NextResponse(body, { status: 201 }),
);
const handler = vi.fn(async () => new NextResponse(body, { status: 201 }));
const wrapped = withIdempotency<[NextRequest]>(handler, async () => "u-1");
await wrapped(makeRequest("POST", { "idempotency-key": "abc-12345678" }));
expect(handler).toHaveBeenCalledTimes(1);
@@ -310,10 +310,11 @@ describe("withIdempotency body-content exclusion (P12)", () => {
const handler = vi.fn(
async () => new NextResponse(body, { status: 422 }),
);
const wrapped = withIdempotency<[NextRequest]>(handler, async () => "u-1");
await wrapped(
makeRequest("POST", { "idempotency-key": "key-12345678" }),
const wrapped = withIdempotency<[NextRequest]>(
handler,
async () => "u-1",
);
await wrapped(makeRequest("POST", { "idempotency-key": "key-12345678" }));
expect(prisma.idempotencyKey.create).toHaveBeenCalledTimes(1);
},
);
+3 -1
View File
@@ -68,7 +68,9 @@ describe("moodLog secret encrypt-at-rest", () => {
for (const update of updates) {
// Each rotation must produce a value that decrypts back to the
// original legacy plaintext — no data loss.
const original = rows.find((r) => r.id === update.id)!.moodLogWebhookSecret;
const original = rows.find(
(r) => r.id === update.id,
)!.moodLogWebhookSecret;
expect(readMoodLogSecret(update.encrypted)).toBe(original);
}
+1 -3
View File
@@ -26,9 +26,7 @@ describe("queryKeys factory", () => {
describe("dependent-key bundles", () => {
it("measurementDependentKeys invalidates analytics/insights/targets", () => {
const keyStrings = measurementDependentKeys.map((k) =>
JSON.stringify(k),
);
const keyStrings = measurementDependentKeys.map((k) => JSON.stringify(k));
expect(keyStrings).toContain(JSON.stringify(["measurements"]));
expect(keyStrings).toContain(JSON.stringify(["analytics"]));
expect(keyStrings).toContain(JSON.stringify(["insights"]));
@@ -33,7 +33,11 @@ vi.mock("@/lib/logging/transports", () => ({
const headersGet = vi.fn<(name: string) => string | null>();
vi.mock("next/headers", () => ({
headers: vi.fn(async () => ({ get: headersGet })),
cookies: vi.fn(async () => ({ get: () => undefined, set: () => {}, delete: () => {} })),
cookies: vi.fn(async () => ({
get: () => undefined,
set: () => {},
delete: () => {},
})),
}));
// --- Imports use the mocked modules above. ---
+3 -3
View File
@@ -14,9 +14,9 @@ describe("getServerTranslator", () => {
it("interpolates {param} placeholders", () => {
const { t } = getServerTranslator("en");
expect(t("telegram.snoozedFor", { name: "Ramipril", duration: "1 hour" })).toBe(
"Ramipril snoozed for 1 hour.",
);
expect(
t("telegram.snoozedFor", { name: "Ramipril", duration: "1 hour" }),
).toBe("Ramipril snoozed for 1 hour.");
});
it("falls back to English when a key is missing in the German bundle", () => {
@@ -47,9 +47,7 @@ describe("AnthropicClient", () => {
expect(body.temperature).toBe(0.4);
// System prompt is a separate top-level field, NOT a message.
expect(body.system).toBe("You are a doctor.");
expect(body.messages).toEqual([
expect.objectContaining({ role: "user" }),
]);
expect(body.messages).toEqual([expect.objectContaining({ role: "user" })]);
// User message should be wrapped to coerce JSON output.
expect(body.messages[0].content).toContain("Analyze this data.");
expect(body.messages[0].content.toLowerCase()).toContain("json");
@@ -87,7 +85,7 @@ describe("AnthropicClient", () => {
status: 529,
text: () =>
Promise.resolve(
'overloaded; debug=sk-ant-supersecretkey1234567890 leaked',
"overloaded; debug=sk-ant-supersecretkey1234567890 leaked",
),
}),
);
+16 -4
View File
@@ -12,7 +12,10 @@ describe("CodexClient", () => {
json: () =>
Promise.resolve({
output: [
{ type: "message", content: [{ type: "output_text", text: '{"summary":"ok"}' }] },
{
type: "message",
content: [{ type: "output_text", text: '{"summary":"ok"}' }],
},
],
usage: { total_tokens: 50 },
}),
@@ -44,13 +47,20 @@ describe("CodexClient", () => {
const mockFetch = vi
.fn()
.mockResolvedValueOnce({ ok: false, status: 401, text: () => Promise.resolve("unauthorized") })
.mockResolvedValueOnce({
ok: false,
status: 401,
text: () => Promise.resolve("unauthorized"),
})
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve({
output: [
{ type: "message", content: [{ type: "output_text", text: '{"test":true}' }] },
{
type: "message",
content: [{ type: "output_text", text: '{"test":true}' }],
},
],
usage: { total_tokens: 10 },
}),
@@ -69,7 +79,9 @@ describe("CodexClient", () => {
expect(onRefresh).toHaveBeenCalledOnce();
expect(result.content).toBe('{"test":true}');
expect(mockFetch.mock.calls[1][1].headers.Authorization).toBe(`Bearer ${newToken}`);
expect(mockFetch.mock.calls[1][1].headers.Authorization).toBe(
`Bearer ${newToken}`,
);
});
it("throws after retry if still 401", async () => {
+3 -1
View File
@@ -69,7 +69,9 @@ describe("codex-oauth", () => {
expect(parsed.searchParams.get("code_challenge")).toBe("test-challenge");
expect(parsed.searchParams.get("code_challenge_method")).toBe("S256");
expect(parsed.searchParams.get("state")).toBe("test-state");
expect(parsed.searchParams.get("redirect_uri")).toBe("https://example.com/callback");
expect(parsed.searchParams.get("redirect_uri")).toBe(
"https://example.com/callback",
);
});
it("throws CodexOAuthNotConfiguredError when CODEX_OAUTH_CLIENT_ID is unset", () => {
+1 -1
View File
@@ -103,7 +103,7 @@ describe("LocalOpenAICompatibleClient", () => {
status: 500,
text: () =>
Promise.resolve(
'upstream rejected: Bearer sk-localabcdef12345 hit /v1',
"upstream rejected: Bearer sk-localabcdef12345 hit /v1",
),
}),
);
+4 -1
View File
@@ -79,7 +79,10 @@ describe("OpenAIClient", () => {
baseUrl: "https://openrouter.ai/api/v1",
});
await client.generateCompletion({ systemPrompt: "test", userPrompt: "test" });
await client.generateCompletion({
systemPrompt: "test",
userPrompt: "test",
});
expect(mockFetch.mock.calls[0][0]).toBe(
"https://openrouter.ai/api/v1/chat/completions",
+1 -4
View File
@@ -22,10 +22,7 @@ vi.mock("@/lib/ai/codex-oauth", () => ({
import { resolveProvider } from "../provider";
import { prisma } from "@/lib/db";
import { decrypt, encrypt } from "@/lib/crypto";
import {
decryptTokens,
encryptTokens,
} from "@/lib/ai/codex-oauth";
import { decryptTokens, encryptTokens } from "@/lib/ai/codex-oauth";
/**
* resolveProvider() makes up to TWO findUnique calls per invocation:
+8 -5
View File
@@ -26,8 +26,13 @@ export class AnthropicClient implements AIProvider {
this.config = config;
}
async generateCompletion(params: CompletionParams): Promise<CompletionResult> {
const baseUrl = (this.config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
async generateCompletion(
params: CompletionParams,
): Promise<CompletionResult> {
const baseUrl = (this.config.baseUrl ?? DEFAULT_BASE_URL).replace(
/\/$/,
"",
);
const url = `${baseUrl}/messages`;
const res = await fetch(url, {
@@ -42,9 +47,7 @@ export class AnthropicClient implements AIProvider {
max_tokens: params.maxTokens ?? 1000,
temperature: params.temperature ?? 0.3,
system: params.systemPrompt,
messages: [
{ role: "user", content: wrapForJson(params.userPrompt) },
],
messages: [{ role: "user", content: wrapForJson(params.userPrompt) }],
}),
});
+9 -4
View File
@@ -18,7 +18,9 @@ export class CodexClient implements AIProvider {
this.onTokenRefresh = config.onTokenRefresh;
}
async generateCompletion(params: CompletionParams): Promise<CompletionResult> {
async generateCompletion(
params: CompletionParams,
): Promise<CompletionResult> {
const firstAttempt = await this.doRequest(params);
if (firstAttempt.status === 401) {
@@ -85,9 +87,12 @@ export class CodexClient implements AIProvider {
private async parseResponse(res: Response): Promise<CompletionResult> {
const json = await res.json();
const messageOutput = (json.output as Array<{ type: string; content?: Array<{ type: string; text?: string }> }>)?.find(
(o) => o.type === "message",
);
const messageOutput = (
json.output as Array<{
type: string;
content?: Array<{ type: string; text?: string }>;
}>
)?.find((o) => o.type === "message");
const textContent = messageOutput?.content?.find(
(c) => c.type === "output_text",
);
+3 -1
View File
@@ -88,7 +88,9 @@ export async function exchangeCodeForTokens(params: {
return res.json();
}
export async function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {
export async function refreshAccessToken(
refreshToken: string,
): Promise<TokenResponse> {
const res = await fetch("https://chatgpt.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
+3 -1
View File
@@ -21,7 +21,9 @@ export class LocalOpenAICompatibleClient implements AIProvider {
this.config = config;
}
async generateCompletion(params: CompletionParams): Promise<CompletionResult> {
async generateCompletion(
params: CompletionParams,
): Promise<CompletionResult> {
const url = `${this.config.baseUrl.replace(/\/$/, "")}/chat/completions`;
const headers: Record<string, string> = {
+3 -1
View File
@@ -14,7 +14,9 @@ export class OpenAIClient implements AIProvider {
this.config = config;
}
async generateCompletion(params: CompletionParams): Promise<CompletionResult> {
async generateCompletion(
params: CompletionParams,
): Promise<CompletionResult> {
const url = `${this.config.baseUrl.replace(/\/$/, "")}/chat/completions`;
const res = await fetch(url, {
+13 -2
View File
@@ -24,7 +24,13 @@ export const insightDataQualitySchema = z.object({
export const insightResultSchema = z.object({
insightType: z.string().optional(),
summary: z.string(),
classification: z.enum(["optimal", "gut", "grenzwertig", "erhoht", "kritisch"]),
classification: z.enum([
"optimal",
"gut",
"grenzwertig",
"erhoht",
"kritisch",
]),
classificationLabel: z.string().optional(),
findings: z.array(insightFindingSchema),
correlations: z.array(insightCorrelationSchema),
@@ -39,7 +45,12 @@ export type InsightFinding = z.infer<typeof insightFindingSchema>;
// ─── Provider Types ────────────────────────────────────────
export type ProviderType = "codex" | "admin-key" | "anthropic" | "local" | "none";
export type ProviderType =
| "codex"
| "admin-key"
| "anthropic"
| "local"
| "none";
export interface AIProvider {
type: ProviderType;
@@ -76,8 +76,16 @@ describe("getEffectiveRange", () => {
});
it("has different defaults for postprandial vs fasting", () => {
const fasting = getEffectiveRange("BLOOD_GLUCOSE_FASTING", baseProfile, null);
const post = getEffectiveRange("BLOOD_GLUCOSE_POSTPRANDIAL", baseProfile, null);
const fasting = getEffectiveRange(
"BLOOD_GLUCOSE_FASTING",
baseProfile,
null,
);
const post = getEffectiveRange(
"BLOOD_GLUCOSE_POSTPRANDIAL",
baseProfile,
null,
);
expect(post.range!.greenMax).toBeGreaterThan(fasting.range!.greenMax);
});
});
+15 -3
View File
@@ -108,7 +108,11 @@ export function classifyPulseByTarget(
target: PersonalizedPulseTarget,
): PulseTargetClassification {
if (bpm < target.orangeMin) {
return { category: "Significantly low", color: "#8be9fd", severity: "info" };
return {
category: "Significantly low",
color: "#8be9fd",
severity: "info",
};
}
if (bpm < target.greenMin) {
return {
@@ -121,7 +125,15 @@ export function classifyPulseByTarget(
return { category: "On target", color: "#50fa7b", severity: "normal" };
}
if (bpm <= target.orangeMax) {
return { category: "Slightly elevated", color: "#ffb86c", severity: "warning" };
return {
category: "Slightly elevated",
color: "#ffb86c",
severity: "warning",
};
}
return { category: "Significantly elevated", color: "#ff5555", severity: "danger" };
return {
category: "Significantly elevated",
color: "#ff5555",
severity: "danger",
};
}
+3 -1
View File
@@ -66,7 +66,9 @@ export async function getReminderThresholds(): Promise<ReminderThresholds> {
missedMinutes: settings?.reminderMissedMinutes ?? 240,
};
} catch {
getEvent()?.addWarning("Failed to load reminder thresholds, using defaults");
getEvent()?.addWarning(
"Failed to load reminder thresholds, using defaults",
);
return { lateMinutes: 120, missedMinutes: 240 };
}
}
+5 -1
View File
@@ -42,7 +42,11 @@ export async function getSession(): Promise<{
try {
await ensureDbCompatibility();
} catch (error) {
getEvent()?.setError(error instanceof Error ? error : new Error("DB compatibility check failed"));
getEvent()?.setError(
error instanceof Error
? error
: new Error("DB compatibility check failed"),
);
}
const cookieStore = await cookies();
+3 -1
View File
@@ -124,7 +124,9 @@ export function bucketTimeSeries(
points: BucketInputPoint[],
options: BucketTimeSeriesOptions = {},
): BucketedChartSeries {
const sorted = [...points].sort((a, b) => toMs(a.timestamp) - toMs(b.timestamp));
const sorted = [...points].sort(
(a, b) => toMs(a.timestamp) - toMs(b.timestamp),
);
const bucket =
options.bucket ??
+17 -13
View File
@@ -59,7 +59,8 @@ export async function getGithubConfig(): Promise<{
}
const token = configuredToken || process.env.GITHUB_ISSUE_TOKEN || "";
const repo = appSettings?.githubIssueRepo || process.env.GITHUB_ISSUE_REPO || "";
const repo =
appSettings?.githubIssueRepo || process.env.GITHUB_ISSUE_REPO || "";
if (!token || !repo) return null;
return { token, repo };
@@ -134,19 +135,22 @@ export async function publishFeedbackToGithub(
const title = `[${categoryLabel}] ${safeSubject || `Feedback – ${dateStr}`}`;
const res = await fetch(`https://api.github.com/repos/${config.repo}/issues`, {
method: "POST",
headers: {
Authorization: `Bearer ${config.token}`,
"Content-Type": "application/json",
Accept: "application/vnd.github.v3+json",
const res = await fetch(
`https://api.github.com/repos/${config.repo}/issues`,
{
method: "POST",
headers: {
Authorization: `Bearer ${config.token}`,
"Content-Type": "application/json",
Accept: "application/vnd.github.v3+json",
},
body: JSON.stringify({
title,
body,
labels: [categoryLabel, "user-reported"],
}),
},
body: JSON.stringify({
title,
body,
labels: [categoryLabel, "user-reported"],
}),
});
);
if (!res.ok) {
const errBody = await res.text();
+4 -1
View File
@@ -90,7 +90,10 @@ export function I18nProvider({
// Prefer the server-resolved initial locale to eliminate the hydration
// flash where the server renders EN ("Loading…") and the client then
// flips to DE ("Laden…") once localStorage/cookie is read at mount.
if (initialLocale && (locales as readonly string[]).includes(initialLocale)) {
if (
initialLocale &&
(locales as readonly string[]).includes(initialLocale)
) {
return initialLocale;
}
return getSavedLocale() ?? detectSystemLocale();
+1 -2
View File
@@ -67,8 +67,7 @@ export async function resolveServerLocale(
if (isLocale(options.userLocale)) return options.userLocale;
const accept =
options.request?.headers.get("accept-language") ?? null;
const accept = options.request?.headers.get("accept-language") ?? null;
const fromHeader = pickLocaleFromAcceptLanguage(accept);
if (fromHeader) return fromHeader;
@@ -43,7 +43,7 @@ describe("generateMoodStatusForUser — v1.4.6 bucketed payload", () => {
// `date` string here is irrelevant for bucketing — keep it
// ISO-shaped so other downstream consumers don't choke.
date: t.toISOString().slice(0, 10),
score: 3 + ((day % 3) * 0.5),
score: 3 + (day % 3) * 0.5,
tags: [],
moodLoggedAt: t,
});
+3 -1
View File
@@ -60,7 +60,9 @@ OUTPUT FORMAT (JSON):
Reply with VALID JSON ONLY in the format above. All natural-language fields in English.`;
export function getInsightsSystemPrompt(locale: Locale): string {
return locale === "en" ? INSIGHTS_SYSTEM_PROMPT_EN : INSIGHTS_SYSTEM_PROMPT_DE;
return locale === "en"
? INSIGHTS_SYSTEM_PROMPT_EN
: INSIGHTS_SYSTEM_PROMPT_DE;
}
/** @deprecated Use getInsightsSystemPrompt(locale) instead. Kept for backwards compatibility. */
+3 -3
View File
@@ -30,9 +30,9 @@ describe("redactSecrets", () => {
});
it("redacts OpenAI and Anthropic API keys", () => {
expect(
redactSecrets("error: invalid key sk-1234567890abcdefABCDEF"),
).toBe("error: invalid key [REDACTED]");
expect(redactSecrets("error: invalid key sk-1234567890abcdefABCDEF")).toBe(
"error: invalid key [REDACTED]",
);
expect(
redactSecrets("Authorization: Bearer sk-ant-api03-xyzABC_42-token"),
).toBe("Authorization: Bearer [REDACTED]");
+2 -1
View File
@@ -12,7 +12,8 @@ export function shouldEmit(event: WideEvent): boolean {
if (!isLevelEnabled(event.level)) return false;
// Fehler und Warnungen immer behalten
if (LOG_LEVEL_PRIORITY[event.level] >= LOG_LEVEL_PRIORITY["warn"]) return true;
if (LOG_LEVEL_PRIORITY[event.level] >= LOG_LEVEL_PRIORITY["warn"])
return true;
// Langsame Requests immer behalten
const config = getLoggingConfig();
+7 -1
View File
@@ -51,7 +51,13 @@ export interface WideEvent {
auth?: {
user_id?: string;
user_role?: string;
auth_method?: "session" | "bearer" | "api_key" | "cron_secret" | "telegram_webhook" | "webhook_secret";
auth_method?:
| "session"
| "bearer"
| "api_key"
| "cron_secret"
| "telegram_webhook"
| "webhook_secret";
};
// Business-Daten (was wurde getan)
+3 -1
View File
@@ -51,7 +51,9 @@ export function isLegacyPlaintext(stored: string | null): boolean {
}
interface MoodLogSecretMigrationDeps {
findLegacy: () => Promise<Array<{ id: string; moodLogWebhookSecret: string | null }>>;
findLegacy: () => Promise<
Array<{ id: string; moodLogWebhookSecret: string | null }>
>;
rotate: (id: string, encryptedSecret: string) => Promise<void>;
}
+5 -1
View File
@@ -84,7 +84,11 @@ export async function sendViaWebPush(
return anySuccess;
} catch (err) {
getEvent()?.setError(err instanceof Error ? err : new Error("[web-push] sendViaWebPush failed"));
getEvent()?.setError(
err instanceof Error
? err
: new Error("[web-push] sendViaWebPush failed"),
);
return false;
}
}
+1 -3
View File
@@ -25,9 +25,7 @@ export async function checkRateLimit(
): Promise<RateLimitResult> {
const windowInterval = `${windowMs} milliseconds`;
const rows = await prisma.$queryRaw<
{ count: number; reset_at: Date }[]
>`
const rows = await prisma.$queryRaw<{ count: number; reset_at: Date }[]>`
INSERT INTO rate_limits (key, count, reset_at)
VALUES (${key}, 1, NOW() + ${windowInterval}::interval)
ON CONFLICT (key) DO UPDATE SET
+15 -5
View File
@@ -82,7 +82,9 @@ export async function sendTelegramMessage(
...(options.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
});
if (!json.ok) {
getEvent()?.addWarning(`[telegram] sendMessage failed: ${json.description}`);
getEvent()?.addWarning(
`[telegram] sendMessage failed: ${json.description}`,
);
return { ok: false };
}
const messageId = (json.result as { message_id?: number })?.message_id;
@@ -99,7 +101,9 @@ export async function answerTelegramCallbackQuery(
...(text ? { text, show_alert: false } : {}),
});
if (!json.ok) {
getEvent()?.addWarning(`[telegram] answerCallbackQuery failed: ${json.description}`);
getEvent()?.addWarning(
`[telegram] answerCallbackQuery failed: ${json.description}`,
);
}
return json.ok;
}
@@ -116,7 +120,9 @@ export async function editMessageReplyMarkup(
reply_markup: replyMarkup ?? { inline_keyboard: [] },
});
if (!json.ok) {
getEvent()?.addWarning(`[telegram] editMessageReplyMarkup failed: ${json.description}`);
getEvent()?.addWarning(
`[telegram] editMessageReplyMarkup failed: ${json.description}`,
);
}
return json.ok;
}
@@ -148,7 +154,9 @@ export async function deleteMessage(
message_id: messageId,
});
if (!json.ok) {
getEvent()?.addWarning(`[telegram] deleteMessage failed: ${json.description}`);
getEvent()?.addWarning(
`[telegram] deleteMessage failed: ${json.description}`,
);
}
return json.ok;
}
@@ -160,7 +168,9 @@ export async function deleteTelegramWebhook(
drop_pending_updates: false,
});
if (!json.ok) {
getEvent()?.addWarning(`[telegram] deleteWebhook failed: ${json.description}`);
getEvent()?.addWarning(
`[telegram] deleteWebhook failed: ${json.description}`,
);
return false;
}
return true;
+8 -2
View File
@@ -12,7 +12,10 @@ export const registerSchema = z.object({
.regex(/^[a-zA-Z0-9_-]+$/, "Only letters, numbers, _ and -"),
password: z
.string()
.min(PASSWORD_MIN_LENGTH, `Password must be at least ${PASSWORD_MIN_LENGTH} characters`),
.min(
PASSWORD_MIN_LENGTH,
`Password must be at least ${PASSWORD_MIN_LENGTH} characters`,
),
});
export const loginPasswordSchema = z.object({
@@ -32,7 +35,10 @@ export const changePasswordSchema = z
currentPassword: z.string().min(1, "Current password is required"),
newPassword: z
.string()
.min(PASSWORD_MIN_LENGTH, `New password must be at least ${PASSWORD_MIN_LENGTH} characters`),
.min(
PASSWORD_MIN_LENGTH,
`New password must be at least ${PASSWORD_MIN_LENGTH} characters`,
),
confirmPassword: z.string().min(1, "Password confirmation is required"),
})
.refine((data) => data.newPassword === data.confirmPassword, {
+4 -1
View File
@@ -27,7 +27,10 @@ export const createFeedbackSchema = z.object({
screenshot: z
.string()
.max(7_000_000, "Screenshot too large (max 5 MB)")
.regex(SCREENSHOT_DATA_URL, "Screenshot must be a base64-encoded PNG/JPEG/WEBP/GIF data URL")
.regex(
SCREENSHOT_DATA_URL,
"Screenshot must be a base64-encoded PNG/JPEG/WEBP/GIF data URL",
)
.optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
+4 -1
View File
@@ -1,5 +1,8 @@
import { z } from "zod/v4";
import { METRIC_BOUNDS, type ThresholdMetric } from "@/lib/analytics/effective-range";
import {
METRIC_BOUNDS,
type ThresholdMetric,
} from "@/lib/analytics/effective-range";
const metricKeys = Object.keys(METRIC_BOUNDS) as ThresholdMetric[];