Repository navigation
chore: merge dev into main - #169
Merged
Merged
Conversation
The section row compared every source against every other one on each client navigation — cubic in the number of sources. duxt.app reached 110, and sectionsForPath took ~69 ms per call behind Vue proxies. The build now writes each source's area into resolvedSources, which content pack resolves through the same module.
useDuxtConfig() was a computed of its own in each of the 34 components that call it, and every one merged the config, appended the generated sections and walked every text field again on each navigation. The Nuxt app now holds one merge and a cache per locale and path; the manifest is resolved once per locale, and a key whose value did not change keeps its object, so its readers are not re-rendered. Workers build of duxt.app: prerender 1156 s -> 777 s, warm client navigation 454 ms -> 61 ms.
…ctions Each client navigation downloaded the SQL dumps of all 15 docs versions and the SQLite WebAssembly module, and kept 4.6 million characters of them in localStorage. Two lookups did it: - DuxtVersion and useDuxtVersion re-asked their per-path question for the page being left: the header sees a navigation a tick late, after Nuxt released that page's payload data. A browser lookup for a path that is not the router's destination now answers nothing, and the redundant watch on the path, which refetched past the payload, is gone. - The section-layout middleware queried the collection for the page type on every navigation. The server now writes the layout into the payload and the client reads it from there, querying only without a payload. Workers build of duxt.app: first client navigation 293 ms -> 85-120 ms, no dump or WebAssembly request, localStorage 4.6M -> 306 characters.
Nitro copies the runtime config for every request useRuntimeConfig(event) is called on, and @nuxtjs/i18n calls it on every request. For each key it derives an environment variable name character by character and reads process.env twice. The layer's redirect tables were 713 of duxt.app's 2,043 keys, most of them whole URL paths, and they are build output no environment variable may override. They now come from a virtual module, #duxt/redirects, which the content-bundles plugin and the devtools panel import. Workers build of duxt.app: prerender 757 s -> 708 s.
Content's parse cache keeps a row for every file it ever parsed: a build never removes one, only the dev watcher does. readContentCache took them all for live pages, so a moved or deleted page kept its redirectFrom rules, was counted by duxt report and shipped in content packs. This repository's own cache held 100 such rows. Content rebuilds each collection table from the files that exist and shares the cache's ids, so a cache row now counts only when that table still holds it.
Every deploy parsed all 12,232 files from cold, three to six minutes measured locally against about thirty seconds warm. The cache was left out because a restored copy fed deleted pages to duxt's own readers, which they no longer read. Restored with a rolling key, as the OG images are, namespaced by the lockfile.
wikiMaintenance resolves every link of every page, and each resolution filtered every row, normalised its path, title and aliases, and walked the whole manifest for the locale chain again: pages x links x pages. The rows are now indexed once per row list, the rows of a scope once per chain, and a scope once per manifest, source and reader, all beside the objects they describe. A test compares the index with the filter it replaces, row for row and in order. Node build of duxt.app: wanted pages 8.0 s -> 6.1 s each.
TitusKirch
marked this pull request as ready for review
November 12, 2026 11:36
node-forge 1.4.3 closes GHSA-6889-vf6x-f3g5, GHSA-w4xr-hqv8-ch44, GHSA-6cv4-p8m7-4r25 and GHSA-j8rw-jp99-435v; fast-xml-parser 5.12.0 closes GHSA-474x-hrwr-fgx4. Both fall inside their parents' ranges (listhen ^1.4.0, sitemapd ^5.11.2) and are past the release cooldown, so the lockfile moves and nothing needs an override or a review.
@rollup/plugin-inject 5.0.5, the latest release and Nitro's, returns a high-resolution sourcemap for every module its prefilter matches, even when it injects nothing. One Node build of apps/www mapped 1,248 unchanged modules, 89 million characters, the 24.6 MB Content dump twice among them; that was the heap spike in Nitro's final server bundle. Returning no transform when nothing was injected removes it and takes about 1.2 minutes off the build. Workspace patch only.
The CI gate's Node build died at the default 4 GB, before this branch's changes too. Even without the plugin-inject maps, prerendering peaks at about 6 GB, most likely in Content's dump routes. GitHub's runners have 16 GB; an existing NODE_OPTIONS is kept and extended.
nuxt 4.6.4, vue 3.5.45, vitest 5.1.0, playwright-core 1.65.0, wrangler 4.158.0, shiki 4.6.0, mermaid 12.3.0, @lucide/vue 1.70.0, tailwindcss 4.3.5 and the lucide and vscode-icons sets, each past the release cooldown; newer releases still inside it stay held. oxlint moves from 1.90.0 to 1.93.0 and stays pinned. Three follow-ups keep the gate green: - @nuxt/cli 4.0.1 could not start `nuxt dev` against nuxt 4.6.4's devServer host; 4.0.3, inside nuxt's own range, can. - vue resolves to 3.5.45 everywhere. Two copies left NuxtLink without a Nuxt instance during the prerender (NUXT_E1001). - oxlint 1.93 rejects the raw escape byte in the CLI tests' colour stripper; node:util's stripVTControlCharacters replaces it. nuxt 4.6.4 moves archiver's readdir-glob to minimatch 10.2.6, so the review of GHSA-g3hv-75wg-m249 gains those paths. It is the caller the review already names, matching build-configuration patterns only.
.nvmrc, the content-bundles example image and the agent files name the same patch release. CI keeps selecting the newest Node 26.
jsdom is the test DOM of the layer and the site, agents the Cloudflare MCP provider of apps/www, oxfmt stays pinned and reformats nothing, and wrangler 4.159 has passed the release cooldown since the minor update. TypeScript stays on 6 until Volar supports 7.
check:wiki and check:wikilinks rename a fixture page and assert that the next content pack parses it. Content keeps a parse-cache row for every file it ever parsed, so a row for that path from an earlier run - on a developer machine, or in a CI run that restored part of .data - made the pack skip the parse and the check fail. The renamed page now carries a per-run comment, so its checksum is always new.
… root DuxtVersion and useDuxtVersion skip asking whether a page exists in other versions when the page is its source's root, which every version has. The unprefixed source's root is /, not its empty prefix, so the landing page asked anyway: on a cold server it restored every version's collection before its first byte. Measured on duxt.app: 16 collection restores and 23 s down to 2 and 2.8 s.
build:cf already pins NUXT_PUBLIC_I18N_BASE_URL and NUXT_SITE_URL so a local .env cannot decide the origin of prerendered pages. The Node build did not, so a developer's .env leaked localhost into the prerendered wanted pages and check:seo failed locally while CI passed.
Content restores a dump one INSERT at a time, each its own commit, so on a disk every row waits for an fsync. A cold docs page restores every version's collection and took over 20 s on disk against under 2 in memory, which pushed check:seo and check:interactive past their 30 s budget for a first response.
Content answers each server-side queryCollection() through an internal request, and @nuxtjs/i18n initialises its context on every request with useRuntimeConfig(event), which clones the whole runtime config and maps every key to an environment variable. A docs page made 26 such queries, so 27 copies per page and about a third of its render time. A Nitro plugin registered before i18n's hands /__nuxt_content requests the config Nitro resolved once at startup. A request with a Cloudflare context keeps Nitro's per-request behaviour. Node build of duxt.app: 27 copies per page down to 1, warm render 515 ms -> 320 ms, prerender 90 s -> 66 s.
@hono/node-server 2.1.6 closes GHSA-qv5m-9pm6-hqp3 and GHSA-jpvv-r467-5p4w. It reaches the layer through the MCP SDK, whose range (^2.0.5) already allows it, and it is past the release cooldown, so only the lockfile moves.
The wiki layer builds on core features that first ship in 0.13.0: build contributions through duxt.build.ts, page types and the ./unstable export, which 0.12.0 does not have. With >=0.4.0 a site could install it next to an older core without a peer warning and only fail at build time. The range stays wide rather than ^0.x, as for the other companion layers. Refs #161
2 tasks done
The test builds a whole Nuxt app (up to 120 s), prepares it (30 s) and polls five times for up to 30 s each, inside a 180 s limit that was smaller than the sum of its own steps. Under CI's parallel package tests it timed out on PR #169 while every step was still inside its budget.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated rollup PR for changes accumulated on
dev. Mark ready when you want it to land — and use a merge commit, not squash, so individualfeat:/fix:commits stay visible to release-please.