@portone/docx-editor 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/CONTRIBUTING.md +5 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +13 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +12 -7
  10. package/dist/docx/commentOnlyChange.js +9 -156
  11. package/dist/docx/comments/constants.d.ts +0 -1
  12. package/dist/docx/comments/constants.js +0 -2
  13. package/dist/docx/comments/grammar.d.ts +10 -4
  14. package/dist/docx/comments/grammar.js +12 -2
  15. package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +16 -92
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.js +8 -10
  22. package/dist/docx/comments/writing.d.ts +19 -7
  23. package/dist/docx/comments/writing.js +59 -112
  24. package/dist/docx/documentSettings.d.ts +7 -0
  25. package/dist/docx/documentSettings.js +10 -2
  26. package/dist/docx/exportDocx.d.ts +40 -5
  27. package/dist/docx/exportDocx.js +88 -74
  28. package/dist/docx/exportRefs.d.ts +5 -2
  29. package/dist/docx/exportRefs.js +3 -1
  30. package/dist/docx/fidelity.d.ts +43 -0
  31. package/dist/docx/fidelity.js +64 -0
  32. package/dist/docx/formatting/attrs.d.ts +45 -0
  33. package/dist/docx/formatting/attrs.js +86 -0
  34. package/dist/docx/formatting/context.d.ts +39 -0
  35. package/dist/docx/formatting/context.js +60 -0
  36. package/dist/docx/formatting/direct.d.ts +6 -5
  37. package/dist/docx/formatting/direct.js +52 -78
  38. package/dist/docx/formatting/resolve.d.ts +51 -0
  39. package/dist/docx/formatting/resolve.js +160 -0
  40. package/dist/docx/formatting/runProperties.d.ts +93 -0
  41. package/dist/docx/formatting/runProperties.js +316 -0
  42. package/dist/docx/formatting/styles.d.ts +25 -2
  43. package/dist/docx/formatting/styles.js +87 -6
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +6 -13
  48. package/dist/docx/hyperlink.d.ts +14 -4
  49. package/dist/docx/hyperlink.js +4 -1
  50. package/dist/docx/identities.d.ts +67 -0
  51. package/dist/docx/identities.js +174 -0
  52. package/dist/docx/importDocx.d.ts +12 -1
  53. package/dist/docx/importDocx.js +90 -88
  54. package/dist/docx/importParagraph.d.ts +1 -1
  55. package/dist/docx/importParagraph.js +4 -3
  56. package/dist/docx/importTable.d.ts +2 -2
  57. package/dist/docx/importTable.js +69 -56
  58. package/dist/docx/invariants.d.ts +33 -0
  59. package/dist/docx/invariants.js +269 -0
  60. package/dist/docx/media.d.ts +6 -4
  61. package/dist/docx/media.js +6 -37
  62. package/dist/docx/newLists.d.ts +50 -0
  63. package/dist/docx/newLists.js +59 -0
  64. package/dist/docx/notes.js +3 -7
  65. package/dist/docx/numberingPlanner.d.ts +11 -0
  66. package/dist/docx/numberingPlanner.js +50 -0
  67. package/dist/docx/packageParts.d.ts +42 -0
  68. package/dist/docx/packageParts.js +135 -0
  69. package/dist/docx/pageGeometry.d.ts +2 -0
  70. package/dist/docx/pageGeometry.js +18 -9
  71. package/dist/docx/paraProps.d.ts +10 -22
  72. package/dist/docx/paraProps.js +47 -76
  73. package/dist/docx/partPlan.d.ts +36 -0
  74. package/dist/docx/partPlan.js +59 -0
  75. package/dist/docx/protectionPolicy.d.ts +127 -0
  76. package/dist/docx/protectionPolicy.js +169 -0
  77. package/dist/docx/relationships.d.ts +1 -1
  78. package/dist/docx/relationships.js +8 -13
  79. package/dist/docx/runProps.d.ts +9 -22
  80. package/dist/docx/runProps.js +15 -168
  81. package/dist/docx/scan.js +20 -52
  82. package/dist/docx/sdt.js +9 -34
  83. package/dist/docx/sdtProps.d.ts +8 -1
  84. package/dist/docx/sdtProps.js +10 -0
  85. package/dist/docx/serializeBlock.d.ts +2 -0
  86. package/dist/docx/serializeBlock.js +8 -5
  87. package/dist/docx/serializeTable.js +32 -15
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +53 -17
  90. package/dist/docx/tableFormatting/conditions.d.ts +76 -0
  91. package/dist/docx/tableFormatting/conditions.js +196 -0
  92. package/dist/docx/tableFormatting/editing.d.ts +7 -3
  93. package/dist/docx/tableFormatting/editing.js +111 -143
  94. package/dist/docx/tableFormatting/reading.d.ts +63 -23
  95. package/dist/docx/tableFormatting/reading.js +132 -38
  96. package/dist/docx/tableFormatting.d.ts +1 -0
  97. package/dist/docx/tableFormatting.js +1 -0
  98. package/dist/docx/tableTemplate.js +30 -19
  99. package/dist/download.d.ts +8 -5
  100. package/dist/download.js +2 -0
  101. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  102. package/dist/editor/commands/comments/editing.d.ts +6 -2
  103. package/dist/editor/commands/comments/editing.js +19 -31
  104. package/dist/editor/commands/exportQueries.d.ts +15 -0
  105. package/dist/editor/commands/exportQueries.js +14 -0
  106. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  107. package/dist/editor/commands/fidelityQueries.js +8 -0
  108. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  109. package/dist/editor/commands/formatting/editing.js +26 -108
  110. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  111. package/dist/editor/commands/formatting/propertyCommands.js +108 -0
  112. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  113. package/dist/editor/commands/formatting/shared.js +5 -2
  114. package/dist/editor/commands/indentCommands.js +5 -4
  115. package/dist/editor/commands/index.d.ts +11 -0
  116. package/dist/editor/commands/index.js +5 -0
  117. package/dist/editor/commands/linkCommands.js +5 -6
  118. package/dist/editor/commands/listCommands.d.ts +6 -3
  119. package/dist/editor/commands/listCommands.js +73 -30
  120. package/dist/editor/commands/lockCommands.d.ts +7 -1
  121. package/dist/editor/commands/lockCommands.js +35 -46
  122. package/dist/editor/commands/paragraphCommands.js +33 -31
  123. package/dist/editor/commands/spacingCommands.js +1 -1
  124. package/dist/editor/createEditor.d.ts +17 -29
  125. package/dist/editor/createEditor.js +58 -58
  126. package/dist/editor/documentStyles.d.ts +11 -35
  127. package/dist/editor/documentStyles.js +9 -52
  128. package/dist/editor/editorDocument.d.ts +57 -0
  129. package/dist/editor/editorDocument.js +86 -0
  130. package/dist/editor/externalClipboard.d.ts +13 -2
  131. package/dist/editor/externalClipboard.js +76 -75
  132. package/dist/editor/insertTable.js +4 -3
  133. package/dist/editor/paragraphEdits.d.ts +27 -19
  134. package/dist/editor/paragraphEdits.js +18 -19
  135. package/dist/editor/paragraphPlacement.d.ts +11 -0
  136. package/dist/editor/paragraphPlacement.js +18 -0
  137. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  138. package/dist/editor/plugins/displayDerivation.js +78 -0
  139. package/dist/editor/plugins/imagePaste.js +11 -7
  140. package/dist/editor/plugins/keymap.js +18 -3
  141. package/dist/editor/plugins/numberingDecorations.d.ts +14 -14
  142. package/dist/editor/plugins/numberingDecorations.js +50 -19
  143. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  144. package/dist/editor/plugins/paragraphDisplay.js +57 -0
  145. package/dist/editor/plugins/tabLayout.js +1 -1
  146. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  147. package/dist/editor/plugins/tableDisplay.js +30 -0
  148. package/dist/index.d.ts +2 -0
  149. package/dist/model/format.d.ts +80 -8
  150. package/dist/model/format.js +100 -22
  151. package/dist/model/tabStops.d.ts +9 -0
  152. package/dist/model/tabStops.js +18 -0
  153. package/dist/numbering/listRegistry.d.ts +37 -0
  154. package/dist/numbering/listRegistry.js +112 -0
  155. package/dist/numbering/listTemplate.d.ts +26 -17
  156. package/dist/numbering/listTemplate.js +88 -25
  157. package/dist/numbering/markers.d.ts +15 -6
  158. package/dist/numbering/markers.js +26 -59
  159. package/dist/numbering/parseNumbering.d.ts +84 -6
  160. package/dist/numbering/parseNumbering.js +131 -44
  161. package/dist/numbering/spellers.d.ts +15 -0
  162. package/dist/numbering/spellers.js +96 -0
  163. package/dist/numbering/writeNumbering.d.ts +8 -7
  164. package/dist/numbering/writeNumbering.js +28 -39
  165. package/dist/ooxml/childOrder.d.ts +34 -0
  166. package/dist/ooxml/childOrder.js +496 -0
  167. package/dist/ooxml/conformance.d.ts +28 -0
  168. package/dist/ooxml/conformance.js +20 -0
  169. package/dist/ooxml/element.d.ts +24 -11
  170. package/dist/ooxml/element.js +31 -12
  171. package/dist/ooxml/errors.d.ts +6 -5
  172. package/dist/ooxml/image.d.ts +4 -0
  173. package/dist/ooxml/image.js +2 -5
  174. package/dist/ooxml/names.d.ts +9 -0
  175. package/dist/ooxml/names.js +8 -1
  176. package/dist/ooxml/partSplice.d.ts +67 -0
  177. package/dist/ooxml/partSplice.js +176 -0
  178. package/dist/ooxml/props.d.ts +112 -0
  179. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  180. package/dist/ooxml/simpleTypes.d.ts +103 -0
  181. package/dist/ooxml/simpleTypes.js +182 -0
  182. package/dist/ooxml/tabStops.js +8 -27
  183. package/dist/ooxml/tagScan.d.ts +34 -0
  184. package/dist/ooxml/tagScan.js +108 -0
  185. package/dist/ooxml/units.d.ts +25 -13
  186. package/dist/ooxml/units.js +65 -31
  187. package/dist/ooxml/xml.d.ts +35 -15
  188. package/dist/ooxml/xml.js +43 -11
  189. package/dist/page/PageGuides.d.ts +1 -1
  190. package/dist/page/PageGuides.js +0 -13
  191. package/dist/page/blockKinds.d.ts +61 -0
  192. package/dist/page/blockKinds.js +11 -0
  193. package/dist/page/kinds/index.d.ts +6 -0
  194. package/dist/page/kinds/index.js +10 -0
  195. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  196. package/dist/page/kinds/paragraphKind.js +71 -0
  197. package/dist/page/kinds/tableKind.d.ts +10 -0
  198. package/dist/page/kinds/tableKind.js +178 -0
  199. package/dist/page/measureBlocks.d.ts +3 -14
  200. package/dist/page/measureBlocks.js +24 -45
  201. package/dist/page/pageDecorations.d.ts +27 -44
  202. package/dist/page/pageDecorations.js +70 -152
  203. package/dist/page/pageLayout.d.ts +2 -29
  204. package/dist/page/pageLayout.js +39 -16
  205. package/dist/page/usePageLayout.d.ts +2 -24
  206. package/dist/page/usePageLayout.js +5 -48
  207. package/dist/schema/attrRoles.d.ts +25 -18
  208. package/dist/schema/attrRoles.js +110 -68
  209. package/dist/schema/displayDerivation.d.ts +82 -0
  210. package/dist/schema/displayDerivation.js +130 -0
  211. package/dist/schema/docxSchema.d.ts +3 -0
  212. package/dist/schema/docxSchema.js +58 -17
  213. package/dist/schema/editGuard.d.ts +1 -1
  214. package/dist/schema/guards.d.ts +37 -14
  215. package/dist/schema/guards.js +29 -3
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +11 -44
  219. package/dist/schema/locks.js +0 -9
  220. package/dist/schema/preservedGuards.d.ts +26 -6
  221. package/dist/schema/preservedGuards.js +31 -1
  222. package/dist/schema/protection.d.ts +7 -1
  223. package/dist/schema/protection.js +2 -1
  224. package/dist/schema/sourceEquality.d.ts +1 -9
  225. package/dist/schema/sourceEquality.js +1 -27
  226. package/dist/styles/classNames.d.ts +13 -2
  227. package/dist/styles/classNames.js +13 -2
  228. package/dist/styles/inlineStyle.js +15 -6
  229. package/dist/styles.css +16 -18
  230. package/dist/table/cellFormatting.d.ts +8 -1
  231. package/dist/table/cellFormatting.js +11 -20
  232. package/dist/table/commands.js +20 -18
  233. package/dist/table/format.d.ts +3 -0
  234. package/dist/table/format.js +3 -9
  235. package/dist/table/gridBorders.d.ts +24 -30
  236. package/dist/table/gridBorders.js +35 -59
  237. package/dist/table/merge.d.ts +2 -6
  238. package/dist/table/merge.js +5 -5
  239. package/package.json +7 -3
  240. package/dist/docx/comments/contentTypes.d.ts +0 -7
  241. package/dist/docx/comments/contentTypes.js +0 -38
  242. package/dist/docx/comments/verifying.js +0 -206
  243. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  244. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  245. package/dist/docx/propsXml.d.ts +0 -67
  246. package/dist/docx/uniqueControls.d.ts +0 -14
  247. package/dist/docx/uniqueControls.js +0 -62
  248. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  249. package/dist/editor/plugins/commentReservations.js +0 -26
  250. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  251. package/dist/editor/plugins/styledParagraphs.js +0 -65
  252. package/dist/page/tableMeasurements.d.ts +0 -18
  253. package/dist/page/tableMeasurements.js +0 -119
