@ansonlai/docx-redline-js 0.8.2 → 0.8.4

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 (33) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/README.md +28 -13
  3. package/core/list-targeting.js +12 -10
  4. package/dist/docx-redline-js.esm.js +837 -549
  5. package/dist/docx-redline-js.esm.js.map +4 -4
  6. package/dist/docx-redline-js.esm.min.js +93 -90
  7. package/dist/docx-redline-js.esm.min.js.map +4 -4
  8. package/dist/docx-redline.bundle.cjs +837 -549
  9. package/dist/docx-redline.bundle.cjs.map +4 -4
  10. package/dist/docx-redline.bundle.js +837 -549
  11. package/dist/docx-redline.bundle.js.map +4 -4
  12. package/docs/AGENT_KNOWLEDGE_BASE.md +4 -1
  13. package/docs/schemas/document-operations.schema.json +168 -168
  14. package/engine/oxml-engine.js +215 -195
  15. package/engine/reconstruction-mode.js +183 -183
  16. package/index.d.ts +4 -0
  17. package/index.js +36 -1
  18. package/node/cli.js +2 -1
  19. package/orchestration/list-structural-fallback.js +530 -518
  20. package/package.json +1 -1
  21. package/pipeline/list-generation.js +203 -168
  22. package/pipeline/serialization.js +111 -94
  23. package/services/batch-operation-orchestrator.js +6 -0
  24. package/services/document-inspection.js +10 -7
  25. package/services/document-operation-applier.js +2 -0
  26. package/services/document-operation-contract.js +553 -537
  27. package/services/document-operation-mutations.js +1140 -1011
  28. package/services/numbering-helpers.js +108 -0
  29. package/services/operation-batch-compiler.js +440 -436
  30. package/services/operation-heuristics.js +197 -173
  31. package/services/revision-comment-management.js +601 -594
  32. package/services/standalone-operation-runner.d.ts +429 -427
  33. package/services/table-reconciliation.js +48 -46
@@ -465,7 +465,7 @@ fragment to insert. Do not choose a type from its English name alone.
465
465
  | Change native list structure | `{ type: 'list-change', target, modified: '<complete Markdown list>' }` |
466
466
  | Reconcile a Word table | `{ type: 'table-reconciliation', target, modified: '<complete Markdown table>' }` |
467
467
  | Comment or highlight existing text | `comment` with `commentContent`, or `highlight` with `textToHighlight` |
468
- | Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties` |
468
+ | Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties`; add `textOccurrence` (1-based, default 1) to format a later match of `textToFormat` inside the targeted paragraph (out of range fails `PATCH_SOURCE_NOT_FOUND`; independent of `target.occurrence`, which selects the paragraph; legacy calls without `textOccurrence` still read `target.occurrence`; capability `format-text-occurrence-v1`) |
469
469
  | Counterpropose a paragraph wholly deleted by another author | `restore` with a rejected-view target |
470
470
  | Insert text inside another author's rejected-view content | `insert` with `target.revisionView: 'rejected'`, an exact `anchor`, and `existingRevisions: 'slice-cross-author'` |
471
471
 
@@ -514,6 +514,9 @@ revision markup internally before writing, so do not add `preflight` or baseline
514
514
  2. Build one final operation per target. Use complete `modified` text for broad
515
515
  semantic revisions or localized `replacements` for small literal changes in
516
516
  an inspected target. Consolidate multiple changes to one paragraph.
517
+ Paragraphs with soft line breaks (`\n` in `exactText`) accept `replacements`
518
+ whose `find` lies within one line; the breaks are preserved. A `find` or
519
+ `replace` that spans or adds a line break is refused.
517
520
  3. Run `apply` once per stable batch. Independent strong targets bind against
518
521
  the batch-start document and do not need bottom-up sorting. Use captures only
519
522
  for intentional created-content dependencies.
