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,457 +1,427 @@
1
- /**
2
- * RevisionWalker - DOM-based tree walker for accepting tracked changes
3
- *
4
- * Replaces the fragile RegEx-based revision acceptance with a robust
5
- * DOM-based approach that properly handles nested elements and preserves
6
- * element ordering.
7
- *
8
- * @module RevisionWalker
9
- */
10
-
11
- import { ParsedXMLObject } from '../xml/XMLParser';
12
-
13
- /**
14
- * Options for controlling which revision types to accept
15
- */
16
- export interface RevisionWalkerOptions {
17
- /** Keep content, remove w:ins wrapper (default: true) */
18
- acceptInsertions?: boolean;
19
- /** Remove w:del and content entirely (default: true) */
20
- acceptDeletions?: boolean;
21
- /** Handle w:moveFrom/w:moveTo (default: true) */
22
- acceptMoves?: boolean;
23
- /** Remove *Change elements (default: true) */
24
- acceptPropertyChanges?: boolean;
25
- }
26
-
27
- /**
28
- * Structure for tracking element order
29
- */
30
- interface OrderedChildInfo {
31
- type: string;
32
- index: number;
33
- }
34
-
35
- /**
36
- * Revision element categories
37
- */
38
- const REVISION_ELEMENTS = {
39
- /** Elements to unwrap (keep content, remove wrapper) */
40
- UNWRAP: ['w:ins', 'w:moveTo'],
41
-
42
- /** Elements to remove entirely (with content) */
43
- REMOVE: ['w:del', 'w:moveFrom'],
44
-
45
- /** Property change tracking elements */
46
- PROPERTY_CHANGES: [
47
- 'w:rPrChange',
48
- 'w:pPrChange',
49
- 'w:tblPrChange',
50
- 'w:tcPrChange',
51
- 'w:trPrChange',
52
- 'w:sectPrChange',
53
- 'w:tblGridChange',
54
- 'w:numberingChange',
55
- 'w:tblPrExChange',
56
- ],
57
-
58
- /** Range marker elements */
59
- RANGE_MARKERS: [
60
- 'w:moveFromRangeStart',
61
- 'w:moveFromRangeEnd',
62
- 'w:moveToRangeStart',
63
- 'w:moveToRangeEnd',
64
- 'w:customXmlInsRangeStart',
65
- 'w:customXmlInsRangeEnd',
66
- 'w:customXmlDelRangeStart',
67
- 'w:customXmlDelRangeEnd',
68
- 'w:customXmlMoveFromRangeStart',
69
- 'w:customXmlMoveFromRangeEnd',
70
- 'w:customXmlMoveToRangeStart',
71
- 'w:customXmlMoveToRangeEnd',
72
- ],
73
- };
74
-
75
- /**
76
- * DOM-based tree walker for accepting Word document revisions
77
- *
78
- * This class processes a parsed XML object tree (from XMLParser.parseToObject())
79
- * and accepts all tracked changes by:
80
- * - Unwrapping insertions (w:ins, w:moveTo) - keeping content
81
- * - Removing deletions (w:del, w:moveFrom) - discarding content
82
- * - Removing property changes (*Change elements)
83
- * - Removing range markers
84
- *
85
- * Element order is preserved using the _orderedChildren metadata.
86
- */
87
- export class RevisionWalker {
88
- /**
89
- * Process a parsed XML object tree and accept all revisions
90
- *
91
- * @param obj - Parsed XML object from XMLParser.parseToObject()
92
- * @param options - Options controlling which revisions to accept
93
- * @returns New object tree with revisions accepted
94
- *
95
- * @example
96
- * ```typescript
97
- * const parsed = XMLParser.parseToObject(documentXml);
98
- * const clean = RevisionWalker.processTree(parsed);
99
- * ```
100
- */
101
- static processTree(
102
- obj: ParsedXMLObject,
103
- options?: RevisionWalkerOptions
104
- ): ParsedXMLObject {
105
- const opts: Required<RevisionWalkerOptions> = {
106
- acceptInsertions: options?.acceptInsertions ?? true,
107
- acceptDeletions: options?.acceptDeletions ?? true,
108
- acceptMoves: options?.acceptMoves ?? true,
109
- acceptPropertyChanges: options?.acceptPropertyChanges ?? true,
110
- };
111
-
112
- // Deep clone the object to avoid mutating the original
113
- const clone = RevisionWalker.deepClone(obj);
114
-
115
- // Walk and transform the tree
116
- RevisionWalker.walkAndTransform(clone, opts);
117
-
118
- return clone;
119
- }
120
-
121
- /**
122
- * Deep clone an object
123
- */
124
- private static deepClone(obj: any): any {
125
- if (obj === null || typeof obj !== 'object') {
126
- return obj;
127
- }
128
-
129
- if (Array.isArray(obj)) {
130
- return obj.map((item) => RevisionWalker.deepClone(item));
131
- }
132
-
133
- const clone: any = {};
134
- for (const key of Object.keys(obj)) {
135
- clone[key] = RevisionWalker.deepClone(obj[key]);
136
- }
137
- return clone;
138
- }
139
-
140
- /**
141
- * Recursively walk and transform the object tree
142
- * Processes children first (depth-first) to handle nested revisions
143
- */
144
- private static walkAndTransform(
145
- obj: any,
146
- options: Required<RevisionWalkerOptions>
147
- ): void {
148
- if (obj === null || typeof obj !== 'object') {
149
- return;
150
- }
151
-
152
- // Get keys to process (excluding metadata keys)
153
- const keys = Object.keys(obj).filter(
154
- (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
155
- );
156
-
157
- // First pass: recurse into children (depth-first)
158
- for (const key of keys) {
159
- const value = obj[key];
160
- if (Array.isArray(value)) {
161
- for (const item of value) {
162
- RevisionWalker.walkAndTransform(item, options);
163
- }
164
- } else if (typeof value === 'object' && value !== null) {
165
- RevisionWalker.walkAndTransform(value, options);
166
- }
167
- }
168
-
169
- // Second pass: process revision elements at this level
170
- // We need to iterate carefully because we're modifying the object
171
- RevisionWalker.processRevisions(obj, options);
172
- }
173
-
174
- /**
175
- * Process revision elements at the current level
176
- */
177
- private static processRevisions(
178
- parent: any,
179
- options: Required<RevisionWalkerOptions>
180
- ): void {
181
- if (!parent || typeof parent !== 'object') {
182
- return;
183
- }
184
-
185
- const keysToProcess = Object.keys(parent).filter(
186
- (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
187
- );
188
-
189
- for (const key of keysToProcess) {
190
- // Check if this is a revision element
191
- if (RevisionWalker.shouldUnwrap(key, options)) {
192
- RevisionWalker.unwrapAllElements(parent, key);
193
- } else if (RevisionWalker.shouldRemove(key, options)) {
194
- RevisionWalker.removeAllElements(parent, key);
195
- }
196
- }
197
- }
198
-
199
- /**
200
- * Check if an element should be unwrapped (content kept)
201
- */
202
- private static shouldUnwrap(
203
- key: string,
204
- options: Required<RevisionWalkerOptions>
205
- ): boolean {
206
- if (REVISION_ELEMENTS.UNWRAP.includes(key)) {
207
- if (key === 'w:ins' && !options.acceptInsertions) return false;
208
- if (key === 'w:moveTo' && !options.acceptMoves) return false;
209
- return true;
210
- }
211
- return false;
212
- }
213
-
214
- /**
215
- * Check if an element should be removed (content discarded)
216
- */
217
- private static shouldRemove(
218
- key: string,
219
- options: Required<RevisionWalkerOptions>
220
- ): boolean {
221
- if (REVISION_ELEMENTS.REMOVE.includes(key)) {
222
- if (key === 'w:del' && !options.acceptDeletions) return false;
223
- if (key === 'w:moveFrom' && !options.acceptMoves) return false;
224
- return true;
225
- }
226
- if (REVISION_ELEMENTS.PROPERTY_CHANGES.includes(key)) {
227
- return options.acceptPropertyChanges;
228
- }
229
- if (REVISION_ELEMENTS.RANGE_MARKERS.includes(key)) {
230
- return true; // Always remove range markers
231
- }
232
- return false;
233
- }
234
-
235
- /**
236
- * Unwrap all elements of a given type, promoting their children to parent
237
- *
238
- * This is the most complex operation in RevisionWalker. When we unwrap a
239
- * revision element like w:ins, we need to:
240
- * 1. Extract the children from inside w:ins
241
- * 2. Insert them at the correct position in the parent's element arrays
242
- * 3. Update _orderedChildren to reflect the new structure
243
- *
244
- * The key challenge is maintaining correct element order. For example:
245
- * Before: <w:tbl><w:tr>Row1</w:tr><w:ins><w:tr>Row2</w:tr></w:ins><w:tr>Row3</w:tr></w:tbl>
246
- * After: <w:tbl><w:tr>Row1</w:tr><w:tr>Row2</w:tr><w:tr>Row3</w:tr></w:tbl>
247
- */
248
- private static unwrapAllElements(parent: any, key: string): void {
249
- const elements = parent[key];
250
- if (!elements) return;
251
-
252
- const elementArray = Array.isArray(elements) ? elements : [elements];
253
-
254
- // Build a complete ordered list of child elements by walking _orderedChildren
255
- // This captures the intended order before we modify anything
256
- const orderedElements: { type: string; element: any }[] = [];
257
-
258
- if (parent._orderedChildren) {
259
- // Track how many of each type we've seen to get correct array index
260
- const typeCounters = new Map<string, number>();
261
- let unwrappedIndex = 0;
262
-
263
- for (const entry of parent._orderedChildren) {
264
- const { type } = entry;
265
-
266
- if (type === key) {
267
- // This is a revision element - extract its children in order
268
- const revElement = elementArray[unwrappedIndex];
269
- unwrappedIndex++;
270
-
271
- if (revElement && typeof revElement === 'object') {
272
- if (revElement._orderedChildren) {
273
- // Use the revision element's _orderedChildren for correct order
274
- const childCounters = new Map<string, number>();
275
- for (const childEntry of revElement._orderedChildren) {
276
- const childType = childEntry.type;
277
- const childIdx = childCounters.get(childType) || 0;
278
- childCounters.set(childType, childIdx + 1);
279
-
280
- const childElements = revElement[childType];
281
- if (childElements) {
282
- const childArray = Array.isArray(childElements)
283
- ? childElements
284
- : [childElements];
285
- if (childIdx < childArray.length) {
286
- orderedElements.push({
287
- type: childType,
288
- element: childArray[childIdx],
289
- });
290
- }
291
- }
292
- }
293
- } else {
294
- // No _orderedChildren, extract children in object key order
295
- const childKeys = Object.keys(revElement).filter(
296
- (k) =>
297
- !k.startsWith('@_') &&
298
- k !== '#text' &&
299
- k !== '_orderedChildren'
300
- );
301
- for (const childKey of childKeys) {
302
- const childValue = revElement[childKey];
303
- const childArray = Array.isArray(childValue)
304
- ? childValue
305
- : [childValue];
306
- for (const child of childArray) {
307
- orderedElements.push({ type: childKey, element: child });
308
- }
309
- }
310
- }
311
- }
312
- } else {
313
- // Regular element - get it from the parent
314
- const idx = typeCounters.get(type) || 0;
315
- typeCounters.set(type, idx + 1);
316
-
317
- const parentElements = parent[type];
318
- if (parentElements) {
319
- const parentArray = Array.isArray(parentElements)
320
- ? parentElements
321
- : [parentElements];
322
- if (idx < parentArray.length) {
323
- orderedElements.push({ type, element: parentArray[idx] });
324
- }
325
- }
326
- }
327
- }
328
- }
329
-
330
- // Remove the revision wrapper
331
- delete parent[key];
332
-
333
- // If we have ordered elements, rebuild the arrays in correct order
334
- if (orderedElements.length > 0) {
335
- // Group elements by type
336
- const rebuiltArrays = new Map<string, any[]>();
337
-
338
- for (const { type, element } of orderedElements) {
339
- if (!rebuiltArrays.has(type)) {
340
- rebuiltArrays.set(type, []);
341
- }
342
- rebuiltArrays.get(type)!.push(element);
343
- }
344
-
345
- // Update parent with rebuilt arrays
346
- for (const [type, elements] of rebuiltArrays) {
347
- if (elements.length === 1) {
348
- parent[type] = elements[0];
349
- } else {
350
- parent[type] = elements;
351
- }
352
- }
353
-
354
- // Rebuild _orderedChildren
355
- const newOrderedChildren: OrderedChildInfo[] = [];
356
- const typeCounters = new Map<string, number>();
357
-
358
- for (const { type } of orderedElements) {
359
- const idx = typeCounters.get(type) || 0;
360
- typeCounters.set(type, idx + 1);
361
- newOrderedChildren.push({ type, index: idx });
362
- }
363
-
364
- parent._orderedChildren = newOrderedChildren;
365
- } else {
366
- // Fallback for when there's no _orderedChildren - just merge
367
- for (const element of elementArray) {
368
- if (!element || typeof element !== 'object') continue;
369
-
370
- const elementKeys = Object.keys(element).filter(
371
- (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
372
- );
373
-
374
- for (const childKey of elementKeys) {
375
- RevisionWalker.mergeIntoParent(parent, childKey, element[childKey]);
376
- }
377
- }
378
- }
379
- }
380
-
381
- /**
382
- * Remove all elements of a given type (including their content)
383
- */
384
- private static removeAllElements(parent: any, key: string): void {
385
- if (!parent[key]) return;
386
-
387
- // Update _orderedChildren before removing
388
- if (parent._orderedChildren) {
389
- parent._orderedChildren = parent._orderedChildren.filter(
390
- (c: OrderedChildInfo) => c.type !== key
391
- );
392
- // Re-index remaining elements of same types
393
- RevisionWalker.reindexOrderedChildren(parent._orderedChildren);
394
- }
395
-
396
- // Remove the element
397
- delete parent[key];
398
- }
399
-
400
- /**
401
- * Merge a child value into the parent, handling arrays properly
402
- */
403
- private static mergeIntoParent(
404
- parent: any,
405
- childKey: string,
406
- childValue: any
407
- ): void {
408
- if (parent[childKey] === undefined) {
409
- // No existing value, just assign
410
- parent[childKey] = childValue;
411
- } else {
412
- // Existing value, need to merge
413
- const existing = parent[childKey];
414
- const incoming = Array.isArray(childValue) ? childValue : [childValue];
415
-
416
- if (Array.isArray(existing)) {
417
- parent[childKey] = [...existing, ...incoming];
418
- } else {
419
- parent[childKey] = [existing, ...incoming];
420
- }
421
- }
422
- }
423
-
424
- /**
425
- * Re-index _orderedChildren to ensure indices are sequential per type
426
- */
427
- private static reindexOrderedChildren(
428
- orderedChildren: OrderedChildInfo[]
429
- ): void {
430
- const typeCounters = new Map<string, number>();
431
-
432
- for (const entry of orderedChildren) {
433
- const currentIndex = typeCounters.get(entry.type) || 0;
434
- entry.index = currentIndex;
435
- typeCounters.set(entry.type, currentIndex + 1);
436
- }
437
- }
438
-
439
- /**
440
- * Check if an element is a revision-related element (any type)
441
- */
442
- static isRevisionElement(key: string): boolean {
443
- return (
444
- REVISION_ELEMENTS.UNWRAP.includes(key) ||
445
- REVISION_ELEMENTS.REMOVE.includes(key) ||
446
- REVISION_ELEMENTS.PROPERTY_CHANGES.includes(key) ||
447
- REVISION_ELEMENTS.RANGE_MARKERS.includes(key)
448
- );
449
- }
450
-
451
- /**
452
- * Get revision element categories (for external use/testing)
453
- */
454
- static getRevisionElementCategories(): typeof REVISION_ELEMENTS {
455
- return { ...REVISION_ELEMENTS };
456
- }
457
- }
1
+ /**
2
+ * RevisionWalker - DOM-based tree walker for accepting tracked changes
3
+ *
4
+ * Replaces the fragile RegEx-based revision acceptance with a robust
5
+ * DOM-based approach that properly handles nested elements and preserves
6
+ * element ordering.
7
+ *
8
+ * @module RevisionWalker
9
+ */
10
+
11
+ import { ParsedXMLObject } from '../xml/XMLParser';
12
+
13
+ /**
14
+ * Options for controlling which revision types to accept
15
+ */
16
+ export interface RevisionWalkerOptions {
17
+ /** Keep content, remove w:ins wrapper (default: true) */
18
+ acceptInsertions?: boolean;
19
+ /** Remove w:del and content entirely (default: true) */
20
+ acceptDeletions?: boolean;
21
+ /** Handle w:moveFrom/w:moveTo (default: true) */
22
+ acceptMoves?: boolean;
23
+ /** Remove *Change elements (default: true) */
24
+ acceptPropertyChanges?: boolean;
25
+ }
26
+
27
+ /**
28
+ * Structure for tracking element order
29
+ */
30
+ interface OrderedChildInfo {
31
+ type: string;
32
+ index: number;
33
+ }
34
+
35
+ /**
36
+ * Revision element categories
37
+ */
38
+ const REVISION_ELEMENTS = {
39
+ /** Elements to unwrap (keep content, remove wrapper) */
40
+ UNWRAP: ['w:ins', 'w:moveTo'],
41
+
42
+ /** Elements to remove entirely (with content) */
43
+ REMOVE: ['w:del', 'w:moveFrom'],
44
+
45
+ /** Property change tracking elements */
46
+ PROPERTY_CHANGES: [
47
+ 'w:rPrChange',
48
+ 'w:pPrChange',
49
+ 'w:tblPrChange',
50
+ 'w:tcPrChange',
51
+ 'w:trPrChange',
52
+ 'w:sectPrChange',
53
+ 'w:tblGridChange',
54
+ 'w:numberingChange',
55
+ 'w:tblPrExChange',
56
+ ],
57
+
58
+ /** Range marker elements */
59
+ RANGE_MARKERS: [
60
+ 'w:moveFromRangeStart',
61
+ 'w:moveFromRangeEnd',
62
+ 'w:moveToRangeStart',
63
+ 'w:moveToRangeEnd',
64
+ 'w:customXmlInsRangeStart',
65
+ 'w:customXmlInsRangeEnd',
66
+ 'w:customXmlDelRangeStart',
67
+ 'w:customXmlDelRangeEnd',
68
+ 'w:customXmlMoveFromRangeStart',
69
+ 'w:customXmlMoveFromRangeEnd',
70
+ 'w:customXmlMoveToRangeStart',
71
+ 'w:customXmlMoveToRangeEnd',
72
+ ],
73
+ };
74
+
75
+ /**
76
+ * DOM-based tree walker for accepting Word document revisions
77
+ *
78
+ * This class processes a parsed XML object tree (from XMLParser.parseToObject())
79
+ * and accepts all tracked changes by:
80
+ * - Unwrapping insertions (w:ins, w:moveTo) - keeping content
81
+ * - Removing deletions (w:del, w:moveFrom) - discarding content
82
+ * - Removing property changes (*Change elements)
83
+ * - Removing range markers
84
+ *
85
+ * Element order is preserved using the _orderedChildren metadata.
86
+ */
87
+ export class RevisionWalker {
88
+ /**
89
+ * Process a parsed XML object tree and accept all revisions
90
+ *
91
+ * @param obj - Parsed XML object from XMLParser.parseToObject()
92
+ * @param options - Options controlling which revisions to accept
93
+ * @returns New object tree with revisions accepted
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const parsed = XMLParser.parseToObject(documentXml);
98
+ * const clean = RevisionWalker.processTree(parsed);
99
+ * ```
100
+ */
101
+ static processTree(obj: ParsedXMLObject, options?: RevisionWalkerOptions): ParsedXMLObject {
102
+ const opts: Required<RevisionWalkerOptions> = {
103
+ acceptInsertions: options?.acceptInsertions ?? true,
104
+ acceptDeletions: options?.acceptDeletions ?? true,
105
+ acceptMoves: options?.acceptMoves ?? true,
106
+ acceptPropertyChanges: options?.acceptPropertyChanges ?? true,
107
+ };
108
+
109
+ // Deep clone the object to avoid mutating the original
110
+ const clone = RevisionWalker.deepClone(obj);
111
+
112
+ // Walk and transform the tree
113
+ RevisionWalker.walkAndTransform(clone, opts);
114
+
115
+ return clone;
116
+ }
117
+
118
+ /**
119
+ * Deep clone an object
120
+ */
121
+ private static deepClone(obj: any): any {
122
+ if (obj === null || typeof obj !== 'object') {
123
+ return obj;
124
+ }
125
+
126
+ if (Array.isArray(obj)) {
127
+ return obj.map((item) => RevisionWalker.deepClone(item));
128
+ }
129
+
130
+ const clone: any = {};
131
+ for (const key of Object.keys(obj)) {
132
+ clone[key] = RevisionWalker.deepClone(obj[key]);
133
+ }
134
+ return clone;
135
+ }
136
+
137
+ /**
138
+ * Recursively walk and transform the object tree
139
+ * Processes children first (depth-first) to handle nested revisions
140
+ */
141
+ private static walkAndTransform(obj: any, options: Required<RevisionWalkerOptions>): void {
142
+ if (obj === null || typeof obj !== 'object') {
143
+ return;
144
+ }
145
+
146
+ // Get keys to process (excluding metadata keys)
147
+ const keys = Object.keys(obj).filter(
148
+ (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
149
+ );
150
+
151
+ // First pass: recurse into children (depth-first)
152
+ for (const key of keys) {
153
+ const value = obj[key];
154
+ if (Array.isArray(value)) {
155
+ for (const item of value) {
156
+ RevisionWalker.walkAndTransform(item, options);
157
+ }
158
+ } else if (typeof value === 'object' && value !== null) {
159
+ RevisionWalker.walkAndTransform(value, options);
160
+ }
161
+ }
162
+
163
+ // Second pass: process revision elements at this level
164
+ // We need to iterate carefully because we're modifying the object
165
+ RevisionWalker.processRevisions(obj, options);
166
+ }
167
+
168
+ /**
169
+ * Process revision elements at the current level
170
+ */
171
+ private static processRevisions(parent: any, options: Required<RevisionWalkerOptions>): void {
172
+ if (!parent || typeof parent !== 'object') {
173
+ return;
174
+ }
175
+
176
+ const keysToProcess = Object.keys(parent).filter(
177
+ (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
178
+ );
179
+
180
+ for (const key of keysToProcess) {
181
+ // Check if this is a revision element
182
+ if (RevisionWalker.shouldUnwrap(key, options)) {
183
+ RevisionWalker.unwrapAllElements(parent, key);
184
+ } else if (RevisionWalker.shouldRemove(key, options)) {
185
+ RevisionWalker.removeAllElements(parent, key);
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Check if an element should be unwrapped (content kept)
192
+ */
193
+ private static shouldUnwrap(key: string, options: Required<RevisionWalkerOptions>): boolean {
194
+ if (REVISION_ELEMENTS.UNWRAP.includes(key)) {
195
+ if (key === 'w:ins' && !options.acceptInsertions) return false;
196
+ if (key === 'w:moveTo' && !options.acceptMoves) return false;
197
+ return true;
198
+ }
199
+ return false;
200
+ }
201
+
202
+ /**
203
+ * Check if an element should be removed (content discarded)
204
+ */
205
+ private static shouldRemove(key: string, options: Required<RevisionWalkerOptions>): boolean {
206
+ if (REVISION_ELEMENTS.REMOVE.includes(key)) {
207
+ if (key === 'w:del' && !options.acceptDeletions) return false;
208
+ if (key === 'w:moveFrom' && !options.acceptMoves) return false;
209
+ return true;
210
+ }
211
+ if (REVISION_ELEMENTS.PROPERTY_CHANGES.includes(key)) {
212
+ return options.acceptPropertyChanges;
213
+ }
214
+ if (REVISION_ELEMENTS.RANGE_MARKERS.includes(key)) {
215
+ return true; // Always remove range markers
216
+ }
217
+ return false;
218
+ }
219
+
220
+ /**
221
+ * Unwrap all elements of a given type, promoting their children to parent
222
+ *
223
+ * This is the most complex operation in RevisionWalker. When we unwrap a
224
+ * revision element like w:ins, we need to:
225
+ * 1. Extract the children from inside w:ins
226
+ * 2. Insert them at the correct position in the parent's element arrays
227
+ * 3. Update _orderedChildren to reflect the new structure
228
+ *
229
+ * The key challenge is maintaining correct element order. For example:
230
+ * Before: <w:tbl><w:tr>Row1</w:tr><w:ins><w:tr>Row2</w:tr></w:ins><w:tr>Row3</w:tr></w:tbl>
231
+ * After: <w:tbl><w:tr>Row1</w:tr><w:tr>Row2</w:tr><w:tr>Row3</w:tr></w:tbl>
232
+ */
233
+ private static unwrapAllElements(parent: any, key: string): void {
234
+ const elements = parent[key];
235
+ if (!elements) return;
236
+
237
+ const elementArray = Array.isArray(elements) ? elements : [elements];
238
+
239
+ // Build a complete ordered list of child elements by walking _orderedChildren
240
+ // This captures the intended order before we modify anything
241
+ const orderedElements: { type: string; element: any }[] = [];
242
+
243
+ if (parent._orderedChildren) {
244
+ // Track how many of each type we've seen to get correct array index
245
+ const typeCounters = new Map<string, number>();
246
+ let unwrappedIndex = 0;
247
+
248
+ for (const entry of parent._orderedChildren) {
249
+ const { type } = entry;
250
+
251
+ if (type === key) {
252
+ // This is a revision element - extract its children in order
253
+ const revElement = elementArray[unwrappedIndex];
254
+ unwrappedIndex++;
255
+
256
+ if (revElement && typeof revElement === 'object') {
257
+ if (revElement._orderedChildren) {
258
+ // Use the revision element's _orderedChildren for correct order
259
+ const childCounters = new Map<string, number>();
260
+ for (const childEntry of revElement._orderedChildren) {
261
+ const childType = childEntry.type;
262
+ const childIdx = childCounters.get(childType) || 0;
263
+ childCounters.set(childType, childIdx + 1);
264
+
265
+ const childElements = revElement[childType];
266
+ if (childElements) {
267
+ const childArray = Array.isArray(childElements) ? childElements : [childElements];
268
+ if (childIdx < childArray.length) {
269
+ orderedElements.push({
270
+ type: childType,
271
+ element: childArray[childIdx],
272
+ });
273
+ }
274
+ }
275
+ }
276
+ } else {
277
+ // No _orderedChildren, extract children in object key order
278
+ const childKeys = Object.keys(revElement).filter(
279
+ (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
280
+ );
281
+ for (const childKey of childKeys) {
282
+ const childValue = revElement[childKey];
283
+ const childArray = Array.isArray(childValue) ? childValue : [childValue];
284
+ for (const child of childArray) {
285
+ orderedElements.push({ type: childKey, element: child });
286
+ }
287
+ }
288
+ }
289
+ }
290
+ } else {
291
+ // Regular element - get it from the parent
292
+ const idx = typeCounters.get(type) || 0;
293
+ typeCounters.set(type, idx + 1);
294
+
295
+ const parentElements = parent[type];
296
+ if (parentElements) {
297
+ const parentArray = Array.isArray(parentElements) ? parentElements : [parentElements];
298
+ if (idx < parentArray.length) {
299
+ orderedElements.push({ type, element: parentArray[idx] });
300
+ }
301
+ }
302
+ }
303
+ }
304
+ }
305
+
306
+ // Remove the revision wrapper
307
+ delete parent[key];
308
+
309
+ // If we have ordered elements, rebuild the arrays in correct order
310
+ if (orderedElements.length > 0) {
311
+ // Group elements by type
312
+ const rebuiltArrays = new Map<string, any[]>();
313
+
314
+ for (const { type, element } of orderedElements) {
315
+ if (!rebuiltArrays.has(type)) {
316
+ rebuiltArrays.set(type, []);
317
+ }
318
+ rebuiltArrays.get(type)!.push(element);
319
+ }
320
+
321
+ // Update parent with rebuilt arrays
322
+ for (const [type, elements] of rebuiltArrays) {
323
+ if (elements.length === 1) {
324
+ parent[type] = elements[0];
325
+ } else {
326
+ parent[type] = elements;
327
+ }
328
+ }
329
+
330
+ // Rebuild _orderedChildren
331
+ const newOrderedChildren: OrderedChildInfo[] = [];
332
+ const typeCounters = new Map<string, number>();
333
+
334
+ for (const { type } of orderedElements) {
335
+ const idx = typeCounters.get(type) || 0;
336
+ typeCounters.set(type, idx + 1);
337
+ newOrderedChildren.push({ type, index: idx });
338
+ }
339
+
340
+ parent._orderedChildren = newOrderedChildren;
341
+ } else {
342
+ // Fallback for when there's no _orderedChildren - just merge
343
+ for (const element of elementArray) {
344
+ if (!element || typeof element !== 'object') continue;
345
+
346
+ const elementKeys = Object.keys(element).filter(
347
+ (k) => !k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren'
348
+ );
349
+
350
+ for (const childKey of elementKeys) {
351
+ RevisionWalker.mergeIntoParent(parent, childKey, element[childKey]);
352
+ }
353
+ }
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Remove all elements of a given type (including their content)
359
+ */
360
+ private static removeAllElements(parent: any, key: string): void {
361
+ if (!parent[key]) return;
362
+
363
+ // Update _orderedChildren before removing
364
+ if (parent._orderedChildren) {
365
+ parent._orderedChildren = parent._orderedChildren.filter(
366
+ (c: OrderedChildInfo) => c.type !== key
367
+ );
368
+ // Re-index remaining elements of same types
369
+ RevisionWalker.reindexOrderedChildren(parent._orderedChildren);
370
+ }
371
+
372
+ // Remove the element
373
+ delete parent[key];
374
+ }
375
+
376
+ /**
377
+ * Merge a child value into the parent, handling arrays properly
378
+ */
379
+ private static mergeIntoParent(parent: any, childKey: string, childValue: any): void {
380
+ if (parent[childKey] === undefined) {
381
+ // No existing value, just assign
382
+ parent[childKey] = childValue;
383
+ } else {
384
+ // Existing value, need to merge
385
+ const existing = parent[childKey];
386
+ const incoming = Array.isArray(childValue) ? childValue : [childValue];
387
+
388
+ if (Array.isArray(existing)) {
389
+ parent[childKey] = [...existing, ...incoming];
390
+ } else {
391
+ parent[childKey] = [existing, ...incoming];
392
+ }
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Re-index _orderedChildren to ensure indices are sequential per type
398
+ */
399
+ private static reindexOrderedChildren(orderedChildren: OrderedChildInfo[]): void {
400
+ const typeCounters = new Map<string, number>();
401
+
402
+ for (const entry of orderedChildren) {
403
+ const currentIndex = typeCounters.get(entry.type) || 0;
404
+ entry.index = currentIndex;
405
+ typeCounters.set(entry.type, currentIndex + 1);
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Check if an element is a revision-related element (any type)
411
+ */
412
+ static isRevisionElement(key: string): boolean {
413
+ return (
414
+ REVISION_ELEMENTS.UNWRAP.includes(key) ||
415
+ REVISION_ELEMENTS.REMOVE.includes(key) ||
416
+ REVISION_ELEMENTS.PROPERTY_CHANGES.includes(key) ||
417
+ REVISION_ELEMENTS.RANGE_MARKERS.includes(key)
418
+ );
419
+ }
420
+
421
+ /**
422
+ * Get revision element categories (for external use/testing)
423
+ */
424
+ static getRevisionElementCategories(): typeof REVISION_ELEMENTS {
425
+ return { ...REVISION_ELEMENTS };
426
+ }
427
+ }