docxmlater 10.1.4 → 10.1.6

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 (372) 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 +51 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +486 -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 +1 -0
  146. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  147. package/dist/formatting/NumberingManager.js +27 -9
  148. package/dist/formatting/NumberingManager.js.map +1 -1
  149. package/dist/formatting/Style.d.ts +11 -11
  150. package/dist/formatting/Style.d.ts.map +1 -1
  151. package/dist/formatting/Style.js +219 -247
  152. package/dist/formatting/Style.js.map +1 -1
  153. package/dist/formatting/StylesManager.d.ts +2 -2
  154. package/dist/formatting/StylesManager.d.ts.map +1 -1
  155. package/dist/formatting/StylesManager.js +96 -102
  156. package/dist/formatting/StylesManager.js.map +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts +1 -1
  158. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  159. package/dist/helpers/CleanupHelper.js +6 -6
  160. package/dist/helpers/CleanupHelper.js.map +1 -1
  161. package/dist/images/ImageOptimizer.js +7 -7
  162. package/dist/images/ImageOptimizer.js.map +1 -1
  163. package/dist/index.d.ts +9 -9
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js.map +1 -1
  166. package/dist/managers/DrawingManager.js.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  168. package/dist/tracking/DocumentTrackingContext.js +23 -7
  169. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  170. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  171. package/dist/tracking/TrackingContext.js.map +1 -1
  172. package/dist/types/compatibility-types.js.map +1 -1
  173. package/dist/types/formatting.js.map +1 -1
  174. package/dist/types/list-types.d.ts +6 -6
  175. package/dist/types/list-types.js.map +1 -1
  176. package/dist/types/settings-types.js.map +1 -1
  177. package/dist/types/styleConfig.d.ts +2 -2
  178. package/dist/types/styleConfig.js.map +1 -1
  179. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  180. package/dist/utils/ChangelogGenerator.js +97 -101
  181. package/dist/utils/ChangelogGenerator.js.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js +1 -1
  184. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  186. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  187. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  188. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  189. package/dist/utils/MoveOperationHelper.js +1 -1
  190. package/dist/utils/MoveOperationHelper.js.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  192. package/dist/utils/RevisionAwareProcessor.js +2 -4
  193. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  194. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  195. package/dist/utils/RevisionWalker.js +4 -12
  196. package/dist/utils/RevisionWalker.js.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  198. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  199. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  200. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  201. package/dist/utils/ShadingResolver.js +1 -1
  202. package/dist/utils/ShadingResolver.js.map +1 -1
  203. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  204. package/dist/utils/acceptRevisions.js +23 -12
  205. package/dist/utils/acceptRevisions.js.map +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  207. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  208. package/dist/utils/cnfStyleDecoder.js +40 -40
  209. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  210. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  211. package/dist/utils/corruptionDetection.js.map +1 -1
  212. package/dist/utils/dateFormatting.js.map +1 -1
  213. package/dist/utils/deepClone.js +1 -1
  214. package/dist/utils/deepClone.js.map +1 -1
  215. package/dist/utils/diagnostics.d.ts.map +1 -1
  216. package/dist/utils/diagnostics.js +1 -1
  217. package/dist/utils/diagnostics.js.map +1 -1
  218. package/dist/utils/errorHandling.js.map +1 -1
  219. package/dist/utils/formatting.d.ts.map +1 -1
  220. package/dist/utils/formatting.js +10 -2
  221. package/dist/utils/formatting.js.map +1 -1
  222. package/dist/utils/list-detection.d.ts +2 -2
  223. package/dist/utils/list-detection.d.ts.map +1 -1
  224. package/dist/utils/list-detection.js +21 -23
  225. package/dist/utils/list-detection.js.map +1 -1
  226. package/dist/utils/logger.d.ts.map +1 -1
  227. package/dist/utils/logger.js +12 -7
  228. package/dist/utils/logger.js.map +1 -1
  229. package/dist/utils/parsingHelpers.js.map +1 -1
  230. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  231. package/dist/utils/stripTrackedChanges.js +3 -3
  232. package/dist/utils/stripTrackedChanges.js.map +1 -1
  233. package/dist/utils/textDiff.d.ts +1 -1
  234. package/dist/utils/textDiff.js +8 -8
  235. package/dist/utils/textDiff.js.map +1 -1
  236. package/dist/utils/units.js.map +1 -1
  237. package/dist/utils/validation.d.ts.map +1 -1
  238. package/dist/utils/validation.js +24 -7
  239. package/dist/utils/validation.js.map +1 -1
  240. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  241. package/dist/utils/xmlSanitization.js +3 -3
  242. package/dist/utils/xmlSanitization.js.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  244. package/dist/validation/RevisionAutoFixer.js +5 -5
  245. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  246. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  247. package/dist/validation/RevisionValidator.js +7 -9
  248. package/dist/validation/RevisionValidator.js.map +1 -1
  249. package/dist/validation/ValidationRules.js +3 -3
  250. package/dist/validation/ValidationRules.js.map +1 -1
  251. package/dist/validation/index.js.map +1 -1
  252. package/dist/xml/XMLBuilder.d.ts +1 -1
  253. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  254. package/dist/xml/XMLBuilder.js +98 -100
  255. package/dist/xml/XMLBuilder.js.map +1 -1
  256. package/dist/xml/XMLParser.d.ts.map +1 -1
  257. package/dist/xml/XMLParser.js +61 -66
  258. package/dist/xml/XMLParser.js.map +1 -1
  259. package/dist/zip/ZipHandler.d.ts.map +1 -1
  260. package/dist/zip/ZipHandler.js.map +1 -1
  261. package/dist/zip/ZipReader.d.ts.map +1 -1
  262. package/dist/zip/ZipReader.js +1 -3
  263. package/dist/zip/ZipReader.js.map +1 -1
  264. package/dist/zip/ZipWriter.d.ts +1 -1
  265. package/dist/zip/ZipWriter.d.ts.map +1 -1
  266. package/dist/zip/ZipWriter.js +28 -36
  267. package/dist/zip/ZipWriter.js.map +1 -1
  268. package/dist/zip/types.js +1 -1
  269. package/dist/zip/types.js.map +1 -1
  270. package/package.json +92 -92
  271. package/src/__tests__/helper-methods.test.ts +512 -512
  272. package/src/constants/legacyCompatFlags.ts +138 -138
  273. package/src/constants/limits.ts +50 -50
  274. package/src/core/Document.ts +1010 -1145
  275. package/src/core/DocumentContent.ts +461 -467
  276. package/src/core/DocumentGenerator.ts +1133 -1104
  277. package/src/core/DocumentIdManager.ts +158 -158
  278. package/src/core/DocumentParser.ts +2347 -2716
  279. package/src/core/DocumentValidator.ts +363 -372
  280. package/src/core/Relationship.ts +367 -367
  281. package/src/core/RelationshipManager.ts +429 -428
  282. package/src/elements/AlternateContent.ts +42 -42
  283. package/src/elements/Bookmark.ts +212 -210
  284. package/src/elements/BookmarkManager.ts +247 -250
  285. package/src/elements/Comment.ts +356 -359
  286. package/src/elements/CommentManager.ts +499 -502
  287. package/src/elements/CommonTypes.ts +524 -549
  288. package/src/elements/CustomXml.ts +36 -36
  289. package/src/elements/Endnote.ts +221 -217
  290. package/src/elements/EndnoteManager.ts +246 -249
  291. package/src/elements/Field.ts +1292 -1233
  292. package/src/elements/FieldHelpers.ts +329 -333
  293. package/src/elements/FontManager.ts +336 -339
  294. package/src/elements/Footer.ts +269 -269
  295. package/src/elements/Footnote.ts +221 -217
  296. package/src/elements/FootnoteManager.ts +246 -249
  297. package/src/elements/Header.ts +269 -269
  298. package/src/elements/HeaderFooterManager.ts +219 -219
  299. package/src/elements/Hyperlink.ts +1288 -1193
  300. package/src/elements/Image.ts +1982 -1756
  301. package/src/elements/ImageManager.ts +437 -432
  302. package/src/elements/ImageRun.ts +59 -59
  303. package/src/elements/MathElement.ts +65 -65
  304. package/src/elements/Paragraph.ts +4347 -4287
  305. package/src/elements/PreservedElement.ts +53 -53
  306. package/src/elements/PropertyChangeTypes.ts +458 -442
  307. package/src/elements/RangeMarker.ts +382 -400
  308. package/src/elements/Revision.ts +1198 -1217
  309. package/src/elements/RevisionContent.ts +73 -73
  310. package/src/elements/RevisionManager.ts +1070 -1070
  311. package/src/elements/Run.ts +3103 -3073
  312. package/src/elements/Section.ts +1521 -1421
  313. package/src/elements/Shape.ts +884 -873
  314. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  315. package/src/elements/Table.ts +2468 -2524
  316. package/src/elements/TableCell.ts +1617 -1621
  317. package/src/elements/TableGridChange.ts +149 -151
  318. package/src/elements/TableOfContents.ts +701 -691
  319. package/src/elements/TableOfContentsElement.ts +89 -89
  320. package/src/elements/TableRow.ts +960 -929
  321. package/src/elements/TextBox.ts +766 -768
  322. package/src/formatting/AbstractNumbering.ts +580 -579
  323. package/src/formatting/NumberingInstance.ts +295 -299
  324. package/src/formatting/NumberingLevel.ts +981 -1040
  325. package/src/formatting/NumberingManager.ts +875 -827
  326. package/src/formatting/Style.ts +1785 -1879
  327. package/src/formatting/StylesManager.ts +1090 -1130
  328. package/src/helpers/CleanupHelper.ts +524 -524
  329. package/src/images/ImageOptimizer.ts +274 -274
  330. package/src/index.ts +559 -554
  331. package/src/managers/DrawingManager.ts +319 -319
  332. package/src/tracking/DocumentTrackingContext.ts +687 -674
  333. package/src/tracking/TrackingContext.ts +175 -173
  334. package/src/types/compatibility-types.ts +49 -49
  335. package/src/types/formatting.ts +210 -210
  336. package/src/types/list-types.ts +14 -14
  337. package/src/types/settings-types.ts +59 -59
  338. package/src/types/styleConfig.ts +189 -189
  339. package/src/utils/ChangelogGenerator.ts +1583 -1581
  340. package/src/utils/CompatibilityUpgrader.ts +235 -237
  341. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  342. package/src/utils/MoveOperationHelper.ts +233 -238
  343. package/src/utils/RevisionAwareProcessor.ts +518 -526
  344. package/src/utils/RevisionWalker.ts +427 -457
  345. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  346. package/src/utils/ShadingResolver.ts +105 -107
  347. package/src/utils/acceptRevisions.ts +723 -714
  348. package/src/utils/cnfStyleDecoder.ts +212 -217
  349. package/src/utils/corruptionDetection.ts +346 -345
  350. package/src/utils/dateFormatting.ts +20 -20
  351. package/src/utils/deepClone.ts +77 -78
  352. package/src/utils/diagnostics.ts +125 -129
  353. package/src/utils/errorHandling.ts +80 -80
  354. package/src/utils/formatting.ts +220 -213
  355. package/src/utils/list-detection.ts +32 -42
  356. package/src/utils/logger.ts +412 -404
  357. package/src/utils/parsingHelpers.ts +190 -190
  358. package/src/utils/stripTrackedChanges.ts +356 -353
  359. package/src/utils/textDiff.ts +100 -100
  360. package/src/utils/units.ts +421 -421
  361. package/src/utils/validation.ts +553 -542
  362. package/src/utils/xmlSanitization.ts +179 -182
  363. package/src/validation/RevisionAutoFixer.ts +541 -542
  364. package/src/validation/RevisionValidator.ts +470 -460
  365. package/src/validation/ValidationRules.ts +338 -338
  366. package/src/validation/index.ts +30 -30
  367. package/src/xml/XMLBuilder.ts +857 -871
  368. package/src/xml/XMLParser.ts +877 -919
  369. package/src/zip/ZipHandler.ts +629 -637
  370. package/src/zip/ZipReader.ts +295 -299
  371. package/src/zip/ZipWriter.ts +374 -390
  372. 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
 
@@ -5738,6 +5741,31 @@ export class Document {
5738
5741
  return this.numberingManager.createMultiLevelList();
5739
5742
  }
5740
5743
 
