@stll/folio-core 0.54.2 → 0.55.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 (199) hide show
  1. package/dist/ai-edits/apply.js +22 -1
  2. package/dist/ai-edits/headless.js +1 -0
  3. package/dist/compare/content-types.d.ts +3 -15
  4. package/dist/controller/canonicalComposition.d.ts +36 -0
  5. package/dist/controller/canonicalComposition.js +168 -0
  6. package/dist/controller/canonicalInput.d.ts +45 -0
  7. package/dist/controller/canonicalInput.js +338 -0
  8. package/dist/controller/canonicalOperations.d.ts +22 -0
  9. package/dist/controller/canonicalOperations.js +85 -0
  10. package/dist/controller/canonicalReview.d.ts +12 -0
  11. package/dist/controller/canonicalReview.js +19 -0
  12. package/dist/controller/canonicalSession.d.ts +149 -0
  13. package/dist/controller/canonicalSession.js +1038 -0
  14. package/dist/controller/canonicalStoryEditor.d.ts +34 -0
  15. package/dist/controller/canonicalStoryEditor.js +82 -0
  16. package/dist/controller/canonicalStructure.d.ts +16 -0
  17. package/dist/controller/canonicalStructure.js +269 -0
  18. package/dist/controller/folioEditor.d.ts +1 -1
  19. package/dist/controller/folioEditor.js +9 -0
  20. package/dist/controller/headerFooterEditorManager.d.ts +4 -0
  21. package/dist/controller/headerFooterEditorManager.js +71 -3
  22. package/dist/controller/hiddenEditorApi.d.ts +35 -0
  23. package/dist/controller/hiddenEditorApi.js +25 -8
  24. package/dist/controller/hiddenEditorManager.d.ts +11 -2
  25. package/dist/controller/hiddenEditorManager.js +456 -30
  26. package/dist/controller/layoutPipeline.d.ts +1 -1
  27. package/dist/controller/layoutPipeline.js +2 -0
  28. package/dist/controller/noteEditorManager.d.ts +4 -0
  29. package/dist/controller/noteEditorManager.js +113 -15
  30. package/dist/docx/attributeRemainder.js +1 -0
  31. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  32. package/dist/docx/canonicalSessionInput.js +27 -0
  33. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  34. package/dist/docx/canonicalStoryRepack.js +41 -0
  35. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  36. package/dist/docx/contentTypeRegistry.js +64 -0
  37. package/dist/docx/ensureParaIds.d.ts +7 -1
  38. package/dist/docx/ensureParaIds.js +163 -211
  39. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  40. package/dist/docx/foldedListNumberFields.js +267 -0
  41. package/dist/docx/headerFooterVerbatim.d.ts +14 -1
  42. package/dist/docx/headerFooterVerbatim.js +95 -2
  43. package/dist/docx/noteIds.d.ts +5 -0
  44. package/dist/docx/noteIds.js +6 -2
  45. package/dist/docx/noteReferenceMark.d.ts +3 -1
  46. package/dist/docx/noteReferenceMark.js +15 -14
  47. package/dist/docx/numberingParser.d.ts +3 -1
  48. package/dist/docx/numberingParser.js +17 -3
  49. package/dist/docx/packageParts.d.ts +3 -1
  50. package/dist/docx/packageParts.js +65 -16
  51. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  52. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  53. package/dist/docx/paragraphParser.js +101 -53
  54. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  55. package/dist/docx/paragraphPropertySource.js +76 -22
  56. package/dist/docx/parser.js +2 -1
  57. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  58. package/dist/docx/reviewHistoryNamespace.js +4 -0
  59. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  60. package/dist/docx/reviewResolutionProvenance.js +153 -0
  61. package/dist/docx/rezip.d.ts +7 -3
  62. package/dist/docx/rezip.js +107 -45
  63. package/dist/docx/runConsolidator.d.ts +2 -58
  64. package/dist/docx/runConsolidator.js +3 -175
  65. package/dist/docx/runParser.d.ts +2 -1
  66. package/dist/docx/runParser.js +156 -116
  67. package/dist/docx/saveDiagnostics.d.ts +11 -0
  68. package/dist/docx/saveDiagnostics.js +0 -0
  69. package/dist/docx/selectiveSave.d.ts +2 -1
  70. package/dist/docx/selectiveSave.js +4 -1
  71. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  72. package/dist/docx/selectiveXmlPatch.js +74 -2
  73. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  74. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  75. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  76. package/dist/docx/serializer/noteSerializer.js +24 -8
  77. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  78. package/dist/docx/serializer/partNamespaces.js +2 -1
  79. package/dist/docx/serializer/runSerializer.js +3 -1
  80. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  81. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  82. package/dist/docx/settingsParser.d.ts +1 -2
  83. package/dist/docx/settingsParser.js +5 -6
  84. package/dist/docx/storyBlockReplay.d.ts +11 -0
  85. package/dist/docx/storyBlockReplay.js +367 -0
  86. package/dist/docx/streamingXmlParser.d.ts +22 -1
  87. package/dist/docx/streamingXmlParser.js +49 -3
  88. package/dist/docx/structuralXmlPatch.js +8 -1
  89. package/dist/docx/verbatimCapture.d.ts +3 -1
  90. package/dist/docx/verbatimCapture.js +49 -6
  91. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  92. package/dist/i18n/messages/ar.gen.js +656 -0
  93. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  94. package/dist/i18n/messages/cs.gen.js +656 -0
  95. package/dist/i18n/messages/de.gen.d.ts +5 -0
  96. package/dist/i18n/messages/de.gen.js +656 -0
  97. package/dist/i18n/messages/en.gen.d.ts +5 -0
  98. package/dist/i18n/messages/en.gen.js +656 -0
  99. package/dist/i18n/messages/es.gen.d.ts +5 -0
  100. package/dist/i18n/messages/es.gen.js +656 -0
  101. package/dist/i18n/messages/et.gen.d.ts +5 -0
  102. package/dist/i18n/messages/et.gen.js +656 -0
  103. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  104. package/dist/i18n/messages/fr.gen.js +656 -0
  105. package/dist/i18n/messages/he.gen.d.ts +5 -0
  106. package/dist/i18n/messages/he.gen.js +656 -0
  107. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  108. package/dist/i18n/messages/hi.gen.js +656 -0
  109. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  110. package/dist/i18n/messages/hu.gen.js +656 -0
  111. package/dist/i18n/messages/locales.d.ts +5 -0
  112. package/dist/i18n/messages/locales.js +22 -0
  113. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  114. package/dist/i18n/messages/lt.gen.js +656 -0
  115. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  116. package/dist/i18n/messages/lv.gen.js +656 -0
  117. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  118. package/dist/i18n/messages/pl.gen.js +656 -0
  119. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  120. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  121. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  122. package/dist/i18n/messages/sk.gen.js +656 -0
  123. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  124. package/dist/i18n/messages/tr.gen.js +656 -0
  125. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  126. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  127. package/dist/i18n/messages.d.ts +2 -3
  128. package/dist/i18n/messages.js +1 -19
  129. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  130. package/dist/internal/noteMarkerAttrs.js +16 -0
  131. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  132. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  133. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  134. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  135. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  136. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  137. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  138. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  139. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  140. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  141. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  142. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  143. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  144. package/dist/managers/DocumentLoaderManager.js +8 -1
  145. package/dist/markdown/renderBlock.js +15 -1
  146. package/dist/markdown/renderParagraph.js +9 -13
  147. package/dist/markdown/renderRuns.js +3 -2
  148. package/dist/markdown/renderTable.js +2 -0
  149. package/dist/markdown/types.d.ts +3 -9
  150. package/dist/prosemirror/attrs/index.d.ts +2 -12
  151. package/dist/prosemirror/attrs/index.js +22 -48
  152. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  153. package/dist/prosemirror/canonicalCommands.d.ts +97 -0
  154. package/dist/prosemirror/canonicalCommands.js +28 -0
  155. package/dist/prosemirror/commands/pageBreak.js +15 -1
  156. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  157. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  158. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  159. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  160. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
  161. package/dist/prosemirror/conversion/toProseDoc.js +51 -11
  162. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  163. package/dist/prosemirror/executeEditorCommand.js +26 -0
  164. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  165. package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
  166. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  167. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  168. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  169. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  170. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  171. package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
  172. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  173. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  174. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  175. package/dist/prosemirror/listMarker.d.ts +1 -0
  176. package/dist/prosemirror/listMarker.js +16 -7
  177. package/dist/prosemirror/listNumbering.js +26 -5
  178. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  179. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  180. package/dist/prosemirror/markupViewNotes.js +3 -2
  181. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  182. package/dist/prosemirror/noteReferenceReview.js +5 -3
  183. package/dist/prosemirror/plugins/suggestionMode.js +50 -2
  184. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  185. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  186. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  187. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  188. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  189. package/dist/prosemirror/schema/marks.d.ts +2 -0
  190. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  192. package/dist/prosemirror/textInput.js +5 -1
  193. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  194. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  195. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  196. package/dist/prosemirror/validation.js +4 -0
  197. package/dist/types/content.d.ts +2 -2
  198. package/dist/utils/createDocument.js +2 -2
  199. package/package.json +14 -2
