Skip to Content
API Referencepdf-export-server

@stesura/pdf-export-server

Server side of PDF export: loads the export page in headless Chromium, prints it and assembles the PDF. Node only; the browser side is @stesura/pdf-export-client, and the PDF export guide covers setup, egress rules and limits.

renderDocToPdf

Launches a browser, renders one doc and closes the browser. Takes RenderDocToPdfArgs (RenderOptions plus headless) and resolves to the PDF bytes. For repeated renders, keep an exporter from createPdfExporter.

RenderDocToPdfArgs

NameTypeDefault
docExportDocJSON
fileNamestring
exportPageUrlstring

Absolute http(s) URL of the export page, e.g. http://localhost:3002/export/print.

signalAbortSignal

Aborts the render: closes the page (interrupting in-flight evaluate / pdf) and rejects with an AbortError. render settles only once its page is closed.

readyTimeoutMsnumber

One deadline from the render call to a ready export page: browser launch, navigation and the readiness wait. Capture comes on top. Defaults to 60s.

captureTimeoutMsnumber

Budget for positioning, printing and merging once the page is ready, on top of readyTimeoutMs. Defaults to 30s + 2s per page.

maxPagesnumber

Cap on captured pages, checked before anything is printed. Embedded PDFs that would push the total past it are left out. Defaults to 500.

maxSubresourceBytesnumber

Per-subresource response cap in bytes. Defaults to 16 MiB.

maxSubresourcesnumber

How many subresources one render may fetch. Defaults to 1000.

maxEmbedBytesnumber

Response cap for one embedded PDF file. Defaults to 50 MiB.

allowedHostsstring[]

Narrows the export page’s requests to its own host plus these host[:port]s. Without it the never-allow class (loopback, private, link-local, cloud metadata, non-http(s)…) is still refused; see buildRequestPolicy . Hosts other than the export page’s own are also checked at the resolved address (DNS rebinding).

The exporter fetches subresources itself, without cookies or authorization, so assets that need credentials must be served unauthenticated.

headlessboolean

Launch headed (for debugging). Defaults to headless unless PDF_EXPORT_HEADED=1.

createPdfExporter

Keeps one Chromium for many renders, each in a fresh context and page, so nothing carries over between exports. The browser launches on the first render (warmup() launches it early) and relaunches if it crashes. Only the process stays warm: request interception disables Chromium’s HTTP cache.

Concurrent render calls are safe but compete for CPU in one browser; queue them, or run one exporter per worker. Every render is bounded by the timeouts and caps in RenderOptions, and signal aborts it.

Parameters:
NameTypeDefault
options.headlessboolean

Launch headed (for debugging). Defaults to headless unless PDF_EXPORT_HEADED=1.

Returns:
Promise<PdfExporter>

CreatePdfExporterOptions

NameTypeDefault
headlessboolean

Launch headed (for debugging). Defaults to headless unless PDF_EXPORT_HEADED=1.

PdfExporter

NameTypeDefault
render(options: RenderOptions) => Promise<Uint8Array<ArrayBufferLike>>

Renders one doc to a PDF using the held browser (fresh context per call).

warmup() => Promise<void>

Pre-launches the browser so the first render skips the ~0.3–1s cold start.

close() => Promise<void>

Closes the browser. render rejects afterwards.

RenderOptions

NameTypeDefault
docExportDocJSON
fileNamestring
exportPageUrlstring

Absolute http(s) URL of the export page, e.g. http://localhost:3002/export/print.

signalAbortSignal

Aborts the render: closes the page (interrupting in-flight evaluate / pdf) and rejects with an AbortError. render settles only once its page is closed.

readyTimeoutMsnumber

One deadline from the render call to a ready export page: browser launch, navigation and the readiness wait. Capture comes on top. Defaults to 60s.

captureTimeoutMsnumber

Budget for positioning, printing and merging once the page is ready, on top of readyTimeoutMs. Defaults to 30s + 2s per page.

maxPagesnumber

Cap on captured pages, checked before anything is printed. Embedded PDFs that would push the total past it are left out. Defaults to 500.

maxSubresourceBytesnumber

Per-subresource response cap in bytes. Defaults to 16 MiB.

maxSubresourcesnumber

How many subresources one render may fetch. Defaults to 1000.

maxEmbedBytesnumber

Response cap for one embedded PDF file. Defaults to 50 MiB.

allowedHostsstring[]