@@ -1,161 +1,14 @@
1
1
  // src/docx/commentOnlyChange.ts
2
- import { decodeUtf8, elementChildren, parseXml } from "../ooxml/xml.js";
3
- import {
4
- commentAdditionsBy,
5
- commentEditsOwned,
6
- commentIdentitiesKept,
7
- withoutComments
8
- } from "../schema/protection.js";
9
- import {
10
- COMMENTS_CONTENT_TYPE,
11
- COMMENTS_EXTENDED_CONTENT_TYPE,
12
- COMMENTS_EXTENDED_REL_TYPE,
13
- COMMENTS_REL_TYPE,
14
- CONTENT_TYPES_PATH,
15
- PEOPLE_CONTENT_TYPE,
16
- PEOPLE_REL_TYPE
17
- } from "./comments/constants.js";
18
- import { commentPartsKept } from "./comments/verifying.js";
19
- import { importDocx } from "./importDocx.js";
20
- import {
21
- readRelationships,
22
- relsPathOf
23
- } from "./relationships.js";
24
- import {
25
- comparableStory,
26
- isModelledBlock
27
- } from "./storyProjection.js";
28
- var COMMENT_REL_TYPES = [
29
- COMMENTS_REL_TYPE,
30
- COMMENTS_EXTENDED_REL_TYPE,
31
- PEOPLE_REL_TYPE
32
- ];
33
- var COMMENT_CONTENT_TYPES = [
34
- COMMENTS_CONTENT_TYPE,
35
- COMMENTS_EXTENDED_CONTENT_TYPE,
36
- PEOPLE_CONTENT_TYPE
37
- ];
38
- var refused = (reason) => ({ ok: false, reason });
39
- function sameBytes(before, after) {
40
- return before.length === after.length && before.every((byte, index) => byte === after[index]);
41
- }
42
- function commentPartsOf(session) {
43
- const { partPath, extendedPartPath, people } = session.comments;
44
- return [partPath, extendedPartPath, people.partPath];
45
- }
46
- function commentPartPaths(session) {
47
- return new Set(
48
- commentPartsOf(session).filter((path) => path !== null)
49
- );
50
- }
51
- function aroundTheStory(session) {
52
- const preserved = session.blocks.filter((block) => !isModelledBlock(block.node)).map((block) => block.xml).join("");
53
- return session.documentPrefix + preserved + session.documentSuffix;
54
- }
55
- function relationshipsKept(before, after) {
56
- if (new Set(before.map((entry) => entry.id)).size !== before.length || new Set(after.map((entry) => entry.id)).size !== after.length) {
57
- return false;
58
- }
59
- const now = new Map(after.map((entry) => [entry.id, entry]));
60
- const kept = before.every((entry) => {
61
- const current = now.get(entry.id);
62
- return current !== void 0 && current.type === entry.type && current.target === entry.target && current.external === entry.external;
2
+ import { commentsPolicy } from "./comments/policy.js";
3
+ import { verifyChange } from "./protectionPolicy.js";
4
+ function onlyCommentsChangedBy(original, submitted, authorId, {
5
+ editableComments = "own",
6
+ xmlParser
7
+ } = {}) {
8
+ return verifyChange(commentsPolicy, original, submitted, authorId, {
9
+ editableComments,
10
+ xmlParser
63
11
  });
64
- const ids = new Set(before.map((entry) => entry.id));
65
- const parts = after.filter((entry) => !entry.external);
66
- return kept && after.filter((entry) => !ids.has(entry.id)).every(
67
- (entry) => COMMENT_REL_TYPES.includes(entry.type) && !entry.external && parts.filter((other) => other.type === entry.type).length === 1
68
- );
69
- }
70
- function contentTypes(bytes) {
71
- if (bytes === void 0) return /* @__PURE__ */ new Map();
72
- const declared = /* @__PURE__ */ new Map();
73
- for (const el of elementChildren(
74
- parseXml(decodeUtf8(bytes).text).documentElement
75
- )) {
76
- const key = el.localName === "Default" ? el.getAttribute("Extension") : el.localName === "Override" ? el.getAttribute("PartName") : null;
77
- if (key !== null) declared.set(key, el.getAttribute("ContentType") ?? "");
78
- }
79
- return declared;
80
- }
81
- function contentTypesKept(before, after) {
82
- for (const [key, type] of before) {
83
- if (after.get(key) !== type) return false;
84
- }
85
- for (const [key, type] of after) {
86
- if (!before.has(key) && !COMMENT_CONTENT_TYPES.includes(type)) return false;
87
- }
88
- return true;
89
- }
90
- function gainedCommentPartsAreNew(before, after) {
91
- const had = commentPartsOf(before);
92
- return commentPartsOf(after).every(
93
- (path, kind) => path === null || path === had[kind] || !before.parts.has(path)
94
- );
95
- }
96
- function packageKept(before, after) {
97
- if (before.mainPartPath !== after.mainPartPath) {
98
- return { ok: false, reason: "part-changed", part: before.mainPartPath };
99
- }
100
- const relsPath = relsPathOf(before.mainPartPath);
101
- const untouched = /* @__PURE__ */ new Set([
102
- before.mainPartPath,
103
- relsPath,
104
- CONTENT_TYPES_PATH,
105
- ...commentPartPaths(before),
106
- ...commentPartPaths(after)
107
- ]);
108
- for (const path of /* @__PURE__ */ new Set([...before.parts.keys(), ...after.parts.keys()])) {
109
- if (untouched.has(path)) continue;
110
- const was = before.parts.get(path);
111
- const now = after.parts.get(path);
112
- if (was === void 0 || now === void 0 || !sameBytes(was, now)) {
113
- return { ok: false, reason: "part-changed", part: path };
114
- }
115
- }
116
- if (!relationshipsKept(
117
- readRelationships(before.parts, relsPath),
118
- readRelationships(after.parts, relsPath)
119
- ) || !gainedCommentPartsAreNew(before, after)) {
120
- return { ok: false, reason: "relationship-changed", part: relsPath };
121
- }
122
- if (!contentTypesKept(
123
- contentTypes(before.parts.get(CONTENT_TYPES_PATH)),
124
- contentTypes(after.parts.get(CONTENT_TYPES_PATH))
125
- )) {
126
- return { ok: false, reason: "part-changed", part: CONTENT_TYPES_PATH };
127
- }
128
- if (aroundTheStory(before) !== aroundTheStory(after)) {
129
- return { ok: false, reason: "part-changed", part: before.mainPartPath };
130
- }
131
- return { ok: true };
132
- }
133
- function sameBody(before, after) {
134
- const was = comparableStory(before, withoutComments);
135
- const now = comparableStory(after, withoutComments);
136
- return was !== null && now !== null && was.length === now.length && was.every((block, at) => block === now[at]);
137
- }
138
- function storyKept(before, after, authorId, editableComments) {
139
- if (!sameBody(before, after)) return refused("body-changed");
140
- if (!commentIdentitiesKept(before.doc, after.doc) || !commentAdditionsBy(before.doc, after.doc, authorId)) {
141
- return refused("comment-author-forged");
142
- }
143
- if (!commentEditsOwned(before.doc, after.doc, {
144
- protection: "comments",
145
- authorId,
146
- editableComments
147
- })) {
148
- return refused("comment-not-owned");
149
- }
150
- return { ok: true };
151
- }
152
- function onlyCommentsChangedBy(original, submitted, authorId, { editableComments = "own" } = {}) {
153
- const before = importDocx(original);
154
- const after = importDocx(submitted);
155
- const packaged = packageKept(before.session, after.session);
156
- if (!packaged.ok) return packaged;
157
- const story = storyKept(before, after, authorId, editableComments);
158
- return story.ok ? commentPartsKept(before, after, authorId, editableComments) : story;
159
12
  }
160
13
  export {
161
14
  onlyCommentsChangedBy
@@ -2,7 +2,6 @@ export declare const COMMENTS_REL_TYPE = "http://schemas.openxmlformats.org/offi
2
2
  export declare const COMMENTS_EXTENDED_REL_TYPE = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
3
3
  export declare const COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml";
4
4
  export declare const COMMENTS_EXTENDED_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml";
5
- export declare const CONTENT_TYPES_PATH = "[Content_Types].xml";
6
5
  export declare const W14_NS: "http://schemas.microsoft.com/office/word/2010/wordml";
7
6
  export declare const W15_NS: "http://schemas.microsoft.com/office/word/2012/wordml";
8
7
  export declare const MC_NS: "http://schemas.openxmlformats.org/markup-compatibility/2006";
@@ -5,7 +5,6 @@ var COMMENTS_REL_TYPE = `${R_NS}/comments`;
5
5
  var COMMENTS_EXTENDED_REL_TYPE = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
6
6
  var COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml";
7
7
  var COMMENTS_EXTENDED_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml";
8
- var CONTENT_TYPES_PATH = "[Content_Types].xml";
9
8
  var W14_NS = NAMESPACES.w14;
10
9
  var W15_NS = NAMESPACES.w15;
11
10
  var MC_NS = NAMESPACES.mc;
@@ -18,7 +17,6 @@ export {
18
17
  COMMENTS_EXTENDED_REL_TYPE,
19
18
  COMMENTS_REL_TYPE,
20
19
  COMMENT_AUTHOR_PROVIDER,
21
- CONTENT_TYPES_PATH,
22
20
  MC_NS,
23
21
  PEOPLE_CONTENT_TYPE,
24
22
  PEOPLE_REL_TYPE,
@@ -14,11 +14,17 @@ export declare const COMMENT_ATTRIBUTES: ReadonlySet<string>;
14
14
  /** The attributes this editor writes on a `w15:commentEx` */
15
15
  export declare const COMMENT_EX_ATTRIBUTES: ReadonlySet<string>;
16
16
  /**
17
- * Whether the element carries no attribute outside this set.
17
+ * Whether the namespace declaration is one this editor's writer puts out, its value included.
18
18
  *
19
- * A namespace declaration is not an attribute of the element in this sense: it says where the
20
- * names come from rather than anything about the entry, and where a producer puts one is its own
21
- * business.
19
+ * A declaration decides what every name around it means, so one binding a prefix to a namespace
20
+ * this package does not write is markup the writer did not put there however ordinary the names
21
+ * under it look. The default declaration is never one of them: the writer spells out a prefix on
22
+ * everything it writes.
23
+ */
24
+ export declare function declarationWritten(attr: Attr): boolean;
25
+ /**
26
+ * Whether the element carries no attribute outside this set, and declares no namespace outside
27
+ * what the writer declares.
22
28
  */
23
29
  export declare function attributesWithin(el: Element, allowed: ReadonlySet<string>): boolean;
24
30
  /** The body of a comment, as a paragraph of one run holding the text and the breaks in it */
@@ -4,7 +4,7 @@ import {
4
4
  elementXml,
5
5
  xmlnsAttr
6
6
  } from "../../ooxml/element.js";
7
- import { qualify, wName } from "../../ooxml/names.js";
7
+ import { NAMESPACES, qualify, wName } from "../../ooxml/names.js";
8
8
  import {
9
9
  attributeByLocalName,
10
10
  elementChildren,
@@ -48,9 +48,18 @@ function threadKeyOrNone(el, localName) {
48
48
  const value = attributeByLocalName(el, localName);
49
49
  return value === null || THREAD_KEY.test(value);
50
50
  }
51
+ var WRITTEN_NAMESPACES = /* @__PURE__ */ new Map([
52
+ ["w", NAMESPACES.w],
53
+ ["w14", NAMESPACES.w14],
54
+ ["w15", NAMESPACES.w15]
55
+ ]);
56
+ function declarationWritten(attr) {
57
+ if (!attr.name.startsWith("xmlns:")) return false;
58
+ return WRITTEN_NAMESPACES.get(attr.name.slice("xmlns:".length)) === attr.value;
59
+ }
51
60
  function attributesWithin(el, allowed) {
52
61
  return Array.from(el.attributes).every(
53
- (attr) => attr.namespaceURI === XMLNS_NS || attr.name === "xmlns" || allowed.has(nameKey(attr.namespaceURI, attr.localName))
62
+ (attr) => attr.namespaceURI === XMLNS_NS ? declarationWritten(attr) : allowed.has(nameKey(attr.namespaceURI, attr.localName))
54
63
  );
55
64
  }
56
65
  function isNamed(el, namespace, localName) {
@@ -201,6 +210,7 @@ export {
201
210
  COMMENT_EX_ATTRIBUTES,
202
211
  arrivedEntries,
203
212
  attributesWithin,
213
+ declarationWritten,
204
214
  lastBodyParagraph,
205
215
  readStrictCommentBody,
206
216
  recordedIdentity,
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Whether every entry of the three comment parts is one this editor wrote, for an author who was
3
- * in a position to write it.
2
+ * The three parts a comment is written across, each declared once: where it sits, what it is
3
+ * declared as in the package, what an entry in it looks like, and who may have written one.
4
4
  *
5
- * The parts a comment is written across are the ones a submission may rewrite, so the package
6
- * comparison (`../commentOnlyChange`) leaves their bytes alone. Something has to read them, or a
7
- * file could carry anything at all inside a comment and still be answered as unchanged. What that
8
- * reading holds an entry to is two separate questions. Grammar asks whether this editor's writer
5
+ * The writer named the relationship and the content type where it added a part and the verifier
6
+ * named them again where it excused one, so the two were free to drift apart. Both read this
7
+ * instead, which is what makes a part the editor writes a part it takes back.
8
+ *
9
+ * What an entry is held to is two separate questions. Grammar asks whether this editor's writer
9
10
  * could have put the entry out, whoever it belongs to. Permission asks whether this author could
10
11
  * have made the change. Asking them as one would refuse a moderator rewriting somebody else's
11
12
  * comment under `editableComments: "all"`, which is a thing the editor lets them do.
12
13
  */
13
14
  import type { EditableComments } from "../../schema/protection";
14
- import type { CommentOnlyVerdict } from "../commentOnlyChange";
15
- import type { Story } from "../storyProjection";
15
+ import type { StoryPartKind } from "../protectionPolicy";
16
16
  import { type ImportedPeople } from "./people";
17
17
  /**
18
18
  * Whether this editor's writer could have put the entry out, whoever it belongs to.
@@ -21,6 +21,15 @@ import { type ImportedPeople } from "./people";
21
21
  * `entryAllowed`'s question. Each kind is read where it is written (`./grammar`).
22
22
  */
23
23
  export declare function wellFormedEntry(entry: Element): boolean;
24
+ /**
25
+ * Whether the only thing between the two is that a thread gained its key.
26
+ *
27
+ * Settling a thread or replying to it belongs to everyone and leaves what the comment says alone,
28
+ * and the writer answers by putting the key on the entry that arrived rather than writing one of
29
+ * its own (`./grammar`). Such an entry is not a rewrite, so it is neither held to the grammar this
30
+ * editor writes bodies in nor to who owns the comment.
31
+ */
32
+ export declare function threadKeyAlone(entry: Element, original: Element): boolean;
24
33
  /**
25
34
  * Whether `authorId` could have written or rewritten this entry.
26
35
  *
@@ -32,13 +41,12 @@ export declare function wellFormedEntry(entry: Element): boolean;
32
41
  * have to answer alike or a file the editor wrote would be turned down here.
33
42
  */
34
43
  export declare function entryAllowed(entry: Element, original: Element | null, authorId: string, editableComments: EditableComments, people: ImportedPeople): boolean;
35
- /**
36
- * Whether the three comment parts came back holding only entries this editor writes, each of them
37
- * one this author was in a position to write.
38
- *
39
- * This runs after the story has been judged, so a comment rewritten, re-anchored or deleted by the
40
- * wrong hand is already named for what it is (`../commentOnlyChange`). What is left to this is
41
- * everything the story cannot show: markup forged into a body, an entry nothing refers to, an
42
- * identity recorded for somebody else.
43
- */
44
- export declare function commentPartsKept(before: Story, after: Story, authorId: string, editableComments: EditableComments): CommentOnlyVerdict;
44
+ export declare const commentsPart: StoryPartKind;
45
+ export declare const commentsExtendedPart: StoryPartKind;
46
+ export declare const peoplePart: StoryPartKind;
47
+ /** The parts a comment protection lets an edit rewrite, in the order an export writes them */
48
+ export declare const COMMENT_STORY_PARTS: readonly [
49
+ StoryPartKind,
50
+ StoryPartKind,
51
+ StoryPartKind
52
+ ];
@@ -0,0 +1,279 @@
1
+ // src/docx/comments/parts.ts
2
+ import { NAMESPACES } from "../../ooxml/names.js";
3
+ import {
4
+ attributeByLocalName,
5
+ isElement,
6
+ parseXml,
7
+ serializeXml,
8
+ W_NS
9
+ } from "../../ooxml/xml.js";
10
+ import { availablePartPath } from "../packageParts.js";
11
+ import {
12
+ COMMENTS_CONTENT_TYPE,
13
+ COMMENTS_EXTENDED_CONTENT_TYPE,
14
+ COMMENTS_EXTENDED_REL_TYPE,
15
+ COMMENTS_REL_TYPE,
16
+ PEOPLE_CONTENT_TYPE,
17
+ PEOPLE_REL_TYPE,
18
+ W14_NS,
19
+ W15_NS
20
+ } from "./constants.js";
21
+ import {
22
+ attributesWithin,
23
+ COMMENT_ATTRIBUTES,
24
+ declarationWritten,
25
+ lastBodyParagraph,
26
+ readStrictCommentBody,
27
+ recordedIdentity,
28
+ wellFormedCommentExtension,
29
+ wellFormedPerson
30
+ } from "./grammar.js";
31
+ import { commentReferencesIn } from "./model.js";
32
+ import { commentAuthorId } from "./people.js";
33
+ import { lastParagraphId } from "./reading.js";
34
+ var COMMENT_IDENTITY = [
35
+ "id",
36
+ "author",
37
+ "date",
38
+ "initials"
39
+ ];
40
+ function sameAttributes(entry, original, names) {
41
+ return names.every(
42
+ (name) => attributeByLocalName(entry, name) === attributeByLocalName(original, name)
43
+ );
44
+ }
45
+ function wellFormedEntry(entry) {
46
+ if (entry.namespaceURI === W_NS && entry.localName === "comment") {
47
+ return attributesWithin(entry, COMMENT_ATTRIBUTES) && readStrictCommentBody(entry) !== null;
48
+ }
49
+ if (entry.namespaceURI === W15_NS && entry.localName === "commentEx") {
50
+ return wellFormedCommentExtension(entry);
51
+ }
52
+ if (entry.namespaceURI === W15_NS && entry.localName === "person") {
53
+ return wellFormedPerson(entry);
54
+ }
55
+ return false;
56
+ }
57
+ function withoutThreadKey(entry) {
58
+ const copy = entry.cloneNode(true);
59
+ if (!isElement(copy)) return serializeXml(entry);
60
+ const last = lastBodyParagraph(copy);
61
+ last?.removeAttributeNS(W14_NS, "paraId");
62
+ return serializeXml(copy);
63
+ }
64
+ function threadKeyAlone(entry, original) {
65
+ const before = lastParagraphId(original);
66
+ const after = lastParagraphId(entry);
67
+ const kept = before === null || after === before;
68
+ return kept && withoutThreadKey(entry) === withoutThreadKey(original);
69
+ }
70
+ function entryAllowed(entry, original, authorId, editableComments, people) {
71
+ if (entry.namespaceURI === W_NS && entry.localName === "comment") {
72
+ const author = attributeByLocalName(entry, "author");
73
+ const recorded = author === null ? null : commentAuthorId(people, author);
74
+ if (original === null) return recorded === authorId;
75
+ const paraId = lastParagraphId(original);
76
+ if (!sameAttributes(entry, original, COMMENT_IDENTITY) || paraId !== null && lastParagraphId(entry) !== paraId) {
77
+ return false;
78
+ }
79
+ return editableComments === "all" || recorded === null || recorded === authorId;
80
+ }
81
+ if (entry.namespaceURI === W15_NS && entry.localName === "commentEx") {
82
+ if (original === null) return true;
83
+ return sameAttributes(entry, original, ["paraId", "paraIdParent"]);
84
+ }
85
+ if (entry.namespaceURI === W15_NS && entry.localName === "person") {
86
+ return original === null && recordedIdentity(entry) === authorId;
87
+ }
88
+ return false;
89
+ }
90
+ var TEXT_NODE = 3;
91
+ var COMPATIBILITY = /* @__PURE__ */ new Map([
92
+ ["xmlns:w14", NAMESPACES.w14],
93
+ ["xmlns:mc", NAMESPACES.mc]
94
+ ]);
95
+ var IGNORABLE = "mc:Ignorable";
96
+ var IGNORED = "w14";
97
+ function isLayout(node) {
98
+ return node.nodeType === TEXT_NODE && /^[ \t\r\n]*$/.test(node.nodeValue ?? "");
99
+ }
100
+ function surroundingNodes(parent) {
101
+ return parent === null ? [] : Array.from(parent.childNodes).filter(
102
+ (node) => !isElement(node) && !isLayout(node)
103
+ );
104
+ }
105
+ function sameNodes(now, was) {
106
+ return now.length === was.length && now.every((node, at) => {
107
+ const original = was[at];
108
+ return node.nodeType === original.nodeType && node.nodeName === original.nodeName && node.nodeValue === original.nodeValue;
109
+ });
110
+ }
111
+ function interEntryContentKept(now, was, rewritesContents) {
112
+ const current = surroundingNodes(now);
113
+ if (sameNodes(current, surroundingNodes(was))) return true;
114
+ return rewritesContents && current.length === 0 && was !== null && Array.from(now.children, serializeXml).join("") !== Array.from(was.children, serializeXml).join("");
115
+ }
116
+ function ignorableTokens(value) {
117
+ return new Set(value.split(/\s+/).filter(Boolean));
118
+ }
119
+ function ignorableKept(now, was) {
120
+ const gained = ignorableTokens(now);
121
+ const had = was === null ? /* @__PURE__ */ new Set() : ignorableTokens(was);
122
+ return Array.from(had).every((token) => gained.has(token)) && Array.from(gained).every((token) => had.has(token) || token === IGNORED);
123
+ }
124
+ function writtenRoot(root, shape) {
125
+ if (root.namespaceURI !== shape.namespace) return false;
126
+ if (root.localName !== shape.rootName) return false;
127
+ return Array.from(root.attributes).every(
128
+ (attr) => attr.name === IGNORABLE ? ignorableKept(attr.value, null) : declarationWritten(attr) || COMPATIBILITY.get(attr.name) === attr.value
129
+ );
130
+ }
131
+ function rootKeptAgainst(now, was, shape) {
132
+ if (was === null) return writtenRoot(now, shape);
133
+ if (now.nodeName !== was.nodeName) return false;
134
+ const had = new Map(
135
+ Array.from(was.attributes, (attr) => [attr.name, attr.value])
136
+ );
137
+ const kept = Array.from(now.attributes).every((attr) => {
138
+ const before = had.get(attr.name) ?? null;
139
+ if (attr.name === IGNORABLE) return ignorableKept(attr.value, before);
140
+ if (before === null) return COMPATIBILITY.get(attr.name) === attr.value;
141
+ return before === attr.value;
142
+ });
143
+ return kept && Array.from(had.keys()).every((name) => now.hasAttribute(name));
144
+ }
145
+ function entriesOf(shape, xml, reading) {
146
+ const entries = /* @__PURE__ */ new Map();
147
+ if (xml === null) return entries;
148
+ const strict = reading === "submitted";
149
+ for (const node of Array.from(parseXml(xml).documentElement.childNodes)) {
150
+ if (!isElement(node)) continue;
151
+ const el = node;
152
+ const named = strict ? el.namespaceURI === shape.namespace && el.localName === shape.localName : el.localName === shape.localName;
153
+ if (!named) {
154
+ if (strict) return null;
155
+ continue;
156
+ }
157
+ const id = attributeByLocalName(el, shape.idAttr);
158
+ if (id === null || entries.has(id)) {
159
+ if (strict) return null;
160
+ continue;
161
+ }
162
+ entries.set(id, { el, xml: serializeXml(el) });
163
+ }
164
+ return entries;
165
+ }
166
+ function arrivedIn(shape, session) {
167
+ return entriesOf(shape, shape.xmlIn(session), "arrived") ?? /* @__PURE__ */ new Map();
168
+ }
169
+ function storyPart(shape) {
170
+ return {
171
+ relType: shape.relType,
172
+ contentType: shape.contentType,
173
+ pathIn: shape.pathIn,
174
+ writePathIn: (session) => shape.pathIn(session) ?? availablePartPath(session.parts, session.mainPartPath, shape.baseName),
175
+ entriesIn: (session, reading) => entriesOf(shape, shape.xmlIn(session), reading),
176
+ rootKept: (before, after) => {
177
+ const submitted = shape.xmlIn(after);
178
+ if (submitted === null) return true;
179
+ const arrived = shape.xmlIn(before);
180
+ const now = parseXml(submitted);
181
+ const was = arrived === null ? null : parseXml(arrived);
182
+ return rootKeptAgainst(
183
+ now.documentElement,
184
+ was?.documentElement ?? null,
185
+ shape
186
+ ) && sameNodes(surroundingNodes(now), surroundingNodes(was)) && interEntryContentKept(
187
+ now.documentElement,
188
+ was?.documentElement ?? null,
189
+ shape.rewritesContents
190
+ );
191
+ },
192
+ referents: shape.referents,
193
+ wellFormed: wellFormedEntry,
194
+ anyonesChange: threadKeyAlone,
195
+ allowed: (entry, original, authorId, options, session) => entryAllowed(
196
+ entry,
197
+ original,
198
+ authorId,
199
+ options.editableComments,
200
+ session.comments.people
201
+ )
202
+ };
203
+ }
204
+ function referencedCommentIds(doc) {
205
+ const ids = /* @__PURE__ */ new Set();
206
+ for (const [id, comment] of commentReferencesIn(doc)) {
207
+ ids.add(id);
208
+ for (const reply of comment.replies) ids.add(reply.id);
209
+ }
210
+ return ids;
211
+ }
212
+ var commentsShape = {
213
+ relType: COMMENTS_REL_TYPE,
214
+ contentType: COMMENTS_CONTENT_TYPE,
215
+ baseName: "comments",
216
+ namespace: W_NS,
217
+ rootName: "comments",
218
+ rewritesContents: true,
219
+ localName: "comment",
220
+ idAttr: "id",
221
+ xmlIn: (session) => session.comments.xml,
222
+ pathIn: (session) => session.comments.partPath,
223
+ referents: (story) => referencedCommentIds(story.doc)
224
+ };
225
+ function commentEntries(story) {
226
+ return Array.from(
227
+ arrivedIn(commentsShape, story.session).values(),
228
+ (entry) => entry.el
229
+ );
230
+ }
231
+ var commentsPart = storyPart(commentsShape);
232
+ var commentsExtendedPart = storyPart({
233
+ relType: COMMENTS_EXTENDED_REL_TYPE,
234
+ contentType: COMMENTS_EXTENDED_CONTENT_TYPE,
235
+ baseName: "commentsExtended",
236
+ namespace: W15_NS,
237
+ rootName: "commentsEx",
238
+ rewritesContents: true,
239
+ localName: "commentEx",
240
+ idAttr: "paraId",
241
+ xmlIn: (session) => session.comments.extendedXml,
242
+ pathIn: (session) => session.comments.extendedPartPath,
243
+ // Thread state stands for a comment, and it is keyed by the comment's own thread key
244
+ referents: (story) => new Set(
245
+ commentEntries(story).flatMap((el) => {
246
+ const key = lastParagraphId(el);
247
+ return key === null ? [] : [key];
248
+ })
249
+ )
250
+ });
251
+ var peoplePart = storyPart({
252
+ relType: PEOPLE_REL_TYPE,
253
+ contentType: PEOPLE_CONTENT_TYPE,
254
+ baseName: "people",
255
+ namespace: W15_NS,
256
+ rootName: "people",
257
+ rewritesContents: false,
258
+ localName: "person",
259
+ idAttr: "author",
260
+ xmlIn: (session) => session.comments.people.xml,
261
+ pathIn: (session) => session.comments.people.partPath,
262
+ // An identity stands for a name somebody writes comments under
263
+ referents: (story) => new Set(
264
+ commentEntries(story).flatMap((el) => {
265
+ const author = attributeByLocalName(el, "author");
266
+ return author === null ? [] : [author];
267
+ })
268
+ )
269
+ });
270
+ var COMMENT_STORY_PARTS = [commentsPart, commentsExtendedPart, peoplePart];
271
+ export {
272
+ COMMENT_STORY_PARTS,
273
+ commentsExtendedPart,
274
+ commentsPart,
275
+ entryAllowed,
276
+ peoplePart,
277
+ threadKeyAlone,
278
+ wellFormedEntry
279
+ };
@@ -9,7 +9,8 @@
9
9
  * it stands and never appended to. Appending a second person for it would leave a file naming two
10
10
  * identities for one name, where a reader keying by name hands one author's comments to the other.
11
11
  */
12
- import { type RelationshipWriter } from "../relationships";
12
+ import type { PartPlanContext } from "../partPlan";
13
+ import type { StoryPartKind } from "../protectionPolicy";
13
14
  import type { SessionStore } from "../session";
14
15
  import type { CommentReferenceData, CommentReplyData } from "./model";
15
16
  export interface ImportedPeople {
@@ -31,9 +32,17 @@ export declare function readPeople(parts: Map<string, Uint8Array>, mainPartPath:
31
32
  * record, and for one the part records under more than one identity.
32
33
  */
33
34
  export declare function commentAuthorId(people: ImportedPeople, author: string): string | null;
35
+ /**
36
+ * The authors the part records no person for at all, each under the name they write as. A name it
37
+ * already records is left as it stands, whichever provider recorded it and whatever it resolves to.
38
+ */
39
+ export declare function unrecordedAuthors(bodies: Iterable<CommentReferenceData | CommentReplyData>, people: ImportedPeople): Map<string, string>;
34
40
  /**
35
41
  * Plans the people part, its relationship and its content type for every identity the current
36
42
  * comments carry that the document has not recorded. Null when it has recorded them all, which
37
43
  * leaves the part as it arrived.
44
+ *
45
+ * `part` is the description the verifier reads the part back through (`./parts`), so where this
46
+ * writes it and what it declares it as are the same facts on both sides.
38
47
  */
39
- export declare function planPeoplePart(bodies: Iterable<CommentReferenceData | CommentReplyData>, session: SessionStore, relationships: RelationshipWriter, currentContentTypes: Uint8Array | undefined): ReadonlyMap<string, Uint8Array> | null;
48
+ export declare function planPeoplePart(part: StoryPartKind, bodies: Iterable<CommentReferenceData | CommentReplyData>, session: SessionStore, context: PartPlanContext): ReadonlyMap<string, Uint8Array> | null;