docxmlater 10.1.3 → 10.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +759 -754
  2. package/dist/constants/legacyCompatFlags.js +1 -1
  3. package/dist/constants/legacyCompatFlags.js.map +1 -1
  4. package/dist/constants/limits.js.map +1 -1
  5. package/dist/core/Document.d.ts +50 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +483 -471
  8. package/dist/core/Document.js.map +1 -1
  9. package/dist/core/DocumentContent.d.ts +9 -9
  10. package/dist/core/DocumentContent.d.ts.map +1 -1
  11. package/dist/core/DocumentContent.js +1 -1
  12. package/dist/core/DocumentContent.js.map +1 -1
  13. package/dist/core/DocumentGenerator.d.ts +11 -11
  14. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  15. package/dist/core/DocumentGenerator.js +251 -251
  16. package/dist/core/DocumentGenerator.js.map +1 -1
  17. package/dist/core/DocumentIdManager.js.map +1 -1
  18. package/dist/core/DocumentParser.d.ts +15 -15
  19. package/dist/core/DocumentParser.d.ts.map +1 -1
  20. package/dist/core/DocumentParser.js +2123 -2155
  21. package/dist/core/DocumentParser.js.map +1 -1
  22. package/dist/core/DocumentValidator.d.ts.map +1 -1
  23. package/dist/core/DocumentValidator.js +2 -5
  24. package/dist/core/DocumentValidator.js.map +1 -1
  25. package/dist/core/Relationship.js.map +1 -1
  26. package/dist/core/RelationshipManager.d.ts.map +1 -1
  27. package/dist/core/RelationshipManager.js +3 -3
  28. package/dist/core/RelationshipManager.js.map +1 -1
  29. package/dist/elements/AlternateContent.js.map +1 -1
  30. package/dist/elements/Bookmark.d.ts.map +1 -1
  31. package/dist/elements/Bookmark.js +3 -1
  32. package/dist/elements/Bookmark.js.map +1 -1
  33. package/dist/elements/BookmarkManager.d.ts.map +1 -1
  34. package/dist/elements/BookmarkManager.js.map +1 -1
  35. package/dist/elements/Comment.d.ts.map +1 -1
  36. package/dist/elements/Comment.js +9 -6
  37. package/dist/elements/Comment.js.map +1 -1
  38. package/dist/elements/CommentManager.d.ts.map +1 -1
  39. package/dist/elements/CommentManager.js +18 -17
  40. package/dist/elements/CommentManager.js.map +1 -1
  41. package/dist/elements/CommonTypes.d.ts +21 -21
  42. package/dist/elements/CommonTypes.d.ts.map +1 -1
  43. package/dist/elements/CommonTypes.js +56 -56
  44. package/dist/elements/CommonTypes.js.map +1 -1
  45. package/dist/elements/CustomXml.js.map +1 -1
  46. package/dist/elements/Endnote.d.ts.map +1 -1
  47. package/dist/elements/Endnote.js +6 -6
  48. package/dist/elements/Endnote.js.map +1 -1
  49. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  50. package/dist/elements/EndnoteManager.js +6 -7
  51. package/dist/elements/EndnoteManager.js.map +1 -1
  52. package/dist/elements/Field.d.ts.map +1 -1
  53. package/dist/elements/Field.js +82 -25
  54. package/dist/elements/Field.js.map +1 -1
  55. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  56. package/dist/elements/FieldHelpers.js.map +1 -1
  57. package/dist/elements/FontManager.d.ts.map +1 -1
  58. package/dist/elements/FontManager.js +1 -1
  59. package/dist/elements/FontManager.js.map +1 -1
  60. package/dist/elements/Footer.js +2 -2
  61. package/dist/elements/Footer.js.map +1 -1
  62. package/dist/elements/Footnote.d.ts.map +1 -1
  63. package/dist/elements/Footnote.js +6 -6
  64. package/dist/elements/Footnote.js.map +1 -1
  65. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  66. package/dist/elements/FootnoteManager.js +6 -7
  67. package/dist/elements/FootnoteManager.js.map +1 -1
  68. package/dist/elements/Header.js +2 -2
  69. package/dist/elements/Header.js.map +1 -1
  70. package/dist/elements/HeaderFooterManager.js.map +1 -1
  71. package/dist/elements/Hyperlink.d.ts +5 -3
  72. package/dist/elements/Hyperlink.d.ts.map +1 -1
  73. package/dist/elements/Hyperlink.js +134 -76
  74. package/dist/elements/Hyperlink.js.map +1 -1
  75. package/dist/elements/Image.d.ts.map +1 -1
  76. package/dist/elements/Image.js +238 -106
  77. package/dist/elements/Image.js.map +1 -1
  78. package/dist/elements/ImageManager.d.ts.map +1 -1
  79. package/dist/elements/ImageManager.js +1 -1
  80. package/dist/elements/ImageManager.js.map +1 -1
  81. package/dist/elements/ImageRun.js +1 -1
  82. package/dist/elements/ImageRun.js.map +1 -1
  83. package/dist/elements/MathElement.js.map +1 -1
  84. package/dist/elements/Paragraph.d.ts +24 -24
  85. package/dist/elements/Paragraph.d.ts.map +1 -1
  86. package/dist/elements/Paragraph.js +181 -188
  87. package/dist/elements/Paragraph.js.map +1 -1
  88. package/dist/elements/PreservedElement.js.map +1 -1
  89. package/dist/elements/PropertyChangeTypes.d.ts.map +1 -1
  90. package/dist/elements/PropertyChangeTypes.js +6 -6
  91. package/dist/elements/PropertyChangeTypes.js.map +1 -1
  92. package/dist/elements/RangeMarker.d.ts.map +1 -1
  93. package/dist/elements/RangeMarker.js.map +1 -1
  94. package/dist/elements/Revision.d.ts.map +1 -1
  95. package/dist/elements/Revision.js +4 -5
  96. package/dist/elements/Revision.js.map +1 -1
  97. package/dist/elements/RevisionContent.js.map +1 -1
  98. package/dist/elements/RevisionManager.d.ts.map +1 -1
  99. package/dist/elements/RevisionManager.js +40 -48
  100. package/dist/elements/RevisionManager.js.map +1 -1
  101. package/dist/elements/Run.d.ts +16 -16
  102. package/dist/elements/Run.d.ts.map +1 -1
  103. package/dist/elements/Run.js +256 -238
  104. package/dist/elements/Run.js.map +1 -1
  105. package/dist/elements/Section.d.ts.map +1 -1
  106. package/dist/elements/Section.js +36 -11
  107. package/dist/elements/Section.js.map +1 -1
  108. package/dist/elements/Shape.d.ts.map +1 -1
  109. package/dist/elements/Shape.js.map +1 -1
  110. package/dist/elements/StructuredDocumentTag.d.ts +6 -6
  111. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  112. package/dist/elements/StructuredDocumentTag.js +99 -104
  113. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  114. package/dist/elements/Table.d.ts +11 -11
  115. package/dist/elements/Table.d.ts.map +1 -1
  116. package/dist/elements/Table.js +102 -107
  117. package/dist/elements/Table.js.map +1 -1
  118. package/dist/elements/TableCell.d.ts +10 -10
  119. package/dist/elements/TableCell.d.ts.map +1 -1
  120. package/dist/elements/TableCell.js +105 -106
  121. package/dist/elements/TableCell.js.map +1 -1
  122. package/dist/elements/TableGridChange.d.ts.map +1 -1
  123. package/dist/elements/TableGridChange.js.map +1 -1
  124. package/dist/elements/TableOfContents.d.ts.map +1 -1
  125. package/dist/elements/TableOfContents.js +4 -4
  126. package/dist/elements/TableOfContents.js.map +1 -1
  127. package/dist/elements/TableOfContentsElement.js.map +1 -1
  128. package/dist/elements/TableRow.d.ts.map +1 -1
  129. package/dist/elements/TableRow.js +13 -6
  130. package/dist/elements/TableRow.js.map +1 -1
  131. package/dist/elements/TextBox.d.ts.map +1 -1
  132. package/dist/elements/TextBox.js +3 -5
  133. package/dist/elements/TextBox.js.map +1 -1
  134. package/dist/formatting/AbstractNumbering.d.ts +4 -4
  135. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  136. package/dist/formatting/AbstractNumbering.js +54 -49
  137. package/dist/formatting/AbstractNumbering.js.map +1 -1
  138. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  139. package/dist/formatting/NumberingInstance.js +1 -3
  140. package/dist/formatting/NumberingInstance.js.map +1 -1
  141. package/dist/formatting/NumberingLevel.d.ts +5 -5
  142. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  143. package/dist/formatting/NumberingLevel.js +119 -125
  144. package/dist/formatting/NumberingLevel.js.map +1 -1
  145. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  146. package/dist/formatting/NumberingManager.js +9 -9
  147. package/dist/formatting/NumberingManager.js.map +1 -1
  148. package/dist/formatting/Style.d.ts +11 -11
  149. package/dist/formatting/Style.d.ts.map +1 -1
  150. package/dist/formatting/Style.js +219 -247
  151. package/dist/formatting/Style.js.map +1 -1
  152. package/dist/formatting/StylesManager.d.ts +2 -2
  153. package/dist/formatting/StylesManager.d.ts.map +1 -1
  154. package/dist/formatting/StylesManager.js +96 -102
  155. package/dist/formatting/StylesManager.js.map +1 -1
  156. package/dist/helpers/CleanupHelper.d.ts +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  158. package/dist/helpers/CleanupHelper.js +6 -6
  159. package/dist/helpers/CleanupHelper.js.map +1 -1
  160. package/dist/images/ImageOptimizer.js +7 -7
  161. package/dist/images/ImageOptimizer.js.map +1 -1
  162. package/dist/index.d.ts +9 -9
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js.map +1 -1
  165. package/dist/managers/DrawingManager.js.map +1 -1
  166. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.js +23 -7
  168. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  169. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  170. package/dist/tracking/TrackingContext.js.map +1 -1
  171. package/dist/types/compatibility-types.js.map +1 -1
  172. package/dist/types/formatting.js.map +1 -1
  173. package/dist/types/list-types.d.ts +6 -6
  174. package/dist/types/list-types.js.map +1 -1
  175. package/dist/types/settings-types.js.map +1 -1
  176. package/dist/types/styleConfig.d.ts +2 -2
  177. package/dist/types/styleConfig.js.map +1 -1
  178. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  179. package/dist/utils/ChangelogGenerator.js +97 -101
  180. package/dist/utils/ChangelogGenerator.js.map +1 -1
  181. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.js +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  184. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  186. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  187. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  188. package/dist/utils/MoveOperationHelper.js +1 -1
  189. package/dist/utils/MoveOperationHelper.js.map +1 -1
  190. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.js +2 -4
  192. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  193. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  194. package/dist/utils/RevisionWalker.js +4 -12
  195. package/dist/utils/RevisionWalker.js.map +1 -1
  196. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  198. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  199. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  200. package/dist/utils/ShadingResolver.js +1 -1
  201. package/dist/utils/ShadingResolver.js.map +1 -1
  202. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  203. package/dist/utils/acceptRevisions.js +23 -12
  204. package/dist/utils/acceptRevisions.js.map +1 -1
  205. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  207. package/dist/utils/cnfStyleDecoder.js +40 -40
  208. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  209. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  210. package/dist/utils/corruptionDetection.js.map +1 -1
  211. package/dist/utils/dateFormatting.js.map +1 -1
  212. package/dist/utils/deepClone.js +1 -1
  213. package/dist/utils/deepClone.js.map +1 -1
  214. package/dist/utils/diagnostics.d.ts.map +1 -1
  215. package/dist/utils/diagnostics.js +1 -1
  216. package/dist/utils/diagnostics.js.map +1 -1
  217. package/dist/utils/errorHandling.js.map +1 -1
  218. package/dist/utils/formatting.d.ts.map +1 -1
  219. package/dist/utils/formatting.js +10 -2
  220. package/dist/utils/formatting.js.map +1 -1
  221. package/dist/utils/list-detection.d.ts +2 -2
  222. package/dist/utils/list-detection.d.ts.map +1 -1
  223. package/dist/utils/list-detection.js +21 -23
  224. package/dist/utils/list-detection.js.map +1 -1
  225. package/dist/utils/logger.d.ts.map +1 -1
  226. package/dist/utils/logger.js +12 -7
  227. package/dist/utils/logger.js.map +1 -1
  228. package/dist/utils/parsingHelpers.js.map +1 -1
  229. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  230. package/dist/utils/stripTrackedChanges.js +3 -3
  231. package/dist/utils/stripTrackedChanges.js.map +1 -1
  232. package/dist/utils/textDiff.d.ts +1 -1
  233. package/dist/utils/textDiff.js +8 -8
  234. package/dist/utils/textDiff.js.map +1 -1
  235. package/dist/utils/units.js.map +1 -1
  236. package/dist/utils/validation.d.ts.map +1 -1
  237. package/dist/utils/validation.js +24 -7
  238. package/dist/utils/validation.js.map +1 -1
  239. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  240. package/dist/utils/xmlSanitization.js +3 -3
  241. package/dist/utils/xmlSanitization.js.map +1 -1
  242. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.js +5 -5
  244. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  245. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  246. package/dist/validation/RevisionValidator.js +7 -9
  247. package/dist/validation/RevisionValidator.js.map +1 -1
  248. package/dist/validation/ValidationRules.js +3 -3
  249. package/dist/validation/ValidationRules.js.map +1 -1
  250. package/dist/validation/index.js.map +1 -1
  251. package/dist/xml/XMLBuilder.d.ts +1 -1
  252. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  253. package/dist/xml/XMLBuilder.js +98 -100
  254. package/dist/xml/XMLBuilder.js.map +1 -1
  255. package/dist/xml/XMLParser.d.ts.map +1 -1
  256. package/dist/xml/XMLParser.js +61 -66
  257. package/dist/xml/XMLParser.js.map +1 -1
  258. package/dist/zip/ZipHandler.d.ts.map +1 -1
  259. package/dist/zip/ZipHandler.js.map +1 -1
  260. package/dist/zip/ZipReader.d.ts.map +1 -1
  261. package/dist/zip/ZipReader.js +1 -3
  262. package/dist/zip/ZipReader.js.map +1 -1
  263. package/dist/zip/ZipWriter.d.ts +1 -1
  264. package/dist/zip/ZipWriter.d.ts.map +1 -1
  265. package/dist/zip/ZipWriter.js +28 -36
  266. package/dist/zip/ZipWriter.js.map +1 -1
  267. package/dist/zip/types.js +1 -1
  268. package/dist/zip/types.js.map +1 -1
  269. package/package.json +92 -92
  270. package/src/__tests__/helper-methods.test.ts +512 -512
  271. package/src/constants/legacyCompatFlags.ts +138 -138
  272. package/src/constants/limits.ts +50 -50
  273. package/src/core/Document.ts +985 -1145
  274. package/src/core/DocumentContent.ts +461 -467
  275. package/src/core/DocumentGenerator.ts +1133 -1104
  276. package/src/core/DocumentIdManager.ts +158 -158
  277. package/src/core/DocumentParser.ts +2347 -2716
  278. package/src/core/DocumentValidator.ts +363 -372
  279. package/src/core/Relationship.ts +367 -367
  280. package/src/core/RelationshipManager.ts +429 -428
  281. package/src/elements/AlternateContent.ts +42 -42
  282. package/src/elements/Bookmark.ts +212 -210
  283. package/src/elements/BookmarkManager.ts +247 -250
  284. package/src/elements/Comment.ts +356 -359
  285. package/src/elements/CommentManager.ts +499 -502
  286. package/src/elements/CommonTypes.ts +524 -549
  287. package/src/elements/CustomXml.ts +36 -36
  288. package/src/elements/Endnote.ts +221 -217
  289. package/src/elements/EndnoteManager.ts +246 -249
  290. package/src/elements/Field.ts +1292 -1233
  291. package/src/elements/FieldHelpers.ts +329 -333
  292. package/src/elements/FontManager.ts +336 -339
  293. package/src/elements/Footer.ts +269 -269
  294. package/src/elements/Footnote.ts +221 -217
  295. package/src/elements/FootnoteManager.ts +246 -249
  296. package/src/elements/Header.ts +269 -269
  297. package/src/elements/HeaderFooterManager.ts +219 -219
  298. package/src/elements/Hyperlink.ts +1288 -1193
  299. package/src/elements/Image.ts +1982 -1756
  300. package/src/elements/ImageManager.ts +437 -432
  301. package/src/elements/ImageRun.ts +59 -59
  302. package/src/elements/MathElement.ts +65 -65
  303. package/src/elements/Paragraph.ts +4347 -4287
  304. package/src/elements/PreservedElement.ts +53 -53
  305. package/src/elements/PropertyChangeTypes.ts +458 -442
  306. package/src/elements/RangeMarker.ts +382 -400
  307. package/src/elements/Revision.ts +1198 -1217
  308. package/src/elements/RevisionContent.ts +73 -73
  309. package/src/elements/RevisionManager.ts +1070 -1070
  310. package/src/elements/Run.ts +3103 -3073
  311. package/src/elements/Section.ts +1521 -1421
  312. package/src/elements/Shape.ts +884 -873
  313. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  314. package/src/elements/Table.ts +2468 -2524
  315. package/src/elements/TableCell.ts +1617 -1621
  316. package/src/elements/TableGridChange.ts +149 -151
  317. package/src/elements/TableOfContents.ts +701 -691
  318. package/src/elements/TableOfContentsElement.ts +89 -89
  319. package/src/elements/TableRow.ts +960 -929
  320. package/src/elements/TextBox.ts +766 -768
  321. package/src/formatting/AbstractNumbering.ts +580 -579
  322. package/src/formatting/NumberingInstance.ts +295 -299
  323. package/src/formatting/NumberingLevel.ts +981 -1040
  324. package/src/formatting/NumberingManager.ts +833 -827
  325. package/src/formatting/Style.ts +1785 -1879
  326. package/src/formatting/StylesManager.ts +1090 -1130
  327. package/src/helpers/CleanupHelper.ts +524 -524
  328. package/src/images/ImageOptimizer.ts +274 -274
  329. package/src/index.ts +559 -554
  330. package/src/managers/DrawingManager.ts +319 -319
  331. package/src/tracking/DocumentTrackingContext.ts +687 -674
  332. package/src/tracking/TrackingContext.ts +175 -173
  333. package/src/types/compatibility-types.ts +49 -49
  334. package/src/types/formatting.ts +210 -210
  335. package/src/types/list-types.ts +14 -14
  336. package/src/types/settings-types.ts +59 -59
  337. package/src/types/styleConfig.ts +189 -189
  338. package/src/utils/ChangelogGenerator.ts +1583 -1581
  339. package/src/utils/CompatibilityUpgrader.ts +235 -237
  340. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  341. package/src/utils/MoveOperationHelper.ts +233 -238
  342. package/src/utils/RevisionAwareProcessor.ts +518 -526
  343. package/src/utils/RevisionWalker.ts +427 -457
  344. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  345. package/src/utils/ShadingResolver.ts +105 -107
  346. package/src/utils/acceptRevisions.ts +723 -714
  347. package/src/utils/cnfStyleDecoder.ts +212 -217
  348. package/src/utils/corruptionDetection.ts +346 -345
  349. package/src/utils/dateFormatting.ts +20 -20
  350. package/src/utils/deepClone.ts +77 -78
  351. package/src/utils/diagnostics.ts +125 -129
  352. package/src/utils/errorHandling.ts +80 -80
  353. package/src/utils/formatting.ts +220 -213
  354. package/src/utils/list-detection.ts +32 -42
  355. package/src/utils/logger.ts +412 -404
  356. package/src/utils/parsingHelpers.ts +190 -190
  357. package/src/utils/stripTrackedChanges.ts +356 -353
  358. package/src/utils/textDiff.ts +100 -100
  359. package/src/utils/units.ts +421 -421
  360. package/src/utils/validation.ts +553 -542
  361. package/src/utils/xmlSanitization.ts +179 -182
  362. package/src/validation/RevisionAutoFixer.ts +541 -542
  363. package/src/validation/RevisionValidator.ts +470 -460
  364. package/src/validation/ValidationRules.ts +338 -338
  365. package/src/validation/index.ts +30 -30
  366. package/src/xml/XMLBuilder.ts +857 -871
  367. package/src/xml/XMLParser.ts +877 -919
  368. package/src/zip/ZipHandler.ts +629 -637
  369. package/src/zip/ZipReader.ts +295 -299
  370. package/src/zip/ZipWriter.ts +374 -390
  371. package/src/zip/types.ts +116 -116
@@ -3,34 +3,34 @@
3
3
  * Provides a simple interface for creating DOCX files without managing ZIP and XML manually
4
4
  */
5
5
 
6
- import { AlternateContent } from "../elements/AlternateContent";
7
- import { Bookmark } from "../elements/Bookmark";
8
- import { BookmarkManager } from "../elements/BookmarkManager";
9
- import { Comment } from "../elements/Comment";
10
- import { CustomXmlBlock } from "../elements/CustomXml";
11
- import { PreservedElement } from "../elements/PreservedElement";
12
- import { MathParagraph } from "../elements/MathElement";
13
- import { CommentManager } from "../elements/CommentManager";
14
- import { Endnote } from "../elements/Endnote";
15
- import { EndnoteManager } from "../elements/EndnoteManager";
16
- import { Field } from "../elements/Field";
17
- import { Footnote } from "../elements/Footnote";
18
- import { FootnoteManager } from "../elements/FootnoteManager";
19
- import { Footer } from "../elements/Footer";
20
- import { Header } from "../elements/Header";
21
- import { HeaderFooterManager } from "../elements/HeaderFooterManager";
22
- import { Hyperlink } from "../elements/Hyperlink";
23
- import { Image } from "../elements/Image";
24
- import { ImageManager } from "../elements/ImageManager";
25
- import { ImageRun } from "../elements/ImageRun";
26
- import { Paragraph, ParagraphContent, FieldLike } from "../elements/Paragraph";
27
- import { RangeMarker } from "../elements/RangeMarker";
28
- import { Revision, RevisionType } from "../elements/Revision";
29
- import { RevisionManager } from "../elements/RevisionManager";
30
- import { RevisionLocation } from "../elements/PropertyChangeTypes";
31
- import { Run, RunFormatting } from "../elements/Run";
32
- import { Shape } from "../elements/Shape";
33
- import { TextBox } from "../elements/TextBox";
6
+ import { AlternateContent } from '../elements/AlternateContent';
7
+ import { Bookmark } from '../elements/Bookmark';
8
+ import { BookmarkManager } from '../elements/BookmarkManager';
9
+ import { Comment } from '../elements/Comment';
10
+ import { CustomXmlBlock } from '../elements/CustomXml';
11
+ import { PreservedElement } from '../elements/PreservedElement';
12
+ import { MathParagraph } from '../elements/MathElement';
13
+ import { CommentManager } from '../elements/CommentManager';
14
+ import { Endnote } from '../elements/Endnote';
15
+ import { EndnoteManager } from '../elements/EndnoteManager';
16
+ import { Field } from '../elements/Field';
17
+ import { Footnote } from '../elements/Footnote';
18
+ import { FootnoteManager } from '../elements/FootnoteManager';
19
+ import { Footer } from '../elements/Footer';
20
+ import { Header } from '../elements/Header';
21
+ import { HeaderFooterManager } from '../elements/HeaderFooterManager';
22
+ import { Hyperlink } from '../elements/Hyperlink';
23
+ import { Image } from '../elements/Image';
24
+ import { ImageManager } from '../elements/ImageManager';
25
+ import { ImageRun } from '../elements/ImageRun';
26
+ import { Paragraph, ParagraphContent, FieldLike } from '../elements/Paragraph';
27
+ import { RangeMarker } from '../elements/RangeMarker';
28
+ import { Revision, RevisionType } from '../elements/Revision';
29
+ import { RevisionManager } from '../elements/RevisionManager';
30
+ import { RevisionLocation } from '../elements/PropertyChangeTypes';
31
+ import { Run, RunFormatting } from '../elements/Run';
32
+ import { Shape } from '../elements/Shape';
33
+ import { TextBox } from '../elements/TextBox';
34
34
  import {
35
35
  RevisionValidator,
36
36
  RevisionAutoFixer,
@@ -38,21 +38,25 @@ import {
38
38
  AutoFixOptions,
39
39
  ValidationResult,
40
40
  AutoFixResult,
41
- } from "../validation";
42
- import { Section } from "../elements/Section";
43
- import { StructuredDocumentTag } from "../elements/StructuredDocumentTag";
44
- import { Table, TableBorder } from "../elements/Table";
45
- import { TableCell } from "../elements/TableCell";
46
- import { TableOfContentsElement } from "../elements/TableOfContentsElement";
47
- import { resolveCellShading } from "../utils/ShadingResolver";
48
- import { NumberingManager, NumberingConsolidationOptions, NumberingConsolidationResult } from "../formatting/NumberingManager";
49
- import { Style, StyleProperties } from "../formatting/Style";
50
- import { StylesManager } from "../formatting/StylesManager";
51
- import { FormatOptions, StyleApplyOptions } from "../types/formatting";
52
- import { CompatibilityMode, CompatibilityInfo, CompatSetting } from "../types/compatibility-types";
53
- import { DocumentProtection, RevisionViewSettings, WebSettingsInfo } from "../types/settings-types";
54
- import { CompatibilityUpgrader, UpgradeReport } from "../utils/CompatibilityUpgrader";
55
- import { MODERN_COMPAT_SETTINGS } from "../constants/legacyCompatFlags";
41
+ } from '../validation';
42
+ import { Section } from '../elements/Section';
43
+ import { StructuredDocumentTag } from '../elements/StructuredDocumentTag';
44
+ import { Table, TableBorder } from '../elements/Table';
45
+ import { TableCell } from '../elements/TableCell';
46
+ import { TableOfContentsElement } from '../elements/TableOfContentsElement';
47
+ import { resolveCellShading } from '../utils/ShadingResolver';
48
+ import {
49
+ NumberingManager,
50
+ NumberingConsolidationOptions,
51
+ NumberingConsolidationResult,
52
+ } from '../formatting/NumberingManager';
53
+ import { Style, StyleProperties } from '../formatting/Style';
54
+ import { StylesManager } from '../formatting/StylesManager';
55
+ import { FormatOptions, StyleApplyOptions } from '../types/formatting';
56
+ import { CompatibilityMode, CompatibilityInfo, CompatSetting } from '../types/compatibility-types';
57
+ import { DocumentProtection, RevisionViewSettings, WebSettingsInfo } from '../types/settings-types';
58
+ import { CompatibilityUpgrader, UpgradeReport } from '../utils/CompatibilityUpgrader';
59
+ import { MODERN_COMPAT_SETTINGS } from '../constants/legacyCompatFlags';
56
60
  // ListNormalizationOptions and ListNormalizationReport removed - normalizeTableLists moved to consumer
57
61
  import {
58
62
  ApplyStylesOptions,
@@ -60,10 +64,10 @@ import {
60
64
  StyleConfig,
61
65
  StyleRunFormatting,
62
66
  StyleParagraphFormatting,
63
- } from "../types/styleConfig";
67
+ } from '../types/styleConfig';
64
68
  // ListNormalizer import removed - moved to consumer
65
- import { defaultLogger, ILogger, getGlobalLogger, createScopedLogger } from "../utils/logger";
66
- import { UNITS } from "../utils/units";
69
+ import { defaultLogger, ILogger, getGlobalLogger, createScopedLogger } from '../utils/logger';
70
+ import { UNITS } from '../utils/units';
67
71
 
68
72
  // Create scoped logger for Document operations
