@arbidocs/coedit 0.5.21 → 0.5.23

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.d.cts CHANGED
@@ -6,7 +6,7 @@ import { TreePackageStore, PackageTransactResult, HeadlessDocumentView, ImageDec
6
6
  import { R as ReferencesPort } from './port-BbN5OZHx.cjs';
7
7
  import { R as Revision } from './track-eYC94jTp.cjs';
8
8
  import { D as DocxCollaborationSession } from './session-WGvWs6yO.cjs';
9
- export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-DXiFQMA6.cjs';
9
+ export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.cjs';
10
10
  import 'y-protocols/awareness';
11
11
  import '@docx-editor.dev/core/collaboration/replication';
12
12
 
package/dist/server.d.ts CHANGED
@@ -6,7 +6,7 @@ import { TreePackageStore, PackageTransactResult, HeadlessDocumentView, ImageDec
6
6
  import { R as ReferencesPort } from './port-BbN5OZHx.js';
7
7
  import { R as Revision } from './track-eYC94jTp.js';
8
8
  import { D as DocxCollaborationSession } from './session-WGvWs6yO.js';
9
- export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-DXiFQMA6.js';
9
+ export { C as COLLAB_PROTOCOL, D as DOCUMENT_COLLABORATION_VERSIONS } from './versions-XSYTVmOU.js';
10
10
  import 'y-protocols/awareness';
11
11
  import '@docx-editor.dev/core/collaboration/replication';
12
12
 
package/dist/server.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Awareness } from 'y-protocols/awareness';
2
2
  import { createServerAutomationHost } from '@docx-editor.dev/core/automation';
3
3
  import { createCollaborationDocumentPort, historyGroupOfJournal, mapOffsetAcrossText } from '@docx-editor.dev/core/collaboration/replication';
4
- import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml } from '@docx-editor.dev/core/store';
4
+ import { TreePackageStore, validateRasterHeader, headlessViewOfStore, ORIGIN_IDS, readOoxmlPackage, writeOoxmlPackage, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml, XML_NAMESPACE_URI } from '@docx-editor.dev/core/store';
5
5
  import { resolveNumberingLevel, resolveStoryListItems, buildStyleCascadeTable, withNumberingStyleLinks, buildNumberingIndex } from '@docx-editor.dev/core/layout';
6
6
  import { zipSync, strToU8, strFromU8, unzipSync } from 'fflate';
7
- import * as Y3 from 'yjs';
7
+ import * as Y from 'yjs';
8
8
  import { createCollaborationStatusTracker, safeParticipantColor } from '@docx-editor.dev/core/collaboration';
9
9
 
10
10
  // src/server.ts
