@usejunior/docx-core 0.9.1 → 0.11.0

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 (335) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +2 -0
  3. package/README.md +2 -2
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/atomizer.d.ts +28 -8
  6. package/dist/atomizer.d.ts.map +1 -1
  7. package/dist/atomizer.js +96 -25
  8. package/dist/atomizer.js.map +1 -1
  9. package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts +99 -0
  10. package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts.map +1 -0
  11. package/dist/baselines/atomizer/auxiliaryIdCollision.js +415 -0
  12. package/dist/baselines/atomizer/auxiliaryIdCollision.js.map +1 -0
  13. package/dist/baselines/atomizer/documentReconstructor.d.ts.map +1 -1
  14. package/dist/baselines/atomizer/documentReconstructor.js +333 -112
  15. package/dist/baselines/atomizer/documentReconstructor.js.map +1 -1
  16. package/dist/baselines/atomizer/formattingFidelity.d.ts +99 -0
  17. package/dist/baselines/atomizer/formattingFidelity.d.ts.map +1 -0
  18. package/dist/baselines/atomizer/formattingFidelity.js +449 -0
  19. package/dist/baselines/atomizer/formattingFidelity.js.map +1 -0
  20. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts +37 -0
  21. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts.map +1 -0
  22. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js +189 -0
  23. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js.map +1 -0
  24. package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts +74 -0
  25. package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts.map +1 -0
  26. package/dist/baselines/atomizer/inPlaceModifier-containers.js +171 -0
  27. package/dist/baselines/atomizer/inPlaceModifier-containers.js.map +1 -0
  28. package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts +88 -0
  29. package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts.map +1 -0
  30. package/dist/baselines/atomizer/inPlaceModifier-deletion.js +326 -0
  31. package/dist/baselines/atomizer/inPlaceModifier-deletion.js.map +1 -0
  32. package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts +85 -0
  33. package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts.map +1 -0
  34. package/dist/baselines/atomizer/inPlaceModifier-postprocess.js +402 -0
  35. package/dist/baselines/atomizer/inPlaceModifier-postprocess.js.map +1 -0
  36. package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts +39 -0
  37. package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts.map +1 -0
  38. package/dist/baselines/atomizer/inPlaceModifier-presplit.js +265 -0
  39. package/dist/baselines/atomizer/inPlaceModifier-presplit.js.map +1 -0
  40. package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts +62 -0
  41. package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts.map +1 -0
  42. package/dist/baselines/atomizer/inPlaceModifier-shared.js +139 -0
  43. package/dist/baselines/atomizer/inPlaceModifier-shared.js.map +1 -0
  44. package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts +198 -0
  45. package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts.map +1 -0
  46. package/dist/baselines/atomizer/inPlaceModifier-wrappers.js +475 -0
  47. package/dist/baselines/atomizer/inPlaceModifier-wrappers.js.map +1 -0
  48. package/dist/baselines/atomizer/inPlaceModifier.d.ts +6 -290
  49. package/dist/baselines/atomizer/inPlaceModifier.d.ts.map +1 -1
  50. package/dist/baselines/atomizer/inPlaceModifier.js +23 -1828
  51. package/dist/baselines/atomizer/inPlaceModifier.js.map +1 -1
  52. package/dist/baselines/atomizer/pipeline.d.ts +36 -2
  53. package/dist/baselines/atomizer/pipeline.d.ts.map +1 -1
  54. package/dist/baselines/atomizer/pipeline.js +216 -144
  55. package/dist/baselines/atomizer/pipeline.js.map +1 -1
  56. package/dist/baselines/atomizer/trackChangesAcceptorAst.d.ts.map +1 -1
  57. package/dist/baselines/atomizer/trackChangesAcceptorAst.js +199 -173
  58. package/dist/baselines/atomizer/trackChangesAcceptorAst.js.map +1 -1
  59. package/dist/baselines/wmlcomparer/DotnetCli.d.ts.map +1 -1
  60. package/dist/baselines/wmlcomparer/DotnetCli.js +7 -0
  61. package/dist/baselines/wmlcomparer/DotnetCli.js.map +1 -1
  62. package/dist/cli/compare-two.d.ts.map +1 -1
  63. package/dist/cli/compare-two.js +3 -1
  64. package/dist/cli/compare-two.js.map +1 -1
  65. package/dist/cli/conformance-adapter.d.ts +3 -0
  66. package/dist/cli/conformance-adapter.d.ts.map +1 -0
  67. package/dist/cli/conformance-adapter.js +93 -0
  68. package/dist/cli/conformance-adapter.js.map +1 -0
  69. package/dist/cli/index.d.ts.map +1 -1
  70. package/dist/cli/index.js +5 -1
  71. package/dist/cli/index.js.map +1 -1
  72. package/dist/compare-types.d.ts +197 -0
  73. package/dist/compare-types.d.ts.map +1 -0
  74. package/dist/compare-types.js +2 -0
  75. package/dist/compare-types.js.map +1 -0
  76. package/dist/core-types.d.ts +5 -1
  77. package/dist/core-types.d.ts.map +1 -1
  78. package/dist/core-types.js +5 -1
  79. package/dist/core-types.js.map +1 -1
  80. package/dist/footnotes.d.ts +8 -3
  81. package/dist/footnotes.d.ts.map +1 -1
  82. package/dist/footnotes.js +8 -3
  83. package/dist/footnotes.js.map +1 -1
  84. package/dist/generation/compile.d.ts +21 -0
  85. package/dist/generation/compile.d.ts.map +1 -0
  86. package/dist/generation/compile.js +46 -0
  87. package/dist/generation/compile.js.map +1 -0
  88. package/dist/generation/context.d.ts +42 -0
  89. package/dist/generation/context.d.ts.map +1 -0
  90. package/dist/generation/context.js +65 -0
  91. package/dist/generation/context.js.map +1 -0
  92. package/dist/generation/emit/comments-part.d.ts +36 -0
  93. package/dist/generation/emit/comments-part.d.ts.map +1 -0
  94. package/dist/generation/emit/comments-part.js +116 -0
  95. package/dist/generation/emit/comments-part.js.map +1 -0
  96. package/dist/generation/emit/document-part.d.ts +24 -0
  97. package/dist/generation/emit/document-part.d.ts.map +1 -0
  98. package/dist/generation/emit/document-part.js +60 -0
  99. package/dist/generation/emit/document-part.js.map +1 -0
  100. package/dist/generation/emit/emit-context.d.ts +26 -0
  101. package/dist/generation/emit/emit-context.d.ts.map +1 -0
  102. package/dist/generation/emit/emit-context.js +19 -0
  103. package/dist/generation/emit/emit-context.js.map +1 -0
  104. package/dist/generation/emit/header-footer-part.d.ts +23 -0
  105. package/dist/generation/emit/header-footer-part.d.ts.map +1 -0
  106. package/dist/generation/emit/header-footer-part.js +57 -0
  107. package/dist/generation/emit/header-footer-part.js.map +1 -0
  108. package/dist/generation/emit/numbering-part.d.ts +29 -0
  109. package/dist/generation/emit/numbering-part.d.ts.map +1 -0
  110. package/dist/generation/emit/numbering-part.js +100 -0
  111. package/dist/generation/emit/numbering-part.js.map +1 -0
  112. package/dist/generation/emit/package-parts.d.ts +24 -0
  113. package/dist/generation/emit/package-parts.d.ts.map +1 -0
  114. package/dist/generation/emit/package-parts.js +121 -0
  115. package/dist/generation/emit/package-parts.js.map +1 -0
  116. package/dist/generation/emit/paragraph.d.ts +24 -0
  117. package/dist/generation/emit/paragraph.d.ts.map +1 -0
  118. package/dist/generation/emit/paragraph.js +63 -0
  119. package/dist/generation/emit/paragraph.js.map +1 -0
  120. package/dist/generation/emit/properties.d.ts +34 -0
  121. package/dist/generation/emit/properties.d.ts.map +1 -0
  122. package/dist/generation/emit/properties.js +138 -0
  123. package/dist/generation/emit/properties.js.map +1 -0
  124. package/dist/generation/emit/run.d.ts +15 -0
  125. package/dist/generation/emit/run.d.ts.map +1 -0
  126. package/dist/generation/emit/run.js +71 -0
  127. package/dist/generation/emit/run.js.map +1 -0
  128. package/dist/generation/emit/section.d.ts +29 -0
  129. package/dist/generation/emit/section.d.ts.map +1 -0
  130. package/dist/generation/emit/section.js +117 -0
  131. package/dist/generation/emit/section.js.map +1 -0
  132. package/dist/generation/emit/settings-part.d.ts +13 -0
  133. package/dist/generation/emit/settings-part.d.ts.map +1 -0
  134. package/dist/generation/emit/settings-part.js +24 -0
  135. package/dist/generation/emit/settings-part.js.map +1 -0
  136. package/dist/generation/emit/styles-part.d.ts +16 -0
  137. package/dist/generation/emit/styles-part.d.ts.map +1 -0
  138. package/dist/generation/emit/styles-part.js +80 -0
  139. package/dist/generation/emit/styles-part.js.map +1 -0
  140. package/dist/generation/emit/table.d.ts +26 -0
  141. package/dist/generation/emit/table.d.ts.map +1 -0
  142. package/dist/generation/emit/table.js +196 -0
  143. package/dist/generation/emit/table.js.map +1 -0
  144. package/dist/generation/errors.d.ts +22 -0
  145. package/dist/generation/errors.d.ts.map +1 -0
  146. package/dist/generation/errors.js +29 -0
  147. package/dist/generation/errors.js.map +1 -0
  148. package/dist/generation/index.d.ts +13 -0
  149. package/dist/generation/index.d.ts.map +1 -0
  150. package/dist/generation/index.js +12 -0
  151. package/dist/generation/index.js.map +1 -0
  152. package/dist/generation/ordering.d.ts +46 -0
  153. package/dist/generation/ordering.d.ts.map +1 -0
  154. package/dist/generation/ordering.js +119 -0
  155. package/dist/generation/ordering.js.map +1 -0
  156. package/dist/generation/recipes.d.ts +47 -0
  157. package/dist/generation/recipes.d.ts.map +1 -0
  158. package/dist/generation/recipes.js +84 -0
  159. package/dist/generation/recipes.js.map +1 -0
  160. package/dist/generation/structural-checks.d.ts +24 -0
  161. package/dist/generation/structural-checks.d.ts.map +1 -0
  162. package/dist/generation/structural-checks.js +318 -0
  163. package/dist/generation/structural-checks.js.map +1 -0
  164. package/dist/generation/types.d.ts +217 -0
  165. package/dist/generation/types.d.ts.map +1 -0
  166. package/dist/generation/types.js +16 -0
  167. package/dist/generation/types.js.map +1 -0
  168. package/dist/generation/validate-spec.d.ts +27 -0
  169. package/dist/generation/validate-spec.d.ts.map +1 -0
  170. package/dist/generation/validate-spec.js +307 -0
  171. package/dist/generation/validate-spec.js.map +1 -0
  172. package/dist/index.d.ts +9 -150
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +14 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/integration/generation-probes.d.ts +15 -0
  177. package/dist/integration/generation-probes.d.ts.map +1 -0
  178. package/dist/integration/generation-probes.js +84 -0
  179. package/dist/integration/generation-probes.js.map +1 -0
  180. package/dist/integration/libreoffice-oracle.d.ts +49 -0
  181. package/dist/integration/libreoffice-oracle.d.ts.map +1 -0
  182. package/dist/integration/libreoffice-oracle.js +290 -0
  183. package/dist/integration/libreoffice-oracle.js.map +1 -0
  184. package/dist/integration/synthetic-docx-fixture.d.ts +72 -0
  185. package/dist/integration/synthetic-docx-fixture.d.ts.map +1 -1
  186. package/dist/integration/synthetic-docx-fixture.js +131 -4
  187. package/dist/integration/synthetic-docx-fixture.js.map +1 -1
  188. package/dist/primitives/accept_changes.d.ts +4 -3
  189. package/dist/primitives/accept_changes.d.ts.map +1 -1
  190. package/dist/primitives/accept_changes.js +163 -77
  191. package/dist/primitives/accept_changes.js.map +1 -1
  192. package/dist/primitives/comments.d.ts +12 -3
  193. package/dist/primitives/comments.d.ts.map +1 -1
  194. package/dist/primitives/comments.js +374 -97
  195. package/dist/primitives/comments.js.map +1 -1
  196. package/dist/primitives/content_fingerprint.d.ts +29 -0
  197. package/dist/primitives/content_fingerprint.d.ts.map +1 -0
  198. package/dist/primitives/content_fingerprint.js +63 -0
  199. package/dist/primitives/content_fingerprint.js.map +1 -0
  200. package/dist/primitives/document.d.ts +94 -15
  201. package/dist/primitives/document.d.ts.map +1 -1
  202. package/dist/primitives/document.js +373 -36
  203. package/dist/primitives/document.js.map +1 -1
  204. package/dist/primitives/document_view-comments.d.ts +18 -0
  205. package/dist/primitives/document_view-comments.d.ts.map +1 -0
  206. package/dist/primitives/document_view-comments.js +160 -0
  207. package/dist/primitives/document_view-comments.js.map +1 -0
  208. package/dist/primitives/document_view-headings.d.ts +45 -0
  209. package/dist/primitives/document_view-headings.d.ts.map +1 -0
  210. package/dist/primitives/document_view-headings.js +247 -0
  211. package/dist/primitives/document_view-headings.js.map +1 -0
  212. package/dist/primitives/document_view-styles.d.ts +11 -0
  213. package/dist/primitives/document_view-styles.d.ts.map +1 -0
  214. package/dist/primitives/document_view-styles.js +104 -0
  215. package/dist/primitives/document_view-styles.js.map +1 -0
  216. package/dist/primitives/document_view-toon.d.ts +37 -0
  217. package/dist/primitives/document_view-toon.d.ts.map +1 -0
  218. package/dist/primitives/document_view-toon.js +199 -0
  219. package/dist/primitives/document_view-toon.js.map +1 -0
  220. package/dist/primitives/document_view-types.d.ts +152 -0
  221. package/dist/primitives/document_view-types.d.ts.map +1 -0
  222. package/dist/primitives/document_view-types.js +2 -0
  223. package/dist/primitives/document_view-types.js.map +1 -0
  224. package/dist/primitives/document_view.d.ts +8 -106
  225. package/dist/primitives/document_view.d.ts.map +1 -1
  226. package/dist/primitives/document_view.js +153 -312
  227. package/dist/primitives/document_view.js.map +1 -1
  228. package/dist/primitives/dom-helpers.d.ts +9 -0
  229. package/dist/primitives/dom-helpers.d.ts.map +1 -1
  230. package/dist/primitives/dom-helpers.js +10 -1
  231. package/dist/primitives/dom-helpers.js.map +1 -1
  232. package/dist/primitives/footnotes.d.ts +4 -3
  233. package/dist/primitives/footnotes.d.ts.map +1 -1
  234. package/dist/primitives/footnotes.js +232 -44
  235. package/dist/primitives/footnotes.js.map +1 -1
  236. package/dist/primitives/formatting_tags.d.ts +7 -0
  237. package/dist/primitives/formatting_tags.d.ts.map +1 -1
  238. package/dist/primitives/formatting_tags.js +22 -11
  239. package/dist/primitives/formatting_tags.js.map +1 -1
  240. package/dist/primitives/index.d.ts +10 -0
  241. package/dist/primitives/index.d.ts.map +1 -1
  242. package/dist/primitives/index.js +9 -0
  243. package/dist/primitives/index.js.map +1 -1
  244. package/dist/primitives/layout.d.ts +4 -3
  245. package/dist/primitives/layout.d.ts.map +1 -1
  246. package/dist/primitives/layout.js +45 -3
  247. package/dist/primitives/layout.js.map +1 -1
  248. package/dist/primitives/merge_runs.d.ts +21 -3
  249. package/dist/primitives/merge_runs.d.ts.map +1 -1
  250. package/dist/primitives/merge_runs.js +32 -10
  251. package/dist/primitives/merge_runs.js.map +1 -1
  252. package/dist/primitives/minimal_save.d.ts +38 -0
  253. package/dist/primitives/minimal_save.d.ts.map +1 -0
  254. package/dist/primitives/minimal_save.js +323 -0
  255. package/dist/primitives/minimal_save.js.map +1 -0
  256. package/dist/primitives/namespaces.d.ts +47 -0
  257. package/dist/primitives/namespaces.d.ts.map +1 -1
  258. package/dist/primitives/namespaces.js +52 -0
  259. package/dist/primitives/namespaces.js.map +1 -1
  260. package/dist/primitives/reject_changes.d.ts +6 -4
  261. package/dist/primitives/reject_changes.d.ts.map +1 -1
  262. package/dist/primitives/reject_changes.js +187 -91
  263. package/dist/primitives/reject_changes.js.map +1 -1
  264. package/dist/primitives/revision-parts.d.ts +7 -0
  265. package/dist/primitives/revision-parts.d.ts.map +1 -0
  266. package/dist/primitives/revision-parts.js +27 -0
  267. package/dist/primitives/revision-parts.js.map +1 -0
  268. package/dist/primitives/revision-vocabulary.d.ts +7 -0
  269. package/dist/primitives/revision-vocabulary.d.ts.map +1 -0
  270. package/dist/primitives/revision-vocabulary.js +39 -0
  271. package/dist/primitives/revision-vocabulary.js.map +1 -0
  272. package/dist/primitives/schema-corpus-capture.d.ts +19 -0
  273. package/dist/primitives/schema-corpus-capture.d.ts.map +1 -0
  274. package/dist/primitives/schema-corpus-capture.js +29 -0
  275. package/dist/primitives/schema-corpus-capture.js.map +1 -0
  276. package/dist/primitives/sectPrAudit.d.ts +19 -0
  277. package/dist/primitives/sectPrAudit.d.ts.map +1 -0
  278. package/dist/primitives/sectPrAudit.js +165 -0
  279. package/dist/primitives/sectPrAudit.js.map +1 -0
  280. package/dist/primitives/semantic_tags.d.ts +7 -0
  281. package/dist/primitives/semantic_tags.d.ts.map +1 -1
  282. package/dist/primitives/semantic_tags.js +23 -4
  283. package/dist/primitives/semantic_tags.js.map +1 -1
  284. package/dist/primitives/serialize_html.d.ts +37 -0
  285. package/dist/primitives/serialize_html.d.ts.map +1 -0
  286. package/dist/primitives/serialize_html.js +395 -0
  287. package/dist/primitives/serialize_html.js.map +1 -0
  288. package/dist/primitives/serialize_markdown.d.ts +16 -0
  289. package/dist/primitives/serialize_markdown.d.ts.map +1 -0
  290. package/dist/primitives/serialize_markdown.js +300 -0
  291. package/dist/primitives/serialize_markdown.js.map +1 -0
  292. package/dist/primitives/serialize_plaintext.d.ts +15 -0
  293. package/dist/primitives/serialize_plaintext.d.ts.map +1 -0
  294. package/dist/primitives/serialize_plaintext.js +154 -0
  295. package/dist/primitives/serialize_plaintext.js.map +1 -0
  296. package/dist/primitives/styles.d.ts +15 -0
  297. package/dist/primitives/styles.d.ts.map +1 -1
  298. package/dist/primitives/styles.js +33 -22
  299. package/dist/primitives/styles.js.map +1 -1
  300. package/dist/primitives/tables.d.ts.map +1 -1
  301. package/dist/primitives/tables.js +13 -3
  302. package/dist/primitives/tables.js.map +1 -1
  303. package/dist/primitives/text.d.ts +2 -1
  304. package/dist/primitives/text.d.ts.map +1 -1
  305. package/dist/primitives/text.js +116 -12
  306. package/dist/primitives/text.js.map +1 -1
  307. package/dist/primitives/track-changes-emitter.d.ts +148 -0
  308. package/dist/primitives/track-changes-emitter.d.ts.map +1 -0
  309. package/dist/primitives/track-changes-emitter.js +291 -0
  310. package/dist/primitives/track-changes-emitter.js.map +1 -0
  311. package/dist/primitives/validate_ai_revisions.d.ts +35 -0
  312. package/dist/primitives/validate_ai_revisions.d.ts.map +1 -0
  313. package/dist/primitives/validate_ai_revisions.js +323 -0
  314. package/dist/primitives/validate_ai_revisions.js.map +1 -0
  315. package/dist/primitives/xml-helpers.d.ts +29 -0
  316. package/dist/primitives/xml-helpers.d.ts.map +1 -0
  317. package/dist/primitives/xml-helpers.js +35 -0
  318. package/dist/primitives/xml-helpers.js.map +1 -0
  319. package/dist/primitives/xml.d.ts +5 -0
  320. package/dist/primitives/xml.d.ts.map +1 -1
  321. package/dist/primitives/xml.js +5 -0
  322. package/dist/primitives/xml.js.map +1 -1
  323. package/dist/primitives/zip.d.ts +1 -0
  324. package/dist/primitives/zip.d.ts.map +1 -1
  325. package/dist/primitives/zip.js +21 -3
  326. package/dist/primitives/zip.js.map +1 -1
  327. package/dist/shared/field-structure.d.ts +14 -0
  328. package/dist/shared/field-structure.d.ts.map +1 -0
  329. package/dist/shared/field-structure.js +166 -0
  330. package/dist/shared/field-structure.js.map +1 -0
  331. package/dist/shared/ooxml/namespaces.d.ts +4 -1
  332. package/dist/shared/ooxml/namespaces.d.ts.map +1 -1
  333. package/dist/shared/ooxml/namespaces.js +4 -1
  334. package/dist/shared/ooxml/namespaces.js.map +1 -1
  335. package/package.json +13 -9
