@arbidocs/coedit 0.5.21 → 0.5.22

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.cjs CHANGED
@@ -6,7 +6,7 @@ var replication = require('@docx-editor.dev/core/collaboration/replication');
6
6
  var store = require('@docx-editor.dev/core/store');
7
7
  var layout = require('@docx-editor.dev/core/layout');
8
8
  var fflate = require('fflate');
9
- var Y3 = require('yjs');
9
+ var Y = require('yjs');
10
10
  var collaboration = require('@docx-editor.dev/core/collaboration');
11
11
 
12
12
  function _interopNamespace(e) {
@@ -27,7 +27,7 @@ function _interopNamespace(e) {
27
27
  return Object.freeze(n);
28
28
  }
29
29
 
30
- var Y3__namespace = /*#__PURE__*/_interopNamespace(Y3);
30
+ var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
31
31
 
32
32
  // src/server.ts
33
33
 
@@ -896,6 +896,92 @@ function writeUnits(text, units) {
896
896
  function isRichParagraph(map) {
897
897
  return map.has("inline");
898
898
  }
899
+ var PACKED = "xml";
900
+ function isPackedElement(map) {
901
+ return map.has(PACKED);
902
+ }
903
+ function isCompact(map) {
904
+ return isRichParagraph(map) || isPackedElement(map);
905
+ }
906
+ var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
907
+ function packable(xml) {
908
+ if (xml.ns === WML2 && TEXT_BEARING.has(xml.local)) return false;
909
+ return xml.children.every((c) => typeof c === "string" || packable(c));
910
+ }
911
+ function createPacked(xml, scope) {
912
+ const map = new Y__namespace.Map();
913
+ map.set(PACKED, serializeXml(xml, scope));
914
+ return map;
915
+ }
916
+ function repack(map, xml) {
917
+ const next = serializeXml(xml, scopeOf(map));
918
+ if (map.get(PACKED) !== next) map.set(PACKED, next);
919
+ }
920
+ function scopeOf(node) {
921
+ const chain = [];
922
+ let parent = node.parent;
923
+ while (parent instanceof Y__namespace.Array) {
924
+ const owner = parent.parent;
925
+ if (!(owner instanceof Y__namespace.Map)) break;
926
+ chain.push(owner);
927
+ parent = owner.parent;
928
+ }
929
+ const scope = /* @__PURE__ */ new Map();
930
+ for (const el2 of chain.reverse()) {
931
+ for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
932
+ }
933
+ return scope;
934
+ }
935
+ function scopeWithin(el2) {
936
+ const scope = scopeOf(el2);
937
+ if (!isPackedElement(el2)) {
938
+ for (const b of el2.get("bindings").toArray()) scope.set(b.prefix, b.ns);
939
+ }
940
+ return scope;
941
+ }
942
+ var unpackLimits = { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 };
943
+ function unpack(text, scope) {
944
+ const read = store.readXml(text, unpackLimits);
945
+ if (!read.ok) throw new Error(`collab: a packed element is not readable XML (${read.reason})`);
946
+ const convert = (node, outer) => {
947
+ const bindings = [];
948
+ const named = [];
949
+ for (const [name, value] of Object.entries(node.attributes)) {
950
+ if (name === "xmlns") bindings.push({ prefix: "", ns: value });
951
+ else if (name.startsWith("xmlns:")) bindings.push({ prefix: name.slice(6), ns: value });
952
+ else named.push([name, value]);
953
+ }
954
+ const inner = new Map(outer);
955
+ for (const b of bindings) inner.set(b.prefix, b.ns);
956
+ const resolve = (prefix) => {
957
+ if (prefix === "xml") return store.XML_NAMESPACE_URI;
958
+ const ns = inner.get(prefix);
959
+ if (ns === void 0)
960
+ throw new Error(`collab: prefix ${prefix} is not bound in a packed element`);
961
+ return ns;
962
+ };
963
+ const colon = node.name.indexOf(":");
964
+ const xml = {
965
+ ns: colon < 0 ? inner.get("") ?? "" : resolve(node.name.slice(0, colon)),
966
+ local: colon < 0 ? node.name : node.name.slice(colon + 1),
967
+ attrs: named.map(([name, value]) => {
968
+ const at = name.indexOf(":");
969
+ if (at < 0) return { ns: "", local: name, value };
970
+ const prefix = name.slice(0, at);
971
+ return { ns: resolve(prefix), local: name.slice(at + 1), prefix, value };
972
+ }),
973
+ bindings,
974
+ children: node.children.map(
975
+ (child) => child.type === "text" ? child.value : convert(child, inner)
976
+ )
977
+ };
978
+ if (colon >= 0) xml.prefix = node.name.slice(0, colon);
979
+ return xml;
980
+ };
981
+ const root = read.nodes.find((n) => n.type === "element");
982
+ if (!root || root.type !== "element") throw new Error("collab: a packed element is empty");
983
+ return convert(root, scope);
984
+ }
899
985
  function isParagraphEl(el2) {
900
986
  return el2.ns === WML2 && el2.local === "p";
901
987
  }
@@ -903,47 +989,53 @@ function fieldsOf(map, xml) {
903
989
  map.set("ns", xml.ns);
904
990
  map.set("local", xml.local);
905
991
  if (xml.prefix !== void 0) map.set("prefix", xml.prefix);
906
- const attrs = new Y3__namespace.Array();
992
+ const attrs = new Y__namespace.Array();
907
993
  attrs.push(xml.attrs.map((a) => ({ ...a })));
908
994
  map.set("attrs", attrs);
909
- const bindings = new Y3__namespace.Array();
995
+ const bindings = new Y__namespace.Array();
910
996
  bindings.push(xml.bindings.map((b) => ({ ...b })));
911
997
  map.set("bindings", bindings);
912
998
  }
913
999
  function newText(value) {
914
- const text = new Y3__namespace.Text();
1000
+ const text = new Y__namespace.Text();
915
1001
  text.insert(0, value);
916
1002
  return text;
917
1003
  }
918
- function createRichParagraph(xml, encoded) {
919
- const map = new Y3__namespace.Map();
1004
+ function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
1005
+ const map = new Y__namespace.Map();
920
1006
  fieldsOf(map, xml);
921
- const children = new Y3__namespace.Array();
922
- children.push(encoded.head.map((el2) => xmlToLive(el2)));
1007
+ const inner = withBindings(scope, xml.bindings);
1008
+ const children = new Y__namespace.Array();
1009
+ children.push(encoded.head.map((el2) => xmlToLive(el2, inner)));
923
1010
  map.set("children", children);
924
- const inline = new Y3__namespace.Text();
1011
+ const inline = new Y__namespace.Text();
925
1012
  writeUnits(inline, encoded.units);
926
1013
  map.set("inline", inline);
927
1014
  return map;
928
1015
  }
929
- function xmlToLive(xml) {
1016
+ function withBindings(scope, bindings) {
1017
+ const inner = new Map(scope);
1018
+ for (const b of bindings) inner.set(b.prefix, b.ns);
1019
+ return inner;
1020
+ }
1021
+ function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
930
1022
  if (isParagraphEl(xml)) {
931
1023
  const encoded = encodeParagraph(xml.children);
932
- if (encoded) return createRichParagraph(xml, encoded);
1024
+ if (encoded) return createRichParagraph(xml, encoded, scope);
933
1025
  }
934
- return xmlToGenericLive(xml);
935
- }
936
- function xmlToGenericLive(xml) {
937
- const map = new Y3__namespace.Map();
1026
+ if (!root && packable(xml)) return createPacked(xml, scope);
1027
+ const map = new Y__namespace.Map();
938
1028
  fieldsOf(map, xml);
939
- const children = new Y3__namespace.Array();
940
- children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c)));
1029
+ const inner = withBindings(scope, xml.bindings);
1030
+ const children = new Y__namespace.Array();
1031
+ children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
941
1032
  map.set("children", children);