@@ -1,8 +1,10 @@
1
1
  import { deterministicHexId } from "../utils/hexId.js";
2
- import { isXmlNameBoundary, spliceXml } from "./selectiveXmlPatch.js";
2
+ import { resolvePackageRelationshipTarget } from "./packageParts.js";
3
+ import { spliceXml } from "./selectiveXmlPatch.js";
3
4
  import { loadDocxArchive } from "./server/boundedArchive.js";
4
- import { matchCloseTag, matchOpenTag, resolveWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
5
- import { WORDPROCESSINGML_NAMESPACE_URIS, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
5
+ import { scanStreamingXmlElements } from "./streamingXmlParser.js";
6
+ import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
7
+ import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, getAttribute, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument, resolveAttributeNamespaceUri } from "./xmlParser.js";
6
8
  import { TaggedError } from "better-result";
7
9
  import JSZip from "jszip";
8
10
  //#region src/docx/ensureParaIds.ts
@@ -27,7 +29,7 @@ import JSZip from "jszip";
27
29
  * so documents carrying features folio's parser does not model come back
28
30
  * with those features byte-identical. Contract:
29
31
  *
30
- * - Parts covered: `word/document.xml`, `word/header*.xml`, `word/footer*.xml`,
32
+ * - Parts covered: the officeDocument relationship target, `word/header*.xml`, `word/footer*.xml`,
31
33
  * `word/footnotes.xml`, `word/endnotes.xml`. Paragraphs nested in table
32
34
  * cells and in `mc:Choice` text boxes are plain `<w:p>` elements inside
33
35
  * those parts and are covered by the same scan. The comments part mints its
@@ -50,12 +52,10 @@ import JSZip from "jszip";
50
52
  * declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
51
53
  * producers declare neither, and absent `mc:Ignorable` handling is what
52
54
  * makes pre-2010 consumers choke on the new attributes.
53
- * - Prefixes are aliases. Each part is scanned under the prefixes its root
54
- * binds to WordprocessingML (a default namespace included), `w14` and `mc`
55
- * (see `resolveWordprocessingPrefixes`), and a minted id is written under
56
- * the part's own `w14` prefix, which is the one `mc:Ignorable` lists. A
57
- * part whose bindings a string scan cannot follow (a nested element that
58
- * rebinds one of them) is refused with {@link EnsureParaIdsError}.
55
+ * - Prefixes are aliases: elements and attributes resolve against their
56
+ * in-scope namespace URI, including Strict and Transitional profiles and
57
+ * nested rebinding. Minted attributes use a root prefix with no conflicting
58
+ * nested binding; a fresh prefix is declared when necessary.
59
59
  * - Idempotent: a document that already has full coverage is returned as the
60
60
  * original bytes, untouched (`alreadyComplete: true`). A body with
61
61
  * paragraphs the scan did not see is refused, never reported complete.
@@ -63,6 +63,11 @@ import JSZip from "jszip";
63
63
  * When normalization would rewrite the package, it fails unless the caller
64
64
  * explicitly allows signature invalidation after warning the user.
65
65
  */
66
+ const ENSURE_PARA_IDS_REASONS = {
67
+ NORMALIZATION_FAILED: "normalization-failed",
68
+ NAMESPACE_INVALID: "namespace-invalid",
69
+ SIGNED_PACKAGE: "signed-package"
70
+ };
66
71
  /** A malformed or unsupported package prevented paragraph-ID normalization. */
67
72
  var EnsureParaIdsError = class extends TaggedError("EnsureParaIdsError") {};
68
73
  const W14_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2010/wordml";
@@ -71,53 +76,15 @@ const MC_IGNORABLE_W14 = "w14";
71
76
  const DIGITAL_SIGNATURE_PART_PREFIX = "_xmlsignatures/";
72
77
  /** Word reads an all-zero `w14:paraId` as "no id assigned". */
73
78
  const RESERVED_ZERO_ID_PATTERN = /^0*$/u;
74
- const DOCUMENT_PART = "word/document.xml";
79
+ const PACKAGE_RELATIONSHIP_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
75
80
  const TARGET_PART_PATTERNS = [
76
- /^word\/document\.xml$/u,
77
81
  /^word\/header\d*\.xml$/u,
78
82
  /^word\/footer\d*\.xml$/u,
79
83
  /^word\/footnotes\.xml$/u,
80
84
  /^word\/endnotes\.xml$/u
81
85
  ];
82
- const OPAQUE_XML_REGIONS = [
83
- {
84
- open: "<!--",
85
- close: "-->"
86
- },
87
- {
88
- open: "<![CDATA[",
89
- close: "]]>"
90
- },
91
- {
92
- open: "<?",
93
- close: "?>"
94
- }
95
- ];
96
- /**
97
- * Every prefixed `paraId` counts toward uniqueness: the parser accepts the
98
- * WordprocessingML fallback, comment parts link replies via `w15:paraId`, and
99
- * a producer may spell any of them under a prefix of its own. Over-collecting
100
- * only steers a minted id away from a value; it never keeps one.
101
- */
102
- const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
103
- const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
104
- /** An attribute `localName` under any of `prefixes`, with `quote` and `value` groups. */
105
- const prefixedAttributePattern = (prefixes, localName) => {
106
- const names = prefixes.filter((prefix) => prefix !== "").map(escapeRegExp);
107
- return names.length === 0 ? /(?!)/u : new RegExp(`\\s(?:${names.join("|")}):${localName}=(?<quote>["'])(?<value>[\\s\\S]*?)\\k<quote>`, "u");
108
- };
109
- const partSpelling = (xml, partPath) => {
110
- const resolution = resolveWordprocessingPrefixes(xml);
111
- if (resolution.type === "unsupported") throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: ${resolution.reason}`);
112
- const { prefixes } = resolution;
113
- const attributePrefixes = [...prefixes.w14, ...prefixes.main];
114
- return {
115
- prefixes,
116
- paraId: prefixedAttributePattern(attributePrefixes, "paraId"),
117
- textId: prefixedAttributePattern(attributePrefixes, "textId"),
118
- w14Prefix: (prefixes.w14Declared ? prefixes.w14[0] : void 0) ?? MC_IGNORABLE_W14
119
- };
120
- };
86
+ /** Reserve package ids even in parts whose content the pass leaves untouched. */
87
+ const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId\s*=\s*(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
121
88
  /**
122
89
  * Stamp the edits into the part through the splice owner, which refuses a
123
90
  * result that would leave a comment range with only one half. These edits
@@ -135,84 +102,98 @@ const applySplices = (xml, edits, partPath) => {
135
102
  return patched;
136
103
  };
137
104
  const createEnsureParaIdsError = (message, cause) => new EnsureParaIdsError({
105
+ reason: ENSURE_PARA_IDS_REASONS.NORMALIZATION_FAILED,
138
106
  message,
139
107
  ...cause === void 0 ? {} : { cause }
140
108
  });
141
- const skipOpaqueXmlRegion = (xml, start, partPath) => {
142
- for (const { open, close } of OPAQUE_XML_REGIONS) {
143
- if (!xml.startsWith(open, start)) continue;
144
- const closeStart = xml.indexOf(close, start + open.length);
145
- if (closeStart === -1) throw createEnsureParaIdsError(`Unterminated ${open} region in ${partPath}`);
146
- return closeStart + close.length;
147
- }
148
- return null;
149
- };
150
- const attributeValueRange = (match, absoluteOffset, group) => {
151
- const quote = match.groups["quote"];
152
- const value = match.groups[group];
153
- const start = absoluteOffset + match.index + match[0].indexOf(quote) + 1;
154
- return {
155
- start,
156
- end: start + value.length
157
- };
158
- };
159
109
  const collectExistingParaIds = (xml, into) => {
160
110
  for (const match of xml.matchAll(ANY_PARA_ID_PATTERN)) {
161
111
  const id = match.groups["id"];
162
112
  if (id.length > 0) into.add(id.toUpperCase());
163
113
  }
164
114
  };
165
- /**
166
- * Every paragraph open tag of a part in document order, under whichever
167
- * WordprocessingML prefix the part binds, with whether it sits in
168
- * `mc:Fallback` (under the part's own `mc` prefix).
169
- */
170
- function* paragraphOpenTags(xml, partPath, { prefixes }) {
171
- let fallbackDepth = 0;
172
- let pos = 0;
173
- while (pos < xml.length) {
174
- const tagStart = xml.indexOf("<", pos);
175
- if (tagStart === -1) return;
176
- const opaqueEnd = skipOpaqueXmlRegion(xml, tagStart, partPath);
177
- if (opaqueEnd !== null) {
178
- pos = opaqueEnd;
179
- continue;
115
+ /** Resolve every element and attribute in its own scope; retain source offsets for splices. */
116
+ const partSpelling = (xml, partPath) => {
117
+ let root;
118
+ let rootNameEnd = 0;
119
+ let ignorable;
120
+ const paragraphs = [];
121
+ const fallbacks = /* @__PURE__ */ new WeakSet();
122
+ const bindings = /* @__PURE__ */ new Map();
123
+ if (scanStreamingXmlElements(xml, ({ element, attributeValueSpans: spans, nameEnd, parent }) => {
124
+ const unboundElement = (element.name ?? "").includes(":") && getNamespaceUri(element) === void 0;
125
+ const unboundAttribute = Object.keys(element.attributes ?? {}).some((attribute) => attribute.includes(":") && !attribute.startsWith("xmlns:") && resolveAttributeNamespaceUri(element, attribute) === void 0);
126
+ if (unboundElement || unboundAttribute) throw new EnsureParaIdsError({
127
+ reason: ENSURE_PARA_IDS_REASONS.NAMESPACE_INVALID,
128
+ message: `Undeclared namespace prefix in ${partPath}`
129
+ });
130
+ if (parent === void 0) {
131
+ if (root !== void 0) throw createEnsureParaIdsError(`Multiple roots in ${partPath}`);
132
+ root = element;
133
+ rootNameEnd = nameEnd;
180
134
  }
181
- if (matchOpenTag(xml, tagStart, prefixes.mc, "Fallback") !== -1) {
182
- const tagEnd = xml.indexOf(">", tagStart);
183
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated mc:Fallback tag in ${partPath}`);
184
- if (xml[tagEnd - 1] !== "/") fallbackDepth += 1;
185
- pos = tagEnd + 1;
186
- continue;
135
+ for (const [name, value] of Object.entries(element.attributes ?? {})) {
136
+ if (name !== "xmlns" && !name.startsWith("xmlns:")) continue;
137
+ const prefix = name === "xmlns" ? "" : name.slice(6);
138
+ let uris = bindings.get(prefix);
139
+ if (uris === void 0) {
140
+ uris = /* @__PURE__ */ new Set();
141
+ bindings.set(prefix, uris);
142
+ }
143
+ uris.add(String(value));
187
144
  }
188
- const fallbackClose = matchCloseTag(xml, tagStart, prefixes.mc, "Fallback");
189
- if (fallbackClose !== -1) {
190
- fallbackDepth = Math.max(0, fallbackDepth - 1);
191
- pos = tagStart + fallbackClose;
192
- continue;
145
+ const inFallback = parent !== void 0 && fallbacks.has(parent) || getLocalName(element.name ?? "") === "Fallback" && getNamespaceUri(element) === MC_NAMESPACE_URI;
146
+ if (inFallback) fallbacks.add(element);
147
+ let paraId;
148
+ let textId;
149
+ for (const [name, span] of spans) {
150
+ const namespace = resolveAttributeNamespaceUri(element, name);
151
+ const localName = getLocalName(name);
152
+ const value = String(element.attributes?.[name] ?? "");
153
+ if (element === root && namespace === MC_NAMESPACE_URI && localName === "Ignorable") ignorable = {
154
+ ...span,
155
+ value
156
+ };
157
+ if (namespace !== W14_NAMESPACE_URI && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace ?? "")) continue;
158
+ if (localName === "paraId") {
159
+ if (paraId === void 0 || namespace === W14_NAMESPACE_URI) paraId = {
160
+ ...span,
161
+ value
162
+ };
163
+ }
164
+ if (localName === "textId") textId = {
165
+ ...span,
166
+ value
167
+ };
193
168
  }
194
- const nameLength = matchOpenTag(xml, tagStart, prefixes.main, "p");
195
- if (nameLength === -1) {
196
- pos = tagStart + 1;
197
- continue;
169
+ if (getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) paragraphs.push({
170
+ nameEnd,
171
+ inFallback,
172
+ paraId,
173
+ textId
174
+ });
175
+ }).status === "unsupported" || root === void 0) throw createEnsureParaIdsError(`Malformed XML in ${partPath}`);
176
+ const rootElement = root;
177
+ const prefixFor = (uri, preferred) => {
178
+ for (const [prefix, boundUri] of rootElement.namespaceScope?.bindings ?? []) {
179
+ if (prefix === "" || boundUri !== uri) continue;
180
+ if (uri !== W14_NAMESPACE_URI || bindings.get(prefix)?.size === 1) return prefix;
198
181
  }
199
- const tagEnd = xml.indexOf(">", tagStart);
200
- if (tagEnd === -1) throw createEnsureParaIdsError(`Unterminated paragraph tag in ${partPath}`);
201
- pos = tagEnd + 1;
202
- yield {
203
- tagStart,
204
- tagEnd,
205
- nameEnd: tagStart + nameLength,
206
- inFallback: fallbackDepth > 0
207
- };
208
- }
209
- }
210
- const collectFallbackParaIds = (xml, partPath, spelling, into) => {
211
- for (const { tagStart, tagEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
212
- if (!inFallback) continue;
213
- const id = spelling.paraId.exec(xml.slice(tagStart, tagEnd + 1))?.groups?.["value"];
214
- if (id) into.add(id.toUpperCase());
215
- }
182
+ let candidate = preferred;
183
+ for (let suffix = 1; bindings.has(candidate); suffix += 1) candidate = `${preferred}_${suffix}`;
184
+ return candidate;
185
+ };
186
+ return {
187
+ root,
188
+ rootNameEnd,
189
+ ignorable,
190
+ paragraphs,
191
+ w14Prefix: prefixFor(W14_NAMESPACE_URI, MC_IGNORABLE_W14),
192
+ mcPrefix: prefixFor(MC_NAMESPACE_URI, "mc")
193
+ };
194
+ };
195
+ const collectFallbackParaIds = (spelling, into) => {
196
+ for (const { inFallback, paraId } of spelling.paragraphs) if (inFallback && paraId?.value) into.add(paraId.value.toUpperCase());
216
197
  };
