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
@@ -1,683 +1,662 @@
1
- /**
2
- * SelectiveRevisionAcceptor - Accept or reject specific revisions based on criteria
3
- *
4
- * Provides granular control over revision acceptance using in-memory DOM transformation.
5
- * Extends the all-or-nothing approach with selective acceptance by author, type, date,
6
- * and custom criteria.
7
- *
8
- * Uses the industry-standard in-memory transformation approach (like OpenXML PowerTools),
9
- * allowing subsequent modifications to be saved correctly.
10
- *
11
- * @module SelectiveRevisionAcceptor
12
- * @see https://github.com/OfficeDev/Open-Xml-PowerTools - RevisionAccepter.cs
13
- */
14
-
15
- import type { Document } from '../core/Document';
16
- import type { Paragraph, ParagraphContent } from '../elements/Paragraph';
17
- import { Revision, RevisionType } from '../elements/Revision';
18
- import type { Run } from '../elements/Run';
19
- import type { Hyperlink } from '../elements/Hyperlink';
20
- import { isRunContent, isHyperlinkContent } from '../elements/RevisionContent';
21
- import { ChangeCategory } from './ChangelogGenerator';
22
- import { SelectionCriteria } from './RevisionAwareProcessor';
23
-
24
- /**
25
- * Result of selective revision acceptance.
26
- */
27
- export interface SelectiveAcceptResult {
28
- /** IDs of accepted revisions */
29
- accepted: string[];
30
- /** IDs of rejected/removed revisions */
31
- rejected: string[];
32
- /** IDs of revisions that remain */
33
- remaining: string[];
34
- /** Summary of actions taken */
35
- summary: {
36
- totalProcessed: number;
37
- acceptedCount: number;
38
- rejectedCount: number;
39
- remainingCount: number;
40
- };
41
- }
42
-
43
- /**
44
- * Provides granular control over revision acceptance using in-memory DOM transformation.
45
- * Allows subsequent modifications to be saved correctly.
46
- */
47
- export class SelectiveRevisionAcceptor {
48
- /**
49
- * Accept revisions matching criteria using in-memory DOM transformation.
50
- * Matching revisions: content kept, revision wrapper removed.
51
- * Non-matching revisions: preserved in the document.
52
- *
53
- * @param doc - Document to process
54
- * @param criteria - Selection criteria
55
- * @returns Result with accepted, rejected, and remaining revision IDs
56
- */
57
- static accept(
58
- doc: Document,
59
- criteria: SelectionCriteria
60
- ): SelectiveAcceptResult {
61
- const accepted: string[] = [];
62
- const remaining: string[] = [];
63
-
64
- // Check if doc has full Document API (getAllParagraphs, getTables)
65
- // or if it's a minimal mock (only getRevisionManager)
66
- const hasFullApi = typeof (doc as any).getAllParagraphs === 'function';
67
-
68
- if (hasFullApi) {
69
- // Full in-memory DOM transformation
70
- const paragraphs = (doc as any).getAllParagraphs();
71
- for (const paragraph of paragraphs) {
72
- this.processSelectiveParagraph(paragraph, criteria, 'accept', accepted, remaining);
73
- }
74
-
75
- // Process paragraphs in tables
76
- const tables = (doc as any).getTables();
77
- for (const table of tables) {
78
- for (const row of table.getRows()) {
79
- for (const cell of row.getCells()) {
80
- for (const paragraph of cell.getParagraphs()) {
81
- this.processSelectiveParagraph(paragraph, criteria, 'accept', accepted, remaining);
82
- }
83
- }
84
- }
85
- }
86
- } else {
87
- // Fallback: Filter revisions from RevisionManager only (for backward compatibility)
88
- const revisionManager = doc.getRevisionManager();
89
- if (revisionManager) {
90
- const allRevisions = revisionManager.getAllRevisions();
91
- for (const rev of allRevisions) {
92
- if (this.matchesCriteria(rev, criteria)) {
93
- accepted.push(rev.getId().toString());
94
- } else {
95
- remaining.push(rev.getId().toString());
96
- }
97
- }
98
- }
99
- }
100
-
101
- // Clear accepted revisions from RevisionManager
102
- const revisionManager = doc.getRevisionManager();
103
- if (revisionManager) {
104
- for (const id of accepted) {
105
- revisionManager.removeById(parseInt(id, 10));
106
- }
107
- }
108
-
109
- return {
110
- accepted,
111
- rejected: [],
112
- remaining,
113
- summary: {
114
- totalProcessed: accepted.length + remaining.length,
115
- acceptedCount: accepted.length,
116
- rejectedCount: 0,
117
- remainingCount: remaining.length,
118
- },
119
- };
120
- }
121
-
122
- /**
123
- * Reject revisions matching criteria using in-memory DOM transformation.
124
- * Matching revisions: content AND wrapper removed (opposite of accept).
125
- * Non-matching revisions: preserved in the document.
126
- *
127
- * For insertions: Rejecting removes the inserted content entirely.
128
- * For deletions: Rejecting keeps the deleted content (opposite of accepting).
129
- *
130
- * @param doc - Document to process
131
- * @param criteria - Selection criteria
132
- * @returns Result with accepted, rejected, and remaining revision IDs
133
- */
134
- static reject(
135
- doc: Document,
136
- criteria: SelectionCriteria
137
- ): SelectiveAcceptResult {
138
- const rejected: string[] = [];
139
- const remaining: string[] = [];
140
-
141
- // Check if doc has full Document API (getAllParagraphs, getTables)
142
- // or if it's a minimal mock (only getRevisionManager)
143
- const hasFullApi = typeof (doc as any).getAllParagraphs === 'function';
144
-
145
- if (hasFullApi) {
146
- // Full in-memory DOM transformation
147
- const paragraphs = (doc as any).getAllParagraphs();
148
- for (const paragraph of paragraphs) {
149
- this.processSelectiveParagraph(paragraph, criteria, 'reject', rejected, remaining);
150
- }
151
-
152
- // Process paragraphs in tables
153
- const tables = (doc as any).getTables();
154
- for (const table of tables) {
155
- for (const row of table.getRows()) {
156
- for (const cell of row.getCells()) {
157
- for (const paragraph of cell.getParagraphs()) {
158
- this.processSelectiveParagraph(paragraph, criteria, 'reject', rejected, remaining);
159
- }
160
- }
161
- }
162
- }
163
- } else {
164
- // Fallback: Filter revisions from RevisionManager only (for backward compatibility)
165
- const revisionManager = doc.getRevisionManager();
166
- if (revisionManager) {
167
- const allRevisions = revisionManager.getAllRevisions();
168
- for (const rev of allRevisions) {
169
- if (this.matchesCriteria(rev, criteria)) {
170
- rejected.push(rev.getId().toString());
171
- } else {
172
- remaining.push(rev.getId().toString());
173
- }
174
- }
175
- }
176
- }
177
-
178
- // Clear rejected revisions from RevisionManager
179
- const revisionManager = doc.getRevisionManager();
180
- if (revisionManager) {
181
- for (const id of rejected) {
182
- revisionManager.removeById(parseInt(id, 10));
183
- }
184
- }
185
-
186
- return {
187
- accepted: [],
188
- rejected,
189
- remaining,
190
- summary: {
191
- totalProcessed: rejected.length + remaining.length,
192
- acceptedCount: 0,
193
- rejectedCount: rejected.length,
194
- remainingCount: remaining.length,
195
- },
196
- };
197
- }
198
-
199
- /**
200
- * Process a paragraph for selective revision acceptance/rejection.
201
- * Transforms matching revisions in-place using DOM transformation.
202
- */
203
- private static processSelectiveParagraph(
204
- paragraph: Paragraph,
205
- criteria: SelectionCriteria,
206
- action: 'accept' | 'reject',
207
- processedIds: string[],
208
- remainingIds: string[]
209
- ): void {
210
- const content = paragraph.getContent();
211
- const newContent: ParagraphContent[] = [];
212
-
213
- for (const item of content) {
214
- if (item instanceof Revision) {
215
- const revisionId = item.getId().toString();
216
-
217
- if (this.matchesCriteria(item, criteria)) {
218
- // This revision matches the criteria - process it
219
- processedIds.push(revisionId);
220
-
221
- if (action === 'accept') {
222
- // Accept: Transform based on revision type
223
- this.acceptRevisionItem(item, newContent);
224
- } else {
225
- // Reject: Transform opposite of accept
226
- this.rejectRevisionItem(item, newContent);
227
- }
228
- } else {
229
- // This revision doesn't match - keep it
230
- remainingIds.push(revisionId);
231
- newContent.push(item);
232
- }
233
- } else {
234
- // Non-revision content - keep as-is
235
- newContent.push(item);
236
- }
237
- }
238
-
239
- // Replace paragraph content with the transformed content
240
- paragraph.setContent(newContent);
241
-
242
- // Handle paragraph mark revision markers (w:del/w:ins in w:pPr/w:rPr)
243
- // Both accept and reject clear the marker — these are metadata-only markers (no content),
244
- // so there is no content to add or remove, only the marker itself to clear.
245
- const formatting = paragraph.getFormatting();
246
- if (formatting.paragraphMarkDeletion) {
247
- const del = formatting.paragraphMarkDeletion;
248
- if (this.matchesMarkerCriteria(del, criteria)) {
249
- paragraph.clearParagraphMarkDeletion();
250
- processedIds.push(del.id.toString());
251
- } else {
252
- remainingIds.push(del.id.toString());
253
- }
254
- }
255
- if (formatting.paragraphMarkInsertion) {
256
- const ins = formatting.paragraphMarkInsertion;
257
- if (this.matchesMarkerCriteria(ins, criteria)) {
258
- paragraph.clearParagraphMarkInsertion();
259
- processedIds.push(ins.id.toString());
260
- } else {
261
- remainingIds.push(ins.id.toString());
262
- }
263
- }
264
- }
265
-
266
- /**
267
- * Check if a paragraph mark revision marker matches the given criteria.
268
- * Simplified version of matchesCriteria for non-Revision marker objects.
269
- */
270
- private static matchesMarkerCriteria(
271
- marker: { id: number; author: string; date: Date },
272
- criteria: SelectionCriteria
273
- ): boolean {
274
- // If no criteria specified, match nothing
275
- if (
276
- !criteria.ids &&
277
- !criteria.types &&
278
- !criteria.authors &&
279
- !criteria.dateRange &&
280
- !criteria.categories &&
281
- !criteria.custom
282
- ) {
283
- return false;
284
- }
285
-
286
- // Filter by IDs
287
- if (criteria.ids && !criteria.ids.includes(marker.id)) {
288
- return false;
289
- }
290
-
291
- // Filter by authors
292
- if (criteria.authors && !criteria.authors.includes(marker.author)) {
293
- return false;
294
- }
295
-
296
- // Filter by date range
297
- if (criteria.dateRange) {
298
- if (marker.date < criteria.dateRange.start || marker.date > criteria.dateRange.end) {
299
- return false;
300
- }
301
- }
302
-
303
- // types, categories, and custom filters don't apply to paragraph mark markers
304
- // (they are not Revision objects with a type/category)
305
- // If criteria only specifies types/categories/custom, markers won't match
306
- if (criteria.types || criteria.categories || criteria.custom) {
307
- return false;
308
- }
309
-
310
- return true;
311
- }
312
-
313
- /**
314
- * Accept a single revision item (unwrap insertions, remove deletions).
315
- */
316
- private static acceptRevisionItem(
317
- revision: Revision,
318
- newContent: ParagraphContent[]
319
- ): void {
320
- const revisionType = revision.getType();
321
- const childContent = revision.getContent();
322
-
323
- switch (revisionType) {
324
- case 'insert':
325
- case 'moveTo':
326
- // Unwrap: Extract child content into parent position
327
- for (const child of childContent) {
328
- if (isRunContent(child)) {
329
- newContent.push(child as Run);
330
- } else if (isHyperlinkContent(child)) {
331
- newContent.push(child);
332
- }
333
- }
334
- break;
335
-
336
- case 'delete':
337
- case 'moveFrom':
338
- // Remove: Don't add to newContent - content is deleted
339
- break;
340
-
341
- case 'runPropertiesChange':
342
- case 'paragraphPropertiesChange':
343
- case 'tablePropertiesChange':
344
- case 'tableExceptionPropertiesChange':
345
- case 'tableRowPropertiesChange':
346
- case 'tableCellPropertiesChange':
347
- case 'sectionPropertiesChange':
348
- case 'numberingChange':
349
- // Property changes: Keep content, remove change metadata
350
- for (const child of childContent) {
351
- if (isRunContent(child)) {
352
- newContent.push(child as Run);
353
- } else if (isHyperlinkContent(child)) {
354
- newContent.push(child);
355
- }
356
- }
357
- break;
358
-
359
- default:
360
- // Unknown type - keep the revision as-is for safety
361
- newContent.push(revision);
362
- }
363
- }
364
-
365
- /**
366
- * Reject a single revision item (opposite of accept).
367
- * - Rejecting an insertion removes the content
368
- * - Rejecting a deletion keeps the content (unwraps it)
369
- */
370
- private static rejectRevisionItem(
371
- revision: Revision,
372
- newContent: ParagraphContent[]
373
- ): void {
374
- const revisionType = revision.getType();
375
- const childContent = revision.getContent();
376
-
377
- switch (revisionType) {
378
- case 'insert':
379
- case 'moveTo':
380
- // Reject insertion: Remove the inserted content entirely
381
- break;
382
-
383
- case 'delete':
384
- case 'moveFrom':
385
- // Reject deletion: Keep the deleted content (unwrap)
386
- for (const child of childContent) {
387
- if (isRunContent(child)) {
388
- newContent.push(child as Run);
389
- } else if (isHyperlinkContent(child)) {
390
- newContent.push(child);
391
- }
392
- }
393
- break;
394
-
395
- case 'runPropertiesChange':
396
- case 'paragraphPropertiesChange':
397
- case 'tablePropertiesChange':
398
- case 'tableExceptionPropertiesChange':
399
- case 'tableRowPropertiesChange':
400
- case 'tableCellPropertiesChange':
401
- case 'sectionPropertiesChange':
402
- case 'numberingChange':
403
- // Rejecting property changes: Would need to restore old properties
404
- // For now, just keep content without the change metadata
405
- // (Full implementation would restore previousProperties)
406
- for (const child of childContent) {
407
- if (isRunContent(child)) {
408
- newContent.push(child as Run);
409
- } else if (isHyperlinkContent(child)) {
410
- newContent.push(child);
411
- }
412
- }
413
- break;
414
-
415
- default:
416
- // Unknown type - keep the revision as-is for safety
417
- newContent.push(revision);
418
- }
419
- }
420
-
421
- /**
422
- * Preview what would happen without making changes.
423
- *
424
- * @param doc - Document to analyze
425
- * @param criteria - Selection criteria
426
- * @param action - Action to preview
427
- * @returns Preview of what would happen
428
- */
429
- static preview(
430
- doc: Document,
431
- criteria: SelectionCriteria,
432
- action: 'accept' | 'reject'
433
- ): SelectiveAcceptResult {
434
- // Preview is the same as the actual operation but without side effects
435
- return action === 'accept'
436
- ? this.accept(doc, criteria)
437
- : this.reject(doc, criteria);
438
- }
439
-
440
- /**
441
- * Accept all revisions by a specific author.
442
- *
443
- * @param doc - Document to process
444
- * @param author - Author name to accept
445
- * @returns Result with accepted, rejected, and remaining revision IDs
446
- */
447
- static acceptByAuthor(doc: Document, author: string): SelectiveAcceptResult {
448
- return this.accept(doc, { authors: [author] });
449
- }
450
-
451
- /**
452
- * Reject all revisions by a specific author.
453
- *
454
- * @param doc - Document to process
455
- * @param author - Author name to reject
456
- * @returns Result with accepted, rejected, and remaining revision IDs
457
- */
458
- static rejectByAuthor(doc: Document, author: string): SelectiveAcceptResult {
459
- return this.reject(doc, { authors: [author] });
460
- }
461
-
462
- /**
463
- * Accept all revisions of specific types.
464
- *
465
- * @param doc - Document to process
466
- * @param types - Revision types to accept
467
- * @returns Result with accepted, rejected, and remaining revision IDs
468
- */
469
- static acceptByType(doc: Document, types: RevisionType[]): SelectiveAcceptResult {
470
- return this.accept(doc, { types });
471
- }
472
-
473
- /**
474
- * Reject all revisions of specific types.
475
- *
476
- * @param doc - Document to process
477
- * @param types - Revision types to reject
478
- * @returns Result with accepted, rejected, and remaining revision IDs
479
- */
480
- static rejectByType(doc: Document, types: RevisionType[]): SelectiveAcceptResult {
481
- return this.reject(doc, { types });
482
- }
483
-
484
- /**
485
- * Accept all revisions before a specific date.
486
- *
487
- * @param doc - Document to process
488
- * @param date - Cutoff date (exclusive)
489
- * @returns Result with accepted, rejected, and remaining revision IDs
490
- */
491
- static acceptBeforeDate(doc: Document, date: Date): SelectiveAcceptResult {
492
- return this.accept(doc, {
493
- dateRange: { start: new Date(0), end: date },
494
- });
495
- }
496
-
497
- /**
498
- * Accept all revisions after a specific date.
499
- *
500
- * @param doc - Document to process
501
- * @param date - Start date (exclusive)
502
- * @returns Result with accepted, rejected, and remaining revision IDs
503
- */
504
- static acceptAfterDate(doc: Document, date: Date): SelectiveAcceptResult {
505
- return this.accept(doc, {
506
- dateRange: { start: date, end: new Date() },
507
- });
508
- }
509
-
510
- /**
511
- * Accept all insertions only.
512
- *
513
- * @param doc - Document to process
514
- * @returns Result with accepted, rejected, and remaining revision IDs
515
- */
516
- static acceptInsertionsOnly(doc: Document): SelectiveAcceptResult {
517
- return this.accept(doc, { types: ['insert'] });
518
- }
519
-
520
- /**
521
- * Accept all deletions only.
522
- *
523
- * @param doc - Document to process
524
- * @returns Result with accepted, rejected, and remaining revision IDs
525
- */
526
- static acceptDeletionsOnly(doc: Document): SelectiveAcceptResult {
527
- return this.accept(doc, { types: ['delete'] });
528
- }
529
-
530
- /**
531
- * Reject all formatting changes (keep content changes).
532
- *
533
- * @param doc - Document to process
534
- * @returns Result with accepted, rejected, and remaining revision IDs
535
- */
536
- static rejectFormattingChanges(doc: Document): SelectiveAcceptResult {
537
- return this.reject(doc, { categories: ['formatting'] });
538
- }
539
-
540
- /**
541
- * Accept content changes only (reject formatting).
542
- *
543
- * @param doc - Document to process
544
- * @returns Result with accepted, rejected, and remaining revision IDs
545
- */
546
- static acceptContentChangesOnly(doc: Document): SelectiveAcceptResult {
547
- return this.accept(doc, { categories: ['content'] });
548
- }
549
-
550
- /**
551
- * Partition revisions into matching and non-matching based on criteria.
552
- */
553
- private static partitionRevisions(
554
- revisions: Revision[],
555
- criteria: SelectionCriteria
556
- ): { matching: Revision[]; nonMatching: Revision[] } {
557
- const matching: Revision[] = [];
558
- const nonMatching: Revision[] = [];
559
-
560
- for (const rev of revisions) {
561
- if (this.matchesCriteria(rev, criteria)) {
562
- matching.push(rev);
563
- } else {
564
- nonMatching.push(rev);
565
- }
566
- }
567
-
568
- return { matching, nonMatching };
569
- }
570
-
571
- /**
572
- * Check if a revision matches the given criteria.
573
- */
574
- private static matchesCriteria(
575
- revision: Revision,
576
- criteria: SelectionCriteria
577
- ): boolean {
578
- // If no criteria specified, match nothing
579
- if (
580
- !criteria.ids &&
581
- !criteria.types &&
582
- !criteria.authors &&
583
- !criteria.dateRange &&
584
- !criteria.categories &&
585
- !criteria.custom
586
- ) {
587
- return false;
588
- }
589
-
590
- // Filter by IDs
591
- if (criteria.ids && !criteria.ids.includes(revision.getId())) {
592
- return false;
593
- }
594
-
595
- // Filter by types
596
- if (criteria.types && !criteria.types.includes(revision.getType())) {
597
- return false;
598
- }
599
-
600
- // Filter by authors
601
- if (criteria.authors && !criteria.authors.includes(revision.getAuthor())) {
602
- return false;
603
- }
604
-
605
- // Filter by date range
606
- if (criteria.dateRange) {
607
- const date = revision.getDate();
608
- if (date < criteria.dateRange.start || date > criteria.dateRange.end) {
609
- return false;
610
- }
611
- }
612
-
613
- // Filter by categories
614
- if (criteria.categories) {
615
- const category = this.getRevisionCategory(revision);
616
- if (!criteria.categories.includes(category)) {
617
- return false;
618
- }
619
- }
620
-
621
- // Custom filter
622
- if (criteria.custom && !criteria.custom(revision)) {
623
- return false;
624
- }
625
-
626
- return true;
627
- }
628
-
629
- /**
630
- * Get the semantic category of a revision.
631
- */
632
- private static getRevisionCategory(revision: Revision): ChangeCategory {
633
- const type = revision.getType();
634
-
635
- if (type === 'insert' || type === 'delete') {
636
- return 'content';
637
- }
638
- if (
639
- type === 'runPropertiesChange' ||
640
- type === 'paragraphPropertiesChange' ||
641
- type === 'numberingChange'
642
- ) {
643
- return 'formatting';
644
- }
645
- if (
646
- type === 'moveFrom' ||
647
- type === 'moveTo' ||
648
- type === 'sectionPropertiesChange'
649
- ) {
650
- return 'structural';
651
- }
652
- if (
653
- type === 'tablePropertiesChange' ||
654
- type === 'tableExceptionPropertiesChange' ||
655
- type === 'tableRowPropertiesChange' ||
656
- type === 'tableCellPropertiesChange' ||
657
- type === 'tableCellInsert' ||
658
- type === 'tableCellDelete' ||
659
- type === 'tableCellMerge'
660
- ) {
661
- return 'table';
662
- }
663
-
664
- return 'content';
665
- }
666
-
667
- /**
668
- * Create an empty result.
669
- */
670
- private static emptyResult(): SelectiveAcceptResult {
671
- return {
672
- accepted: [],
673
- rejected: [],
674
- remaining: [],
675
- summary: {
676
- totalProcessed: 0,
677
- acceptedCount: 0,
678
- rejectedCount: 0,
679
- remainingCount: 0,
680
- },
681
- };
682
- }
683
- }
1
+ /**
2
+ * SelectiveRevisionAcceptor - Accept or reject specific revisions based on criteria
3
+ *
4
+ * Provides granular control over revision acceptance using in-memory DOM transformation.
5
+ * Extends the all-or-nothing approach with selective acceptance by author, type, date,
6
+ * and custom criteria.
7
+ *
8
+ * Uses the industry-standard in-memory transformation approach (like OpenXML PowerTools),
9
+ * allowing subsequent modifications to be saved correctly.
10
+ *
11
+ * @module SelectiveRevisionAcceptor
12
+ * @see https://github.com/OfficeDev/Open-Xml-PowerTools - RevisionAccepter.cs
13
+ */
14
+
15
+ import type { Document } from '../core/Document';
16
+ import type { Paragraph, ParagraphContent } from '../elements/Paragraph';
17
+ import { Revision, RevisionType } from '../elements/Revision';
18
+ import type { Run } from '../elements/Run';
19
+ import type { Hyperlink } from '../elements/Hyperlink';
20
+ import { isRunContent, isHyperlinkContent } from '../elements/RevisionContent';
21
+ import { ChangeCategory } from './ChangelogGenerator';
22
+ import { SelectionCriteria } from './RevisionAwareProcessor';
23
+
24
+ /**
25
+ * Result of selective revision acceptance.
26
+ */
27
+ export interface SelectiveAcceptResult {
28
+ /** IDs of accepted revisions */
29
+ accepted: string[];
30
+ /** IDs of rejected/removed revisions */
31
+ rejected: string[];
32
+ /** IDs of revisions that remain */
33
+ remaining: string[];
34
+ /** Summary of actions taken */
35
+ summary: {
36
+ totalProcessed: number;
37
+ acceptedCount: number;
38
+ rejectedCount: number;
39
+ remainingCount: number;
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Provides granular control over revision acceptance using in-memory DOM transformation.
45
+ * Allows subsequent modifications to be saved correctly.
46
+ */
47
+ export class SelectiveRevisionAcceptor {
48
+ /**
49
+ * Accept revisions matching criteria using in-memory DOM transformation.
50
+ * Matching revisions: content kept, revision wrapper removed.
51
+ * Non-matching revisions: preserved in the document.
52
+ *
53
+ * @param doc - Document to process
54
+ * @param criteria - Selection criteria
55
+ * @returns Result with accepted, rejected, and remaining revision IDs
56
+ */
57
+ static accept(doc: Document, criteria: SelectionCriteria): SelectiveAcceptResult {
58
+ const accepted: string[] = [];
59
+ const remaining: string[] = [];
60
+
61
+ // Check if doc has full Document API (getAllParagraphs, getTables)
62
+ // or if it's a minimal mock (only getRevisionManager)
63
+ const hasFullApi = typeof (doc as any).getAllParagraphs === 'function';
64
+
65
+ if (hasFullApi) {
66
+ // Full in-memory DOM transformation
67
+ const paragraphs = (doc as any).getAllParagraphs();
68
+ for (const paragraph of paragraphs) {
69
+ this.processSelectiveParagraph(paragraph, criteria, 'accept', accepted, remaining);
70
+ }
71
+
72
+ // Process paragraphs in tables
73
+ const tables = (doc as any).getTables();
74
+ for (const table of tables) {
75
+ for (const row of table.getRows()) {
76
+ for (const cell of row.getCells()) {
77
+ for (const paragraph of cell.getParagraphs()) {
78
+ this.processSelectiveParagraph(paragraph, criteria, 'accept', accepted, remaining);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ } else {
84
+ // Fallback: Filter revisions from RevisionManager only (for backward compatibility)
85
+ const revisionManager = doc.getRevisionManager();
86
+ if (revisionManager) {
87
+ const allRevisions = revisionManager.getAllRevisions();
88
+ for (const rev of allRevisions) {
89
+ if (this.matchesCriteria(rev, criteria)) {
90
+ accepted.push(rev.getId().toString());
91
+ } else {
92
+ remaining.push(rev.getId().toString());
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ // Clear accepted revisions from RevisionManager
99
+ const revisionManager = doc.getRevisionManager();
100
+ if (revisionManager) {
101
+ for (const id of accepted) {
102
+ revisionManager.removeById(parseInt(id, 10));
103
+ }
104
+ }
105
+
106
+ return {
107
+ accepted,
108
+ rejected: [],
109
+ remaining,
110
+ summary: {
111
+ totalProcessed: accepted.length + remaining.length,
112
+ acceptedCount: accepted.length,
113
+ rejectedCount: 0,
114
+ remainingCount: remaining.length,
115
+ },
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Reject revisions matching criteria using in-memory DOM transformation.
121
+ * Matching revisions: content AND wrapper removed (opposite of accept).
122
+ * Non-matching revisions: preserved in the document.
123
+ *
124
+ * For insertions: Rejecting removes the inserted content entirely.
125
+ * For deletions: Rejecting keeps the deleted content (opposite of accepting).
126
+ *
127
+ * @param doc - Document to process
128
+ * @param criteria - Selection criteria
129
+ * @returns Result with accepted, rejected, and remaining revision IDs
130
+ */
131
+ static reject(doc: Document, criteria: SelectionCriteria): SelectiveAcceptResult {
132
+ const rejected: string[] = [];
133
+ const remaining: string[] = [];
134
+
135
+ // Check if doc has full Document API (getAllParagraphs, getTables)
136
+ // or if it's a minimal mock (only getRevisionManager)
137
+ const hasFullApi = typeof (doc as any).getAllParagraphs === 'function';
138
+
139
+ if (hasFullApi) {
140
+ // Full in-memory DOM transformation
141
+ const paragraphs = (doc as any).getAllParagraphs();
142
+ for (const paragraph of paragraphs) {
143
+ this.processSelectiveParagraph(paragraph, criteria, 'reject', rejected, remaining);
144
+ }
145
+
146
+ // Process paragraphs in tables
147
+ const tables = (doc as any).getTables();
148
+ for (const table of tables) {
149
+ for (const row of table.getRows()) {
150
+ for (const cell of row.getCells()) {
151
+ for (const paragraph of cell.getParagraphs()) {
152
+ this.processSelectiveParagraph(paragraph, criteria, 'reject', rejected, remaining);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ } else {
158
+ // Fallback: Filter revisions from RevisionManager only (for backward compatibility)
159
+ const revisionManager = doc.getRevisionManager();
160
+ if (revisionManager) {
161
+ const allRevisions = revisionManager.getAllRevisions();
162
+ for (const rev of allRevisions) {
163
+ if (this.matchesCriteria(rev, criteria)) {
164
+ rejected.push(rev.getId().toString());
165
+ } else {
166
+ remaining.push(rev.getId().toString());
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ // Clear rejected revisions from RevisionManager
173
+ const revisionManager = doc.getRevisionManager();
174
+ if (revisionManager) {
175
+ for (const id of rejected) {
176
+ revisionManager.removeById(parseInt(id, 10));
177
+ }
178
+ }
179
+
180
+ return {
181
+ accepted: [],
182
+ rejected,
183
+ remaining,
184
+ summary: {
185
+ totalProcessed: rejected.length + remaining.length,
186
+ acceptedCount: 0,
187
+ rejectedCount: rejected.length,
188
+ remainingCount: remaining.length,
189
+ },
190
+ };
191
+ }
192
+
193
+ /**
194
+ * Process a paragraph for selective revision acceptance/rejection.
195
+ * Transforms matching revisions in-place using DOM transformation.
196
+ */
197
+ private static processSelectiveParagraph(
198
+ paragraph: Paragraph,
199
+ criteria: SelectionCriteria,
200
+ action: 'accept' | 'reject',
201
+ processedIds: string[],
202
+ remainingIds: string[]
203
+ ): void {
204
+ const content = paragraph.getContent();
205
+ const newContent: ParagraphContent[] = [];
206
+
207
+ for (const item of content) {
208
+ if (item instanceof Revision) {
209
+ const revisionId = item.getId().toString();
210
+
211
+ if (this.matchesCriteria(item, criteria)) {
212
+ // This revision matches the criteria - process it
213
+ processedIds.push(revisionId);
214
+
215
+ if (action === 'accept') {
216
+ // Accept: Transform based on revision type
217
+ this.acceptRevisionItem(item, newContent);
218
+ } else {
219
+ // Reject: Transform opposite of accept
220
+ this.rejectRevisionItem(item, newContent);
221
+ }
222
+ } else {
223
+ // This revision doesn't match - keep it
224
+ remainingIds.push(revisionId);
225
+ newContent.push(item);
226
+ }
227
+ } else {
228
+ // Non-revision content - keep as-is
229
+ newContent.push(item);
230
+ }
231
+ }
232
+
233
+ // Replace paragraph content with the transformed content
234
+ paragraph.setContent(newContent);
235
+
236
+ // Handle paragraph mark revision markers (w:del/w:ins in w:pPr/w:rPr)
237
+ // Both accept and reject clear the marker — these are metadata-only markers (no content),
238
+ // so there is no content to add or remove, only the marker itself to clear.
239
+ const formatting = paragraph.getFormatting();
240
+ if (formatting.paragraphMarkDeletion) {
241
+ const del = formatting.paragraphMarkDeletion;
242
+ if (this.matchesMarkerCriteria(del, criteria)) {
243
+ paragraph.clearParagraphMarkDeletion();
244
+ processedIds.push(del.id.toString());
245
+ } else {
246
+ remainingIds.push(del.id.toString());
247
+ }
248
+ }
249
+ if (formatting.paragraphMarkInsertion) {
250
+ const ins = formatting.paragraphMarkInsertion;
251
+ if (this.matchesMarkerCriteria(ins, criteria)) {
252
+ paragraph.clearParagraphMarkInsertion();
253
+ processedIds.push(ins.id.toString());
254
+ } else {
255
+ remainingIds.push(ins.id.toString());
256
+ }
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Check if a paragraph mark revision marker matches the given criteria.
262
+ * Simplified version of matchesCriteria for non-Revision marker objects.
263
+ */
264
+ private static matchesMarkerCriteria(
265
+ marker: { id: number; author: string; date: Date },
266
+ criteria: SelectionCriteria
267
+ ): boolean {
268
+ // If no criteria specified, match nothing
269
+ if (
270
+ !criteria.ids &&
271
+ !criteria.types &&
272
+ !criteria.authors &&
273
+ !criteria.dateRange &&
274
+ !criteria.categories &&
275
+ !criteria.custom
276
+ ) {
277
+ return false;
278
+ }
279
+
280
+ // Filter by IDs
281
+ if (criteria.ids && !criteria.ids.includes(marker.id)) {
282
+ return false;
283
+ }
284
+
285
+ // Filter by authors
286
+ if (criteria.authors && !criteria.authors.includes(marker.author)) {
287
+ return false;
288
+ }
289
+
290
+ // Filter by date range
291
+ if (criteria.dateRange) {
292
+ if (marker.date < criteria.dateRange.start || marker.date > criteria.dateRange.end) {
293
+ return false;
294
+ }
295
+ }
296
+
297
+ // types, categories, and custom filters don't apply to paragraph mark markers
298
+ // (they are not Revision objects with a type/category)
299
+ // If criteria only specifies types/categories/custom, markers won't match
300
+ if (criteria.types || criteria.categories || criteria.custom) {
301
+ return false;
302
+ }
303
+
304
+ return true;
305
+ }
306
+
307
+ /**
308
+ * Accept a single revision item (unwrap insertions, remove deletions).
309
+ */
310
+ private static acceptRevisionItem(revision: Revision, newContent: ParagraphContent[]): void {
311
+ const revisionType = revision.getType();
312
+ const childContent = revision.getContent();
313
+
314
+ switch (revisionType) {
315
+ case 'insert':
316
+ case 'moveTo':
317
+ // Unwrap: Extract child content into parent position
318
+ for (const child of childContent) {
319
+ if (isRunContent(child)) {
320
+ newContent.push(child as Run);
321
+ } else if (isHyperlinkContent(child)) {
322
+ newContent.push(child);
323
+ }
324
+ }
325
+ break;
326
+
327
+ case 'delete':
328
+ case 'moveFrom':
329
+ // Remove: Don't add to newContent - content is deleted
330
+ break;
331
+
332
+ case 'runPropertiesChange':
333
+ case 'paragraphPropertiesChange':
334
+ case 'tablePropertiesChange':
335
+ case 'tableExceptionPropertiesChange':
336
+ case 'tableRowPropertiesChange':
337
+ case 'tableCellPropertiesChange':
338
+ case 'sectionPropertiesChange':
339
+ case 'numberingChange':
340
+ // Property changes: Keep content, remove change metadata
341
+ for (const child of childContent) {
342
+ if (isRunContent(child)) {
343
+ newContent.push(child as Run);
344
+ } else if (isHyperlinkContent(child)) {
345
+ newContent.push(child);
346
+ }
347
+ }
348
+ break;
349
+
350
+ default:
351
+ // Unknown type - keep the revision as-is for safety
352
+ newContent.push(revision);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Reject a single revision item (opposite of accept).
358
+ * - Rejecting an insertion removes the content
359
+ * - Rejecting a deletion keeps the content (unwraps it)
360
+ */
361
+ private static rejectRevisionItem(revision: Revision, newContent: ParagraphContent[]): void {
362
+ const revisionType = revision.getType();
363
+ const childContent = revision.getContent();
364
+
365
+ switch (revisionType) {
366
+ case 'insert':
367
+ case 'moveTo':
368
+ // Reject insertion: Remove the inserted content entirely
369
+ break;
370
+
371
+ case 'delete':
372
+ case 'moveFrom':
373
+ // Reject deletion: Keep the deleted content (unwrap)
374
+ for (const child of childContent) {
375
+ if (isRunContent(child)) {
376
+ newContent.push(child as Run);
377
+ } else if (isHyperlinkContent(child)) {
378
+ newContent.push(child);
379
+ }
380
+ }
381
+ break;
382
+
383
+ case 'runPropertiesChange':
384
+ case 'paragraphPropertiesChange':
385
+ case 'tablePropertiesChange':
386
+ case 'tableExceptionPropertiesChange':
387
+ case 'tableRowPropertiesChange':
388
+ case 'tableCellPropertiesChange':
389
+ case 'sectionPropertiesChange':
390
+ case 'numberingChange':
391
+ // Rejecting property changes: Would need to restore old properties
392
+ // For now, just keep content without the change metadata
393
+ // (Full implementation would restore previousProperties)
394
+ for (const child of childContent) {
395
+ if (isRunContent(child)) {
396
+ newContent.push(child as Run);
397
+ } else if (isHyperlinkContent(child)) {
398
+ newContent.push(child);
399
+ }
400
+ }
401
+ break;
402
+
403
+ default:
404
+ // Unknown type - keep the revision as-is for safety
405
+ newContent.push(revision);
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Preview what would happen without making changes.
411
+ *
412
+ * @param doc - Document to analyze
413
+ * @param criteria - Selection criteria
414
+ * @param action - Action to preview
415
+ * @returns Preview of what would happen
416
+ */
417
+ static preview(
418
+ doc: Document,
419
+ criteria: SelectionCriteria,
420
+ action: 'accept' | 'reject'
421
+ ): SelectiveAcceptResult {
422
+ // Preview is the same as the actual operation but without side effects
423
+ return action === 'accept' ? this.accept(doc, criteria) : this.reject(doc, criteria);
424
+ }
425
+
426
+ /**
427
+ * Accept all revisions by a specific author.
428
+ *
429
+ * @param doc - Document to process
430
+ * @param author - Author name to accept
431
+ * @returns Result with accepted, rejected, and remaining revision IDs
432
+ */
433
+ static acceptByAuthor(doc: Document, author: string): SelectiveAcceptResult {
434
+ return this.accept(doc, { authors: [author] });
435
+ }
436
+
437
+ /**
438
+ * Reject all revisions by a specific author.
439
+ *
440
+ * @param doc - Document to process
441
+ * @param author - Author name to reject
442
+ * @returns Result with accepted, rejected, and remaining revision IDs
443
+ */
444
+ static rejectByAuthor(doc: Document, author: string): SelectiveAcceptResult {
445
+ return this.reject(doc, { authors: [author] });
446
+ }
447
+
448
+ /**
449
+ * Accept all revisions of specific types.
450
+ *
451
+ * @param doc - Document to process
452
+ * @param types - Revision types to accept
453
+ * @returns Result with accepted, rejected, and remaining revision IDs
454
+ */
455
+ static acceptByType(doc: Document, types: RevisionType[]): SelectiveAcceptResult {
456
+ return this.accept(doc, { types });
457
+ }
458
+
459
+ /**
460
+ * Reject all revisions of specific types.
461
+ *
462
+ * @param doc - Document to process
463
+ * @param types - Revision types to reject
464
+ * @returns Result with accepted, rejected, and remaining revision IDs
465
+ */
466
+ static rejectByType(doc: Document, types: RevisionType[]): SelectiveAcceptResult {
467
+ return this.reject(doc, { types });
468
+ }
469
+
470
+ /**
471
+ * Accept all revisions before a specific date.
472
+ *
473
+ * @param doc - Document to process
474
+ * @param date - Cutoff date (exclusive)
475
+ * @returns Result with accepted, rejected, and remaining revision IDs
476
+ */
477
+ static acceptBeforeDate(doc: Document, date: Date): SelectiveAcceptResult {
478
+ return this.accept(doc, {
479
+ dateRange: { start: new Date(0), end: date },
480
+ });
481
+ }
482
+
483
+ /**
484
+ * Accept all revisions after a specific date.
485
+ *
486
+ * @param doc - Document to process
487
+ * @param date - Start date (exclusive)
488
+ * @returns Result with accepted, rejected, and remaining revision IDs
489
+ */
490
+ static acceptAfterDate(doc: Document, date: Date): SelectiveAcceptResult {
491
+ return this.accept(doc, {
492
+ dateRange: { start: date, end: new Date() },
493
+ });
494
+ }
495
+
496
+ /**
497
+ * Accept all insertions only.
498
+ *
499
+ * @param doc - Document to process
500
+ * @returns Result with accepted, rejected, and remaining revision IDs
501
+ */
502
+ static acceptInsertionsOnly(doc: Document): SelectiveAcceptResult {
503
+ return this.accept(doc, { types: ['insert'] });
504
+ }
505
+
506
+ /**
507
+ * Accept all deletions only.
508
+ *
509
+ * @param doc - Document to process
510
+ * @returns Result with accepted, rejected, and remaining revision IDs
511
+ */
512
+ static acceptDeletionsOnly(doc: Document): SelectiveAcceptResult {
513
+ return this.accept(doc, { types: ['delete'] });
514
+ }
515
+
516
+ /**
517
+ * Reject all formatting changes (keep content changes).
518
+ *
519
+ * @param doc - Document to process
520
+ * @returns Result with accepted, rejected, and remaining revision IDs
521
+ */
522
+ static rejectFormattingChanges(doc: Document): SelectiveAcceptResult {
523
+ return this.reject(doc, { categories: ['formatting'] });
524
+ }
525
+
526
+ /**
527
+ * Accept content changes only (reject formatting).
528
+ *
529
+ * @param doc - Document to process
530
+ * @returns Result with accepted, rejected, and remaining revision IDs
531
+ */
532
+ static acceptContentChangesOnly(doc: Document): SelectiveAcceptResult {
533
+ return this.accept(doc, { categories: ['content'] });
534
+ }
535
+
536
+ /**
537
+ * Partition revisions into matching and non-matching based on criteria.
538
+ */
539
+ private static partitionRevisions(
540
+ revisions: Revision[],
541
+ criteria: SelectionCriteria
542
+ ): { matching: Revision[]; nonMatching: Revision[] } {
543
+ const matching: Revision[] = [];
544
+ const nonMatching: Revision[] = [];
545
+
546
+ for (const rev of revisions) {
547
+ if (this.matchesCriteria(rev, criteria)) {
548
+ matching.push(rev);
549
+ } else {
550
+ nonMatching.push(rev);
551
+ }
552
+ }
553
+
554
+ return { matching, nonMatching };
555
+ }
556
+
557
+ /**
558
+ * Check if a revision matches the given criteria.
559
+ */
560
+ private static matchesCriteria(revision: Revision, criteria: SelectionCriteria): boolean {
561
+ // If no criteria specified, match nothing
562
+ if (
563
+ !criteria.ids &&
564
+ !criteria.types &&
565
+ !criteria.authors &&
566
+ !criteria.dateRange &&
567
+ !criteria.categories &&
568
+ !criteria.custom
569
+ ) {
570
+ return false;
571
+ }
572
+
573
+ // Filter by IDs
574
+ if (criteria.ids && !criteria.ids.includes(revision.getId())) {
575
+ return false;
576
+ }
577
+
578
+ // Filter by types
579
+ if (criteria.types && !criteria.types.includes(revision.getType())) {
580
+ return false;
581
+ }
582
+
583
+ // Filter by authors
584
+ if (criteria.authors && !criteria.authors.includes(revision.getAuthor())) {
585
+ return false;
586
+ }
587
+
588
+ // Filter by date range
589
+ if (criteria.dateRange) {
590
+ const date = revision.getDate();
591
+ if (date < criteria.dateRange.start || date > criteria.dateRange.end) {
592
+ return false;
593
+ }
594
+ }
595
+
596
+ // Filter by categories
597
+ if (criteria.categories) {
598
+ const category = this.getRevisionCategory(revision);
599
+ if (!criteria.categories.includes(category)) {
600
+ return false;
601
+ }
602
+ }
603
+
604
+ // Custom filter
605
+ if (criteria.custom && !criteria.custom(revision)) {
606
+ return false;
607
+ }
608
+
609
+ return true;
610
+ }
611
+
612
+ /**
613
+ * Get the semantic category of a revision.
614
+ */
615
+ private static getRevisionCategory(revision: Revision): ChangeCategory {
616
+ const type = revision.getType();
617
+
618
+ if (type === 'insert' || type === 'delete') {
619
+ return 'content';
620
+ }
621
+ if (
622
+ type === 'runPropertiesChange' ||
623
+ type === 'paragraphPropertiesChange' ||
624
+ type === 'numberingChange'
625
+ ) {
626
+ return 'formatting';
627
+ }
628
+ if (type === 'moveFrom' || type === 'moveTo' || type === 'sectionPropertiesChange') {
629
+ return 'structural';
630
+ }
631
+ if (
632
+ type === 'tablePropertiesChange' ||
633
+ type === 'tableExceptionPropertiesChange' ||
634
+ type === 'tableRowPropertiesChange' ||
635
+ type === 'tableCellPropertiesChange' ||
636
+ type === 'tableCellInsert' ||
637
+ type === 'tableCellDelete' ||
638
+ type === 'tableCellMerge'
639
+ ) {
640
+ return 'table';
641
+ }
642
+
643
+ return 'content';
644
+ }
645
+
646
+ /**
647
+ * Create an empty result.
648
+ */
649
+ private static emptyResult(): SelectiveAcceptResult {
650
+ return {
651
+ accepted: [],
652
+ rejected: [],
653
+ remaining: [],
654
+ summary: {
655
+ totalProcessed: 0,
656
+ acceptedCount: 0,
657
+ rejectedCount: 0,
658
+ remainingCount: 0,
659
+ },
660
+ };
661
+ }
662
+ }