@@ -874,6 +874,92 @@ function writeUnits(text, units) {
874
874
  function isRichParagraph(map) {
875
875
  return map.has("inline");
876
876
  }
877
+ var PACKED = "xml";
878
+ function isPackedElement(map) {
879
+ return map.has(PACKED);
880
+ }
881
+ function isCompact(map) {
882
+ return isRichParagraph(map) || isPackedElement(map);
883
+ }
884
+ var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
885
+ function packable(xml) {
886
+ if (xml.ns === WML2 && TEXT_BEARING.has(xml.local)) return false;
887
+ return xml.children.every((c) => typeof c === "string" || packable(c));
888
+ }
889
+ function createPacked(xml, scope) {
890
+ const map = new Y.Map();
891
+ map.set(PACKED, serializeXml(xml, scope));
892
+ return map;
893
+ }
894
+ function repack(map, xml) {
895
+ const next = serializeXml(xml, scopeOf(map));
896
+ if (map.get(PACKED) !== next) map.set(PACKED, next);
897
+ }
898
+ function scopeOf(node) {
899
+ const chain = [];
900
+ let parent = node.parent;
901
+ while (parent instanceof Y.Array) {
902
+ const owner = parent.parent;
903
+ if (!(owner instanceof Y.Map)) break;
904
+ chain.push(owner);
905
+ parent = owner.parent;
906
+ }
907
+ const scope = /* @__PURE__ */ new Map();
908
+ for (const el2 of chain.reverse()) {
909
+ for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
910
+ }
911
+ return scope;
912
+ }
913
+ function scopeWithin(el2) {
914
+ const scope = scopeOf(el2);
915
+ if (!isPackedElement(el2)) {
916
+ for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
917
+ }
918
+ return scope;
919
+ }
920
+ var unpackLimits = { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 };
921
+ function unpack(text, scope) {
922
+ const read = readXml(text, unpackLimits);
923
+ if (!read.ok) throw new Error(`collab: a packed element is not readable XML (${read.reason})`);
924
+ const convert = (node, outer) => {
925
+ const bindings = [];
926
+ const named = [];
927
+ for (const [name, value] of Object.entries(node.attributes)) {
928
+ if (name === "xmlns") bindings.push({ prefix: "", ns: value });
929
+ else if (name.startsWith("xmlns:")) bindings.push({ prefix: name.slice(6), ns: value });
930
+ else named.push([name, value]);
931
+ }
932
+ const inner = new Map(outer);
933
+ for (const b of bindings) inner.set(b.prefix, b.ns);
934
+ const resolve = (prefix) => {
935
+ if (prefix === "xml") return XML_NAMESPACE_URI;
936
+ const ns = inner.get(prefix);
937
+ if (ns === void 0)
938
+ throw new Error(`collab: prefix ${prefix} is not bound in a packed element`);
939
+ return ns;
940
+ };
941
+ const colon = node.name.indexOf(":");
942
+ const xml = {
943
+ ns: colon < 0 ? inner.get("") ?? "" : resolve(node.name.slice(0, colon)),
944
+ local: colon < 0 ? node.name : node.name.slice(colon + 1),
945
+ attrs: named.map(([name, value]) => {
946
+ const at = name.indexOf(":");
947
+ if (at < 0) return { ns: "", local: name, value };
948
+ const prefix = name.slice(0, at);
949
+ return { ns: resolve(prefix), local: name.slice(at + 1), prefix, value };
950
+ }),
951
+ bindings,
952
+ children: node.children.map(
953
+ (child) => child.type === "text" ? child.value : convert(child, inner)
954
+ )
955
+ };
956
+ if (colon >= 0) xml.prefix = node.name.slice(0, colon);
957
+ return xml;
958
+ };
959
+ const root = read.nodes.find((n) => n.type === "element");
960
+ if (!root || root.type !== "element") throw new Error("collab: a packed element is empty");
961
+ return convert(root, scope);
962
+ }
877
963
  function isParagraphEl(el2) {
878
964
  return el2.ns === WML2 && el2.local === "p";
879
965
  }
@@ -881,47 +967,53 @@ function fieldsOf(map, xml) {
881
967
  map.set("ns", xml.ns);
882
968
  map.set("local", xml.local);
883
969
  if (xml.prefix !== void 0) map.set("prefix", xml.prefix);
884
- const attrs = new Y3.Array();
970
+ const attrs = new Y.Array();
885
971
  attrs.push(xml.attrs.map((a) => ({ ...a })));
886
972
  map.set("attrs", attrs);
887
- const bindings = new Y3.Array();
973
+ const bindings = new Y.Array();
888
974
  bindings.push(xml.bindings.map((b) => ({ ...b })));
889
975
  map.set("bindings", bindings);
890
976
  }
891
977
  function newText(value) {
892
- const text = new Y3.Text();
978
+ const text = new Y.Text();
893
979
  text.insert(0, value);
894
980
  return text;
895
981
  }
896
- function createRichParagraph(xml, encoded) {
897
- const map = new Y3.Map();
982
+ function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
983
+ const map = new Y.Map();
898
984
  fieldsOf(map, xml);
899
- const children = new Y3.Array();
900
- children.push(encoded.head.map((el2) => xmlToLive(el2)));
985
+ const inner = withBindings(scope, xml.bindings);
986
+ const children = new Y.Array();
987
+ children.push(encoded.head.map((el2) => xmlToLive(el2, inner)));
901
988
  map.set("children", children);
902
- const inline = new Y3.Text();
989
+ const inline = new Y.Text();
903
990
  writeUnits(inline, encoded.units);
904
991
  map.set("inline", inline);
905
992
  return map;
906
993
  }
907
- function xmlToLive(xml) {
994
+ function withBindings(scope, bindings) {
995
+ const inner = new Map(scope);
996
+ for (const b of bindings) inner.set(b.prefix, b.ns);
997
+ return inner;
998
+ }
999
+ function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
908
1000
  if (isParagraphEl(xml)) {
909
1001
  const encoded = encodeParagraph(xml.children);
910
- if (encoded) return createRichParagraph(xml, encoded);
1002
+ if (encoded) return createRichParagraph(xml, encoded, scope);
911
1003
  }
912
- return xmlToGenericLive(xml);
913
- }
914
- function xmlToGenericLive(xml) {
915
- const map = new Y3.Map();
1004
+ if (!root && packable(xml)) return createPacked(xml, scope);
1005
+ const map = new Y.Map();
916
1006
  fieldsOf(map, xml);
917
- const children = new Y3.Array();
918
- children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c)));
1007
+ const inner = withBindings(scope, xml.bindings);
1008
+ const children = new Y.Array();
1009
+ children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
919
1010
  map.set("children", children);