Narrows the export page’s requests to its own host plus these host[:port]s. Without it the never-allow class (loopback, private, link-local, cloud metadata, non-http(s)…) is still refused; see buildRequestPolicy . Hosts other than the export page’s own are also checked at the resolved address (DNS rebinding).

The exporter fetches subresources itself, without cookies or authorization, so assets that need credentials must be served unauthenticated.

ExportDocJSON

NameTypeDefault
typestring

buildRequestPolicy

Parameters:
NameTypeDefault
exportPageUrlstring
allowedHostsstring[]
Returns:
(url: string) => RequestDecision

RequestDecision

NameTypeDefault
toString() => string
charAt(pos: number) => string
charCodeAt(index: number) => number
concat(...strings: string[]) => string
indexOf(searchString: string, position?: number | undefined) => number
lastIndexOf(searchString: string, position?: number | undefined) => number
localeCompare{ (that: string): number; (that: string, locales?: string | string[] | undefined, options?: CollatorOptions | undefined): number; (that: string, locales?: LocalesArgument, options?: CollatorOptions | undefined): number; }
match{ (regexp: string | RegExp): RegExpMatchArray | null; (matcher: { [Symbol.match](string: string): RegExpMatchArray | null; }): RegExpMatchArray | null; }
replace{ (searchValue: string | RegExp, replaceValue: string): string; (searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: { ...; }, replaceValue: string): string; (searchValue: { ...; }, replacer: (substring: string, ...args: any[]) => string): string; }
slice(start?: number | undefined, end?: number | undefined) => string
split{ (separator: string | RegExp, limit?: number | undefined): string[]; (splitter: { [Symbol.split](string: string, limit?: number | undefined): string[]; }, limit?: number | undefined): string[]; }
substring(start: number, end?: number | undefined) => string
toLowerCase() => string
toLocaleLowerCase{ (locales?: string | string[] | undefined): string; (locales?: LocalesArgument): string; }
toUpperCase() => string
toLocaleUpperCase{ (locales?: string | string[] | undefined): string; (locales?: LocalesArgument): string; }
trim() => string
lengthnumber
substr(from: number, length?: number | undefined) => string
valueOf() => string
codePointAt(pos: number) => number | undefined
includes(searchString: string, position?: number | undefined) => boolean
endsWith(searchString: string, endPosition?: number | undefined) => boolean
normalize{ (form: "NFC" | "NFD" | "NFKC" | "NFKD"): string; (form?: string | undefined): string; }
repeat(count: number) => string
startsWith(searchString: string, position?: number | undefined) => boolean
anchor(name: string) => string
big() => string
bold() => string
fixed() => string
fontcolor(color: string) => string
fontsize{ (size: number): string; (size: string): string; }
italics() => string
small() => string
strike() => string
sub() => string
sup() => string
padStart(maxLength: number, fillString?: string | undefined) => string
padEnd(maxLength: number, fillString?: string | undefined) => string
trimEnd() => string
trimStart() => string
trimLeft() => string
trimRight() => string
matchAll(regexp: RegExp) => RegExpStringIterator<RegExpExecArray>
replaceAll{ (searchValue: string | RegExp, replaceValue: string): string; (searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
at(index: number) => string | undefined
__@iterator@36() => StringIterator<string>

sanitizeFileName

Parameters:
NameTypeDefault
namestring
Returns:
string

contentDispositionAttachment

Parameters:
NameTypeDefault
fileNamestring
Returns:
string

resolveExportPageOrigin

Parameters:
NameTypeDefault
configuredOriginstring
requestOriginstring
Returns:
string

readPdfExportRequest

Reads and validates a { doc, fileName? } body from the request stream. Use it instead of await request.json(), which buffers the whole body before any cap applies; this stops reading at maxBytes. Throws PdfExportRequestError, whose status is the one to answer with: 413 for too large, 400 for malformed.

Parameters:
NameTypeDefault
bodyReadableStream<Uint8Array<ArrayBufferLike>>
options{ maxBytes: number; }
Returns:
Promise<PdfExportRequest>

PdfExportRequest

NameTypeDefault
docExportDocJSON
fileNamestring

readJsonBody

The same capped read without the doc validation, for a route with its own body shape.

Parameters:
NameTypeDefault
bodyReadableStream<Uint8Array<ArrayBufferLike>>
__1{ maxBytes: number; }
Returns:
Promise<unknown>
Last updated on