@arbidocs/coedit 0.5.20 → 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.
@@ -1,7 +1,7 @@
1
- import * as Y3 from 'yjs';
1
+ import * as Y from 'yjs';
2
2
  import { Awareness } from 'y-protocols/awareness';
3
3
  import { HocuspocusProvider } from '@hocuspocus/provider';
4
- import { writeOoxmlPackage, ORIGIN_IDS, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride } from '@docx-editor.dev/core/store';
4
+ import { writeOoxmlPackage, ORIGIN_IDS, readOoxmlPart, validateExternalTarget, relsPartNameFor, contentTypesPartBytes, withContentTypeOverride, readXml, XML_NAMESPACE_URI } from '@docx-editor.dev/core/store';
5
5
  import { createCollaborationStatusTracker, safeParticipantColor } from '@docx-editor.dev/core/collaboration';
6
6
  import { historyGroupOfJournal, mapOffsetAcrossText } from '@docx-editor.dev/core/collaboration/replication';
7
7
 
@@ -316,6 +316,92 @@ function writeUnits(text, units) {
316
316
  function isRichParagraph(map) {
317
317
  return map.has("inline");
318
318
  }
319
+ var PACKED = "xml";
320
+ function isPackedElement(map) {
321
+ return map.has(PACKED);
322
+ }
323
+ function isCompact(map) {
324
+ return isRichParagraph(map) || isPackedElement(map);
325
+ }
326
+ var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
327
+ function packable(xml) {
328
+ if (xml.ns === WML && TEXT_BEARING.has(xml.local)) return false;
329
+ return xml.children.every((c) => typeof c === "string" || packable(c));
330
+ }
331
+ function createPacked(xml, scope) {
332
+ const map = new Y.Map();
333
+ map.set(PACKED, serializeXml(xml, scope));
334
+ return map;
335
+ }
336
+ function repack(map, xml) {
337
+ const next = serializeXml(xml, scopeOf(map));
338
+ if (map.get(PACKED) !== next) map.set(PACKED, next);
339
+ }
340
+ function scopeOf(node) {
341
+ const chain = [];
342
+ let parent = node.parent;
343
+ while (parent instanceof Y.Array) {
344
+ const owner = parent.parent;
345
+ if (!(owner instanceof Y.Map)) break;
346
+ chain.push(owner);
347
+ parent = owner.parent;
348
+ }
349
+ const scope = /* @__PURE__ */ new Map();
350
+ for (const el of chain.reverse()) {
351
+ for (const b of el.get("bindings").toArray()) scope.set(b.prefix, b.ns);
352
+ }
353
+ return scope;
354
+ }
355
+ function scopeWithin(el) {
356
+ const scope = scopeOf(el);
357
+ if (!isPackedElement(el)) {
358
+ for (const b of el.get("bindings").toArray()) scope.set(b.prefix, b.ns);
359
+ }
360
+ return scope;
361
+ }
362
+ var unpackLimits = { maxBytes: 64 * 1024 * 1024, maxElements: 2e6 };
363
+ function unpack(text, scope) {
364
+ const read = readXml(text, unpackLimits);
365
+ if (!read.ok) throw new Error(`collab: a packed element is not readable XML (${read.reason})`);
366
+ const convert = (node, outer) => {
367
+ const bindings = [];
368
+ const named = [];
369
+ for (const [name, value] of Object.entries(node.attributes)) {
370
+ if (name === "xmlns") bindings.push({ prefix: "", ns: value });
371
+ else if (name.startsWith("xmlns:")) bindings.push({ prefix: name.slice(6), ns: value });
372
+ else named.push([name, value]);
373
+ }
374
+ const inner = new Map(outer);
375
+ for (const b of bindings) inner.set(b.prefix, b.ns);
376
+ const resolve = (prefix) => {
377
+ if (prefix === "xml") return XML_NAMESPACE_URI;
378
+ const ns = inner.get(prefix);
379
+ if (ns === void 0)
380
+ throw new Error(`collab: prefix ${prefix} is not bound in a packed element`);
381
+ return ns;
382
+ };
383
+ const colon = node.name.indexOf(":");
384
+ const xml = {
385
+ ns: colon < 0 ? inner.get("") ?? "" : resolve(node.name.slice(0, colon)),
386
+ local: colon < 0 ? node.name : node.name.slice(colon + 1),
387
+ attrs: named.map(([name, value]) => {
388
+ const at = name.indexOf(":");
389
+ if (at < 0) return { ns: "", local: name, value };
390
+ const prefix = name.slice(0, at);
391
+ return { ns: resolve(prefix), local: name.slice(at + 1), prefix, value };
392
+ }),
393
+ bindings,
394
+ children: node.children.map(
395
+ (child) => child.type === "text" ? child.value : convert(child, inner)
396
+ )
397
+ };
398
+ if (colon >= 0) xml.prefix = node.name.slice(0, colon);
399
+ return xml;
400
+ };
401
+ const root = read.nodes.find((n) => n.type === "element");
402
+ if (!root || root.type !== "element") throw new Error("collab: a packed element is empty");
403
+ return convert(root, scope);
404
+ }
319
405
  function isParagraphEl(el) {
320
406
  return el.ns === WML && el.local === "p";
321
407
  }
@@ -323,47 +409,53 @@ function fieldsOf(map, xml) {
323
409
  map.set("ns", xml.ns);
324
410
  map.set("local", xml.local);
325
411
  if (xml.prefix !== void 0) map.set("prefix", xml.prefix);
326
- const attrs = new Y3.Array();
412
+ const attrs = new Y.Array();
327
413
  attrs.push(xml.attrs.map((a) => ({ ...a })));
328
414
  map.set("attrs", attrs);
329
- const bindings = new Y3.Array();
415
+ const bindings = new Y.Array();
330
416
  bindings.push(xml.bindings.map((b) => ({ ...b })));
331
417
  map.set("bindings", bindings);
332
418
  }
333
419
  function newText(value) {
334
- const text = new Y3.Text();
420
+ const text = new Y.Text();
335
421
  text.insert(0, value);
336
422
  return text;
337
423
  }
338
- function createRichParagraph(xml, encoded) {
339
- const map = new Y3.Map();
424
+ function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
425
+ const map = new Y.Map();
340
426
  fieldsOf(map, xml);
341
- const children = new Y3.Array();
342
- children.push(encoded.head.map((el) => xmlToLive(el)));
427
+ const inner = withBindings(scope, xml.bindings);
428
+ const children = new Y.Array();
429
+ children.push(encoded.head.map((el) => xmlToLive(el, inner)));
343
430
  map.set("children", children);
344
- const inline = new Y3.Text();
431
+ const inline = new Y.Text();
345
432
  writeUnits(inline, encoded.units);
346
433
  map.set("inline", inline);
347
434
  return map;
348
435
  }
349
- function xmlToLive(xml) {
436
+ function withBindings(scope, bindings) {
437
+ const inner = new Map(scope);
438
+ for (const b of bindings) inner.set(b.prefix, b.ns);
439
+ return inner;
440
+ }
441
+ function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
350
442
  if (isParagraphEl(xml)) {
351
443
  const encoded = encodeParagraph(xml.children);
352
- if (encoded) return createRichParagraph(xml, encoded);
444
+ if (encoded) return createRichParagraph(xml, encoded, scope);
353
445
  }
354
- return xmlToGenericLive(xml);
355
- }
356
- function xmlToGenericLive(xml) {
357
- const map = new Y3.Map();
446
+ if (!root && packable(xml)) return createPacked(xml, scope);
447
+ const map = new Y.Map();
358
448
  fieldsOf(map, xml);
359
- const children = new Y3.Array();
360
- children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c)));
449
+ const inner = withBindings(scope, xml.bindings);
450
+ const children = new Y.Array();
451
+ children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
361
452
  map.set("children", children);