@@ -1,168 +1,168 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/AnsonLai/docx-redline-js/docs/schemas/document-operations.schema.json",
4
- "title": "docx-redline document operations",
5
- "oneOf": [
6
- { "$ref": "#/$defs/operationArray" },
7
- {
8
- "type": "object",
9
- "required": ["operations"],
10
- "properties": {
11
- "operations": { "$ref": "#/$defs/operationArray" },
12
- "expectedRevision": { "$ref": "#/$defs/revisionToken" }
13
- },
14
- "additionalProperties": false
15
- }
16
- ],
17
- "$defs": {
18
- "revisionToken": {
19
- "type": "object",
20
- "required": ["algorithm", "version", "scope", "value"],
21
- "properties": {
22
- "algorithm": { "type": "string", "enum": ["sha256"] },
23
- "version": { "type": "integer", "enum": [1] },
24
- "scope": { "type": "string", "enum": ["document-parts", "package"] },
25
- "value": { "type": "string", "pattern": "^[0-9a-fA-F]{64}$" },
26
- "coveredParts": { "type": "array", "items": { "type": "string" } }
27
- },
28
- "additionalProperties": false
29
- },
30
- "operationArray": { "type": "array", "items": { "$ref": "#/$defs/operation" } },
31
- "rejectedTextInsertionAnchor": {
32
- "type": "object",
33
- "required": ["exactText", "offset"],
34
- "properties": {
35
- "exactText": { "type": "string", "minLength": 1 },
36
- "occurrence": { "type": "integer", "minimum": 1 },
37
- "offset": { "type": "integer", "minimum": 0 }
38
- },
39
- "additionalProperties": false
40
- },
41
- "localizedReplacement": {
42
- "type": "object",
43
- "required": ["find", "replace"],
44
- "properties": {
45
- "find": { "type": "string", "minLength": 1 },
46
- "replace": { "type": "string" },
47
- "occurrence": { "type": "integer", "minimum": 1 }
48
- },
49
- "additionalProperties": false
50
- },
51
- "target": {
52
- "oneOf": [
53
- { "type": "string", "minLength": 1 },
54
- {
55
- "type": "object",
56
- "properties": {
57
- "exactText": { "type": "string" }, "text": { "type": "string" },
58
- "index": { "type": ["integer", "string"] }, "paragraphIndex": { "type": ["integer", "string"] },
59
- "paragraphId": { "type": "string" }, "occurrence": { "type": "integer", "minimum": 1 },
60
- "inTable": { "type": "boolean" }, "fingerprint": { "type": "string" }, "sourceFingerprint": { "type": "string" },
61
- "revisionView": {
62
- "type": "string",
63
- "enum": ["accepted", "rejected"],
64
- "description": "Defaults to rejected for restore operations and accepted for other operation types."
65
- },
66
- "captureRef": { "type": "string", "minLength": 1, "maxLength": 256 },
67
- "select": { "type": "string", "minLength": 1 }
68
- },
69
- "additionalProperties": false,
70
- "anyOf": [ { "required": ["exactText"] }, { "required": ["text"] }, { "required": ["index"] }, { "required": ["paragraphIndex"] }, { "required": ["paragraphId"] }, { "required": ["fingerprint"] }, { "required": ["sourceFingerprint"] }, { "required": ["captureRef"] } ]
71
- }
72
- ]
73
- },
74
- "headerFooterPart": {
75
- "description": "Targets a header or footer instead of the body. Use a part path from inspect().headersFooters, or a selector. Comments are not supported in headers or footers.",
76
- "oneOf": [
77
- { "type": "string", "minLength": 1, "examples": ["word/footer2.xml"] },
78
- {
79
- "type": "object",
80
- "required": ["kind"],
81
- "properties": {
82
- "kind": { "enum": ["header", "footer"] },
83
- "type": { "enum": ["default", "first", "even"], "description": "Defaults to default." },
84
- "section": { "type": "integer", "minimum": 0, "description": "Zero-based section; required when several sections have their own part." }
85
- },
86
- "additionalProperties": false
87
- }
88
- ]
89
- },
90
- "base": {
91
- "type": "object",
92
- "required": ["type", "target"],
93
- "properties": {
94
- "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
95
- "captureKey": { "type": "string", "minLength": 1, "maxLength": 256 },
96
- "target": { "$ref": "#/$defs/target" }, "targetRef": { "type": ["integer", "string", "null"] },
97
- "part": { "$ref": "#/$defs/headerFooterPart" },
98
- "author": { "type": "string", "minLength": 1 }, "generateRedlines": { "type": "boolean" },
99
- "existingRevisions": { "enum": ["merge-same-author", "slice-cross-author", "reject-input", "accept-all-first", "accept-all-first-keep-normalized"] },
100
- "pairReplacements": { "type": "boolean" },
101
- "insertionAffinity": {
102
- "type": "object",
103
- "properties": {
104
- "formatting": { "enum": ["left", "right", "none"] },
105
- "hyperlink": { "enum": ["inside", "outside", "preserve"] },
106
- "revision": { "enum": ["coalesce_same_author", "separate"] },
107
- "bookmark": { "enum": ["inside", "outside"] },
108
- "comment": { "enum": ["inside", "outside"] }
109
- },
110
- "additionalProperties": false
111
- }
112
- }
113
- },
114
- "operation": {
115
- "oneOf": [
116
- {
117
- "type": "object",
118
- "required": ["type", "parentCommentId", "commentContent"],
119
- "properties": {
120
- "type": { "const": "comment_reply" },
121
- "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
122
- "parentCommentId": { "type": ["integer", "string"] },
123
- "commentContent": { "type": "string", "minLength": 1 },
124
- "author": { "type": "string", "minLength": 1 },
125
- "date": { "type": "string", "format": "date-time" }
126
- },
127
- "additionalProperties": false
128
- },
129
- {
130
- "type": "object",
131
- "description": "Resolve or reopen the comment thread containing commentId. Resolved is thread-level in Word, so the root and every reply change together. Body-only; not allowed with part.",
132
- "required": ["type", "commentId"],
133
- "properties": {
134
- "type": { "const": "comment_resolve" },
135
- "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
136
- "commentId": { "type": ["integer", "string"] },
137
- "resolved": { "type": "boolean", "default": true }
138
- },
139
- "additionalProperties": false
140
- },
141
- {
142
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["replacements"], "properties": { "type": { "enum": ["redline", "replace"] }, "replacements": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/localizedReplacement" } } } } ]
143
- },
144
- {
145
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["modified"], "properties": { "type": { "enum": ["redline", "replace", "format", "list-change", "table-reconciliation", "insert"] }, "modified": { "type": "string" }, "structuredContent": { "type": "boolean" }, "targetEnd": { "$ref": "#/$defs/target" }, "targetEndRef": { "type": ["integer", "string", "null"] }, "anchor": { "$ref": "#/$defs/rejectedTextInsertionAnchor" } } } ]
146
- },
147
- {
148
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "properties": { "type": { "const": "restore" }, "modified": { "oneOf": [ { "type": "string", "minLength": 1 }, { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } } ] }, "replacements": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/localizedReplacement" } }, "targetEnd": { "$ref": "#/$defs/target" }, "targetEndRef": { "type": ["integer", "string", "null"] } }, "oneOf": [ { "required": ["modified"], "not": { "required": ["replacements"] } }, { "required": ["replacements"], "not": { "required": ["modified"] } }, { "not": { "anyOf": [ { "required": ["modified"] }, { "required": ["replacements"] } ] } } ] } ]
149
- },
150
- {
151
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "properties": { "type": { "const": "delete" }, "modified": { "const": "" } } } ]
152
- },
153
- {
154
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["commentContent"], "properties": { "type": { "const": "comment" }, "textToComment": { "type": "string" }, "commentContent": { "type": "string", "minLength": 1 } } } ]
155
- },
156
- {
157
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["textToHighlight"], "properties": { "type": { "const": "highlight" }, "textToHighlight": { "type": "string", "minLength": 1 }, "color": { "type": "string" } } } ]
158
- },
159
- {
160
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["textToFormat", "properties"], "properties": { "type": { "enum": ["format", "character-format"] }, "textToFormat": { "type": "string", "minLength": 1 }, "properties": { "type": "object" }, "formattingRevisionPolicy": { "enum": ["always", "coalesce-own-insertion"] } } } ]
161
- },
162
- {
163
- "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["properties"], "properties": { "type": { "const": "paragraph-format" }, "properties": { "type": "object" } } } ]
164
- }
165
- ]
166
- }
167
- }
168
- }
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/AnsonLai/docx-redline-js/docs/schemas/document-operations.schema.json",
4
+ "title": "docx-redline document operations",
5
+ "oneOf": [
6
+ { "$ref": "#/$defs/operationArray" },
7
+ {
8
+ "type": "object",
9
+ "required": ["operations"],
10
+ "properties": {
11
+ "operations": { "$ref": "#/$defs/operationArray" },
12
+ "expectedRevision": { "$ref": "#/$defs/revisionToken" }
13
+ },
14
+ "additionalProperties": false
15
+ }
16
+ ],
17
+ "$defs": {
18
+ "revisionToken": {
19
+ "type": "object",
20
+ "required": ["algorithm", "version", "scope", "value"],
21
+ "properties": {
22
+ "algorithm": { "type": "string", "enum": ["sha256"] },
23
+ "version": { "type": "integer", "enum": [1] },
24
+ "scope": { "type": "string", "enum": ["document-parts", "package"] },
25
+ "value": { "type": "string", "pattern": "^[0-9a-fA-F]{64}$" },
26
+ "coveredParts": { "type": "array", "items": { "type": "string" } }
27
+ },
28
+ "additionalProperties": false
29
+ },
30
+ "operationArray": { "type": "array", "items": { "$ref": "#/$defs/operation" } },
31
+ "rejectedTextInsertionAnchor": {
32
+ "type": "object",
33
+ "required": ["exactText", "offset"],
34
+ "properties": {
35
+ "exactText": { "type": "string", "minLength": 1 },
36
+ "occurrence": { "type": "integer", "minimum": 1 },
37
+ "offset": { "type": "integer", "minimum": 0 }
38
+ },
39
+ "additionalProperties": false
40
+ },
41
+ "localizedReplacement": {
42
+ "type": "object",
43
+ "required": ["find", "replace"],
44
+ "properties": {
45
+ "find": { "type": "string", "minLength": 1 },
46
+ "replace": { "type": "string" },
47
+ "occurrence": { "type": "integer", "minimum": 1 }
48
+ },
49
+ "additionalProperties": false
50
+ },
51
+ "target": {
52
+ "oneOf": [
53
+ { "type": "string", "minLength": 1 },
54
+ {
55
+ "type": "object",
56
+ "properties": {
57
+ "exactText": { "type": "string" }, "text": { "type": "string" },
58
+ "index": { "type": ["integer", "string"] }, "paragraphIndex": { "type": ["integer", "string"] },
59
+ "paragraphId": { "type": "string" }, "occurrence": { "type": "integer", "minimum": 1 },
60
+ "inTable": { "type": "boolean" }, "fingerprint": { "type": "string" }, "sourceFingerprint": { "type": "string" },
61
+ "revisionView": {
62
+ "type": "string",
63
+ "enum": ["accepted", "rejected"],
64
+ "description": "Defaults to rejected for restore operations and accepted for other operation types."
65
+ },
66
+ "captureRef": { "type": "string", "minLength": 1, "maxLength": 256 },
67
+ "select": { "type": "string", "minLength": 1 }
68
+ },
69
+ "additionalProperties": false,
70
+ "anyOf": [ { "required": ["exactText"] }, { "required": ["text"] }, { "required": ["index"] }, { "required": ["paragraphIndex"] }, { "required": ["paragraphId"] }, { "required": ["fingerprint"] }, { "required": ["sourceFingerprint"] }, { "required": ["captureRef"] } ]
71
+ }
72
+ ]
73
+ },
74
+ "headerFooterPart": {
75
+ "description": "Targets a header or footer instead of the body. Use a part path from inspect().headersFooters, or a selector. Comments are not supported in headers or footers.",
76
+ "oneOf": [
77
+ { "type": "string", "minLength": 1, "examples": ["word/footer2.xml"] },
78
+ {
79
+ "type": "object",
80
+ "required": ["kind"],
81
+ "properties": {
82
+ "kind": { "enum": ["header", "footer"] },
83
+ "type": { "enum": ["default", "first", "even"], "description": "Defaults to default." },
84
+ "section": { "type": "integer", "minimum": 0, "description": "Zero-based section; required when several sections have their own part." }
85
+ },
86
+ "additionalProperties": false
87
+ }
88
+ ]
89
+ },
90
+ "base": {
91
+ "type": "object",
92
+ "required": ["type", "target"],
93
+ "properties": {
94
+ "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
95
+ "captureKey": { "type": "string", "minLength": 1, "maxLength": 256 },
96
+ "target": { "$ref": "#/$defs/target" }, "targetRef": { "type": ["integer", "string", "null"] },
97
+ "part": { "$ref": "#/$defs/headerFooterPart" },
98
+ "author": { "type": "string", "minLength": 1 }, "generateRedlines": { "type": "boolean" },
99
+ "existingRevisions": { "enum": ["merge-same-author", "slice-cross-author", "reject-input", "accept-all-first", "accept-all-first-keep-normalized"] },
100
+ "pairReplacements": { "type": "boolean" },
101
+ "insertionAffinity": {
102
+ "type": "object",
103
+ "properties": {
104
+ "formatting": { "enum": ["left", "right", "none"] },
105
+ "hyperlink": { "enum": ["inside", "outside", "preserve"] },
106
+ "revision": { "enum": ["coalesce_same_author", "separate"] },
107
+ "bookmark": { "enum": ["inside", "outside"] },
108
+ "comment": { "enum": ["inside", "outside"] }
109
+ },
110
+ "additionalProperties": false
111
+ }
112
+ }
113
+ },
114
+ "operation": {
115
+ "oneOf": [
116
+ {
117
+ "type": "object",
118
+ "required": ["type", "parentCommentId", "commentContent"],
119
+ "properties": {
120
+ "type": { "const": "comment_reply" },
121
+ "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
122
+ "parentCommentId": { "type": ["integer", "string"] },
123
+ "commentContent": { "type": "string", "minLength": 1 },
124
+ "author": { "type": "string", "minLength": 1 },
125
+ "date": { "type": "string", "format": "date-time" }
126
+ },
127
+ "additionalProperties": false
128
+ },
129
+ {
130
+ "type": "object",
131
+ "description": "Resolve or reopen the comment thread containing commentId. Resolved is thread-level in Word, so the root and every reply change together. Body-only; not allowed with part.",
132
+ "required": ["type", "commentId"],
133
+ "properties": {
134
+ "type": { "const": "comment_resolve" },
135
+ "operationId": { "type": "string", "minLength": 1, "maxLength": 256 },
136
+ "commentId": { "type": ["integer", "string"] },
137
+ "resolved": { "type": "boolean", "default": true }
138
+ },
139
+ "additionalProperties": false
140
+ },
141
+ {
142
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["replacements"], "properties": { "type": { "enum": ["redline", "replace"] }, "replacements": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/localizedReplacement" } } } } ]
143
+ },
144
+ {
145
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["modified"], "properties": { "type": { "enum": ["redline", "replace", "format", "list-change", "table-reconciliation", "insert"] }, "modified": { "type": "string" }, "structuredContent": { "type": "boolean" }, "targetEnd": { "$ref": "#/$defs/target" }, "targetEndRef": { "type": ["integer", "string", "null"] }, "anchor": { "$ref": "#/$defs/rejectedTextInsertionAnchor" } } } ]
146
+ },
147
+ {
148
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "properties": { "type": { "const": "restore" }, "modified": { "oneOf": [ { "type": "string", "minLength": 1 }, { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } } ] }, "replacements": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/localizedReplacement" } }, "targetEnd": { "$ref": "#/$defs/target" }, "targetEndRef": { "type": ["integer", "string", "null"] } }, "oneOf": [ { "required": ["modified"], "not": { "required": ["replacements"] } }, { "required": ["replacements"], "not": { "required": ["modified"] } }, { "not": { "anyOf": [ { "required": ["modified"] }, { "required": ["replacements"] } ] } } ] } ]
149
+ },
150
+ {
151
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "properties": { "type": { "const": "delete" }, "modified": { "const": "" } } } ]
152
+ },
153
+ {
154
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["commentContent"], "properties": { "type": { "const": "comment" }, "textToComment": { "type": "string" }, "commentContent": { "type": "string", "minLength": 1 } } } ]
155
+ },
156
+ {
157
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["textToHighlight"], "properties": { "type": { "const": "highlight" }, "textToHighlight": { "type": "string", "minLength": 1 }, "color": { "type": "string" } } } ]
158
+ },
159
+ {
160
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["textToFormat", "properties"], "properties": { "type": { "enum": ["format", "character-format"] }, "textToFormat": { "type": "string", "minLength": 1 }, "textOccurrence": { "type": "integer", "minimum": 1, "default": 1 }, "properties": { "type": "object" }, "formattingRevisionPolicy": { "enum": ["always", "coalesce-own-insertion"] } } } ]
161
+ },
162
+ {
163
+ "allOf": [ { "$ref": "#/$defs/base" }, { "type": "object", "required": ["properties"], "properties": { "type": { "const": "paragraph-format" }, "properties": { "type": "object" } } } ]
164
+ }
165
+ ]
166
+ }
167
+ }
168
+ }