Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/admin/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->

## This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.

<!-- END:nextjs-agent-rules -->
26 changes: 26 additions & 0 deletions apps/admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,29 @@ Validate the page-kind form with `npm run admin:build` followed by
legacy defaults, saved decisions, and desktop/mobile layout.
Screenshots: [desktop](screenshots/article-page-kind-desktop.png),
[mobile](screenshots/article-page-kind-mobile.png).

## Next.js 16.4 tooling

The app uses Next.js 16.4 and React 19.3. The Rust React Compiler optimizes
component rendering with Turbopack. Cache garbage collection, lazy client dynamic
imports, and worker-thread plugin execution are enabled for development/builds.
Next.js may fall back to child processes on Node.js versions affected by its
worker-thread compatibility checks. Upgrade reminders use the `latest` policy.

Run `npm run admin:analyze` from the repository root for the interactive Turbopack
bundle analyzer, or `npm run admin:analyze -- --output` for an offline report under
`apps/admin/.next/`. Analysis does not replace a production build. Use the analyzer
to compare route sizes and identify large dependencies before making changes.
Save a named capture with `npm run admin:analyze -- --output --snapshot baseline`
and compare subsequent captures in the analyzer.

Cache Components and Partial Prefetching remain disabled: both apps use a fresh
script nonce per response, and Next.js static shells are incompatible with this
[CSP model](https://nextjs.org/docs/app/guides/content-security-policy).
Consequently, `ensureStatic`, `navigation()`, and `prefetch()` are not applied to
these request-rendered routes. Adopting them requires a compatible CSP design
and verification of runtime settings, authentication, and personalized feeds.

Validate with `npm run admin:lint`, `npm run admin:test`, and `npm run admin:build`.
The overview browser suite checks nonce rotation after its overview scenarios so
the extra page loads do not affect panel concurrency or lazy-loading measurements.
9 changes: 9 additions & 0 deletions apps/admin/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import path from "node:path";
const config: NextConfig = {
env: { DEVFEED_BUILD_VERSION: version },
output: "standalone",
// Nonce-based CSP requires request-time rendering; keep Cache Components disabled.
reactCompiler: true,
experimental: {
agentUpgrade: "latest",
turbopackRustReactCompiler: true,
turbopackGc: true,
turbopackLazyDynamicImports: true,
turbopackPluginRuntimeStrategy: "workerThreads",
},
productionBrowserSourceMaps: true,
serverExternalPackages: ["@pyroscope/nodejs", "@prometheus-io/client", "@opentelemetry/sdk-node"],
outputFileTracingRoot: path.join(import.meta.dirname, "../.."),
Expand Down
7 changes: 4 additions & 3 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"start": "next start --port 3001",
"lint": "eslint . && tsc --noEmit",
"test": "vitest run",
"generate": "orval --config orval.config.ts"
"generate": "orval --config orval.config.ts",
"analyze": "next analyze"
},
"dependencies": {
"@chimely/client": "0.2.2",
Expand All @@ -22,7 +23,7 @@
"cmdk": "^1.1.1",
"cn": "^0.4.0",
"lucide-react": "^1.49.0",
"next": "16.3.8",
"next": "16.4.0",
"radix-ui": "^1.6.7",
"react": "19.3.0",
"react-dom": "19.3.0",
Expand All @@ -41,7 +42,7 @@
"@types/react-dom": "^19.3.0",
"@vitest/coverage-v8": "5.0.3",
"eslint": "^9.39.5",
"eslint-config-next": "16.3.8",
"eslint-config-next": "16.4.0",
"jsdom": "^30.1.1",
"orval": "8.38.0",
"shadcn": "4.21.0",
Expand Down
8 changes: 7 additions & 1 deletion apps/admin/tests/browser/overview.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { checkNonceCsp } from "../../../../scripts/testing/nonce-csp.mjs";
import assert from "node:assert/strict";
import { createServer } from "node:http";
import { spawn } from "node:child_process";
Expand Down Expand Up @@ -434,12 +435,17 @@ try {
await page.waitForFunction(() => document.documentElement.scrollWidth <= innerWidth);
await workload.screenshot({ path: `${output}/workload-live-mobile.png` });
assert.deepEqual(errors, []);
const maximumConcurrentPanelRequests = maximum;
await page.close();
mode = "populated";
// Separate nonce-check navigations must not affect overview request measurements.
await checkNonceCsp(context, origin);
await writeFile(
`${output}/results.json`,
JSON.stringify(
{
passed: true,
maximumConcurrentPanelRequests: maximum,
maximumConcurrentPanelRequests,
initialPanels,
scenarios: [
"desktop",
Expand Down
9 changes: 9 additions & 0 deletions apps/web/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->

## This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.

<!-- END:nextjs-agent-rules -->
24 changes: 24 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,27 @@ browser checks on the website and built Chrome and Edge extensions. The matrix s
long titles, unbroken topic names, long publisher names, and large engagement counts.
It checks column adaptation, overflow, title space, footer fit, 44px bookmark targets,
and the container-query boundary. Screenshots remain in ignored reports/build output.

## Next.js 16.4 tooling

The app uses Next.js 16.4 and React 19.3. The Rust React Compiler optimizes
component rendering with Turbopack. Cache garbage collection, lazy client dynamic
imports, and worker-thread plugin execution are enabled for development/builds.
Next.js may fall back to child processes on Node.js versions affected by its
worker-thread compatibility checks. Upgrade reminders use the `latest` policy.

Run `npm run web:analyze` from the repository root for the interactive Turbopack
bundle analyzer, or `npm run web:analyze -- --output` for an offline report under
`apps/web/.next/`. Analysis does not replace a production build. Use the analyzer
to compare route sizes and identify large dependencies before making changes.
Save a named capture with `npm run web:analyze -- --output --snapshot baseline`
and compare subsequent captures in the analyzer.

Cache Components and Partial Prefetching remain disabled: both apps use a fresh
script nonce per response, and Next.js static shells are incompatible with this
[CSP model](https://nextjs.org/docs/app/guides/content-security-policy).
Consequently, `ensureStatic`, `navigation()`, and `prefetch()` are not applied to
these request-rendered routes. Adopting them requires a compatible CSP design
and verification of runtime settings, authentication, and personalized feeds.

Validate with `npm run web:lint`, `npm run web:test`, and `npm run web:build`.
9 changes: 9 additions & 0 deletions apps/web/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import { withDualmark } from "@dualmark/nextjs";

const config: NextConfig = {
output: "standalone",
// Nonce-based CSP requires request-time rendering; keep Cache Components disabled.
reactCompiler: true,
experimental: {
agentUpgrade: "latest",
turbopackRustReactCompiler: true,
turbopackGc: true,
turbopackLazyDynamicImports: true,
turbopackPluginRuntimeStrategy: "workerThreads",
},
productionBrowserSourceMaps: true,
serverExternalPackages: ["@pyroscope/nodejs", "@prometheus-io/client", "@opentelemetry/sdk-node"],
outputFileTracingRoot: path.join(import.meta.dirname, "../.."),
Expand Down
7 changes: 4 additions & 3 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "next build",
"start": "next start --hostname 0.0.0.0 --port 3000",
"lint": "eslint . && tsc --noEmit",
"test": "vitest run"
"test": "vitest run",
"analyze": "next analyze"
},
"dependencies": {
"@chimely/client": "0.2.2",
Expand All @@ -19,7 +20,7 @@
"@microsoft/clarity": "1.0.2",
"@tanstack/react-query": "5.104.1",
"lucide-react": "^1.49.0",
"next": "16.3.8",
"next": "16.4.0",
"radix-ui": "1.6.7",
"react": "19.3.0",
"react-dom": "19.3.0",
Expand All @@ -36,7 +37,7 @@
"@types/react-dom": "^19.3.0",
"@vitest/coverage-v8": "5.0.3",
"eslint": "^9.39.5",
"eslint-config-next": "16.3.8",
"eslint-config-next": "16.4.0",
"jsdom": "^30.0.1",
"libxml2-wasm": "0.7.2",
"typescript": "^7.0.2",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/tests/browser/feed.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { checkNonceCsp } from "../../../../scripts/testing/nonce-csp.mjs";
import { checkArticleGrid } from "../../../../scripts/testing/article-grid.mjs";
import { checkArticleViews, compactListArticle, compactLoadingGate } from "./article-views.mjs";
import { checkCompactLoading } from "../../../../scripts/testing/compact-loading.mjs";
Expand Down Expand Up @@ -486,6 +487,7 @@ try {
context.on("request", (request) => {
if (request.headers()["next-router-prefetch"] === "1") prefetchedRoutes.push(request.url());
});
await checkNonceCsp(context, `${origin}/latest`);
const page = await context.newPage();
await page.addInitScript(() => {
window.__cspViolations = [];
Expand Down
Loading