@gmickel/gno 1.46.0 → 2.0.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 (230) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.md +46 -0
  3. package/assets/skill/SKILL.md +7 -6
  4. package/assets/spa-production.json.gz +0 -0
  5. package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
  6. package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
  7. package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
  8. package/browser-extension/dist/manifest.json +1 -1
  9. package/browser-extension/dist/preview.html +1 -1
  10. package/browser-extension/dist/service-worker.js +32 -33
  11. package/bunfig.toml +2 -0
  12. package/package.json +40 -26
  13. package/spec/cli.md +21 -4
  14. package/spec/db/schema.sql +146 -1
  15. package/spec/mcp.md +26 -0
  16. package/src/app/context-runtime-types.ts +3 -0
  17. package/src/app/context-runtime.ts +2 -0
  18. package/src/cli/commands/ask.ts +6 -1
  19. package/src/cli/commands/daemon.ts +21 -8
  20. package/src/cli/commands/embed.ts +77 -41
  21. package/src/cli/detach.ts +3 -2
  22. package/src/config/types.ts +3 -3
  23. package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
  24. package/src/converters/adapters/officeparser/adapter.ts +1 -2
  25. package/src/converters/versions.ts +6 -8
  26. package/src/core/context-evidence.ts +8 -4
  27. package/src/core/job-manager.ts +95 -13
  28. package/src/core/network-boundary-inventory.ts +10 -0
  29. package/src/core/shutdown-budget.ts +45 -0
  30. package/src/embed/backlog.ts +107 -4
  31. package/src/embed/batch.ts +42 -2
  32. package/src/embed/fingerprint.ts +16 -0
  33. package/src/embed/retry.ts +113 -5
  34. package/src/embed/variant-backlog.ts +105 -0
  35. package/src/embed/variant-plan.ts +62 -0
  36. package/src/embed/variant-retry.ts +113 -0
  37. package/src/ingestion/graph-reconciliation.ts +327 -0
  38. package/src/ingestion/sync.ts +9 -272
  39. package/src/llm/http-inference.ts +6 -0
  40. package/src/llm/httpEmbedding.ts +37 -6
  41. package/src/llm/httpGeneration.ts +18 -3
  42. package/src/llm/httpRerank.ts +23 -5
  43. package/src/llm/inference-cancellation.ts +168 -0
  44. package/src/llm/inference-scope.ts +202 -0
  45. package/src/llm/lazy-ports.ts +115 -0
  46. package/src/llm/native-worker/client.ts +541 -0
  47. package/src/llm/native-worker/dispatcher.ts +228 -0
  48. package/src/llm/native-worker/embedding-identity.ts +33 -0
  49. package/src/llm/native-worker/entry.ts +173 -0
  50. package/src/llm/native-worker/errors.ts +32 -0
  51. package/src/llm/native-worker/evaluation.ts +16 -0
  52. package/src/llm/native-worker/owned-exit.ts +108 -0
  53. package/src/llm/native-worker/owner.ts +141 -0
  54. package/src/llm/native-worker/ports.ts +317 -0
  55. package/src/llm/native-worker/protocol.ts +442 -0
  56. package/src/llm/native-worker/runtime-config.ts +92 -0
  57. package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
  58. package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
  59. package/src/llm/nodeLlamaCpp/generation.ts +34 -5
  60. package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
  61. package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
  62. package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
  63. package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
  64. package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
  65. package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
  66. package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
  67. package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
  68. package/src/llm/types.ts +35 -5
  69. package/src/mcp/context.ts +27 -0
  70. package/src/mcp/http-transport.ts +12 -10
  71. package/src/mcp/server.ts +3 -0
  72. package/src/mcp/tool-profile.ts +30 -8
  73. package/src/mcp/tools/context.ts +8 -11
  74. package/src/mcp/tools/embed.ts +1 -1
  75. package/src/mcp/tools/index-cmd.ts +1 -1
  76. package/src/mcp/tools/index.ts +10 -8
  77. package/src/mcp/tools/query.ts +14 -30
  78. package/src/mcp/tools/vsearch.ts +1 -1
  79. package/src/pipeline/answer.ts +23 -3
  80. package/src/pipeline/claim-verifier.ts +6 -0
  81. package/src/pipeline/expansion.ts +43 -40
  82. package/src/pipeline/explain.ts +6 -2
  83. package/src/pipeline/filters.ts +63 -0
  84. package/src/pipeline/fusion.ts +29 -9
  85. package/src/pipeline/graph-retrieval.ts +29 -9
  86. package/src/pipeline/hybrid.ts +198 -55
  87. package/src/pipeline/hydration.ts +161 -0
  88. package/src/pipeline/owner-fusion.ts +87 -0
  89. package/src/pipeline/rerank.ts +35 -11
  90. package/src/pipeline/search.ts +13 -2
  91. package/src/pipeline/types.ts +5 -3
  92. package/src/pipeline/vsearch.ts +87 -7
  93. package/src/sdk/client.ts +47 -3
  94. package/src/sdk/embed.ts +63 -39
  95. package/src/serve/background-runtime.ts +1 -1
  96. package/src/serve/context.ts +41 -56
  97. package/src/serve/embed-scheduler.ts +58 -35
  98. package/src/serve/public/components/IndexingProgress.tsx +46 -60
  99. package/src/serve/public/globals.built.css +1 -1
  100. package/src/serve/public/lib/shiki-language-ids.ts +14 -0
  101. package/src/serve/resident-admission.ts +36 -36
  102. package/src/serve/resident-background-work.ts +20 -2
  103. package/src/serve/resident-request.ts +11 -5
  104. package/src/serve/resident-runtime.ts +97 -61
  105. package/src/serve/resident-shutdown.ts +153 -0
  106. package/src/serve/routes/api.ts +3 -1
  107. package/src/serve/server.ts +47 -26
  108. package/src/store/migrations/028-vector-variants.ts +54 -0
  109. package/src/store/migrations/029-graph-reference-state.ts +77 -0
  110. package/src/store/migrations/index.ts +4 -0
  111. package/src/store/sqlite/adapter.ts +251 -183
  112. package/src/store/sqlite/eligibility.ts +174 -0
  113. package/src/store/sqlite/graph-edge-application.ts +66 -0
  114. package/src/store/sqlite/graph-reference-state.ts +194 -0
  115. package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
  116. package/src/store/types.ts +80 -12
  117. package/src/store/vector/eligibility.ts +36 -0
  118. package/src/store/vector/freshness.ts +33 -6
  119. package/src/store/vector/lazy.ts +81 -0
  120. package/src/store/vector/sqlite-vec.ts +106 -54
  121. package/src/store/vector/stats.ts +14 -3
  122. package/src/store/vector/types.ts +35 -2
  123. package/src/store/vector/variant-search.ts +192 -0
  124. package/src/store/vector/variants.ts +451 -0
  125. package/vendor/converters/markitdown-ts/LICENSE +21 -0
  126. package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
  127. package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
  128. package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
  129. package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
  130. package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
  131. package/vendor/converters/markitdown-ts/package.json +77 -0
  132. package/vendor/converters/officeparser/LICENSE +21 -0
  133. package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
  134. package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
  135. package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
  136. package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
  137. package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
  138. package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
  139. package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
  140. package/vendor/converters/officeparser/dist/cli.js +381 -0
  141. package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
  142. package/vendor/converters/officeparser/dist/defaults.js +218 -0
  143. package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
  144. package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
  145. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
  146. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
  147. package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
  148. package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
  149. package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
  150. package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
  151. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
  152. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
  153. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
  154. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
  155. package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
  156. package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
  157. package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
  158. package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
  159. package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
  160. package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
  161. package/vendor/converters/officeparser/dist/index.d.ts +60 -0
  162. package/vendor/converters/officeparser/dist/index.js +72 -0
  163. package/vendor/converters/officeparser/dist/index.mjs +18 -0
  164. package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
  165. package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
  166. package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
  167. package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
  168. package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
  169. package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
  170. package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
  171. package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
  172. package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
  173. package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
  174. package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
  175. package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
  176. package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
  177. package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
  178. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
  179. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
  180. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
  181. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
  182. package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
  183. package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
  184. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
  185. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
  186. package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
  187. package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
  188. package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
  189. package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
  190. package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
  191. package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
  192. package/vendor/converters/officeparser/dist/types.js +107 -0
  193. package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
  194. package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
  195. package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
  196. package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
  197. package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
  198. package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
  199. package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
  200. package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
  201. package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
  202. package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
  203. package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
  204. package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
  205. package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
  206. package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
  207. package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
  208. package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
  209. package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
  210. package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
  211. package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
  212. package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
  213. package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
  214. package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
  215. package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
  216. package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
  217. package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
  218. package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
  219. package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
  220. package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
  221. package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
  222. package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
  223. package/vendor/converters/officeparser/package.json +147 -0
  224. package/vendor/converters/upstream-manifest.json +124 -0
  225. package/vendor/dependency-fixes/README.md +77 -0
  226. package/vendor/dependency-fixes/vendor-converters.py +83 -0
  227. package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
  228. package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
  229. package/browser-extension/dist/chunk-627emwpj.js +0 -75
  230. /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