217
198
  /**
218
199
  * Deterministic fresh id for the paragraph at `ordinal` in `partPath`,
@@ -225,35 +206,27 @@ const mintParaId = (context, partPath, ordinal) => {
225
206
  context.taken.add(id);
226
207
  return id;
227
208
  };
228
- /**
229
- * One forward scan over a part: every paragraph open tag outside
230
- * `mc:Fallback` either keeps its paraId (valid, first occurrence) or gets a
231
- * splice edit minting / replacing one. `seen` spans parts so the
232
- * first-occurrence rule is document-wide across the scan order.
233
- */
234
- const scanPart = (xml, partPath, spelling, context, seen) => {
209
+ const scanPart = ({ partPath, spelling, context, seen }) => {
235
210
  const edits = [];
236
211
  const minted = [];
237
212
  const w14 = spelling.w14Prefix;
238
213
  let assigned = 0;
239
214
  let deduplicated = 0;
240
215
  let ordinal = 0;
241
- for (const { tagStart, tagEnd, nameEnd, inFallback } of paragraphOpenTags(xml, partPath, spelling)) {
216
+ for (const { nameEnd, inFallback, paraId, textId } of spelling.paragraphs) {
242
217
  if (inFallback) continue;
243
218
  ordinal += 1;
244
- const openTag = xml.slice(tagStart, tagEnd + 1);
245
- const paraIdMatch = spelling.paraId.exec(openTag);
246
- if (!paraIdMatch) {
219
+ if (paraId === void 0) {
247
220
  const id = mintParaId(context, partPath, ordinal);
248
- const textIdMatch = spelling.textId.exec(openTag);
249
- if (textIdMatch) {
221
+ if (textId !== void 0) {
250
222
  edits.push({
251
223
  start: nameEnd,
252
224
  end: nameEnd,
253
225
  text: ` ${w14}:paraId="${id}"`
254
226
  });
255
227
  edits.push({
256
- ...attributeValueRange(textIdMatch, tagStart, "value"),
228
+ start: textId.start,
229
+ end: textId.end,
257
230
  text: id
258
231
  });
259
232
  } else edits.push({
@@ -266,7 +239,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
266
239
  seen.add(id);
267
240
  continue;
268
241
  }
269
- const value = paraIdMatch.groups["value"].toUpperCase();
242
+ const value = paraId.value.toUpperCase();
270
243
  const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
271
244
  if (!unassigned && !seen.has(value)) {
272
245
  seen.add(value);
@@ -274,12 +247,13 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
274
247
  }
275
248
  const id = mintParaId(context, partPath, ordinal);
276
249
  edits.push({
277
- ...attributeValueRange(paraIdMatch, tagStart, "value"),
250
+ start: paraId.start,
251
+ end: paraId.end,
278
252
  text: id
279
253
  });
280
- const textIdMatch = spelling.textId.exec(openTag);
281
- if (textIdMatch) edits.push({
282
- ...attributeValueRange(textIdMatch, tagStart, "value"),
254
+ if (textId !== void 0) edits.push({
255
+ start: textId.start,
256
+ end: textId.end,
283
257
  text: id
284
258
  });
285
259
  else edits.push({
@@ -296,8 +270,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
296
270
  edits,
297
271
  minted,
298
272
  assigned,
299
- deduplicated,
300
- paragraphs: ordinal
273
+ deduplicated
301
274
  };
302
275
  };
303
276
  /**
@@ -305,71 +278,42 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
305
278
  * `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
306
279
  * 2010 extensions skip the new attributes instead of rejecting the part.
307
280
  */
308
- const ensureRootNamespaces = (xml, partPath, { prefixes, w14Prefix }) => {
309
- let pos = 0;
310
- let rootStart = -1;
311
- while (pos < xml.length) {
312
- const lt = xml.indexOf("<", pos);
313
- if (lt === -1) break;
314
- const opaqueEnd = skipOpaqueXmlRegion(xml, lt, partPath);
315
- if (opaqueEnd !== null) {
316
- pos = opaqueEnd;
317
- continue;
318
- }
319
- if (xml[lt + 1] === "!") {
320
- const skipTo = xml.indexOf(">", lt);
321
- if (skipTo === -1) throw createEnsureParaIdsError(`Unterminated prolog in ${partPath}`);
322
- pos = skipTo + 1;
323
- continue;
324
- }
325
- rootStart = lt;
326
- break;
327
- }
328
- if (rootStart === -1) throw createEnsureParaIdsError(`No root element in ${partPath}`);
329
- const rootEnd = xml.indexOf(">", rootStart);
330
- if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
331
- const rootTag = xml.slice(rootStart, rootEnd + 1);
332
- if (!prefixes.mcDeclared && prefixes.mc.length === 0) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
333
- if (!prefixes.w14Declared && prefixes.w14.length === 0) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
281
+ const ensureRootNamespaces = ({ root, rootNameEnd, ignorable, w14Prefix, mcPrefix }) => {
334
282
  const edits = [];
335
283
  const declarations = [];
336
- const mcPrefix = (prefixes.mcDeclared ? prefixes.mc[0] : void 0) ?? "mc";
337
- if (!prefixes.mcDeclared) declarations.push(` xmlns:mc="${MC_NAMESPACE_URI}"`);
338
- if (!prefixes.w14Declared) declarations.push(` xmlns:w14="${W14_NAMESPACE_URI}"`);
339
- const ignorable = prefixedAttributePattern(prefixes.mcDeclared ? prefixes.mc : [mcPrefix], "Ignorable").exec(rootTag);
340
- if (ignorable) {
341
- const tokens = ignorable.groups["value"].split(/\s+/u).filter((token) => token.length > 0);
342
- if (!tokens.includes(w14Prefix)) {
343
- const { end } = attributeValueRange(ignorable, rootStart, "value");
344
- const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
345
- edits.push({
346
- start: end,
347
- end,
348
- text
349
- });
350
- }
351
- } else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
352
- if (declarations.length > 0) {
353
- let nameEnd = rootStart + 1;
354
- while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
355
- edits.push({
356
- start: nameEnd,
357
- end: nameEnd,
358
- text: declarations.join("")
284
+ if (resolveNamespaceUri(root.namespaceScope, mcPrefix) !== MC_NAMESPACE_URI) declarations.push(` xmlns:${mcPrefix}="${MC_NAMESPACE_URI}"`);
285
+ if (resolveNamespaceUri(root.namespaceScope, w14Prefix) !== W14_NAMESPACE_URI) declarations.push(` xmlns:${w14Prefix}="${W14_NAMESPACE_URI}"`);
286
+ if (ignorable !== void 0) {
287
+ const tokens = ignorable.value.split(/\s+/u).filter((token) => token.length > 0);
288
+ if (!tokens.includes(w14Prefix)) edits.push({
289
+ start: ignorable.end,
290
+ end: ignorable.end,
291
+ text: tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`
359
292
  });
360
- }
293
+ } else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
294
+ if (declarations.length > 0) edits.push({
295
+ start: rootNameEnd,
296
+ end: rootNameEnd,
297
+ text: declarations.join("")
298
+ });
361
299
  return edits;
362
300
  };
363
- const hasWordParagraph = (element) => getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "") || getChildElements(element).some(hasWordParagraph);
364
- /**
365
- * A body the string scan saw no paragraph in is either genuinely empty or
366
- * spelled in a way the scan missed. Only the namespace-aware parser can tell
367
- * which, and it only has to be asked in that rare case: `alreadyComplete`
368
- * must never describe paragraphs nobody looked at.
369
- */
370
- const assertNoUnscannedParagraphs = (xml, partPath) => {
371
- const root = parseXmlDocument(xml);
372
- if (root !== null && hasWordParagraph(root)) throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: its paragraphs are not spelled in a way the scan can patch`);
301
+ const mainDocumentPart = (rels, entries) => {
302
+ const root = parseXmlDocument(rels);
303
+ if (root === null || getLocalName(root.name ?? "") !== "Relationships" || getNamespaceUri(root) !== PACKAGE_RELATIONSHIP_URI) throw createEnsureParaIdsError("Malformed package relationships");
304
+ const documents = getChildElements(root).filter((element) => {
305
+ if (getLocalName(element.name ?? "") !== "Relationship" || getNamespaceUri(element) !== PACKAGE_RELATIONSHIP_URI) return false;
306
+ const type = getAttribute(element, null, "Type");
307
+ return [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((uri) => type === `${uri}/officeDocument`);
308
+ });
309
+ const document = documents.at(0);
310
+ if (documents.length !== 1 || document === void 0 || getAttribute(document, null, "TargetMode") === "External") throw createEnsureParaIdsError("Package must have one internal officeDocument relationship");
311
+ const target = getAttribute(document, null, "Target");
312
+ const partPath = target === null ? void 0 : resolvePackageRelationshipTarget(target, "_rels/.rels");
313
+ if (partPath === void 0) throw createEnsureParaIdsError("Invalid officeDocument target");
314
+ const name = entries.find((entry) => entry.toLowerCase() === partPath);
315
+ if (name === void 0) throw createEnsureParaIdsError("officeDocument part not found");
316
+ return name;
373
317
  };
374
318
  /** OPC part names are case-insensitive; compare lowercased. */
375
319
  const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
@@ -382,12 +326,11 @@ const hasDigitalSignatureParts = (zip) => Object.values(zip.files).some(({ dir,
382
326
  */
383
327
  const ensureParaIdsInternal = async (docx, options) => {
384
328
  const archive = await loadDocxArchive(docx);
385
- const xmlPartNames = archive.entries.filter((relativePath) => {
386
- const lower = relativePath.toLowerCase();
387
- return lower.startsWith("word/") && lower.endsWith(".xml");
388
- });
389
- const documentPartName = xmlPartNames.find((name) => name.toLowerCase() === DOCUMENT_PART);
390
- if (documentPartName === void 0) throw createEnsureParaIdsError("word/document.xml not found: not a WordprocessingML package");
329
+ const relsPath = archive.entries.find((name) => name.toLowerCase() === "_rels/.rels");
330
+ const rels = relsPath === void 0 ? null : await archive.readEntryString(relsPath);
331
+ if (rels === null) throw createEnsureParaIdsError("Package relationships not found");
332
+ const documentPartName = mainDocumentPart(rels, archive.entries);
333
+ const xmlPartNames = archive.entries.filter((name) => name.toLowerCase().endsWith(".xml") || name === documentPartName);
391
334
  const partTexts = /* @__PURE__ */ new Map();
392
335
  for (const name of xmlPartNames) {
393
336
  const text = await archive.readEntryString(name);
@@ -395,17 +338,19 @@ const ensureParaIdsInternal = async (docx, options) => {
395
338
  }
396
339
  const taken = /* @__PURE__ */ new Set();
397
340
  for (const text of partTexts.values()) collectExistingParaIds(text, taken);
341
+ const documentXml = partTexts.get(documentPartName);
342
+ if (documentXml === void 0) throw createEnsureParaIdsError("officeDocument part is unreadable");
398
343
  const context = {
399
344
  taken,
400
- docKey: deterministicHexId(partTexts.get(documentPartName))
345
+ docKey: deterministicHexId(documentXml)
401
346
  };
402
347
  const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
403
348
  const spellings = /* @__PURE__ */ new Map();
404
349
  const seen = /* @__PURE__ */ new Set();
405
- for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
350
+ for (const [partPath, xml] of partTexts) if (partPath === documentPartName || isTargetPart(partPath)) {
406
351
  const spelling = partSpelling(xml, partPath);
407
352
  spellings.set(partPath, spelling);
408
- collectFallbackParaIds(xml, partPath, spelling, seen);
353
+ collectFallbackParaIds(spelling, seen);
409
354
  } else collectExistingParaIds(xml, seen);
410
355
  const updates = /* @__PURE__ */ new Map();
411
356
  const mintedParaIds = [];
@@ -414,13 +359,17 @@ const ensureParaIdsInternal = async (docx, options) => {
414
359
  for (const partPath of targetParts) {
415
360
  const xml = partTexts.get(partPath);
416
361
  const spelling = spellings.get(partPath);
417
- const scan = scanPart(xml, partPath, spelling, context, seen);
418
- if (partPath === documentPartName && scan.paragraphs === 0) assertNoUnscannedParagraphs(xml, partPath);
362
+ const scan = scanPart({
363
+ partPath,
364
+ spelling,
365
+ context,
366
+ seen
367
+ });
419
368
  if (scan.edits.length === 0) continue;
420
369
  assigned += scan.assigned;
421
370
  deduplicated += scan.deduplicated;
422
371
  for (const id of scan.minted) mintedParaIds.push(id);
423
- updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(xml, partPath, spelling)], partPath));
372
+ updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(spelling)], partPath));
424
373
  }
425
374
  if (updates.size === 0) return {
426
375
  docx: toUint8Array(docx),
@@ -430,7 +379,10 @@ const ensureParaIdsInternal = async (docx, options) => {
430
379
  mintedParaIds: []
431
380
  };
432
381
  const zip = await JSZip.loadAsync(docx);
433
- if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw createEnsureParaIdsError("Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable.");
382
+ if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw new EnsureParaIdsError({
383
+ reason: ENSURE_PARA_IDS_REASONS.SIGNED_PACKAGE,
384
+ message: "Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable."
385
+ });
434
386
  for (const [partPath, content] of updates) {
435
387
  const sourceEntry = zip.file(partPath);
436
388
  if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
@@ -466,4 +418,4 @@ const ensureParaIds = async (docx, options = {}) => {
466
418
  }
467
419
  };
468
420
  //#endregion
469
- export { EnsureParaIdsError, ensureParaIds };
421
+ export { ENSURE_PARA_IDS_REASONS, EnsureParaIdsError, ensureParaIds };
@@ -0,0 +1,85 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/foldedListNumberFields.d.ts
3
+ /** Whether `content` is markup that may stand between a field and the tab that follows it. */
4
+ declare const isListNumberGapMarker: (content: document_d_exports.ParagraphContent) => boolean;
5
+ /** Whether `content` is a `LISTNUM` field, by its parsed type or its instruction. */
6
+ declare const isListNumberField: (content: document_d_exports.ParagraphContent) => content is document_d_exports.ComplexField;
7
+ /** A run that holds one tab and nothing else. */
8
+ declare const isTabOnlyRun: (content: document_d_exports.ParagraphContent) => content is document_d_exports.Run;
9
+ /** Whether `value` is what a capture stands for: a complex field, or a run. */
10
+ declare const isFoldedListNumber: (value: unknown) => value is document_d_exports.FoldedListNumber;
11
+ /** What `content` stands for when it is a capture the fold left, else nothing. */
12
+ declare const foldedListNumberOf: (content: document_d_exports.ParagraphContent) => document_d_exports.FoldedListNumber | undefined;
13
+ /** Whether `content` is a capture the fold left in place of a field or its tab. */
14
+ declare const isFoldedListNumberCapture: (content: document_d_exports.ParagraphContent) => boolean;
15
+ type ListNumberFieldFold = {
16
+ /** The content, each folded field and its tab replaced by a capture. */
17
+ content: document_d_exports.ParagraphContent[];
18
+ /** The cached display of each folded field that has one, in source order. */
19
+ cached: string[];
20
+ /** Every `LISTNUM` field the content holds, folded or not. */
21
+ fieldCount: number;
22
+ };
23
+ /**
24
+ * Replace the `LISTNUM` fields that open a paragraph, and the tab after each,
25
+ * by captures of the markup they were read from.
26
+ *
27
+ * Only what stands ahead of the first thing the line shows is folded: a field
28
+ * after text is on the line where the text is. `sourceMarkupOf` answers the
29
+ * markup; a field it has none for is left as the field it is, and ends the
30
+ * fold. Bookmark and comment markers between a field and its tab stay where
31
+ * they are, and the tab is still the field's when only they separate the two.
32
+ */
33
+ declare const foldListNumberFields: (content: readonly document_d_exports.ParagraphContent[], sourceMarkupOf: (item: document_d_exports.ComplexField | document_d_exports.Run) => string | undefined) => ListNumberFieldFold;
34
+ /** One item of a paragraph, as far as the fold needs to know it. */
35
+ type ListNumberFoldItem =
36
+ /** A capture of a field, with the text its cached result shows. */
37
+ {
38
+ kind: "field";
39
+ cached: string;
40
+ } |
41
+ /** A capture of the tab after a field. */
42
+ {
43
+ kind: "tab";
44
+ } |
45
+ /** Markup that shows nothing. */
46
+ {
47
+ kind: "hidden";
48
+ } |
49
+ /** Anything the line shows. */
50
+ {
51
+ kind: "shown";
52
+ };
53
+ type ListNumberFoldPlan = {
54
+ /** The items' indices in the order they are to stand in. */
55
+ order: number[];
56
+ /** Indices of the captures that stay hidden; every other capture goes on the line. */
57
+ hidden: ReadonlySet<number>;
58
+ /** What the marker shows after its own text, or nothing. */
59
+ suffix: string | undefined;
60
+ };
61
+ /**
62
+ * Decide which captures of a paragraph stay hidden, and what its marker shows.
63
+ *
64
+ * `markerShowsFields` says whether the paragraph's marker shows folded fields
65
+ * at all. When it does not, no capture is hidden.
66
+ *
67
+ * When it does, the captures it shows are the ones that open the paragraph:
68
+ * those ahead of the first thing the line shows, each field followed at most
69
+ * by its own tab. Text put in front of them was put in front of the body, not
70
+ * of the marker, so if the first capture no longer opens the paragraph, it
71
+ * and the captures that stand with it go back to the start. Every capture
72
+ * further in is on the line.
73
+ */
74
+ declare const planListNumberFold: (items: readonly ListNumberFoldItem[], markerShowsFields: boolean) => ListNumberFoldPlan;
75
+ /** The item a capture stands for, which shows on the line; any other item is itself. */
76
+ declare const unfoldedListNumberContent: (content: document_d_exports.ParagraphContent) => document_d_exports.ParagraphContent;
77
+ /**
78
+ * Bring `paragraph` to the one form the fold allows: the captures its marker
79
+ * shows stand at its start, every other capture is the field or the tab it
80
+ * stood for, and the marker shows exactly the fields still hidden behind it.
81
+ * A capture under a tracked change is never hidden.
82
+ */
83
+ declare const normalizeFoldedListNumbers: (paragraph: document_d_exports.Paragraph) => void;
84
+ //#endregion
85
+ export { ListNumberFieldFold, ListNumberFoldItem, ListNumberFoldPlan, foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };