@portone/docx-editor 0.1.1 → 0.2.1
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/CHANGELOG.md +82 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +17 -5
- package/dist/DocxEditor.d.ts +34 -10
- package/dist/DocxEditor.js +69 -24
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/docx/commentOnlyChange.d.ts +47 -0
- package/dist/docx/commentOnlyChange.js +162 -0
- package/dist/docx/comments/constants.d.ts +11 -3
- package/dist/docx/comments/constants.js +10 -3
- package/dist/docx/comments/contentTypes.d.ts +7 -0
- package/dist/docx/comments/contentTypes.js +38 -0
- package/dist/docx/comments/grammar.d.ts +80 -0
- package/dist/docx/comments/grammar.js +213 -0
- package/dist/docx/comments/model.d.ts +2 -0
- package/dist/docx/comments/model.js +3 -0
- package/dist/docx/comments/people.d.ts +39 -0
- package/dist/docx/comments/people.js +196 -0
- package/dist/docx/comments/reading.d.ts +18 -1
- package/dist/docx/comments/reading.js +33 -20
- package/dist/docx/comments/verifying.d.ts +44 -0
- package/dist/docx/comments/verifying.js +206 -0
- package/dist/docx/comments/writing.d.ts +4 -1
- package/dist/docx/comments/writing.js +84 -77
- package/dist/docx/exportDocx.js +2 -1
- package/dist/docx/formatting/direct.js +3 -6
- package/dist/docx/headersFooters.js +8 -10
- package/dist/docx/importParagraph.js +1 -0
- package/dist/docx/notes.js +3 -5
- package/dist/docx/paraProps.js +51 -46
- package/dist/docx/propsXml.d.ts +20 -2
- package/dist/docx/propsXml.js +72 -32
- package/dist/docx/relationships.js +8 -3
- package/dist/docx/runProps.js +34 -34
- package/dist/docx/sdt.js +16 -14
- package/dist/docx/serializeBlock.js +1 -2
- package/dist/docx/serializeParagraph.d.ts +2 -1
- package/dist/docx/serializeParagraph.js +25 -14
- package/dist/docx/serializeTable.js +38 -24
- package/dist/docx/storyProjection.d.ts +36 -0
- package/dist/docx/storyProjection.js +23 -0
- package/dist/docx/tableFormatting/editing.js +49 -73
- package/dist/docx/tableTemplate.js +24 -3
- package/dist/docx/theme.d.ts +0 -6
- package/dist/docx/theme.js +0 -8
- package/dist/editor/clipboard/images.js +17 -3
- package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
- package/dist/editor/clipboard/inlineFormatting.js +1 -0
- package/dist/editor/commands/breakCommands.js +6 -2
- package/dist/editor/commands/canRunCommand.d.ts +3 -2
- package/dist/editor/commands/canRunCommand.js +2 -2
- package/dist/editor/commands/comments/editing.js +9 -10
- package/dist/editor/commands/comments/model.d.ts +9 -0
- package/dist/editor/commands/comments/reading.d.ts +7 -0
- package/dist/editor/commands/comments/reading.js +14 -0
- package/dist/editor/commands/formatting/editing.js +6 -1
- package/dist/editor/commands/historyCommands.js +14 -5
- package/dist/editor/commands/index.d.ts +7 -1
- package/dist/editor/commands/index.js +4 -0
- package/dist/editor/commands/linkCommands.js +2 -0
- package/dist/editor/commands/lockCommands.js +12 -1
- package/dist/editor/commands/tabCommands.js +4 -2
- package/dist/editor/createEditor.d.ts +13 -3
- package/dist/editor/createEditor.js +23 -15
- package/dist/editor/externalClipboard.js +166 -4
- package/dist/editor/insertImage.js +6 -2
- package/dist/editor/insertTable.js +2 -1
- package/dist/editor/paragraphEdits.d.ts +2 -0
- package/dist/editor/paragraphEdits.js +2 -0
- package/dist/editor/plugins/documentProtection.d.ts +21 -0
- package/dist/editor/plugins/documentProtection.js +44 -0
- package/dist/editor/plugins/imagePaste.js +4 -1
- package/dist/editor/plugins/keymap.d.ts +11 -1
- package/dist/editor/plugins/keymap.js +24 -4
- package/dist/editor/plugins/lockedContent.d.ts +4 -2
- package/dist/editor/plugins/lockedContent.js +2 -2
- package/dist/editor/plugins/tableContextMenu.js +2 -1
- package/dist/editor/plugins/textContextMenu.js +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/numbering/listTemplate.js +32 -10
- package/dist/ooxml/element.d.ts +52 -0
- package/dist/ooxml/element.js +49 -0
- package/dist/ooxml/fragment.d.ts +53 -0
- package/dist/ooxml/fragment.js +76 -0
- package/dist/ooxml/image.js +4 -3
- package/dist/ooxml/names.d.ts +27 -0
- package/dist/ooxml/names.js +29 -0
- package/dist/ooxml/precedence.d.ts +31 -0
- package/dist/ooxml/precedence.js +51 -0
- package/dist/ooxml/xml.d.ts +31 -0
- package/dist/ooxml/xml.js +27 -0
- package/dist/page/blockKinds.d.ts +42 -0
- package/dist/page/blockKinds.js +0 -0
- package/dist/page/measureBlocks.d.ts +14 -1
- package/dist/page/measureBlocks.js +31 -11
- package/dist/page/pageDecorations.d.ts +16 -0
- package/dist/page/pageDecorations.js +1 -0
- package/dist/page/pageLayout.d.ts +8 -43
- package/dist/page/pageLayout.js +28 -51
- package/dist/page/tableMeasurements.d.ts +10 -2
- package/dist/page/tableMeasurements.js +20 -16
- package/dist/page/usePageLayout.d.ts +15 -0
- package/dist/page/usePageLayout.js +30 -2
- package/dist/schema/attrRoles.d.ts +35 -0
- package/dist/schema/attrRoles.js +106 -0
- package/dist/schema/docxSchema.d.ts +6 -0
- package/dist/schema/docxSchema.js +216 -100
- package/dist/schema/editGuard.d.ts +99 -0
- package/dist/schema/editGuard.js +43 -0
- package/dist/schema/guards.d.ts +68 -0
- package/dist/schema/guards.js +82 -0
- package/dist/schema/locks.d.ts +9 -8
- package/dist/schema/locks.js +32 -16
- package/dist/schema/preservedGuards.d.ts +30 -0
- package/dist/schema/preservedGuards.js +50 -0
- package/dist/schema/protection.d.ts +84 -0
- package/dist/schema/protection.js +171 -0
- package/dist/schema/protectionState.d.ts +20 -0
- package/dist/schema/protectionState.js +37 -0
- package/dist/schema/sourceEquality.d.ts +27 -0
- package/dist/schema/sourceEquality.js +65 -0
- package/dist/table/cellFormatting.js +5 -2
- package/dist/table/commands.d.ts +2 -2
- package/dist/table/commands.js +2 -2
- package/dist/table/index.d.ts +2 -2
- package/dist/table/merge.d.ts +2 -2
- package/dist/table/merge.js +3 -3
- package/dist/ui/CommentsPanel.d.ts +3 -3
- package/dist/ui/CommentsPanel.js +26 -18
- package/dist/ui/LinkCard.d.ts +1 -2
- package/dist/ui/LinkCard.js +3 -4
- package/dist/ui/TextMenu.js +15 -10
- package/package.json +5 -2
- package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
- package/dist/editor/plugins/bookmarkProtection.js +0 -28
- package/dist/editor/plugins/noteProtection.d.ts +0 -3
- package/dist/editor/plugins/noteProtection.js +0 -25
|
@@ -0,0 +1,162 @@
|
|
|
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;
|
|
63
|
+
});
|
|
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
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
onlyCommentsChangedBy
|
|
162
|
+
};
|
|
@@ -3,6 +3,14 @@ export declare const COMMENTS_EXTENDED_REL_TYPE = "http://schemas.microsoft.com/
|
|
|
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
5
|
export declare const CONTENT_TYPES_PATH = "[Content_Types].xml";
|
|
6
|
-
export declare const W14_NS
|
|
7
|
-
export declare const W15_NS
|
|
8
|
-
export declare const MC_NS
|
|
6
|
+
export declare const W14_NS: "http://schemas.microsoft.com/office/word/2010/wordml";
|
|
7
|
+
export declare const W15_NS: "http://schemas.microsoft.com/office/word/2012/wordml";
|
|
8
|
+
export declare const MC_NS: "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
9
|
+
export declare const PEOPLE_REL_TYPE = "http://schemas.microsoft.com/office/2011/relationships/people";
|
|
10
|
+
export declare const PEOPLE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.people+xml";
|
|
11
|
+
/**
|
|
12
|
+
* The `w15:providerId` under which this editor records a comment author's identity. An identity
|
|
13
|
+
* another provider recorded, Word's directory above all, is one this editor cannot vouch for and
|
|
14
|
+
* is read as none.
|
|
15
|
+
*/
|
|
16
|
+
export declare const COMMENT_AUTHOR_PROVIDER = "portone-docx-editor";
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
// src/docx/comments/constants.ts
|
|
2
|
+
import { NAMESPACES } from "../../ooxml/names.js";
|
|
2
3
|
import { R_NS } from "../../ooxml/xml.js";
|
|
3
4
|
var COMMENTS_REL_TYPE = `${R_NS}/comments`;
|
|
4
5
|
var COMMENTS_EXTENDED_REL_TYPE = "http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
|
|
5
6
|
var COMMENTS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml";
|
|
6
7
|
var COMMENTS_EXTENDED_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml";
|
|
7
8
|
var CONTENT_TYPES_PATH = "[Content_Types].xml";
|
|
8
|
-
var W14_NS =
|
|
9
|
-
var W15_NS =
|
|
10
|
-
var MC_NS =
|
|
9
|
+
var W14_NS = NAMESPACES.w14;
|
|
10
|
+
var W15_NS = NAMESPACES.w15;
|
|
11
|
+
var MC_NS = NAMESPACES.mc;
|
|
12
|
+
var PEOPLE_REL_TYPE = "http://schemas.microsoft.com/office/2011/relationships/people";
|
|
13
|
+
var PEOPLE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.people+xml";
|
|
14
|
+
var COMMENT_AUTHOR_PROVIDER = "portone-docx-editor";
|
|
11
15
|
export {
|
|
12
16
|
COMMENTS_CONTENT_TYPE,
|
|
13
17
|
COMMENTS_EXTENDED_CONTENT_TYPE,
|
|
14
18
|
COMMENTS_EXTENDED_REL_TYPE,
|
|
15
19
|
COMMENTS_REL_TYPE,
|
|
20
|
+
COMMENT_AUTHOR_PROVIDER,
|
|
16
21
|
CONTENT_TYPES_PATH,
|
|
17
22
|
MC_NS,
|
|
23
|
+
PEOPLE_CONTENT_TYPE,
|
|
24
|
+
PEOPLE_REL_TYPE,
|
|
18
25
|
W14_NS,
|
|
19
26
|
W15_NS
|
|
20
27
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Declares a part an export adds in `[Content_Types].xml`, leaving the original text as it came. */
|
|
2
|
+
/**
|
|
3
|
+
* The content types part with an override for this part, or null when it already declares one.
|
|
4
|
+
* `current` is the part as an earlier addition in the same export left it, so that two additions
|
|
5
|
+
* do not each write over the other's declaration.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withContentType(parts: Map<string, Uint8Array>, partPath: string, contentType: string, current: Uint8Array | undefined): Uint8Array | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/docx/comments/contentTypes.ts
|
|
2
|
+
import { DocxExportError } from "../../ooxml/errors.js";
|
|
3
|
+
import { decodeUtf8, encodeUtf8 } from "../../ooxml/xml.js";
|
|
4
|
+
import { CONTENT_TYPES_PATH } from "./constants.js";
|
|
5
|
+
var TYPES_OPEN_TAG = /<(?:[\w.-]+:)?Types\b[^>]*>/;
|
|
6
|
+
function withContentType(parts, partPath, contentType, current) {
|
|
7
|
+
const original = current ?? parts.get(CONTENT_TYPES_PATH);
|
|
8
|
+
if (!original) {
|
|
9
|
+
throw new DocxExportError(
|
|
10
|
+
"missing-content-types",
|
|
11
|
+
`cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const { text, hadBom } = decodeUtf8(original);
|
|
15
|
+
const partName = `/${partPath}`;
|
|
16
|
+
if (new RegExp(
|
|
17
|
+
`<(?:[\\w.-]+:)?Override[^>]+PartName=["']${partName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`,
|
|
18
|
+
"i"
|
|
19
|
+
).test(text)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const open = TYPES_OPEN_TAG.exec(text);
|
|
23
|
+
if (!open) {
|
|
24
|
+
throw new DocxExportError(
|
|
25
|
+
"malformed-xml",
|
|
26
|
+
`${CONTENT_TYPES_PATH} has no Types element`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const rootName = /^<([^\s>]+)/.exec(open[0])?.[1] ?? "Types";
|
|
30
|
+
const separator = rootName.indexOf(":");
|
|
31
|
+
const prefix = separator < 0 ? "" : `${rootName.slice(0, separator)}:`;
|
|
32
|
+
const declaration = `<${prefix}Override PartName="${partName}" ContentType="${contentType}"/>`;
|
|
33
|
+
const at = open.index + open[0].length;
|
|
34
|
+
return encodeUtf8(text.slice(0, at) + declaration + text.slice(at), hadBom);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
withContentType
|
|
38
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes this editor writes the three comment parts in, and the reading of those shapes.
|
|
3
|
+
*
|
|
4
|
+
* Each entry is written here and judged here, so the two halves cannot drift apart: a body written
|
|
5
|
+
* by hand can be told from one this editor put out only where the writer and the reader agree on
|
|
6
|
+
* what it puts out. `./reading` keeps a lenient flattener for display, which takes whatever a
|
|
7
|
+
* producer wrote and shows what it can; this one judges an entry that came back and says no to
|
|
8
|
+
* everything else.
|
|
9
|
+
*/
|
|
10
|
+
import { type XmlAttr } from "../../ooxml/element";
|
|
11
|
+
import type { CommentReferenceData, CommentReplyData } from "./model";
|
|
12
|
+
/** The attributes this editor writes on a `w:comment`. The thread key goes on the body's paragraph */
|
|
13
|
+
export declare const COMMENT_ATTRIBUTES: ReadonlySet<string>;
|
|
14
|
+
/** The attributes this editor writes on a `w15:commentEx` */
|
|
15
|
+
export declare const COMMENT_EX_ATTRIBUTES: ReadonlySet<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the element carries no attribute outside this set.
|
|
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.
|
|
22
|
+
*/
|
|
23
|
+
export declare function attributesWithin(el: Element, allowed: ReadonlySet<string>): boolean;
|
|
24
|
+
/** The body of a comment, as a paragraph of one run holding the text and the breaks in it */
|
|
25
|
+
export declare function renderCommentBody(text: string, paraId: string | null): string;
|
|
26
|
+
/**
|
|
27
|
+
* The paragraph a thread key belongs on, which is the last one of the body.
|
|
28
|
+
*
|
|
29
|
+
* A body may hold a paragraph of another vocabulary, a picture's DrawingML `a:p` among them, and a
|
|
30
|
+
* key put on that says nothing about the thread. The namespace decides, since a prefix means only
|
|
31
|
+
* what the element it sits on binds it to, and a document may bind the same prefix twice over.
|
|
32
|
+
*/
|
|
33
|
+
export declare function lastBodyParagraph(comment: Element): Element | null;
|
|
34
|
+
/**
|
|
35
|
+
* The entries a part arrived holding, under the id each carries.
|
|
36
|
+
*
|
|
37
|
+
* The writer asks the document which paragraph a key goes on rather than asking the text, because
|
|
38
|
+
* only the document knows what a prefix means where it is written.
|
|
39
|
+
*/
|
|
40
|
+
export declare function arrivedEntries(partXml: string | null): ReadonlyMap<string, Element>;
|
|
41
|
+
/**
|
|
42
|
+
* The entry with the thread key on its body's last paragraph, and unchanged where it has one.
|
|
43
|
+
*
|
|
44
|
+
* Settling a thread or replying to it hangs the state off that key, and an entry that arrived
|
|
45
|
+
* without one has to gain it. Writing the entry afresh instead would put back only what this
|
|
46
|
+
* editor models, so a body holding more than plain text would lose it to a change nobody asked
|
|
47
|
+
* for and nobody made.
|
|
48
|
+
*
|
|
49
|
+
* `arrived` is that entry as the document has it, which is what says which paragraph is a
|
|
50
|
+
* WordprocessingML one and whether it already carries a key under some prefix. The text is only
|
|
51
|
+
* asked where that paragraph is: its opening tag is the one at the same place among the tags named
|
|
52
|
+
* `p`, counted the same way in both.
|
|
53
|
+
*
|
|
54
|
+
* The `w14` prefix is declared on the part rather than here: a part holding any thread state
|
|
55
|
+
* declares it on its root along with the compatibility markup that goes with it (`./writing`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function withThreadKey(commentXml: string, paraId: string, arrived: Element | null): string;
|
|
58
|
+
/**
|
|
59
|
+
* The text of a comment written in that shape, and null for an entry holding anything else: a
|
|
60
|
+
* field, a second run, markup a producer wrapped it in, an attribute this editor does not write.
|
|
61
|
+
*
|
|
62
|
+
* An entry that arrived and was not edited is compared as it stands rather than read here, so
|
|
63
|
+
* saying no to a shape this editor would not have written turns down only a rewrite.
|
|
64
|
+
*/
|
|
65
|
+
/** Whether this editor's writer could have put out this thread state */
|
|
66
|
+
export declare function wellFormedCommentExtension(entry: Element): boolean;
|
|
67
|
+
/** Whether this editor's writer could have put out this recorded identity */
|
|
68
|
+
export declare function wellFormedPerson(entry: Element): boolean;
|
|
69
|
+
/** The identity a recorded person stands for, and null for an entry recording none */
|
|
70
|
+
export declare function recordedIdentity(person: Element): string | null;
|
|
71
|
+
export declare function readStrictCommentBody(comment: Element): string | null;
|
|
72
|
+
/** The thread state of one comment, as this editor writes it into the extended part */
|
|
73
|
+
export declare function renderCommentExtension(comment: CommentReferenceData | CommentReplyData): string;
|
|
74
|
+
/**
|
|
75
|
+
* The identity this editor records for an author, as it writes it into the people part.
|
|
76
|
+
*
|
|
77
|
+
* The prefix and the declaration come from the caller, which is writing into a part whose root
|
|
78
|
+
* already binds them or is being written from nothing.
|
|
79
|
+
*/
|
|
80
|
+
export declare function renderPerson(author: string, userId: string, prefix: string, declaration: XmlAttr | null): string;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// src/docx/comments/grammar.ts
|
|
2
|
+
import {
|
|
3
|
+
attrsText,
|
|
4
|
+
elementXml,
|
|
5
|
+
xmlnsAttr
|
|
6
|
+
} from "../../ooxml/element.js";
|
|
7
|
+
import { qualify, wName } from "../../ooxml/names.js";
|
|
8
|
+
import {
|
|
9
|
+
attributeByLocalName,
|
|
10
|
+
elementChildren,
|
|
11
|
+
escapeXml,
|
|
12
|
+
parseXml,
|
|
13
|
+
W_NS
|
|
14
|
+
} from "../../ooxml/xml.js";
|
|
15
|
+
import { COMMENT_AUTHOR_PROVIDER, W14_NS, W15_NS } from "./constants.js";
|
|
16
|
+
var ELEMENT_NODE = 1;
|
|
17
|
+
var TEXT_NODE = 3;
|
|
18
|
+
var XMLNS_NS = "http://www.w3.org/2000/xmlns/";
|
|
19
|
+
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
20
|
+
var nameKey = (namespace, localName) => `${namespace ?? ""} ${localName}`;
|
|
21
|
+
var COMMENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
22
|
+
nameKey(W_NS, "id"),
|
|
23
|
+
nameKey(W_NS, "author"),
|
|
24
|
+
nameKey(W_NS, "date"),
|
|
25
|
+
nameKey(W_NS, "initials")
|
|
26
|
+
]);
|
|
27
|
+
var COMMENT_EX_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
28
|
+
nameKey(W15_NS, "paraId"),
|
|
29
|
+
nameKey(W15_NS, "paraIdParent"),
|
|
30
|
+
nameKey(W15_NS, "done")
|
|
31
|
+
]);
|
|
32
|
+
var PARAGRAPH_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
33
|
+
nameKey(W14_NS, "paraId")
|
|
34
|
+
]);
|
|
35
|
+
var TEXT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
36
|
+
nameKey(XML_NS, "space")
|
|
37
|
+
]);
|
|
38
|
+
var PERSON_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
39
|
+
nameKey(W15_NS, "author")
|
|
40
|
+
]);
|
|
41
|
+
var PRESENCE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
42
|
+
nameKey(W15_NS, "providerId"),
|
|
43
|
+
nameKey(W15_NS, "userId")
|
|
44
|
+
]);
|
|
45
|
+
var NO_ATTRIBUTES = /* @__PURE__ */ new Set();
|
|
46
|
+
var THREAD_KEY = /^[0-9A-Fa-f]{8}$/;
|
|
47
|
+
function threadKeyOrNone(el, localName) {
|
|
48
|
+
const value = attributeByLocalName(el, localName);
|
|
49
|
+
return value === null || THREAD_KEY.test(value);
|
|
50
|
+
}
|
|
51
|
+
function attributesWithin(el, allowed) {
|
|
52
|
+
return Array.from(el.attributes).every(
|
|
53
|
+
(attr) => attr.namespaceURI === XMLNS_NS || attr.name === "xmlns" || allowed.has(nameKey(attr.namespaceURI, attr.localName))
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function isNamed(el, namespace, localName) {
|
|
57
|
+
return el.namespaceURI === namespace && el.localName === localName;
|
|
58
|
+
}
|
|
59
|
+
function holdsElementsOnly(el) {
|
|
60
|
+
return Array.from(el.childNodes).every(
|
|
61
|
+
(node) => node.nodeType === ELEMENT_NODE
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function holdsTextOnly(el) {
|
|
65
|
+
return Array.from(el.childNodes).every((node) => node.nodeType === TEXT_NODE);
|
|
66
|
+
}
|
|
67
|
+
function renderCommentBody(text, paraId) {
|
|
68
|
+
const lines = text.split("\n");
|
|
69
|
+
const pieces = [];
|
|
70
|
+
lines.forEach((line, index) => {
|
|
71
|
+
if (index > 0) pieces.push(elementXml(wName("br"), []));
|
|
72
|
+
if (line.length > 0 || lines.length === 1) {
|
|
73
|
+
pieces.push(
|
|
74
|
+
elementXml(wName("t"), [["xml:space", "preserve"]], [escapeXml(line)])
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const key = paraId === null ? [] : [xmlnsAttr("w14"), [qualify("w14", "paraId"), paraId]];
|
|
79
|
+
return elementXml(wName("p"), key, [elementXml(wName("r"), [], pieces)]);
|
|
80
|
+
}
|
|
81
|
+
var PARAGRAPH_OR_SKIPPED = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<([^\s<>/:="']+:)?p(?=[\s/>])[^>]*>/g;
|
|
82
|
+
function lastBodyParagraph(comment) {
|
|
83
|
+
const paragraphs = comment.getElementsByTagNameNS(W_NS, "p");
|
|
84
|
+
return paragraphs.length === 0 ? null : paragraphs[paragraphs.length - 1];
|
|
85
|
+
}
|
|
86
|
+
function namedParagraphs(comment) {
|
|
87
|
+
return Array.from(comment.getElementsByTagName("*")).filter(
|
|
88
|
+
(element) => element.localName === "p"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function arrivedEntries(partXml) {
|
|
92
|
+
const entries = /* @__PURE__ */ new Map();
|
|
93
|
+
if (partXml === null) return entries;
|
|
94
|
+
let root;
|
|
95
|
+
try {
|
|
96
|
+
root = parseXml(partXml).documentElement;
|
|
97
|
+
} catch {
|
|
98
|
+
return entries;
|
|
99
|
+
}
|
|
100
|
+
for (const entry of elementChildren(root)) {
|
|
101
|
+
if (entry.namespaceURI !== W_NS || entry.localName !== "comment") continue;
|
|
102
|
+
const id = attributeByLocalName(entry, "id");
|
|
103
|
+
if (id !== null && !entries.has(id)) entries.set(id, entry);
|
|
104
|
+
}
|
|
105
|
+
return entries;
|
|
106
|
+
}
|
|
107
|
+
function withThreadKey(commentXml, paraId, arrived) {
|
|
108
|
+
if (arrived === null) return commentXml;
|
|
109
|
+
const target = lastBodyParagraph(arrived);
|
|
110
|
+
if (target === null) return commentXml;
|
|
111
|
+
if (target.getAttributeNS(W14_NS, "paraId") !== null || target.hasAttribute("w14:paraId")) {
|
|
112
|
+
return commentXml;
|
|
113
|
+
}
|
|
114
|
+
const at = namedParagraphs(arrived).indexOf(target);
|
|
115
|
+
const openings = Array.from(commentXml.matchAll(PARAGRAPH_OR_SKIPPED)).filter(
|
|
116
|
+
(match) => !match[0].startsWith("<!")
|
|
117
|
+
);
|
|
118
|
+
const opening = openings[at];
|
|
119
|
+
if (at === -1 || opening === void 0 || opening.index === void 0) {
|
|
120
|
+
return commentXml;
|
|
121
|
+
}
|
|
122
|
+
const selfClosing = opening[0].endsWith("/>");
|
|
123
|
+
const keyed = opening[0].slice(0, selfClosing ? -2 : -1) + ` ${attrsText([[qualify("w14", "paraId"), paraId]])}` + (selfClosing ? "/>" : ">");
|
|
124
|
+
return commentXml.slice(0, opening.index) + keyed + commentXml.slice(opening.index + opening[0].length);
|
|
125
|
+
}
|
|
126
|
+
function readRunText(run) {
|
|
127
|
+
if (!holdsElementsOnly(run)) return null;
|
|
128
|
+
const pieces = [];
|
|
129
|
+
for (const child of Array.from(run.children)) {
|
|
130
|
+
if (isNamed(child, W_NS, "br")) {
|
|
131
|
+
if (!attributesWithin(child, NO_ATTRIBUTES) || child.childNodes.length > 0) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
pieces.push("\n");
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (!isNamed(child, W_NS, "t")) return null;
|
|
138
|
+
if (!attributesWithin(child, TEXT_ATTRIBUTES) || child.getAttributeNS(XML_NS, "space") !== "preserve" || !holdsTextOnly(child)) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
pieces.push(child.textContent ?? "");
|
|
142
|
+
}
|
|
143
|
+
return pieces.join("");
|
|
144
|
+
}
|
|
145
|
+
function wellFormedCommentExtension(entry) {
|
|
146
|
+
return attributesWithin(entry, COMMENT_EX_ATTRIBUTES) && entry.childNodes.length === 0 && threadKeyOrNone(entry, "paraId") && threadKeyOrNone(entry, "paraIdParent") && // The writer says a thread is settled or open, and nothing else `ST_OnOff` would take
|
|
147
|
+
["0", "1", null].includes(attributeByLocalName(entry, "done"));
|
|
148
|
+
}
|
|
149
|
+
function wellFormedPerson(entry) {
|
|
150
|
+
if (!attributesWithin(entry, PERSON_ATTRIBUTES)) return false;
|
|
151
|
+
if (!holdsElementsOnly(entry)) return false;
|
|
152
|
+
const children = Array.from(entry.children);
|
|
153
|
+
if (children.length !== 1) return false;
|
|
154
|
+
const [presence] = children;
|
|
155
|
+
return isNamed(presence, W15_NS, "presenceInfo") && attributesWithin(presence, PRESENCE_ATTRIBUTES) && attributeByLocalName(presence, "providerId") === COMMENT_AUTHOR_PROVIDER && attributeByLocalName(presence, "userId") !== null && presence.childNodes.length === 0;
|
|
156
|
+
}
|
|
157
|
+
function recordedIdentity(person) {
|
|
158
|
+
const [presence] = Array.from(person.children);
|
|
159
|
+
return presence === void 0 ? null : attributeByLocalName(presence, "userId");
|
|
160
|
+
}
|
|
161
|
+
function readStrictCommentBody(comment) {
|
|
162
|
+
if (!holdsElementsOnly(comment)) return null;
|
|
163
|
+
const paragraphs = Array.from(comment.children);
|
|
164
|
+
if (paragraphs.length !== 1) return null;
|
|
165
|
+
const [paragraph] = paragraphs;
|
|
166
|
+
if (!isNamed(paragraph, W_NS, "p")) return null;
|
|
167
|
+
if (!attributesWithin(paragraph, PARAGRAPH_ATTRIBUTES)) return null;
|
|
168
|
+
if (!holdsElementsOnly(paragraph)) return null;
|
|
169
|
+
const runs = Array.from(paragraph.children);
|
|
170
|
+
if (runs.length !== 1) return null;
|
|
171
|
+
const [run] = runs;
|
|
172
|
+
if (!isNamed(run, W_NS, "r")) return null;
|
|
173
|
+
if (!attributesWithin(run, NO_ATTRIBUTES)) return null;
|
|
174
|
+
return readRunText(run);
|
|
175
|
+
}
|
|
176
|
+
function renderCommentExtension(comment) {
|
|
177
|
+
if (comment.extensionXml !== null) return comment.extensionXml;
|
|
178
|
+
const parent = "parentParaId" in comment ? [[qualify("w15", "paraIdParent"), comment.parentParaId]] : [];
|
|
179
|
+
const done = "resolved" in comment ? [[qualify("w15", "done"), comment.resolved ? "1" : "0"]] : [];
|
|
180
|
+
return elementXml(qualify("w15", "commentEx"), [
|
|
181
|
+
[qualify("w15", "paraId"), comment.paraId],
|
|
182
|
+
...parent,
|
|
183
|
+
...done
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
function renderPerson(author, userId, prefix, declaration) {
|
|
187
|
+
const declared = declaration === null ? [] : [declaration];
|
|
188
|
+
return elementXml(
|
|
189
|
+
`${prefix}person`,
|
|
190
|
+
[...declared, [`${prefix}author`, author]],
|
|
191
|
+
[
|
|
192
|
+
elementXml(`${prefix}presenceInfo`, [
|
|
193
|
+
[`${prefix}providerId`, COMMENT_AUTHOR_PROVIDER],
|
|
194
|
+
[`${prefix}userId`, userId]
|
|
195
|
+
])
|
|
196
|
+
]
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
COMMENT_ATTRIBUTES,
|
|
201
|
+
COMMENT_EX_ATTRIBUTES,
|
|
202
|
+
arrivedEntries,
|
|
203
|
+
attributesWithin,
|
|
204
|
+
lastBodyParagraph,
|
|
205
|
+
readStrictCommentBody,
|
|
206
|
+
recordedIdentity,
|
|
207
|
+
renderCommentBody,
|
|
208
|
+
renderCommentExtension,
|
|
209
|
+
renderPerson,
|
|
210
|
+
wellFormedCommentExtension,
|
|
211
|
+
wellFormedPerson,
|
|
212
|
+
withThreadKey
|
|
213
|
+
};
|
|
@@ -6,6 +6,7 @@ import type { ImportedComments } from "./reading";
|
|
|
6
6
|
export interface CommentReferenceData {
|
|
7
7
|
id: string;
|
|
8
8
|
author: string | null;
|
|
9
|
+
authorId: string | null;
|
|
9
10
|
initials: string | null;
|
|
10
11
|
date: string | null;
|
|
11
12
|
text: string;
|
|
@@ -20,6 +21,7 @@ export interface CommentReferenceData {
|
|
|
20
21
|
export interface CommentReplyData {
|
|
21
22
|
id: string;
|
|
22
23
|
author: string | null;
|
|
24
|
+
authorId: string | null;
|
|
23
25
|
initials: string | null;
|
|
24
26
|
date: string | null;
|
|
25
27
|
text: string;
|
|
@@ -23,6 +23,7 @@ function importedCommentReplies(comments, rootId) {
|
|
|
23
23
|
replies.push({
|
|
24
24
|
id: reply.id,
|
|
25
25
|
author: reply.author,
|
|
26
|
+
authorId: reply.authorId,
|
|
26
27
|
initials: reply.initials,
|
|
27
28
|
date: reply.date,
|
|
28
29
|
text: reply.text,
|
|
@@ -55,6 +56,7 @@ function replyData(value) {
|
|
|
55
56
|
{
|
|
56
57
|
id,
|
|
57
58
|
author: nullableString(entry.author),
|
|
59
|
+
authorId: nullableString(entry.authorId),
|
|
58
60
|
initials: nullableString(entry.initials),
|
|
59
61
|
date: nullableString(entry.date),
|
|
60
62
|
text: nullableString(entry.text) ?? "",
|
|
@@ -74,6 +76,7 @@ function referenceData(node) {
|
|
|
74
76
|
return {
|
|
75
77
|
id,
|
|
76
78
|
author: nullableString(node.attrs.author),
|
|
79
|
+
authorId: nullableString(node.attrs.authorId),
|
|
77
80
|
initials: nullableString(node.attrs.initials),
|
|
78
81
|
date: nullableString(node.attrs.date),
|
|
79
82
|
text: nullableString(node.attrs.text) ?? "",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The people part, where Word records who a comment author is beyond the display name
|
|
3
|
+
* (`w15:people`): a `w15:person` carrying a `w15:presenceInfo` whose `w15:providerId` names the
|
|
4
|
+
* directory that issued `w15:userId`.
|
|
5
|
+
*
|
|
6
|
+
* This editor records the identity a host application hands it under a provider of its own
|
|
7
|
+
* (`COMMENT_AUTHOR_PROVIDER`). The `w15:author` name is the key both Word and this editor read the
|
|
8
|
+
* part by, so a name stands for one identity per file: a name the part already records is read as
|
|
9
|
+
* it stands and never appended to. Appending a second person for it would leave a file naming two
|
|
10
|
+
* identities for one name, where a reader keying by name hands one author's comments to the other.
|
|
11
|
+
*/
|
|
12
|
+
import { type RelationshipWriter } from "../relationships";
|
|
13
|
+
import type { SessionStore } from "../session";
|
|
14
|
+
import type { CommentReferenceData, CommentReplyData } from "./model";
|
|
15
|
+
export interface ImportedPeople {
|
|
16
|
+
partPath: string | null;
|
|
17
|
+
xml: string | null;
|
|
18
|
+
hadBom: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Every author name the part records, mapped to the identity it stands for. Null where this
|
|
21
|
+
* editor cannot vouch for the name: another provider recorded it, or it is recorded twice over
|
|
22
|
+
* under different identities and nothing in the file says which of them wrote what.
|
|
23
|
+
*/
|
|
24
|
+
byAuthor: ReadonlyMap<string, string | null>;
|
|
25
|
+
}
|
|
26
|
+
export declare const NO_PEOPLE: ImportedPeople;
|
|
27
|
+
/** Reads the people part related from the main document story. */
|
|
28
|
+
export declare function readPeople(parts: Map<string, Uint8Array>, mainPartPath: string): ImportedPeople;
|
|
29
|
+
/**
|
|
30
|
+
* The identity this editor recorded for the author of that name. Null for a name it did not
|
|
31
|
+
* record, and for one the part records under more than one identity.
|
|
32
|
+
*/
|
|
33
|
+
export declare function commentAuthorId(people: ImportedPeople, author: string): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Plans the people part, its relationship and its content type for every identity the current
|
|
36
|
+
* comments carry that the document has not recorded. Null when it has recorded them all, which
|
|
37
|
+
* leaves the part as it arrived.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planPeoplePart(bodies: Iterable<CommentReferenceData | CommentReplyData>, session: SessionStore, relationships: RelationshipWriter, currentContentTypes: Uint8Array | undefined): ReadonlyMap<string, Uint8Array> | null;
|