docxmlater 10.1.3 → 10.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +759 -754
  2. package/dist/constants/legacyCompatFlags.js +1 -1
  3. package/dist/constants/legacyCompatFlags.js.map +1 -1
  4. package/dist/constants/limits.js.map +1 -1
  5. package/dist/core/Document.d.ts +50 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +483 -471
  8. package/dist/core/Document.js.map +1 -1
  9. package/dist/core/DocumentContent.d.ts +9 -9
  10. package/dist/core/DocumentContent.d.ts.map +1 -1
  11. package/dist/core/DocumentContent.js +1 -1
  12. package/dist/core/DocumentContent.js.map +1 -1
  13. package/dist/core/DocumentGenerator.d.ts +11 -11
  14. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  15. package/dist/core/DocumentGenerator.js +251 -251
  16. package/dist/core/DocumentGenerator.js.map +1 -1
  17. package/dist/core/DocumentIdManager.js.map +1 -1
  18. package/dist/core/DocumentParser.d.ts +15 -15
  19. package/dist/core/DocumentParser.d.ts.map +1 -1
  20. package/dist/core/DocumentParser.js +2123 -2155
  21. package/dist/core/DocumentParser.js.map +1 -1
  22. package/dist/core/DocumentValidator.d.ts.map +1 -1
  23. package/dist/core/DocumentValidator.js +2 -5
  24. package/dist/core/DocumentValidator.js.map +1 -1
  25. package/dist/core/Relationship.js.map +1 -1
  26. package/dist/core/RelationshipManager.d.ts.map +1 -1
  27. package/dist/core/RelationshipManager.js +3 -3
  28. package/dist/core/RelationshipManager.js.map +1 -1
  29. package/dist/elements/AlternateContent.js.map +1 -1
  30. package/dist/elements/Bookmark.d.ts.map +1 -1
  31. package/dist/elements/Bookmark.js +3 -1
  32. package/dist/elements/Bookmark.js.map +1 -1
  33. package/dist/elements/BookmarkManager.d.ts.map +1 -1
  34. package/dist/elements/BookmarkManager.js.map +1 -1
  35. package/dist/elements/Comment.d.ts.map +1 -1
  36. package/dist/elements/Comment.js +9 -6
  37. package/dist/elements/Comment.js.map +1 -1
  38. package/dist/elements/CommentManager.d.ts.map +1 -1
  39. package/dist/elements/CommentManager.js +18 -17
  40. package/dist/elements/CommentManager.js.map +1 -1
  41. package/dist/elements/CommonTypes.d.ts +21 -21
  42. package/dist/elements/CommonTypes.d.ts.map +1 -1
  43. package/dist/elements/CommonTypes.js +56 -56
  44. package/dist/elements/CommonTypes.js.map +1 -1
  45. package/dist/elements/CustomXml.js.map +1 -1
  46. package/dist/elements/Endnote.d.ts.map +1 -1
  47. package/dist/elements/Endnote.js +6 -6
  48. package/dist/elements/Endnote.js.map +1 -1
  49. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  50. package/dist/elements/EndnoteManager.js +6 -7
  51. package/dist/elements/EndnoteManager.js.map +1 -1
  52. package/dist/elements/Field.d.ts.map +1 -1
  53. package/dist/elements/Field.js +82 -25
  54. package/dist/elements/Field.js.map +1 -1
  55. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  56. package/dist/elements/FieldHelpers.js.map +1 -1
  57. package/dist/elements/FontManager.d.ts.map +1 -1
  58. package/dist/elements/FontManager.js +1 -1
  59. package/dist/elements/FontManager.js.map +1 -1
  60. package/dist/elements/Footer.js +2 -2
  61. package/dist/elements/Footer.js.map +1 -1
  62. package/dist/elements/Footnote.d.ts.map +1 -1
  63. package/dist/elements/Footnote.js +6 -6
  64. package/dist/elements/Footnote.js.map +1 -1
  65. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  66. package/dist/elements/FootnoteManager.js +6 -7
  67. package/dist/elements/FootnoteManager.js.map +1 -1
  68. package/dist/elements/Header.js +2 -2
  69. package/dist/elements/Header.js.map +1 -1
  70. package/dist/elements/HeaderFooterManager.js.map +1 -1
  71. package/dist/elements/Hyperlink.d.ts +5 -3
  72. package/dist/elements/Hyperlink.d.ts.map +1 -1
  73. package/dist/elements/Hyperlink.js +134 -76
  74. package/dist/elements/Hyperlink.js.map +1 -1
  75. package/dist/elements/Image.d.ts.map +1 -1
  76. package/dist/elements/Image.js +238 -106
  77. package/dist/elements/Image.js.map +1 -1
  78. package/dist/elements/ImageManager.d.ts.map +1 -1
  79. package/dist/elements/ImageManager.js +1 -1
  80. package/dist/elements/ImageManager.js.map +1 -1
  81. package/dist/elements/ImageRun.js +1 -1
  82. package/dist/elements/ImageRun.js.map +1 -1
  83. package/dist/elements/MathElement.js.map +1 -1
  84. package/dist/elements/Paragraph.d.ts +24 -24
  85. package/dist/elements/Paragraph.d.ts.map +1 -1
  86. package/dist/elements/Paragraph.js +181 -188
  87. package/dist/elements/Paragraph.js.map +1 -1
  88. package/dist/elements/PreservedElement.js.map +1 -1
  89. package/dist/elements/PropertyChangeTypes.d.ts.map +1 -1
  90. package/dist/elements/PropertyChangeTypes.js +6 -6
  91. package/dist/elements/PropertyChangeTypes.js.map +1 -1
  92. package/dist/elements/RangeMarker.d.ts.map +1 -1
  93. package/dist/elements/RangeMarker.js.map +1 -1
  94. package/dist/elements/Revision.d.ts.map +1 -1
  95. package/dist/elements/Revision.js +4 -5
  96. package/dist/elements/Revision.js.map +1 -1
  97. package/dist/elements/RevisionContent.js.map +1 -1
  98. package/dist/elements/RevisionManager.d.ts.map +1 -1
  99. package/dist/elements/RevisionManager.js +40 -48
  100. package/dist/elements/RevisionManager.js.map +1 -1
  101. package/dist/elements/Run.d.ts +16 -16
  102. package/dist/elements/Run.d.ts.map +1 -1
  103. package/dist/elements/Run.js +256 -238
  104. package/dist/elements/Run.js.map +1 -1
  105. package/dist/elements/Section.d.ts.map +1 -1
  106. package/dist/elements/Section.js +36 -11
  107. package/dist/elements/Section.js.map +1 -1
  108. package/dist/elements/Shape.d.ts.map +1 -1
  109. package/dist/elements/Shape.js.map +1 -1
  110. package/dist/elements/StructuredDocumentTag.d.ts +6 -6
  111. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  112. package/dist/elements/StructuredDocumentTag.js +99 -104
  113. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  114. package/dist/elements/Table.d.ts +11 -11
  115. package/dist/elements/Table.d.ts.map +1 -1
  116. package/dist/elements/Table.js +102 -107
  117. package/dist/elements/Table.js.map +1 -1
  118. package/dist/elements/TableCell.d.ts +10 -10
  119. package/dist/elements/TableCell.d.ts.map +1 -1
  120. package/dist/elements/TableCell.js +105 -106
  121. package/dist/elements/TableCell.js.map +1 -1
  122. package/dist/elements/TableGridChange.d.ts.map +1 -1
  123. package/dist/elements/TableGridChange.js.map +1 -1
  124. package/dist/elements/TableOfContents.d.ts.map +1 -1
  125. package/dist/elements/TableOfContents.js +4 -4
  126. package/dist/elements/TableOfContents.js.map +1 -1
  127. package/dist/elements/TableOfContentsElement.js.map +1 -1
  128. package/dist/elements/TableRow.d.ts.map +1 -1
  129. package/dist/elements/TableRow.js +13 -6
  130. package/dist/elements/TableRow.js.map +1 -1
  131. package/dist/elements/TextBox.d.ts.map +1 -1
  132. package/dist/elements/TextBox.js +3 -5
  133. package/dist/elements/TextBox.js.map +1 -1
  134. package/dist/formatting/AbstractNumbering.d.ts +4 -4
  135. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  136. package/dist/formatting/AbstractNumbering.js +54 -49
  137. package/dist/formatting/AbstractNumbering.js.map +1 -1
  138. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  139. package/dist/formatting/NumberingInstance.js +1 -3
  140. package/dist/formatting/NumberingInstance.js.map +1 -1
  141. package/dist/formatting/NumberingLevel.d.ts +5 -5
  142. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  143. package/dist/formatting/NumberingLevel.js +119 -125
  144. package/dist/formatting/NumberingLevel.js.map +1 -1
  145. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  146. package/dist/formatting/NumberingManager.js +9 -9
  147. package/dist/formatting/NumberingManager.js.map +1 -1
  148. package/dist/formatting/Style.d.ts +11 -11
  149. package/dist/formatting/Style.d.ts.map +1 -1
  150. package/dist/formatting/Style.js +219 -247
  151. package/dist/formatting/Style.js.map +1 -1
  152. package/dist/formatting/StylesManager.d.ts +2 -2
  153. package/dist/formatting/StylesManager.d.ts.map +1 -1
  154. package/dist/formatting/StylesManager.js +96 -102
  155. package/dist/formatting/StylesManager.js.map +1 -1
  156. package/dist/helpers/CleanupHelper.d.ts +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  158. package/dist/helpers/CleanupHelper.js +6 -6
  159. package/dist/helpers/CleanupHelper.js.map +1 -1
  160. package/dist/images/ImageOptimizer.js +7 -7
  161. package/dist/images/ImageOptimizer.js.map +1 -1
  162. package/dist/index.d.ts +9 -9
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js.map +1 -1
  165. package/dist/managers/DrawingManager.js.map +1 -1
  166. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.js +23 -7
  168. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  169. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  170. package/dist/tracking/TrackingContext.js.map +1 -1
  171. package/dist/types/compatibility-types.js.map +1 -1
  172. package/dist/types/formatting.js.map +1 -1
  173. package/dist/types/list-types.d.ts +6 -6
  174. package/dist/types/list-types.js.map +1 -1
  175. package/dist/types/settings-types.js.map +1 -1
  176. package/dist/types/styleConfig.d.ts +2 -2
  177. package/dist/types/styleConfig.js.map +1 -1
  178. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  179. package/dist/utils/ChangelogGenerator.js +97 -101
  180. package/dist/utils/ChangelogGenerator.js.map +1 -1
  181. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.js +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  184. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  186. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  187. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  188. package/dist/utils/MoveOperationHelper.js +1 -1
  189. package/dist/utils/MoveOperationHelper.js.map +1 -1
  190. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.js +2 -4
  192. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  193. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  194. package/dist/utils/RevisionWalker.js +4 -12
  195. package/dist/utils/RevisionWalker.js.map +1 -1
  196. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  198. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  199. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  200. package/dist/utils/ShadingResolver.js +1 -1
  201. package/dist/utils/ShadingResolver.js.map +1 -1
  202. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  203. package/dist/utils/acceptRevisions.js +23 -12
  204. package/dist/utils/acceptRevisions.js.map +1 -1
  205. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  207. package/dist/utils/cnfStyleDecoder.js +40 -40
  208. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  209. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  210. package/dist/utils/corruptionDetection.js.map +1 -1
  211. package/dist/utils/dateFormatting.js.map +1 -1
  212. package/dist/utils/deepClone.js +1 -1
  213. package/dist/utils/deepClone.js.map +1 -1
  214. package/dist/utils/diagnostics.d.ts.map +1 -1
  215. package/dist/utils/diagnostics.js +1 -1
  216. package/dist/utils/diagnostics.js.map +1 -1
  217. package/dist/utils/errorHandling.js.map +1 -1
  218. package/dist/utils/formatting.d.ts.map +1 -1
  219. package/dist/utils/formatting.js +10 -2
  220. package/dist/utils/formatting.js.map +1 -1
  221. package/dist/utils/list-detection.d.ts +2 -2
  222. package/dist/utils/list-detection.d.ts.map +1 -1
  223. package/dist/utils/list-detection.js +21 -23
  224. package/dist/utils/list-detection.js.map +1 -1
  225. package/dist/utils/logger.d.ts.map +1 -1
  226. package/dist/utils/logger.js +12 -7
  227. package/dist/utils/logger.js.map +1 -1
  228. package/dist/utils/parsingHelpers.js.map +1 -1
  229. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  230. package/dist/utils/stripTrackedChanges.js +3 -3
  231. package/dist/utils/stripTrackedChanges.js.map +1 -1
  232. package/dist/utils/textDiff.d.ts +1 -1
  233. package/dist/utils/textDiff.js +8 -8
  234. package/dist/utils/textDiff.js.map +1 -1
  235. package/dist/utils/units.js.map +1 -1
  236. package/dist/utils/validation.d.ts.map +1 -1
  237. package/dist/utils/validation.js +24 -7
  238. package/dist/utils/validation.js.map +1 -1
  239. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  240. package/dist/utils/xmlSanitization.js +3 -3
  241. package/dist/utils/xmlSanitization.js.map +1 -1
  242. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.js +5 -5
  244. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  245. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  246. package/dist/validation/RevisionValidator.js +7 -9
  247. package/dist/validation/RevisionValidator.js.map +1 -1
  248. package/dist/validation/ValidationRules.js +3 -3
  249. package/dist/validation/ValidationRules.js.map +1 -1
  250. package/dist/validation/index.js.map +1 -1
  251. package/dist/xml/XMLBuilder.d.ts +1 -1
  252. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  253. package/dist/xml/XMLBuilder.js +98 -100
  254. package/dist/xml/XMLBuilder.js.map +1 -1
  255. package/dist/xml/XMLParser.d.ts.map +1 -1
  256. package/dist/xml/XMLParser.js +61 -66
  257. package/dist/xml/XMLParser.js.map +1 -1
  258. package/dist/zip/ZipHandler.d.ts.map +1 -1
  259. package/dist/zip/ZipHandler.js.map +1 -1
  260. package/dist/zip/ZipReader.d.ts.map +1 -1
  261. package/dist/zip/ZipReader.js +1 -3
  262. package/dist/zip/ZipReader.js.map +1 -1
  263. package/dist/zip/ZipWriter.d.ts +1 -1
  264. package/dist/zip/ZipWriter.d.ts.map +1 -1
  265. package/dist/zip/ZipWriter.js +28 -36
  266. package/dist/zip/ZipWriter.js.map +1 -1
  267. package/dist/zip/types.js +1 -1
  268. package/dist/zip/types.js.map +1 -1
  269. package/package.json +92 -92
  270. package/src/__tests__/helper-methods.test.ts +512 -512
  271. package/src/constants/legacyCompatFlags.ts +138 -138
  272. package/src/constants/limits.ts +50 -50
  273. package/src/core/Document.ts +985 -1145
  274. package/src/core/DocumentContent.ts +461 -467
  275. package/src/core/DocumentGenerator.ts +1133 -1104
  276. package/src/core/DocumentIdManager.ts +158 -158
  277. package/src/core/DocumentParser.ts +2347 -2716
  278. package/src/core/DocumentValidator.ts +363 -372
  279. package/src/core/Relationship.ts +367 -367
  280. package/src/core/RelationshipManager.ts +429 -428
  281. package/src/elements/AlternateContent.ts +42 -42
  282. package/src/elements/Bookmark.ts +212 -210
  283. package/src/elements/BookmarkManager.ts +247 -250
  284. package/src/elements/Comment.ts +356 -359
  285. package/src/elements/CommentManager.ts +499 -502
  286. package/src/elements/CommonTypes.ts +524 -549
  287. package/src/elements/CustomXml.ts +36 -36
  288. package/src/elements/Endnote.ts +221 -217
  289. package/src/elements/EndnoteManager.ts +246 -249
  290. package/src/elements/Field.ts +1292 -1233
  291. package/src/elements/FieldHelpers.ts +329 -333
  292. package/src/elements/FontManager.ts +336 -339
  293. package/src/elements/Footer.ts +269 -269
  294. package/src/elements/Footnote.ts +221 -217
  295. package/src/elements/FootnoteManager.ts +246 -249
  296. package/src/elements/Header.ts +269 -269
  297. package/src/elements/HeaderFooterManager.ts +219 -219
  298. package/src/elements/Hyperlink.ts +1288 -1193
  299. package/src/elements/Image.ts +1982 -1756
  300. package/src/elements/ImageManager.ts +437 -432
  301. package/src/elements/ImageRun.ts +59 -59
  302. package/src/elements/MathElement.ts +65 -65
  303. package/src/elements/Paragraph.ts +4347 -4287
  304. package/src/elements/PreservedElement.ts +53 -53
  305. package/src/elements/PropertyChangeTypes.ts +458 -442
  306. package/src/elements/RangeMarker.ts +382 -400
  307. package/src/elements/Revision.ts +1198 -1217
  308. package/src/elements/RevisionContent.ts +73 -73
  309. package/src/elements/RevisionManager.ts +1070 -1070
  310. package/src/elements/Run.ts +3103 -3073
  311. package/src/elements/Section.ts +1521 -1421
  312. package/src/elements/Shape.ts +884 -873
  313. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  314. package/src/elements/Table.ts +2468 -2524
  315. package/src/elements/TableCell.ts +1617 -1621
  316. package/src/elements/TableGridChange.ts +149 -151
  317. package/src/elements/TableOfContents.ts +701 -691
  318. package/src/elements/TableOfContentsElement.ts +89 -89
  319. package/src/elements/TableRow.ts +960 -929
  320. package/src/elements/TextBox.ts +766 -768
  321. package/src/formatting/AbstractNumbering.ts +580 -579
  322. package/src/formatting/NumberingInstance.ts +295 -299
  323. package/src/formatting/NumberingLevel.ts +981 -1040
  324. package/src/formatting/NumberingManager.ts +833 -827
  325. package/src/formatting/Style.ts +1785 -1879
  326. package/src/formatting/StylesManager.ts +1090 -1130
  327. package/src/helpers/CleanupHelper.ts +524 -524
  328. package/src/images/ImageOptimizer.ts +274 -274
  329. package/src/index.ts +559 -554
  330. package/src/managers/DrawingManager.ts +319 -319
  331. package/src/tracking/DocumentTrackingContext.ts +687 -674
  332. package/src/tracking/TrackingContext.ts +175 -173
  333. package/src/types/compatibility-types.ts +49 -49
  334. package/src/types/formatting.ts +210 -210
  335. package/src/types/list-types.ts +14 -14
  336. package/src/types/settings-types.ts +59 -59
  337. package/src/types/styleConfig.ts +189 -189
  338. package/src/utils/ChangelogGenerator.ts +1583 -1581
  339. package/src/utils/CompatibilityUpgrader.ts +235 -237
  340. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  341. package/src/utils/MoveOperationHelper.ts +233 -238
  342. package/src/utils/RevisionAwareProcessor.ts +518 -526
  343. package/src/utils/RevisionWalker.ts +427 -457
  344. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  345. package/src/utils/ShadingResolver.ts +105 -107
  346. package/src/utils/acceptRevisions.ts +723 -714
  347. package/src/utils/cnfStyleDecoder.ts +212 -217
  348. package/src/utils/corruptionDetection.ts +346 -345
  349. package/src/utils/dateFormatting.ts +20 -20
  350. package/src/utils/deepClone.ts +77 -78
  351. package/src/utils/diagnostics.ts +125 -129
  352. package/src/utils/errorHandling.ts +80 -80
  353. package/src/utils/formatting.ts +220 -213
  354. package/src/utils/list-detection.ts +32 -42
  355. package/src/utils/logger.ts +412 -404
  356. package/src/utils/parsingHelpers.ts +190 -190
  357. package/src/utils/stripTrackedChanges.ts +356 -353
  358. package/src/utils/textDiff.ts +100 -100
  359. package/src/utils/units.ts +421 -421
  360. package/src/utils/validation.ts +553 -542
  361. package/src/utils/xmlSanitization.ts +179 -182
  362. package/src/validation/RevisionAutoFixer.ts +541 -542
  363. package/src/validation/RevisionValidator.ts +470 -460
  364. package/src/validation/ValidationRules.ts +338 -338
  365. package/src/validation/index.ts +30 -30
  366. package/src/xml/XMLBuilder.ts +857 -871
  367. package/src/xml/XMLParser.ts +877 -919
  368. package/src/zip/ZipHandler.ts +629 -637
  369. package/src/zip/ZipReader.ts +295 -299
  370. package/src/zip/ZipWriter.ts +374 -390
  371. package/src/zip/types.ts +116 -116
@@ -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
+ }