69
73
  function getLogger(): ILogger {
@@ -71,25 +75,25 @@ function getLogger(): ILogger {
71
75
  }
72
76
  // Raw XML revision acceptance - used at load time BEFORE parsing
73
77
  // cleanupRevisionMetadata - cleanup metadata files after in-memory acceptance
74
- import { acceptAllRevisions, cleanupRevisionMetadata } from "../utils/acceptRevisions";
78
+ import { acceptAllRevisions, cleanupRevisionMetadata } from '../utils/acceptRevisions';
75
79
  // In-memory revision acceptance - used AFTER parsing, allows subsequent modifications
76
- import { acceptRevisionsInMemory, AcceptRevisionsResult } from "../utils/InMemoryRevisionAcceptor";
77
- import { stripTrackedChanges } from "../utils/stripTrackedChanges";
78
- import { diffText, diffHasUnchangedParts } from "../utils/textDiff";
79
- import { XMLBuilder } from "../xml/XMLBuilder";
80
- import { XMLParser } from "../xml/XMLParser";
81
- import { DocumentTrackingContext } from "../tracking/DocumentTrackingContext";
82
- import type { TrackingContext } from "../tracking/TrackingContext";
83
- import { ZipHandler } from "../zip/ZipHandler";
84
- import { DOCX_PATHS } from "../zip/types";
85
- import { DocumentGenerator } from "./DocumentGenerator";
86
- import { DocumentIdManager } from "./DocumentIdManager";
87
- import { DocumentParser } from "./DocumentParser";
88
- import { DocumentValidator } from "./DocumentValidator";
89
- import { RelationshipManager } from "./RelationshipManager";
90
- import { RelationshipType } from "./Relationship";
91
- import { BodyElement } from "./DocumentContent";
92
- import { optimizeImage, ImageOptimizationResult } from "../images/ImageOptimizer";
80
+ import { acceptRevisionsInMemory, AcceptRevisionsResult } from '../utils/InMemoryRevisionAcceptor';
81
+ import { stripTrackedChanges } from '../utils/stripTrackedChanges';
82
+ import { diffText, diffHasUnchangedParts } from '../utils/textDiff';
83
+ import { XMLBuilder } from '../xml/XMLBuilder';
84
+ import { XMLParser } from '../xml/XMLParser';
85
+ import { DocumentTrackingContext } from '../tracking/DocumentTrackingContext';
86
+ import type { TrackingContext } from '../tracking/TrackingContext';
87
+ import { ZipHandler } from '../zip/ZipHandler';
88
+ import { DOCX_PATHS } from '../zip/types';
89
+ import { DocumentGenerator } from './DocumentGenerator';
90
+ import { DocumentIdManager } from './DocumentIdManager';
91
+ import { DocumentParser } from './DocumentParser';
92
+ import { DocumentValidator } from './DocumentValidator';
93
+ import { RelationshipManager } from './RelationshipManager';
94
+ import { RelationshipType } from './Relationship';
95
+ import { BodyElement } from './DocumentContent';
96
+ import { optimizeImage, ImageOptimizationResult } from '../images/ImageOptimizer';
93
97
 
94
98
  /**
95
99
  * Document properties (core and extended)
@@ -249,7 +253,9 @@ export class Document {
249
253
  this.numberingManager = NumberingManager.create();
250
254
  this.section = new Section();
251
255
  this.imageManager = ImageManager.create();
252
- this.relationshipManager = initDefaults ? RelationshipManager.createForDocument() : RelationshipManager.create();
256
+ this.relationshipManager = initDefaults
257
+ ? RelationshipManager.createForDocument()
258
+ : RelationshipManager.create();
253
259
  this.headerFooterManager = HeaderFooterManager.create();
254
260
  this.bookmarkManager = BookmarkManager.create();
255
261
  this.revisionManager = RevisionManager.create();
@@ -326,7 +332,7 @@ export class Document {
326
332
 
327
333
  // TOC auto-population setting
328
334
  private autoPopulateTOCs = false;
329
-
335
+
330
336
  // TOC field instruction sync setting (default: OFF to preserve original instructions)
331
337
  private autoSyncTOCStyles = false;
332
338
 
@@ -404,7 +410,12 @@ export class Document {
404
410
  private documentProtection?: DocumentProtection;
405
411
 
406
412
  /** Document background (w:background) per ECMA-376 Part 1 §17.2.1 */
407
- private _documentBackground?: { color?: string; themeColor?: string; themeTint?: string; themeShade?: string };
413
+ private _documentBackground?: {
414
+ color?: string;
415
+ themeColor?: string;
416
+ themeTint?: string;
417
+ themeShade?: string;
418
+ };
408
419
 
409
420
  /** Even and odd headers setting (w:evenAndOddHeaders) per ECMA-376 Part 1 §17.15.1.28 */
410
421
  private _evenAndOddHeaders?: boolean;
@@ -579,10 +590,7 @@ export class Document {
579
590
  * }
580
591
  * ```
581
592
  */
582
- static async load(
583
- filePath: string,
584
- options?: DocumentLoadOptions
585
- ): Promise<Document> {
593
+ static async load(filePath: string, options?: DocumentLoadOptions): Promise<Document> {
586
594
  const logger = getLogger();
587
595
  logger.info('Loading document from file', { path: filePath });
588
596
 
@@ -620,10 +628,7 @@ export class Document {
620
628
  * const doc = await Document.loadFromBuffer(buffer);
621
629
  * ```
622
630
  */
623
- static async loadFromBuffer(
624
- buffer: Buffer,
625
- options?: DocumentLoadOptions
626
- ): Promise<Document> {
631
+ static async loadFromBuffer(buffer: Buffer, options?: DocumentLoadOptions): Promise<Document> {
627
632
  const logger = getLogger();
628
633
  logger.info('Loading document from buffer', { bufferSize: buffer.length });
629
634
 
@@ -648,7 +653,7 @@ export class Document {
648
653
  // so they can be accepted using in-memory transformation after parsing
649
654
  const useInMemoryAccept = options?.acceptRevisions === true;
650
655
  const revisionHandling = useInMemoryAccept
651
- ? 'preserve' // Force preserve so revisions are parsed into model
656
+ ? 'preserve' // Force preserve so revisions are parsed into model
652
657
  : (options?.revisionHandling ?? 'accept'); // Default to accept
653
658
 
654
659
  // Handle tracked changes BEFORE parsing (unless using in-memory accept)
@@ -662,8 +667,13 @@ export class Document {
662
667
  // Check if document has tracked changes and warn (unless intentionally accepting later)
663
668
  if (!useInMemoryAccept) {
664
669
  const documentXml = zipHandler.getFileAsString('word/document.xml');
665
- if (documentXml && (documentXml.includes('<w:ins') || documentXml.includes('<w:del') ||
666
- documentXml.includes('<w:moveFrom') || documentXml.includes('<w:moveTo'))) {
670
+ if (
671
+ documentXml &&
672
+ (documentXml.includes('<w:ins') ||
673
+ documentXml.includes('<w:del') ||
674
+ documentXml.includes('<w:moveFrom') ||
675
+ documentXml.includes('<w:moveTo'))
676
+ ) {
667
677
  logger.warn('Document contains tracked changes in preserve mode');
668
678
  }
669
679
  }
@@ -717,7 +727,7 @@ export class Document {
717
727
  if (parseWarnings.length > 0) {
718
728
  logger.warn('Document loaded with parse warnings', {
719
729
  warningCount: parseWarnings.length,
720
- elements: parseWarnings.map(w => w.element).join(', '),
730
+ elements: parseWarnings.map((w) => w.element).join(', '),
721
731
  });
722
732
  }
723
733
 
@@ -815,11 +825,16 @@ export class Document {
815
825
  const hashMatch = /w:hash\s*=\s*"([^"]*)"/.exec(attrs);
816
826
  const saltMatch = /w:salt\s*=\s*"([^"]*)"/.exec(attrs);
817
827
 
818
- if (cryptProviderMatch?.[1]) this.documentProtection.cryptProviderType = cryptProviderMatch[1];
819
- if (cryptAlgClassMatch?.[1]) this.documentProtection.cryptAlgorithmClass = cryptAlgClassMatch[1];
820
- if (cryptAlgTypeMatch?.[1]) this.documentProtection.cryptAlgorithmType = cryptAlgTypeMatch[1];
821
- if (cryptAlgSidMatch?.[1]) this.documentProtection.cryptAlgorithmSid = parseInt(cryptAlgSidMatch[1], 10);
822
- if (cryptSpinMatch?.[1]) this.documentProtection.cryptSpinCount = parseInt(cryptSpinMatch[1], 10);
828
+ if (cryptProviderMatch?.[1])
829
+ this.documentProtection.cryptProviderType = cryptProviderMatch[1];
830
+ if (cryptAlgClassMatch?.[1])
831
+ this.documentProtection.cryptAlgorithmClass = cryptAlgClassMatch[1];
832
+ if (cryptAlgTypeMatch?.[1])
833
+ this.documentProtection.cryptAlgorithmType = cryptAlgTypeMatch[1];
834
+ if (cryptAlgSidMatch?.[1])
835
+ this.documentProtection.cryptAlgorithmSid = parseInt(cryptAlgSidMatch[1], 10);
836
+ if (cryptSpinMatch?.[1])
837
+ this.documentProtection.cryptSpinCount = parseInt(cryptSpinMatch[1], 10);
823
838
  if (hashMatch?.[1]) this.documentProtection.hash = hashMatch[1];
824
839
  if (saltMatch?.[1]) this.documentProtection.salt = saltMatch[1];
825
840
  }
@@ -960,8 +975,10 @@ export class Document {
960
975
  compatSettings.push(setting);
961
976
 
962
977
  // Extract compatibility mode value
963
- if (setting.name === 'compatibilityMode' &&
964
- setting.uri === 'http://schemas.microsoft.com/office/word') {
978
+ if (
979
+ setting.name === 'compatibilityMode' &&
980
+ setting.uri === 'http://schemas.microsoft.com/office/word'
981
+ ) {
965
982
  const modeVal = parseInt(setting.val, 10);
966
983
  if (!isNaN(modeVal)) {
967
984
  mode = modeVal as CompatibilityMode;
@@ -1064,15 +1081,12 @@ export class Document {
1064
1081
  }
1065
1082
  } catch (headerFooterError) {
1066
1083
  const logger = getLogger();
1067
- logger.warn(
1068
- "Failed to parse headers/footers - document will load without them",
1069
- {
1070
- error:
1071
- headerFooterError instanceof Error
1072
- ? headerFooterError.message
1073
- : String(headerFooterError),
1074
- }
1075
- );
1084
+ logger.warn('Failed to parse headers/footers - document will load without them', {
1085
+ error:
1086
+ headerFooterError instanceof Error
1087
+ ? headerFooterError.message
1088
+ : String(headerFooterError),
1089
+ });
1076
1090
  // Continue loading - headers/footers are not critical for document structure
1077
1091
  // User should be aware that headers/footers may be missing
1078
1092
  }
@@ -1125,7 +1139,10 @@ export class Document {
1125
1139
  // Initialize the centralized DocumentIdManager from document XML
1126
1140
  // This scans ALL w:id attributes and sets nextId to globalMax + 1
1127
1141
  // All managers (Bookmark, Revision, Comment) use this shared counter via callbacks
1128
- this.documentIdManager.initializeFromDocument(documentXml || undefined, commentsXml || undefined);
1142
+ this.documentIdManager.initializeFromDocument(
1143
+ documentXml || undefined,
1144
+ commentsXml || undefined
1145
+ );
1129
1146
  }
1130
1147
 
1131
1148
  /**
@@ -1143,7 +1160,11 @@ export class Document {
1143
1160
  this._originalCommentsXml = commentsXml;
1144
1161
 
1145
1162
  // Also preserve companion files from the ZIP
1146
- for (const path of [DOCX_PATHS.COMMENTS_EXTENDED, DOCX_PATHS.COMMENTS_IDS, DOCX_PATHS.COMMENTS_EXTENSIBLE]) {
1163
+ for (const path of [
1164
+ DOCX_PATHS.COMMENTS_EXTENDED,
1165
+ DOCX_PATHS.COMMENTS_IDS,
1166
+ DOCX_PATHS.COMMENTS_EXTENSIBLE,
1167
+ ]) {
1147
1168
  const content = this.zipHandler.getFileAsString(path);
1148
1169
  if (content) {
1149
1170
  this._originalCommentCompanionFiles.set(path, content);
@@ -1267,10 +1288,7 @@ export class Document {
1267
1288
  */
1268
1289
  private initializeRequiredFiles(): void {
1269
1290
  // [Content_Types].xml
1270
- this.zipHandler.addFile(
1271
- DOCX_PATHS.CONTENT_TYPES,
1272
- this.generator.generateContentTypes()
1273
- );
1291
+ this.zipHandler.addFile(DOCX_PATHS.CONTENT_TYPES, this.generator.generateContentTypes());
1274
1292
 
1275
1293
  // _rels/.rels
1276
1294
  this.zipHandler.addFile(DOCX_PATHS.RELS, this.generator.generateRels());
@@ -1287,34 +1305,19 @@ export class Document {
1287
1305
  );
1288
1306
 
1289
1307
  // word/_rels/document.xml.rels
1290
- this.zipHandler.addFile(
1291
- "word/_rels/document.xml.rels",
1292
- this.relationshipManager.generateXml()
1293
- );
1308
+ this.zipHandler.addFile('word/_rels/document.xml.rels', this.relationshipManager.generateXml());
1294
1309
 
1295
1310
  // word/styles.xml
1296
- this.zipHandler.addFile(
1297
- DOCX_PATHS.STYLES,
1298
- this.stylesManager.generateStylesXml()
1299
- );
1311
+ this.zipHandler.addFile(DOCX_PATHS.STYLES, this.stylesManager.generateStylesXml());
1300
1312
 
1301
1313
  // word/numbering.xml
1302
- this.zipHandler.addFile(
1303
- DOCX_PATHS.NUMBERING,
1304
- this.numberingManager.generateNumberingXml()
1305
- );
1314
+ this.zipHandler.addFile(DOCX_PATHS.NUMBERING, this.numberingManager.generateNumberingXml());
1306
1315
 
1307
1316
  // word/fontTable.xml (REQUIRED for DOCX compliance)
1308
- this.zipHandler.addFile(
1309
- "word/fontTable.xml",
1310
- this.generator.generateFontTable()
1311
- );
1317
+ this.zipHandler.addFile('word/fontTable.xml', this.generator.generateFontTable());
1312
1318
 
1313
1319
  // word/webSettings.xml
1314
- this.zipHandler.addFile(
1315
- DOCX_PATHS.WEB_SETTINGS,
1316
- this.generator.generateWebSettings()
1317
- );
1320
+ this.zipHandler.addFile(DOCX_PATHS.WEB_SETTINGS, this.generator.generateWebSettings());
1318
1321
 
1319
1322
  // word/settings.xml (REQUIRED for DOCX compliance)
1320
1323
  this.zipHandler.addFile(
@@ -1335,19 +1338,32 @@ export class Document {
1335
1338
  isLegacyMode: false,
1336
1339
  compatSettings: [
1337
1340
  { name: 'compatibilityMode', uri: 'http://schemas.microsoft.com/office/word', val: '15' },
1338
- { name: 'overrideTableStyleFontSizeAndJustification', uri: 'http://schemas.microsoft.com/office/word', val: '1' },
1339
- { name: 'enableOpenTypeFeatures', uri: 'http://schemas.microsoft.com/office/word', val: '1' },
1340
- { name: 'doNotFlipMirrorIndents', uri: 'http://schemas.microsoft.com/office/word', val: '1' },
1341
- { name: 'differentiateMultirowTableHeaders', uri: 'http://schemas.microsoft.com/office/word', val: '1' },
1341
+ {
1342
+ name: 'overrideTableStyleFontSizeAndJustification',
1343
+ uri: 'http://schemas.microsoft.com/office/word',
1344
+ val: '1',
1345
+ },
1346
+ {
1347
+ name: 'enableOpenTypeFeatures',
1348
+ uri: 'http://schemas.microsoft.com/office/word',
1349
+ val: '1',
1350
+ },
1351
+ {
1352
+ name: 'doNotFlipMirrorIndents',
1353
+ uri: 'http://schemas.microsoft.com/office/word',
1354
+ val: '1',
1355
+ },
1356
+ {
1357
+ name: 'differentiateMultirowTableHeaders',
1358
+ uri: 'http://schemas.microsoft.com/office/word',
1359
+ val: '1',
1360
+ },
1342
1361
  ],
1343
1362
  legacyFlags: [],
1344
1363
  };
1345
1364
 
1346
1365
  // word/theme/theme1.xml (REQUIRED for DOCX compliance)
1347
- this.zipHandler.addFile(
1348
- "word/theme/theme1.xml",
1349
- this.generator.generateTheme()
1350
- );
1366
+ this.zipHandler.addFile('word/theme/theme1.xml', this.generator.generateTheme());
1351
1367
 
1352
1368
  // docProps/core.xml
1353
1369
  this.zipHandler.addFile(
@@ -1356,15 +1372,11 @@ export class Document {
1356
1372
  );
1357
1373
 
1358
1374
  // docProps/app.xml
1359
- this.zipHandler.addFile(
1360
- DOCX_PATHS.APP_PROPS,
1361
- this.generator.generateAppProps(this.properties)
1362
- );
1375
+ this.zipHandler.addFile(DOCX_PATHS.APP_PROPS, this.generator.generateAppProps(this.properties));
1363
1376
 
1364
1377
  // Note: docProps/custom.xml is added during save() if custom properties exist
1365
1378
  }
1366
1379
 
1367
-
1368
1380
  /**
1369
1381
  * Adds an existing paragraph to the document body
1370
1382
  *
@@ -1654,9 +1666,7 @@ export class Document {
1654
1666
  * @returns The paragraph at that index, or undefined if out of bounds
1655
1667
  */
1656
1668
  getParagraphAt(index: number): Paragraph | undefined {
1657
- const paragraphs = this.bodyElements.filter(
1658
- (el): el is Paragraph => el instanceof Paragraph
1659
- );
1669
+ const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
1660
1670
  return paragraphs[index];
1661
1671
  }
1662
1672
 
@@ -1666,9 +1676,7 @@ export class Document {
1666
1676
  * @returns The table at that index, or undefined if out of bounds
1667
1677
  */
1668
1678
  getTableAt(index: number): Table | undefined {
1669
- const tables = this.bodyElements.filter(
1670
- (el): el is Table => el instanceof Table
1671
- );
1679
+ const tables = this.bodyElements.filter((el): el is Table => el instanceof Table);
1672
1680
  return tables[index];
1673
1681
  }
1674
1682
 
@@ -1687,9 +1695,7 @@ export class Document {
1687
1695
  * @returns The index of the paragraph, or -1 if not found
1688
1696
  */
1689
1697
  getParagraphIndex(paragraph: Paragraph): number {
1690
- const paragraphs = this.bodyElements.filter(
1691
- (el): el is Paragraph => el instanceof Paragraph
1692
- );
1698
+ const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
1693
1699
  return paragraphs.indexOf(paragraph);
1694
1700
  }
1695
1701
 
@@ -1699,9 +1705,7 @@ export class Document {
1699
1705
  * @returns The index of the table, or -1 if not found
1700
1706
  */
1701
1707
  getTableIndex(table: Table): number {
1702
- const tables = this.bodyElements.filter(
1703
- (el): el is Table => el instanceof Table
1704
- );
1708
+ const tables = this.bodyElements.filter((el): el is Table => el instanceof Table);
1705
1709
  return tables.indexOf(table);
1706
1710
  }
1707
1711
 
@@ -1711,13 +1715,9 @@ export class Document {
1711
1715
  * @returns The next paragraph, or undefined if none exists
1712
1716
  */
1713
1717
  getNextParagraph(paragraph: Paragraph): Paragraph | undefined {
1714
- const paragraphs = this.bodyElements.filter(
1715
- (el): el is Paragraph => el instanceof Paragraph
1716
- );
1718
+ const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
1717
1719
  const index = paragraphs.indexOf(paragraph);
1718
- return index >= 0 && index < paragraphs.length - 1
1719
- ? paragraphs[index + 1]
1720
- : undefined;
1720
+ return index >= 0 && index < paragraphs.length - 1 ? paragraphs[index + 1] : undefined;
1721
1721
  }
1722
1722
 
1723
1723
  /**
@@ -1726,9 +1726,7 @@ export class Document {
1726
1726
  * @returns The previous paragraph, or undefined if none exists
1727
1727
  */
1728
1728
  getPreviousParagraph(paragraph: Paragraph): Paragraph | undefined {
1729
- const paragraphs = this.bodyElements.filter(
1730
- (el): el is Paragraph => el instanceof Paragraph
1731
- );
1729
+ const paragraphs = this.bodyElements.filter((el): el is Paragraph => el instanceof Paragraph);
1732
1730
  const index = paragraphs.indexOf(paragraph);
1733
1731
  return index > 0 ? paragraphs[index - 1] : undefined;
1734
1732
  }
@@ -1872,8 +1870,11 @@ export class Document {
1872
1870
  * @param value - Property value
1873
1871
  * @returns This document for chaining
1874
1872
  */
1875
- setProperty(key: keyof DocumentProperties, value: DocumentProperties[keyof DocumentProperties]): this {
1876
- (this.properties[key]) = value as never;
1873
+ setProperty(
1874
+ key: keyof DocumentProperties,
1875
+ value: DocumentProperties[keyof DocumentProperties]
1876
+ ): this {
1877
+ this.properties[key] = value as never;
1877
1878
  return this;
1878
1879
  }
1879
1880
 
@@ -2011,10 +2012,7 @@ export class Document {
2011
2012
  * @param value - Property value (string, number, boolean, or Date)
2012
2013
  * @returns This document for chaining
2013
2014
  */
2014
- setCustomProperty(
2015
- name: string,
2016
- value: string | number | boolean | Date
2017
- ): this {
2015
+ setCustomProperty(name: string, value: string | number | boolean | Date): this {
2018
2016
  if (!this.properties.customProperties) {
2019
2017
  this.properties.customProperties = {};
2020
2018
  }
@@ -2027,9 +2025,7 @@ export class Document {
2027
2025
  * @param properties - Object containing custom properties
2028
2026
  * @returns This document for chaining
2029
2027
  */
2030
- setCustomProperties(
2031
- properties: Record<string, string | number | boolean | Date>
2032
- ): this {
2028
+ setCustomProperties(properties: Record<string, string | number | boolean | Date>): this {
2033
2029
  this.properties.customProperties = { ...properties };
2034
2030
  return this;
2035
2031
  }
@@ -2039,9 +2035,7 @@ export class Document {
2039
2035
  * @param name - Property name
2040
2036
  * @returns Property value or undefined
2041
2037
  */
2042
- getCustomProperty(
2043
- name: string
2044
- ): string | number | boolean | Date | undefined {
2038
+ getCustomProperty(name: string): string | number | boolean | Date | undefined {
2045
2039
  return this.properties.customProperties?.[name];
2046
2040
  }
2047
2041
 
@@ -2120,10 +2114,7 @@ export class Document {
2120
2114
  this.validator.checkMemoryThreshold();
2121
2115
 
2122
2116
  // Check document size and warn if too large
2123
- const sizeInfo = this.validator.estimateSize(
2124
- this.bodyElements,
2125
- this.imageManager
2126
- );
2117
+ const sizeInfo = this.validator.estimateSize(this.bodyElements, this.imageManager);
2127
2118
  if (sizeInfo.warning) {
2128
2119
  this.logger.warn(sizeInfo.warning, {
2129
2120
  totalMB: sizeInfo.totalEstimatedMB,
@@ -2174,7 +2165,7 @@ export class Document {
2174
2165
  if (this.skipDocumentXmlRegeneration) {
2175
2166
  this.logger.warn(
2176
2167
  'skipDocumentXmlRegeneration is set: in-memory content modifications will NOT be saved. ' +
2177
- 'Use acceptAllRevisions() instead of acceptAllRevisionsRawXml() if you need to modify the document after accepting revisions.'
2168
+ 'Use acceptAllRevisions() instead of acceptAllRevisionsRawXml() if you need to modify the document after accepting revisions.'
2178
2169
  );
2179
2170
  } else {
2180
2171
  this.updateDocumentXml();
@@ -2229,7 +2220,7 @@ export class Document {
2229
2220
  }
2230
2221
 
2231
2222
  // Atomic rename - only if save succeeded
2232
- const { promises: fs } = await import("fs");
2223
+ const { promises: fs } = await import('fs');
2233
2224
  await fs.rename(tempPath, filePath);
2234
2225
 
2235
2226
  // Mark save as successful - image data can now be released safely
@@ -2242,7 +2233,7 @@ export class Document {
2242
2233
 
2243
2234
  // Cleanup temporary file on error
2244
2235
  try {
2245
- const { promises: fs } = await import("fs");
2236
+ const { promises: fs } = await import('fs');
2246
2237
  await fs.unlink(tempPath);
2247
2238
  } catch (cleanupErr) {
2248
2239
  logger.debug('Failed to clean up temp file', { tempPath, error: String(cleanupErr) });
@@ -2300,11 +2291,11 @@ export class Document {
2300
2291
 
2301
2292
  // Auto-populate TOCs if enabled
2302
2293
  if (this.autoPopulateTOCs) {
2303
- const docXml = this.zipHandler.getFileAsString("word/document.xml");
2294
+ const docXml = this.zipHandler.getFileAsString('word/document.xml');
2304
2295
  if (docXml) {
2305
2296
  const populatedXml = this.populateAllTOCsInXML(docXml);
2306
2297
  if (populatedXml !== docXml) {
2307
- this.zipHandler.updateFile("word/document.xml", populatedXml);
2298
+ this.zipHandler.updateFile('word/document.xml', populatedXml);
2308
2299
  }
2309
2300
  }
2310
2301
  }
@@ -2374,7 +2365,11 @@ export class Document {
2374
2365
  * @private
2375
2366
  */
2376
2367
  private _postProcessDocumentXml(): void {
2377
- if (!this._flattenIncludePictureFields && !this._stripOrphanRSIDs && !this._clearDirectSpacingStyles) {
2368
+ if (
2369
+ !this._flattenIncludePictureFields &&
2370
+ !this._stripOrphanRSIDs &&
2371
+ !this._clearDirectSpacingStyles
2372
+ ) {
2378
2373
  return;
2379
2374
  }
2380
2375
 
@@ -2453,7 +2448,9 @@ export class Document {
2453
2448
  const runContent = xml.substring(runStart, runEnd);
2454
2449
 
2455
2450
  // Check for fldChar
2456
- const fldCharMatch = /<w:fldChar\s+w:fldCharType\s*=\s*"(begin|separate|end)"/.exec(runContent);
2451
+ const fldCharMatch = /<w:fldChar\s+w:fldCharType\s*=\s*"(begin|separate|end)"/.exec(
2452
+ runContent
2453
+ );
2457
2454
  if (fldCharMatch) {
2458
2455
  fieldTokens.push({
2459
2456
  type: fldCharMatch[1] as 'begin' | 'separate' | 'end',
@@ -2498,7 +2495,11 @@ export class Document {
2498
2495
 
2499
2496
  // Collect any instrText runs between this begin and the next fldChar
2500
2497
  const nextFldCharIdx = i + 1 < fieldTokens.length ? fieldTokens[i + 1]!.runStart : Infinity;
2501
- while (instrTokenIdx < instrTokens.length && instrTokens[instrTokenIdx]!.runStart < nextFldCharIdx && instrTokens[instrTokenIdx]!.runStart > token.runStart) {
2498
+ while (
2499
+ instrTokenIdx < instrTokens.length &&
2500
+ instrTokens[instrTokenIdx]!.runStart < nextFldCharIdx &&
2501
+ instrTokens[instrTokenIdx]!.runStart > token.runStart
2502
+ ) {
2502
2503
  const instr = instrTokens[instrTokenIdx]!;
2503
2504
  stackTop.instrRuns.push({ start: instr.runStart, end: instr.runEnd });
2504
2505
 
@@ -2516,9 +2517,11 @@ export class Document {
2516
2517
  // Also collect instrText runs between begin and separate that we may have missed
2517
2518
  // (when instrText spans multiple runs between begin and separate)
2518
2519
  const beginToken = fieldTokens[current.tokenIndex]!;
2519
- while (instrTokenIdx < instrTokens.length &&
2520
- instrTokens[instrTokenIdx]!.runStart > beginToken.runStart &&
2521
- instrTokens[instrTokenIdx]!.runStart < token.runStart) {
2520
+ while (
2521
+ instrTokenIdx < instrTokens.length &&
2522
+ instrTokens[instrTokenIdx]!.runStart > beginToken.runStart &&
2523
+ instrTokens[instrTokenIdx]!.runStart < token.runStart
2524
+ ) {
2522
2525
  const instr = instrTokens[instrTokenIdx]!;
2523
2526
  current.instrRuns.push({ start: instr.runStart, end: instr.runEnd });
2524
2527
  if (/^\s*INCLUDEPICTURE\b/i.test(instr.text)) {
@@ -2556,7 +2559,7 @@ export class Document {
2556
2559
  // Deduplicate overlapping ranges
2557
2560
  const uniqueRuns: { start: number; end: number }[] = [];
2558
2561
  for (const run of runsToRemove) {
2559
- const isDuplicate = uniqueRuns.some(u => u.start === run.start && u.end === run.end);
2562
+ const isDuplicate = uniqueRuns.some((u) => u.start === run.start && u.end === run.end);
2560
2563
  if (!isDuplicate) {
2561
2564
  uniqueRuns.push(run);
2562
2565
  }
@@ -2657,20 +2660,14 @@ export class Document {
2657
2660
  const protectedBlocks: string[] = [];
2658
2661
  // Protect pPrChange and rPr blocks separately to prevent cross-tag
2659
2662
  // mismatch (e.g., <w:rPr> matching </w:pPrChange> in the alternation).
2660
- let safeInner = inner.replace(
2661
- /<w:pPrChange\b[\s\S]*?<\/w:pPrChange>/g,
2662
- (block) => {
2663
- protectedBlocks.push(block);
2664
- return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
2665
- }
2666
- );
2667
- safeInner = safeInner.replace(
2668
- /<w:rPr\b[\s\S]*?<\/w:rPr>/g,
2669
- (block) => {
2670
- protectedBlocks.push(block);
2671
- return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
2672
- }
2673
- );
2663
+ let safeInner = inner.replace(/<w:pPrChange\b[\s\S]*?<\/w:pPrChange>/g, (block) => {
2664
+ protectedBlocks.push(block);
2665
+ return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
2666
+ });
2667
+ safeInner = safeInner.replace(/<w:rPr\b[\s\S]*?<\/w:rPr>/g, (block) => {
2668
+ protectedBlocks.push(block);
2669
+ return `\x00PROTECTED_${protectedBlocks.length - 1}\x00`;
2670
+ });
2674
2671
 
2675
2672
  // Check if this pPr contains a pStyle matching our list
2676
2673
  // (checked against safeInner to avoid matching styles inside pPrChange)
@@ -2756,7 +2753,11 @@ export class Document {
2756
2753
  * Updates the core properties with current values
2757
2754
  */
2758
2755
  private updateCoreProps(): void {
2759
- if (this._removedParts.has(DOCX_PATHS.CORE_PROPS) || this._removedParts.has('docProps/core.xml')) return;
2756
+ if (
2757
+ this._removedParts.has(DOCX_PATHS.CORE_PROPS) ||
2758
+ this._removedParts.has('docProps/core.xml')
2759
+ )
2760
+ return;
2760
2761
  const xml = this.generator.generateCoreProps(this.properties);
2761
2762
  this.zipHandler.updateFile(DOCX_PATHS.CORE_PROPS, xml);
2762
2763
  }
@@ -2766,7 +2767,8 @@ export class Document {
2766
2767
  * Uses preservation strategy to maintain original metadata when unmodified
2767
2768
  */
2768
2769
  private updateAppProps(): void {
2769
- if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml')) return;
2770
+ if (this._removedParts.has(DOCX_PATHS.APP_PROPS) || this._removedParts.has('docProps/app.xml'))
2771
+ return;
2770
2772
  if (this._originalAppPropsXml && !this._appPropsModified) {
2771
2773
  // Preserve original as-is — no changes to app properties
2772
2774
  return;
@@ -2808,7 +2810,10 @@ export class Document {
2808
2810
  if (this.properties.application !== undefined) {
2809
2811
  const escaped = XMLBuilder.sanitizeXmlContent(this.properties.application);
2810
2812
  if (xml.includes('<Application>')) {
2811
- xml = xml.replace(/<Application>[^<]*<\/Application>/, `<Application>${escaped}</Application>`);
2813
+ xml = xml.replace(
2814
+ /<Application>[^<]*<\/Application>/,
2815
+ `<Application>${escaped}</Application>`
2816
+ );
2812
2817
  }
2813
2818
  }
2814
2819
 
@@ -3028,10 +3033,7 @@ export class Document {
3028
3033
  resultXml = resultXml.replace(stylePattern, newStyleXml);
3029
3034
  } else {
3030
3035
  // Style doesn't exist in original - append before </w:styles>
3031
- resultXml = resultXml.replace(
3032
- '</w:styles>',
3033
- `${newStyleXml}\n</w:styles>`
3034
- );
3036
+ resultXml = resultXml.replace('</w:styles>', `${newStyleXml}\n</w:styles>`);
3035
3037
  }
3036
3038
  }
3037
3039
 
@@ -3097,7 +3099,10 @@ export class Document {
3097
3099
  }
3098
3100
 
3099
3101
  for (const entry of entriesToRemove) {
3100
- cleanedRels = cleanedRels.replace(new RegExp(`\\s*${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`), '');
3102
+ cleanedRels = cleanedRels.replace(
3103
+ new RegExp(`\\s*${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`),
3104
+ ''
3105
+ );
3101
3106
  }
3102
3107
 
3103
3108
  if (entriesToRemove.length > 0) {
@@ -3132,8 +3137,12 @@ export class Document {
3132
3137
  const removedNumIds = this.numberingManager.getRemovedNumIds();
3133
3138
 
3134
3139
  // If nothing was modified or removed, return original as-is
3135
- if (modifiedAbstractNumIds.size === 0 && modifiedNumIds.size === 0 &&
3136
- removedAbstractNumIds.size === 0 && removedNumIds.size === 0) {
3140
+ if (
3141
+ modifiedAbstractNumIds.size === 0 &&
3142
+ modifiedNumIds.size === 0 &&
3143
+ removedAbstractNumIds.size === 0 &&
3144
+ removedNumIds.size === 0
3145
+ ) {
3137
3146
  return this._originalNumberingXml;
3138
3147
  }
3139
3148
 
@@ -3445,9 +3454,12 @@ export class Document {
3445
3454
  const esc = XMLBuilder.escapeXmlAttribute;
3446
3455
  let protXml = `\n <w:documentProtection w:edit="${esc(prot.edit)}" w:enforcement="${prot.enforcement ? '1' : '0'}"`;
3447
3456
  if (prot.cryptProviderType) protXml += ` w:cryptProviderType="${esc(prot.cryptProviderType)}"`;
3448
- if (prot.cryptAlgorithmClass) protXml += ` w:cryptAlgorithmClass="${esc(prot.cryptAlgorithmClass)}"`;
3449
- if (prot.cryptAlgorithmType) protXml += ` w:cryptAlgorithmType="${esc(prot.cryptAlgorithmType)}"`;
3450
- if (prot.cryptAlgorithmSid) protXml += ` w:cryptAlgorithmSid="${esc(String(prot.cryptAlgorithmSid))}"`;
3457
+ if (prot.cryptAlgorithmClass)
3458
+ protXml += ` w:cryptAlgorithmClass="${esc(prot.cryptAlgorithmClass)}"`;
3459
+ if (prot.cryptAlgorithmType)
3460
+ protXml += ` w:cryptAlgorithmType="${esc(prot.cryptAlgorithmType)}"`;
3461
+ if (prot.cryptAlgorithmSid)
3462
+ protXml += ` w:cryptAlgorithmSid="${esc(String(prot.cryptAlgorithmSid))}"`;
3451
3463
  if (prot.cryptSpinCount) protXml += ` w:cryptSpinCount="${esc(String(prot.cryptSpinCount))}"`;
3452
3464
  if (prot.hash) protXml += ` w:hash="${esc(prot.hash)}"`;
3453
3465
  if (prot.salt) protXml += ` w:salt="${esc(prot.salt)}"`;
@@ -3561,7 +3573,7 @@ export class Document {
3561
3573
 
3562
3574
  // Insert the block before defaultTabStop (schema order preserved since array is ordered)
3563
3575
  if (preElements.length > 0) {
3564
- const preBlock = preElements.map(e => '\n ' + e).join('');
3576
+ const preBlock = preElements.map((e) => '\n ' + e).join('');
3565
3577
  if (/<w:defaultTabStop\b/.test(xml)) {
3566
3578
  xml = xml.replace(/<w:defaultTabStop\b/, preBlock + '\n <w:defaultTabStop');
3567
3579
  } else {
@@ -3575,9 +3587,15 @@ export class Document {
3575
3587
  if (mod.has('defaultTabStop') && this._defaultTabStop !== undefined) {
3576
3588
  const hasDTS = /<w:defaultTabStop\b[^>]*\/?>/.test(xml);
3577
3589
  if (hasDTS) {
3578
- xml = xml.replace(/<w:defaultTabStop\b[^>]*\/?>/, `<w:defaultTabStop w:val="${this._defaultTabStop}"/>`);
3590
+ xml = xml.replace(
3591
+ /<w:defaultTabStop\b[^>]*\/?>/,
3592
+ `<w:defaultTabStop w:val="${this._defaultTabStop}"/>`
3593
+ );
3579
3594
  } else {
3580
- xml = xml.replace(/<\/w:settings>/, ` <w:defaultTabStop w:val="${this._defaultTabStop}"/>\n</w:settings>`);
3595
+ xml = xml.replace(
3596
+ /<\/w:settings>/,
3597
+ ` <w:defaultTabStop w:val="${this._defaultTabStop}"/>\n</w:settings>`
3598
+ );
3581
3599
  }
3582
3600
  }
3583
3601
 
@@ -3598,9 +3616,12 @@ export class Document {
3598
3616
  }
3599
3617
 
3600
3618
  if (postElements.length > 0) {
3601
- const postBlock = postElements.map(e => '\n ' + e).join('');
3619
+ const postBlock = postElements.map((e) => '\n ' + e).join('');
3602
3620
  if (/<w:characterSpacingControl\b/.test(xml)) {
3603
- xml = xml.replace(/<w:characterSpacingControl\b/, postBlock + '\n <w:characterSpacingControl');
3621
+ xml = xml.replace(
3622
+ /<w:characterSpacingControl\b/,
3623
+ postBlock + '\n <w:characterSpacingControl'
3624
+ );
3604
3625
  } else {
3605
3626
  xml = xml.replace(/<\/w:settings>/, postBlock + '\n</w:settings>');
3606
3627
  }
@@ -3676,7 +3697,11 @@ export class Document {
3676
3697
  * @param col - Column index (0-based)
3677
3698
  * @returns The resolved ShadingConfig, or undefined if no shading applies
3678
3699
  */
3679
- getComputedCellShading(table: Table, row: number, col: number): import("../elements/CommonTypes").ShadingConfig | undefined {
3700
+ getComputedCellShading(
3701
+ table: Table,
3702
+ row: number,
3703
+ col: number
3704
+ ): import('../elements/CommonTypes').ShadingConfig | undefined {
3680
3705
  const tableRow = table.getRow(row);
3681
3706
  if (!tableRow) return undefined;
3682
3707
  const cell = tableRow.getCell(col);
@@ -3852,12 +3877,7 @@ export class Document {
3852
3877
  * console.log(`Updated ${count} elements`);
3853
3878
  * ```
3854
3879
  */
3855
- applyStyleToAll(
3856
- styleId: string,
3857
- predicate: (
3858
- element: BodyElement
3859
- ) => boolean
3860
- ): number {
3880
+ applyStyleToAll(styleId: string, predicate: (element: BodyElement) => boolean): number {
3861
3881
  let count = 0;
3862
3882
 
3863
3883
  for (const element of this.bodyElements) {
@@ -3947,10 +3967,7 @@ export class Document {
3947
3967
  * console.log(`Updated ${count} paragraphs`);
3948
3968
  * ```
3949
3969
  */
3950
- applyStyleToAllParagraphsWithStyle(
3951
- currentStyleId: string,
3952
- newStyleId: string
3953
- ): number {
3970
+ applyStyleToAllParagraphsWithStyle(currentStyleId: string, newStyleId: string): number {
3954
3971
  let count = 0;
3955
3972
 
3956
3973
  // Check body paragraphs
@@ -4104,7 +4121,7 @@ export class Document {
4104
4121
  * console.log(`Updated ${count} tables`);
4105
4122
  * ```
4106
4123
  */
4107
- setAllTablesLayout(layout: "auto" | "fixed"): number {
4124
+ setAllTablesLayout(layout: 'auto' | 'fixed'): number {
4108
4125
  const tables = this.getTables();
4109
4126
 
4110
4127
  for (const table of tables) {
@@ -4211,17 +4228,17 @@ export class Document {
4211
4228
  fixTODHyperlinks(): number {
4212
4229
  console.warn(
4213
4230
  'DEPRECATION WARNING: fixTODHyperlinks() is deprecated. ' +
4214
- 'Use Template_UI WordDocumentProcessor.fixExistingTopHyperlinks() instead.'
4231
+ 'Use Template_UI WordDocumentProcessor.fixExistingTopHyperlinks() instead.'
4215
4232
  );
4216
4233
  let count = 0;
4217
4234
 
4218
4235
  // Ensure _top bookmark exists at document start
4219
- if (!this.hasBookmark("_top")) {
4236
+ if (!this.hasBookmark('_top')) {
4220
4237
  const paragraphs = this.getAllParagraphs();
4221
4238
  if (paragraphs.length > 0) {
4222
4239
  const firstPara = paragraphs[0];
4223
4240
  if (firstPara) {
4224
- const bookmark = new Bookmark({ name: "_top" });
4241
+ const bookmark = new Bookmark({ name: '_top' });
4225
4242
  const registered = this.bookmarkManager.register(bookmark);
4226
4243
  firstPara.addBookmark(registered);
4227
4244
  }
@@ -4235,23 +4252,23 @@ export class Document {
4235
4252
  const text = hyperlink.getText().toLowerCase();
4236
4253
 
4237
4254
  // Match variations: "top of document", "top of the document", etc.
4238
- if (text.includes("top") && text.includes("document")) {
4255
+ if (text.includes('top') && text.includes('document')) {
4239
4256
  // Update text
4240
- hyperlink.setText("Top of the Document");
4257
+ hyperlink.setText('Top of the Document');
4241
4258
 
4242
4259
  // Update formatting
4243
4260
  hyperlink.setFormatting({
4244
- font: "Verdana",
4261
+ font: 'Verdana',
4245
4262
  size: 12,
4246
- underline: "single",
4247
- color: "0000FF",
4263
+ underline: 'single',
4264
+ color: '0000FF',
4248
4265
  });
4249
4266
 
4250
4267
  // Update anchor to _top
4251
- hyperlink.setAnchor("_top");
4268
+ hyperlink.setAnchor('_top');
4252
4269
 
4253
4270
  // Set paragraph alignment to right
4254
- paragraph.setAlignment("right");
4271
+ paragraph.setAlignment('right');
4255
4272
 
4256
4273
  count++;
4257
4274
  }
@@ -4294,7 +4311,7 @@ export class Document {
4294
4311
  const firstCell = cells[0];
4295
4312
  if (firstCell) {
4296
4313
  const text = firstCell.getText().toLowerCase();
4297
- if (text.includes("if")) {
4314
+ if (text.includes('if')) {
4298
4315
  hasIfColumn = true;
4299
4316
  break;
4300
4317
  }
@@ -4382,7 +4399,7 @@ export class Document {
4382
4399
  /** Header row text formatting */
4383
4400
  headerRowFormatting?: {
4384
4401
  bold?: boolean;
4385
- alignment?: "left" | "center" | "right" | "justify";
4402
+ alignment?: 'left' | 'center' | 'right' | 'justify';
4386
4403
  font?: string;
4387
4404
  size?: number;
4388
4405
  color?: string;
@@ -4407,23 +4424,25 @@ export class Document {
4407
4424
  singleCellTablesShaded: number;
4408
4425
  } {
4409
4426
  // Handle different parameter combinations
4410
- let options: {
4411
- autofitToWindow?: boolean;
4412
- singleCellShading?: string;
4413
- headerRowShading?: string;
4414
- headerRowFormatting?: {
4415
- bold?: boolean;
4416
- alignment?: "left" | "center" | "right" | "justify";
4417
- font?: string;
4418
- size?: number;
4419
- color?: string;
4420
- spacingBefore?: number;
4421
- spacingAfter?: number;
4422
- };
4423
- cellMargins?: { top?: number; bottom?: number; left?: number; right?: number };
4424
- skipSingleCellTables?: boolean;
4425
- } | undefined;
4426
- if (typeof colorOrOptions === "string") {
4427
+ let options:
4428
+ | {
4429
+ autofitToWindow?: boolean;
4430
+ singleCellShading?: string;
4431
+ headerRowShading?: string;
4432
+ headerRowFormatting?: {
4433
+ bold?: boolean;
4434
+ alignment?: 'left' | 'center' | 'right' | 'justify';
4435
+ font?: string;
4436
+ size?: number;
4437
+ color?: string;
4438
+ spacingBefore?: number;
4439
+ spacingAfter?: number;
4440
+ };
4441
+ cellMargins?: { top?: number; bottom?: number; left?: number; right?: number };
4442
+ skipSingleCellTables?: boolean;
4443
+ }
4444
+ | undefined;
4445
+ if (typeof colorOrOptions === 'string') {
4427
4446
  if (multiCellColor) {
4428
4447
  // Two colors provided: applyStandardTableFormatting('BFBFBF', 'E9E9E9')
4429
4448
  options = {
@@ -4440,15 +4459,13 @@ export class Document {
4440
4459
 
4441
4460
  // Default values
4442
4461
  const singleCellShading = options?.singleCellShading?.toUpperCase();
4443
- const headerRowShading = (
4444
- options?.headerRowShading || "E9E9E9"
4445
- ).toUpperCase();
4462
+ const headerRowShading = (options?.headerRowShading || 'E9E9E9').toUpperCase();
4446
4463
  const headerRowFormatting = {
4447
4464
  bold: options?.headerRowFormatting?.bold !== false,
4448
- alignment: options?.headerRowFormatting?.alignment || ("center" as const),
4449
- font: options?.headerRowFormatting?.font || "Verdana",
4465
+ alignment: options?.headerRowFormatting?.alignment || ('center' as const),
4466
+ font: options?.headerRowFormatting?.font || 'Verdana',
4450
4467
  size: options?.headerRowFormatting?.size || 12,
4451
- color: options?.headerRowFormatting?.color || "000000",
4468
+ color: options?.headerRowFormatting?.color || '000000',
4452
4469
  spacingBefore: options?.headerRowFormatting?.spacingBefore ?? 60,
4453
4470
  spacingAfter: options?.headerRowFormatting?.spacingAfter ?? 60,
4454
4471
  };
@@ -4458,8 +4475,7 @@ export class Document {
4458
4475
  left: options?.cellMargins?.left ?? 115, // 0.08 inches
4459
4476
  right: options?.cellMargins?.right ?? 115, // 0.08 inches
4460
4477
  };
4461
- const skipSingleCellTables =
4462
- options?.skipSingleCellTables !== false && !singleCellShading;
4478
+ const skipSingleCellTables = options?.skipSingleCellTables !== false && !singleCellShading;
4463
4479
 
4464
4480
  // Statistics
4465
4481
  let tablesProcessed = 0;
@@ -4476,14 +4492,14 @@ export class Document {
4476
4492
 
4477
4493
  // Apply borders to all cells (always applied to all tables)
4478
4494
  table.setAllBorders({
4479
- style: "single",
4495
+ style: 'single',
4480
4496
  size: 4,
4481
- color: "000000",
4497
+ color: '000000',
4482
4498
  });
4483
4499
 
4484
4500
  // Set table width to autofit to window (always applied to all tables)
4485
- table.setLayout("auto");
4486
- table.setWidthType("pct");
4501
+ table.setLayout('auto');
4502
+ table.setWidthType('pct');
4487
4503
  table.setWidth(5000);
4488
4504
 
4489
4505
  // Handle 1x1 (single-cell) tables separately
@@ -4516,10 +4532,7 @@ export class Document {
4516
4532
  for (const para of cell.getParagraphs()) {
4517
4533
  // Skip paragraphs that are part of numbered or bulleted lists
4518
4534
  const numPr = para.getFormatting().numbering;
4519
- if (
4520
- numPr &&
4521
- (numPr.level !== undefined || numPr.numId !== undefined)
4522
- ) {
4535
+ if (numPr && (numPr.level !== undefined || numPr.numId !== undefined)) {
4523
4536
  continue; // Preserve list formatting
4524
4537
  }
4525
4538
 
@@ -4557,17 +4570,16 @@ export class Document {
4557
4570
  const currentPattern = currentShading?.pattern?.toLowerCase();
4558
4571
 
4559
4572
  // Check if color is a valid 6-character hex code (not 'auto' or other special values)
4560
- const isValidHexColor = /^[0-9A-F]{6}$/i.test(currentColor || "");
4561
- const hasHexFillShading =
4562
- currentColor && currentColor !== "FFFFFF" && isValidHexColor;
4573
+ const isValidHexColor = /^[0-9A-F]{6}$/i.test(currentColor || '');
4574
+ const hasHexFillShading = currentColor && currentColor !== 'FFFFFF' && isValidHexColor;
4563
4575
 
4564
4576
  // Check if cell has pattern-based shading (like pct10, pct20, etc.)
4565
4577
  // Patterns like 'clear' or 'nil' don't count as shading
4566
4578
  const hasPatternShading =
4567
4579
  currentPattern &&
4568
- currentPattern !== "clear" &&
4569
- currentPattern !== "nil" &&
4570
- currentPattern !== "auto";
4580
+ currentPattern !== 'clear' &&
4581
+ currentPattern !== 'nil' &&
4582
+ currentPattern !== 'auto';
4571
4583
 
4572
4584
  if (hasHexFillShading || hasPatternShading) {
4573
4585
  // Apply the color passed to the method
@@ -4578,14 +4590,11 @@ export class Document {
4578
4590
  for (const para of cell.getParagraphs()) {
4579
4591
  // Skip paragraphs that are part of numbered or bulleted lists
4580
4592
  const numPr = para.getFormatting().numbering;
4581
- if (
4582
- numPr &&
4583
- (numPr.level !== undefined || numPr.numId !== undefined)
4584
- ) {
4593
+ if (numPr && (numPr.level !== undefined || numPr.numId !== undefined)) {
4585
4594
  continue; // Preserve list formatting
4586
4595
  }
4587
4596
 
4588
- para.setAlignment("center");
4597
+ para.setAlignment('center');
4589
4598
  para.setSpaceBefore(60); // 3pt
4590
4599
  para.setSpaceAfter(60); // 3pt
4591
4600
 
@@ -4595,11 +4604,11 @@ export class Document {
4595
4604
  continue;
4596
4605
  }
4597
4606
  run.setBold(true);
4598
- run.setFont("Verdana", 12);
4607
+ run.setFont('Verdana', 12);
4599
4608
  // Preserve white font - don't change color if run is white (FFFFFF)
4600
4609
  const currentColor = run.getColor()?.toUpperCase();
4601
4610
  if (currentColor !== 'FFFFFF') {
4602
- run.setColor("000000");
4611
+ run.setColor('000000');
4603
4612
  }
4604
4613
  }
4605
4614
  }
@@ -4676,7 +4685,7 @@ export class Document {
4676
4685
  if (relId && largeImageIds.has(relId)) {
4677
4686
  // Remove indentation before centering
4678
4687
  paragraph.formatting.indentation = undefined;
4679
- paragraph.setAlignment("center");
4688
+ paragraph.setAlignment('center');
4680
4689
  count++;
4681
4690
  break; // Only count paragraph once
4682
4691
  }
@@ -4778,7 +4787,7 @@ export class Document {
4778
4787
  if (hasLargeImage) {
4779
4788
  // Remove indentation before centering
4780
4789
  paragraph.formatting.indentation = undefined;
4781
- paragraph.setAlignment("center");
4790
+ paragraph.setAlignment('center');
4782
4791
  count++;
4783
4792
  }
4784
4793
  }
@@ -4805,7 +4814,7 @@ export class Document {
4805
4814
 
4806
4815
  if (numbering) {
4807
4816
  // Has numbering - it's a list item
4808
- paragraph.setLineSpacing(spacingTwips, "auto");
4817
+ paragraph.setLineSpacing(spacingTwips, 'auto');
4809
4818
  count++;
4810
4819
  }
4811
4820
  }
@@ -4834,9 +4843,9 @@ export class Document {
4834
4843
 
4835
4844
  // Create a standard numbered list
4836
4845
  const standardNumId = this.numberingManager.createNumberedList(3, [
4837
- "decimal",
4838
- "lowerLetter",
4839
- "lowerRoman",
4846
+ 'decimal',
4847
+ 'lowerLetter',
4848
+ 'lowerRoman',
4840
4849
  ]);
4841
4850
 
4842
4851
  // Collect all paragraphs with numbering and identify numbered lists
@@ -4851,9 +4860,7 @@ export class Document {
4851
4860
  const instance = this.numberingManager.getInstance(numbering.numId);
4852
4861
  if (!instance) continue;
4853
4862
 
4854
- const abstractNum = this.numberingManager.getAbstractNumbering(
4855
- instance.getAbstractNumId()
4856
- );
4863
+ const abstractNum = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
4857
4864
  if (!abstractNum) continue;
4858
4865
 
4859
4866
  // Check if level 0 is a numbered format (not bullet)
@@ -4862,7 +4869,7 @@ export class Document {
4862
4869
 
4863
4870
  const format = level0.getFormat();
4864
4871
  // Numbered formats: decimal, lowerRoman, upperRoman, lowerLetter, upperLetter, etc.
4865
- if (format !== "bullet") {
4872
+ if (format !== 'bullet') {
4866
4873
  numberedParas.push({ para, level: numbering.level });
4867
4874
  }
4868
4875
  }
@@ -4899,11 +4906,7 @@ export class Document {
4899
4906
  let count = 0;
4900
4907
 
4901
4908
  // Create a standard bullet list with custom bullets
4902
- const standardNumId = this.numberingManager.createBulletList(3, [
4903
- "•",
4904
- "○",
4905
- "■",
4906
- ]);
4909
+ const standardNumId = this.numberingManager.createBulletList(3, ['•', '○', '■']);
4907
4910
 
4908
4911
  // Collect all paragraphs with numbering and identify bullet lists
4909
4912
  const paragraphs = this.getAllParagraphs();
@@ -4917,9 +4920,7 @@ export class Document {
4917
4920
  const instance = this.numberingManager.getInstance(numbering.numId);
4918
4921
  if (!instance) continue;
4919
4922
 
4920
- const abstractNum = this.numberingManager.getAbstractNumbering(
4921
- instance.getAbstractNumId()
4922
- );
4923
+ const abstractNum = this.numberingManager.getAbstractNumbering(instance.getAbstractNumId());
4923
4924
  if (!abstractNum) continue;
4924
4925
 
4925
4926
  // Check if level 0 is a bullet format
@@ -4927,7 +4928,7 @@ export class Document {
4927
4928
  if (!level0) continue;
4928
4929
 
4929
4930
  const format = level0.getFormat();
4930
- if (format === "bullet") {
4931
+ if (format === 'bullet') {
4931
4932
  bulletParas.push({ para, level: numbering.level });
4932
4933
  }
4933
4934
  }
@@ -5015,11 +5016,11 @@ export class Document {
5015
5016
 
5016
5017
  // 3. Scan headers and footers for numId references
5017
5018
  this.collectNumIdsFromElements(
5018
- this.headerFooterManager.getAllHeaders().flatMap(entry => entry.header.getElements()),
5019
+ this.headerFooterManager.getAllHeaders().flatMap((entry) => entry.header.getElements()),
5019
5020
  usedNumIds
5020
5021
  );
5021
5022
  this.collectNumIdsFromElements(
5022
- this.headerFooterManager.getAllFooters().flatMap(entry => entry.footer.getElements()),
5023
+ this.headerFooterManager.getAllFooters().flatMap((entry) => entry.footer.getElements()),
5023
5024
  usedNumIds
5024
5025
  );
5025
5026
 
@@ -5223,7 +5224,9 @@ export class Document {
5223
5224
  // Add synthetic bookmarkEnd to the last paragraph in the document body
5224
5225
  const lastPara = this.getLastParagraph();
5225
5226
  if (lastPara) {
5226
- lastPara.addBookmarkEnd(new Bookmark({ id, name: `_repair_${id}`, skipNormalization: true }));
5227
+ lastPara.addBookmarkEnd(
5228
+ new Bookmark({ id, name: `_repair_${id}`, skipNormalization: true })
5229
+ );
5227
5230
  this.logger.warn(`Bookmark validation: added missing bookmarkEnd for ID ${id}`);
5228
5231
  repairs++;
5229
5232
  }
@@ -5374,7 +5377,10 @@ export class Document {
5374
5377
  * Handles both paragraph numbering and raw nested content in tables.
5375
5378
  * @private
5376
5379
  */
5377
- private collectNumIdsFromElements(elements: (Paragraph | Table)[], usedNumIds: Set<number>): void {
5380
+ private collectNumIdsFromElements(
5381
+ elements: (Paragraph | Table)[],
5382
+ usedNumIds: Set<number>
5383
+ ): void {
5378
5384
  for (const element of elements) {
5379
5385
  if (element instanceof Paragraph) {
5380
5386
  this.collectNumIdsFromParagraphs([element], usedNumIds);
@@ -5472,7 +5478,7 @@ export class Document {
5472
5478
  if (!alreadyHasBlank) {
5473
5479
  // Insert blank paragraph with Normal style after this list item
5474
5480
  const blankPara = new Paragraph();
5475
- blankPara.setStyle("Normal");
5481
+ blankPara.setStyle('Normal');
5476
5482
 
5477
5483
  // Insert at position i+1 (after current element)
5478
5484
  this.bodyElements.splice(i + 1, 0, blankPara);
@@ -5509,10 +5515,10 @@ export class Document {
5509
5515
 
5510
5516
  // Step 1: Remove relationship entries for headers and footers
5511
5517
  const headerRels = this.relationshipManager.getRelationshipsByType(
5512
- "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
5518
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header'
5513
5519
  );
5514
5520
  const footerRels = this.relationshipManager.getRelationshipsByType(
5515
- "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
5521
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer'
5516
5522
  );
5517
5523
 
5518
5524
  for (const rel of [...headerRels, ...footerRels]) {
@@ -5723,10 +5729,7 @@ export class Document {
5723
5729
  * doc.createParagraph('Letter').setNumbering(listId, 1); // a.
5724
5730
  * ```
5725
5731
  */
5726
- createNumberedList(
5727
- levels = 3,
5728
- formats?: ("decimal" | "lowerLetter" | "lowerRoman")[]
5729
- ): number {
5732
+ createNumberedList(levels = 3, formats?: ('decimal' | 'lowerLetter' | 'lowerRoman')[]): number {
5730
5733
  return this.numberingManager.createNumberedList(levels, formats);
5731
5734
  }
5732
5735
 
@@ -5783,12 +5786,7 @@ export class Document {
5783
5786
  leftIndent: number,
5784
5787
  hangingIndent?: number
5785
5788
  ): this {
5786
- this.numberingManager.setListIndentation(
5787
- numId,
5788
- level,
5789
- leftIndent,
5790
- hangingIndent
5791
- );
5789
+ this.numberingManager.setListIndentation(numId, level, leftIndent, hangingIndent);
5792
5790
  return this;
5793
5791
  }
5794
5792
 
@@ -5838,14 +5836,13 @@ export class Document {
5838
5836
 
5839
5837
  for (const instance of instances) {
5840
5838
  const abstractNumId = instance.getAbstractNumId();
5841
- const abstractNum =
5842
- this.numberingManager.getAbstractNumbering(abstractNumId);
5839
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
5843
5840
 
5844
5841
  if (!abstractNum) continue;
5845
5842
 
5846
5843
  // Only process bullet lists (skip numbered lists)
5847
5844
  const level0 = abstractNum.getLevel(0);
5848
- if (level0?.getFormat() !== "bullet") continue;
5845
+ if (level0?.getFormat() !== 'bullet') continue;
5849
5846
 
5850
5847
  // Update all 9 levels (0-8) with standard formatting
5851
5848
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -5853,11 +5850,11 @@ export class Document {
5853
5850
  if (!numLevel) continue;
5854
5851
 
5855
5852
  // Alternate bullets: even levels = solid (•), odd levels = open (○)
5856
- const bullet = levelIndex % 2 === 0 ? "" : "";
5853
+ const bullet = levelIndex % 2 === 0 ? '' : '';
5857
5854
  numLevel.setText(bullet);
5858
5855
 
5859
5856
  // Set bullet font to Arial (Unicode bullets require a regular font, not Symbol)
5860
- numLevel.setFont("Arial");
5857
+ numLevel.setFont('Arial');
5861
5858
 
5862
5859
  // Set bullet size to 12pt (24 half-points)
5863
5860
  numLevel.setFontSize(24);
@@ -5908,14 +5905,13 @@ export class Document {
5908
5905
 
5909
5906
  for (const instance of instances) {
5910
5907
  const abstractNumId = instance.getAbstractNumId();
5911
- const abstractNum =
5912
- this.numberingManager.getAbstractNumbering(abstractNumId);
5908
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
5913
5909
 
5914
5910
  if (!abstractNum) continue;
5915
5911
 
5916
5912
  // Only process numbered lists (skip bullet lists)
5917
5913
  const level0 = abstractNum.getLevel(0);
5918
- if (!level0 || level0.getFormat() === "bullet") continue;
5914
+ if (!level0 || level0.getFormat() === 'bullet') continue;
5919
5915
 
5920
5916
  // Update all 9 levels (0-8) with standard formatting
5921
5917
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -5923,7 +5919,7 @@ export class Document {
5923
5919
  if (!numLevel) continue;
5924
5920
 
5925
5921
  // Set number font to Verdana 12pt
5926
- numLevel.setFont("Verdana");
5922
+ numLevel.setFont('Verdana');
5927
5923
  numLevel.setFontSize(24); // 12pt = 24 half-points
5928
5924
 
5929
5925
  // Indentation: 0.5" per level (720 twips)
@@ -5934,7 +5930,7 @@ export class Document {
5934
5930
  numLevel.setHangingIndent(360);
5935
5931
 
5936
5932
  // Set alignment to left
5937
- numLevel.setAlignment("left");
5933
+ numLevel.setAlignment('left');
5938
5934
  }
5939
5935
 
5940
5936
  // Apply paragraph formatting to all paragraphs using this list
@@ -5960,7 +5956,7 @@ export class Document {
5960
5956
  // Apply font to all runs in the paragraph
5961
5957
  const runs = para.getRuns();
5962
5958
  for (const run of runs) {
5963
- run.setFont("Verdana", 12);
5959
+ run.setFont('Verdana', 12);
5964
5960
  }
5965
5961
 
5966
5962
  // Apply paragraph spacing
@@ -6024,7 +6020,7 @@ export class Document {
6024
6020
  // Find the paragraph index in bodyElements
6025
6021
  const paraIndex = this.bodyElements.indexOf(para);
6026
6022
  if (paraIndex === -1) {
6027
- throw new Error("Paragraph not found in document body elements");
6023
+ throw new Error('Paragraph not found in document body elements');
6028
6024
  }
6029
6025
 
6030
6026
  // Create 1x1 table
@@ -6032,7 +6028,7 @@ export class Document {
6032
6028
  const cell = table.getCell(0, 0);
6033
6029
 
6034
6030
  if (!cell) {
6035
- throw new Error("Failed to get cell from newly created table");
6031
+ throw new Error('Failed to get cell from newly created table');
6036
6032
  }
6037
6033
 
6038
6034
  // Move paragraph to cell
@@ -6064,7 +6060,7 @@ export class Document {
6064
6060
  // Set table width (percentage of page width)
6065
6061
  if (options.tableWidthPercent !== undefined) {
6066
6062
  table.setWidth(options.tableWidthPercent);
6067
- table.setWidthType("pct");
6063
+ table.setWidthType('pct');
6068
6064
  }
6069
6065
 
6070
6066
  // Insert table where paragraph was
@@ -6076,30 +6072,30 @@ export class Document {
6076
6072
  // Default style configurations for applyStyles()
6077
6073
  private static readonly DEFAULT_HEADING1_CONFIG: StyleConfig = {
6078
6074
  run: {
6079
- font: "Verdana",
6075
+ font: 'Verdana',
6080
6076
  size: 18,
6081
6077
  bold: true,
6082
- color: "000000",
6078
+ color: '000000',
6083
6079
  },
6084
6080
  paragraph: {
6085
- alignment: "left",
6086
- spacing: { before: 0, after: 240, line: 240, lineRule: "auto" },
6081
+ alignment: 'left',
6082
+ spacing: { before: 0, after: 240, line: 240, lineRule: 'auto' },
6087
6083
  },
6088
6084
  };
6089
6085
 
6090
6086
  private static readonly DEFAULT_HEADING2_CONFIG: Heading2Config = {
6091
6087
  run: {
6092
- font: "Verdana",
6088
+ font: 'Verdana',
6093
6089
  size: 14,
6094
6090
  bold: true,
6095
- color: "000000",
6091
+ color: '000000',
6096
6092
  },
6097
6093
  paragraph: {
6098
- alignment: "left",
6099
- spacing: { before: 120, after: 120, line: 240, lineRule: "auto" },
6094
+ alignment: 'left',
6095
+ spacing: { before: 120, after: 120, line: 240, lineRule: 'auto' },
6100
6096
  },
6101
6097
  tableOptions: {
6102
- shading: "BFBFBF",
6098
+ shading: 'BFBFBF',
6103
6099
  marginTop: 0,
6104
6100
  marginBottom: 0,
6105
6101
  marginLeft: 115,
@@ -6110,38 +6106,38 @@ export class Document {
6110
6106
 
6111
6107
  private static readonly DEFAULT_HEADING3_CONFIG: StyleConfig = {
6112
6108
  run: {
6113
- font: "Verdana",
6109
+ font: 'Verdana',
6114
6110
  size: 12,
6115
6111
  bold: true,
6116
- color: "000000",
6112
+ color: '000000',
6117
6113
  },
6118
6114
  paragraph: {
6119
- alignment: "left",
6120
- spacing: { before: 60, after: 60, line: 240, lineRule: "auto" },
6115
+ alignment: 'left',
6116
+ spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
6121
6117
  },
6122
6118
  };
6123
6119
 
6124
6120
  private static readonly DEFAULT_NORMAL_CONFIG: StyleConfig = {
6125
6121
  run: {
6126
- font: "Verdana",
6122
+ font: 'Verdana',
6127
6123
  size: 12,
6128
- color: "000000",
6124
+ color: '000000',
6129
6125
  },
6130
6126
  paragraph: {
6131
- alignment: "left",
6132
- spacing: { before: 60, after: 60, line: 240, lineRule: "auto" },
6127
+ alignment: 'left',
6128
+ spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
6133
6129
  },
6134
6130
  };
6135
6131
 
6136
6132
  private static readonly DEFAULT_LIST_PARAGRAPH_CONFIG: StyleConfig = {
6137
6133
  run: {
6138
- font: "Verdana",
6134
+ font: 'Verdana',
6139
6135
  size: 12,
6140
- color: "000000",
6136
+ color: '000000',
6141
6137
  },
6142
6138
  paragraph: {
6143
- alignment: "left",
6144
- spacing: { before: 0, after: 60, line: 240, lineRule: "auto" },
6139
+ alignment: 'left',
6140
+ spacing: { before: 0, after: 60, line: 240, lineRule: 'auto' },
6145
6141
  indentation: { left: 360, hanging: 360 },
6146
6142
  contextualSpacing: true,
6147
6143
  },
@@ -6260,35 +6256,31 @@ export class Document {
6260
6256
  };
6261
6257
 
6262
6258
  // Get existing styles from StylesManager
6263
- const heading1 = this.stylesManager.getStyle("Heading1");
6264
- const heading2 = this.stylesManager.getStyle("Heading2");
6265
- const heading3 = this.stylesManager.getStyle("Heading3");
6266
- const normal = this.stylesManager.getStyle("Normal");
6267
- const listParagraph = this.stylesManager.getStyle("ListParagraph");
6259
+ const heading1 = this.stylesManager.getStyle('Heading1');
6260
+ const heading2 = this.stylesManager.getStyle('Heading2');
6261
+ const heading3 = this.stylesManager.getStyle('Heading3');
6262
+ const normal = this.stylesManager.getStyle('Normal');
6263
+ const listParagraph = this.stylesManager.getStyle('ListParagraph');
6268
6264
 
6269
6265
  // Merge provided options with ACTUAL current style values (not hardcoded defaults)
6270
6266
  // This allows users to only specify properties they want to change
6271
6267
  const h1Config = {
6272
6268
  run: {
6273
- ...(heading1?.getRunFormatting() ||
6274
- Document.DEFAULT_HEADING1_CONFIG.run),
6269
+ ...(heading1?.getRunFormatting() || Document.DEFAULT_HEADING1_CONFIG.run),
6275
6270
  ...options?.heading1?.run,
6276
6271
  },
6277
6272
  paragraph: {
6278
- ...(heading1?.getParagraphFormatting() ||
6279
- Document.DEFAULT_HEADING1_CONFIG.paragraph),
6273
+ ...(heading1?.getParagraphFormatting() || Document.DEFAULT_HEADING1_CONFIG.paragraph),
6280
6274
  ...options?.heading1?.paragraph,
6281
6275
  },
6282
6276
  };
6283
6277
  const h2Config = {
6284
6278
  run: {
6285
- ...(heading2?.getRunFormatting() ||
6286
- Document.DEFAULT_HEADING2_CONFIG.run),
6279
+ ...(heading2?.getRunFormatting() || Document.DEFAULT_HEADING2_CONFIG.run),
6287
6280
  ...options?.heading2?.run,
6288
6281
  },
6289
6282
  paragraph: {
6290
- ...(heading2?.getParagraphFormatting() ||
6291
- Document.DEFAULT_HEADING2_CONFIG.paragraph),
6283
+ ...(heading2?.getParagraphFormatting() || Document.DEFAULT_HEADING2_CONFIG.paragraph),
6292
6284
  ...options?.heading2?.paragraph,
6293
6285
  },
6294
6286
  tableOptions: {
@@ -6298,13 +6290,11 @@ export class Document {
6298
6290
  };
6299
6291
  const h3Config = {
6300
6292
  run: {
6301
- ...(heading3?.getRunFormatting() ||
6302
- Document.DEFAULT_HEADING3_CONFIG.run),
6293
+ ...(heading3?.getRunFormatting() || Document.DEFAULT_HEADING3_CONFIG.run),
6303
6294
  ...options?.heading3?.run,
6304
6295
  },
6305
6296
  paragraph: {
6306
- ...(heading3?.getParagraphFormatting() ||
6307
- Document.DEFAULT_HEADING3_CONFIG.paragraph),
6297
+ ...(heading3?.getParagraphFormatting() || Document.DEFAULT_HEADING3_CONFIG.paragraph),
6308
6298
  ...options?.heading3?.paragraph,
6309
6299
  },
6310
6300
  };
@@ -6314,15 +6304,13 @@ export class Document {
6314
6304
  ...options?.normal?.run,
6315
6305
  },
6316
6306
  paragraph: {
6317
- ...(normal?.getParagraphFormatting() ||
6318
- Document.DEFAULT_NORMAL_CONFIG.paragraph),
6307
+ ...(normal?.getParagraphFormatting() || Document.DEFAULT_NORMAL_CONFIG.paragraph),
6319
6308
  ...options?.normal?.paragraph,
6320
6309
  },
6321
6310
  };
6322
6311
  const listParaConfig = {
6323
6312
  run: {
6324
- ...(listParagraph?.getRunFormatting() ||
6325
- Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
6313
+ ...(listParagraph?.getRunFormatting() || Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
6326
6314
  ...options?.listParagraph?.run,
6327
6315
  },
6328
6316
  paragraph: {
@@ -6333,14 +6321,12 @@ export class Document {
6333
6321
  };
6334
6322
 
6335
6323
  // Extract preserve blank lines option (defaults to true)
6336
- const preserveBlankLines =
6337
- options?.preserveBlankLinesAfterHeading2Tables ?? true;
6324
+ const preserveBlankLines = options?.preserveBlankLinesAfterHeading2Tables ?? true;
6338
6325
 
6339
6326
  // Modify Heading1 definition
6340
6327
  if (heading1 && h1Config.run && h1Config.paragraph) {
6341
6328
  if (h1Config.run) heading1.setRunFormatting(h1Config.run);
6342
- if (h1Config.paragraph)
6343
- heading1.setParagraphFormatting(h1Config.paragraph);
6329
+ if (h1Config.paragraph) heading1.setParagraphFormatting(h1Config.paragraph);
6344
6330
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6345
6331
  this.addStyle(heading1);
6346
6332
  results.heading1 = true;
@@ -6349,8 +6335,7 @@ export class Document {
6349
6335
  // Modify Heading2 definition
6350
6336
  if (heading2 && h2Config.run && h2Config.paragraph) {
6351
6337
  if (h2Config.run) heading2.setRunFormatting(h2Config.run);
6352
- if (h2Config.paragraph)
6353
- heading2.setParagraphFormatting(h2Config.paragraph);
6338
+ if (h2Config.paragraph) heading2.setParagraphFormatting(h2Config.paragraph);
6354
6339
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6355
6340
  this.addStyle(heading2);
6356
6341
  results.heading2 = true;
@@ -6359,8 +6344,7 @@ export class Document {
6359
6344
  // Modify Heading3 definition
6360
6345
  if (heading3 && h3Config.run && h3Config.paragraph) {
6361
6346
  if (h3Config.run) heading3.setRunFormatting(h3Config.run);
6362
- if (h3Config.paragraph)
6363
- heading3.setParagraphFormatting(h3Config.paragraph);
6347
+ if (h3Config.paragraph) heading3.setParagraphFormatting(h3Config.paragraph);
6364
6348
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6365
6349
  this.addStyle(heading3);
6366
6350
  results.heading3 = true;
@@ -6369,8 +6353,7 @@ export class Document {
6369
6353
  // Modify Normal definition
6370
6354
  if (normal && normalConfig.run && normalConfig.paragraph) {
6371
6355
  if (normalConfig.run) normal.setRunFormatting(normalConfig.run);
6372
- if (normalConfig.paragraph)
6373
- normal.setParagraphFormatting(normalConfig.paragraph);
6356
+ if (normalConfig.paragraph) normal.setParagraphFormatting(normalConfig.paragraph);
6374
6357
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6375
6358
  this.addStyle(normal);
6376
6359
 
@@ -6378,12 +6361,11 @@ export class Document {
6378
6361
  // Default is true - changes to Normal automatically apply to NormalWeb
6379
6362
  const shouldLinkNormalWeb = options?.linkNormalWebToNormal !== false;
6380
6363
  if (shouldLinkNormalWeb) {
6381
- const normalWeb = this.stylesManager.getStyle("NormalWeb");
6364
+ const normalWeb = this.stylesManager.getStyle('NormalWeb');
6382
6365
  if (normalWeb) {
6383
6366
  // Apply same formatting to NormalWeb
6384
6367
  if (normalConfig.run) normalWeb.setRunFormatting(normalConfig.run);
6385
- if (normalConfig.paragraph)
6386
- normalWeb.setParagraphFormatting(normalConfig.paragraph);
6368
+ if (normalConfig.paragraph) normalWeb.setParagraphFormatting(normalConfig.paragraph);
6387
6369
  // Mark as modified for selective merging during save
6388
6370
  this.addStyle(normalWeb);
6389
6371
  }
@@ -6394,8 +6376,7 @@ export class Document {
6394
6376
 
6395
6377
  // Modify List Paragraph definition
6396
6378
  if (listParagraph && listParaConfig.run && listParaConfig.paragraph) {
6397
- if (listParaConfig.run)
6398
- listParagraph.setRunFormatting(listParaConfig.run);
6379
+ if (listParaConfig.run) listParagraph.setRunFormatting(listParaConfig.run);
6399
6380
  if (listParaConfig.paragraph) {
6400
6381
  // Validate indentation: hanging must not exceed left to prevent negative bullet position
6401
6382
  const indent = listParaConfig.paragraph.indentation;
@@ -6404,7 +6385,7 @@ export class Document {
6404
6385
  const logger = getGlobalLogger();
6405
6386
  logger.warn(
6406
6387
  `[Document] ListParagraph indentation: hanging (${indent.hanging}) > left (${indent.left}). ` +
6407
- `Capping hanging to left to prevent negative bullet position.`
6388
+ `Capping hanging to left to prevent negative bullet position.`
6408
6389
  );
6409
6390
  indent.hanging = indent.left;
6410
6391
  }
@@ -6459,12 +6440,12 @@ export class Document {
6459
6440
  const styleId = para.getStyle();
6460
6441
 
6461
6442
  // Process Heading1 paragraphs
6462
- if (styleId === "Heading1" && heading1) {
6443
+ if (styleId === 'Heading1' && heading1) {
6463
6444
  // Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
6464
6445
  const allRuns = this.getAllRunsFromParagraph(para);
6465
6446
  const whiteFontRuns = new Set(
6466
6447
  options?.preserveWhiteFont
6467
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6448
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6468
6449
  : []
6469
6450
  );
6470
6451
 
@@ -6483,7 +6464,7 @@ export class Document {
6483
6464
  run.setItalic(h1Config.run?.italic ?? false);
6484
6465
  }
6485
6466
  if (!h1Preserve.underline) {
6486
- run.setUnderline(h1Config.run?.underline ? "single" : false);
6467
+ run.setUnderline(h1Config.run?.underline ? 'single' : false);
6487
6468
  }
6488
6469
  // Apply font, color, and size - skip color if run was white font
6489
6470
  if (h1Config.run?.font) {
@@ -6505,25 +6486,13 @@ export class Document {
6505
6486
  // Update paragraph mark properties to match configuration
6506
6487
  if (para.formatting.paragraphMarkRunProperties) {
6507
6488
  const markProps = para.formatting.paragraphMarkRunProperties;
6508
- if (
6509
- !h1Preserve.bold &&
6510
- h1Config.run?.bold === false &&
6511
- markProps.bold
6512
- ) {
6489
+ if (!h1Preserve.bold && h1Config.run?.bold === false && markProps.bold) {
6513
6490
  delete markProps.bold;
6514
6491
  }
6515
- if (
6516
- !h1Preserve.italic &&
6517
- h1Config.run?.italic === false &&
6518
- markProps.italic
6519
- ) {
6492
+ if (!h1Preserve.italic && h1Config.run?.italic === false && markProps.italic) {
6520
6493
  delete markProps.italic;
6521
6494
  }
6522
- if (
6523
- !h1Preserve.underline &&
6524
- h1Config.run?.underline === false &&
6525
- markProps.underline
6526
- ) {
6495
+ if (!h1Preserve.underline && h1Config.run?.underline === false && markProps.underline) {
6527
6496
  delete markProps.underline;
6528
6497
  }
6529
6498
  // Update paragraph mark font, color, size
@@ -6542,10 +6511,11 @@ export class Document {
6542
6511
  }
6543
6512
 
6544
6513
  // Process Heading2 paragraphs
6545
- else if (styleId === "Heading2" && heading2) {
6514
+ else if (styleId === 'Heading2' && heading2) {
6546
6515
  // Check if paragraph has actual text content (skip empty paragraphs)
6547
- const hasContent = this.getAllRunsFromParagraph(para)
6548
- .some((run) => run.getText().trim().length > 0);
6516
+ const hasContent = this.getAllRunsFromParagraph(para).some(
6517
+ (run) => run.getText().trim().length > 0
6518
+ );
6549
6519
 
6550
6520
  if (!hasContent) {
6551
6521
  // Skip empty Heading2 paragraphs - don't wrap them in tables
@@ -6578,8 +6548,7 @@ export class Document {
6578
6548
  const isMultiCellTable = !(rowCount === 1 && colCount === 1);
6579
6549
  const cellFormatting = cell.getFormatting();
6580
6550
  const cellHasShading = !!(
6581
- cellFormatting?.shading?.fill ||
6582
- cellFormatting?.shading?.pattern
6551
+ cellFormatting?.shading?.fill || cellFormatting?.shading?.pattern
6583
6552
  );
6584
6553
 
6585
6554
  if (isMultiCellTable && cellHasShading && para.formatting.alignment) {
@@ -6592,7 +6561,7 @@ export class Document {
6592
6561
  const allRuns = this.getAllRunsFromParagraph(para);
6593
6562
  const whiteFontRuns = new Set(
6594
6563
  options?.preserveWhiteFont
6595
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6564
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6596
6565
  : []
6597
6566
  );
6598
6567
 
@@ -6617,7 +6586,7 @@ export class Document {
6617
6586
  run.setItalic(h2Config.run?.italic ?? false);
6618
6587
  }
6619
6588
  if (!h2Preserve.underline) {
6620
- run.setUnderline(h2Config.run?.underline ? "single" : false);
6589
+ run.setUnderline(h2Config.run?.underline ? 'single' : false);
6621
6590
  }
6622
6591
  // Apply font, color, and size - skip color if run was white font
6623
6592
  if (h2Config.run?.font) {
@@ -6639,25 +6608,13 @@ export class Document {
6639
6608
  // Update paragraph mark properties to match configuration
6640
6609
  if (para.formatting.paragraphMarkRunProperties) {
6641
6610
  const markProps = para.formatting.paragraphMarkRunProperties;
6642
- if (
6643
- !h2Preserve.bold &&
6644
- h2Config.run?.bold === false &&
6645
- markProps.bold
6646
- ) {
6611
+ if (!h2Preserve.bold && h2Config.run?.bold === false && markProps.bold) {
6647
6612
  delete markProps.bold;
6648
6613
  }
6649
- if (
6650
- !h2Preserve.italic &&
6651
- h2Config.run?.italic === false &&
6652
- markProps.italic
6653
- ) {
6614
+ if (!h2Preserve.italic && h2Config.run?.italic === false && markProps.italic) {
6654
6615
  delete markProps.italic;
6655
6616
  }
6656
- if (
6657
- !h2Preserve.underline &&
6658
- h2Config.run?.underline === false &&
6659
- markProps.underline
6660
- ) {
6617
+ if (!h2Preserve.underline && h2Config.run?.underline === false && markProps.underline) {
6661
6618
  delete markProps.underline;
6662
6619
  }
6663
6620
  // Update paragraph mark font, color, size
@@ -6703,13 +6660,13 @@ export class Document {
6703
6660
  });
6704
6661
  if (table) {
6705
6662
  table.setWidth(h2Config.tableOptions.tableWidthPercent);
6706
- table.setWidthType("pct");
6663
+ table.setWidthType('pct');
6707
6664
  }
6708
6665
  }
6709
6666
  } else {
6710
6667
  // Paragraph is not in a table - wrap it using config
6711
6668
  const table = this.wrapParagraphInTable(para, {
6712
- shading: h2Config.tableOptions?.shading ?? "BFBFBF",
6669
+ shading: h2Config.tableOptions?.shading ?? 'BFBFBF',
6713
6670
  marginTop: h2Config.tableOptions?.marginTop ?? 0,
6714
6671
  marginBottom: h2Config.tableOptions?.marginBottom ?? 0,
6715
6672
  marginLeft: h2Config.tableOptions?.marginLeft ?? 115,
@@ -6740,7 +6697,7 @@ export class Document {
6740
6697
  // Runs with text count as content
6741
6698
  if ((item as any).getText) {
6742
6699
  const text = (item as any).getText().trim();
6743
- if (text !== "") return false;
6700
+ if (text !== '') return false;
6744
6701
  }
6745
6702
  }
6746
6703
 
@@ -6765,12 +6722,12 @@ export class Document {
6765
6722
  }
6766
6723
 
6767
6724
  // Process Heading3 paragraphs
6768
- else if (styleId === "Heading3" && heading3) {
6725
+ else if (styleId === 'Heading3' && heading3) {
6769
6726
  // Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
6770
6727
  const allRuns = this.getAllRunsFromParagraph(para);
6771
6728
  const whiteFontRuns = new Set(
6772
6729
  options?.preserveWhiteFont
6773
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6730
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6774
6731
  : []
6775
6732
  );
6776
6733
 
@@ -6789,7 +6746,7 @@ export class Document {
6789
6746
  run.setItalic(h3Config.run?.italic ?? false);
6790
6747
  }
6791
6748
  if (!h3Preserve.underline) {
6792
- run.setUnderline(h3Config.run?.underline ? "single" : false);
6749
+ run.setUnderline(h3Config.run?.underline ? 'single' : false);
6793
6750
  }
6794
6751
  // Apply font, color, and size - skip color if run was white font
6795
6752
  if (h3Config.run?.font) {
@@ -6811,25 +6768,13 @@ export class Document {
6811
6768
  // Update paragraph mark properties to match configuration
6812
6769
  if (para.formatting.paragraphMarkRunProperties) {
6813
6770
  const markProps = para.formatting.paragraphMarkRunProperties;
6814
- if (
6815
- !h3Preserve.bold &&
6816
- h3Config.run?.bold === false &&
6817
- markProps.bold
6818
- ) {
6771
+ if (!h3Preserve.bold && h3Config.run?.bold === false && markProps.bold) {
6819
6772
  delete markProps.bold;
6820
6773
  }
6821
- if (
6822
- !h3Preserve.italic &&
6823
- h3Config.run?.italic === false &&
6824
- markProps.italic
6825
- ) {
6774
+ if (!h3Preserve.italic && h3Config.run?.italic === false && markProps.italic) {
6826
6775
  delete markProps.italic;
6827
6776
  }
6828
- if (
6829
- !h3Preserve.underline &&
6830
- h3Config.run?.underline === false &&
6831
- markProps.underline
6832
- ) {
6777
+ if (!h3Preserve.underline && h3Config.run?.underline === false && markProps.underline) {
6833
6778
  delete markProps.underline;
6834
6779
  }
6835
6780
  // Update paragraph mark font, color, size
@@ -6848,7 +6793,7 @@ export class Document {
6848
6793
  }
6849
6794
 
6850
6795
  // Process List Paragraph paragraphs
6851
- else if (styleId === "ListParagraph" && listParagraph) {
6796
+ else if (styleId === 'ListParagraph' && listParagraph) {
6852
6797
  // Check for mis-styled paragraphs: ListParagraph + left:0 + no numbering
6853
6798
  // These are not actual list items - change them to Normal style
6854
6799
  const paraIndentation = para.getFormatting().indentation;
@@ -6856,7 +6801,7 @@ export class Document {
6856
6801
  if (paraIndentation?.left === 0 && !hasNumbering) {
6857
6802
  // This paragraph has ListParagraph style but explicitly overrides indent to 0
6858
6803
  // and has no numbering - it should be Normal style, not ListParagraph
6859
- para.setStyle("Normal");
6804
+ para.setStyle('Normal');
6860
6805
 
6861
6806
  // Preserve existing bold formatting and white font before applying Normal style
6862
6807
  const allRuns = this.getAllRunsFromParagraph(para);
@@ -6942,7 +6887,7 @@ export class Document {
6942
6887
  run.setItalic(listParaConfig.run?.italic ?? false);
6943
6888
  }
6944
6889
  if (!listParaPreserve.underline) {
6945
- run.setUnderline(listParaConfig.run?.underline ? "single" : false);
6890
+ run.setUnderline(listParaConfig.run?.underline ? 'single' : false);
6946
6891
  }
6947
6892
  // Apply font, color, and size - skip color if run was white font
6948
6893
  if (listParaConfig.run?.font) {
@@ -6964,11 +6909,7 @@ export class Document {
6964
6909
  // Update paragraph mark properties to match configuration
6965
6910
  if (para.formatting.paragraphMarkRunProperties) {
6966
6911
  const markProps = para.formatting.paragraphMarkRunProperties;
6967
- if (
6968
- !listParaPreserve.bold &&
6969
- listParaConfig.run?.bold === false &&
6970
- markProps.bold
6971
- ) {
6912
+ if (!listParaPreserve.bold && listParaConfig.run?.bold === false && markProps.bold) {
6972
6913
  delete markProps.bold;
6973
6914
  }
6974
6915
  if (
@@ -7002,7 +6943,12 @@ export class Document {
7002
6943
 
7003
6944
  // Process Normal paragraphs (including undefined style which defaults to Normal)
7004
6945
  // Also process NormalWeb paragraphs when linkNormalWebToNormal is enabled (default: true)
7005
- else if ((styleId === "Normal" || styleId === undefined || (styleId === "NormalWeb" && options?.linkNormalWebToNormal !== false)) && normal) {
6946
+ else if (
6947
+ (styleId === 'Normal' ||
6948
+ styleId === undefined ||
6949
+ (styleId === 'NormalWeb' && options?.linkNormalWebToNormal !== false)) &&
6950
+ normal
6951
+ ) {
7006
6952
  // Save formatting that should be preserved BEFORE clearing
7007
6953
  const allRuns = this.getAllRunsFromParagraph(para);
7008
6954
  const preservedFormatting = allRuns.map((run) => {
@@ -7017,8 +6963,8 @@ export class Document {
7017
6963
  });
7018
6964
 
7019
6965
  // Save center alignment BEFORE clearing if preserveCenterAlignment is set
7020
- const savedCenterAlignment = options?.normal?.preserveCenterAlignment &&
7021
- para.getAlignment() === 'center';
6966
+ const savedCenterAlignment =
6967
+ options?.normal?.preserveCenterAlignment && para.getAlignment() === 'center';
7022
6968
 
7023
6969
  para.clearDirectFormattingConflicts(normal);
7024
6970
 
@@ -7054,7 +7000,7 @@ export class Document {
7054
7000
  run.setItalic(normalConfig.run?.italic ?? false);
7055
7001
  }
7056
7002
  if (!normalPreserve.underline) {
7057
- run.setUnderline(normalConfig.run?.underline ? "single" : false);
7003
+ run.setUnderline(normalConfig.run?.underline ? 'single' : false);
7058
7004
  }
7059
7005
  // Apply font, color, and size - skip color if run was white font
7060
7006
  if (normalConfig.run?.font) {
@@ -7077,18 +7023,10 @@ export class Document {
7077
7023
  // Update paragraph mark properties to match configuration
7078
7024
  if (para.formatting.paragraphMarkRunProperties) {
7079
7025
  const markProps = para.formatting.paragraphMarkRunProperties;
7080
- if (
7081
- !normalPreserve.bold &&
7082
- normalConfig.run?.bold === false &&
7083
- markProps.bold
7084
- ) {
7026
+ if (!normalPreserve.bold && normalConfig.run?.bold === false && markProps.bold) {
7085
7027
  delete markProps.bold;
7086
7028
  }
7087
- if (
7088
- !normalPreserve.italic &&
7089
- normalConfig.run?.italic === false &&
7090
- markProps.italic
7091
- ) {
7029
+ if (!normalPreserve.italic && normalConfig.run?.italic === false && markProps.italic) {
7092
7030
  delete markProps.italic;
7093
7031
  }
7094
7032
  if (
@@ -7174,8 +7112,9 @@ export class Document {
7174
7112
  indentPerLevel?: number;
7175
7113
  }): { formatted: number[] } {
7176
7114
  // Filter valid levels, deduplicate, and sort
7177
- const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9])
7178
- .filter((l) => l >= 1 && l <= 9);
7115
+ const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9]).filter(
7116
+ (l) => l >= 1 && l <= 9
7117
+ );
7179
7118
  const levels = [...new Set(validLevels)].sort((a, b) => a - b);
7180
7119
  const formatted: number[] = [];
7181
7120
 
@@ -7277,14 +7216,14 @@ export class Document {
7277
7216
  const styleId = style.getStyleId();
7278
7217
 
7279
7218
  switch (styleId) {
7280
- case "Heading1":
7219
+ case 'Heading1':
7281
7220
  options.heading1 = {
7282
7221
  run: style.getRunFormatting() as any,
7283
7222
  paragraph: style.getParagraphFormatting() as any,
7284
7223
  };
7285
7224
  break;
7286
7225
 
7287
- case "Heading2":
7226
+ case 'Heading2':
7288
7227
  options.heading2 = {
7289
7228
  run: style.getRunFormatting() as any,
7290
7229
  paragraph: style.getParagraphFormatting() as any,
@@ -7292,21 +7231,21 @@ export class Document {
7292
7231
  };
7293
7232
  break;
7294
7233
 
7295
- case "Heading3":
7234
+ case 'Heading3':
7296
7235
  options.heading3 = {
7297
7236
  run: style.getRunFormatting() as any,
7298
7237
  paragraph: style.getParagraphFormatting() as any,
7299
7238
  };
7300
7239
  break;
7301
7240
 
7302
- case "Normal":
7241
+ case 'Normal':
7303
7242
  options.normal = {
7304
7243
  run: style.getRunFormatting() as any,
7305
7244
  paragraph: style.getParagraphFormatting() as any,
7306
7245
  };
7307
7246
  break;
7308
7247
 
7309
- case "ListParagraph":
7248
+ case 'ListParagraph':
7310
7249
  options.listParagraph = {
7311
7250
  run: style.getRunFormatting() as any,
7312
7251
  paragraph: style.getParagraphFormatting() as any,
@@ -7346,8 +7285,8 @@ export class Document {
7346
7285
  */
7347
7286
  public updateTableStyleShading(oldColor: string, newColor: string): number {
7348
7287
  // Normalize colors (uppercase, no #)
7349
- const normalizedOld = oldColor.replace("#", "").toUpperCase();
7350
- const normalizedNew = newColor.replace("#", "").toUpperCase();
7288
+ const normalizedOld = oldColor.replace('#', '').toUpperCase();
7289
+ const normalizedNew = newColor.replace('#', '').toUpperCase();
7351
7290
 
7352
7291
  if (normalizedOld === normalizedNew) {
7353
7292
  return 0; // No change needed
@@ -7363,10 +7302,7 @@ export class Document {
7363
7302
 
7364
7303
  // Pattern to match shading elements with fill attribute containing the old color
7365
7304
  // Matches: w:fill="A5A5A5" (case-insensitive)
7366
- const fillPattern = new RegExp(
7367
- `(w:fill=["'])${normalizedOld}(["'])`,
7368
- "gi"
7369
- );
7305
+ const fillPattern = new RegExp(`(w:fill=["'])${normalizedOld}(["'])`, 'gi');
7370
7306
 
7371
7307
  // Replace all occurrences
7372
7308
  stylesXml = stylesXml.replace(fillPattern, (match, prefix, suffix) => {
@@ -7376,10 +7312,7 @@ export class Document {
7376
7312
 
7377
7313
  // Also handle color attribute in shading elements
7378
7314
  // Matches: w:color="A5A5A5" within shd elements
7379
- const colorPattern = new RegExp(
7380
- `(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`,
7381
- "gi"
7382
- );
7315
+ const colorPattern = new RegExp(`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`, 'gi');
7383
7316
 
7384
7317
  stylesXml = stylesXml.replace(colorPattern, (match, prefix, suffix) => {
7385
7318
  updateCount++;
@@ -7425,28 +7358,22 @@ export class Document {
7425
7358
  let totalUpdated = 0;
7426
7359
 
7427
7360
  // Default colors commonly used in table styles
7428
- const defaultReplaceColors = ["A5A5A5", "C0C0C0", "D9D9D9", "E7E6E6"];
7361
+ const defaultReplaceColors = ['A5A5A5', 'C0C0C0', 'D9D9D9', 'E7E6E6'];
7429
7362
  const colorsToReplace = settings.replaceColors || defaultReplaceColors;
7430
7363
 
7431
7364
  // Replace header-type colors with header shading
7432
7365
  if (settings.headerShading) {
7433
7366
  for (const oldColor of colorsToReplace) {
7434
- totalUpdated += this.updateTableStyleShading(
7435
- oldColor,
7436
- settings.headerShading
7437
- );
7367
+ totalUpdated += this.updateTableStyleShading(oldColor, settings.headerShading);
7438
7368
  }
7439
7369
  }
7440
7370
 
7441
7371
  // If data shading differs from header, apply it to lighter colors
7442
7372
  if (settings.dataShading && settings.dataShading !== settings.headerShading) {
7443
7373
  // Typically data cells use lighter shading
7444
- const dataColors = ["D9D9D9", "E7E6E6", "F2F2F2"];
7374
+ const dataColors = ['D9D9D9', 'E7E6E6', 'F2F2F2'];
7445
7375
  for (const oldColor of dataColors) {
7446
- totalUpdated += this.updateTableStyleShading(
7447
- oldColor,
7448
- settings.dataShading
7449
- );
7376
+ totalUpdated += this.updateTableStyleShading(oldColor, settings.dataShading);
7450
7377
  }
7451
7378
  }
7452
7379
 
@@ -7518,8 +7445,8 @@ export class Document {
7518
7445
  const {
7519
7446
  bold = false,
7520
7447
  fontSize = 24, // 12pt
7521
- color = "000000",
7522
- font = "Arial",
7448
+ color = '000000',
7449
+ font = 'Arial',
7523
7450
  } = options || {};
7524
7451
 
7525
7452
  let listsUpdated = 0;
@@ -7529,14 +7456,13 @@ export class Document {
7529
7456
 
7530
7457
  for (const instance of instances) {
7531
7458
  const abstractNumId = instance.getAbstractNumId();
7532
- const abstractNum =
7533
- this.numberingManager.getAbstractNumbering(abstractNumId);
7459
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
7534
7460
 
7535
7461
  if (!abstractNum) continue;
7536
7462
 
7537
7463
  // Only process bullet lists (skip numbered lists)
7538
7464
  const level0 = abstractNum.getLevel(0);
7539
- if (level0?.getFormat() !== "bullet") continue;
7465
+ if (level0?.getFormat() !== 'bullet') continue;
7540
7466
 
7541
7467
  // Update all 9 levels (0-8) with formatting only (preserve existing symbols)
7542
7468
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -7597,8 +7523,8 @@ export class Document {
7597
7523
  const {
7598
7524
  bold = false,
7599
7525
  fontSize = 24, // 12pt
7600
- color = "000000",
7601
- font = "Verdana",
7526
+ color = '000000',
7527
+ font = 'Verdana',
7602
7528
  } = options || {};
7603
7529
 
7604
7530
  let listsUpdated = 0;
@@ -7608,14 +7534,13 @@ export class Document {
7608
7534
 
7609
7535
  for (const instance of instances) {
7610
7536
  const abstractNumId = instance.getAbstractNumId();
7611
- const abstractNum =
7612
- this.numberingManager.getAbstractNumbering(abstractNumId);
7537
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
7613
7538
 
7614
7539
  if (!abstractNum) continue;
7615
7540
 
7616
7541
  // Only process numbered lists (skip bullet lists)
7617
7542
  const level0 = abstractNum.getLevel(0);
7618
- if (!level0 || level0.getFormat() === "bullet") continue;
7543
+ if (!level0 || level0.getFormat() === 'bullet') continue;
7619
7544
 
7620
7545
  // Update all 9 levels (0-8)
7621
7546
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -7666,12 +7591,7 @@ export class Document {
7666
7591
  color?: string;
7667
7592
  underline?: boolean;
7668
7593
  }): number {
7669
- const {
7670
- font = "Verdana",
7671
- size = 12,
7672
- color = "0000FF",
7673
- underline = true,
7674
- } = options || {};
7594
+ const { font = 'Verdana', size = 12, color = '0000FF', underline = true } = options || {};
7675
7595
 
7676
7596
  const hyperlinks = this.getHyperlinks();
7677
7597
 
@@ -7680,14 +7600,13 @@ export class Document {
7680
7600
  font: font,
7681
7601
  size: size,
7682
7602
  color: color,
7683
- underline: underline ? "single" : false,
7603
+ underline: underline ? 'single' : false,
7684
7604
  });
7685
7605
  }
7686
7606
 
7687
7607
  return hyperlinks.length;
7688
7608
  }
7689
7609
 
7690
-
7691
7610
  /**
7692
7611
  * Applies Heading 1 style to paragraphs with H1-like style names
7693
7612
  * @param options Optional style application options
@@ -7710,7 +7629,7 @@ export class Document {
7710
7629
  * ```
7711
7630
  */
7712
7631
  public applyH1(options?: StyleApplyOptions): number {
7713
- return this.applyStyleToMatching("Heading1", options, (style) =>
7632
+ return this.applyStyleToMatching('Heading1', options, (style) =>
7714
7633
  /^(heading\s*1|header\s*1|h1)$/i.test(style)
7715
7634
  );
7716
7635
  }
@@ -7727,7 +7646,7 @@ export class Document {
7727
7646
  * ```
7728
7647
  */
7729
7648
  public applyH2(options?: StyleApplyOptions): number {
7730
- return this.applyStyleToMatching("Heading2", options, (style) =>
7649
+ return this.applyStyleToMatching('Heading2', options, (style) =>
7731
7650
  /^(heading\s*2|header\s*2|h2)$/i.test(style)
7732
7651
  );
7733
7652
  }
@@ -7744,7 +7663,7 @@ export class Document {
7744
7663
  * ```
7745
7664
  */
7746
7665
  public applyH3(options?: StyleApplyOptions): number {
7747
- return this.applyStyleToMatching("Heading3", options, (style) =>
7666
+ return this.applyStyleToMatching('Heading3', options, (style) =>
7748
7667
  /^(heading\s*3|header\s*3|h3)$/i.test(style)
7749
7668
  );
7750
7669
  }
@@ -7759,16 +7678,13 @@ export class Document {
7759
7678
  options?.paragraphs ||
7760
7679
  this.getAllParagraphs().filter((p) => {
7761
7680
  const style = p.getStyle();
7762
- return (
7763
- !style ||
7764
- !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style)
7765
- );
7681
+ return !style || !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style);
7766
7682
  });
7767
7683
 
7768
7684
  let count = 0;
7769
7685
  for (const para of targets) {
7770
7686
  if (para.isPreserved()) continue;
7771
- para.setStyle("Normal");
7687
+ para.setStyle('Normal');
7772
7688
 
7773
7689
  if (options?.keepProperties && options.keepProperties.length > 0) {
7774
7690
  this.clearFormattingExcept(para, options.keepProperties);
@@ -7791,7 +7707,7 @@ export class Document {
7791
7707
  * @returns Number of paragraphs updated
7792
7708
  */
7793
7709
  public applyNumList(options?: StyleApplyOptions): number {
7794
- return this.applyStyleToMatching("ListParagraph", options, (style) =>
7710
+ return this.applyStyleToMatching('ListParagraph', options, (style) =>
7795
7711
  /^(list\s*number|numbered\s*list|list\s*paragraph)$/i.test(style)
7796
7712
  );
7797
7713
  }
@@ -7802,7 +7718,7 @@ export class Document {
7802
7718
  * @returns Number of paragraphs updated
7803
7719
  */
7804
7720
  public applyBulletList(options?: StyleApplyOptions): number {
7805
- return this.applyStyleToMatching("ListParagraph", options, (style) =>
7721
+ return this.applyStyleToMatching('ListParagraph', options, (style) =>
7806
7722
  /^(list\s*bullet|bullet\s*list|list\s*paragraph)$/i.test(style)
7807
7723
  );
7808
7724
  }
@@ -7813,7 +7729,7 @@ export class Document {
7813
7729
  * @returns Number of paragraphs updated
7814
7730
  */
7815
7731
  public applyTOC(options?: StyleApplyOptions): number {
7816
- return this.applyStyleToMatching("TOC", options, (style) =>
7732
+ return this.applyStyleToMatching('TOC', options, (style) =>
7817
7733
  /^(toc|table\s*of\s*contents|toc\s*heading)$/i.test(style)
7818
7734
  );
7819
7735
  }
@@ -7824,7 +7740,7 @@ export class Document {
7824
7740
  * @returns Number of paragraphs updated
7825
7741
  */
7826
7742
  public applyTOD(options?: StyleApplyOptions): number {
7827
- return this.applyStyleToMatching("TopOfDocument", options, (style) =>
7743
+ return this.applyStyleToMatching('TopOfDocument', options, (style) =>
7828
7744
  /^(tod|top\s*of\s*document|document\s*top)$/i.test(style)
7829
7745
  );
7830
7746
  }
@@ -7835,7 +7751,7 @@ export class Document {
7835
7751
  * @returns Number of paragraphs updated
7836
7752
  */
7837
7753
  public applyCaution(options?: StyleApplyOptions): number {
7838
- return this.applyStyleToMatching("Caution", options, (style) =>
7754
+ return this.applyStyleToMatching('Caution', options, (style) =>
7839
7755
  /^(caution|warning|important|alert)$/i.test(style)
7840
7756
  );
7841
7757
  }
@@ -7856,7 +7772,7 @@ export class Document {
7856
7772
  for (const cell of firstRow.getCells()) {
7857
7773
  for (const para of cell.getParagraphs()) {
7858
7774
  if (para.isPreserved()) continue;
7859
- para.setStyle("TableHeader");
7775
+ para.setStyle('TableHeader');
7860
7776
 
7861
7777
  if (options?.keepProperties && options.keepProperties.length > 0) {
7862
7778
  this.clearFormattingExcept(para, options.keepProperties);
@@ -7905,9 +7821,9 @@ export class Document {
7905
7821
  if (options.color) run.setColor(options.color);
7906
7822
  if (options.emphasis) {
7907
7823
  options.emphasis.forEach((emp) => {
7908
- if (emp === "bold") run.setBold(true);
7909
- if (emp === "italic") run.setItalic(true);
7910
- if (emp === "underline") run.setUnderline("single");
7824
+ if (emp === 'bold') run.setBold(true);
7825
+ if (emp === 'italic') run.setItalic(true);
7826
+ if (emp === 'underline') run.setUnderline('single');
7911
7827
  });
7912
7828
  }
7913
7829
  }
@@ -7960,10 +7876,7 @@ export class Document {
7960
7876
  * Helper method to selectively clear formatting while preserving specific properties
7961
7877
  * @private
7962
7878
  */
7963
- private clearFormattingExcept(
7964
- para: Paragraph,
7965
- keepProperties: string[]
7966
- ): void {
7879
+ private clearFormattingExcept(para: Paragraph, keepProperties: string[]): void {
7967
7880
  // Save properties to keep
7968
7881
  const savedProps: Record<string, unknown> = {};
7969
7882
  const formatting = para.formatting as Record<string, unknown>;
@@ -8000,21 +7913,15 @@ export class Document {
8000
7913
 
8001
7914
  // Restore saved properties using appropriate setters
8002
7915
  if (runSavedProps.bold !== undefined) run.setBold(runSavedProps.bold);
8003
- if (runSavedProps.italic !== undefined)
8004
- run.setItalic(runSavedProps.italic);
8005
- if (runSavedProps.underline !== undefined)
8006
- run.setUnderline(runSavedProps.underline);
7916
+ if (runSavedProps.italic !== undefined) run.setItalic(runSavedProps.italic);
7917
+ if (runSavedProps.underline !== undefined) run.setUnderline(runSavedProps.underline);
8007
7918
  if (runSavedProps.color !== undefined) run.setColor(runSavedProps.color);
8008
7919
  if (runSavedProps.font !== undefined) run.setFont(runSavedProps.font);
8009
7920
  if (runSavedProps.size !== undefined) run.setSize(runSavedProps.size);
8010
- if (runSavedProps.highlight !== undefined)
8011
- run.setHighlight(runSavedProps.highlight);
8012
- if (runSavedProps.strike !== undefined)
8013
- run.setStrike(runSavedProps.strike);
8014
- if (runSavedProps.subscript !== undefined)
8015
- run.setSubscript(runSavedProps.subscript);
8016
- if (runSavedProps.superscript !== undefined)
8017
- run.setSuperscript(runSavedProps.superscript);
7921
+ if (runSavedProps.highlight !== undefined) run.setHighlight(runSavedProps.highlight);
7922
+ if (runSavedProps.strike !== undefined) run.setStrike(runSavedProps.strike);
7923
+ if (runSavedProps.subscript !== undefined) run.setSubscript(runSavedProps.subscript);
7924
+ if (runSavedProps.superscript !== undefined) run.setSuperscript(runSavedProps.superscript);
8018
7925
  }
8019
7926
  }
8020
7927
 
@@ -8130,7 +8037,7 @@ export class Document {
8130
8037
 
8131
8038
  for (const match of tMatches) {
8132
8039
  hasTableSwitch = true;
8133
- const content = (match[1] || "").trim();
8040
+ const content = (match[1] || '').trim();
8134
8041
  if (!content) continue;
8135
8042
 
8136
8043
  // --- Case 1: Range format "X-Y" ---
@@ -8147,7 +8054,7 @@ export class Document {
8147
8054
  // --- Case 2: Style format "StyleName,Level," (e.g., "Heading 2,2,") ---
8148
8055
  // Split by comma, expect pattern: styleName, level, [optional trailing comma]
8149
8056
  const parts = content
8150
- .split(",")
8057
+ .split(',')
8151
8058
  .map((p) => p.trim())
8152
8059
  .filter(Boolean);
8153
8060
  for (let i = 0; i < parts.length; i += 2) {
@@ -8207,14 +8114,14 @@ export class Document {
8207
8114
 
8208
8115
  // Decode XML entities in instruction
8209
8116
  const fieldInstruction = match[1]
8210
- .replace(/&amp;/g, "&")
8211
- .replace(/&lt;/g, "<")
8212
- .replace(/&gt;/g, ">")
8117
+ .replace(/&amp;/g, '&')
8118
+ .replace(/&lt;/g, '<')
8119
+ .replace(/&gt;/g, '>')
8213
8120
  .replace(/&quot;/g, '"')
8214
8121
  .replace(/&apos;/g, "'");
8215
8122
 
8216
8123
  // Check if instruction contains \t switches (style-specific TOC)
8217
- if (!fieldInstruction.includes("\\t")) {
8124
+ if (!fieldInstruction.includes('\\t')) {
8218
8125
  continue; // Outline-based TOC, no style names to sync
8219
8126
  }
8220
8127
 
@@ -8225,11 +8132,11 @@ export class Document {
8225
8132
  if (updatedInstruction !== fieldInstruction) {
8226
8133
  // Re-encode for XML
8227
8134
  const encodedInstruction = updatedInstruction
8228
- .replace(/&/g, "&amp;")
8229
- .replace(/</g, "&lt;")
8230
- .replace(/>/g, "&gt;")
8231
- .replace(/"/g, "&quot;")
8232
- .replace(/'/g, "&apos;");
8135
+ .replace(/&/g, '&amp;')
8136
+ .replace(/</g, '&lt;')
8137
+ .replace(/>/g, '&gt;')
8138
+ .replace(/"/g, '&quot;')
8139
+ .replace(/'/g, '&apos;');
8233
8140
 
8234
8141
  // Replace the instruction
8235
8142
  const updatedInstrXml = `<w:instrText xml:space="preserve">${encodedInstruction}</w:instrText>`;
@@ -8250,7 +8157,7 @@ export class Document {
8250
8157
  } catch (error: unknown) {
8251
8158
  // Log error but don't fail the save
8252
8159
  this.logger.error(
8253
- "Error syncing TOC field instructions - document will save with original instructions",
8160
+ 'Error syncing TOC field instructions - document will save with original instructions',
8254
8161
  error instanceof Error
8255
8162
  ? { message: error.message, stack: error.stack }
8256
8163
  : { error: String(error) }
@@ -8263,9 +8170,7 @@ export class Document {
8263
8170
  * Helper to sync TOC instruction within an XML fragment
8264
8171
  * @private
8265
8172
  */
8266
- private syncTOCInstructionInXml(
8267
- xml: string
8268
- ): { xml: string; changed: boolean } {
8173
+ private syncTOCInstructionInXml(xml: string): { xml: string; changed: boolean } {
8269
8174
  const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(xml);
8270
8175
  if (!instrMatch?.[1]) {
8271
8176
  return { xml, changed: false };
@@ -8273,14 +8178,14 @@ export class Document {
8273
8178
 
8274
8179
  // Decode XML entities in instruction
8275
8180
  const fieldInstruction = instrMatch[1]
8276
- .replace(/&amp;/g, "&")
8277
- .replace(/&lt;/g, "<")
8278
- .replace(/&gt;/g, ">")
8181
+ .replace(/&amp;/g, '&')
8182
+ .replace(/&lt;/g, '<')
8183
+ .replace(/&gt;/g, '>')
8279
8184
  .replace(/&quot;/g, '"')
8280
8185
  .replace(/&apos;/g, "'");
8281
8186
 
8282
8187
  // Check if instruction contains \t switches (style-specific TOC)
8283
- if (!fieldInstruction.includes("\\t")) {
8188
+ if (!fieldInstruction.includes('\\t')) {
8284
8189
  return { xml, changed: false };
8285
8190
  }
8286
8191
 
@@ -8293,11 +8198,11 @@ export class Document {
8293
8198
 
8294
8199
  // Re-encode for XML
8295
8200
  const encodedInstruction = updatedInstruction
8296
- .replace(/&/g, "&amp;")
8297
- .replace(/</g, "&lt;")
8298
- .replace(/>/g, "&gt;")
8299
- .replace(/"/g, "&quot;")
8300
- .replace(/'/g, "&apos;");
8201
+ .replace(/&/g, '&amp;')
8202
+ .replace(/</g, '&lt;')
8203
+ .replace(/>/g, '&gt;')
8204
+ .replace(/"/g, '&quot;')
8205
+ .replace(/'/g, '&apos;');
8301
8206
 
8302
8207
  const updatedXml = xml.replace(
8303
8208
  /<w:instrText[^>]*>[\s\S]*?<\/w:instrText>/,
@@ -8331,7 +8236,10 @@ export class Document {
8331
8236
 
8332
8237
  // Parse style names and levels from the switch content
8333
8238
  // Format: "StyleName,Level," or "StyleName,Level,StyleName2,Level2,..."
8334
- const parts = content.split(',').map(p => p.trim()).filter(Boolean);
8239
+ const parts = content
8240
+ .split(',')
8241
+ .map((p) => p.trim())
8242
+ .filter(Boolean);
8335
8243
  const updatedParts: string[] = [];
8336
8244
 
8337
8245
  for (let i = 0; i < parts.length; i += 2) {
@@ -8496,31 +8404,30 @@ export class Document {
8496
8404
  docXml: string,
8497
8405
  levels: number[]
8498
8406
  ): { level: number; text: string; bookmark: string }[] {
8499
- const headings: { level: number; text: string; bookmark: string }[] =
8500
- [];
8407
+ const headings: { level: number; text: string; bookmark: string }[] = [];
8501
8408
  const levelSet = new Set(levels);
8502
8409
 
8503
8410
  try {
8504
8411
  // Parse document.xml to object structure
8505
8412
  const parsed = XMLParser.parseToObject(docXml, { trimValues: false });
8506
- const document = parsed["w:document"];
8413
+ const document = parsed['w:document'];
8507
8414
  if (!document) {
8508
8415
  return headings;
8509
8416
  }
8510
8417
 
8511
- const body = (document as any)["w:body"];
8418
+ const body = (document as any)['w:body'];
8512
8419
  if (!body) {
8513
8420
  return headings;
8514
8421
  }
8515
8422
 
8516
8423
  // Helper function to extract heading info from a parsed paragraph object
8517
8424
  const extractHeading = (para: any): void => {
8518
- const pPr = para["w:pPr"];
8519
- if (!pPr?.["w:pStyle"]) {
8425
+ const pPr = para['w:pPr'];
8426
+ if (!pPr?.['w:pStyle']) {
8520
8427
  return;
8521
8428
  }
8522
8429
 
8523
- const styleVal = pPr["w:pStyle"]["@_w:val"];
8430
+ const styleVal = pPr['w:pStyle']['@_w:val'];
8524
8431
  if (!styleVal) {
8525
8432
  return;
8526
8433
  }
@@ -8539,26 +8446,27 @@ export class Document {
8539
8446
  }
8540
8447
 
8541
8448
  // Extract bookmark (use any existing bookmark, prioritize "_heading" or "_Toc")
8542
- let bookmark = "";
8543
- const bookmarkStart = para["w:bookmarkStart"];
8449
+ let bookmark = '';
8450
+ const bookmarkStart = para['w:bookmarkStart'];
8544
8451
  if (bookmarkStart) {
8545
- const bookmarkArray = Array.isArray(bookmarkStart)
8546
- ? bookmarkStart
8547
- : [bookmarkStart];
8548
-
8452
+ const bookmarkArray = Array.isArray(bookmarkStart) ? bookmarkStart : [bookmarkStart];
8453
+
8549
8454
  // First try to find preferred bookmark types
8550
8455
  for (const bm of bookmarkArray) {
8551
- const bmName = bm["@_w:name"];
8552
- if (bmName && (bmName.toLowerCase().includes("_heading") || bmName.toLowerCase().includes("_toc"))) {
8456
+ const bmName = bm['@_w:name'];
8457
+ if (
8458
+ bmName &&
8459
+ (bmName.toLowerCase().includes('_heading') || bmName.toLowerCase().includes('_toc'))
8460
+ ) {
8553
8461
  bookmark = bmName;
8554
8462
  break;
8555
8463
  }
8556
8464
  }
8557
-
8465
+
8558
8466
  // If no preferred bookmark found, use the first available bookmark
8559
8467
  if (!bookmark && bookmarkArray.length > 0) {
8560
8468
  const firstBm = bookmarkArray[0];
8561
- const bmName = firstBm["@_w:name"];
8469
+ const bmName = firstBm['@_w:name'];
8562
8470
  if (bmName) {
8563
8471
  bookmark = bmName;
8564
8472
  }
@@ -8566,17 +8474,17 @@ export class Document {
8566
8474
  }
8567
8475
 
8568
8476
  // Extract text from runs
8569
- let text = "";
8570
- const runs = para["w:r"];
8477
+ let text = '';
8478
+ const runs = para['w:r'];
8571
8479
  if (runs) {
8572
8480
  const runArray = Array.isArray(runs) ? runs : [runs];
8573
8481
  for (const run of runArray) {
8574
- const textElement = run["w:t"];
8482
+ const textElement = run['w:t'];
8575
8483
  if (textElement) {
8576
- if (typeof textElement === "string") {
8484
+ if (typeof textElement === 'string') {
8577
8485
  text += textElement;
8578
- } else if (textElement["#text"]) {
8579
- text += textElement["#text"];
8486
+ } else if (textElement['#text']) {
8487
+ text += textElement['#text'];
8580
8488
  }
8581
8489
  }
8582
8490
  }
@@ -8601,7 +8509,7 @@ export class Document {
8601
8509
  };
8602
8510
 
8603
8511
  // Search in direct paragraphs
8604
- const paragraphs = body["w:p"];
8512
+ const paragraphs = body['w:p'];
8605
8513
  if (paragraphs) {
8606
8514
  const paraArray = Array.isArray(paragraphs) ? paragraphs : [paragraphs];
8607
8515
  for (const para of paraArray) {
@@ -8610,26 +8518,24 @@ export class Document {
8610
8518
  }
8611
8519
 
8612
8520
  // Search in tables (this is critical - many documents have headings in tables)
8613
- const tables = body["w:tbl"];
8521
+ const tables = body['w:tbl'];
8614
8522
  if (tables) {
8615
8523
  const tableArray = Array.isArray(tables) ? tables : [tables];
8616
8524
  for (const table of tableArray) {
8617
- const rows = table["w:tr"];
8525
+ const rows = table['w:tr'];
8618
8526
  if (!rows) continue;
8619
8527
 
8620
8528
  const rowArray = Array.isArray(rows) ? rows : [rows];
8621
8529
  for (const row of rowArray) {
8622
- const cells = row["w:tc"];
8530
+ const cells = row['w:tc'];
8623
8531
  if (!cells) continue;
8624
8532
 
8625
8533
  const cellArray = Array.isArray(cells) ? cells : [cells];
8626
8534
  for (const cell of cellArray) {
8627
- const cellParas = cell["w:p"];
8535
+ const cellParas = cell['w:p'];
8628
8536
  if (!cellParas) continue;
8629
8537
 
8630
- const cellParaArray = Array.isArray(cellParas)
8631
- ? cellParas
8632
- : [cellParas];
8538
+ const cellParaArray = Array.isArray(cellParas) ? cellParas : [cellParas];
8633
8539
  for (const para of cellParaArray) {
8634
8540
  extractHeading(para);
8635
8541
  }
@@ -8639,7 +8545,7 @@ export class Document {
8639
8545
  }
8640
8546
  } catch (error: unknown) {
8641
8547
  defaultLogger.error(
8642
- "Error parsing document.xml for headings:",
8548
+ 'Error parsing document.xml for headings:',
8643
8549
  error instanceof Error
8644
8550
  ? { message: error.message, stack: error.stack }
8645
8551
  : { error: String(error) }
@@ -8657,8 +8563,7 @@ export class Document {
8657
8563
  private findHeadingsForTOC(
8658
8564
  levels: number[]
8659
8565
  ): { level: number; text: string; bookmark: string }[] {
8660
- const headings: { level: number; text: string; bookmark: string }[] =
8661
- [];
8566
+ const headings: { level: number; text: string; bookmark: string }[] = [];
8662
8567
  const levelSet = new Set(levels);
8663
8568
 
8664
8569
  // Iterate through body elements
@@ -8679,8 +8584,7 @@ export class Document {
8679
8584
 
8680
8585
  if (text) {
8681
8586
  // Create or get bookmark for this heading
8682
- const bookmark =
8683
- this.bookmarkManager.createHeadingBookmark(text);
8587
+ const bookmark = this.bookmarkManager.createHeadingBookmark(text);
8684
8588
 
8685
8589
  headings.push({
8686
8590
  level: headingLevel,
@@ -8715,30 +8619,28 @@ export class Document {
8715
8619
  ): string {
8716
8620
  const sdtId = Math.floor(Math.random() * 2000000000) - 1000000000;
8717
8621
 
8718
- let tocXml = "<w:sdt>";
8622
+ let tocXml = '<w:sdt>';
8719
8623
 
8720
8624
  // SDT properties
8721
- tocXml += "<w:sdtPr>";
8625
+ tocXml += '<w:sdtPr>';
8722
8626
  tocXml += `<w:id w:val="${sdtId}"/>`;
8723
- tocXml += "<w:docPartObj>";
8627
+ tocXml += '<w:docPartObj>';
8724
8628
  tocXml += '<w:docPartGallery w:val="Table of Contents"/>';
8725
8629
  tocXml += '<w:docPartUnique w:val="1"/>';
8726
- tocXml += "</w:docPartObj>";
8727
- tocXml += "</w:sdtPr>";
8630
+ tocXml += '</w:docPartObj>';
8631
+ tocXml += '</w:sdtPr>';
8728
8632
 
8729
8633
  // SDT content
8730
- tocXml += "<w:sdtContent>";
8634
+ tocXml += '<w:sdtContent>';
8731
8635
 
8732
8636
  // Calculate minimum level for relative indentation
8733
8637
  // If TOC shows only Heading 2s, minLevel=2, so Heading 2 gets 0" indent
8734
- const minLevel =
8735
- headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
8638
+ const minLevel = headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
8736
8639
 
8737
8640
  // First paragraph: field begin + instruction + separator + first entry (if any)
8738
- tocXml += "<w:p>";
8739
- tocXml += "<w:pPr>";
8740
- tocXml +=
8741
- '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8641
+ tocXml += '<w:p>';
8642
+ tocXml += '<w:pPr>';
8643
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8742
8644
 
8743
8645
  // Add indentation for first entry relative to minimum level (0.25" per level)
8744
8646
  if (headings.length > 0 && headings[0]) {
@@ -8748,17 +8650,17 @@ export class Document {
8748
8650
  }
8749
8651
  }
8750
8652
 
8751
- tocXml += "</w:pPr>";
8653
+ tocXml += '</w:pPr>';
8752
8654
 
8753
8655
  // Field begin
8754
8656
  tocXml += '<w:r><w:fldChar w:fldCharType="begin"/></w:r>';
8755
8657
 
8756
8658
  // Field instruction (preserve original switches)
8757
- tocXml += "<w:r>";
8659
+ tocXml += '<w:r>';
8758
8660
  tocXml += `<w:instrText xml:space="preserve">${this.escapeXml(
8759
8661
  originalInstrText
8760
8662
  )}</w:instrText>`;
8761
- tocXml += "</w:r>";
8663
+ tocXml += '</w:r>';
8762
8664
 
8763
8665
  // Field separator
8764
8666
  tocXml += '<w:r><w:fldChar w:fldCharType="separate"/></w:r>';
@@ -8768,17 +8670,16 @@ export class Document {
8768
8670
  tocXml += this.buildTOCEntryXML(headings[0]);
8769
8671
  }
8770
8672
 
8771
- tocXml += "</w:p>";
8673
+ tocXml += '</w:p>';
8772
8674
 
8773
8675
  // Remaining entries (each in its own paragraph)
8774
8676
  for (let i = 1; i < headings.length; i++) {
8775
8677
  const heading = headings[i];
8776
8678
  if (!heading) continue;
8777
8679
 
8778
- tocXml += "<w:p>";
8779
- tocXml += "<w:pPr>";
8780
- tocXml +=
8781
- '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8680
+ tocXml += '<w:p>';
8681
+ tocXml += '<w:pPr>';
8682
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8782
8683
 
8783
8684
  // Add indentation relative to minimum level (0.25" per level above minimum)
8784
8685
  const indent = (heading.level - minLevel) * 360;
@@ -8786,22 +8687,21 @@ export class Document {
8786
8687
  tocXml += `<w:ind w:left="${indent}"/>`;
8787
8688
  }
8788
8689
 
8789
- tocXml += "</w:pPr>";
8690
+ tocXml += '</w:pPr>';
8790
8691
  tocXml += this.buildTOCEntryXML(heading);
8791
- tocXml += "</w:p>";
8692
+ tocXml += '</w:p>';
8792
8693
  }
8793
8694
 
8794
8695
  // Final paragraph with field end
8795
- tocXml += "<w:p>";
8796
- tocXml += "<w:pPr>";
8797
- tocXml +=
8798
- '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8799
- tocXml += "</w:pPr>";
8696
+ tocXml += '<w:p>';
8697
+ tocXml += '<w:pPr>';
8698
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8699
+ tocXml += '</w:pPr>';
8800
8700
  tocXml += '<w:r><w:fldChar w:fldCharType="end"/></w:r>';
8801
- tocXml += "</w:p>";
8701
+ tocXml += '</w:p>';
8802
8702
 
8803
- tocXml += "</w:sdtContent>";
8804
- tocXml += "</w:sdt>";
8703
+ tocXml += '</w:sdtContent>';
8704
+ tocXml += '</w:sdt>';
8805
8705
 
8806
8706
  return tocXml;
8807
8707
  }
@@ -8812,27 +8712,22 @@ export class Document {
8812
8712
  * @param heading Heading information
8813
8713
  * @returns XML string for the TOC entry
8814
8714
  */
8815
- private buildTOCEntryXML(heading: {
8816
- level: number;
8817
- text: string;
8818
- bookmark: string;
8819
- }): string {
8715
+ private buildTOCEntryXML(heading: { level: number; text: string; bookmark: string }): string {
8820
8716
  const escapedText = this.escapeXml(heading.text);
8821
8717
 
8822
- let xml = "";
8718
+ let xml = '';
8823
8719
  xml += `<w:hyperlink w:anchor="${this.escapeXml(heading.bookmark)}">`;
8824
- xml += "<w:r>";
8825
- xml += "<w:rPr>";
8826
- xml +=
8827
- '<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
8720
+ xml += '<w:r>';
8721
+ xml += '<w:rPr>';
8722
+ xml += '<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
8828
8723
  xml += '<w:color w:val="0000FF"/>';
8829
8724
  xml += '<w:sz w:val="24"/>';
8830
8725
  xml += '<w:szCs w:val="24"/>';
8831
8726
  xml += '<w:u w:val="single"/>';
8832
- xml += "</w:rPr>";
8727
+ xml += '</w:rPr>';
8833
8728
  xml += `<w:t xml:space="preserve">${escapedText}</w:t>`;
8834
- xml += "</w:r>";
8835
- xml += "</w:hyperlink>";
8729
+ xml += '</w:r>';
8730
+ xml += '</w:hyperlink>';
8836
8731
 
8837
8732
  return xml;
8838
8733
  }
@@ -8845,11 +8740,11 @@ export class Document {
8845
8740
  */
8846
8741
  private escapeXml(text: string): string {
8847
8742
  return text
8848
- .replace(/&/g, "&amp;")
8849
- .replace(/</g, "&lt;")
8850
- .replace(/>/g, "&gt;")
8851
- .replace(/"/g, "&quot;")
8852
- .replace(/'/g, "&apos;");
8743
+ .replace(/&/g, '&amp;')
8744
+ .replace(/</g, '&lt;')
8745
+ .replace(/>/g, '&gt;')
8746
+ .replace(/"/g, '&quot;')
8747
+ .replace(/'/g, '&apos;');
8853
8748
  }
8854
8749
 
8855
8750
  /**
@@ -8866,7 +8761,7 @@ export class Document {
8866
8761
  await handler.load(filePath);
8867
8762
 
8868
8763
  // Get document.xml
8869
- const docXml = handler.getFileAsString("word/document.xml");
8764
+ const docXml = handler.getFileAsString('word/document.xml');
8870
8765
  if (!docXml) {
8871
8766
  return 0;
8872
8767
  }
@@ -8876,7 +8771,7 @@ export class Document {
8876
8771
 
8877
8772
  // Update and save if changes were made
8878
8773
  if (modifiedXml !== docXml) {
8879
- handler.updateFile("word/document.xml", modifiedXml);
8774
+ handler.updateFile('word/document.xml', modifiedXml);
8880
8775
  await handler.save(filePath);
8881
8776
 
8882
8777
  // Count TOCs that were populated
@@ -8913,9 +8808,9 @@ export class Document {
8913
8808
  if (!instrMatch?.[1]) continue;
8914
8809
 
8915
8810
  const fieldInstruction = instrMatch[1]
8916
- .replace(/&amp;/g, "&")
8917
- .replace(/&lt;/g, "<")
8918
- .replace(/&gt;/g, ">")
8811
+ .replace(/&amp;/g, '&')
8812
+ .replace(/&lt;/g, '<')
8813
+ .replace(/&gt;/g, '>')
8919
8814
  .replace(/&quot;/g, '"')
8920
8815
  .replace(/&apos;/g, "'");
8921
8816
 
@@ -8927,7 +8822,7 @@ export class Document {
8927
8822
  modifiedXml = modifiedXml.replace(tocXml, newTocXml);
8928
8823
  } catch (error: unknown) {
8929
8824
  this.logger.error(
8930
- "Error populating SDT TOC",
8825
+ 'Error populating SDT TOC',
8931
8826
  error instanceof Error
8932
8827
  ? { message: error.message, stack: error.stack }
8933
8828
  : { error: String(error) }
@@ -8952,9 +8847,9 @@ export class Document {
8952
8847
  if (!instrText) continue;
8953
8848
 
8954
8849
  const fieldInstruction = instrText
8955
- .replace(/&amp;/g, "&")
8956
- .replace(/&lt;/g, "<")
8957
- .replace(/&gt;/g, ">")
8850
+ .replace(/&amp;/g, '&')
8851
+ .replace(/&lt;/g, '<')
8852
+ .replace(/&gt;/g, '>')
8958
8853
  .replace(/&quot;/g, '"')
8959
8854
  .replace(/&apos;/g, "'");
8960
8855
 
@@ -8989,12 +8884,10 @@ export class Document {
8989
8884
  const newTocXml = this.generateSimpleTOCXML(headings, fieldInstruction);
8990
8885
  modifiedXml = modifiedXml.replace(fullTocXml, newTocXml);
8991
8886
 
8992
- this.logger.info(
8993
- `Populated simple TOC with ${headings.length} heading entries`
8994
- );
8887
+ this.logger.info(`Populated simple TOC with ${headings.length} heading entries`);
8995
8888
  } catch (error: unknown) {
8996
8889
  this.logger.error(
8997
- "Error populating simple TOC",
8890
+ 'Error populating simple TOC',
8998
8891
  error instanceof Error
8999
8892
  ? { message: error.message, stack: error.stack }
9000
8893
  : { error: String(error) }
@@ -9029,29 +8922,29 @@ export class Document {
9029
8922
 
9030
8923
  // Escape text for XML
9031
8924
  const escapedText = heading.text
9032
- .replace(/&/g, "&amp;")
9033
- .replace(/</g, "&lt;")
9034
- .replace(/>/g, "&gt;")
9035
- .replace(/"/g, "&quot;");
8925
+ .replace(/&/g, '&amp;')
8926
+ .replace(/</g, '&lt;')
8927
+ .replace(/>/g, '&gt;')
8928
+ .replace(/"/g, '&quot;');
9036
8929
 
9037
8930
  // Create hyperlinked TOC entry paragraph
9038
8931
  entries.push(
9039
8932
  `<w:p>` +
9040
- `<w:pPr><w:pStyle w:val="${tocStyle}"/></w:pPr>` +
9041
- `<w:hyperlink w:anchor="${heading.bookmark}" w:history="1">` +
9042
- `<w:r><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr>` +
9043
- `<w:t>${escapedText}</w:t></w:r>` +
9044
- `</w:hyperlink>` +
9045
- `</w:p>`
8933
+ `<w:pPr><w:pStyle w:val="${tocStyle}"/></w:pPr>` +
8934
+ `<w:hyperlink w:anchor="${heading.bookmark}" w:history="1">` +
8935
+ `<w:r><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr>` +
8936
+ `<w:t>${escapedText}</w:t></w:r>` +
8937
+ `</w:hyperlink>` +
8938
+ `</w:p>`
9046
8939
  );
9047
8940
  }
9048
8941
 
9049
8942
  // Escape field instruction for XML
9050
8943
  const escapedInstruction = fieldInstruction
9051
- .replace(/&/g, "&amp;")
9052
- .replace(/</g, "&lt;")
9053
- .replace(/>/g, "&gt;")
9054
- .replace(/"/g, "&quot;");
8944
+ .replace(/&/g, '&amp;')
8945
+ .replace(/</g, '&lt;')
8946
+ .replace(/>/g, '&gt;')
8947
+ .replace(/"/g, '&quot;');
9055
8948
 
9056
8949
  // Build the complete TOC field structure
9057
8950
  return (
@@ -9063,7 +8956,7 @@ export class Document {
9063
8956
  `<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
9064
8957
  `</w:p>` +
9065
8958
  // TOC entry paragraphs (between separate and end)
9066
- entries.join("") +
8959
+ entries.join('') +
9067
8960
  // Last paragraph: fldChar end
9068
8961
  `<w:p>` +
9069
8962
  `<w:pPr><w:pStyle w:val="TOC2"/></w:pPr>` +
@@ -9127,11 +9020,7 @@ export class Document {
9127
9020
  * @param orientation Page orientation
9128
9021
  * @returns This document for chaining
9129
9022
  */
9130
- setPageSize(
9131
- width: number,
9132
- height: number,
9133
- orientation?: "portrait" | "landscape"
9134
- ): this {
9023
+ setPageSize(width: number, height: number, orientation?: 'portrait' | 'landscape'): this {
9135
9024
  this.section.setPageSize(width, height, orientation);
9136
9025
  return this;
9137
9026
  }
@@ -9141,7 +9030,7 @@ export class Document {
9141
9030
  * @param orientation Page orientation
9142
9031
  * @returns This document for chaining
9143
9032
  */
9144
- setPageOrientation(orientation: "portrait" | "landscape"): this {
9033
+ setPageOrientation(orientation: 'portrait' | 'landscape'): this {
9145
9034
  this.section.setOrientation(orientation);
9146
9035
  return this;
9147
9036
  }
@@ -9171,15 +9060,13 @@ export class Document {
9171
9060
  */
9172
9061
  setHeader(header: Header): this {
9173
9062
  // Generate relationship for header
9174
- const relationship = this.relationshipManager.addHeader(
9175
- `${header.getFilename(1)}`
9176
- );
9063
+ const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
9177
9064
 
9178
9065
  // Register with manager
9179
9066
  this.headerFooterManager.registerHeader(header, relationship.getId());
9180
9067
 
9181
9068
  // Link to section
9182
- this.section.setHeaderReference("default", relationship.getId());
9069
+ this.section.setHeaderReference('default', relationship.getId());
9183
9070
 
9184
9071
  return this;
9185
9072
  }
@@ -9202,7 +9089,7 @@ export class Document {
9202
9089
  this.headerFooterManager.registerHeader(header, relationship.getId());
9203
9090
 
9204
9091
  // Link to section
9205
- this.section.setHeaderReference("first", relationship.getId());
9092
+ this.section.setHeaderReference('first', relationship.getId());
9206
9093
 
9207
9094
  return this;
9208
9095
  }
@@ -9222,7 +9109,7 @@ export class Document {
9222
9109
  this.headerFooterManager.registerHeader(header, relationship.getId());
9223
9110
 
9224
9111
  // Link to section
9225
- this.section.setHeaderReference("even", relationship.getId());
9112
+ this.section.setHeaderReference('even', relationship.getId());
9226
9113
 
9227
9114
  return this;
9228
9115
  }
@@ -9234,15 +9121,13 @@ export class Document {
9234
9121
  */
9235
9122
  setFooter(footer: Footer): this {
9236
9123
  // Generate relationship for footer
9237
- const relationship = this.relationshipManager.addFooter(
9238
- `${footer.getFilename(1)}`
9239
- );
9124
+ const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
9240
9125
 
9241
9126
  // Register with manager
9242
9127
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9243
9128
 
9244
9129
  // Link to section
9245
- this.section.setFooterReference("default", relationship.getId());
9130
+ this.section.setFooterReference('default', relationship.getId());
9246
9131
 
9247
9132
  return this;
9248
9133
  }
@@ -9265,7 +9150,7 @@ export class Document {
9265
9150
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9266
9151
 
9267
9152
  // Link to section
9268
- this.section.setFooterReference("first", relationship.getId());
9153
+ this.section.setFooterReference('first', relationship.getId());
9269
9154
 
9270
9155
  return this;
9271
9156
  }
@@ -9285,7 +9170,7 @@ export class Document {
9285
9170
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9286
9171
 
9287
9172
  // Link to section
9288
- this.section.setFooterReference("even", relationship.getId());
9173
+ this.section.setFooterReference('even', relationship.getId());
9289
9174
 
9290
9175
  return this;
9291
9176
  }
@@ -9305,7 +9190,7 @@ export class Document {
9305
9190
  * @param type Header type to remove (default, first, even)
9306
9191
  * @returns This document for chaining
9307
9192
  */
9308
- removeHeader(type: "default" | "first" | "even"): this {
9193
+ removeHeader(type: 'default' | 'first' | 'even'): this {
9309
9194
  const sectionProps = this.section.getProperties();
9310
9195
 
9311
9196
  // Get the relationship ID from section properties
@@ -9351,7 +9236,7 @@ export class Document {
9351
9236
  * @param type Footer type to remove (default, first, even)
9352
9237
  * @returns This document for chaining
9353
9238
  */
9354
- removeFooter(type: "default" | "first" | "even"): this {
9239
+ removeFooter(type: 'default' | 'first' | 'even'): this {
9355
9240
  const sectionProps = this.section.getProperties();
9356
9241
 
9357
9242
  // Get the relationship ID from section properties
@@ -9400,7 +9285,7 @@ export class Document {
9400
9285
 
9401
9286
  // Remove each header type
9402
9287
  if (sectionProps.headers) {
9403
- const types = Object.keys(sectionProps.headers) as ("default" | "first" | "even")[];
9288
+ const types = Object.keys(sectionProps.headers) as ('default' | 'first' | 'even')[];
9404
9289
  for (const type of types) {
9405
9290
  this.removeHeader(type);
9406
9291
  }
@@ -9422,7 +9307,7 @@ export class Document {
9422
9307
 
9423
9308
  // Remove each footer type
9424
9309
  if (sectionProps.footers) {
9425
- const types = Object.keys(sectionProps.footers) as ("default" | "first" | "even")[];
9310
+ const types = Object.keys(sectionProps.footers) as ('default' | 'first' | 'even')[];
9426
9311
  for (const type of types) {
9427
9312
  this.removeFooter(type);
9428
9313
  }
@@ -9441,9 +9326,7 @@ export class Document {
9441
9326
  */
9442
9327
  addImage(image: Image): this {
9443
9328
  // Generate relationship ID
9444
- const target = `media/image${
9445
- this.imageManager.getImageCount() + 1
9446
- }.${image.getExtension()}`;
9329
+ const target = `media/image${this.imageManager.getImageCount() + 1}.${image.getExtension()}`;
9447
9330
  const relationship = this.relationshipManager.addImage(target);
9448
9331
 
9449
9332
  // Register image with manager
@@ -9555,7 +9438,7 @@ export class Document {
9555
9438
  */
9556
9439
  private updateRelationships(): void {
9557
9440
  const xml = this.relationshipManager.generateXml();
9558
- this.zipHandler.updateFile("word/_rels/document.xml.rels", xml);
9441
+ this.zipHandler.updateFile('word/_rels/document.xml.rels', xml);
9559
9442
  }
9560
9443
 
9561
9444
  /**
@@ -9566,26 +9449,29 @@ export class Document {
9566
9449
  // Comments were modified — regenerate from in-memory model
9567
9450
  if (this.commentManager.getCount() > 0) {
9568
9451
  const xml = this.commentManager.generateCommentsXml();
9569
- this.zipHandler.addFile("word/comments.xml", xml);
9452
+ this.zipHandler.addFile('word/comments.xml', xml);
9570
9453
 
9571
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9454
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9455
+ RelationshipType.COMMENTS
9456
+ );
9572
9457
  if (existingRels.length === 0) {
9573
9458
  this.relationshipManager.addComments();
9574
9459
  }
9575
9460
  } else {
9576
9461
  // All comments removed — clean up comments.xml and its relationship
9577
- this.zipHandler.removeFile("word/comments.xml");
9578
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9462
+ this.zipHandler.removeFile('word/comments.xml');
9463
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9464
+ RelationshipType.COMMENTS
9465
+ );
9579
9466
  for (const rel of existingRels) {
9580
9467
  this.relationshipManager.removeRelationship(rel.getId());
9581
9468
  }
9582
9469
  }
9583
9470
  // Remove companion files since regenerated comments lack w14:paraId
9584
9471
  this.removeCommentCompanionFiles();
9585
-
9586
9472
  } else if (this._originalCommentsXml) {
9587
9473
  // Passthrough — preserve original comments.xml exactly
9588
- this.zipHandler.addFile("word/comments.xml", this._originalCommentsXml);
9474
+ this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
9589
9475
 
9590
9476
  // Preserve companion files as-is
9591
9477
  for (const [path, content] of this._originalCommentCompanionFiles) {
@@ -9593,17 +9479,20 @@ export class Document {
9593
9479
  }
9594
9480
 
9595
9481
  // Ensure comments relationship exists
9596
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9482
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9483
+ RelationshipType.COMMENTS
9484
+ );
9597
9485
  if (existingRels.length === 0) {
9598
9486
  this.relationshipManager.addComments();
9599
9487
  }
9600
-
9601
9488
  } else if (this.commentManager.getCount() > 0) {
9602
9489
  // New comments created on a document that had no original comments
9603
9490
  const xml = this.commentManager.generateCommentsXml();
9604
- this.zipHandler.addFile("word/comments.xml", xml);
9491
+ this.zipHandler.addFile('word/comments.xml', xml);
9605
9492
 
9606
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9493
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9494
+ RelationshipType.COMMENTS
9495
+ );
9607
9496
  if (existingRels.length === 0) {
9608
9497
  this.relationshipManager.addComments();
9609
9498
  }
@@ -9611,7 +9500,11 @@ export class Document {
9611
9500
  }
9612
9501
 
9613
9502
  private removeCommentCompanionFiles(): void {
9614
- const companionPaths = [DOCX_PATHS.COMMENTS_EXTENDED, DOCX_PATHS.COMMENTS_IDS, DOCX_PATHS.COMMENTS_EXTENSIBLE];
9503
+ const companionPaths = [
9504
+ DOCX_PATHS.COMMENTS_EXTENDED,
9505
+ DOCX_PATHS.COMMENTS_IDS,
9506
+ DOCX_PATHS.COMMENTS_EXTENSIBLE,
9507
+ ];
9615
9508
  for (const filePath of companionPaths) {
9616
9509
  this.zipHandler.removeFile(filePath);
9617
9510
  const fileName = filePath.replace('word/', '');
@@ -9628,7 +9521,9 @@ export class Document {
9628
9521
  const xml = this.footnoteManager.generateFootnotesXml();
9629
9522
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
9630
9523
 
9631
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
9524
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9525
+ RelationshipType.FOOTNOTES
9526
+ );
9632
9527
  if (existingRels.length === 0) {
9633
9528
  this.relationshipManager.addFootnotes();
9634
9529
  }
@@ -9636,14 +9531,16 @@ export class Document {
9636
9531
  // Per OOXML, relationships are part-scoped: hyperlinks in footnotes.xml
9637
9532
  // need word/_rels/footnotes.xml.rels, not word/_rels/document.xml.rels
9638
9533
  this.generatePartLevelHyperlinkRels(
9639
- this.footnoteManager.getAllFootnotes().flatMap(fn => fn.getParagraphs()),
9534
+ this.footnoteManager.getAllFootnotes().flatMap((fn) => fn.getParagraphs()),
9640
9535
  'word/_rels/footnotes.xml.rels'
9641
9536
  );
9642
9537
  } else if (this._originalFootnotesXml) {
9643
9538
  // Passthrough — preserve original XML exactly
9644
9539
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, this._originalFootnotesXml);
9645
9540
 
9646
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
9541
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9542
+ RelationshipType.FOOTNOTES
9543
+ );
9647
9544
  if (existingRels.length === 0) {
9648
9545
  this.relationshipManager.addFootnotes();
9649
9546
  }
@@ -9655,7 +9552,9 @@ export class Document {
9655
9552
  const xml = this.endnoteManager.generateEndnotesXml();
9656
9553
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
9657
9554
 
9658
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
9555
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9556
+ RelationshipType.ENDNOTES
9557
+ );
9659
9558
  if (existingRels.length === 0) {
9660
9559
  this.relationshipManager.addEndnotes();
9661
9560
  }
@@ -9663,14 +9562,16 @@ export class Document {
9663
9562
  // Per OOXML, relationships are part-scoped: hyperlinks in endnotes.xml
9664
9563
  // need word/_rels/endnotes.xml.rels, not word/_rels/document.xml.rels
9665
9564
  this.generatePartLevelHyperlinkRels(
9666
- this.endnoteManager.getAllEndnotes().flatMap(en => en.getParagraphs()),
9565
+ this.endnoteManager.getAllEndnotes().flatMap((en) => en.getParagraphs()),
9667
9566
  'word/_rels/endnotes.xml.rels'
9668
9567
  );
9669
9568
  } else if (this._originalEndnotesXml) {
9670
9569
  // Passthrough — preserve original XML exactly
9671
9570
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, this._originalEndnotesXml);
9672
9571
 
9673
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
9572
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9573
+ RelationshipType.ENDNOTES
9574
+ );
9674
9575
  if (existingRels.length === 0) {
9675
9576
  this.relationshipManager.addEndnotes();
9676
9577
  }
@@ -9733,7 +9634,8 @@ export class Document {
9733
9634
 
9734
9635
  if (hyperlinkRels.length > 0) {
9735
9636
  let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
9736
- xml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
9637
+ xml +=
9638
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
9737
9639
  for (const rel of hyperlinkRels) {
9738
9640
  const escapedId = XMLBuilder.escapeXmlAttribute(rel.id);
9739
9641
  const escapedUrl = XMLBuilder.escapeXmlAttribute(rel.url);
@@ -9814,14 +9716,23 @@ export class Document {
9814
9716
  if (missingAuthors.length === 0) return;
9815
9717
 
9816
9718
  // Build person elements for missing authors
9817
- const personElements = missingAuthors.map(author => {
9818
- const escapedAuthor = author.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
9819
- return `<w15:person w15:author="${escapedAuthor}"><w15:presenceInfo w15:providerId="None" w15:userId="${escapedAuthor}"/></w15:person>`;
9820
- }).join('');
9719
+ const personElements = missingAuthors
9720
+ .map((author) => {
9721
+ const escapedAuthor = author
9722
+ .replace(/&/g, '&amp;')
9723
+ .replace(/"/g, '&quot;')
9724
+ .replace(/</g, '&lt;')
9725
+ .replace(/>/g, '&gt;');
9726
+ return `<w15:person w15:author="${escapedAuthor}"><w15:presenceInfo w15:providerId="None" w15:userId="${escapedAuthor}"/></w15:person>`;
9727
+ })
9728
+ .join('');
9821
9729
 
9822
9730
  if (existingPeopleXml) {
9823
9731
  // Insert new person elements before closing tag
9824
- const updatedXml = existingPeopleXml.replace('</w15:people>', `${personElements}</w15:people>`);
9732
+ const updatedXml = existingPeopleXml.replace(
9733
+ '</w15:people>',
9734
+ `${personElements}</w15:people>`
9735
+ );
9825
9736
  this.zipHandler.updateFile('word/people.xml', updatedXml);
9826
9737
  } else {
9827
9738
  // Create new people.xml
@@ -9836,7 +9747,10 @@ export class Document {
9836
9747
  this.relationshipManager.addPeople();
9837
9748
  }
9838
9749
 
9839
- this.logger.info('Updated people.xml with missing authors', { added: missingAuthors.length, authors: missingAuthors });
9750
+ this.logger.info('Updated people.xml with missing authors', {
9751
+ added: missingAuthors.length,
9752
+ authors: missingAuthors,
9753
+ });
9840
9754
  }
9841
9755
 
9842
9756
  /**
@@ -9880,11 +9794,9 @@ export class Document {
9880
9794
  this.properties.customProperties &&
9881
9795
  Object.keys(this.properties.customProperties).length > 0
9882
9796
  ) {
9883
- const customXml = this.generator.generateCustomProps(
9884
- this.properties.customProperties
9885
- );
9797
+ const customXml = this.generator.generateCustomProps(this.properties.customProperties);
9886
9798
  if (customXml) {
9887
- this.zipHandler.addFile("docProps/custom.xml", customXml);
9799
+ this.zipHandler.addFile('docProps/custom.xml', customXml);
9888
9800
  }
9889
9801
  }
9890
9802
  }
@@ -9901,13 +9813,17 @@ export class Document {
9901
9813
  const overrides = new Set<string>();
9902
9814
 
9903
9815
  // Extract all <Default Extension="..." ContentType="..."/> entries
9904
- const defaultMatches = xml.matchAll(/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
9816
+ const defaultMatches = xml.matchAll(
9817
+ /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
9818
+ );
9905
9819
  for (const match of defaultMatches) {
9906
9820
  defaults.add(`${match[1]}|${match[2]}`); // Store as "ext|mimetype"
9907
9821
  }
9908
9822
 
9909
9823
  // Extract all <Override PartName="..." ContentType="..."/> entries
9910
- const overrideMatches = xml.matchAll(/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
9824
+ const overrideMatches = xml.matchAll(
9825
+ /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
9826
+ );
9911
9827
  for (const match of overrideMatches) {
9912
9828
  overrides.add(`${match[1]}|${match[2]}`); // Store as "path|mimetype"
9913
9829
  }
@@ -9921,21 +9837,19 @@ export class Document {
9921
9837
  */
9922
9838
  private updateContentTypesWithImagesHeadersFootersAndComments(): void {
9923
9839
  const hasCustomProps =
9924
- this.properties.customProperties &&
9925
- Object.keys(this.properties.customProperties).length > 0;
9926
-
9927
- const contentTypes =
9928
- this.generator.generateContentTypesWithImagesHeadersFootersAndComments(
9929
- this.imageManager,
9930
- this.headerFooterManager,
9931
- this.commentManager,
9932
- this.zipHandler, // Pass zipHandler to check file existence
9933
- undefined, // fontManager (optional)
9934
- hasCustomProps, // Flag to include custom.xml override
9935
- this._originalContentTypes, // Pass preserved original entries for round-trip fidelity
9936
- this.footnoteManager,
9937
- this.endnoteManager
9938
- );
9840
+ this.properties.customProperties && Object.keys(this.properties.customProperties).length > 0;
9841
+
9842
+ const contentTypes = this.generator.generateContentTypesWithImagesHeadersFootersAndComments(
9843
+ this.imageManager,
9844
+ this.headerFooterManager,
9845
+ this.commentManager,
9846
+ this.zipHandler, // Pass zipHandler to check file existence
9847
+ undefined, // fontManager (optional)
9848
+ hasCustomProps, // Flag to include custom.xml override
9849
+ this._originalContentTypes, // Pass preserved original entries for round-trip fidelity
9850
+ this.footnoteManager,
9851
+ this.endnoteManager
9852
+ );
9939
9853
  this.zipHandler.updateFile(DOCX_PATHS.CONTENT_TYPES, contentTypes);
9940
9854
  }
9941
9855
 
@@ -10016,14 +9930,9 @@ export class Document {
10016
9930
  * @param bookmarkOrName - Bookmark object or bookmark name
10017
9931
  * @returns The bookmark that was added
10018
9932
  */
10019
- addBookmarkToParagraph(
10020
- paragraph: Paragraph,
10021
- bookmarkOrName: Bookmark | string
10022
- ): Bookmark {
9933
+ addBookmarkToParagraph(paragraph: Paragraph, bookmarkOrName: Bookmark | string): Bookmark {
10023
9934
  const bookmark =
10024
- typeof bookmarkOrName === "string"
10025
- ? this.createBookmark(bookmarkOrName)
10026
- : bookmarkOrName;
9935
+ typeof bookmarkOrName === 'string' ? this.createBookmark(bookmarkOrName) : bookmarkOrName;
10027
9936
 
10028
9937
  paragraph.addBookmark(bookmark);
10029
9938
  return bookmark;
@@ -10065,7 +9974,7 @@ export class Document {
10065
9974
  anchor: string;
10066
9975
  hyperlink: (text: string, formatting?: RunFormatting) => Hyperlink;
10067
9976
  } {
10068
- const BOOKMARK_NAME = "_top";
9977
+ const BOOKMARK_NAME = '_top';
10069
9978
 
10070
9979
  // Check if _top bookmark already exists
10071
9980
  let bookmark = this.getBookmark(BOOKMARK_NAME);
@@ -10223,10 +10132,10 @@ export class Document {
10223
10132
  * }
10224
10133
  * ```
10225
10134
  */
10226
- validateAndFixRevisions(options?: {
10227
- validation?: ValidationOptions;
10228
- autoFix?: AutoFixOptions;
10229
- }): { validation: ValidationResult; fix: AutoFixResult } {
10135
+ validateAndFixRevisions(options?: { validation?: ValidationOptions; autoFix?: AutoFixOptions }): {
10136
+ validation: ValidationResult;
10137
+ fix: AutoFixResult;
10138
+ } {
10230
10139
  // First validate
10231
10140
  const validation = this.validateRevisions(options?.validation);
10232
10141
 
@@ -10279,12 +10188,7 @@ export class Document {
10279
10188
  * @param date - Optional date (defaults to now)
10280
10189
  * @returns The created and registered revision
10281
10190
  */
10282
- createRevisionFromText(
10283
- type: RevisionType,
10284
- author: string,
10285
- text: string,
10286
- date?: Date
10287
- ): Revision {
10191
+ createRevisionFromText(type: RevisionType, author: string, text: string, date?: Date): Revision {
10288
10192
  const revision = Revision.fromText(type, author, text, date);
10289
10193
  return this.revisionManager.register(revision);
10290
10194
  }
@@ -10305,7 +10209,10 @@ export class Document {
10305
10209
  * @param runIndex - Optional run index within the paragraph
10306
10210
  * @returns RevisionLocation with paragraph index, or undefined if not found
10307
10211
  */
10308
- private createRevisionLocation(paragraph: Paragraph, runIndex?: number): RevisionLocation | undefined {
10212
+ private createRevisionLocation(
10213
+ paragraph: Paragraph,
10214
+ runIndex?: number
10215
+ ): RevisionLocation | undefined {
10309
10216
  const paragraphIndex = this.findParagraphIndex(paragraph);
10310
10217
  if (paragraphIndex === -1) {
10311
10218
  return undefined; // Paragraph not found in document
@@ -10325,13 +10232,8 @@ export class Document {
10325
10232
  * @param date - Optional date (defaults to now)
10326
10233
  * @returns The created revision with location set
10327
10234
  */
10328
- trackInsertion(
10329
- paragraph: Paragraph,
10330
- author: string,
10331
- text: string,
10332
- date?: Date
10333
- ): Revision {
10334
- const revision = this.createRevisionFromText("insert", author, text, date);
10235
+ trackInsertion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
10236
+ const revision = this.createRevisionFromText('insert', author, text, date);
10335
10237
 
10336
10238
  // Set location for changelog/tracking purposes
10337
10239
  const location = this.createRevisionLocation(paragraph);
@@ -10353,13 +10255,8 @@ export class Document {
10353
10255
  * @param date - Optional date (defaults to now)
10354
10256
  * @returns The created revision with location set
10355
10257
  */
10356
- trackDeletion(
10357
- paragraph: Paragraph,
10358
- author: string,
10359
- text: string,
10360
- date?: Date
10361
- ): Revision {
10362
- const revision = this.createRevisionFromText("delete", author, text, date);
10258
+ trackDeletion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
10259
+ const revision = this.createRevisionFromText('delete', author, text, date);
10363
10260
 
10364
10261
  // Set location for changelog/tracking purposes
10365
10262
  const location = this.createRevisionLocation(paragraph);
@@ -10424,11 +10321,7 @@ export class Document {
10424
10321
  * doc.trackParagraphMarkDeletion(para, 'Alice');
10425
10322
  * // In Word, this shows the ¶ symbol as deleted
10426
10323
  */
10427
- trackParagraphMarkDeletion(
10428
- paragraph: Paragraph,
10429
- author: string,
10430
- date?: Date
10431
- ): Paragraph {
10324
+ trackParagraphMarkDeletion(paragraph: Paragraph, author: string, date?: Date): Paragraph {
10432
10325
  const revisionId = this.revisionManager.consumeNextId();
10433
10326
  paragraph.markParagraphMarkAsDeleted(revisionId, author, date);
10434
10327
  return paragraph;
@@ -10622,15 +10515,13 @@ export class Document {
10622
10515
  this.trackFormatting = options.trackFormatting;
10623
10516
  }
10624
10517
  if (options.showInsertionsAndDeletions !== undefined) {
10625
- this.revisionViewSettings.showInsertionsAndDeletions =
10626
- options.showInsertionsAndDeletions;
10518
+ this.revisionViewSettings.showInsertionsAndDeletions = options.showInsertionsAndDeletions;
10627
10519
  }
10628
10520
  if (options.showFormatting !== undefined) {
10629
10521
  this.revisionViewSettings.showFormatting = options.showFormatting;
10630
10522
  }
10631
10523
  if (options.showInkAnnotations !== undefined) {
10632
- this.revisionViewSettings.showInkAnnotations =
10633
- options.showInkAnnotations;
10524
+ this.revisionViewSettings.showInkAnnotations = options.showInkAnnotations;
10634
10525
  }
10635
10526
  }
10636
10527
 
@@ -10769,6 +10660,41 @@ export class Document {
10769
10660
  }
10770
10661
  // Bind tracking to section
10771
10662
  this.bindTrackingToElement(this.section);
10663
+
10664
+ // Headers
10665
+ for (const entry of this.headerFooterManager.getAllHeaders()) {
10666
+ for (const element of entry.header.getElements()) {
10667
+ this.bindTrackingToElement(element);
10668
+ }
10669
+ }
10670
+
10671
+ // Footers
10672
+ for (const entry of this.headerFooterManager.getAllFooters()) {
10673
+ for (const element of entry.footer.getElements()) {
10674
+ this.bindTrackingToElement(element);
10675
+ }
10676
+ }
10677
+
10678
+ // Footnotes
10679
+ for (const footnote of this.footnoteManager.getAllFootnotes()) {
10680
+ for (const para of footnote.getParagraphs()) {
10681
+ this.bindTrackingToElement(para);
10682
+ }
10683
+ }
10684
+
10685
+ // Endnotes
10686
+ for (const endnote of this.endnoteManager.getAllEndnotes()) {
10687
+ for (const para of endnote.getParagraphs()) {
10688
+ this.bindTrackingToElement(para);
10689
+ }
10690
+ }
10691
+
10692
+ // Comments
10693
+ for (const comment of this.commentManager.getAllCommentsWithReplies()) {
10694
+ for (const run of comment.getRuns()) {
10695
+ this.bindTrackingToElement(run);
10696
+ }
10697
+ }
10772
10698
  }
10773
10699
 
10774
10700
  /**
@@ -10823,7 +10749,7 @@ export class Document {
10823
10749
  setRsidRoot(rsidRoot: string): this {
10824
10750
  // Validate RSID format (8 hex characters)
10825
10751
  if (!/^[0-9A-Fa-f]{8}$/.test(rsidRoot)) {
10826
- throw new Error("RSID must be an 8-character hexadecimal value");
10752
+ throw new Error('RSID must be an 8-character hexadecimal value');
10827
10753
  }
10828
10754
  this.rsidRoot = rsidRoot.toUpperCase();
10829
10755
  this.rsids.add(this.rsidRoot);
@@ -10839,7 +10765,7 @@ export class Document {
10839
10765
  addRsid(rsid: string): this {
10840
10766
  // Validate RSID format
10841
10767
  if (!/^[0-9A-Fa-f]{8}$/.test(rsid)) {
10842
- throw new Error("RSID must be an 8-character hexadecimal value");
10768
+ throw new Error('RSID must be an 8-character hexadecimal value');
10843
10769
  }
10844
10770
  this.rsids.add(rsid.toUpperCase());
10845
10771
  this._settingsModified = true;
@@ -10854,7 +10780,7 @@ export class Document {
10854
10780
  const rsid = Math.floor(Math.random() * 0xffffffff)
10855
10781
  .toString(16)
10856
10782
  .toUpperCase()
10857
- .padStart(8, "0");
10783
+ .padStart(8, '0');
10858
10784
  this.rsids.add(rsid);
10859
10785
  this._settingsModified = true;
10860
10786
  return rsid;
@@ -10881,7 +10807,7 @@ export class Document {
10881
10807
  * @param protection - Document protection settings
10882
10808
  */
10883
10809
  protectDocument(protection: {
10884
- edit: "readOnly" | "comments" | "trackedChanges" | "forms";
10810
+ edit: 'readOnly' | 'comments' | 'trackedChanges' | 'forms';
10885
10811
  enforcement?: boolean;
10886
10812
  password?: string;
10887
10813
  cryptProviderType?: string;
@@ -10904,17 +10830,11 @@ export class Document {
10904
10830
  // If password provided, generate hash and salt
10905
10831
  if (protection.password) {
10906
10832
  // For now, use a simple hash. In production, use proper cryptographic functions
10907
- const crypto = require("crypto");
10908
- const salt = crypto.randomBytes(16).toString("base64");
10833
+ const crypto = require('crypto');
10834
+ const salt = crypto.randomBytes(16).toString('base64');
10909
10835
  const hash = crypto
10910
- .pbkdf2Sync(
10911
- protection.password,
10912
- salt,
10913
- protection.cryptSpinCount || 100000,
10914
- 32,
10915
- "sha512"
10916
- )
10917
- .toString("base64");
10836
+ .pbkdf2Sync(protection.password, salt, protection.cryptSpinCount || 100000, 32, 'sha512')
10837
+ .toString('base64');
10918
10838
 
10919
10839
  this.documentProtection.hash = hash;
10920
10840
  this.documentProtection.salt = salt;
@@ -10967,7 +10887,9 @@ export class Document {
10967
10887
  * Gets the document background per ECMA-376 Part 1 §17.2.1
10968
10888
  * @returns Background properties or undefined
10969
10889
  */
10970
- getDocumentBackground(): { color?: string; themeColor?: string; themeTint?: string; themeShade?: string } | undefined {
10890
+ getDocumentBackground():
10891
+ | { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
10892
+ | undefined {
10971
10893
  return this._documentBackground ? { ...this._documentBackground } : undefined;
10972
10894
  }
10973
10895
 
@@ -10975,7 +10897,11 @@ export class Document {
10975
10897
  * Sets the document background per ECMA-376 Part 1 §17.2.1
10976
10898
  * @param background - Background properties (color, themeColor, etc.) or undefined to remove
10977
10899
  */
10978
- setDocumentBackground(background: { color?: string; themeColor?: string; themeTint?: string; themeShade?: string } | undefined): void {
10900
+ setDocumentBackground(
10901
+ background:
10902
+ | { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
10903
+ | undefined
10904
+ ): void {
10979
10905
  this._documentBackground = background ? { ...background } : undefined;
10980
10906
  }
10981
10907
 
@@ -11196,12 +11122,14 @@ export class Document {
11196
11122
  * @returns CompatibilityInfo with all parsed settings
11197
11123
  */
11198
11124
  getCompatibilityInfo(): CompatibilityInfo {
11199
- return this._compatInfo ?? {
11200
- mode: CompatibilityMode.Word2007,
11201
- isLegacyMode: true,
11202
- compatSettings: [],
11203
- legacyFlags: [],
11204
- };
11125
+ return (
11126
+ this._compatInfo ?? {
11127
+ mode: CompatibilityMode.Word2007,
11128
+ isLegacyMode: true,
11129
+ compatSettings: [],
11130
+ legacyFlags: [],
11131
+ }
11132
+ );
11205
11133
  }
11206
11134
 
11207
11135
  /**
@@ -11281,7 +11209,7 @@ export class Document {
11281
11209
  previousMode: currentMode,
11282
11210
  newMode: 15,
11283
11211
  removedFlags: [],
11284
- addedSettings: MODERN_COMPAT_SETTINGS.map(s => s.name),
11212
+ addedSettings: MODERN_COMPAT_SETTINGS.map((s) => s.name),
11285
11213
  namespacesExpanded: nsResult.expanded,
11286
11214
  changed: currentMode !== CompatibilityMode.Word2013Plus,
11287
11215
  };
@@ -11301,12 +11229,7 @@ export class Document {
11301
11229
  previousProperties: Record<string, any>,
11302
11230
  date?: Date
11303
11231
  ): Revision {
11304
- const revision = Revision.createRunPropertiesChange(
11305
- author,
11306
- content,
11307
- previousProperties,
11308
- date
11309
- );
11232
+ const revision = Revision.createRunPropertiesChange(author, content, previousProperties, date);
11310
11233
  return this.revisionManager.register(revision);
11311
11234
  }
11312
11235
 
@@ -11364,12 +11287,7 @@ export class Document {
11364
11287
  * @param date - Optional date (defaults to now)
11365
11288
  * @returns The created and registered revision
11366
11289
  */
11367
- createMoveFrom(
11368
- author: string,
11369
- content: Run | Run[],
11370
- moveId: string,
11371
- date?: Date
11372
- ): Revision {
11290
+ createMoveFrom(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
11373
11291
  const revision = Revision.createMoveFrom(author, content, moveId, date);
11374
11292
  return this.revisionManager.register(revision);
11375
11293
  }
@@ -11382,12 +11300,7 @@ export class Document {
11382
11300
  * @param date - Optional date (defaults to now)
11383
11301
  * @returns The created and registered revision
11384
11302
  */
11385
- createMoveTo(
11386
- author: string,
11387
- content: Run | Run[],
11388
- moveId: string,
11389
- date?: Date
11390
- ): Revision {
11303
+ createMoveTo(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
11391
11304
  const revision = Revision.createMoveTo(author, content, moveId, date);
11392
11305
  return this.revisionManager.register(revision);
11393
11306
  }
@@ -11413,9 +11326,7 @@ export class Document {
11413
11326
  moveToRangeEnd: RangeMarker;
11414
11327
  } {
11415
11328
  // Generate unique move ID and name
11416
- const moveId = `move${Date.now()}_${Math.random()
11417
- .toString(36)
11418
- .substr(2, 9)}`;
11329
+ const moveId = `move${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
11419
11330
  const moveName = `move${Date.now()}`;
11420
11331
 
11421
11332
  // Get unique IDs for range markers (use revision manager's next ID)
@@ -11431,12 +11342,7 @@ export class Document {
11431
11342
  const moveFromRangeEnd = RangeMarker.createMoveFromEnd(rangeIdStart);
11432
11343
 
11433
11344
  // Create range markers for moveTo
11434
- const moveToRangeStart = RangeMarker.createMoveToStart(
11435
- rangeIdStart,
11436
- moveName,
11437
- author,
11438
- date
11439
- );
11345
+ const moveToRangeStart = RangeMarker.createMoveToStart(rangeIdStart, moveName, author, date);
11440
11346
  const moveToRangeEnd = RangeMarker.createMoveToEnd(rangeIdStart);
11441
11347
 
11442
11348
  // Create the actual move revisions
@@ -11461,11 +11367,7 @@ export class Document {
11461
11367
  * @param date - Optional date (defaults to now)
11462
11368
  * @returns The created and registered revision
11463
11369
  */
11464
- createTableCellInsert(
11465
- author: string,
11466
- content: Run | Run[],
11467
- date?: Date
11468
- ): Revision {
11370
+ createTableCellInsert(author: string, content: Run | Run[], date?: Date): Revision {
11469
11371
  const revision = Revision.createTableCellInsert(author, content, date);
11470
11372
  return this.revisionManager.register(revision);
11471
11373
  }
@@ -11477,11 +11379,7 @@ export class Document {
11477
11379
  * @param date - Optional date (defaults to now)
11478
11380
  * @returns The created and registered revision
11479
11381
  */
11480
- createTableCellDelete(
11481
- author: string,
11482
- content: Run | Run[],
11483
- date?: Date
11484
- ): Revision {
11382
+ createTableCellDelete(author: string, content: Run | Run[], date?: Date): Revision {
11485
11383
  const revision = Revision.createTableCellDelete(author, content, date);
11486
11384
  return this.revisionManager.register(revision);
11487
11385
  }
@@ -11493,11 +11391,7 @@ export class Document {
11493
11391
  * @param date - Optional date (defaults to now)
11494
11392
  * @returns The created and registered revision
11495
11393
  */
11496
- createTableCellMerge(
11497
- author: string,
11498
- content: Run | Run[],
11499
- date?: Date
11500
- ): Revision {
11394
+ createTableCellMerge(author: string, content: Run | Run[], date?: Date): Revision {
11501
11395
  const revision = Revision.createTableCellMerge(author, content, date);
11502
11396
  return this.revisionManager.register(revision);
11503
11397
  }
@@ -11516,12 +11410,7 @@ export class Document {
11516
11410
  previousProperties: Record<string, any>,
11517
11411
  date?: Date
11518
11412
  ): Revision {
11519
- const revision = Revision.createNumberingChange(
11520
- author,
11521
- content,
11522
- previousProperties,
11523
- date
11524
- );
11413
+ const revision = Revision.createNumberingChange(author, content, previousProperties, date);
11525
11414
  return this.revisionManager.register(revision);
11526
11415
  }
11527
11416
 
@@ -11575,11 +11464,7 @@ export class Document {
11575
11464
  * const comment = doc.createComment('Alice', run, 'A');
11576
11465
  * ```
11577
11466
  */
11578
- createComment(
11579
- author: string,
11580
- content: string | Run | Run[],
11581
- initials?: string
11582
- ): Comment {
11467
+ createComment(author: string, content: string | Run | Run[], initials?: string): Comment {
11583
11468
  this._commentsModified = true;
11584
11469
  return this.commentManager.createComment(author, content, initials);
11585
11470
  }
@@ -11599,12 +11484,7 @@ export class Document {
11599
11484
  initials?: string
11600
11485
  ): Comment {
11601
11486
  this._commentsModified = true;
11602
- return this.commentManager.createReply(
11603
- parentCommentId,
11604
- author,
11605
- content,
11606
- initials
11607
- );
11487
+ return this.commentManager.createReply(parentCommentId, author, content, initials);
11608
11488
  }
11609
11489
 
11610
11490
  /**
@@ -11676,7 +11556,7 @@ export class Document {
11676
11556
  initials?: string
11677
11557
  ): Comment {
11678
11558
  const comment =
11679
- typeof commentOrAuthor === "string"
11559
+ typeof commentOrAuthor === 'string'
11680
11560
  ? this.createComment(commentOrAuthor, content!, initials)
11681
11561
  : commentOrAuthor;
11682
11562
 
@@ -11719,9 +11599,7 @@ export class Document {
11719
11599
  * @param commentId - ID of the top-level comment
11720
11600
  * @returns Object with the comment and its replies, or undefined if not found
11721
11601
  */
11722
- getCommentThread(
11723
- commentId: number
11724
- ): { comment: Comment; replies: Comment[] } | undefined {
11602
+ getCommentThread(commentId: number): { comment: Comment; replies: Comment[] } | undefined {
11725
11603
  return this.commentManager.getCommentThread(commentId);
11726
11604
  }
11727
11605
 
@@ -12000,16 +11878,16 @@ export class Document {
12000
11878
 
12001
11879
  /**
12002
11880
  * Strips all tracked changes from the document
12003
- *
11881
+ *
12004
11882
  * Removes all revision markup (<w:ins>, <w:del>, <w:moveFrom>, <w:moveTo>) from the document's XML
12005
- * and cleans up related metadata. This effectively "accepts" all changes without using Word's
11883
+ * and cleans up related metadata. This effectively "accepts" all changes without using Word's
12006
11884
  * built-in Accept Changes feature.
12007
- *
11885
+ *
12008
11886
  * **IMPORTANT**: This operation:
12009
11887
  * 1. Modifies the raw XML in the ZIP package to remove all tracked changes
12010
11888
  * 2. Clears Revision objects from the in-memory object model to prevent re-serialization
12011
11889
  * 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
12012
- *
11890
+ *
12013
11891
  * What gets removed:
12014
11892
  * - All insertion markers (<w:ins>) - content is kept, wrapper removed
12015
11893
  * - All deletion markers (<w:del>) - entire element including content removed
@@ -12019,22 +11897,22 @@ export class Document {
12019
11897
  * - Revision authors from word/people.xml
12020
11898
  * - Track changes settings from word/settings.xml
12021
11899
  * - Revision count from docProps/core.xml
12022
- *
11900
+ *
12023
11901
  * @returns This document instance for method chaining
12024
- *
11902
+ *
12025
11903
  * @example
12026
11904
  * ```typescript
12027
11905
  * // Load document with tracked changes
12028
11906
  * const doc = await Document.load('document-with-revisions.docx');
12029
- *
11907
+ *
12030
11908
  * // Strip all tracked changes
12031
11909
  * await doc.stripTrackedChanges();
12032
- *
11910
+ *
12033
11911
  * // Now process the document as normal
12034
11912
  * doc.applyStyles();
12035
11913
  * await doc.save('cleaned.docx');
12036
11914
  * ```
12037
- *
11915
+ *
12038
11916
  * @example
12039
11917
  * ```typescript
12040
11918
  * // Check for tracked changes first
@@ -12045,7 +11923,7 @@ export class Document {
12045
11923
  * }
12046
11924
  * await doc.save('output.docx');
12047
11925
  * ```
12048
- *
11926
+ *
12049
11927
  * @deprecated Use {@link acceptAllRevisions} instead - this method will be removed in a future version
12050
11928
  */
12051
11929
  async stripTrackedChanges(): Promise<this> {
@@ -12055,7 +11933,7 @@ export class Document {
12055
11933
 
12056
11934
  /**
12057
11935
  * Accepts all tracked changes in the document
12058
- *
11936
+ *
12059
11937
  * Processes all revision markup following Microsoft's official OpenXML SDK approach:
12060
11938
  * - Insertions (<w:ins>): Keep the inserted content, remove wrapper tags
12061
11939
  * - Deletions (<w:del>): Remove entirely (content was deleted, so discard it)
@@ -12063,32 +11941,32 @@ export class Document {
12063
11941
  * - Move To (<w:moveTo>): Keep content, remove wrapper (destination of moved content)
12064
11942
  * - Property changes: Remove all tracking elements
12065
11943
  * - Range markers: Remove all boundary markers
12066
- *
11944
+ *
12067
11945
  * Also cleans up metadata:
12068
11946
  * - Revision authors from word/people.xml
12069
11947
  * - Track changes settings from word/settings.xml
12070
11948
  * - Revision count from docProps/core.xml
12071
- *
11949
+ *
12072
11950
  * **IMPORTANT**: This operation:
12073
11951
  * 1. Modifies the raw XML in the ZIP package
12074
11952
  * 2. Clears Revision objects from the in-memory object model
12075
11953
  * 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
12076
- *
11954
+ *
12077
11955
  * @returns This document instance for method chaining
12078
- *
11956
+ *
12079
11957
  * @example
12080
11958
  * ```typescript
12081
11959
  * // Load document with tracked changes
12082
11960
  * const doc = await Document.load('document-with-revisions.docx');
12083
- *
11961
+ *
12084
11962
  * // Accept all tracked changes
12085
11963
  * await doc.acceptAllRevisions();
12086
- *
11964
+ *
12087
11965
  * // Now process the document as normal
12088
11966
  * doc.applyStyles();
12089
11967
  * await doc.save('cleaned.docx');
12090
11968
  * ```
12091
- *
11969
+ *
12092
11970
  * @example
12093
11971
  * ```typescript
12094
11972
  * // Check for tracked changes first
@@ -12099,7 +11977,7 @@ export class Document {
12099
11977
  * }
12100
11978
  * await doc.save('output.docx');
12101
11979
  * ```
12102
- *
11980
+ *
12103
11981
  * @see https://learn.microsoft.com/en-us/office/open-xml/how-to-accept-all-revisions
12104
11982
  */
12105
11983
  async acceptAllRevisions(): Promise<this> {
@@ -12172,7 +12050,10 @@ export class Document {
12172
12050
  * await doc.save('output.docx');
12173
12051
  * ```
12174
12052
  */
12175
- consolidateAllRevisions(timeWindowMs = 1000): { paragraphsProcessed: number; revisionsConsolidated: number } {
12053
+ consolidateAllRevisions(timeWindowMs = 1000): {
12054
+ paragraphsProcessed: number;
12055
+ revisionsConsolidated: number;
12056
+ } {
12176
12057
  let paragraphsProcessed = 0;
12177
12058
  let totalConsolidated = 0;
12178
12059
 
@@ -12295,16 +12176,16 @@ export class Document {
12295
12176
  */
12296
12177
  private clearRevisionsFromAllParagraphs(): void {
12297
12178
  let clearedCount = 0;
12298
-
12179
+
12299
12180
  // Clear revisions from all paragraphs in the document
12300
12181
  for (const para of this.getAllParagraphs()) {
12301
12182
  const revisions = para.getRevisions();
12302
-
12183
+
12303
12184
  if (revisions.length > 0) {
12304
12185
  // Filter out all Revision objects from paragraph content
12305
12186
  const content = para.getContent();
12306
- const nonRevisionContent = content.filter(item => !(item instanceof Revision));
12307
-
12187
+ const nonRevisionContent = content.filter((item) => !(item instanceof Revision));
12188
+
12308
12189
  // Replace paragraph content with filtered version
12309
12190
  para.clearContent();
12310
12191
  for (const item of nonRevisionContent) {
@@ -12316,11 +12197,11 @@ export class Document {
12316
12197
  para.addField(item);
12317
12198
  }
12318
12199
  }
12319
-
12200
+
12320
12201
  clearedCount += revisions.length;
12321
12202
  }
12322
12203
  }
12323
-
12204
+
12324
12205
  if (clearedCount > 0) {
12325
12206
  this.logger.info(`Cleared ${clearedCount} Revision object(s) from in-memory document model`);
12326
12207
  }
@@ -12563,7 +12444,7 @@ export class Document {
12563
12444
  // ZipWriter stores all content as Buffer internally, but DocumentPart expects string for text
12564
12445
  let content: string | Buffer = file.content;
12565
12446
  if (!file.isBinary && Buffer.isBuffer(file.content)) {
12566
- content = file.content.toString("utf-8");
12447
+ content = file.content.toString('utf-8');
12567
12448
  }
12568
12449
 
12569
12450
  return {
@@ -12641,7 +12522,7 @@ export class Document {
12641
12522
  }
12642
12523
  }
12643
12524
  // Remove relationships targeting this part from _rels/.rels
12644
- const relsXml = this.zipHandler.getFileAsString("_rels/.rels");
12525
+ const relsXml = this.zipHandler.getFileAsString('_rels/.rels');
12645
12526
  if (relsXml) {
12646
12527
  const target = partName.replace(/^\//, '');
12647
12528
  const relPattern = new RegExp(
@@ -12650,7 +12531,7 @@ export class Document {
12650
12531
  );
12651
12532
  const cleaned = relsXml.replace(relPattern, '');
12652
12533
  if (cleaned !== relsXml) {
12653
- this.zipHandler.updateFile("_rels/.rels", cleaned);
12534
+ this.zipHandler.updateFile('_rels/.rels', cleaned);
12654
12535
  }
12655
12536
  }
12656
12537
  // Track removed parts to skip regeneration during save
@@ -12716,17 +12597,14 @@ export class Document {
12716
12597
  const contentTypes = new Map<string, string>();
12717
12598
 
12718
12599
  try {
12719
- const contentTypesXml = this.zipHandler.getFileAsString(
12720
- "[Content_Types].xml"
12721
- );
12600
+ const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
12722
12601
  if (!contentTypesXml) {
12723
12602
  return contentTypes;
12724
12603
  }
12725
12604
 
12726
12605
  // Parse content types XML
12727
12606
  // Match Default elements (by extension)
12728
- const defaultPattern =
12729
- /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
12607
+ const defaultPattern = /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
12730
12608
  let match;
12731
12609
  while ((match = defaultPattern.exec(contentTypesXml)) !== null) {
12732
12610
  if (match[1] && match[2]) {
@@ -12735,8 +12613,7 @@ export class Document {
12735
12613
  }
12736
12614
 
12737
12615
  // Match Override elements (by part name)
12738
- const overridePattern =
12739
- /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
12616
+ const overridePattern = /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
12740
12617
  while ((match = overridePattern.exec(contentTypesXml)) !== null) {
12741
12618
  if (match[1] && match[2]) {
12742
12619
  contentTypes.set(match[1], match[2]);
@@ -12783,13 +12660,13 @@ export class Document {
12783
12660
  }
12784
12661
 
12785
12662
  // If already a string, return as-is
12786
- if (typeof part.content === "string") {
12663
+ if (typeof part.content === 'string') {
12787
12664
  return part.content;
12788
12665
  }
12789
12666
 
12790
12667
  // If Buffer, decode as UTF-8 (standard for XML files)
12791
12668
  if (Buffer.isBuffer(part.content)) {
12792
- return part.content.toString("utf8");
12669
+ return part.content.toString('utf8');
12793
12670
  }
12794
12671
 
12795
12672
  return null;
@@ -12876,8 +12753,8 @@ export class Document {
12876
12753
  * ```
12877
12754
  */
12878
12755
  async setRawXml(partName: string, xmlContent: string): Promise<void> {
12879
- if (typeof xmlContent !== "string") {
12880
- throw new Error("XML content must be a string");
12756
+ if (typeof xmlContent !== 'string') {
12757
+ throw new Error('XML content must be a string');
12881
12758
  }
12882
12759
 
12883
12760
  // Use setPart to update the part (handles both string and binary detection)
@@ -12903,19 +12780,14 @@ export class Document {
12903
12780
  * await doc.addContentType('.json', 'application/json');
12904
12781
  * ```
12905
12782
  */
12906
- async addContentType(
12907
- partNameOrExtension: string,
12908
- contentType: string
12909
- ): Promise<boolean> {
12783
+ async addContentType(partNameOrExtension: string, contentType: string): Promise<boolean> {
12910
12784
  try {
12911
- let contentTypesXml = this.zipHandler.getFileAsString(
12912
- "[Content_Types].xml"
12913
- );
12785
+ let contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
12914
12786
  if (!contentTypesXml) {
12915
12787
  return false;
12916
12788
  }
12917
12789
 
12918
- const isExtension = partNameOrExtension.startsWith(".");
12790
+ const isExtension = partNameOrExtension.startsWith('.');
12919
12791
 
12920
12792
  if (isExtension) {
12921
12793
  // Add as Default element (for extensions)
@@ -12924,7 +12796,7 @@ export class Document {
12924
12796
  // Check if already exists
12925
12797
  const existingPattern = new RegExp(
12926
12798
  `<Default\\s+Extension="${extension}"\\s+ContentType="[^"]+"/?>`,
12927
- "g"
12799
+ 'g'
12928
12800
  );
12929
12801
  if (existingPattern.test(contentTypesXml)) {
12930
12802
  // Update existing
@@ -12935,13 +12807,13 @@ export class Document {
12935
12807
  } else {
12936
12808
  // Add new before closing tag
12937
12809
  contentTypesXml = contentTypesXml.replace(
12938
- "</Types>",
12810
+ '</Types>',
12939
12811
  ` <Default Extension="${extension}" ContentType="${contentType}"/>\n</Types>`
12940
12812
  );
12941
12813
  }
12942
12814
  } else {
12943
12815
  // Add as Override element (for specific parts)
12944
- const partName = partNameOrExtension.startsWith("/")
12816
+ const partName = partNameOrExtension.startsWith('/')
12945
12817
  ? partNameOrExtension
12946
12818
  : `/${partNameOrExtension}`;
12947
12819
 
@@ -12949,9 +12821,9 @@ export class Document {
12949
12821
  const existingPattern = new RegExp(
12950
12822
  `<Override\\s+PartName="${partName.replace(
12951
12823
  /[.*+?^${}()|[\]\\]/g,
12952
- "\\$&"
12824
+ '\\$&'
12953
12825
  )}"\\s+ContentType="[^"]+"/?>`,
12954
- "g"
12826
+ 'g'
12955
12827
  );
12956
12828
  if (existingPattern.test(contentTypesXml)) {
12957
12829
  // Update existing
@@ -12962,14 +12834,14 @@ export class Document {
12962
12834
  } else {
12963
12835
  // Add new before closing tag
12964
12836
  contentTypesXml = contentTypesXml.replace(
12965
- "</Types>",
12837
+ '</Types>',
12966
12838
  ` <Override PartName="${partName}" ContentType="${contentType}"/>\n</Types>`
12967
12839
  );
12968
12840
  }
12969
12841
  }
12970
12842
 
12971
12843
  // Update the content types file
12972
- this.zipHandler.updateFile("[Content_Types].xml", contentTypesXml);
12844
+ this.zipHandler.updateFile('[Content_Types].xml', contentTypesXml);
12973
12845
  return true;
12974
12846
  } catch (error: unknown) {
12975
12847
  return false;
@@ -12997,9 +12869,7 @@ export class Document {
12997
12869
 
12998
12870
  try {
12999
12871
  // Get all .rels files
13000
- const relsPaths = this.zipHandler
13001
- .getFilePaths()
13002
- .filter((path) => path.endsWith(".rels"));
12872
+ const relsPaths = this.zipHandler.getFilePaths().filter((path) => path.endsWith('.rels'));
13003
12873
 
13004
12874
  for (const relsPath of relsPaths) {
13005
12875
  const relsContent = this.zipHandler.getFileAsString(relsPath);
@@ -13014,22 +12884,16 @@ export class Document {
13014
12884
  const rels: ParsedRelationship[] = [];
13015
12885
 
13016
12886
  // Use XMLParser to extract all Relationship elements
13017
- const relationshipElements = XMLParser.extractElements(
13018
- relsContent,
13019
- "Relationship"
13020
- );
12887
+ const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
13021
12888
 
13022
12889
  for (const relElement of relationshipElements) {
13023
12890
  const rel: ParsedRelationship = {};
13024
12891
 
13025
12892
  // Extract attributes using XMLParser
13026
- const id = XMLParser.extractAttribute(relElement, "Id");
13027
- const type = XMLParser.extractAttribute(relElement, "Type");
13028
- const target = XMLParser.extractAttribute(relElement, "Target");
13029
- const targetMode = XMLParser.extractAttribute(
13030
- relElement,
13031
- "TargetMode"
13032
- );
12893
+ const id = XMLParser.extractAttribute(relElement, 'Id');
12894
+ const type = XMLParser.extractAttribute(relElement, 'Type');
12895
+ const target = XMLParser.extractAttribute(relElement, 'Target');
12896
+ const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
13033
12897
 
13034
12898
  if (id) rel.id = id;
13035
12899
  if (type) rel.type = type;
@@ -13078,19 +12942,15 @@ export class Document {
13078
12942
  */
13079
12943
  async getRelationships(
13080
12944
  partName: string
13081
- ): Promise<
13082
- { id?: string; type?: string; target?: string; targetMode?: string }[]
13083
- > {
12945
+ ): Promise<{ id?: string; type?: string; target?: string; targetMode?: string }[]> {
13084
12946
  try {
13085
12947
  // Construct the .rels path from the part name
13086
12948
  // For 'word/document.xml' -> 'word/_rels/document.xml.rels'
13087
- const lastSlash = partName.lastIndexOf("/");
12949
+ const lastSlash = partName.lastIndexOf('/');
13088
12950
  const relsPath =
13089
12951
  lastSlash === -1
13090
12952
  ? `_rels/${partName}.rels`
13091
- : `${partName.substring(0, lastSlash)}/_rels/${partName.substring(
13092
- lastSlash + 1
13093
- )}.rels`;
12953
+ : `${partName.substring(0, lastSlash)}/_rels/${partName.substring(lastSlash + 1)}.rels`;
13094
12954
 
13095
12955
  const relsContent = this.zipHandler.getFileAsString(relsPath);
13096
12956
  if (!relsContent) {
@@ -13107,19 +12967,16 @@ export class Document {
13107
12967
  const relationships: ParsedRelationship[] = [];
13108
12968
 
13109
12969
  // Use XMLParser to extract all Relationship elements
13110
- const relationshipElements = XMLParser.extractElements(
13111
- relsContent,
13112
- "Relationship"
13113
- );
12970
+ const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
13114
12971
 
13115
12972
  for (const relElement of relationshipElements) {
13116
12973
  const rel: ParsedRelationship = {};
13117
12974
 
13118
12975
  // Extract attributes using XMLParser
13119
- const id = XMLParser.extractAttribute(relElement, "Id");
13120
- const type = XMLParser.extractAttribute(relElement, "Type");
13121
- const target = XMLParser.extractAttribute(relElement, "Target");
13122
- const targetMode = XMLParser.extractAttribute(relElement, "TargetMode");
12976
+ const id = XMLParser.extractAttribute(relElement, 'Id');
12977
+ const type = XMLParser.extractAttribute(relElement, 'Type');
12978
+ const target = XMLParser.extractAttribute(relElement, 'Target');
12979
+ const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
13123
12980
 
13124
12981
  if (id) rel.id = id;
13125
12982
  if (type) rel.type = type;
@@ -13142,9 +12999,7 @@ export class Document {
13142
12999
  */
13143
13000
  private getContentTypeForPart(partName: string): string | undefined {
13144
13001
  try {
13145
- const contentTypesXml = this.zipHandler.getFileAsString(
13146
- "[Content_Types].xml"
13147
- );
13002
+ const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
13148
13003
  if (!contentTypesXml) {
13149
13004
  return undefined;
13150
13005
  }
@@ -13153,9 +13008,9 @@ export class Document {
13153
13008
  const overridePattern = new RegExp(
13154
13009
  `<Override\\s+PartName="${partName.replace(
13155
13010
  /[.*+?^${}()|[\]\\]/g,
13156
- "\\$&"
13011
+ '\\$&'
13157
13012
  )}"\\s+ContentType="([^"]+)"`,
13158
- "i"
13013
+ 'i'
13159
13014
  );
13160
13015
  const overrideMatch = contentTypesXml.match(overridePattern);
13161
13016
  if (overrideMatch) {
@@ -13163,13 +13018,11 @@ export class Document {
13163
13018
  }
13164
13019
 
13165
13020
  // Check for extension default
13166
- const ext = partName.substring(partName.lastIndexOf("."));
13021
+ const ext = partName.substring(partName.lastIndexOf('.'));
13167
13022
  if (ext) {
13168
13023
  const defaultPattern = new RegExp(
13169
- `<Default\\s+Extension="${ext.substring(
13170
- 1
13171
- )}"\\s+ContentType="([^"]+)"`,
13172
- "i"
13024
+ `<Default\\s+Extension="${ext.substring(1)}"\\s+ContentType="([^"]+)"`,
13025
+ 'i'
13173
13026
  );
13174
13027
  const defaultMatch = contentTypesXml.match(defaultPattern);
13175
13028
  if (defaultMatch) {
@@ -13261,8 +13114,8 @@ export class Document {
13261
13114
  if (wholeWord) {
13262
13115
  // Create word boundary regex
13263
13116
  const wordPattern = new RegExp(
13264
- `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
13265
- caseSensitive ? "g" : "gi"
13117
+ `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13118
+ caseSensitive ? 'g' : 'gi'
13266
13119
  );
13267
13120
  let match;
13268
13121
  while ((match = wordPattern.exec(runText)) !== null) {
@@ -13278,9 +13131,7 @@ export class Document {
13278
13131
  } else {
13279
13132
  // Simple substring search
13280
13133
  let startIndex = 0;
13281
- while (
13282
- (startIndex = compareText.indexOf(searchText, startIndex)) !== -1
13283
- ) {
13134
+ while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
13284
13135
  results.push({
13285
13136
  paragraph,
13286
13137
  paragraphIndex: pIndex,
@@ -13317,18 +13168,13 @@ export class Document {
13317
13168
  const run = runs[rIndex];
13318
13169
  if (!run) continue;
13319
13170
  const runText = run.getText();
13320
- const compareText = caseSensitive
13321
- ? runText
13322
- : runText.toLowerCase();
13171
+ const compareText = caseSensitive ? runText : runText.toLowerCase();
13323
13172
 
13324
13173
  if (wholeWord) {
13325
13174
  // Create word boundary regex
13326
13175
  const wordPattern = new RegExp(
13327
- `\\b${searchText.replace(
13328
- /[.*+?^${}()|[\]\\]/g,
13329
- "\\$&"
13330
- )}\\b`,
13331
- caseSensitive ? "g" : "gi"
13176
+ `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13177
+ caseSensitive ? 'g' : 'gi'
13332
13178
  );
13333
13179
  let match;
13334
13180
  while ((match = wordPattern.exec(runText)) !== null) {
@@ -13344,12 +13190,7 @@ export class Document {
13344
13190
  } else {
13345
13191
  // Simple substring search
13346
13192
  let startIndex = 0;
13347
- while (
13348
- (startIndex = compareText.indexOf(
13349
- searchText,
13350
- startIndex
13351
- )) !== -1
13352
- ) {
13193
+ while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
13353
13194
  results.push({
13354
13195
  paragraph,
13355
13196
  paragraphIndex: -1, // Not in main body, in table
@@ -13392,12 +13233,9 @@ export class Document {
13392
13233
  * }
13393
13234
  * ```
13394
13235
  */
13395
- findParagraphsByText(
13396
- pattern: string | RegExp
13397
- ): { paragraph: Paragraph; matches: string[] }[] {
13236
+ findParagraphsByText(pattern: string | RegExp): { paragraph: Paragraph; matches: string[] }[] {
13398
13237
  const results: { paragraph: Paragraph; matches: string[] }[] = [];
13399
- const regex =
13400
- typeof pattern === "string" ? new RegExp(pattern, "gi") : pattern;
13238
+ const regex = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
13401
13239
 
13402
13240
  for (const paragraph of this.getAllParagraphs()) {
13403
13241
  const text = paragraph.getText();
@@ -13457,13 +13295,13 @@ export class Document {
13457
13295
  getRunsByColor(color: string): Run[] {
13458
13296
  const results: Run[] = [];
13459
13297
  // Normalize color - remove # and convert to uppercase
13460
- const normalizedColor = color.replace(/^#/, "").toUpperCase();
13298
+ const normalizedColor = color.replace(/^#/, '').toUpperCase();
13461
13299
 
13462
13300
  for (const paragraph of this.getAllParagraphs()) {
13463
13301
  for (const run of paragraph.getRuns()) {
13464
13302
  const formatting = run.getFormatting();
13465
13303
  if (formatting.color) {
13466
- const runColor = formatting.color.replace(/^#/, "").toUpperCase();
13304
+ const runColor = formatting.color.replace(/^#/, '').toUpperCase();
13467
13305
  if (runColor === normalizedColor) {
13468
13306
  results.push(run);
13469
13307
  }
@@ -13636,7 +13474,7 @@ export class Document {
13636
13474
  sizes.set(formatting.size, (sizes.get(formatting.size) || 0) + 1);
13637
13475
  }
13638
13476
  if (formatting.color) {
13639
- const normalizedColor = formatting.color.toUpperCase().replace(/^#/, "");
13477
+ const normalizedColor = formatting.color.toUpperCase().replace(/^#/, '');
13640
13478
  colors.set(normalizedColor, (colors.get(normalizedColor) || 0) + 1);
13641
13479
  }
13642
13480
  }
@@ -13736,8 +13574,8 @@ export class Document {
13736
13574
  if (wholeWord) {
13737
13575
  // Use word boundary regex for whole word replacement
13738
13576
  const wordPattern = new RegExp(
13739
- `\\b${find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
13740
- caseSensitive ? "g" : "gi"
13577
+ `\\b${find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13578
+ caseSensitive ? 'g' : 'gi'
13741
13579
  );
13742
13580
  const matches = originalText.match(wordPattern);
13743
13581
  if (matches) {
@@ -13747,8 +13585,8 @@ export class Document {
13747
13585
  } else {
13748
13586
  // Simple substring replacement
13749
13587
  const searchPattern = new RegExp(
13750
- find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
13751
- caseSensitive ? "g" : "gi"
13588
+ find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
13589
+ caseSensitive ? 'g' : 'gi'
13752
13590
  );
13753
13591
  const matches = originalText.match(searchPattern);
13754
13592
  if (matches) {
@@ -13818,7 +13656,7 @@ export class Document {
13818
13656
  caseSensitive = false,
13819
13657
  wholeWord = false,
13820
13658
  trackChanges = false,
13821
- author = "Unknown",
13659
+ author = 'Unknown',
13822
13660
  } = options || {};
13823
13661
 
13824
13662
  let count = 0;
@@ -13826,17 +13664,15 @@ export class Document {
13826
13664
 
13827
13665
  // Convert pattern to RegExp if it's a string
13828
13666
  let regex: RegExp;
13829
- if (typeof pattern === "string") {
13667
+ if (typeof pattern === 'string') {
13830
13668
  // Escape special regex characters
13831
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13669
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13832
13670
  const boundaryPattern = wholeWord ? `\\b${escaped}\\b` : escaped;
13833
- const flags = caseSensitive ? "g" : "gi";
13671
+ const flags = caseSensitive ? 'g' : 'gi';
13834
13672
  regex = new RegExp(boundaryPattern, flags);
13835
13673
  } else {
13836
13674
  // Use provided RegExp, ensure global flag
13837
- const flags = pattern.flags.includes("g")
13838
- ? pattern.flags
13839
- : pattern.flags + "g";
13675
+ const flags = pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g';
13840
13676
  regex = new RegExp(pattern.source, flags);
13841
13677
  }
13842
13678
 
@@ -13864,15 +13700,23 @@ export class Document {
13864
13700
 
13865
13701
  if (useGranular) {
13866
13702
  for (const seg of segments) {
13867
- if (seg.type === "equal") {
13703
+ if (seg.type === 'equal') {
13868
13704
  newContent.push(new Run(seg.text, formatting));
13869
- } else if (seg.type === "delete") {
13870
- const delRev = Revision.createDeletion(author, new Run(seg.text, formatting), now);
13705
+ } else if (seg.type === 'delete') {
13706
+ const delRev = Revision.createDeletion(
13707
+ author,
13708
+ new Run(seg.text, formatting),
13709
+ now
13710
+ );
13871
13711
  this.revisionManager.register(delRev);
13872
13712
  revisions.push(delRev);
13873
13713
  newContent.push(delRev);
13874
- } else if (seg.type === "insert") {
13875
- const insRev = Revision.createInsertion(author, new Run(seg.text, formatting), now);
13714
+ } else if (seg.type === 'insert') {
13715
+ const insRev = Revision.createInsertion(
13716
+ author,
13717
+ new Run(seg.text, formatting),
13718
+ now
13719
+ );
13876
13720
  this.revisionManager.register(insRev);
13877
13721
  revisions.push(insRev);
13878
13722
  newContent.push(insRev);
@@ -13880,7 +13724,11 @@ export class Document {
13880
13724
  }
13881
13725
  } else {
13882
13726
  // Whole-run delete + insert
13883
- const deletion = Revision.createDeletion(author, new Run(originalText, formatting), now);
13727
+ const deletion = Revision.createDeletion(
13728
+ author,
13729
+ new Run(originalText, formatting),
13730
+ now
13731
+ );
13884
13732
  this.revisionManager.register(deletion);
13885
13733
  revisions.push(deletion);
13886
13734
  newContent.push(deletion);
@@ -14006,7 +13854,7 @@ export class Document {
14006
13854
  if (includeSpaces) {
14007
13855
  totalChars += text.length;
14008
13856
  } else {
14009
- totalChars += text.replace(/\s/g, "").length;
13857
+ totalChars += text.replace(/\s/g, '').length;
14010
13858
  }
14011
13859
  }
14012
13860
 
@@ -14029,7 +13877,7 @@ export class Document {
14029
13877
  if (includeSpaces) {
14030
13878
  totalChars += text.length;
14031
13879
  } else {
14032
- totalChars += text.replace(/\s/g, "").length;
13880
+ totalChars += text.replace(/\s/g, '').length;
14033
13881
  }
14034
13882
  }
14035
13883
  }
@@ -14047,7 +13895,7 @@ export class Document {
14047
13895
  removeParagraph(paragraphOrIndex: Paragraph | number): boolean {
14048
13896
  let index: number;
14049
13897
 
14050
- if (typeof paragraphOrIndex === "number") {
13898
+ if (typeof paragraphOrIndex === 'number') {
14051
13899
  index = paragraphOrIndex;
14052
13900
  } else {
14053
13901
  // Find the index of the paragraph
@@ -14084,7 +13932,7 @@ export class Document {
14084
13932
  removeTable(tableOrIndex: Table | number): boolean {
14085
13933
  let index: number;
14086
13934
 
14087
- if (typeof tableOrIndex === "number") {
13935
+ if (typeof tableOrIndex === 'number') {
14088
13936
  // If number provided, find the nth table
14089
13937
  const tables = this.getTables();
14090
13938
  if (tableOrIndex >= 0 && tableOrIndex < tables.length) {
@@ -14118,9 +13966,7 @@ export class Document {
14118
13966
  private validateParagraph(paragraph: Paragraph): void {
14119
13967
  // Type validation
14120
13968
  if (!(paragraph instanceof Paragraph)) {
14121
- throw new Error(
14122
- "insertParagraphAt: parameter must be a Paragraph instance"
14123
- );
13969
+ throw new Error('insertParagraphAt: parameter must be a Paragraph instance');
14124
13970
  }
14125
13971
 
14126
13972
  // Check for duplicate paragraph IDs
@@ -14163,19 +14009,19 @@ export class Document {
14163
14009
  private validateTable(table: Table): void {
14164
14010
  // Type validation
14165
14011
  if (!(table instanceof Table)) {
14166
- throw new Error("insertTableAt: parameter must be a Table instance");
14012
+ throw new Error('insertTableAt: parameter must be a Table instance');
14167
14013
  }
14168
14014
 
14169
14015
  // Content validation - table must have rows
14170
14016
  const rows = table.getRows();
14171
14017
  if (rows.length === 0) {
14172
- throw new Error("insertTableAt: table must have at least one row");
14018
+ throw new Error('insertTableAt: table must have at least one row');
14173
14019
  }
14174
14020
 
14175
14021
  // Check first row has cells (rows.length > 0 already checked above)
14176
14022
  const firstRow = rows[0];
14177
14023
  if (firstRow?.getCells().length === 0) {
14178
- throw new Error("insertTableAt: table rows must have at least one cell");
14024
+ throw new Error('insertTableAt: table rows must have at least one cell');
14179
14025
  }
14180
14026
 
14181
14027
  // Warn about missing table styles
@@ -14195,27 +14041,25 @@ export class Document {
14195
14041
  private validateToc(toc: TableOfContentsElement): void {
14196
14042
  // Type validation
14197
14043
  if (!(toc instanceof TableOfContentsElement)) {
14198
- throw new Error(
14199
- "insertTocAt: parameter must be a TableOfContentsElement instance"
14200
- );
14044
+ throw new Error('insertTocAt: parameter must be a TableOfContentsElement instance');
14201
14045
  }
14202
14046
 
14203
14047
  // Check if document has heading styles for TOC to reference
14204
14048
  const hasHeadings = [
14205
- "Heading1",
14206
- "Heading2",
14207
- "Heading3",
14208
- "Heading4",
14209
- "Heading5",
14210
- "Heading6",
14211
- "Heading7",
14212
- "Heading8",
14213
- "Heading9",
14049
+ 'Heading1',
14050
+ 'Heading2',
14051
+ 'Heading3',
14052
+ 'Heading4',
14053
+ 'Heading5',
14054
+ 'Heading6',
14055
+ 'Heading7',
14056
+ 'Heading8',
14057
+ 'Heading9',
14214
14058
  ].some((style) => this.stylesManager.hasStyle(style));
14215
14059
 
14216
14060
  if (!hasHeadings) {
14217
14061
  defaultLogger.warn(
14218
- "No heading styles found in document. Table of Contents may not display entries correctly."
14062
+ 'No heading styles found in document. Table of Contents may not display entries correctly.'
14219
14063
  );
14220
14064
  }
14221
14065
  }
@@ -14544,8 +14388,7 @@ export class Document {
14544
14388
  * ```
14545
14389
  */
14546
14390
  getHyperlinks(): { hyperlink: Hyperlink; paragraph: Paragraph }[] {
14547
- const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] =
14548
- [];
14391
+ const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] = [];
14549
14392
 
14550
14393
  // Helper function to extract hyperlinks from paragraph content,
14551
14394
  // including those inside Revision elements (w:ins, w:del, etc.)
@@ -14573,8 +14416,7 @@ export class Document {
14573
14416
  for (const row of table.getRows()) {
14574
14417
  for (const cell of row.getCells()) {
14575
14418
  // TableCell has getParagraphs method
14576
- const cellParagraphs =
14577
- cell instanceof TableCell ? cell.getParagraphs() : [];
14419
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14578
14420
  for (const para of cellParagraphs) {
14579
14421
  extractHyperlinksFromParagraph(para);
14580
14422
  }
@@ -14611,7 +14453,9 @@ export class Document {
14611
14453
  }
14612
14454
  };
14613
14455
 
14614
- const scanElement = (element: BodyElement | Paragraph | Table | StructuredDocumentTag): void => {
14456
+ const scanElement = (
14457
+ element: BodyElement | Paragraph | Table | StructuredDocumentTag
14458
+ ): void => {
14615
14459
  if (element instanceof Paragraph) {
14616
14460
  scanParagraph(element);
14617
14461
  } else if (element instanceof Table) {
@@ -14697,8 +14541,7 @@ export class Document {
14697
14541
  resetFormatting?: boolean;
14698
14542
  cleanupRelationships?: boolean;
14699
14543
  }): number {
14700
- const { resetFormatting = false, cleanupRelationships = false } =
14701
- options || {};
14544
+ const { resetFormatting = false, cleanupRelationships = false } = options || {};
14702
14545
 
14703
14546
  // Guard: Skip when track changes is enabled - prevents field structure corruption
14704
14547
  // The mergeConsecutiveHyperlinks() method uses clearContent() + addHyperlink()
@@ -14707,7 +14550,7 @@ export class Document {
14707
14550
  if (this.trackChangesEnabled) {
14708
14551
  defaultLogger.warn(
14709
14552
  'defragmentHyperlinks skipped: track changes is enabled. ' +
14710
- 'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
14553
+ 'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
14711
14554
  );
14712
14555
  return 0;
14713
14556
  }
@@ -14736,8 +14579,7 @@ export class Document {
14736
14579
  for (const table of this.getTables()) {
14737
14580
  for (const row of table.getRows()) {
14738
14581
  for (const cell of row.getCells()) {
14739
- const cellParagraphs =
14740
- cell instanceof TableCell ? cell.getParagraphs() : [];
14582
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14741
14583
  for (const para of cellParagraphs) {
14742
14584
  const originalContent = para.getContent();
14743
14585
 
@@ -14759,12 +14601,9 @@ export class Document {
14759
14601
  const referencedIds = this.collectAllReferencedHyperlinkIds();
14760
14602
 
14761
14603
  // Remove orphaned hyperlink relationships
14762
- const removedCount =
14763
- this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
14604
+ const removedCount = this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
14764
14605
  if (removedCount > 0) {
14765
- defaultLogger.info(
14766
- `Cleaned up ${removedCount} orphaned hyperlink relationship(s)`
14767
- );
14606
+ defaultLogger.info(`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`);
14768
14607
  }
14769
14608
  }
14770
14609
 
@@ -14848,8 +14687,7 @@ export class Document {
14848
14687
  for (const table of this.getTables()) {
14849
14688
  for (const row of table.getRows()) {
14850
14689
  for (const cell of row.getCells()) {
14851
- const cellParagraphs =
14852
- cell instanceof TableCell ? cell.getParagraphs() : [];
14690
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14853
14691
  for (const para of cellParagraphs) {
14854
14692
  for (const field of para.getFields()) {
14855
14693
  results.push({ field, paragraph: para, table });
@@ -14910,7 +14748,10 @@ export class Document {
14910
14748
  await this.imageManager.loadAllImageData();
14911
14749
 
14912
14750
  // 2. Group images by filename (avoid processing same file twice)
14913
- const imagesByFilename = new Map<string, { image: Image; relationshipId: string; filename: string }[]>();
14751
+ const imagesByFilename = new Map<
14752
+ string,
14753
+ { image: Image; relationshipId: string; filename: string }[]
14754
+ >();
14914
14755
  for (const entry of this.imageManager.getAllImages()) {
14915
14756
  const group = imagesByFilename.get(entry.filename) || [];
14916
14757
  group.push(entry);
@@ -14968,7 +14809,11 @@ export class Document {
14968
14809
  * Handles both document body and header/footer relationships.
14969
14810
  * @private
14970
14811
  */
14971
- private updateImageRelationshipTarget(relId: string, oldFilename: string, newFilename: string): void {
14812
+ private updateImageRelationshipTarget(
14813
+ relId: string,
14814
+ oldFilename: string,
14815
+ newFilename: string
14816
+ ): void {
14972
14817
  // Try document body relationship manager first
14973
14818
  const rel = this.relationshipManager.getRelationship(relId);
14974
14819
  if (rel) {
@@ -15029,8 +14874,7 @@ export class Document {
15029
14874
  for (const table of this.getTables()) {
15030
14875
  for (const row of table.getRows()) {
15031
14876
  for (const cell of row.getCells()) {
15032
- const cellParagraphs =
15033
- cell instanceof TableCell ? cell.getParagraphs() : [];
14877
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
15034
14878
  for (const para of cellParagraphs) {
15035
14879
  runs.push(...para.getRuns());
15036
14880
  }
@@ -15070,9 +14914,10 @@ export class Document {
15070
14914
  * });
15071
14915
  * ```
15072
14916
  */
15073
- hyperlinkEmails(options?: {
15074
- formatting?: RunFormatting;
15075
- }): { emailsLinked: number; paragraphsModified: number } {
14917
+ hyperlinkEmails(options?: { formatting?: RunFormatting }): {
14918
+ emailsLinked: number;
14919
+ paragraphsModified: number;
14920
+ } {
15076
14921
  // Default formatting: Verdana 12pt, Underline, no bold, #0000FF
15077
14922
  const defaultFormatting: RunFormatting = {
15078
14923
  font: 'Verdana',
@@ -15189,23 +15034,23 @@ export class Document {
15189
15034
  */
15190
15035
  removeFormattingFromAll(
15191
15036
  type:
15192
- | "bold"
15193
- | "italic"
15194
- | "underline"
15195
- | "strike"
15196
- | "dstrike"
15197
- | "highlight"
15198
- | "color"
15199
- | "font"
15200
- | "size"
15201
- | "subscript"
15202
- | "superscript"
15203
- | "smallCaps"
15204
- | "allCaps"
15205
- | "outline"
15206
- | "shadow"
15207
- | "emboss"
15208
- | "imprint"
15037
+ | 'bold'
15038
+ | 'italic'
15039
+ | 'underline'
15040
+ | 'strike'
15041
+ | 'dstrike'
15042
+ | 'highlight'
15043
+ | 'color'
15044
+ | 'font'
15045
+ | 'size'
15046
+ | 'subscript'
15047
+ | 'superscript'
15048
+ | 'smallCaps'
15049
+ | 'allCaps'
15050
+ | 'outline'
15051
+ | 'shadow'
15052
+ | 'emboss'
15053
+ | 'imprint'
15209
15054
  ): number {
15210
15055
  let modifiedCount = 0;
15211
15056
 
@@ -15274,9 +15119,7 @@ export class Document {
15274
15119
  * });
15275
15120
  * ```
15276
15121
  */
15277
- updateAllHyperlinks(
15278
- formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void
15279
- ): number {
15122
+ updateAllHyperlinks(formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void): number {
15280
15123
  // Get all hyperlinks with their containing paragraphs
15281
15124
  const hyperlinks = this.getHyperlinks();
15282
15125
 
@@ -15349,7 +15192,7 @@ export class Document {
15349
15192
 
15350
15193
  this.bodyElements.forEach((element, index) => {
15351
15194
  if (element instanceof Paragraph) {
15352
- const isEmpty = element.getText().trim() === "";
15195
+ const isEmpty = element.getText().trim() === '';
15353
15196
  if (isEmpty && lastWasEmpty) {
15354
15197
  toRemove.push(index);
15355
15198
  }
@@ -15380,7 +15223,7 @@ export class Document {
15380
15223
  }
15381
15224
 
15382
15225
  if (standardLineSpacing !== undefined) {
15383
- para.setLineSpacing(standardLineSpacing, "auto");
15226
+ para.setLineSpacing(standardLineSpacing, 'auto');
15384
15227
  normalized++;
15385
15228
  }
15386
15229
 
@@ -15405,8 +15248,7 @@ export class Document {
15405
15248
  for (const table of this.getTables()) {
15406
15249
  for (const row of table.getRows()) {
15407
15250
  for (const cell of row.getCells()) {
15408
- const cellParagraphs =
15409
- cell instanceof TableCell ? cell.getParagraphs() : [];
15251
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
15410
15252
  for (const para of cellParagraphs) {
15411
15253
  if (standardParagraphSpacing) {
15412
15254
  if (standardParagraphSpacing.before !== undefined) {
@@ -15420,7 +15262,7 @@ export class Document {
15420
15262
  }
15421
15263
 
15422
15264
  if (standardLineSpacing !== undefined) {
15423
- para.setLineSpacing(standardLineSpacing, "auto");
15265
+ para.setLineSpacing(standardLineSpacing, 'auto');
15424
15266
  normalized++;
15425
15267
  }
15426
15268
 
@@ -15509,13 +15351,13 @@ export class Document {
15509
15351
 
15510
15352
  /**
15511
15353
  * Rebuilds all Table of Contents in the document
15512
- *
15354
+ *
15513
15355
  * Analyzes each TOC in the document, parses its field instructions to determine
15514
15356
  * which heading levels to include, searches for matching headings (including those
15515
15357
  * in nested tables), and returns a summary of TOC instructions and heading counts.
15516
- *
15358
+ *
15517
15359
  * **NEW: This method now also populates the TOCs with hyperlinked entries automatically!**
15518
- *
15360
+ *
15519
15361
  * The method:
15520
15362
  * 1. Removes SDT wrappers around tables if found (uses clearCustom helper)
15521
15363
  * 2. Ensures `_top` bookmark exists at document start for TOC linking
@@ -15528,7 +15370,7 @@ export class Document {
15528
15370
  * 9. **Updates document.xml with the populated TOC**
15529
15371
  * 10. Retains field instructions so TOCs can be manually updated later
15530
15372
  * 11. Returns summary: [instruction, [h1Count, h2Count, h3Count, ...]]
15531
- *
15373
+ *
15532
15374
  * **Key Features:**
15533
15375
  * - No arguments required - analyzes the current document state
15534
15376
  * - Searches nested tables when counting headings
@@ -15539,19 +15381,19 @@ export class Document {
15539
15381
  * - **Field instructions preserved for manual updates**
15540
15382
  * - **No page numbers displayed (pure hyperlink navigation)**
15541
15383
  * - Returns summary data for diagnostics and verification
15542
- *
15384
+ *
15543
15385
  * **Output Format:**
15544
15386
  * Returns a 2D array where each row contains:
15545
15387
  * - Index 0: The TOC field instruction text (e.g., "TOC \\o \"1-3\"")
15546
15388
  * - Index 1: Array of heading counts by level (e.g., [5, 12, 8] = 5 H1s, 12 H2s, 8 H3s)
15547
- *
15389
+ *
15548
15390
  * @returns Two-dimensional array of [instruction, headingCounts[]] for each TOC
15549
- *
15391
+ *
15550
15392
  * @example
15551
15393
  * ```typescript
15552
15394
  * const doc = await Document.load('document.docx');
15553
15395
  * const tocInfo = doc.rebuildTOCs();
15554
- *
15396
+ *
15555
15397
  * console.log(`Found ${tocInfo.length} Table(s) of Contents`);
15556
15398
  * for (const [instruction, counts] of tocInfo) {
15557
15399
  * console.log(`TOC Instruction: ${instruction}`);
@@ -15561,59 +15403,59 @@ export class Document {
15561
15403
  * }
15562
15404
  * });
15563
15405
  * }
15564
- *
15406
+ *
15565
15407
  * // TOCs are now populated with hyperlinks - save the document
15566
15408
  * await doc.save('output.docx');
15567
15409
  * // When opened in Word, TOCs will display with clickable links, no manual update needed
15568
15410
  * ```
15569
- *
15411
+ *
15570
15412
  * @example
15571
15413
  * ```typescript
15572
15414
  * // Rebuild TOCs and save with populated entries
15573
15415
  * const doc = await Document.load('input.docx');
15574
15416
  * const tocSummary = doc.rebuildTOCs();
15575
15417
  * await doc.save('output.docx');
15576
- *
15418
+ *
15577
15419
  * console.log(`Processed ${tocSummary.length} TOCs with hyperlinked entries`);
15578
15420
  * ```
15579
15421
  */
15580
15422
  public rebuildTOCs(): [string, number[]][] {
15581
15423
  const results: [string, number[]][] = [];
15582
-
15424
+
15583
15425
  // Step 1: Remove SDT wrappers around tables if found (helper already exists)
15584
15426
  this.clearCustom();
15585
-
15427
+
15586
15428
  // Step 2: Ensure _top bookmark exists at document start
15587
15429
  this.addTopBookmark();
15588
-
15430
+
15589
15431
  // Step 3: Get document.xml to scan for TOC elements
15590
15432
  const docXml = this.zipHandler.getFileAsString('word/document.xml');
15591
15433
  if (!docXml) {
15592
15434
  return results;
15593
15435
  }
15594
-
15436
+
15595
15437
  // Step 4: Find all TOC SDT elements
15596
15438
  const tocRegex = /<w:sdt>[\s\S]*?<w:docPartGallery w:val="Table of Contents"[\s\S]*?<\/w:sdt>/g;
15597
15439
  const tocMatches = Array.from(docXml.matchAll(tocRegex));
15598
-
15440
+
15599
15441
  if (tocMatches.length === 0) {
15600
15442
  return results;
15601
15443
  }
15602
-
15444
+
15603
15445
  // Step 5: For each TOC, parse instructions and count headings
15604
15446
  for (const match of tocMatches) {
15605
15447
  try {
15606
15448
  const tocXml = match[0];
15607
-
15449
+
15608
15450
  // Extract field instruction
15609
15451
  const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(tocXml);
15610
15452
  if (!instrMatch?.[1]) {
15611
15453
  continue;
15612
15454
  }
15613
-
15455
+
15614
15456
  // TypeScript type narrowing: assign to const variable
15615
15457
  const instrText = instrMatch[1];
15616
-
15458
+
15617
15459
  // Decode XML entities
15618
15460
  const fieldInstruction = instrText
15619
15461
  .replace(/&/g, '&')
@@ -15621,23 +15463,23 @@ export class Document {
15621
15463
  .replace(/>/g, '>')
15622
15464
  .replace(/"/g, '"')
15623
15465
  .replace(/'/g, "'");
15624
-
15466
+
15625
15467
  // Parse the instruction to get heading levels
15626
15468
  const levels = this.parseTOCFieldInstruction(fieldInstruction);
15627
-
15469
+
15628
15470
  // Find all headings in document (including nested tables)
15629
15471
  const headings = this.findHeadingsForTOCFromXML(docXml, levels);
15630
-
15472
+
15631
15473
  // Count headings by level (create array with counts for each level 1-9)
15632
15474
  const headingCounts: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Indices 0-8 for levels 1-9
15633
-
15475
+
15634
15476
  for (const heading of headings) {
15635
15477
  if (heading.level >= 1 && heading.level <= 9) {
15636
15478
  const index = heading.level - 1;
15637
15479
  headingCounts[index] = (headingCounts[index] || 0) + 1;
15638
15480
  }
15639
15481
  }
15640
-
15482
+
15641
15483
  // Add to results: [instruction, counts]
15642
15484
  results.push([fieldInstruction, headingCounts]);
15643
15485
  } catch (error: unknown) {
@@ -15651,20 +15493,18 @@ export class Document {
15651
15493
  continue;
15652
15494
  }
15653
15495
  }
15654
-
15496
+
15655
15497
  // Step 6: Populate all TOCs in the document with hyperlinked entries
15656
15498
  // This modifies the XML to include pre-populated TOC entries with hyperlinks
15657
15499
  const populatedXml = this.populateAllTOCsInXML(docXml);
15658
-
15500
+
15659
15501
  // Step 7: Update document.xml with the populated TOCs
15660
15502
  if (populatedXml !== docXml) {
15661
15503
  this.zipHandler.updateFile('word/document.xml', populatedXml);
15662
-
15663
- this.logger.info(
15664
- `Successfully populated ${results.length} TOC(s) with hyperlinked entries`
15665
- );
15504
+
15505
+ this.logger.info(`Successfully populated ${results.length} TOC(s) with hyperlinked entries`);
15666
15506
  }
15667
-
15507
+
15668
15508
  return results;
15669
15509
  }
15670
15510
 
@@ -15696,14 +15536,14 @@ export class Document {
15696
15536
  * ```
15697
15537
  */
15698
15538
  normalizeTableBorders(options?: {
15699
- style?: "single" | "double" | "dotted" | "dashed" | "thick" | "none";
15539
+ style?: 'single' | 'double' | 'dotted' | 'dashed' | 'thick' | 'none';
15700
15540
  size?: number;
15701
15541
  color?: string;
15702
15542
  }): number {
15703
15543
  const border: TableBorder = {
15704
- style: options?.style ?? "single",
15544
+ style: options?.style ?? 'single',
15705
15545
  size: options?.size ?? 4,
15706
- color: options?.color ?? "000000",
15546
+ color: options?.color ?? '000000',
15707
15547
  };
15708
15548
 
15709
15549
  return this.applyBordersToAllTables(border);
@@ -15754,8 +15594,8 @@ export class Document {
15754
15594
 
15755
15595
  // Create regex pattern
15756
15596
  const pattern = matchCase
15757
- ? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")
15758
- : new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
15597
+ ? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
15598
+ : new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
15759
15599
 
15760
15600
  // Process body paragraphs
15761
15601
  for (const para of this.getAllParagraphs()) {
@@ -15839,36 +15679,36 @@ export class Document {
15839
15679
 
15840
15680
  // [Content_Types].xml - minimal
15841
15681
  zipHandler.addFile(
15842
- "[Content_Types].xml",
15682
+ '[Content_Types].xml',
15843
15683
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15844
15684
  '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
15845
15685
  ' <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n' +
15846
15686
  ' <Default Extension="xml" ContentType="application/xml"/>\n' +
15847
15687
  ' <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>\n' +
15848
- "</Types>"
15688
+ '</Types>'
15849
15689
  );
15850
15690
 
15851
15691
  // _rels/.rels - only reference parts that actually exist
15852
15692
  zipHandler.addFile(
15853
- "_rels/.rels",
15693
+ '_rels/.rels',
15854
15694
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15855
15695
  '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
15856
15696
  ' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>\n' +
15857
- "</Relationships>"
15697
+ '</Relationships>'
15858
15698
  );
15859
15699
 
15860
15700
  // word/document.xml - empty body
15861
15701
  zipHandler.addFile(
15862
- "word/document.xml",
15702
+ 'word/document.xml',
15863
15703
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15864
15704
  '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">\n' +
15865
- " <w:body/>\n" +
15866
- "</w:document>"
15705
+ ' <w:body/>\n' +
15706
+ '</w:document>'
15867
15707
  );
15868
15708
 
15869
15709
  // word/_rels/document.xml.rels - empty relationships
15870
15710
  zipHandler.addFile(
15871
- "word/_rels/document.xml.rels",
15711
+ 'word/_rels/document.xml.rels',
15872
15712
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15873
15713
  '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>'
15874
15714
  );