@usejunior/docx-core 0.10.0 → 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 +13 -7
- package/dist/atomizer.d.ts.map +1 -1
- package/dist/atomizer.js +59 -24
- 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 +120 -27
- package/dist/baselines/atomizer/documentReconstructor.js.map +1 -1
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts +9 -0
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts.map +1 -1
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.js +52 -4
- package/dist/baselines/atomizer/inPlaceModifier-wrappers.js.map +1 -1
- package/dist/baselines/atomizer/pipeline.d.ts +12 -53
- package/dist/baselines/atomizer/pipeline.d.ts.map +1 -1
- package/dist/baselines/atomizer/pipeline.js +126 -231
- 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 +153 -23
- 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 +46 -0
- package/dist/compare-types.d.ts.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 +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -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 +8 -0
- package/dist/integration/libreoffice-oracle.d.ts.map +1 -1
- package/dist/integration/libreoffice-oracle.js +14 -6
- package/dist/integration/libreoffice-oracle.js.map +1 -1
- 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 +2 -1
- package/dist/primitives/accept_changes.d.ts.map +1 -1
- package/dist/primitives/accept_changes.js +153 -12
- package/dist/primitives/accept_changes.js.map +1 -1
- package/dist/primitives/document.d.ts +38 -0
- package/dist/primitives/document.d.ts.map +1 -1
- package/dist/primitives/document.js +75 -9
- package/dist/primitives/document.js.map +1 -1
- package/dist/primitives/document_view-comments.d.ts.map +1 -1
- package/dist/primitives/document_view-comments.js +4 -3
- package/dist/primitives/document_view-comments.js.map +1 -1
- package/dist/primitives/document_view-types.d.ts +15 -0
- package/dist/primitives/document_view-types.d.ts.map +1 -1
- package/dist/primitives/document_view.d.ts.map +1 -1
- package/dist/primitives/document_view.js +21 -13
- package/dist/primitives/document_view.js.map +1 -1
- package/dist/primitives/formatting_tags.d.ts +1 -0
- package/dist/primitives/formatting_tags.d.ts.map +1 -1
- package/dist/primitives/formatting_tags.js +16 -10
- package/dist/primitives/formatting_tags.js.map +1 -1
- package/dist/primitives/index.d.ts +4 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +4 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/layout.d.ts.map +1 -1
- package/dist/primitives/layout.js +13 -0
- package/dist/primitives/layout.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 +41 -0
- package/dist/primitives/namespaces.d.ts.map +1 -1
- package/dist/primitives/namespaces.js +43 -0
- package/dist/primitives/namespaces.js.map +1 -1
- package/dist/primitives/reject_changes.d.ts +4 -2
- package/dist/primitives/reject_changes.d.ts.map +1 -1
- package/dist/primitives/reject_changes.js +177 -24
- 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.map +1 -1
- package/dist/primitives/semantic_tags.js +2 -1
- package/dist/primitives/semantic_tags.js.map +1 -1
- package/dist/primitives/serialize_html.d.ts +1 -0
- package/dist/primitives/serialize_html.d.ts.map +1 -1
- package/dist/primitives/serialize_html.js +4 -2
- package/dist/primitives/serialize_html.js.map +1 -1
- package/dist/primitives/styles.d.ts +15 -0
- package/dist/primitives/styles.d.ts.map +1 -1
- package/dist/primitives/styles.js +11 -0
- package/dist/primitives/styles.js.map +1 -1
- package/dist/primitives/track-changes-emitter.d.ts +9 -0
- package/dist/primitives/track-changes-emitter.d.ts.map +1 -1
- package/dist/primitives/track-changes-emitter.js +54 -4
- package/dist/primitives/track-changes-emitter.js.map +1 -1
- 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.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/package.json +7 -4
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* Integrates atomization, LCS comparison, move detection, format detection,
|
|
6
6
|
* and document reconstruction.
|
|
7
7
|
*/
|
|
8
|
-
import type { CompareResult, ReconstructionMode } from '../../compare-types.js';
|
|
9
|
-
import type { MoveDetectionSettings, FormatDetectionSettings } from '../../core-types.js';
|
|
8
|
+
import type { CompareResult, CompareStats, ReconstructionMode } from '../../compare-types.js';
|
|
9
|
+
import type { ComparisonUnitAtom, MoveDetectionSettings, FormatDetectionSettings } from '../../core-types.js';
|
|
10
10
|
import { type NumberingIntegrationOptions } from './numberingIntegration.js';
|
|
11
|
+
export { hasFldCharInsideDel, validateFieldStructure, type FieldStory, } from '../../shared/field-structure.js';
|
|
12
|
+
import { type FieldStory } from '../../shared/field-structure.js';
|
|
11
13
|
/**
|
|
12
14
|
* Options for the atomizer pipeline.
|
|
13
15
|
*/
|
|
@@ -40,18 +42,6 @@ export interface AtomizerOptions {
|
|
|
40
42
|
*/
|
|
41
43
|
reconstructionMode?: ReconstructionMode;
|
|
42
44
|
}
|
|
43
|
-
/**
|
|
44
|
-
* One story (a self-contained complex-field state machine): the main document
|
|
45
|
-
* body, an individual footnote entry, or an individual endnote entry. `label`
|
|
46
|
-
* is for diagnostics only; `xml` is the serialized fragment that gets parsed
|
|
47
|
-
* and walked.
|
|
48
|
-
*
|
|
49
|
-
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
50
|
-
*/
|
|
51
|
-
export interface FieldStory {
|
|
52
|
-
label: string;
|
|
53
|
-
xml: string;
|
|
54
|
-
}
|
|
55
45
|
/**
|
|
56
46
|
* Split a docx into per-story XML fragments for field-closure validation.
|
|
57
47
|
*
|
|
@@ -76,45 +66,6 @@ export interface FieldStory {
|
|
|
76
66
|
* @see https://github.com/UseJunior/safe-docx/issues/212
|
|
77
67
|
*/
|
|
78
68
|
export declare function splitStories(documentXml: string, footnotesXmls: ReadonlyArray<string | null>, endnotesXmls: ReadonlyArray<string | null>): FieldStory[];
|
|
79
|
-
/**
|
|
80
|
-
* Validate field structure integrity across one or more document stories.
|
|
81
|
-
*
|
|
82
|
-
* Enforces three constraints on complex fields **per story**:
|
|
83
|
-
* 1. `w:fldChar` begin/end count balance within the story.
|
|
84
|
-
* 2. Every `w:instrText` AND `w:delInstrText` sits inside an open field body
|
|
85
|
-
* (between `begin` and `separate`). Orphaned instruction text renders as
|
|
86
|
-
* literal text in Word.
|
|
87
|
-
* 3. `w:delInstrText` is nested inside a `<w:del>` ancestor (DeletedFieldCode
|
|
88
|
-
* schema constraint), and conversely `w:fldChar` is NEVER inside `<w:del>`
|
|
89
|
-
* (Word treats this as fatal and discards the field state machine).
|
|
90
|
-
*
|
|
91
|
-
* Called on both pre-accept/reject combined XML (with track-change wrappers)
|
|
92
|
-
* and on post-accept/reject XML (wrappers removed). Both cases must satisfy the
|
|
93
|
-
* field placement check; constraint (3) is vacuous post-accept/reject.
|
|
94
|
-
*
|
|
95
|
-
* Accepts either a single XML string (legacy single-story call) or an array of
|
|
96
|
-
* `FieldStory` fragments. Stories are validated independently and short-circuit
|
|
97
|
-
* on the first failure.
|
|
98
|
-
*
|
|
99
|
-
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
100
|
-
*/
|
|
101
|
-
/**
|
|
102
|
-
* Targeted check for one of the constraints above: `w:fldChar` MUST NOT appear
|
|
103
|
-
* inside any `<w:del>` element. Word treats this violation as fatal — the
|
|
104
|
-
* field state machine is discarded and the field renders as literal-text
|
|
105
|
-
* fallback.
|
|
106
|
-
*
|
|
107
|
-
* Used as a combined-output safety gate alongside the per-projection
|
|
108
|
-
* `validateFieldStructure` checks. Kept narrower than the full structural
|
|
109
|
-
* validation so that legacy shapes (e.g. `delInstrText` inside `<w:moveFrom>`)
|
|
110
|
-
* don't trigger fallback when the inplace candidate is otherwise sound on its
|
|
111
|
-
* accept/reject projections.
|
|
112
|
-
*
|
|
113
|
-
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
114
|
-
* @see https://github.com/UseJunior/safe-docx/issues/217
|
|
115
|
-
*/
|
|
116
|
-
export declare function hasFldCharInsideDel(documentXml: string): boolean;
|
|
117
|
-
export declare function validateFieldStructure(input: string | FieldStory[]): boolean;
|
|
118
69
|
/**
|
|
119
70
|
* Compare two DOCX documents using the atomizer-based approach.
|
|
120
71
|
*
|
|
@@ -141,4 +92,12 @@ export interface AuxiliaryMergeResult {
|
|
|
141
92
|
mergedIds: Set<string>;
|
|
142
93
|
createdPart: boolean;
|
|
143
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Compute comparison statistics from merged atoms.
|
|
97
|
+
*
|
|
98
|
+
* Range counts are contiguous same-status runs in the merged atom stream, scoped
|
|
99
|
+
* to a paragraph. Atom counts remain available under explicit names for callers
|
|
100
|
+
* that need the old granular benchmark signal.
|
|
101
|
+
*/
|
|
102
|
+
export declare function computeAtomizerStats(mergedAtoms: ComparisonUnitAtom[]): CompareStats;
|
|
144
103
|
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../src/baselines/atomizer/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACV,aAAa,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../src/baselines/atomizer/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAeZ,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EAExB,MAAM,qBAAqB,CAAC;AAkC7B,OAAO,EAEL,KAAK,2BAA2B,EAEjC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,UAAU,GAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AAUzC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,8BAA8B;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/C,gCAAgC;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACnD,qCAAqC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACjD;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAkPD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,EAC3C,YAAY,EAAE,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,GACzC,UAAU,EAAE,CA4Bd;AAqHD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAwWxB;AAYD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;AAwgBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,YAAY,CAyDpF"}
|
|
@@ -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;
|
|
@@ -264,160 +268,6 @@ export function splitStories(documentXml, footnotesXmls, endnotesXmls) {
|
|
|
264
268
|
collectEntries(endnotesXmls, 'w:endnote', 'endnote');
|
|
265
269
|
return stories;
|
|
266
270
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Validate field structure integrity across one or more document stories.
|
|
269
|
-
*
|
|
270
|
-
* Enforces three constraints on complex fields **per story**:
|
|
271
|
-
* 1. `w:fldChar` begin/end count balance within the story.
|
|
272
|
-
* 2. Every `w:instrText` AND `w:delInstrText` sits inside an open field body
|
|
273
|
-
* (between `begin` and `separate`). Orphaned instruction text renders as
|
|
274
|
-
* literal text in Word.
|
|
275
|
-
* 3. `w:delInstrText` is nested inside a `<w:del>` ancestor (DeletedFieldCode
|
|
276
|
-
* schema constraint), and conversely `w:fldChar` is NEVER inside `<w:del>`
|
|
277
|
-
* (Word treats this as fatal and discards the field state machine).
|
|
278
|
-
*
|
|
279
|
-
* Called on both pre-accept/reject combined XML (with track-change wrappers)
|
|
280
|
-
* and on post-accept/reject XML (wrappers removed). Both cases must satisfy the
|
|
281
|
-
* field placement check; constraint (3) is vacuous post-accept/reject.
|
|
282
|
-
*
|
|
283
|
-
* Accepts either a single XML string (legacy single-story call) or an array of
|
|
284
|
-
* `FieldStory` fragments. Stories are validated independently and short-circuit
|
|
285
|
-
* on the first failure.
|
|
286
|
-
*
|
|
287
|
-
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
288
|
-
*/
|
|
289
|
-
/**
|
|
290
|
-
* Targeted check for one of the constraints above: `w:fldChar` MUST NOT appear
|
|
291
|
-
* inside any `<w:del>` element. Word treats this violation as fatal — the
|
|
292
|
-
* field state machine is discarded and the field renders as literal-text
|
|
293
|
-
* fallback.
|
|
294
|
-
*
|
|
295
|
-
* Used as a combined-output safety gate alongside the per-projection
|
|
296
|
-
* `validateFieldStructure` checks. Kept narrower than the full structural
|
|
297
|
-
* validation so that legacy shapes (e.g. `delInstrText` inside `<w:moveFrom>`)
|
|
298
|
-
* don't trigger fallback when the inplace candidate is otherwise sound on its
|
|
299
|
-
* accept/reject projections.
|
|
300
|
-
*
|
|
301
|
-
* @conformance ECMA-376 edition 5, Part 4 § 17.16.5
|
|
302
|
-
* @see https://github.com/UseJunior/safe-docx/issues/217
|
|
303
|
-
*/
|
|
304
|
-
export function hasFldCharInsideDel(documentXml) {
|
|
305
|
-
const root = parseDocumentXml(documentXml);
|
|
306
|
-
let insideDelDepth = 0;
|
|
307
|
-
let violation = false;
|
|
308
|
-
function scan(node) {
|
|
309
|
-
if (violation)
|
|
310
|
-
return;
|
|
311
|
-
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
312
|
-
if (child.nodeType !== 1)
|
|
313
|
-
continue;
|
|
314
|
-
const el = child;
|
|
315
|
-
const tag = el.tagName;
|
|
316
|
-
if (tag === 'w:del') {
|
|
317
|
-
insideDelDepth++;
|
|
318
|
-
scan(el);
|
|
319
|
-
insideDelDepth--;
|
|
320
|
-
if (violation)
|
|
321
|
-
return;
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
if (tag === 'w:fldChar' && insideDelDepth > 0) {
|
|
325
|
-
violation = true;
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
scan(el);
|
|
329
|
-
if (violation)
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
scan(root);
|
|
334
|
-
return violation;
|
|
335
|
-
}
|
|
336
|
-
export function validateFieldStructure(input) {
|
|
337
|
-
if (typeof input === 'string') {
|
|
338
|
-
return validateFieldStructureForStory(input);
|
|
339
|
-
}
|
|
340
|
-
for (const story of input) {
|
|
341
|
-
if (!validateFieldStructureForStory(story.xml))
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
function validateFieldStructureForStory(documentXml) {
|
|
347
|
-
const root = parseDocumentXml(documentXml);
|
|
348
|
-
const allFldChars = findAllByTagName(root, 'w:fldChar');
|
|
349
|
-
const allInstrTexts = findAllByTagName(root, 'w:instrText');
|
|
350
|
-
const allDelInstrTexts = findAllByTagName(root, 'w:delInstrText');
|
|
351
|
-
// Constraint (1): global fldChar begin/end balance.
|
|
352
|
-
let begins = 0;
|
|
353
|
-
let ends = 0;
|
|
354
|
-
for (const fc of allFldChars) {
|
|
355
|
-
const type = fc.getAttribute('w:fldCharType');
|
|
356
|
-
if (type === 'begin')
|
|
357
|
-
begins++;
|
|
358
|
-
else if (type === 'end')
|
|
359
|
-
ends++;
|
|
360
|
-
}
|
|
361
|
-
if (begins !== ends)
|
|
362
|
-
return false;
|
|
363
|
-
if (allFldChars.length === 0 &&
|
|
364
|
-
allInstrTexts.length === 0 &&
|
|
365
|
-
allDelInstrTexts.length === 0) {
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
// Depth-first scan tracking field nesting (for constraint 2) and <w:del>
|
|
369
|
-
// ancestor nesting (for constraint 3).
|
|
370
|
-
let depth = 0;
|
|
371
|
-
const pastSeparatorAtDepth = [];
|
|
372
|
-
let insideDelDepth = 0;
|
|
373
|
-
function scan(node) {
|
|
374
|
-
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
375
|
-
if (child.nodeType !== 1)
|
|
376
|
-
continue;
|
|
377
|
-
const el = child;
|
|
378
|
-
const tag = el.tagName;
|
|
379
|
-
if (tag === 'w:del') {
|
|
380
|
-
insideDelDepth++;
|
|
381
|
-
const ok = scan(el);
|
|
382
|
-
insideDelDepth--;
|
|
383
|
-
if (!ok)
|
|
384
|
-
return false;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (tag === 'w:fldChar') {
|
|
388
|
-
if (insideDelDepth > 0)
|
|
389
|
-
return false;
|
|
390
|
-
const type = el.getAttribute('w:fldCharType');
|
|
391
|
-
if (type === 'begin') {
|
|
392
|
-
depth++;
|
|
393
|
-
pastSeparatorAtDepth[depth] = 0;
|
|
394
|
-
}
|
|
395
|
-
else if (type === 'separate') {
|
|
396
|
-
if (depth > 0)
|
|
397
|
-
pastSeparatorAtDepth[depth] = 1;
|
|
398
|
-
}
|
|
399
|
-
else if (type === 'end') {
|
|
400
|
-
if (depth > 0)
|
|
401
|
-
depth--;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
else if (tag === 'w:instrText') {
|
|
405
|
-
if (depth === 0 || pastSeparatorAtDepth[depth])
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
else if (tag === 'w:delInstrText') {
|
|
409
|
-
if (insideDelDepth === 0)
|
|
410
|
-
return false;
|
|
411
|
-
if (depth === 0 || pastSeparatorAtDepth[depth])
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
if (!scan(el))
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
|
-
return scan(root);
|
|
420
|
-
}
|
|
421
271
|
function evaluateSafetyChecks(originalTextForRoundTrip, revisedTextForRoundTrip, originalBookmarkDiagnostics, revisedBookmarkDiagnostics, candidateXml, auxiliarySidecars) {
|
|
422
272
|
const acceptedXml = acceptAllChanges(candidateXml);
|
|
423
273
|
const rejectedXml = rejectAllChanges(candidateXml);
|
|
@@ -526,6 +376,14 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
526
376
|
// Step 1: Load DOCX archives
|
|
527
377
|
const originalArchive = await DocxArchive.load(original);
|
|
528
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);
|
|
529
387
|
// Step 2: Extract document.xml
|
|
530
388
|
const originalXml = await originalArchive.getDocumentXml();
|
|
531
389
|
const revisedXml = await revisedArchive.getDocumentXml();
|
|
@@ -718,6 +576,23 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
718
576
|
else {
|
|
719
577
|
comparisonResult = runComparisonPass({ atomizeParagraphLevelMarkers: true }, 'rebuild');
|
|
720
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
|
+
}
|
|
721
596
|
const { mergedAtoms, newDocumentXml } = comparisonResult;
|
|
722
597
|
// Step 12: Clone appropriate archive and update document.xml.
|
|
723
598
|
// Use the revised archive only for true inplace output.
|
|
@@ -729,6 +604,7 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
729
604
|
// auxiliary part that the revised side introduced (issue #94).
|
|
730
605
|
const mergeSourceArchive = comparisonResult.outputMode === 'inplace' ? originalArchive : revisedArchive;
|
|
731
606
|
const resultArchive = await baseArchive.clone();
|
|
607
|
+
maybeCaptureEmittedDocumentXml(newDocumentXml);
|
|
732
608
|
resultArchive.setDocumentXml(newDocumentXml);
|
|
733
609
|
// Step 12b: Merge auxiliary part definitions (footnotes, endnotes, comments).
|
|
734
610
|
// Reconstruction may insert content (deleted in inplace, added in rebuild)
|
|
@@ -740,13 +616,14 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
740
616
|
// Gated on root comment IDs in the *result* document (not on what the
|
|
741
617
|
// generic merge appended), so the pass runs even when the original already
|
|
742
618
|
// contains the root and revised only adds replies under it (issue #108).
|
|
743
|
-
|
|
619
|
+
// Comments anchored on footnote/endnote text count as roots too.
|
|
620
|
+
const rootCommentIds = await collectStoryReferenceIds(resultArchive, newDocumentXml, 'w:commentReference', null);
|
|
744
621
|
if (rootCommentIds.size > 0) {
|
|
745
622
|
await mergeCommentAncillaryParts(mergeSourceArchive, resultArchive, rootCommentIds);
|
|
746
623
|
}
|
|
747
624
|
// Step 13: Save result and compute stats
|
|
748
625
|
const resultBuffer = await resultArchive.save();
|
|
749
|
-
const stats =
|
|
626
|
+
const stats = computeAtomizerStats(mergedAtoms);
|
|
750
627
|
return {
|
|
751
628
|
document: resultBuffer,
|
|
752
629
|
stats,
|
|
@@ -755,37 +632,29 @@ export async function compareDocumentsAtomizer(original, revised, options = {})
|
|
|
755
632
|
reconstructionModeUsed: comparisonResult.outputMode,
|
|
756
633
|
fallbackReason,
|
|
757
634
|
fallbackDiagnostics,
|
|
635
|
+
rebuildSafetyDiagnostics,
|
|
758
636
|
};
|
|
759
637
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
label: 'comment',
|
|
781
|
-
partPath: 'word/comments.xml',
|
|
782
|
-
referenceTag: 'w:commentReference',
|
|
783
|
-
entryTag: 'w:comment',
|
|
784
|
-
rootTag: 'w:comments',
|
|
785
|
-
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml',
|
|
786
|
-
relationshipType: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
|
|
787
|
-
},
|
|
788
|
-
];
|
|
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
|
+
}
|
|
789
658
|
/**
|
|
790
659
|
* Collect reference IDs from document.xml using DOM parsing.
|
|
791
660
|
*/
|
|
@@ -800,21 +669,6 @@ function collectReferenceIds(documentXml, referenceTag) {
|
|
|
800
669
|
}
|
|
801
670
|
return ids;
|
|
802
671
|
}
|
|
803
|
-
/**
|
|
804
|
-
* Parse an auxiliary part and extract entry elements by ID.
|
|
805
|
-
*/
|
|
806
|
-
function parseEntries(xml, entryTag) {
|
|
807
|
-
const doc = parseXml(xml);
|
|
808
|
-
const entries = new Map();
|
|
809
|
-
const elements = doc.getElementsByTagName(entryTag);
|
|
810
|
-
for (let i = 0; i < elements.length; i++) {
|
|
811
|
-
const el = elements[i];
|
|
812
|
-
const id = el.getAttribute('w:id');
|
|
813
|
-
if (id)
|
|
814
|
-
entries.set(id, el);
|
|
815
|
-
}
|
|
816
|
-
return { doc, entries };
|
|
817
|
-
}
|
|
818
672
|
/**
|
|
819
673
|
* Merge auxiliary part definitions (footnotes, endnotes, comments) from the
|
|
820
674
|
* source archive into the result archive. The source archive is whichever
|
|
@@ -824,7 +678,11 @@ function parseEntries(xml, entryTag) {
|
|
|
824
678
|
*/
|
|
825
679
|
async function mergeAuxiliaryPartDefinitions(sourceArchive, resultArchive, documentXml, descriptor) {
|
|
826
680
|
const result = { mergedIds: new Set(), createdPart: false };
|
|
827
|
-
|
|
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);
|
|
828
686
|
if (referencedIds.size === 0)
|
|
829
687
|
return result;
|
|
830
688
|
const sourcePartXml = await sourceArchive.getFile(descriptor.partPath);
|
|
@@ -1154,6 +1012,7 @@ const COMMENTS_EXTENDED_DESCRIPTOR = {
|
|
|
1154
1012
|
rootTag: 'w15:commentsEx',
|
|
1155
1013
|
contentType: 'application/vnd.ms-word.commentsExtended+xml',
|
|
1156
1014
|
relationshipType: 'http://schemas.microsoft.com/office/2011/relationships/commentsExtended',
|
|
1015
|
+
idBearingTags: [], // keyed by w15:paraId, not w:id
|
|
1157
1016
|
};
|
|
1158
1017
|
const PEOPLE_DESCRIPTOR = {
|
|
1159
1018
|
label: 'people',
|
|
@@ -1163,6 +1022,7 @@ const PEOPLE_DESCRIPTOR = {
|
|
|
1163
1022
|
rootTag: 'w15:people',
|
|
1164
1023
|
contentType: 'application/vnd.ms-word.people+xml',
|
|
1165
1024
|
relationshipType: 'http://schemas.microsoft.com/office/2011/relationships/people',
|
|
1025
|
+
idBearingTags: [], // keyed by w15:author, not w:id
|
|
1166
1026
|
};
|
|
1167
1027
|
async function mergePeople(sourceArchive, resultArchive, mergedAuthors) {
|
|
1168
1028
|
if (mergedAuthors.size === 0)
|
|
@@ -1219,47 +1079,82 @@ async function mergePeople(sourceArchive, resultArchive, mergedAuthors) {
|
|
|
1219
1079
|
resultArchive.setFile('word/people.xml', serializer.serializeToString(newDoc));
|
|
1220
1080
|
await ensureOpcMetadata(resultArchive, PEOPLE_DESCRIPTOR);
|
|
1221
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
|
+
}
|
|
1222
1098
|
/**
|
|
1223
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.
|
|
1224
1104
|
*/
|
|
1225
|
-
function
|
|
1105
|
+
export function computeAtomizerStats(mergedAtoms) {
|
|
1226
1106
|
const reconstructionStats = computeReconstructionStats(mergedAtoms);
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
let
|
|
1231
|
-
let
|
|
1232
|
-
|
|
1107
|
+
let insertedRanges = 0;
|
|
1108
|
+
let deletedRanges = 0;
|
|
1109
|
+
let formatChanges = 0;
|
|
1110
|
+
let previousRangeStatus = null;
|
|
1111
|
+
let previousRangeParagraph;
|
|
1112
|
+
const paragraphs = new Map();
|
|
1233
1113
|
for (const atom of mergedAtoms) {
|
|
1234
|
-
|
|
1235
|
-
const
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
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++;
|
|
1243
1129
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
hasInserted = false;
|
|
1130
|
+
previousRangeStatus = rangeStatus;
|
|
1131
|
+
previousRangeParagraph = paragraphKey;
|
|
1247
1132
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1133
|
+
else {
|
|
1134
|
+
previousRangeStatus = null;
|
|
1135
|
+
previousRangeParagraph = undefined;
|
|
1250
1136
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
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);
|
|
1253
1144
|
}
|
|
1254
1145
|
}
|
|
1255
|
-
|
|
1256
|
-
if (currentParagraph && hasDeleted && hasInserted) {
|
|
1257
|
-
modifiedParagraphs.add(currentParagraph);
|
|
1258
|
-
}
|
|
1146
|
+
const modifiedParagraphs = Array.from(paragraphs.values()).filter((flags) => flags.hasDeleted && flags.hasInserted).length;
|
|
1259
1147
|
return {
|
|
1260
|
-
insertions:
|
|
1261
|
-
deletions:
|
|
1262
|
-
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,
|
|
1263
1158
|
};
|
|
1264
1159
|
}
|
|
1265
1160
|
//# sourceMappingURL=pipeline.js.map
|