/packages/doc-history-server/CLAUDE.md
CLAUDE.md at /packages/doc-history-server/CLAUDE.md
Path: packages/
doc-history-server
Standalone package for document git history with dual modes: REST API server for local dev, CLI batch generator for CI builds. Extracted from src/ to decouple expensive git operations from the documentation build pipeline.
Tech Stack
Node.js — HTTP server + CLI (no framework dependency)
Git —
execFile/spawn(async) for log, show, follow;execFileSynconly for the one-timerev-parse --show-toplevelrepo-root probeTypeScript — strict mode, ESM
Commands
pnpm dev -- --port 4322 --content-dir <path> --locale <key>:<dir>— start REST API serverpnpm generate -- --content-dir <path> --locale <key>:<dir> --out-dir <path>— batch generate JSONspnpm typecheck— TypeScript type checkingpnpm build— build via tsup (ESM + DTS)
Architecture
src/
├── index.ts # Server entry — parses args, starts HTTP server
├── cli.ts # CLI entry — parses args, batch generates JSONs
├── args.ts # Shared argument parsing with bounds checking
├── server.ts # HTTP server (GET /doc-history/{slug}.json, /health)
├── git-history.ts # Core git logic (log, show, follow, rename tracking)
├── concurrency.ts # Tiny semaphore (no p-limit dep) bounding the CLI's parallel walks
├── exclude.ts # Exclude-glob → slug predicate. DUPLICATED BY DESIGN with
│ # packages/zudo-doc/src/doc-history-exclude/ (#3110 — that graph
│ # is always bundled, this package is an optional peer); the two
│ # are pinned by that package's parity.test.ts
├── shared.ts # Shared helpers (getContentDirEntries)
└── types.ts # DocHistoryEntry, DocHistoryData types Server Mode (Local Dev)
Binds to
127.0.0.1by default (loopback only); pass--host 0.0.0.0to accept LAN connectionsRuns on configurable port (default 4322)
GET /doc-history/{slug}.json— returns full history for a documentGET /doc-history/{locale}/{slug}.json— locale-prefixed historyGET /health— health checkFile index refreshes every 10 seconds (picks up new/renamed files)
CORS headers on all responses
CLI Mode (CI Build)
Generates
{slug}.jsonfiles in the output directoryReports progress and timing
Used by CI
build-historyjob (parallel with the main site build)
zfb Integration
In dev mode, the zfb plugin implemented at packages/ proxies / requests to this server. In build mode, that plugin's postBuild hook falls back to inline generation per its own shouldGeneratePostBuild decision table (CI or GEN_DOC_HISTORY=1), suppressed by either SKIP_DOC_HISTORY=1 (also blanks preBuild) or DOC_HISTORY_SKIP_POSTBUILD=1 (postBuild only, #2927).
Root pnpm dev runs both the zfb dev server and this server via run-p.
Key Design Decisions
One async extraction path — both the server and CLI use
getDocHistoryAsync(#1986). The CLI wraps every file in a semaphore-bounded Promise; async history calls (execFile/spawn) mean the concurrency cap actually parallelizes work. The server awaits the same function, keeping history walks and content reads off the blocking path. A cachedexecFileSynccall remains only for the one-time repository-root probe. The pure parsers (parseCommitLog,parseHashToPathMap,parseBatchContents) are shared between the content-fetch and rename-fallback paths.Localhost-only bind — the server binds to
127.0.0.1by default to avoid exposing full git history (including unpublished content) on the LAN. The zfb dev plugin proxies server-side, so LAN exposure is not needed. Pass--host 0.0.0.0to opt in.Repo-relative paths — API responses use relative file paths to avoid leaking absolute server paths
--followfor renames — tracks file history across renames with multiple fallback strategiespnpm --filter paths — when run via
pnpm --filter,process.cwd()is the package dir, but pnpm setsINIT_CWDto where pnpm was invoked (the repo root).resolveContentPathresolves relative--content-dir/--localepaths againstINIT_CWD, so pass the clean repo-root-relative form (src/,content/ docs ja:) with NOsrc/ content/ docs- ja .prefix — the same form. / . . / dev:historyand CI'sbuild-historyuse. A path that resolves to a non-existent directory is a hard error (exit 1), not a silent zero-entry run (#1907 / #1913).--out-diris the exception: it is stored verbatim (not resolved viaINIT_CWD), so CI keeps.to write the artifact at the repo root.. / . . / doc- history- out
CLI Arguments
| Flag | Required | Default | Description |
|---|---|---|---|
--content-dir | Yes | — | Content directory to scan |
--locale <key>:<dir> | No | — | Additional locale (repeatable) |
--out-dir | CLI only | — | Output directory for JSONs |
--port | Server only | 4322 | Server port |
--host | Server only | 127.0.0.1 | Network interface to bind |
--max-entries | No | 50 | Max commits per file |