920
1011
  return map;
921
1012
  }
922
1013
  function liveToXml(map) {
1014
+ if (isPackedElement(map)) return unpack(map.get(PACKED), scopeOf(map));
923
1015
  const prefix = map.get("prefix");
924
- const stored = map.get("children").toArray().map((child) => child instanceof Y3.Text ? child.toString() : liveToXml(child));
1016
+ const stored = map.get("children").toArray().map((child) => child instanceof Y.Text ? child.toString() : liveToXml(child));
925
1017
  const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
926
1018
  const xml = {
927
1019
  ns: map.get("ns"),
@@ -936,14 +1028,13 @@ function liveToXml(map) {
936
1028
  var escapeText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
937
1029
  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;");
938
1030
  var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
939
- function serializeXml(el2, scope) {
940
- const inScope = new Map(scope);
941
- for (const b of el2.bindings) inScope.set(b.prefix, b.ns);
942
- let out = `<${qualified(el2.prefix, el2.local)}`;
943
- for (const b of el2.bindings) {
1031
+ function startTag(prefix, local, bindings, attrs, scope) {
1032
+ const inScope = withBindings(scope, bindings);
1033
+ let out = `<${qualified(prefix, local)}`;
1034
+ for (const b of bindings) {
944
1035
  out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
945
1036
  }
946
- for (const a of el2.attrs) {
1037
+ for (const a of attrs) {
947
1038
  let p = a.prefix;
948
1039
  if (a.ns !== "" && !p) {
949
1040
  for (const [candidate, uri] of inScope) {
@@ -955,48 +1046,41 @@ function serializeXml(el2, scope) {
955
1046
  }
956
1047
  out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
957
1048
  }
1049
+ return [out, inScope];
1050
+ }
1051
+ function serializeXml(el2, scope) {
1052
+ const [out, inScope] = startTag(el2.prefix, el2.local, el2.bindings, el2.attrs, scope);
958
1053
  if (el2.children.length === 0) return `${out}/>`;
959
- out += ">";
1054
+ let body = "";
960
1055
  for (const child of el2.children) {
961
- out += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
1056
+ body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
962
1057
  }
963
- return `${out}</${qualified(el2.prefix, el2.local)}>`;
1058
+ return `${out}>${body}</${qualified(el2.prefix, el2.local)}>`;
964
1059
  }
965
1060
  function serializeLive(el2, scope) {
966
- if (isRichParagraph(el2)) return serializeXml(liveToXml(el2), scope);
1061
+ if (isPackedElement(el2)) return el2.get(PACKED);
967
1062
  const prefix = el2.get("prefix");
968
1063
  const local = el2.get("local");
969
1064
  const bindings = el2.get("bindings").toArray();
970
1065
  const attrs = el2.get("attrs").toArray();
971
1066
  const children = el2.get("children").toArray();
972
- const inScope = new Map(scope);
973
- for (const b of bindings) inScope.set(b.prefix, b.ns);
974
- let out = `<${qualified(prefix, local)}`;
975
- for (const b of bindings) {
976
- out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
977
- }
978
- for (const a of attrs) {
979
- let p = a.prefix;
980
- if (a.ns !== "" && !p) {
981
- for (const [candidate, uri] of inScope) {
982
- if (candidate !== "" && uri === a.ns) {
983
- p = candidate;
984
- break;
985
- }
986
- }
987
- }
988
- out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
989
- }
990
- if (children.length === 0) return `${out}/>`;
991
- out += ">";
1067
+ const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
1068
+ const inline = isRichParagraph(el2) ? decodeInline(readUnits(el2.get("inline"))) : [];
1069
+ if (children.length === 0 && inline.length === 0) return `${out}/>`;
1070
+ let body = "";
992
1071
  for (const child of children) {
993
- out += child instanceof Y3.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
1072
+ body += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
994
1073
  }
995
- return `${out}</${qualified(prefix, local)}>`;
1074
+ for (const child of inline) body += serializeXml(child, inScope);
1075
+ return `${out}>${body}</${qualified(prefix, local)}>`;
996
1076
  }
997
1077
 
998
1078
  // src/encoding.ts
999
- var COLLAB_FORMAT = "arbi-docx/1";
1079
+ var COLLAB_FORMAT = "arbi-docx/2";
1080
+ var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
1081
+ function isReadableFormat(format) {
1082
+ return format !== void 0 && READABLE_FORMATS.has(format);
1083
+ }
1000
1084
  function isText(node) {
1001
1085
  return node.kind === "textValue";
1002
1086
  }
@@ -1017,7 +1101,7 @@ function canonicalToXml(node) {
1017
1101
  return xml;
1018
1102
  }
1019
1103
  function encodeElement(el2) {
1020
- return xmlToLive(canonicalToXml(el2));
1104
+ return xmlToLive(canonicalToXml(el2), /* @__PURE__ */ new Map(), true);
1021
1105
  }
1022
1106
  function bootstrap(doc, pkg) {
1023
1107
  doc.transact(() => {
@@ -1035,7 +1119,7 @@ function bootstrap(doc, pkg) {
1035
1119
  }
1036
1120
  const rels = doc.getMap("rels");
1037
1121
  for (const [owner, records] of pkg.relationships) {
1038
- const byId = new Y3.Map();
1122
+ const byId = new Y.Map();
1039
1123
  for (const r of records) {
1040
1124
  byId.set(r.id, {
1041
1125
  ownerPart: r.ownerPart,
@@ -1051,7 +1135,7 @@ function bootstrap(doc, pkg) {
1051
1135
  for (const e of pkg.externalTargets) {
1052
1136
  let byId = rels.get(e.ownerPart);
1053
1137
  if (!byId) {
1054
- byId = new Y3.Map();
1138
+ byId = new Y.Map();
1055
1139
  rels.set(e.ownerPart, byId);
1056
1140
  }
1057
1141
  byId.set(e.id, {
@@ -1371,7 +1455,7 @@ var NodeRegistry = class {
1371
1455
  require(id) {
1372
1456
  let entry = this.entries.get(id) ?? this.resolveInsideParagraph(id);
1373
1457
  if (!entry) throw new RegistryError(`journal names unknown node ${id}`);
1374
- if ("live" in entry && entry.live instanceof Y3.Map && isRichParagraph(entry.live)) {
1458
+ if ("live" in entry && entry.live instanceof Y.Map && isCompact(entry.live)) {
1375
1459
  this.openParagraph(entry.live);
1376
1460
  entry = this.entries.get(id);
1377
1461
  }
@@ -1389,7 +1473,7 @@ var NodeRegistry = class {
1389
1473
  for (let length = path.length - 1; length >= 1; length--) {
1390
1474
  const ancestor = this.entries.get(id.slice(0, hash + 1) + path.slice(0, length).join("."));
1391
1475
  if (!ancestor) continue;
1392
- if ("live" in ancestor && ancestor.live instanceof Y3.Map && isRichParagraph(ancestor.live)) {
1476
+ if ("live" in ancestor && ancestor.live instanceof Y.Map && isCompact(ancestor.live)) {
1393
1477
  this.openParagraph(ancestor.live);
1394
1478
  return this.entries.get(id);
1395
1479
  }
@@ -1421,13 +1505,13 @@ var NodeRegistry = class {
1421
1505
  /**
1422
1506
  * Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
1423
1507
  * `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
1424
- * paragraph is registered as one node; its inner nodes appear when it is opened.
1508
+ * paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
1425
1509
  */
1426
1510
  registerTree(partName, root) {
1427
1511
  const walk = (node, id) => {
1428
1512
  this.registerLive(id, node);
1429
- if (node instanceof Y3.Text) return;
1430
- if (isRichParagraph(node)) return;
1513
+ if (node instanceof Y.Text) return;
1514
+ if (isCompact(node)) return;
1431
1515
  childrenOf(node).forEach((child, index) => walk(child, `${id}.${index}`));
1432
1516
  };
1433
1517
  walk(root, `${partName}#0`);
@@ -1484,7 +1568,7 @@ var NodeRegistry = class {
1484
1568
  for (const child of children) this.own(child, owner);
1485
1569
  if (owner) this.dirty.add(owner);
1486
1570
  }
1487
- /** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
1571
+ /** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
1488
1572
  flushRich() {
1489
1573
  for (const live of [...this.dirty]) {
1490
1574
  this.dirty.delete(live);
@@ -1495,6 +1579,11 @@ var NodeRegistry = class {
1495
1579
  writeBack(record) {
1496
1580
  const { live, root } = record;
1497
1581
  const xml = plainToXml(root);
1582
+ if (isPackedElement(live)) {
1583
+ if (packable(xml)) repack(live, xml);
1584
+ else this.degrade(record);
1585
+ return;
1586
+ }
1498
1587
  const encoded = encodeParagraph(xml.children);
1499
1588
  if (!encoded) {
1500
1589
  this.degrade(record);
@@ -1511,21 +1600,22 @@ var NodeRegistry = class {
1511
1600
  bindings.push(xml.bindings.map((b) => ({ ...b })));
1512
1601
  }
1513
1602
  const head = childrenOf(live);
1514
- const stored = head.toArray().map((child) => liveToXml(child));
1515
- if (JSON.stringify(stored) !== JSON.stringify(encoded.head)) {
1603
+ const scope = scopeWithin(live);
1604
+ const stored = head.toArray().map((child) => serializeLive(child, scope)).join("");
1605
+ if (stored !== encoded.head.map((el2) => serializeXml(el2, scope)).join("")) {
1516
1606
  head.delete(0, head.length);
1517
- head.push(encoded.head.map((el2) => xmlToLive(el2)));
1607
+ head.push(encoded.head.map((el2) => xmlToLive(el2, scope)));
1518
1608
  }
1519
1609
  const inline = live.get("inline");
1520
1610
  applyUnits(inline, readUnits(inline), encoded.units);
1521
1611
  }
1522
- /** The paragraph can no longer be held as rich text: store it as plain elements instead. */
1612
+ /** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
1523
1613
  degrade(record) {
1524
1614
  const parent = record.live.parent;
1525
- if (!(parent instanceof Y3.Array))
1615
+ if (!(parent instanceof Y.Array))
1526
1616
  throw new RegistryError("a paragraph outside a parent cannot change form");
1527
1617
  const index = parent.toArray().indexOf(record.live);
1528
- const generic = this.toGenericLive(record.root);
1618
+ const generic = this.toGenericLive(record.root, scopeOf(record.live));
1529
1619
  this.open.delete(record.live);
1530
1620
  parent.delete(index, 1);
1531
1621
  parent.insert(index, [generic]);
@@ -1540,8 +1630,8 @@ var NodeRegistry = class {
1540
1630
  snapshotLive(node) {
1541
1631
  const id = this.liveIds.get(node);
1542
1632
  if (id === void 0) throw new RegistryError("live node has no registered id");
1543
- if (node instanceof Y3.Text) return { type: "text", id, value: node.toString() };
1544
- if (isRichParagraph(node)) {
1633
+ if (node instanceof Y.Text) return { type: "text", id, value: node.toString() };
1634
+ if (isCompact(node)) {
1545
1635
  return this.open.get(node)?.root ?? xmlToPlain(liveToXml(node), id);
1546
1636
  }
1547
1637
  const prefix = node.get("prefix");
@@ -1569,8 +1659,8 @@ var NodeRegistry = class {
1569
1659
  if (id !== void 0 && this.isCurrentLive(id, node)) {
1570
1660
  this.entries.set(id, { plain });
1571
1661
  }
1572
- if (node instanceof Y3.Text || plain.type === "text") return;
1573
- if (isRichParagraph(node)) {
1662
+ if (node instanceof Y.Text || plain.type === "text") return;
1663
+ if (isCompact(node)) {
1574
1664
  this.open.delete(node);
1575
1665
  this.dirty.delete(node);
1576
1666
  return;
@@ -1578,53 +1668,61 @@ var NodeRegistry = class {
1578
1668
  const liveChildren = childrenOf(node).toArray();
1579
1669
  liveChildren.forEach((child, index) => this.detachInto(child, plain.children[index]));
1580
1670
  }
1581
- /** Build Yjs types for a plain subtree and register every node as live. */
1582
- toLive(plain) {
1671
+ /**
1672
+ * Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
1673
+ * live. A rich paragraph or packed element stays open on the plain tree, which is already current.
1674
+ * A part's `root` is never packed.
1675
+ */
1676
+ toLive(plain, scope = /* @__PURE__ */ new Map(), root = false) {
1583
1677
  if (plain.type === "text") {
1584
- const text = new Y3.Text();
1678
+ const text = new Y.Text();
1585
1679
  text.insert(0, plain.value);
1586
1680
  this.registerLive(plain.id, text);
1587
1681
  return text;
1588
1682
  }
1683
+ const xml = plainToXml(plain);
1684
+ let map;
1589
1685
  if (isParagraphEl(plain)) {
1590
- const xml = plainToXml(plain);
1591
1686
  const encoded = encodeParagraph(xml.children);
1592
- if (encoded) {
1593
- const map = createRichParagraph(xml, encoded);
1594
- this.liveIds.set(map, plain.id);
1595
- this.registerPlain(plain);
1596
- this.own(plain, map);
1597
- this.open.set(map, { live: map, root: plain, id: plain.id });
1598
- return map;
1599
- }
1687
+ if (encoded) map = createRichParagraph(xml, encoded, scope);
1688
+ }
1689
+ if (!map && !root && packable(xml)) map = xmlToLive(xml, scope);
1690
+ if (map) {
1691
+ this.liveIds.set(map, plain.id);
1692
+ this.registerPlain(plain);
1693
+ this.own(plain, map);
1694
+ this.open.set(map, { live: map, root: plain, id: plain.id });
1695
+ return map;
1600
1696
  }
1601
- return this.toGenericLive(plain);
1697
+ return this.toGenericLive(plain, scope);
1602
1698
  }
1603
- toGenericLive(plain) {
1699
+ toGenericLive(plain, scope) {
1604
1700
  const map = xmlToGenericShell(plain);
1605
- const children = new Y3.Array();
1606
- children.push(plain.children.map((child) => this.toLive(child)));
1701
+ const inner = withBindings(scope, plain.bindings);
1702
+ const children = new Y.Array();
1703
+ children.push(plain.children.map((child) => this.toLive(child, inner)));
1607
1704
  map.set("children", children);
1608
1705
  this.registerLive(plain.id, map);
1609
1706
  return map;
1610
1707
  }
1611
1708
  /**
1612
- * A live node that can be inserted into a parent. Anything not yet attached is built fresh; an
1613
- * attached node is copied, because a Yjs type cannot have two parents.
1709
+ * A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
1710
+ * Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
1711
+ * have two parents.
1614
1712
  */
1615
- attachable(id) {
1616
- return this.toLive(this.snapshot(id));
1713
+ attachable(id, scope = /* @__PURE__ */ new Map(), root = false) {
1714
+ return this.toLive(this.snapshot(id), scope, root);
1617
1715
  }
1618
1716
  };
1619
1717
  function xmlToGenericShell(plain) {
1620
- const map = new Y3.Map();
1718
+ const map = new Y.Map();
1621
1719
  map.set("ns", plain.ns);
1622
1720
  map.set("local", plain.local);
1623
1721
  if (plain.prefix !== void 0) map.set("prefix", plain.prefix);
1624
- const attrs = new Y3.Array();
1722
+ const attrs = new Y.Array();
1625
1723
  attrs.push(plain.attrs.map((a) => ({ ...a })));
1626
1724
  map.set("attrs", attrs);
1627
- const bindings = new Y3.Array();
1725
+ const bindings = new Y.Array();
1628
1726
  bindings.push(plain.bindings.map((b) => ({ ...b })));
1629
1727
  map.set("bindings", bindings);
1630
1728
  return map;
@@ -1689,7 +1787,7 @@ function setNamespaceBinding(registry, id, prefix, uri) {
1689
1787
  }
1690
1788
  }
1691
1789
  function asLiveElement(node, id) {
1692
- if (node instanceof Y3.Text) throw new JournalError(`${id} is a text node, not an element`);
1790
+ if (node instanceof Y.Text) throw new JournalError(`${id} is a text node, not an element`);
1693
1791
  return node;
1694
1792
  }
1695
1793
  function asPlainElement(node, id) {
@@ -1705,7 +1803,7 @@ function spliceText(registry, id, start, deleteCount, insert) {
1705
1803
  return;
1706
1804
  }
1707
1805
  const text = entry.live;
1708
- if (!(text instanceof Y3.Text)) throw new JournalError(`${id} is not a text node`);
1806
+ if (!(text instanceof Y.Text)) throw new JournalError(`${id} is not a text node`);
1709
1807
  if (start < 0 || start + deleteCount > text.length) {
1710
1808
  throw new JournalError(`text splice out of range on ${id}`);
1711
1809
  }
@@ -1718,27 +1816,29 @@ function detachRemoved(registry, parent, start, count) {
1718
1816
  function spliceChildren(registry, parentId, start, deleteCount, childIds) {
1719
1817
  const parentEntry = registry.require(parentId);
1720
1818
  if ("plain" in parentEntry) {
1721
- const parent = asPlainElement(parentEntry.plain, parentId);
1722
- if (start < 0 || start + deleteCount > parent.children.length) {
1819
+ const parent2 = asPlainElement(parentEntry.plain, parentId);
1820
+ if (start < 0 || start + deleteCount > parent2.children.length) {
1723
1821
  throw new JournalError(`child splice out of range on ${parentId}`);
1724
1822
  }
1725
1823
  const inserted2 = childIds.map((id) => registry.snapshot(id));
1726
- parent.children.splice(start, deleteCount, ...inserted2);
1727
- registry.adopt(parent, inserted2);
1824
+ parent2.children.splice(start, deleteCount, ...inserted2);
1825
+ registry.adopt(parent2, inserted2);
1728
1826
  return;
1729
1827
  }
1730
- const children = childrenOf(asLiveElement(parentEntry.live, parentId));
1828
+ const parent = asLiveElement(parentEntry.live, parentId);
1829
+ const children = childrenOf(parent);
1731
1830
  if (start < 0 || start + deleteCount > children.length) {
1732
1831
  throw new JournalError(`child splice out of range on ${parentId}`);
1733
1832
  }
1734
1833
  detachRemoved(registry, children, start, deleteCount);
1735
- const inserted = childIds.map((id) => registry.attachable(id));
1834
+ const scope = scopeWithin(parent);
1835
+ const inserted = childIds.map((id) => registry.attachable(id, scope));
1736
1836
  if (deleteCount > 0) children.delete(start, deleteCount);
1737
1837
  if (inserted.length > 0) children.insert(start, inserted);
1738
1838
  }
1739
1839
  function locate(registry, id, node) {
1740
1840
  const children = node.parent;
1741
- if (!(children instanceof Y3.Array)) throw new JournalError(`${id} is not attached`);
1841
+ if (!(children instanceof Y.Array)) throw new JournalError(`${id} is not attached`);
1742
1842
  const index = children.toArray().indexOf(node);
1743
1843
  if (index < 0) throw new JournalError(`${id} is not in its parent`);
1744
1844
  return { children, index };
@@ -1765,8 +1865,10 @@ function moveNode(registry, id, destinationId, destinationIndex) {
1765
1865
  registry.adopt(destination2, [copy]);
1766
1866
  return;
1767
1867
  }
1768
- const destination = childrenOf(asLiveElement(destinationEntry.live, destinationId));
1769
- destination.insert(destinationIndex, [registry.toLive(copy)]);
1868
+ const destination = asLiveElement(destinationEntry.live, destinationId);
1869
+ childrenOf(destination).insert(destinationIndex, [
1870
+ registry.toLive(copy, scopeWithin(destination))
1871
+ ]);
1770
1872
  }
1771
1873
  function renameNode(registry, id, qname) {
1772
1874
  const entry = registry.require(id);
@@ -1781,7 +1883,7 @@ function renameNode(registry, id, qname) {
1781
1883
  return true;
1782
1884
  }
1783
1885
  const live = entry.live;
1784
- if (live instanceof Y3.Text || isRichParagraph(live)) return false;
1886
+ if (live instanceof Y.Text || isCompact(live)) return false;
1785
1887
  if (live.get("ns") !== qname.namespaceUri) live.set("ns", qname.namespaceUri);
1786
1888
  if (live.get("local") !== qname.localName) live.set("local", qname.localName);
1787
1889
  if (qname.prefix === void 0) {
@@ -1811,7 +1913,7 @@ function putNode(registry, effect) {
1811
1913
  }
1812
1914
  function rootFor(registry, id) {
1813
1915
  const entry = registry.require(id);
1814
- const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id);
1916
+ const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id, /* @__PURE__ */ new Map(), true);
1815
1917
  return asLiveElement(live, id);
1816
1918
  }
1817
1919
  function applyEffect(doc, registry, effect, context) {
@@ -1855,7 +1957,7 @@ function applyEffect(doc, registry, effect, context) {
1855
1957
  const rels = doc.getMap("rels");
1856
1958
  let byId = rels.get(effect.owner);
1857
1959
  if (!byId) {
1858
- byId = new Y3.Map();
1960
+ byId = new Y.Map();
1859
1961
  rels.set(effect.owner, byId);
1860
1962
  }
1861
1963
  byId.set(effect.record.id, { ...effect.record });
@@ -2060,7 +2162,7 @@ var DocxCollaborationSession = class {
2060
2162
  this.doc = options.doc;
2061
2163
  this.awareness = options.awareness;
2062
2164
  this.offlineEditing = options.offlineEditing ?? false;
2063
- this.undoManager = new Y3.UndoManager(
2165
+ this.undoManager = new Y.UndoManager(
2064
2166
  [
2065
2167
  this.doc.getMap("parts"),
2066
2168
  this.doc.getMap("rels"),
@@ -2100,7 +2202,7 @@ var DocxCollaborationSession = class {
2100
2202
  this.setStatus("error", "not-initialized");
2101
2203
  return;
2102
2204
  }
2103
- if (readFormat(this.doc) !== COLLAB_FORMAT) {
2205
+ if (!isReadableFormat(readFormat(this.doc))) {
2104
2206
  this.setStatus("error", "collaboration-format-mismatch", readFormat(this.doc));
2105
2207
  return;
2106
2208
  }
@@ -2374,7 +2476,7 @@ function resolveAddress(port, address) {
2374
2476
  };
2375
2477
  }
2376
2478
  function vectorKey(doc) {
2377
- return Array.from(Y3.encodeStateVector(doc)).join(",");
2479
+ return Array.from(Y.encodeStateVector(doc)).join(",");
2378
2480
  }
2379
2481
  function randomToken() {
2380
2482
  if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -2407,7 +2509,7 @@ function assertCompatibleVersions(versions) {
2407
2509
  function assertRoomFormat(doc) {
2408
2510
  if (!isBootstrapped(doc)) throw new CollaborationCompatibilityError("The room holds no document.");
2409
2511
  const format = readFormat(doc);
2410
- if (format !== COLLAB_FORMAT) {
2512
+ if (!isReadableFormat(format)) {
2411
2513
  throw new CollaborationCompatibilityError(`Unsupported room format ${String(format)}.`);
2412
2514
  }
2413
2515
  }