@lupinum/nuxt-pdf 0.4.0-beta.4 → 0.4.0-beta.5

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/CONFORMANCE.md +4 -4
  3. package/README.md +28 -0
  4. package/dist/agent/AGENTS.md +48 -0
  5. package/dist/agent/manifest.json +215 -0
  6. package/dist/agent/pages/docs/examples/supported-behavior.md +75 -0
  7. package/dist/agent/pages/docs/examples.md +163 -0
  8. package/dist/agent/pages/docs/getting-started/installation.md +90 -0
  9. package/dist/agent/pages/docs/getting-started/quickstart.md +126 -0
  10. package/dist/agent/pages/docs/getting-started.md +61 -0
  11. package/dist/agent/pages/docs/guides/contents-links-bookmarks.md +123 -0
  12. package/dist/agent/pages/docs/guides/deployment.md +118 -0
  13. package/dist/agent/pages/docs/guides/errors-and-debugging.md +90 -0
  14. package/dist/agent/pages/docs/guides/images-and-fonts.md +102 -0
  15. package/dist/agent/pages/docs/guides/recipes.md +258 -0
  16. package/dist/agent/pages/docs/guides/remote-images.md +72 -0
  17. package/dist/agent/pages/docs/guides/reusable-components.md +81 -0
  18. package/dist/agent/pages/docs/guides/standalone-node.md +107 -0
  19. package/dist/agent/pages/docs/guides/svg-graphics.md +165 -0
  20. package/dist/agent/pages/docs/guides/testing.md +119 -0
  21. package/dist/agent/pages/docs/learn/document-tree.md +91 -0
  22. package/dist/agent/pages/docs/learn/runtime-and-data.md +63 -0
  23. package/dist/agent/pages/docs/learn/styling-and-layout.md +79 -0
  24. package/dist/agent/pages/docs/learn/templates-and-registry.md +96 -0
  25. package/dist/agent/pages/docs/reference/composables.md +55 -0
  26. package/dist/agent/pages/docs/reference/define-pdf.md +96 -0
  27. package/dist/agent/pages/docs/reference/errors-and-limits.md +89 -0
  28. package/dist/agent/pages/docs/reference/module-options.md +104 -0
  29. package/dist/agent/pages/docs/reference/page-sizes.md +54 -0
  30. package/dist/agent/pages/docs/reference/primitives.md +216 -0
  31. package/dist/agent/pages/docs/reference/registry.md +185 -0
  32. package/dist/agent/pages/docs/reference/standalone.md +53 -0
  33. package/dist/agent/pages/docs/reference/styles.md +176 -0
  34. package/dist/agent/pages/docs/reference/test-utilities.md +124 -0
  35. package/dist/module.json +1 -1
  36. package/dist/module.mjs +1 -1
  37. package/package.json +12 -9
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: "Install Nuxt PDF"
3
+ description: "Add server-side PDF rendering to an existing Nuxt 4 application."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/getting-started/installation"
5
+ route: "/docs/getting-started/installation"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Install Nuxt PDF
13
+
14
+ > Add server-side PDF rendering to an existing Nuxt 4 application.
15
+
16
+ Nuxt PDF is a Nuxt module. It does not require a separate service or CLI.
17
+
18
+ ## Check the requirements
19
+
20
+ | Layer | Supported versions |
21
+ | --- | --- |
22
+ | Node.js | `^22.14.0`, `^24.0.0`, or `^26.0.0` |
23
+ | Nuxt | `^4.4.8` |
24
+ | Vue | `^3.5.0` |
25
+
26
+ Nuxt 3, Node 20, browser rendering, and edge rendering are outside the current
27
+ support boundary.
28
+
29
+ ## Add the module
30
+
31
+ Install the package:
32
+
33
+ ```bash [Terminal]
34
+ pnpm add @lupinum/nuxt-pdf
35
+ ```
36
+
37
+ Add it to `nuxt.config.ts`:
38
+
39
+ ```ts [nuxt.config.ts]
40
+ export default defineNuxtConfig({
41
+ modules: ['@lupinum/nuxt-pdf'],
42
+ })
43
+ ```
44
+
45
+ ### Use a coding agent
46
+
47
+ A coding agent is a development tool that can inspect and change your project.
48
+ After installation, copy this prompt into your coding agent:
49
+
50
+ ```text
51
+ Add Nuxt PDF to this Nuxt application and render one PDF from a Vue template.
52
+ Read the project's existing instructions first. Resolve
53
+ @lupinum/nuxt-pdf/agent-docs from this application's directory and read its
54
+ starting pages. Use the installed version's examples and public types. Preserve
55
+ existing routes, security limits, conventions, and AGENTS.md instructions. Add
56
+ or update one short Nuxt PDF pointer in AGENTS.md if the project allows it; do
57
+ not duplicate the documentation. If the file is absent, create only that
58
+ pointer. Report missing guidance. Verify the server route, PDF output, and one
59
+ invalid request.
60
+ ```
61
+
62
+ If the installed package has no `agent-docs` export, read its packaged README,
63
+ types, and `CONFORMANCE.md`. Use documentation from the matching source tag when
64
+ more detail is needed. Installing or updating the package does not edit project
65
+ instructions. The pointer resolves the installed package, so upgrades and
66
+ rollbacks select the matching documentation without copying it into your
67
+ application.
68
+
69
+ The module now discovers Vue templates under `pdfs/`, generates the typed
70
+ server-only `#pdf` registry, and adds the `/_pdf` development preview.
71
+
72
+ <info icon="lucide:info">
73
+ If your editor does not recognize `#pdf` after installation, run
74
+ `pnpm nuxt prepare` once. Nuxt also refreshes the generated types when the
75
+ development server starts.
76
+ </info>
77
+
78
+ Continue with [Render your first PDF](/raw/docs/getting-started/quickstart.md).
79
+
80
+ ## Add test dependencies later
81
+
82
+ The optional test entry uses `pdfjs-dist` for semantic inspection and
83
+ `@napi-rs/canvas` for raster comparisons. Install them only when you start
84
+ testing PDF output:
85
+
86
+ ```bash [Terminal]
87
+ pnpm add -D pdfjs-dist @napi-rs/canvas
88
+ ```
89
+
90
+ The [testing guide](/raw/docs/guides/testing.md) shows the complete workflow.
@@ -0,0 +1,126 @@
1
+ ---
2
+ title: "Render your first PDF"
3
+ description: "Create, download, and preview a small invoice in a Nuxt application."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/getting-started/quickstart"
5
+ route: "/docs/getting-started/quickstart"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Render your first PDF
13
+
14
+ > Create, download, and preview a small invoice in a Nuxt application.
15
+
16
+ This tutorial starts after you [install the module](/raw/docs/getting-started/installation.md).
17
+ It creates one invoice from typed data and returns it from a Nitro route.
18
+
19
+ <steps>
20
+ ### Create the invoice template
21
+
22
+ Create `pdfs/invoice.vue` at the application root:
23
+
24
+ ```vue [pdfs/invoice.vue]
25
+ <script setup lang="ts">
26
+ type InvoiceProps = {
27
+ invoice: {
28
+ customer: string
29
+ number: string
30
+ total: string
31
+ }
32
+ }
33
+
34
+ const props = defineProps<InvoiceProps>()
35
+
36
+ definePdf<InvoiceProps>({
37
+ title: ({ invoice }) => `Invoice ${invoice.number}`,
38
+ filename: ({ invoice }) => `invoice-${invoice.number}.pdf`,
39
+ sampleData: {
40
+ invoice: {
41
+ customer: 'Ada Lovelace',
42
+ number: 'INV-001',
43
+ total: 'EUR 1,250.00',
44
+ },
45
+ },
46
+ })
47
+ </script>
48
+
49
+ <template>
50
+ <PdfDocument>
51
+ <PdfPage :style="{ fontSize: 11, padding: 48 }">
52
+ <PdfText :style="{ fontSize: 24, marginBottom: 24 }">
53
+ Invoice {{ props.invoice.number }}
54
+ </PdfText>
55
+ <PdfText>{{ props.invoice.customer }}</PdfText>
56
+ <PdfText :style="{ marginTop: 12 }">
57
+ Total: {{ props.invoice.total }}
58
+ </PdfText>
59
+ </PdfPage>
60
+ </PdfDocument>
61
+ </template>
62
+ ```
63
+
64
+ This is the minimum authoring contract:
65
+
66
+ - Templates live under `pdfs/`.
67
+ - The relative filename becomes the registry key. `pdfs/invoice.vue` becomes
68
+ `pdf.invoice`; `pdfs/reports/monthly.vue` becomes `pdf['reports/monthly']`.
69
+ - Each template calls `definePdf()` exactly once.
70
+ - The template has one `PdfDocument` root and at least one `PdfPage`.
71
+ - Text belongs inside `PdfText`.
72
+ - Props are typed with `defineProps()` and passed to `render()`.
73
+ - Style keys use camelCase, such as `fontSize` and `marginBottom`.
74
+ - `sampleData` and preview scenarios exist only in development.
75
+
76
+ ### Add a Nitro route
77
+
78
+ Create `server/api/invoice.get.ts`:
79
+
80
+ ```ts [server/api/invoice.get.ts]
81
+ import { pdf } from '#pdf'
82
+
83
+ export default defineEventHandler(async () => {
84
+ const result = await pdf.invoice.render({
85
+ invoice: {
86
+ customer: 'Ada Lovelace',
87
+ number: 'INV-001',
88
+ total: 'EUR 1,250.00',
89
+ },
90
+ })
91
+
92
+ return result.response()
93
+ })
94
+ ```
95
+
96
+ `#pdf` is generated from the files under `pdfs/`. Its `render()` method infers
97
+ the props from the matching Vue template. It is available to server code only.
98
+
99
+ ### Start Nuxt
100
+
101
+ Run the application's normal development command:
102
+
103
+ ```bash [Terminal]
104
+ pnpm dev
105
+ ```
106
+
107
+ ### Download the result
108
+
109
+ Keep the development server running. In another terminal, download the PDF:
110
+
111
+ ```bash [Terminal]
112
+ curl -o invoice.pdf http://localhost:3000/api/invoice
113
+ ```
114
+
115
+ Open `invoice.pdf` in a PDF viewer. The route sends `application/pdf` with the
116
+ filename resolved by `definePdf()`.
117
+
118
+ ### Open the development preview
119
+
120
+ Open `http://localhost:3000/_pdf/invoice`. The preview uses `sampleData` and
121
+ shows render diagnostics. The `/_pdf` routes and preview data do not exist in a
122
+ production build.
123
+ </steps>
124
+
125
+ The next useful concepts are [templates and the registry](/raw/docs/learn/templates-and-registry.md)
126
+ and [the document tree](/raw/docs/learn/document-tree.md).
@@ -0,0 +1,61 @@
1
+ ---
2
+ title: "Overview"
3
+ description: "Decide whether Nuxt PDF fits the document you need to create."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/getting-started"
5
+ route: "/docs/getting-started"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Overview
13
+
14
+ > Decide whether Nuxt PDF fits the document you need to create.
15
+
16
+ Nuxt PDF turns Vue components into PDFs on a Nuxt server. Put a typed Vue
17
+ template in `pdfs/`, pass it data from a Nitro route, and return the completed
18
+ PDF response.
19
+
20
+ Use Nuxt PDF when you control a structured, data-driven document:
21
+
22
+ - invoices, quotes, and receipts;
23
+ - reports and statements;
24
+ - certificates, tickets, and labels.
25
+
26
+ The package provides PDF layout primitives, local images and fonts, SVG
27
+ graphics, links, bookmarks, tables of contents, development previews, and test
28
+ helpers. Templates run on Node and stay out of the browser bundle.
29
+
30
+ ## Pick the right rendering tool
31
+
32
+ Pick a different tool when:
33
+
34
+ - **You want to print an existing web page.** Use headless Chromium through
35
+ Playwright or Puppeteer. Nuxt PDF does not compile HTML or browser CSS.
36
+ - **You want to display an existing PDF.** Use
37
+ [PDF.js](https://mozilla.github.io/pdf.js/). Nuxt PDF creates files; it is not
38
+ a viewer.
39
+ - **You want to edit, merge, fill, or sign an existing PDF.** Use
40
+ [pdf-lib](https://pdf-lib.js.org/) or a signing service. Nuxt PDF creates new
41
+ documents from application data.
42
+
43
+ ## Current scope
44
+
45
+ Nuxt PDF is in public beta. A minor release can contain a documented
46
+ breaking change before version 1.0.
47
+
48
+ The current release does not include an HTML or CSS compiler, a table engine, a
49
+ chart engine, form fields, signing, tagged PDF output, browser rendering, or
50
+ edge rendering. It does not promise byte-identical output across machines.
51
+
52
+ The [supported behavior](/raw/docs/examples/supported-behavior.md) page summarizes the
53
+ tested boundary. The repository's
54
+ [CONFORMANCE.md](https://github.com/lupinum-dev/nuxt-pdf/blob/main/CONFORMANCE.md)
55
+ contains the maintainer-grade evidence.
56
+
57
+ <cards cols="2">
58
+ <card title="Install Nuxt PDF" description="Add the module to a Nuxt 4 application." icon="lucide:download" to="/docs/getting-started/installation" />
59
+
60
+ <card title="Render your first PDF" description="Create and download a small invoice." icon="lucide:rocket" to="/docs/getting-started/quickstart" />
61
+ </cards>
@@ -0,0 +1,123 @@
1
+ ---
2
+ title: "Add links, bookmarks, and a table of contents"
3
+ description: "usePdfPageNumbers, the multi-pass layout loop, convergence and maxPasses, internal links, and the bookmark outline."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/guides/contents-links-bookmarks"
5
+ route: "/docs/guides/contents-links-bookmarks"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Add links, bookmarks, and a table of contents
13
+
14
+ > usePdfPageNumbers, the multi-pass layout loop, convergence and maxPasses, internal links, and the bookmark outline.
15
+
16
+ Nuxt PDF builds a table of contents, internal links, and a bookmark outline from
17
+ ordinary components. There is no TOC component and no automatic heading
18
+ collection. You author the list, and the engine resolves the page numbers.
19
+
20
+ ## Named destinations and internal links
21
+
22
+ Give any primitive an `id` to make it a named destination, then link to it with
23
+ `<PdfLink href="#id">`:
24
+
25
+ ```vue
26
+ <PdfText id="terms">Terms &amp; conditions</PdfText>
27
+
28
+ <PdfLink href="#terms">Jump to terms</PdfLink>
29
+ ```
30
+
31
+ Internal `#id` links resolve **by name in a single pass**. They do not trigger
32
+ the multi-pass loop and add no cost. A `#id` that does not match any destination
33
+ in the mounted document fails the render with `PDF_TREE_INVALID`.
34
+ The link and destination must both exist in the initial mounted tree and after
35
+ every page-number update; do not conditionally create a destination only after
36
+ its page number becomes available.
37
+
38
+ <info icon="lucide:info">
39
+ When an `id` sits on a node that spans a page boundary, both the printed page
40
+ number and the jump target resolve to the section's **first** page. This is a
41
+ deliberate divergence from React PDF, whose last-writer-wins table points at the
42
+ last page.
43
+ </info>
44
+
45
+ ## Printing page numbers: usePdfPageNumbers
46
+
47
+ To print the page a destination lands on, read the auto-imported
48
+ `usePdfPageNumbers()` composable. It returns a readonly, reactive map from `id`
49
+ to its 1-based page:
50
+
51
+ ```vue
52
+ <script setup lang="ts">
53
+ const pageNumbers = usePdfPageNumbers()
54
+
55
+ const entries = sections.map(s => ({ id: s.id, title: s.title }))
56
+ </script>
57
+
58
+ <template>
59
+ <PdfLink
60
+ v-for="entry in entries"
61
+ :key="entry.id"
62
+ :href="`#${entry.id}`"
63
+ >
64
+ {{ entry.title }} {{ pageNumbers[entry.id] ?? '' }}
65
+ </PdfLink>
66
+ </template>
67
+ ```
68
+
69
+ Reading the composable is the **only** signal that turns on multi-pass layout.
70
+ The engine lays out the document once per pass. Each pass feeds its destination
71
+ page map into the next pass until the map stops changing.
72
+
73
+ <warning icon="lucide:triangle-alert">
74
+ On the first pass every entry is `undefined`. Always keep a fallback
75
+ (`pageNumbers[id] ?? ''`) so the first pass renders a blank rather than throwing.
76
+ </warning>
77
+
78
+ Calling `usePdfPageNumbers()` outside a PDF render throws, rather than returning
79
+ an empty map that could be mistaken for first-pass state.
80
+
81
+ ## Convergence and maxPasses
82
+
83
+ The loop is a fixed point. An ordinary table of contents converges in **two
84
+ passes**: pass one fills the numbers in, pass two lays out with them and the map
85
+ no longer changes.
86
+
87
+ A document does not converge if its layout depends on the page numbers that it prints.
88
+ For example, a TOC entry can change height when its page number changes. After `maxPasses`
89
+ (a validated positive integer on `definePdf`, default 5) the render fails with a
90
+ `PDF_LIMIT_EXCEEDED` `NuxtPdfError`, attributed to the template:
91
+
92
+ ```ts
93
+ definePdf<Props>({
94
+ // Raise the cap only if a genuinely convergent document needs more passes.
95
+ maxPasses: 8,
96
+ })
97
+ ```
98
+
99
+ Non-convergence fails closed; the loop does not force a wrong answer. Raise
100
+ `maxPasses` only when you know the document actually settles.
101
+
102
+ ## Bookmarks (the outline)
103
+
104
+ Add a `bookmark` to any of `PdfPage`, `PdfView`, `PdfText`, or `PdfImage` to
105
+ build the PDF outline. The value is a title string, or an object:
106
+
107
+ ```vue
108
+ <PdfView :id="section.id" :bookmark="{ title: section.title, expanded: true }">
109
+ <PdfText :id="sub.id" :bookmark="sub.title">{{ sub.title }}</PdfText>
110
+ </PdfView>
111
+ ```
112
+
113
+ Nesting follows the component tree, so a child's bookmark nests under its
114
+ ancestor's. The outline carries title text, its initially expanded state, and
115
+ parent/child nesting. Nuxt PDF does not expose reader page-mode preferences or
116
+ bookmark destination geometry (`top`/`left`/`zoom`/`fit`).
117
+
118
+ ## A full example
119
+
120
+ `playground/pdfs/report.vue` in the repository demonstrates the whole feature: a
121
+ contents page linking to each section, live page numbers, per-section bookmarks,
122
+ and a fixed footer with a dynamic page counter. The engine resolves all these features through the
123
+ multi-pass loop.
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: "Deploy and operate"
3
+ description: "Size, cache, queue, and monitor PDF rendering on a tested Node target."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/guides/deployment"
5
+ route: "/docs/guides/deployment"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Deploy and operate
13
+
14
+ > Size, cache, queue, and monitor PDF rendering on a tested Node target.
15
+
16
+ Nuxt PDF renders on Node. The engine, the templates, and the embedded assets
17
+ live in the Nitro server output. The client bundle contains none of them.
18
+ For a separate Node worker, use the
19
+ [standalone registry build](/raw/docs/guides/standalone-node.md).
20
+
21
+ ## Pick a runtime
22
+
23
+ | Runtime | Support | Notes |
24
+ | --- | --- | --- |
25
+ | Node server (default Nitro preset) | Supported | The tested path. Use it for high-volume or large documents. |
26
+ | Standalone Node registry | Locally tested | Compile with the build entry. Test the deployed worker before production use. |
27
+ | Convex Node action | Not deployment-verified | A candidate for the standalone registry. Measure bundle size, memory, and execution limits on the target deployment. |
28
+ | Vercel serverless build | Build-verified | The package test verifies the Vercel Nitro output. Execute your deployed function before production use. |
29
+ | Other Node serverless functions | Expected, not verified | Use a Node-compatible Nitro preset and test the deployed runtime. |
30
+ | Edge workers | Not supported | The engine needs Node APIs. Do not target edge presets. |
31
+
32
+ ## Size the memory budget
33
+
34
+ One render holds the document tree, the laid-out pages, and the output bytes in
35
+ memory at the same time. Give each function or container headroom above your
36
+ largest expected document:
37
+
38
+ - Start from `pdf.limits.maxOutputBytes` (64 MB by default) plus the tree and
39
+ image budgets.
40
+ - Lower `pdf.limits.maxPages` and `pdf.limits.maxOutputBytes` to cap the worst
41
+ case for untrusted input.
42
+ - Load-test with your real templates before you pick an instance size.
43
+
44
+ ## Align timeouts
45
+
46
+ The render deadline lives in `pdf.limits.timeoutMs` (30 seconds by default).
47
+ The platform timeout must stay longer than that budget, or the platform kills
48
+ a render that Nuxt PDF would have finished.
49
+
50
+ ```ts [nuxt.config.ts]
51
+ export default defineNuxtConfig({
52
+ pdf: {
53
+ limits: {
54
+ timeoutMs: 10_000,
55
+ },
56
+ },
57
+ })
58
+ ```
59
+
60
+ Set the platform function timeout to at least the same value plus queueing
61
+ headroom.
62
+
63
+ ## Cache completed renders
64
+
65
+ Cache a PDF only when your application controls every input to the render. A
66
+ template can read the current time or imported application code. An allowlisted
67
+ remote image can also change without a props change. Nuxt PDF does not promise
68
+ that these renders produce the same bytes.
69
+
70
+ Build the cache key from the template version and the canonical render inputs.
71
+ Include the tenant or authorization boundary when documents differ between
72
+ customers. Store the completed bytes in a bounded cache, object store, or CDN
73
+ with an explicit expiry and invalidation rule.
74
+
75
+ Do not use a module-level `Map` as a production cache. It has no size limit,
76
+ does not persist reliably across serverless invocations, and can cross request
77
+ boundaries when its key is incomplete.
78
+
79
+ ## Queue long documents
80
+
81
+ A 200-page report can occupy a worker for seconds. Keep that work off the
82
+ request path when you can:
83
+
84
+ 1. Accept the request and return a job identifier.
85
+ 2. Render in a background worker.
86
+ 3. Store the bytes and let the client poll or subscribe.
87
+
88
+ This pattern also keeps interactive routes responsive while batch jobs run.
89
+
90
+ ## Serverless notes
91
+
92
+ Local fonts and images travel inside the server bundle as validated bytes. The
93
+ repository verifies the Vercel output structure but does not execute that
94
+ deployed function. Test the real target before you send production traffic.
95
+
96
+ - **Bundle size.** Every font and image under `pdfs/` adds to the bundle.
97
+ Remove assets that no template uses; the module embeds only discovered
98
+ files.
99
+ - **Cold starts.** The first invoke parses the bundle. Fewer embedded assets
100
+ mean faster cold starts.
101
+ - **Concurrency.** Size concurrency from load tests on your deployed runtime.
102
+ Nuxt PDF does not currently claim cross-render concurrency guarantees.
103
+ - **Filesystem access.** Templates read no files at runtime. Do not add code
104
+ that reads from the deployment filesystem during a render.
105
+
106
+ ## Monitor renders
107
+
108
+ Every render returns content-free diagnostics: duration, byte length, page
109
+ count, layout passes, layout warnings, and registered font faces. Log them next
110
+ to your request logs to spot slow or oversized documents before users do.
111
+
112
+ ```ts [server/api/invoice.get.ts]
113
+ export default defineEventHandler(async () => {
114
+ const result = await pdf.invoice.render(props)
115
+ console.info('invoice rendered', result.diagnostics)
116
+ return result.response()
117
+ })
118
+ ```
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: "Diagnose render failures"
3
+ description: "Use template attribution, preview diagnostics, and exact error messages to recover from a failed render."
4
+ url: "https://nuxt-pdf.lupinum.com/docs/guides/errors-and-debugging"
5
+ route: "/docs/guides/errors-and-debugging"
6
+ locale: "en"
7
+ section: "Documentation"
8
+ collection: "docs"
9
+ source: "ginko-content"
10
+ ---
11
+
12
+ # Diagnose render failures
13
+
14
+ > Use template attribution, preview diagnostics, and exact error messages to recover from a failed render.
15
+
16
+ First identify whether the failure happens during module setup or during
17
+ `render()`.
18
+
19
+ - Setup validates configuration, template definitions, local images, and
20
+ fonts. Invalid setup can throw `TypeError` before a template render exists.
21
+ - Render failures use `NuxtPdfError` with a machine-readable `code` and
22
+ `templateKey`. Development also includes the relative `templateFile`.
23
+
24
+ The [errors and limits reference](/raw/docs/reference/errors-and-limits.md) defines
25
+ every code and default budget.
26
+
27
+ ## Inspect a render error
28
+
29
+ ```ts [server/api/report.get.ts]
30
+ import { NuxtPdfError, pdf } from '#pdf'
31
+
32
+ export default defineEventHandler(async () => {
33
+ try {
34
+ const result = await pdf.report.render(props)
35
+ return result.response()
36
+ }
37
+ catch (error) {
38
+ if (error instanceof NuxtPdfError) {
39
+ console.error(error.code, error.templateKey, error.templateFile)
40
+ }
41
+ throw error
42
+ }
43
+ })
44
+ ```
45
+
46
+ Log identifiers and diagnostics through the application's normal error system.
47
+ Do not log render props or document text unless the application has a separate
48
+ data-handling policy for them.
49
+
50
+ ## Use the development preview
51
+
52
+ Open `/_pdf/<template-key>`. The preview renders through the same public
53
+ template method as server code and shows duration, byte length, page count,
54
+ layout passes, and registered font faces.
55
+
56
+ When a render fails, the preview keeps document content out of the error panel.
57
+ It shows the code, template, relative source path, and a short message. If the
58
+ previous render succeeded, the old PDF stays visible with a stale marker.
59
+
60
+ ## Match the message to a fix
61
+
62
+ | Message or code | Inspect or change |
63
+ | --- | --- |
64
+ | `Cannot find module '#pdf'` or an untyped import | Run `pnpm nuxt prepare` or restart the development server. |
65
+ | `/_pdf` returns 404 | Use the preview only in development. It is absent from production output. |
66
+ | `PDF_TEMPLATE_NOT_FOUND` | Check the relative filename and registry key. Nested keys retain `/`. |
67
+ | `PDF_TREE_INVALID` | Read the named primitive and prop or nesting rule. Remove DOM attributes and put text inside `PdfText`. |
68
+ | `Font family not registered` | Register the requested family, weight, and style in `pdf.fonts`. |
69
+ | A font throws `TypeError` during setup | Check its relative path, extension, signature, size, and location under `pdfs/fonts`. |
70
+ | `PDF_ASSET_BLOCKED` | Check the image source type, HTTPS allowlist, redirects, and timeout. |
71
+ | `PDF_ASSET_INVALID` | Replace the admitted bytes with a valid PNG or JPEG of the declared format. |
72
+ | `PDF_LIMIT_EXCEEDED` | Read the named limit and cap. Reduce the input or raise only that limit for a known document. |
73
+
74
+ For `maxPasses`, fix layout that changes when destination page numbers appear.
75
+ Raising the cap does not make unstable geometry converge. See the
76
+ [table-of-contents guide](/raw/docs/guides/contents-links-bookmarks.md).
77
+
78
+ ## Monitor successful renders
79
+
80
+ Every completed result includes immutable metadata and diagnostics. Record
81
+ duration, output bytes, page count, layout passes, layout warnings, and
82
+ registered font faces.
83
+
84
+ The development preview explains each layout warning. For example, an
85
+ unbreakable `PdfView` that is taller than the page can render with overflow;
86
+ the preview names the page, node type, node height, and usable page height.
87
+ This is a warning rather than a render failure because the output is still a
88
+ valid PDF and some authors intentionally use oversized pages.
89
+ The failure shows up in a customer's invoice, not a test run. Alert on trends
90
+ before time or output budgets become routine failures.