Files
suaveplan/openspec/archive/2026-05-25-enterprise-pdf/tasks.md
2026-05-27 23:29:53 +01:00

6.8 KiB

Tasks — @suaveplan/pdf migration

Chunk A — Foundation (THIS CHANGE)

Status: SHIPPED in this change.

  • Scaffold packages/devtools/docs/pdf/ (package.json, vite, vitest, tsconfig.lib.json, README, biome).
  • src/types/ — page-size enum, dimensions table, margins, document / page / element types, colour types, unit type. One .md per .ts.
  • src/error-codes/ — PDF_CODES via defineCodes, PdfError class, asPdfError helper.
  • src/schemas/ — Zod schemas (UnitSchema, HexColorSchema, RgbColorSchema, MarginsSchema, PageConfigSchema, DocumentMetadataSchema) + parseOrThrow helper raising PdfError.
  • src/telemetry/ — six OTel instruments + createPdfObservability factory + recording helpers.
  • src/units/ — toPoints, fromPoints, convert, parseUnit, formatUnit raising typed errors.
  • src/colors/ — hexToRgb, rgbToPdfColor, hexToPdfColor, rgbToHex, lighten, darken, COLORS, isTransparent.
  • Pre-publish gate: bunx turbo lint typecheck test build --filter=@suaveplan/pdf green, bunx turbo test:coverage --filter=@suaveplan/pdf 100 / 100 / 100 / 100.
  • Refresh MANIFEST.md.

Chunk B — Low-level writer

Status: SHIPPED in this change.

  • src/pdf-writer/ — hand-rolled PDF 1.4 writer with: indirect-object table, font registry for the 14 standard fonts, stream-object assembly (raw + FlateDecode via node:zlib), xref + trailer, deterministic ordering, injectable Clock for /CreationDate, AbortSignal-aware generate, dispose() guard.
  • src/content-stream/ — encoder for BT/ET, Td/T*/TL/Tm, Tj/Tf, q/Q, rg/RG, m/l/h/S/s/f/B, re, cm, w, and image XObject Do placement with text-mode invariants.
  • Real PDF parse verification via pdf-parse@^2.4 round-trip (text content + page count + metadata + ProducerInfo).

Chunk C — Elements

Status: SHIPPED in this change.

  • src/text-element/ — TextElement class + wrapText + resolveStandardFont + measureLineWidth with alignment, max-lines overflow flag, kerning, AbortSignal.
  • src/table-element/ — TableElement<TRow> with column-width auto-distribution, header repeat across pages via renderPage(context, rowStart), alternating row backgrounds, border + column dividers, LAYOUT_OVERFLOW typed error.
  • src/image-element/ — ImageElement with PNG decode via pngjs (RGB + optional SMask alpha plane), JPEG pass-through via SOF-marker walk for width/height, detectImageFormat, scaleToFit, validateImageProps, declared-format mismatch detection.
  • src/divider-element/ — DividerElement with horizontal / vertical orientation, dash pattern, spacing above / below, computeDividerSpacing flow helper.
  • Zod input schema per element type.
  • Real pdf-parse round-trip per text + table element.
  • Pre-publish gate: lint + typecheck + test + build green, coverage 100 / 100 / 100 / 100.

Chunk D — Layout, styles, document builder, renderer

Status: SHIPPED in this change.

  • src/style-resolver/ — StyleResolver cascade (DEFAULT_STYLE → theme → base → overrides), mergeStyles, mergeTheme deep merge incl. headingSizes, normalizeSpacing with PDF.INVALID_ELEMENT on non-finite input, DEFAULT_THEME palette.
  • src/layout-engine/ — LayoutEngine with cursor + content-area + header/footer slot rectangles, landscape orientation swap, advance(cursor, height) returning { fit, remaining }, nextPage, computeTextHeight, applyMargin, AbortSignal propagation, typed PDF.LAYOUT_OVERFLOW / PDF.INVALID_PAGE_CONFIG errors.
  • src/document-builder/ — DocumentBuilder<TData> fluent API (title / author / subject / keywords / creator / creationDate / pageConfig / withData / addText / addTable / addImage / addDivider / newPage / setHeader / setFooter / build) + createDocument<TData>() factory.
  • src/image-registry/ — ImageRegistry + registerImageXObject that allocate per-element PDF XObjects (DCTDecode for JPEG, FlateDecode for PNG RGB plane) and share them across logical pages.
  • src/render-pipeline/ — placeNode / renderTableSlice / renderSlot / pickFontResource helpers operating on a PipelineState bundle.
  • src/renderer/ — top-level renderDocument(doc, opts) returning { bytes, pageCount, durationMs }. AbortSignal-aware, emits six OTel instruments (documents-rendered counter, pages-emitted counter, render-errors counter tagged by code, validation-failures counter, render-duration histogram, document-size histogram). Page-break + table-continuation loop; raises PDF.LAYOUT_OVERFLOW for elements that cannot fit on a fresh page.
  • Real pdf-parse round-trip tests covering text, multi-page flow, oversize-table pagination with header repeat, header / footer stamping across emitted pages, image XObject embedding.
  • Pre-publish gate: bunx turbo lint typecheck test build --filter=@suaveplan/pdf green AND bunx turbo test:coverage --filter=@suaveplan/pdf 100 / 100 / 100 / 100.

Chunk E — Templates

Status: SHIPPED in this change.

  • src/templates/invoice-template/ — createInvoiceTemplate + InvoiceDataSchema + computeInvoiceTotals + formatInvoiceAmount.
  • src/templates/receipt-template/ — createReceiptTemplate + ReceiptDataSchema + computeReceiptTotals + formatReceiptAmount.
  • src/templates/report-template/ — createReportTemplate + ReportDataSchema.
  • Zod input schema per template with PDF.INVALID_TEMPLATE_INPUT error path attaching the failing issue paths.
  • Real pdf-parse round-trip per template.
  • Pre-publish gate: bunx turbo lint typecheck test build --filter=@suaveplan/pdf green AND bunx turbo test:coverage --filter=@suaveplan/pdf 100 / 100 / 100 / 100.

React PDF preview component — deferred to a follow-up change (enterprise-react-pdf-preview stub left under openspec/changes/).

Cross-chunk invariants (preserved at every step)

  • One .md per .ts ≥ 200 words covering Purpose, Features, Usage: Basic, Usage: Advanced, API Reference, Implementation Notes.
  • 100 / 100 / 100 / 100 Istanbul coverage on every touched module — no istanbul ignore comments, no // biome-ignore.
  • Every it / test carries { timeout: 5000 } (30 000 for async).
  • Tests import from @suaveplan/testing/runner per rule 11.
  • No export * in non-root barrels (rule 10).