5.2 KiB
Proposal — Migrate @suaveplan/pdf to genesis (multi-chunk)
- Design: ./design.md
- Spec delta: ./specs/pdf/spec.md
- Status: PROPOSED (Chunk A ships in this change; Chunks B–E follow)
Why
Upstream @suaveplan/pdf (/mnt/s/web/suaveplan/packages/server/pdf/, ~2 100
LOC implementation across eleven feature modules, ~7 300 LOC of tests) ships a
working server-tier PDF 1.4 generator built on the 14 standard PDF fonts and a
hand-rolled binary writer. It exports a fluent DocumentBuilder, four element
primitives (text, table, image, divider), a layout engine with cursor +
flow-block tracking, a style resolver with theme merging, three high-level
templates (invoice, receipt, report), unit + colour utilities, and a React
preview component. The genesis enterprise/pdf.md audit calls out the
cross-cutting gaps shared with every other migration target:
- No typed error registry. Every failure path raises plain
Errorinstances (new Error("Invalid hex color: …"),Unknown unit, etc.) so consumers cannot classify failures without parsing message strings. - No OpenTelemetry surface. PDF generation is a load-bearing primitive (invoices, receipts, financial reports) yet emits zero counters, histograms, or spans, leaving operators blind to throughput, latency, and failure rates.
- No Zod-validated public boundary. Hex strings, unit strings, element props, document definitions, and template inputs are accepted as plain TS types — invalid input surfaces as cryptic runtime errors deep in the render pipeline.
- No AbortSignal support. Rendering a large report cannot be cancelled.
- No injectable seams. The PDF writer hard-codes
TextEncoder/Dateso tests must rely on real wall-clock metadata.
What
Land @suaveplan/pdf@0.1.0 at packages/devtools/docs/pdf/ (server-tier;
allocation confirmed in scripts/folder-reorg/assignment.yml line 261 under
the docs plugin) across five chunks. Each chunk is an independently
shippable subset with its own 100 / 100 / 100 / 100 coverage gate. The
chunks are sized so a single agent session can land each one with full
enterprise additions (error codes, Zod, OTel, AbortSignal, .md per source
file, real round-trip tests).
Chunk A — Foundation (this change)
Pure utilities + the cross-cutting wiring. Zero external runtime deps beyond
@suaveplan/error, @suaveplan/opentelemetry, @suaveplan/types, tslib,
and zod. No PDF I/O. Easy to verify to 100 % in one pass.
types/—PageSize,PAGE_DIMENSIONS,PageOrientation,Margins,DEFAULT_MARGINS,DocumentMetadata,PageConfig,DEFAULT_PAGE_CONFIG,DocumentDefinition,PageDefinition,ElementNode,RgbColor,PdfColor,Unit.error-codes/—PDF_CODESviadefineCodes("PDF", [...])(INVALID_UNIT,INVALID_HEX_COLOR,INVALID_RGB,INVALID_PAGE_CONFIG,INVALID_DOCUMENT,INVALID_ELEMENT,INVALID_TEMPLATE_INPUT,RENDER_FAILED,LAYOUT_OVERFLOW,IMAGE_DECODE_FAILED,FONT_UNKNOWN,WRITER_DISPOSED,ABORTED) +PdfErrorclass.schemas/— Zod schemas (UnitSchema,HexColorSchema,RgbColorSchema,MarginsSchema,PageConfigSchema,DocumentMetadataSchema).telemetry/— OTel counters (pdf_documents_rendered_total,pdf_pages_emitted_total,pdf_render_errors_total,pdf_validation_failures_total) + histograms (pdf_render_duration_ms,pdf_document_size_bytes) on the@suaveplan/pdfmeter, plus recording helpers.units/—toPoints,fromPoints,convert,parseUnit,formatUnitraisingPdfError(PDF_CODES.INVALID_UNIT)on bad input.colors/—hexToRgb,rgbToPdfColor,hexToPdfColor,rgbToHex,lighten,darken,COLORS,isTransparentraisingPdfError(PDF_CODES.INVALID_HEX_COLOR)on bad input.
Chunk B — Low-level writer + content streams (next change)
pdf-writer/— Real PDF 1.4 binary writer with deterministic byte output (injectableClockfor metadata), font registry for the 14 standard fonts, indirect-object + xref + trailer assembly,escapePdfString,fmt, AbortSignal-awaregenerate.content-stream/— Encoder for text-positioning, line-drawing, and state-save / state-restore operators.
Chunk C — Elements
text-element/,table-element/,image-element/,divider-element/— each with Zod input schemas, OTel counters tagged by element kind, and round-trip verification viapdf-parse.
Chunk D — Layout, styles, document builder, renderer
layout-engine/,style-resolver/,document-builder/,renderer/— cursor tracking, theme merging, fluent builder, top-levelrenderDocument(definition, opts).
Chunk E — Templates + React preview
invoice-template/,receipt-template/,report-template/,pdf-preview/(React 19 component with peer-only React dep).
Out of scope
- PDF/UA and PDF/A profiles — tracked in a follow-up enterprise plan.
- Embedded font subsetting — handled by
@suaveplan/font-subset/@suaveplan/font-compressand wired in via a future plugin slot. - Digital signatures — gated on
@suaveplan/jwsreaching enterprise parity for PDF (/Sigdictionaries). - HTTP plugin (
@suaveplan/api-plugin-pdf) — separate change once Chunks A–E land.