Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
# 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:
1. **No typed error registry.** Every failure path raises plain `Error`
instances (`new Error("Invalid hex color: …")`, `Unknown unit`, etc.) so
consumers cannot classify failures without parsing message strings.
2. **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.
3. **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.
4. **No AbortSignal support.** Rendering a large report cannot be cancelled.
5. **No injectable seams.** The PDF writer hard-codes `TextEncoder`/`Date`
so 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_CODES` via `defineCodes("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`) + `PdfError` class.
- `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/pdf` meter, plus recording
helpers.
- `units/` — `toPoints`, `fromPoints`, `convert`, `parseUnit`, `formatUnit`
raising `PdfError(PDF_CODES.INVALID_UNIT)` on bad input.
- `colors/` — `hexToRgb`, `rgbToPdfColor`, `hexToPdfColor`, `rgbToHex`,
`lighten`, `darken`, `COLORS`, `isTransparent` raising
`PdfError(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
(injectable `Clock` for metadata), font registry for the 14 standard
fonts, indirect-object + xref + trailer assembly, `escapePdfString`,
`fmt`, AbortSignal-aware `generate`.
- `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 via `pdf-parse`.
### Chunk D — Layout, styles, document builder, renderer
- `layout-engine/`, `style-resolver/`, `document-builder/`, `renderer/` —
cursor tracking, theme merging, fluent builder, top-level
`renderDocument(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-compress` and wired in via a future plugin slot.
- Digital signatures — gated on `@suaveplan/jws` reaching enterprise
parity for PDF (`/Sig` dictionaries).
- HTTP plugin (`@suaveplan/api-plugin-pdf`) — separate change once Chunks
A–E land.