@ulvio/html-to-pdf
v0.2.0
Published
Official TypeScript SDK for the Ulvio html-to-pdf microservice.
Downloads
226
Readme
@ulvio/html-to-pdf
Official TypeScript SDK for the Ulvio html-to-pdf microservice.
A thin, zero-dependency wrapper around the service's POST /api/html-to-pdf
Server-Sent-Events endpoint. It streams progress events and resolves with the
final result.
Talking to the hosted Ulvio platform instead of a self-hosted instance? Use
@ulvio/client, whosehtmlToPdfsub-client targets the platform gateway (with auth + rate limiting). This package talks directly to a standalone html-to-pdf service.
Install
npm install @ulvio/html-to-pdfRequires Node.js 24+.
Configuration
import { HtmlToPdfClient } from '@ulvio/html-to-pdf';
const client = new HtmlToPdfClient({
baseUrl: process.env.HTML_TO_PDF_URL, // e.g. http://localhost:3000
});baseUrl is required. If it's missing when a method is called, the call throws
an HtmlToPdfError with code not_configured — distinct from runtime/network
errors, so misconfiguration is easy to detect:
import { HtmlToPdfError, NOT_CONFIGURED_CODE } from '@ulvio/html-to-pdf';
try {
await client.convert({ html, outputMode: 'base64' });
} catch (err) {
if (err instanceof HtmlToPdfError && err.code === NOT_CONFIGURED_CODE) {
// surface a deployment-time configuration error
}
throw err;
}You can also inject a custom fetch implementation via { fetch }.
Usage
Convert HTML to a base64 PDF
The html field must be base64-encoded. Use the exported encodeHtml
helper, or the renderHtml convenience which encodes for you.
import { HtmlToPdfClient, encodeHtml } from '@ulvio/html-to-pdf';
const client = new HtmlToPdfClient({ baseUrl });
const { pdf } = await client.convert(
{ html: encodeHtml('<h1>Invoice</h1>'), outputMode: 'base64' },
{
onQueued: ({ position }) => console.log('queued at', position),
onProcessing: ({ step, progress }) => console.log(step, progress),
},
);
// `pdf` is a base64 stringrenderHtml is the same thing for plain HTML:
const { pdf } = await client.renderHtml('<h1>Invoice</h1>', { format: 'A4' });Render from a URL
const { pdf } = await client.convert({
sourceUrl: 'https://example.com/invoice/1',
outputMode: 'base64',
});Upload the PDF directly to a presigned URL
Instead of returning the PDF, the service can PUT it to a presigned URL:
const { uploaded, bytes } = await client.convert({
sourceUrl: 'https://example.com/invoice/1',
uploadUrl: presignedPutUrl,
});Provide exactly one input (html or sourceUrl) and exactly one output
(outputMode: 'base64' or uploadUrl).
Render options
await client.convert({
html: encodeHtml('<h1>Report</h1>'),
outputMode: 'base64',
options: {
format: 'A4', // default A4
printBackground: true, // default true
margin: { top: '1cm', bottom: '1cm' },
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:8px">Header</div>',
footerTemplate: '<div style="font-size:8px">Page <span class="pageNumber"></span></div>',
// Wait / network control (all optional; defaults below are the service's)
waitUntil: 'load', // load | domcontentloaded
waitForFunction: undefined, // expression polled in the page after waitUntil
timeoutMs: 60000, // budget for each wait step
blockNetwork: false, // abort every request the page makes
},
});Omitting a field leaves the service's default in place.
waitUntiland networkidle. Puppeteer removed the networkidle lifecycle events fromsetContentin 24.43.1 — without a navigation the signal was never reliable — sonetworkidle0/networkidle2are deprecated here. The service still accepts both and maps them toload, and the default changed fromnetworkidle0toload. A document whose subresources are declared in the markup (images, stylesheets, webfonts,<script src>) is unaffected:loadalready waits for those. A document that keeps fetching afterload— data overfetch/XHR, dynamically injected scripts — should usewaitForFunction, which waits for the document's own readiness signal rather than guessing from network traffic.
Browser-paginated documents (Paged.js)
Documents that paginate themselves in the browser finish laying out long after
load has fired, so the default wait prints a blank page. Tell the service to
wait for the document itself:
const { pdf } = await client.renderHtml(quoteHtml, {
waitUntil: 'domcontentloaded',
waitForFunction: 'window.PAGED_READY === true',
timeoutMs: 30000,
blockNetwork: true,
});waitForFunction is an expression, not a selector, on purpose:
.pagedjs_page exists long before Paged.js has finished, so a selector would
print a half-paginated document. Have the document set a flag when it is done:
window.PagedPolyfill.preview().then(() => { window.PAGED_READY = true; });If the expression never becomes truthy, the render fails with
waitForFunction did not become true within Nms: <expression>.
blockNetwork: true aborts every request the page makes — worth setting for a
self-contained document (inlined CSS, fonts and images as data URIs), so a
render cannot hang on, or leak to, an outside host.
Request size
The service accepts request bodies up to 20 MB. Because html is
base64-encoded inside a JSON body, that is roughly a 15 MB HTML budget. Over
the limit the service returns a 413 that names both numbers:
catch (err) {
if (err instanceof HtmlToPdfError && err.code === 'payload_too_large') {
// err.message: "Request body is 22369740 bytes, which exceeds the 20971520-byte limit"
// err.response.error.limitBytes / .receivedBytes
}
}Errors
Every failure surfaces as an HtmlToPdfError with a code:
| code | Meaning |
| ---------------------- | --------------------------------------------------- |
| not_configured | baseUrl was not set on the client |
| request_failed | Non-2xx response (e.g. a 400 validation error) |
| payload_too_large | Request body exceeded the service's 20 MB limit |
| html_to_pdf_error | The service emitted an SSE error event |
| html_to_pdf_truncated| The SSE stream ended without a complete event |
| html_to_pdf_no_body | The response had no body to stream |
HtmlToPdfError also carries status (HTTP status, when available) and
response (the parsed error body).
API
new HtmlToPdfClient({ baseUrl, fetch? })client.convert(params, callbacks?)— base64 or upload mode (overloaded return type)client.renderHtml(html, options?, callbacks?)— convenience for plain HTMLencodeHtml(html)— base64-encode a UTF-8 HTML stringHtmlToPdfError,NOT_CONFIGURED_CODE
Exported types: HtmlToPdfConfig, PdfOptions, WaitUntil, DeprecatedWaitUntil,
HtmlToPdfRequest (and the
Base64/Upload variants), HtmlToPdfResponse (and variants), QueuedEvent,
ProcessingEvent, HtmlToPdfCallbacks, ErrorResponseBody.
License
UNLICENSED — internal Ulvio package.
