pretext-pdf 1.1.0 → 1.6.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 (342) hide show
  1. package/CHANGELOG.md +687 -0
  2. package/README.md +83 -8
  3. package/dist/allowed-props.d.ts +76 -0
  4. package/dist/allowed-props.d.ts.map +1 -1
  5. package/dist/allowed-props.js.map +1 -1
  6. package/dist/assets/generators/barcode.d.ts +9 -0
  7. package/dist/assets/generators/barcode.d.ts.map +1 -0
  8. package/dist/assets/generators/barcode.js +24 -0
  9. package/dist/assets/generators/barcode.js.map +1 -0
  10. package/dist/assets/generators/chart.d.ts +13 -0
  11. package/dist/assets/generators/chart.d.ts.map +1 -0
  12. package/dist/assets/generators/chart.js +32 -0
  13. package/dist/assets/generators/chart.js.map +1 -0
  14. package/dist/assets/generators/qr.d.ts +9 -0
  15. package/dist/assets/generators/qr.d.ts.map +1 -0
  16. package/dist/assets/generators/qr.js +25 -0
  17. package/dist/assets/generators/qr.js.map +1 -0
  18. package/dist/assets/index.d.ts +19 -0
  19. package/dist/assets/index.d.ts.map +1 -0
  20. package/dist/assets/index.js +19 -0
  21. package/dist/assets/index.js.map +1 -0
  22. package/dist/assets/loaders/images.d.ts +20 -0
  23. package/dist/assets/loaders/images.d.ts.map +1 -0
  24. package/dist/assets/loaders/images.js +69 -0
  25. package/dist/assets/loaders/images.js.map +1 -0
  26. package/dist/assets/loaders/orchestrator.d.ts +24 -0
  27. package/dist/assets/loaders/orchestrator.d.ts.map +1 -0
  28. package/dist/assets/loaders/orchestrator.js +109 -0
  29. package/dist/assets/loaders/orchestrator.js.map +1 -0
  30. package/dist/assets/loaders/vectors.d.ts +25 -0
  31. package/dist/assets/loaders/vectors.d.ts.map +1 -0
  32. package/dist/assets/loaders/vectors.js +118 -0
  33. package/dist/assets/loaders/vectors.js.map +1 -0
  34. package/dist/assets/loaders/watermark.d.ts +12 -0
  35. package/dist/assets/loaders/watermark.d.ts.map +1 -0
  36. package/dist/assets/loaders/watermark.js +40 -0
  37. package/dist/assets/loaders/watermark.js.map +1 -0
  38. package/dist/assets/security/fetch.d.ts +14 -0
  39. package/dist/assets/security/fetch.d.ts.map +1 -0
  40. package/dist/assets/security/fetch.js +112 -0
  41. package/dist/assets/security/fetch.js.map +1 -0
  42. package/dist/assets/security/ipv4-normalize.d.ts +28 -0
  43. package/dist/assets/security/ipv4-normalize.d.ts.map +1 -0
  44. package/dist/assets/security/ipv4-normalize.js +116 -0
  45. package/dist/assets/security/ipv4-normalize.js.map +1 -0
  46. package/dist/assets/security/path-allowlist.d.ts +12 -0
  47. package/dist/assets/security/path-allowlist.d.ts.map +1 -0
  48. package/dist/assets/security/path-allowlist.js +26 -0
  49. package/dist/assets/security/path-allowlist.js.map +1 -0
  50. package/dist/assets/security/url-validation.d.ts +22 -0
  51. package/dist/assets/security/url-validation.d.ts.map +1 -0
  52. package/dist/assets/security/url-validation.js +164 -0
  53. package/dist/assets/security/url-validation.js.map +1 -0
  54. package/dist/assets/svg/dimensions.d.ts +19 -0
  55. package/dist/assets/svg/dimensions.d.ts.map +1 -0
  56. package/dist/assets/svg/dimensions.js +43 -0
  57. package/dist/assets/svg/dimensions.js.map +1 -0
  58. package/dist/assets/svg/rasterize.d.ts +6 -0
  59. package/dist/assets/svg/rasterize.d.ts.map +1 -0
  60. package/dist/assets/svg/rasterize.js +38 -0
  61. package/dist/assets/svg/rasterize.js.map +1 -0
  62. package/dist/assets/svg/resolve-content.d.ts +16 -0
  63. package/dist/assets/svg/resolve-content.d.ts.map +1 -0
  64. package/dist/assets/svg/resolve-content.js +38 -0
  65. package/dist/assets/svg/resolve-content.js.map +1 -0
  66. package/dist/assets/svg/sanitize.d.ts +22 -0
  67. package/dist/assets/svg/sanitize.d.ts.map +1 -0
  68. package/dist/assets/svg/sanitize.js +46 -0
  69. package/dist/assets/svg/sanitize.js.map +1 -0
  70. package/dist/assets/util/redact-path.d.ts +14 -0
  71. package/dist/assets/util/redact-path.d.ts.map +1 -0
  72. package/dist/assets/util/redact-path.js +16 -0
  73. package/dist/assets/util/redact-path.js.map +1 -0
  74. package/dist/assets.d.ts +10 -27
  75. package/dist/assets.d.ts.map +1 -1
  76. package/dist/assets.js +10 -549
  77. package/dist/assets.js.map +1 -1
  78. package/dist/builder.d.ts.map +1 -1
  79. package/dist/builder.js +2 -1
  80. package/dist/builder.js.map +1 -1
  81. package/dist/cli.js +11 -1
  82. package/dist/cli.js.map +1 -1
  83. package/dist/compat.d.ts +63 -1
  84. package/dist/compat.d.ts.map +1 -1
  85. package/dist/compat.js +42 -5
  86. package/dist/compat.js.map +1 -1
  87. package/dist/errors.d.ts +2 -2
  88. package/dist/errors.d.ts.map +1 -1
  89. package/dist/errors.js +2 -2
  90. package/dist/errors.js.map +1 -1
  91. package/dist/fonts.d.ts.map +1 -1
  92. package/dist/fonts.js +8 -10
  93. package/dist/fonts.js.map +1 -1
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +7 -5
  97. package/dist/index.js.map +1 -1
  98. package/dist/layout-state.d.ts +1 -1
  99. package/dist/layout-state.d.ts.map +1 -1
  100. package/dist/layout-state.js +5 -0
  101. package/dist/layout-state.js.map +1 -1
  102. package/dist/measure-blocks/float-group.d.ts +9 -0
  103. package/dist/measure-blocks/float-group.d.ts.map +1 -0
  104. package/dist/measure-blocks/float-group.js +103 -0
  105. package/dist/measure-blocks/float-group.js.map +1 -0
  106. package/dist/measure-blocks/helpers.d.ts +44 -0
  107. package/dist/measure-blocks/helpers.d.ts.map +1 -0
  108. package/dist/measure-blocks/helpers.js +43 -0
  109. package/dist/measure-blocks/helpers.js.map +1 -0
  110. package/dist/measure-blocks/highlight.d.ts +26 -0
  111. package/dist/measure-blocks/highlight.d.ts.map +1 -0
  112. package/dist/measure-blocks/highlight.js +169 -0
  113. package/dist/measure-blocks/highlight.js.map +1 -0
  114. package/dist/measure-blocks/image.d.ts +9 -0
  115. package/dist/measure-blocks/image.d.ts.map +1 -0
  116. package/dist/measure-blocks/image.js +136 -0
  117. package/dist/measure-blocks/image.js.map +1 -0
  118. package/dist/measure-blocks/index.d.ts +24 -0
  119. package/dist/measure-blocks/index.d.ts.map +1 -0
  120. package/dist/measure-blocks/index.js +179 -0
  121. package/dist/measure-blocks/index.js.map +1 -0
  122. package/dist/measure-blocks/list.d.ts +8 -0
  123. package/dist/measure-blocks/list.d.ts.map +1 -0
  124. package/dist/measure-blocks/list.js +108 -0
  125. package/dist/measure-blocks/list.js.map +1 -0
  126. package/dist/measure-blocks/simple-blocks.d.ts +18 -0
  127. package/dist/measure-blocks/simple-blocks.d.ts.map +1 -0
  128. package/dist/measure-blocks/simple-blocks.js +121 -0
  129. package/dist/measure-blocks/simple-blocks.js.map +1 -0
  130. package/dist/measure-blocks/table/columns.d.ts +17 -0
  131. package/dist/measure-blocks/table/columns.d.ts.map +1 -0
  132. package/dist/measure-blocks/table/columns.js +83 -0
  133. package/dist/measure-blocks/table/columns.js.map +1 -0
  134. package/dist/measure-blocks/table/measure.d.ts +8 -0
  135. package/dist/measure-blocks/table/measure.d.ts.map +1 -0
  136. package/dist/measure-blocks/table/measure.js +231 -0
  137. package/dist/measure-blocks/table/measure.js.map +1 -0
  138. package/dist/measure-blocks/table/spans.d.ts +25 -0
  139. package/dist/measure-blocks/table/spans.d.ts.map +1 -0
  140. package/dist/measure-blocks/table/spans.js +55 -0
  141. package/dist/measure-blocks/table/spans.js.map +1 -0
  142. package/dist/measure-blocks/text-blocks.d.ts +17 -0
  143. package/dist/measure-blocks/text-blocks.d.ts.map +1 -0
  144. package/dist/measure-blocks/text-blocks.js +242 -0
  145. package/dist/measure-blocks/text-blocks.js.map +1 -0
  146. package/dist/measure-text.d.ts +21 -3
  147. package/dist/measure-text.d.ts.map +1 -1
  148. package/dist/measure-text.js +87 -36
  149. package/dist/measure-text.js.map +1 -1
  150. package/dist/measure.d.ts +1 -1
  151. package/dist/measure.d.ts.map +1 -1
  152. package/dist/measure.js +8 -6
  153. package/dist/measure.js.map +1 -1
  154. package/dist/node-polyfill.d.ts.map +1 -1
  155. package/dist/node-polyfill.js +9 -0
  156. package/dist/node-polyfill.js.map +1 -1
  157. package/dist/pipeline-footnotes.d.ts +1 -1
  158. package/dist/pipeline-footnotes.d.ts.map +1 -1
  159. package/dist/pipeline-toc.d.ts +1 -1
  160. package/dist/pipeline-toc.d.ts.map +1 -1
  161. package/dist/pipeline.d.ts +3 -3
  162. package/dist/pipeline.d.ts.map +1 -1
  163. package/dist/pipeline.js +4 -5
  164. package/dist/pipeline.js.map +1 -1
  165. package/dist/plugin-types.d.ts +1 -1
  166. package/dist/plugin-types.d.ts.map +1 -1
  167. package/dist/post-process.d.ts +2 -2
  168. package/dist/post-process.d.ts.map +1 -1
  169. package/dist/post-process.js +32 -9
  170. package/dist/post-process.js.map +1 -1
  171. package/dist/render-blocks/blockquote.d.ts +7 -0
  172. package/dist/render-blocks/blockquote.d.ts.map +1 -0
  173. package/dist/render-blocks/blockquote.js +87 -0
  174. package/dist/render-blocks/blockquote.js.map +1 -0
  175. package/dist/render-blocks/callout.d.ts +7 -0
  176. package/dist/render-blocks/callout.d.ts.map +1 -0
  177. package/dist/render-blocks/callout.js +84 -0
  178. package/dist/render-blocks/callout.js.map +1 -0
  179. package/dist/render-blocks/code.d.ts +7 -0
  180. package/dist/render-blocks/code.d.ts.map +1 -0
  181. package/dist/render-blocks/code.js +84 -0
  182. package/dist/render-blocks/code.js.map +1 -0
  183. package/dist/render-blocks/footnote.d.ts +11 -0
  184. package/dist/render-blocks/footnote.d.ts.map +1 -0
  185. package/dist/render-blocks/footnote.js +45 -0
  186. package/dist/render-blocks/footnote.js.map +1 -0
  187. package/dist/render-blocks/header-footer.d.ts +11 -0
  188. package/dist/render-blocks/header-footer.d.ts.map +1 -0
  189. package/dist/render-blocks/header-footer.js +56 -0
  190. package/dist/render-blocks/header-footer.js.map +1 -0
  191. package/dist/render-blocks/hr.d.ts +7 -0
  192. package/dist/render-blocks/hr.d.ts.map +1 -0
  193. package/dist/render-blocks/hr.js +24 -0
  194. package/dist/render-blocks/hr.js.map +1 -0
  195. package/dist/render-blocks/image.d.ts +9 -0
  196. package/dist/render-blocks/image.d.ts.map +1 -0
  197. package/dist/render-blocks/image.js +135 -0
  198. package/dist/render-blocks/image.js.map +1 -0
  199. package/dist/render-blocks/index.d.ts +17 -0
  200. package/dist/render-blocks/index.d.ts.map +1 -0
  201. package/dist/render-blocks/index.js +17 -0
  202. package/dist/render-blocks/index.js.map +1 -0
  203. package/dist/render-blocks/list-item.d.ts +7 -0
  204. package/dist/render-blocks/list-item.d.ts.map +1 -0
  205. package/dist/render-blocks/list-item.js +80 -0
  206. package/dist/render-blocks/list-item.js.map +1 -0
  207. package/dist/render-blocks/rich.d.ts +7 -0
  208. package/dist/render-blocks/rich.d.ts.map +1 -0
  209. package/dist/render-blocks/rich.js +160 -0
  210. package/dist/render-blocks/rich.js.map +1 -0
  211. package/dist/render-blocks/table.d.ts +7 -0
  212. package/dist/render-blocks/table.d.ts.map +1 -0
  213. package/dist/render-blocks/table.js +139 -0
  214. package/dist/render-blocks/table.js.map +1 -0
  215. package/dist/render-blocks/text.d.ts +7 -0
  216. package/dist/render-blocks/text.d.ts.map +1 -0
  217. package/dist/render-blocks/text.js +183 -0
  218. package/dist/render-blocks/text.js.map +1 -0
  219. package/dist/render-blocks/watermark.d.ts +8 -0
  220. package/dist/render-blocks/watermark.d.ts.map +1 -0
  221. package/dist/render-blocks/watermark.js +52 -0
  222. package/dist/render-blocks/watermark.js.map +1 -0
  223. package/dist/render-extras.d.ts.map +1 -1
  224. package/dist/render-extras.js +1 -2
  225. package/dist/render-extras.js.map +1 -1
  226. package/dist/render-utils.d.ts.map +1 -1
  227. package/dist/render-utils.js +10 -6
  228. package/dist/render-utils.js.map +1 -1
  229. package/dist/render.d.ts.map +1 -1
  230. package/dist/render.js +9 -3
  231. package/dist/render.js.map +1 -1
  232. package/dist/rich-text.d.ts +2 -1
  233. package/dist/rich-text.d.ts.map +1 -1
  234. package/dist/rich-text.js +0 -1
  235. package/dist/rich-text.js.map +1 -1
  236. package/dist/types-internal.d.ts +19 -3
  237. package/dist/types-internal.d.ts.map +1 -1
  238. package/dist/types-public/document.d.ts +261 -0
  239. package/dist/types-public/document.d.ts.map +1 -0
  240. package/dist/types-public/document.js +2 -0
  241. package/dist/types-public/document.js.map +1 -0
  242. package/dist/types-public/elements-block.d.ts +246 -0
  243. package/dist/types-public/elements-block.d.ts.map +1 -0
  244. package/dist/types-public/elements-block.js +8 -0
  245. package/dist/types-public/elements-block.js.map +1 -0
  246. package/dist/types-public/elements-media.d.ts +199 -0
  247. package/dist/types-public/elements-media.d.ts.map +1 -0
  248. package/dist/types-public/elements-media.js +2 -0
  249. package/dist/types-public/elements-media.js.map +1 -0
  250. package/dist/types-public/elements-text.d.ts +327 -0
  251. package/dist/types-public/elements-text.d.ts.map +1 -0
  252. package/dist/types-public/elements-text.js +2 -0
  253. package/dist/types-public/elements-text.js.map +1 -0
  254. package/dist/types-public/index.d.ts +14 -0
  255. package/dist/types-public/index.d.ts.map +1 -0
  256. package/dist/types-public/index.js +2 -0
  257. package/dist/types-public/index.js.map +1 -0
  258. package/dist/types-public/render-options.d.ts +38 -0
  259. package/dist/types-public/render-options.d.ts.map +1 -0
  260. package/dist/types-public/render-options.js +2 -0
  261. package/dist/types-public/render-options.js.map +1 -0
  262. package/dist/types-public/union.d.ts +13 -0
  263. package/dist/types-public/union.d.ts.map +1 -0
  264. package/dist/types-public/union.js +2 -0
  265. package/dist/types-public/union.js.map +1 -0
  266. package/dist/types-public/validation.d.ts +64 -0
  267. package/dist/types-public/validation.d.ts.map +1 -0
  268. package/dist/types-public/validation.js +2 -0
  269. package/dist/types-public/validation.js.map +1 -0
  270. package/dist/types-public.d.ts +5 -1081
  271. package/dist/types-public.d.ts.map +1 -1
  272. package/dist/types.d.ts +1 -1
  273. package/dist/types.d.ts.map +1 -1
  274. package/dist/validate/document.d.ts +28 -0
  275. package/dist/validate/document.d.ts.map +1 -0
  276. package/dist/validate/document.js +295 -0
  277. package/dist/validate/document.js.map +1 -0
  278. package/dist/validate/elements/forms-floats.d.ts +19 -0
  279. package/dist/validate/elements/forms-floats.d.ts.map +1 -0
  280. package/dist/validate/elements/forms-floats.js +96 -0
  281. package/dist/validate/elements/forms-floats.js.map +1 -0
  282. package/dist/validate/elements/list.d.ts +10 -0
  283. package/dist/validate/elements/list.d.ts.map +1 -0
  284. package/dist/validate/elements/list.js +66 -0
  285. package/dist/validate/elements/list.js.map +1 -0
  286. package/dist/validate/elements/media.d.ts +23 -0
  287. package/dist/validate/elements/media.d.ts.map +1 -0
  288. package/dist/validate/elements/media.js +179 -0
  289. package/dist/validate/elements/media.js.map +1 -0
  290. package/dist/validate/elements/structural-simple.d.ts +21 -0
  291. package/dist/validate/elements/structural-simple.d.ts.map +1 -0
  292. package/dist/validate/elements/structural-simple.js +63 -0
  293. package/dist/validate/elements/structural-simple.js.map +1 -0
  294. package/dist/validate/elements/structural.d.ts +12 -0
  295. package/dist/validate/elements/structural.d.ts.map +1 -0
  296. package/dist/validate/elements/structural.js +12 -0
  297. package/dist/validate/elements/structural.js.map +1 -0
  298. package/dist/validate/elements/table.d.ts +10 -0
  299. package/dist/validate/elements/table.d.ts.map +1 -0
  300. package/dist/validate/elements/table.js +165 -0
  301. package/dist/validate/elements/table.js.map +1 -0
  302. package/dist/validate/elements/text.d.ts +26 -0
  303. package/dist/validate/elements/text.d.ts.map +1 -0
  304. package/dist/validate/elements/text.js +331 -0
  305. package/dist/validate/elements/text.js.map +1 -0
  306. package/dist/validate/errors.d.ts +9 -0
  307. package/dist/validate/errors.d.ts.map +1 -0
  308. package/dist/validate/errors.js +43 -0
  309. package/dist/validate/errors.js.map +1 -0
  310. package/dist/validate/fonts.d.ts +11 -0
  311. package/dist/validate/fonts.d.ts.map +1 -0
  312. package/dist/validate/fonts.js +118 -0
  313. package/dist/validate/fonts.js.map +1 -0
  314. package/dist/validate/helpers.d.ts +76 -0
  315. package/dist/validate/helpers.d.ts.map +1 -0
  316. package/dist/validate/helpers.js +169 -0
  317. package/dist/validate/helpers.js.map +1 -0
  318. package/dist/validate/index.d.ts +37 -0
  319. package/dist/validate/index.d.ts.map +1 -0
  320. package/dist/validate/index.js +279 -0
  321. package/dist/validate/index.js.map +1 -0
  322. package/dist/validate.d.ts +6 -18
  323. package/dist/validate.d.ts.map +1 -1
  324. package/dist/validate.js +6 -1582
  325. package/dist/validate.js.map +1 -1
  326. package/dist/vendor/pretext/VERSION.d.ts +3 -0
  327. package/dist/vendor/pretext/VERSION.d.ts.map +1 -0
  328. package/dist/vendor/pretext/VERSION.js +12 -0
  329. package/dist/vendor/pretext/VERSION.js.map +1 -0
  330. package/dist/version-check.d.ts +47 -0
  331. package/dist/version-check.d.ts.map +1 -0
  332. package/dist/version-check.js +75 -0
  333. package/dist/version-check.js.map +1 -0
  334. package/package.json +27 -8
  335. package/dist/measure-blocks.d.ts +0 -26
  336. package/dist/measure-blocks.d.ts.map +0 -1
  337. package/dist/measure-blocks.js +0 -1317
  338. package/dist/measure-blocks.js.map +0 -1
  339. package/dist/render-blocks.d.ts +0 -28
  340. package/dist/render-blocks.d.ts.map +0 -1
  341. package/dist/render-blocks.js +0 -1059
  342. package/dist/render-blocks.js.map +0 -1
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Image-loading orchestrator — extracted from src/assets.ts in v1.6.0 commit 15/16.
3
+ *
4
+ * Stage 2b of the render pipeline: loads + embeds every ImageElement,
5
+ * float-group image, vector asset (SVG / QR / barcode / chart), plugin
6
+ * asset, and watermark image into a single ImageMap that the layout
7
+ * engine consumes.
8
+ *
9
+ * Image keys are 'img-N' / 'float-group-N' / 'watermark' / per-vector keys
10
+ * assigned inside loadVectorAssets / plugin-supplied keys.
11
+ *
12
+ * IMPORTANT: @cantoo/pdf-lib image embedding is NOT thread-safe. Raw bytes
13
+ * load in parallel; embed calls run sequentially.
14
+ *
15
+ * Images that fail to load (network error, file not found, unreachable URL)
16
+ * are routed through doc.onImageLoadError (default = warn-and-skip) so a
17
+ * single broken image does not crash the document.
18
+ */
19
+ import { PDFDocument } from '@cantoo/pdf-lib';
20
+ import { PretextPdfError } from '../../errors.js';
21
+ import { findPlugin, runPluginLoadAsset } from '../../plugin-registry.js';
22
+ import { loadImageBytes, resolveImageFormat } from './images.js';
23
+ import { loadVectorAssets } from './vectors.js';
24
+ import { loadWatermarkImage } from './watermark.js';
25
+ export async function loadImages(doc, pdfDoc, contentWidth, plugins, logger) {
26
+ const warn = logger ? logger.warn.bind(logger) : console.warn.bind(console);
27
+ const imageMap = new Map();
28
+ const allowedDirs = doc.allowedFileDirs;
29
+ // Collect all ImageElement entries with their content index for stable keys
30
+ const imageEntries = [];
31
+ for (let i = 0; i < doc.content.length; i++) {
32
+ const el = doc.content[i];
33
+ if (el.type === 'image') {
34
+ imageEntries.push({ el, key: `img-${i}` });
35
+ }
36
+ else if (el.type === 'float-group') {
37
+ const fgImage = {
38
+ type: 'image',
39
+ src: el.image.src,
40
+ ...(el.image.format !== undefined ? { format: el.image.format } : {}),
41
+ ...(el.image.height !== undefined ? { height: el.image.height } : {}),
42
+ align: 'left',
43
+ spaceAfter: 0,
44
+ spaceBefore: 0,
45
+ };
46
+ imageEntries.push({ el: fgImage, key: `float-group-${i}` });
47
+ }
48
+ }
49
+ // Load all image bytes in parallel; capture both successes and failures
50
+ const loadResults = imageEntries.length > 0
51
+ ? await Promise.allSettled(imageEntries.map(async ({ el, key }) => {
52
+ const bytes = await loadImageBytes(el, key, allowedDirs);
53
+ return { el, key, bytes };
54
+ }))
55
+ : [];
56
+ // Process results: embed successful images, warn on failures
57
+ for (let ri = 0; ri < loadResults.length; ri++) {
58
+ const result = loadResults[ri];
59
+ const entry = imageEntries[ri];
60
+ if (result.status === 'rejected') {
61
+ const err = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
62
+ const action = doc.onImageLoadError ? doc.onImageLoadError(entry.el.src, err) : 'skip';
63
+ if (action === 'throw')
64
+ throw err;
65
+ warn(`[pretext-pdf] Image load skipped: ${err.message}`);
66
+ continue;
67
+ }
68
+ const { el, key, bytes } = result.value;
69
+ const resolvedFormat = resolveImageFormat(el, bytes, key);
70
+ try {
71
+ const pdfImage = resolvedFormat === 'png'
72
+ ? await pdfDoc.embedPng(bytes)
73
+ : await pdfDoc.embedJpg(bytes);
74
+ imageMap.set(key, pdfImage);
75
+ }
76
+ catch (err) {
77
+ const error = err instanceof Error ? err : new Error(String(err));
78
+ const action = doc.onImageLoadError ? doc.onImageLoadError(entry.el.src, error) : 'skip';
79
+ if (action === 'throw')
80
+ throw error;
81
+ warn(`[pretext-pdf] Image embed failed: key "${key}" (format: '${resolvedFormat}')`);
82
+ // Continue without this image rather than crashing
83
+ }
84
+ }
85
+ await loadVectorAssets(doc, pdfDoc, imageMap, contentWidth, allowedDirs, warn);
86
+ // Load plugin assets
87
+ if (plugins && plugins.length > 0) {
88
+ for (let i = 0; i < doc.content.length; i++) {
89
+ const el = doc.content[i];
90
+ const plugin = findPlugin(plugins, el.type);
91
+ if (!plugin)
92
+ continue;
93
+ try {
94
+ const assetResult = await runPluginLoadAsset(plugin, el, pdfDoc, contentWidth, i);
95
+ if (assetResult) {
96
+ imageMap.set(assetResult.key, assetResult.image);
97
+ }
98
+ }
99
+ catch (err) {
100
+ if (err instanceof PretextPdfError)
101
+ throw err;
102
+ warn(`[pretext-pdf] Plugin '${plugin.type}' loadAsset failed at index ${i}: ${err instanceof Error ? err.message : String(err)}`);
103
+ }
104
+ }
105
+ }
106
+ await loadWatermarkImage(doc, pdfDoc, imageMap, allowedDirs, warn);
107
+ return imageMap;
108
+ }
109
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/assets/loaders/orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Vector asset loader — extracted from src/assets.ts in v1.6.0 commit 14/16.
3
+ *
4
+ * Two-phase load for SVG / QR / barcode / chart elements:
5
+ * Phase A — generate svg + rasterize to PNG in bounded-parallel (CPU/I/O,
6
+ * safe to fan out)
7
+ * Phase B — embed PNGs sequentially because pdf-lib xref mutation is not
8
+ * concurrency-safe
9
+ *
10
+ * One failed asset must not block other embeds (error isolation matches
11
+ * prior behavior). SVG failures propagate; QR/barcode/chart failures warn
12
+ * and leave the slot blank.
13
+ */
14
+ import { PDFDocument } from '@cantoo/pdf-lib';
15
+ import type { PdfDocument } from '../../types.js';
16
+ import type { ImageMap } from '../../types-internal.js';
17
+ /** Maximum number of vector-asset rasterization tasks allowed to run in parallel. */
18
+ export declare const VECTOR_RASTER_CONCURRENCY = 4;
19
+ /**
20
+ * Bounded-parallel allSettled. Runs at most `limit` tasks concurrently and
21
+ * collects per-task fulfilled/rejected results in the original order.
22
+ */
23
+ export declare function allSettledWithLimit<T>(items: ReadonlyArray<() => Promise<T>>, limit: number): Promise<PromiseSettledResult<T>[]>;
24
+ export declare function loadVectorAssets(doc: PdfDocument, pdfDoc: PDFDocument, imageMap: ImageMap, contentWidth: number, allowedDirs: string[] | undefined, warn: (msg: string) => void): Promise<void>;
25
+ //# sourceMappingURL=vectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vectors.d.ts","sourceRoot":"","sources":["../../../src/assets/loaders/vectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,gBAAgB,CAAA;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AASvD,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,KAAK,EAAE,aAAa,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,EACtC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAiBpC;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqEf"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Vector asset loader — extracted from src/assets.ts in v1.6.0 commit 14/16.
3
+ *
4
+ * Two-phase load for SVG / QR / barcode / chart elements:
5
+ * Phase A — generate svg + rasterize to PNG in bounded-parallel (CPU/I/O,
6
+ * safe to fan out)
7
+ * Phase B — embed PNGs sequentially because pdf-lib xref mutation is not
8
+ * concurrency-safe
9
+ *
10
+ * One failed asset must not block other embeds (error isolation matches
11
+ * prior behavior). SVG failures propagate; QR/barcode/chart failures warn
12
+ * and leave the slot blank.
13
+ */
14
+ import { PDFDocument } from '@cantoo/pdf-lib';
15
+ import { PretextPdfError } from '../../errors.js';
16
+ import { resolveSvgContent } from '../svg/resolve-content.js';
17
+ import { resolveSvgDimensions } from '../svg/dimensions.js';
18
+ import { rasterizeSvgToPng } from '../svg/rasterize.js';
19
+ import { generateQrSvg } from '../generators/qr.js';
20
+ import { generateBarcodeSvg } from '../generators/barcode.js';
21
+ import { generateChartSvg } from '../generators/chart.js';
22
+ /** Maximum number of vector-asset rasterization tasks allowed to run in parallel. */
23
+ export const VECTOR_RASTER_CONCURRENCY = 4;
24
+ /**
25
+ * Bounded-parallel allSettled. Runs at most `limit` tasks concurrently and
26
+ * collects per-task fulfilled/rejected results in the original order.
27
+ */
28
+ export async function allSettledWithLimit(items, limit) {
29
+ const results = new Array(items.length);
30
+ let cursor = 0;
31
+ async function next() {
32
+ while (cursor < items.length) {
33
+ const idx = cursor++;
34
+ try {
35
+ const value = await items[idx]();
36
+ results[idx] = { status: 'fulfilled', value };
37
+ }
38
+ catch (reason) {
39
+ results[idx] = { status: 'rejected', reason };
40
+ }
41
+ }
42
+ }
43
+ const workerCount = Math.min(Math.max(1, limit), items.length);
44
+ await Promise.all(Array.from({ length: workerCount }, () => next()));
45
+ return results;
46
+ }
47
+ export async function loadVectorAssets(doc, pdfDoc, imageMap, contentWidth, allowedDirs, warn) {
48
+ const tasks = [];
49
+ for (let i = 0; i < doc.content.length; i++) {
50
+ const el = doc.content[i];
51
+ if (el.type === 'svg') {
52
+ tasks.push({ key: `svg-${i}`, index: i, kind: 'svg', run: async () => {
53
+ const svgContent = await resolveSvgContent(el, allowedDirs);
54
+ const svgWithContent = { type: 'svg', svg: svgContent, width: el.width, height: el.height, align: el.align, spaceBefore: el.spaceBefore, spaceAfter: el.spaceAfter };
55
+ const { widthPt, heightPt } = resolveSvgDimensions(svgWithContent, contentWidth);
56
+ return rasterizeSvgToPng(svgContent, widthPt, heightPt);
57
+ } });
58
+ }
59
+ else if (el.type === 'qr-code') {
60
+ tasks.push({ key: `qr-${i}`, index: i, kind: 'qr-code', run: async () => {
61
+ const svgString = await generateQrSvg(el);
62
+ const sizePt = el.size ?? 80;
63
+ return rasterizeSvgToPng(svgString, sizePt, sizePt);
64
+ } });
65
+ }
66
+ else if (el.type === 'barcode') {
67
+ tasks.push({ key: `barcode-${i}`, index: i, kind: 'barcode', run: async () => {
68
+ const svgString = await generateBarcodeSvg(el);
69
+ const widthPt = el.width ?? 200;
70
+ const heightPt = el.height ?? 60;
71
+ return rasterizeSvgToPng(svgString, widthPt, heightPt);
72
+ } });
73
+ }
74
+ else if (el.type === 'chart') {
75
+ tasks.push({ key: `chart-${i}`, index: i, kind: 'chart', run: async () => {
76
+ const svgString = await generateChartSvg(el, contentWidth);
77
+ const widthPt = el.width ?? contentWidth;
78
+ const heightPt = el.height ?? 300;
79
+ return rasterizeSvgToPng(svgString, widthPt, heightPt);
80
+ } });
81
+ }
82
+ }
83
+ if (tasks.length === 0)
84
+ return;
85
+ // Phase A: bounded-parallel rasterization. allSettled isolates per-task failures.
86
+ // Cap concurrency to avoid resource exhaustion (sharp/svg2pdfkit workers, FDs)
87
+ // when a document carries many vector assets.
88
+ const results = await allSettledWithLimit(tasks.map(t => () => t.run()), VECTOR_RASTER_CONCURRENCY);
89
+ // Phase B: MUST remain sequential — pdf-lib xref mutation is not concurrency-safe
90
+ for (let ti = 0; ti < tasks.length; ti++) {
91
+ const task = tasks[ti];
92
+ const result = results[ti];
93
+ if (result.status === 'rejected') {
94
+ const err = result.reason;
95
+ // SVG failures historically propagated (file/url/render errors thrown).
96
+ if (task.kind === 'svg')
97
+ throw err;
98
+ if (err instanceof PretextPdfError)
99
+ throw err;
100
+ const message = err instanceof Error ? err.message : String(err);
101
+ warn(`[pretext-pdf] ${task.kind} load failed at index ${task.index} (CHART_LOAD_FAILED): ${message}. Slot will be blank in PDF.`);
102
+ continue;
103
+ }
104
+ try {
105
+ const pdfImage = await pdfDoc.embedPng(result.value);
106
+ imageMap.set(task.key, pdfImage);
107
+ }
108
+ catch (err) {
109
+ if (task.kind === 'svg')
110
+ throw err;
111
+ if (err instanceof PretextPdfError)
112
+ throw err;
113
+ const message = err instanceof Error ? err.message : String(err);
114
+ warn(`[pretext-pdf] ${task.kind} embed failed at index ${task.index} (CHART_LOAD_FAILED): ${message}. Slot will be blank in PDF.`);
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=vectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vectors.js","sourceRoot":"","sources":["../../../src/assets/loaders/vectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Watermark image loader — extracted from src/assets.ts in v1.6.0 commit 15/16.
3
+ *
4
+ * Loads + embeds the optional `doc.watermark.image` (path or Uint8Array)
5
+ * into the imageMap under the 'watermark' key. PATH_TRAVERSAL errors
6
+ * propagate; other failures are warn-and-skip (matches prior behavior).
7
+ */
8
+ import { PDFDocument } from '@cantoo/pdf-lib';
9
+ import type { PdfDocument } from '../../types.js';
10
+ import type { ImageMap } from '../../types-internal.js';
11
+ export declare function loadWatermarkImage(doc: PdfDocument, pdfDoc: PDFDocument, imageMap: ImageMap, allowedDirs: string[] | undefined, warn: (msg: string) => void): Promise<void>;
12
+ //# sourceMappingURL=watermark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watermark.d.ts","sourceRoot":"","sources":["../../../src/assets/loaders/watermark.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIvD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Watermark image loader — extracted from src/assets.ts in v1.6.0 commit 15/16.
3
+ *
4
+ * Loads + embeds the optional `doc.watermark.image` (path or Uint8Array)
5
+ * into the imageMap under the 'watermark' key. PATH_TRAVERSAL errors
6
+ * propagate; other failures are warn-and-skip (matches prior behavior).
7
+ */
8
+ import { PDFDocument } from '@cantoo/pdf-lib';
9
+ import { PretextPdfError } from '../../errors.js';
10
+ import { assertPathAllowed } from '../security/path-allowlist.js';
11
+ export async function loadWatermarkImage(doc, pdfDoc, imageMap, allowedDirs, warn) {
12
+ if (!doc.watermark?.image)
13
+ return;
14
+ const src = doc.watermark.image;
15
+ try {
16
+ let bytes;
17
+ if (src instanceof Uint8Array) {
18
+ bytes = src;
19
+ }
20
+ else {
21
+ const [fs, pathMod] = await Promise.all([import('fs'), import('path')]);
22
+ const filePath = pathMod.resolve(src);
23
+ assertPathAllowed(filePath, allowedDirs, 'watermark image');
24
+ if (!fs.existsSync(filePath))
25
+ throw new Error(`Watermark image not found`);
26
+ bytes = new Uint8Array(fs.readFileSync(filePath));
27
+ }
28
+ const isPng = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47;
29
+ const pdfImage = isPng
30
+ ? await pdfDoc.embedPng(bytes)
31
+ : await pdfDoc.embedJpg(bytes);
32
+ imageMap.set('watermark', pdfImage);
33
+ }
34
+ catch (err) {
35
+ if (err instanceof PretextPdfError && err.code === 'PATH_TRAVERSAL')
36
+ throw err;
37
+ warn(`[pretext-pdf] Watermark image skipped: ${err instanceof Error ? err.message : String(err)}`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=watermark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watermark.js","sourceRoot":"","sources":["../../../src/assets/loaders/watermark.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEjE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAgB,EAChB,MAAmB,EACnB,QAAkB,EAClB,WAAiC,EACjC,IAA2B;IAE3B,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK;QAAE,OAAM;IAEjC,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,KAAK,CAAA;IAC/B,IAAI,CAAC;QACH,IAAI,KAAiB,CAAA;QACrB,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;YAC9B,KAAK,GAAG,GAAG,CAAA;QACb,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAa,CAAC,CAAA;YAC/C,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;YAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;YAC1E,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAA;QACnD,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;QAC9F,MAAM,QAAQ,GAAG,KAAK;YACpB,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC9B,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAChC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,eAAe,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB;YAAE,MAAM,GAAG,CAAA;QAC9E,IAAI,CAAC,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACpG,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Fetch with a hard 10-second timeout AND a manual redirect chain that
3
+ * re-validates each hop against `resolveAndValidateUrl`. Without manual
4
+ * redirect handling, a public URL could 302 to `http://127.0.0.1:8080/internal`
5
+ * and bypass the upfront check — the connection would still be made to
6
+ * the private target.
7
+ *
8
+ * Each hop creates a fresh undici Agent that pins the socket to the IP
9
+ * that just passed validation. This defeats DNS-rebinding TOCTOU attacks
10
+ * where an attacker controls a TTL=0 DNS record and swaps the answer
11
+ * between our `dns.lookup()` and the actual TCP connect.
12
+ */
13
+ export declare function fetchWithTimeout(url: string, errorCode: 'IMAGE_LOAD_FAILED' | 'SVG_LOAD_FAILED', label: string): Promise<Response>;
14
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/assets/security/fetch.ts"],"names":[],"mappings":"AA0DA;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,mBAAmB,GAAG,iBAAiB,EAClD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAgDnB"}
@@ -0,0 +1,112 @@
1
+ import { Agent, fetch as undiciFetch } from 'undici';
2
+ import { PretextPdfError } from '../../errors.js';
3
+ import { resolveAndValidateUrl } from './url-validation.js';
4
+ /**
5
+ * Hardened remote fetch primitives for image / SVG loading.
6
+ *
7
+ * Extracted from `src/assets.ts` in v1.6.0 commit 8/16 — the architect-flagged
8
+ * highest-risk commit of the split. Key invariants preserved here:
9
+ *
10
+ * 1. NO module-level side effects. In particular, `createPinnedAgent`
11
+ * must remain lazy — every call constructs a fresh undici Agent that
12
+ * lives only for the duration of one HTTP request. There is NO module-
13
+ * level `new Agent({...})` allocation. This preserves the G7 cold-start
14
+ * perf baseline (the previous home in `src/assets.ts` had the same
15
+ * lazy behavior).
16
+ * 2. The undici imports are top-level bindings only; no Agent is
17
+ * constructed at module evaluation time.
18
+ * 3. `fetchWithTimeout` re-validates every redirect hop. Public targets
19
+ * that 302 to private addresses are rejected at the next hop.
20
+ * 4. `Agent.close()` is fire-and-forget inside `finally` so callers never
21
+ * block on shutdown.
22
+ *
23
+ * `src/assets.ts` re-exports `fetchWithTimeout` so existing consumers
24
+ * (notably `test/security-ssrf.test.ts` and `test/assets-dns-dedup.test.ts`
25
+ * which import via `'../src/assets.js'` / `'../dist/assets.js'`) keep working.
26
+ * `createPinnedAgent` is intentionally NOT re-exported — it is a private
27
+ * implementation detail of the fetch primitive.
28
+ */
29
+ /**
30
+ * Build an undici Agent whose every TCP connection is pinned to the supplied
31
+ * pre-validated IP. Closes the DNS-rebinding TOCTOU window: even if DNS is
32
+ * re-resolved by the runtime mid-flight, the socket targets the IP we already
33
+ * confirmed is public.
34
+ *
35
+ * Caller MUST `close()` the agent after the fetch completes.
36
+ *
37
+ * LAZY: called only inside `fetchWithTimeout`, never at module load.
38
+ */
39
+ function createPinnedAgent(ip, family) {
40
+ return new Agent({
41
+ connect: {
42
+ // Undici accepts a Node `dns.lookup`-compatible function here.
43
+ // We unconditionally return the pre-validated IP so a malicious DNS
44
+ // server cannot rebind to a private address between validation and
45
+ // connect.
46
+ lookup: (_hostname, _options, cb) => {
47
+ cb(null, ip, family);
48
+ },
49
+ },
50
+ });
51
+ }
52
+ /**
53
+ * Fetch with a hard 10-second timeout AND a manual redirect chain that
54
+ * re-validates each hop against `resolveAndValidateUrl`. Without manual
55
+ * redirect handling, a public URL could 302 to `http://127.0.0.1:8080/internal`
56
+ * and bypass the upfront check — the connection would still be made to
57
+ * the private target.
58
+ *
59
+ * Each hop creates a fresh undici Agent that pins the socket to the IP
60
+ * that just passed validation. This defeats DNS-rebinding TOCTOU attacks
61
+ * where an attacker controls a TTL=0 DNS record and swaps the answer
62
+ * between our `dns.lookup()` and the actual TCP connect.
63
+ */
64
+ export async function fetchWithTimeout(url, errorCode, label) {
65
+ const MAX_REDIRECTS = 3;
66
+ let currentUrl = url;
67
+ for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
68
+ const { url: parsed, ip, family } = await resolveAndValidateUrl(currentUrl, errorCode, label);
69
+ const controller = new AbortController();
70
+ const timer = setTimeout(() => controller.abort(), 10_000);
71
+ // Only create a pinned dispatcher when we have a resolved IP. For
72
+ // IP-literal hosts or DNS-unavailable cases we let undici handle
73
+ // resolution itself (a DNS failure there is already safe).
74
+ const pinnedAgent = ip && family ? createPinnedAgent(ip, family) : null;
75
+ let res;
76
+ try {
77
+ const fetchOpts = {
78
+ signal: controller.signal,
79
+ redirect: 'manual',
80
+ };
81
+ if (pinnedAgent) {
82
+ // `dispatcher` is an undici-specific extension; cast keeps fetch
83
+ // typings happy without leaking undici types into the public API.
84
+ ;
85
+ fetchOpts.dispatcher = pinnedAgent;
86
+ }
87
+ res = (await undiciFetch(parsed.toString(), fetchOpts));
88
+ }
89
+ finally {
90
+ clearTimeout(timer);
91
+ if (pinnedAgent) {
92
+ // Don't block the caller on agent shutdown; swallow close errors.
93
+ void pinnedAgent.close().catch(() => undefined);
94
+ }
95
+ }
96
+ // Undici fetch does not produce `opaqueredirect`, but keep parity with
97
+ // the browser-fetch contract: if we somehow get one, refuse.
98
+ if (res.type === 'opaqueredirect') {
99
+ throw new PretextPdfError(errorCode, `${label}: cannot follow opaque redirect. Pre-resolve the URL.`);
100
+ }
101
+ if (res.status >= 300 && res.status < 400) {
102
+ const loc = res.headers.get('Location');
103
+ if (!loc)
104
+ throw new PretextPdfError(errorCode, `${label}: redirect (${res.status}) with no Location header`);
105
+ currentUrl = new URL(loc, parsed).toString();
106
+ continue;
107
+ }
108
+ return res;
109
+ }
110
+ throw new PretextPdfError(errorCode, `${label}: too many redirects (max ${MAX_REDIRECTS})`);
111
+ }
112
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/assets/security/fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Normalize alternative IPv4 notations to dotted-decimal form so the
3
+ * private-range regexes catch them. WHATWG URL does NOT normalize these,
4
+ * so an attacker can use any of these forms to bypass `/^127\./`-style
5
+ * checks:
6
+ * - Pure decimal: `2130706433` → 127.0.0.1
7
+ * - Pure hex: `0x7f000001` → 127.0.0.1
8
+ * - Octal octet: `0177.0.0.1` → 127.0.0.1
9
+ * - Hex octet: `0x7f.0.0.1` → 127.0.0.1
10
+ * - Short form: `127.1`, `127.0.1` → 127.0.0.1
11
+ *
12
+ * Returns the dotted-decimal form when `hostname` is any valid IPv4
13
+ * representation, otherwise `null` (leaving the original hostname for
14
+ * DNS resolution / regex matching). Never throws.
15
+ *
16
+ * NOTE: parts with leading zeros (e.g. `008`) are parsed as octal per the
17
+ * traditional inet_aton rules — `008` is INVALID (8 is not an octal digit)
18
+ * so we return null. `010` parses as octal 8. This matches how Linux's
19
+ * getaddrinfo / inet_aton historically resolved these forms, which is what
20
+ * we need to defend against.
21
+ *
22
+ * Extracted from `src/assets.ts` in v1.6.0 commit 6/16 as part of the
23
+ * post-v1.5.2 assets.ts file-size sprint. Pure function — no module-level
24
+ * side effects. Re-exported from `src/assets.ts` so existing direct importers
25
+ * (`test/security-ipv4-bypass.test.ts`) keep working.
26
+ */
27
+ export declare function normalizeIpv4Hostname(hostname: string): string | null;
28
+ //# sourceMappingURL=ipv4-normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipv4-normalize.d.ts","sourceRoot":"","sources":["../../../src/assets/security/ipv4-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuErE"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Normalize alternative IPv4 notations to dotted-decimal form so the
3
+ * private-range regexes catch them. WHATWG URL does NOT normalize these,
4
+ * so an attacker can use any of these forms to bypass `/^127\./`-style
5
+ * checks:
6
+ * - Pure decimal: `2130706433` → 127.0.0.1
7
+ * - Pure hex: `0x7f000001` → 127.0.0.1
8
+ * - Octal octet: `0177.0.0.1` → 127.0.0.1
9
+ * - Hex octet: `0x7f.0.0.1` → 127.0.0.1
10
+ * - Short form: `127.1`, `127.0.1` → 127.0.0.1
11
+ *
12
+ * Returns the dotted-decimal form when `hostname` is any valid IPv4
13
+ * representation, otherwise `null` (leaving the original hostname for
14
+ * DNS resolution / regex matching). Never throws.
15
+ *
16
+ * NOTE: parts with leading zeros (e.g. `008`) are parsed as octal per the
17
+ * traditional inet_aton rules — `008` is INVALID (8 is not an octal digit)
18
+ * so we return null. `010` parses as octal 8. This matches how Linux's
19
+ * getaddrinfo / inet_aton historically resolved these forms, which is what
20
+ * we need to defend against.
21
+ *
22
+ * Extracted from `src/assets.ts` in v1.6.0 commit 6/16 as part of the
23
+ * post-v1.5.2 assets.ts file-size sprint. Pure function — no module-level
24
+ * side effects. Re-exported from `src/assets.ts` so existing direct importers
25
+ * (`test/security-ipv4-bypass.test.ts`) keep working.
26
+ */
27
+ export function normalizeIpv4Hostname(hostname) {
28
+ if (!hostname || hostname.includes(':') || hostname.includes('['))
29
+ return null;
30
+ // Parse a single part as decimal / octal / hex per inet_aton semantics.
31
+ // Returns null on any malformed input (non-digit chars, out-of-range, etc.)
32
+ const parsePart = (part) => {
33
+ if (part.length === 0)
34
+ return null;
35
+ let radix = 10;
36
+ let body = part;
37
+ if (part.length > 1 && (part[0] === '0' || part[0] === '-')) {
38
+ // Reject negative parts outright
39
+ if (part[0] === '-')
40
+ return null;
41
+ }
42
+ if (part === '0')
43
+ return 0;
44
+ if (part.length >= 2 && (part[0] === '0') && (part[1] === 'x' || part[1] === 'X')) {
45
+ radix = 16;
46
+ body = part.slice(2);
47
+ if (body.length === 0)
48
+ return null;
49
+ if (!/^[0-9a-fA-F]+$/.test(body))
50
+ return null;
51
+ }
52
+ else if (part[0] === '0') {
53
+ radix = 8;
54
+ body = part.slice(1);
55
+ if (!/^[0-7]+$/.test(body))
56
+ return null;
57
+ }
58
+ else {
59
+ if (!/^[0-9]+$/.test(part))
60
+ return null;
61
+ }
62
+ const n = parseInt(body, radix);
63
+ if (!Number.isFinite(n) || n < 0)
64
+ return null;
65
+ return n;
66
+ };
67
+ const parts = hostname.split('.');
68
+ if (parts.length === 0 || parts.length > 4)
69
+ return null;
70
+ const parsed = [];
71
+ for (const p of parts) {
72
+ const n = parsePart(p);
73
+ if (n === null)
74
+ return null;
75
+ parsed.push(n);
76
+ }
77
+ // Apply inet_aton-style packing for short forms:
78
+ // 1 part: a → a (32-bit, all octets)
79
+ // 2 parts: a.b → a.0.0.0 | b (last covers low 24 bits)
80
+ // 3 parts: a.b.c → a.b.0.0 | c (last covers low 16 bits)
81
+ // 4 parts: a.b.c.d → each octet 0–255
82
+ let value;
83
+ if (parsed.length === 1) {
84
+ value = parsed[0];
85
+ // Single-int forms (decimal/hex) must fit in 32 bits; reject before the
86
+ // `>>> 0` truncation below would silently wrap.
87
+ if (value < 0 || value > 0xffffffff)
88
+ return null;
89
+ }
90
+ else if (parsed.length === 2) {
91
+ const [a, b] = parsed;
92
+ if (a > 0xff || b > 0xffffff)
93
+ return null;
94
+ value = (a << 24) >>> 0 | b;
95
+ }
96
+ else if (parsed.length === 3) {
97
+ const [a, b, c] = parsed;
98
+ if (a > 0xff || b > 0xff || c > 0xffff)
99
+ return null;
100
+ value = ((a << 24) >>> 0) | (b << 16) | c;
101
+ }
102
+ else {
103
+ const [a, b, c, d] = parsed;
104
+ if (a > 0xff || b > 0xff || c > 0xff || d > 0xff)
105
+ return null;
106
+ value = ((a << 24) >>> 0) | (b << 16) | (c << 8) | d;
107
+ }
108
+ // `|` in JS is a signed 32-bit op — coerce to unsigned for the final value
109
+ // so 0xFFFFFFFF reads as 4294967295 rather than -1.
110
+ value = value >>> 0;
111
+ // Final 32-bit range guard (covers single-int forms like `2130706433`)
112
+ if (value > 0xffffffff)
113
+ return null;
114
+ return `${(value >>> 24) & 0xff}.${(value >>> 16) & 0xff}.${(value >>> 8) & 0xff}.${value & 0xff}`;
115
+ }
116
+ //# sourceMappingURL=ipv4-normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipv4-normalize.js","sourceRoot":"","sources":["../../../src/assets/security/ipv4-normalize.ts"],"names":[],"mappings":"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"}