@@ -0,0 +1,84 @@
1
+ /**
2
+ * LibreOffice probes for generated full-package documents.
3
+ *
4
+ * Deliberately separate from runLibreOfficeOracle: the oracle's contract is
5
+ * "main XML string per accept/reject/identity job over a bare document.xml or
6
+ * .odt", driven by an injected Basic macro because the resolve-all commands
7
+ * are dispatch-only. These probes have a different contract — a complete
8
+ * generated package goes in, and the *saved package* (or rendered PDF) comes
9
+ * back out — and need no macro: `--convert-to` performs exactly the
10
+ * load→save (or load→render) cycle that exposes recovery-dialog failures,
11
+ * because a package LibreOffice cannot load cleanly produces no output.
12
+ *
13
+ * Local-only, like the oracle: callers skip when `resolveSoffice()` is null
14
+ * (CI does not install LibreOffice; the structural checks are the
15
+ * CI-enforceable proxy).
16
+ */
17
+ import { execFile } from 'node:child_process';
18
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
19
+ import os from 'node:os';
20
+ import path from 'node:path';
21
+ import { pathToFileURL } from 'node:url';
22
+ import { promisify } from 'node:util';
23
+ import { resolveSoffice } from './libreoffice-oracle.js';
24
+ const execFileAsync = promisify(execFile);
25
+ async function runConvert(input, convertTo, outExt, soffice) {
26
+ const work = mkdtempSync(path.join(os.tmpdir(), 'sdx-gen-probe-'));
27
+ try {
28
+ const inDir = path.join(work, 'in');
29
+ const outDir = path.join(work, 'out');
30
+ const profile = path.join(work, 'profile');
31
+ mkdirSync(inDir, { recursive: true });
32
+ mkdirSync(outDir, { recursive: true });
33
+ mkdirSync(profile, { recursive: true });
34
+ const inPath = path.join(inDir, 'probe.docx');
35
+ writeFileSync(inPath, new Uint8Array(input));
36
+ const args = [
37
+ '--headless',
38
+ '--norestore',
39
+ '--nologo',
40
+ `-env:UserInstallation=${pathToFileURL(profile).href}`,
41
+ '--convert-to',
42
+ convertTo,
43
+ '--outdir',
44
+ outDir,
45
+ inPath,
46
+ ];
47
+ let diag = '';
48
+ try {
49
+ const r = await execFileAsync(soffice, args, { timeout: 45_000, killSignal: 'SIGKILL', maxBuffer: 8 * 1024 * 1024 });
50
+ diag = String(r.stderr ?? '') || String(r.stdout ?? '');
51
+ }
52
+ catch (err) {
53
+ const e = err;
54
+ diag = String(e.stderr ?? e.stdout ?? e.message ?? '');
55
+ }
56
+ const outPath = path.join(outDir, `probe.${outExt}`);
57
+ if (!existsSync(outPath)) {
58
+ throw new Error(`LibreOffice could not convert the generated package to ${outExt} — likely a load failure ` +
59
+ `(the recovery-dialog class of bug).\nsoffice output:\n${diag.trim() || '(no output)'}`);
60
+ }
61
+ return readFileSync(outPath);
62
+ }
63
+ finally {
64
+ rmSync(work, { recursive: true, force: true });
65
+ }
66
+ }
67
+ /**
68
+ * Load→save a complete generated package through LibreOffice's DOCX filter.
69
+ * Throws when LibreOffice cannot load the package (no output is produced).
70
+ */
71
+ export async function probeDocxIdentity(generated, soffice = resolveSoffice()) {
72
+ if (!soffice)
73
+ throw new Error('probeDocxIdentity: no soffice binary (call resolveSoffice() and skip)');
74
+ const savedPackage = await runConvert(generated, 'docx:MS Word 2007 XML', 'docx', soffice);
75
+ return { savedPackage };
76
+ }
77
+ /** Render a generated package to PDF headlessly — the headless-renderer probe. */
78
+ export async function probeDocxToPdf(generated, soffice = resolveSoffice()) {
79
+ if (!soffice)
80
+ throw new Error('probeDocxToPdf: no soffice binary (call resolveSoffice() and skip)');
81
+ const pdf = await runConvert(generated, 'pdf', 'pdf', soffice);
82
+ return { pdf };
83
+ }
84
+ //# sourceMappingURL=generation-probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generation-probes.js","sourceRoot":"","sources":["../../src/integration/generation-probes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /** Resolve a LibreOffice binary, or null if none is available (callers skip the oracle). */
2
+ export declare function resolveSoffice(): string | null;
3
+ /** Pack a bare `word/document.xml` string into a minimal valid .docx package. */
4
+ export declare function packMinimalDocx(documentXml: string): Promise<Buffer>;
5
+ /** Read `word/document.xml` back out of a .docx package. */
6
+ export declare function extractDocumentXml(docx: Buffer): Promise<string>;
7
+ /**
8
+ * `accept` / `reject` dispatch the corresponding resolve-all command before saving — the oracle's
9
+ * normal voting mode. `identity` loads and saves WITHOUT any dispatch, so unresolved tracked
10
+ * changes flow through LibreOffice's import/export: it exists to characterize the oracle's
11
+ * trust boundary (LibreOffice's save round-trip mangles some unresolved revision shapes — see
12
+ * libreoffice-oracle-trust-boundary.test.ts), NOT to vote on engine behavior.
13
+ *
14
+ * DOCX jobs carry a bare `word/document.xml` (packed into a minimal package and read back out);
15
+ * ODT jobs carry a complete `.odt` package buffer (ODF packaging — mimetype-first STORED — is
16
+ * the caller's concern) and return its post-op `content.xml`.
17
+ *
18
+ * CONVERSION jobs (`docx` + `saveAs: 'odt'`) carry a complete `.docx` package buffer and save
19
+ * through LibreOffice's `writer8` filter, returning the converted `content.xml` — the reference
20
+ * path for differential-testing odf-core's native DOCX→ODT converter (issue #331).
21
+ */
22
+ type OracleOp = 'accept' | 'reject' | 'identity';
23
+ export type OracleJob = {
24
+ op: OracleOp;
25
+ documentXml: string;
26
+ } | {
27
+ op: OracleOp;
28
+ odt: Buffer;
29
+ } | {
30
+ op: OracleOp;
31
+ docx: Buffer;
32
+ saveAs: 'odt';
33
+ };
34
+ /**
35
+ * Run LibreOffice over a batch of jobs in ONE headless launch and return each job's resulting
36
+ * main XML part — `word/document.xml` for DOCX jobs, `content.xml` for ODT jobs. Throws if the
37
+ * binary is missing or the macro did not run.
38
+ */
39
+ export declare function runLibreOfficeOracle(jobs: OracleJob[], soffice?: string | null): Promise<string[]>;
40
+ /**
41
+ * Structural projection of a `word/document.xml`: one entry per top-level body paragraph,
42
+ * recording whether it carries visible text (a non-whitespace `w:t` descendant). This captures
43
+ * the paragraph-collapse claim the oracle is authoritative for — how many paragraphs survive and
44
+ * which collapsed to empty — without depending on revision-markup or formatting details (which
45
+ * LibreOffice rewrites). `[]`-length is the paragraph count.
46
+ */
47
+ export declare function paragraphShape(documentXml: string): boolean[];
48
+ export {};
49
+ //# sourceMappingURL=libreoffice-oracle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"libreoffice-oracle.d.ts","sourceRoot":"","sources":["../../src/integration/libreoffice-oracle.ts"],"names":[],"mappings":"AAoEA,4FAA4F;AAC5F,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAU9C;AAeD,iFAAiF;AACjF,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO1E;AAED,4DAA4D;AAC5D,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AA+DD;;;;;;;;;;;;;;GAcG;AACH,KAAK,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;AACjD,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,CAAA;CAAE,CAAC;AAUlD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,gBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4F3G;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,EAAE,CAkB7D"}
@@ -0,0 +1,290 @@
1
+ /**
2
+ * LibreOffice accept/reject oracle — a committed, reproducible reference voter.
3
+ *
4
+ * Drives LibreOffice headless as a track-changes accept/reject implementation so the
5
+ * production engine's paragraph-collapse behavior (the G3/G4/G5 differential cases) can be
6
+ * validated against a real word processor, not just Lean↔TS self-consistency. LibreOffice is
7
+ * the native engine for the .uno:AcceptAllTrackedChanges / .uno:RejectAllTrackedChanges
8
+ * dispatches, so its paragraph-structure output is authoritative ground truth for the
9
+ * mark-based rule (an untracked paragraph mark is kept on accept/reject; a PPR-INS/PPR-DEL mark
10
+ * is dropped).
11
+ *
12
+ * Mechanism (macOS-portable; also works on Linux): pyuno from a terminal is blocked on macOS by
13
+ * Launch Constraints, so this injects a Basic macro into a throwaway user profile and invokes it
14
+ * with a bare `macro:///` URL. The order matters — a fresh profile regenerates the Standard Basic
15
+ * library on first launch, clobbering any hand-placed Module1.xba — so we (1) init the profile via
16
+ * a throwaway convert, (2) THEN overwrite Module1.xba, (3) THEN run the macro. See
17
+ * reference_libreoffice_macos_oracle.
18
+ *
19
+ * This module is gated by callers: when `resolveSoffice()` returns null the oracle is skipped.
20
+ * CI does not install LibreOffice, so the oracle voter is a local developer check.
21
+ */
22
+ import { execFile } from 'node:child_process';
23
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
24
+ import os from 'node:os';
25
+ import path from 'node:path';
26
+ import { pathToFileURL } from 'node:url';
27
+ import { promisify } from 'node:util';
28
+ import { createZipBuffer, readZipText } from '../primitives/zip.js';
29
+ import { parseXml } from '../primitives/xml.js';
30
+ const execFileAsync = promisify(execFile);
31
+ const W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
32
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
33
+ /** Run soffice, capturing stdout/stderr; never throws (the macro terminates the desktop, so a
34
+ * nonzero exit is expected). */
35
+ async function runSoffice(soffice, args, timeout) {
36
+ try {
37
+ const r = await execFileAsync(soffice, args, { timeout, killSignal: 'SIGKILL', maxBuffer: 8 * 1024 * 1024 });
38
+ return { stdout: String(r.stdout ?? ''), stderr: String(r.stderr ?? '') };
39
+ }
40
+ catch (err) {
41
+ const e = err;
42
+ return { stdout: String(e.stdout ?? ''), stderr: String(e.stderr ?? e.message ?? '') };
43
+ }
44
+ }
45
+ /**
46
+ * LibreOffice's single-instance model forwards a new `soffice` invocation to an existing instance
47
+ * that shares the same UserInstallation — so the init-convert process MUST be fully gone before the
48
+ * macro launch, or the `macro:///` command is delivered to the dying init instance (which has no
49
+ * Module1 yet) and never runs. The instance lock is `<profile>/.lock`. LibreOffice often leaves a
50
+ * STALE lock after `--convert-to` exits, so we wait a short bounded window for it to self-clear and
51
+ * then remove it unconditionally; the macro launch's own retry backstops a slow init exit.
52
+ */
53
+ async function settleProfile(profile, timeoutMs = 1_500) {
54
+ const lock = path.join(profile, '.lock');
55
+ const deadline = Date.now() + timeoutMs;
56
+ while (existsSync(lock) && Date.now() < deadline)
57
+ await sleep(150);
58
+ if (existsSync(lock)) {
59
+ try {
60
+ rmSync(lock, { force: true });
61
+ }
62
+ catch { /* best effort — fresh launch will relock */ }
63
+ }
64
+ }
65
+ /** Resolve a LibreOffice binary, or null if none is available (callers skip the oracle). */
66
+ export function resolveSoffice() {
67
+ const candidates = [
68
+ process.env.SAFE_DOCX_SOFFICE_BIN,
69
+ process.env.ODF_SOFFICE_BIN,
70
+ '/opt/homebrew/bin/soffice',
71
+ '/usr/bin/soffice',
72
+ '/usr/local/bin/soffice',
73
+ '/Applications/LibreOffice.app/Contents/MacOS/soffice',
74
+ ].filter(Boolean);
75
+ return candidates.find((c) => existsSync(c)) ?? null;
76
+ }
77
+ const CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
78
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
79
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
80
+ <Default Extension="xml" ContentType="application/xml"/>
81
+ <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
82
+ </Types>`;
83
+ const RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
84
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
85
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
86
+ </Relationships>`;
87
+ const DOC_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
88
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>`;
89
+ /** Pack a bare `word/document.xml` string into a minimal valid .docx package. */
90
+ export async function packMinimalDocx(documentXml) {
91
+ return createZipBuffer({
92
+ '[Content_Types].xml': CONTENT_TYPES,
93
+ '_rels/.rels': RELS,
94
+ 'word/_rels/document.xml.rels': DOC_RELS,
95
+ 'word/document.xml': documentXml,
96
+ });
97
+ }
98
+ /** Read `word/document.xml` back out of a .docx package. */
99
+ export async function extractDocumentXml(docx) {
100
+ const xml = await readZipText(docx, 'word/document.xml');
101
+ if (xml == null)
102
+ throw new Error('word/document.xml not found in oracle output');
103
+ return xml;
104
+ }
105
+ const SCRIPT_XLC = `<?xml version="1.0" encoding="UTF-8"?>
106
+ <!DOCTYPE library:libraries PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "libraries.dtd">
107
+ <library:libraries xmlns:library="http://openoffice.org/2000/library" xmlns:xlink="http://www.w3.org/1999/xlink">
108
+ <library:library library:name="Standard" xlink:href="$(USER)/basic/Standard/script.xlb/" xlink:type="simple" library:link="false"/>
109
+ </library:libraries>`;
110
+ const SCRIPT_XLB = `<?xml version="1.0" encoding="UTF-8"?>
111
+ <!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
112
+ <library:library xmlns:library="http://openoffice.org/2000/library" library:name="Standard" library:readonly="false" library:passwordprotected="false">
113
+ <library:element library:name="Module1"/>
114
+ </library:library>`;
115
+ const REGMOD = `<?xml version="1.0" encoding="UTF-8"?>
116
+ <oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
117
+ <item oor:path="/org.openoffice.Office.Common/Security/Scripting"><prop oor:name="MacroSecurityLevel" oor:op="fuse"><value>0</value></prop></item>
118
+ <item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>
119
+ </oor:items>`;
120
+ /** The Basic macro: load each doc Hidden, dispatch accept/reject-all (or, for `identity`,
121
+ * dispatch nothing at all), save with the job's filter. */
122
+ function module1Xba(jobsPath, markerPath) {
123
+ return `<?xml version="1.0" encoding="UTF-8"?>
124
+ <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
125
+ <script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">
126
+ Sub RunOracle
127
+ Dim iFile As Integer, sLine As String, parts() As String, m As Integer
128
+ m = FreeFile : Open "${markerPath}" For Output As #m : Print #m, "started" : Close #m
129
+ iFile = FreeFile
130
+ Open "${jobsPath}" For Input As #iFile
131
+ Do While Not EOF(iFile)
132
+ Line Input #iFile, sLine
133
+ If Len(Trim(sLine)) &gt; 0 Then
134
+ parts = Split(sLine, "|")
135
+ ProcessOne(parts(0), ConvertToURL(parts(1)), ConvertToURL(parts(2)), parts(3))
136
+ End If
137
+ Loop
138
+ Close #iFile
139
+ StarDesktop.terminate()
140
+ End Sub
141
+
142
+ Sub ProcessOne(op As String, inUrl As String, outUrl As String, filterName As String)
143
+ Dim oDoc As Object, oFrame As Object, oDisp As Object
144
+ Dim loadArgs(0) As New com.sun.star.beans.PropertyValue
145
+ loadArgs(0).Name = "Hidden" : loadArgs(0).Value = True
146
+ oDoc = StarDesktop.loadComponentFromURL(inUrl, "_blank", 0, loadArgs())
147
+ oFrame = oDoc.getCurrentController().getFrame()
148
+ oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
149
+ Dim noArgs()
150
+ If op = "accept" Then
151
+ oDisp.executeDispatch(oFrame, ".uno:AcceptAllTrackedChanges", "", 0, noArgs())
152
+ ElseIf op = "reject" Then
153
+ oDisp.executeDispatch(oFrame, ".uno:RejectAllTrackedChanges", "", 0, noArgs())
154
+ End If
155
+ ' op = "identity": no dispatch — a plain load-&gt;save, exposing LibreOffice's own DOCX
156
+ ' import/export of UNRESOLVED tracked changes (the oracle trust-boundary check).
157
+ Dim saveArgs(0) As New com.sun.star.beans.PropertyValue
158
+ saveArgs(0).Name = "FilterName" : saveArgs(0).Value = filterName
159
+ oDoc.storeToURL(outUrl, saveArgs())
160
+ oDoc.close(False)
161
+ End Sub
162
+ </script:module>`;
163
+ }
164
+ function isOdtJob(job) {
165
+ return 'odt' in job;
166
+ }
167
+ function isConvertJob(job) {
168
+ return 'docx' in job;
169
+ }
170
+ /**
171
+ * Run LibreOffice over a batch of jobs in ONE headless launch and return each job's resulting
172
+ * main XML part — `word/document.xml` for DOCX jobs, `content.xml` for ODT jobs. Throws if the
173
+ * binary is missing or the macro did not run.
174
+ */
175
+ export async function runLibreOfficeOracle(jobs, soffice = resolveSoffice()) {
176
+ if (!soffice)
177
+ throw new Error('runLibreOfficeOracle: no soffice binary (call resolveSoffice() and skip)');
178
+ if (jobs.length === 0)
179
+ return [];
180
+ const work = mkdtempSync(path.join(os.tmpdir(), 'lo-oracle-'));
181
+ const profile = path.join(work, 'profile');
182
+ const userDir = path.join(profile, 'user');
183
+ const basicDir = path.join(userDir, 'basic', 'Standard');
184
+ const inDir = path.join(work, 'in');
185
+ const outDir = path.join(work, 'out');
186
+ const marker = path.join(work, 'macro_ran.txt');
187
+ const jobsPath = path.join(work, 'jobs.txt');
188
+ const profileUrl = pathToFileURL(profile).href;
189
+ let keepWork = false;
190
+ try {
191
+ for (const d of [userDir, basicDir, inDir, outDir])
192
+ mkdirSync(d, { recursive: true });
193
+ // Write each job's input package and build the jobs file (op|inPath|outPath|filter).
194
+ const outPaths = [];
195
+ const jobLines = [];
196
+ for (let i = 0; i < jobs.length; i++) {
197
+ const job = jobs[i];
198
+ const inExt = isOdtJob(job) ? 'odt' : 'docx';
199
+ const outExt = isOdtJob(job) || isConvertJob(job) ? 'odt' : 'docx';
200
+ const filter = isOdtJob(job) || isConvertJob(job) ? 'writer8' : 'MS Word 2007 XML';
201
+ const inPath = path.join(inDir, `job${i}.${inExt}`);
202
+ const outPath = path.join(outDir, `job${i}.${outExt}`);
203
+ writeFileSync(inPath, isOdtJob(job)
204
+ ? new Uint8Array(job.odt)
205
+ : isConvertJob(job)
206
+ ? new Uint8Array(job.docx)
207
+ : new Uint8Array(await packMinimalDocx(job.documentXml)));
208
+ outPaths.push(outPath);
209
+ jobLines.push(`${job.op}|${inPath}|${outPath}|${filter}`);
210
+ }
211
+ writeFileSync(jobsPath, jobLines.join('\n') + '\n');
212
+ // Macro security level 0 so the Standard-library macro runs headless.
213
+ writeFileSync(path.join(userDir, 'registrymodifications.xcu'), REGMOD);
214
+ const baseArgs = ['--headless', '--norestore', '--nologo', `-env:UserInstallation=${profileUrl}`];
215
+ const diag = [];
216
+ // (1) INIT the profile: a throwaway convert makes soffice populate user/basic/Standard
217
+ // (which would otherwise clobber a pre-placed Module1.xba on first real launch).
218
+ const init = await runSoffice(soffice, [...baseArgs, '--convert-to', 'txt', '--outdir', path.join(work, 'init'), path.join(inDir, 'job0.docx')], 20_000);
219
+ diag.push(`[init] ${(init.stderr || init.stdout || '(no output)').trim()}`);
220
+ // (2) Overwrite the Basic library with our macro.
221
+ mkdirSync(basicDir, { recursive: true });
222
+ writeFileSync(path.join(userDir, 'basic', 'script.xlc'), SCRIPT_XLC);
223
+ writeFileSync(path.join(basicDir, 'script.xlb'), SCRIPT_XLB);
224
+ writeFileSync(path.join(basicDir, 'Module1.xba'), module1Xba(jobsPath, marker));
225
+ // (3) Run the macro via a bare macro:/// URL — but only once the init instance has fully
226
+ // released the profile, so LibreOffice's single-instance model can't forward our macro command
227
+ // to the dying init process. Retry once: the first macro launch can still race a slow init exit.
228
+ for (let attempt = 1; attempt <= 2 && !existsSync(marker); attempt++) {
229
+ await settleProfile(profile);
230
+ const run = await runSoffice(soffice, [...baseArgs, 'macro:///Standard.Module1.RunOracle'], 45_000);
231
+ diag.push(`[macro attempt ${attempt}] ${(run.stderr || run.stdout || '(no output)').trim()}`);
232
+ if (!existsSync(marker))
233
+ await sleep(400);
234
+ }
235
+ if (!existsSync(marker)) {
236
+ keepWork = Boolean(process.env.SAFE_DOCX_ORACLE_DEBUG);
237
+ throw new Error('LibreOffice oracle macro did not run (no marker file) after 2 attempts.\nsoffice output:\n' +
238
+ diag.join('\n') +
239
+ (keepWork ? `\n(profile kept for debugging at ${work})` : ' (set SAFE_DOCX_ORACLE_DEBUG=1 to keep the profile)'));
240
+ }
241
+ return Promise.all(outPaths.map(async (p, i) => {
242
+ if (!existsSync(p))
243
+ throw new Error(`LibreOffice oracle produced no output for ${path.basename(p)}`);
244
+ if (isOdtJob(jobs[i]) || isConvertJob(jobs[i])) {
245
+ const contentXml = await readZipText(readFileSync(p), 'content.xml');
246
+ if (contentXml == null)
247
+ throw new Error(`content.xml not found in oracle output ${path.basename(p)}`);
248
+ return contentXml;
249
+ }
250
+ return extractDocumentXml(readFileSync(p));
251
+ }));
252
+ }
253
+ finally {
254
+ if (!keepWork)
255
+ rmSync(work, { recursive: true, force: true });
256
+ }
257
+ }
258
+ /**
259
+ * Structural projection of a `word/document.xml`: one entry per top-level body paragraph,
260
+ * recording whether it carries visible text (a non-whitespace `w:t` descendant). This captures
261
+ * the paragraph-collapse claim the oracle is authoritative for — how many paragraphs survive and
262
+ * which collapsed to empty — without depending on revision-markup or formatting details (which
263
+ * LibreOffice rewrites). `[]`-length is the paragraph count.
264
+ */
265
+ export function paragraphShape(documentXml) {
266
+ const doc = parseXml(documentXml);
267
+ const body = doc.getElementsByTagNameNS(W_NS, 'body').item(0) ?? doc.documentElement;
268
+ if (!body)
269
+ return [];
270
+ const shape = [];
271
+ for (let i = 0; i < body.childNodes.length; i++) {
272
+ const node = body.childNodes[i];
273
+ if (node.nodeType !== 1)
274
+ continue;
275
+ const el = node;
276
+ if (el.namespaceURI !== W_NS || el.localName !== 'p')
277
+ continue; // skip w:sectPr etc.
278
+ const texts = el.getElementsByTagNameNS(W_NS, 't');
279
+ let hasText = false;
280
+ for (let j = 0; j < texts.length; j++) {
281
+ if ((texts.item(j).textContent ?? '').trim().length > 0) {
282
+ hasText = true;
283
+ break;
284
+ }
285
+ }
286
+ shape.push(hasText);
287
+ }
288
+ return shape;
289
+ }
290
+ //# sourceMappingURL=libreoffice-oracle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"libreoffice-oracle.js","sourceRoot":"","sources":["../../src/integration/libreoffice-oracle.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@ export interface SyntheticDocxOptions {
2
2
  paragraphs: string[];
3
3
  footnoteOnParagraph?: number;
4
4
  footnoteText?: string;
5
+ endnoteOnParagraph?: number;
6
+ endnoteText?: string;
5
7
  commentOnParagraph?: number;
6
8
  commentText?: string;
7
9
  commentAuthor?: string;
@@ -46,14 +48,84 @@ export interface SyntheticDocxOptions {
46
48
  name: string;
47
49
  id?: number;
48
50
  };
51
+ /**
52
+ * Multi-paragraph comment range with body-level markers: the
53
+ * commentRangeStart is emitted as a sibling of <w:p> before
54
+ * paragraphs[startBeforeParagraph] and the commentRangeEnd as a sibling
55
+ * after paragraphs[endAfterParagraph]. The commentReference run is
56
+ * appended inside paragraphs[endAfterParagraph]. This is the issue #103
57
+ * shape: range markers spanning whole paragraphs sit outside any <w:p>.
58
+ *
59
+ * Mutually exclusive with commentOnParagraph and commentSpanParagraphs.
60
+ */
61
+ siblingCommentRange?: {
62
+ startBeforeParagraph: number;
63
+ endAfterParagraph: number;
64
+ };
65
+ /**
66
+ * Pre-existing tracked move with explicit in-paragraph range markers.
67
+ * paragraphs[from]'s text is wrapped in <w:moveFrom> (as w:delText)
68
+ * bracketed by w:moveFromRangeStart/End; paragraphs[to]'s text is wrapped
69
+ * in <w:moveTo> bracketed by w:moveToRangeStart/End. All four markers are
70
+ * direct children of their <w:p>, sharing the given w:name. Callers should
71
+ * pass identical text for paragraphs[from] and paragraphs[to] so the shape
72
+ * matches what Word produces for a real tracked move. Used to verify
73
+ * explicit move-range marker reconstruction (issue #110).
74
+ */
75
+ trackedMove?: {
76
+ from: number;
77
+ to: number;
78
+ name: string;
79
+ author?: string;
80
+ firstId?: number;
81
+ };
82
+ /**
83
+ * Range permission spanning paragraphs. permStart opens at the start of
84
+ * paragraphs[start] and permEnd closes at the end of paragraphs[end].
85
+ * start === end yields a single-paragraph span around that paragraph's run.
86
+ */
87
+ permSpanParagraphs?: {
88
+ start: number;
89
+ end: number;
90
+ id?: number;
91
+ };
92
+ /**
93
+ * Body-level (sibling of <w:p>) permStart/permEnd pair inserted between
94
+ * paragraphs[index - 1] and paragraphs[index]. Used to verify scaffold
95
+ * perm markers do not leak into reconstructed paragraphs.
96
+ */
97
+ siblingPermBefore?: {
98
+ index: number;
99
+ id?: number;
100
+ };
49
101
  }
50
102
  export declare function buildSyntheticDocx(opts: SyntheticDocxOptions): Promise<Buffer>;
103
+ /** Parts for {@link buildDocxFromParts}. Only the body XML is required. */
104
+ export interface DocxPartsOptions {
105
+ /** Raw `<w:body>` children (paragraphs/tables), without the `<w:body>` wrapper. */
106
+ bodyXml: string;
107
+ /** Full `word/styles.xml` content. */
108
+ stylesXml?: string;
109
+ /** Full `word/numbering.xml` content. */
110
+ numberingXml?: string;
111
+ /** Extra `<Relationship …/>` entries for `word/_rels/document.xml.rels` (e.g. hyperlinks). */
112
+ documentRelEntries?: string[];
113
+ }
114
+ /**
115
+ * Build a loadable DOCX from raw part XML. The `testing/ooxml-fixtures.ts`
116
+ * `buildDocxFromBodyXml` covers the body-only case for docx-core's own tests, but it lives
117
+ * in the build-excluded testing tree; this builder is exported from the package root for
118
+ * downstream suites (odf-core's DOCX→ODT conversion tests) and additionally accepts the
119
+ * optional styles/numbering/relationship parts those tests exercise.
120
+ */
121
+ export declare function buildDocxFromParts(opts: DocxPartsOptions): Promise<Buffer>;
51
122
  export interface SyntheticResultParts {
52
123
  documentXml: string;
53
124
  footnotesXml: string | null;
54
125
  endnotesXml: string | null;
55
126
  commentsXml: string | null;
56
127
  commentsExtendedXml: string | null;
128
+ commentsIdsXml: string | null;
57
129
  peopleXml: string | null;
58
130
  contentTypesXml: string | null;
59
131
  relsXml: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"synthetic-docx-fixture.d.ts","sourceRoot":"","sources":["../../src/integration/synthetic-docx-fixture.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD;;;OAGG;IACH,mBAAmB,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE;;;;OAIG;IACH,qBAAqB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtE;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4MpF;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAYxF"}
1
+ {"version":3,"file":"synthetic-docx-fixture.d.ts","sourceRoot":"","sources":["../../src/integration/synthetic-docx-fixture.ts"],"names":[],"mappings":"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"}