pretext-pdf 1.9.0 → 2.0.13

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 (159) hide show
  1. package/CHANGELOG.md +414 -0
  2. package/dist/allowed-props.d.ts +23 -8
  3. package/dist/allowed-props.d.ts.map +1 -1
  4. package/dist/allowed-props.js +38 -6
  5. package/dist/allowed-props.js.map +1 -1
  6. package/dist/assets/index.d.ts +1 -1
  7. package/dist/assets/index.d.ts.map +1 -1
  8. package/dist/assets/index.js +1 -1
  9. package/dist/assets/index.js.map +1 -1
  10. package/dist/assets/loaders/images.d.ts.map +1 -1
  11. package/dist/assets/loaders/images.js +4 -1
  12. package/dist/assets/loaders/images.js.map +1 -1
  13. package/dist/assets/security/path-allowlist.d.ts.map +1 -1
  14. package/dist/assets/security/path-allowlist.js +10 -1
  15. package/dist/assets/security/path-allowlist.js.map +1 -1
  16. package/dist/assets/svg/resolve-content.d.ts.map +1 -1
  17. package/dist/assets/svg/resolve-content.js +4 -1
  18. package/dist/assets/svg/resolve-content.js.map +1 -1
  19. package/dist/assets/svg/sanitize.d.ts +2 -0
  20. package/dist/assets/svg/sanitize.d.ts.map +1 -1
  21. package/dist/assets/svg/sanitize.js +41 -10
  22. package/dist/assets/svg/sanitize.js.map +1 -1
  23. package/dist/builder.js +2 -2
  24. package/dist/builder.js.map +1 -1
  25. package/dist/compat/normalize.d.ts +13 -0
  26. package/dist/compat/normalize.d.ts.map +1 -0
  27. package/dist/compat/normalize.js +146 -0
  28. package/dist/compat/normalize.js.map +1 -0
  29. package/dist/compat/pdfmake-types.d.ts +91 -0
  30. package/dist/compat/pdfmake-types.d.ts.map +1 -0
  31. package/dist/compat/pdfmake-types.js +8 -0
  32. package/dist/compat/pdfmake-types.js.map +1 -0
  33. package/dist/compat/translate.d.ts +7 -0
  34. package/dist/compat/translate.d.ts.map +1 -0
  35. package/dist/compat/translate.js +205 -0
  36. package/dist/compat/translate.js.map +1 -0
  37. package/dist/compat.d.ts +6 -161
  38. package/dist/compat.d.ts.map +1 -1
  39. package/dist/compat.js +13 -404
  40. package/dist/compat.js.map +1 -1
  41. package/dist/fonts/bundled-paths.d.ts +13 -0
  42. package/dist/fonts/bundled-paths.d.ts.map +1 -0
  43. package/dist/fonts/bundled-paths.js +43 -0
  44. package/dist/fonts/bundled-paths.js.map +1 -0
  45. package/dist/fonts/collect-needed.d.ts +10 -0
  46. package/dist/fonts/collect-needed.d.ts.map +1 -0
  47. package/dist/fonts/collect-needed.js +85 -0
  48. package/dist/fonts/collect-needed.js.map +1 -0
  49. package/dist/fonts/collect-text.d.ts +9 -0
  50. package/dist/fonts/collect-text.d.ts.map +1 -0
  51. package/dist/fonts/collect-text.js +162 -0
  52. package/dist/fonts/collect-text.js.map +1 -0
  53. package/dist/fonts/load-bytes.d.ts +10 -0
  54. package/dist/fonts/load-bytes.d.ts.map +1 -0
  55. package/dist/fonts/load-bytes.js +56 -0
  56. package/dist/fonts/load-bytes.js.map +1 -0
  57. package/dist/fonts.d.ts +11 -10
  58. package/dist/fonts.d.ts.map +1 -1
  59. package/dist/fonts.js +17 -419
  60. package/dist/fonts.js.map +1 -1
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/measure-blocks/simple-blocks.js +3 -3
  66. package/dist/measure-blocks/simple-blocks.js.map +1 -1
  67. package/dist/measure-text.d.ts.map +1 -1
  68. package/dist/measure-text.js +7 -0
  69. package/dist/measure-text.js.map +1 -1
  70. package/dist/pipeline-footnotes.d.ts +2 -2
  71. package/dist/pipeline-footnotes.d.ts.map +1 -1
  72. package/dist/pipeline-footnotes.js +5 -2
  73. package/dist/pipeline-footnotes.js.map +1 -1
  74. package/dist/pipeline.d.ts.map +1 -1
  75. package/dist/pipeline.js +45 -12
  76. package/dist/pipeline.js.map +1 -1
  77. package/dist/plugin-types.d.ts +3 -3
  78. package/dist/plugin-types.d.ts.map +1 -1
  79. package/dist/render-blocks/hr.d.ts.map +1 -1
  80. package/dist/render-blocks/hr.js +2 -3
  81. package/dist/render-blocks/hr.js.map +1 -1
  82. package/dist/render-extras.d.ts +0 -2
  83. package/dist/render-extras.d.ts.map +1 -1
  84. package/dist/render-extras.js +15 -78
  85. package/dist/render-extras.js.map +1 -1
  86. package/dist/render-utils.d.ts +1 -7
  87. package/dist/render-utils.d.ts.map +1 -1
  88. package/dist/render-utils.js +18 -35
  89. package/dist/render-utils.js.map +1 -1
  90. package/dist/render.js +2 -1
  91. package/dist/render.js.map +1 -1
  92. package/dist/schema/document.d.ts +2115 -0
  93. package/dist/schema/document.d.ts.map +1 -0
  94. package/dist/schema/document.js +275 -0
  95. package/dist/schema/document.js.map +1 -0
  96. package/dist/schema/elements-block.d.ts +481 -0
  97. package/dist/schema/elements-block.d.ts.map +1 -0
  98. package/dist/schema/elements-block.js +155 -0
  99. package/dist/schema/elements-block.js.map +1 -0
  100. package/dist/schema/elements-media.d.ts +282 -0
  101. package/dist/schema/elements-media.d.ts.map +1 -0
  102. package/dist/schema/elements-media.js +83 -0
  103. package/dist/schema/elements-media.js.map +1 -0
  104. package/dist/schema/elements-table.d.ts +134 -0
  105. package/dist/schema/elements-table.d.ts.map +1 -0
  106. package/dist/schema/elements-table.js +60 -0
  107. package/dist/schema/elements-table.js.map +1 -0
  108. package/dist/schema/elements-text.d.ts +782 -0
  109. package/dist/schema/elements-text.d.ts.map +1 -0
  110. package/dist/schema/elements-text.js +260 -0
  111. package/dist/schema/elements-text.js.map +1 -0
  112. package/dist/schema/shared.d.ts +84 -0
  113. package/dist/schema/shared.d.ts.map +1 -0
  114. package/dist/schema/shared.js +29 -0
  115. package/dist/schema/shared.js.map +1 -0
  116. package/dist/schema.d.ts +10 -1869
  117. package/dist/schema.d.ts.map +1 -1
  118. package/dist/schema.js +10 -793
  119. package/dist/schema.js.map +1 -1
  120. package/dist/signing/index.d.ts +3 -0
  121. package/dist/signing/index.d.ts.map +1 -0
  122. package/dist/signing/index.js +3 -0
  123. package/dist/signing/index.js.map +1 -0
  124. package/dist/signing/placeholder.d.ts +5 -0
  125. package/dist/signing/placeholder.d.ts.map +1 -0
  126. package/dist/signing/placeholder.js +75 -0
  127. package/dist/signing/placeholder.js.map +1 -0
  128. package/dist/signing/post-process.d.ts +16 -0
  129. package/dist/signing/post-process.d.ts.map +1 -0
  130. package/dist/signing/post-process.js +125 -0
  131. package/dist/signing/post-process.js.map +1 -0
  132. package/dist/types-public/document.d.ts +106 -110
  133. package/dist/types-public/document.d.ts.map +1 -1
  134. package/dist/types-public/elements-block.d.ts +54 -35
  135. package/dist/types-public/elements-block.d.ts.map +1 -1
  136. package/dist/types-public/validation.d.ts +9 -15
  137. package/dist/types-public/validation.d.ts.map +1 -1
  138. package/dist/types.d.ts +1 -1
  139. package/dist/types.d.ts.map +1 -1
  140. package/dist/validate/document.d.ts.map +1 -1
  141. package/dist/validate/document.js +5 -1
  142. package/dist/validate/document.js.map +1 -1
  143. package/dist/validate/elements/forms-floats.d.ts +1 -1
  144. package/dist/validate/elements/forms-floats.d.ts.map +1 -1
  145. package/dist/validate/elements/forms-floats.js +44 -6
  146. package/dist/validate/elements/forms-floats.js.map +1 -1
  147. package/dist/validate/elements/media.d.ts.map +1 -1
  148. package/dist/validate/elements/media.js +14 -0
  149. package/dist/validate/elements/media.js.map +1 -1
  150. package/dist/validate/elements/structural-simple.js +4 -4
  151. package/dist/validate/elements/structural-simple.js.map +1 -1
  152. package/dist/validate/helpers.d.ts +6 -6
  153. package/dist/validate/helpers.d.ts.map +1 -1
  154. package/dist/validate/helpers.js +7 -7
  155. package/dist/validate/helpers.js.map +1 -1
  156. package/dist/validate/index.d.ts.map +1 -1
  157. package/dist/validate/index.js +4 -6
  158. package/dist/validate/index.js.map +1 -1
  159. package/package.json +7 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,420 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