@@ -0,0 +1,1177 @@
1
+ "use strict";
2
+ /**
3
+ * Word Document (DOCX) Parser
4
+ *
5
+ * **DOCX Format Overview:**
6
+ * DOCX is the default format for Microsoft Word documents since Office 2007.
7
+ * It's based on the Office Open XML (OOXML) standard (ECMA-376, ISO/IEC 29500).
8
+ *
9
+ * **File Structure:**
10
+ * DOCX files are ZIP archives containing:
11
+ * - `word/document.xml` - Main document content
12
+ * - `word/styles.xml` - Style definitions
13
+ * - `word/numbering.xml` - List numbering definitions
14
+ * - `word/footnotes.xml` - Footnotes content
15
+ * - `word/media/*` - Embedded images and media
16
+ * - `docProps/core.xml` - Document metadata
17
+ * - `[Content_Types].xml` - MIME type mappings
18
+ *
19
+ * **XML Structure (word/document.xml):**
20
+ * ```xml
21
+ * <w:document>
22
+ * <w:body>
23
+ * <w:p> <!-- Paragraph -->
24
+ * <w:pPr> <!-- Paragraph properties -->
25
+ * <w:pStyle w:val="Heading1"/>
26
+ * </w:pPr>
27
+ * <w:r> <!-- Run (text with same formatting) -->
28
+ * <w:rPr> <!-- Run properties -->
29
+ * <w:b/> <!-- Bold -->
30
+ * <w:sz w:val="24"/> <!-- Font size (half-points) -->
31
+ * </w:rPr>
32
+ * <w:t>Hello</w:t> <!-- Text -->
33
+ * </w:r>
34
+ * </w:p>
35
+ * </w:body>
36
+ * </w:document>
37
+ * ```
38
+ *
39
+ * **Key OOXML Elements:**
40
+ * - `<w:p>` - Paragraph
41
+ * - `<w:r>` - Run (contiguous text with same formatting)
42
+ * - `<w:t>` - Text content
43
+ * - `<w:br>` - Line or page break
44
+ * - `<w:b>`, `<w:i>`, `<w:u>` - Bold, italic, underline
45
+ * - `<w:pStyle>` - Paragraph style (for headings)
46
+ * - `<w:numPr>` - List numbering properties
47
+ * - `<w:tbl>` - Table
48
+ * - `<w:drawing>` - Drawing/image
49
+ *
50
+ * **Parsing Approach:**
51
+ * 1. Extract ZIP contents
52
+ * 2. Parse word/document.xml for structure and text
53
+ * 3. Extract formatting from run properties (rPr)
54
+ * 4. Identify headings via paragraph styles
55
+ * 5. Extract footnotes from word/footnotes.xml
56
+ * 6. Process embedded images from word/media/*
57
+ * 7. Parse metadata from docProps/core.xml
58
+ *
59
+ * @module WordParser
60
+ * @see https://www.ecma-international.org/publications-and-standards/standards/ecma-376/ OOXML Standard
61
+ * @see https://learn.microsoft.com/en-us/openspecs/office_standards/ms-docx/ [MS-DOCX] Specification
62
+ */
63
+ Object.defineProperty(exports, "__esModule", { value: true });
64
+ exports.parseWord = void 0;
65
+ const types_js_1 = require("../types.js");
66
+ const astUtils_js_1 = require("../utils/astUtils.js");
67
+ const errorUtils_js_1 = require("../utils/errorUtils.js");
68
+ const imageUtils_js_1 = require("../utils/imageUtils.js");
69
+ const mathUtils_js_1 = require("../utils/mathUtils.js");
70
+ const ocrUtils_js_1 = require("../utils/ocrUtils.js");
71
+ const xmlUtils_js_1 = require("../utils/xmlUtils.js");
72
+ const zipUtils_js_1 = require("../utils/zipUtils.js");
73
+ /**
74
+ * Parses a Word document (.docx) and extracts content, formatting, and metadata.
75
+ *
76
+ * The parsing process:
77
+ * 1. Unzip the DOCX file
78
+ * 2. Parse word/document.xml to extract paragraphs and runs
79
+ * 3. Extract text formatting from run properties
80
+ * 4. Identify headings from paragraph styles
81
+ * 5. Process lists from numbering properties
82
+ * 6. Extract images and optionally perform OCR
83
+ * 7. Parse document metadata
84
+ *
85
+ * @param buffer - The DOCX file as a Buffer
86
+ * @param config - Parser configuration options
87
+ * @returns A promise resolving to the parsed AST
88
+ */
89
+ const parseWord = async (buffer, config) => {
90
+ // Honour cancellation requests immediately — before opening the ZIP archive, loading XML
91
+ // files, or kicking off any OCR work. DOCX files can be large and the inflate + XML-parse
92
+ // steps are synchronous-heavy, so failing fast here avoids wasted CPU time.
93
+ (0, errorUtils_js_1.checkAbortSignal)(config.abortSignal);
94
+ const documentFileRegex = /word\/document[\d+]?.xml/;
95
+ const footnotesFileRegex = /word\/footnotes[\d+]?.xml/;
96
+ const endnotesFileRegex = /word\/endnotes[\d+]?.xml/;
97
+ const commentsFileRegex = /word\/comments[\d+]?.xml/;
98
+ // Headers and footers are the only parts a document can have many of: Word writes up to
99
+ // three per section (default, first page, even pages), so a handful of sections is enough
100
+ // to reach header10.xml. The single-character form the other parts use stops matching at
101
+ // nine, which would drop those later files as silently as not extracting them at all.
102
+ const headerFileRegex = /word\/header\d*\.xml/;
103
+ const footerFileRegex = /word\/footer\d*\.xml/;
104
+ const numberingFileRegex = /word\/numbering[\d+]?.xml/;
105
+ const mediaFileRegex = /(word\/)?media\/.*/;
106
+ const corePropsFileRegex = /docProps\/core[\d+]?.xml/;
107
+ const customPropsFileRegex = /docProps\/custom\.xml/;
108
+ const appPropsFileRegex = /docProps\/app[\d+]?.xml/;
109
+ const relsFileRegex = /word\/_rels\/document[\d+]?.xml\.rels/;
110
+ const stylesFileRegex = /word\/styles[\d+]?.xml/;
111
+ // Helper to extract formatting from run properties XML string
112
+ const extractFormattingFromXml = (rPr) => {
113
+ const formatting = {};
114
+ // Helper to check boolean properties (e.g., <w:b />, <w:i w:val="0" />)
115
+ const getBoolVal = (parent, tagName) => {
116
+ const el = (0, xmlUtils_js_1.getFirstElementByTagName)(parent, tagName);
117
+ if (el) {
118
+ const val = el.getAttribute('w:val');
119
+ // In OOXML, if the element is present without w:val, it's true.
120
+ // If w:val is present, it can be '1', 'true', 'on' for true.
121
+ if (val === null)
122
+ return true;
123
+ return val === '1' || val === 'true' || val === 'on';
124
+ }
125
+ return null;
126
+ };
127
+ const bold = getBoolVal(rPr, 'w:b');
128
+ if (bold !== null)
129
+ formatting.bold = bold;
130
+ const italic = getBoolVal(rPr, 'w:i');
131
+ if (italic !== null)
132
+ formatting.italic = italic;
133
+ const u = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:u');
134
+ if (u) {
135
+ const val = u.getAttribute('w:val');
136
+ // If val is missing, it's a default underline (true).
137
+ // If val is present, it's true unless explicit 'none'.
138
+ if (!val || val !== 'none') {
139
+ formatting.underline = true;
140
+ }
141
+ }
142
+ const strike = getBoolVal(rPr, 'w:strike');
143
+ const dstrike = getBoolVal(rPr, 'w:dstrike');
144
+ if (strike !== null)
145
+ formatting.strikethrough = strike;
146
+ else if (dstrike !== null)
147
+ formatting.strikethrough = dstrike;
148
+ // Font size (w:sz) - stored in half-points
149
+ const sz = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:sz');
150
+ if (sz) {
151
+ const val = sz.getAttribute('w:val');
152
+ if (val) {
153
+ formatting.size = (parseInt(val, 10) / 2).toString() + 'pt';
154
+ }
155
+ }
156
+ // Color (w:color)
157
+ const color = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:color');
158
+ if (color) {
159
+ const val = color.getAttribute('w:val');
160
+ if (val && val !== 'auto') {
161
+ formatting.color = '#' + val;
162
+ }
163
+ }
164
+ // Background color (w:shd) - shading
165
+ const shd = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:shd');
166
+ if (shd) {
167
+ const val = shd.getAttribute('w:fill');
168
+ if (val && val !== 'auto') {
169
+ formatting.backgroundColor = '#' + val;
170
+ }
171
+ }
172
+ // Highlight (w:highlight) - maps to background color in our AST
173
+ const highlight = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:highlight');
174
+ if (highlight) {
175
+ const val = highlight.getAttribute('w:val');
176
+ if (val && val !== 'none') {
177
+ const colorMap = {
178
+ 'yellow': '#FFFF00', 'green': '#00FF00', 'cyan': '#00FFFF', 'magenta': '#FF00FF',
179
+ 'blue': '#0000FF', 'red': '#FF0000', 'darkBlue': '#00008B', 'darkCyan': '#008B8B',
180
+ 'darkGreen': '#006400', 'darkMagenta': '#8B008B', 'darkRed': '#8B0000',
181
+ 'darkYellow': '#808000', 'darkGray': '#A9A9A9', 'lightGray': '#D3D3D3', 'black': '#000000'
182
+ };
183
+ formatting.backgroundColor = colorMap[val] || val;
184
+ }
185
+ }
186
+ // Font family (w:rFonts)
187
+ const rFonts = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:rFonts');
188
+ if (rFonts) {
189
+ // Priority: ascii (Western) > hAnsi (High ANSI)
190
+ const font = rFonts.getAttribute('w:ascii') || rFonts.getAttribute('w:hAnsi');
191
+ if (font) {
192
+ formatting.font = font;
193
+ }
194
+ }
195
+ // Subscript/Superscript (w:vertAlign)
196
+ const vertAlign = (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, 'w:vertAlign');
197
+ if (vertAlign) {
198
+ const val = vertAlign.getAttribute('w:val');
199
+ if (val === 'subscript')
200
+ formatting.subscript = true;
201
+ else if (val === 'superscript')
202
+ formatting.superscript = true;
203
+ }
204
+ return formatting;
205
+ };
206
+ // Helper to extract indentation from paragraph properties XML string
207
+ const extractIndentationFromXml = (pPr) => {
208
+ const ind = (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:ind");
209
+ if (ind) {
210
+ const indentation = {};
211
+ const left = ind.getAttribute("w:left") || ind.getAttribute("w:start");
212
+ const right = ind.getAttribute("w:right") || ind.getAttribute("w:end");
213
+ const firstLine = ind.getAttribute("w:firstLine");
214
+ const hanging = ind.getAttribute("w:hanging");
215
+ if (left)
216
+ indentation.left = parseInt(left, 10);
217
+ if (right)
218
+ indentation.right = parseInt(right, 10);
219
+ if (firstLine)
220
+ indentation.firstLine = parseInt(firstLine, 10);
221
+ if (hanging)
222
+ indentation.hanging = parseInt(hanging, 10);
223
+ return Object.keys(indentation).length > 0 ? indentation : undefined;
224
+ }
225
+ return undefined;
226
+ };
227
+ /**
228
+ * Resolves mc:AlternateContent by preferring mc:Fallback if choice namespace is not recognized,
229
+ * or simply the first available valid child.
230
+ */
231
+ const resolveAlternateContent = (element) => {
232
+ const choice = (0, xmlUtils_js_1.getFirstElementByTagName)(element, "mc:Choice");
233
+ // In most cases, mc:Choice contains the modern version, but mc:Fallback is safer for legacy compatibility
234
+ // Mammoth often skips Choice if it's not handled. We'll try Choice first.
235
+ if (choice)
236
+ return Array.from(choice.childNodes);
237
+ const fallback = (0, xmlUtils_js_1.getFirstElementByTagName)(element, "mc:Fallback");
238
+ if (fallback)
239
+ return Array.from(fallback.childNodes);
240
+ return Array.from(element.childNodes);
241
+ };
242
+ const files = await (0, zipUtils_js_1.extractFiles)(buffer, x => !!x.match(documentFileRegex) ||
243
+ !!x.match(footnotesFileRegex) ||
244
+ !!x.match(endnotesFileRegex) ||
245
+ !!x.match(numberingFileRegex) ||
246
+ !!x.match(corePropsFileRegex) ||
247
+ !!x.match(customPropsFileRegex) ||
248
+ !!x.match(appPropsFileRegex) ||
249
+ !!x.match(relsFileRegex) ||
250
+ !!x.match(stylesFileRegex) ||
251
+ (!config.ignoreComments && !!x.match(commentsFileRegex)) ||
252
+ (!config.ignoreHeadersAndFooters && (!!x.match(headerFileRegex) || !!x.match(footerFileRegex))) ||
253
+ (!!config.extractAttachments && !!x.match(mediaFileRegex)), config.decompressionLimits, config);
254
+ // A DOCX without its main document part is not a DOCX. Checked with the same regex the
255
+ // parse loop below uses to recognize it, so the two cannot fall out of step.
256
+ (0, zipUtils_js_1.findRequiredPart)(files, path => !!path.match(documentFileRegex), config, { fileType: 'docx', part: 'word/document.xml' });
257
+ // Extract metadata
258
+ const corePropsFile = files.find(f => f.path.match(corePropsFileRegex));
259
+ const metadata = corePropsFile ? (0, xmlUtils_js_1.parseOfficeMetadata)(corePropsFile.content.toString()) : {};
260
+ const customPropsFile = files.find(f => f.path.match(customPropsFileRegex));
261
+ if (customPropsFile) {
262
+ const customProperties = (0, xmlUtils_js_1.parseOOXMLCustomProperties)(customPropsFile.content.toString());
263
+ if (Object.keys(customProperties).length > 0)
264
+ metadata.customProperties = customProperties;
265
+ }
266
+ const appPropsFile = files.find(f => f.path.match(appPropsFileRegex));
267
+ if (appPropsFile) {
268
+ const appProperties = (0, xmlUtils_js_1.parseOOXMLAppProperties)(appPropsFile.content.toString());
269
+ if (Object.keys(appProperties).length > 0) {
270
+ metadata.nativeProperties = appProperties;
271
+ if (appProperties['Pages'] && typeof appProperties['Pages'] === 'number') {
272
+ metadata.pages = appProperties['Pages'];
273
+ }
274
+ }
275
+ }
276
+ const footnoteMap = new Map();
277
+ const endnoteMap = new Map();
278
+ const commentMap = new Map();
279
+ const commentMetadataMap = new Map();
280
+ const attachments = [];
281
+ const mediaFiles = files.filter(f => f.path.match(mediaFileRegex));
282
+ // Extract relationships
283
+ const relsFile = files.find(f => f.path.match(relsFileRegex));
284
+ const relsMap = {};
285
+ if (relsFile) {
286
+ const relsXml = (0, xmlUtils_js_1.parseXmlString)(relsFile.content.toString());
287
+ const relationships = (0, xmlUtils_js_1.getElementsByTagName)(relsXml, "Relationship");
288
+ for (const relationship of relationships) {
289
+ const id = relationship.getAttribute("Id");
290
+ const target = relationship.getAttribute("Target");
291
+ if (id && target) {
292
+ relsMap[id] = target;
293
+ }
294
+ }
295
+ }
296
+ const numberingFile = files.find(f => f.path.match(numberingFileRegex));
297
+ const numberingMap = {};
298
+ if (numberingFile) {
299
+ const numberingXml = (0, xmlUtils_js_1.parseXmlString)(numberingFile.content.toString());
300
+ const nums = (0, xmlUtils_js_1.getElementsByTagName)(numberingXml, "w:num");
301
+ const abstractNums = (0, xmlUtils_js_1.getElementsByTagName)(numberingXml, "w:abstractNum");
302
+ const abstractNumMap = {};
303
+ for (const abstractNum of abstractNums) {
304
+ const abstractNumId = abstractNum.getAttribute("w:abstractNumId");
305
+ if (abstractNumId) {
306
+ abstractNumMap[abstractNumId] = abstractNum;
307
+ }
308
+ }
309
+ for (const num of nums) {
310
+ const numId = num.getAttribute("w:numId");
311
+ const abstractNumIdNode = (0, xmlUtils_js_1.getFirstElementByTagName)(num, "w:abstractNumId");
312
+ const abstractNumId = abstractNumIdNode?.getAttribute("w:val");
313
+ if (numId && abstractNumId && abstractNumMap[abstractNumId]) {
314
+ numberingMap[numId] = {};
315
+ // Inherit from abstractNum
316
+ const lvls = (0, xmlUtils_js_1.getElementsByTagName)(abstractNumMap[abstractNumId], "w:lvl");
317
+ for (const lvl of lvls) {
318
+ const ilvl = lvl.getAttribute("w:ilvl");
319
+ const numFmtNode = (0, xmlUtils_js_1.getFirstElementByTagName)(lvl, "w:numFmt");
320
+ const lvlTextNode = (0, xmlUtils_js_1.getFirstElementByTagName)(lvl, "w:lvlText");
321
+ const startNode = (0, xmlUtils_js_1.getFirstElementByTagName)(lvl, "w:start");
322
+ if (ilvl) {
323
+ numberingMap[numId][ilvl] = {
324
+ numFmt: numFmtNode?.getAttribute("w:val") || 'decimal',
325
+ lvlText: lvlTextNode?.getAttribute("w:val") || '',
326
+ start: parseInt(startNode?.getAttribute("w:val") || '1', 10)
327
+ };
328
+ }
329
+ }
330
+ // Apply instance overrides (w:lvlOverride)
331
+ const overrides = (0, xmlUtils_js_1.getElementsByTagName)(num, "w:lvlOverride");
332
+ for (const override of overrides) {
333
+ const ilvl = override.getAttribute("w:ilvl");
334
+ if (ilvl && numberingMap[numId][ilvl]) {
335
+ const startOverride = (0, xmlUtils_js_1.getFirstElementByTagName)(override, "w:startOverride");
336
+ if (startOverride) {
337
+ numberingMap[numId][ilvl].start = parseInt(startOverride.getAttribute("w:val") || '1', 10);
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+ }
344
+ // Parse Styles
345
+ const stylesFile = files.find(f => f.path.match(stylesFileRegex));
346
+ const styleMap = {};
347
+ if (stylesFile) {
348
+ const stylesXml = (0, xmlUtils_js_1.parseXmlString)(stylesFile.content.toString());
349
+ const styles = (0, xmlUtils_js_1.getElementsByTagName)(stylesXml, "w:style");
350
+ for (const style of styles) {
351
+ const styleId = style.getAttribute("w:styleId");
352
+ if (styleId) {
353
+ const rPr = (0, xmlUtils_js_1.getFirstElementByTagName)(style, "w:rPr");
354
+ const pPr = (0, xmlUtils_js_1.getFirstElementByTagName)(style, "w:pPr");
355
+ const formatting = rPr ? extractFormattingFromXml(rPr) : {};
356
+ let alignment = undefined;
357
+ let backgroundColor = undefined;
358
+ let paragraphIndentation = undefined;
359
+ if (pPr) {
360
+ const jc = (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:jc");
361
+ if (jc) {
362
+ const val = jc.getAttribute("w:val");
363
+ if (val === 'left' || val === 'center' || val === 'right' || val === 'justify') {
364
+ alignment = val;
365
+ }
366
+ }
367
+ const shd = (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:shd");
368
+ if (shd) {
369
+ const fill = shd.getAttribute("w:fill");
370
+ if (fill && fill !== 'auto')
371
+ backgroundColor = '#' + fill;
372
+ }
373
+ const ind = extractIndentationFromXml(pPr);
374
+ if (ind)
375
+ paragraphIndentation = ind;
376
+ }
377
+ styleMap[styleId] = { formatting, alignment, backgroundColor, paragraphIndentation };
378
+ }
379
+ }
380
+ }
381
+ // Extract document defaults
382
+ let docDefaults = {};
383
+ if (stylesFile) {
384
+ const stylesXml = (0, xmlUtils_js_1.parseXmlString)(stylesFile.content.toString());
385
+ const docDefaultsNode = (0, xmlUtils_js_1.getFirstElementByTagName)(stylesXml, "w:docDefaults");
386
+ if (docDefaultsNode) {
387
+ const rPrDefaultNode = (0, xmlUtils_js_1.getFirstElementByTagName)(docDefaultsNode, "w:rPrDefault");
388
+ if (rPrDefaultNode) {
389
+ const rPr = (0, xmlUtils_js_1.getFirstElementByTagName)(rPrDefaultNode, "w:rPr");
390
+ if (rPr) {
391
+ docDefaults = extractFormattingFromXml(rPr);
392
+ }
393
+ }
394
+ }
395
+ }
396
+ // Detect the default paragraph style (for international compatibility)
397
+ let defaultParaStyleId = undefined;
398
+ if (stylesFile) {
399
+ const stylesXml = (0, xmlUtils_js_1.parseXmlString)(stylesFile.content.toString());
400
+ const styles = (0, xmlUtils_js_1.getElementsByTagName)(stylesXml, "w:style");
401
+ // Look for a style with w:type="paragraph" and w:default="1"
402
+ for (const style of styles) {
403
+ const styleType = style.getAttribute("w:type");
404
+ const isDefault = style.getAttribute("w:default");
405
+ const styleId = style.getAttribute("w:styleId");
406
+ if (styleType === "paragraph" && isDefault === "1" && styleId) {
407
+ defaultParaStyleId = styleId;
408
+ break;
409
+ }
410
+ }
411
+ // Fallback: if no default found, try "Normal"
412
+ if (!defaultParaStyleId && styleMap["Normal"]) {
413
+ defaultParaStyleId = "Normal";
414
+ }
415
+ }
416
+ const content = [];
417
+ const numberingState = {};
418
+ const listCounters = {}; // Track item index per listId/level
419
+ // Helper to parse a paragraph node
420
+ const parseParagraph = (pNode, documentContent, pendingAnchorIds = []) => {
421
+ // Check if it's a list item
422
+ const numPr = (0, xmlUtils_js_1.getFirstElementByTagName)(pNode, "w:numPr");
423
+ const isList = !!numPr;
424
+ // Check if it's a heading
425
+ const pPr = (0, xmlUtils_js_1.getFirstElementByTagName)(pNode, "w:pPr");
426
+ const pStyle = pPr ? (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:pStyle") : null;
427
+ const pStyleVal = pStyle?.getAttribute("w:val");
428
+ const isHeading = pStyleVal ? (pStyleVal.startsWith("Heading") || pStyleVal === "Title") : false;
429
+ // Extract Paragraph Style Properties
430
+ const styleProps = pStyleVal && styleMap[pStyleVal] ? styleMap[pStyleVal] : { formatting: {} };
431
+ // Extract Alignment
432
+ let alignment = styleProps.alignment;
433
+ if (pPr) {
434
+ const jc = (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:jc");
435
+ if (jc) {
436
+ const val = jc.getAttribute("w:val");
437
+ if (val === 'left' || val === 'center' || val === 'right' || val === 'justify') {
438
+ alignment = val;
439
+ }
440
+ }
441
+ }
442
+ // Extract Indentation
443
+ let paraIndentation = styleProps.paragraphIndentation;
444
+ if (pPr) {
445
+ const ind = extractIndentationFromXml(pPr);
446
+ if (ind) {
447
+ paraIndentation = { ...paraIndentation, ...ind };
448
+ }
449
+ }
450
+ // Extract Paragraph Background
451
+ let paraBackgroundColor = styleProps.backgroundColor;
452
+ if (pPr) {
453
+ const shd = (0, xmlUtils_js_1.getFirstElementByTagName)(pPr, "w:shd");
454
+ if (shd) {
455
+ const fill = shd.getAttribute("w:fill");
456
+ if (fill && fill !== 'auto') {
457
+ paraBackgroundColor = '#' + fill;
458
+ }
459
+ }
460
+ }
461
+ // Runs inherit their base formatting from the style chain: the paragraph style (seeded
462
+ // here, and re-applied via the run-style path below), then any character style, then the
463
+ // run's own properties. The paragraph-mark run properties (`<w:pPr><w:rPr>`) format only
464
+ // the paragraph mark glyph itself per OOXML ISO 29500 §17.3.1.29, so they are deliberately
465
+ // NOT folded into the run base - doing so bled the paragraph mark's bold/italic/color/etc.
466
+ // onto every run in the paragraph (issue #109).
467
+ const paragraphRunFormatting = { ...styleProps.formatting };
468
+ // Extract text and children
469
+ let text = '';
470
+ const children = [];
471
+ const notes = [];
472
+ const comments = [];
473
+ // Traverse children of paragraph (runs, hyperlinks, etc.)
474
+ const processChildNode = (node) => {
475
+ if ((0, xmlUtils_js_1.isElement)(node) && (node.nodeName === 'w:r' || node.nodeName === 'm:r')) {
476
+ const runNode = node;
477
+ const rPr = (0, xmlUtils_js_1.getFirstElementByTagName)(runNode, "w:rPr");
478
+ // Formatting
479
+ let formatting = {};
480
+ // Apply paragraph-level formatting
481
+ for (const key in paragraphRunFormatting) {
482
+ formatting[key] = paragraphRunFormatting[key];
483
+ }
484
+ // Check for run style
485
+ const rStyle = rPr ? (0, xmlUtils_js_1.getFirstElementByTagName)(rPr, "w:rStyle") : null;
486
+ const rStyleVal = rStyle ? rStyle.getAttribute("w:val") : pStyleVal;
487
+ if (rStyleVal && styleMap[rStyleVal]) {
488
+ for (const key in styleMap[rStyleVal].formatting) {
489
+ formatting[key] = styleMap[rStyleVal].formatting[key];
490
+ }
491
+ }
492
+ // Apply direct run properties
493
+ if (rPr) {
494
+ const directFormatting = extractFormattingFromXml(rPr);
495
+ for (const key in directFormatting) {
496
+ const value = directFormatting[key];
497
+ if (value === false) {
498
+ delete formatting[key];
499
+ }
500
+ else if (value !== undefined) {
501
+ formatting[key] = value;
502
+ }
503
+ }
504
+ }
505
+ // Inherit paragraph background
506
+ if (!formatting.backgroundColor && paraBackgroundColor) {
507
+ formatting.backgroundColor = paraBackgroundColor;
508
+ }
509
+ for (const child of runNode.childNodes) {
510
+ if (!(0, xmlUtils_js_1.isElement)(child))
511
+ continue;
512
+ // also handle unprefixed version (mirroring the behaviour of getElementsByTagName)
513
+ // Text content
514
+ if (child.tagName === "w:t" || child.tagName === "t" || child.tagName === "m:t") {
515
+ const tNode = child;
516
+ const tContent = tNode.textContent || '';
517
+ text += tContent;
518
+ const textNode = {
519
+ type: 'text',
520
+ text: tContent,
521
+ formatting: formatting
522
+ };
523
+ if (config.includeRawContent) {
524
+ textNode.rawContent = (0, xmlUtils_js_1.getRawContent)(tNode, documentContent, config);
525
+ }
526
+ // Always set a style: run style > paragraph style > detected default
527
+ // Use detected default style for international compatibility
528
+ const nodeStyle = rStyleVal || pStyleVal || defaultParaStyleId;
529
+ if (nodeStyle) {
530
+ textNode.metadata = { style: nodeStyle };
531
+ }
532
+ children.push(textNode);
533
+ }
534
+ // Break nodes
535
+ else if (config.includeBreakNodes &&
536
+ (child.tagName === "w:br"
537
+ || child.tagName === "br"
538
+ || child.tagName === "w:cr"
539
+ || child.tagName === "cr")) {
540
+ const brNode = child;
541
+ let breakType = 'textWrapping';
542
+ if (child.tagName === "w:cr" || child.tagName === "cr") {
543
+ breakType = 'carriageReturn';
544
+ }
545
+ else {
546
+ const nodeBreakType = brNode.getAttribute("w:type") || brNode.getAttribute("type");
547
+ if (nodeBreakType !== null) {
548
+ breakType = nodeBreakType;
549
+ }
550
+ }
551
+ let breakClear = undefined;
552
+ if (breakType === 'textWrapping' && brNode.getAttribute("w:clear") !== null) {
553
+ breakClear = brNode.getAttribute("w:clear");
554
+ }
555
+ const breakNode = {
556
+ type: 'break',
557
+ metadata: { breakType, clear: breakClear }
558
+ };
559
+ if (config.includeRawContent) {
560
+ breakNode.rawContent = (0, xmlUtils_js_1.getRawContent)(brNode, documentContent, config);
561
+ }
562
+ children.push(breakNode);
563
+ }
564
+ else if (config.includeBreakNodes && (child.tagName === "w:lastRenderedPageBreak" || child.tagName === "lastRenderedPageBreak")) {
565
+ const breakNode = {
566
+ type: 'break',
567
+ metadata: { breakType: 'lastRenderedPage' }
568
+ };
569
+ if (config.includeRawContent) {
570
+ breakNode.rawContent = (0, xmlUtils_js_1.getRawContent)(child, documentContent, config);
571
+ }
572
+ children.push(breakNode);
573
+ }
574
+ }
575
+ // Images/Drawings
576
+ if (config.extractAttachments) {
577
+ const drawings = (0, xmlUtils_js_1.getElementsByTagName)(runNode, "w:drawing");
578
+ const picts = (0, xmlUtils_js_1.getElementsByTagName)(runNode, "w:pict");
579
+ const allImages = [...drawings, ...picts];
580
+ for (const imgNode of allImages) {
581
+ const imgXml = (0, xmlUtils_js_1.serializeXml)(imgNode);
582
+ // Extract Alt Text
583
+ let altText = '';
584
+ const docPr = (0, xmlUtils_js_1.getFirstElementByTagName)(imgNode, "wp:docPr");
585
+ if (docPr) {
586
+ altText = docPr.getAttribute("descr") || docPr.getAttribute("title") || '';
587
+ }
588
+ // Extract Relationship ID
589
+ let rId = '';
590
+ const blip = (0, xmlUtils_js_1.getFirstElementByTagName)(imgNode, "a:blip");
591
+ if (blip) {
592
+ rId = blip.getAttribute("r:embed") || '';
593
+ }
594
+ else {
595
+ const imagedata = (0, xmlUtils_js_1.getFirstElementByTagName)(imgNode, "v:imagedata");
596
+ if (imagedata) {
597
+ rId = imagedata.getAttribute("r:id") || '';
598
+ }
599
+ }
600
+ if (rId && relsMap[rId]) {
601
+ const target = relsMap[rId];
602
+ const filename = target.split('/').pop();
603
+ if (filename) {
604
+ const imageNode = {
605
+ type: 'image',
606
+ text: '',
607
+ metadata: { attachmentName: filename, altText: altText }
608
+ };
609
+ if (config.includeRawContent) {
610
+ imageNode.rawContent = (0, xmlUtils_js_1.getRawContent)(imgNode, documentContent, config);
611
+ }
612
+ children.push(imageNode);
613
+ }
614
+ }
615
+ else {
616
+ const imageNode = {
617
+ type: 'image',
618
+ text: '',
619
+ };
620
+ if (config.includeRawContent) {
621
+ imageNode.rawContent = (0, xmlUtils_js_1.getRawContent)(imgNode, documentContent, config);
622
+ }
623
+ children.push(imageNode);
624
+ }
625
+ }
626
+ }
627
+ // Footnotes/Endnotes inside runs
628
+ if (!config.ignoreNotes) {
629
+ const footnoteRef = (0, xmlUtils_js_1.getFirstElementByTagName)(runNode, "w:footnoteReference");
630
+ if (footnoteRef) {
631
+ const id = footnoteRef.getAttribute("w:id");
632
+ if (id && footnoteMap.has(id)) {
633
+ const noteNodes = footnoteMap.get(id);
634
+ const noteNode = {
635
+ type: 'note',
636
+ text: noteNodes.map((n) => n.text).join(' '),
637
+ children: noteNodes,
638
+ metadata: { noteType: 'footnote', noteId: id }
639
+ };
640
+ if (children.length > 0) {
641
+ const target = children[children.length - 1];
642
+ if (!target.notes)
643
+ target.notes = [];
644
+ target.notes.push(noteNode);
645
+ }
646
+ else {
647
+ notes.push(noteNode);
648
+ }
649
+ }
650
+ }
651
+ const endnoteRef = (0, xmlUtils_js_1.getFirstElementByTagName)(runNode, "w:endnoteReference");
652
+ if (endnoteRef) {
653
+ const id = endnoteRef.getAttribute("w:id");
654
+ if (id && endnoteMap.has(id)) {
655
+ const noteNodes = endnoteMap.get(id);
656
+ const noteNode = {
657
+ type: 'note',
658
+ text: noteNodes.map((n) => n.text).join(' '),
659
+ children: noteNodes,
660
+ metadata: { noteType: 'endnote', noteId: id }
661
+ };
662
+ if (children.length > 0) {
663
+ const target = children[children.length - 1];
664
+ if (!target.notes)
665
+ target.notes = [];
666
+ target.notes.push(noteNode);
667
+ }
668
+ else {
669
+ notes.push(noteNode);
670
+ }
671
+ }
672
+ }
673
+ }
674
+ // Comments inside runs
675
+ if (!config.ignoreComments) {
676
+ const commentRef = (0, xmlUtils_js_1.getFirstElementByTagName)(runNode, "w:commentReference");
677
+ if (commentRef) {
678
+ const id = commentRef.getAttribute("w:id");
679
+ if (id && commentMap.has(id)) {
680
+ const commentNodes = commentMap.get(id);
681
+ const commentInfo = commentMetadataMap.get(id);
682
+ const commentNode = {
683
+ type: 'comment',
684
+ text: commentNodes.map((n) => n.text).join(' '),
685
+ children: commentNodes,
686
+ metadata: commentInfo || { commentId: id }
687
+ };
688
+ if (children.length > 0) {
689
+ const target = children[children.length - 1];
690
+ if (!target.comments)
691
+ target.comments = [];
692
+ target.comments.push(commentNode);
693
+ }
694
+ else {
695
+ comments.push(commentNode);
696
+ }
697
+ }
698
+ }
699
+ }
700
+ }
701
+ else if ((0, xmlUtils_js_1.isElement)(node) && node.nodeName === 'w:hyperlink') {
702
+ const hlNode = node;
703
+ const rId = hlNode.getAttribute("r:id");
704
+ const anchor = hlNode.getAttribute("w:anchor");
705
+ let linkMetadata;
706
+ if (anchor && !config.ignoreInternalLinks) {
707
+ linkMetadata = { link: '#' + anchor, linkType: 'internal' };
708
+ }
709
+ else if (rId && relsMap[rId]) {
710
+ linkMetadata = { link: relsMap[rId], linkType: 'external' };
711
+ }
712
+ // Process children of hyperlink (usually runs)
713
+ const hlChildren = Array.from(hlNode.childNodes);
714
+ for (const child of hlChildren) {
715
+ // Capture the current length of children to apply metadata to new nodes
716
+ const startIndex = children.length;
717
+ processChildNode(child);
718
+ // Apply link metadata to the newly added text nodes
719
+ if (linkMetadata) {
720
+ for (let i = startIndex; i < children.length; i++) {
721
+ if (children[i].type === 'text') {
722
+ children[i].metadata = { ...(children[i].metadata ?? {}), ...linkMetadata };
723
+ }
724
+ }
725
+ }
726
+ }
727
+ }
728
+ else if ((0, xmlUtils_js_1.isElement)(node) && node.nodeName === 'w:bookmarkStart') {
729
+ const bookmarkName = node.getAttribute("w:name");
730
+ if (bookmarkName && !bookmarkName.startsWith('_GoBack') && !config.ignoreInternalLinks) {
731
+ anchorIds.push(bookmarkName);
732
+ }
733
+ }
734
+ else if ((0, xmlUtils_js_1.isElement)(node) && (node.nodeName === 'mc:AlternateContent' || node.nodeName === 'AlternateContent')) {
735
+ const resolved = resolveAlternateContent(node);
736
+ for (const rNode of resolved)
737
+ processChildNode(rNode);
738
+ }
739
+ else if ((0, xmlUtils_js_1.isElement)(node) && (node.nodeName === 'w:pict' || node.nodeName === 'pict' || node.nodeName === 'w:drawing' || node.nodeName === 'drawing')) {
740
+ // Extract text boxes from legacy shapes or modern drawings
741
+ const textBoxes = (0, xmlUtils_js_1.getElementsByTagName)(node, "w:txbxContent");
742
+ for (const txbx of textBoxes) {
743
+ const txbxChildren = Array.from(txbx.childNodes);
744
+ for (const txbxChild of txbxChildren) {
745
+ if ((0, xmlUtils_js_1.isElement)(txbxChild) && txbxChild.nodeName === 'w:p') {
746
+ const nestedP = parseParagraph(txbxChild, documentContent);
747
+ children.push(...(nestedP.children || []));
748
+ text += nestedP.text;
749
+ }
750
+ }
751
+ }
752
+ }
753
+ else if ((0, xmlUtils_js_1.isElement)(node) && (node.nodeName === 'm:oMath' || node.nodeName === 'oMath'
754
+ || node.nodeName === 'm:oMathPara' || node.nodeName === 'oMathPara')) {
755
+ // Equations. Without this branch they reach the generic fallback below, which
756
+ // recurses into every child and concatenates the `m:t` runs with no separators -
757
+ // so `<m:num>1</m:num><m:den>2</m:den>` came out as "12". That is worse than
758
+ // dropping the formula: the result still reads as a number, so nothing downstream
759
+ // can tell it is wrong.
760
+ //
761
+ // `m:oMathPara` is a display equation on its own line; a bare `m:oMath` is inline.
762
+ const isBlock = node.nodeName === 'm:oMathPara' || node.nodeName === 'oMathPara';
763
+ const latex = (0, mathUtils_js_1.ommlToLatex)(node);
764
+ if (!(0, mathUtils_js_1.isEmptyMath)(latex)) {
765
+ text += latex;
766
+ children.push({
767
+ type: 'code',
768
+ text: latex,
769
+ metadata: { math: isBlock ? 'block' : 'inline' }
770
+ });
771
+ }
772
+ }
773
+ else if (node.childNodes.length > 0) {
774
+ // Generic fallback for unknown elements that might contain content
775
+ for (const child of Array.from(node.childNodes))
776
+ processChildNode(child);
777
+ }
778
+ };
779
+ const anchorIds = [...pendingAnchorIds];
780
+ const childNodes = Array.from(pNode.childNodes);
781
+ for (const child of childNodes) {
782
+ processChildNode(child);
783
+ }
784
+ const commonMetadata = anchorIds.length > 0 ? { anchorIds } : {};
785
+ if (isList) {
786
+ const numIdNode = (0, xmlUtils_js_1.getFirstElementByTagName)(numPr, "w:numId");
787
+ const ilvlNode = (0, xmlUtils_js_1.getFirstElementByTagName)(numPr, "w:ilvl");
788
+ const numId = numIdNode ? numIdNode.getAttribute("w:val") || '0' : '0';
789
+ const ilvl = ilvlNode ? parseInt(ilvlNode.getAttribute("w:val") || '0', 10) : 0;
790
+ let listType = 'ordered';
791
+ let itemIndex = 0;
792
+ if (numId && numberingMap[numId]) {
793
+ const ilvlStr = ilvl.toString();
794
+ if (!numberingState[numId])
795
+ numberingState[numId] = {};
796
+ if (!numberingState[numId][ilvlStr])
797
+ numberingState[numId][ilvlStr] = 0;
798
+ numberingState[numId][ilvlStr]++;
799
+ for (let k = ilvl + 1; k < 10; k++) {
800
+ if (numberingState[numId][k.toString()])
801
+ numberingState[numId][k.toString()] = 0;
802
+ }
803
+ const numFmt = numberingMap[numId][ilvlStr]?.numFmt || 'decimal';
804
+ listType = numFmt === 'bullet' ? 'unordered' : 'ordered';
805
+ // Track itemIndex (starts at override or default, continues across interruptions for same listId)
806
+ if (!listCounters[numId])
807
+ listCounters[numId] = {};
808
+ if (listCounters[numId][ilvlStr] === undefined) {
809
+ listCounters[numId][ilvlStr] = (numberingMap[numId][ilvlStr]?.start ?? 1) - 1;
810
+ }
811
+ else {
812
+ listCounters[numId][ilvlStr]++;
813
+ }
814
+ itemIndex = listCounters[numId][ilvlStr];
815
+ }
816
+ const listNode = {
817
+ type: 'list',
818
+ text: text,
819
+ children: children,
820
+ ...(notes.length > 0 ? { notes } : {}),
821
+ ...(comments.length > 0 ? { comments } : {}),
822
+ metadata: {
823
+ listType,
824
+ indentation: ilvl,
825
+ paragraphIndentation: paraIndentation,
826
+ alignment: (alignment || 'left'),
827
+ listId: numId,
828
+ itemIndex: itemIndex,
829
+ style: pStyleVal,
830
+ ...commonMetadata
831
+ }
832
+ };
833
+ if (config.includeRawContent)
834
+ listNode.rawContent = (0, xmlUtils_js_1.getRawContent)(pNode, documentContent, config);
835
+ return listNode;
836
+ }
837
+ else if (isHeading) {
838
+ const level = pStyleVal ? parseInt(pStyleVal.replace("Heading", ""), 10) || 1 : 1;
839
+ const headingNode = {
840
+ type: 'heading',
841
+ text: text,
842
+ children: children,
843
+ ...(notes.length > 0 ? { notes } : {}),
844
+ ...(comments.length > 0 ? { comments } : {}),
845
+ metadata: { level, alignment, paragraphIndentation: paraIndentation, style: pStyleVal ?? undefined, ...commonMetadata }
846
+ };
847
+ if (config.includeRawContent)
848
+ headingNode.rawContent = (0, xmlUtils_js_1.getRawContent)(pNode, documentContent, config);
849
+ return headingNode;
850
+ }
851
+ else {
852
+ const paraNode = {
853
+ type: 'paragraph',
854
+ text: text,
855
+ children: children,
856
+ ...(notes.length > 0 ? { notes } : {}),
857
+ ...(comments.length > 0 ? { comments } : {}),
858
+ metadata: { alignment, paragraphIndentation: paraIndentation, style: pStyleVal ?? undefined, ...commonMetadata }
859
+ };
860
+ if (config.includeRawContent)
861
+ paraNode.rawContent = (0, xmlUtils_js_1.getRawContent)(pNode, documentContent, config);
862
+ return paraNode;
863
+ }
864
+ };
865
+ // Helper to parse a table node
866
+ const parseTable = (tblNode, documentContent, pendingAnchorIds = []) => {
867
+ const rows = [];
868
+ const trNodes = (0, xmlUtils_js_1.getDirectChildren)(tblNode, "w:tr");
869
+ // Track vertical merges: colIndex -> { startCellNode, rowSpan }
870
+ const vMergeMap = new Map();
871
+ for (let rIndex = 0; rIndex < trNodes.length; rIndex++) {
872
+ const trNode = trNodes[rIndex];
873
+ const cells = [];
874
+ // Only get direct child cells, not nested table cells
875
+ const tcNodes = (0, xmlUtils_js_1.getDirectChildren)(trNode, "w:tc");
876
+ let visualCol = 0;
877
+ for (let tcIndex = 0; tcIndex < tcNodes.length; tcIndex++) {
878
+ const tcNode = tcNodes[tcIndex];
879
+ const tcPr = (0, xmlUtils_js_1.getFirstElementByTagName)(tcNode, "w:tcPr");
880
+ // Horizontal merge (colspan)
881
+ let colSpan = 1;
882
+ if (tcPr) {
883
+ const gridSpan = (0, xmlUtils_js_1.getFirstElementByTagName)(tcPr, "w:gridSpan");
884
+ if (gridSpan) {
885
+ colSpan = parseInt(gridSpan.getAttribute("w:val") || "1", 10);
886
+ }
887
+ }
888
+ let vMergeRestart = false;
889
+ let isVMerge = false;
890
+ if (tcPr) {
891
+ const vMerge = (0, xmlUtils_js_1.getFirstElementByTagName)(tcPr, "w:vMerge");
892
+ if (vMerge) {
893
+ isVMerge = true;
894
+ const val = vMerge.getAttribute("w:val");
895
+ // If it's explicit restart, or if we don't have an active merge for this column, treat as restart
896
+ if (val === "restart" || !vMergeMap.has(visualCol)) {
897
+ vMergeRestart = true;
898
+ }
899
+ }
900
+ }
901
+ const cellChildren = [];
902
+ let cellText = '';
903
+ // Cells contain paragraphs (and other block-level elements)
904
+ const cellContentNodes = Array.from(tcNode.childNodes);
905
+ for (const child of cellContentNodes) {
906
+ if ((0, xmlUtils_js_1.isElement)(child) && child.nodeName === 'w:p') {
907
+ const pNode = parseParagraph(child, documentContent);
908
+ cellChildren.push(pNode);
909
+ cellText += pNode.text;
910
+ }
911
+ else if ((0, xmlUtils_js_1.isElement)(child) && child.nodeName === 'w:tbl') {
912
+ const nestedTable = parseTable(child, documentContent);
913
+ cellChildren.push(nestedTable);
914
+ }
915
+ }
916
+ const cellNode = {
917
+ type: 'cell',
918
+ text: cellText,
919
+ children: cellChildren,
920
+ metadata: { row: rIndex, col: visualCol }
921
+ };
922
+ if (colSpan > 1)
923
+ cellNode.metadata.colSpan = colSpan;
924
+ if (tcPr) {
925
+ const shd = (0, xmlUtils_js_1.getFirstElementByTagName)(tcPr, "w:shd");
926
+ if (shd) {
927
+ const fill = shd.getAttribute("w:fill");
928
+ if (fill && fill !== "auto") {
929
+ cellNode.metadata.backgroundColor = "#" + fill;
930
+ }
931
+ }
932
+ }
933
+ if (isVMerge) {
934
+ if (vMergeRestart) {
935
+ vMergeMap.set(visualCol, { node: cellNode, span: 1 });
936
+ cells.push(cellNode);
937
+ }
938
+ else {
939
+ const mergeInfo = vMergeMap.get(visualCol);
940
+ if (mergeInfo) {
941
+ mergeInfo.span++;
942
+ mergeInfo.node.metadata.rowSpan = mergeInfo.span;
943
+ if (cellChildren.length > 0) {
944
+ if (!mergeInfo.node.children)
945
+ mergeInfo.node.children = [];
946
+ mergeInfo.node.children.push(...cellChildren);
947
+ mergeInfo.node.text += " " + cellText;
948
+ }
949
+ }
950
+ else {
951
+ // Fallback: if we found a continue but no restart, treat as normal cell
952
+ cells.push(cellNode);
953
+ }
954
+ }
955
+ }
956
+ else {
957
+ vMergeMap.delete(visualCol);
958
+ cells.push(cellNode);
959
+ }
960
+ visualCol += colSpan;
961
+ }
962
+ const rowNode = {
963
+ type: 'row',
964
+ children: cells,
965
+ };
966
+ rows.push(rowNode);
967
+ }
968
+ return {
969
+ type: 'table',
970
+ children: rows
971
+ };
972
+ };
973
+ // Pre-process footnotes and endnotes to be inserted inline later
974
+ if (!config.ignoreNotes) {
975
+ const footnotesFile = files.find(f => f.path.match(footnotesFileRegex));
976
+ if (footnotesFile) {
977
+ const footnotesDoc = (0, xmlUtils_js_1.parseXmlString)(footnotesFile.content.toString());
978
+ const footnoteXml = footnotesFile.content.toString();
979
+ const footnoteNodes = (0, xmlUtils_js_1.getElementsByTagName)(footnotesDoc, "w:footnote");
980
+ for (const node of footnoteNodes) {
981
+ const id = node.getAttribute("w:id");
982
+ if (!id || id === "-1" || id === "0")
983
+ continue;
984
+ const pNodes = (0, xmlUtils_js_1.getElementsByTagName)(node, "w:p");
985
+ footnoteMap.set(id, pNodes.map(p => parseParagraph(p, footnoteXml)));
986
+ }
987
+ }
988
+ const endnotesFile = files.find(f => f.path.match(endnotesFileRegex));
989
+ if (endnotesFile) {
990
+ const endnotesDoc = (0, xmlUtils_js_1.parseXmlString)(endnotesFile.content.toString());
991
+ const endnoteXml = endnotesFile.content.toString();
992
+ const endnoteNodes = (0, xmlUtils_js_1.getElementsByTagName)(endnotesDoc, "w:endnote");
993
+ for (const node of endnoteNodes) {
994
+ const id = node.getAttribute("w:id");
995
+ if (!id || id === "-1" || id === "0")
996
+ continue;
997
+ const pNodes = (0, xmlUtils_js_1.getElementsByTagName)(node, "w:p");
998
+ endnoteMap.set(id, pNodes.map(p => parseParagraph(p, endnoteXml)));
999
+ }
1000
+ }
1001
+ }
1002
+ // Pre-process comments
1003
+ if (!config.ignoreComments) {
1004
+ const commentsFile = files.find(f => f.path.match(commentsFileRegex));
1005
+ if (commentsFile) {
1006
+ const commentsDoc = (0, xmlUtils_js_1.parseXmlString)(commentsFile.content.toString());
1007
+ const commentsXml = commentsFile.content.toString();
1008
+ const commentNodes = (0, xmlUtils_js_1.getElementsByTagName)(commentsDoc, "w:comment");
1009
+ for (const node of commentNodes) {
1010
+ const id = node.getAttribute("w:id");
1011
+ if (!id)
1012
+ continue;
1013
+ const author = node.getAttribute("w:author") || undefined;
1014
+ const date = node.getAttribute("w:date") || undefined;
1015
+ const initials = node.getAttribute("w:initials") || undefined;
1016
+ commentMetadataMap.set(id, { commentId: id, author, date, initials });
1017
+ const pNodes = (0, xmlUtils_js_1.getElementsByTagName)(node, "w:p");
1018
+ commentMap.set(id, pNodes.map(p => parseParagraph(p, commentsXml)));
1019
+ }
1020
+ }
1021
+ }
1022
+ // Pre-process headers and footers
1023
+ const headers = [];
1024
+ const footers = [];
1025
+ if (!config.ignoreHeadersAndFooters) {
1026
+ const headerFiles = files.filter(f => f.path.match(headerFileRegex));
1027
+ for (const hFile of headerFiles) {
1028
+ const hDoc = (0, xmlUtils_js_1.parseXmlString)(hFile.content.toString());
1029
+ const hXml = hFile.content.toString();
1030
+ const hNodes = Array.from(hDoc.documentElement.childNodes).filter(xmlUtils_js_1.isElement);
1031
+ for (const child of hNodes) {
1032
+ if (child.nodeName === 'w:p')
1033
+ headers.push(parseParagraph(child, hXml));
1034
+ else if (child.nodeName === 'w:tbl')
1035
+ headers.push(parseTable(child, hXml));
1036
+ else if (child.nodeName === 'w:sdt') {
1037
+ const contentNode = (0, xmlUtils_js_1.getFirstElementByTagName)(child, "w:sdtContent");
1038
+ if (contentNode) {
1039
+ for (const sdtChild of Array.from(contentNode.childNodes).filter(xmlUtils_js_1.isElement)) {
1040
+ if (sdtChild.nodeName === 'w:p')
1041
+ headers.push(parseParagraph(sdtChild, hXml));
1042
+ else if (sdtChild.nodeName === 'w:tbl')
1043
+ headers.push(parseTable(sdtChild, hXml));
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ }
1049
+ const footerFiles = files.filter(f => f.path.match(footerFileRegex));
1050
+ for (const fFile of footerFiles) {
1051
+ const fDoc = (0, xmlUtils_js_1.parseXmlString)(fFile.content.toString());
1052
+ const fXml = fFile.content.toString();
1053
+ const fNodes = Array.from(fDoc.documentElement.childNodes).filter(xmlUtils_js_1.isElement);
1054
+ for (const child of fNodes) {
1055
+ if (child.nodeName === 'w:p')
1056
+ footers.push(parseParagraph(child, fXml));
1057
+ else if (child.nodeName === 'w:tbl')
1058
+ footers.push(parseTable(child, fXml));
1059
+ else if (child.nodeName === 'w:sdt') {
1060
+ const contentNode = (0, xmlUtils_js_1.getFirstElementByTagName)(child, "w:sdtContent");
1061
+ if (contentNode) {
1062
+ for (const sdtChild of Array.from(contentNode.childNodes).filter(xmlUtils_js_1.isElement)) {
1063
+ if (sdtChild.nodeName === 'w:p')
1064
+ footers.push(parseParagraph(sdtChild, fXml));
1065
+ else if (sdtChild.nodeName === 'w:tbl')
1066
+ footers.push(parseTable(sdtChild, fXml));
1067
+ }
1068
+ }
1069
+ }
1070
+ }
1071
+ }
1072
+ }
1073
+ for (const file of files) {
1074
+ if (file.path.match(mediaFileRegex))
1075
+ continue;
1076
+ if (file.path.match(numberingFileRegex))
1077
+ continue;
1078
+ if (file.path.match(relsFileRegex))
1079
+ continue;
1080
+ if (file.path.match(stylesFileRegex))
1081
+ continue;
1082
+ if (file.path.match(footnotesFileRegex))
1083
+ continue;
1084
+ if (file.path.match(endnotesFileRegex))
1085
+ continue;
1086
+ if (file.path.match(commentsFileRegex))
1087
+ continue;
1088
+ if (file.path.match(headerFileRegex))
1089
+ continue;
1090
+ if (file.path.match(footerFileRegex))
1091
+ continue;
1092
+ const documentContent = file.content.toString();
1093
+ const doc = (0, xmlUtils_js_1.parseXmlString)(documentContent, { locator: config.includeRawContent });
1094
+ const body = (0, xmlUtils_js_1.getFirstElementByTagName)(doc, "w:body");
1095
+ if (body) {
1096
+ const bodyChildren = Array.from(body.childNodes);
1097
+ let pendingAnchorIds = [];
1098
+ for (const child of bodyChildren) {
1099
+ (0, errorUtils_js_1.checkAbortSignal)(config.abortSignal);
1100
+ if ((0, xmlUtils_js_1.isElement)(child)) {
1101
+ if (child.nodeName === 'w:p') {
1102
+ content.push(parseParagraph(child, documentContent, pendingAnchorIds));
1103
+ pendingAnchorIds = [];
1104
+ }
1105
+ else if (child.nodeName === 'w:tbl') {
1106
+ content.push(parseTable(child, documentContent, pendingAnchorIds));
1107
+ pendingAnchorIds = [];
1108
+ }
1109
+ else if (child.nodeName === 'w:bookmarkStart') {
1110
+ const bookmarkName = child.getAttribute("w:name");
1111
+ if (bookmarkName && !bookmarkName.startsWith('_GoBack') && !config.ignoreInternalLinks) {
1112
+ pendingAnchorIds.push(bookmarkName);
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+ }
1119
+ // Extract attachments
1120
+ if (config.extractAttachments) {
1121
+ for (const media of mediaFiles) {
1122
+ const attachment = (0, imageUtils_js_1.createAttachment)(media.path.split('/').pop() || 'image', media.content);
1123
+ attachments.push(attachment);
1124
+ if (config.ocr) {
1125
+ if (attachment.mimeType.startsWith('image/')) {
1126
+ try {
1127
+ attachment.ocrText = (await (0, ocrUtils_js_1.performOcr)(media.content, { ...config.ocrConfig })).trim();
1128
+ }
1129
+ catch (e) {
1130
+ (0, errorUtils_js_1.logWarning)(types_js_1.OfficeWarningType.OCR_FAILED, config, attachment.name, e);
1131
+ }
1132
+ }
1133
+ }
1134
+ }
1135
+ // Assign OCR text to image nodes
1136
+ if (config.ocr) {
1137
+ const assignOcr = (nodes) => {
1138
+ for (const node of nodes) {
1139
+ if (node.type === 'image' && 'attachmentName' in (node.metadata || {})) {
1140
+ const meta = node.metadata;
1141
+ const attachment = attachments.find(a => a.name === meta.attachmentName);
1142
+ if (attachment && attachment.ocrText) {
1143
+ node.text = attachment.ocrText;
1144
+ attachment.altText = meta.altText;
1145
+ }
1146
+ }
1147
+ if (node.children) {
1148
+ assignOcr(node.children);
1149
+ }
1150
+ }
1151
+ };
1152
+ assignOcr(content);
1153
+ }
1154
+ }
1155
+ const toTextSync = () => content.map(c => {
1156
+ // Recursive text extraction
1157
+ const getText = (node) => {
1158
+ let t = '';
1159
+ if (node.children) {
1160
+ t += node.children.map(getText).filter(t => t != '').join(!node.children[0]?.children ? '' : config.newlineDelimiter);
1161
+ }
1162
+ else if (node.type === 'break') {
1163
+ t += config.newlineDelimiter;
1164
+ }
1165
+ else
1166
+ t += node.text || '';
1167
+ return t;
1168
+ };
1169
+ return getText(c);
1170
+ }).filter(t => t != '').join(config.newlineDelimiter);
1171
+ const auxiliaryContent = (headers.length > 0 || footers.length > 0) ? {
1172
+ ...(headers.length > 0 ? { headers } : {}),
1173
+ ...(footers.length > 0 ? { footers } : {})
1174
+ } : undefined;
1175
+ return (0, astUtils_js_1.createAST)('docx', { ...metadata, formatting: docDefaults, styleMap: styleMap }, content, attachments, config, auxiliaryContent, toTextSync);
1176
+ };
1177
+ exports.parseWord = parseWord;