@arbidocs/coedit 0.4.111 → 0.4.112

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/dist/server.js CHANGED
@@ -1,14 +1,536 @@
1
1
  import { Awareness } from 'y-protocols/awareness';
2
2
  import { createServerAutomationHost } from '@docx-editor.dev/core/automation';
3
3
  import { createCollaborationDocumentPort, historyGroupOfJournal } from '@docx-editor.dev/core/collaboration/replication';
4
- import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride } from '@docx-editor.dev/core/store';
4
+ import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml } from '@docx-editor.dev/core/store';
5
+ import { resolveNumberingLevel, resolveStoryListItems, buildStyleCascadeTable, withNumberingStyleLinks, buildNumberingIndex } from '@docx-editor.dev/core/layout';
6
+ import { zipSync, strToU8, strFromU8, unzipSync } from 'fflate';
5
7
  import * as Y3 from 'yjs';
6
8
  import { createCollaborationStatusTracker, safeParticipantColor } from '@docx-editor.dev/core/collaboration';
7
9
 
8
10
  // src/server.ts
9
11
 
12
+ // src/commands/read.ts
13
+ var DEFINED_TERM_STYLE = "DefinedTerm";
14
+ var CROSS_REFERENCE_STYLE = "CrossReference";
15
+ var BROKEN_CROSS_REFERENCE_STYLE = "CrossReferenceBroken";
16
+ var WATCHED_STYLES = /* @__PURE__ */ new Set([BROKEN_CROSS_REFERENCE_STYLE]);
17
+ var attr = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
18
+ var FIELD_ATOM = "\uFFFC";
19
+ var HEADING_STYLE = /^(heading|berschrift|titre|t[ií]tulo|titolo|kop)\s*\d/i;
20
+ function readParagraphs(root) {
21
+ const out = [];
22
+ const body = root.children?.find((n) => n.kind === "body") ?? root;
23
+ const visit = (node, topLevel, pending) => {
24
+ for (const child of node.children ?? []) {
25
+ if (child.kind === "paragraph") {
26
+ out.push(readParagraph(child, topLevel, pending.splice(0)));
27
+ } else if (child.localName === "bookmarkStart") {
28
+ const name = attr(child, "name");
29
+ if (name) pending.push(name);
30
+ } else if (child.localName === "tbl" || child.localName === "tr" || child.localName === "tc") {
31
+ visit(child, false, pending);
32
+ } else if (child.localName === "sdt" || child.localName === "sdtContent") {
33
+ visit(child, topLevel, pending);
34
+ }
35
+ }
36
+ };
37
+ visit(body, true, []);
38
+ return out;
39
+ }
40
+ function readParagraph(node, topLevel, bookmarks) {
41
+ let text = "";
42
+ const links = [];
43
+ let styleId;
44
+ let outline = false;
45
+ let fieldDepth = 0;
46
+ const marks = [];
47
+ let runStyle;
48
+ const fields = [];
49
+ const notes = [];
50
+ let current = null;
51
+ const field = (c) => {
52
+ const type = attr(c, "fldCharType");
53
+ if (type === "begin") {
54
+ if (fieldDepth === 0) {
55
+ current = { offset: text.length, instruction: "", result: "", inResult: false };
56
+ text += FIELD_ATOM;
57
+ }
58
+ fieldDepth++;
59
+ } else if (type === "separate" && fieldDepth === 1 && current) current.inResult = true;
60
+ else if (type === "end") {
61
+ fieldDepth = Math.max(0, fieldDepth - 1);
62
+ if (fieldDepth === 0 && current) {
63
+ const { offset, instruction, result } = current;
64
+ fields.push({ offset, instruction: instruction.trim(), result });
65
+ current = null;
66
+ }
67
+ }
68
+ };
69
+ const walk = (n, link) => {
70
+ for (const c of n.children ?? []) {
71
+ if (c.localName === "fldChar") {
72
+ field(c);
73
+ continue;
74
+ }
75
+ if (c.localName === "fldSimple") {
76
+ if (fieldDepth === 0) text += FIELD_ATOM;
77
+ continue;
78
+ }
79
+ if (c.localName === "instrText") {
80
+ if (fieldDepth === 1 && current)
81
+ current.instruction += (c.children ?? []).map((t) => t.value ?? "").join("");
82
+ continue;
83
+ }
84
+ if (c.kind === "paragraphProperties" || c.localName === "pPr") {
85
+ for (const p of c.children ?? []) {
86
+ if (p.localName === "pStyle") styleId = attr(p, "val");
87
+ if (p.localName === "outlineLvl" && Number(attr(p, "val")) < 9) outline = true;
88
+ }
89
+ continue;
90
+ }
91
+ if (c.localName === "delText" || c.localName === "del" || c.localName === "rPr") continue;
92
+ if (c.localName === "r") {
93
+ const rStyle = c.children?.find((x) => x.localName === "rPr")?.children?.find((x) => x.localName === "rStyle");
94
+ const watched = rStyle && attr(rStyle, "val");
95
+ runStyle = watched && WATCHED_STYLES.has(watched) ? watched : void 0;
96
+ walk(c, link);
97
+ runStyle = void 0;
98
+ continue;
99
+ }
100
+ if ((c.localName === "footnoteReference" || c.localName === "endnoteReference") && fieldDepth === 0) {
101
+ notes.push({
102
+ offset: text.length,
103
+ kind: c.localName === "footnoteReference" ? "footnote" : "endnote",
104
+ id: attr(c, "id") ?? ""
105
+ });
106
+ text += FIELD_ATOM;
107
+ continue;
108
+ }
109
+ if (c.localName === "bookmarkStart") {
110
+ const name = attr(c, "name");
111
+ if (name) bookmarks.push(name);
112
+ continue;
113
+ }
114
+ if (c.localName === "hyperlink") {
115
+ const anchor = attr(c, "anchor");
116
+ const next = anchor ? { anchor, text: "", offset: text.length } : void 0;
117
+ walk(c, next ?? link);
118
+ if (next) links.push(next);
119
+ continue;
120
+ }
121
+ if (c.kind === "textValue") {
122
+ if (fieldDepth > 0) {
123
+ const open = current;
124
+ if (fieldDepth === 1 && open?.inResult) open.result += c.value ?? "";
125
+ continue;
126
+ }
127
+ if (runStyle && c.value) {
128
+ const last = marks[marks.length - 1];
129
+ if (last && last.styleId === runStyle && last.offset + last.text.length === text.length)
130
+ last.text += c.value;
131
+ else marks.push({ styleId: runStyle, offset: text.length, text: c.value });
132
+ }
133
+ text += c.value ?? "";
134
+ if (link) link.text += c.value ?? "";
135
+ continue;
136
+ }
137
+ if (c.localName === "tab" && fieldDepth === 0) text += " ";
138
+ walk(c, link);
139
+ }
140
+ };
141
+ walk(node);
142
+ const paraId = attr(node, "paraId");
143
+ return {
144
+ id: node.id,
145
+ ...paraId ? { paraId } : {},
146
+ text,
147
+ heading: outline || !!styleId && HEADING_STYLE.test(styleId),
148
+ styleId,
149
+ topLevel,
150
+ bookmarks,
151
+ links,
152
+ ...fields.length ? { fields } : {},
153
+ ...marks.length ? { marks } : {},
154
+ ...notes.length ? { notes } : {}
155
+ };
156
+ }
157
+ function documentNumbering(source) {
158
+ let numbering;
159
+ let cascade;
160
+ const index = () => {
161
+ if (!numbering) {
162
+ cascade = buildStyleCascadeTable(
163
+ source.stylesRoot(),
164
+ void 0,
165
+ source.settingsRoot()
166
+ );
167
+ numbering = withNumberingStyleLinks(
168
+ buildNumberingIndex(source.numberingRoot()),
169
+ cascade
170
+ );
171
+ }
172
+ return numbering;
173
+ };
174
+ return {
175
+ listItems: () => {
176
+ const body = source.root().children?.find((n) => n.kind === "body");
177
+ const items = resolveStoryListItems(
178
+ body?.children ?? [],
179
+ index(),
180
+ cascade
181
+ );
182
+ const out = /* @__PURE__ */ new Map();
183
+ for (const [id, item] of items)
184
+ out.set(id, {
185
+ numId: item.numId,
186
+ ilvl: item.ilvl,
187
+ markerText: item.markerText,
188
+ numFmt: item.numFmt
189
+ });
190
+ return out;
191
+ },
192
+ levelOf: (numId, ilvl) => {
193
+ const level = resolveNumberingLevel(index(), numId, ilvl)?.level;
194
+ return level ? { lvlText: level.lvlText, numFmt: level.numFmt } : null;
195
+ }
196
+ };
197
+ }
198
+ var W_NAMESPACES = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
199
+ var INVALID_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F￾￿]/g;
200
+ var esc = (text) => text.replace(INVALID_XML, "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
201
+ var documentXml = (body) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
202
+ <w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`;
203
+ var stylesXml = (defaults, styles) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
204
+ <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${defaults}${styles}</w:styles>`;
205
+ var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
206
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`;
207
+ var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
208
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`;
209
+ var documentRels = (more = "") => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
210
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${more}</Relationships>`;
211
+ var coreXml = (title, at) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
212
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc(
213
+ title
214
+ )}</dc:title><dcterms:created xsi:type="dcterms:W3CDTF">${at}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">${at}</dcterms:modified></cp:coreProperties>`;
215
+ function docxPackage(parts) {
216
+ const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\.\d{3}Z$/, "Z");
217
+ return zipSync({
218
+ "[Content_Types].xml": strToU8(CONTENT_TYPES),
219
+ "_rels/.rels": strToU8(ROOT_RELS),
220
+ "docProps/core.xml": strToU8(coreXml(parts.title, at)),
221
+ "word/document.xml": strToU8(parts.document),
222
+ "word/styles.xml": strToU8(parts.styles),
223
+ "word/_rels/document.xml.rels": strToU8(documentRels(parts.relationships))
224
+ });
225
+ }
226
+
227
+ // src/commands/references.ts
228
+ var RUN_PARTS = /* @__PURE__ */ new Set(["rPr", "t", "tab", "br", "lastRenderedPageBreak"]);
229
+ var SKIPPED = /* @__PURE__ */ new Set(["pPr", "bookmarkStart", "bookmarkEnd", "proofErr", "permStart", "permEnd"]);
230
+ function serialize(node) {
231
+ if (node.kind === "textValue") return esc(node.value ?? "");
232
+ const name = `${node.prefix ?? "w"}:${node.localName}`;
233
+ const attrs = (node.attributes ?? []).map((a) => ` ${a.prefix ? `${a.prefix}:` : ""}${a.localName}="${esc(a.value)}"`).join("");
234
+ const children = (node.children ?? []).map((c) => serialize(c)).join("");
235
+ return children ? `<${name}${attrs}>${children}</${name}>` : `<${name}${attrs}/>`;
236
+ }
237
+ function paragraphRunsXml(paragraph) {
238
+ const runs = [];
239
+ for (const child of paragraph.children ?? []) {
240
+ if (child.localName && SKIPPED.has(child.localName)) continue;
241
+ if (child.localName !== "r") return null;
242
+ if ((child.children ?? []).some((c) => !c.localName || !RUN_PARTS.has(c.localName))) return null;
243
+ runs.push(serialize(child));
244
+ }
245
+ return runs.length ? runs.join("") : null;
246
+ }
247
+ var W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
248
+ var TemplateError = class extends Error {
249
+ constructor(message) {
250
+ super(message);
251
+ this.name = "TemplateError";
252
+ }
253
+ };
254
+ function parse(xml, part = "part") {
255
+ const read = readXml(xml, { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 });
256
+ if (!read.ok) throw new TemplateError(`${part} is not readable XML (${read.reason})`);
257
+ const convert = (node) => node.type === "text" ? node.value : { name: node.name, attrs: { ...node.attributes }, children: node.children.map(convert) };
258
+ const root = read.nodes.map(convert).find((n) => typeof n !== "string");
259
+ if (!root) throw new TemplateError(`${part} has no root element`);
260
+ const w = root.attrs["xmlns:w"];
261
+ if (w !== void 0 && w !== W_NS) throw new TemplateError(`${part} binds w: to ${w}`);
262
+ return root;
263
+ }
264
+ var escText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
265
+ var escAttr = (s) => escText(s).replace(/"/g, "&quot;");
266
+ function serialize2(root) {
267
+ const out = ['<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'];
268
+ const walk = (node) => {
269
+ if (typeof node === "string") return void out.push(escText(node));
270
+ out.push("<", node.name);
271
+ for (const [k, v] of Object.entries(node.attrs)) out.push(" ", k, '="', escAttr(v), '"');
272
+ if (node.children.length === 0) return void out.push("/>");
273
+ out.push(">");
274
+ node.children.forEach(walk);
275
+ out.push("</", node.name, ">");
276
+ };
277
+ walk(root);
278
+ return out.join("");
279
+ }
280
+ var el = (name, attrs = {}, children = []) => ({
281
+ name,
282
+ attrs,
283
+ children
284
+ });
285
+ var isEl = (n) => typeof n === "object" && n !== null;
286
+ var kids = (e, name) => e.children.filter((c) => isEl(c) && (name === void 0 || c.name === name));
287
+ var kid = (e, name) => e ? kids(e, name)[0] : void 0;
288
+
289
+ // src/template/pkg.ts
290
+ var REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
291
+ var RT = {
292
+ officeDocument: `${REL}/officeDocument`};
293
+ var Pkg = class _Pkg {
294
+ files;
295
+ types;
296
+ constructor(files) {
297
+ this.files = files;
298
+ const ct = this.find("[Content_Types].xml");
299
+ if (!ct) throw new TemplateError("not a Word package: no [Content_Types].xml");
300
+ this.types = parse(strFromU8(files.get(ct)), "[Content_Types].xml");
301
+ }
302
+ static read(bytes) {
303
+ let files;
304
+ try {
305
+ files = unzipSync(bytes);
306
+ } catch {
307
+ throw new TemplateError("not a Word package: not a zip");
308
+ }
309
+ return new _Pkg(new Map(Object.entries(files).filter(([n]) => !n.endsWith("/"))));
310
+ }
311
+ write() {
312
+ this.files.set(
313
+ this.find("[Content_Types].xml") ?? "[Content_Types].xml",
314
+ strToU8(serialize2(this.types))
315
+ );
316
+ return zipSync(Object.fromEntries(this.files), { level: 6 });
317
+ }
318
+ /** The stored name of a part, matched case-insensitively as OPC does. */
319
+ find(name) {
320
+ const key = name.replace(/^\//, "").toLowerCase();
321
+ for (const n of this.files.keys()) if (n.toLowerCase() === key) return n;
322
+ return void 0;
323
+ }
324
+ has(name) {
325
+ return this.find(name) !== void 0;
326
+ }
327
+ text(name) {
328
+ const n = this.find(name);
329
+ return n === void 0 ? void 0 : strFromU8(this.files.get(n));
330
+ }
331
+ xml(name) {
332
+ const t = this.text(name);
333
+ return t === void 0 ? void 0 : parse(t, name);
334
+ }
335
+ setXml(name, root, contentType) {
336
+ this.files.set(this.find(name) ?? name, strToU8(serialize2(root)));
337
+ if (contentType) this.setContentType(name, contentType);
338
+ }
339
+ setBytes(name, bytes, contentType) {
340
+ this.files.set(this.find(name) ?? name, bytes);
341
+ if (contentType) this.setContentType(name, contentType);
342
+ }
343
+ remove(name) {
344
+ const n = this.find(name);
345
+ if (n === void 0) return;
346
+ this.files.delete(n);
347
+ this.types.children = this.types.children.filter(
348
+ (c) => !(typeof c !== "string" && c.name === "Override" && c.attrs.PartName.toLowerCase() === `/${n.toLowerCase()}`)
349
+ );
350
+ this.files.delete(relsName(n));
351
+ }
352
+ contentType(name) {
353
+ const part = `/${name.replace(/^\//, "")}`.toLowerCase();
354
+ const override = kids(this.types, "Override").find(
355
+ (o) => o.attrs.PartName.toLowerCase() === part
356
+ );
357
+ if (override) return override.attrs.ContentType;
358
+ const ext = part.slice(part.lastIndexOf(".") + 1);
359
+ return kids(this.types, "Default").find((d) => d.attrs.Extension.toLowerCase() === ext)?.attrs.ContentType;
360
+ }
361
+ setContentType(name, type) {
362
+ const part = `/${name.replace(/^\//, "")}`;
363
+ const override = kids(this.types, "Override").find(
364
+ (o) => o.attrs.PartName.toLowerCase() === part.toLowerCase()
365
+ );
366
+ if (override) override.attrs.ContentType = type;
367
+ else this.types.children.push(el("Override", { PartName: part, ContentType: type }));
368
+ }
369
+ /** A `Default` for an extension (media), unless one exists. */
370
+ ensureDefault(ext, type) {
371
+ if (!kids(this.types, "Default").some(
372
+ (d) => d.attrs.Extension.toLowerCase() === ext.toLowerCase()
373
+ ))
374
+ this.types.children.unshift(el("Default", { Extension: ext, ContentType: type }));
375
+ }
376
+ rels(owner) {
377
+ const root = this.xml(relsName(owner));
378
+ if (!root) return [];
379
+ return kids(root, "Relationship").map((r) => ({
380
+ id: r.attrs.Id,
381
+ type: r.attrs.Type,
382
+ target: r.attrs.Target,
383
+ external: r.attrs.TargetMode === "External"
384
+ }));
385
+ }
386
+ setRels(owner, rels) {
387
+ const root = el(
388
+ "Relationships",
389
+ { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
390
+ rels.map(
391
+ (r) => el("Relationship", {
392
+ Id: r.id,
393
+ Type: r.type,
394
+ Target: r.target,
395
+ ...r.external ? { TargetMode: "External" } : {}
396
+ })
397
+ )
398
+ );
399
+ this.setXml(relsName(owner), root);
400
+ }
401
+ /** Add a relationship from `owner`; returns its new id. */
402
+ addRel(owner, type, target, external = false) {
403
+ const rels = this.rels(owner);
404
+ const id = freshId(new Set(rels.map((r) => r.id)), "rIdT");
405
+ rels.push({ id, type, target, external });
406
+ this.setRels(owner, rels);
407
+ return id;
408
+ }
409
+ /** The part a relationship of `owner` points at (internal targets only). */
410
+ target(owner, rel) {
411
+ return resolveTarget(owner, rel.target);
412
+ }
413
+ /** The first internal part of a relationship type from `owner`. */
414
+ partOf(owner, type) {
415
+ const rel = this.rels(owner).find((r) => r.type === type && !r.external);
416
+ return rel && this.find(this.target(owner, rel));
417
+ }
418
+ /** The main document part (`word/document.xml` by convention). */
419
+ get main() {
420
+ const main = this.partOf("", RT.officeDocument);
421
+ if (!main) throw new TemplateError("not a Word package: no main document part");
422
+ return main;
423
+ }
424
+ };
425
+ function relsName(owner) {
426
+ const slash = owner.lastIndexOf("/");
427
+ return `${owner.slice(0, slash + 1)}_rels/${owner.slice(slash + 1)}.rels`;
428
+ }
429
+ function resolveTarget(owner, target) {
430
+ if (target.startsWith("/")) return target.slice(1);
431
+ const base = owner.slice(0, owner.lastIndexOf("/") + 1).split("/").filter(Boolean);
432
+ for (const seg of target.split("/")) {
433
+ if (seg === "..") base.pop();
434
+ else if (seg !== "." && seg !== "") base.push(seg);
435
+ }
436
+ return base.join("/");
437
+ }
438
+ function freshId(taken, prefix) {
439
+ for (let i = 1; ; i++) if (!taken.has(`${prefix}${i}`)) return `${prefix}${i}`;
440
+ }
441
+
442
+ // src/template/track.ts
443
+ var TRACKED = /* @__PURE__ */ new Set(["w:ins", "w:del", "w:moveFrom", "w:moveTo"]);
444
+ function trackInsertion(fragment, revision, covered) {
445
+ const pkg = Pkg.read(fragment);
446
+ const main = pkg.main;
447
+ const root = pkg.xml(main);
448
+ const body = kid(root, "w:body");
449
+ if (!root || !body) return fragment;
450
+ const attrs = { "w:id": "1", "w:author": revision.author, "w:date": revision.date };
451
+ const lastParagraph = body.children.filter(isEl).findLast((c) => c.name === "w:p");
452
+ const visit = (node) => {
453
+ node.children = node.children.map((child) => {
454
+ if (!isEl(child)) return child;
455
+ if (child.name === "w:r") return TRACKED.has(node.name) ? child : el("w:ins", attrs, [child]);
456
+ if (TRACKED.has(child.name)) return child;
457
+ if (child.name === "w:tr") {
458
+ markRow(child, attrs);
459
+ return child;
460
+ }
461
+ visit(child);
462
+ if (child.name === "w:p" && (covered || child !== lastParagraph)) markParagraph(child, attrs);
463
+ return child;
464
+ });
465
+ };
466
+ visit(body);
467
+ pkg.setXml(main, root);
468
+ return pkg.write();
469
+ }
470
+ function markParagraph(p, attrs) {
471
+ let pPr = kid(p, "w:pPr");
472
+ if (!pPr) p.children.unshift(pPr = el("w:pPr"));
473
+ let rPr = kid(pPr, "w:rPr");
474
+ if (!rPr) {
475
+ rPr = el("w:rPr");
476
+ const at = pPr.children.findIndex(
477
+ (c) => isEl(c) && (c.name === "w:sectPr" || c.name === "w:pPrChange")
478
+ );
479
+ pPr.children.splice(at < 0 ? pPr.children.length : at, 0, rPr);
480
+ }
481
+ if (!kid(rPr, "w:ins")) rPr.children.unshift(el("w:ins", attrs));
482
+ }
483
+ function markRow(tr, attrs) {
484
+ let trPr = kid(tr, "w:trPr");
485
+ if (!trPr) {
486
+ trPr = el("w:trPr");
487
+ const after = tr.children.findIndex((c) => isEl(c) && c.name === "w:tblPrEx");
488
+ tr.children.splice(after + 1, 0, trPr);
489
+ }
490
+ if (kid(trPr, "w:ins")) return;
491
+ const at = trPr.children.findIndex((c) => isEl(c) && c.name === "w:trPrChange");
492
+ trPr.children.splice(at < 0 ? trPr.children.length : at, 0, el("w:ins", attrs));
493
+ }
494
+
495
+ // src/commands/fragment.ts
496
+ var CHARACTER_STYLES = {
497
+ [DEFINED_TERM_STYLE]: `<w:style w:type="character" w:customStyle="1" w:styleId="${DEFINED_TERM_STYLE}"><w:name w:val="Defined Term"/><w:rPr><w:u w:val="dotted" w:color="7F7F7F"/></w:rPr></w:style>`,
498
+ [CROSS_REFERENCE_STYLE]: `<w:style w:type="character" w:customStyle="1" w:styleId="${CROSS_REFERENCE_STYLE}"><w:name w:val="Cross Reference"/><w:rPr><w:color w:val="1F4E79"/><w:u w:val="dotted" w:color="8EA9C1"/></w:rPr></w:style>`,
499
+ [BROKEN_CROSS_REFERENCE_STYLE]: `<w:style w:type="character" w:customStyle="1" w:styleId="${BROKEN_CROSS_REFERENCE_STYLE}"><w:name w:val="Cross Reference Broken"/><w:basedOn w:val="${CROSS_REFERENCE_STYLE}"/><w:rPr><w:color w:val="C00000"/><w:u w:val="wave" w:color="C00000"/></w:rPr></w:style>`
500
+ };
501
+ function stylesUsed(body) {
502
+ return [...new Set([...body.matchAll(/<w:rStyle w:val="([^"]+)"/g)].map((m) => m[1]))];
503
+ }
504
+ var fragmentStyles = (ids) => stylesXml(
505
+ "",
506
+ `<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>${ids.map((id) => CHARACTER_STYLES[id] ?? "").join("")}`
507
+ );
508
+ function fragmentPackage(body, covered, {
509
+ revision,
510
+ relationships,
511
+ styles = stylesUsed(body)
512
+ } = {}) {
513
+ const untracked = docxPackage({
514
+ document: documentXml(body),
515
+ styles: fragmentStyles(styles),
516
+ title: "Defined terms",
517
+ relationships
518
+ });
519
+ return revision ? trackInsertion(untracked, revision, covered) : untracked;
520
+ }
521
+ var attr2 = (node, name) => node.attributes?.find((a) => a.localName === name)?.value;
522
+ function documentStyleIds(root) {
523
+ const ids = /* @__PURE__ */ new Set();
524
+ for (const style of root?.children ?? [])
525
+ if (style.localName === "style") {
526
+ const id = attr2(style, "styleId");
527
+ if (id) ids.add(id);
528
+ }
529
+ return ids;
530
+ }
531
+
10
532
  // src/rich.ts
11
- var WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
533
+ var WML2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
12
534
  var WRAPPERS = /* @__PURE__ */ new Set([
13
535
  "ins",
14
536
  "del",
@@ -23,24 +545,24 @@ var WRAPPERS = /* @__PURE__ */ new Set([
23
545
  "sdtContent"
24
546
  ]);
25
547
  var TEXT_ELEMENTS = /* @__PURE__ */ new Set(["t", "delText", "instrText"]);
26
- var isEl = (n) => typeof n !== "string";
27
- var isWml = (el, local) => el.ns === WML && el.local === local;
28
- function describe(el) {
29
- const desc = { ns: el.ns, local: el.local, attrs: el.attrs, bindings: el.bindings };
30
- if (el.prefix !== void 0) desc.prefix = el.prefix;
548
+ var isEl2 = (n) => typeof n !== "string";
549
+ var isWml = (el2, local) => el2.ns === WML2 && el2.local === local;
550
+ function describe(el2) {
551
+ const desc = { ns: el2.ns, local: el2.local, attrs: el2.attrs, bindings: el2.bindings };
552
+ if (el2.prefix !== void 0) desc.prefix = el2.prefix;
31
553
  return desc;
32
554
  }
33
555
  function withChildren(desc, children, id) {
34
- const el = {
556
+ const el2 = {
35
557
  ns: desc.ns,
36
558
  local: desc.local,
37
559
  attrs: desc.attrs,
38
560
  bindings: desc.bindings,
39
561
  children
40
562
  };
41
- if (desc.prefix !== void 0) el.prefix = desc.prefix;
42
- if (id !== void 0) el._id = id;
43
- return el;
563
+ if (desc.prefix !== void 0) el2.prefix = desc.prefix;
564
+ if (id !== void 0) el2._id = id;
565
+ return el2;
44
566
  }
45
567
  var counter = 0;
46
568
  function newRunId() {
@@ -57,7 +579,7 @@ function encodeRun(run, wrappers, out) {
57
579
  let rPr = null;
58
580
  let start = 0;
59
581
  const first = children[0];
60
- if (first !== void 0 && isEl(first) && isWml(first, "rPr")) {
582
+ if (first !== void 0 && isEl2(first) && isWml(first, "rPr")) {
61
583
  rPr = first;
62
584
  start = 1;
63
585
  }
@@ -65,9 +587,9 @@ function encodeRun(run, wrappers, out) {
65
587
  let produced = false;
66
588
  for (let i = start; i < children.length; i++) {
67
589
  const child = children[i];
68
- if (!isEl(child)) fail();
590
+ if (!isEl2(child)) fail();
69
591
  const only = child.children[0];
70
- if (child.ns === WML && TEXT_ELEMENTS.has(child.local) && child.children.length === 1 && typeof only === "string" && only.length > 0) {
592
+ if (child.ns === WML2 && TEXT_ELEMENTS.has(child.local) && child.children.length === 1 && typeof only === "string" && only.length > 0) {
71
593
  const tx = describe(child);
72
594
  for (let c = 0; c < only.length; c++) out.push({ v: only[c], a: { ...base, tx, k: null } });
73
595
  } else {
@@ -77,16 +599,16 @@ function encodeRun(run, wrappers, out) {
77
599
  }
78
600
  if (!produced)
79
601
  out.push({
80
- v: { x: { ns: WML, local: "e", attrs: [], bindings: [], children: [] } },
602
+ v: { x: { ns: WML2, local: "e", attrs: [], bindings: [], children: [] } },
81
603
  a: { ...base, tx: null, k: "e" }
82
604
  });
83
605
  }
84
606
  function encodeInline(children, wrappers, out) {
85
607
  for (const child of children) {
86
- if (!isEl(child)) fail();
87
- if (child.ns === WML && child.local === "r") {
608
+ if (!isEl2(child)) fail();
609
+ if (child.ns === WML2 && child.local === "r") {
88
610
  encodeRun(child, wrappers, out);
89
- } else if (child.ns === WML && WRAPPERS.has(child.local)) {
611
+ } else if (child.ns === WML2 && WRAPPERS.has(child.local)) {
90
612
  const before = out.length;
91
613
  const desc = { ...describe(child), head: [], id: child._id ?? newRunId() };
92
614
  let ok = true;
@@ -102,10 +624,10 @@ function encodeInline(children, wrappers, out) {
102
624
  out.length = before;
103
625
  out.push(atom(child, wrappers));
104
626
  }
105
- } else if (child.ns === WML && child.local === "sdt") {
627
+ } else if (child.ns === WML2 && child.local === "sdt") {
106
628
  const last = child.children[child.children.length - 1];
107
629
  const head = child.children.slice(0, -1);
108
- if (last !== void 0 && isEl(last) && isWml(last, "sdtContent") && head.every(isEl)) {
630
+ if (last !== void 0 && isEl2(last) && isWml(last, "sdtContent") && head.every(isEl2)) {
109
631
  const before = out.length;
110
632
  const sdt = {
111
633
  ...describe(child),
@@ -132,14 +654,14 @@ function encodeInline(children, wrappers, out) {
132
654
  }
133
655
  }
134
656
  }
135
- function atom(el, wrappers) {
136
- return { v: { x: el }, a: { r: null, ra: null, ri: null, w: wrappers, tx: null, k: "a" } };
657
+ function atom(el2, wrappers) {
658
+ return { v: { x: el2 }, a: { r: null, ra: null, ri: null, w: wrappers, tx: null, k: "a" } };
137
659
  }
138
660
  function encodeParagraph(children) {
139
661
  const head = [];
140
662
  let start = 0;
141
663
  const first = children[0];
142
- if (first !== void 0 && isEl(first) && isWml(first, "pPr")) {
664
+ if (first !== void 0 && isEl2(first) && isWml(first, "pPr")) {
143
665
  head.push(first);
144
666
  start = 1;
145
667
  }
@@ -316,8 +838,8 @@ function writeUnits(text, units) {
316
838
  function isRichParagraph(map) {
317
839
  return map.has("inline");
318
840
  }
319
- function isParagraphEl(el) {
320
- return el.ns === WML && el.local === "p";
841
+ function isParagraphEl(el2) {
842
+ return el2.ns === WML2 && el2.local === "p";
321
843
  }
322
844
  function fieldsOf(map, xml) {
323
845
  map.set("ns", xml.ns);
@@ -339,7 +861,7 @@ function createRichParagraph(xml, encoded) {
339
861
  const map = new Y3.Map();
340
862
  fieldsOf(map, xml);
341
863
  const children = new Y3.Array();
342
- children.push(encoded.head.map((el) => xmlToLive(el)));
864
+ children.push(encoded.head.map((el2) => xmlToLive(el2)));
343
865
  map.set("children", children);
344
866
  const inline = new Y3.Text();
345
867
  writeUnits(inline, encoded.units);
@@ -378,14 +900,14 @@ function liveToXml(map) {
378
900
  var escapeText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
379
901
  var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\t/g, "&#9;").replace(/\n/g, "&#10;").replace(/\r/g, "&#13;");
380
902
  var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
381
- function serializeXml(el, scope) {
903
+ function serializeXml(el2, scope) {
382
904
  const inScope = new Map(scope);
383
- for (const b of el.bindings) inScope.set(b.prefix, b.ns);
384
- let out = `<${qualified(el.prefix, el.local)}`;
385
- for (const b of el.bindings) {
905
+ for (const b of el2.bindings) inScope.set(b.prefix, b.ns);
906
+ let out = `<${qualified(el2.prefix, el2.local)}`;
907
+ for (const b of el2.bindings) {
386
908
  out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
387
909
  }
388
- for (const a of el.attrs) {
910
+ for (const a of el2.attrs) {
389
911
  let p = a.prefix;
390
912
  if (a.ns !== "" && !p) {
391
913
  for (const [candidate, uri] of inScope) {
@@ -397,20 +919,20 @@ function serializeXml(el, scope) {
397
919
  }
398
920
  out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
399
921
  }
400
- if (el.children.length === 0) return `${out}/>`;
922
+ if (el2.children.length === 0) return `${out}/>`;
401
923
  out += ">";
402
- for (const child of el.children) {
924
+ for (const child of el2.children) {
403
925
  out += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
404
926
  }
405
- return `${out}</${qualified(el.prefix, el.local)}>`;
927
+ return `${out}</${qualified(el2.prefix, el2.local)}>`;
406
928
  }
407
- function serializeLive(el, scope) {
408
- if (isRichParagraph(el)) return serializeXml(liveToXml(el), scope);
409
- const prefix = el.get("prefix");
410
- const local = el.get("local");
411
- const bindings = el.get("bindings").toArray();
412
- const attrs = el.get("attrs").toArray();
413
- const children = el.get("children").toArray();
929
+ function serializeLive(el2, scope) {
930
+ if (isRichParagraph(el2)) return serializeXml(liveToXml(el2), scope);
931
+ const prefix = el2.get("prefix");
932
+ const local = el2.get("local");
933
+ const bindings = el2.get("bindings").toArray();
934
+ const attrs = el2.get("attrs").toArray();
935
+ const children = el2.get("children").toArray();
414
936
  const inScope = new Map(scope);
415
937
  for (const b of bindings) inScope.set(b.prefix, b.ns);
416
938
  let out = `<${qualified(prefix, local)}`;
@@ -458,8 +980,8 @@ function canonicalToXml(node) {
458
980
  if (node.prefix !== void 0) xml.prefix = node.prefix;
459
981
  return xml;
460
982
  }
461
- function encodeElement(el) {
462
- return xmlToLive(canonicalToXml(el));
983
+ function encodeElement(el2) {
984
+ return xmlToLive(canonicalToXml(el2));
463
985
  }
464
986
  function bootstrap(doc, pkg) {
465
987
  doc.transact(() => {
@@ -644,14 +1166,14 @@ function materialize(doc, cache, changed = null) {
644
1166
  sinkSafe: validateExternalTarget(r.rawTarget).ok
645
1167
  });
646
1168
  }
647
- const relsName = relsPartNameFor(owner);
1169
+ const relsName2 = relsPartNameFor(owner);
648
1170
  const read = readOoxmlPart(relationshipsXml(records), {
649
- name: relsName,
1171
+ name: relsName2,
650
1172
  contentType: RELS_CONTENT_TYPE
651
1173
  });
652
1174
  if (!read.ok)
653
1175
  throw new MaterializeError(`relationships of ${owner} are not valid: ${read.reason}`);
654
- parts.set(relsName, read.part);
1176
+ parts.set(relsName2, read.part);
655
1177
  }
656
1178
  const contentTypes = {
657
1179
  defaults: new Map(doc.getMap("ctDefaults")),
@@ -717,14 +1239,14 @@ var RegistryError = class extends Error {
717
1239
  this.name = "RegistryError";
718
1240
  }
719
1241
  };
720
- function childrenOf(el) {
721
- return el.get("children");
1242
+ function childrenOf(el2) {
1243
+ return el2.get("children");
722
1244
  }
723
- function attrsOf(el) {
724
- return el.get("attrs");
1245
+ function attrsOf(el2) {
1246
+ return el2.get("attrs");
725
1247
  }
726
- function bindingsOf(el) {
727
- return el.get("bindings");
1248
+ function bindingsOf(el2) {
1249
+ return el2.get("bindings");
728
1250
  }
729
1251
  var IDENTIFIED = /* @__PURE__ */ new Set([
730
1252
  "r",
@@ -751,7 +1273,7 @@ function plainToXml(node) {
751
1273
  children: node.children.map(plainToXml)
752
1274
  };
753
1275
  if (node.prefix !== void 0) xml.prefix = node.prefix;
754
- if (node.ns === WML && IDENTIFIED.has(node.local)) {
1276
+ if (node.ns === WML2 && IDENTIFIED.has(node.local)) {
755
1277
  node.rid ??= newRunId();
756
1278
  xml._id = node.rid;
757
1279
  }
@@ -952,7 +1474,7 @@ var NodeRegistry = class {
952
1474
  const stored = head.toArray().map((child) => liveToXml(child));
953
1475
  if (JSON.stringify(stored) !== JSON.stringify(encoded.head)) {
954
1476
  head.delete(0, head.length);
955
- head.push(encoded.head.map((el) => xmlToLive(el)));
1477
+ head.push(encoded.head.map((el2) => xmlToLive(el2)));
956
1478
  }
957
1479
  const inline = live.get("inline");
958
1480
  applyUnits(inline, readUnits(inline), encoded.units);
@@ -1086,14 +1608,14 @@ function attrJson(qname, value) {
1086
1608
  function setAttribute(registry, id, qname, value) {
1087
1609
  const entry = registry.require(id);
1088
1610
  if ("plain" in entry) {
1089
- const el = asPlainElement(entry.plain, id);
1090
- const index2 = el.attrs.findIndex((a) => sameAttr(a, qname));
1611
+ const el2 = asPlainElement(entry.plain, id);
1612
+ const index2 = el2.attrs.findIndex((a) => sameAttr(a, qname));
1091
1613
  if (value === null) {
1092
- if (index2 >= 0) el.attrs.splice(index2, 1);
1614
+ if (index2 >= 0) el2.attrs.splice(index2, 1);
1093
1615
  } else if (index2 >= 0) {
1094
- el.attrs[index2] = attrJson(qname, value);
1616
+ el2.attrs[index2] = attrJson(qname, value);
1095
1617
  } else {
1096
- el.attrs.push(attrJson(qname, value));
1618
+ el2.attrs.push(attrJson(qname, value));
1097
1619
  }
1098
1620
  return;
1099
1621
  }
@@ -1109,12 +1631,12 @@ function setNamespaceBinding(registry, id, prefix, uri) {
1109
1631
  const entry = registry.require(id);
1110
1632
  const binding = uri === null ? null : { prefix, ns: uri };
1111
1633
  if ("plain" in entry) {
1112
- const el = asPlainElement(entry.plain, id);
1113
- const index2 = el.bindings.findIndex((b) => b.prefix === prefix);
1114
- if (index2 >= 0) el.bindings.splice(index2, 1);
1634
+ const el2 = asPlainElement(entry.plain, id);
1635
+ const index2 = el2.bindings.findIndex((b) => b.prefix === prefix);
1636
+ if (index2 >= 0) el2.bindings.splice(index2, 1);
1115
1637
  if (binding) {
1116
- if (index2 >= 0) el.bindings.splice(index2, 0, binding);
1117
- else el.bindings.push(binding);
1638
+ if (index2 >= 0) el2.bindings.splice(index2, 0, binding);
1639
+ else el2.bindings.push(binding);
1118
1640
  }
1119
1641
  return;
1120
1642
  }
@@ -1837,8 +2359,8 @@ function openRoomStore(options) {
1837
2359
  return {
1838
2360
  store,
1839
2361
  session,
1840
- transact(build) {
1841
- const result = store.transact({ kind: "body" }, build, {
2362
+ transact(build, scope = { kind: "body" }) {
2363
+ const result = store.transact(scope, build, {
1842
2364
  origin: ORIGIN_IDS.mutationAgent,
1843
2365
  actorId: options.identity.actorId,
1844
2366
  recordsHistory: false
@@ -1871,7 +2393,94 @@ function openRoomStore(options) {
1871
2393
  throw error;
1872
2394
  }
1873
2395
  }
2396
+ function roomCommandsPort(room, { revision, actorId } = {}) {
2397
+ const view = () => room.view();
2398
+ const root = () => view().part().root;
2399
+ const numbering = () => documentNumbering({
2400
+ root,
2401
+ stylesRoot: () => view().stylesRoot(),
2402
+ numberingRoot: () => view().numberingRoot(),
2403
+ settingsRoot: () => view().settingsRoot()
2404
+ });
2405
+ let caret = null;
2406
+ const at = () => {
2407
+ if (!caret) throw new Error("No position to write at.");
2408
+ return caret;
2409
+ };
2410
+ const apply = (op) => {
2411
+ const result = room.transact((ctx) => {
2412
+ ctx.apply(op);
2413
+ });
2414
+ if (!result.ok) throw new Error(`The editor refused the change (${op.op}).`);
2415
+ };
2416
+ const find = (node, id) => {
2417
+ if (node.id === id) return node;
2418
+ for (const child of node.children ?? []) {
2419
+ const found = find(child, id);
2420
+ if (found) return found;
2421
+ }
2422
+ return void 0;
2423
+ };
2424
+ return {
2425
+ paragraphs: () => readParagraphs(root()),
2426
+ caret: () => caret,
2427
+ setCaret: (point) => {
2428
+ caret = point;
2429
+ },
2430
+ deleteRange: (from, to) => {
2431
+ if (from.paragraphId !== to.paragraphId) throw new Error("A deletion stays in one paragraph.");
2432
+ apply({
2433
+ op: "deleteText",
2434
+ paragraphId: from.paragraphId,
2435
+ start: from.offset,
2436
+ end: to.offset,
2437
+ ...revision ? { revision } : {}
2438
+ });
2439
+ },
2440
+ paste: (body, _text, covered, relationships) => {
2441
+ const point = at();
2442
+ const defined = documentStyleIds(view().stylesRoot());
2443
+ const styles = stylesUsed(body).filter((id) => !defined.has(id));
2444
+ const result = room.store.applyFragmentPaste(
2445
+ { kind: "body" },
2446
+ {
2447
+ paragraphId: point.paragraphId,
2448
+ offset: point.offset,
2449
+ fragmentBytes: fragmentPackage(body, covered, { revision, relationships, styles }),
2450
+ lastMarkCovered: covered,
2451
+ actorId
2452
+ }
2453
+ );
2454
+ if (result.ok) room.flush();
2455
+ return result.ok;
2456
+ },
2457
+ splitParagraph: () => {
2458
+ const point = at();
2459
+ apply({ op: "splitParagraph", paragraphId: point.paragraphId, offset: point.offset });
2460
+ const paragraphs = readParagraphs(root());
2461
+ const next = paragraphs[paragraphs.findIndex((p) => p.id === point.paragraphId) + 1];
2462
+ caret = next ? { paragraphId: next.id, offset: 0 } : point;
2463
+ },
2464
+ typeText: (text) => {
2465
+ const point = at();
2466
+ apply({
2467
+ op: "insertText",
2468
+ paragraphId: point.paragraphId,
2469
+ offset: point.offset,
2470
+ text,
2471
+ ...revision ? { revision } : {}
2472
+ });
2473
+ caret = { paragraphId: point.paragraphId, offset: point.offset + text.length };
2474
+ },
2475
+ listItems: () => numbering().listItems(),
2476
+ levelOf: (numId, ilvl) => numbering().levelOf(numId, ilvl),
2477
+ runsXml: (paragraphId) => {
2478
+ const node = find(root(), paragraphId);
2479
+ return node ? paragraphRunsXml(node) : null;
2480
+ }
2481
+ };
2482
+ }
1874
2483
 
1875
- export { COLLAB_PROTOCOL, CollaborationCompatibilityError, DOCUMENT_COLLABORATION_VERSIONS, assertCompatibleVersions, assertRoomFormat, normalizeDocx, openRoomAutomation, openRoomStore, readRoom, seedRoom };
2484
+ export { COLLAB_PROTOCOL, CollaborationCompatibilityError, DOCUMENT_COLLABORATION_VERSIONS, assertCompatibleVersions, assertRoomFormat, normalizeDocx, openRoomAutomation, openRoomStore, readRoom, roomCommandsPort, seedRoom };
1876
2485
  //# sourceMappingURL=server.js.map
1877
2486
  //# sourceMappingURL=server.js.map