5744
+ /**
5745
+ * Creates a new numbering instance that restarts numbering for an existing list
5746
+ *
5747
+ * This creates a new `<w:num>` referencing the same abstract numbering as the
5748
+ * given numId, but with a level override to restart counting at the specified value.
5749
+ *
5750
+ * @param numId The existing numbering instance ID to base the restart on
5751
+ * @param level The level to restart (0-8, default: 0)
5752
+ * @param startValue The value to restart from (>= 1, default: 1)
5753
+ * @returns The new numId to use with paragraph.setNumbering()
5754
+ *
5755
+ * @example
5756
+ * ```typescript
5757
+ * const listId = doc.createNumberedList();
5758
+ * doc.createParagraph('First list item 1').setNumbering(listId, 0);
5759
+ * doc.createParagraph('First list item 2').setNumbering(listId, 0);
5760
+ *
5761
+ * const restartId = doc.restartNumbering(listId);
5762
+ * doc.createParagraph('Second list item 1').setNumbering(restartId, 0);
5763
+ * ```
5764
+ */
5765
+ restartNumbering(numId: number, level?: number, startValue?: number): number {
5766
+ return this.numberingManager.restartNumbering(numId, level, startValue);
5767
+ }
5768
+
5741
5769
  /**
5742
5770
  * Gets the framework's standard indentation for a list level
5743
5771
  *
@@ -5783,12 +5811,7 @@ export class Document {
5783
5811
  leftIndent: number,
5784
5812
  hangingIndent?: number
5785
5813
  ): this {
5786
- this.numberingManager.setListIndentation(
5787
- numId,
5788
- level,
5789
- leftIndent,
5790
- hangingIndent
5791
- );
5814
+ this.numberingManager.setListIndentation(numId, level, leftIndent, hangingIndent);
5792
5815
  return this;
5793
5816
  }
5794
5817
 
@@ -5838,14 +5861,13 @@ export class Document {
5838
5861
 
5839
5862
  for (const instance of instances) {
5840
5863
  const abstractNumId = instance.getAbstractNumId();
5841
- const abstractNum =
5842
- this.numberingManager.getAbstractNumbering(abstractNumId);
5864
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
5843
5865
 
5844
5866
  if (!abstractNum) continue;
5845
5867
 
5846
5868
  // Only process bullet lists (skip numbered lists)
5847
5869
  const level0 = abstractNum.getLevel(0);
5848
- if (level0?.getFormat() !== "bullet") continue;
5870
+ if (level0?.getFormat() !== 'bullet') continue;
5849
5871
 
5850
5872
  // Update all 9 levels (0-8) with standard formatting
5851
5873
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -5853,11 +5875,11 @@ export class Document {
5853
5875
  if (!numLevel) continue;
5854
5876
 
5855
5877
  // Alternate bullets: even levels = solid (•), odd levels = open (○)
5856
- const bullet = levelIndex % 2 === 0 ? "" : "";
5878
+ const bullet = levelIndex % 2 === 0 ? '' : '';
5857
5879
  numLevel.setText(bullet);
5858
5880
 
5859
5881
  // Set bullet font to Arial (Unicode bullets require a regular font, not Symbol)
5860
- numLevel.setFont("Arial");
5882
+ numLevel.setFont('Arial');
5861
5883
 
5862
5884
  // Set bullet size to 12pt (24 half-points)
5863
5885
  numLevel.setFontSize(24);
@@ -5908,14 +5930,13 @@ export class Document {
5908
5930
 
5909
5931
  for (const instance of instances) {
5910
5932
  const abstractNumId = instance.getAbstractNumId();
5911
- const abstractNum =
5912
- this.numberingManager.getAbstractNumbering(abstractNumId);
5933
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
5913
5934
 
5914
5935
  if (!abstractNum) continue;
5915
5936
 
5916
5937
  // Only process numbered lists (skip bullet lists)
5917
5938
  const level0 = abstractNum.getLevel(0);
5918
- if (!level0 || level0.getFormat() === "bullet") continue;
5939
+ if (!level0 || level0.getFormat() === 'bullet') continue;
5919
5940
 
5920
5941
  // Update all 9 levels (0-8) with standard formatting
5921
5942
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -5923,7 +5944,7 @@ export class Document {
5923
5944
  if (!numLevel) continue;
5924
5945
 
5925
5946
  // Set number font to Verdana 12pt
5926
- numLevel.setFont("Verdana");
5947
+ numLevel.setFont('Verdana');
5927
5948
  numLevel.setFontSize(24); // 12pt = 24 half-points
5928
5949
 
5929
5950
  // Indentation: 0.5" per level (720 twips)
@@ -5934,7 +5955,7 @@ export class Document {
5934
5955
  numLevel.setHangingIndent(360);
5935
5956
 
5936
5957
  // Set alignment to left
5937
- numLevel.setAlignment("left");
5958
+ numLevel.setAlignment('left');
5938
5959
  }
5939
5960
 
5940
5961
  // Apply paragraph formatting to all paragraphs using this list
@@ -5960,7 +5981,7 @@ export class Document {
5960
5981
  // Apply font to all runs in the paragraph
5961
5982
  const runs = para.getRuns();
5962
5983
  for (const run of runs) {
5963
- run.setFont("Verdana", 12);
5984
+ run.setFont('Verdana', 12);
5964
5985
  }
5965
5986
 
5966
5987
  // Apply paragraph spacing
@@ -6024,7 +6045,7 @@ export class Document {
6024
6045
  // Find the paragraph index in bodyElements
6025
6046
  const paraIndex = this.bodyElements.indexOf(para);
6026
6047
  if (paraIndex === -1) {
6027
- throw new Error("Paragraph not found in document body elements");
6048
+ throw new Error('Paragraph not found in document body elements');
6028
6049
  }
6029
6050
 
6030
6051
  // Create 1x1 table
@@ -6032,7 +6053,7 @@ export class Document {
6032
6053
  const cell = table.getCell(0, 0);
6033
6054
 
6034
6055
  if (!cell) {
6035
- throw new Error("Failed to get cell from newly created table");
6056
+ throw new Error('Failed to get cell from newly created table');
6036
6057
  }
6037
6058
 
6038
6059
  // Move paragraph to cell
@@ -6064,7 +6085,7 @@ export class Document {
6064
6085
  // Set table width (percentage of page width)
6065
6086
  if (options.tableWidthPercent !== undefined) {
6066
6087
  table.setWidth(options.tableWidthPercent);
6067
- table.setWidthType("pct");
6088
+ table.setWidthType('pct');
6068
6089
  }
6069
6090
 
6070
6091
  // Insert table where paragraph was
@@ -6076,30 +6097,30 @@ export class Document {
6076
6097
  // Default style configurations for applyStyles()
6077
6098
  private static readonly DEFAULT_HEADING1_CONFIG: StyleConfig = {
6078
6099
  run: {
6079
- font: "Verdana",
6100
+ font: 'Verdana',
6080
6101
  size: 18,
6081
6102
  bold: true,
6082
- color: "000000",
6103
+ color: '000000',
6083
6104
  },
6084
6105
  paragraph: {
6085
- alignment: "left",
6086
- spacing: { before: 0, after: 240, line: 240, lineRule: "auto" },
6106
+ alignment: 'left',
6107
+ spacing: { before: 0, after: 240, line: 240, lineRule: 'auto' },
6087
6108
  },
6088
6109
  };
6089
6110
 
6090
6111
  private static readonly DEFAULT_HEADING2_CONFIG: Heading2Config = {
6091
6112
  run: {
6092
- font: "Verdana",
6113
+ font: 'Verdana',
6093
6114
  size: 14,
6094
6115
  bold: true,
6095
- color: "000000",
6116
+ color: '000000',
6096
6117
  },
6097
6118
  paragraph: {
6098
- alignment: "left",
6099
- spacing: { before: 120, after: 120, line: 240, lineRule: "auto" },
6119
+ alignment: 'left',
6120
+ spacing: { before: 120, after: 120, line: 240, lineRule: 'auto' },
6100
6121
  },
6101
6122
  tableOptions: {
6102
- shading: "BFBFBF",
6123
+ shading: 'BFBFBF',
6103
6124
  marginTop: 0,
6104
6125
  marginBottom: 0,
6105
6126
  marginLeft: 115,
@@ -6110,38 +6131,38 @@ export class Document {
6110
6131
 
6111
6132
  private static readonly DEFAULT_HEADING3_CONFIG: StyleConfig = {
6112
6133
  run: {
6113
- font: "Verdana",
6134
+ font: 'Verdana',
6114
6135
  size: 12,
6115
6136
  bold: true,
6116
- color: "000000",
6137
+ color: '000000',
6117
6138
  },
6118
6139
  paragraph: {
6119
- alignment: "left",
6120
- spacing: { before: 60, after: 60, line: 240, lineRule: "auto" },
6140
+ alignment: 'left',
6141
+ spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
6121
6142
  },
6122
6143
  };
6123
6144
 
6124
6145
  private static readonly DEFAULT_NORMAL_CONFIG: StyleConfig = {
6125
6146
  run: {
6126
- font: "Verdana",
6147
+ font: 'Verdana',
6127
6148
  size: 12,
6128
- color: "000000",
6149
+ color: '000000',
6129
6150
  },
6130
6151
  paragraph: {
6131
- alignment: "left",
6132
- spacing: { before: 60, after: 60, line: 240, lineRule: "auto" },
6152
+ alignment: 'left',
6153
+ spacing: { before: 60, after: 60, line: 240, lineRule: 'auto' },
6133
6154
  },
6134
6155
  };
6135
6156
 
6136
6157
  private static readonly DEFAULT_LIST_PARAGRAPH_CONFIG: StyleConfig = {
6137
6158
  run: {
6138
- font: "Verdana",
6159
+ font: 'Verdana',
6139
6160
  size: 12,
6140
- color: "000000",
6161
+ color: '000000',
6141
6162
  },
6142
6163
  paragraph: {
6143
- alignment: "left",
6144
- spacing: { before: 0, after: 60, line: 240, lineRule: "auto" },
6164
+ alignment: 'left',
6165
+ spacing: { before: 0, after: 60, line: 240, lineRule: 'auto' },
6145
6166
  indentation: { left: 360, hanging: 360 },
6146
6167
  contextualSpacing: true,
6147
6168
  },
@@ -6260,35 +6281,31 @@ export class Document {
6260
6281
  };
6261
6282
 
6262
6283
  // 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");
6284
+ const heading1 = this.stylesManager.getStyle('Heading1');
6285
+ const heading2 = this.stylesManager.getStyle('Heading2');
6286
+ const heading3 = this.stylesManager.getStyle('Heading3');
6287
+ const normal = this.stylesManager.getStyle('Normal');
6288
+ const listParagraph = this.stylesManager.getStyle('ListParagraph');
6268
6289
 
6269
6290
  // Merge provided options with ACTUAL current style values (not hardcoded defaults)
6270
6291
  // This allows users to only specify properties they want to change
6271
6292
  const h1Config = {
6272
6293
  run: {
6273
- ...(heading1?.getRunFormatting() ||
6274
- Document.DEFAULT_HEADING1_CONFIG.run),
6294
+ ...(heading1?.getRunFormatting() || Document.DEFAULT_HEADING1_CONFIG.run),
6275
6295
  ...options?.heading1?.run,
6276
6296
  },
6277
6297
  paragraph: {
6278
- ...(heading1?.getParagraphFormatting() ||
6279
- Document.DEFAULT_HEADING1_CONFIG.paragraph),
6298
+ ...(heading1?.getParagraphFormatting() || Document.DEFAULT_HEADING1_CONFIG.paragraph),
6280
6299
  ...options?.heading1?.paragraph,
6281
6300
  },
6282
6301
  };
6283
6302
  const h2Config = {
6284
6303
  run: {
6285
- ...(heading2?.getRunFormatting() ||
6286
- Document.DEFAULT_HEADING2_CONFIG.run),
6304
+ ...(heading2?.getRunFormatting() || Document.DEFAULT_HEADING2_CONFIG.run),
6287
6305
  ...options?.heading2?.run,
6288
6306
  },
6289
6307
  paragraph: {
6290
- ...(heading2?.getParagraphFormatting() ||
6291
- Document.DEFAULT_HEADING2_CONFIG.paragraph),
6308
+ ...(heading2?.getParagraphFormatting() || Document.DEFAULT_HEADING2_CONFIG.paragraph),
6292
6309
  ...options?.heading2?.paragraph,
6293
6310
  },
6294
6311
  tableOptions: {
@@ -6298,13 +6315,11 @@ export class Document {
6298
6315
  };
6299
6316
  const h3Config = {
6300
6317
  run: {
6301
- ...(heading3?.getRunFormatting() ||
6302
- Document.DEFAULT_HEADING3_CONFIG.run),
6318
+ ...(heading3?.getRunFormatting() || Document.DEFAULT_HEADING3_CONFIG.run),
6303
6319
  ...options?.heading3?.run,
6304
6320
  },
6305
6321
  paragraph: {
6306
- ...(heading3?.getParagraphFormatting() ||
6307
- Document.DEFAULT_HEADING3_CONFIG.paragraph),
6322
+ ...(heading3?.getParagraphFormatting() || Document.DEFAULT_HEADING3_CONFIG.paragraph),
6308
6323
  ...options?.heading3?.paragraph,
6309
6324
  },
6310
6325
  };
@@ -6314,15 +6329,13 @@ export class Document {
6314
6329
  ...options?.normal?.run,
6315
6330
  },
6316
6331
  paragraph: {
6317
- ...(normal?.getParagraphFormatting() ||
6318
- Document.DEFAULT_NORMAL_CONFIG.paragraph),
6332
+ ...(normal?.getParagraphFormatting() || Document.DEFAULT_NORMAL_CONFIG.paragraph),
6319
6333
  ...options?.normal?.paragraph,
6320
6334
  },
6321
6335
  };
6322
6336
  const listParaConfig = {
6323
6337
  run: {
6324
- ...(listParagraph?.getRunFormatting() ||
6325
- Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
6338
+ ...(listParagraph?.getRunFormatting() || Document.DEFAULT_LIST_PARAGRAPH_CONFIG.run),
6326
6339
  ...options?.listParagraph?.run,
6327
6340
  },
6328
6341
  paragraph: {
@@ -6333,14 +6346,12 @@ export class Document {
6333
6346
  };
6334
6347
 
6335
6348
  // Extract preserve blank lines option (defaults to true)
6336
- const preserveBlankLines =
6337
- options?.preserveBlankLinesAfterHeading2Tables ?? true;
6349
+ const preserveBlankLines = options?.preserveBlankLinesAfterHeading2Tables ?? true;
6338
6350
 
6339
6351
  // Modify Heading1 definition
6340
6352
  if (heading1 && h1Config.run && h1Config.paragraph) {
6341
6353
  if (h1Config.run) heading1.setRunFormatting(h1Config.run);
6342
- if (h1Config.paragraph)
6343
- heading1.setParagraphFormatting(h1Config.paragraph);
6354
+ if (h1Config.paragraph) heading1.setParagraphFormatting(h1Config.paragraph);
6344
6355
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6345
6356
  this.addStyle(heading1);
6346
6357
  results.heading1 = true;
@@ -6349,8 +6360,7 @@ export class Document {
6349
6360
  // Modify Heading2 definition
6350
6361
  if (heading2 && h2Config.run && h2Config.paragraph) {
6351
6362
  if (h2Config.run) heading2.setRunFormatting(h2Config.run);
6352
- if (h2Config.paragraph)
6353
- heading2.setParagraphFormatting(h2Config.paragraph);
6363
+ if (h2Config.paragraph) heading2.setParagraphFormatting(h2Config.paragraph);
6354
6364
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6355
6365
  this.addStyle(heading2);
6356
6366
  results.heading2 = true;
@@ -6359,8 +6369,7 @@ export class Document {
6359
6369
  // Modify Heading3 definition
6360
6370
  if (heading3 && h3Config.run && h3Config.paragraph) {
6361
6371
  if (h3Config.run) heading3.setRunFormatting(h3Config.run);
6362
- if (h3Config.paragraph)
6363
- heading3.setParagraphFormatting(h3Config.paragraph);
6372
+ if (h3Config.paragraph) heading3.setParagraphFormatting(h3Config.paragraph);
6364
6373
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6365
6374
  this.addStyle(heading3);
6366
6375
  results.heading3 = true;
@@ -6369,8 +6378,7 @@ export class Document {
6369
6378
  // Modify Normal definition
6370
6379
  if (normal && normalConfig.run && normalConfig.paragraph) {
6371
6380
  if (normalConfig.run) normal.setRunFormatting(normalConfig.run);
6372
- if (normalConfig.paragraph)
6373
- normal.setParagraphFormatting(normalConfig.paragraph);
6381
+ if (normalConfig.paragraph) normal.setParagraphFormatting(normalConfig.paragraph);
6374
6382
  // Mark style as modified so it gets included in mergeStylesWithOriginal()
6375
6383
  this.addStyle(normal);
6376
6384
 
@@ -6378,12 +6386,11 @@ export class Document {
6378
6386
  // Default is true - changes to Normal automatically apply to NormalWeb
6379
6387
  const shouldLinkNormalWeb = options?.linkNormalWebToNormal !== false;
6380
6388
  if (shouldLinkNormalWeb) {
6381
- const normalWeb = this.stylesManager.getStyle("NormalWeb");
6389
+ const normalWeb = this.stylesManager.getStyle('NormalWeb');
6382
6390
  if (normalWeb) {
6383
6391
  // Apply same formatting to NormalWeb
6384
6392
  if (normalConfig.run) normalWeb.setRunFormatting(normalConfig.run);
6385
- if (normalConfig.paragraph)
6386
- normalWeb.setParagraphFormatting(normalConfig.paragraph);
6393
+ if (normalConfig.paragraph) normalWeb.setParagraphFormatting(normalConfig.paragraph);
6387
6394
  // Mark as modified for selective merging during save
6388
6395
  this.addStyle(normalWeb);
6389
6396
  }
@@ -6394,8 +6401,7 @@ export class Document {
6394
6401
 
6395
6402
  // Modify List Paragraph definition
6396
6403
  if (listParagraph && listParaConfig.run && listParaConfig.paragraph) {
6397
- if (listParaConfig.run)
6398
- listParagraph.setRunFormatting(listParaConfig.run);
6404
+ if (listParaConfig.run) listParagraph.setRunFormatting(listParaConfig.run);
6399
6405
  if (listParaConfig.paragraph) {
6400
6406
  // Validate indentation: hanging must not exceed left to prevent negative bullet position
6401
6407
  const indent = listParaConfig.paragraph.indentation;
@@ -6404,7 +6410,7 @@ export class Document {
6404
6410
  const logger = getGlobalLogger();
6405
6411
  logger.warn(
6406
6412
  `[Document] ListParagraph indentation: hanging (${indent.hanging}) > left (${indent.left}). ` +
6407
- `Capping hanging to left to prevent negative bullet position.`
6413
+ `Capping hanging to left to prevent negative bullet position.`
6408
6414
  );
6409
6415
  indent.hanging = indent.left;
6410
6416
  }
@@ -6459,12 +6465,12 @@ export class Document {
6459
6465
  const styleId = para.getStyle();
6460
6466
 
6461
6467
  // Process Heading1 paragraphs
6462
- if (styleId === "Heading1" && heading1) {
6468
+ if (styleId === 'Heading1' && heading1) {
6463
6469
  // Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
6464
6470
  const allRuns = this.getAllRunsFromParagraph(para);
6465
6471
  const whiteFontRuns = new Set(
6466
6472
  options?.preserveWhiteFont
6467
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6473
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6468
6474
  : []
6469
6475
  );
6470
6476
 
@@ -6483,7 +6489,7 @@ export class Document {
6483
6489
  run.setItalic(h1Config.run?.italic ?? false);
6484
6490
  }
6485
6491
  if (!h1Preserve.underline) {
6486
- run.setUnderline(h1Config.run?.underline ? "single" : false);
6492
+ run.setUnderline(h1Config.run?.underline ? 'single' : false);
6487
6493
  }
6488
6494
  // Apply font, color, and size - skip color if run was white font
6489
6495
  if (h1Config.run?.font) {
@@ -6505,25 +6511,13 @@ export class Document {
6505
6511
  // Update paragraph mark properties to match configuration
6506
6512
  if (para.formatting.paragraphMarkRunProperties) {
6507
6513
  const markProps = para.formatting.paragraphMarkRunProperties;
6508
- if (
6509
- !h1Preserve.bold &&
6510
- h1Config.run?.bold === false &&
6511
- markProps.bold
6512
- ) {
6514
+ if (!h1Preserve.bold && h1Config.run?.bold === false && markProps.bold) {
6513
6515
  delete markProps.bold;
6514
6516
  }
6515
- if (
6516
- !h1Preserve.italic &&
6517
- h1Config.run?.italic === false &&
6518
- markProps.italic
6519
- ) {
6517
+ if (!h1Preserve.italic && h1Config.run?.italic === false && markProps.italic) {
6520
6518
  delete markProps.italic;
6521
6519
  }
6522
- if (
6523
- !h1Preserve.underline &&
6524
- h1Config.run?.underline === false &&
6525
- markProps.underline
6526
- ) {
6520
+ if (!h1Preserve.underline && h1Config.run?.underline === false && markProps.underline) {
6527
6521
  delete markProps.underline;
6528
6522
  }
6529
6523
  // Update paragraph mark font, color, size
@@ -6542,10 +6536,11 @@ export class Document {
6542
6536
  }
6543
6537
 
6544
6538
  // Process Heading2 paragraphs
6545
- else if (styleId === "Heading2" && heading2) {
6539
+ else if (styleId === 'Heading2' && heading2) {
6546
6540
  // Check if paragraph has actual text content (skip empty paragraphs)
6547
- const hasContent = this.getAllRunsFromParagraph(para)
6548
- .some((run) => run.getText().trim().length > 0);
6541
+ const hasContent = this.getAllRunsFromParagraph(para).some(
6542
+ (run) => run.getText().trim().length > 0
6543
+ );
6549
6544
 
6550
6545
  if (!hasContent) {
6551
6546
  // Skip empty Heading2 paragraphs - don't wrap them in tables
@@ -6578,8 +6573,7 @@ export class Document {
6578
6573
  const isMultiCellTable = !(rowCount === 1 && colCount === 1);
6579
6574
  const cellFormatting = cell.getFormatting();
6580
6575
  const cellHasShading = !!(
6581
- cellFormatting?.shading?.fill ||
6582
- cellFormatting?.shading?.pattern
6576
+ cellFormatting?.shading?.fill || cellFormatting?.shading?.pattern
6583
6577
  );
6584
6578
 
6585
6579
  if (isMultiCellTable && cellHasShading && para.formatting.alignment) {
@@ -6592,7 +6586,7 @@ export class Document {
6592
6586
  const allRuns = this.getAllRunsFromParagraph(para);
6593
6587
  const whiteFontRuns = new Set(
6594
6588
  options?.preserveWhiteFont
6595
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6589
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6596
6590
  : []
6597
6591
  );
6598
6592
 
@@ -6617,7 +6611,7 @@ export class Document {
6617
6611
  run.setItalic(h2Config.run?.italic ?? false);
6618
6612
  }
6619
6613
  if (!h2Preserve.underline) {
6620
- run.setUnderline(h2Config.run?.underline ? "single" : false);
6614
+ run.setUnderline(h2Config.run?.underline ? 'single' : false);
6621
6615
  }
6622
6616
  // Apply font, color, and size - skip color if run was white font
6623
6617
  if (h2Config.run?.font) {
@@ -6639,25 +6633,13 @@ export class Document {
6639
6633
  // Update paragraph mark properties to match configuration
6640
6634
  if (para.formatting.paragraphMarkRunProperties) {
6641
6635
  const markProps = para.formatting.paragraphMarkRunProperties;
6642
- if (
6643
- !h2Preserve.bold &&
6644
- h2Config.run?.bold === false &&
6645
- markProps.bold
6646
- ) {
6636
+ if (!h2Preserve.bold && h2Config.run?.bold === false && markProps.bold) {
6647
6637
  delete markProps.bold;
6648
6638
  }
6649
- if (
6650
- !h2Preserve.italic &&
6651
- h2Config.run?.italic === false &&
6652
- markProps.italic
6653
- ) {
6639
+ if (!h2Preserve.italic && h2Config.run?.italic === false && markProps.italic) {
6654
6640
  delete markProps.italic;
6655
6641
  }
6656
- if (
6657
- !h2Preserve.underline &&
6658
- h2Config.run?.underline === false &&
6659
- markProps.underline
6660
- ) {
6642
+ if (!h2Preserve.underline && h2Config.run?.underline === false && markProps.underline) {
6661
6643
  delete markProps.underline;
6662
6644
  }
6663
6645
  // Update paragraph mark font, color, size
@@ -6703,13 +6685,13 @@ export class Document {
6703
6685
  });
6704
6686
  if (table) {
6705
6687
  table.setWidth(h2Config.tableOptions.tableWidthPercent);
6706
- table.setWidthType("pct");
6688
+ table.setWidthType('pct');
6707
6689
  }
6708
6690
  }
6709
6691
  } else {
6710
6692
  // Paragraph is not in a table - wrap it using config
6711
6693
  const table = this.wrapParagraphInTable(para, {
6712
- shading: h2Config.tableOptions?.shading ?? "BFBFBF",
6694
+ shading: h2Config.tableOptions?.shading ?? 'BFBFBF',
6713
6695
  marginTop: h2Config.tableOptions?.marginTop ?? 0,
6714
6696
  marginBottom: h2Config.tableOptions?.marginBottom ?? 0,
6715
6697
  marginLeft: h2Config.tableOptions?.marginLeft ?? 115,
@@ -6740,7 +6722,7 @@ export class Document {
6740
6722
  // Runs with text count as content
6741
6723
  if ((item as any).getText) {
6742
6724
  const text = (item as any).getText().trim();
6743
- if (text !== "") return false;
6725
+ if (text !== '') return false;
6744
6726
  }
6745
6727
  }
6746
6728
 
@@ -6765,12 +6747,12 @@ export class Document {
6765
6747
  }
6766
6748
 
6767
6749
  // Process Heading3 paragraphs
6768
- else if (styleId === "Heading3" && heading3) {
6750
+ else if (styleId === 'Heading3' && heading3) {
6769
6751
  // Save white font status BEFORE clearing (clearDirectFormattingConflicts clears color)
6770
6752
  const allRuns = this.getAllRunsFromParagraph(para);
6771
6753
  const whiteFontRuns = new Set(
6772
6754
  options?.preserveWhiteFont
6773
- ? allRuns.filter(run => run.getColor()?.toUpperCase() === 'FFFFFF')
6755
+ ? allRuns.filter((run) => run.getColor()?.toUpperCase() === 'FFFFFF')
6774
6756
  : []
6775
6757
  );
6776
6758
 
@@ -6789,7 +6771,7 @@ export class Document {
6789
6771
  run.setItalic(h3Config.run?.italic ?? false);
6790
6772
  }
6791
6773
  if (!h3Preserve.underline) {
6792
- run.setUnderline(h3Config.run?.underline ? "single" : false);
6774
+ run.setUnderline(h3Config.run?.underline ? 'single' : false);
6793
6775
  }
6794
6776
  // Apply font, color, and size - skip color if run was white font
6795
6777
  if (h3Config.run?.font) {
@@ -6811,25 +6793,13 @@ export class Document {
6811
6793
  // Update paragraph mark properties to match configuration
6812
6794
  if (para.formatting.paragraphMarkRunProperties) {
6813
6795
  const markProps = para.formatting.paragraphMarkRunProperties;
6814
- if (
6815
- !h3Preserve.bold &&
6816
- h3Config.run?.bold === false &&
6817
- markProps.bold
6818
- ) {
6796
+ if (!h3Preserve.bold && h3Config.run?.bold === false && markProps.bold) {
6819
6797
  delete markProps.bold;
6820
6798
  }
6821
- if (
6822
- !h3Preserve.italic &&
6823
- h3Config.run?.italic === false &&
6824
- markProps.italic
6825
- ) {
6799
+ if (!h3Preserve.italic && h3Config.run?.italic === false && markProps.italic) {
6826
6800
  delete markProps.italic;
6827
6801
  }
6828
- if (
6829
- !h3Preserve.underline &&
6830
- h3Config.run?.underline === false &&
6831
- markProps.underline
6832
- ) {
6802
+ if (!h3Preserve.underline && h3Config.run?.underline === false && markProps.underline) {
6833
6803
  delete markProps.underline;
6834
6804
  }
6835
6805
  // Update paragraph mark font, color, size
@@ -6848,7 +6818,7 @@ export class Document {
6848
6818
  }
6849
6819
 
6850
6820
  // Process List Paragraph paragraphs
6851
- else if (styleId === "ListParagraph" && listParagraph) {
6821
+ else if (styleId === 'ListParagraph' && listParagraph) {
6852
6822
  // Check for mis-styled paragraphs: ListParagraph + left:0 + no numbering
6853
6823
  // These are not actual list items - change them to Normal style
6854
6824
  const paraIndentation = para.getFormatting().indentation;
@@ -6856,7 +6826,7 @@ export class Document {
6856
6826
  if (paraIndentation?.left === 0 && !hasNumbering) {
6857
6827
  // This paragraph has ListParagraph style but explicitly overrides indent to 0
6858
6828
  // and has no numbering - it should be Normal style, not ListParagraph
6859
- para.setStyle("Normal");
6829
+ para.setStyle('Normal');
6860
6830
 
6861
6831
  // Preserve existing bold formatting and white font before applying Normal style
6862
6832
  const allRuns = this.getAllRunsFromParagraph(para);
@@ -6942,7 +6912,7 @@ export class Document {
6942
6912
  run.setItalic(listParaConfig.run?.italic ?? false);
6943
6913
  }
6944
6914
  if (!listParaPreserve.underline) {
6945
- run.setUnderline(listParaConfig.run?.underline ? "single" : false);
6915
+ run.setUnderline(listParaConfig.run?.underline ? 'single' : false);
6946
6916
  }
6947
6917
  // Apply font, color, and size - skip color if run was white font
6948
6918
  if (listParaConfig.run?.font) {
@@ -6964,11 +6934,7 @@ export class Document {
6964
6934
  // Update paragraph mark properties to match configuration
6965
6935
  if (para.formatting.paragraphMarkRunProperties) {
6966
6936
  const markProps = para.formatting.paragraphMarkRunProperties;
6967
- if (
6968
- !listParaPreserve.bold &&
6969
- listParaConfig.run?.bold === false &&
6970
- markProps.bold
6971
- ) {
6937
+ if (!listParaPreserve.bold && listParaConfig.run?.bold === false && markProps.bold) {
6972
6938
  delete markProps.bold;
6973
6939
  }
6974
6940
  if (
@@ -7002,7 +6968,12 @@ export class Document {
7002
6968
 
7003
6969
  // Process Normal paragraphs (including undefined style which defaults to Normal)
7004
6970
  // Also process NormalWeb paragraphs when linkNormalWebToNormal is enabled (default: true)
7005
- else if ((styleId === "Normal" || styleId === undefined || (styleId === "NormalWeb" && options?.linkNormalWebToNormal !== false)) && normal) {
6971
+ else if (
6972
+ (styleId === 'Normal' ||
6973
+ styleId === undefined ||
6974
+ (styleId === 'NormalWeb' && options?.linkNormalWebToNormal !== false)) &&
6975
+ normal
6976
+ ) {
7006
6977
  // Save formatting that should be preserved BEFORE clearing
7007
6978
  const allRuns = this.getAllRunsFromParagraph(para);
7008
6979
  const preservedFormatting = allRuns.map((run) => {
@@ -7017,8 +6988,8 @@ export class Document {
7017
6988
  });
7018
6989
 
7019
6990
  // Save center alignment BEFORE clearing if preserveCenterAlignment is set
7020
- const savedCenterAlignment = options?.normal?.preserveCenterAlignment &&
7021
- para.getAlignment() === 'center';
6991
+ const savedCenterAlignment =
6992
+ options?.normal?.preserveCenterAlignment && para.getAlignment() === 'center';
7022
6993
 
7023
6994
  para.clearDirectFormattingConflicts(normal);
7024
6995
 
@@ -7054,7 +7025,7 @@ export class Document {
7054
7025
  run.setItalic(normalConfig.run?.italic ?? false);
7055
7026
  }
7056
7027
  if (!normalPreserve.underline) {
7057
- run.setUnderline(normalConfig.run?.underline ? "single" : false);
7028
+ run.setUnderline(normalConfig.run?.underline ? 'single' : false);
7058
7029
  }
7059
7030
  // Apply font, color, and size - skip color if run was white font
7060
7031
  if (normalConfig.run?.font) {
@@ -7077,18 +7048,10 @@ export class Document {
7077
7048
  // Update paragraph mark properties to match configuration
7078
7049
  if (para.formatting.paragraphMarkRunProperties) {
7079
7050
  const markProps = para.formatting.paragraphMarkRunProperties;
7080
- if (
7081
- !normalPreserve.bold &&
7082
- normalConfig.run?.bold === false &&
7083
- markProps.bold
7084
- ) {
7051
+ if (!normalPreserve.bold && normalConfig.run?.bold === false && markProps.bold) {
7085
7052
  delete markProps.bold;
7086
7053
  }
7087
- if (
7088
- !normalPreserve.italic &&
7089
- normalConfig.run?.italic === false &&
7090
- markProps.italic
7091
- ) {
7054
+ if (!normalPreserve.italic && normalConfig.run?.italic === false && markProps.italic) {
7092
7055
  delete markProps.italic;
7093
7056
  }
7094
7057
  if (
@@ -7174,8 +7137,9 @@ export class Document {
7174
7137
  indentPerLevel?: number;
7175
7138
  }): { formatted: number[] } {
7176
7139
  // 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);
7140
+ const validLevels = (options.levels ?? [1, 2, 3, 4, 5, 6, 7, 8, 9]).filter(
7141
+ (l) => l >= 1 && l <= 9
7142
+ );
7179
7143
  const levels = [...new Set(validLevels)].sort((a, b) => a - b);
7180
7144
  const formatted: number[] = [];
7181
7145
 
@@ -7277,14 +7241,14 @@ export class Document {
7277
7241
  const styleId = style.getStyleId();
7278
7242
 
7279
7243
  switch (styleId) {
7280
- case "Heading1":
7244
+ case 'Heading1':
7281
7245
  options.heading1 = {
7282
7246
  run: style.getRunFormatting() as any,
7283
7247
  paragraph: style.getParagraphFormatting() as any,
7284
7248
  };
7285
7249
  break;
7286
7250
 
7287
- case "Heading2":
7251
+ case 'Heading2':
7288
7252
  options.heading2 = {
7289
7253
  run: style.getRunFormatting() as any,
7290
7254
  paragraph: style.getParagraphFormatting() as any,
@@ -7292,21 +7256,21 @@ export class Document {
7292
7256
  };
7293
7257
  break;
7294
7258
 
7295
- case "Heading3":
7259
+ case 'Heading3':
7296
7260
  options.heading3 = {
7297
7261
  run: style.getRunFormatting() as any,
7298
7262
  paragraph: style.getParagraphFormatting() as any,
7299
7263
  };
7300
7264
  break;
7301
7265
 
7302
- case "Normal":
7266
+ case 'Normal':
7303
7267
  options.normal = {
7304
7268
  run: style.getRunFormatting() as any,
7305
7269
  paragraph: style.getParagraphFormatting() as any,
7306
7270
  };
7307
7271
  break;
7308
7272
 
7309
- case "ListParagraph":
7273
+ case 'ListParagraph':
7310
7274
  options.listParagraph = {
7311
7275
  run: style.getRunFormatting() as any,
7312
7276
  paragraph: style.getParagraphFormatting() as any,
@@ -7346,8 +7310,8 @@ export class Document {
7346
7310
  */