942
1033
  return map;
943
1034
  }
944
1035
  function liveToXml(map) {
1036
+ if (isPackedElement(map)) return unpack(map.get(PACKED), scopeOf(map));
945
1037
  const prefix = map.get("prefix");
946
- const stored = map.get("children").toArray().map((child) => child instanceof Y3__namespace.Text ? child.toString() : liveToXml(child));
1038
+ const stored = map.get("children").toArray().map((child) => child instanceof Y__namespace.Text ? child.toString() : liveToXml(child));
947
1039
  const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
948
1040
  const xml = {
949
1041
  ns: map.get("ns"),
@@ -958,14 +1050,13 @@ function liveToXml(map) {
958
1050
  var escapeText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
959
1051
  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;");
960
1052
  var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
961
- function serializeXml(el2, scope) {
962
- const inScope = new Map(scope);
963
- for (const b of el2.bindings) inScope.set(b.prefix, b.ns);
964
- let out = `<${qualified(el2.prefix, el2.local)}`;
965
- for (const b of el2.bindings) {
1053
+ function startTag(prefix, local, bindings, attrs, scope) {
1054
+ const inScope = withBindings(scope, bindings);
1055
+ let out = `<${qualified(prefix, local)}`;
1056
+ for (const b of bindings) {
966
1057
  out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
967
1058
  }
968
- for (const a of el2.attrs) {
1059
+ for (const a of attrs) {
969
1060
  let p = a.prefix;
970
1061
  if (a.ns !== "" && !p) {
971
1062
  for (const [candidate, uri] of inScope) {
@@ -977,48 +1068,41 @@ function serializeXml(el2, scope) {
977
1068
  }
978
1069
  out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
979
1070
  }
1071
+ return [out, inScope];
1072
+ }
1073
+ function serializeXml(el2, scope) {
1074
+ const [out, inScope] = startTag(el2.prefix, el2.local, el2.bindings, el2.attrs, scope);
980
1075
  if (el2.children.length === 0) return `${out}/>`;
981
- out += ">";
1076
+ let body = "";
982
1077
  for (const child of el2.children) {
983
- out += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
1078
+ body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
984
1079
  }
985
- return `${out}</${qualified(el2.prefix, el2.local)}>`;
1080
+ return `${out}>${body}</${qualified(el2.prefix, el2.local)}>`;
986
1081
  }
987
1082
  function serializeLive(el2, scope) {
988
- if (isRichParagraph(el2)) return serializeXml(liveToXml(el2), scope);
1083
+ if (isPackedElement(el2)) return el2.get(PACKED);
989
1084
  const prefix = el2.get("prefix");
990
1085
  const local = el2.get("local");
991
1086
  const bindings = el2.get("bindings").toArray();
992
1087
  const attrs = el2.get("attrs").toArray();
993
1088
  const children = el2.get("children").toArray();
994
- const inScope = new Map(scope);
995
- for (const b of bindings) inScope.set(b.prefix, b.ns);
996
- let out = `<${qualified(prefix, local)}`;
997
- for (const b of bindings) {
998
- out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
999
- }
1000
- for (const a of attrs) {
1001
- let p = a.prefix;
1002
- if (a.ns !== "" && !p) {
1003
- for (const [candidate, uri] of inScope) {
1004
- if (candidate !== "" && uri === a.ns) {
1005
- p = candidate;
1006
- break;
1007
- }
1008
- }
1009
- }
1010
- out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
1011
- }
1012
- if (children.length === 0) return `${out}/>`;
1013
- out += ">";
1089
+ const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
1090
+ const inline = isRichParagraph(el2) ? decodeInline(readUnits(el2.get("inline"))) : [];
1091
+ if (children.length === 0 && inline.length === 0) return `${out}/>`;
1092
+ let body = "";
1014
1093
  for (const child of children) {
1015
- out += child instanceof Y3__namespace.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
1094
+ body += child instanceof Y__namespace.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
1016
1095
  }
1017
- return `${out}</${qualified(prefix, local)}>`;
1096
+ for (const child of inline) body += serializeXml(child, inScope);
1097
+ return `${out}>${body}</${qualified(prefix, local)}>`;
1018
1098
  }
1019
1099
 
1020
1100
  // src/encoding.ts
1021
- var COLLAB_FORMAT = "arbi-docx/1";
1101
+ var COLLAB_FORMAT = "arbi-docx/2";
1102
+ var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
1103
+ function isReadableFormat(format) {
1104
+ return format !== void 0 && READABLE_FORMATS.has(format);
1105
+ }
1022
1106
  function isText(node) {
1023
1107
  return node.kind === "textValue";
1024
1108
  }
@@ -1039,7 +1123,7 @@ function canonicalToXml(node) {
1039
1123
  return xml;
1040
1124
  }
1041
1125
  function encodeElement(el2) {
1042
- return xmlToLive(canonicalToXml(el2));
1126
+ return xmlToLive(canonicalToXml(el2), /* @__PURE__ */ new Map(), true);
1043
1127
  }
1044
1128
  function bootstrap(doc, pkg) {
1045
1129
  doc.transact(() => {
@@ -1057,7 +1141,7 @@ function bootstrap(doc, pkg) {
1057
1141
  }
1058
1142
  const rels = doc.getMap("rels");
1059
1143
  for (const [owner, records] of pkg.relationships) {
1060
- const byId = new Y3__namespace.Map();
1144
+ const byId = new Y__namespace.Map();
1061
1145
  for (const r of records) {
1062
1146
  byId.set(r.id, {
1063
1147
  ownerPart: r.ownerPart,
@@ -1073,7 +1157,7 @@ function bootstrap(doc, pkg) {
1073
1157
  for (const e of pkg.externalTargets) {
1074
1158
  let byId = rels.get(e.ownerPart);
1075
1159
  if (!byId) {
1076
- byId = new Y3__namespace.Map();
1160
+ byId = new Y__namespace.Map();
1077
1161
  rels.set(e.ownerPart, byId);
1078
1162
  }
1079
1163
  byId.set(e.id, {
@@ -1393,7 +1477,7 @@ var NodeRegistry = class {
1393
1477
  require(id) {
1394
1478
  let entry = this.entries.get(id) ?? this.resolveInsideParagraph(id);
1395
1479
  if (!entry) throw new RegistryError(`journal names unknown node ${id}`);
1396
- if ("live" in entry && entry.live instanceof Y3__namespace.Map && isRichParagraph(entry.live)) {
1480
+ if ("live" in entry && entry.live instanceof Y__namespace.Map && isCompact(entry.live)) {
1397
1481
  this.openParagraph(entry.live);
1398
1482
  entry = this.entries.get(id);
1399
1483
  }
@@ -1411,7 +1495,7 @@ var NodeRegistry = class {
1411
1495
  for (let length = path.length - 1; length >= 1; length--) {
1412
1496
  const ancestor = this.entries.get(id.slice(0, hash + 1) + path.slice(0, length).join("."));
1413
1497
  if (!ancestor) continue;
1414
- if ("live" in ancestor && ancestor.live instanceof Y3__namespace.Map && isRichParagraph(ancestor.live)) {
1498
+ if ("live" in ancestor && ancestor.live instanceof Y__namespace.Map && isCompact(ancestor.live)) {
1415
1499
  this.openParagraph(ancestor.live);
1416
1500
  return this.entries.get(id);
1417
1501
  }
@@ -1443,13 +1527,13 @@ var NodeRegistry = class {
1443
1527
  /**
1444
1528
  * Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
1445
1529
  * `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
1446
- * paragraph is registered as one node; its inner nodes appear when it is opened.
1530
+ * paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
1447
1531
  */
1448
1532
  registerTree(partName, root) {
1449
1533
  const walk = (node, id) => {
1450
1534
  this.registerLive(id, node);
1451
- if (node instanceof Y3__namespace.Text) return;
1452
- if (isRichParagraph(node)) return;
1535
+ if (node instanceof Y__namespace.Text) return;
1536
+ if (isCompact(node)) return;
1453
1537
  childrenOf(node).forEach((child, index) => walk(child, `${id}.${index}`));
1454
1538
  };
1455
1539
  walk(root, `${partName}#0`);
@@ -1506,7 +1590,7 @@ var NodeRegistry = class {
1506
1590
  for (const child of children) this.own(child, owner);
1507
1591
  if (owner) this.dirty.add(owner);
1508
1592
  }
1509
- /** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
1593
+ /** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
1510
1594
  flushRich() {
1511
1595
  for (const live of [...this.dirty]) {
1512
1596
  this.dirty.delete(live);
@@ -1517,6 +1601,11 @@ var NodeRegistry = class {
1517
1601
  writeBack(record) {
1518
1602
  const { live, root } = record;
1519
1603
  const xml = plainToXml(root);
1604
+ if (isPackedElement(live)) {
1605
+ if (packable(xml)) repack(live, xml);
1606
+ else this.degrade(record);
1607
+ return;
1608
+ }
1520
1609
  const encoded = encodeParagraph(xml.children);
1521
1610
  if (!encoded) {
1522
1611
  this.degrade(record);
@@ -1533,21 +1622,22 @@ var NodeRegistry = class {
1533
1622
  bindings.push(xml.bindings.map((b) => ({ ...b })));
1534
1623
  }
1535
1624
  const head = childrenOf(live);
1536
- const stored = head.toArray().map((child) => liveToXml(child));
1537
- if (JSON.stringify(stored) !== JSON.stringify(encoded.head)) {
1625
+ const scope = scopeWithin(live);
1626
+ const stored = head.toArray().map((child) => serializeLive(child, scope)).join("");
1627
+ if (stored !== encoded.head.map((el2) => serializeXml(el2, scope)).join("")) {
1538
1628
  head.delete(0, head.length);
1539
- head.push(encoded.head.map((el2) => xmlToLive(el2)));
1629
+ head.push(encoded.head.map((el2) => xmlToLive(el2, scope)));
1540
1630
  }
1541
1631
  const inline = live.get("inline");
1542
1632
  applyUnits(inline, readUnits(inline), encoded.units);
1543
1633
  }
1544
- /** The paragraph can no longer be held as rich text: store it as plain elements instead. */
1634
+ /** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
1545
1635
  degrade(record) {
1546
1636
  const parent = record.live.parent;
1547
- if (!(parent instanceof Y3__namespace.Array))
1637
+ if (!(parent instanceof Y__namespace.Array))
1548
1638
  throw new RegistryError("a paragraph outside a parent cannot change form");
1549
1639
  const index = parent.toArray().indexOf(record.live);
1550
- const generic = this.toGenericLive(record.root);
1640
+ const generic = this.toGenericLive(record.root, scopeOf(record.live));
1551
1641
  this.open.delete(record.live);
1552
1642
  parent.delete(index, 1);
1553
1643
  parent.insert(index, [generic]);
@@ -1562,8 +1652,8 @@ var NodeRegistry = class {
1562
1652
  snapshotLive(node) {
1563
1653
  const id = this.liveIds.get(node);
1564
1654
  if (id === void 0) throw new RegistryError("live node has no registered id");
1565
- if (node instanceof Y3__namespace.Text) return { type: "text", id, value: node.toString() };
1566
- if (isRichParagraph(node)) {
1655
+ if (node instanceof Y__namespace.Text) return { type: "text", id, value: node.toString() };
1656
+ if (isCompact(node)) {
1567
1657
  return this.open.get(node)?.root ?? xmlToPlain(liveToXml(node), id);
1568
1658
  }
1569
1659
  const prefix = node.get("prefix");
@@ -1591,8 +1681,8 @@ var NodeRegistry = class {
1591
1681
  if (id !== void 0 && this.isCurrentLive(id, node)) {
1592
1682
  this.entries.set(id, { plain });
1593
1683
  }
1594
- if (node instanceof Y3__namespace.Text || plain.type === "text") return;
1595
- if (isRichParagraph(node)) {
1684
+ if (node instanceof Y__namespace.Text || plain.type === "text") return;
1685
+ if (isCompact(node)) {
1596
1686
  this.open.delete(node);
1597
1687
  this.dirty.delete(node);
1598
1688
  return;
@@ -1600,53 +1690,61 @@ var NodeRegistry = class {
1600
1690
  const liveChildren = childrenOf(node).toArray();
1601
1691
  liveChildren.forEach((child, index) => this.detachInto(child, plain.children[index]));
1602
1692
  }
1603
- /** Build Yjs types for a plain subtree and register every node as live. */
1604
- toLive(plain) {
1693
+ /**
1694
+ * Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
1695
+ * live. A rich paragraph or packed element stays open on the plain tree, which is already current.
1696
+ * A part's `root` is never packed.
1697
+ */
1698
+ toLive(plain, scope = /* @__PURE__ */ new Map(), root = false) {
1605
1699
  if (plain.type === "text") {
1606
- const text = new Y3__namespace.Text();
1700
+ const text = new Y__namespace.Text();
1607
1701
  text.insert(0, plain.value);
1608
1702
  this.registerLive(plain.id, text);
1609
1703
  return text;
1610
1704
  }
1705
+ const xml = plainToXml(plain);
1706
+ let map;
1611
1707
  if (isParagraphEl(plain)) {
1612
- const xml = plainToXml(plain);
1613
1708
  const encoded = encodeParagraph(xml.children);
1614
- if (encoded) {
1615
- const map = createRichParagraph(xml, encoded);
1616
- this.liveIds.set(map, plain.id);
1617
- this.registerPlain(plain);
1618
- this.own(plain, map);
1619
- this.open.set(map, { live: map, root: plain, id: plain.id });
1620
- return map;
1621
- }
1709
+ if (encoded) map = createRichParagraph(xml, encoded, scope);
1710
+ }
1711
+ if (!map && !root && packable(xml)) map = xmlToLive(xml, scope);
1712
+ if (map) {
1713
+ this.liveIds.set(map, plain.id);
1714
+ this.registerPlain(plain);
1715
+ this.own(plain, map);
1716
+ this.open.set(map, { live: map, root: plain, id: plain.id });
1717
+ return map;
1622
1718
  }
1623
- return this.toGenericLive(plain);
1719
+ return this.toGenericLive(plain, scope);
1624
1720
  }
1625
- toGenericLive(plain) {
1721
+ toGenericLive(plain, scope) {
1626
1722
  const map = xmlToGenericShell(plain);
1627
- const children = new Y3__namespace.Array();
1628
- children.push(plain.children.map((child) => this.toLive(child)));
1723
+ const inner = withBindings(scope, plain.bindings);
1724
+ const children = new Y__namespace.Array();
1725
+ children.push(plain.children.map((child) => this.toLive(child, inner)));
1629
1726
  map.set("children", children);
1630
1727
  this.registerLive(plain.id, map);
1631
1728
  return map;
1632
1729
  }
1633
1730
  /**
1634
- * A live node that can be inserted into a parent. Anything not yet attached is built fresh; an
1635
- * attached node is copied, because a Yjs type cannot have two parents.
1731
+ * A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
1732
+ * Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
1733
+ * have two parents.
1636
1734
  */
1637
- attachable(id) {
1638
- return this.toLive(this.snapshot(id));
1735
+ attachable(id, scope = /* @__PURE__ */ new Map(), root = false) {
1736
+ return this.toLive(this.snapshot(id), scope, root);
1639
1737
  }
1640
1738
  };
1641
1739
  function xmlToGenericShell(plain) {
1642
- const map = new Y3__namespace.Map();
1740
+ const map = new Y__namespace.Map();
1643
1741
  map.set("ns", plain.ns);
1644
1742
  map.set("local", plain.local);
1645
1743
  if (plain.prefix !== void 0) map.set("prefix", plain.prefix);
1646
- const attrs = new Y3__namespace.Array();
1744
+ const attrs = new Y__namespace.Array();
1647
1745
  attrs.push(plain.attrs.map((a) => ({ ...a })));
1648
1746
  map.set("attrs", attrs);
1649
- const bindings = new Y3__namespace.Array();
1747
+ const bindings = new Y__namespace.Array();
1650
1748
  bindings.push(plain.bindings.map((b) => ({ ...b })));
1651
1749
  map.set("bindings", bindings);
1652
1750
  return map;
@@ -1711,7 +1809,7 @@ function setNamespaceBinding(registry, id, prefix, uri) {
1711
1809
  }
1712
1810
  }
1713
1811
  function asLiveElement(node, id) {
1714
- if (node instanceof Y3__namespace.Text) throw new JournalError(`${id} is a text node, not an element`);
1812
+ if (node instanceof Y__namespace.Text) throw new JournalError(`${id} is a text node, not an element`);
1715
1813
  return node;
1716
1814
  }
1717
1815
  function asPlainElement(node, id) {
@@ -1727,7 +1825,7 @@ function spliceText(registry, id, start, deleteCount, insert) {
1727
1825
  return;
1728
1826
  }
1729
1827
  const text = entry.live;
1730
- if (!(text instanceof Y3__namespace.Text)) throw new JournalError(`${id} is not a text node`);
1828
+ if (!(text instanceof Y__namespace.Text)) throw new JournalError(`${id} is not a text node`);
1731
1829
  if (start < 0 || start + deleteCount > text.length) {
1732
1830
  throw new JournalError(`text splice out of range on ${id}`);
1733
1831
  }
@@ -1740,27 +1838,29 @@ function detachRemoved(registry, parent, start, count) {
1740
1838
  function spliceChildren(registry, parentId, start, deleteCount, childIds) {
1741
1839
  const parentEntry = registry.require(parentId);
1742
1840
  if ("plain" in parentEntry) {
1743
- const parent = asPlainElement(parentEntry.plain, parentId);
1744
- if (start < 0 || start + deleteCount > parent.children.length) {
1841
+ const parent2 = asPlainElement(parentEntry.plain, parentId);
1842
+ if (start < 0 || start + deleteCount > parent2.children.length) {
1745
1843
  throw new JournalError(`child splice out of range on ${parentId}`);
1746
1844
  }
1747
1845
  const inserted2 = childIds.map((id) => registry.snapshot(id));
1748
- parent.children.splice(start, deleteCount, ...inserted2);
1749
- registry.adopt(parent, inserted2);
1846
+ parent2.children.splice(start, deleteCount, ...inserted2);
1847
+ registry.adopt(parent2, inserted2);
1750
1848
  return;
1751
1849
  }
1752
- const children = childrenOf(asLiveElement(parentEntry.live, parentId));
1850
+ const parent = asLiveElement(parentEntry.live, parentId);
1851
+ const children = childrenOf(parent);
1753
1852
  if (start < 0 || start + deleteCount > children.length) {
1754
1853
  throw new JournalError(`child splice out of range on ${parentId}`);
1755
1854
  }
1756
1855
  detachRemoved(registry, children, start, deleteCount);
1757
- const inserted = childIds.map((id) => registry.attachable(id));
1856
+ const scope = scopeWithin(parent);
1857
+ const inserted = childIds.map((id) => registry.attachable(id, scope));
1758
1858
  if (deleteCount > 0) children.delete(start, deleteCount);
1759
1859
  if (inserted.length > 0) children.insert(start, inserted);
1760
1860
  }
1761
1861
  function locate(registry, id, node) {
1762
1862
  const children = node.parent;
1763
- if (!(children instanceof Y3__namespace.Array)) throw new JournalError(`${id} is not attached`);
1863
+ if (!(children instanceof Y__namespace.Array)) throw new JournalError(`${id} is not attached`);
1764
1864
  const index = children.toArray().indexOf(node);
1765
1865
  if (index < 0) throw new JournalError(`${id} is not in its parent`);
1766
1866
  return { children, index };
@@ -1787,8 +1887,10 @@ function moveNode(registry, id, destinationId, destinationIndex) {
1787
1887
  registry.adopt(destination2, [copy]);
1788
1888
  return;
1789
1889
  }
1790
- const destination = childrenOf(asLiveElement(destinationEntry.live, destinationId));
1791
- destination.insert(destinationIndex, [registry.toLive(copy)]);
1890
+ const destination = asLiveElement(destinationEntry.live, destinationId);
1891
+ childrenOf(destination).insert(destinationIndex, [
1892
+ registry.toLive(copy, scopeWithin(destination))
1893
+ ]);
1792
1894
  }
1793
1895
  function renameNode(registry, id, qname) {
1794
1896
  const entry = registry.require(id);
@@ -1803,7 +1905,7 @@ function renameNode(registry, id, qname) {
1803
1905
  return true;
1804
1906
  }
1805
1907
  const live = entry.live;
1806
- if (live instanceof Y3__namespace.Text || isRichParagraph(live)) return false;
1908
+ if (live instanceof Y__namespace.Text || isCompact(live)) return false;
1807
1909
  if (live.get("ns") !== qname.namespaceUri) live.set("ns", qname.namespaceUri);
1808
1910
  if (live.get("local") !== qname.localName) live.set("local", qname.localName);
1809
1911
  if (qname.prefix === void 0) {
@@ -1833,7 +1935,7 @@ function putNode(registry, effect) {
1833
1935
  }
1834
1936
  function rootFor(registry, id) {
1835
1937
  const entry = registry.require(id);
1836
- const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id);
1938
+ const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id, /* @__PURE__ */ new Map(), true);
1837
1939
  return asLiveElement(live, id);
1838
1940
  }
1839
1941
  function applyEffect(doc, registry, effect, context) {
@@ -1877,7 +1979,7 @@ function applyEffect(doc, registry, effect, context) {
1877
1979
  const rels = doc.getMap("rels");
1878
1980
  let byId = rels.get(effect.owner);
1879
1981
  if (!byId) {
1880
- byId = new Y3__namespace.Map();
1982
+ byId = new Y__namespace.Map();
1881
1983
  rels.set(effect.owner, byId);
1882
1984
  }
1883
1985
  byId.set(effect.record.id, { ...effect.record });
@@ -2082,7 +2184,7 @@ var DocxCollaborationSession = class {
2082
2184
  this.doc = options.doc;
2083
2185
  this.awareness = options.awareness;
2084
2186
  this.offlineEditing = options.offlineEditing ?? false;
2085
- this.undoManager = new Y3__namespace.UndoManager(
2187
+ this.undoManager = new Y__namespace.UndoManager(
2086
2188
  [
2087
2189
  this.doc.getMap("parts"),
2088
2190
  this.doc.getMap("rels"),
@@ -2122,7 +2224,7 @@ var DocxCollaborationSession = class {
2122
2224
  this.setStatus("error", "not-initialized");
2123
2225
  return;
2124
2226
  }
2125
- if (readFormat(this.doc) !== COLLAB_FORMAT) {
2227
+ if (!isReadableFormat(readFormat(this.doc))) {
2126
2228
  this.setStatus("error", "collaboration-format-mismatch", readFormat(this.doc));
2127
2229
  return;
2128
2230
  }
@@ -2396,7 +2498,7 @@ function resolveAddress(port, address) {
2396
2498
  };
2397
2499
  }
2398
2500
  function vectorKey(doc) {
2399
- return Array.from(Y3__namespace.encodeStateVector(doc)).join(",");
2501
+ return Array.from(Y__namespace.encodeStateVector(doc)).join(",");
2400
2502
  }
2401
2503
  function randomToken() {
2402
2504
  if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -2429,7 +2531,7 @@ function assertCompatibleVersions(versions) {
2429
2531
  function assertRoomFormat(doc) {
2430
2532
  if (!isBootstrapped(doc)) throw new CollaborationCompatibilityError("The room holds no document.");
2431
2533
  const format = readFormat(doc);
2432
- if (format !== COLLAB_FORMAT) {
2534
+ if (!isReadableFormat(format)) {
2433
2535
  throw new CollaborationCompatibilityError(`Unsupported room format ${String(format)}.`);
2434
2536
  }
2435
2537
  }