From 9bfabfb77a4f0d7b64de7f6b7793633224e7ca44 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 9 Aug 2026 13:03:34 +0530 Subject: [PATCH] Fix browser monitoring and deterministic builds --- docs/development/commands.md | 2 +- docs/development/testing.md | 8 ++++++-- next.config.ts | 2 +- package.json | 6 +++--- playwright.config.ts | 2 +- scripts/clean-build-output.mjs | 12 ++++++++++++ src/app/layout.tsx | 2 -- src/components/VitalsReporter.tsx | 13 ------------- src/components/providers.tsx | 7 ++++++- src/lib/vitals.ts | 6 ------ 10 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 scripts/clean-build-output.mjs delete mode 100644 src/components/VitalsReporter.tsx diff --git a/docs/development/commands.md b/docs/development/commands.md index 9cd3d4d..7dd667d 100644 --- a/docs/development/commands.md +++ b/docs/development/commands.md @@ -21,7 +21,7 @@ Source of truth: `scripts` in `package.json`. This page annotates intent; run | Command | Purpose | | --- | --- | -| `pnpm cf:build` | `next build --webpack` + inline critical CSS + `opennextjs-cloudflare build --skipNextBuild` + `populateCache local` + landing-astro build + overlay | +| `pnpm cf:build` | clean generated build output + `next build --webpack` + inline critical CSS + `opennextjs-cloudflare build --skipNextBuild` + `populateCache local` + landing-astro build + overlay | | `pnpm build:cf` | Alias for `cf:build` | | `pnpm build:e2e` | Credential-free OpenNext build plus Astro landing overlay for browser tests | | `pnpm preview:cf` | `build:cf` + `opennextjs-cloudflare preview` | diff --git a/docs/development/testing.md b/docs/development/testing.md index 137c5fa..a72662d 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -38,11 +38,15 @@ pnpm test:coverage # vitest run --coverage OpenNext artifact, overlays the real Astro landing, then exercises landing and mocked public-product journeys at desktop and mobile widths. CI builds that artifact once before starting Playwright. +- Cloudflare and E2E builds remove only the generated `.next` and `.open-next` + directories first. E2E also uses and clears its isolated + `.wrangler/e2e-state`, preventing stale HTML and client chunk hashes from + being combined across consecutive local builds. - Playwright starts the artifact with `wrangler.e2e.jsonc`, a local-only binding set and applies migrations to its disposable local D1 before the preview starts. The config omits Cloudflare AI, Vectorize, and service - bindings, so the suite needs no operator credentials and cannot call those - production resources. + bindings, and the runner loads `/dev/null` instead of `.dev.vars`, so the + suite needs no operator credentials and cannot call production resources. - Covered journeys: public project CTA, Discover search stability, bounded Tool Intelligence pagination, the shared repository-intelligence shell, and the uncataloged-preview sign-in boundary. diff --git a/next.config.ts b/next.config.ts index 296777a..d4e8e3f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,7 +16,7 @@ const securityHeaders = [ "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://us-assets.i.posthog.com", // unsafe-inline/eval required by Next.js "style-src 'self' 'unsafe-inline'", "img-src 'self' data: https://avatars.githubusercontent.com https://github.com", - "connect-src 'self' https://api.github.com https://us.i.posthog.com https://us-assets.i.posthog.com https://vitals.fleet.workers.dev", + "connect-src 'self' https://api.github.com https://us.i.posthog.com https://us-assets.i.posthog.com", "frame-ancestors 'none'", ].join('; '), }, diff --git a/package.json b/package.json index 4429d91..3ba107e 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "db:enrich-repos": "tsx scripts/enrich-repos.ts", "db:seed-popular": "tsx scripts/seed-popular.ts", "db:enrich-tools": "tsx scripts/enrich-tools.ts", - "cf:build": "next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && opennextjs-cloudflare populateCache local && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", - "build:cf": "next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && opennextjs-cloudflare populateCache local && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", - "build:e2e": "next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", + "cf:build": "node scripts/clean-build-output.mjs && next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && opennextjs-cloudflare populateCache local && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", + "build:cf": "node scripts/clean-build-output.mjs && next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && opennextjs-cloudflare populateCache local && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", + "build:e2e": "node scripts/clean-build-output.mjs && next build --webpack && node scripts/run-inline-critical-css.mjs && opennextjs-cloudflare build --skipNextBuild && pnpm --filter ./landing-astro build && node scripts/run-overlay-astro-landing.mjs", "preview:cf": "pnpm build:cf && opennextjs-cloudflare preview", "deploy:cf": "pnpm build:cf && opennextjs-cloudflare deploy --tag \"$(git rev-parse HEAD)\"", "cf:typegen": "wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts", diff --git a/playwright.config.ts b/playwright.config.ts index 2556034..7f282d0 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,7 +7,7 @@ import { defineConfig, devices } from '@playwright/test'; const appURL = 'http://127.0.0.1:8787'; const browserPreview = - 'pnpm exec wrangler d1 migrations apply starboard-e2e --local --config wrangler.e2e.jsonc && pnpm exec wrangler dev --config wrangler.e2e.jsonc --port 8787 --var AUTH_SECRET:starboard-browser-test-secret-at-least-32-characters --var AUTH_GITHUB_ID:browser-test-client --var AUTH_GITHUB_SECRET:browser-test-secret'; + 'pnpm exec wrangler d1 migrations apply starboard-e2e --local --config wrangler.e2e.jsonc --persist-to .wrangler/e2e-state --env-file /dev/null && pnpm exec wrangler dev --config wrangler.e2e.jsonc --port 8787 --persist-to .wrangler/e2e-state --env-file /dev/null --var AUTH_SECRET:starboard-browser-test-secret-at-least-32-characters --var AUTH_GITHUB_ID:browser-test-client --var AUTH_GITHUB_SECRET:browser-test-secret'; export default defineConfig({ testDir: './e2e', diff --git a/scripts/clean-build-output.mjs b/scripts/clean-build-output.mjs new file mode 100644 index 0000000..a285734 --- /dev/null +++ b/scripts/clean-build-output.mjs @@ -0,0 +1,12 @@ +import { rmSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +for (const directory of ['.next', '.open-next', '.wrangler/e2e-state']) { + const target = resolve(projectRoot, directory); + rmSync(target, { force: true, recursive: true }); +} + +console.log('[clean-build-output] removed generated build output and E2E state'); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 52b824b..c644ffe 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,7 +5,6 @@ import { Geist, Geist_Mono } from 'next/font/google'; import { Providers } from '@/components/providers'; import { SaaSMakerFeedback } from '@/components/saasmaker-feedback'; -import { VitalsReporter } from '@/components/VitalsReporter'; import { auth } from '@/lib/auth'; const geistSans = Geist({ @@ -94,7 +93,6 @@ export default async function RootLayout({ {children} - diff --git a/src/components/VitalsReporter.tsx b/src/components/VitalsReporter.tsx deleted file mode 100644 index 94e3163..0000000 --- a/src/components/VitalsReporter.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; -import { initVitals } from '@/lib/vitals'; -import { initApiTiming } from '@/lib/api-timing'; - -export function VitalsReporter() { - useEffect(() => { - initVitals(); - initApiTiming(); - }, []); - return null; -} diff --git a/src/components/providers.tsx b/src/components/providers.tsx index 6da2d87..2a805d6 100644 --- a/src/components/providers.tsx +++ b/src/components/providers.tsx @@ -9,8 +9,10 @@ import { PostHogProvider } from 'posthog-js/react'; import { useEffect } from 'react'; import { SWRConfig } from 'swr'; +import { initApiTiming } from '@/lib/api-timing'; import { installBrowserMonitoring } from '@/lib/foundry-monitoring'; import { swrErrorRetry } from '@/lib/swr-fetcher'; +import { initVitals } from '@/lib/vitals'; export function Providers({ children, @@ -21,7 +23,10 @@ export function Providers({ session?: Session | null; }) { useEffect(() => { - return installBrowserMonitoring(); + const uninstallBrowserMonitoring = installBrowserMonitoring(); + initVitals(); + initApiTiming(); + return uninstallBrowserMonitoring; }, []); return ( diff --git a/src/lib/vitals.ts b/src/lib/vitals.ts index b61b470..c737d86 100644 --- a/src/lib/vitals.ts +++ b/src/lib/vitals.ts @@ -18,12 +18,6 @@ function sendToAnalytics(metric: VitalMetric) { id: metric.id, navigation_type: metric.navigationType, }); - } else { - const body = JSON.stringify({ - project: process.env.NEXT_PUBLIC_PROJECT_SLUG ?? 'starboard', - ...metric, - }); - navigator.sendBeacon('https://vitals.fleet.workers.dev/collect', body); } }