feat: warm-up deps — ts-reset, web-vitals, RHF devtool, query-broadcast

Four low-risk adds before the Zod 4 / drizzle-zod headliner:

- @total-typescript/ts-reset: tightens TS stdlib types globally (JSON.parse
  → unknown, fetch().json() → unknown, .filter(Boolean) narrows, Set
  literals respect typed Set targets). Caught 179 latent type errors;
  fixed all production sites (8 files) and added `any` cast escape hatch
  in test files (ESLint exemption scoped to tests/).
- web-vitals + /api/v1/internal/vitals endpoint + WebVitalsReporter
  client component: establishes Core Web Vitals baseline (LCP/INP/CLS/
  FCP/TTFB) via navigator.sendBeacon. Required before optimisation work.
- @hookform/devtools + FormDevtool wrapper: dev-only RHF state inspector,
  lazy-loaded via next/dynamic so the chunk is excluded from prod
  bundles entirely.
- @tanstack/query-broadcast-client-experimental: cross-tab cache sync
  via BroadcastChannel — wired in query-provider.tsx, 1-liner.

Audit doc updated with sections 35 + 36 (PDF stack overhaul + comprehensive
second-pass package sweep) covering ~20 package adoption candidates and
4-5 deprecation candidates.

Verified: tsc clean, vitest 1293/1293 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 18:16:18 +02:00
parent 82049eea92
commit d3960af340
38 changed files with 1590 additions and 119 deletions

View File

@@ -1,5 +1,5 @@
import { test, expect } from '@playwright/test';
import { login, navigateTo, PORT_SLUG } from './helpers';
import { login, navigateTo } from './helpers';
test.describe('AI Features', () => {
test.beforeEach(async ({ page }) => {
@@ -9,9 +9,12 @@ test.describe('AI Features', () => {
// Test 39: AI features are hidden when flag is off
test('AI score badge hidden when feature flag disabled', async ({ page }) => {
// First, ensure the flag is off by checking via API
const flagRes = await page.request.get('/api/v1/settings/feature-flag?key=ai_interest_scoring', {
headers: { 'X-Port-Id': '' }, // Will use session port
});
const flagRes = await page.request.get(
'/api/v1/settings/feature-flag?key=ai_interest_scoring',
{
headers: { 'X-Port-Id': '' }, // Will use session port
},
);
// Navigate to an interest
await navigateTo(page, '/interests');
@@ -27,11 +30,17 @@ test.describe('AI Features', () => {
const hotBadge = page.getByText(/^(Hot|Warm|Cool|Cold)$/).first();
if (flagRes.ok()) {
const flagData = await flagRes.json().catch(() => ({ enabled: false }));
const flagData = (await flagRes.json().catch(() => ({ enabled: false }))) as {
enabled?: boolean;
};
if (!flagData.enabled) {
// Score badge should NOT be visible
await expect(scoreBadge.first()).not.toBeVisible({ timeout: 3_000 }).catch(() => {});
await expect(hotBadge).not.toBeVisible({ timeout: 2_000 }).catch(() => {});
await expect(scoreBadge.first())
.not.toBeVisible({ timeout: 3_000 })
.catch(() => {});
await expect(hotBadge)
.not.toBeVisible({ timeout: 2_000 })
.catch(() => {});
}
}
}
@@ -48,7 +57,10 @@ test.describe('AI Features', () => {
const aiToggle = page.getByText(/ai.*scoring|interest.*scoring/i).first();
if (await aiToggle.isVisible({ timeout: 5_000 }).catch(() => false)) {
// Find the associated switch/toggle
const toggle = aiToggle.locator('..').locator('button[role="switch"], input[type="checkbox"]').first();
const toggle = aiToggle
.locator('..')
.locator('button[role="switch"], input[type="checkbox"]')
.first();
if (await toggle.isVisible({ timeout: 2_000 }).catch(() => false)) {
await toggle.click();
await page.waitForTimeout(2_000);
@@ -56,10 +68,12 @@ test.describe('AI Features', () => {
} else {
// If no UI for feature flags, try setting it via API
// This is an acceptable approach for testing
await page.request.put('/api/v1/settings/feature-flag', {
data: { key: 'ai_interest_scoring', value: true },
headers: { 'Content-Type': 'application/json' },
}).catch(() => {});
await page.request
.put('/api/v1/settings/feature-flag', {
data: { key: 'ai_interest_scoring', value: true },
headers: { 'Content-Type': 'application/json' },
})
.catch(() => {});
}
expect(true).toBeTruthy();
});
@@ -78,10 +92,10 @@ test.describe('AI Features', () => {
// Try calling the scoring API directly to verify it works
const scoreRes = await page.request.get('/api/v1/ai/interest-score/bulk');
if (scoreRes.ok()) {
const data = await scoreRes.json().catch(() => null);
const data = (await scoreRes.json().catch(() => null)) as any;
if (data && Array.isArray(data) && data.length > 0) {
// Verify scores are in 0-100 range
const score = data[0].score?.totalScore ?? data[0].totalScore;
const score = (data as any)[0].score?.totalScore ?? (data as any)[0].totalScore;
if (score !== undefined) {
expect(score).toBeGreaterThanOrEqual(0);
expect(score).toBeLessThanOrEqual(100);
@@ -97,7 +111,7 @@ test.describe('AI Features', () => {
// Test via API to avoid UI dependencies
const interests = await page.request.get(`/api/v1/interests?limit=1`).catch(() => null);
if (interests?.ok()) {
const data = await interests.json().catch(() => ({ data: [] }));
const data = (await interests.json().catch(() => ({ data: [] }))) as any;
const interest = data.data?.[0];
if (interest) {
@@ -115,7 +129,7 @@ test.describe('AI Features', () => {
expect([200, 202, 404].includes(draftRes.status())).toBeTruthy();
if (draftRes.status() === 202) {
const result = await draftRes.json();
const result = (await draftRes.json()) as any;
expect(result.jobId).toBeTruthy();
}
}

View File

@@ -44,7 +44,7 @@ async function signUpUser(email: string, password: string, name: string) {
});
if (res.ok) {
const data = await res.json();
const data = (await res.json()) as any;
return data.user?.id ?? data.id;
}
@@ -56,7 +56,7 @@ async function signUpUser(email: string, password: string, name: string) {
});
if (loginRes.ok) {
const loginData = await loginRes.json();
const loginData = (await loginRes.json()) as any;
return loginData.user?.id ?? loginData.id;
}