7
7
 
8
8
  ---
9
9
 
10
+ ## [2.0.13] — 2026-05-29
11
+
12
+ Security hardening and coverage improvements from final review pass.
13
+
14
+ ### Security
15
+
16
+ - **Symlink escape closed in `assertPathAllowed`** — `path-allowlist.ts` now calls `realpathSync` before the prefix comparison, so a symlink placed inside an allowed directory cannot dereference to an arbitrary path outside the allowlist (F-1).
17
+ - **Chart `spec` scanned for prototype-pollution keys** — `validateChart` in `media.ts` now rejects any `spec` object containing `__proto__`, `constructor`, or `prototype` keys at any nesting depth before passing it to the Vega renderer (F-9).
18
+ - **`http://` URLs rejected at loader entry points** — `images.ts` and `resolve-content.ts` now throw immediately on `http://` src values instead of entering the fetch path and relying on the inner SSRF guard (F-4).
19
+
20
+ ### Fixed
21
+
22
+ - **`stageInit` metadata fallback logs message only** — `pipeline.ts` no longer serializes the full error object into `console.warn`; only `err.message` is included, preventing internal API names and stack frames from appearing in library stderr (F-3).
23
+
24
+ ### Tests Added
25
+
26
+ - `test/compat-normalize.test.ts` — 62 tests covering all exported functions in `src/compat/normalize.ts` directly via tsx (previously only reachable through the dist/ barrel).
27
+
28
+ ---
29
+
30
+ ## [2.0.12] — 2026-05-29
31
+
32
+ Multi-perspective review fixes: security, correctness, silent failures, dead code, and test coverage.
33
+
34
+ ### Security
35
+
36
+ - **`allowedFileDirs` array now copied in `fromPdfmake()`** — Previously assigned by reference, allowing the caller to mutate the allowlist after translation and silently expand path-traversal permissions mid-render. Now copied with `[...arr]` (SEC-1).
37
+
38
+ ### Fixed
39
+
40
+ - **`content` field is now `readonly ContentElement[]` on `PdfDocument`** — The last mutable public field. Prevents callers from `.push()`-ing into a document after passing it to `render()`, which could corrupt multi-stage async pipeline results. `builder.toDocument()` now snapshots the content array at call time (H-1).
41
+ - **`buildFootnoteNumbering` and `runFootnoteTwoPass` parameter widened to `readonly ContentElement[]`** — Required by the `content` readonly change; no semantic change.
42
+ - **Footnote sentinel `?? 0` replaced with `PretextPdfError`** — A missing footnote number now throws `PAGINATION_FAILED` with a clear message instead of silently rendering footnote `0` (M-3).
43
+ - **`onUnsupported` default now logs a one-time warning** — The previous default was a silent no-op, contradicting its own JSDoc. Default is now `console.warn('[pretext-pdf/compat] Unsupported pdfmake feature skipped: <feature>')` with per-call deduplication (M-1).
44
+ - **`stageInit` metadata catch block now logs the error** — The fallback path for `getInfoDict()` unavailability no longer silently swallows the root cause (M-2).
45
+ - **`FONT_EMBED_FAILED` error message is user-facing** — Removed "this is a bug" from the message; replaced with actionable guidance (M-5).
46
+ - **Internal exports removed from `translate.ts`** — `translateNodeInner`, `applyStyleToParagraph`, `collectSpans` are no longer exported from the sub-module (M-4).
47
+ - **`_require` unexported from `bundled-paths.ts`** — Was exported with a misleading `_` prefix but only used internally (L-1).
48
+ - **`fonts.ts` header comment includes `collect-text.ts`** — Fourth sub-module was omitted (L-2).
49
+ - **`translate.ts` uses named `ListItem` import** — Replaced inline `import('../types.js').ListItem` expression (L-3).
50
+
51
+ ### Tests Added
52
+
53
+ - `test/compat/normalize.test.ts` — 28 direct unit tests for `mergeStyles`, `normalizePageSize`, `normalizeMargins`, `normalizeHeaderFooter`, `normalizeStyleNames`
54
+ - `test/fonts/collect-text.test.ts` — 9 direct unit tests for `collectTextByFont` across paragraph, heading, rich-paragraph, header/footer, and defaultFont override
55
+ - `test/compat.test.ts` — 3 isolation tests for `allowedFileDirs` array copy (SEC-1)
56
+ - `test/builder.test.ts` — 2 snapshot isolation tests for `toDocument()` content copy (H-1)
57
+
58
+ ### Changed
59
+
60
+ - **`pdfmake-types.ts`: `allowedFileDirs` is now `readonly string[]`** — Type made symmetric with `PdfDocument.allowedFileDirs`
61
+
62
+ ## [2.0.11] — 2026-05-29
63
+
64
+ Phase 6a: `readonly` on all scalar public interface fields (non-breaking).
65
+
66
+ ### Changed
67
+
68
+ - **All scalar fields on public interfaces marked `readonly`** — `PdfDocument`, `DocumentMetadata`,
69
+ `Margins`, `FontSpec`, `HeaderFooterSpec`, `WatermarkBase`/`WatermarkSpec`, `EncryptionSpec`,
70
+ `SignatureSpec`, `BookmarkConfig`, `HyphenationConfig`, `AnnotationSpec`.
71
+
72
+ Array fields (`content`, `fonts`, `keywords`, `sections`, `allowedFileDirs`) are intentionally
73
+ left mutable — marking those `readonly` is a breaking change deferred to v3.
74
+
75
+ This is non-breaking for all callers: TypeScript allows assigning to `readonly` fields in
76
+ object literal initializers, and the runtime behavior is identical.
77
+
78
+ - **`compat.ts`**: Internal construction variables use `Mutable<T>` to allow incremental property
79
+ assignment, cast to the full interface at return. Externally the API is unchanged.
80
+
81
+ ---
82
+
83
+ ## [2.0.10] — 2026-05-29
84
+
85
+ Sprint 5B: Split `compat.ts` (613L) and `fonts.ts` (530L) into focused sub-directories.
86
+
87
+ ### Changed
88
+
89
+ - **`src/compat.ts` split into `src/compat/` (3 files)**
90
+ - `compat/pdfmake-types.ts` (76L) — `PdfmakeDocument`, `PdfmakeObjectNode`, `PdfmakeStyle`, `CompatOptions`, `TranslateCtx`, `DEFAULT_HEADING_MAP`
91
+ - `compat/translate.ts` (155L) — `translateNode`, `translateNodeInner`, `translateTextNode`, `applyStyleToParagraph`, `collectSpans`, `pdfmakeNodeToListItem`
92
+ - `compat/normalize.ts` (115L) — `extractFlatText`, `translateTable`, `mergeStyles`, `normalizeStyleNames`, `pdfmakeAlignToPretext`, `normalizePageSize`, `normalizeMargins`, `normalizeHeaderFooter`
93
+ - `compat.ts` (85L) — `fromPdfmake` (the only public export) + type re-exports
94
+ Circular-dependency between translate↔normalize avoided by moving `extractFlatText` to `normalize.ts`.
95
+
96
+ - **`src/fonts.ts` split into `src/fonts/` (4 files)**
97
+ - `fonts/bundled-paths.ts` (50L) — `IS_NODE`, `resolveInterFile`, all four `BUNDLED_INTER_*_PATHS` arrays
98
+ - `fonts/load-bytes.ts` (80L) — `loadFontBytes` (file / Uint8Array / bundled-Inter async loader)
99
+ - `fonts/collect-needed.ts` (75L) — `collectNeededFonts` (document font-variant scanner)
100
+ - `fonts/collect-text.ts` (120L) — `collectTextByFont` (glyph subsetting text collector)
101
+ - `fonts.ts` (70L) — `loadFonts` + `collectTextByFont` re-export (both public exports intact)
102
+
103
+ The public API is unchanged. All existing test and source imports continue to work.
104
+
105
+ ---
106
+
107
+ ## [2.0.9] — 2026-05-29
108
+
109
+ Sprint 5A: dead-export removal, ts-prune scan.
110
+
111
+ ### Changed
112
+
113
+ - **`addGoToAnnotation` removed from `src/render-utils.ts`** — The function was exported but
114
+ never called anywhere in the codebase. GoTo annotation support (internal document links)
115
+ was prepared prematurely; the implementation is preserved in git history and can be
116
+ restored when anchor-link rendering is actually wired up.
117
+
118
+ - **`RTL_REGEX` removed from `src/validate/helpers.ts`** — Exported constant never imported
119
+ anywhere. The regex pattern is preserved in git history.
120
+
121
+ - **`PDFRef` import removed from `render-utils.ts`** — Only needed for the now-deleted
122
+ `addGoToAnnotation` signature.
123
+
124
+ ---
125
+
126
+ ## [2.0.7] — 2026-05-28
127
+
128
+ Sprint 4: Split `schema.ts` (907 LOC) into focused `src/schema/` modules.
129
+
130
+ ### Changed
131
+
132
+ - **`src/schema.ts` split into `src/schema/` (6 focused files)** — The 907-line monolith is
133
+ replaced by a 19-line barrel re-export and six cohesive sub-files, each under 300 lines:
134
+ - `schema/shared.ts` (30L) — atomic sub-schemas: align, color, fontWeight, dir, space, inlineSpan
135
+ - `schema/elements-text.ts` (270L) — paragraph, heading, blockquote, code, callout, richParagraph, list, toc, footnoteDef
136
+ - `schema/elements-media.ts` (87L) — image, svg, qrCode, barcode, chart
137
+ - `schema/elements-block.ts` (164L) — spacer, hr, pageBreak, comment, formField, floatGroup
138
+ - `schema/elements-table.ts` (60L) — table
139
+ - `schema/document.ts` (293L) — top-level pdfDocumentSchema (assembles all element schemas)
140
+ The public API (`import { pdfDocumentSchema } from 'pretext-pdf/schema'`) is unchanged.
141
+
142
+ ---
143
+
144
+ ## [2.0.6] — 2026-05-28
145
+
146
+ Post-sprint audit fixes: /Lang encoding, BCP47 regex, test correctness.
147
+
148
+ ### Fixed
149
+
150
+ - **`/Lang` catalog entry now uses `PDFString.of()` (correct literal string)** — Previously
151
+ used `PDFHexString.of()` which stored raw ASCII bytes as invalid hex. `/Lang` is a
152
+ PDF text string per spec §14.9.2; `PDFString` (literal string) is correct and matches
153
+ pdf-lib's own `setLanguage()`. BCP47 tags are ASCII-only (validated by regex) so
154
+ literal-string injection risk is zero.
155
+
156
+ - **BCP47 regex widened to accept `i-` and `x-` prefixed tags** — `/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/`
157
+ now allows the single-char leading subtag required by grandfathered (`i-klingon`) and
158
+ private-use (`x-custom-app`) tags per RFC 5646 §2.2.7. Previously `{2,8}` incorrectly
159
+ rejected valid language codes.
160
+
161
+ ### Tests
162
+
163
+ - **H6 (signerName truncation)** strengthened — replaced the vacuous 5% byte-size ratio with
164
+ a `deepEqual(pdf100, pdf101)` identity check: a 100-char and 101-char signerName must produce
165
+ byte-identical PDFs, directly testing that `slice(0, 100)` is applied.
166
+
167
+ - **M5 (/TU encoding)** — added `FEFF` BOM assertion to the radio and dropdown loop,
168
+ making it consistent with the checkbox/button loop.
169
+
170
+ - **M6 (bookmark Title)** — replaced the `FEFF0043` prefix check (too broad) with the full
171
+ 6-character "Chapte" UTF-16BE hex prefix for an unambiguous unique match.
172
+
173
+ - **M7 (signing field escaping)** — clarified test scope: the visual placeholder path uses
174
+ `drawText()` (CIDFont glyph encoding — no injection risk). The `escapePdfLit` contract is
175
+ tested in `signatures-validation.test.ts` which covers the crypto signing code path.
176
+
177
+ ---
178
+
179
+ ## [2.0.5] — 2026-05-28
180
+
181
+ Sprint 3 audit fixes: annotation array safety, test coverage gaps closed.
182
+
183
+ ### Fixed
184
+
185
+ - **`addStickyNoteAnnotation` now has `instanceof PDFArray` guard** — the last remaining
186
+ `as any` annotation-push in `render-utils.ts`. All three annotation functions
187
+ (`addLinkAnnotation`, `addGoToAnnotation`, `addStickyNoteAnnotation`) now safely fall back
188
+ to creating a new array if the existing `Annots` entry is not a direct `PDFArray`.
189
+
190
+ ### Tests
191
+
192
+ - **H6** (`test/signatures-visual.test.ts`): `signerName` longer than 100 chars renders to
193
+ the same byte-size PDF as a 100-char name — confirms the `slice(0, 100)` truncation is applied.
194
+ - **M5** (`test/forms.test.ts`): `/TU` encoding verified for checkbox, radio, dropdown, and
195
+ button field types — not just `text`.
196
+ - **M6** (`test/bookmarks.test.ts`): Bookmark `Title` with special chars (parentheses) appears
197
+ as UTF-16BE hex in the PDF bytes — raw literal form `(Chapter (1): ...)` does not appear.
198
+ - **M7** (`test/signatures-visual.test.ts`): Backslash and parentheses in `signerName`,
199
+ `reason`, and `location` render without throwing (escaping confirmed).
200
+
201
+ ---
202
+
203
+ ## [2.0.4] — 2026-05-28
204
+
205
+ Sprint 2 audit fixes: type-safety improvements in render-utils.ts and vendor declarations.
206
+
207
+ ### Changed
208
+
209
+ - **`pdf-lib-augment.d.ts` expanded** — Declares `PdfFontEmbedder` interface covering the
210
+ private `embedder.font` (underlinePosition, underlineThickness, xHeight) and `embedder.scale`
211
+ fields accessed for text decoration metrics. The `(pdfFont as any).embedder` cast in
212
+ `render-utils.ts` is now `(pdfFont as unknown as { embedder?: PdfFontEmbedder }).embedder`,
213
+ eliminating untyped `any` propagation into font-metric arithmetic.
214
+
215
+ ---
216
+
217
+ ## [2.0.3] — 2026-05-28
218
+
219
+ Sprint 1 audit fixes: GoTo annotation guard, BCP47 metadata validation, dependency security.
220
+
221
+ ### Fixed
222
+
223
+ - **`addGoToAnnotation` now has `instanceof PDFArray` guard** — the same fix applied to
224
+ `addLinkAnnotation` and `addStickyNoteAnnotation` in v2.0.2 was missed for GoTo annotations.
225
+ Existing `Annots` entries that are not a direct `PDFArray` now fall back to creating a new
226
+ array instead of silently calling `.push()` on an `any`-cast value.
227
+
228
+ - **`metadata.language` validated against `LANGUAGE_TAG_REGEX`** — previously only checked for
229
+ non-empty string and max length. Now enforces the same BCP47 regex (`/^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{2,8})*$/`)
230
+ used for `hyphenation.language`, closing an inconsistency in the validation layer.
231
+
232
+ - **Dependency audit** — `npm audit fix` resolved `fast-uri <=3.1.1` CVE (GHSA-v39h-62p7-jpjc,
233
+ GHSA-q3j6-qgpj-74h6) in the dev dependency chain.
234
+
235
+ ---
236
+
237
+ ## [2.0.2] — 2026-05-28
238
+
239
+ Second post-release audit patch: PDF injection hardening (metadata, AcroForm names), PDFHexString encoding correctness, signing field escaping, annotation array safety.
240
+
241
+ ### Fixed
242
+
243
+ - **Metadata Info dict fields now use `PDFHexString.fromText()` (correct UTF-16BE encoding)** —
244
+ The v2.0.1 fix used `PDFHexString.of()` which stored raw bytes between `<>` delimiters (not
245
+ valid hex). `fromText` correctly produces `<FEFF...>` UTF-16BE hex, the same encoding used by
246
+ pdf-lib's built-in `setTitle()` etc. Injection protection is now both correct AND effective.
247
+
248
+ - **Form field `name` and option `value` restricted to AcroForm-safe characters** — Field names
249
+ and radio/dropdown option export values are written as PDF literal strings by pdf-lib. Characters
250
+ like `)`, `\`, and null bytes that could corrupt the AcroForm `/T` dictionary are now rejected
251
+ at validation time with a clear error message. Allowed: `[a-zA-Z0-9_.@-]+` (`HIGH-2`).
252
+
253
+ - **Signing placeholder fields escaped for PDF literal strings** — `sig.reason`, `sig.location`,
254
+ `sig.contactInfo`, and `sig.signerName` are passed to `@signpdf/placeholder-pdf-lib` which
255
+ writes them as PDF literal strings. Backslashes and parentheses are now escaped with `\` before
256
+ passing, so values like "New York (USA)" are preserved correctly without breaking the dict.
257
+
258
+ - **Annotation array push guarded with `instanceof PDFArray`** — The previous `as any` cast on
259
+ `pdfDoc.context.lookup(existingAnnots)` allowed silent no-ops if the value wasn't a PDFArray.
260
+ All three annotation functions now check `instanceof PDFArray` and fall back to creating a new
261
+ array rather than silently dropping the annotation.
262
+
263
+ - **Bookmark `Title` and AcroForm `/TU` use `PDFHexString.fromText()`** — Same `of()` → `fromText()`
264
+ correction applied to bookmark headings and all five AcroForm `/TU` (accessibility tooltip) writes.
265
+
266
+ - **Sticky note `Contents` and author `T` use `PDFHexString.fromText()`** — Human-readable text in
267
+ sticky note annotations now uses proper UTF-16BE encoding.
268
+
269
+ - **Signature placeholder `signerName` truncated to 100 chars** — Prevents glyph overflow outside
270
+ the visual signature box for very long signer names.
271
+
272
+ - **Schema `content.items` changed from `anyOf` to `oneOf`** — JSON Schema validators and AI agent
273
+ code generators now get exclusive-match semantics, preventing multi-schema ambiguity.
274
+
275
+ ### Tests
276
+
277
+ - Added T6: `/TU` accessibilityLabel byte-level injection guard in `test/forms.test.ts`.
278
+ - Added T7: metadata title/author/accessibility UTF-16BE hex-encoding byte checks in `test/metadata.test.ts`.
279
+ - Added field name and option value AcroForm-safety tests in `test/forms.test.ts`.
280
+
281
+ ---
282
+
283
+ ## [2.0.1] — 2026-05-28
284
+
285
+ Post-release patch: audit-driven hardening of every change introduced in v2.0.0.
286
+
287
+ ### Fixed
288
+
289
+ - **SVG sanitizer: `on*` handlers with whitespace in attribute name now stripped** — Attackers
290
+ could inject `on\nload=` or `on\tclick=` to bypass the previous `\w+` name regex. The pattern
291
+ now uses `[\w\r\n\t ]+` for the attribute name portion (`H3`).
292
+
293
+ - **SVG sanitizer: `expression()` strips arguments with nested parens** — `expression(alert(1))`
294
+ and `expression(eval(x))` are now stripped in a single pass. The inner-parens pattern
295
+ `(?:[^()]*|\([^()]*\))*` handles one level of argument nesting; multi-pass unwinds deeper
296
+ nesting (`M6`).
297
+
298
+ - **SVG sanitizer: size/element-count guards now throw `PretextPdfError('SVG_LOAD_FAILED')`** —
299
+ Previously both guards returned the raw unstripped SVG on overflow, silently bypassing
300
+ script/event stripping. Now both throw, so callers always receive a typed error (`H1`).
301
+
302
+ - **Form-field strict mode no longer flags cross-variant props as unknown** — `ALLOWED_PROPS`
303
+ entry for `form-field` now covers the union of all variant-specific keys so the first strict
304
+ check never fires false positives. Per-variant narrowing still runs inside `validateFormField`
305
+ to catch cross-contamination (`B1`).
306
+
307
+ - **Bookmark `Title` and AcroForm `/TU` now use `PDFHexString`** — Previously `PDFString` was
308
+ used, which is vulnerable to unbalanced-parenthesis injection in user-controlled strings. All
309
+ five `/TU` writes and the outline `Title` write now use the hex-encoded form (`B3`).
310
+
311
+ - **Link URI annotation and sticky-note `Contents`/`T` use `PDFHexString`** — Same injection
312
+ guard extended to `addLinkAnnotation` and `addStickyNoteAnnotation` (`B3`).
313
+
314
+ - **`getInfoDict()` private-API call is now try/caught** — Accessibility and semantic metadata
315
+ are silently omitted (rather than throwing) if the `@cantoo/pdf-lib` internal API is removed
316
+ in a future library version (`M7`).
317
+
318
+ - **`SignPdf`/`P12Signer` dynamic imports typed via local interfaces** — Removes `any` casts
319
+ on the signpdf module destructuring, catching future API drift at compile time (`M4`).
320
+
321
+ - **Signing error message scrubbed of certificate details** — `SIGNATURE_FAILED` no longer
322
+ leaks P12 structural details or ASN.1 internals; only the first 120 chars of the underlying
323
+ error message are included (`F9`).
324
+
325
+ - **`FORM_FIELD_VARIANT_PROPS` type narrowed** — The export type is now
326
+ `Record<'text'|'checkbox'|'radio'|'dropdown'|'button', ReadonlySet<string>>` so callers that
327
+ iterate the map get exhaustive narrowing rather than a plain string index (`M1`).
328
+
329
+ - **`fieldType` validation is now derived from `FORM_FIELD_VARIANT_PROPS`** — The allowed-values
330
+ list and the dispatch map are now the same object, removing the risk of one going stale (`M2`).
331
+
332
+ - **Form-field options arrays are structurally validated** — Each item in `options` for `radio`
333
+ and `dropdown` is now checked to be a `{value: string, label: string}` object. Invalid items
334
+ throw `VALIDATION_ERROR` with an indexed path like `options[1].value` (`H2`).
335
+
336
+ - **`accessibilityLabel` validated as non-empty string** — Empty or non-string values now
337
+ throw `VALIDATION_ERROR` rather than embedding an empty `/TU` annotation (`H2`).
338
+
339
+ - **`signing/placeholder.ts` uses `buildFontKey()` instead of hardcoded literal** — Keeps the
340
+ font lookup in sync with the canonical key format if the naming convention ever changes (`L1`).
341
+
342
+ - **`PluginMeasureResult.spaceBefore/spaceAfter` marked readonly** — Plugins cannot mutate the
343
+ pipeline's spacing side-table; callers can still construct the object with literal values (`L2`).
344
+
345
+ - **`package.json` `mcpCompat` range updated** — Was `>=1.4.0 <2.0.0` (excluded v2.0.0 itself);
346
+ now `>=1.5.0 <3.0.0` (`B2`).
347
+
348
+ - **`pdfDocumentSchema`: `form-field` now a `oneOf` discriminated union** — The flat single-object
349
+ schema was replaced with five variant schemas keyed by `fieldType: { const: '...' }`, with
350
+ correct per-variant `required` arrays (radio/dropdown require `options`) (`M3`).
351
+
352
+ ### Tests
353
+
354
+ - Added T1: `on\nload` and `on\tclick` attribute-name injection regression guard in
355
+ `test/svg-sanitizer.test.ts`.
356
+ - Added T2: Per-variant strict-mode unknown-props tests for form-field in
357
+ `test/validate-strict.test.ts` (Group 8).
358
+ - Added T3: `./signing` entry point added to `test/public-api-surface.test.ts` tripwire.
359
+ - Added T4: `MAX_SVG_ELEMENTS` boundary tests (at-limit sanitizes, over-limit throws) in
360
+ `test/svg-sanitizer.test.ts`.
361
+ - Added T5: `fieldType: 'textarea'` (unknown value) test triggering `VALIDATION_ERROR`.
362
+
363
+ ---
364
+
365
+ ## [2.0.0] — 2026-05-28
366
+
367
+ Major release. Three breaking changes, six improvements, and two security fixes.
368
+
369
+ ### Breaking Changes
370
+
371
+ - **`FormFieldElement` is now a discriminated union** — Replace the single flat interface with
372
+ five per-variant types (`TextFormField | CheckboxFormField | RadioFormField | DropdownFormField |
373
+ ButtonFormField`) that narrow available properties by `fieldType`. The `BaseFormField` interface
374
+ holds all shared fields and is also exported. Callers that assign into `FormFieldElement` without
375
+ narrowing will need to specify the concrete variant. **Migration:** narrow on `fieldType` or
376
+ import the specific variant type.
377
+
378
+ - **`HorizontalRuleElement.spaceAbove` / `spaceBelow` removed** — Use `spaceBefore` / `spaceAfter`
379
+ (stable since v0.9). The deprecated aliases were present in v1.x with `@deprecated` JSDoc.
380
+ **Migration:** replace `spaceAbove` → `spaceBefore`, `spaceBelow` → `spaceAfter`.
381
+
382
+ - **`ValidationResult.warningCount` removed** — The field was always `0` in v1.x (validator emits
383
+ errors only). **Migration:** use `result.errors.filter(e => e.severity === 'warning').length` if
384
+ you need a count — returns `0` until warning-severity issues are introduced.
385
+
386
+ ### Added
387
+
388
+ - **`./signing` export** — New subpath export `pretext-pdf/signing` exposes `applySignature`,
389
+ `applyEncryption`, `applyPostProcessing`, and `renderSignaturePlaceholder` as a standalone
390
+ signing module. Useful for post-render pipeline composition without importing the full library.
391
+
392
+ - **`MAX_SVG_ELEMENTS` constant** — Exported from `dist/assets.js` alongside `SVG_MAX_BYTES`.
393
+ Heuristic element-count guard (5,000 open tags) that skips sanitization of pathologically deep
394
+ SVGs to prevent memory exhaustion during rasterization.
395
+
396
+ - **Per-variant `accessibilityLabel` wired to AcroForm `/TU`** — `accessibilityLabel` on form
397
+ fields is now written to the PDF annotation `/TU` (tooltip/alt-text) dictionary entry.
398
+ Screen readers and assistive technology that consume AcroForm annotations will pick it up.
399
+
400
+ - **`metadata.accessibility` / `metadata.semantic` written to Info dict** — Both reserved fields
401
+ are now serialized as JSON strings into the PDF Info dictionary (`Accessibility` and `Semantic`
402
+ keys). Previously they were accepted but ignored at render time.
403
+
404
+ ### Changed
405
+
406
+ - **`ValidationError` and `ValidationResult` fields are `readonly`** — All fields on both
407
+ interfaces are now `readonly`. This is technically breaking for callers that mutate validation
408
+ results directly (which should be none in practice).
409
+
410
+ - **`PluginMeasureResult.height` is `readonly`** — Prevents accidental mutation of the measured
411
+ height by render hooks.
412
+
413
+ ### Fixed (security)
414
+
415
+ - **SVG `on*` handler regex now catches newline-injected attributes** — The previous pattern
416
+ `\bon\w+\s*=` missed attributes like `on\nload=`. Fixed to `\bon\w+[\s\n\r]*=`.
417
+
418
+ - **`allowed-props` strict mode now covers `accessibilityLabel`, `accessibility`, `semantic`** —
419
+ These fields were missing from their respective key arrays; strict-mode validation would
420
+ incorrectly report them as unknown properties. Fixed.
421
+
422
+ ---
423
+
10
424
  ## [1.9.0] — 2026-05-28
11
425
 
12
426
  Additive release: error categorisation, typed plugin generics, beta graduations, soft deprecations, and a 100 MB output size guard.
@@ -3,12 +3,12 @@
3
3
  * Enforced at runtime by strict: true validation.
4
4
  * Compile-time drift guards via Exact<T, Keys> ensure types stay synchronized.
5
5
  */
6
- import type { PdfDocument, ParagraphElement, HeadingElement, SpacerElement, TableElement, TableCell, ColumnDef, ImageElement, SvgElement, QrCodeElement, BarcodeElement, ChartElement, ListElement, ListItem, HorizontalRuleElement, PageBreakElement, CodeBlockElement, RichParagraphElement, InlineSpan, BlockquoteElement, CalloutElement, CommentElement, FormFieldElement, FootnoteDefElement, TocElement, FloatGroupElement, AnnotationSpec, TableRow, DocumentMetadata, EncryptionSpec } from './types.js';
6
+ import type { PdfDocument, ParagraphElement, HeadingElement, SpacerElement, TableElement, TableCell, ColumnDef, ImageElement, SvgElement, QrCodeElement, BarcodeElement, ChartElement, ListElement, ListItem, HorizontalRuleElement, PageBreakElement, CodeBlockElement, RichParagraphElement, InlineSpan, BlockquoteElement, CalloutElement, CommentElement, TextFormField, CheckboxFormField, RadioFormField, DropdownFormField, ButtonFormField, FootnoteDefElement, TocElement, FloatGroupElement, AnnotationSpec, TableRow, DocumentMetadata, EncryptionSpec } from './types.js';
7
7
  import type { TocEntryElement } from './types-internal.js';
8
8
  /** Compile-time assertion that T has exactly the keys in Keys (no more, no less) */
9
9
  type Exact<T, Keys extends readonly (keyof T)[]> = T & Record<Exclude<keyof T, Keys[number]>, never>;
10
10
  declare const DOC_KEYS: readonly ["pageSize", "margins", "defaultFont", "defaultFontSize", "defaultLineHeight", "fonts", "header", "footer", "watermark", "encryption", "signature", "bookmarks", "hyphenation", "metadata", "defaultParagraphStyle", "sections", "content", "flattenForms", "onImageLoadError", "onFormFieldError", "renderDate", "allowedFileDirs"];
11
- declare const METADATA_KEYS: readonly ["title", "author", "subject", "keywords", "creator", "language", "producer"];
11
+ declare const METADATA_KEYS: readonly ["title", "author", "subject", "keywords", "creator", "language", "producer", "accessibility", "semantic"];
12
12
  declare const PARAGRAPH_KEYS: readonly ["type", "text", "dir", "fontSize", "lineHeight", "fontFamily", "fontWeight", "color", "align", "bgColor", "spaceAfter", "spaceBefore", "keepTogether", "underline", "strikethrough", "url", "columns", "columnGap", "hyphenate", "letterSpacing", "smallCaps", "tabularNumbers", "annotation"];
13
13
  declare const HEADING_KEYS: readonly ["type", "level", "text", "dir", "fontFamily", "fontWeight", "fontSize", "lineHeight", "align", "color", "bgColor", "spaceBefore", "spaceAfter", "keepTogether", "underline", "strikethrough", "bookmark", "hyphenate", "url", "anchor", "letterSpacing", "smallCaps", "tabularNumbers", "annotation"];
14
14
  declare const SPACER_KEYS: readonly ["type", "height"];
@@ -23,7 +23,7 @@ declare const BARCODE_KEYS: readonly ["type", "symbology", "data", "width", "hei
23
23
  declare const CHART_KEYS: readonly ["type", "spec", "width", "height", "caption", "align", "spaceBefore", "spaceAfter"];
24
24
  declare const LIST_KEYS: readonly ["type", "style", "items", "marker", "indent", "markerWidth", "fontSize", "lineHeight", "itemSpaceAfter", "spaceAfter", "spaceBefore", "color", "nestedNumberingStyle"];
25
25
  declare const LIST_ITEM_KEYS: readonly ["text", "dir", "fontWeight", "items"];
26
- declare const HR_KEYS: readonly ["type", "thickness", "color", "spaceAbove", "spaceBelow", "spaceBefore", "spaceAfter"];
26
+ declare const HR_KEYS: readonly ["type", "thickness", "color", "spaceBefore", "spaceAfter"];
27
27
  declare const PAGE_BREAK_KEYS: readonly ["type"];
28
28
  declare const CODE_KEYS: readonly ["type", "text", "dir", "fontFamily", "fontSize", "lineHeight", "bgColor", "color", "padding", "spaceAfter", "spaceBefore", "keepTogether", "language", "highlightTheme"];
29
29
  declare const RICH_PARAGRAPH_KEYS: readonly ["type", "spans", "dir", "fontSize", "lineHeight", "align", "bgColor", "spaceBefore", "spaceAfter", "keepTogether", "columns", "columnGap", "letterSpacing", "smallCaps", "tabularNumbers"];
@@ -31,7 +31,11 @@ declare const INLINE_SPAN_KEYS: readonly ["text", "dir", "fontFamily", "fontWeig
31
31
  declare const BLOCKQUOTE_KEYS: readonly ["type", "text", "dir", "borderColor", "borderWidth", "bgColor", "color", "fontFamily", "fontWeight", "fontStyle", "fontSize", "lineHeight", "padding", "paddingH", "paddingV", "align", "spaceBefore", "spaceAfter", "keepTogether", "underline", "strikethrough"];
32
32
  declare const CALLOUT_KEYS: readonly ["type", "content", "style", "title", "backgroundColor", "borderColor", "color", "titleColor", "fontFamily", "fontWeight", "fontSize", "lineHeight", "padding", "paddingH", "paddingV", "spaceAfter", "spaceBefore", "keepTogether", "dir"];
33
33
  declare const COMMENT_KEYS: readonly ["type", "contents", "author", "color", "open", "spaceAfter"];
34
- declare const FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "placeholder", "defaultValue", "multiline", "maxLength", "checked", "options", "defaultSelected", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether"];
34
+ declare const TEXT_FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether", "accessibilityLabel", "placeholder", "defaultValue", "multiline", "maxLength"];
35
+ declare const CHECKBOX_FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether", "accessibilityLabel", "checked"];
36
+ declare const RADIO_FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether", "accessibilityLabel", "options", "defaultSelected"];
37
+ declare const DROPDOWN_FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether", "accessibilityLabel", "options", "defaultSelected"];
38
+ declare const BUTTON_FORM_FIELD_KEYS: readonly ["type", "fieldType", "name", "label", "width", "height", "fontSize", "borderColor", "backgroundColor", "spaceAfter", "spaceBefore", "keepTogether", "accessibilityLabel"];
35
39
  declare const FOOTNOTE_DEF_KEYS: readonly ["type", "id", "text", "fontSize", "fontFamily", "spaceAfter"];
36
40
  declare const TOC_KEYS: readonly ["type", "title", "showTitle", "minLevel", "maxLevel", "fontSize", "titleFontSize", "levelIndent", "leader", "entrySpacing", "fontFamily", "spaceBefore", "spaceAfter"];
37
41
  declare const TOC_ENTRY_KEYS: readonly ["type", "text", "pageNumber", "level", "levelIndent", "leader", "fontFamily", "fontWeight"];
@@ -70,7 +74,11 @@ export type _AllowedPropsDriftGuard = [
70
74
  Exact<BlockquoteElement, typeof BLOCKQUOTE_KEYS>,
71
75
  Exact<CalloutElement, typeof CALLOUT_KEYS>,
72
76
  Exact<CommentElement, typeof COMMENT_KEYS>,
73
- Exact<FormFieldElement, typeof FORM_FIELD_KEYS>,
77
+ Exact<TextFormField, typeof TEXT_FORM_FIELD_KEYS>,
78
+ Exact<CheckboxFormField, typeof CHECKBOX_FORM_FIELD_KEYS>,
79
+ Exact<RadioFormField, typeof RADIO_FORM_FIELD_KEYS>,
80
+ Exact<DropdownFormField, typeof DROPDOWN_FORM_FIELD_KEYS>,
81
+ Exact<ButtonFormField, typeof BUTTON_FORM_FIELD_KEYS>,
74
82
  Exact<FootnoteDefElement, typeof FOOTNOTE_DEF_KEYS>,
75
83
  Exact<TocElement, typeof TOC_KEYS>,
76
84
  Exact<TocEntryElement, typeof TOC_ENTRY_KEYS>,
@@ -89,7 +97,7 @@ export declare const ALLOWED_PROPS: {
89
97
  readonly barcode: Set<"type" | "align" | "spaceAfter" | "spaceBefore" | "height" | "width" | "data" | "symbology" | "includeText">;
90
98
  readonly chart: Set<"type" | "align" | "spaceAfter" | "spaceBefore" | "height" | "width" | "spec" | "caption">;
91
99
  readonly list: Set<"type" | "fontSize" | "lineHeight" | "color" | "spaceAfter" | "spaceBefore" | "style" | "items" | "marker" | "indent" | "markerWidth" | "itemSpaceAfter" | "nestedNumberingStyle">;
92
- readonly hr: Set<"type" | "color" | "spaceAfter" | "spaceBefore" | "thickness" | "spaceAbove" | "spaceBelow">;
100
+ readonly hr: Set<"type" | "color" | "spaceAfter" | "spaceBefore" | "thickness">;
93
101
  readonly 'page-break': Set<"type">;
94
102
  readonly code: Set<"text" | "language" | "type" | "dir" | "fontSize" | "lineHeight" | "fontFamily" | "color" | "bgColor" | "spaceAfter" | "spaceBefore" | "keepTogether" | "padding" | "highlightTheme">;
95
103
  readonly 'rich-paragraph': Set<"type" | "dir" | "fontSize" | "lineHeight" | "align" | "bgColor" | "spaceAfter" | "spaceBefore" | "keepTogether" | "columns" | "columnGap" | "letterSpacing" | "smallCaps" | "tabularNumbers" | "spans">;
@@ -97,14 +105,21 @@ export declare const ALLOWED_PROPS: {
97
105
  readonly toc: Set<"title" | "type" | "fontSize" | "fontFamily" | "spaceAfter" | "spaceBefore" | "showTitle" | "minLevel" | "maxLevel" | "titleFontSize" | "levelIndent" | "leader" | "entrySpacing">;
98
106
  readonly 'toc-entry': Set<"text" | "type" | "fontFamily" | "fontWeight" | "level" | "levelIndent" | "leader" | "pageNumber">;
99
107
  readonly comment: Set<"author" | "type" | "color" | "spaceAfter" | "contents" | "open">;
100
- readonly 'form-field': Set<"type" | "fontSize" | "spaceAfter" | "spaceBefore" | "keepTogether" | "height" | "borderColor" | "width" | "backgroundColor" | "fieldType" | "name" | "label" | "placeholder" | "defaultValue" | "multiline" | "maxLength" | "checked" | "options" | "defaultSelected">;
101
108
  readonly callout: Set<"content" | "title" | "type" | "dir" | "fontSize" | "lineHeight" | "fontFamily" | "fontWeight" | "color" | "spaceAfter" | "spaceBefore" | "keepTogether" | "borderColor" | "style" | "padding" | "paddingH" | "paddingV" | "backgroundColor" | "titleColor">;
102
109
  readonly 'footnote-def': Set<"text" | "type" | "fontSize" | "fontFamily" | "spaceAfter" | "id">;
103
110
  readonly 'float-group': Set<"image" | "content" | "type" | "spaceAfter" | "spaceBefore" | "float" | "floatWidth" | "floatGap">;
111
+ readonly 'form-field': Set<"type" | "fontSize" | "spaceAfter" | "spaceBefore" | "keepTogether" | "height" | "borderColor" | "width" | "backgroundColor" | "fieldType" | "name" | "label" | "accessibilityLabel" | "placeholder" | "defaultValue" | "multiline" | "maxLength" | "checked" | "options" | "defaultSelected">;
104
112
  };
113
+ /**
114
+ * Per-variant allowed-property sets for form-field strict validation.
115
+ * Typed as a closed record over the exact fieldType literals so TypeScript
116
+ * will fail the build if a new variant is added to FormFieldElement without
117
+ * a corresponding entry here.
118
+ */
119
+ export declare const FORM_FIELD_VARIANT_PROPS: Record<'text' | 'checkbox' | 'radio' | 'dropdown' | 'button', ReadonlySet<string>>;
105
120
  export declare const ALLOWED_PROPS_SUB: {
106
121
  readonly document: Set<"header" | "footer" | "signature" | "pageSize" | "margins" | "defaultFont" | "defaultFontSize" | "defaultLineHeight" | "fonts" | "watermark" | "encryption" | "bookmarks" | "hyphenation" | "metadata" | "defaultParagraphStyle" | "sections" | "content" | "flattenForms" | "onImageLoadError" | "onFormFieldError" | "renderDate" | "allowedFileDirs">;
107
- readonly metadata: Set<"title" | "author" | "subject" | "keywords" | "creator" | "language" | "producer">;
122
+ readonly metadata: Set<"title" | "author" | "subject" | "keywords" | "creator" | "language" | "producer" | "accessibility" | "semantic">;
108
123
  readonly 'column-def': Set<"align" | "width">;
109
124
  readonly 'table-row': Set<"cells" | "isHeader">;
110
125
  readonly 'table-cell': Set<"text" | "dir" | "fontSize" | "fontFamily" | "fontWeight" | "color" | "align" | "bgColor" | "tabularNumbers" | "colspan" | "rowspan">;
@@ -1 +1 @@
1
- {"version":3,"file":"allowed-props.d.ts","sourceRoot":"","sources":["../src/allowed-props.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"allowed-props.d.ts","sourceRoot":"","sources":["../src/allowed-props.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ const DOC_KEYS = [
10
10
  'hyphenation', 'metadata', 'defaultParagraphStyle', 'sections', 'content',
11
11
  'flattenForms', 'onImageLoadError', 'onFormFieldError', 'renderDate', 'allowedFileDirs',
12
12
  ];
13
- const METADATA_KEYS = ['title', 'author', 'subject', 'keywords', 'creator', 'language', 'producer'];
13
+ const METADATA_KEYS = ['title', 'author', 'subject', 'keywords', 'creator', 'language', 'producer', 'accessibility', 'semantic'];
14
14
  const PARAGRAPH_KEYS = [
15
15
  'type', 'text', 'dir', 'fontSize', 'lineHeight', 'fontFamily', 'fontWeight', 'color',
16
16
  'align', 'bgColor', 'spaceAfter', 'spaceBefore', 'keepTogether', 'underline',
@@ -53,7 +53,7 @@ const LIST_KEYS = [
53
53
  'itemSpaceAfter', 'spaceAfter', 'spaceBefore', 'color', 'nestedNumberingStyle',
54
54
  ];
55
55
  const LIST_ITEM_KEYS = ['text', 'dir', 'fontWeight', 'items'];
56
- const HR_KEYS = ['type', 'thickness', 'color', 'spaceAbove', 'spaceBelow', 'spaceBefore', 'spaceAfter'];
56
+ const HR_KEYS = ['type', 'thickness', 'color', 'spaceBefore', 'spaceAfter'];
57
57
  const PAGE_BREAK_KEYS = ['type'];
58
58
  const CODE_KEYS = [
59
59
  'type', 'text', 'dir', 'fontFamily', 'fontSize', 'lineHeight', 'bgColor', 'color',
@@ -79,10 +79,29 @@ const CALLOUT_KEYS = [
79
79
  'spaceAfter', 'spaceBefore', 'keepTogether', 'dir',
80
80
  ];
81
81
  const COMMENT_KEYS = ['type', 'contents', 'author', 'color', 'open', 'spaceAfter'];
82
- const FORM_FIELD_KEYS = [
83
- 'type', 'fieldType', 'name', 'label', 'placeholder', 'defaultValue', 'multiline',
84
- 'maxLength', 'checked', 'options', 'defaultSelected', 'width', 'height', 'fontSize',
82
+ const FORM_FIELD_BASE_KEYS = [
83
+ 'type', 'fieldType', 'name', 'label', 'width', 'height', 'fontSize',
85
84
  'borderColor', 'backgroundColor', 'spaceAfter', 'spaceBefore', 'keepTogether',
85
+ 'accessibilityLabel',
86
+ ];
87
+ const TEXT_FORM_FIELD_KEYS = [...FORM_FIELD_BASE_KEYS, 'placeholder', 'defaultValue', 'multiline', 'maxLength'];
88
+ const CHECKBOX_FORM_FIELD_KEYS = [...FORM_FIELD_BASE_KEYS, 'checked'];
89
+ const RADIO_FORM_FIELD_KEYS = [...FORM_FIELD_BASE_KEYS, 'options', 'defaultSelected'];
90
+ const DROPDOWN_FORM_FIELD_KEYS = [...FORM_FIELD_BASE_KEYS, 'options', 'defaultSelected'];
91
+ const BUTTON_FORM_FIELD_KEYS = [...FORM_FIELD_BASE_KEYS];
92
+ /**
93
+ * Union of every field that can appear on *any* FormFieldElement variant.
94
+ * Used for the top-level ALLOWED_PROPS dispatch so the generic strict check in
95
+ * validate/index.ts never false-flags a valid variant-specific key (e.g.
96
+ * `placeholder` on a text field). The per-variant check inside
97
+ * validateFormField then narrows further and rejects cross-variant
98
+ * contamination (e.g. `checked` on a text field).
99
+ */
100
+ const FORM_FIELD_ALL_KEYS = [
101
+ ...FORM_FIELD_BASE_KEYS,
102
+ 'placeholder', 'defaultValue', 'multiline', 'maxLength', // text only
103
+ 'checked', // checkbox only
104
+ 'options', 'defaultSelected', // radio + dropdown
86
105
  ];
87
106
  const FOOTNOTE_DEF_KEYS = ['type', 'id', 'text', 'fontSize', 'fontFamily', 'spaceAfter'];
88
107
  const TOC_KEYS = [
@@ -113,10 +132,23 @@ export const ALLOWED_PROPS = {
113
132
  'toc': new Set(TOC_KEYS),
114
133
  'toc-entry': new Set(TOC_ENTRY_KEYS),
115
134
  'comment': new Set(COMMENT_KEYS),
116
- 'form-field': new Set(FORM_FIELD_KEYS),
117
135
  'callout': new Set(CALLOUT_KEYS),
118
136
  'footnote-def': new Set(FOOTNOTE_DEF_KEYS),
119
137
  'float-group': new Set(FLOAT_GROUP_KEYS),
138
+ 'form-field': new Set(FORM_FIELD_ALL_KEYS),
139
+ };
140
+ /**
141
+ * Per-variant allowed-property sets for form-field strict validation.
142
+ * Typed as a closed record over the exact fieldType literals so TypeScript
143
+ * will fail the build if a new variant is added to FormFieldElement without
144
+ * a corresponding entry here.
145
+ */
146
+ export const FORM_FIELD_VARIANT_PROPS = {
147
+ text: new Set(TEXT_FORM_FIELD_KEYS),
148
+ checkbox: new Set(CHECKBOX_FORM_FIELD_KEYS),
149
+ radio: new Set(RADIO_FORM_FIELD_KEYS),
150
+ dropdown: new Set(DROPDOWN_FORM_FIELD_KEYS),
151
+ button: new Set(BUTTON_FORM_FIELD_KEYS),
120
152
  };
121
153
  export const ALLOWED_PROPS_SUB = {
122
154
  'document': new Set(DOC_KEYS),