362
453
  return map;
363
454
  }
364
455
  function liveToXml(map) {
456
+ if (isPackedElement(map)) return unpack(map.get(PACKED), scopeOf(map));
365
457
  const prefix = map.get("prefix");
366
- const stored = map.get("children").toArray().map((child) => child instanceof Y3.Text ? child.toString() : liveToXml(child));
458
+ const stored = map.get("children").toArray().map((child) => child instanceof Y.Text ? child.toString() : liveToXml(child));
367
459
  const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
368
460
  const xml = {
369
461
  ns: map.get("ns"),
@@ -378,14 +470,13 @@ function liveToXml(map) {
378
470
  var escapeText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
379
471
  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
472
  var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
381
- function serializeXml(el, scope) {
382
- 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) {
473
+ function startTag(prefix, local, bindings, attrs, scope) {
474
+ const inScope = withBindings(scope, bindings);
475
+ let out = `<${qualified(prefix, local)}`;
476
+ for (const b of bindings) {
386
477
  out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
387
478
  }
388
- for (const a of el.attrs) {
479
+ for (const a of attrs) {
389
480
  let p = a.prefix;
390
481
  if (a.ns !== "" && !p) {
391
482
  for (const [candidate, uri] of inScope) {
@@ -397,48 +488,41 @@ function serializeXml(el, scope) {
397
488
  }
398
489
  out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
399
490
  }
491
+ return [out, inScope];
492
+ }
493
+ function serializeXml(el, scope) {
494
+ const [out, inScope] = startTag(el.prefix, el.local, el.bindings, el.attrs, scope);
400
495
  if (el.children.length === 0) return `${out}/>`;
401
- out += ">";
496
+ let body = "";
402
497
  for (const child of el.children) {
403
- out += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
498
+ body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
404
499
  }
405
- return `${out}</${qualified(el.prefix, el.local)}>`;
500
+ return `${out}>${body}</${qualified(el.prefix, el.local)}>`;
406
501
  }
407
502
  function serializeLive(el, scope) {
408
- if (isRichParagraph(el)) return serializeXml(liveToXml(el), scope);
503
+ if (isPackedElement(el)) return el.get(PACKED);
409
504
  const prefix = el.get("prefix");
410
505
  const local = el.get("local");
411
506
  const bindings = el.get("bindings").toArray();
412
507
  const attrs = el.get("attrs").toArray();
413
508
  const children = el.get("children").toArray();
414
- const inScope = new Map(scope);
415
- for (const b of bindings) inScope.set(b.prefix, b.ns);
416
- let out = `<${qualified(prefix, local)}`;
417
- for (const b of bindings) {
418
- out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
419
- }
420
- for (const a of attrs) {
421
- let p = a.prefix;
422
- if (a.ns !== "" && !p) {
423
- for (const [candidate, uri] of inScope) {
424
- if (candidate !== "" && uri === a.ns) {
425
- p = candidate;
426
- break;
427
- }
428
- }
429
- }
430
- out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
431
- }
432
- if (children.length === 0) return `${out}/>`;
433
- out += ">";
509
+ const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
510
+ const inline = isRichParagraph(el) ? decodeInline(readUnits(el.get("inline"))) : [];
511
+ if (children.length === 0 && inline.length === 0) return `${out}/>`;
512
+ let body = "";
434
513
  for (const child of children) {
435
- out += child instanceof Y3.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
514
+ body += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
436
515
  }
437
- return `${out}</${qualified(prefix, local)}>`;
516
+ for (const child of inline) body += serializeXml(child, inScope);
517
+ return `${out}>${body}</${qualified(prefix, local)}>`;
438
518
  }
439
519
 
440
520
  // src/encoding.ts
441
- var COLLAB_FORMAT = "arbi-docx/1";
521
+ var COLLAB_FORMAT = "arbi-docx/2";
522
+ var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
523
+ function isReadableFormat(format) {
524
+ return format !== void 0 && READABLE_FORMATS.has(format);
525
+ }
442
526
  function isBootstrapped(doc) {
443
527
  return doc.getMap("meta").get("format") !== void 0;
444
528
  }
@@ -735,7 +819,7 @@ var NodeRegistry = class {
735
819
  require(id) {
736
820
  let entry = this.entries.get(id) ?? this.resolveInsideParagraph(id);
737
821
  if (!entry) throw new RegistryError(`journal names unknown node ${id}`);
738
- if ("live" in entry && entry.live instanceof Y3.Map && isRichParagraph(entry.live)) {
822
+ if ("live" in entry && entry.live instanceof Y.Map && isCompact(entry.live)) {
739
823
  this.openParagraph(entry.live);
740
824
  entry = this.entries.get(id);
741
825
  }
@@ -753,7 +837,7 @@ var NodeRegistry = class {
753
837
  for (let length = path.length - 1; length >= 1; length--) {
754
838
  const ancestor = this.entries.get(id.slice(0, hash + 1) + path.slice(0, length).join("."));
755
839
  if (!ancestor) continue;
756
- if ("live" in ancestor && ancestor.live instanceof Y3.Map && isRichParagraph(ancestor.live)) {
840
+ if ("live" in ancestor && ancestor.live instanceof Y.Map && isCompact(ancestor.live)) {
757
841
  this.openParagraph(ancestor.live);
758
842
  return this.entries.get(id);
759
843
  }
@@ -785,13 +869,13 @@ var NodeRegistry = class {
785
869
  /**
786
870
  * Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
787
871
  * `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
788
- * paragraph is registered as one node; its inner nodes appear when it is opened.
872
+ * paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
789
873
  */
790
874
  registerTree(partName, root) {
791
875
  const walk = (node, id) => {
792
876
  this.registerLive(id, node);
793
- if (node instanceof Y3.Text) return;
794
- if (isRichParagraph(node)) return;
877
+ if (node instanceof Y.Text) return;
878
+ if (isCompact(node)) return;
795
879
  childrenOf(node).forEach((child, index) => walk(child, `${id}.${index}`));
796
880
  };
797
881
  walk(root, `${partName}#0`);
@@ -848,7 +932,7 @@ var NodeRegistry = class {
848
932
  for (const child of children) this.own(child, owner);
849
933
  if (owner) this.dirty.add(owner);
850
934
  }
851
- /** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
935
+ /** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
852
936
  flushRich() {
853
937
  for (const live of [...this.dirty]) {
854
938
  this.dirty.delete(live);
@@ -859,6 +943,11 @@ var NodeRegistry = class {
859
943
  writeBack(record) {
860
944
  const { live, root } = record;
861
945
  const xml = plainToXml(root);
946
+ if (isPackedElement(live)) {
947
+ if (packable(xml)) repack(live, xml);
948
+ else this.degrade(record);
949
+ return;
950
+ }
862
951
  const encoded = encodeParagraph(xml.children);
863
952
  if (!encoded) {
864
953
  this.degrade(record);
@@ -875,21 +964,22 @@ var NodeRegistry = class {
875
964
  bindings.push(xml.bindings.map((b) => ({ ...b })));
876
965
  }
877
966
  const head = childrenOf(live);
878
- const stored = head.toArray().map((child) => liveToXml(child));
879
- if (JSON.stringify(stored) !== JSON.stringify(encoded.head)) {
967
+ const scope = scopeWithin(live);
968
+ const stored = head.toArray().map((child) => serializeLive(child, scope)).join("");
969
+ if (stored !== encoded.head.map((el) => serializeXml(el, scope)).join("")) {
880
970
  head.delete(0, head.length);
881
- head.push(encoded.head.map((el) => xmlToLive(el)));
971
+ head.push(encoded.head.map((el) => xmlToLive(el, scope)));
882
972
  }
883
973
  const inline = live.get("inline");
884
974
  applyUnits(inline, readUnits(inline), encoded.units);
885
975
  }
886
- /** The paragraph can no longer be held as rich text: store it as plain elements instead. */
976
+ /** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
887
977
  degrade(record) {
888
978
  const parent = record.live.parent;
889
- if (!(parent instanceof Y3.Array))
979
+ if (!(parent instanceof Y.Array))
890
980
  throw new RegistryError("a paragraph outside a parent cannot change form");
891
981
  const index = parent.toArray().indexOf(record.live);
892
- const generic = this.toGenericLive(record.root);
982
+ const generic = this.toGenericLive(record.root, scopeOf(record.live));
893
983
  this.open.delete(record.live);
894
984
  parent.delete(index, 1);
895
985
  parent.insert(index, [generic]);
@@ -904,8 +994,8 @@ var NodeRegistry = class {
904
994
  snapshotLive(node) {
905
995
  const id = this.liveIds.get(node);
906
996
  if (id === void 0) throw new RegistryError("live node has no registered id");
907
- if (node instanceof Y3.Text) return { type: "text", id, value: node.toString() };
908
- if (isRichParagraph(node)) {
997
+ if (node instanceof Y.Text) return { type: "text", id, value: node.toString() };
998
+ if (isCompact(node)) {
909
999
  return this.open.get(node)?.root ?? xmlToPlain(liveToXml(node), id);
910
1000
  }
911
1001
  const prefix = node.get("prefix");
@@ -933,8 +1023,8 @@ var NodeRegistry = class {
933
1023
  if (id !== void 0 && this.isCurrentLive(id, node)) {
934
1024
  this.entries.set(id, { plain });
935
1025
  }
936
- if (node instanceof Y3.Text || plain.type === "text") return;
937
- if (isRichParagraph(node)) {
1026
+ if (node instanceof Y.Text || plain.type === "text") return;
1027
+ if (isCompact(node)) {
938
1028
  this.open.delete(node);
939
1029
  this.dirty.delete(node);
940
1030
  return;
@@ -942,53 +1032,61 @@ var NodeRegistry = class {
942
1032
  const liveChildren = childrenOf(node).toArray();
943
1033
  liveChildren.forEach((child, index) => this.detachInto(child, plain.children[index]));
944
1034
  }
945
- /** Build Yjs types for a plain subtree and register every node as live. */
946
- toLive(plain) {
1035
+ /**
1036
+ * Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
1037
+ * live. A rich paragraph or packed element stays open on the plain tree, which is already current.
1038
+ * A part's `root` is never packed.
1039
+ */
1040
+ toLive(plain, scope = /* @__PURE__ */ new Map(), root = false) {
947
1041
  if (plain.type === "text") {
948
- const text = new Y3.Text();
1042
+ const text = new Y.Text();
949
1043
  text.insert(0, plain.value);
950
1044
  this.registerLive(plain.id, text);
951
1045
  return text;
952
1046
  }
1047
+ const xml = plainToXml(plain);
1048
+ let map;
953
1049
  if (isParagraphEl(plain)) {
954
- const xml = plainToXml(plain);
955
1050
  const encoded = encodeParagraph(xml.children);
956
- if (encoded) {
957
- const map = createRichParagraph(xml, encoded);
958
- this.liveIds.set(map, plain.id);
959
- this.registerPlain(plain);
960
- this.own(plain, map);
961
- this.open.set(map, { live: map, root: plain, id: plain.id });
962
- return map;
963
- }
1051
+ if (encoded) map = createRichParagraph(xml, encoded, scope);
1052
+ }
1053
+ if (!map && !root && packable(xml)) map = xmlToLive(xml, scope);
1054
+ if (map) {
1055
+ this.liveIds.set(map, plain.id);
1056
+ this.registerPlain(plain);
1057
+ this.own(plain, map);
1058
+ this.open.set(map, { live: map, root: plain, id: plain.id });
1059
+ return map;
964
1060
  }
965
- return this.toGenericLive(plain);
1061
+ return this.toGenericLive(plain, scope);
966
1062
  }
967
- toGenericLive(plain) {
1063
+ toGenericLive(plain, scope) {
968
1064
  const map = xmlToGenericShell(plain);
969
- const children = new Y3.Array();
970
- children.push(plain.children.map((child) => this.toLive(child)));
1065
+ const inner = withBindings(scope, plain.bindings);
1066
+ const children = new Y.Array();
1067
+ children.push(plain.children.map((child) => this.toLive(child, inner)));
971
1068
  map.set("children", children);
972
1069
  this.registerLive(plain.id, map);
973
1070
  return map;
974
1071
  }
975
1072
  /**
976
- * A live node that can be inserted into a parent. Anything not yet attached is built fresh; an
977
- * attached node is copied, because a Yjs type cannot have two parents.
1073
+ * A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
1074
+ * Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
1075
+ * have two parents.
978
1076
  */
979
- attachable(id) {
980
- return this.toLive(this.snapshot(id));
1077
+ attachable(id, scope = /* @__PURE__ */ new Map(), root = false) {
1078
+ return this.toLive(this.snapshot(id), scope, root);
981
1079
  }
982
1080
  };
983
1081
  function xmlToGenericShell(plain) {
984
- const map = new Y3.Map();
1082
+ const map = new Y.Map();
985
1083
  map.set("ns", plain.ns);
986
1084
  map.set("local", plain.local);
987
1085
  if (plain.prefix !== void 0) map.set("prefix", plain.prefix);
988
- const attrs = new Y3.Array();
1086
+ const attrs = new Y.Array();
989
1087
  attrs.push(plain.attrs.map((a) => ({ ...a })));
990
1088
  map.set("attrs", attrs);
991
- const bindings = new Y3.Array();
1089
+ const bindings = new Y.Array();
992
1090
  bindings.push(plain.bindings.map((b) => ({ ...b })));
993
1091
  map.set("bindings", bindings);
994
1092
  return map;
@@ -1053,7 +1151,7 @@ function setNamespaceBinding(registry, id, prefix, uri) {
1053
1151
  }
1054
1152
  }
1055
1153
  function asLiveElement(node, id) {
1056
- if (node instanceof Y3.Text) throw new JournalError(`${id} is a text node, not an element`);
1154
+ if (node instanceof Y.Text) throw new JournalError(`${id} is a text node, not an element`);
1057
1155
  return node;
1058
1156
  }
1059
1157
  function asPlainElement(node, id) {
@@ -1069,7 +1167,7 @@ function spliceText(registry, id, start, deleteCount, insert) {
1069
1167
  return;
1070
1168
  }
1071
1169
  const text = entry.live;
1072
- if (!(text instanceof Y3.Text)) throw new JournalError(`${id} is not a text node`);
1170
+ if (!(text instanceof Y.Text)) throw new JournalError(`${id} is not a text node`);
1073
1171
  if (start < 0 || start + deleteCount > text.length) {
1074
1172
  throw new JournalError(`text splice out of range on ${id}`);
1075
1173
  }
@@ -1082,27 +1180,29 @@ function detachRemoved(registry, parent, start, count) {
1082
1180
  function spliceChildren(registry, parentId, start, deleteCount, childIds) {
1083
1181
  const parentEntry = registry.require(parentId);
1084
1182
  if ("plain" in parentEntry) {
1085
- const parent = asPlainElement(parentEntry.plain, parentId);
1086
- if (start < 0 || start + deleteCount > parent.children.length) {
1183
+ const parent2 = asPlainElement(parentEntry.plain, parentId);
1184
+ if (start < 0 || start + deleteCount > parent2.children.length) {
1087
1185
  throw new JournalError(`child splice out of range on ${parentId}`);
1088
1186
  }
1089
1187
  const inserted2 = childIds.map((id) => registry.snapshot(id));
1090
- parent.children.splice(start, deleteCount, ...inserted2);
1091
- registry.adopt(parent, inserted2);
1188
+ parent2.children.splice(start, deleteCount, ...inserted2);
1189
+ registry.adopt(parent2, inserted2);
1092
1190
  return;
1093
1191
  }
1094
- const children = childrenOf(asLiveElement(parentEntry.live, parentId));
1192
+ const parent = asLiveElement(parentEntry.live, parentId);
1193
+ const children = childrenOf(parent);
1095
1194
  if (start < 0 || start + deleteCount > children.length) {
1096
1195
  throw new JournalError(`child splice out of range on ${parentId}`);
1097
1196
  }
1098
1197
  detachRemoved(registry, children, start, deleteCount);
1099
- const inserted = childIds.map((id) => registry.attachable(id));
1198
+ const scope = scopeWithin(parent);
1199
+ const inserted = childIds.map((id) => registry.attachable(id, scope));
1100
1200
  if (deleteCount > 0) children.delete(start, deleteCount);
1101
1201
  if (inserted.length > 0) children.insert(start, inserted);
1102
1202
  }
1103
1203
  function locate(registry, id, node) {
1104
1204
  const children = node.parent;
1105
- if (!(children instanceof Y3.Array)) throw new JournalError(`${id} is not attached`);
1205
+ if (!(children instanceof Y.Array)) throw new JournalError(`${id} is not attached`);
1106
1206
  const index = children.toArray().indexOf(node);
1107
1207
  if (index < 0) throw new JournalError(`${id} is not in its parent`);
1108
1208
  return { children, index };
@@ -1129,8 +1229,10 @@ function moveNode(registry, id, destinationId, destinationIndex) {
1129
1229
  registry.adopt(destination2, [copy]);
1130
1230
  return;
1131
1231
  }
1132
- const destination = childrenOf(asLiveElement(destinationEntry.live, destinationId));
1133
- destination.insert(destinationIndex, [registry.toLive(copy)]);
1232
+ const destination = asLiveElement(destinationEntry.live, destinationId);
1233
+ childrenOf(destination).insert(destinationIndex, [
1234
+ registry.toLive(copy, scopeWithin(destination))
1235
+ ]);
1134
1236
  }
1135
1237
  function renameNode(registry, id, qname) {
1136
1238
  const entry = registry.require(id);
@@ -1145,7 +1247,7 @@ function renameNode(registry, id, qname) {
1145
1247
  return true;
1146
1248
  }
1147
1249
  const live = entry.live;
1148
- if (live instanceof Y3.Text || isRichParagraph(live)) return false;
1250
+ if (live instanceof Y.Text || isCompact(live)) return false;
1149
1251
  if (live.get("ns") !== qname.namespaceUri) live.set("ns", qname.namespaceUri);
1150
1252
  if (live.get("local") !== qname.localName) live.set("local", qname.localName);
1151
1253
  if (qname.prefix === void 0) {
@@ -1175,7 +1277,7 @@ function putNode(registry, effect) {
1175
1277
  }
1176
1278
  function rootFor(registry, id) {
1177
1279
  const entry = registry.require(id);
1178
- const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id);
1280
+ const live = "live" in entry && entry.live.parent === null ? entry.live : registry.attachable(id, /* @__PURE__ */ new Map(), true);
1179
1281
  return asLiveElement(live, id);
1180
1282
  }
1181
1283
  function applyEffect(doc, registry, effect, context) {
@@ -1219,7 +1321,7 @@ function applyEffect(doc, registry, effect, context) {
1219
1321
  const rels = doc.getMap("rels");
1220
1322
  let byId = rels.get(effect.owner);
1221
1323
  if (!byId) {
1222
- byId = new Y3.Map();
1324
+ byId = new Y.Map();
1223
1325
  rels.set(effect.owner, byId);
1224
1326
  }
1225
1327
  byId.set(effect.record.id, { ...effect.record });
@@ -1424,7 +1526,7 @@ var DocxCollaborationSession = class {
1424
1526
  this.doc = options.doc;
1425
1527
  this.awareness = options.awareness;
1426
1528
  this.offlineEditing = options.offlineEditing ?? false;
1427
- this.undoManager = new Y3.UndoManager(
1529
+ this.undoManager = new Y.UndoManager(
1428
1530
  [
1429
1531
  this.doc.getMap("parts"),
1430
1532
  this.doc.getMap("rels"),
@@ -1464,7 +1566,7 @@ var DocxCollaborationSession = class {
1464
1566
  this.setStatus("error", "not-initialized");
1465
1567
  return;
1466
1568
  }
1467
- if (readFormat(this.doc) !== COLLAB_FORMAT) {
1569
+ if (!isReadableFormat(readFormat(this.doc))) {
1468
1570
  this.setStatus("error", "collaboration-format-mismatch", readFormat(this.doc));
1469
1571
  return;
1470
1572
  }
@@ -1738,7 +1840,7 @@ function resolveAddress(port, address) {
1738
1840
  };
1739
1841
  }
1740
1842
  function vectorKey(doc) {
1741
- return Array.from(Y3.encodeStateVector(doc)).join(",");
1843
+ return Array.from(Y.encodeStateVector(doc)).join(",");
1742
1844
  }
1743
1845
  function randomToken() {
1744
1846
  if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -1757,7 +1859,7 @@ var CollaborationJoinError = class extends Error {
1757
1859
  }
1758
1860
  };
1759
1861
  async function createHocuspocusCollaboration(options) {
1760
- const ydoc = new Y3.Doc();
1862
+ const ydoc = new Y.Doc();
1761
1863
  const awareness = new Awareness(ydoc);
1762
1864
  const session = new DocxCollaborationSession({
1763
1865
  doc: ydoc,