@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
@@ -0,0 +1,428 @@
1
+ import { NuxtPdfError, PDF_ERROR_CODES } from "../shared/errors.js";
2
+ import {
3
+ PDF_PRIMITIVES
4
+ } from "../authoring.js";
5
+ import { PDF_PAGE_SIZE_NAMES } from "../components/index.js";
6
+ import {
7
+ PDF_PRIMITIVE_NAMES
8
+ } from "./types.js";
9
+ const RESERVED_DESTINATION_IDS = /* @__PURE__ */ new Set([
10
+ "__proto__",
11
+ "constructor",
12
+ "prototype"
13
+ ]);
14
+ const exactStyleKeys = (keys) => new Set(keys);
15
+ const PDF_STYLE_KEYS = exactStyleKeys([
16
+ "alignItems",
17
+ "backgroundColor",
18
+ "borderBottomColor",
19
+ "borderBottomLeftRadius",
20
+ "borderBottomRightRadius",
21
+ "borderBottomStyle",
22
+ "borderBottomWidth",
23
+ "borderColor",
24
+ "borderLeftColor",
25
+ "borderLeftStyle",
26
+ "borderLeftWidth",
27
+ "borderRadius",
28
+ "borderRightColor",
29
+ "borderRightStyle",
30
+ "borderRightWidth",
31
+ "borderStyle",
32
+ "borderTopColor",
33
+ "borderTopLeftRadius",
34
+ "borderTopRightRadius",
35
+ "borderTopStyle",
36
+ "borderTopWidth",
37
+ "borderWidth",
38
+ "bottom",
39
+ "color",
40
+ "flex",
41
+ "flexBasis",
42
+ "flexDirection",
43
+ "flexGrow",
44
+ "flexShrink",
45
+ "flexWrap",
46
+ "fontFamily",
47
+ "fontSize",
48
+ "fontStyle",
49
+ "fontWeight",
50
+ "gap",
51
+ "height",
52
+ "justifyContent",
53
+ "left",
54
+ "letterSpacing",
55
+ "lineHeight",
56
+ "margin",
57
+ "marginBottom",
58
+ "marginHorizontal",
59
+ "marginLeft",
60
+ "marginRight",
61
+ "marginTop",
62
+ "marginVertical",
63
+ "maxHeight",
64
+ "maxLines",
65
+ "maxWidth",
66
+ "minHeight",
67
+ "minWidth",
68
+ "objectFit",
69
+ "opacity",
70
+ "padding",
71
+ "paddingBottom",
72
+ "paddingHorizontal",
73
+ "paddingLeft",
74
+ "paddingRight",
75
+ "paddingTop",
76
+ "paddingVertical",
77
+ "position",
78
+ "right",
79
+ "textAlign",
80
+ "textDecoration",
81
+ "textDecorationColor",
82
+ "textDecorationStyle",
83
+ "textOverflow",
84
+ "textTransform",
85
+ "top",
86
+ "transform",
87
+ "width"
88
+ ]);
89
+ const DESTINATION_TYPES = /* @__PURE__ */ new Set([
90
+ PDF_PRIMITIVES.Page,
91
+ PDF_PRIMITIVES.View,
92
+ PDF_PRIMITIVES.Text,
93
+ PDF_PRIMITIVES.Image,
94
+ PDF_PRIMITIVES.Link,
95
+ PDF_PRIMITIVES.Note
96
+ ]);
97
+ const REQUIRED_PROPS = {
98
+ [PDF_PRIMITIVES.Path]: ["d"],
99
+ [PDF_PRIMITIVES.Rect]: ["width", "height"],
100
+ [PDF_PRIMITIVES.Circle]: ["r"],
101
+ [PDF_PRIMITIVES.Ellipse]: ["rx", "ry"],
102
+ [PDF_PRIMITIVES.Line]: ["x1", "y1", "x2", "y2"],
103
+ [PDF_PRIMITIVES.Polyline]: ["points"],
104
+ [PDF_PRIMITIVES.Polygon]: ["points"],
105
+ [PDF_PRIMITIVES.ClipPath]: ["id"],
106
+ [PDF_PRIMITIVES.LinearGradient]: ["id"],
107
+ [PDF_PRIMITIVES.RadialGradient]: ["id"],
108
+ [PDF_PRIMITIVES.Stop]: ["offset", "stopColor"]
109
+ };
110
+ const SVG_NUMERIC_PROPS = [
111
+ "cx",
112
+ "cy",
113
+ "fillOpacity",
114
+ "fx",
115
+ "fy",
116
+ "height",
117
+ "offset",
118
+ "r",
119
+ "rx",
120
+ "ry",
121
+ "stopOpacity",
122
+ "strokeOpacity",
123
+ "strokeWidth",
124
+ "width",
125
+ "x",
126
+ "x1",
127
+ "x2",
128
+ "y",
129
+ "y1",
130
+ "y2"
131
+ ];
132
+ const NON_NEGATIVE_SVG_PROPS = /* @__PURE__ */ new Set([
133
+ "height",
134
+ "r",
135
+ "rx",
136
+ "ry",
137
+ "strokeWidth",
138
+ "width"
139
+ ]);
140
+ const UNIT_INTERVAL_SVG_PROPS = /* @__PURE__ */ new Set([
141
+ "fillOpacity",
142
+ "offset",
143
+ "stopOpacity",
144
+ "strokeOpacity"
145
+ ]);
146
+ const FLOW_ONLY_TEXT_PROPS = [
147
+ "bookmark",
148
+ "break",
149
+ "debug",
150
+ "fixed",
151
+ "id",
152
+ "minPresenceAhead",
153
+ "orphans",
154
+ "render",
155
+ "widows",
156
+ "wrap"
157
+ ];
158
+ const SVG_NUMBER = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(%?)$/i;
159
+ const SVG_TRANSFORM_NUMBER = String.raw`[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?`;
160
+ const SVG_TRANSFORM_OPERATION = String.raw`(?:translate\(${SVG_TRANSFORM_NUMBER}(?:(?:,\s*|\s+)${SVG_TRANSFORM_NUMBER})?\)|rotate\(${SVG_TRANSFORM_NUMBER}\))`;
161
+ const SVG_TRANSFORM = new RegExp(`^${SVG_TRANSFORM_OPERATION}(?:\\s+${SVG_TRANSFORM_OPERATION}){0,2}$`, "i");
162
+ const SVG_DEFINITION_ID = /^[a-z_][\w.:-]*$/i;
163
+ const SVG_REFERENCE = /^url\(['"]?#([^'")]+)['"]?\)$/;
164
+ const PAGE_DIMENSION = /^\+?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(?:pt|in|mm|cm|px)$/i;
165
+ const PAGE_SIZE_NAMES = new Set(PDF_PAGE_SIZE_NAMES);
166
+ const treeInvalid = (message) => {
167
+ throw new NuxtPdfError(PDF_ERROR_CODES.TreeInvalid, message);
168
+ };
169
+ const validateDestinationId = (value) => {
170
+ if (value === void 0) return void 0;
171
+ if (typeof value === "string") {
172
+ if (value.trim() === "") {
173
+ treeInvalid("PDF destination ids must be non-empty strings.");
174
+ }
175
+ return value;
176
+ }
177
+ return treeInvalid("PDF destination ids must be non-empty strings.");
178
+ };
179
+ const hasProp = (node, key) => Object.hasOwn(node.props, key);
180
+ const validateRequiredProps = (node) => {
181
+ for (const key of REQUIRED_PROPS[node.type] ?? []) {
182
+ if (!hasProp(node, key)) {
183
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> requires the "${key}" prop.`);
184
+ }
185
+ }
186
+ };
187
+ const parseSvgNumber = (value) => {
188
+ if (typeof value === "number") {
189
+ return Number.isFinite(value) ? { value, percent: false } : void 0;
190
+ }
191
+ if (typeof value !== "string") return void 0;
192
+ const match = SVG_NUMBER.exec(value);
193
+ if (!match) return void 0;
194
+ const parsed = Number.parseFloat(value);
195
+ return Number.isFinite(parsed) ? { value: parsed, percent: match[1] === "%" } : void 0;
196
+ };
197
+ const validateSvgNumbers = (node) => {
198
+ for (const key of SVG_NUMERIC_PROPS) {
199
+ if (!hasProp(node, key)) continue;
200
+ const parsed = parseSvgNumber(node.props[key]);
201
+ if (!parsed) {
202
+ return treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> has an invalid "${key}" value.`);
203
+ }
204
+ if (NON_NEGATIVE_SVG_PROPS.has(key) && parsed.value < 0) {
205
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> requires a non-negative "${key}" value.`);
206
+ }
207
+ if (UNIT_INTERVAL_SVG_PROPS.has(key) && (parsed.value < 0 || parsed.value > (parsed.percent ? 100 : 1))) {
208
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> requires "${key}" between 0 and 1 (or 0% and 100%).`);
209
+ }
210
+ if (key === "strokeWidth" && parsed.percent) {
211
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> does not support a percentage stroke width.`);
212
+ }
213
+ }
214
+ };
215
+ const validateViewBox = (node) => {
216
+ if (node.type !== PDF_PRIMITIVES.Svg || !hasProp(node, "viewBox")) return;
217
+ const value = node.props.viewBox;
218
+ const parts = typeof value === "string" ? value.trim().split(/[\s,]+/).map(Number) : [];
219
+ if (parts.length !== 4 || parts.some((part) => !Number.isFinite(part)) || parts[2] <= 0 || parts[3] <= 0) {
220
+ treeInvalid("<PdfSvg> viewBox must contain four finite numbers with positive width and height.");
221
+ }
222
+ };
223
+ const validateSvgTransform = (node) => {
224
+ if (!hasProp(node, "transform")) return;
225
+ const value = node.props.transform;
226
+ if (typeof value !== "string" || !SVG_TRANSFORM.test(value)) {
227
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> has an unsupported SVG transform.`);
228
+ }
229
+ };
230
+ const pageDimensionIsPositive = (value) => {
231
+ if (typeof value === "number") return Number.isFinite(value) && value > 0;
232
+ return typeof value === "string" && PAGE_DIMENSION.test(value) && Number.parseFloat(value) > 0;
233
+ };
234
+ const validatePage = (node) => {
235
+ if (node.type !== PDF_PRIMITIVES.Page) return;
236
+ if (hasProp(node, "dpi")) {
237
+ const dpi = node.props.dpi;
238
+ if (typeof dpi !== "number" || !Number.isFinite(dpi) || dpi <= 0) {
239
+ treeInvalid("<PdfPage> dpi must be a positive finite number.");
240
+ }
241
+ }
242
+ if (!hasProp(node, "size")) return;
243
+ const size = node.props.size;
244
+ let valid = false;
245
+ if (typeof size === "string") {
246
+ valid = PAGE_SIZE_NAMES.has(size);
247
+ } else if (Array.isArray(size)) {
248
+ valid = size.length === 2 && size.every(pageDimensionIsPositive);
249
+ } else if (typeof size === "object" && size !== null) {
250
+ const dimensions = size;
251
+ valid = pageDimensionIsPositive(dimensions.width) && pageDimensionIsPositive(dimensions.height);
252
+ }
253
+ if (!valid) {
254
+ treeInvalid("<PdfPage> size must be a known page name or positive width and height.");
255
+ }
256
+ };
257
+ const validateLinkAndImageSources = (node) => {
258
+ if (node.type === PDF_PRIMITIVES.Image) {
259
+ if (hasProp(node, "src") === hasProp(node, "source")) {
260
+ treeInvalid('<PdfImage> requires exactly one of "src" or "source".');
261
+ }
262
+ }
263
+ if (node.type === PDF_PRIMITIVES.Link) {
264
+ const hasHref = hasProp(node, "href");
265
+ const hasSrc = hasProp(node, "src");
266
+ if (hasHref === hasSrc) {
267
+ treeInvalid('<PdfLink> requires exactly one of "href" or "src".');
268
+ }
269
+ const target = hasHref ? node.props.href : node.props.src;
270
+ if (typeof target !== "string" || target.trim() === "") {
271
+ treeInvalid("<PdfLink> targets must be non-empty strings.");
272
+ }
273
+ }
274
+ };
275
+ const validateScalarInvariants = (node) => {
276
+ if (hasProp(node, "minPresenceAhead")) {
277
+ const value = node.props.minPresenceAhead;
278
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
279
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> minPresenceAhead must be a non-negative finite number.`);
280
+ }
281
+ }
282
+ for (const key of ["break", "debug", "fixed", "wrap"]) {
283
+ if (hasProp(node, key) && typeof node.props[key] !== "boolean") {
284
+ treeInvalid(`<${PDF_PRIMITIVE_NAMES[node.type]}> requires a boolean "${key}" value.`);
285
+ }
286
+ }
287
+ };
288
+ const validateStyle = (style) => {
289
+ if (Array.isArray(style)) {
290
+ for (const entry of style) {
291
+ if (entry !== false && entry != null) validateStyle(entry);
292
+ }
293
+ return;
294
+ }
295
+ if (typeof style !== "object" || style === null) {
296
+ treeInvalid("PDF styles must be an object or a nested array of style objects.");
297
+ }
298
+ for (const key of Object.keys(style)) {
299
+ if (!PDF_STYLE_KEYS.has(key)) {
300
+ treeInvalid(`Unsupported PDF style "${key}".`);
301
+ }
302
+ }
303
+ };
304
+ const validateTextContext = (node, insideSvg) => {
305
+ if (node.type === PDF_PRIMITIVES.Tspan && !insideSvg) {
306
+ treeInvalid("<PdfTspan> is only supported inside SVG text.");
307
+ }
308
+ if (node.type !== PDF_PRIMITIVES.Text) return;
309
+ if (!insideSvg) {
310
+ if (hasProp(node, "fill") || hasProp(node, "x") || hasProp(node, "y")) {
311
+ treeInvalid("The PdfText fill, x, and y props are only supported inside <PdfSvg>.");
312
+ }
313
+ return;
314
+ }
315
+ if (!hasProp(node, "x") || !hasProp(node, "y")) {
316
+ treeInvalid('SVG <PdfText> requires both "x" and "y" props.');
317
+ }
318
+ if (FLOW_ONLY_TEXT_PROPS.some((key) => hasProp(node, key))) {
319
+ treeInvalid("SVG <PdfText> does not accept page-flow text props.");
320
+ }
321
+ };
322
+ const validateDefinitionId = (value) => {
323
+ if (typeof value !== "string" || !SVG_DEFINITION_ID.test(value)) {
324
+ return treeInvalid("SVG definition ids must be safe non-empty identifiers.");
325
+ }
326
+ if (RESERVED_DESTINATION_IDS.has(value)) {
327
+ treeInvalid("An SVG definition uses a reserved identifier.");
328
+ }
329
+ return value;
330
+ };
331
+ const validateSvgDefinitions = (svg) => {
332
+ const defs = svg.children.filter(
333
+ (child) => "children" in child && child.type === PDF_PRIMITIVES.Defs
334
+ );
335
+ if (defs.length > 1) {
336
+ treeInvalid("<PdfSvg> accepts at most one <PdfDefs> child.");
337
+ }
338
+ const definitions = /* @__PURE__ */ new Map();
339
+ for (const definition of defs[0]?.children ?? []) {
340
+ if (!("children" in definition)) continue;
341
+ const id = validateDefinitionId(definition.props.id);
342
+ if (definitions.has(id)) treeInvalid("SVG definition ids must be unique within one <PdfSvg>.");
343
+ definitions.set(id, definition.type);
344
+ }
345
+ const pending = [svg];
346
+ while (pending.length > 0) {
347
+ const node = pending.pop();
348
+ if (hasProp(node, "fill") && typeof node.props.fill === "string") {
349
+ const fill = node.props.fill;
350
+ if (fill.startsWith("url(")) {
351
+ const match = SVG_REFERENCE.exec(fill);
352
+ const type = match ? definitions.get(match[1]) : void 0;
353
+ if (type !== PDF_PRIMITIVES.LinearGradient && type !== PDF_PRIMITIVES.RadialGradient) {
354
+ treeInvalid("An SVG fill references a missing or incompatible definition.");
355
+ }
356
+ }
357
+ }
358
+ if (hasProp(node, "clipPath")) {
359
+ const clipPath = node.props.clipPath;
360
+ const match = typeof clipPath === "string" ? SVG_REFERENCE.exec(clipPath) : null;
361
+ if (!match || definitions.get(match[1]) !== PDF_PRIMITIVES.ClipPath) {
362
+ treeInvalid("An SVG clipPath references a missing or incompatible definition.");
363
+ }
364
+ }
365
+ for (const child of node.children) {
366
+ if ("children" in child) pending.push(child);
367
+ }
368
+ }
369
+ };
370
+ export const validatePdfDocumentTree = (document) => {
371
+ const destinationIds = /* @__PURE__ */ new Set();
372
+ const internalLinkTargets = /* @__PURE__ */ new Set();
373
+ let pageCount = 0;
374
+ const pending = [{
375
+ node: document,
376
+ insideSvg: false
377
+ }];
378
+ while (pending.length > 0) {
379
+ const { node, insideSvg } = pending.pop();
380
+ validateRequiredProps(node);
381
+ validateSvgNumbers(node);
382
+ validateSvgTransform(node);
383
+ validateViewBox(node);
384
+ validatePage(node);
385
+ validateLinkAndImageSources(node);
386
+ validateScalarInvariants(node);
387
+ validateStyle(node.style);
388
+ validateTextContext(node, insideSvg);
389
+ if (node.type === PDF_PRIMITIVES.Svg) validateSvgDefinitions(node);
390
+ if (node.type === PDF_PRIMITIVES.Page) pageCount += 1;
391
+ if (node.type === PDF_PRIMITIVES.Link) {
392
+ const target = hasProp(node, "href") ? node.props.href : node.props.src;
393
+ if (typeof target === "string" && target.startsWith("#")) {
394
+ const id2 = target.slice(1);
395
+ if (id2.trim() === "") {
396
+ treeInvalid("<PdfLink> internal destinations must name a non-empty id.");
397
+ }
398
+ internalLinkTargets.add(id2);
399
+ }
400
+ }
401
+ const id = DESTINATION_TYPES.has(node.type) ? validateDestinationId(node.props.id) : void 0;
402
+ if (id !== void 0) {
403
+ if (RESERVED_DESTINATION_IDS.has(id)) {
404
+ treeInvalid("A PDF destination uses a reserved identifier.");
405
+ }
406
+ if (destinationIds.has(id)) {
407
+ treeInvalid("PDF destination ids must be unique within a document.");
408
+ }
409
+ destinationIds.add(id);
410
+ }
411
+ for (const child of node.children) {
412
+ if ("children" in child) {
413
+ pending.push({
414
+ node: child,
415
+ insideSvg: insideSvg || node.type === PDF_PRIMITIVES.Svg
416
+ });
417
+ }
418
+ }
419
+ }
420
+ if (pageCount === 0) {
421
+ treeInvalid("<PdfDocument> requires at least one <PdfPage>.");
422
+ }
423
+ for (const target of internalLinkTargets) {
424
+ if (!destinationIds.has(target)) {
425
+ treeInvalid("<PdfLink> internal destination does not match any id in the document.");
426
+ }
427
+ }
428
+ };
@@ -0,0 +1,12 @@
1
+ import { NuxtPdfError } from '../../shared/errors.js';
2
+ export declare const PDF_ASSET_ERROR_CODES: {
3
+ readonly Blocked: "PDF_ASSET_BLOCKED";
4
+ readonly Invalid: "PDF_ASSET_INVALID";
5
+ readonly LimitExceeded: "PDF_LIMIT_EXCEEDED";
6
+ };
7
+ export type PdfAssetErrorCode = (typeof PDF_ASSET_ERROR_CODES)[keyof typeof PDF_ASSET_ERROR_CODES];
8
+ export declare class PdfAssetError extends NuxtPdfError {
9
+ constructor(code: PdfAssetErrorCode, message: string, options?: {
10
+ cause?: unknown;
11
+ });
12
+ }
@@ -0,0 +1,15 @@
1
+ import {
2
+ NuxtPdfError,
3
+ PDF_ERROR_CODES
4
+ } from "../../shared/errors.js";
5
+ export const PDF_ASSET_ERROR_CODES = {
6
+ Blocked: PDF_ERROR_CODES.AssetBlocked,
7
+ Invalid: PDF_ERROR_CODES.AssetInvalid,
8
+ LimitExceeded: PDF_ERROR_CODES.LimitExceeded
9
+ };
10
+ export class PdfAssetError extends NuxtPdfError {
11
+ constructor(code, message, options = {}) {
12
+ super(code, message, options);
13
+ this.name = "PdfAssetError";
14
+ }
15
+ }
@@ -0,0 +1,48 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import type { RenderLimits } from '../render-limits.js';
3
+ /** Opt-in policy for runtime image requests. Remote fonts are not supported. */
4
+ export interface RemoteAssetOptions {
5
+ /** Exact `https://host/path/` prefixes. Wildcard hosts are rejected. */
6
+ allow: readonly string[];
7
+ /** Per-hop timeout, bounded by the remaining render deadline. */
8
+ timeoutMs?: number;
9
+ }
10
+ export interface RemoteAssetRule {
11
+ readonly host: string;
12
+ readonly port: string;
13
+ readonly pathPrefix: string;
14
+ }
15
+ /** JSON-serializable policy generated once during Nuxt module setup. */
16
+ export interface RemoteAssetPolicy {
17
+ readonly allow: readonly RemoteAssetRule[];
18
+ readonly timeoutMs: number;
19
+ }
20
+ export declare const DEFAULT_REMOTE_TIMEOUT_MS = 10000;
21
+ export declare const MAX_REMOTE_REDIRECTS = 3;
22
+ /** Runtime URL label safe for logs, preview diagnostics, and attributed errors. */
23
+ export declare const redactUrl: (url: string) => string;
24
+ export declare const normalizeRemoteAssetPolicy: (options: RemoteAssetOptions | undefined) => RemoteAssetPolicy | undefined;
25
+ /** Revalidate scheme, credentials, fragment, host, port, and path on every hop. */
26
+ export declare const matchesAllowlist: (url: string, policy: RemoteAssetPolicy) => boolean;
27
+ type RemoteWaiter = {
28
+ reject(error: unknown): void;
29
+ resolve(): void;
30
+ };
31
+ /** Mutable request accounting owned by exactly one render. */
32
+ export interface RemoteRequestState {
33
+ readonly limits: RenderLimits;
34
+ active: number;
35
+ requests: number;
36
+ readonly waiters: RemoteWaiter[];
37
+ }
38
+ export declare const createRemoteRequestState: (limits: RenderLimits) => RemoteRequestState;
39
+ export interface FetchRemoteResourceOptions {
40
+ readonly policy: RemoteAssetPolicy;
41
+ readonly maxBytes: number;
42
+ readonly state: RemoteRequestState;
43
+ /** Per-render dedup map so a repeated URL is fetched once. */
44
+ readonly inflight?: Map<string, Promise<Buffer>>;
45
+ }
46
+ /** Fetch one allowlisted image under the render-wide deadline and budgets. */
47
+ export declare const fetchRemoteResource: (url: string, options: FetchRemoteResourceOptions) => Promise<Buffer>;
48
+ export {};