@lupinum/nuxt-pdf 0.3.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.
Files changed (75) hide show
  1. package/API_REPORT.md +262 -0
  2. package/CHANGELOG.md +168 -0
  3. package/CONFORMANCE.md +496 -0
  4. package/LICENSE +21 -0
  5. package/README.md +203 -0
  6. package/THIRD_PARTY_NOTICES.md +75 -0
  7. package/dist/module.d.mts +24 -0
  8. package/dist/module.json +12 -0
  9. package/dist/module.mjs +368 -0
  10. package/dist/runtime/authoring.d.ts +141 -0
  11. package/dist/runtime/authoring.js +25 -0
  12. package/dist/runtime/components/_props.d.ts +196 -0
  13. package/dist/runtime/components/_props.js +63 -0
  14. package/dist/runtime/components/document.d.ts +9 -0
  15. package/dist/runtime/components/document.js +26 -0
  16. package/dist/runtime/components/index.d.ts +4 -0
  17. package/dist/runtime/components/index.js +27 -0
  18. package/dist/runtime/components/svg.d.ts +17 -0
  19. package/dist/runtime/components/svg.js +50 -0
  20. package/dist/runtime/composables/index.d.ts +2 -0
  21. package/dist/runtime/composables/index.js +3 -0
  22. package/dist/runtime/composables/use-pdf-page-numbers.d.ts +39 -0
  23. package/dist/runtime/composables/use-pdf-page-numbers.js +17 -0
  24. package/dist/runtime/define-pdf.d.ts +6 -0
  25. package/dist/runtime/define-pdf.js +5 -0
  26. package/dist/runtime/fonts.d.ts +16 -0
  27. package/dist/runtime/fonts.js +0 -0
  28. package/dist/runtime/renderer/index.d.ts +3 -0
  29. package/dist/runtime/renderer/index.js +1 -0
  30. package/dist/runtime/renderer/node-ops.d.ts +5 -0
  31. package/dist/runtime/renderer/node-ops.js +281 -0
  32. package/dist/runtime/renderer/patch-prop.d.ts +3 -0
  33. package/dist/runtime/renderer/patch-prop.js +223 -0
  34. package/dist/runtime/renderer/render-component.d.ts +14 -0
  35. package/dist/runtime/renderer/render-component.js +201 -0
  36. package/dist/runtime/renderer/types.d.ts +33 -0
  37. package/dist/runtime/renderer/types.js +56 -0
  38. package/dist/runtime/renderer/validate-tree.d.ts +3 -0
  39. package/dist/runtime/renderer/validate-tree.js +428 -0
  40. package/dist/runtime/server/assets/errors.d.ts +12 -0
  41. package/dist/runtime/server/assets/errors.js +15 -0
  42. package/dist/runtime/server/assets/remote.d.ts +48 -0
  43. package/dist/runtime/server/assets/remote.js +234 -0
  44. package/dist/runtime/server/assets/resolve-asset.d.ts +53 -0
  45. package/dist/runtime/server/assets/resolve-asset.js +482 -0
  46. package/dist/runtime/server/engine/CONTRACTS.md +386 -0
  47. package/dist/runtime/server/engine/fonts.d.ts +8 -0
  48. package/dist/runtime/server/engine/fonts.js +19 -0
  49. package/dist/runtime/server/engine/layout-passes.d.ts +42 -0
  50. package/dist/runtime/server/engine/layout-passes.js +58 -0
  51. package/dist/runtime/server/engine/react-pdf-pdfkit.d.ts +7 -0
  52. package/dist/runtime/server/engine/render-document.d.ts +31 -0
  53. package/dist/runtime/server/engine/render-document.js +236 -0
  54. package/dist/runtime/server/index.d.ts +5 -0
  55. package/dist/runtime/server/index.js +9 -0
  56. package/dist/runtime/server/preview.d.ts +17 -0
  57. package/dist/runtime/server/preview.js +332 -0
  58. package/dist/runtime/server/registry.d.ts +41 -0
  59. package/dist/runtime/server/registry.js +270 -0
  60. package/dist/runtime/server/render-limits.d.ts +51 -0
  61. package/dist/runtime/server/render-limits.js +143 -0
  62. package/dist/runtime/server/result.d.ts +6 -0
  63. package/dist/runtime/server/result.js +81 -0
  64. package/dist/runtime/server/tsconfig.json +3 -0
  65. package/dist/runtime/shared/errors.d.ts +22 -0
  66. package/dist/runtime/shared/errors.js +22 -0
  67. package/dist/runtime/shared/index.d.ts +4 -0
  68. package/dist/runtime/shared/index.js +7 -0
  69. package/dist/runtime/shared/template.d.ts +63 -0
  70. package/dist/runtime/shared/template.js +1 -0
  71. package/dist/shared/nuxt-pdf.D2ZziYn4.mjs +1132 -0
  72. package/dist/test.d.mts +198 -0
  73. package/dist/test.mjs +696 -0
  74. package/dist/types.d.mts +13 -0
  75. package/package.json +135 -0
