@usejunior/docx-core 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/NOTICE +2 -0
- package/README.md +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/atomizer.d.ts +28 -8
- package/dist/atomizer.d.ts.map +1 -1
- package/dist/atomizer.js +96 -25
- package/dist/atomizer.js.map +1 -1
- package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts +99 -0
- package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts.map +1 -0
- package/dist/baselines/atomizer/auxiliaryIdCollision.js +415 -0
- package/dist/baselines/atomizer/auxiliaryIdCollision.js.map +1 -0
- package/dist/baselines/atomizer/documentReconstructor.d.ts.map +1 -1
- package/dist/baselines/atomizer/documentReconstructor.js +333 -112
- package/dist/baselines/atomizer/documentReconstructor.js.map +1 -1
- package/dist/baselines/atomizer/formattingFidelity.d.ts +99 -0
- package/dist/baselines/atomizer/formattingFidelity.d.ts.map +1 -0
- package/dist/baselines/atomizer/formattingFidelity.js +449 -0
- package/dist/baselines/atomizer/formattingFidelity.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts +37 -0
- package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js +189 -0
- package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts +74 -0
- package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-containers.js +171 -0
- package/dist/baselines/atomizer/inPlaceModifier-containers.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts +88 -0
- package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-deletion.js +326 -0
- package/dist/baselines/atomizer/inPlaceModifier-deletion.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts +85 -0
- package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-postprocess.js +402 -0
- package/dist/baselines/atomizer/inPlaceModifier-postprocess.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts +39 -0
- package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-presplit.js +265 -0
- package/dist/baselines/atomizer/inPlaceModifier-presplit.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts +62 -0
- package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-shared.js +139 -0
- package/dist/baselines/atomizer/inPlaceModifier-shared.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts +198 -0
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.js +475 -0
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.js.map +1 -0
- package/dist/baselines/atomizer/inPlaceModifier.d.ts +6 -290
- package/dist/baselines/atomizer/inPlaceModifier.d.ts.map +1 -1
- package/dist/baselines/atomizer/inPlaceModifier.js +23 -1828
- package/dist/baselines/atomizer/inPlaceModifier.js.map +1 -1
- package/dist/baselines/atomizer/pipeline.d.ts +36 -2
- package/dist/baselines/atomizer/pipeline.d.ts.map +1 -1
- package/dist/baselines/atomizer/pipeline.js +216 -144
- package/dist/baselines/atomizer/pipeline.js.map +1 -1
- package/dist/baselines/atomizer/trackChangesAcceptorAst.d.ts.map +1 -1
- package/dist/baselines/atomizer/trackChangesAcceptorAst.js +199 -173
- package/dist/baselines/atomizer/trackChangesAcceptorAst.js.map +1 -1
- package/dist/baselines/wmlcomparer/DotnetCli.d.ts.map +1 -1
- package/dist/baselines/wmlcomparer/DotnetCli.js +7 -0
- package/dist/baselines/wmlcomparer/DotnetCli.js.map +1 -1
- package/dist/cli/compare-two.d.ts.map +1 -1
- package/dist/cli/compare-two.js +3 -1
- package/dist/cli/compare-two.js.map +1 -1
- package/dist/cli/conformance-adapter.d.ts +3 -0
- package/dist/cli/conformance-adapter.d.ts.map +1 -0
- package/dist/cli/conformance-adapter.js +93 -0
- package/dist/cli/conformance-adapter.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/compare-types.d.ts +197 -0
- package/dist/compare-types.d.ts.map +1 -0
- package/dist/compare-types.js +2 -0
- package/dist/compare-types.js.map +1 -0
- package/dist/core-types.d.ts +5 -1
- package/dist/core-types.d.ts.map +1 -1
- package/dist/core-types.js +5 -1
- package/dist/core-types.js.map +1 -1
- package/dist/footnotes.d.ts +8 -3
- package/dist/footnotes.d.ts.map +1 -1
- package/dist/footnotes.js +8 -3
- package/dist/footnotes.js.map +1 -1
- package/dist/generation/compile.d.ts +21 -0
- package/dist/generation/compile.d.ts.map +1 -0
- package/dist/generation/compile.js +46 -0
- package/dist/generation/compile.js.map +1 -0
- package/dist/generation/context.d.ts +42 -0
- package/dist/generation/context.d.ts.map +1 -0
- package/dist/generation/context.js +65 -0
- package/dist/generation/context.js.map +1 -0
- package/dist/generation/emit/comments-part.d.ts +36 -0
- package/dist/generation/emit/comments-part.d.ts.map +1 -0
- package/dist/generation/emit/comments-part.js +116 -0
- package/dist/generation/emit/comments-part.js.map +1 -0
- package/dist/generation/emit/document-part.d.ts +24 -0
- package/dist/generation/emit/document-part.d.ts.map +1 -0
- package/dist/generation/emit/document-part.js +60 -0
- package/dist/generation/emit/document-part.js.map +1 -0
- package/dist/generation/emit/emit-context.d.ts +26 -0
- package/dist/generation/emit/emit-context.d.ts.map +1 -0
- package/dist/generation/emit/emit-context.js +19 -0
- package/dist/generation/emit/emit-context.js.map +1 -0
- package/dist/generation/emit/header-footer-part.d.ts +23 -0
- package/dist/generation/emit/header-footer-part.d.ts.map +1 -0
- package/dist/generation/emit/header-footer-part.js +57 -0
- package/dist/generation/emit/header-footer-part.js.map +1 -0
- package/dist/generation/emit/numbering-part.d.ts +29 -0
- package/dist/generation/emit/numbering-part.d.ts.map +1 -0
- package/dist/generation/emit/numbering-part.js +100 -0
- package/dist/generation/emit/numbering-part.js.map +1 -0
- package/dist/generation/emit/package-parts.d.ts +24 -0
- package/dist/generation/emit/package-parts.d.ts.map +1 -0
- package/dist/generation/emit/package-parts.js +121 -0
- package/dist/generation/emit/package-parts.js.map +1 -0
- package/dist/generation/emit/paragraph.d.ts +24 -0
- package/dist/generation/emit/paragraph.d.ts.map +1 -0
- package/dist/generation/emit/paragraph.js +63 -0
- package/dist/generation/emit/paragraph.js.map +1 -0
- package/dist/generation/emit/properties.d.ts +34 -0
- package/dist/generation/emit/properties.d.ts.map +1 -0
- package/dist/generation/emit/properties.js +138 -0
- package/dist/generation/emit/properties.js.map +1 -0
- package/dist/generation/emit/run.d.ts +15 -0
- package/dist/generation/emit/run.d.ts.map +1 -0
- package/dist/generation/emit/run.js +71 -0
- package/dist/generation/emit/run.js.map +1 -0
- package/dist/generation/emit/section.d.ts +29 -0
- package/dist/generation/emit/section.d.ts.map +1 -0
- package/dist/generation/emit/section.js +117 -0
- package/dist/generation/emit/section.js.map +1 -0
- package/dist/generation/emit/settings-part.d.ts +13 -0
- package/dist/generation/emit/settings-part.d.ts.map +1 -0
- package/dist/generation/emit/settings-part.js +24 -0
- package/dist/generation/emit/settings-part.js.map +1 -0
- package/dist/generation/emit/styles-part.d.ts +16 -0
- package/dist/generation/emit/styles-part.d.ts.map +1 -0
- package/dist/generation/emit/styles-part.js +80 -0
- package/dist/generation/emit/styles-part.js.map +1 -0
- package/dist/generation/emit/table.d.ts +26 -0
- package/dist/generation/emit/table.d.ts.map +1 -0
- package/dist/generation/emit/table.js +196 -0
- package/dist/generation/emit/table.js.map +1 -0
- package/dist/generation/errors.d.ts +22 -0
- package/dist/generation/errors.d.ts.map +1 -0
- package/dist/generation/errors.js +29 -0
- package/dist/generation/errors.js.map +1 -0
- package/dist/generation/index.d.ts +13 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +12 -0
- package/dist/generation/index.js.map +1 -0
- package/dist/generation/ordering.d.ts +46 -0
- package/dist/generation/ordering.d.ts.map +1 -0
- package/dist/generation/ordering.js +119 -0
- package/dist/generation/ordering.js.map +1 -0
- package/dist/generation/recipes.d.ts +47 -0
- package/dist/generation/recipes.d.ts.map +1 -0
- package/dist/generation/recipes.js +84 -0
- package/dist/generation/recipes.js.map +1 -0
- package/dist/generation/structural-checks.d.ts +24 -0
- package/dist/generation/structural-checks.d.ts.map +1 -0
- package/dist/generation/structural-checks.js +318 -0
- package/dist/generation/structural-checks.js.map +1 -0
- package/dist/generation/types.d.ts +217 -0
- package/dist/generation/types.d.ts.map +1 -0
- package/dist/generation/types.js +16 -0
- package/dist/generation/types.js.map +1 -0
- package/dist/generation/validate-spec.d.ts +27 -0
- package/dist/generation/validate-spec.d.ts.map +1 -0
- package/dist/generation/validate-spec.js +307 -0
- package/dist/generation/validate-spec.js.map +1 -0
- package/dist/index.d.ts +9 -150
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/generation-probes.d.ts +15 -0
- package/dist/integration/generation-probes.d.ts.map +1 -0
- package/dist/integration/generation-probes.js +84 -0
- package/dist/integration/generation-probes.js.map +1 -0
- package/dist/integration/libreoffice-oracle.d.ts +49 -0
- package/dist/integration/libreoffice-oracle.d.ts.map +1 -0
- package/dist/integration/libreoffice-oracle.js +290 -0
- package/dist/integration/libreoffice-oracle.js.map +1 -0
- package/dist/integration/synthetic-docx-fixture.d.ts +72 -0
- package/dist/integration/synthetic-docx-fixture.d.ts.map +1 -1
- package/dist/integration/synthetic-docx-fixture.js +131 -4
- package/dist/integration/synthetic-docx-fixture.js.map +1 -1
- package/dist/primitives/accept_changes.d.ts +4 -3
- package/dist/primitives/accept_changes.d.ts.map +1 -1
- package/dist/primitives/accept_changes.js +163 -77
- package/dist/primitives/accept_changes.js.map +1 -1
- package/dist/primitives/comments.d.ts +12 -3
- package/dist/primitives/comments.d.ts.map +1 -1
- package/dist/primitives/comments.js +374 -97
- package/dist/primitives/comments.js.map +1 -1
- package/dist/primitives/content_fingerprint.d.ts +29 -0
- package/dist/primitives/content_fingerprint.d.ts.map +1 -0
- package/dist/primitives/content_fingerprint.js +63 -0
- package/dist/primitives/content_fingerprint.js.map +1 -0
- package/dist/primitives/document.d.ts +94 -15
- package/dist/primitives/document.d.ts.map +1 -1
- package/dist/primitives/document.js +373 -36
- package/dist/primitives/document.js.map +1 -1
- package/dist/primitives/document_view-comments.d.ts +18 -0
- package/dist/primitives/document_view-comments.d.ts.map +1 -0
- package/dist/primitives/document_view-comments.js +160 -0
- package/dist/primitives/document_view-comments.js.map +1 -0
- package/dist/primitives/document_view-headings.d.ts +45 -0
- package/dist/primitives/document_view-headings.d.ts.map +1 -0
- package/dist/primitives/document_view-headings.js +247 -0
- package/dist/primitives/document_view-headings.js.map +1 -0
- package/dist/primitives/document_view-styles.d.ts +11 -0
- package/dist/primitives/document_view-styles.d.ts.map +1 -0
- package/dist/primitives/document_view-styles.js +104 -0
- package/dist/primitives/document_view-styles.js.map +1 -0
- package/dist/primitives/document_view-toon.d.ts +37 -0
- package/dist/primitives/document_view-toon.d.ts.map +1 -0
- package/dist/primitives/document_view-toon.js +199 -0
- package/dist/primitives/document_view-toon.js.map +1 -0
- package/dist/primitives/document_view-types.d.ts +152 -0
- package/dist/primitives/document_view-types.d.ts.map +1 -0
- package/dist/primitives/document_view-types.js +2 -0
- package/dist/primitives/document_view-types.js.map +1 -0
- package/dist/primitives/document_view.d.ts +8 -106
- package/dist/primitives/document_view.d.ts.map +1 -1
- package/dist/primitives/document_view.js +153 -312
- package/dist/primitives/document_view.js.map +1 -1
- package/dist/primitives/dom-helpers.d.ts +9 -0
- package/dist/primitives/dom-helpers.d.ts.map +1 -1
- package/dist/primitives/dom-helpers.js +10 -1
- package/dist/primitives/dom-helpers.js.map +1 -1
- package/dist/primitives/footnotes.d.ts +4 -3
- package/dist/primitives/footnotes.d.ts.map +1 -1
- package/dist/primitives/footnotes.js +232 -44
- package/dist/primitives/footnotes.js.map +1 -1
- package/dist/primitives/formatting_tags.d.ts +7 -0
- package/dist/primitives/formatting_tags.d.ts.map +1 -1
- package/dist/primitives/formatting_tags.js +22 -11
- package/dist/primitives/formatting_tags.js.map +1 -1
- package/dist/primitives/index.d.ts +10 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +9 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/layout.d.ts +4 -3
- package/dist/primitives/layout.d.ts.map +1 -1
- package/dist/primitives/layout.js +45 -3
- package/dist/primitives/layout.js.map +1 -1
- package/dist/primitives/merge_runs.d.ts +21 -3
- package/dist/primitives/merge_runs.d.ts.map +1 -1
- package/dist/primitives/merge_runs.js +32 -10
- package/dist/primitives/merge_runs.js.map +1 -1
- package/dist/primitives/minimal_save.d.ts +38 -0
- package/dist/primitives/minimal_save.d.ts.map +1 -0
- package/dist/primitives/minimal_save.js +323 -0
- package/dist/primitives/minimal_save.js.map +1 -0
- package/dist/primitives/namespaces.d.ts +47 -0
- package/dist/primitives/namespaces.d.ts.map +1 -1
- package/dist/primitives/namespaces.js +52 -0
- package/dist/primitives/namespaces.js.map +1 -1
- package/dist/primitives/reject_changes.d.ts +6 -4
- package/dist/primitives/reject_changes.d.ts.map +1 -1
- package/dist/primitives/reject_changes.js +187 -91
- package/dist/primitives/reject_changes.js.map +1 -1
- package/dist/primitives/revision-parts.d.ts +7 -0
- package/dist/primitives/revision-parts.d.ts.map +1 -0
- package/dist/primitives/revision-parts.js +27 -0
- package/dist/primitives/revision-parts.js.map +1 -0
- package/dist/primitives/revision-vocabulary.d.ts +7 -0
- package/dist/primitives/revision-vocabulary.d.ts.map +1 -0
- package/dist/primitives/revision-vocabulary.js +39 -0
- package/dist/primitives/revision-vocabulary.js.map +1 -0
- package/dist/primitives/schema-corpus-capture.d.ts +19 -0
- package/dist/primitives/schema-corpus-capture.d.ts.map +1 -0
- package/dist/primitives/schema-corpus-capture.js +29 -0
- package/dist/primitives/schema-corpus-capture.js.map +1 -0
- package/dist/primitives/sectPrAudit.d.ts +19 -0
- package/dist/primitives/sectPrAudit.d.ts.map +1 -0
- package/dist/primitives/sectPrAudit.js +165 -0
- package/dist/primitives/sectPrAudit.js.map +1 -0
- package/dist/primitives/semantic_tags.d.ts +7 -0
- package/dist/primitives/semantic_tags.d.ts.map +1 -1
- package/dist/primitives/semantic_tags.js +23 -4
- package/dist/primitives/semantic_tags.js.map +1 -1
- package/dist/primitives/serialize_html.d.ts +37 -0
- package/dist/primitives/serialize_html.d.ts.map +1 -0
- package/dist/primitives/serialize_html.js +395 -0
- package/dist/primitives/serialize_html.js.map +1 -0
- package/dist/primitives/serialize_markdown.d.ts +16 -0
- package/dist/primitives/serialize_markdown.d.ts.map +1 -0
- package/dist/primitives/serialize_markdown.js +300 -0
- package/dist/primitives/serialize_markdown.js.map +1 -0
- package/dist/primitives/serialize_plaintext.d.ts +15 -0
- package/dist/primitives/serialize_plaintext.d.ts.map +1 -0
- package/dist/primitives/serialize_plaintext.js +154 -0
- package/dist/primitives/serialize_plaintext.js.map +1 -0
- package/dist/primitives/styles.d.ts +15 -0
- package/dist/primitives/styles.d.ts.map +1 -1
- package/dist/primitives/styles.js +33 -22
- package/dist/primitives/styles.js.map +1 -1
- package/dist/primitives/tables.d.ts.map +1 -1
- package/dist/primitives/tables.js +13 -3
- package/dist/primitives/tables.js.map +1 -1
- package/dist/primitives/text.d.ts +2 -1
- package/dist/primitives/text.d.ts.map +1 -1
- package/dist/primitives/text.js +116 -12
- package/dist/primitives/text.js.map +1 -1
- package/dist/primitives/track-changes-emitter.d.ts +148 -0
- package/dist/primitives/track-changes-emitter.d.ts.map +1 -0
- package/dist/primitives/track-changes-emitter.js +291 -0
- package/dist/primitives/track-changes-emitter.js.map +1 -0
- package/dist/primitives/validate_ai_revisions.d.ts +35 -0
- package/dist/primitives/validate_ai_revisions.d.ts.map +1 -0
- package/dist/primitives/validate_ai_revisions.js +323 -0
- package/dist/primitives/validate_ai_revisions.js.map +1 -0
- package/dist/primitives/xml-helpers.d.ts +29 -0
- package/dist/primitives/xml-helpers.d.ts.map +1 -0
- package/dist/primitives/xml-helpers.js +35 -0
- package/dist/primitives/xml-helpers.js.map +1 -0
- package/dist/primitives/xml.d.ts +5 -0
- package/dist/primitives/xml.d.ts.map +1 -1
- package/dist/primitives/xml.js +5 -0
- package/dist/primitives/xml.js.map +1 -1
- package/dist/primitives/zip.d.ts +1 -0
- package/dist/primitives/zip.d.ts.map +1 -1
- package/dist/primitives/zip.js +21 -3
- package/dist/primitives/zip.js.map +1 -1
- package/dist/shared/field-structure.d.ts +14 -0
- package/dist/shared/field-structure.d.ts.map +1 -0
- package/dist/shared/field-structure.js +166 -0
- package/dist/shared/field-structure.js.map +1 -0
- package/dist/shared/ooxml/namespaces.d.ts +4 -1
- package/dist/shared/ooxml/namespaces.d.ts.map +1 -1
- package/dist/shared/ooxml/namespaces.js +4 -1
- package/dist/shared/ooxml/namespaces.js.map +1 -1
- package/package.json +13 -9
|
@@ -21,6 +21,10 @@ import { modifyRevisedDocument, ContainerResolutionError } from './inPlaceModifi
|
|
|
21
21
|
import { acceptAllChanges, rejectAllChanges, extractTextWithParagraphs, compareTexts, } from './trackChangesAcceptorAst.js';
|
|
22
22
|
import { virtualizeNumberingLabels, DEFAULT_NUMBERING_OPTIONS, } from './numberingIntegration.js';
|
|
23
23
|
import { premergeAdjacentRuns } from './premergeRuns.js';
|
|
24
|
+
export { hasFldCharInsideDel, validateFieldStructure, } from '../../shared/field-structure.js';
|
|
25
|
+
import { hasFldCharInsideDel, validateFieldStructure, } from '../../shared/field-structure.js';
|
|
26
|
+
import { AUXILIARY_PARTS, parseEntries, renumberCollidingAuxiliaryIds, restampCollidingCommentParaIds, } from './auxiliaryIdCollision.js';
|
|
27
|
+
import { maybeCaptureEmittedDocumentXml } from '../../primitives/schema-corpus-capture.js';
|
|
24
28
|
function arraysEqual(a, b) {
|
|
25
29
|
if (a.length !== b.length)
|
|
26
30
|
return false;
|
|
@@ -215,70 +219,56 @@ function buildFailureSummary(failureDetails) {
|
|
|
215
219
|
}
|
|
216
220
|
return Object.keys(summary).length > 0 ? summary : undefined;
|
|
217
221
|
}
|
|
222
|
+
// Declared above splitStories so the function body never observes an
|
|
223
|
+
// uninitialized binding under circular imports.
|
|
224
|
+
const serializer = new XMLSerializer();
|
|
218
225
|
/**
|
|
219
|
-
*
|
|
226
|
+
* Split a docx into per-story XML fragments for field-closure validation.
|
|
227
|
+
*
|
|
228
|
+
* Each footnote/endnote entry is treated as an isolated story: a complex
|
|
229
|
+
* field whose `begin` and `end` markers straddle stories breaks Word's
|
|
230
|
+
* field state machine. We therefore validate each `<w:footnote>` and
|
|
231
|
+
* `<w:endnote>` entry independently rather than treating the whole
|
|
232
|
+
* `footnotes.xml`/`endnotes.xml` as one stream.
|
|
233
|
+
*
|
|
234
|
+
* Accepts arrays of sidecar XMLs (one per source archive) so callers can
|
|
235
|
+
* validate the union of entries from every archive that may contribute to the
|
|
236
|
+
* final result. Step 12 of `compareDocumentsAtomizer` merges entries from a
|
|
237
|
+
* mode-dependent source archive into the base archive; passing both archives'
|
|
238
|
+
* sidecars guarantees that whichever path the merge takes, the entries it
|
|
239
|
+
* could publish have already been screened. Duplicates (same `w:id` in both
|
|
240
|
+
* archives) yield redundant but harmless validation work.
|
|
220
241
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
242
|
+
* Header/footer stories are not yet covered — they require relationship
|
|
243
|
+
* walking to enumerate `headerN.xml`/`footerN.xml`.
|
|
244
|
+
*
|
|
245
|
+
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
246
|
+
* @see https://github.com/UseJunior/safe-docx/issues/212
|
|
224
247
|
*/
|
|
225
|
-
function
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return false;
|
|
242
|
-
// Check that instrText elements are inside a field (between begin and separate).
|
|
243
|
-
// Walk all elements in document order using a recursive scan.
|
|
244
|
-
if (allInstrTexts.length === 0)
|
|
245
|
-
return true; // No instrText, nothing to validate
|
|
246
|
-
// Depth-first scan to check instrText placement
|
|
247
|
-
let depth = 0;
|
|
248
|
-
const pastSeparatorAtDepth = []; // track separator state per depth
|
|
249
|
-
function scan(node) {
|
|
250
|
-
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
251
|
-
if (child.nodeType !== 1)
|
|
252
|
-
continue; // skip non-elements
|
|
253
|
-
const el = child;
|
|
254
|
-
if (el.tagName === 'w:fldChar') {
|
|
255
|
-
const type = el.getAttribute('w:fldCharType');
|
|
256
|
-
if (type === 'begin') {
|
|
257
|
-
depth++;
|
|
258
|
-
pastSeparatorAtDepth[depth] = 0;
|
|
259
|
-
}
|
|
260
|
-
else if (type === 'separate') {
|
|
261
|
-
if (depth > 0)
|
|
262
|
-
pastSeparatorAtDepth[depth] = 1;
|
|
263
|
-
}
|
|
264
|
-
else if (type === 'end') {
|
|
265
|
-
if (depth > 0)
|
|
266
|
-
depth--;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
else if (el.tagName === 'w:instrText') {
|
|
270
|
-
// instrText must be inside a field (depth > 0) and before the separator
|
|
271
|
-
if (depth === 0 || pastSeparatorAtDepth[depth])
|
|
272
|
-
return false;
|
|
248
|
+
export function splitStories(documentXml, footnotesXmls, endnotesXmls) {
|
|
249
|
+
const stories = [{ label: 'document', xml: documentXml }];
|
|
250
|
+
const collectEntries = (sidecars, entryTag, labelPrefix) => {
|
|
251
|
+
for (let s = 0; s < sidecars.length; s++) {
|
|
252
|
+
const sidecarXml = sidecars[s];
|
|
253
|
+
if (!sidecarXml)
|
|
254
|
+
continue;
|
|
255
|
+
const doc = parseXml(sidecarXml);
|
|
256
|
+
const entries = doc.getElementsByTagName(entryTag);
|
|
257
|
+
for (let i = 0; i < entries.length; i++) {
|
|
258
|
+
const entry = entries[i];
|
|
259
|
+
const id = entry.getAttribute('w:id') ?? String(i);
|
|
260
|
+
stories.push({
|
|
261
|
+
label: `${labelPrefix}[${s}]:${id}`,
|
|
262
|
+
xml: serializer.serializeToString(entry),
|
|
263
|
+
});
|
|
273
264
|
}
|
|
274
|
-
if (!scan(el))
|
|
275
|
-
return false;
|
|
276
265
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
266
|
+
};
|
|
267
|
+
collectEntries(footnotesXmls, 'w:footnote', 'footnote');
|
|
268
|
+
collectEntries(endnotesXmls, 'w:endnote', 'endnote');
|
|
269
|
+
return stories;
|
|
280
270
|
}
|
|
281
|
-
function evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip, originalBookmarkDiagnostics, revisedBookmarkDiagnostics, candidateXml) {
|
|
271
|
+
function evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip, originalBookmarkDiagnostics, revisedBookmarkDiagnostics, candidateXml, auxiliarySidecars) {
|
|
282
272
|
const acceptedXml = acceptAllChanges(candidateXml);
|
|
283
273
|
const rejectedXml = rejectAllChanges(candidateXml);
|
|
284
274
|
const acceptedText = extractTextWithParagraphs(acceptedXml);
|
|
@@ -289,11 +279,28 @@ function evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip,
|
|
|
289
279
|
const rejectTextComparison = compareTexts(originalTextForRoundTrip, rejectedText);
|
|
290
280
|
const acceptBookmarksOk = bookmarkDiagnosticsSemanticallyEqual(revisedBookmarkDiagnostics, acceptedBookmarkDiagnostics);
|
|
291
281
|
const rejectBookmarksOk = bookmarkDiagnosticsSemanticallyEqual(originalBookmarkDiagnostics, rejectedBookmarkDiagnostics);
|
|
292
|
-
// Validate field structure
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
|
|
282
|
+
// Validate field structure per-story. Each footnote/endnote entry is its own
|
|
283
|
+
// ECMA-376 story; a complex field that crosses a story boundary breaks
|
|
284
|
+
// Word's field state machine even when global begin/end counts balance.
|
|
285
|
+
// Sidecars from BOTH archives are validated because Step 12's auxiliary-part
|
|
286
|
+
// merge picks its base and source archives by reconstruction mode (inplace
|
|
287
|
+
// base = revised; rebuild base = original) and validating only one side
|
|
288
|
+
// would miss field issues that would still ship in the merged result.
|
|
289
|
+
// `acceptAllChanges` / `rejectAllChanges` only transform document.xml, so
|
|
290
|
+
// the sidecar set is identical for both transforms.
|
|
291
|
+
const acceptedStories = splitStories(acceptedXml, auxiliarySidecars.footnotesXmls, auxiliarySidecars.endnotesXmls);
|
|
292
|
+
const rejectedStories = splitStories(rejectedXml, auxiliarySidecars.footnotesXmls, auxiliarySidecars.endnotesXmls);
|
|
293
|
+
// Issue #217 conformance gate on the COMBINED output: w:fldChar MUST NOT
|
|
294
|
+
// appear inside <w:del>. ECMA-376 Part 4 § 17.16.5 makes this fatal for
|
|
295
|
+
// Word's field state machine. The full validateFieldStructure check is run
|
|
296
|
+
// on the accept/reject projections (per-story); on the combined view we
|
|
297
|
+
// only gate the strict no-fldChar-in-del rule because some legacy emit
|
|
298
|
+
// paths (e.g. delInstrText inside <w:moveFrom>) are non-conformant in shape
|
|
299
|
+
// but out of scope for #217.
|
|
300
|
+
const combinedNoFldCharInDel = !hasFldCharInsideDel(candidateXml);
|
|
301
|
+
const fieldStructureOk = combinedNoFldCharInDel &&
|
|
302
|
+
validateFieldStructure(acceptedStories) &&
|
|
303
|
+
validateFieldStructure(rejectedStories);
|
|
297
304
|
const checks = {
|
|
298
305
|
acceptText: acceptTextComparison.normalizedIdentical,
|
|
299
306
|
rejectText: rejectTextComparison.normalizedIdentical,
|
|
@@ -369,12 +376,36 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
369
376
|
// Step 1: Load DOCX archives
|
|
370
377
|
const originalArchive = await DocxArchive.load(original);
|
|
371
378
|
const revisedArchive = await DocxArchive.load(revised);
|
|
379
|
+
// Step 1b: Resolve auxiliary ID collisions. When both sides define
|
|
380
|
+
// different content under the same comment/footnote/endnote w:id or the
|
|
381
|
+
// same comment paraId, rewrite the revised side so no anchor or ancillary
|
|
382
|
+
// row in the merged output can bind to the other document's definition.
|
|
383
|
+
// Must run before any document.xml extraction so every downstream step sees
|
|
384
|
+
// the rewritten archive.
|
|
385
|
+
await renumberCollidingAuxiliaryIds(originalArchive, revisedArchive);
|
|
386
|
+
await restampCollidingCommentParaIds(originalArchive, revisedArchive);
|
|
372
387
|
// Step 2: Extract document.xml
|
|
373
388
|
const originalXml = await originalArchive.getDocumentXml();
|
|
374
389
|
const revisedXml = await revisedArchive.getDocumentXml();
|
|
375
390
|
// Extract numbering.xml if available
|
|
376
391
|
const originalNumberingXml = await originalArchive.getNumberingXml() ?? undefined;
|
|
377
392
|
const revisedNumberingXml = await revisedArchive.getNumberingXml() ?? undefined;
|
|
393
|
+
// Extract footnote/endnote sidecars from BOTH archives for per-story
|
|
394
|
+
// field-closure validation (issue #212). Step 12 picks the base archive by
|
|
395
|
+
// reconstruction mode (inplace = revised, rebuild = original) and merges
|
|
396
|
+
// missing referenced entries from the opposite archive. Validating both
|
|
397
|
+
// archives' sidecars covers the union of entries that could ship without
|
|
398
|
+
// having to duplicate the merge logic at safety-check time.
|
|
399
|
+
const [originalFootnotesXml, originalEndnotesXml, revisedFootnotesXml, revisedEndnotesXml,] = await Promise.all([
|
|
400
|
+
originalArchive.getFile('word/footnotes.xml'),
|
|
401
|
+
originalArchive.getFile('word/endnotes.xml'),
|
|
402
|
+
revisedArchive.getFile('word/footnotes.xml'),
|
|
403
|
+
revisedArchive.getFile('word/endnotes.xml'),
|
|
404
|
+
]);
|
|
405
|
+
const auxiliarySidecars = {
|
|
406
|
+
footnotesXmls: [originalFootnotesXml, revisedFootnotesXml],
|
|
407
|
+
endnotesXmls: [originalEndnotesXml, revisedEndnotesXml],
|
|
408
|
+
};
|
|
378
409
|
const originalPart = {
|
|
379
410
|
uri: 'word/document.xml',
|
|
380
411
|
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',
|
|
@@ -383,8 +414,13 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
383
414
|
uri: 'word/document.xml',
|
|
384
415
|
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',
|
|
385
416
|
};
|
|
386
|
-
|
|
387
|
-
|
|
417
|
+
// Project each input through the SAME accept/reject operation the candidate is
|
|
418
|
+
// checked under, so the round-trip comparison is like-for-like even when an
|
|
419
|
+
// input already carries its own tracked changes (pre-tracked w:ins / w:del,
|
|
420
|
+
// comment anchors, multi-author stacks). For a clean input these equal the raw
|
|
421
|
+
// extraction, so behavior on the common case is unchanged. (#347)
|
|
422
|
+
const originalTextForRoundTrip = extractTextWithParagraphs(rejectAllChanges(originalXml));
|
|
423
|
+
const revisedTextForRoundTrip = extractTextWithParagraphs(acceptAllChanges(revisedXml));
|
|
388
424
|
const originalBookmarkDiagnostics = collectBookmarkDiagnostics(originalXml);
|
|
389
425
|
const revisedBookmarkDiagnostics = collectBookmarkDiagnostics(revisedXml);
|
|
390
426
|
const runComparisonPass = (atomizeOptions, outputMode) => {
|
|
@@ -445,7 +481,7 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
445
481
|
}
|
|
446
482
|
return { mergedAtoms, newDocumentXml, outputMode };
|
|
447
483
|
};
|
|
448
|
-
const evaluateRoundTripSafety = (candidateXml) => evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip, originalBookmarkDiagnostics, revisedBookmarkDiagnostics, candidateXml);
|
|
484
|
+
const evaluateRoundTripSafety = (candidateXml) => evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip, originalBookmarkDiagnostics, revisedBookmarkDiagnostics, candidateXml, auxiliarySidecars);
|
|
449
485
|
let comparisonResult;
|
|
450
486
|
let fallbackReason;
|
|
451
487
|
let fallbackDiagnostics;
|
|
@@ -540,6 +576,23 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
540
576
|
else {
|
|
541
577
|
comparisonResult = runComparisonPass({ atomizeParagraphLevelMarkers: true }, 'rebuild');
|
|
542
578
|
}
|
|
579
|
+
// Rebuild output gets the same safety screening as inplace attempts, whether
|
|
580
|
+
// rebuild was requested directly or reached via inplace fallback. Rebuild is
|
|
581
|
+
// the terminal strategy, so failures are surfaced in diagnostics rather than
|
|
582
|
+
// blocking the output.
|
|
583
|
+
// @see https://github.com/UseJunior/safe-docx/issues/226
|
|
584
|
+
let rebuildSafetyDiagnostics;
|
|
585
|
+
if (comparisonResult.outputMode === 'rebuild') {
|
|
586
|
+
const safety = evaluateRoundTripSafety(comparisonResult.newDocumentXml);
|
|
587
|
+
if (!safety.safe) {
|
|
588
|
+
rebuildSafetyDiagnostics = {
|
|
589
|
+
checks: safety.checks,
|
|
590
|
+
failedChecks: safety.failedChecks,
|
|
591
|
+
failureDetails: safety.failureDetails,
|
|
592
|
+
firstDiffSummary: safety.failureSummary,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
543
596
|
const { mergedAtoms, newDocumentXml } = comparisonResult;
|
|
544
597
|
// Step 12: Clone appropriate archive and update document.xml.
|
|
545
598
|
// Use the revised archive only for true inplace output.
|
|
@@ -551,6 +604,7 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
551
604
|
// auxiliary part that the revised side introduced (issue #94).
|
|
552
605
|
const mergeSourceArchive = comparisonResult.outputMode === 'inplace' ? originalArchive : revisedArchive;
|
|
553
606
|
const resultArchive = await baseArchive.clone();
|
|
607
|
+
maybeCaptureEmittedDocumentXml(newDocumentXml);
|
|
554
608
|
resultArchive.setDocumentXml(newDocumentXml);
|
|
555
609
|
// Step 12b: Merge auxiliary part definitions (footnotes, endnotes, comments).
|
|
556
610
|
// Reconstruction may insert content (deleted in inplace, added in rebuild)
|
|
@@ -562,13 +616,14 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
562
616
|
// Gated on root comment IDs in the *result* document (not on what the
|
|
563
617
|
// generic merge appended), so the pass runs even when the original already
|
|
564
618
|
// contains the root and revised only adds replies under it (issue #108).
|
|
565
|
-
|
|
619
|
+
// Comments anchored on footnote/endnote text count as roots too.
|
|
620
|
+
const rootCommentIds = await collectStoryReferenceIds(resultArchive, newDocumentXml, 'w:commentReference', null);
|
|
566
621
|
if (rootCommentIds.size > 0) {
|
|
567
622
|
await mergeCommentAncillaryParts(mergeSourceArchive, resultArchive, rootCommentIds);
|
|
568
623
|
}
|
|
569
624
|
// Step 13: Save result and compute stats
|
|
570
625
|
const resultBuffer = await resultArchive.save();
|
|
571
|
-
const stats =
|
|
626
|
+
const stats = computeAtomizerStats(mergedAtoms);
|
|
572
627
|
return {
|
|
573
628
|
document: resultBuffer,
|
|
574
629
|
stats,
|
|
@@ -577,37 +632,29 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
577
632
|
reconstructionModeUsed: comparisonResult.outputMode,
|
|
578
633
|
fallbackReason,
|
|
579
634
|
fallbackDiagnostics,
|
|
635
|
+
rebuildSafetyDiagnostics,
|
|
580
636
|
};
|
|
581
637
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
label: 'comment',
|
|
603
|
-
partPath: 'word/comments.xml',
|
|
604
|
-
referenceTag: 'w:commentReference',
|
|
605
|
-
entryTag: 'w:comment',
|
|
606
|
-
rootTag: 'w:comments',
|
|
607
|
-
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml',
|
|
608
|
-
relationshipType: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
|
|
609
|
-
},
|
|
610
|
-
];
|
|
638
|
+
/**
|
|
639
|
+
* Collect reference IDs across every result story that can host anchors: the
|
|
640
|
+
* merged document.xml plus the result archive's footnote/endnote parts (Word
|
|
641
|
+
* allows comments anchored on note text). `excludePartPath` skips the part
|
|
642
|
+
* whose own definitions are being merged — entries can't reference
|
|
643
|
+
* themselves.
|
|
644
|
+
*/
|
|
645
|
+
async function collectStoryReferenceIds(resultArchive, documentXml, referenceTag, excludePartPath) {
|
|
646
|
+
const ids = collectReferenceIds(documentXml, referenceTag);
|
|
647
|
+
for (const storyPath of ['word/footnotes.xml', 'word/endnotes.xml']) {
|
|
648
|
+
if (storyPath === excludePartPath)
|
|
649
|
+
continue;
|
|
650
|
+
const storyXml = await resultArchive.getFile(storyPath);
|
|
651
|
+
if (!storyXml)
|
|
652
|
+
continue;
|
|
653
|
+
for (const id of collectReferenceIds(storyXml, referenceTag))
|
|
654
|
+
ids.add(id);
|
|
655
|
+
}
|
|
656
|
+
return ids;
|
|
657
|
+
}
|
|
611
658
|
/**
|
|
612
659
|
* Collect reference IDs from document.xml using DOM parsing.
|
|
613
660
|
*/
|
|
@@ -622,22 +669,6 @@ function collectReferenceIds(documentXml, referenceTag) {
|
|
|
622
669
|
}
|
|
623
670
|
return ids;
|
|
624
671
|
}
|
|
625
|
-
/**
|
|
626
|
-
* Parse an auxiliary part and extract entry elements by ID.
|
|
627
|
-
*/
|
|
628
|
-
function parseEntries(xml, entryTag) {
|
|
629
|
-
const doc = parseXml(xml);
|
|
630
|
-
const entries = new Map();
|
|
631
|
-
const elements = doc.getElementsByTagName(entryTag);
|
|
632
|
-
for (let i = 0; i < elements.length; i++) {
|
|
633
|
-
const el = elements[i];
|
|
634
|
-
const id = el.getAttribute('w:id');
|
|
635
|
-
if (id)
|
|
636
|
-
entries.set(id, el);
|
|
637
|
-
}
|
|
638
|
-
return { doc, entries };
|
|
639
|
-
}
|
|
640
|
-
const serializer = new XMLSerializer();
|
|
641
672
|
/**
|
|
642
673
|
* Merge auxiliary part definitions (footnotes, endnotes, comments) from the
|
|
643
674
|
* source archive into the result archive. The source archive is whichever
|
|
@@ -647,7 +678,11 @@ const serializer = new XMLSerializer();
|
|
|
647
678
|
*/
|
|
648
679
|
async function mergeAuxiliaryPartDefinitions(sourceArchive, resultArchive, documentXml, descriptor) {
|
|
649
680
|
const result = { mergedIds: new Set(), createdPart: false };
|
|
650
|
-
|
|
681
|
+
// Anchors may live in the merged body or on note text in the result's
|
|
682
|
+
// footnote/endnote stories. AUXILIARY_PARTS merges notes before comments,
|
|
683
|
+
// so by the comment pass the note stories already carry any merged-in
|
|
684
|
+
// comment anchors.
|
|
685
|
+
const referencedIds = await collectStoryReferenceIds(resultArchive, documentXml, descriptor.referenceTag, descriptor.partPath);
|
|
651
686
|
if (referencedIds.size === 0)
|
|
652
687
|
return result;
|
|
653
688
|
const sourcePartXml = await sourceArchive.getFile(descriptor.partPath);
|
|
@@ -977,6 +1012,7 @@ const COMMENTS_EXTENDED_DESCRIPTOR = {
|
|
|
977
1012
|
rootTag: 'w15:commentsEx',
|
|
978
1013
|
contentType: 'application/vnd.ms-word.commentsExtended+xml',
|
|
979
1014
|
relationshipType: 'http://schemas.microsoft.com/office/2011/relationships/commentsExtended',
|
|
1015
|
+
idBearingTags: [], // keyed by w15:paraId, not w:id
|
|
980
1016
|
};
|
|
981
1017
|
const PEOPLE_DESCRIPTOR = {
|
|
982
1018
|
label: 'people',
|
|
@@ -986,6 +1022,7 @@ const PEOPLE_DESCRIPTOR = {
|
|
|
986
1022
|
rootTag: 'w15:people',
|
|
987
1023
|
contentType: 'application/vnd.ms-word.people+xml',
|
|
988
1024
|
relationshipType: 'http://schemas.microsoft.com/office/2011/relationships/people',
|
|
1025
|
+
idBearingTags: [], // keyed by w15:author, not w:id
|
|
989
1026
|
};
|
|
990
1027
|
async function mergePeople(sourceArchive, resultArchive, mergedAuthors) {
|
|
991
1028
|
if (mergedAuthors.size === 0)
|
|
@@ -1042,47 +1079,82 @@ async function mergePeople(sourceArchive, resultArchive, mergedAuthors) {
|
|
|
1042
1079
|
resultArchive.setFile('word/people.xml', serializer.serializeToString(newDoc));
|
|
1043
1080
|
await ensureOpcMetadata(resultArchive, PEOPLE_DESCRIPTOR);
|
|
1044
1081
|
}
|
|
1082
|
+
const fallbackParagraphStatsKeys = new WeakMap();
|
|
1083
|
+
let nextFallbackParagraphStatsKey = 0;
|
|
1084
|
+
function paragraphStatsKey(atom) {
|
|
1085
|
+
if (atom.paragraphIndex !== undefined) {
|
|
1086
|
+
return `${atom.part.uri}:${atom.paragraphIndex}`;
|
|
1087
|
+
}
|
|
1088
|
+
const pAncestor = atom.ancestorElements.find((a) => a.tagName === 'w:p');
|
|
1089
|
+
if (!pAncestor)
|
|
1090
|
+
return undefined;
|
|
1091
|
+
let key = fallbackParagraphStatsKeys.get(pAncestor);
|
|
1092
|
+
if (!key) {
|
|
1093
|
+
key = `${atom.part.uri}:paragraph-ref:${nextFallbackParagraphStatsKey++}`;
|
|
1094
|
+
fallbackParagraphStatsKeys.set(pAncestor, key);
|
|
1095
|
+
}
|
|
1096
|
+
return key;
|
|
1097
|
+
}
|
|
1045
1098
|
/**
|
|
1046
1099
|
* Compute comparison statistics from merged atoms.
|
|
1100
|
+
*
|
|
1101
|
+
* Range counts are contiguous same-status runs in the merged atom stream, scoped
|
|
1102
|
+
* to a paragraph. Atom counts remain available under explicit names for callers
|
|
1103
|
+
* that need the old granular benchmark signal.
|
|
1047
1104
|
*/
|
|
1048
|
-
function
|
|
1105
|
+
export function computeAtomizerStats(mergedAtoms) {
|
|
1049
1106
|
const reconstructionStats = computeReconstructionStats(mergedAtoms);
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
let
|
|
1054
|
-
let
|
|
1055
|
-
|
|
1107
|
+
let insertedRanges = 0;
|
|
1108
|
+
let deletedRanges = 0;
|
|
1109
|
+
let formatChanges = 0;
|
|
1110
|
+
let previousRangeStatus = null;
|
|
1111
|
+
let previousRangeParagraph;
|
|
1112
|
+
const paragraphs = new Map();
|
|
1056
1113
|
for (const atom of mergedAtoms) {
|
|
1057
|
-
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1114
|
+
const paragraphKey = paragraphStatsKey(atom);
|
|
1115
|
+
const status = atom.correlationStatus;
|
|
1116
|
+
const rangeStatus = status === CorrelationStatus.Inserted ||
|
|
1117
|
+
status === CorrelationStatus.Deleted ||
|
|
1118
|
+
status === CorrelationStatus.FormatChanged
|
|
1119
|
+
? status
|
|
1120
|
+
: null;
|
|
1121
|
+
if (rangeStatus) {
|
|
1122
|
+
if (rangeStatus !== previousRangeStatus || paragraphKey !== previousRangeParagraph) {
|
|
1123
|
+
if (rangeStatus === CorrelationStatus.Inserted)
|
|
1124
|
+
insertedRanges++;
|
|
1125
|
+
if (rangeStatus === CorrelationStatus.Deleted)
|
|
1126
|
+
deletedRanges++;
|
|
1127
|
+
if (rangeStatus === CorrelationStatus.FormatChanged)
|
|
1128
|
+
formatChanges++;
|
|
1066
1129
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
hasInserted = false;
|
|
1130
|
+
previousRangeStatus = rangeStatus;
|
|
1131
|
+
previousRangeParagraph = paragraphKey;
|
|
1070
1132
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1133
|
+
else {
|
|
1134
|
+
previousRangeStatus = null;
|
|
1135
|
+
previousRangeParagraph = undefined;
|
|
1073
1136
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1137
|
+
if (paragraphKey && (status === CorrelationStatus.Deleted || status === CorrelationStatus.Inserted)) {
|
|
1138
|
+
const flags = paragraphs.get(paragraphKey) ?? { hasDeleted: false, hasInserted: false };
|
|
1139
|
+
if (status === CorrelationStatus.Deleted)
|
|
1140
|
+
flags.hasDeleted = true;
|
|
1141
|
+
if (status === CorrelationStatus.Inserted)
|
|
1142
|
+
flags.hasInserted = true;
|
|
1143
|
+
paragraphs.set(paragraphKey, flags);
|
|
1076
1144
|
}
|
|
1077
1145
|
}
|
|
1078
|
-
|
|
1079
|
-
if (currentParagraph && hasDeleted && hasInserted) {
|
|
1080
|
-
modifiedParagraphs.add(currentParagraph);
|
|
1081
|
-
}
|
|
1146
|
+
const modifiedParagraphs = Array.from(paragraphs.values()).filter((flags) => flags.hasDeleted && flags.hasInserted).length;
|
|
1082
1147
|
return {
|
|
1083
|
-
insertions:
|
|
1084
|
-
deletions:
|
|
1085
|
-
modifications: modifiedParagraphs
|
|
1148
|
+
insertions: insertedRanges,
|
|
1149
|
+
deletions: deletedRanges,
|
|
1150
|
+
modifications: modifiedParagraphs,
|
|
1151
|
+
insertedRanges,
|
|
1152
|
+
deletedRanges,
|
|
1153
|
+
insertedAtoms: reconstructionStats.insertions,
|
|
1154
|
+
deletedAtoms: reconstructionStats.deletions,
|
|
1155
|
+
modifiedParagraphs,
|
|
1156
|
+
formatChanges,
|
|
1157
|
+
formatChangeAtoms: reconstructionStats.formatChanges,
|
|
1086
1158
|
};
|
|
1087
1159
|
}
|
|
1088
1160
|
//# sourceMappingURL=pipeline.js.map
|