0.2.1
Release notes for 0.2.1.
Released: 2026-06-11
Maintenance release. Aggregates two full-project review waves, a batch of agent-found hardening fixes, the zfb 0.1.0-next.38 engine bump, and dev-experience fixes. @takazudo/zudo-doc, @takazudo/zudo-doc-history-server, and create-zudo-doc are released together in lockstep.
Migrating a 0.2.0 consumer
If you maintain a downstream site scaffolded from create-zudo-doc, the jump from 0.2.0 to 0.2.1 is more than a version bump. The template wiring evolved in interlocking ways, and the zfb 0.1.0-next.38 engine bump that ships with this release surfaces a pre-existing island-hydration gap. This section collects what three real consumer upgrades ran into, so you can do the migration in one pass.
The new island warnings expose islands that were already dead
zfb 0.1.0-next.38 adds an island-marker registry warning (zfb #984 / #990). On a 0.2.0-era wiring you will suddenly see up to seven of them — one per emitted-but-unregistered marker: ThemeToggle, Toc, MobileToc, Sidebar, DocHistory, ImageEnlarge, and AiChatModal. These islands server-render correctly but never hydrate, because the island registry under-registers against the markers the page emits. The build still exits 0.
The hydration gap predates 0.2.1
next.38 did not break these islands — it revealed them. On any zfb release below next.38 the same toggles and TOCs ship dead (non-interactive) with no warning at all. Read the warnings as a diagnosis of a long-standing gap, not as a regression introduced by this release.
Adopt the 0.2.1 template changes as one wave
Run pnpm check:template-drift. On a 0.2.0 baseline it flags the full interlocked set — roughly 17 to 31 files, depending on which template generation you last synced — spanning:
#2010 — doc route consolidation (new
_doc-page-renderer/_doc-route-entriesmodules)#2030 — nav-data dedupe (new
_nav-data-prepmodule)#2012 — bare
theme-togglesubpath export#2016 —
docs-schemaextraction (a single zod source of truth)#2022, #2024, #2037 — versioned-page metadata, search-index / llms-txt dedupe, and zdtp state survival
These changes reference one another, so partial adoption leaves dead glue behind. Sync them together.
One field data point (zudolab/zudo-css-wisdom#103): a full adoption synced 31 flagged files plus 4 new modules cleanly in a single wave. The only build break was a new getLocaleConfig export now expected from src/ — if you have customized that file, add the export.
Keep a scanner-visible ThemeToggle shim on next.38
Adopting the 0.2.1 templates makes ThemeToggle newly dead on next.38. The 0.2.1 _header-with-defaults.tsx switches from a local src/ copy to import { ThemeToggle } from "@takazudo/ (the npm dist). The next.38 island scanner does not register islands that live inside a node_modules package (zfb #999), so a consumer that syncs templates goes from "six dead, ThemeToggle still works" to "seven dead."
Workaround until you are on a zfb release with the forward fix (below): keep a local, scanner-visible pass-through shim and point the header import at it. Make it a local wrapper component with a pinned displayName — a bare export { ThemeToggle } from … re-export does not give the next.38 scanner a local binding to register, so the toggle stays dead.
// src/components/theme-toggle.tsx
"use client";
import type { ComponentProps } from "preact";
import { ThemeToggle as PackageThemeToggle } from "@takazudo/zudo-doc/theme-toggle";
// Local wrapper (not a bare re-export) so the next.38 island scanner sees a
// component binding it can register; displayName names the island marker.
export function ThemeToggle(props: ComponentProps<typeof PackageThemeToggle>) {
return <PackageThemeToggle {...props} />;
}
ThemeToggle.displayName = "ThemeToggle";Sync the residual layout-island shims (#2061)
On released next.38, consumers of zudo-doc ≤ 0.2.2 still see three dead layout islands: Toc and MobileToc on docs pages, and an empty-data Sidebar on hide_sidebar pages. These have been silently dead since at least 0.2.0.
PR #2061 fixes this for template consumers with scanner-visible Toc / MobileToc shims and an empty-fragment sidebarOverride on hide_sidebar pages. Hand-sync those files, or wait for the next create-zudo-doc release, which ships them by default.
Prune the now-orphaned local modules
0.2.1 stopped shipping local copies of several modules that the package now owns. After you sync the templates, the old full local implementations are unreferenced — delete them: the rehype plugins, hast-utils, url-utils, docs-source-map, use-active-heading, sidebar-resizer, and the doc-history util. (Counting the replaced theme-toggle / Toc copies, the zudo-css-wisdom upgrade removed 13 such files.)
Keep the scanner-visible shims while on next.38
Do not delete src/, src/, or src/ on next.38. Those filenames now hold the thin scanner-visible shims from the two sections above, and the header and page shell still import them — pruning them reintroduces the dead-island warnings. They can go once you are on next.39 or later (see below).
The forward fix: zfb 0.1.0-next.39
Every scanner-visible shim above is interim debt for the next.38 scanner. zfb 0.1.0-next.39 (released 2026-06-12, after both 0.2.1 and 0.2.2 had shipped against next.38) adds "use client" island scanning for npm-dist packages (zfb #999 / PR #1001): islands inside a regular package under node_modules are registered and hydrated, resolved through that package's exports map.
Once you are on next.39 or later
The scanner-visible ThemeToggle / Toc / MobileToc shims are no longer needed, and the interim shim debt can be retired. Note that 0.2.1 itself shipped against next.38 — next.39 is the follow-on engine fix, not part of this release.
Consumers that render DocLayoutWithDefaults directly from the package without template pages (no scaffold) are not healed by the template shims at all — that path needs zfb next.39 or later.
Features
Optional HMAC IP hashing for the AI-chat endpoint. When the
IP_HASH_SECRETCloudflare secret is set, rate-limit and audit-log keys derive from HMAC-SHA-256(ip) instead of unsalted SHA-256, defeating hash reversal by IPv4 enumeration. Without the secret, behavior is unchanged — the step is optional and non-breaking (#2038).Configurable
sandboxprop on HTML Preview. The preview iframe's sandbox attribute can now be configured per call site instead of being hardcoded (#2035).Theme toggle: bare subpath, cross-instance sync.
@takazudo/zudo-docships the theme toggle as a bare subpath export with cross-instance state synchronization and a shiki peer dependency (#2012).pages/andplugins/typecheck coverage. The host's file-routedpages/and all six zfb engine plugins are now type-checked (check:pages;check:pluginsvia@ts-checkJSDoc +checkJs), wired into bothb4pushand CI (#2018).
Bug Fixes
zfb engine bumped to
0.1.0-next.38with a warning-clean build. Pins move from0.1.0-next.35across the host and generator. Two broken hierarchical anchors were fixed, e2e fixture trees are excluded from the host bundler walk (the now-functionalimageDimensionsfeature otherwise stats fixture image refs against the hostpublic/), and thelinkValidation/imageDimensionsdocs were rewritten for the new behavior — the no-opallowExternaloption was removed upstream, andimageDimensionsnow actually injectswidth/height(#2045).Design-token panel state survives color-scheme toggles. The theme toggle no longer wipes zdtp tweak state, and per-scheme
shikiThemesurvives thetoZdtpColorSchemesconversion (#2037).AI-chat history trust policy hardened and documented. Strict
user/assistantrole whitelist, entry-count and per-entry length caps, smuggled-field stripping, and a recorded decision on the forged-assistant-turn residual risk (#2036).Versioned-page metadata correctness. Stale metainfo/tag chips are hidden on
/pages, EN-fallback metainfo parity restored, tag URL segments are percent-encoded at every emitter, and view-source links honor the recorded source extension instead of assumingv/ .mdx(#2022).Sidebar and navigation fixes. The root docs-index node is preserved through the sidebar-tree delegation (#2030), unsectioned pages fall back to the full nav tree, and content centers correctly when the sidebar is hidden via the toggle (#2002).
Generator input validation.
create-zudo-docrejects a non-string presetprojectNamebefore the grammar check, dead template plugins were deleted, and project-name validation is centralized (#2013).doc-history-server robustness. Malformed percent-encoded slugs return HTTP 400 instead of crashing the server,
getDocHistoryremains exported as an async alias, and the server binds to localhost by default (#2011).Theme-toggle island hydration. The compiled island uses a named export so it actually hydrates (#2012).
Dev server: pinned port, watcher loop fixed.
pnpm devpins port 4321 (failing fast onEADDRINUSE) and the.claude/watcher no longer triggers a symlink feedback loop (#2044).Two full-project review waves. Dozens of small correctness fixes: empty-slug falsiness sites, hook-order issues, header toggle mode, template i18n, an 8-item small-fixes batch,
Localeliteral-union type hygiene, and ai-chat route-handler fixture tests (#2008, #2020, #2025, #2034).
Performance
Client hydration and search.
SidebarTogglehydration is gated withwhen=visible, sidebar node components are memoized, and search-index fields are pre-lowercased once at load time.Build-time memoization. Tag-map, footer taglist, and the md-plugins source-map are memoized, and the
check:linksdist walk was merged into a single pass.
Security
shell-quoteadvisory resolved. The transitiveshell-quotedependency is overridden to>=1.8.4(GHSA-w7jw-789q-3m8p)..npmrctrust policy tightened fromanytono-downgrade, with the rationale documented inline (#2039).
Other Changes
Host-wide consolidation. The nav-tree builder is deduped into the sidebar-tree package behind a bounded LRU cache (#2030), the sidebar resizer is consolidated to a single canonical implementation (#2029), the four doc catch-all routes share one module (#2010), a
useModalDialoghook replaces four-way dialog plumbing, the ai-chat endpoint is split into focused modules, search-index/llms-txt utilities are deduped and honor frontmatterslugoverrides (#2024), docs frontmatter has a single zod source of truth (#2016), and seven unused root dependencies were pruned.CI: wrangler pinned to 4.98.0 while the Cloudflare
/API failure (error 10013) on production deploys is handled by Cloudflare support (#2007). The failure is Cloudflare-side; uploads succeed and content deploys.subdomain Documentation. The stale pre-publish dev-workflow README section was replaced (#2005) and the consumer Tailwind safelist import is documented.