7347
7311
  public updateTableStyleShading(oldColor: string, newColor: string): number {
7348
7312
  // Normalize colors (uppercase, no #)
7349
- const normalizedOld = oldColor.replace("#", "").toUpperCase();
7350
- const normalizedNew = newColor.replace("#", "").toUpperCase();
7313
+ const normalizedOld = oldColor.replace('#', '').toUpperCase();
7314
+ const normalizedNew = newColor.replace('#', '').toUpperCase();
7351
7315
 
7352
7316
  if (normalizedOld === normalizedNew) {
7353
7317
  return 0; // No change needed
@@ -7363,10 +7327,7 @@ export class Document {
7363
7327
 
7364
7328
  // Pattern to match shading elements with fill attribute containing the old color
7365
7329
  // Matches: w:fill="A5A5A5" (case-insensitive)
7366
- const fillPattern = new RegExp(
7367
- `(w:fill=["'])${normalizedOld}(["'])`,
7368
- "gi"
7369
- );
7330
+ const fillPattern = new RegExp(`(w:fill=["'])${normalizedOld}(["'])`, 'gi');
7370
7331
 
7371
7332
  // Replace all occurrences
7372
7333
  stylesXml = stylesXml.replace(fillPattern, (match, prefix, suffix) => {
@@ -7376,10 +7337,7 @@ export class Document {
7376
7337
 
7377
7338
  // Also handle color attribute in shading elements
7378
7339
  // Matches: w:color="A5A5A5" within shd elements
7379
- const colorPattern = new RegExp(
7380
- `(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`,
7381
- "gi"
7382
- );
7340
+ const colorPattern = new RegExp(`(<w:shd[^>]*w:color=["'])${normalizedOld}(["'])`, 'gi');
7383
7341
 
7384
7342
  stylesXml = stylesXml.replace(colorPattern, (match, prefix, suffix) => {
7385
7343
  updateCount++;
@@ -7425,28 +7383,22 @@ export class Document {
7425
7383
  let totalUpdated = 0;
7426
7384
 
7427
7385
  // Default colors commonly used in table styles
7428
- const defaultReplaceColors = ["A5A5A5", "C0C0C0", "D9D9D9", "E7E6E6"];
7386
+ const defaultReplaceColors = ['A5A5A5', 'C0C0C0', 'D9D9D9', 'E7E6E6'];
7429
7387
  const colorsToReplace = settings.replaceColors || defaultReplaceColors;
7430
7388
 
7431
7389
  // Replace header-type colors with header shading
7432
7390
  if (settings.headerShading) {
7433
7391
  for (const oldColor of colorsToReplace) {
7434
- totalUpdated += this.updateTableStyleShading(
7435
- oldColor,
7436
- settings.headerShading
7437
- );
7392
+ totalUpdated += this.updateTableStyleShading(oldColor, settings.headerShading);
7438
7393
  }
7439
7394
  }
7440
7395
 
7441
7396
  // If data shading differs from header, apply it to lighter colors
7442
7397
  if (settings.dataShading && settings.dataShading !== settings.headerShading) {
7443
7398
  // Typically data cells use lighter shading
7444
- const dataColors = ["D9D9D9", "E7E6E6", "F2F2F2"];
7399
+ const dataColors = ['D9D9D9', 'E7E6E6', 'F2F2F2'];
7445
7400
  for (const oldColor of dataColors) {
7446
- totalUpdated += this.updateTableStyleShading(
7447
- oldColor,
7448
- settings.dataShading
7449
- );
7401
+ totalUpdated += this.updateTableStyleShading(oldColor, settings.dataShading);
7450
7402
  }
7451
7403
  }
7452
7404
 
@@ -7518,8 +7470,8 @@ export class Document {
7518
7470
  const {
7519
7471
  bold = false,
7520
7472
  fontSize = 24, // 12pt
7521
- color = "000000",
7522
- font = "Arial",
7473
+ color = '000000',
7474
+ font = 'Arial',
7523
7475
  } = options || {};
7524
7476
 
7525
7477
  let listsUpdated = 0;
@@ -7529,14 +7481,13 @@ export class Document {
7529
7481
 
7530
7482
  for (const instance of instances) {
7531
7483
  const abstractNumId = instance.getAbstractNumId();
7532
- const abstractNum =
7533
- this.numberingManager.getAbstractNumbering(abstractNumId);
7484
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
7534
7485
 
7535
7486
  if (!abstractNum) continue;
7536
7487
 
7537
7488
  // Only process bullet lists (skip numbered lists)
7538
7489
  const level0 = abstractNum.getLevel(0);
7539
- if (level0?.getFormat() !== "bullet") continue;
7490
+ if (level0?.getFormat() !== 'bullet') continue;
7540
7491
 
7541
7492
  // Update all 9 levels (0-8) with formatting only (preserve existing symbols)
7542
7493
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -7597,8 +7548,8 @@ export class Document {
7597
7548
  const {
7598
7549
  bold = false,
7599
7550
  fontSize = 24, // 12pt
7600
- color = "000000",
7601
- font = "Verdana",
7551
+ color = '000000',
7552
+ font = 'Verdana',
7602
7553
  } = options || {};
7603
7554
 
7604
7555
  let listsUpdated = 0;
@@ -7608,14 +7559,13 @@ export class Document {
7608
7559
 
7609
7560
  for (const instance of instances) {
7610
7561
  const abstractNumId = instance.getAbstractNumId();
7611
- const abstractNum =
7612
- this.numberingManager.getAbstractNumbering(abstractNumId);
7562
+ const abstractNum = this.numberingManager.getAbstractNumbering(abstractNumId);
7613
7563
 
7614
7564
  if (!abstractNum) continue;
7615
7565
 
7616
7566
  // Only process numbered lists (skip bullet lists)
7617
7567
  const level0 = abstractNum.getLevel(0);
7618
- if (!level0 || level0.getFormat() === "bullet") continue;
7568
+ if (!level0 || level0.getFormat() === 'bullet') continue;
7619
7569
 
7620
7570
  // Update all 9 levels (0-8)
7621
7571
  for (let levelIndex = 0; levelIndex < 9; levelIndex++) {
@@ -7666,12 +7616,7 @@ export class Document {
7666
7616
  color?: string;
7667
7617
  underline?: boolean;
7668
7618
  }): number {
7669
- const {
7670
- font = "Verdana",
7671
- size = 12,
7672
- color = "0000FF",
7673
- underline = true,
7674
- } = options || {};
7619
+ const { font = 'Verdana', size = 12, color = '0000FF', underline = true } = options || {};
7675
7620
 
7676
7621
  const hyperlinks = this.getHyperlinks();
7677
7622
 
@@ -7680,14 +7625,13 @@ export class Document {
7680
7625
  font: font,
7681
7626
  size: size,
7682
7627
  color: color,
7683
- underline: underline ? "single" : false,
7628
+ underline: underline ? 'single' : false,
7684
7629
  });
7685
7630
  }
7686
7631
 
7687
7632
  return hyperlinks.length;
7688
7633
  }
7689
7634
 
7690
-
7691
7635
  /**
7692
7636
  * Applies Heading 1 style to paragraphs with H1-like style names
7693
7637
  * @param options Optional style application options
@@ -7710,7 +7654,7 @@ export class Document {
7710
7654
  * ```
7711
7655
  */
7712
7656
  public applyH1(options?: StyleApplyOptions): number {
7713
- return this.applyStyleToMatching("Heading1", options, (style) =>
7657
+ return this.applyStyleToMatching('Heading1', options, (style) =>
7714
7658
  /^(heading\s*1|header\s*1|h1)$/i.test(style)
7715
7659
  );
7716
7660
  }
@@ -7727,7 +7671,7 @@ export class Document {
7727
7671
  * ```
7728
7672
  */
7729
7673
  public applyH2(options?: StyleApplyOptions): number {
7730
- return this.applyStyleToMatching("Heading2", options, (style) =>
7674
+ return this.applyStyleToMatching('Heading2', options, (style) =>
7731
7675
  /^(heading\s*2|header\s*2|h2)$/i.test(style)
7732
7676
  );
7733
7677
  }
@@ -7744,7 +7688,7 @@ export class Document {
7744
7688
  * ```
7745
7689
  */
7746
7690
  public applyH3(options?: StyleApplyOptions): number {
7747
- return this.applyStyleToMatching("Heading3", options, (style) =>
7691
+ return this.applyStyleToMatching('Heading3', options, (style) =>
7748
7692
  /^(heading\s*3|header\s*3|h3)$/i.test(style)
7749
7693
  );
7750
7694
  }
@@ -7759,16 +7703,13 @@ export class Document {
7759
7703
  options?.paragraphs ||
7760
7704
  this.getAllParagraphs().filter((p) => {
7761
7705
  const style = p.getStyle();
7762
- return (
7763
- !style ||
7764
- !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style)
7765
- );
7706
+ return !style || !/^(heading|header|h\d|list|toc|tod|caution|table)/i.test(style);
7766
7707
  });
7767
7708
 
7768
7709
  let count = 0;
7769
7710
  for (const para of targets) {
7770
7711
  if (para.isPreserved()) continue;
7771
- para.setStyle("Normal");
7712
+ para.setStyle('Normal');
7772
7713
 
7773
7714
  if (options?.keepProperties && options.keepProperties.length > 0) {
7774
7715
  this.clearFormattingExcept(para, options.keepProperties);
@@ -7791,7 +7732,7 @@ export class Document {
7791
7732
  * @returns Number of paragraphs updated
7792
7733
  */
7793
7734
  public applyNumList(options?: StyleApplyOptions): number {
7794
- return this.applyStyleToMatching("ListParagraph", options, (style) =>
7735
+ return this.applyStyleToMatching('ListParagraph', options, (style) =>
7795
7736
  /^(list\s*number|numbered\s*list|list\s*paragraph)$/i.test(style)
7796
7737
  );
7797
7738
  }
@@ -7802,7 +7743,7 @@ export class Document {
7802
7743
  * @returns Number of paragraphs updated
7803
7744
  */
7804
7745
  public applyBulletList(options?: StyleApplyOptions): number {
7805
- return this.applyStyleToMatching("ListParagraph", options, (style) =>
7746
+ return this.applyStyleToMatching('ListParagraph', options, (style) =>
7806
7747
  /^(list\s*bullet|bullet\s*list|list\s*paragraph)$/i.test(style)
7807
7748
  );
7808
7749
  }
@@ -7813,7 +7754,7 @@ export class Document {
7813
7754
  * @returns Number of paragraphs updated
7814
7755
  */
7815
7756
  public applyTOC(options?: StyleApplyOptions): number {
7816
- return this.applyStyleToMatching("TOC", options, (style) =>
7757
+ return this.applyStyleToMatching('TOC', options, (style) =>
7817
7758
  /^(toc|table\s*of\s*contents|toc\s*heading)$/i.test(style)
7818
7759
  );
7819
7760
  }
@@ -7824,7 +7765,7 @@ export class Document {
7824
7765
  * @returns Number of paragraphs updated
7825
7766
  */
7826
7767
  public applyTOD(options?: StyleApplyOptions): number {
7827
- return this.applyStyleToMatching("TopOfDocument", options, (style) =>
7768
+ return this.applyStyleToMatching('TopOfDocument', options, (style) =>
7828
7769
  /^(tod|top\s*of\s*document|document\s*top)$/i.test(style)
7829
7770
  );
7830
7771
  }
@@ -7835,7 +7776,7 @@ export class Document {
7835
7776
  * @returns Number of paragraphs updated
7836
7777
  */
7837
7778
  public applyCaution(options?: StyleApplyOptions): number {
7838
- return this.applyStyleToMatching("Caution", options, (style) =>
7779
+ return this.applyStyleToMatching('Caution', options, (style) =>
7839
7780
  /^(caution|warning|important|alert)$/i.test(style)
7840
7781
  );
7841
7782
  }
@@ -7856,7 +7797,7 @@ export class Document {
7856
7797
  for (const cell of firstRow.getCells()) {
7857
7798
  for (const para of cell.getParagraphs()) {
7858
7799
  if (para.isPreserved()) continue;
7859
- para.setStyle("TableHeader");
7800
+ para.setStyle('TableHeader');
7860
7801
 
7861
7802
  if (options?.keepProperties && options.keepProperties.length > 0) {
7862
7803
  this.clearFormattingExcept(para, options.keepProperties);
@@ -7905,9 +7846,9 @@ export class Document {
7905
7846
  if (options.color) run.setColor(options.color);
7906
7847
  if (options.emphasis) {
7907
7848
  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");
7849
+ if (emp === 'bold') run.setBold(true);
7850
+ if (emp === 'italic') run.setItalic(true);
7851
+ if (emp === 'underline') run.setUnderline('single');
7911
7852
  });
7912
7853
  }
7913
7854
  }
@@ -7960,10 +7901,7 @@ export class Document {
7960
7901
  * Helper method to selectively clear formatting while preserving specific properties
7961
7902
  * @private
7962
7903
  */
7963
- private clearFormattingExcept(
7964
- para: Paragraph,
7965
- keepProperties: string[]
7966
- ): void {
7904
+ private clearFormattingExcept(para: Paragraph, keepProperties: string[]): void {
7967
7905
  // Save properties to keep
7968
7906
  const savedProps: Record<string, unknown> = {};
7969
7907
  const formatting = para.formatting as Record<string, unknown>;
@@ -8000,21 +7938,15 @@ export class Document {
8000
7938
 
8001
7939
  // Restore saved properties using appropriate setters
8002
7940
  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);
7941
+ if (runSavedProps.italic !== undefined) run.setItalic(runSavedProps.italic);
7942
+ if (runSavedProps.underline !== undefined) run.setUnderline(runSavedProps.underline);
8007
7943
  if (runSavedProps.color !== undefined) run.setColor(runSavedProps.color);
8008
7944
  if (runSavedProps.font !== undefined) run.setFont(runSavedProps.font);
8009
7945
  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);
7946
+ if (runSavedProps.highlight !== undefined) run.setHighlight(runSavedProps.highlight);
7947
+ if (runSavedProps.strike !== undefined) run.setStrike(runSavedProps.strike);
7948
+ if (runSavedProps.subscript !== undefined) run.setSubscript(runSavedProps.subscript);
7949
+ if (runSavedProps.superscript !== undefined) run.setSuperscript(runSavedProps.superscript);
8018
7950
  }
8019
7951
  }
8020
7952
 
@@ -8130,7 +8062,7 @@ export class Document {
8130
8062
 
8131
8063
  for (const match of tMatches) {
8132
8064
  hasTableSwitch = true;
8133
- const content = (match[1] || "").trim();
8065
+ const content = (match[1] || '').trim();
8134
8066
  if (!content) continue;
8135
8067
 
8136
8068
  // --- Case 1: Range format "X-Y" ---
@@ -8147,7 +8079,7 @@ export class Document {
8147
8079
  // --- Case 2: Style format "StyleName,Level," (e.g., "Heading 2,2,") ---
8148
8080
  // Split by comma, expect pattern: styleName, level, [optional trailing comma]
8149
8081
  const parts = content
8150
- .split(",")
8082
+ .split(',')
8151
8083
  .map((p) => p.trim())
8152
8084
  .filter(Boolean);
8153
8085
  for (let i = 0; i < parts.length; i += 2) {
@@ -8207,14 +8139,14 @@ export class Document {
8207
8139
 
8208
8140
  // Decode XML entities in instruction
8209
8141
  const fieldInstruction = match[1]
8210
- .replace(/&amp;/g, "&")
8211
- .replace(/&lt;/g, "<")
8212
- .replace(/&gt;/g, ">")
8142
+ .replace(/&amp;/g, '&')
8143
+ .replace(/&lt;/g, '<')
8144
+ .replace(/&gt;/g, '>')
8213
8145
  .replace(/&quot;/g, '"')
8214
8146
  .replace(/&apos;/g, "'");
8215
8147
 
8216
8148
  // Check if instruction contains \t switches (style-specific TOC)
8217
- if (!fieldInstruction.includes("\\t")) {
8149
+ if (!fieldInstruction.includes('\\t')) {
8218
8150
  continue; // Outline-based TOC, no style names to sync
8219
8151
  }
8220
8152
 
@@ -8225,11 +8157,11 @@ export class Document {
8225
8157
  if (updatedInstruction !== fieldInstruction) {
8226
8158
  // Re-encode for XML
8227
8159
  const encodedInstruction = updatedInstruction
8228
- .replace(/&/g, "&amp;")
8229
- .replace(/</g, "&lt;")
8230
- .replace(/>/g, "&gt;")
8231
- .replace(/"/g, "&quot;")
8232
- .replace(/'/g, "&apos;");
8160
+ .replace(/&/g, '&amp;')
8161
+ .replace(/</g, '&lt;')
8162
+ .replace(/>/g, '&gt;')
8163
+ .replace(/"/g, '&quot;')
8164
+ .replace(/'/g, '&apos;');
8233
8165
 
8234
8166
  // Replace the instruction
8235
8167
  const updatedInstrXml = `<w:instrText xml:space="preserve">${encodedInstruction}</w:instrText>`;
@@ -8250,7 +8182,7 @@ export class Document {
8250
8182
  } catch (error: unknown) {
8251
8183
  // Log error but don't fail the save
8252
8184
  this.logger.error(
8253
- "Error syncing TOC field instructions - document will save with original instructions",
8185
+ 'Error syncing TOC field instructions - document will save with original instructions',
8254
8186
  error instanceof Error
8255
8187
  ? { message: error.message, stack: error.stack }
8256
8188
  : { error: String(error) }
@@ -8263,9 +8195,7 @@ export class Document {
8263
8195
  * Helper to sync TOC instruction within an XML fragment
8264
8196
  * @private
8265
8197
  */
8266
- private syncTOCInstructionInXml(
8267
- xml: string
8268
- ): { xml: string; changed: boolean } {
8198
+ private syncTOCInstructionInXml(xml: string): { xml: string; changed: boolean } {
8269
8199
  const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(xml);
8270
8200
  if (!instrMatch?.[1]) {
8271
8201
  return { xml, changed: false };
@@ -8273,14 +8203,14 @@ export class Document {
8273
8203
 
8274
8204
  // Decode XML entities in instruction
8275
8205
  const fieldInstruction = instrMatch[1]
8276
- .replace(/&amp;/g, "&")
8277
- .replace(/&lt;/g, "<")
8278
- .replace(/&gt;/g, ">")
8206
+ .replace(/&amp;/g, '&')
8207
+ .replace(/&lt;/g, '<')
8208
+ .replace(/&gt;/g, '>')
8279
8209
  .replace(/&quot;/g, '"')
8280
8210
  .replace(/&apos;/g, "'");
8281
8211
 
8282
8212
  // Check if instruction contains \t switches (style-specific TOC)
8283
- if (!fieldInstruction.includes("\\t")) {
8213
+ if (!fieldInstruction.includes('\\t')) {
8284
8214
  return { xml, changed: false };
8285
8215
  }
8286
8216
 
@@ -8293,11 +8223,11 @@ export class Document {
8293
8223
 
8294
8224
  // Re-encode for XML
8295
8225
  const encodedInstruction = updatedInstruction
8296
- .replace(/&/g, "&amp;")
8297
- .replace(/</g, "&lt;")
8298
- .replace(/>/g, "&gt;")
8299
- .replace(/"/g, "&quot;")
8300
- .replace(/'/g, "&apos;");
8226
+ .replace(/&/g, '&amp;')
8227
+ .replace(/</g, '&lt;')
8228
+ .replace(/>/g, '&gt;')
8229
+ .replace(/"/g, '&quot;')
8230
+ .replace(/'/g, '&apos;');
8301
8231
 
8302
8232
  const updatedXml = xml.replace(
8303
8233
  /<w:instrText[^>]*>[\s\S]*?<\/w:instrText>/,
@@ -8331,7 +8261,10 @@ export class Document {
8331
8261
 
8332
8262
  // Parse style names and levels from the switch content
8333
8263
  // Format: "StyleName,Level," or "StyleName,Level,StyleName2,Level2,..."
8334
- const parts = content.split(',').map(p => p.trim()).filter(Boolean);
8264
+ const parts = content
8265
+ .split(',')
8266
+ .map((p) => p.trim())
8267
+ .filter(Boolean);
8335
8268
  const updatedParts: string[] = [];
8336
8269
 
8337
8270
  for (let i = 0; i < parts.length; i += 2) {
@@ -8496,31 +8429,30 @@ export class Document {
8496
8429
  docXml: string,
8497
8430
  levels: number[]
8498
8431
  ): { level: number; text: string; bookmark: string }[] {
8499
- const headings: { level: number; text: string; bookmark: string }[] =
8500
- [];
8432
+ const headings: { level: number; text: string; bookmark: string }[] = [];
8501
8433
  const levelSet = new Set(levels);
8502
8434
 
8503
8435
  try {
8504
8436
  // Parse document.xml to object structure
8505
8437
  const parsed = XMLParser.parseToObject(docXml, { trimValues: false });
8506
- const document = parsed["w:document"];
8438
+ const document = parsed['w:document'];
8507
8439
  if (!document) {
8508
8440
  return headings;
8509
8441
  }
8510
8442
 
8511
- const body = (document as any)["w:body"];
8443
+ const body = (document as any)['w:body'];
8512
8444
  if (!body) {
8513
8445
  return headings;
8514
8446
  }
8515
8447
 
8516
8448
  // Helper function to extract heading info from a parsed paragraph object
8517
8449
  const extractHeading = (para: any): void => {
8518
- const pPr = para["w:pPr"];
8519
- if (!pPr?.["w:pStyle"]) {
8450
+ const pPr = para['w:pPr'];
8451
+ if (!pPr?.['w:pStyle']) {
8520
8452
  return;
8521
8453
  }
8522
8454
 
8523
- const styleVal = pPr["w:pStyle"]["@_w:val"];
8455
+ const styleVal = pPr['w:pStyle']['@_w:val'];
8524
8456
  if (!styleVal) {
8525
8457
  return;
8526
8458
  }
@@ -8539,26 +8471,27 @@ export class Document {
8539
8471
  }
8540
8472
 
8541
8473
  // Extract bookmark (use any existing bookmark, prioritize "_heading" or "_Toc")
8542
- let bookmark = "";
8543
- const bookmarkStart = para["w:bookmarkStart"];
8474
+ let bookmark = '';
8475
+ const bookmarkStart = para['w:bookmarkStart'];
8544
8476
  if (bookmarkStart) {
8545
- const bookmarkArray = Array.isArray(bookmarkStart)
8546
- ? bookmarkStart
8547
- : [bookmarkStart];
8548
-
8477
+ const bookmarkArray = Array.isArray(bookmarkStart) ? bookmarkStart : [bookmarkStart];
8478
+
8549
8479
  // First try to find preferred bookmark types
8550
8480
  for (const bm of bookmarkArray) {
8551
- const bmName = bm["@_w:name"];
8552
- if (bmName && (bmName.toLowerCase().includes("_heading") || bmName.toLowerCase().includes("_toc"))) {
8481
+ const bmName = bm['@_w:name'];
8482
+ if (
8483
+ bmName &&
8484
+ (bmName.toLowerCase().includes('_heading') || bmName.toLowerCase().includes('_toc'))
8485
+ ) {
8553
8486
  bookmark = bmName;
8554
8487
  break;
8555
8488
  }
8556
8489
  }
8557
-
8490
+
8558
8491
  // If no preferred bookmark found, use the first available bookmark
8559
8492
  if (!bookmark && bookmarkArray.length > 0) {
8560
8493
  const firstBm = bookmarkArray[0];
8561
- const bmName = firstBm["@_w:name"];
8494
+ const bmName = firstBm['@_w:name'];
8562
8495
  if (bmName) {
8563
8496
  bookmark = bmName;
8564
8497
  }
@@ -8566,17 +8499,17 @@ export class Document {
8566
8499
  }
8567
8500
 
8568
8501
  // Extract text from runs
8569
- let text = "";
8570
- const runs = para["w:r"];
8502
+ let text = '';
8503
+ const runs = para['w:r'];
8571
8504
  if (runs) {
8572
8505
  const runArray = Array.isArray(runs) ? runs : [runs];
8573
8506
  for (const run of runArray) {
8574
- const textElement = run["w:t"];
8507
+ const textElement = run['w:t'];
8575
8508
  if (textElement) {
8576
- if (typeof textElement === "string") {
8509
+ if (typeof textElement === 'string') {
8577
8510
  text += textElement;
8578
- } else if (textElement["#text"]) {
8579
- text += textElement["#text"];
8511
+ } else if (textElement['#text']) {
8512
+ text += textElement['#text'];
8580
8513
  }
8581
8514
  }
8582
8515
  }
@@ -8601,7 +8534,7 @@ export class Document {
8601
8534
  };
8602
8535
 
8603
8536
  // Search in direct paragraphs
8604
- const paragraphs = body["w:p"];
8537
+ const paragraphs = body['w:p'];
8605
8538
  if (paragraphs) {
8606
8539
  const paraArray = Array.isArray(paragraphs) ? paragraphs : [paragraphs];
8607
8540
  for (const para of paraArray) {
@@ -8610,26 +8543,24 @@ export class Document {
8610
8543
  }
8611
8544
 
8612
8545
  // Search in tables (this is critical - many documents have headings in tables)
8613
- const tables = body["w:tbl"];
8546
+ const tables = body['w:tbl'];
8614
8547
  if (tables) {
8615
8548
  const tableArray = Array.isArray(tables) ? tables : [tables];
8616
8549
  for (const table of tableArray) {
8617
- const rows = table["w:tr"];
8550
+ const rows = table['w:tr'];
8618
8551
  if (!rows) continue;
8619
8552
 
8620
8553
  const rowArray = Array.isArray(rows) ? rows : [rows];
8621
8554
  for (const row of rowArray) {
8622
- const cells = row["w:tc"];
8555
+ const cells = row['w:tc'];
8623
8556
  if (!cells) continue;
8624
8557
 
8625
8558
  const cellArray = Array.isArray(cells) ? cells : [cells];
8626
8559
  for (const cell of cellArray) {
8627
- const cellParas = cell["w:p"];
8560
+ const cellParas = cell['w:p'];
8628
8561
  if (!cellParas) continue;
8629
8562
 
8630
- const cellParaArray = Array.isArray(cellParas)
8631
- ? cellParas
8632
- : [cellParas];
8563
+ const cellParaArray = Array.isArray(cellParas) ? cellParas : [cellParas];
8633
8564
  for (const para of cellParaArray) {
8634
8565
  extractHeading(para);
8635
8566
  }
@@ -8639,7 +8570,7 @@ export class Document {
8639
8570
  }
8640
8571
  } catch (error: unknown) {
8641
8572
  defaultLogger.error(
8642
- "Error parsing document.xml for headings:",
8573
+ 'Error parsing document.xml for headings:',
8643
8574
  error instanceof Error
8644
8575
  ? { message: error.message, stack: error.stack }
8645
8576
  : { error: String(error) }
@@ -8657,8 +8588,7 @@ export class Document {
8657
8588
  private findHeadingsForTOC(
8658
8589
  levels: number[]
8659
8590
  ): { level: number; text: string; bookmark: string }[] {
8660
- const headings: { level: number; text: string; bookmark: string }[] =
8661
- [];
8591
+ const headings: { level: number; text: string; bookmark: string }[] = [];
8662
8592
  const levelSet = new Set(levels);
8663
8593
 
8664
8594
  // Iterate through body elements
@@ -8679,8 +8609,7 @@ export class Document {
8679
8609
 
8680
8610
  if (text) {
8681
8611
  // Create or get bookmark for this heading
8682
- const bookmark =
8683
- this.bookmarkManager.createHeadingBookmark(text);
8612
+ const bookmark = this.bookmarkManager.createHeadingBookmark(text);
8684
8613
 
8685
8614
  headings.push({
8686
8615
  level: headingLevel,
@@ -8715,30 +8644,28 @@ export class Document {
8715
8644
  ): string {
8716
8645
  const sdtId = Math.floor(Math.random() * 2000000000) - 1000000000;
8717
8646
 
8718
- let tocXml = "<w:sdt>";
8647
+ let tocXml = '<w:sdt>';
8719
8648
 
8720
8649
  // SDT properties
8721
- tocXml += "<w:sdtPr>";
8650
+ tocXml += '<w:sdtPr>';
8722
8651
  tocXml += `<w:id w:val="${sdtId}"/>`;
8723
- tocXml += "<w:docPartObj>";
8652
+ tocXml += '<w:docPartObj>';
8724
8653
  tocXml += '<w:docPartGallery w:val="Table of Contents"/>';
8725
8654
  tocXml += '<w:docPartUnique w:val="1"/>';
8726
- tocXml += "</w:docPartObj>";
8727
- tocXml += "</w:sdtPr>";
8655
+ tocXml += '</w:docPartObj>';
8656
+ tocXml += '</w:sdtPr>';
8728
8657
 
8729
8658
  // SDT content
8730
- tocXml += "<w:sdtContent>";
8659
+ tocXml += '<w:sdtContent>';
8731
8660
 
8732
8661
  // Calculate minimum level for relative indentation
8733
8662
  // 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;
8663
+ const minLevel = headings.length > 0 ? Math.min(...headings.map((h) => h.level)) : 1;
8736
8664
 
8737
8665
  // 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"/>';
8666
+ tocXml += '<w:p>';
8667
+ tocXml += '<w:pPr>';
8668
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8742
8669
 
8743
8670
  // Add indentation for first entry relative to minimum level (0.25" per level)
8744
8671
  if (headings.length > 0 && headings[0]) {
@@ -8748,17 +8675,17 @@ export class Document {
8748
8675
  }
8749
8676
  }
8750
8677
 
8751
- tocXml += "</w:pPr>";
8678
+ tocXml += '</w:pPr>';
8752
8679
 
8753
8680
  // Field begin
8754
8681
  tocXml += '<w:r><w:fldChar w:fldCharType="begin"/></w:r>';
8755
8682
 
8756
8683
  // Field instruction (preserve original switches)
8757
- tocXml += "<w:r>";
8684
+ tocXml += '<w:r>';
8758
8685
  tocXml += `<w:instrText xml:space="preserve">${this.escapeXml(
8759
8686
  originalInstrText
8760
8687
  )}</w:instrText>`;
8761
- tocXml += "</w:r>";
8688
+ tocXml += '</w:r>';
8762
8689
 
8763
8690
  // Field separator
8764
8691
  tocXml += '<w:r><w:fldChar w:fldCharType="separate"/></w:r>';
@@ -8768,17 +8695,16 @@ export class Document {
8768
8695
  tocXml += this.buildTOCEntryXML(headings[0]);
8769
8696
  }
8770
8697
 
8771
- tocXml += "</w:p>";
8698
+ tocXml += '</w:p>';
8772
8699
 
8773
8700
  // Remaining entries (each in its own paragraph)
8774
8701
  for (let i = 1; i < headings.length; i++) {
8775
8702
  const heading = headings[i];
8776
8703
  if (!heading) continue;
8777
8704
 
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"/>';
8705
+ tocXml += '<w:p>';
8706
+ tocXml += '<w:pPr>';
8707
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8782
8708
 
8783
8709
  // Add indentation relative to minimum level (0.25" per level above minimum)
8784
8710
  const indent = (heading.level - minLevel) * 360;
@@ -8786,22 +8712,21 @@ export class Document {
8786
8712
  tocXml += `<w:ind w:left="${indent}"/>`;
8787
8713
  }
8788
8714
 
8789
- tocXml += "</w:pPr>";
8715
+ tocXml += '</w:pPr>';
8790
8716
  tocXml += this.buildTOCEntryXML(heading);
8791
- tocXml += "</w:p>";
8717
+ tocXml += '</w:p>';
8792
8718
  }
8793
8719
 
8794
8720
  // 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>";
8721
+ tocXml += '<w:p>';
8722
+ tocXml += '<w:pPr>';
8723
+ tocXml += '<w:spacing w:after="0" w:before="0" w:line="240" w:lineRule="auto"/>';
8724
+ tocXml += '</w:pPr>';
8800
8725
  tocXml += '<w:r><w:fldChar w:fldCharType="end"/></w:r>';
8801
- tocXml += "</w:p>";
8726
+ tocXml += '</w:p>';
8802
8727
 
8803
- tocXml += "</w:sdtContent>";
8804
- tocXml += "</w:sdt>";
8728
+ tocXml += '</w:sdtContent>';
8729
+ tocXml += '</w:sdt>';
8805
8730
 
8806
8731
  return tocXml;
8807
8732
  }
@@ -8812,27 +8737,22 @@ export class Document {
8812
8737
  * @param heading Heading information
8813
8738
  * @returns XML string for the TOC entry
8814
8739
  */
8815
- private buildTOCEntryXML(heading: {
8816
- level: number;
8817
- text: string;
8818
- bookmark: string;
8819
- }): string {
8740
+ private buildTOCEntryXML(heading: { level: number; text: string; bookmark: string }): string {
8820
8741
  const escapedText = this.escapeXml(heading.text);
8821
8742
 
8822
- let xml = "";
8743
+ let xml = '';
8823
8744
  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"/>';
8745
+ xml += '<w:r>';
8746
+ xml += '<w:rPr>';
8747
+ xml += '<w:rFonts w:ascii="Verdana" w:hAnsi="Verdana" w:cs="Verdana" w:eastAsia="Verdana"/>';
8828
8748
  xml += '<w:color w:val="0000FF"/>';
8829
8749
  xml += '<w:sz w:val="24"/>';
8830
8750
  xml += '<w:szCs w:val="24"/>';
8831
8751
  xml += '<w:u w:val="single"/>';
8832
- xml += "</w:rPr>";
8752
+ xml += '</w:rPr>';
8833
8753
  xml += `<w:t xml:space="preserve">${escapedText}</w:t>`;
8834
- xml += "</w:r>";
8835
- xml += "</w:hyperlink>";
8754
+ xml += '</w:r>';
8755
+ xml += '</w:hyperlink>';
8836
8756
 
8837
8757
  return xml;
8838
8758
  }
@@ -8845,11 +8765,11 @@ export class Document {
8845
8765
  */
8846
8766
  private escapeXml(text: string): string {
8847
8767
  return text
8848
- .replace(/&/g, "&amp;")
8849
- .replace(/</g, "&lt;")
8850
- .replace(/>/g, "&gt;")
8851
- .replace(/"/g, "&quot;")
8852
- .replace(/'/g, "&apos;");
8768
+ .replace(/&/g, '&amp;')
8769
+ .replace(/</g, '&lt;')
8770
+ .replace(/>/g, '&gt;')
8771
+ .replace(/"/g, '&quot;')
8772
+ .replace(/'/g, '&apos;');
8853
8773
  }
8854
8774
 
8855
8775
  /**
@@ -8866,7 +8786,7 @@ export class Document {
8866
8786
  await handler.load(filePath);
8867
8787
 
8868
8788
  // Get document.xml
8869
- const docXml = handler.getFileAsString("word/document.xml");
8789
+ const docXml = handler.getFileAsString('word/document.xml');
8870
8790
  if (!docXml) {
8871
8791
  return 0;
8872
8792
  }
@@ -8876,7 +8796,7 @@ export class Document {
8876
8796
 
8877
8797
  // Update and save if changes were made
8878
8798
  if (modifiedXml !== docXml) {
8879
- handler.updateFile("word/document.xml", modifiedXml);
8799
+ handler.updateFile('word/document.xml', modifiedXml);
8880
8800
  await handler.save(filePath);
8881
8801
 
8882
8802
  // Count TOCs that were populated
@@ -8913,9 +8833,9 @@ export class Document {
8913
8833
  if (!instrMatch?.[1]) continue;
8914
8834
 
8915
8835
  const fieldInstruction = instrMatch[1]
8916
- .replace(/&amp;/g, "&")
8917
- .replace(/&lt;/g, "<")
8918
- .replace(/&gt;/g, ">")
8836
+ .replace(/&amp;/g, '&')
8837
+ .replace(/&lt;/g, '<')
8838
+ .replace(/&gt;/g, '>')
8919
8839
  .replace(/&quot;/g, '"')
8920
8840
  .replace(/&apos;/g, "'");
8921
8841
 
@@ -8927,7 +8847,7 @@ export class Document {
8927
8847
  modifiedXml = modifiedXml.replace(tocXml, newTocXml);
8928
8848
  } catch (error: unknown) {
8929
8849
  this.logger.error(
8930
- "Error populating SDT TOC",
8850
+ 'Error populating SDT TOC',
8931
8851
  error instanceof Error
8932
8852
  ? { message: error.message, stack: error.stack }
8933
8853
  : { error: String(error) }
@@ -8952,9 +8872,9 @@ export class Document {
8952
8872
  if (!instrText) continue;
8953
8873
 
8954
8874
  const fieldInstruction = instrText
8955
- .replace(/&amp;/g, "&")
8956
- .replace(/&lt;/g, "<")
8957
- .replace(/&gt;/g, ">")
8875
+ .replace(/&amp;/g, '&')
8876
+ .replace(/&lt;/g, '<')
8877
+ .replace(/&gt;/g, '>')
8958
8878
  .replace(/&quot;/g, '"')
8959
8879
  .replace(/&apos;/g, "'");
8960
8880
 
@@ -8989,12 +8909,10 @@ export class Document {
8989
8909
  const newTocXml = this.generateSimpleTOCXML(headings, fieldInstruction);
8990
8910
  modifiedXml = modifiedXml.replace(fullTocXml, newTocXml);
8991
8911
 
8992
- this.logger.info(
8993
- `Populated simple TOC with ${headings.length} heading entries`
8994
- );
8912
+ this.logger.info(`Populated simple TOC with ${headings.length} heading entries`);
8995
8913
  } catch (error: unknown) {
8996
8914
  this.logger.error(
8997
- "Error populating simple TOC",
8915
+ 'Error populating simple TOC',
8998
8916
  error instanceof Error
8999
8917
  ? { message: error.message, stack: error.stack }
9000
8918
  : { error: String(error) }
@@ -9029,29 +8947,29 @@ export class Document {
9029
8947
 
9030
8948
  // Escape text for XML
9031
8949
  const escapedText = heading.text
9032
- .replace(/&/g, "&amp;")
9033
- .replace(/</g, "&lt;")
9034
- .replace(/>/g, "&gt;")
9035
- .replace(/"/g, "&quot;");
8950
+ .replace(/&/g, '&amp;')
8951
+ .replace(/</g, '&lt;')
8952
+ .replace(/>/g, '&gt;')
8953
+ .replace(/"/g, '&quot;');
9036
8954
 
9037
8955
  // Create hyperlinked TOC entry paragraph
9038
8956
  entries.push(
9039
8957
  `<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>`
8958
+ `<w:pPr><w:pStyle w:val="${tocStyle}"/></w:pPr>` +
8959
+ `<w:hyperlink w:anchor="${heading.bookmark}" w:history="1">` +
8960
+ `<w:r><w:rPr><w:rStyle w:val="Hyperlink"/></w:rPr>` +
8961
+ `<w:t>${escapedText}</w:t></w:r>` +
8962
+ `</w:hyperlink>` +
8963
+ `</w:p>`
9046
8964
  );
9047
8965
  }
9048
8966
 
9049
8967
  // Escape field instruction for XML
9050
8968
  const escapedInstruction = fieldInstruction
9051
- .replace(/&/g, "&amp;")
9052
- .replace(/</g, "&lt;")
9053
- .replace(/>/g, "&gt;")
9054
- .replace(/"/g, "&quot;");
8969
+ .replace(/&/g, '&amp;')
8970
+ .replace(/</g, '&lt;')
8971
+ .replace(/>/g, '&gt;')
8972
+ .replace(/"/g, '&quot;');
9055
8973
 
9056
8974
  // Build the complete TOC field structure
9057
8975
  return (
@@ -9063,7 +8981,7 @@ export class Document {
9063
8981
  `<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
9064
8982
  `</w:p>` +
9065
8983
  // TOC entry paragraphs (between separate and end)
9066
- entries.join("") +
8984
+ entries.join('') +
9067
8985
  // Last paragraph: fldChar end
9068
8986
  `<w:p>` +
9069
8987
  `<w:pPr><w:pStyle w:val="TOC2"/></w:pPr>` +
@@ -9127,11 +9045,7 @@ export class Document {
9127
9045
  * @param orientation Page orientation
9128
9046
  * @returns This document for chaining
9129
9047
  */
9130
- setPageSize(
9131
- width: number,
9132
- height: number,
9133
- orientation?: "portrait" | "landscape"
9134
- ): this {
9048
+ setPageSize(width: number, height: number, orientation?: 'portrait' | 'landscape'): this {
9135
9049
  this.section.setPageSize(width, height, orientation);
9136
9050
  return this;
9137
9051
  }
@@ -9141,7 +9055,7 @@ export class Document {
9141
9055
  * @param orientation Page orientation
9142
9056
  * @returns This document for chaining
9143
9057
  */
9144
- setPageOrientation(orientation: "portrait" | "landscape"): this {
9058
+ setPageOrientation(orientation: 'portrait' | 'landscape'): this {
9145
9059
  this.section.setOrientation(orientation);
9146
9060
  return this;
9147
9061
  }
@@ -9171,15 +9085,13 @@ export class Document {
9171
9085
  */
9172
9086
  setHeader(header: Header): this {
9173
9087
  // Generate relationship for header
9174
- const relationship = this.relationshipManager.addHeader(
9175
- `${header.getFilename(1)}`
9176
- );
9088
+ const relationship = this.relationshipManager.addHeader(`${header.getFilename(1)}`);
9177
9089
 
9178
9090
  // Register with manager
9179
9091
  this.headerFooterManager.registerHeader(header, relationship.getId());
9180
9092
 
9181
9093
  // Link to section
9182
- this.section.setHeaderReference("default", relationship.getId());
9094
+ this.section.setHeaderReference('default', relationship.getId());
9183
9095
 
9184
9096
  return this;
9185
9097
  }
@@ -9202,7 +9114,7 @@ export class Document {
9202
9114
  this.headerFooterManager.registerHeader(header, relationship.getId());
9203
9115
 
9204
9116
  // Link to section
9205
- this.section.setHeaderReference("first", relationship.getId());
9117
+ this.section.setHeaderReference('first', relationship.getId());
9206
9118
 
9207
9119
  return this;
9208
9120
  }
@@ -9222,7 +9134,7 @@ export class Document {
9222
9134
  this.headerFooterManager.registerHeader(header, relationship.getId());
9223
9135
 
9224
9136
  // Link to section
9225
- this.section.setHeaderReference("even", relationship.getId());
9137
+ this.section.setHeaderReference('even', relationship.getId());
9226
9138
 
9227
9139
  return this;
9228
9140
  }
@@ -9234,15 +9146,13 @@ export class Document {
9234
9146
  */
9235
9147
  setFooter(footer: Footer): this {
9236
9148
  // Generate relationship for footer
9237
- const relationship = this.relationshipManager.addFooter(
9238
- `${footer.getFilename(1)}`
9239
- );
9149
+ const relationship = this.relationshipManager.addFooter(`${footer.getFilename(1)}`);
9240
9150
 
9241
9151
  // Register with manager
9242
9152
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9243
9153
 
9244
9154
  // Link to section
9245
- this.section.setFooterReference("default", relationship.getId());
9155
+ this.section.setFooterReference('default', relationship.getId());
9246
9156
 
9247
9157
  return this;
9248
9158
  }
@@ -9265,7 +9175,7 @@ export class Document {
9265
9175
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9266
9176
 
9267
9177
  // Link to section
9268
- this.section.setFooterReference("first", relationship.getId());
9178
+ this.section.setFooterReference('first', relationship.getId());
9269
9179
 
9270
9180
  return this;
9271
9181
  }
@@ -9285,7 +9195,7 @@ export class Document {
9285
9195
  this.headerFooterManager.registerFooter(footer, relationship.getId());
9286
9196
 
9287
9197
  // Link to section
9288
- this.section.setFooterReference("even", relationship.getId());
9198
+ this.section.setFooterReference('even', relationship.getId());
9289
9199
 
9290
9200
  return this;
9291
9201
  }
@@ -9305,7 +9215,7 @@ export class Document {
9305
9215
  * @param type Header type to remove (default, first, even)
9306
9216
  * @returns This document for chaining
9307
9217
  */
9308
- removeHeader(type: "default" | "first" | "even"): this {
9218
+ removeHeader(type: 'default' | 'first' | 'even'): this {
9309
9219
  const sectionProps = this.section.getProperties();
9310
9220
 
9311
9221
  // Get the relationship ID from section properties
@@ -9351,7 +9261,7 @@ export class Document {
9351
9261
  * @param type Footer type to remove (default, first, even)
9352
9262
  * @returns This document for chaining
9353
9263
  */
9354
- removeFooter(type: "default" | "first" | "even"): this {
9264
+ removeFooter(type: 'default' | 'first' | 'even'): this {
9355
9265
  const sectionProps = this.section.getProperties();
9356
9266
 
9357
9267
  // Get the relationship ID from section properties
@@ -9400,7 +9310,7 @@ export class Document {
9400
9310
 
9401
9311
  // Remove each header type
9402
9312
  if (sectionProps.headers) {
9403
- const types = Object.keys(sectionProps.headers) as ("default" | "first" | "even")[];
9313
+ const types = Object.keys(sectionProps.headers) as ('default' | 'first' | 'even')[];
9404
9314
  for (const type of types) {
9405
9315
  this.removeHeader(type);
9406
9316
  }
@@ -9422,7 +9332,7 @@ export class Document {
9422
9332
 
9423
9333
  // Remove each footer type
9424
9334
  if (sectionProps.footers) {
9425
- const types = Object.keys(sectionProps.footers) as ("default" | "first" | "even")[];
9335
+ const types = Object.keys(sectionProps.footers) as ('default' | 'first' | 'even')[];
9426
9336
  for (const type of types) {
9427
9337
  this.removeFooter(type);
9428
9338
  }
@@ -9441,9 +9351,7 @@ export class Document {
9441
9351
  */
9442
9352
  addImage(image: Image): this {
9443
9353
  // Generate relationship ID
9444
- const target = `media/image${
9445
- this.imageManager.getImageCount() + 1
9446
- }.${image.getExtension()}`;
9354
+ const target = `media/image${this.imageManager.getImageCount() + 1}.${image.getExtension()}`;
9447
9355
  const relationship = this.relationshipManager.addImage(target);
9448
9356
 
9449
9357
  // Register image with manager
@@ -9555,7 +9463,7 @@ export class Document {
9555
9463
  */
9556
9464
  private updateRelationships(): void {
9557
9465
  const xml = this.relationshipManager.generateXml();
9558
- this.zipHandler.updateFile("word/_rels/document.xml.rels", xml);
9466
+ this.zipHandler.updateFile('word/_rels/document.xml.rels', xml);
9559
9467
  }
9560
9468
 
9561
9469
  /**
@@ -9566,26 +9474,29 @@ export class Document {
9566
9474
  // Comments were modified — regenerate from in-memory model
9567
9475
  if (this.commentManager.getCount() > 0) {
9568
9476
  const xml = this.commentManager.generateCommentsXml();
9569
- this.zipHandler.addFile("word/comments.xml", xml);
9477
+ this.zipHandler.addFile('word/comments.xml', xml);
9570
9478
 
9571
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9479
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9480
+ RelationshipType.COMMENTS
9481
+ );
9572
9482
  if (existingRels.length === 0) {
9573
9483
  this.relationshipManager.addComments();
9574
9484
  }
9575
9485
  } else {
9576
9486
  // 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);
9487
+ this.zipHandler.removeFile('word/comments.xml');
9488
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9489
+ RelationshipType.COMMENTS
9490
+ );
9579
9491
  for (const rel of existingRels) {
9580
9492
  this.relationshipManager.removeRelationship(rel.getId());
9581
9493
  }
9582
9494
  }
9583
9495
  // Remove companion files since regenerated comments lack w14:paraId
9584
9496
  this.removeCommentCompanionFiles();
9585
-
9586
9497
  } else if (this._originalCommentsXml) {
9587
9498
  // Passthrough — preserve original comments.xml exactly
9588
- this.zipHandler.addFile("word/comments.xml", this._originalCommentsXml);
9499
+ this.zipHandler.addFile('word/comments.xml', this._originalCommentsXml);
9589
9500
 
9590
9501
  // Preserve companion files as-is
9591
9502
  for (const [path, content] of this._originalCommentCompanionFiles) {
@@ -9593,17 +9504,20 @@ export class Document {
9593
9504
  }
9594
9505
 
9595
9506
  // Ensure comments relationship exists
9596
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9507
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9508
+ RelationshipType.COMMENTS
9509
+ );
9597
9510
  if (existingRels.length === 0) {
9598
9511
  this.relationshipManager.addComments();
9599
9512
  }
9600
-
9601
9513
  } else if (this.commentManager.getCount() > 0) {
9602
9514
  // New comments created on a document that had no original comments
9603
9515
  const xml = this.commentManager.generateCommentsXml();
9604
- this.zipHandler.addFile("word/comments.xml", xml);
9516
+ this.zipHandler.addFile('word/comments.xml', xml);
9605
9517
 
9606
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.COMMENTS);
9518
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9519
+ RelationshipType.COMMENTS
9520
+ );
9607
9521
  if (existingRels.length === 0) {
9608
9522
  this.relationshipManager.addComments();
9609
9523
  }
@@ -9611,7 +9525,11 @@ export class Document {
9611
9525
  }
9612
9526
 
9613
9527
  private removeCommentCompanionFiles(): void {
9614
- const companionPaths = [DOCX_PATHS.COMMENTS_EXTENDED, DOCX_PATHS.COMMENTS_IDS, DOCX_PATHS.COMMENTS_EXTENSIBLE];
9528
+ const companionPaths = [
9529
+ DOCX_PATHS.COMMENTS_EXTENDED,
9530
+ DOCX_PATHS.COMMENTS_IDS,
9531
+ DOCX_PATHS.COMMENTS_EXTENSIBLE,
9532
+ ];
9615
9533
  for (const filePath of companionPaths) {
9616
9534
  this.zipHandler.removeFile(filePath);
9617
9535
  const fileName = filePath.replace('word/', '');
@@ -9628,7 +9546,9 @@ export class Document {
9628
9546
  const xml = this.footnoteManager.generateFootnotesXml();
9629
9547
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, xml);
9630
9548
 
9631
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
9549
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9550
+ RelationshipType.FOOTNOTES
9551
+ );
9632
9552
  if (existingRels.length === 0) {
9633
9553
  this.relationshipManager.addFootnotes();
9634
9554
  }
@@ -9636,14 +9556,16 @@ export class Document {
9636
9556
  // Per OOXML, relationships are part-scoped: hyperlinks in footnotes.xml
9637
9557
  // need word/_rels/footnotes.xml.rels, not word/_rels/document.xml.rels
9638
9558
  this.generatePartLevelHyperlinkRels(
9639
- this.footnoteManager.getAllFootnotes().flatMap(fn => fn.getParagraphs()),
9559
+ this.footnoteManager.getAllFootnotes().flatMap((fn) => fn.getParagraphs()),
9640
9560
  'word/_rels/footnotes.xml.rels'
9641
9561
  );
9642
9562
  } else if (this._originalFootnotesXml) {
9643
9563
  // Passthrough — preserve original XML exactly
9644
9564
  this.zipHandler.addFile(DOCX_PATHS.FOOTNOTES, this._originalFootnotesXml);
9645
9565
 
9646
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.FOOTNOTES);
9566
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9567
+ RelationshipType.FOOTNOTES
9568
+ );
9647
9569
  if (existingRels.length === 0) {
9648
9570
  this.relationshipManager.addFootnotes();
9649
9571
  }
@@ -9655,7 +9577,9 @@ export class Document {
9655
9577
  const xml = this.endnoteManager.generateEndnotesXml();
9656
9578
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, xml);
9657
9579
 
9658
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
9580
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9581
+ RelationshipType.ENDNOTES
9582
+ );
9659
9583
  if (existingRels.length === 0) {
9660
9584
  this.relationshipManager.addEndnotes();
9661
9585
  }
@@ -9663,14 +9587,16 @@ export class Document {
9663
9587
  // Per OOXML, relationships are part-scoped: hyperlinks in endnotes.xml
9664
9588
  // need word/_rels/endnotes.xml.rels, not word/_rels/document.xml.rels
9665
9589
  this.generatePartLevelHyperlinkRels(
9666
- this.endnoteManager.getAllEndnotes().flatMap(en => en.getParagraphs()),
9590
+ this.endnoteManager.getAllEndnotes().flatMap((en) => en.getParagraphs()),
9667
9591
  'word/_rels/endnotes.xml.rels'
9668
9592
  );
9669
9593
  } else if (this._originalEndnotesXml) {
9670
9594
  // Passthrough — preserve original XML exactly
9671
9595
  this.zipHandler.addFile(DOCX_PATHS.ENDNOTES, this._originalEndnotesXml);
9672
9596
 
9673
- const existingRels = this.relationshipManager.getRelationshipsByType(RelationshipType.ENDNOTES);
9597
+ const existingRels = this.relationshipManager.getRelationshipsByType(
9598
+ RelationshipType.ENDNOTES
9599
+ );
9674
9600
  if (existingRels.length === 0) {
9675
9601
  this.relationshipManager.addEndnotes();
9676
9602
  }
@@ -9733,7 +9659,8 @@ export class Document {
9733
9659
 
9734
9660
  if (hyperlinkRels.length > 0) {
9735
9661
  let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
9736
- xml += '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
9662
+ xml +=
9663
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n';
9737
9664
  for (const rel of hyperlinkRels) {
9738
9665
  const escapedId = XMLBuilder.escapeXmlAttribute(rel.id);
9739
9666
  const escapedUrl = XMLBuilder.escapeXmlAttribute(rel.url);
@@ -9814,14 +9741,23 @@ export class Document {
9814
9741
  if (missingAuthors.length === 0) return;
9815
9742
 
9816
9743
  // 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('');
9744
+ const personElements = missingAuthors
9745
+ .map((author) => {
9746
+ const escapedAuthor = author
9747
+ .replace(/&/g, '&amp;')
9748
+ .replace(/"/g, '&quot;')
9749
+ .replace(/</g, '&lt;')
9750
+ .replace(/>/g, '&gt;');
9751
+ return `<w15:person w15:author="${escapedAuthor}"><w15:presenceInfo w15:providerId="None" w15:userId="${escapedAuthor}"/></w15:person>`;
9752
+ })
9753
+ .join('');
9821
9754
 
9822
9755
  if (existingPeopleXml) {
9823
9756
  // Insert new person elements before closing tag
9824
- const updatedXml = existingPeopleXml.replace('</w15:people>', `${personElements}</w15:people>`);
9757
+ const updatedXml = existingPeopleXml.replace(
9758
+ '</w15:people>',
9759
+ `${personElements}</w15:people>`
9760
+ );
9825
9761
  this.zipHandler.updateFile('word/people.xml', updatedXml);
9826
9762
  } else {
9827
9763
  // Create new people.xml
@@ -9836,7 +9772,10 @@ export class Document {
9836
9772
  this.relationshipManager.addPeople();
9837
9773
  }
9838
9774
 
9839
- this.logger.info('Updated people.xml with missing authors', { added: missingAuthors.length, authors: missingAuthors });
9775
+ this.logger.info('Updated people.xml with missing authors', {
9776
+ added: missingAuthors.length,
9777
+ authors: missingAuthors,
9778
+ });
9840
9779
  }
9841
9780
 
9842
9781
  /**
@@ -9880,11 +9819,9 @@ export class Document {
9880
9819
  this.properties.customProperties &&
9881
9820
  Object.keys(this.properties.customProperties).length > 0
9882
9821
  ) {
9883
- const customXml = this.generator.generateCustomProps(
9884
- this.properties.customProperties
9885
- );
9822
+ const customXml = this.generator.generateCustomProps(this.properties.customProperties);
9886
9823
  if (customXml) {
9887
- this.zipHandler.addFile("docProps/custom.xml", customXml);
9824
+ this.zipHandler.addFile('docProps/custom.xml', customXml);
9888
9825
  }
9889
9826
  }
9890
9827
  }
@@ -9901,13 +9838,17 @@ export class Document {
9901
9838
  const overrides = new Set<string>();
9902
9839
 
9903
9840
  // Extract all <Default Extension="..." ContentType="..."/> entries
9904
- const defaultMatches = xml.matchAll(/<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
9841
+ const defaultMatches = xml.matchAll(
9842
+ /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
9843
+ );
9905
9844
  for (const match of defaultMatches) {
9906
9845
  defaults.add(`${match[1]}|${match[2]}`); // Store as "ext|mimetype"
9907
9846
  }
9908
9847
 
9909
9848
  // Extract all <Override PartName="..." ContentType="..."/> entries
9910
- const overrideMatches = xml.matchAll(/<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g);
9849
+ const overrideMatches = xml.matchAll(
9850
+ /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"\s*\/>/g
9851
+ );
9911
9852
  for (const match of overrideMatches) {
9912
9853
  overrides.add(`${match[1]}|${match[2]}`); // Store as "path|mimetype"
9913
9854
  }
@@ -9921,21 +9862,19 @@ export class Document {
9921
9862
  */
9922
9863
  private updateContentTypesWithImagesHeadersFootersAndComments(): void {
9923
9864
  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
- );
9865
+ this.properties.customProperties && Object.keys(this.properties.customProperties).length > 0;
9866
+
9867
+ const contentTypes = this.generator.generateContentTypesWithImagesHeadersFootersAndComments(
9868
+ this.imageManager,
9869
+ this.headerFooterManager,
9870
+ this.commentManager,
9871
+ this.zipHandler, // Pass zipHandler to check file existence
9872
+ undefined, // fontManager (optional)
9873
+ hasCustomProps, // Flag to include custom.xml override
9874
+ this._originalContentTypes, // Pass preserved original entries for round-trip fidelity
9875
+ this.footnoteManager,
9876
+ this.endnoteManager
9877
+ );
9939
9878
  this.zipHandler.updateFile(DOCX_PATHS.CONTENT_TYPES, contentTypes);
9940
9879
  }
9941
9880
 
@@ -10016,14 +9955,9 @@ export class Document {
10016
9955
  * @param bookmarkOrName - Bookmark object or bookmark name
10017
9956
  * @returns The bookmark that was added
10018
9957
  */
10019
- addBookmarkToParagraph(
10020
- paragraph: Paragraph,
10021
- bookmarkOrName: Bookmark | string
10022
- ): Bookmark {
9958
+ addBookmarkToParagraph(paragraph: Paragraph, bookmarkOrName: Bookmark | string): Bookmark {
10023
9959
  const bookmark =
10024
- typeof bookmarkOrName === "string"
10025
- ? this.createBookmark(bookmarkOrName)
10026
- : bookmarkOrName;
9960
+ typeof bookmarkOrName === 'string' ? this.createBookmark(bookmarkOrName) : bookmarkOrName;
10027
9961
 
10028
9962
  paragraph.addBookmark(bookmark);
10029
9963
  return bookmark;
@@ -10065,7 +9999,7 @@ export class Document {
10065
9999
  anchor: string;
10066
10000
  hyperlink: (text: string, formatting?: RunFormatting) => Hyperlink;
10067
10001
  } {
10068
- const BOOKMARK_NAME = "_top";
10002
+ const BOOKMARK_NAME = '_top';
10069
10003
 
10070
10004
  // Check if _top bookmark already exists
10071
10005
  let bookmark = this.getBookmark(BOOKMARK_NAME);
@@ -10223,10 +10157,10 @@ export class Document {
10223
10157
  * }
10224
10158
  * ```
10225
10159
  */
10226
- validateAndFixRevisions(options?: {
10227
- validation?: ValidationOptions;
10228
- autoFix?: AutoFixOptions;
10229
- }): { validation: ValidationResult; fix: AutoFixResult } {
10160
+ validateAndFixRevisions(options?: { validation?: ValidationOptions; autoFix?: AutoFixOptions }): {
10161
+ validation: ValidationResult;
10162
+ fix: AutoFixResult;
10163
+ } {
10230
10164
  // First validate
10231
10165
  const validation = this.validateRevisions(options?.validation);
10232
10166
 
@@ -10279,12 +10213,7 @@ export class Document {
10279
10213
  * @param date - Optional date (defaults to now)
10280
10214
  * @returns The created and registered revision
10281
10215
  */
10282
- createRevisionFromText(
10283
- type: RevisionType,
10284
- author: string,
10285
- text: string,
10286
- date?: Date
10287
- ): Revision {
10216
+ createRevisionFromText(type: RevisionType, author: string, text: string, date?: Date): Revision {
10288
10217
  const revision = Revision.fromText(type, author, text, date);
10289
10218
  return this.revisionManager.register(revision);
10290
10219
  }
@@ -10305,7 +10234,10 @@ export class Document {
10305
10234
  * @param runIndex - Optional run index within the paragraph
10306
10235
  * @returns RevisionLocation with paragraph index, or undefined if not found
10307
10236
  */
10308
- private createRevisionLocation(paragraph: Paragraph, runIndex?: number): RevisionLocation | undefined {
10237
+ private createRevisionLocation(
10238
+ paragraph: Paragraph,
10239
+ runIndex?: number
10240
+ ): RevisionLocation | undefined {
10309
10241
  const paragraphIndex = this.findParagraphIndex(paragraph);
10310
10242
  if (paragraphIndex === -1) {
10311
10243
  return undefined; // Paragraph not found in document
@@ -10325,13 +10257,8 @@ export class Document {
10325
10257
  * @param date - Optional date (defaults to now)
10326
10258
  * @returns The created revision with location set
10327
10259
  */
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);
10260
+ trackInsertion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
10261
+ const revision = this.createRevisionFromText('insert', author, text, date);
10335
10262
 
10336
10263
  // Set location for changelog/tracking purposes
10337
10264
  const location = this.createRevisionLocation(paragraph);
@@ -10353,13 +10280,8 @@ export class Document {
10353
10280
  * @param date - Optional date (defaults to now)
10354
10281
  * @returns The created revision with location set
10355
10282
  */
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);
10283
+ trackDeletion(paragraph: Paragraph, author: string, text: string, date?: Date): Revision {
10284
+ const revision = this.createRevisionFromText('delete', author, text, date);
10363
10285
 
10364
10286
  // Set location for changelog/tracking purposes
10365
10287
  const location = this.createRevisionLocation(paragraph);
@@ -10424,11 +10346,7 @@ export class Document {
10424
10346
  * doc.trackParagraphMarkDeletion(para, 'Alice');
10425
10347
  * // In Word, this shows the ¶ symbol as deleted
10426
10348
  */
10427
- trackParagraphMarkDeletion(
10428
- paragraph: Paragraph,
10429
- author: string,
10430
- date?: Date
10431
- ): Paragraph {
10349
+ trackParagraphMarkDeletion(paragraph: Paragraph, author: string, date?: Date): Paragraph {
10432
10350
  const revisionId = this.revisionManager.consumeNextId();
10433
10351
  paragraph.markParagraphMarkAsDeleted(revisionId, author, date);
10434
10352
  return paragraph;
@@ -10622,15 +10540,13 @@ export class Document {
10622
10540
  this.trackFormatting = options.trackFormatting;
10623
10541
  }
10624
10542
  if (options.showInsertionsAndDeletions !== undefined) {
10625
- this.revisionViewSettings.showInsertionsAndDeletions =
10626
- options.showInsertionsAndDeletions;
10543
+ this.revisionViewSettings.showInsertionsAndDeletions = options.showInsertionsAndDeletions;
10627
10544
  }
10628
10545
  if (options.showFormatting !== undefined) {
10629
10546
  this.revisionViewSettings.showFormatting = options.showFormatting;
10630
10547
  }
10631
10548
  if (options.showInkAnnotations !== undefined) {
10632
- this.revisionViewSettings.showInkAnnotations =
10633
- options.showInkAnnotations;
10549
+ this.revisionViewSettings.showInkAnnotations = options.showInkAnnotations;
10634
10550
  }
10635
10551
  }
10636
10552
 
@@ -10769,6 +10685,41 @@ export class Document {
10769
10685
  }
10770
10686
  // Bind tracking to section
10771
10687
  this.bindTrackingToElement(this.section);
10688
+
10689
+ // Headers
10690
+ for (const entry of this.headerFooterManager.getAllHeaders()) {
10691
+ for (const element of entry.header.getElements()) {
10692
+ this.bindTrackingToElement(element);
10693
+ }
10694
+ }
10695
+
10696
+ // Footers
10697
+ for (const entry of this.headerFooterManager.getAllFooters()) {
10698
+ for (const element of entry.footer.getElements()) {
10699
+ this.bindTrackingToElement(element);
10700
+ }
10701
+ }
10702
+
10703
+ // Footnotes
10704
+ for (const footnote of this.footnoteManager.getAllFootnotes()) {
10705
+ for (const para of footnote.getParagraphs()) {
10706
+ this.bindTrackingToElement(para);
10707
+ }
10708
+ }
10709
+
10710
+ // Endnotes
10711
+ for (const endnote of this.endnoteManager.getAllEndnotes()) {
10712
+ for (const para of endnote.getParagraphs()) {
10713
+ this.bindTrackingToElement(para);
10714
+ }
10715
+ }
10716
+
10717
+ // Comments
10718
+ for (const comment of this.commentManager.getAllCommentsWithReplies()) {
10719
+ for (const run of comment.getRuns()) {
10720
+ this.bindTrackingToElement(run);
10721
+ }
10722
+ }
10772
10723
  }
10773
10724
 
10774
10725
  /**
@@ -10823,7 +10774,7 @@ export class Document {
10823
10774
  setRsidRoot(rsidRoot: string): this {
10824
10775
  // Validate RSID format (8 hex characters)
10825
10776
  if (!/^[0-9A-Fa-f]{8}$/.test(rsidRoot)) {
10826
- throw new Error("RSID must be an 8-character hexadecimal value");
10777
+ throw new Error('RSID must be an 8-character hexadecimal value');
10827
10778
  }
10828
10779
  this.rsidRoot = rsidRoot.toUpperCase();
10829
10780
  this.rsids.add(this.rsidRoot);
@@ -10839,7 +10790,7 @@ export class Document {
10839
10790
  addRsid(rsid: string): this {
10840
10791
  // Validate RSID format
10841
10792
  if (!/^[0-9A-Fa-f]{8}$/.test(rsid)) {
10842
- throw new Error("RSID must be an 8-character hexadecimal value");
10793
+ throw new Error('RSID must be an 8-character hexadecimal value');
10843
10794
  }
10844
10795
  this.rsids.add(rsid.toUpperCase());
10845
10796
  this._settingsModified = true;
@@ -10854,7 +10805,7 @@ export class Document {
10854
10805
  const rsid = Math.floor(Math.random() * 0xffffffff)
10855
10806
  .toString(16)
10856
10807
  .toUpperCase()
10857
- .padStart(8, "0");
10808
+ .padStart(8, '0');
10858
10809
  this.rsids.add(rsid);
10859
10810
  this._settingsModified = true;
10860
10811
  return rsid;
@@ -10881,7 +10832,7 @@ export class Document {
10881
10832
  * @param protection - Document protection settings
10882
10833
  */
10883
10834
  protectDocument(protection: {
10884
- edit: "readOnly" | "comments" | "trackedChanges" | "forms";
10835
+ edit: 'readOnly' | 'comments' | 'trackedChanges' | 'forms';
10885
10836
  enforcement?: boolean;
10886
10837
  password?: string;
10887
10838
  cryptProviderType?: string;
@@ -10904,17 +10855,11 @@ export class Document {
10904
10855
  // If password provided, generate hash and salt
10905
10856
  if (protection.password) {
10906
10857
  // 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");
10858
+ const crypto = require('crypto');
10859
+ const salt = crypto.randomBytes(16).toString('base64');
10909
10860
  const hash = crypto
10910
- .pbkdf2Sync(
10911
- protection.password,
10912
- salt,
10913
- protection.cryptSpinCount || 100000,
10914
- 32,
10915
- "sha512"
10916
- )
10917
- .toString("base64");
10861
+ .pbkdf2Sync(protection.password, salt, protection.cryptSpinCount || 100000, 32, 'sha512')
10862
+ .toString('base64');
10918
10863
 
10919
10864
  this.documentProtection.hash = hash;
10920
10865
  this.documentProtection.salt = salt;
@@ -10967,7 +10912,9 @@ export class Document {
10967
10912
  * Gets the document background per ECMA-376 Part 1 §17.2.1
10968
10913
  * @returns Background properties or undefined
10969
10914
  */
10970
- getDocumentBackground(): { color?: string; themeColor?: string; themeTint?: string; themeShade?: string } | undefined {
10915
+ getDocumentBackground():
10916
+ | { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
10917
+ | undefined {
10971
10918
  return this._documentBackground ? { ...this._documentBackground } : undefined;
10972
10919
  }
10973
10920
 
@@ -10975,7 +10922,11 @@ export class Document {
10975
10922
  * Sets the document background per ECMA-376 Part 1 §17.2.1
10976
10923
  * @param background - Background properties (color, themeColor, etc.) or undefined to remove
10977
10924
  */
10978
- setDocumentBackground(background: { color?: string; themeColor?: string; themeTint?: string; themeShade?: string } | undefined): void {
10925
+ setDocumentBackground(
10926
+ background:
10927
+ | { color?: string; themeColor?: string; themeTint?: string; themeShade?: string }
10928
+ | undefined
10929
+ ): void {
10979
10930
  this._documentBackground = background ? { ...background } : undefined;
10980
10931
  }
10981
10932
 
@@ -11196,12 +11147,14 @@ export class Document {
11196
11147
  * @returns CompatibilityInfo with all parsed settings
11197
11148
  */
11198
11149
  getCompatibilityInfo(): CompatibilityInfo {
11199
- return this._compatInfo ?? {
11200
- mode: CompatibilityMode.Word2007,
11201
- isLegacyMode: true,
11202
- compatSettings: [],
11203
- legacyFlags: [],
11204
- };
11150
+ return (
11151
+ this._compatInfo ?? {
11152
+ mode: CompatibilityMode.Word2007,
11153
+ isLegacyMode: true,
11154
+ compatSettings: [],
11155
+ legacyFlags: [],
11156
+ }
11157
+ );
11205
11158
  }
11206
11159
 
11207
11160
  /**
@@ -11281,7 +11234,7 @@ export class Document {
11281
11234
  previousMode: currentMode,
11282
11235
  newMode: 15,
11283
11236
  removedFlags: [],
11284
- addedSettings: MODERN_COMPAT_SETTINGS.map(s => s.name),
11237
+ addedSettings: MODERN_COMPAT_SETTINGS.map((s) => s.name),
11285
11238
  namespacesExpanded: nsResult.expanded,
11286
11239
  changed: currentMode !== CompatibilityMode.Word2013Plus,
11287
11240
  };
@@ -11301,12 +11254,7 @@ export class Document {
11301
11254
  previousProperties: Record<string, any>,
11302
11255
  date?: Date
11303
11256
  ): Revision {
11304
- const revision = Revision.createRunPropertiesChange(
11305
- author,
11306
- content,
11307
- previousProperties,
11308
- date
11309
- );
11257
+ const revision = Revision.createRunPropertiesChange(author, content, previousProperties, date);
11310
11258
  return this.revisionManager.register(revision);
11311
11259
  }
11312
11260
 
@@ -11364,12 +11312,7 @@ export class Document {
11364
11312
  * @param date - Optional date (defaults to now)
11365
11313
  * @returns The created and registered revision
11366
11314
  */
11367
- createMoveFrom(
11368
- author: string,
11369
- content: Run | Run[],
11370
- moveId: string,
11371
- date?: Date
11372
- ): Revision {
11315
+ createMoveFrom(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
11373
11316
  const revision = Revision.createMoveFrom(author, content, moveId, date);
11374
11317
  return this.revisionManager.register(revision);
11375
11318
  }
@@ -11382,12 +11325,7 @@ export class Document {
11382
11325
  * @param date - Optional date (defaults to now)
11383
11326
  * @returns The created and registered revision
11384
11327
  */
11385
- createMoveTo(
11386
- author: string,
11387
- content: Run | Run[],
11388
- moveId: string,
11389
- date?: Date
11390
- ): Revision {
11328
+ createMoveTo(author: string, content: Run | Run[], moveId: string, date?: Date): Revision {
11391
11329
  const revision = Revision.createMoveTo(author, content, moveId, date);
11392
11330
  return this.revisionManager.register(revision);
11393
11331
  }
@@ -11413,9 +11351,7 @@ export class Document {
11413
11351
  moveToRangeEnd: RangeMarker;
11414
11352
  } {
11415
11353
  // Generate unique move ID and name
11416
- const moveId = `move${Date.now()}_${Math.random()
11417
- .toString(36)
11418
- .substr(2, 9)}`;
11354
+ const moveId = `move${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
11419
11355
  const moveName = `move${Date.now()}`;
11420
11356
 
11421
11357
  // Get unique IDs for range markers (use revision manager's next ID)
@@ -11431,12 +11367,7 @@ export class Document {
11431
11367
  const moveFromRangeEnd = RangeMarker.createMoveFromEnd(rangeIdStart);
11432
11368
 
11433
11369
  // Create range markers for moveTo
11434
- const moveToRangeStart = RangeMarker.createMoveToStart(
11435
- rangeIdStart,
11436
- moveName,
11437
- author,
11438
- date
11439
- );
11370
+ const moveToRangeStart = RangeMarker.createMoveToStart(rangeIdStart, moveName, author, date);
11440
11371
  const moveToRangeEnd = RangeMarker.createMoveToEnd(rangeIdStart);
11441
11372
 
11442
11373
  // Create the actual move revisions
@@ -11461,11 +11392,7 @@ export class Document {
11461
11392
  * @param date - Optional date (defaults to now)
11462
11393
  * @returns The created and registered revision
11463
11394
  */
11464
- createTableCellInsert(
11465
- author: string,
11466
- content: Run | Run[],
11467
- date?: Date
11468
- ): Revision {
11395
+ createTableCellInsert(author: string, content: Run | Run[], date?: Date): Revision {
11469
11396
  const revision = Revision.createTableCellInsert(author, content, date);
11470
11397
  return this.revisionManager.register(revision);
11471
11398
  }
@@ -11477,11 +11404,7 @@ export class Document {
11477
11404
  * @param date - Optional date (defaults to now)
11478
11405
  * @returns The created and registered revision
11479
11406
  */
11480
- createTableCellDelete(
11481
- author: string,
11482
- content: Run | Run[],
11483
- date?: Date
11484
- ): Revision {
11407
+ createTableCellDelete(author: string, content: Run | Run[], date?: Date): Revision {
11485
11408
  const revision = Revision.createTableCellDelete(author, content, date);
11486
11409
  return this.revisionManager.register(revision);
11487
11410
  }
@@ -11493,11 +11416,7 @@ export class Document {
11493
11416
  * @param date - Optional date (defaults to now)
11494
11417
  * @returns The created and registered revision
11495
11418
  */
11496
- createTableCellMerge(
11497
- author: string,
11498
- content: Run | Run[],
11499
- date?: Date
11500
- ): Revision {
11419
+ createTableCellMerge(author: string, content: Run | Run[], date?: Date): Revision {
11501
11420
  const revision = Revision.createTableCellMerge(author, content, date);
11502
11421
  return this.revisionManager.register(revision);
11503
11422
  }
@@ -11516,12 +11435,7 @@ export class Document {
11516
11435
  previousProperties: Record<string, any>,
11517
11436
  date?: Date
11518
11437
  ): Revision {
11519
- const revision = Revision.createNumberingChange(
11520
- author,
11521
- content,
11522
- previousProperties,
11523
- date
11524
- );
11438
+ const revision = Revision.createNumberingChange(author, content, previousProperties, date);
11525
11439
  return this.revisionManager.register(revision);
11526
11440
  }
11527
11441
 
@@ -11575,11 +11489,7 @@ export class Document {
11575
11489
  * const comment = doc.createComment('Alice', run, 'A');
11576
11490
  * ```
11577
11491
  */
11578
- createComment(
11579
- author: string,
11580
- content: string | Run | Run[],
11581
- initials?: string
11582
- ): Comment {
11492
+ createComment(author: string, content: string | Run | Run[], initials?: string): Comment {
11583
11493
  this._commentsModified = true;
11584
11494
  return this.commentManager.createComment(author, content, initials);
11585
11495
  }
@@ -11599,12 +11509,7 @@ export class Document {
11599
11509
  initials?: string
11600
11510
  ): Comment {
11601
11511
  this._commentsModified = true;
11602
- return this.commentManager.createReply(
11603
- parentCommentId,
11604
- author,
11605
- content,
11606
- initials
11607
- );
11512
+ return this.commentManager.createReply(parentCommentId, author, content, initials);
11608
11513
  }
11609
11514
 
11610
11515
  /**
@@ -11676,7 +11581,7 @@ export class Document {
11676
11581
  initials?: string
11677
11582
  ): Comment {
11678
11583
  const comment =
11679
- typeof commentOrAuthor === "string"
11584
+ typeof commentOrAuthor === 'string'
11680
11585
  ? this.createComment(commentOrAuthor, content!, initials)
11681
11586
  : commentOrAuthor;
11682
11587
 
@@ -11719,9 +11624,7 @@ export class Document {
11719
11624
  * @param commentId - ID of the top-level comment
11720
11625
  * @returns Object with the comment and its replies, or undefined if not found
11721
11626
  */
11722
- getCommentThread(
11723
- commentId: number
11724
- ): { comment: Comment; replies: Comment[] } | undefined {
11627
+ getCommentThread(commentId: number): { comment: Comment; replies: Comment[] } | undefined {
11725
11628
  return this.commentManager.getCommentThread(commentId);
11726
11629
  }
11727
11630
 
@@ -12000,16 +11903,16 @@ export class Document {
12000
11903
 
12001
11904
  /**
12002
11905
  * Strips all tracked changes from the document
12003
- *
11906
+ *
12004
11907
  * 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
11908
+ * and cleans up related metadata. This effectively "accepts" all changes without using Word's
12006
11909
  * built-in Accept Changes feature.
12007
- *
11910
+ *
12008
11911
  * **IMPORTANT**: This operation:
12009
11912
  * 1. Modifies the raw XML in the ZIP package to remove all tracked changes
12010
11913
  * 2. Clears Revision objects from the in-memory object model to prevent re-serialization
12011
11914
  * 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
12012
- *
11915
+ *
12013
11916
  * What gets removed:
12014
11917
  * - All insertion markers (<w:ins>) - content is kept, wrapper removed
12015
11918
  * - All deletion markers (<w:del>) - entire element including content removed
@@ -12019,22 +11922,22 @@ export class Document {
12019
11922
  * - Revision authors from word/people.xml
12020
11923
  * - Track changes settings from word/settings.xml
12021
11924
  * - Revision count from docProps/core.xml
12022
- *
11925
+ *
12023
11926
  * @returns This document instance for method chaining
12024
- *
11927
+ *
12025
11928
  * @example
12026
11929
  * ```typescript
12027
11930
  * // Load document with tracked changes
12028
11931
  * const doc = await Document.load('document-with-revisions.docx');
12029
- *
11932
+ *
12030
11933
  * // Strip all tracked changes
12031
11934
  * await doc.stripTrackedChanges();
12032
- *
11935
+ *
12033
11936
  * // Now process the document as normal
12034
11937
  * doc.applyStyles();
12035
11938
  * await doc.save('cleaned.docx');
12036
11939
  * ```
12037
- *
11940
+ *
12038
11941
  * @example
12039
11942
  * ```typescript
12040
11943
  * // Check for tracked changes first
@@ -12045,7 +11948,7 @@ export class Document {
12045
11948
  * }
12046
11949
  * await doc.save('output.docx');
12047
11950
  * ```
12048
- *
11951
+ *
12049
11952
  * @deprecated Use {@link acceptAllRevisions} instead - this method will be removed in a future version
12050
11953
  */
12051
11954
  async stripTrackedChanges(): Promise<this> {
@@ -12055,7 +11958,7 @@ export class Document {
12055
11958
 
12056
11959
  /**
12057
11960
  * Accepts all tracked changes in the document
12058
- *
11961
+ *
12059
11962
  * Processes all revision markup following Microsoft's official OpenXML SDK approach:
12060
11963
  * - Insertions (<w:ins>): Keep the inserted content, remove wrapper tags
12061
11964
  * - Deletions (<w:del>): Remove entirely (content was deleted, so discard it)
@@ -12063,32 +11966,32 @@ export class Document {
12063
11966
  * - Move To (<w:moveTo>): Keep content, remove wrapper (destination of moved content)
12064
11967
  * - Property changes: Remove all tracking elements
12065
11968
  * - Range markers: Remove all boundary markers
12066
- *
11969
+ *
12067
11970
  * Also cleans up metadata:
12068
11971
  * - Revision authors from word/people.xml
12069
11972
  * - Track changes settings from word/settings.xml
12070
11973
  * - Revision count from docProps/core.xml
12071
- *
11974
+ *
12072
11975
  * **IMPORTANT**: This operation:
12073
11976
  * 1. Modifies the raw XML in the ZIP package
12074
11977
  * 2. Clears Revision objects from the in-memory object model
12075
11978
  * 3. Sets flag to prevent XML regeneration on save (preserves the cleaned XML)
12076
- *
11979
+ *
12077
11980
  * @returns This document instance for method chaining
12078
- *
11981
+ *
12079
11982
  * @example
12080
11983
  * ```typescript
12081
11984
  * // Load document with tracked changes
12082
11985
  * const doc = await Document.load('document-with-revisions.docx');
12083
- *
11986
+ *
12084
11987
  * // Accept all tracked changes
12085
11988
  * await doc.acceptAllRevisions();
12086
- *
11989
+ *
12087
11990
  * // Now process the document as normal
12088
11991
  * doc.applyStyles();
12089
11992
  * await doc.save('cleaned.docx');
12090
11993
  * ```
12091
- *
11994
+ *
12092
11995
  * @example
12093
11996
  * ```typescript
12094
11997
  * // Check for tracked changes first
@@ -12099,7 +12002,7 @@ export class Document {
12099
12002
  * }
12100
12003
  * await doc.save('output.docx');
12101
12004
  * ```
12102
- *
12005
+ *
12103
12006
  * @see https://learn.microsoft.com/en-us/office/open-xml/how-to-accept-all-revisions
12104
12007
  */
12105
12008
  async acceptAllRevisions(): Promise<this> {
@@ -12172,7 +12075,10 @@ export class Document {
12172
12075
  * await doc.save('output.docx');
12173
12076
  * ```
12174
12077
  */
12175
- consolidateAllRevisions(timeWindowMs = 1000): { paragraphsProcessed: number; revisionsConsolidated: number } {
12078
+ consolidateAllRevisions(timeWindowMs = 1000): {
12079
+ paragraphsProcessed: number;
12080
+ revisionsConsolidated: number;
12081
+ } {
12176
12082
  let paragraphsProcessed = 0;
12177
12083
  let totalConsolidated = 0;
12178
12084
 
@@ -12295,16 +12201,16 @@ export class Document {
12295
12201
  */
12296
12202
  private clearRevisionsFromAllParagraphs(): void {
12297
12203
  let clearedCount = 0;
12298
-
12204
+
12299
12205
  // Clear revisions from all paragraphs in the document
12300
12206
  for (const para of this.getAllParagraphs()) {
12301
12207
  const revisions = para.getRevisions();
12302
-
12208
+
12303
12209
  if (revisions.length > 0) {
12304
12210
  // Filter out all Revision objects from paragraph content
12305
12211
  const content = para.getContent();
12306
- const nonRevisionContent = content.filter(item => !(item instanceof Revision));
12307
-
12212
+ const nonRevisionContent = content.filter((item) => !(item instanceof Revision));
12213
+
12308
12214
  // Replace paragraph content with filtered version
12309
12215
  para.clearContent();
12310
12216
  for (const item of nonRevisionContent) {
@@ -12316,11 +12222,11 @@ export class Document {
12316
12222
  para.addField(item);
12317
12223
  }
12318
12224
  }
12319
-
12225
+
12320
12226
  clearedCount += revisions.length;
12321
12227
  }
12322
12228
  }
12323
-
12229
+
12324
12230
  if (clearedCount > 0) {
12325
12231
  this.logger.info(`Cleared ${clearedCount} Revision object(s) from in-memory document model`);
12326
12232
  }
@@ -12563,7 +12469,7 @@ export class Document {
12563
12469
  // ZipWriter stores all content as Buffer internally, but DocumentPart expects string for text
12564
12470
  let content: string | Buffer = file.content;
12565
12471
  if (!file.isBinary && Buffer.isBuffer(file.content)) {
12566
- content = file.content.toString("utf-8");
12472
+ content = file.content.toString('utf-8');
12567
12473
  }
12568
12474
 
12569
12475
  return {
@@ -12641,7 +12547,7 @@ export class Document {
12641
12547
  }
12642
12548
  }
12643
12549
  // Remove relationships targeting this part from _rels/.rels
12644
- const relsXml = this.zipHandler.getFileAsString("_rels/.rels");
12550
+ const relsXml = this.zipHandler.getFileAsString('_rels/.rels');
12645
12551
  if (relsXml) {
12646
12552
  const target = partName.replace(/^\//, '');
12647
12553
  const relPattern = new RegExp(
@@ -12650,7 +12556,7 @@ export class Document {
12650
12556
  );
12651
12557
  const cleaned = relsXml.replace(relPattern, '');
12652
12558
  if (cleaned !== relsXml) {
12653
- this.zipHandler.updateFile("_rels/.rels", cleaned);
12559
+ this.zipHandler.updateFile('_rels/.rels', cleaned);
12654
12560
  }
12655
12561
  }
12656
12562
  // Track removed parts to skip regeneration during save
@@ -12716,17 +12622,14 @@ export class Document {
12716
12622
  const contentTypes = new Map<string, string>();
12717
12623
 
12718
12624
  try {
12719
- const contentTypesXml = this.zipHandler.getFileAsString(
12720
- "[Content_Types].xml"
12721
- );
12625
+ const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
12722
12626
  if (!contentTypesXml) {
12723
12627
  return contentTypes;
12724
12628
  }
12725
12629
 
12726
12630
  // Parse content types XML
12727
12631
  // Match Default elements (by extension)
12728
- const defaultPattern =
12729
- /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
12632
+ const defaultPattern = /<Default\s+Extension="([^"]+)"\s+ContentType="([^"]+)"/g;
12730
12633
  let match;
12731
12634
  while ((match = defaultPattern.exec(contentTypesXml)) !== null) {
12732
12635
  if (match[1] && match[2]) {
@@ -12735,8 +12638,7 @@ export class Document {
12735
12638
  }
12736
12639
 
12737
12640
  // Match Override elements (by part name)
12738
- const overridePattern =
12739
- /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
12641
+ const overridePattern = /<Override\s+PartName="([^"]+)"\s+ContentType="([^"]+)"/g;
12740
12642
  while ((match = overridePattern.exec(contentTypesXml)) !== null) {
12741
12643
  if (match[1] && match[2]) {
12742
12644
  contentTypes.set(match[1], match[2]);
@@ -12783,13 +12685,13 @@ export class Document {
12783
12685
  }
12784
12686
 
12785
12687
  // If already a string, return as-is
12786
- if (typeof part.content === "string") {
12688
+ if (typeof part.content === 'string') {
12787
12689
  return part.content;
12788
12690
  }
12789
12691
 
12790
12692
  // If Buffer, decode as UTF-8 (standard for XML files)
12791
12693
  if (Buffer.isBuffer(part.content)) {
12792
- return part.content.toString("utf8");
12694
+ return part.content.toString('utf8');
12793
12695
  }
12794
12696
 
12795
12697
  return null;
@@ -12876,8 +12778,8 @@ export class Document {
12876
12778
  * ```
12877
12779
  */
12878
12780
  async setRawXml(partName: string, xmlContent: string): Promise<void> {
12879
- if (typeof xmlContent !== "string") {
12880
- throw new Error("XML content must be a string");
12781
+ if (typeof xmlContent !== 'string') {
12782
+ throw new Error('XML content must be a string');
12881
12783
  }
12882
12784
 
12883
12785
  // Use setPart to update the part (handles both string and binary detection)
@@ -12903,19 +12805,14 @@ export class Document {
12903
12805
  * await doc.addContentType('.json', 'application/json');
12904
12806
  * ```
12905
12807
  */
12906
- async addContentType(
12907
- partNameOrExtension: string,
12908
- contentType: string
12909
- ): Promise<boolean> {
12808
+ async addContentType(partNameOrExtension: string, contentType: string): Promise<boolean> {
12910
12809
  try {
12911
- let contentTypesXml = this.zipHandler.getFileAsString(
12912
- "[Content_Types].xml"
12913
- );
12810
+ let contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
12914
12811
  if (!contentTypesXml) {
12915
12812
  return false;
12916
12813
  }
12917
12814
 
12918
- const isExtension = partNameOrExtension.startsWith(".");
12815
+ const isExtension = partNameOrExtension.startsWith('.');
12919
12816
 
12920
12817
  if (isExtension) {
12921
12818
  // Add as Default element (for extensions)
@@ -12924,7 +12821,7 @@ export class Document {
12924
12821
  // Check if already exists
12925
12822
  const existingPattern = new RegExp(
12926
12823
  `<Default\\s+Extension="${extension}"\\s+ContentType="[^"]+"/?>`,
12927
- "g"
12824
+ 'g'
12928
12825
  );
12929
12826
  if (existingPattern.test(contentTypesXml)) {
12930
12827
  // Update existing
@@ -12935,13 +12832,13 @@ export class Document {
12935
12832
  } else {
12936
12833
  // Add new before closing tag
12937
12834
  contentTypesXml = contentTypesXml.replace(
12938
- "</Types>",
12835
+ '</Types>',
12939
12836
  ` <Default Extension="${extension}" ContentType="${contentType}"/>\n</Types>`
12940
12837
  );
12941
12838
  }
12942
12839
  } else {
12943
12840
  // Add as Override element (for specific parts)
12944
- const partName = partNameOrExtension.startsWith("/")
12841
+ const partName = partNameOrExtension.startsWith('/')
12945
12842
  ? partNameOrExtension
12946
12843
  : `/${partNameOrExtension}`;
12947
12844
 
@@ -12949,9 +12846,9 @@ export class Document {
12949
12846
  const existingPattern = new RegExp(
12950
12847
  `<Override\\s+PartName="${partName.replace(
12951
12848
  /[.*+?^${}()|[\]\\]/g,
12952
- "\\$&"
12849
+ '\\$&'
12953
12850
  )}"\\s+ContentType="[^"]+"/?>`,
12954
- "g"
12851
+ 'g'
12955
12852
  );
12956
12853
  if (existingPattern.test(contentTypesXml)) {
12957
12854
  // Update existing
@@ -12962,14 +12859,14 @@ export class Document {
12962
12859
  } else {
12963
12860
  // Add new before closing tag
12964
12861
  contentTypesXml = contentTypesXml.replace(
12965
- "</Types>",
12862
+ '</Types>',
12966
12863
  ` <Override PartName="${partName}" ContentType="${contentType}"/>\n</Types>`
12967
12864
  );
12968
12865
  }
12969
12866
  }
12970
12867
 
12971
12868
  // Update the content types file
12972
- this.zipHandler.updateFile("[Content_Types].xml", contentTypesXml);
12869
+ this.zipHandler.updateFile('[Content_Types].xml', contentTypesXml);
12973
12870
  return true;
12974
12871
  } catch (error: unknown) {
12975
12872
  return false;
@@ -12997,9 +12894,7 @@ export class Document {
12997
12894
 
12998
12895
  try {
12999
12896
  // Get all .rels files
13000
- const relsPaths = this.zipHandler
13001
- .getFilePaths()
13002
- .filter((path) => path.endsWith(".rels"));
12897
+ const relsPaths = this.zipHandler.getFilePaths().filter((path) => path.endsWith('.rels'));
13003
12898
 
13004
12899
  for (const relsPath of relsPaths) {
13005
12900
  const relsContent = this.zipHandler.getFileAsString(relsPath);
@@ -13014,22 +12909,16 @@ export class Document {
13014
12909
  const rels: ParsedRelationship[] = [];
13015
12910
 
13016
12911
  // Use XMLParser to extract all Relationship elements
13017
- const relationshipElements = XMLParser.extractElements(
13018
- relsContent,
13019
- "Relationship"
13020
- );
12912
+ const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
13021
12913
 
13022
12914
  for (const relElement of relationshipElements) {
13023
12915
  const rel: ParsedRelationship = {};
13024
12916
 
13025
12917
  // 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
- );
12918
+ const id = XMLParser.extractAttribute(relElement, 'Id');
12919
+ const type = XMLParser.extractAttribute(relElement, 'Type');
12920
+ const target = XMLParser.extractAttribute(relElement, 'Target');
12921
+ const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
13033
12922
 
13034
12923
  if (id) rel.id = id;
13035
12924
  if (type) rel.type = type;
@@ -13078,19 +12967,15 @@ export class Document {
13078
12967
  */
13079
12968
  async getRelationships(
13080
12969
  partName: string
13081
- ): Promise<
13082
- { id?: string; type?: string; target?: string; targetMode?: string }[]
13083
- > {
12970
+ ): Promise<{ id?: string; type?: string; target?: string; targetMode?: string }[]> {
13084
12971
  try {
13085
12972
  // Construct the .rels path from the part name
13086
12973
  // For 'word/document.xml' -> 'word/_rels/document.xml.rels'
13087
- const lastSlash = partName.lastIndexOf("/");
12974
+ const lastSlash = partName.lastIndexOf('/');
13088
12975
  const relsPath =
13089
12976
  lastSlash === -1
13090
12977
  ? `_rels/${partName}.rels`
13091
- : `${partName.substring(0, lastSlash)}/_rels/${partName.substring(
13092
- lastSlash + 1
13093
- )}.rels`;
12978
+ : `${partName.substring(0, lastSlash)}/_rels/${partName.substring(lastSlash + 1)}.rels`;
13094
12979
 
13095
12980
  const relsContent = this.zipHandler.getFileAsString(relsPath);
13096
12981
  if (!relsContent) {
@@ -13107,19 +12992,16 @@ export class Document {
13107
12992
  const relationships: ParsedRelationship[] = [];
13108
12993
 
13109
12994
  // Use XMLParser to extract all Relationship elements
13110
- const relationshipElements = XMLParser.extractElements(
13111
- relsContent,
13112
- "Relationship"
13113
- );
12995
+ const relationshipElements = XMLParser.extractElements(relsContent, 'Relationship');
13114
12996
 
13115
12997
  for (const relElement of relationshipElements) {
13116
12998
  const rel: ParsedRelationship = {};
13117
12999
 
13118
13000
  // 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");
13001
+ const id = XMLParser.extractAttribute(relElement, 'Id');
13002
+ const type = XMLParser.extractAttribute(relElement, 'Type');
13003
+ const target = XMLParser.extractAttribute(relElement, 'Target');
13004
+ const targetMode = XMLParser.extractAttribute(relElement, 'TargetMode');
13123
13005
 
13124
13006
  if (id) rel.id = id;
13125
13007
  if (type) rel.type = type;
@@ -13142,9 +13024,7 @@ export class Document {
13142
13024
  */
13143
13025
  private getContentTypeForPart(partName: string): string | undefined {
13144
13026
  try {
13145
- const contentTypesXml = this.zipHandler.getFileAsString(
13146
- "[Content_Types].xml"
13147
- );
13027
+ const contentTypesXml = this.zipHandler.getFileAsString('[Content_Types].xml');
13148
13028
  if (!contentTypesXml) {
13149
13029
  return undefined;
13150
13030
  }
@@ -13153,9 +13033,9 @@ export class Document {
13153
13033
  const overridePattern = new RegExp(
13154
13034
  `<Override\\s+PartName="${partName.replace(
13155
13035
  /[.*+?^${}()|[\]\\]/g,
13156
- "\\$&"
13036
+ '\\$&'
13157
13037
  )}"\\s+ContentType="([^"]+)"`,
13158
- "i"
13038
+ 'i'
13159
13039
  );
13160
13040
  const overrideMatch = contentTypesXml.match(overridePattern);
13161
13041
  if (overrideMatch) {
@@ -13163,13 +13043,11 @@ export class Document {
13163
13043
  }
13164
13044
 
13165
13045
  // Check for extension default
13166
- const ext = partName.substring(partName.lastIndexOf("."));
13046
+ const ext = partName.substring(partName.lastIndexOf('.'));
13167
13047
  if (ext) {
13168
13048
  const defaultPattern = new RegExp(
13169
- `<Default\\s+Extension="${ext.substring(
13170
- 1
13171
- )}"\\s+ContentType="([^"]+)"`,
13172
- "i"
13049
+ `<Default\\s+Extension="${ext.substring(1)}"\\s+ContentType="([^"]+)"`,
13050
+ 'i'
13173
13051
  );
13174
13052
  const defaultMatch = contentTypesXml.match(defaultPattern);
13175
13053
  if (defaultMatch) {
@@ -13261,8 +13139,8 @@ export class Document {
13261
13139
  if (wholeWord) {
13262
13140
  // Create word boundary regex
13263
13141
  const wordPattern = new RegExp(
13264
- `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
13265
- caseSensitive ? "g" : "gi"
13142
+ `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13143
+ caseSensitive ? 'g' : 'gi'
13266
13144
  );
13267
13145
  let match;
13268
13146
  while ((match = wordPattern.exec(runText)) !== null) {
@@ -13278,9 +13156,7 @@ export class Document {
13278
13156
  } else {
13279
13157
  // Simple substring search
13280
13158
  let startIndex = 0;
13281
- while (
13282
- (startIndex = compareText.indexOf(searchText, startIndex)) !== -1
13283
- ) {
13159
+ while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
13284
13160
  results.push({
13285
13161
  paragraph,
13286
13162
  paragraphIndex: pIndex,
@@ -13317,18 +13193,13 @@ export class Document {
13317
13193
  const run = runs[rIndex];
13318
13194
  if (!run) continue;
13319
13195
  const runText = run.getText();
13320
- const compareText = caseSensitive
13321
- ? runText
13322
- : runText.toLowerCase();
13196
+ const compareText = caseSensitive ? runText : runText.toLowerCase();
13323
13197
 
13324
13198
  if (wholeWord) {
13325
13199
  // Create word boundary regex
13326
13200
  const wordPattern = new RegExp(
13327
- `\\b${searchText.replace(
13328
- /[.*+?^${}()|[\]\\]/g,
13329
- "\\$&"
13330
- )}\\b`,
13331
- caseSensitive ? "g" : "gi"
13201
+ `\\b${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13202
+ caseSensitive ? 'g' : 'gi'
13332
13203
  );
13333
13204
  let match;
13334
13205
  while ((match = wordPattern.exec(runText)) !== null) {
@@ -13344,12 +13215,7 @@ export class Document {
13344
13215
  } else {
13345
13216
  // Simple substring search
13346
13217
  let startIndex = 0;
13347
- while (
13348
- (startIndex = compareText.indexOf(
13349
- searchText,
13350
- startIndex
13351
- )) !== -1
13352
- ) {
13218
+ while ((startIndex = compareText.indexOf(searchText, startIndex)) !== -1) {
13353
13219
  results.push({
13354
13220
  paragraph,
13355
13221
  paragraphIndex: -1, // Not in main body, in table
@@ -13392,12 +13258,9 @@ export class Document {
13392
13258
  * }
13393
13259
  * ```
13394
13260
  */
13395
- findParagraphsByText(
13396
- pattern: string | RegExp
13397
- ): { paragraph: Paragraph; matches: string[] }[] {
13261
+ findParagraphsByText(pattern: string | RegExp): { paragraph: Paragraph; matches: string[] }[] {
13398
13262
  const results: { paragraph: Paragraph; matches: string[] }[] = [];
13399
- const regex =
13400
- typeof pattern === "string" ? new RegExp(pattern, "gi") : pattern;
13263
+ const regex = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
13401
13264
 
13402
13265
  for (const paragraph of this.getAllParagraphs()) {
13403
13266
  const text = paragraph.getText();
@@ -13457,13 +13320,13 @@ export class Document {
13457
13320
  getRunsByColor(color: string): Run[] {
13458
13321
  const results: Run[] = [];
13459
13322
  // Normalize color - remove # and convert to uppercase
13460
- const normalizedColor = color.replace(/^#/, "").toUpperCase();
13323
+ const normalizedColor = color.replace(/^#/, '').toUpperCase();
13461
13324
 
13462
13325
  for (const paragraph of this.getAllParagraphs()) {
13463
13326
  for (const run of paragraph.getRuns()) {
13464
13327
  const formatting = run.getFormatting();
13465
13328
  if (formatting.color) {
13466
- const runColor = formatting.color.replace(/^#/, "").toUpperCase();
13329
+ const runColor = formatting.color.replace(/^#/, '').toUpperCase();
13467
13330
  if (runColor === normalizedColor) {
13468
13331
  results.push(run);
13469
13332
  }
@@ -13636,7 +13499,7 @@ export class Document {
13636
13499
  sizes.set(formatting.size, (sizes.get(formatting.size) || 0) + 1);
13637
13500
  }
13638
13501
  if (formatting.color) {
13639
- const normalizedColor = formatting.color.toUpperCase().replace(/^#/, "");
13502
+ const normalizedColor = formatting.color.toUpperCase().replace(/^#/, '');
13640
13503
  colors.set(normalizedColor, (colors.get(normalizedColor) || 0) + 1);
13641
13504
  }
13642
13505
  }
@@ -13736,8 +13599,8 @@ export class Document {
13736
13599
  if (wholeWord) {
13737
13600
  // Use word boundary regex for whole word replacement
13738
13601
  const wordPattern = new RegExp(
13739
- `\\b${find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
13740
- caseSensitive ? "g" : "gi"
13602
+ `\\b${find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`,
13603
+ caseSensitive ? 'g' : 'gi'
13741
13604
  );
13742
13605
  const matches = originalText.match(wordPattern);
13743
13606
  if (matches) {
@@ -13747,8 +13610,8 @@ export class Document {
13747
13610
  } else {
13748
13611
  // Simple substring replacement
13749
13612
  const searchPattern = new RegExp(
13750
- find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
13751
- caseSensitive ? "g" : "gi"
13613
+ find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
13614
+ caseSensitive ? 'g' : 'gi'
13752
13615
  );
13753
13616
  const matches = originalText.match(searchPattern);
13754
13617
  if (matches) {
@@ -13818,7 +13681,7 @@ export class Document {
13818
13681
  caseSensitive = false,
13819
13682
  wholeWord = false,
13820
13683
  trackChanges = false,
13821
- author = "Unknown",
13684
+ author = 'Unknown',
13822
13685
  } = options || {};
13823
13686
 
13824
13687
  let count = 0;
@@ -13826,17 +13689,15 @@ export class Document {
13826
13689
 
13827
13690
  // Convert pattern to RegExp if it's a string
13828
13691
  let regex: RegExp;
13829
- if (typeof pattern === "string") {
13692
+ if (typeof pattern === 'string') {
13830
13693
  // Escape special regex characters
13831
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13694
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13832
13695
  const boundaryPattern = wholeWord ? `\\b${escaped}\\b` : escaped;
13833
- const flags = caseSensitive ? "g" : "gi";
13696
+ const flags = caseSensitive ? 'g' : 'gi';
13834
13697
  regex = new RegExp(boundaryPattern, flags);
13835
13698
  } else {
13836
13699
  // Use provided RegExp, ensure global flag
13837
- const flags = pattern.flags.includes("g")
13838
- ? pattern.flags
13839
- : pattern.flags + "g";
13700
+ const flags = pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g';
13840
13701
  regex = new RegExp(pattern.source, flags);
13841
13702
  }
13842
13703
 
@@ -13864,15 +13725,23 @@ export class Document {
13864
13725
 
13865
13726
  if (useGranular) {
13866
13727
  for (const seg of segments) {
13867
- if (seg.type === "equal") {
13728
+ if (seg.type === 'equal') {
13868
13729
  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);
13730
+ } else if (seg.type === 'delete') {
13731
+ const delRev = Revision.createDeletion(
13732
+ author,
13733
+ new Run(seg.text, formatting),
13734
+ now
13735
+ );
13871
13736
  this.revisionManager.register(delRev);
13872
13737
  revisions.push(delRev);
13873
13738
  newContent.push(delRev);
13874
- } else if (seg.type === "insert") {
13875
- const insRev = Revision.createInsertion(author, new Run(seg.text, formatting), now);
13739
+ } else if (seg.type === 'insert') {
13740
+ const insRev = Revision.createInsertion(
13741
+ author,
13742
+ new Run(seg.text, formatting),
13743
+ now
13744
+ );
13876
13745
  this.revisionManager.register(insRev);
13877
13746
  revisions.push(insRev);
13878
13747
  newContent.push(insRev);
@@ -13880,7 +13749,11 @@ export class Document {
13880
13749
  }
13881
13750
  } else {
13882
13751
  // Whole-run delete + insert
13883
- const deletion = Revision.createDeletion(author, new Run(originalText, formatting), now);
13752
+ const deletion = Revision.createDeletion(
13753
+ author,
13754
+ new Run(originalText, formatting),
13755
+ now
13756
+ );
13884
13757
  this.revisionManager.register(deletion);
13885
13758
  revisions.push(deletion);
13886
13759
  newContent.push(deletion);
@@ -14006,7 +13879,7 @@ export class Document {
14006
13879
  if (includeSpaces) {
14007
13880
  totalChars += text.length;
14008
13881
  } else {
14009
- totalChars += text.replace(/\s/g, "").length;
13882
+ totalChars += text.replace(/\s/g, '').length;
14010
13883
  }
14011
13884
  }
14012
13885
 
@@ -14029,7 +13902,7 @@ export class Document {
14029
13902
  if (includeSpaces) {
14030
13903
  totalChars += text.length;
14031
13904
  } else {
14032
- totalChars += text.replace(/\s/g, "").length;
13905
+ totalChars += text.replace(/\s/g, '').length;
14033
13906
  }
14034
13907
  }
14035
13908
  }
@@ -14047,7 +13920,7 @@ export class Document {
14047
13920
  removeParagraph(paragraphOrIndex: Paragraph | number): boolean {
14048
13921
  let index: number;
14049
13922
 
14050
- if (typeof paragraphOrIndex === "number") {
13923
+ if (typeof paragraphOrIndex === 'number') {
14051
13924
  index = paragraphOrIndex;
14052
13925
  } else {
14053
13926
  // Find the index of the paragraph
@@ -14084,7 +13957,7 @@ export class Document {
14084
13957
  removeTable(tableOrIndex: Table | number): boolean {
14085
13958
  let index: number;
14086
13959
 
14087
- if (typeof tableOrIndex === "number") {
13960
+ if (typeof tableOrIndex === 'number') {
14088
13961
  // If number provided, find the nth table
14089
13962
  const tables = this.getTables();
14090
13963
  if (tableOrIndex >= 0 && tableOrIndex < tables.length) {
@@ -14118,9 +13991,7 @@ export class Document {
14118
13991
  private validateParagraph(paragraph: Paragraph): void {
14119
13992
  // Type validation
14120
13993
  if (!(paragraph instanceof Paragraph)) {
14121
- throw new Error(
14122
- "insertParagraphAt: parameter must be a Paragraph instance"
14123
- );
13994
+ throw new Error('insertParagraphAt: parameter must be a Paragraph instance');
14124
13995
  }
14125
13996
 
14126
13997
  // Check for duplicate paragraph IDs
@@ -14163,19 +14034,19 @@ export class Document {
14163
14034
  private validateTable(table: Table): void {
14164
14035
  // Type validation
14165
14036
  if (!(table instanceof Table)) {
14166
- throw new Error("insertTableAt: parameter must be a Table instance");
14037
+ throw new Error('insertTableAt: parameter must be a Table instance');
14167
14038
  }
14168
14039
 
14169
14040
  // Content validation - table must have rows
14170
14041
  const rows = table.getRows();
14171
14042
  if (rows.length === 0) {
14172
- throw new Error("insertTableAt: table must have at least one row");
14043
+ throw new Error('insertTableAt: table must have at least one row');
14173
14044
  }
14174
14045
 
14175
14046
  // Check first row has cells (rows.length > 0 already checked above)
14176
14047
  const firstRow = rows[0];
14177
14048
  if (firstRow?.getCells().length === 0) {
14178
- throw new Error("insertTableAt: table rows must have at least one cell");
14049
+ throw new Error('insertTableAt: table rows must have at least one cell');
14179
14050
  }
14180
14051
 
14181
14052
  // Warn about missing table styles
@@ -14195,27 +14066,25 @@ export class Document {
14195
14066
  private validateToc(toc: TableOfContentsElement): void {
14196
14067
  // Type validation
14197
14068
  if (!(toc instanceof TableOfContentsElement)) {
14198
- throw new Error(
14199
- "insertTocAt: parameter must be a TableOfContentsElement instance"
14200
- );
14069
+ throw new Error('insertTocAt: parameter must be a TableOfContentsElement instance');
14201
14070
  }
14202
14071
 
14203
14072
  // Check if document has heading styles for TOC to reference
14204
14073
  const hasHeadings = [
14205
- "Heading1",
14206
- "Heading2",
14207
- "Heading3",
14208
- "Heading4",
14209
- "Heading5",
14210
- "Heading6",
14211
- "Heading7",
14212
- "Heading8",
14213
- "Heading9",
14074
+ 'Heading1',
14075
+ 'Heading2',
14076
+ 'Heading3',
14077
+ 'Heading4',
14078
+ 'Heading5',
14079
+ 'Heading6',
14080
+ 'Heading7',
14081
+ 'Heading8',
14082
+ 'Heading9',
14214
14083
  ].some((style) => this.stylesManager.hasStyle(style));
14215
14084
 
14216
14085
  if (!hasHeadings) {
14217
14086
  defaultLogger.warn(
14218
- "No heading styles found in document. Table of Contents may not display entries correctly."
14087
+ 'No heading styles found in document. Table of Contents may not display entries correctly.'
14219
14088
  );
14220
14089
  }
14221
14090
  }
@@ -14544,8 +14413,7 @@ export class Document {
14544
14413
  * ```
14545
14414
  */
14546
14415
  getHyperlinks(): { hyperlink: Hyperlink; paragraph: Paragraph }[] {
14547
- const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] =
14548
- [];
14416
+ const hyperlinks: { hyperlink: Hyperlink; paragraph: Paragraph }[] = [];
14549
14417
 
14550
14418
  // Helper function to extract hyperlinks from paragraph content,
14551
14419
  // including those inside Revision elements (w:ins, w:del, etc.)
@@ -14573,8 +14441,7 @@ export class Document {
14573
14441
  for (const row of table.getRows()) {
14574
14442
  for (const cell of row.getCells()) {
14575
14443
  // TableCell has getParagraphs method
14576
- const cellParagraphs =
14577
- cell instanceof TableCell ? cell.getParagraphs() : [];
14444
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14578
14445
  for (const para of cellParagraphs) {
14579
14446
  extractHyperlinksFromParagraph(para);
14580
14447
  }
@@ -14611,7 +14478,9 @@ export class Document {
14611
14478
  }
14612
14479
  };
14613
14480
 
14614
- const scanElement = (element: BodyElement | Paragraph | Table | StructuredDocumentTag): void => {
14481
+ const scanElement = (
14482
+ element: BodyElement | Paragraph | Table | StructuredDocumentTag
14483
+ ): void => {
14615
14484
  if (element instanceof Paragraph) {
14616
14485
  scanParagraph(element);
14617
14486
  } else if (element instanceof Table) {
@@ -14697,8 +14566,7 @@ export class Document {
14697
14566
  resetFormatting?: boolean;
14698
14567
  cleanupRelationships?: boolean;
14699
14568
  }): number {
14700
- const { resetFormatting = false, cleanupRelationships = false } =
14701
- options || {};
14569
+ const { resetFormatting = false, cleanupRelationships = false } = options || {};
14702
14570
 
14703
14571
  // Guard: Skip when track changes is enabled - prevents field structure corruption
14704
14572
  // The mergeConsecutiveHyperlinks() method uses clearContent() + addHyperlink()
@@ -14707,7 +14575,7 @@ export class Document {
14707
14575
  if (this.trackChangesEnabled) {
14708
14576
  defaultLogger.warn(
14709
14577
  'defragmentHyperlinks skipped: track changes is enabled. ' +
14710
- 'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
14578
+ 'Call defragmentHyperlinks before enableTrackChanges() to avoid field corruption.'
14711
14579
  );
14712
14580
  return 0;
14713
14581
  }
@@ -14736,8 +14604,7 @@ export class Document {
14736
14604
  for (const table of this.getTables()) {
14737
14605
  for (const row of table.getRows()) {
14738
14606
  for (const cell of row.getCells()) {
14739
- const cellParagraphs =
14740
- cell instanceof TableCell ? cell.getParagraphs() : [];
14607
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14741
14608
  for (const para of cellParagraphs) {
14742
14609
  const originalContent = para.getContent();
14743
14610
 
@@ -14759,12 +14626,9 @@ export class Document {
14759
14626
  const referencedIds = this.collectAllReferencedHyperlinkIds();
14760
14627
 
14761
14628
  // Remove orphaned hyperlink relationships
14762
- const removedCount =
14763
- this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
14629
+ const removedCount = this.relationshipManager.removeOrphanedHyperlinks(referencedIds);
14764
14630
  if (removedCount > 0) {
14765
- defaultLogger.info(
14766
- `Cleaned up ${removedCount} orphaned hyperlink relationship(s)`
14767
- );
14631
+ defaultLogger.info(`Cleaned up ${removedCount} orphaned hyperlink relationship(s)`);
14768
14632
  }
14769
14633
  }
14770
14634
 
@@ -14848,8 +14712,7 @@ export class Document {
14848
14712
  for (const table of this.getTables()) {
14849
14713
  for (const row of table.getRows()) {
14850
14714
  for (const cell of row.getCells()) {
14851
- const cellParagraphs =
14852
- cell instanceof TableCell ? cell.getParagraphs() : [];
14715
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
14853
14716
  for (const para of cellParagraphs) {
14854
14717
  for (const field of para.getFields()) {
14855
14718
  results.push({ field, paragraph: para, table });
@@ -14910,7 +14773,10 @@ export class Document {
14910
14773
  await this.imageManager.loadAllImageData();
14911
14774
 
14912
14775
  // 2. Group images by filename (avoid processing same file twice)
14913
- const imagesByFilename = new Map<string, { image: Image; relationshipId: string; filename: string }[]>();
14776
+ const imagesByFilename = new Map<
14777
+ string,
14778
+ { image: Image; relationshipId: string; filename: string }[]
14779
+ >();
14914
14780
  for (const entry of this.imageManager.getAllImages()) {
14915
14781
  const group = imagesByFilename.get(entry.filename) || [];
14916
14782
  group.push(entry);
@@ -14968,7 +14834,11 @@ export class Document {
14968
14834
  * Handles both document body and header/footer relationships.
14969
14835
  * @private
14970
14836
  */
14971
- private updateImageRelationshipTarget(relId: string, oldFilename: string, newFilename: string): void {
14837
+ private updateImageRelationshipTarget(
14838
+ relId: string,
14839
+ oldFilename: string,
14840
+ newFilename: string
14841
+ ): void {
14972
14842
  // Try document body relationship manager first
14973
14843
  const rel = this.relationshipManager.getRelationship(relId);
14974
14844
  if (rel) {
@@ -15029,8 +14899,7 @@ export class Document {
15029
14899
  for (const table of this.getTables()) {
15030
14900
  for (const row of table.getRows()) {
15031
14901
  for (const cell of row.getCells()) {
15032
- const cellParagraphs =
15033
- cell instanceof TableCell ? cell.getParagraphs() : [];
14902
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
15034
14903
  for (const para of cellParagraphs) {
15035
14904
  runs.push(...para.getRuns());
15036
14905
  }
@@ -15070,9 +14939,10 @@ export class Document {
15070
14939
  * });
15071
14940
  * ```
15072
14941
  */
15073
- hyperlinkEmails(options?: {
15074
- formatting?: RunFormatting;
15075
- }): { emailsLinked: number; paragraphsModified: number } {
14942
+ hyperlinkEmails(options?: { formatting?: RunFormatting }): {
14943
+ emailsLinked: number;
14944
+ paragraphsModified: number;
14945
+ } {
15076
14946
  // Default formatting: Verdana 12pt, Underline, no bold, #0000FF
15077
14947
  const defaultFormatting: RunFormatting = {
15078
14948
  font: 'Verdana',
@@ -15189,23 +15059,23 @@ export class Document {
15189
15059
  */
15190
15060
  removeFormattingFromAll(
15191
15061
  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"
15062
+ | 'bold'
15063
+ | 'italic'
15064
+ | 'underline'
15065
+ | 'strike'
15066
+ | 'dstrike'
15067
+ | 'highlight'
15068
+ | 'color'
15069
+ | 'font'
15070
+ | 'size'
15071
+ | 'subscript'
15072
+ | 'superscript'
15073
+ | 'smallCaps'
15074
+ | 'allCaps'
15075
+ | 'outline'
15076
+ | 'shadow'
15077
+ | 'emboss'
15078
+ | 'imprint'
15209
15079
  ): number {
15210
15080
  let modifiedCount = 0;
15211
15081
 
@@ -15274,9 +15144,7 @@ export class Document {
15274
15144
  * });
15275
15145
  * ```
15276
15146
  */
15277
- updateAllHyperlinks(
15278
- formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void
15279
- ): number {
15147
+ updateAllHyperlinks(formatter: (hyperlink: Hyperlink, paragraph: Paragraph) => void): number {
15280
15148
  // Get all hyperlinks with their containing paragraphs
15281
15149
  const hyperlinks = this.getHyperlinks();
15282
15150
 
@@ -15349,7 +15217,7 @@ export class Document {
15349
15217
 
15350
15218
  this.bodyElements.forEach((element, index) => {
15351
15219
  if (element instanceof Paragraph) {
15352
- const isEmpty = element.getText().trim() === "";
15220
+ const isEmpty = element.getText().trim() === '';
15353
15221
  if (isEmpty && lastWasEmpty) {
15354
15222
  toRemove.push(index);
15355
15223
  }
@@ -15380,7 +15248,7 @@ export class Document {
15380
15248
  }
15381
15249
 
15382
15250
  if (standardLineSpacing !== undefined) {
15383
- para.setLineSpacing(standardLineSpacing, "auto");
15251
+ para.setLineSpacing(standardLineSpacing, 'auto');
15384
15252
  normalized++;
15385
15253
  }
15386
15254
 
@@ -15405,8 +15273,7 @@ export class Document {
15405
15273
  for (const table of this.getTables()) {
15406
15274
  for (const row of table.getRows()) {
15407
15275
  for (const cell of row.getCells()) {
15408
- const cellParagraphs =
15409
- cell instanceof TableCell ? cell.getParagraphs() : [];
15276
+ const cellParagraphs = cell instanceof TableCell ? cell.getParagraphs() : [];
15410
15277
  for (const para of cellParagraphs) {
15411
15278
  if (standardParagraphSpacing) {
15412
15279
  if (standardParagraphSpacing.before !== undefined) {
@@ -15420,7 +15287,7 @@ export class Document {
15420
15287
  }
15421
15288
 
15422
15289
  if (standardLineSpacing !== undefined) {
15423
- para.setLineSpacing(standardLineSpacing, "auto");
15290
+ para.setLineSpacing(standardLineSpacing, 'auto');
15424
15291
  normalized++;
15425
15292
  }
15426
15293
 
@@ -15509,13 +15376,13 @@ export class Document {
15509
15376
 
15510
15377
  /**
15511
15378
  * Rebuilds all Table of Contents in the document
15512
- *
15379
+ *
15513
15380
  * Analyzes each TOC in the document, parses its field instructions to determine
15514
15381
  * which heading levels to include, searches for matching headings (including those
15515
15382
  * in nested tables), and returns a summary of TOC instructions and heading counts.
15516
- *
15383
+ *
15517
15384
  * **NEW: This method now also populates the TOCs with hyperlinked entries automatically!**
15518
- *
15385
+ *
15519
15386
  * The method:
15520
15387
  * 1. Removes SDT wrappers around tables if found (uses clearCustom helper)
15521
15388
  * 2. Ensures `_top` bookmark exists at document start for TOC linking
@@ -15528,7 +15395,7 @@ export class Document {
15528
15395
  * 9. **Updates document.xml with the populated TOC**
15529
15396
  * 10. Retains field instructions so TOCs can be manually updated later
15530
15397
  * 11. Returns summary: [instruction, [h1Count, h2Count, h3Count, ...]]
15531
- *
15398
+ *
15532
15399
  * **Key Features:**
15533
15400
  * - No arguments required - analyzes the current document state
15534
15401
  * - Searches nested tables when counting headings
@@ -15539,19 +15406,19 @@ export class Document {
15539
15406
  * - **Field instructions preserved for manual updates**
15540
15407
  * - **No page numbers displayed (pure hyperlink navigation)**
15541
15408
  * - Returns summary data for diagnostics and verification
15542
- *
15409
+ *
15543
15410
  * **Output Format:**
15544
15411
  * Returns a 2D array where each row contains:
15545
15412
  * - Index 0: The TOC field instruction text (e.g., "TOC \\o \"1-3\"")
15546
15413
  * - Index 1: Array of heading counts by level (e.g., [5, 12, 8] = 5 H1s, 12 H2s, 8 H3s)
15547
- *
15414
+ *
15548
15415
  * @returns Two-dimensional array of [instruction, headingCounts[]] for each TOC
15549
- *
15416
+ *
15550
15417
  * @example
15551
15418
  * ```typescript
15552
15419
  * const doc = await Document.load('document.docx');
15553
15420
  * const tocInfo = doc.rebuildTOCs();
15554
- *
15421
+ *
15555
15422
  * console.log(`Found ${tocInfo.length} Table(s) of Contents`);
15556
15423
  * for (const [instruction, counts] of tocInfo) {
15557
15424
  * console.log(`TOC Instruction: ${instruction}`);
@@ -15561,59 +15428,59 @@ export class Document {
15561
15428
  * }
15562
15429
  * });
15563
15430
  * }
15564
- *
15431
+ *
15565
15432
  * // TOCs are now populated with hyperlinks - save the document
15566
15433
  * await doc.save('output.docx');
15567
15434
  * // When opened in Word, TOCs will display with clickable links, no manual update needed
15568
15435
  * ```
15569
- *
15436
+ *
15570
15437
  * @example
15571
15438
  * ```typescript
15572
15439
  * // Rebuild TOCs and save with populated entries
15573
15440
  * const doc = await Document.load('input.docx');
15574
15441
  * const tocSummary = doc.rebuildTOCs();
15575
15442
  * await doc.save('output.docx');
15576
- *
15443
+ *
15577
15444
  * console.log(`Processed ${tocSummary.length} TOCs with hyperlinked entries`);
15578
15445
  * ```
15579
15446
  */
15580
15447
  public rebuildTOCs(): [string, number[]][] {
15581
15448
  const results: [string, number[]][] = [];
15582
-
15449
+
15583
15450
  // Step 1: Remove SDT wrappers around tables if found (helper already exists)
15584
15451
  this.clearCustom();
15585
-
15452
+
15586
15453
  // Step 2: Ensure _top bookmark exists at document start
15587
15454
  this.addTopBookmark();
15588
-
15455
+
15589
15456
  // Step 3: Get document.xml to scan for TOC elements
15590
15457
  const docXml = this.zipHandler.getFileAsString('word/document.xml');
15591
15458
  if (!docXml) {
15592
15459
  return results;
15593
15460
  }
15594
-
15461
+
15595
15462
  // Step 4: Find all TOC SDT elements
15596
15463
  const tocRegex = /<w:sdt>[\s\S]*?<w:docPartGallery w:val="Table of Contents"[\s\S]*?<\/w:sdt>/g;
15597
15464
  const tocMatches = Array.from(docXml.matchAll(tocRegex));
15598
-
15465
+
15599
15466
  if (tocMatches.length === 0) {
15600
15467
  return results;
15601
15468
  }
15602
-
15469
+
15603
15470
  // Step 5: For each TOC, parse instructions and count headings
15604
15471
  for (const match of tocMatches) {
15605
15472
  try {
15606
15473
  const tocXml = match[0];
15607
-
15474
+
15608
15475
  // Extract field instruction
15609
15476
  const instrMatch = /<w:instrText[^>]*>([\s\S]*?)<\/w:instrText>/.exec(tocXml);
15610
15477
  if (!instrMatch?.[1]) {
15611
15478
  continue;
15612
15479
  }
15613
-
15480
+
15614
15481
  // TypeScript type narrowing: assign to const variable
15615
15482
  const instrText = instrMatch[1];
15616
-
15483
+
15617
15484
  // Decode XML entities
15618
15485
  const fieldInstruction = instrText
15619
15486
  .replace(/&/g, '&')
@@ -15621,23 +15488,23 @@ export class Document {
15621
15488
  .replace(/>/g, '>')
15622
15489
  .replace(/"/g, '"')
15623
15490
  .replace(/'/g, "'");
15624
-
15491
+
15625
15492
  // Parse the instruction to get heading levels
15626
15493
  const levels = this.parseTOCFieldInstruction(fieldInstruction);
15627
-
15494
+
15628
15495
  // Find all headings in document (including nested tables)
15629
15496
  const headings = this.findHeadingsForTOCFromXML(docXml, levels);
15630
-
15497
+
15631
15498
  // Count headings by level (create array with counts for each level 1-9)
15632
15499
  const headingCounts: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Indices 0-8 for levels 1-9
15633
-
15500
+
15634
15501
  for (const heading of headings) {
15635
15502
  if (heading.level >= 1 && heading.level <= 9) {
15636
15503
  const index = heading.level - 1;
15637
15504
  headingCounts[index] = (headingCounts[index] || 0) + 1;
15638
15505
  }
15639
15506
  }
15640
-
15507
+
15641
15508
  // Add to results: [instruction, counts]
15642
15509
  results.push([fieldInstruction, headingCounts]);
15643
15510
  } catch (error: unknown) {
@@ -15651,20 +15518,18 @@ export class Document {
15651
15518
  continue;
15652
15519
  }
15653
15520
  }
15654
-
15521
+
15655
15522
  // Step 6: Populate all TOCs in the document with hyperlinked entries
15656
15523
  // This modifies the XML to include pre-populated TOC entries with hyperlinks
15657
15524
  const populatedXml = this.populateAllTOCsInXML(docXml);
15658
-
15525
+
15659
15526
  // Step 7: Update document.xml with the populated TOCs
15660
15527
  if (populatedXml !== docXml) {
15661
15528
  this.zipHandler.updateFile('word/document.xml', populatedXml);
15662
-
15663
- this.logger.info(
15664
- `Successfully populated ${results.length} TOC(s) with hyperlinked entries`
15665
- );
15529
+
15530
+ this.logger.info(`Successfully populated ${results.length} TOC(s) with hyperlinked entries`);
15666
15531
  }
15667
-
15532
+
15668
15533
  return results;
15669
15534
  }
15670
15535
 
@@ -15696,14 +15561,14 @@ export class Document {
15696
15561
  * ```
15697
15562
  */
15698
15563
  normalizeTableBorders(options?: {
15699
- style?: "single" | "double" | "dotted" | "dashed" | "thick" | "none";
15564
+ style?: 'single' | 'double' | 'dotted' | 'dashed' | 'thick' | 'none';
15700
15565
  size?: number;
15701
15566
  color?: string;
15702
15567
  }): number {
15703
15568
  const border: TableBorder = {
15704
- style: options?.style ?? "single",
15569
+ style: options?.style ?? 'single',
15705
15570
  size: options?.size ?? 4,
15706
- color: options?.color ?? "000000",
15571
+ color: options?.color ?? '000000',
15707
15572
  };
15708
15573
 
15709
15574
  return this.applyBordersToAllTables(border);
@@ -15754,8 +15619,8 @@ export class Document {
15754
15619
 
15755
15620
  // Create regex pattern
15756
15621
  const pattern = matchCase
15757
- ? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")
15758
- : new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
15622
+ ? new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
15623
+ : new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
15759
15624
 
15760
15625
  // Process body paragraphs
15761
15626
  for (const para of this.getAllParagraphs()) {
@@ -15839,36 +15704,36 @@ export class Document {
15839
15704
 
15840
15705
  // [Content_Types].xml - minimal
15841
15706
  zipHandler.addFile(
15842
- "[Content_Types].xml",
15707
+ '[Content_Types].xml',
15843
15708
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15844
15709
  '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
15845
15710
  ' <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n' +
15846
15711
  ' <Default Extension="xml" ContentType="application/xml"/>\n' +
15847
15712
  ' <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>\n' +
15848
- "</Types>"
15713
+ '</Types>'
15849
15714
  );
15850
15715
 
15851
15716
  // _rels/.rels - only reference parts that actually exist
15852
15717
  zipHandler.addFile(
15853
- "_rels/.rels",
15718
+ '_rels/.rels',
15854
15719
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15855
15720
  '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
15856
15721
  ' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>\n' +
15857
- "</Relationships>"
15722
+ '</Relationships>'
15858
15723
  );
15859
15724
 
15860
15725
  // word/document.xml - empty body
15861
15726
  zipHandler.addFile(
15862
- "word/document.xml",
15727
+ 'word/document.xml',
15863
15728
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15864
15729
  '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">\n' +
15865
- " <w:body/>\n" +
15866
- "</w:document>"
15730
+ ' <w:body/>\n' +
15731
+ '</w:document>'
15867
15732
  );
15868
15733
 
15869
15734
  // word/_rels/document.xml.rels - empty relationships
15870
15735
  zipHandler.addFile(
15871
- "word/_rels/document.xml.rels",
15736
+ 'word/_rels/document.xml.rels',
15872
15737
  '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
15873
15738
  '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>'
15874
15739
  );