4.7 KiB
4.7 KiB
Tasks: enterprise-server-pdf
Change ID: enterprise-server-pdf
Package: @suaveplan/pdf
Target path: server/services/pdf
Prerequisites on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/templates, @suaveplan/logger
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree per
proposal.md. package.jsonat0.1.0,"sideEffects": false, exports"."+builder+renderers/puppeteer+renderers/pdfkit.- Deps: all workspace + catalog deps listed in proposal. Mark
puppeteer-coreandpdfkitasoptionalDependencies. - DevDeps:
vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,@suaveplan/testing workspace:^. tsconfig.jsonextendinglibrary-node.json;vitest.config.tswithpool: 'forks'.- Scripts:
lint,typecheck,test,test:coverage,build,docs:check.
2. Types and errors
- Define all interfaces and Zod schemas in
src/types/pdf.types.ts. - Write
pdf.types.md(≥200 words, both examples, API reference). src/error-codes.ts: four codes;registerPdfErrorCodes(); eager call.
3. PDF builder
- Implement
PdfBuilderinsrc/builder/pdf-builder/pdf-builder.ts:- Fluent API using internal
currentPage: PdfPageaccumulator. page(options?): push current page (if populated) and start a new one.text,heading,table,image,spacer,footer,header: append elements tocurrentPage.elements.build(): flush current page; returnPdfDocument.- Validate non-empty content on
build(); throwPDF_INVALID_CONTENTif no pages.
- Fluent API using internal
- Write
pdf-builder.types.tswithPdfPage,PdfElementdiscriminated union. - Write
pdf-builder.test.ts(≤500 lines): multi-page build; all element types; empty build throws; metadata. - Write
pdf-builder.md(≥200 words, simple + advanced, API reference).
4. PdfKit renderer
- Implement
PdfKitRendererinsrc/renderers/pdfkit-renderer/pdfkit-renderer.ts:- Convert
PdfDocumentelements to PDFKit calls; collect output inPassThroughstream; return asUint8Array. - Support: text, heading (larger font), table (basic grid), image (from URL or bytes), spacer, header, footer.
- Guard import of
pdfkitwith a try/catch; throwPDF_RENDERER_UNAVAILABLEif not installed. - AbortSignal: check signal between pages.
- OTel span
pdf.renderwithrenderer: 'pdfkit'; histogrampdf.render_duration_ms.
- Convert
- Write
pdfkit-renderer.test.ts(≤500 lines): all element types produce non-empty PDF bytes; abort signal; unavailable pdfkit error. - Write
pdfkit-renderer.md(≥200 words).
5. Puppeteer renderer
- Implement
PuppeteerRendererinsrc/renderers/puppeteer-renderer/puppeteer-renderer.ts:- Convert
PdfDocumentto an HTML string using@suaveplan/templates. - Launch headless Chrome via
puppeteer-core; navigate to data URL; callpage.pdf(). dispose(): close browser.- Guard import of
puppeteer-core; throwPDF_RENDERER_UNAVAILABLEif absent. - AbortSignal: abort browser navigation.
- OTel span with
renderer: 'puppeteer'.
- Convert
- Write
puppeteer-renderer.test.ts(≤500 lines): skip ifSKIP_PUPPETEER=1; basic render produces valid PDF bytes; dispose closes browser;{ timeout: 30000 }. - Write
puppeteer-renderer.md(≥200 words).
6. Integration tests
tests/integration.test.ts: build a multi-page invoice with table + text + image; render with PdfKit; verify output size > 1000 bytes;{ timeout: 30000 }.
7. Docs + verify + publish
- Root
README.md: quick-start invoice example, renderer comparison table, template customisation, API table. bunx turbo lint --filter=@suaveplan/pdf— zero errors.bunx turbo typecheck --filter=@suaveplan/pdf— zero errors.bunx turbo test --filter=@suaveplan/pdf— all pass.bunx turbo test:coverage --filter=@suaveplan/pdf— 100%.bun scripts/publish.ts --package @suaveplan/pdf.- Update
CLAUDE.md; archive toopenspec/archive/2026-04-24-enterprise-server-pdf/.