package/API_REPORT.md ADDED
@@ -0,0 +1,262 @@
1
+ # Public API declaration report
2
+
3
+ This file is derived from the built declarations and is intentionally checked
4
+ in. Rebuild with `pnpm build && pnpm api:write`; CI verifies it with
5
+ `pnpm test:api`. Package code and generated `#pdf` registries remain the
6
+ canonical sources.
7
+
8
+ ## Package root
9
+
10
+ Source: `dist/types.d.mts`
11
+
12
+ ```ts
13
+ export { type RemoteAssetOptions } from '../dist/runtime/server/assets/remote.js'
14
+
15
+ export { type PdfLimitsOptions } from '../dist/runtime/server/render-limits.js'
16
+
17
+ export { type PdfFontDeclaration, type PdfFontStyle, type PdfFontWeight, type PdfFontWeightName } from '../dist/runtime/fonts.js'
18
+
19
+ export { type PdfLength, type PdfLengthOrPercentage, type PdfPercentage, type PdfStyle, type PdfStyleEntry, type PdfStyleValue } from '../dist/runtime/authoring.js'
20
+
21
+ export { type PdfBaseProps, type PdfBookmark, type PdfCircleProps, type PdfClipPathProps, type PdfDefsProps, type PdfDocumentProps, type PdfEllipseProps, type PdfGProps, type PdfImageProps, type PdfImageSource, type PdfLineProps, type PdfLinearGradientProps, type PdfLinkProps, type PdfNoteProps, type PdfPageDimension, type PdfPageProps, type PdfPageSize, type PdfPageSizeName, type PdfPageUnit, type PdfPathProps, type PdfPolygonProps, type PdfPolylineProps, type PdfRadialGradientProps, type PdfRectProps, type PdfStopProps, type PdfSvgLength, type PdfSvgNumber, type PdfSvgPresentationProps, type PdfSvgProps, type PdfSvgTransform, type PdfSvgTransformOperation, type PdfTextProps, type PdfTspanProps, type PdfViewProps } from '../dist/runtime/components/index.js'
22
+
23
+ export { default } from './module.mjs'
24
+
25
+ export { type ModuleOptions } from './module.mjs'
26
+ ```
27
+
28
+ ## Nuxt module
29
+
30
+ Source: `dist/module.d.mts`
31
+
32
+ ```ts
33
+ import * as _nuxt_schema from '@nuxt/schema';
34
+ import { RemoteAssetOptions } from '../dist/runtime/server/assets/remote.js';
35
+ export { RemoteAssetOptions } from '../dist/runtime/server/assets/remote.js';
36
+ import { PdfLimitsOptions } from '../dist/runtime/server/render-limits.js';
37
+ export { PdfLimitsOptions } from '../dist/runtime/server/render-limits.js';
38
+ import { PdfFontDeclaration } from '../dist/runtime/fonts.js';
39
+ export { PdfFontDeclaration, PdfFontStyle, PdfFontWeight, PdfFontWeightName } from '../dist/runtime/fonts.js';
40
+ export { PdfLength, PdfLengthOrPercentage, PdfPercentage, PdfStyle, PdfStyleEntry, PdfStyleValue } from '../dist/runtime/authoring.js';
41
+ export { PdfBaseProps, PdfBookmark, PdfCircleProps, PdfClipPathProps, PdfDefsProps, PdfDocumentProps, PdfEllipseProps, PdfGProps, PdfImageProps, PdfImageSource, PdfLineProps, PdfLinearGradientProps, PdfLinkProps, PdfNoteProps, PdfPageDimension, PdfPageProps, PdfPageSize, PdfPageSizeName, PdfPageUnit, PdfPathProps, PdfPolygonProps, PdfPolylineProps, PdfRadialGradientProps, PdfRectProps, PdfStopProps, PdfSvgLength, PdfSvgNumber, PdfSvgPresentationProps, PdfSvgProps, PdfSvgTransform, PdfSvgTransformOperation, PdfTextProps, PdfTspanProps, PdfViewProps } from '../dist/runtime/components/index.js';
42
+
43
+ interface ModuleOptions {
44
+ fonts?: readonly PdfFontDeclaration[];
45
+ remote?: RemoteAssetOptions;
46
+ /**
47
+ * Canonical render, tree, image, remote-request, and output budgets. Every
48
+ * field is an optional positive safe integer; omitted fields use the
49
+ * documented built-in defaults.
50
+ */
51
+ limits?: PdfLimitsOptions;
52
+ }
53
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
54
+
55
+ export { _default as default };
56
+ export type { ModuleOptions };
57
+ ```
58
+
59
+ ## Test entry
60
+
61
+ Source: `dist/test.d.mts`
62
+
63
+ ```ts
64
+ import { PdfRenderResult, PdfComponentProps } from '../dist/runtime/shared/template.js';
65
+ export { PdfComponentProps } from '../dist/runtime/shared/template.js';
66
+ import { Component } from 'vue';
67
+ import { ModuleOptions } from './module.mjs';
68
+ import '@nuxt/schema';
69
+ import '../dist/runtime/server/assets/remote.js';
70
+ import '../dist/runtime/server/render-limits.js';
71
+ import '../dist/runtime/fonts.js';
72
+ import '../dist/runtime/authoring.js';
73
+ import '../dist/runtime/components/index.js';
74
+
75
+ /** Raw PDF (or PNG) bytes accepted by the low-level readers. */
76
+ type PdfData = ArrayBuffer | Uint8Array;
77
+ /** Anything the public `parsePdf`/`rasterizePdf` accept: bytes or a render result. */
78
+ type PdfInput = PdfData | PdfRenderResult;
79
+ interface PdfAnnotationReference {
80
+ generation: number;
81
+ number: number;
82
+ }
83
+ type PdfAnnotationDestination = string | Array<string | number | null | PdfAnnotationReference>;
84
+ interface PdfAnnotation {
85
+ annotationType?: number;
86
+ destination?: PdfAnnotationDestination;
87
+ rect?: number[];
88
+ subtype: string;
89
+ unsafeUrl?: string;
90
+ url?: string;
91
+ }
92
+ /** A link annotation flattened to the fields users assert against. */
93
+ interface ParsedPdfLink {
94
+ /** 1-based page the link sits on. */
95
+ page: number;
96
+ /** Named internal destination (e.g. a `#id` target), when the link is internal. */
97
+ destination?: string;
98
+ /** External URL, when the link points outside the document. */
99
+ url?: string;
100
+ }
101
+ /** A normalized PDF.js text run for tolerant layout and typography assertions. */
102
+ interface ParsedPdfTextRun {
103
+ direction: string;
104
+ fontName: string;
105
+ fontSize: number;
106
+ height: number;
107
+ text: string;
108
+ width: number;
109
+ x: number;
110
+ y: number;
111
+ }
112
+ interface ParsedPdfPage {
113
+ annotations: PdfAnnotation[];
114
+ height: number;
115
+ number: number;
116
+ rawText: string;
117
+ text: string;
118
+ textItems: string[];
119
+ textRuns: ParsedPdfTextRun[];
120
+ width: number;
121
+ }
122
+ interface PdfOutlineItem {
123
+ title: string;
124
+ /** Initial viewer state for entries with children. Absent on leaf entries. */
125
+ expanded?: boolean;
126
+ children: PdfOutlineItem[];
127
+ }
128
+ interface ParsedPdf {
129
+ pageCount: number;
130
+ pages: ParsedPdfPage[];
131
+ /** Every link annotation in the document, flattened across pages. */
132
+ links: ParsedPdfLink[];
133
+ /** The bookmark tree (outline), including initial expansion state. */
134
+ outline: PdfOutlineItem[];
135
+ }
136
+ interface RasterizePdfOptions {
137
+ background?: string;
138
+ scale?: number;
139
+ }
140
+ interface PdfPageImage {
141
+ height: number;
142
+ number: number;
143
+ pixels: Uint8ClampedArray;
144
+ png: Uint8Array;
145
+ width: number;
146
+ }
147
+ interface PageImageComparison {
148
+ changedPixelRatio: number;
149
+ changedPixels: number;
150
+ dimensionsMatch: boolean;
151
+ matches: boolean;
152
+ maxChannelDifference: number;
153
+ pageNumbersMatch: boolean;
154
+ totalPixels: number;
155
+ }
156
+ /** Resolve any accepted input (bytes or a `PdfRenderResult`) to raw PDF bytes. */
157
+ declare function toPdfBytes(input: PdfInput): Promise<Uint8Array>;
158
+ /** Parse stable, semantic information from every page of a PDF. */
159
+ declare function parsePdf(input: PdfInput): Promise<ParsedPdf>;
160
+ /** Rasterize every PDF page independently for deterministic page-level diffs. */
161
+ declare function rasterizePdf(input: PdfInput, options?: RasterizePdfOptions): Promise<PdfPageImage[]>;
162
+
163
+ /**
164
+ * Error thrown by the `expectPdf` helpers. Named so test runners surface it like
165
+ * their own assertion errors, without this module depending on any runner.
166
+ */
167
+ declare class PdfAssertionError extends Error {
168
+ constructor(message: string);
169
+ }
170
+ interface ToContainTextOptions {
171
+ /** Restrict the search to a single 1-based page. */
172
+ page?: number;
173
+ }
174
+ type LinkQuery = {
175
+ destination: string;
176
+ page?: number;
177
+ } | {
178
+ url: string;
179
+ page?: number;
180
+ } | {
181
+ destination: string;
182
+ url: string;
183
+ page?: number;
184
+ };
185
+ /** A partial outline shape: title is required; state and children are optional. */
186
+ interface OutlineShape {
187
+ title: string;
188
+ expanded?: boolean;
189
+ children?: OutlineShape[];
190
+ }
191
+ interface PdfExpectation {
192
+ toHavePageCount(count: number): PdfExpectation;
193
+ toContainText(text: string, options?: ToContainTextOptions): PdfExpectation;
194
+ toHaveLink(query: LinkQuery): PdfExpectation;
195
+ toHaveOutline(shape: OutlineShape[]): PdfExpectation;
196
+ }
197
+ /**
198
+ * Fluent, runner-agnostic assertions over a `ParsedPdf`. Every method returns the
199
+ * same expectation so calls chain, and throws a `PdfAssertionError` on failure
200
+ * with a message naming what was expected and what the document actually holds.
201
+ */
202
+ declare function expectPdf(parsed: ParsedPdf): PdfExpectation;
203
+
204
+ /** User-shaped render policy, matching the corresponding Nuxt module options. */
205
+ type RenderPdfTemplateOptions = Pick<ModuleOptions, 'limits' | 'remote'>;
206
+ interface RenderedPdfTemplate {
207
+ /** The rendered PDF bytes. */
208
+ bytes: Uint8Array;
209
+ /** The parsed document, ready for `expectPdf`. */
210
+ parsed: ParsedPdf;
211
+ /** The underlying completed result (diagnostics, bytes, buffer, and response). */
212
+ result: PdfRenderResult;
213
+ }
214
+ /**
215
+ * Render a Vue PDF component through the real Nuxt PDF pipeline — mount, asset
216
+ * resolution, font registration, single- or multi-pass layout — without booting
217
+ * Nuxt, then parse the bytes so a test can assert against them immediately.
218
+ */
219
+ declare function renderPdfTemplate<C extends Component>(component: C, props: PdfComponentProps<C>, options?: RenderPdfTemplateOptions): Promise<RenderedPdfTemplate>;
220
+
221
+ /** User-shaped SFC render configuration, matching the Nuxt module options. */
222
+ type RenderPdfSfcOptions = RenderPdfTemplateOptions & Pick<ModuleOptions, 'fonts'>;
223
+ /** Compile a real PDF SFC graph with the same compiler used by the Nuxt module. */
224
+ declare function loadPdfSfc(filename: string): Promise<Component>;
225
+ /** Compile and render a real `pdfs/*.vue` template with production resource handling. */
226
+ declare function renderPdfSfc<Props extends object>(filename: string, props: Props, options?: RenderPdfSfcOptions): Promise<RenderedPdfTemplate>;
227
+
228
+ interface ComparePdfSnapshotOptions {
229
+ /** Directory for expected, actual, diff, and JSON failure artifacts. */
230
+ artifactDir?: string;
231
+ /** Maximum ratio of changed pixels allowed per page (default 0.005). */
232
+ threshold?: number;
233
+ /** Maximum per-channel RGBA difference for a matching pixel (default 25). */
234
+ channelThreshold?: number;
235
+ /** Raster scale (default 1). */
236
+ scale?: number;
237
+ /**
238
+ * Write the current render as the reviewed baseline instead of comparing.
239
+ * Defaults to `process.env.UPDATE_PDF_BASELINES === '1'`.
240
+ */
241
+ update?: boolean;
242
+ }
243
+ interface PdfSnapshotResult {
244
+ /** Whether the render matched the reviewed baseline. */
245
+ matches: boolean;
246
+ /** Whether the baseline was (re)written this run. */
247
+ updated: boolean;
248
+ /** Per-page comparison detail (empty when the baseline was written). */
249
+ pages: PageImageComparison[];
250
+ }
251
+ /**
252
+ * Compare a rendered PDF against a directory of reviewed per-page PNG baselines,
253
+ * following the `UPDATE_PDF_BASELINES` review policy. With `update` (or the env
254
+ * flag) set, it (re)writes the baselines and returns; otherwise it rasterizes the
255
+ * document, checks every page against `baselineDir/page-N.png`, and throws a
256
+ * `PdfAssertionError` on any mismatch.
257
+ */
258
+ declare function comparePdfSnapshot(input: PdfInput, baselineDir: string, options?: ComparePdfSnapshotOptions): Promise<PdfSnapshotResult>;
259
+
260
+ export { PdfAssertionError, comparePdfSnapshot, expectPdf, loadPdfSfc, parsePdf, rasterizePdf, renderPdfSfc, renderPdfTemplate, toPdfBytes };
261
+ export type { ComparePdfSnapshotOptions, LinkQuery, OutlineShape, ParsedPdf, ParsedPdfLink, ParsedPdfPage, ParsedPdfTextRun, PdfExpectation, PdfInput, PdfOutlineItem, PdfPageImage, PdfSnapshotResult, RasterizePdfOptions, RenderPdfSfcOptions, RenderPdfTemplateOptions, RenderedPdfTemplate, ToContainTextOptions };
262
+ ```
package/CHANGELOG.md ADDED
@@ -0,0 +1,168 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@lupinum/nuxt-pdf` are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres
5
+ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## Unreleased
8
+
9
+ ## 0.3.0 - 2026-08-11
10
+
11
+ ### Added
12
+
13
+ - Public font configuration types (`PdfFontDeclaration`, `PdfFontStyle`,
14
+ `PdfFontWeight`, and `PdfFontWeightName`) are now exported from the package
15
+ root instead of leaking through a private server implementation path.
16
+ - `PdfLimitsOptions` is now a named package-root export backed by the same
17
+ canonical option shape used by the Nuxt module.
18
+ - `RemoteAssetOptions` is now a named package-root export for callers that
19
+ share remote-image policy between Nuxt configuration and test helpers.
20
+ - One canonical `pdf.limits` budget covering tree nodes/depth/text, pages,
21
+ image count/source bytes/decoded pixels, remote request count/concurrency,
22
+ the whole-render deadline, and completed output bytes.
23
+ - Structural PNG/JPEG dimension validation before engine admission, aggregate
24
+ per-render image accounting, atomic output-cap failure, and fatal sibling
25
+ request cancellation.
26
+ - Structural SFNT table-directory validation for local TTF/OTF files plus mixed
27
+ 20-render concurrency, 100 sequential-render, and 100-page isolation suites.
28
+ - Immutable resolved render metadata, development-only preview sidecars, stale
29
+ preview state after render failures, and automatic SFC preview refresh.
30
+ - Normalized PDF text-run geometry and complete raster failure artifacts with
31
+ expected, actual, diff, and JSON metrics for every changed page.
32
+ - One-tarball npm/pnpm consumer verification, pinned CI/release workflows,
33
+ CycloneDX SBOM and checksum generation, and registry post-publish smoke tests.
34
+ - Safe registered-font-face diagnostics and an international typography
35
+ calibration fixture that classifies CJK, bidi text, combining marks, variable
36
+ fonts, emoji, fallback, and hyphenation from semantic and raster evidence.
37
+ - Scope-aware Vue Composition API and `usePdfPageNumbers` auto-imports inside
38
+ PDF SFCs, without pretending that the isolated renderer has Nuxt app context.
39
+
40
+ ### Changed
41
+
42
+ - Failed Vue mounts are transactional, component effects are always disposed,
43
+ and unhandled production errors fail the render instead of returning an
44
+ incomplete PDF.
45
+ - PDF SFC compilation rejects async setup, top-level `await`, `Teleport`, and
46
+ `v-show` with targeted diagnostics; unsupported PDF styles fail closed.
47
+ - Generated registry and test-helper types expose authored component props
48
+ instead of Vue framework props, and misplaced `definePdf()` calls fail with a
49
+ focused runtime explanation.
50
+ - Script-setup source maps now compose the Vue compiler, auto-import, and
51
+ TypeScript transforms back to the original `.vue` source.
52
+ - Nuxt discovery honors `.nuxtignore` and configured ignore rules. Structural
53
+ PDF/component changes and embedded image/font changes restart Nuxt so the
54
+ registry, declarations, and bundled resources cannot become stale.
55
+ - Preview HMR derives its Vite client path from `app.baseURL` and
56
+ `app.buildAssetsDir`; project and layer resource precedence is covered by
57
+ integration tests.
58
+ - Documentation now states the Node runtime-core lifecycle, synchronous
59
+ authoring contract, isolated Vue app boundary, and PDF-specific styling model.
60
+
61
+ ### Breaking changes
62
+
63
+ - `renderPdfTemplate` options now mirror user-facing `pdf.remote` and
64
+ `pdf.limits` configuration. Generated asset maps, embedded font descriptors,
65
+ and template attribution fields are internal. `renderPdfSfc` additionally
66
+ accepts `pdf.fonts` declarations and always infers the application root from
67
+ the `pdfs/*.vue` path; its `rootDir` override is removed.
68
+ - Root render props are now limited to props authored by the Vue component;
69
+ renderer-owned `class`, `style`, VNode, and reserved props are no longer
70
+ accepted by the generated registry or `renderPdfTemplate`.
71
+ - Previously unsupported async components, top-level `await`, `Teleport`,
72
+ `v-show`, and unrecognized style properties now fail explicitly instead of
73
+ being silently omitted or admitted as no-ops.
74
+ - Template keys now use one slash-separated vocabulary everywhere. A nested
75
+ template such as `pdfs/reports/monthly.vue` is registered as
76
+ `pdf['reports/monthly']`; camel-cased aliases such as `pdf.reportsMonthly`
77
+ are removed.
78
+ - The inert `diagnostics.warnings` field and preview warning plumbing are
79
+ removed. Render failures remain typed errors; diagnostics report only
80
+ measured facts about a completed render.
81
+ - `pdf.remote` now permits images only and contains only `allow` plus
82
+ `timeoutMs`. Move former `maxImageBytes` configuration to
83
+ `pdf.limits.maxImageBytes`; remote fonts must become local `pdfs/fonts`
84
+ inputs.
85
+ - Allowlist entries must be exact `https://host/path/` prefixes. Wildcard hosts
86
+ and prefixes without a trailing slash are rejected. Redirects are capped at
87
+ three hops.
88
+
89
+ ## 0.2.0
90
+
91
+ The contract widens from 0.1.0's core layout primitives to vector graphics, a
92
+ table of contents, internal links, bookmarks, opt-in remote resources, shipped
93
+ testing utilities, and enforced render limits — each backed by an executable
94
+ conformance fixture. Everything is additive: no 0.1.0 public API changed.
95
+
96
+ ### Added
97
+
98
+ - **SVG drawing primitives.** `PdfSvg`, `PdfG`, `PdfPath`, `PdfRect`,
99
+ `PdfCircle`, `PdfEllipse`, `PdfLine`, `PdfPolyline`, `PdfPolygon`, `PdfDefs`,
100
+ `PdfClipPath`, `PdfLinearGradient`, `PdfRadialGradient`, `PdfStop`, and
101
+ `PdfTspan`, with enforced nesting rules and `url(#id)` def references. Verified
102
+ by a paired React/Vue raster fixture.
103
+ - **Table of contents via `usePdfPageNumbers()`.** An auto-imported composable
104
+ returning a readonly, reactive `id → 1-based page` map. Reading it activates a
105
+ fixed-point multi-pass layout that converges (an ordinary TOC settles in two
106
+ passes); a document whose geometry depends on the numbers it prints fails
107
+ closed with `PDF_LIMIT_EXCEEDED`.
108
+ - **Internal `#id` links and bookmarks.** Any primitive `id` becomes a named
109
+ destination reachable with `<PdfLink src="#id">`; a `bookmark` prop builds a
110
+ nested PDF outline following the component tree. Internal links stay
111
+ single-pass (they resolve by name).
112
+ - **Opt-in remote images and fonts.** Off by default. `pdf.remote.allow`
113
+ configures an explicit `https`-only host + path-prefix allowlist enforced by
114
+ the module (never the engine) with per-hop redirect re-checks, byte caps,
115
+ signature validation, and timeouts; admitted bytes take the same embedded path
116
+ as local assets.
117
+ - **`@lupinum/nuxt-pdf/test` utilities.** The helpers this package is tested with
118
+ now ship: `renderPdfTemplate`, `parsePdf`, `expectPdf`, `rasterizePdf`, and
119
+ `comparePdfSnapshot` (reviewed-baseline policy). `pdfjs-dist` and
120
+ `@napi-rs/canvas` are new **optional** peer dependencies, loaded lazily only by
121
+ this entry.
122
+ - **Render limits.** Every render is bounded by `pdf.limits.maxPages` (default
123
+ 2000) and `pdf.limits.timeoutMs` (default 30000), validated at setup and
124
+ enforced on both the single- and multi-pass paths; breaches fail with
125
+ `PDF_LIMIT_EXCEEDED`. Defaults apply even with no `pdf.limits` configured.
126
+ - **`NuxtPdfError` and `PDF_ERROR_CODES`** exported from `#pdf`, and
127
+ template-attributed errors: every failure from a template's `render()` carries
128
+ `templateKey`/`templateFile` and a message prefixed with the source file.
129
+ - **Development preview workbench** at `/_pdf`: scenario tabs, a per-render
130
+ diagnostics strip (duration, size, page count, and layout passes), and a
131
+ readable error panel. Server-rendered, absent from production builds.
132
+ - **Documentation site** under `docs/` and a `playground/pdfs/report.vue`
133
+ demonstrating the TOC, bookmarks, and page numbers end to end.
134
+ - **Serverless build proof.** The module is verified to build under the Vercel
135
+ Nitro preset (execution remains verified on node-server).
136
+
137
+ ### Improvements (deliberate divergences from React PDF)
138
+
139
+ - **Dynamic-text line spacing.** Page-number footers now render at correct
140
+ page-bottom geometry even when a `lineHeight` reaches the dynamic node
141
+ (inherited, percentage, or its own). Upstream React PDF produces invalid
142
+ geometry for that combination and silently drops such footers; Nuxt PDF
143
+ neutralizes the un-carryable `lineHeight` and renders them.
144
+ - **Named-destination anchoring.** Internal links and TOC page numbers resolve to
145
+ the **first** page a section starts on, even when the section spans pages.
146
+ React PDF's last-writer-wins destination table points at the last page; Nuxt
147
+ PDF anchors at the first, copy-on-write so a `fixed` node repeated across pages
148
+ keeps its first-page destination.
149
+
150
+ ### Breaking changes
151
+
152
+ One type-level narrowing: the `bookmark` prop is no longer **typed** on
153
+ `PdfLink` and `PdfNote`. It was never tested or claimed there, and outline
154
+ entries are verified only on `PdfPage`/`PdfView`/`PdfText`/`PdfImage`, so the
155
+ types now match the tested contract. Runtime behavior is unchanged.
156
+
157
+ Everything else in the 0.1.0 public surface — module options, `#pdf` registry,
158
+ components, and `definePdf` — is unchanged; every 0.2.0 addition is opt-in.
159
+ `pdfjs-dist` and `@napi-rs/canvas` are added only as **optional** peer
160
+ dependencies for the new `/test` entry.
161
+
162
+ ## 0.1.0
163
+
164
+ Initial external alpha: one Nuxt module, one Vue document tree, one Node server
165
+ renderer over exact-pinned React PDF engine packages, and a small set of tested
166
+ layout primitives (`PdfDocument`, `PdfPage`, `PdfView`, `PdfText`, `PdfImage`,
167
+ `PdfLink`, `PdfNote`) with local image and font embedding and a typed `#pdf`
168
+ server registry.