perf: keep the API-docs SPA and MCP SDK out of the server graph
@scalar/api-reference-react is a documentation SPA reachable only from one admin tab, and it renders nothing usable on the server, yet it was compiled into the server graph for an SSR pass whose output is discarded. It is a client component already, so next/dynamic with ssr: false is allowed in place and needs no restructuring. @modelcontextprotocol/sdk is only reachable through /api/mcp/[transport], so it moves to serverExternalPackages. Honest accounting: this is an image-size and architecture change, not a measured memory win. Idle landed at 89.4 MiB against 90.3/91.1/100.5 for the previous build — the better end of that spread, but inside it, so on one sample it cannot be claimed as a reduction. What is deterministic is image size, 401 -> 398 MB. Kept because a docs SPA should not be server-rendered regardless of what the memory number does. pnpm typecheck and lint clean; pnpm test 1794/1795 with the known Testcontainers pi-hole flake, which passes 9/9 in isolation.
This commit is contained in:
4 files changed
+39
-5
No files matched your search
@@ -128,6 +128,33 @@ a real trade, not a free win.
|
||||
**Cumulative vs merge-base: idle 141.7 → 90.3 MiB (−36%), board loaded 257.8 → 164.6 MiB
|
||||
(−36%), under load 392.7 → 302.5 MiB (−23%).**
|
||||
|
||||
## Final A/B, with repetitions
|
||||
|
||||
Everything above is one sample per configuration, which is fine for ranking changes but
|
||||
not for a headline. This is the merge-base image against the PR's end state, alternated,
|
||||
on the same backup — 2 runs before, 3 after:
|
||||
|
||||
| run | idle | board loaded | under load | after soak | peak |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| before #1 | 144.2 | 249.0 | 392.8 | 529.6 | 753.0 |
|
||||
| before #2 | 142.3 | 268.2 | 385.1 | 538.5 | 626.1 |
|
||||
| after #1 | 100.5 | 176.8 | 311.1 | 204.3 | 590.9 |
|
||||
| after #2 | 91.1 | 163.5 | 319.9 | 212.7 | 568.0 |
|
||||
| after #3 | 90.3 | 164.6 | 302.5 | 206.4 | 594.2 |
|
||||
|
||||
| metric | before (mean) | after (mean) | change |
|
||||
| --- | --- | --- | --- |
|
||||
| boot idle | 143.3 | **94.0** | **−34%** |
|
||||
| board loaded | 258.6 | **168.3** | **−35%** |
|
||||
| under load | 389.0 | **311.2** | **−20%** |
|
||||
| after idle soak | 534.1 | **207.8** | **−61%** |
|
||||
| peak | 689.6 | **584.4** | −15% |
|
||||
|
||||
The two groups do not overlap on any stage. The before-side idle samples land within
|
||||
1.9 MiB of each other, which is what justifies treating a ~50 MiB gap as real rather than
|
||||
noise. Earlier single-sample rounds suggested −36%; the repeated figure is **−34%**, and
|
||||
that is the number used in the PR.
|
||||
|
||||
## Is the measurement actually behind a login?
|
||||
|
||||
Worth stating explicitly, because "28 widgets rendered" would be meaningless if the board
|
||||
|
||||
@@ -51,6 +51,8 @@ const nextConfig: NextConfig = {
|
||||
"node-unifi",
|
||||
"@kubernetes/client-node",
|
||||
"linkedom",
|
||||
// Only reachable through /api/mcp/[transport].
|
||||
"@modelcontextprotocol/sdk",
|
||||
],
|
||||
experimental: {
|
||||
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useComputedColorScheme } from "@mantine/core";
|
||||
import { ApiReferenceReact } from "@scalar/api-reference-react";
|
||||
|
||||
import "@scalar/api-reference-react/style.css";
|
||||
import "./scalar-theme.css";
|
||||
|
||||
// The API reference is a whole documentation SPA reachable only from this one admin
|
||||
// tab, and it renders nothing useful on the server. Loading it lazily keeps it out
|
||||
// of the server graph instead of compiling it for an SSR pass that is discarded.
|
||||
const ApiReferenceReact = dynamic(() => import("@scalar/api-reference-react").then((mod) => mod.ApiReferenceReact), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
interface ScalarApiReferenceProps {
|
||||
document: object;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
transform 150ms ease;
|
||||
}
|
||||
|
||||
:global(.grid-stack-item[data-type="item"]:not([data-kind="app"]):hover)
|
||||
.wrapper,
|
||||
:global(.grid-stack-item[data-type="item"]:not([data-kind="app"]):focus-within)
|
||||
.wrapper {
|
||||
:global(.grid-stack-item[data-type="item"]:not([data-kind="app"]):hover) .wrapper,
|
||||
:global(.grid-stack-item[data-type="item"]:not([data-kind="app"]):focus-within) .wrapper {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user