@shad0wz7/nestjs-pdf 3.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,5 @@
1
1
  # nestjs-pdf
2
2
 
3
- > **v3 — ESM-first, NestJS 10, 11 & 12.** This is the active `main` line: authored
4
- > in ESM, published as a dual CJS/ESM package, and supports **NestJS 10, 11 and 12**.
5
- >
6
- > Need a pure CommonJS package or a Node.js version older than `20.19`? Use the
7
- > maintenance `v2` branch instead:
8
- >
9
- > ```bash
10
- > npm install @shad0wz7/nestjs-pdf@^2
11
- > ```
12
- >
13
- > See the [`v2` branch README](https://github.com/ShadowZ11/nestjs-pdf/blob/v2/README.md)
14
- > for details. It receives maintenance/security fixes only — new features land on v3.
15
-
16
3
  [![npm version](https://img.shields.io/npm/v/%40shad0wz7%2Fnestjs-pdf?label=npm%20version)](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
17
4
  [![NPM License](https://img.shields.io/npm/l/%40shad0wz7%2Fnestjs-pdf)](https://github.com/ShadowZ11/nestjs-pdf?tab=MIT-1-ov-file)
18
5
  [![npm downloads](https://img.shields.io/npm/dm/%40shad0wz7%2Fnestjs-pdf?label=npm%20downloads)](https://www.npmjs.com/package/%40shad0wz7%2Fnestjs-pdf)
@@ -20,12 +7,13 @@
20
7
  [![codecov](https://codecov.io/github/ShadowZ11/nestjs-pdf/graph/badge.svg?token=K0MNB1ZKYK)](https://codecov.io/github/ShadowZ11/nestjs-pdf)
21
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-6.0.3-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
22
9
 
23
- A NestJS library to generate and manipulate PDFs using Puppeteer and multiple template engines, including Handlebars, EJS, Pug, MJML, Nunjucks, Eta and Mustache.
10
+ A NestJS library to generate and manipulate PDFs from multiple template engines (Handlebars, EJS, Pug, MJML, Nunjucks, Eta, Mustache and Liquid), printed by a local Puppeteer browser, a [Gotenberg](https://gotenberg.dev) server or the [Api2Pdf](https://www.api2pdf.com) API.
24
11
 
25
12
  Main features:
26
13
 
27
- - Generate PDFs from HTML, Handlebars, EJS, Pug, MJML, Nunjucks, Eta or Mustache templates
28
- - Concurrency limiting for Puppeteer jobs (p-limit)
14
+ - Generate PDFs from HTML, Handlebars, EJS, Pug, MJML, Nunjucks, Eta, Mustache or Liquid templates
15
+ - Pluggable PDF providers: local Puppeteer (default), Gotenberg or Api2Pdf, behind the same service API
16
+ - Concurrency limiting for PDF jobs (p-limit)
29
17
  - Add signature fields based on a text anchor in an existing PDF
30
18
  - Text or image watermarks (centered or tiled) on generated or existing PDFs
31
19
  - Simple API (module and service) to integrate easily into a Nest application
@@ -50,19 +38,53 @@ yarn add @shad0wz7/nestjs-pdf
50
38
  pnpm add ../path/to/nestjs-pdf
51
39
  ```
52
40
 
53
- ## Quick start
41
+ ### PDF provider
54
42
 
55
- Import the module and configure it in your Nest application:
43
+ Pick who prints the PDFs and install what it needs:
44
+
45
+ | Provider | Install | Notes |
46
+ | --------------------- | ------------------------------------------- | -------------------------------------------------------------------- |
47
+ | `puppeteer` (default) | `npm install puppeteer @puppeteer/browsers` | Local Chromium, downloaded on startup unless `executablePath` is set |
48
+ | `gotenberg` | nothing | Needs a [Gotenberg 8](https://gotenberg.dev) server |
49
+ | `api2pdf` | nothing | Needs an [Api2Pdf](https://www.api2pdf.com) API key |
50
+
51
+ `puppeteer` and `@puppeteer/browsers` are **optional peer dependencies**: with a remote provider, no browser is installed nor downloaded. The `puppeteer` provider needs **both** packages: if either is missing, the application fails on startup with an `EngineNotAvailableException` telling you what to install. With `executablePath`, the browser is not downloaded, so only `puppeteer` is needed. See [PDF providers](#pdf-providers) to configure Gotenberg or Api2Pdf.
52
+
53
+ ### Template engines
54
+
55
+ Template engines are **optional peer dependencies**: none of them is installed with the library. Install only the ones you use, as dependencies of your own project:
56
+
57
+ | Engine | Package to install |
58
+ | ---------- | ------------------ |
59
+ | Handlebars | `handlebars` |
60
+ | EJS | `ejs` |
61
+ | Pug | `pug` |
62
+ | MJML | `mjml` |
63
+ | Nunjucks | `nunjucks` |
64
+ | Eta | `eta` |
65
+ | Mustache | `mustache` |
66
+ | Liquid | `liquidjs` |
56
67
 
57
- To use a template engine, you need to install the corresponding package as a dependency in your project:
68
+ ```bash
69
+ # e.g. to render Handlebars templates
70
+ npm install handlebars
71
+ ```
72
+
73
+ Engines are loaded lazily, the first time you render a template with them: your application starts fine with any subset of engines installed. Rendering with an engine whose package is missing throws an `EngineNotAvailableException` (code `ENGINE_NOT_AVAILABLE`) telling you what to install:
74
+
75
+ ```text
76
+ Pug engine is not available: the 'pug' package is not installed. Run `npm install pug` to use it.
77
+ ```
78
+
79
+ Anchor-based signature placement also relies on `pdfjs-dist`, which needs `@napi-rs/canvas` to be installed in your project (see the signature example in [Quick start](#quick-start)).
58
80
 
59
- - For Handlebars: `npm install handlebars`
60
- - For EJS: `npm install ejs`
61
- - For Pug: `npm install pug`
62
- - For MJML: `npm install mjml`
63
- - For Nunjucks: `npm install nunjucks`
64
- - For Eta: `npm install eta`
65
- - For Mustache: `npm install mustache`
81
+ > **Upgrading from v3:** engines used to be installed automatically. If you relied on that, add the engines you use to your project's dependencies.
82
+
83
+ > **Upgrading from v4:** Puppeteer is no longer installed with the library. If you keep the default provider, add it to your project: `npm install puppeteer @puppeteer/browsers`. `NestjsPdfErrorCode` also gained a `RENDERER_CONFIGURATION_ERROR` member: update any exhaustive `Record<NestjsPdfErrorCode, …>` map. `pdfOptions` is now typed by the library's own `PdfOptions` (same shape as Puppeteer's `PDFOptions`) and `browser` accepts plain strings (`'chromium'`) as well as the `Browser` enum, so the published types no longer depend on Puppeteer.
84
+
85
+ ## Quick start
86
+
87
+ Import the module and configure it in your Nest application:
66
88
 
67
89
  ```ts
68
90
  import { Module } from '@nestjs/common';
@@ -227,16 +249,92 @@ Good to know:
227
249
  - Names are checked by resolving them before the request, so a domain that changes its address between the check and the request (DNS rebinding) can slip through. Use `allowedHosts` (or `javascriptEnabled: false`) when the content is fully untrusted.
228
250
  - `window.open` pop-ups are blocked: the library no longer passes `--disable-popup-blocking` to Chrome. If you set `extraPuppeteerArgs`, do not add it back.
229
251
 
252
+ ## PDF providers
253
+
254
+ Templates, watermarks and signatures always run in your application; the provider only turns the rendered HTML into a PDF. Choose it with `provider` in `forRoot` / `forRootAsync` (it is ignored in per-call options):
255
+
256
+ ```ts
257
+ // Gotenberg (self-hosted): docker run --rm -p 3000:3000 gotenberg/gotenberg:8
258
+ NestjsPdfModule.forRoot({
259
+ provider: {
260
+ type: 'gotenberg',
261
+ url: 'http://localhost:3000',
262
+ // optional: when Gotenberg runs with --api-enable-basic-auth
263
+ basicAuth: { username: 'user', password: 'pass' },
264
+ // optional: extra headers, e.g. for an authenticating proxy
265
+ headers: { 'X-Api-Key': '...' },
266
+ },
267
+ pdfOptions: { format: 'A4', printBackground: true },
268
+ });
269
+
270
+ // Api2Pdf (SaaS)
271
+ NestjsPdfModule.forRootAsync({
272
+ inject: [ConfigService],
273
+ useFactory: (config: ConfigService) => ({
274
+ provider: { type: 'api2pdf', apiKey: config.getOrThrow('API2PDF_KEY') },
275
+ }),
276
+ });
277
+ ```
278
+
279
+ The service API does not change: `generatePdfFromHtml`, every `generatePdfFrom<Engine>*` method, `watermark` and `signal` work the same with every provider. A missing `url` / `apiKey` or an unknown `type` throws a `RendererConfigurationException` on startup.
280
+
281
+ ### Page options
282
+
283
+ Keep using `pdfOptions` (Puppeteer's format). For Gotenberg and Api2Pdf, these fields are translated: `format`, `width`, `height`, `margin` (numbers are pixels; strings in `px`, `in`, `cm` or `mm`), `landscape`, `printBackground`, `omitBackground`, `scale`, `pageRanges`, `displayHeaderFooter` with `headerTemplate` / `footerTemplate`, plus `preferCSSPageSize`, `tagged` and `outline` for Gotenberg. The other fields are ignored. Like the Puppeteer provider, the page is rendered with the `screen` media type, and with zero margins unless you set them.
284
+
285
+ Provider-specific options, per call or in `forRoot`:
286
+
287
+ ```ts
288
+ await pdfService.generatePdfFromHtml(
289
+ '<img src="logo.png"><link rel="stylesheet" href="style.css">',
290
+ {
291
+ gotenbergOptions: {
292
+ // files the HTML references by relative name, sent next to index.html
293
+ assets: [
294
+ {
295
+ filename: 'logo.png',
296
+ content: await readFile('logo.png'),
297
+ contentType: 'image/png',
298
+ },
299
+ {
300
+ filename: 'style.css',
301
+ content: 'body { font-family: sans-serif; }',
302
+ contentType: 'text/css',
303
+ },
304
+ ],
305
+ // any Gotenberg Chromium form field (PDF/A, wait...), wins over the derived ones
306
+ formFields: { pdfa: 'PDF/A-2b', waitDelay: '500ms' },
307
+ },
308
+ },
309
+ );
310
+
311
+ await pdfService.generatePdfFromHtml(html, {
312
+ // any Api2Pdf Chrome option, merged over the derived ones
313
+ api2pdfOptions: { fileName: 'invoice.pdf', options: { delay: 500 } },
314
+ });
315
+ ```
316
+
317
+ ### Good to know
318
+
319
+ - **Local files:** a remote provider cannot read your disk. With Gotenberg, send images, fonts and CSS as `assets`; with Api2Pdf, use public URLs or inline them (`data:` URIs).
320
+ - **`security` is Puppeteer-only:** the request filter runs in the local browser, so with a remote provider it throws a `RendererConfigurationException` instead of being silently skipped. With Gotenberg, restrict outbound requests on the server (`--chromium-allow-list`, `--chromium-deny-list`); with Api2Pdf, only render HTML you trust.
321
+ - **With Api2Pdf, data leaves your infrastructure:** the HTML is sent to a third-party service, which matters for personal data (GDPR). Gotenberg stays on your own servers.
322
+ - **Errors:** an HTTP error from the provider becomes a `PdfGenerationException` whose message holds the status and the start of the response body.
323
+ - **Browser options** (`headless`, `browser`, `browserTag`, `buildId`, `useLockedBrowser`, `executablePath`, `extraPuppeteerArgs`, `browserInstallBaseUrl`, `cleanupBrowserCacheOnExit`) only apply to the `puppeteer` provider.
324
+
230
325
  ## Options and configuration
231
326
 
232
- The library exposes Puppeteer options via the `PuppeteerParameters` interface (see `src/puppeteer/puppeteer-parameters.interface.ts`). You can configure:
327
+ The library exposes its options via the `PuppeteerParameters` interface (see `src/puppeteer/puppeteer-parameters.interface.ts`). You can configure:
233
328
 
234
329
  | | Description |
235
330
  | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
236
- | `pdfOptions` | The pdf options can be found on: https://pptr.dev/api/puppeteer.pdfoptions. Native Puppeteer options for `page.pdf` |
331
+ | `provider` | Who prints the PDF: `{ type: 'puppeteer' }` (default), `{ type: 'gotenberg', url, basicAuth?, headers? }` or `{ type: 'api2pdf', apiKey, baseUrl?, headers? }`. Module-level only. See [PDF providers](#pdf-providers) |
332
+ | `pdfOptions` | The pdf options can be found on: https://pptr.dev/api/puppeteer.pdfoptions. Native Puppeteer options for `page.pdf`. Remote providers translate the main ones, see [Page options](#page-options) |
333
+ | `gotenbergOptions` | Gotenberg provider only: `assets` (files sent next to `index.html`) and raw `formFields` |
334
+ | `api2pdfOptions` | Api2Pdf provider only: `fileName` and raw Chrome `options` |
237
335
  | `watermark` | Stamps a text or image watermark on the generated PDF: `text` or `image`, `opacity`, `rotation`, `fontSize`, `font`, `color`, `imageWidth`, `position`, `tileSpacing`, `pages`. See [Watermark](#watermark) |
238
336
  | `signal` | An `AbortSignal` that cancels the generation, including while it waits for a free slot. The promise rejects with `signal.reason`; use `AbortSignal.timeout(ms)` for a timeout. See [Cancellation and timeout](#cancellation-and-timeout) |
239
- | `security` | Filters what the rendered HTML may load (SSRF protection): `blockPrivateNetworks`, `allowedHosts`, `allowedFileRoots`, `javascriptEnabled`. Disabled unless set; pass `{}` for the defaults. See [Restricting what the HTML can load](#restricting-what-the-html-can-load-ssrf-protection) |
337
+ | `security` | Puppeteer provider only. Filters what the rendered HTML may load (SSRF protection): `blockPrivateNetworks`, `allowedHosts`, `allowedFileRoots`, `javascriptEnabled`. Disabled unless set; pass `{}` for the defaults. See [Restricting what the HTML can load](#restricting-what-the-html-can-load-ssrf-protection) |
240
338
  | `hbsOptions` | Handlebars configuration: `templateDirectory` (base dir for `renderFile`), `partialDirectory` (files registered as partials), `compileOptions`, `templateOptions`, and `helpers` (`{ name, fn }[]`). Like the other engines it can be set globally here or overridden per call (`generatePdfFromTemplateHbs*(..., { hbsOptions })`). Helpers/partials run on an isolated Handlebars environment (no global leakage). |
241
339
  | `ejsOptions` | The ejs options can be found on [EJS documentation](https://ejs.co/#options) |
242
340
  | `pugOptions` | The pug options can be found on [Pug documentation](https://pugjs.org/api/reference.html#options) |
@@ -244,6 +342,7 @@ The library exposes Puppeteer options via the `PuppeteerParameters` interface (s
244
342
  | `nunjucksOptions` | The nunjucks options can be found on [Nunjucks documentation](https://mozilla.github.io/nunjucks/api.html#configure) |
245
343
  | `etaOptions` | The eta options can be found on [Eta documentation](https://eta.js.org/docs/4.x.x/api/configuration) |
246
344
  | `mustacheOptions` | The mustache options can be found on [Mustache documentation](https://mustache.github.io/mustache.5.html) |
345
+ | `liquidOptions` | The liquid options can be found on [LiquidJS documentation](https://liquidjs.com/tutorials/options.html) |
247
346
  | `browser` | The browser to use for the PDF generation. By default: `Browser.CHROMIUM`. Allowed values `Browser.CHROMIUM`, `Browser.CHROMEHEADLESSSHELL`, `Browser.CHROME`, `Browser.FIREFOX`, `Browser.CHROMEDRIVER` ([Official documentation of Browser](https://pptr.dev/browsers-api/browsers.browser)) |
248
347
  | `browserTag` | The version of the browser to use for the PDF generation. By default: `BrowserTag.LATEST` if Browser is **CHROMIUM** else `BrowserTag.STABLE`. Allowed values `BrowserTag.STABLE`, `BrowserTag.LATEST`, `BrowserTag.BETA`, `BrowserTag.DEV`, `BrowserTag.CANARY` |
249
348
  | `browserInstallBaseUrl` | The baseUrl used for the installation of the browser. This baseUrl is passed to the [`install`](https://pptr.dev/browsers-api/browsers.install) method of `@puppeteer/browsers` |
@@ -300,7 +399,7 @@ You can use `forRootAsync(...)` if you need to provide configuration asynchronou
300
399
 
301
400
  ## Error handling
302
401
 
303
- All errors thrown by the library (missing template engine, template rendering failures, browser installation issues, PDF generation failures, signature placement issues, ...) are typed exceptions extending the abstract `NestjsPdfException` class, exported from the package root alongside a stable `NestjsPdfErrorCode` enum:
402
+ All errors thrown by the library (missing template engine, template rendering failures, browser installation issues, PDF generation failures, signature placement issues, invalid watermarks, ...) are typed exceptions extending the abstract `NestjsPdfException` class, exported from the package root alongside a stable `NestjsPdfErrorCode` enum:
304
403
 
305
404
  ```typescript
306
405
  import {