ooxml-core 1.22.0 → 1.24.0

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.
Files changed (126) hide show
  1. package/README.md +7 -2
  2. package/dist/automation/index.cjs +559 -339
  3. package/dist/automation/index.d.ts +1 -0
  4. package/dist/automation/index.d.ts.map +1 -1
  5. package/dist/automation/index.mjs +558 -340
  6. package/dist/automation/live.d.ts +89 -0
  7. package/dist/automation/live.d.ts.map +1 -0
  8. package/dist/collab/embedding.d.ts +7 -0
  9. package/dist/collab/embedding.d.ts.map +1 -1
  10. package/dist/collab/index.cjs +189 -0
  11. package/dist/collab/index.d.ts +2 -0
  12. package/dist/collab/index.d.ts.map +1 -1
  13. package/dist/collab/index.mjs +186 -1
  14. package/dist/collab/write-back-lifecycle.d.ts +33 -0
  15. package/dist/collab/write-back-lifecycle.d.ts.map +1 -0
  16. package/dist/collab/write-back.d.ts +75 -0
  17. package/dist/collab/write-back.d.ts.map +1 -0
  18. package/dist/docx/ui/customization.d.ts +30 -0
  19. package/dist/docx/ui/customization.d.ts.map +1 -0
  20. package/dist/docx/ui/index.cjs +51 -0
  21. package/dist/docx/ui/index.d.ts +1 -0
  22. package/dist/docx/ui/index.d.ts.map +1 -1
  23. package/dist/docx/ui/index.mjs +46 -1
  24. package/dist/embedding/backstage.d.ts +48 -0
  25. package/dist/embedding/backstage.d.ts.map +1 -0
  26. package/dist/embedding/capabilities.d.ts +60 -0
  27. package/dist/embedding/capabilities.d.ts.map +1 -0
  28. package/dist/embedding/customization.d.ts +32 -0
  29. package/dist/embedding/customization.d.ts.map +1 -0
  30. package/dist/embedding/index.cjs +229 -0
  31. package/dist/embedding/index.d.ts +7 -0
  32. package/dist/embedding/index.d.ts.map +1 -0
  33. package/dist/embedding/index.mjs +204 -0
  34. package/dist/embedding/menu-commands.d.ts +66 -0
  35. package/dist/embedding/menu-commands.d.ts.map +1 -0
  36. package/dist/embedding/regions.d.ts +58 -0
  37. package/dist/embedding/regions.d.ts.map +1 -0
  38. package/dist/embedding/storage.d.ts +111 -0
  39. package/dist/embedding/storage.d.ts.map +1 -0
  40. package/dist/index.cjs +233 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.mjs +233 -1
  44. package/dist/visio/collab/baseline.d.ts +36 -0
  45. package/dist/visio/collab/baseline.d.ts.map +1 -0
  46. package/dist/visio/collab/bind.d.ts +20 -2
  47. package/dist/visio/collab/bind.d.ts.map +1 -1
  48. package/dist/visio/collab/compose-lists.d.ts +27 -0
  49. package/dist/visio/collab/compose-lists.d.ts.map +1 -0
  50. package/dist/visio/collab/compose.d.ts +6 -2
  51. package/dist/visio/collab/compose.d.ts.map +1 -1
  52. package/dist/visio/collab/fixups.d.ts +5 -0
  53. package/dist/visio/collab/fixups.d.ts.map +1 -0
  54. package/dist/visio/collab/index.cjs +1603 -361
  55. package/dist/visio/collab/index.d.ts +3 -2
  56. package/dist/visio/collab/index.d.ts.map +1 -1
  57. package/dist/visio/collab/index.mjs +1596 -362
  58. package/dist/visio/collab/keys.d.ts +32 -1
  59. package/dist/visio/collab/keys.d.ts.map +1 -1
  60. package/dist/visio/collab/package.d.ts +22 -1
  61. package/dist/visio/collab/package.d.ts.map +1 -1
  62. package/dist/visio/collab/pieces.d.ts +41 -0
  63. package/dist/visio/collab/pieces.d.ts.map +1 -0
  64. package/dist/visio/collab/read-ids.d.ts +35 -0
  65. package/dist/visio/collab/read-ids.d.ts.map +1 -0
  66. package/dist/visio/collab/read-parts.d.ts +34 -0
  67. package/dist/visio/collab/read-parts.d.ts.map +1 -0
  68. package/dist/visio/collab/read-shapes.d.ts +34 -0
  69. package/dist/visio/collab/read-shapes.d.ts.map +1 -0
  70. package/dist/visio/collab/read-util.d.ts +20 -0
  71. package/dist/visio/collab/read-util.d.ts.map +1 -0
  72. package/dist/visio/collab/read.d.ts +40 -19
  73. package/dist/visio/collab/read.d.ts.map +1 -1
  74. package/dist/visio/collab/schema.d.ts +55 -1
  75. package/dist/visio/collab/schema.d.ts.map +1 -1
  76. package/dist/visio/collab/snapshot-contents.d.ts +9 -0
  77. package/dist/visio/collab/snapshot-contents.d.ts.map +1 -0
  78. package/dist/visio/collab/snapshot-io.d.ts +26 -0
  79. package/dist/visio/collab/snapshot-io.d.ts.map +1 -0
  80. package/dist/visio/collab/snapshot-parts.d.ts +14 -0
  81. package/dist/visio/collab/snapshot-parts.d.ts.map +1 -0
  82. package/dist/visio/collab/snapshot.d.ts +31 -24
  83. package/dist/visio/collab/snapshot.d.ts.map +1 -1
  84. package/dist/visio/collab/write-parts.d.ts +8 -0
  85. package/dist/visio/collab/write-parts.d.ts.map +1 -0
  86. package/dist/visio/collab/write-shapes.d.ts +30 -0
  87. package/dist/visio/collab/write-shapes.d.ts.map +1 -0
  88. package/dist/visio/collab/write.d.ts +4 -16
  89. package/dist/visio/collab/write.d.ts.map +1 -1
  90. package/dist/visio/collab/zip-store.d.ts +4 -0
  91. package/dist/visio/collab/zip-store.d.ts.map +1 -0
  92. package/dist/visio/document-level.d.ts +57 -0
  93. package/dist/visio/document-level.d.ts.map +1 -0
  94. package/dist/visio/index.cjs +485 -333
  95. package/dist/visio/index.d.ts +2 -0
  96. package/dist/visio/index.d.ts.map +1 -1
  97. package/dist/visio/index.mjs +484 -334
  98. package/dist/visio/layers.d.ts +3 -0
  99. package/dist/visio/layers.d.ts.map +1 -1
  100. package/dist/visio/package.d.ts +9 -0
  101. package/dist/visio/package.d.ts.map +1 -1
  102. package/dist/visio/parse-incremental.d.ts +16 -0
  103. package/dist/visio/parse-incremental.d.ts.map +1 -0
  104. package/dist/visio/parser.d.ts +14 -0
  105. package/dist/visio/parser.d.ts.map +1 -1
  106. package/dist/visio/shape-metadata.d.ts +3 -0
  107. package/dist/visio/shape-metadata.d.ts.map +1 -1
  108. package/dist/visio/ui/collab-options.d.ts +10 -5
  109. package/dist/visio/ui/collab-options.d.ts.map +1 -1
  110. package/dist/visio/ui/contract.d.ts +27 -2
  111. package/dist/visio/ui/contract.d.ts.map +1 -1
  112. package/dist/visio/ui/customization.d.ts +46 -0
  113. package/dist/visio/ui/customization.d.ts.map +1 -0
  114. package/dist/visio/ui/index.cjs +460 -320
  115. package/dist/visio/ui/index.d.ts +1 -0
  116. package/dist/visio/ui/index.d.ts.map +1 -1
  117. package/dist/visio/ui/index.mjs +458 -321
  118. package/dist/xlsx/ui/commands.d.ts +2 -0
  119. package/dist/xlsx/ui/commands.d.ts.map +1 -1
  120. package/dist/xlsx/ui/customization.d.ts +49 -0
  121. package/dist/xlsx/ui/customization.d.ts.map +1 -0
  122. package/dist/xlsx/ui/index.cjs +39 -1
  123. package/dist/xlsx/ui/index.d.ts +1 -0
  124. package/dist/xlsx/ui/index.d.ts.map +1 -1
  125. package/dist/xlsx/ui/index.mjs +37 -2
  126. package/package.json +6 -1
@@ -1,8 +1,145 @@
1
1
  import * as Y from 'yjs';
2
- import JSZip from 'jszip';
3
2
  import { XMLSerializer, DOMParser } from '@xmldom/xmldom';
3
+ import JSZip from 'jszip';
4
4
 
5
5
  // visio/collab/bind.ts
6
+
7
+ // digest/sha256.ts
8
+ var K = new Uint32Array([
9
+ 1116352408,
10
+ 1899447441,
11
+ 3049323471,
12
+ 3921009573,
13
+ 961987163,
14
+ 1508970993,
15
+ 2453635748,
16
+ 2870763221,
17
+ 3624381080,
18
+ 310598401,
19
+ 607225278,
20
+ 1426881987,
21
+ 1925078388,
22
+ 2162078206,
23
+ 2614888103,
24
+ 3248222580,
25
+ 3835390401,
26
+ 4022224774,
27
+ 264347078,
28
+ 604807628,
29
+ 770255983,
30
+ 1249150122,
31
+ 1555081692,
32
+ 1996064986,
33
+ 2554220882,
34
+ 2821834349,
35
+ 2952996808,
36
+ 3210313671,
37
+ 3336571891,
38
+ 3584528711,
39
+ 113926993,
40
+ 338241895,
41
+ 666307205,
42
+ 773529912,
43
+ 1294757372,
44
+ 1396182291,
45
+ 1695183700,
46
+ 1986661051,
47
+ 2177026350,
48
+ 2456956037,
49
+ 2730485921,
50
+ 2820302411,
51
+ 3259730800,
52
+ 3345764771,
53
+ 3516065817,
54
+ 3600352804,
55
+ 4094571909,
56
+ 275423344,
57
+ 430227734,
58
+ 506948616,
59
+ 659060556,
60
+ 883997877,
61
+ 958139571,
62
+ 1322822218,
63
+ 1537002063,
64
+ 1747873779,
65
+ 1955562222,
66
+ 2024104815,
67
+ 2227730452,
68
+ 2361852424,
69
+ 2428436474,
70
+ 2756734187,
71
+ 3204031479,
72
+ 3329325298
73
+ ]);
74
+ var IV = [
75
+ 1779033703,
76
+ 3144134277,
77
+ 1013904242,
78
+ 2773480762,
79
+ 1359893119,
80
+ 2600822924,
81
+ 528734635,
82
+ 1541459225
83
+ ];
84
+ var W = new Uint32Array(64);
85
+ var rotr = (x, n) => x >>> n | x << 32 - n;
86
+ function compress(h, view, offset) {
87
+ for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);
88
+ for (let i = 16; i < 64; i++) {
89
+ const a2 = W[i - 15];
90
+ const b2 = W[i - 2];
91
+ const s0 = rotr(a2, 7) ^ rotr(a2, 18) ^ a2 >>> 3;
92
+ const s1 = rotr(b2, 17) ^ rotr(b2, 19) ^ b2 >>> 10;
93
+ W[i] = W[i - 16] + s0 + W[i - 7] + s1 >>> 0;
94
+ }
95
+ let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], k = h[7];
96
+ for (let i = 0; i < 64; i++) {
97
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
98
+ const ch = e & f ^ ~e & g;
99
+ const t1 = k + S1 + ch + K[i] + W[i] >>> 0;
100
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
101
+ const maj = a & b ^ a & c ^ b & c;
102
+ const t2 = S0 + maj >>> 0;
103
+ k = g;
104
+ g = f;
105
+ f = e;
106
+ e = d + t1 >>> 0;
107
+ d = c;
108
+ c = b;
109
+ b = a;
110
+ a = t1 + t2 >>> 0;
111
+ }
112
+ h[0] = h[0] + a >>> 0;
113
+ h[1] = h[1] + b >>> 0;
114
+ h[2] = h[2] + c >>> 0;
115
+ h[3] = h[3] + d >>> 0;
116
+ h[4] = h[4] + e >>> 0;
117
+ h[5] = h[5] + f >>> 0;
118
+ h[6] = h[6] + g >>> 0;
119
+ h[7] = h[7] + k >>> 0;
120
+ }
121
+ function pad64(data) {
122
+ const padded = new Uint8Array(Math.ceil((data.length + 9) / 64) * 64);
123
+ padded.set(data);
124
+ padded[data.length] = 128;
125
+ const bits = data.length * 8;
126
+ const view = new DataView(padded.buffer);
127
+ view.setUint32(padded.length - 8, Math.floor(bits / 4294967296));
128
+ view.setUint32(padded.length - 4, bits >>> 0);
129
+ return view;
130
+ }
131
+ function wordsOut(h) {
132
+ const out = new Uint8Array(h.length * 4);
133
+ const view = new DataView(out.buffer);
134
+ for (let i = 0; i < h.length; i++) view.setUint32(i * 4, h[i]);
135
+ return out;
136
+ }
137
+ function sha256(data) {
138
+ const h = new Uint32Array(IV);
139
+ const view = pad64(data);
140
+ for (let offset = 0; offset < view.byteLength; offset += 64) compress(h, view, offset);
141
+ return wordsOut(h);
142
+ }
6
143
  var DEFAULT_XML_LIMITS = {
7
144
  maxDepth: 512,
8
145
  maxNodes: 5e6
@@ -253,11 +390,51 @@ function stripPageOverrides(xml) {
253
390
  (tag) => attrOf(tag, "ContentType") === PAGE_CONTENT_TYPE ? "" : tag
254
391
  );
255
392
  }
256
- function withPageOverrides(xml, pagePaths) {
257
- const clean = stripPageOverrides(xml);
258
- const add = pagePaths.map((path) => `<Override PartName="/${path}" ContentType="${PAGE_CONTENT_TYPE}"/>`).join("");
259
- const close = clean.lastIndexOf("</");
260
- return close < 0 ? clean : clean.slice(0, close) + add + clean.slice(close);
393
+ var withPageOverrides = (xml, pagePaths) => withOverrides(stripPageOverrides(xml), PAGE_CONTENT_TYPE, pagePaths);
394
+ var MASTER_TYPE = "http://schemas.microsoft.com/visio/2010/relationships/master";
395
+ var MASTER_CONTENT_TYPE = "application/vnd.ms-visio.master+xml";
396
+ function withOverrides(xml, contentType, paths) {
397
+ const add = paths.map((path) => `<Override PartName="/${path}" ContentType="${contentType}"/>`).join("");
398
+ const close = xml.lastIndexOf("</");
399
+ return close < 0 ? xml : xml.slice(0, close) + add + xml.slice(close);
400
+ }
401
+ function stripOverrides(xml, types, parts = /* @__PURE__ */ new Set()) {
402
+ return xml.replace(/<(?:\w+:)?Override\b[^>]*>/g, (tag) => {
403
+ const type = attrOf(tag, "ContentType");
404
+ const name = attrOf(tag, "PartName");
405
+ return type !== void 0 && types.includes(type) || name !== void 0 && parts.has(name.replace(/^\//, "")) ? "" : tag;
406
+ });
407
+ }
408
+ function declaredType(xml, path) {
409
+ for (const tag of xml.matchAll(/<(?:\w+:)?Override\b[^>]*>/g)) {
410
+ const name = attrOf(tag[0], "PartName");
411
+ const type = attrOf(tag[0], "ContentType");
412
+ if (name === `/${path}` && type !== void 0) return { type, byDefault: false };
413
+ }
414
+ const ext = /\.([^./]+)$/.exec(path)?.[1]?.toLowerCase();
415
+ if (ext === void 0) return void 0;
416
+ for (const tag of xml.matchAll(/<(?:\w+:)?Default\b[^>]*>/g)) {
417
+ const e = attrOf(tag[0], "Extension");
418
+ const type = attrOf(tag[0], "ContentType");
419
+ if (e?.toLowerCase() === ext && type !== void 0) return { type, byDefault: true };
420
+ }
421
+ return void 0;
422
+ }
423
+ function ensureType(xml, path, declared) {
424
+ if (declaredType(xml, path)) return xml;
425
+ const close = xml.lastIndexOf("</");
426
+ if (close < 0) return xml;
427
+ const ext = /\.([^./]+)$/.exec(path)?.[1];
428
+ const tag = declared.byDefault && ext ? `<Default Extension="${ext}" ContentType="${declared.type}"/>` : `<Override PartName="/${path}" ContentType="${declared.type}"/>`;
429
+ return xml.slice(0, close) + tag + xml.slice(close);
430
+ }
431
+ var isMediaPart = (path) => /(^|\/)media\/[^/]+$/i.test(path);
432
+ function relativeTarget(dir, path) {
433
+ const from = dir ? dir.split("/") : [];
434
+ const to = path.split("/");
435
+ let i = 0;
436
+ while (i < from.length && i < to.length - 1 && from[i] === to[i]) i++;
437
+ return [...from.slice(i).map(() => ".."), ...to.slice(i)].join("/");
261
438
  }
262
439
  function toBase64(bytes) {
263
440
  let out = "";
@@ -291,15 +468,154 @@ function canon(xml) {
291
468
  canonCache.set(xml, result);
292
469
  return result;
293
470
  }
471
+ function contentHash(bytes) {
472
+ let out = "";
473
+ for (const byte of sha256(bytes)) out += byte.toString(16).padStart(2, "0");
474
+ return out;
475
+ }
476
+
477
+ // visio/collab/pieces.ts
478
+ var seg = (value) => value.replace(/[%/]/g, (c) => c === "%" ? "%25" : "%2F");
479
+ var tagName = (open) => /^<([^\s/>]+)/.exec(open)?.[1] ?? "Shape";
480
+ var openTagOf = (text, span) => {
481
+ const tag = text.slice(span.start, span.openEnd);
482
+ return span.selfClosing ? tag.replace(/\s*\/>$/, ">") : tag;
483
+ };
484
+ var closeTagOf = (open) => `</${tagName(open)}>`;
485
+ function splitShape(xml) {
486
+ const box = scanContainer(xml, "Shape");
487
+ if (!box) return void 0;
488
+ const used = /* @__PURE__ */ new Set();
489
+ const unique = (path) => {
490
+ let out = path;
491
+ for (let n = 2; used.has(out); n++) out = `${path}~${n}`;
492
+ used.add(out);
493
+ return out;
494
+ };
495
+ const pieces = [];
496
+ const nested = [];
497
+ const others = /* @__PURE__ */ new Map();
498
+ const other = (parent, name, text) => {
499
+ const k = others.get(parent + name) ?? 0;
500
+ others.set(parent + name, k + 1);
501
+ pieces.push({
502
+ path: unique(`${parent ? `${parent}/` : ""}x/${name}#${k}`),
503
+ parent,
504
+ kind: "leaf",
505
+ text
506
+ });
507
+ };
508
+ const cell = (parent, text) => {
509
+ const n = attrOf(text.slice(0, text.search(/>/) + 1), "N");
510
+ if (n === void 0) return other(parent, "Cell", text);
511
+ pieces.push({
512
+ path: unique(`${parent ? `${parent}/` : ""}c/${seg(n)}`),
513
+ parent,
514
+ kind: "leaf",
515
+ text
516
+ });
517
+ };
518
+ for (const child of box.children) {
519
+ const text = xml.slice(child.start, child.end);
520
+ if (child.name === "Cell") cell("", text);
521
+ else if (child.name === "Shapes") {
522
+ const list = scanContainer(text, "Shapes");
523
+ for (const s of list?.children ?? [])
524
+ if (s.name === "Shape") nested.push(text.slice(s.start, s.end));
525
+ } else if (child.name === "Section") {
526
+ const head = openTagOf(xml, child);
527
+ const n = attrOf(head, "N");
528
+ const ix = attrOf(head, "IX");
529
+ if (n === void 0 && ix === void 0) {
530
+ other("", "Section", text);
531
+ continue;
532
+ }
533
+ const sec = unique(`s/${seg(n ?? "")}${ix !== void 0 ? `:${seg(ix)}` : ""}`);
534
+ pieces.push({ path: sec, parent: "", kind: "open", text: head });
535
+ const inner = scanContainer(text, "Section");
536
+ let rowIndex = 0;
537
+ for (const part of inner?.children ?? []) {
538
+ const sub = text.slice(part.start, part.end);
539
+ if (part.name === "Cell") cell(sec, sub);
540
+ else if (part.name === "Row") {
541
+ const rowHead = openTagOf(text, part);
542
+ const id = attrOf(rowHead, "IX") ?? attrOf(rowHead, "N") ?? `#${rowIndex}`;
543
+ rowIndex++;
544
+ const row = unique(`${sec}/r/${seg(id)}`);
545
+ pieces.push({ path: row, parent: sec, kind: "open", text: rowHead });
546
+ for (const rc of scanContainer(sub, "Row")?.children ?? [])
547
+ if (rc.name === "Cell") cell(row, sub.slice(rc.start, rc.end));
548
+ else other(row, rc.name, sub.slice(rc.start, rc.end));
549
+ } else other(sec, part.name, sub);
550
+ }
551
+ } else other("", child.name, text);
552
+ }
553
+ return { open: openTagOf(xml, box), pieces, nested };
554
+ }
555
+ function buildShape(open, entries, tail) {
556
+ const byParent = /* @__PURE__ */ new Map();
557
+ for (const [path, entry] of entries) {
558
+ const parent = parentOf(path);
559
+ const list = byParent.get(parent) ?? [];
560
+ list.push({ path, entry });
561
+ byParent.set(parent, list);
562
+ }
563
+ const render = (parent) => (byParent.get(parent) ?? []).sort((a, b) => a.entry.pos - b.entry.pos || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0)).map(
564
+ ({ path, entry }) => entry.open !== void 0 ? entry.open + render(path) + closeTagOf(entry.open) : entry.xml ?? ""
565
+ ).join("");
566
+ return open + render("") + tail + closeTagOf(open);
567
+ }
568
+ function parentOf(path) {
569
+ const parts = path.split("/");
570
+ return parts.length <= 2 ? "" : parts.slice(0, -2).join("/");
571
+ }
572
+ var SIMPLE = /^<([^\s/>]+)((?:\s+[^\s=/>]+\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*(?:\/>|>([^<]*)<\/\1>)$/;
573
+ var ATTR = /\s([^\s=/>]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
574
+ function pieceContent(kind, text) {
575
+ const simple = SIMPLE.exec(kind === "open" ? `${text}</${tagName(text)}>` : text);
576
+ if (!simple) return kind === "open" ? canon(`${text}</${tagName(text)}>`) : canon(text);
577
+ const attrs = [...(simple[2] ?? "").matchAll(ATTR)].map((m) => [m[1], m[2] ?? m[3] ?? ""]).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
578
+ return JSON.stringify([simple[1], attrs, simple[3] ?? ""]);
579
+ }
580
+
581
+ // visio/collab/baseline.ts
582
+ var masterScope = (key) => `m~${key}`;
583
+ var pageId = (key) => `p:${key}`;
584
+ var bodyId = (key) => `b:${key}`;
585
+ var shapeId = (page, key) => `s:${page}/${key}`;
586
+ var cellId = (page, shape, path) => `f:${page}/${shape}/${path}`;
587
+ var connectId = (key) => `c:${key}`;
588
+ var masterId = (key) => `m:${key}`;
589
+ var mediaId = (key) => `i:${key}`;
590
+ var pageContent = (e) => JSON.stringify([e.id, e.back ?? null, canon(e.xml)]);
591
+ var bodyContent = (e) => JSON.stringify([canon(e.shell), e.rels ?? null]);
592
+ var shellContent = (e) => JSON.stringify([e.parent ?? null, pieceContent("open", e.open)]);
593
+ var entryContent = (e) => e.xml !== void 0 ? pieceContent("leaf", e.xml) : pieceContent("open", e.open ?? "");
594
+ var masterContent = (e) => JSON.stringify([e.id, canon(e.xml), e.rels ?? null, e.print]);
294
595
 
295
596
  // visio/collab/schema.ts
296
- var SCHEMA_VERSION = 1;
597
+ var SCHEMA_VERSION = 4;
598
+ var SchemaVersionError = class extends Error {
599
+ constructor(found) {
600
+ super(
601
+ `The shared drawing uses schema version ${String(found)}; this build reads version ${SCHEMA_VERSION}.`
602
+ );
603
+ this.found = found;
604
+ this.name = "SchemaVersionError";
605
+ }
606
+ found;
607
+ };
297
608
  var META = "visio:meta";
298
609
  var PAGES = "visio:pages";
299
610
  var BODIES = "visio:bodies";
300
611
  var SHAPES = "visio:shapes";
612
+ var CELLS = "visio:cells";
301
613
  var CONNECTS = "visio:connects";
302
614
  var DEAD = "visio:dead";
615
+ var MASTERS = "visio:masters";
616
+ var RENUM = "visio:renum";
617
+ var MEDIA = "visio:media";
618
+ var BLOBS = "visio:blobs";
303
619
  var PARTS = "visio:parts";
304
620
  var REL_MARK = "<!--@rel-->";
305
621
  var SHAPES_MARK = "<!--@shapes-->";
@@ -310,8 +626,13 @@ function sharedDrawing(doc) {
310
626
  pages: doc.getMap(PAGES),
311
627
  bodies: doc.getMap(BODIES),
312
628
  shapes: doc.getMap(SHAPES),
629
+ cells: doc.getMap(CELLS),
313
630
  connects: doc.getMap(CONNECTS),
314
631
  dead: doc.getMap(DEAD),
632
+ masters: doc.getMap(MASTERS),
633
+ renum: doc.getMap(RENUM),
634
+ media: doc.getMap(MEDIA),
635
+ blobs: doc.getMap(BLOBS),
315
636
  parts: doc.getMap(PARTS)
316
637
  };
317
638
  }
@@ -320,17 +641,27 @@ var sharedTypes = (shared) => [
320
641
  shared.pages,
321
642
  shared.bodies,
322
643
  shared.shapes,
644
+ shared.cells,
323
645
  shared.connects,
324
646
  shared.dead,
647
+ shared.masters,
648
+ shared.renum,
649
+ shared.media,
650
+ shared.blobs,
325
651
  shared.parts
326
652
  ];
327
653
  var isSharedDrawingEmpty = (shared) => shared.pages.size === 0 && shared.meta.get("v") === void 0;
654
+ function assertSchemaVersion(shared) {
655
+ const v = shared.meta.get("v");
656
+ if (v !== void 0 && v !== SCHEMA_VERSION) throw new SchemaVersionError(v);
657
+ }
328
658
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
329
659
  function sameRecord(a, b) {
330
660
  if (!isRecord(a) || !isRecord(b)) return a === b;
331
661
  const ka = Object.keys(a).filter((k) => a[k] !== void 0);
332
662
  const kb = Object.keys(b).filter((k) => b[k] !== void 0);
333
- return ka.length === kb.length && ka.every((k) => a[k] === b[k]);
663
+ const same = (x, y) => x === y || Array.isArray(x) && Array.isArray(y) && JSON.stringify(x) === JSON.stringify(y);
664
+ return ka.length === kb.length && ka.every((k) => same(a[k], b[k]));
334
665
  }
335
666
  function setIfChanged(map, key, value) {
336
667
  if (sameRecord(map.get(key), value)) return false;
@@ -339,27 +670,59 @@ function setIfChanged(map, key, value) {
339
670
  }
340
671
  var str = (value) => typeof value === "string" ? value : void 0;
341
672
  var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
673
+ var opt = (key, value) => value === void 0 ? {} : { [key]: value };
342
674
  function pageEntry(value) {
343
675
  if (!isRecord(value)) return void 0;
344
676
  const pos = num(value.pos);
345
677
  const id = str(value.id);
346
678
  const xml = str(value.xml);
347
679
  if (pos === void 0 || id === void 0 || xml === void 0) return void 0;
348
- const back = str(value.back);
349
- return { pos, id, xml, ...back !== void 0 ? { back } : {} };
680
+ return { pos, id, xml, ...opt("back", str(value.back)) };
350
681
  }
351
682
  function bodyEntry(value) {
352
683
  if (!isRecord(value)) return void 0;
353
684
  const shell = str(value.shell);
354
685
  if (shell === void 0) return void 0;
355
- const rels = str(value.rels);
356
- return { shell, ...rels !== void 0 ? { rels } : {} };
686
+ return { shell, ...opt("rels", str(value.rels)), ...opt("b", str(value.b)) };
357
687
  }
358
688
  function shapeEntry(value) {
689
+ if (!isRecord(value)) return void 0;
690
+ const pos = num(value.pos);
691
+ const open = str(value.open);
692
+ if (pos === void 0 || open === void 0) return void 0;
693
+ return { pos, open, ...opt("parent", str(value.parent)), ...opt("b", str(value.b)) };
694
+ }
695
+ function cellEntry(value) {
359
696
  if (!isRecord(value)) return void 0;
360
697
  const pos = num(value.pos);
361
698
  const xml = str(value.xml);
362
- return pos === void 0 || xml === void 0 ? void 0 : { pos, xml };
699
+ const open = str(value.open);
700
+ if (pos === void 0 || xml === void 0 && open === void 0) return void 0;
701
+ const r = Array.isArray(value.r) && value.r.length % 2 === 0 && value.r.every((v) => typeof v === "string") ? value.r : void 0;
702
+ return {
703
+ pos,
704
+ ...opt("xml", xml),
705
+ ...opt("open", xml === void 0 ? open : void 0),
706
+ ...r && r.length ? { r } : {}
707
+ };
708
+ }
709
+ function masterEntry(value) {
710
+ if (!isRecord(value)) return void 0;
711
+ const pos = num(value.pos);
712
+ const id = str(value.id);
713
+ const xml = str(value.xml);
714
+ const part = str(value.part);
715
+ if (pos === void 0 || id === void 0 || xml === void 0 || part === void 0)
716
+ return void 0;
717
+ return { pos, id, xml, part, ...opt("rels", str(value.rels)), ...opt("b", str(value.b)) };
718
+ }
719
+ function mediaEntry(value) {
720
+ if (!isRecord(value)) return void 0;
721
+ const path = str(value.path);
722
+ const hash = str(value.hash);
723
+ const ct = str(value.ct);
724
+ if (path === void 0 || hash === void 0 || ct === void 0) return void 0;
725
+ return { path, hash, ct, d: value.d === true, ...opt("b", str(value.b)) };
363
726
  }
364
727
  function connectEntry(value) {
365
728
  if (!isRecord(value)) return void 0;
@@ -371,11 +734,15 @@ function connectEntry(value) {
371
734
  }
372
735
  return out;
373
736
  }
374
- var DEFAULT_MAX_PART_BYTES = 48 * 1024 * 1024;
375
- var SHAPE_ID = /<(?:\w+:)?Shape\s[^>]*?\bID\s*=\s*(["'])(\d+)\1/g;
376
- function shapeIds(xml) {
377
- return [...xml.matchAll(SHAPE_ID)].map((m) => m[2]);
737
+ function renumEntry(value) {
738
+ if (!isRecord(value)) return void 0;
739
+ const pair = (v) => Array.isArray(v) && v.length === 2 && typeof v[0] === "string" && typeof v[1] === "string" ? [v[0], v[1]] : void 0;
740
+ const i = pair(value.i);
741
+ const m = pair(value.m);
742
+ return i || m ? { ...i ? { i } : {}, ...m ? { m } : {} } : void 0;
378
743
  }
744
+
745
+ // visio/collab/snapshot-contents.ts
379
746
  function splitList(text, name, mark, child) {
380
747
  const box = scanContainer(text, name);
381
748
  if (!box) return { text, items: [] };
@@ -391,6 +758,66 @@ function splitList(text, name, mark, child) {
391
758
  }
392
759
  return { text: text.slice(0, box.openEnd) + mark + text.slice(box.closeStart), items };
393
760
  }
761
+ function formulaRefs(text, owner) {
762
+ const out = [];
763
+ const seen = /* @__PURE__ */ new Set();
764
+ for (const f of text.matchAll(/\sF\s*=\s*("[^"]*"|'[^']*')/g))
765
+ for (const m of f[1].matchAll(/Sheet\.(\d+)(?!\d)/g)) {
766
+ const id = m[1];
767
+ const key = owner.get(id);
768
+ if (key !== void 0 && !seen.has(id)) {
769
+ seen.add(id);
770
+ out.push(id, key);
771
+ }
772
+ }
773
+ return out;
774
+ }
775
+ function flatten(xml, parent, page, keys, out) {
776
+ const split2 = splitShape(xml);
777
+ const id = split2 ? attrOf(split2.open, "ID") : void 0;
778
+ if (!split2 || id === void 0) return;
779
+ const key = keys.shapeKey(page, id);
780
+ out.push({
781
+ key,
782
+ id,
783
+ ...parent !== void 0 ? { parent } : {},
784
+ open: split2.open,
785
+ pieces: split2.pieces
786
+ });
787
+ for (const nested of split2.nested) flatten(nested, key, page, keys, out);
788
+ }
789
+ function splitContents(content, scope, keys) {
790
+ const shapesSplit = splitList(content, "Shapes", SHAPES_MARK, "Shape");
791
+ const connectsSplit = splitList(shapesSplit.text, "Connects", CONNECTS_MARK, "Connect");
792
+ const shapes = [];
793
+ for (const text of shapesSplit.items) flatten(text, void 0, scope, keys, shapes);
794
+ const owner = new Map(shapes.map((s) => [s.id, s.key]));
795
+ for (const shape of shapes)
796
+ for (const piece of shape.pieces)
797
+ if (piece.kind === "leaf") {
798
+ const refs = formulaRefs(piece.text, owner);
799
+ if (refs.length) piece.refs = refs;
800
+ }
801
+ const connects = [];
802
+ for (const text of connectsSplit.items) {
803
+ const a = (name) => attrOf(text, name) ?? "";
804
+ const entry = {
805
+ fk: owner.get(a("FromSheet")) ?? "",
806
+ fi: a("FromSheet"),
807
+ tk: owner.get(a("ToSheet")) ?? "",
808
+ ti: a("ToSheet"),
809
+ fc: a("FromCell"),
810
+ tc: a("ToCell"),
811
+ fp: a("FromPart"),
812
+ tp: a("ToPart")
813
+ };
814
+ connects.push({
815
+ key: `${scope}/${entry.fk}.${entry.fi}.${entry.fc}.${entry.fp}>${entry.tk}.${entry.ti}.${entry.tc}.${entry.tp}`,
816
+ entry
817
+ });
818
+ }
819
+ return { shell: connectsSplit.text, shapes, connects };
820
+ }
394
821
  var readZip = (bytes) => JSZip.loadAsync(bytes);
395
822
  var zipText = async (zip, path) => zip.file(path)?.async("string");
396
823
  async function readLayout(zip) {
@@ -401,19 +828,25 @@ async function readLayout(zip) {
401
828
  const documentPath = resolveTarget("", root.target);
402
829
  const rels = parseRelationships(await zipText(zip, relsPathOf(documentPath)));
403
830
  const pages = rels.find((r) => r.type.endsWith("/pages"));
404
- return pages ? { documentPath, pagesPath: resolveTarget(dirName(documentPath), pages.target) } : void 0;
831
+ const masters = rels.find((r) => r.type.endsWith("/masters"));
832
+ const dir = dirName(documentPath);
833
+ return pages ? {
834
+ documentPath,
835
+ pagesPath: resolveTarget(dir, pages.target),
836
+ ...masters ? { mastersPath: resolveTarget(dir, masters.target) } : {}
837
+ } : void 0;
405
838
  }
406
- async function readLocalPages(zip, layout, pagesXml) {
407
- const box = scanContainer(pagesXml, "Pages");
839
+ async function readLocalItems(zip, listPath, xml, container, child) {
840
+ const box = scanContainer(xml, container);
408
841
  if (!box) return void 0;
409
842
  const rels = new Map(
410
- parseRelationships(await zipText(zip, relsPathOf(layout.pagesPath))).map((r) => [r.id, r])
843
+ parseRelationships(await zipText(zip, relsPathOf(listPath))).map((r) => [r.id, r])
411
844
  );
412
- const pages = [];
845
+ const items = [];
413
846
  for (const span of box.children) {
414
- if (span.name !== "Page") continue;
415
- const text = pagesXml.slice(span.start, span.end);
416
- const inner = scanContainer(text, "Page");
847
+ if (span.name !== child) continue;
848
+ const text = xml.slice(span.start, span.end);
849
+ const inner = scanContainer(text, child);
417
850
  const relSpan = inner?.children.find((c) => c.name === "Rel");
418
851
  if (!inner || !relSpan) return void 0;
419
852
  const relTag = text.slice(relSpan.start, relSpan.openEnd);
@@ -421,239 +854,268 @@ async function readLocalPages(zip, layout, pagesXml) {
421
854
  const target = rid ? rels.get(rid)?.target : void 0;
422
855
  const id = attrOf(text.slice(0, inner.openEnd), "ID");
423
856
  if (target === void 0 || id === void 0) return void 0;
424
- pages.push({
857
+ items.push({
425
858
  id,
426
859
  text,
427
- path: resolveTarget(dirName(layout.pagesPath), target),
860
+ path: resolveTarget(dirName(listPath), target),
428
861
  rel: { start: relSpan.start, end: relSpan.end }
429
862
  });
430
863
  }
431
- return { box, pages };
864
+ return { box, items };
432
865
  }
866
+ var withMark = (item) => item.text.slice(0, item.rel.start) + REL_MARK + item.text.slice(item.rel.end);
867
+
868
+ // visio/collab/snapshot-parts.ts
869
+ async function snapshotMasters(zip, layout, keys, derived) {
870
+ const masters = [];
871
+ let mastersOpen;
872
+ let mastersClose;
873
+ if (layout.mastersPath) {
874
+ const listXml = await zipText(zip, layout.mastersPath) ?? "";
875
+ const list = await readLocalItems(zip, layout.mastersPath, listXml, "Masters", "Master");
876
+ if (list) {
877
+ derived.add(layout.mastersPath).add(relsPathOf(layout.mastersPath));
878
+ mastersOpen = listXml.slice(0, list.box.openEnd);
879
+ mastersClose = listXml.slice(list.box.closeStart);
880
+ for (const item of list.items) {
881
+ const content = await zipText(zip, item.path);
882
+ if (content === void 0) continue;
883
+ derived.add(item.path).add(relsPathOf(item.path));
884
+ const key = keys.masterKey(item.id);
885
+ keys.masterPaths.set(key, item.path);
886
+ const rels = await zipText(zip, relsPathOf(item.path));
887
+ const { shell, shapes, connects } = splitContents(content, masterScope(key), keys);
888
+ masters.push({
889
+ key,
890
+ id: item.id,
891
+ xml: withMark(item),
892
+ path: item.path,
893
+ part: encoder.encode(shell),
894
+ ...rels !== void 0 ? { rels } : {},
895
+ shapes,
896
+ connects
897
+ });
898
+ }
899
+ }
900
+ }
901
+ return { masters, mastersOpen, mastersClose };
902
+ }
903
+ async function snapshotRest(zip, derived, limit) {
904
+ const parts = /* @__PURE__ */ new Map();
905
+ const media = [];
906
+ const typesXml = await zipText(zip, CONTENT_TYPES) ?? "";
907
+ const mediaPaths = /* @__PURE__ */ new Set();
908
+ let total = 0;
909
+ for (const path of Object.keys(zip.files)) {
910
+ const file = zip.files[path];
911
+ if (file.dir || derived.has(path) || isLocalOnlyPart(path)) continue;
912
+ const data = await file.async("uint8array");
913
+ total += data.length;
914
+ if (total > limit) throw new Error("The package parts exceed the shared size limit.");
915
+ const declared = isMediaPart(path) ? declaredType(typesXml, path) : void 0;
916
+ if (declared) {
917
+ mediaPaths.add(path);
918
+ media.push({
919
+ path,
920
+ hash: contentHash(data),
921
+ ct: declared.type,
922
+ d: declared.byDefault,
923
+ data
924
+ });
925
+ } else parts.set(path, data);
926
+ }
927
+ const types = parts.get(CONTENT_TYPES);
928
+ if (types)
929
+ parts.set(
930
+ CONTENT_TYPES,
931
+ encoder.encode(
932
+ stripOverrides(decoder.decode(types), [PAGE_CONTENT_TYPE, MASTER_CONTENT_TYPE], mediaPaths)
933
+ )
934
+ );
935
+ return { parts, media };
936
+ }
937
+
938
+ // visio/collab/snapshot.ts
939
+ var DEFAULT_MAX_PART_BYTES = 48 * 1024 * 1024;
433
940
  async function snapshotDrawing(bytes, keys, options = {}) {
434
941
  const zip = await readZip(bytes);
435
942
  const layout = await readLayout(zip);
436
943
  if (!layout) throw new Error("The package has no Visio pages part.");
437
944
  const pagesXml = await zipText(zip, layout.pagesPath) ?? "";
438
- const local = await readLocalPages(zip, layout, pagesXml);
945
+ const local = await readLocalItems(zip, layout.pagesPath, pagesXml, "Pages", "Page");
439
946
  if (!local) throw new Error("The pages part cannot be split into pages.");
440
947
  const derived = /* @__PURE__ */ new Set([layout.pagesPath, relsPathOf(layout.pagesPath)]);
441
- const idToKey = new Map(local.pages.map((p) => [p.id, keys.pageKey(p.id)]));
948
+ const idToKey = new Map(local.items.map((p) => [p.id, keys.pageKey(p.id)]));
442
949
  const pages = [];
443
- for (const page of local.pages) {
950
+ for (const page of local.items) {
444
951
  const key = idToKey.get(page.id);
445
952
  derived.add(page.path).add(relsPathOf(page.path));
446
953
  const head = page.text.slice(0, scanContainer(page.text, "Page").openEnd);
447
954
  const backId = attrOf(head, "BackPage");
448
- const xml = page.text.slice(0, page.rel.start) + REL_MARK + page.text.slice(page.rel.end);
449
955
  const content = await zipText(zip, page.path) ?? "";
450
956
  const rels = await zipText(zip, relsPathOf(page.path));
451
- const shapesSplit = splitList(content, "Shapes", SHAPES_MARK, "Shape");
452
- const connectsSplit = splitList(shapesSplit.text, "Connects", CONNECTS_MARK, "Connect");
453
- const owner = /* @__PURE__ */ new Map();
454
- const shapes = [];
455
- for (const text of shapesSplit.items) {
456
- const ids = shapeIds(text);
457
- const id = ids[0];
458
- if (id === void 0) continue;
459
- const shapeKey = keys.shapeKey(key, id);
460
- for (const nested of ids) owner.set(nested, shapeKey);
461
- shapes.push({ key: shapeKey, id, xml: text });
462
- }
463
- const connects = [];
464
- for (const text of connectsSplit.items) {
465
- const a = (name) => attrOf(text, name) ?? "";
466
- const entry = {
467
- fk: owner.get(a("FromSheet")) ?? "",
468
- fi: a("FromSheet"),
469
- tk: owner.get(a("ToSheet")) ?? "",
470
- ti: a("ToSheet"),
471
- fc: a("FromCell"),
472
- tc: a("ToCell"),
473
- fp: a("FromPart"),
474
- tp: a("ToPart")
475
- };
476
- connects.push({
477
- key: `${key}/${entry.fk}.${entry.fi}.${entry.fc}.${entry.fp}>${entry.tk}.${entry.ti}.${entry.tc}.${entry.tp}`,
478
- entry
479
- });
480
- }
957
+ const { shell, shapes, connects } = splitContents(content, key, keys);
481
958
  keys.pagePaths.set(key, page.path);
482
959
  pages.push({
483
960
  key,
484
961
  id: page.id,
485
962
  ...backId !== void 0 && idToKey.has(backId) ? { back: idToKey.get(backId) } : {},
486
- xml,
487
- shell: connectsSplit.text,
963
+ xml: withMark(page),
964
+ shell,
488
965
  ...rels !== void 0 ? { rels } : {},
489
966
  path: page.path,
490
967
  shapes,
491
968
  connects
492
969
  });
493
970
  }
494
- const parts = /* @__PURE__ */ new Map();
495
- let total = 0;
496
- const limit = options.maxPartBytes ?? DEFAULT_MAX_PART_BYTES;
497
- for (const path of Object.keys(zip.files)) {
498
- const file = zip.files[path];
499
- if (file.dir || derived.has(path) || isLocalOnlyPart(path)) continue;
500
- let data = await file.async("uint8array");
501
- if (path === CONTENT_TYPES) data = encoder.encode(stripPageOverrides(decoder.decode(data)));
502
- total += data.length;
503
- if (total > limit) throw new Error("The package parts exceed the shared size limit.");
504
- parts.set(path, data);
505
- }
971
+ const { masters, mastersOpen, mastersClose } = await snapshotMasters(zip, layout, keys, derived);
972
+ const { parts, media } = await snapshotRest(
973
+ zip,
974
+ derived,
975
+ options.maxPartBytes ?? DEFAULT_MAX_PART_BYTES
976
+ );
506
977
  return {
507
978
  pagesPath: layout.pagesPath,
508
979
  pagesOpen: pagesXml.slice(0, local.box.openEnd),
509
980
  pagesClose: pagesXml.slice(local.box.closeStart),
510
981
  pages,
982
+ ...mastersOpen !== void 0 && mastersClose !== void 0 && layout.mastersPath ? { mastersPath: layout.mastersPath, mastersOpen, mastersClose } : {},
983
+ masters,
984
+ media,
511
985
  parts
512
986
  };
513
987
  }
514
988
 
515
- // visio/collab/order.ts
516
- function assignPositions(order, current) {
517
- const result = /* @__PURE__ */ new Map();
518
- const kept = [];
519
- let last = -Infinity;
520
- for (const key of order) {
521
- const pos = current(key);
522
- if (pos !== void 0 && pos > last) {
523
- kept.push(pos);
524
- last = pos;
525
- } else kept.push(void 0);
526
- }
527
- let i = 0;
528
- while (i < order.length) {
529
- const known = kept[i];
530
- if (known !== void 0) {
531
- result.set(order[i], known);
532
- i++;
533
- continue;
534
- }
535
- let end = i;
536
- while (end < order.length && kept[end] === void 0) end++;
537
- const lo = i > 0 ? kept[i - 1] : void 0;
538
- const hi = end < order.length ? kept[end] : void 0;
539
- const count = end - i;
540
- for (let j = 0; j < count; j++) {
541
- const key = order[i + j];
542
- if (lo !== void 0 && hi !== void 0)
543
- result.set(key, lo + (hi - lo) * (j + 1) / (count + 1));
544
- else if (lo !== void 0) result.set(key, lo + j + 1);
545
- else if (hi !== void 0) result.set(key, hi - count + j);
546
- else result.set(key, j + 1);
989
+ // visio/collab/compose-lists.ts
990
+ var relTarget = (dir, path) => dir && path.startsWith(`${dir}/`) ? path.slice(dir.length + 1) : `/${path}`;
991
+ async function placeList(options) {
992
+ const { files, taken, localZip, items } = options;
993
+ const dir = dirName(options.listPath);
994
+ taken.add(options.listPath).add(relsPathOf(options.listPath));
995
+ const paths = /* @__PURE__ */ new Map();
996
+ for (const item of items) {
997
+ const known = options.known.get(item.key);
998
+ if (known && !taken.has(known) && localZip?.file(known)) {
999
+ paths.set(item.key, known);
1000
+ taken.add(known);
547
1001
  }
548
- i = end;
549
1002
  }
550
- return result;
1003
+ let counter = 0;
1004
+ for (const item of items) {
1005
+ if (paths.has(item.key)) continue;
1006
+ let path;
1007
+ do
1008
+ path = `${dir ? `${dir}/` : ""}${options.stem}${++counter}.xml`;
1009
+ while (taken.has(path) || localZip?.file(path));
1010
+ paths.set(item.key, path);
1011
+ taken.add(path);
1012
+ }
1013
+ const others = parseRelationships(
1014
+ localZip ? await zipText(localZip, relsPathOf(options.listPath)) : void 0
1015
+ ).filter((r) => r.type !== options.relType);
1016
+ const usedIds = new Set(others.map((r) => r.id));
1017
+ let nextRel = 0;
1018
+ const rels = [];
1019
+ const elements = [];
1020
+ for (const item of items) {
1021
+ const path = paths.get(item.key);
1022
+ let id;
1023
+ do
1024
+ id = `rId${++nextRel}`;
1025
+ while (usedIds.has(id));
1026
+ usedIds.add(id);
1027
+ rels.push(
1028
+ `<Relationship Id="${id}" Type="${options.relType}" Target="${escapeAttr(relTarget(dir, path))}"/>`
1029
+ );
1030
+ const rel = `<Rel xmlns:r="${REL_NS}" r:id="${id}"/>`;
1031
+ elements.push(
1032
+ item.xml.includes(REL_MARK) ? item.xml.replace(REL_MARK, () => rel) : item.xml.slice(0, item.xml.lastIndexOf("</")) + rel + item.xml.slice(item.xml.lastIndexOf("</"))
1033
+ );
1034
+ files.set(path, item.part);
1035
+ if (item.rels !== void 0) files.set(relsPathOf(path), encoder.encode(item.rels));
1036
+ }
1037
+ files.set(options.listPath, encoder.encode(options.open + elements.join("") + options.close));
1038
+ files.set(
1039
+ relsPathOf(options.listPath),
1040
+ encoder.encode(
1041
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' + others.map((r) => r.text).join("") + rels.join("") + "</Relationships>"
1042
+ )
1043
+ );
1044
+ return paths;
551
1045
  }
552
- function byPosition(items) {
553
- return items.sort((a, b) => a.pos - b.pos || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
1046
+ new TextDecoder("utf-8", { fatal: true });
1047
+ var crcTable = Uint32Array.from({ length: 256 }, (_, value) => {
1048
+ for (let i = 0; i < 8; i++) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
1049
+ return value >>> 0;
1050
+ });
1051
+ function crc32(bytes, crc = 4294967295) {
1052
+ for (const byte of bytes) crc = (crcTable[(crc ^ byte) & 255] ?? 0) ^ crc >>> 8;
1053
+ return crc >>> 0;
554
1054
  }
555
1055
 
556
- // visio/collab/write.ts
557
- var pageId = (key) => `p:${key}`;
558
- var bodyId = (key) => `b:${key}`;
559
- var shapeId = (page, key) => `s:${page}/${key}`;
560
- var connectId = (key) => `c:${key}`;
561
- var pageContent = (e) => JSON.stringify([e.id, e.back ?? null, canon(e.xml)]);
562
- var bodyContent = (e) => JSON.stringify([canon(e.shell), e.rels ?? null]);
563
- function writeSharedDrawing(shared, snapshot, keys) {
564
- let writes = 0;
565
- const set = (map, key, value) => {
566
- if (setIfChanged(map, key, value)) writes++;
567
- };
568
- set(shared.meta, "v", SCHEMA_VERSION);
569
- set(shared.meta, "pagesPath", snapshot.pagesPath);
570
- set(shared.meta, "pagesOpen", snapshot.pagesOpen);
571
- set(shared.meta, "pagesClose", snapshot.pagesClose);
572
- const now = /* @__PURE__ */ new Map();
573
- const changed = (id, content, room) => {
574
- const base = keys.known.get(id);
575
- return base === void 0 ? room !== content : base !== content;
576
- };
577
- const pagePos = assignPositions(
578
- snapshot.pages.map((p) => p.key),
579
- (key) => pageEntry(shared.pages.get(key))?.pos
580
- );
581
- for (const page of snapshot.pages) {
582
- const room = pageEntry(shared.pages.get(page.key));
583
- const content = pageContent(page);
584
- now.set(pageId(page.key), content);
585
- const own = changed(pageId(page.key), content, room && pageContent(room));
586
- const source = own || !room ? page : room;
587
- set(shared.pages, page.key, {
588
- pos: pagePos.get(page.key),
589
- id: source.id,
590
- ...source.back !== void 0 ? { back: source.back } : {},
591
- xml: source.xml
592
- });
593
- const roomBody = bodyEntry(shared.bodies.get(page.key));
594
- const bodyText = bodyContent(page);
595
- now.set(bodyId(page.key), bodyText);
596
- const ownBody = changed(bodyId(page.key), bodyText, roomBody && bodyContent(roomBody));
597
- const body = ownBody || !roomBody ? page : roomBody;
598
- set(shared.bodies, page.key, {
599
- shell: body.shell,
600
- ...body.rels !== void 0 ? { rels: body.rels } : {}
601
- });
602
- const shapePos = assignPositions(
603
- page.shapes.map((s) => s.key),
604
- (key) => shapeEntry(shared.shapes.get(`${page.key}/${key}`))?.pos
605
- );
606
- for (const shape of page.shapes) {
607
- const id = shapeId(page.key, shape.key);
608
- const roomShape = shapeEntry(shared.shapes.get(`${page.key}/${shape.key}`));
609
- const text = canon(shape.xml);
610
- now.set(id, text);
611
- const xml = changed(id, text, roomShape && canon(roomShape.xml)) || !roomShape ? shape.xml : roomShape.xml;
612
- set(shared.shapes, `${page.key}/${shape.key}`, { pos: shapePos.get(shape.key), xml });
613
- }
614
- for (const connect of page.connects) {
615
- now.set(connectId(connect.key), "");
616
- if (!keys.known.has(connectId(connect.key)) || !shared.connects.has(connect.key))
617
- set(shared.connects, connect.key, connect.entry);
1056
+ // visio/collab/zip-store.ts
1057
+ var encoder2 = new TextEncoder();
1058
+ var DOS_TIME = 0;
1059
+ var DOS_DATE = 33;
1060
+ function writeStoredZip(files, crcs) {
1061
+ const prepared = files.map(([name, data]) => {
1062
+ let crc = crcs.get(data);
1063
+ if (crc === void 0) {
1064
+ crc = (crc32(data) ^ 4294967295) >>> 0;
1065
+ crcs.set(data, crc);
618
1066
  }
1067
+ return { name: encoder2.encode(name), data, crc };
1068
+ });
1069
+ let local = 0;
1070
+ let central = 0;
1071
+ for (const f of prepared) {
1072
+ local += 30 + f.name.length + f.data.length;
1073
+ central += 46 + f.name.length;
619
1074
  }
620
- for (const id of keys.known.keys()) {
621
- if (now.has(id)) continue;
622
- const name = id.slice(2);
623
- if (id.startsWith("p:")) {
624
- shared.dead.set(name, true);
625
- for (const map of [shared.pages, shared.bodies]) map.delete(name);
626
- for (const map of [shared.shapes, shared.connects])
627
- for (const key of [...map.keys()]) if (key.startsWith(`${name}/`)) map.delete(key);
628
- keys.forgetPage(name);
629
- writes++;
630
- } else if (id.startsWith("s:")) {
631
- const slash = name.indexOf("/");
632
- const page = name.slice(0, slash);
633
- if (!now.has(pageId(page))) continue;
634
- shared.dead.set(name, true);
635
- shared.shapes.delete(name);
636
- keys.forgetShape(page, name.slice(slash + 1));
637
- writes++;
638
- } else if (id.startsWith("c:") && shared.connects.has(name)) {
639
- shared.connects.delete(name);
640
- writes++;
641
- }
1075
+ const out = new Uint8Array(local + central + 22);
1076
+ const view = new DataView(out.buffer);
1077
+ let at = 0;
1078
+ const offsets = [];
1079
+ for (const f of prepared) {
1080
+ offsets.push(at);
1081
+ view.setUint32(at, 67324752, true);
1082
+ view.setUint16(at + 4, 20, true);
1083
+ view.setUint16(at + 6, 2048, true);
1084
+ view.setUint16(at + 8, 0, true);
1085
+ view.setUint16(at + 10, DOS_TIME, true);
1086
+ view.setUint16(at + 12, DOS_DATE, true);
1087
+ view.setUint32(at + 14, f.crc, true);
1088
+ view.setUint32(at + 18, f.data.length, true);
1089
+ view.setUint32(at + 22, f.data.length, true);
1090
+ view.setUint16(at + 26, f.name.length, true);
1091
+ view.setUint16(at + 28, 0, true);
1092
+ out.set(f.name, at + 30);
1093
+ out.set(f.data, at + 30 + f.name.length);
1094
+ at += 30 + f.name.length + f.data.length;
642
1095
  }
643
- keys.known = now;
644
- for (const [path, data] of snapshot.parts) {
645
- const print = fingerprint(data);
646
- if (keys.partPrints.get(path) === print) continue;
647
- set(shared.parts, path, toBase64(data));
648
- keys.partPrints.set(path, print);
649
- }
650
- for (const path of [...keys.partPrints.keys()])
651
- if (!snapshot.parts.has(path)) {
652
- shared.parts.delete(path);
653
- keys.partPrints.delete(path);
654
- writes++;
655
- }
656
- return writes;
1096
+ const centralStart = at;
1097
+ prepared.forEach((f, i) => {
1098
+ view.setUint32(at, 33639248, true);
1099
+ view.setUint16(at + 4, 20, true);
1100
+ view.setUint16(at + 6, 20, true);
1101
+ view.setUint16(at + 8, 2048, true);
1102
+ view.setUint16(at + 10, 0, true);
1103
+ view.setUint16(at + 12, DOS_TIME, true);
1104
+ view.setUint16(at + 14, DOS_DATE, true);
1105
+ view.setUint32(at + 16, f.crc, true);
1106
+ view.setUint32(at + 20, f.data.length, true);
1107
+ view.setUint32(at + 24, f.data.length, true);
1108
+ view.setUint16(at + 28, f.name.length, true);
1109
+ view.setUint32(at + 42, offsets[i], true);
1110
+ out.set(f.name, at + 46);
1111
+ at += 46 + f.name.length;
1112
+ });
1113
+ view.setUint32(at, 101010256, true);
1114
+ view.setUint16(at + 8, prepared.length, true);
1115
+ view.setUint16(at + 10, prepared.length, true);
1116
+ view.setUint32(at + 12, at - centralStart, true);
1117
+ view.setUint32(at + 16, centralStart, true);
1118
+ return out;
657
1119
  }
658
1120
 
659
1121
  // visio/collab/compose.ts
@@ -669,32 +1131,51 @@ function connectXml(item) {
669
1131
  ];
670
1132
  return `<Connect ${attrs.filter(([, v]) => v !== "").map(([k, v]) => `${k}="${escapeAttr(v)}"`).join(" ")}/>`;
671
1133
  }
672
- function pagePartXml(page) {
673
- let shell = page.body.shell;
674
- const root = scanContainer(shell, "PageContents");
675
- if (page.shapes.length && !shell.includes(SHAPES_MARK) && root && !root.selfClosing)
676
- shell = shell.slice(0, root.openEnd) + `<Shapes>${SHAPES_MARK}</Shapes>` + shell.slice(root.openEnd);
677
- const root2 = scanContainer(shell, "PageContents");
678
- if (page.connects.length && !shell.includes(CONNECTS_MARK) && root2 && !root2.selfClosing)
679
- shell = shell.slice(0, root2.closeStart) + `<Connects>${CONNECTS_MARK}</Connects>` + shell.slice(root2.closeStart);
680
- return shell.replace(SHAPES_MARK, () => page.shapes.map((s) => s.xml).join("")).replace(CONNECTS_MARK, () => page.connects.map(connectXml).join(""));
681
- }
682
- function relTarget(pagesDir, path) {
683
- return pagesDir && path.startsWith(`${pagesDir}/`) ? path.slice(pagesDir.length + 1) : `/${path}`;
1134
+ function contentsXml(shell, root, shapesXml, hasShapes, connects) {
1135
+ const outer = scanContainer(shell, root);
1136
+ if (hasShapes && !shell.includes(SHAPES_MARK) && outer && !outer.selfClosing)
1137
+ shell = shell.slice(0, outer.openEnd) + `<Shapes>${SHAPES_MARK}</Shapes>` + shell.slice(outer.openEnd);
1138
+ const outer2 = scanContainer(shell, root);
1139
+ if (connects.length && !shell.includes(CONNECTS_MARK) && outer2 && !outer2.selfClosing)
1140
+ shell = shell.slice(0, outer2.closeStart) + `<Connects>${CONNECTS_MARK}</Connects>` + shell.slice(outer2.closeStart);
1141
+ return shell.replace(SHAPES_MARK, () => shapesXml).replace(CONNECTS_MARK, () => connects.map(connectXml).join(""));
684
1142
  }
1143
+ var pagePartXml = (page) => contentsXml(
1144
+ page.body.shell,
1145
+ "PageContents",
1146
+ page.shapesXml,
1147
+ page.shapes.length > 0,
1148
+ page.connects
1149
+ );
1150
+ var masterPartXml = (master) => contentsXml(
1151
+ decoder.decode(fromBase64(master.part)),
1152
+ "MasterContents",
1153
+ master.shapesXml,
1154
+ master.shapes.length > 0,
1155
+ master.connects
1156
+ );
685
1157
  async function composeDrawing(drawing, local, keys, options = {}) {
686
1158
  const localZip = local ? await readZip(local) : void 0;
687
1159
  const files = /* @__PURE__ */ new Map();
688
1160
  const prints = /* @__PURE__ */ new Map();
689
1161
  let total = 0;
690
1162
  const limit = options.maxPartBytes ?? 48 * 1024 * 1024;
691
- for (const [path, b64] of drawing.parts) {
692
- const data = fromBase64(b64);
1163
+ const cache = keys.cache.parts;
1164
+ const decode = (path, b64) => {
1165
+ const hit = cache.get(path);
1166
+ const data = hit && hit.b64 === b64 ? hit.data : fromBase64(b64);
1167
+ if (!hit || hit.b64 !== b64) cache.set(path, { b64, data });
693
1168
  total += data.length;
694
1169
  if (total > limit) throw new Error("The shared parts exceed the size limit.");
1170
+ return data;
1171
+ };
1172
+ for (const [path, b64] of drawing.parts) {
1173
+ const data = decode(path, b64);
695
1174
  files.set(path, data);
696
1175
  prints.set(path, fingerprint(data));
697
1176
  }
1177
+ for (const path of [...cache.keys()])
1178
+ if (!drawing.parts.has(path) && !path.startsWith("media:")) cache.delete(path);
698
1179
  if (localZip)
699
1180
  for (const path of Object.keys(localZip.files)) {
700
1181
  const file = localZip.files[path];
@@ -702,6 +1183,10 @@ async function composeDrawing(drawing, local, keys, options = {}) {
702
1183
  files.set(path, await file.async("uint8array"));
703
1184
  }
704
1185
  let contentTypes = files.get(CONTENT_TYPES) !== void 0 ? decoder.decode(files.get(CONTENT_TYPES)) : (localZip && await zipText(localZip, CONTENT_TYPES)) ?? DEFAULT_CONTENT_TYPES;
1186
+ for (const item of drawing.media) {
1187
+ files.set(item.path, decode(`media:${item.path}`, item.data));
1188
+ contentTypes = ensureType(contentTypes, item.path, item.declared);
1189
+ }
705
1190
  for (const [path, data] of [...files]) {
706
1191
  if (!path.endsWith(".rels") || typeof data === "string") continue;
707
1192
  const source = path.replace(/(^|\/)_rels\/([^/]+)\.rels$/, "$1$2");
@@ -720,91 +1205,119 @@ async function composeDrawing(drawing, local, keys, options = {}) {
720
1205
  });
721
1206
  if (next !== text) files.set(path, encoder.encode(next));
722
1207
  }
723
- const pagesDir = dirName(drawing.pagesPath);
724
- const taken = /* @__PURE__ */ new Set([
725
- ...files.keys(),
726
- drawing.pagesPath,
727
- relsPathOf(drawing.pagesPath)
728
- ]);
729
- const paths = /* @__PURE__ */ new Map();
730
- for (const page of drawing.pages) {
731
- const known = keys.pagePaths.get(page.key);
732
- if (known && !taken.has(known) && localZip?.file(known)) {
733
- paths.set(page.key, known);
734
- taken.add(known);
735
- }
736
- }
737
- let counter = 0;
738
- for (const page of drawing.pages) {
739
- if (paths.has(page.key)) continue;
740
- let path;
741
- do
742
- path = `${pagesDir ? `${pagesDir}/` : ""}page${++counter}.xml`;
743
- while (taken.has(path) || localZip?.file(path));
744
- paths.set(page.key, path);
745
- taken.add(path);
746
- }
747
- const others = parseRelationships(
748
- localZip ? await zipText(localZip, relsPathOf(drawing.pagesPath)) : void 0
749
- ).filter((r) => r.type !== PAGE_TYPE);
750
- const usedIds = new Set(others.map((r) => r.id));
751
- let nextRel = 0;
752
- const pageRels = [];
753
- const pageXml = [];
754
- for (const page of drawing.pages) {
755
- const path = paths.get(page.key);
756
- let id;
757
- do
758
- id = `rId${++nextRel}`;
759
- while (usedIds.has(id));
760
- usedIds.add(id);
761
- pageRels.push(
762
- `<Relationship Id="${id}" Type="${PAGE_TYPE}" Target="${escapeAttr(relTarget(pagesDir, path))}"/>`
763
- );
764
- const rel = `<Rel xmlns:r="${REL_NS}" r:id="${id}"/>`;
765
- pageXml.push(
766
- page.xml.includes(REL_MARK) ? page.xml.replace(REL_MARK, () => rel) : page.xml.slice(0, page.xml.lastIndexOf("</")) + rel + page.xml.slice(page.xml.lastIndexOf("</"))
767
- );
768
- files.set(path, encoder.encode(pagePartXml(page)));
769
- if (page.body.rels !== void 0) files.set(relsPathOf(path), encoder.encode(page.body.rels));
1208
+ const taken = new Set(files.keys());
1209
+ const pageCache = keys.cache.pageParts;
1210
+ const pageItems = drawing.pages.map((page) => {
1211
+ const hit = pageCache.get(page.key);
1212
+ const bytes2 = hit && hit.sig === page.signature ? hit.bytes : encoder.encode(pagePartXml(page));
1213
+ pageCache.set(page.key, { sig: page.signature, bytes: bytes2 });
1214
+ return {
1215
+ key: page.key,
1216
+ xml: page.xml,
1217
+ part: bytes2,
1218
+ ...page.body.rels !== void 0 ? { rels: page.body.rels } : {}
1219
+ };
1220
+ });
1221
+ for (const key of [...pageCache.keys()])
1222
+ if (!drawing.pages.some((p) => p.key === key)) pageCache.delete(key);
1223
+ const pagePaths = await placeList({
1224
+ listPath: drawing.pagesPath,
1225
+ open: drawing.pagesOpen,
1226
+ close: drawing.pagesClose,
1227
+ relType: PAGE_TYPE,
1228
+ stem: "page",
1229
+ items: pageItems,
1230
+ known: keys.pagePaths,
1231
+ files,
1232
+ taken,
1233
+ localZip
1234
+ });
1235
+ contentTypes = withPageOverrides(contentTypes, [...pagePaths.values()]);
1236
+ let masterPaths = /* @__PURE__ */ new Map();
1237
+ if (drawing.mastersPath !== void 0) {
1238
+ masterPaths = await placeList({
1239
+ listPath: drawing.mastersPath,
1240
+ open: drawing.mastersOpen ?? "<Masters>",
1241
+ close: drawing.mastersClose ?? "</Masters>",
1242
+ relType: MASTER_TYPE,
1243
+ stem: "master",
1244
+ items: drawing.masters.map((m) => ({
1245
+ key: m.key,
1246
+ xml: m.xml,
1247
+ part: encoder.encode(masterPartXml(m)),
1248
+ ...m.rels !== void 0 ? { rels: m.rels } : {}
1249
+ })),
1250
+ known: keys.masterPaths,
1251
+ files,
1252
+ taken,
1253
+ localZip
1254
+ });
1255
+ contentTypes = withOverrides(contentTypes, MASTER_CONTENT_TYPE, [...masterPaths.values()]);
770
1256
  }
771
- files.set(
772
- drawing.pagesPath,
773
- encoder.encode(drawing.pagesOpen + pageXml.join("") + drawing.pagesClose)
774
- );
775
- files.set(
776
- relsPathOf(drawing.pagesPath),
777
- encoder.encode(
778
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' + others.map((r) => r.text).join("") + pageRels.join("") + "</Relationships>"
779
- )
780
- );
781
- files.set(CONTENT_TYPES, encoder.encode(withPageOverrides(contentTypes, [...paths.values()])));
782
- const zip = new JSZip();
1257
+ files.set(CONTENT_TYPES, encoder.encode(contentTypes));
783
1258
  const names = [...files.keys()].sort(
784
1259
  (a, b) => a === CONTENT_TYPES ? -1 : b === CONTENT_TYPES ? 1 : 0
785
1260
  );
786
- for (const name of names) zip.file(name, files.get(name), { createFolders: false });
787
- const bytes = await zip.generateAsync({ type: "uint8array", compression: "STORE" });
1261
+ const bytes = writeStoredZip(
1262
+ names.map((name) => {
1263
+ const data = files.get(name);
1264
+ return [name, typeof data === "string" ? encoder.encode(data) : data];
1265
+ }),
1266
+ keys.cache.crcs
1267
+ );
788
1268
  const commit = () => {
789
1269
  const pageIds = /* @__PURE__ */ new Map();
790
- const shapeIds2 = /* @__PURE__ */ new Map();
1270
+ const shapeIds = /* @__PURE__ */ new Map();
1271
+ const masterIds = /* @__PURE__ */ new Map();
791
1272
  const known = /* @__PURE__ */ new Map();
1273
+ const recordShapes = (scope, shapes, connects) => {
1274
+ shapeIds.set(scope, new Map(shapes.map((s) => [s.id, s.key])));
1275
+ for (const shape of shapes) {
1276
+ known.set(shapeId(scope, shape.key), shellContent(shape));
1277
+ for (const [path, entry] of shape.entries)
1278
+ known.set(cellId(scope, shape.key, path), entryContent(entry));
1279
+ }
1280
+ for (const connect of connects) known.set(connectId(connect.key), "");
1281
+ };
792
1282
  keys.pagePaths.clear();
1283
+ keys.masterPaths.clear();
1284
+ keys.mediaKeys.clear();
793
1285
  for (const page of drawing.pages) {
794
1286
  pageIds.set(page.id, page.key);
795
- keys.pagePaths.set(page.key, paths.get(page.key));
1287
+ keys.pagePaths.set(page.key, pagePaths.get(page.key));
796
1288
  known.set(pageId(page.key), pageContent(page));
797
1289
  known.set(bodyId(page.key), bodyContent(page.body));
798
- shapeIds2.set(page.key, new Map(page.shapes.map((s) => [s.id, s.key])));
799
- for (const shape of page.shapes) known.set(shapeId(page.key, shape.key), canon(shape.xml));
800
- for (const connect of page.connects) known.set(connectId(connect.key), "");
1290
+ recordShapes(page.key, page.shapes, page.connects);
1291
+ }
1292
+ for (const master of drawing.masters) {
1293
+ masterIds.set(master.id, master.key);
1294
+ recordShapes(masterScope(master.key), master.shapes, master.connects);
1295
+ const path = masterPaths.get(master.key);
1296
+ if (path) keys.masterPaths.set(master.key, path);
1297
+ known.set(
1298
+ masterId(master.key),
1299
+ masterContent({ ...master, print: fingerprint(fromBase64(master.part)) })
1300
+ );
801
1301
  }
802
- keys.reset(pageIds, shapeIds2);
1302
+ for (const item of drawing.media) {
1303
+ known.set(mediaId(item.key), "");
1304
+ keys.mediaKeys.set(item.path, item.key);
1305
+ }
1306
+ keys.reset(pageIds, shapeIds, masterIds);
803
1307
  keys.known = known;
804
1308
  keys.partPrints.clear();
805
1309
  for (const [path, print] of prints) keys.partPrints.set(path, print);
806
1310
  };
807
- return { bytes, commit };
1311
+ return { bytes, signature: drawing.signature, commit };
1312
+ }
1313
+
1314
+ // visio/collab/fixups.ts
1315
+ function applyFixups(shared, fixups) {
1316
+ let changed = 0;
1317
+ for (const fixup of fixups) {
1318
+ if (setIfChanged(shared.renum, fixup.key, fixup.value)) changed++;
1319
+ }
1320
+ return changed;
808
1321
  }
809
1322
 
810
1323
  // visio/collab/keys.ts
@@ -818,15 +1331,36 @@ var DrawingKeys = class {
818
1331
  seeding = false;
819
1332
  #pages = /* @__PURE__ */ new Map();
820
1333
  #shapes = /* @__PURE__ */ new Map();
1334
+ #masters = /* @__PURE__ */ new Map();
821
1335
  /** Content of each entry this peer last wrote or applied, to tell a local edit or deletion from a change it has not applied yet. */
822
1336
  known = /* @__PURE__ */ new Map();
823
1337
  /** Fingerprints of the parts this peer last wrote or applied. */
824
1338
  partPrints = /* @__PURE__ */ new Map();
825
1339
  /** The part path of each page key in the local package. */
826
1340
  pagePaths = /* @__PURE__ */ new Map();
1341
+ /** The part path of each master key in the local package. */
1342
+ masterPaths = /* @__PURE__ */ new Map();
1343
+ /** The room key of each media path in the local package. */
1344
+ mediaKeys = /* @__PURE__ */ new Map();
1345
+ /** Serialised page parts and decoded shared parts of the last rebuild, to skip unchanged work. */
1346
+ cache = {
1347
+ pageParts: /* @__PURE__ */ new Map(),
1348
+ parts: /* @__PURE__ */ new Map(),
1349
+ /** CRC-32 of the bytes of each stored part, by identity (see zip-store.ts). */
1350
+ crcs: /* @__PURE__ */ new WeakMap()
1351
+ };
1352
+ /**
1353
+ * What was last applied to the editor: the drawing signature, the editor version it produced,
1354
+ * the signature of everything but page content and the content signature of each page.
1355
+ */
1356
+ applied;
827
1357
  mint() {
828
1358
  return `${this.tag}.${(++this.#counter).toString(36)}`;
829
1359
  }
1360
+ /** A fresh id for one write batch: entries written together share it. */
1361
+ batch() {
1362
+ return this.mint();
1363
+ }
830
1364
  pageKey(id) {
831
1365
  let key = this.#pages.get(id);
832
1366
  if (key === void 0) {
@@ -845,6 +1379,14 @@ var DrawingKeys = class {
845
1379
  }
846
1380
  return key;
847
1381
  }
1382
+ masterKey(id) {
1383
+ let key = this.#masters.get(id);
1384
+ if (key === void 0) {
1385
+ key = this.seeding ? `s${id}` : this.mint();
1386
+ this.#masters.set(id, key);
1387
+ }
1388
+ return key;
1389
+ }
848
1390
  findPage(id) {
849
1391
  return this.#pages.get(id);
850
1392
  }
@@ -858,38 +1400,352 @@ var DrawingKeys = class {
858
1400
  for (const [id, k] of map) if (k === key) map.delete(id);
859
1401
  }
860
1402
  }
1403
+ forgetMaster(key) {
1404
+ for (const [id, k] of this.#masters) if (k === key) this.#masters.delete(id);
1405
+ this.#shapes.delete(masterScope(key));
1406
+ this.masterPaths.delete(key);
1407
+ }
861
1408
  /** Replaces the id-to-key tables with those of a freshly composed package. */
862
- reset(pages, shapes) {
1409
+ reset(pages, shapes, masters = /* @__PURE__ */ new Map()) {
863
1410
  this.#pages.clear();
864
1411
  this.#shapes.clear();
1412
+ this.#masters.clear();
865
1413
  for (const [id, key] of pages) this.#pages.set(id, key);
866
1414
  for (const [page, map] of shapes) this.#shapes.set(page, new Map(map));
1415
+ for (const [id, key] of masters) this.#masters.set(id, key);
867
1416
  }
868
1417
  };
869
1418
 
870
- // visio/collab/read.ts
1419
+ // visio/collab/order.ts
1420
+ function assignPositions(order, current) {
1421
+ const result = /* @__PURE__ */ new Map();
1422
+ const kept = [];
1423
+ let last = -Infinity;
1424
+ for (const key of order) {
1425
+ const pos = current(key);
1426
+ if (pos !== void 0 && pos > last) {
1427
+ kept.push(pos);
1428
+ last = pos;
1429
+ } else kept.push(void 0);
1430
+ }
1431
+ let i = 0;
1432
+ while (i < order.length) {
1433
+ const known = kept[i];
1434
+ if (known !== void 0) {
1435
+ result.set(order[i], known);
1436
+ i++;
1437
+ continue;
1438
+ }
1439
+ let end = i;
1440
+ while (end < order.length && kept[end] === void 0) end++;
1441
+ const lo = i > 0 ? kept[i - 1] : void 0;
1442
+ const hi = end < order.length ? kept[end] : void 0;
1443
+ const count = end - i;
1444
+ for (let j = 0; j < count; j++) {
1445
+ const key = order[i + j];
1446
+ if (lo !== void 0 && hi !== void 0)
1447
+ result.set(key, lo + (hi - lo) * (j + 1) / (count + 1));
1448
+ else if (lo !== void 0) result.set(key, lo + j + 1);
1449
+ else if (hi !== void 0) result.set(key, hi - count + j);
1450
+ else result.set(key, j + 1);
1451
+ }
1452
+ i = end;
1453
+ }
1454
+ return result;
1455
+ }
1456
+ function byPosition(items) {
1457
+ return items.sort((a, b) => a.pos - b.pos || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
1458
+ }
1459
+
1460
+ // visio/collab/read-ids.ts
871
1461
  var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
872
1462
  var numeric = (id) => /^\d+$/.test(id) ? Number(id) : 0;
1463
+ function assignIds(items) {
1464
+ const effective = (item) => item.fixed ?? item.id;
1465
+ let max = Math.max(0, ...items.map((item) => numeric(effective(item))));
1466
+ const order = [...items].sort(
1467
+ (a, b) => Number(b.fixed !== void 0) - Number(a.fixed !== void 0) || compare(a.key, b.key)
1468
+ );
1469
+ const ids = /* @__PURE__ */ new Map();
1470
+ const fresh = /* @__PURE__ */ new Map();
1471
+ const holder = /* @__PURE__ */ new Map();
1472
+ const claims = /* @__PURE__ */ new Set();
1473
+ for (const item of order) {
1474
+ let id = effective(item);
1475
+ const taken = holder.get(id);
1476
+ if (taken) {
1477
+ if (taken.fixed === void 0) claims.add(taken.key);
1478
+ id = String(++max);
1479
+ fresh.set(item.key, { from: item.id, to: id });
1480
+ }
1481
+ holder.set(id, item);
1482
+ ids.set(item.key, id);
1483
+ }
1484
+ return { ids, fresh, claims };
1485
+ }
1486
+ var fixedId = (renum, key, id) => {
1487
+ const pair = renum.get(key)?.i;
1488
+ return pair && pair[0] === id ? pair[1] : void 0;
1489
+ };
1490
+ function readRenum(shared) {
1491
+ const out = /* @__PURE__ */ new Map();
1492
+ for (const [key, value] of shared.renum) {
1493
+ const entry = renumEntry(value);
1494
+ if (entry) out.set(key, entry);
1495
+ }
1496
+ return out;
1497
+ }
1498
+ function renumRecord(renum, recordKey, item, change, claimed) {
1499
+ if (change) return { ...renum.get(recordKey), i: [change.from, change.to] };
1500
+ if (claimed) return { ...renum.get(recordKey), i: [item.id, item.id] };
1501
+ return void 0;
1502
+ }
1503
+
1504
+ // visio/collab/read-util.ts
1505
+ function hashTexts(texts) {
1506
+ let a = 2166136261;
1507
+ let b = 16777619 ^ 2654435769;
1508
+ for (const text of texts) {
1509
+ for (let i = 0; i < text.length; i++) {
1510
+ const c = text.charCodeAt(i);
1511
+ a = Math.imul(a ^ c, 16777619);
1512
+ b = Math.imul(b + c, 2246822507) ^ b >>> 13;
1513
+ }
1514
+ a = Math.imul(a ^ 255, 16777619);
1515
+ b = Math.imul(b ^ 255, 3266489909);
1516
+ }
1517
+ return `${(a >>> 0).toString(16)}${(b >>> 0).toString(16)}`;
1518
+ }
1519
+ var split = (key, depth) => {
1520
+ const out = [];
1521
+ let from = 0;
1522
+ for (let i = 0; i < depth; i++) {
1523
+ const slash = key.indexOf("/", from);
1524
+ if (slash < 0) return void 0;
1525
+ out.push(key.slice(from, slash));
1526
+ from = slash + 1;
1527
+ }
1528
+ out.push(key.slice(from));
1529
+ return out;
1530
+ };
1531
+ var connectSignature = (connects) => connects.flatMap((c) => [c.from, c.to, c.entry.fc, c.entry.tc, c.entry.fp, c.entry.tp]);
1532
+ function resolveConnects(list, shapes) {
1533
+ const byKey = new Map(shapes.map((s) => [s.key, s]));
1534
+ const out = [];
1535
+ for (const item of [...list ?? []].sort((a, b) => compare(a.key, b.key))) {
1536
+ const { entry } = item;
1537
+ const from = entry.fk ? byKey.get(entry.fk) : void 0;
1538
+ const to = entry.tk ? byKey.get(entry.tk) : void 0;
1539
+ if (entry.fk && !from || entry.tk && !to) continue;
1540
+ out.push({ key: item.key, entry, from: from?.id ?? entry.fi, to: to?.id ?? entry.ti });
1541
+ }
1542
+ return out;
1543
+ }
1544
+
1545
+ // visio/collab/read-shapes.ts
873
1546
  function setAttr(open, name, value) {
874
1547
  return open.replace(
875
1548
  new RegExp(String.raw`(\s${name}\s*=\s*)(?:"[^"]*"|'[^']*')`),
876
1549
  (_m, head) => `${head}"${value}"`
877
1550
  );
878
1551
  }
879
- function renumberShape(xml, remap) {
880
- if (!remap.size) return xml;
881
- return xml.replace(
882
- /<(?:\w+:)?Shape\s[^>]*>/g,
883
- (tag) => tag.replace(/(\bID\s*=\s*)(["'])(\d+)\2/, (m, head, q, id) => {
1552
+ function renumberFormulas(xml, remap) {
1553
+ if (!remap.size || !xml.includes("Sheet.")) return xml;
1554
+ return xml.replace(/(\sF\s*=\s*)("[^"]*"|'[^']*')/g, (_m, head, value) => {
1555
+ const body = value.slice(1, -1).replace(/Sheet\.(\d+)(?!\d)/g, (m, id) => {
884
1556
  const next = remap.get(id);
885
- return next === void 0 ? m : `${head}${q}${next}${q}`;
1557
+ return next === void 0 ? m : `Sheet.${next}`;
1558
+ });
1559
+ return `${head}${value[0]}${body}${value[0]}`;
1560
+ });
1561
+ }
1562
+ function referenced(r, finalId) {
1563
+ const out = /* @__PURE__ */ new Map();
1564
+ for (let i = 0; i + 1 < r.length; i += 2) {
1565
+ const next = finalId.get(r[i + 1]);
1566
+ if (next !== void 0) out.set(r[i], next);
1567
+ }
1568
+ return out;
1569
+ }
1570
+ function resolveShapes(input) {
1571
+ const raw = /* @__PURE__ */ new Map();
1572
+ for (const [key, entry] of input.shapes) {
1573
+ const id = attrOf(entry.open, "ID");
1574
+ if (id !== void 0) raw.set(key, { entry, id });
1575
+ }
1576
+ const state = /* @__PURE__ */ new Map();
1577
+ const valid = (key, seen = /* @__PURE__ */ new Set()) => {
1578
+ const known = state.get(key);
1579
+ if (known !== void 0) return known;
1580
+ const item = raw.get(key);
1581
+ const parent = item?.entry.parent;
1582
+ let ok = !!item;
1583
+ if (ok && parent !== void 0) ok = !seen.has(key) && valid(parent, seen.add(key));
1584
+ state.set(key, ok);
1585
+ return ok;
1586
+ };
1587
+ const live = [...raw.keys()].filter((k) => valid(k)).sort(compare);
1588
+ const {
1589
+ ids: finalId,
1590
+ fresh: renumbered,
1591
+ claims
1592
+ } = assignIds(
1593
+ live.map((key) => {
1594
+ const { id } = raw.get(key);
1595
+ return { key, id, fixed: fixedId(input.renum, key, id) };
886
1596
  })
887
- ).replace(/Sheet\.(\d+)(?!\d)/g, (m, id) => {
888
- const next = remap.get(id);
889
- return next === void 0 ? m : `Sheet.${next}`;
1597
+ );
1598
+ const children = /* @__PURE__ */ new Map();
1599
+ for (const key of live) {
1600
+ const parent = raw.get(key).entry.parent ?? "";
1601
+ children.set(parent, [...children.get(parent) ?? [], key]);
1602
+ }
1603
+ const sortSiblings = (keys) => keys.sort((a, b) => raw.get(a).entry.pos - raw.get(b).entry.pos || compare(a, b));
1604
+ const shapes = [];
1605
+ const fixups = [];
1606
+ const render = (key) => {
1607
+ const { entry } = raw.get(key);
1608
+ const id = finalId.get(key);
1609
+ let open = setAttr(entry.open, "ID", id);
1610
+ const idRecord = renumRecord(
1611
+ input.renum,
1612
+ `${input.scope}/${key}`,
1613
+ { id: raw.get(key).id },
1614
+ renumbered.get(key),
1615
+ claims.has(key)
1616
+ );
1617
+ const record = { ...idRecord ?? input.renum.get(key) };
1618
+ let dirty = idRecord !== void 0;
1619
+ const master = attrOf(open, "Master");
1620
+ const persisted = record.m && record.m[0] === master ? record.m[1] : void 0;
1621
+ const batched = master !== void 0 ? input.masterRemap.get(entry.b ?? "")?.get(master) : void 0;
1622
+ const next = persisted ?? batched;
1623
+ if (next !== void 0) open = setAttr(open, "Master", next);
1624
+ if (persisted === void 0 && batched !== void 0) {
1625
+ record.m = [master, batched];
1626
+ dirty = true;
1627
+ }
1628
+ if (dirty) fixups.push({ map: "renum", key: `${input.scope}/${key}`, value: record });
1629
+ const entries = /* @__PURE__ */ new Map();
1630
+ for (const [path, piece] of input.cells.get(key) ?? []) {
1631
+ const leaf = piece.xml !== void 0 && piece.r !== void 0;
1632
+ const xml2 = leaf ? renumberFormulas(piece.xml, referenced(piece.r, finalId)) : void 0;
1633
+ entries.set(path, xml2 !== void 0 && xml2 !== piece.xml ? { ...piece, xml: xml2 } : piece);
1634
+ }
1635
+ shapes.push({
1636
+ key,
1637
+ id,
1638
+ ...entry.parent !== void 0 ? { parent: entry.parent } : {},
1639
+ open,
1640
+ entries
1641
+ });
1642
+ const inner = sortSiblings(children.get(key) ?? []).map(render).join("");
1643
+ return buildShape(open, entries, inner ? `<Shapes>${inner}</Shapes>` : "");
1644
+ };
1645
+ const xml = sortSiblings(children.get("") ?? []).map(render).join("");
1646
+ return { shapes, xml, fixups };
1647
+ }
1648
+
1649
+ // visio/collab/read-parts.ts
1650
+ var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
1651
+ function resolveMasters(shared, renum) {
1652
+ const alive = [];
1653
+ for (const [key, value] of shared.masters) {
1654
+ const entry = masterEntry(value);
1655
+ if (entry) alive.push({ key, entry });
1656
+ }
1657
+ const { ids, fresh, claims } = assignIds(
1658
+ alive.map(({ key, entry }) => ({
1659
+ key,
1660
+ id: entry.id,
1661
+ fixed: fixedId(renum, masterScope(key), entry.id)
1662
+ }))
1663
+ );
1664
+ const remap = /* @__PURE__ */ new Map();
1665
+ const fixups = [];
1666
+ for (const [key, change] of [...fresh].sort((a, b) => compare2(a[0], b[0]))) {
1667
+ const b = alive.find((m) => m.key === key).entry.b;
1668
+ if (b !== void 0) {
1669
+ const map = remap.get(b) ?? /* @__PURE__ */ new Map();
1670
+ map.set(change.from, change.to);
1671
+ remap.set(b, map);
1672
+ }
1673
+ }
1674
+ for (const { key, entry } of [...alive].sort((a, b) => compare2(a.key, b.key))) {
1675
+ const value = renumRecord(renum, masterScope(key), entry, fresh.get(key), claims.has(key));
1676
+ if (value) fixups.push({ map: "renum", key: masterScope(key), value });
1677
+ }
1678
+ alive.sort((a, b) => a.entry.pos - b.entry.pos || compare2(a.key, b.key));
1679
+ return {
1680
+ masters: alive.map(({ key, entry }) => {
1681
+ const id = ids.get(key);
1682
+ const open = /^<[^>]*>/.exec(entry.xml)?.[0];
1683
+ return {
1684
+ key,
1685
+ id,
1686
+ xml: open ? setAttr(open, "ID", id) + entry.xml.slice(open.length) : entry.xml,
1687
+ part: entry.part,
1688
+ ...entry.rels !== void 0 ? { rels: entry.rels } : {}
1689
+ };
1690
+ }),
1691
+ remap,
1692
+ fixups
1693
+ };
1694
+ }
1695
+ function resolveMedia(shared) {
1696
+ const items = [...shared.media].flatMap(([key, value]) => {
1697
+ const entry = mediaEntry(value);
1698
+ const data = entry ? str(shared.blobs.get(entry.hash)) : void 0;
1699
+ return entry && data !== void 0 ? [{ key, entry, data }] : [];
1700
+ }).sort((a, b) => compare2(a.key, b.key));
1701
+ const taken = /* @__PURE__ */ new Set();
1702
+ const first = /* @__PURE__ */ new Map();
1703
+ for (const item of items) if (!first.has(item.entry.path)) first.set(item.entry.path, item.key);
1704
+ for (const path of first.keys()) taken.add(path);
1705
+ const renames = /* @__PURE__ */ new Map();
1706
+ const media = [];
1707
+ for (const { key, entry, data } of items) {
1708
+ let path = entry.path;
1709
+ if (first.get(entry.path) !== key) {
1710
+ const dot = path.lastIndexOf(".");
1711
+ const stem = dot > path.lastIndexOf("/") ? path.slice(0, dot) : path;
1712
+ const ext = dot > path.lastIndexOf("/") ? path.slice(dot) : "";
1713
+ path = `${stem}-${entry.hash.slice(0, 8)}${ext}`;
1714
+ for (let n = 2; taken.has(path); n++) path = `${stem}-${entry.hash.slice(0, 8)}-${n}${ext}`;
1715
+ taken.add(path);
1716
+ if (entry.b !== void 0) {
1717
+ const map = renames.get(entry.b) ?? /* @__PURE__ */ new Map();
1718
+ map.set(entry.path, path);
1719
+ renames.set(entry.b, map);
1720
+ }
1721
+ }
1722
+ media.push({
1723
+ key,
1724
+ path,
1725
+ hash: entry.hash,
1726
+ declared: { type: entry.ct, byDefault: entry.d },
1727
+ data
1728
+ });
1729
+ }
1730
+ return { media, renames };
1731
+ }
1732
+ function renameTargets(rels, baseDir, renames) {
1733
+ return rels.replace(/<(?:\w+:)?Relationship\b[^>]*>/g, (tag) => {
1734
+ const target = attrOf(tag, "Target");
1735
+ if (target === void 0 || attrOf(tag, "TargetMode") === "External") return tag;
1736
+ const next = renames.get(resolveTarget(baseDir, target));
1737
+ if (next === void 0) return tag;
1738
+ const value = target.startsWith("/") ? `/${next}` : relativeTarget(baseDir, next);
1739
+ return tag.replace(
1740
+ /(\sTarget\s*=\s*)(?:"[^"]*"|'[^']*')/,
1741
+ (_m, head) => `${head}"${value}"`
1742
+ );
890
1743
  });
891
1744
  }
1745
+
1746
+ // visio/collab/read.ts
892
1747
  function readSharedDrawing(shared) {
1748
+ assertSchemaVersion(shared);
893
1749
  if (isSharedDrawingEmpty(shared)) return void 0;
894
1750
  const pagesPath = str(shared.meta.get("pagesPath"));
895
1751
  const pagesOpen = str(shared.meta.get("pagesOpen"));
@@ -901,25 +1757,52 @@ function readSharedDrawing(shared) {
901
1757
  const body = bodyEntry(shared.bodies.get(key));
902
1758
  return entry && body ? [{ key, pos: entry.pos, entry, body }] : [];
903
1759
  });
904
- const pageIds = /* @__PURE__ */ new Map();
905
- const taken = /* @__PURE__ */ new Set();
906
- let maxPage = Math.max(0, ...alive.map((p) => numeric(p.entry.id)));
907
- for (const page of [...alive].sort((a, b) => compare(a.key, b.key))) {
908
- let id = page.entry.id;
909
- if (taken.has(id)) id = String(++maxPage);
910
- taken.add(id);
911
- pageIds.set(page.key, id);
1760
+ const renum = readRenum(shared);
1761
+ const fixups = [];
1762
+ const {
1763
+ ids: pageIds,
1764
+ fresh: freshPages,
1765
+ claims: pageClaims
1766
+ } = assignIds(
1767
+ alive.map((p) => ({
1768
+ key: p.key,
1769
+ id: p.entry.id,
1770
+ fixed: fixedId(renum, p.key, p.entry.id)
1771
+ }))
1772
+ );
1773
+ for (const p of [...alive].sort((a, b) => compare(a.key, b.key))) {
1774
+ const value = renumRecord(renum, p.key, p.entry, freshPages.get(p.key), pageClaims.has(p.key));
1775
+ if (value) fixups.push({ map: "renum", key: p.key, value });
912
1776
  }
913
1777
  const shapesByPage = /* @__PURE__ */ new Map();
914
1778
  for (const [full, value] of shared.shapes) {
915
- const slash = full.indexOf("/");
916
- if (slash < 0 || shared.dead.has(full)) continue;
1779
+ const parts2 = split(full, 1);
1780
+ if (!parts2 || shared.dead.has(full)) continue;
917
1781
  const entry = shapeEntry(value);
918
1782
  if (!entry) continue;
919
- const page = full.slice(0, slash);
920
- const list = shapesByPage.get(page) ?? [];
921
- list.push({ key: full.slice(slash + 1), pos: entry.pos, xml: entry.xml });
922
- shapesByPage.set(page, list);
1783
+ const map = shapesByPage.get(parts2[0]) ?? /* @__PURE__ */ new Map();
1784
+ map.set(parts2[1], entry);
1785
+ shapesByPage.set(parts2[0], map);
1786
+ }
1787
+ const cellsByPage = /* @__PURE__ */ new Map();
1788
+ for (const [full, value] of shared.cells) {
1789
+ const parts2 = split(full, 2);
1790
+ const entry = parts2 && cellEntry(value);
1791
+ if (!parts2 || !entry) continue;
1792
+ const [page, shape, path] = parts2;
1793
+ const shapes = cellsByPage.get(page) ?? /* @__PURE__ */ new Map();
1794
+ const cells = shapes.get(shape) ?? /* @__PURE__ */ new Map();
1795
+ cells.set(path, entry);
1796
+ shapes.set(shape, cells);
1797
+ cellsByPage.set(page, shapes);
1798
+ }
1799
+ const renumByScope = /* @__PURE__ */ new Map();
1800
+ for (const [full, entry] of renum) {
1801
+ const parts2 = split(full, 1);
1802
+ if (!parts2) continue;
1803
+ const map = renumByScope.get(parts2[0]) ?? /* @__PURE__ */ new Map();
1804
+ map.set(parts2[1], entry);
1805
+ renumByScope.set(parts2[0], map);
923
1806
  }
924
1807
  const connectsByPage = /* @__PURE__ */ new Map();
925
1808
  for (const [full, value] of shared.connects) {
@@ -930,24 +1813,25 @@ function readSharedDrawing(shared) {
930
1813
  list.push({ key: full, entry });
931
1814
  connectsByPage.set(full.slice(0, slash), list);
932
1815
  }
1816
+ const { masters, remap: masterRemap, fixups: masterFixups } = resolveMasters(shared, renum);
1817
+ fixups.push(...masterFixups);
1818
+ const { media, renames } = resolveMedia(shared);
1819
+ const mastersPath = str(shared.meta.get("mastersPath"));
1820
+ const mastersOpen = str(shared.meta.get("mastersOpen"));
1821
+ const mastersClose = str(shared.meta.get("mastersClose"));
1822
+ const pagesDir = dirName(pagesPath);
933
1823
  const pages = [];
934
1824
  for (const page of byPosition(alive)) {
935
1825
  const finalId = pageIds.get(page.key);
936
- const shapes = resolveShapes(shapesByPage.get(page.key) ?? []);
937
- const byKey = new Map(shapes.map((s) => [s.key, s]));
938
- const connects = [];
939
- for (const item of (connectsByPage.get(page.key) ?? []).sort((a, b) => compare(a.key, b.key))) {
940
- const { entry } = item;
941
- const from = entry.fk ? byKey.get(entry.fk) : void 0;
942
- const to = entry.tk ? byKey.get(entry.tk) : void 0;
943
- if (entry.fk && !from || entry.tk && !to) continue;
944
- connects.push({
945
- key: item.key,
946
- entry,
947
- from: from?.remap.get(entry.fi) ?? entry.fi,
948
- to: to?.remap.get(entry.ti) ?? entry.ti
949
- });
950
- }
1826
+ const resolved = resolveShapes({
1827
+ scope: page.key,
1828
+ shapes: shapesByPage.get(page.key) ?? /* @__PURE__ */ new Map(),
1829
+ cells: cellsByPage.get(page.key) ?? /* @__PURE__ */ new Map(),
1830
+ renum: renumByScope.get(page.key) ?? /* @__PURE__ */ new Map(),
1831
+ masterRemap
1832
+ });
1833
+ fixups.push(...resolved.fixups);
1834
+ const connects = resolveConnects(connectsByPage.get(page.key), resolved.shapes);
951
1835
  let xml = page.entry.xml;
952
1836
  const open = /^<[^>]*>/.exec(xml)?.[0];
953
1837
  if (open) {
@@ -956,46 +1840,361 @@ function readSharedDrawing(shared) {
956
1840
  if (back !== void 0) next = setAttr(next, "BackPage", back);
957
1841
  xml = next + xml.slice(open.length);
958
1842
  }
1843
+ const rename = page.body.b !== void 0 ? renames.get(page.body.b) : void 0;
1844
+ const body = rename && page.body.rels !== void 0 ? { ...page.body, rels: renameTargets(page.body.rels, pagesDir, rename) } : page.body;
1845
+ const contentSignature = hashTexts([
1846
+ body.shell,
1847
+ body.rels ?? "\0",
1848
+ resolved.xml,
1849
+ ...connectSignature(connects)
1850
+ ]);
959
1851
  pages.push({
960
1852
  key: page.key,
961
1853
  id: finalId,
962
1854
  ...page.entry.back !== void 0 ? { back: page.entry.back } : {},
963
1855
  xml,
964
- body: page.body,
965
- shapes,
966
- connects
1856
+ body,
1857
+ shapes: resolved.shapes,
1858
+ shapesXml: resolved.xml,
1859
+ connects,
1860
+ signature: hashTexts([xml, contentSignature]),
1861
+ contentSignature
967
1862
  });
968
1863
  }
1864
+ const mastersDir = mastersPath !== void 0 ? dirName(mastersPath) : pagesDir;
1865
+ const finalMasters = masters.map((m) => {
1866
+ const batch = shared.masters.get(m.key)?.b;
1867
+ const rename = typeof batch === "string" ? renames.get(batch) : void 0;
1868
+ const scope = masterScope(m.key);
1869
+ const resolved = resolveShapes({
1870
+ scope,
1871
+ shapes: shapesByPage.get(scope) ?? /* @__PURE__ */ new Map(),
1872
+ cells: cellsByPage.get(scope) ?? /* @__PURE__ */ new Map(),
1873
+ renum: renumByScope.get(scope) ?? /* @__PURE__ */ new Map(),
1874
+ masterRemap: /* @__PURE__ */ new Map()
1875
+ });
1876
+ fixups.push(...resolved.fixups);
1877
+ return {
1878
+ ...m,
1879
+ ...rename && m.rels !== void 0 ? { rels: renameTargets(m.rels, mastersDir, rename) } : {},
1880
+ shapes: resolved.shapes,
1881
+ shapesXml: resolved.xml,
1882
+ connects: resolveConnects(connectsByPage.get(scope), resolved.shapes)
1883
+ };
1884
+ });
969
1885
  const parts = /* @__PURE__ */ new Map();
970
1886
  for (const [path, value] of shared.parts) if (typeof value === "string") parts.set(path, value);
971
- return { pagesPath, pagesOpen, pagesClose, pages, parts };
1887
+ const baseSignature = hashTexts([
1888
+ pagesPath,
1889
+ pagesOpen,
1890
+ pagesClose,
1891
+ mastersPath ?? "",
1892
+ mastersOpen ?? "",
1893
+ mastersClose ?? "",
1894
+ ...pages.map((p) => `${p.key}:${p.id}:${p.xml}`),
1895
+ ...finalMasters.flatMap((m) => [
1896
+ m.key,
1897
+ m.id,
1898
+ m.xml,
1899
+ m.part,
1900
+ m.rels ?? "\0",
1901
+ m.shapesXml,
1902
+ ...connectSignature(m.connects)
1903
+ ]),
1904
+ ...media.flatMap((m) => [m.path, m.hash, m.declared.type, String(m.declared.byDefault)]),
1905
+ ...[...parts].flatMap(([path, data]) => [path, data])
1906
+ ]);
1907
+ const signature = hashTexts([
1908
+ baseSignature,
1909
+ ...pages.map((p) => `${p.key}:${p.contentSignature}`)
1910
+ ]);
1911
+ return {
1912
+ pagesPath,
1913
+ pagesOpen,
1914
+ pagesClose,
1915
+ pages,
1916
+ ...mastersPath !== void 0 && mastersOpen !== void 0 && mastersClose !== void 0 ? { mastersPath, mastersOpen, mastersClose } : {},
1917
+ masters: finalMasters,
1918
+ media,
1919
+ parts,
1920
+ signature,
1921
+ baseSignature,
1922
+ fixups
1923
+ };
972
1924
  }
973
- function resolveShapes(entries) {
974
- const parsed = entries.map((entry) => ({ ...entry, ids: shapeIds(entry.xml) }));
975
- let max = Math.max(0, ...parsed.flatMap((p) => p.ids.map(numeric)));
976
- const used = /* @__PURE__ */ new Set();
977
- const done = /* @__PURE__ */ new Map();
978
- for (const item of [...parsed].sort((a, b) => compare(a.key, b.key))) {
979
- const remap = /* @__PURE__ */ new Map();
980
- for (const id of item.ids) {
981
- if (used.has(id) || remap.has(id)) remap.set(id, String(++max));
1925
+
1926
+ // visio/collab/write-shapes.ts
1927
+ function syncEntry(ctx, map, mapKey, id, content, room, roomContent, local, stamp = false) {
1928
+ ctx.now.set(id, content);
1929
+ const base = ctx.keys.known.get(id);
1930
+ const own = base === void 0 ? !room || roomContent() !== content : base !== content;
1931
+ if (own || !room) {
1932
+ const value = local();
1933
+ ctx.write(map, mapKey, stamp ? { ...value, b: ctx.batch } : value);
1934
+ } else {
1935
+ const pos = local().pos;
1936
+ ctx.write(map, mapKey, pos === void 0 ? room : { ...room, pos });
1937
+ }
1938
+ }
1939
+ function groupPositions(items, current) {
1940
+ const groups = /* @__PURE__ */ new Map();
1941
+ for (const item of items) groups.set(item.group, [...groups.get(item.group) ?? [], item.key]);
1942
+ const out = /* @__PURE__ */ new Map();
1943
+ for (const list of groups.values())
1944
+ for (const [key, pos] of assignPositions(list, current)) out.set(key, pos);
1945
+ return out;
1946
+ }
1947
+ function syncConnects(ctx, connects) {
1948
+ for (const connect of connects) {
1949
+ ctx.now.set(connectId(connect.key), "");
1950
+ if (!ctx.keys.known.has(connectId(connect.key)) || !ctx.shared.connects.has(connect.key))
1951
+ ctx.write(ctx.shared.connects, connect.key, connect.entry);
1952
+ }
1953
+ }
1954
+ function syncShapes(ctx, page) {
1955
+ const { shared } = ctx;
1956
+ const shapePos = groupPositions(
1957
+ page.shapes.map((s) => ({ key: s.key, group: s.parent ?? "" })),
1958
+ (key) => shapeEntry(shared.shapes.get(`${page.key}/${key}`))?.pos
1959
+ );
1960
+ for (const shape of page.shapes) {
1961
+ const full = `${page.key}/${shape.key}`;
1962
+ const room = shapeEntry(shared.shapes.get(full));
1963
+ const parent = shape.parent;
1964
+ syncEntry(
1965
+ ctx,
1966
+ shared.shapes,
1967
+ full,
1968
+ shapeId(page.key, shape.key),
1969
+ shellContent({ parent, open: shape.open }),
1970
+ room,
1971
+ () => shellContent(room),
1972
+ () => ({
1973
+ pos: shapePos.get(shape.key),
1974
+ ...parent !== void 0 ? { parent } : {},
1975
+ open: shape.open,
1976
+ b: room?.b ?? ctx.batch
1977
+ })
1978
+ );
1979
+ const piecePos = groupPositions(
1980
+ shape.pieces.map((p) => ({ key: p.path, group: p.parent })),
1981
+ (path) => cellEntry(shared.cells.get(`${full}/${path}`))?.pos
1982
+ );
1983
+ for (const piece of shape.pieces) {
1984
+ const cellKey = `${full}/${piece.path}`;
1985
+ const roomCell = cellEntry(shared.cells.get(cellKey));
1986
+ const local = () => ({
1987
+ pos: piecePos.get(piece.path),
1988
+ ...piece.kind === "leaf" ? { xml: piece.text } : { open: piece.text },
1989
+ ...piece.refs ? { r: piece.refs } : {}
1990
+ });
1991
+ syncEntry(
1992
+ ctx,
1993
+ shared.cells,
1994
+ cellKey,
1995
+ cellId(page.key, shape.key, piece.path),
1996
+ entryContent(piece.kind === "leaf" ? { xml: piece.text } : { open: piece.text }),
1997
+ roomCell,
1998
+ () => entryContent(roomCell),
1999
+ local
2000
+ );
982
2001
  }
983
- const ids = item.ids.map((id) => remap.get(id) ?? id);
984
- for (const id of ids) used.add(id);
985
- done.set(item.key, {
986
- key: item.key,
987
- id: ids[0] ?? "",
988
- xml: renumberShape(item.xml, remap),
989
- remap,
990
- ids
991
- });
992
2002
  }
993
- return byPosition(parsed).map((item) => done.get(item.key));
2003
+ }
2004
+
2005
+ // visio/collab/write-parts.ts
2006
+ var mediaKey = (hash, path) => `${hash}|${path}`;
2007
+ function syncMasters(ctx, snapshot) {
2008
+ const { shared } = ctx;
2009
+ const pos = assignPositions(
2010
+ snapshot.masters.map((m) => m.key),
2011
+ (key) => masterEntry(shared.masters.get(key))?.pos
2012
+ );
2013
+ for (const master of snapshot.masters) {
2014
+ const room = masterEntry(shared.masters.get(master.key));
2015
+ syncEntry(
2016
+ ctx,
2017
+ shared.masters,
2018
+ master.key,
2019
+ masterId(master.key),
2020
+ masterContent({ ...master, print: fingerprint(master.part) }),
2021
+ room,
2022
+ () => masterContent({
2023
+ ...room,
2024
+ print: fingerprint(fromBase64(room.part))
2025
+ }),
2026
+ () => ({
2027
+ pos: pos.get(master.key),
2028
+ id: master.id,
2029
+ xml: master.xml,
2030
+ part: toBase64(master.part),
2031
+ ...master.rels !== void 0 ? { rels: master.rels } : {}
2032
+ }),
2033
+ true
2034
+ );
2035
+ syncShapes(ctx, { key: masterScope(master.key), shapes: master.shapes });
2036
+ syncConnects(ctx, master.connects);
2037
+ }
2038
+ }
2039
+ function syncMedia(ctx, snapshot) {
2040
+ const { shared } = ctx;
2041
+ for (const item of snapshot.media) {
2042
+ const known = ctx.keys.mediaKeys.get(item.path);
2043
+ const key = known?.startsWith(`${item.hash}|`) ? known : mediaKey(item.hash, item.path);
2044
+ ctx.now.set(mediaId(key), "");
2045
+ if (!mediaEntry(shared.media.get(key))) {
2046
+ ctx.write(shared.media, key, {
2047
+ path: item.path,
2048
+ hash: item.hash,
2049
+ ct: item.ct,
2050
+ d: item.d,
2051
+ b: ctx.batch
2052
+ });
2053
+ shared.blobs.set(item.hash, toBase64(item.data));
2054
+ } else if (!shared.blobs.has(item.hash)) shared.blobs.set(item.hash, toBase64(item.data));
2055
+ }
2056
+ }
2057
+ function syncParts(ctx, snapshot) {
2058
+ const { shared, keys } = ctx;
2059
+ for (const [path, data] of snapshot.parts) {
2060
+ const print = fingerprint(data);
2061
+ if (keys.partPrints.get(path) === print) continue;
2062
+ ctx.write(shared.parts, path, toBase64(data));
2063
+ keys.partPrints.set(path, print);
2064
+ }
2065
+ let removed = 0;
2066
+ for (const path of [...keys.partPrints.keys()])
2067
+ if (!snapshot.parts.has(path)) {
2068
+ shared.parts.delete(path);
2069
+ keys.partPrints.delete(path);
2070
+ removed++;
2071
+ }
2072
+ return removed;
2073
+ }
2074
+
2075
+ // visio/collab/write.ts
2076
+ function removeScope(shared, name) {
2077
+ shared.renum.delete(name);
2078
+ for (const map of [shared.shapes, shared.cells, shared.connects, shared.renum])
2079
+ for (const key of [...map.keys()]) if (key.startsWith(`${name}/`)) map.delete(key);
2080
+ }
2081
+ function removePage(shared, name) {
2082
+ shared.dead.set(name, true);
2083
+ for (const map of [shared.pages, shared.bodies]) map.delete(name);
2084
+ removeScope(shared, name);
2085
+ }
2086
+ function writeSharedDrawing(shared, snapshot, keys) {
2087
+ assertSchemaVersion(shared);
2088
+ let writes = 0;
2089
+ const write = (map, key, value) => {
2090
+ if (setIfChanged(map, key, value)) writes++;
2091
+ };
2092
+ const now = /* @__PURE__ */ new Map();
2093
+ const ctx = { shared, keys, now, batch: keys.batch(), write };
2094
+ write(shared.meta, "v", SCHEMA_VERSION);
2095
+ write(shared.meta, "pagesPath", snapshot.pagesPath);
2096
+ write(shared.meta, "pagesOpen", snapshot.pagesOpen);
2097
+ write(shared.meta, "pagesClose", snapshot.pagesClose);
2098
+ if (snapshot.mastersPath !== void 0) {
2099
+ write(shared.meta, "mastersPath", snapshot.mastersPath);
2100
+ write(shared.meta, "mastersOpen", snapshot.mastersOpen);
2101
+ write(shared.meta, "mastersClose", snapshot.mastersClose);
2102
+ }
2103
+ const pagePos = assignPositions(
2104
+ snapshot.pages.map((p) => p.key),
2105
+ (key) => pageEntry(shared.pages.get(key))?.pos
2106
+ );
2107
+ for (const page of snapshot.pages) {
2108
+ const room = pageEntry(shared.pages.get(page.key));
2109
+ syncEntry(
2110
+ ctx,
2111
+ shared.pages,
2112
+ page.key,
2113
+ pageId(page.key),
2114
+ pageContent(page),
2115
+ room,
2116
+ () => pageContent(room),
2117
+ () => ({
2118
+ pos: pagePos.get(page.key),
2119
+ id: page.id,
2120
+ ...page.back !== void 0 ? { back: page.back } : {},
2121
+ xml: page.xml
2122
+ })
2123
+ );
2124
+ const roomBody = bodyEntry(shared.bodies.get(page.key));
2125
+ syncEntry(
2126
+ ctx,
2127
+ shared.bodies,
2128
+ page.key,
2129
+ bodyId(page.key),
2130
+ bodyContent(page),
2131
+ roomBody,
2132
+ () => bodyContent(roomBody),
2133
+ () => ({ shell: page.shell, ...page.rels !== void 0 ? { rels: page.rels } : {} }),
2134
+ true
2135
+ );
2136
+ syncShapes(ctx, page);
2137
+ syncConnects(ctx, page.connects);
2138
+ }
2139
+ syncMasters(ctx, snapshot);
2140
+ syncMedia(ctx, snapshot);
2141
+ for (const id of keys.known.keys()) {
2142
+ if (now.has(id)) continue;
2143
+ const name = id.slice(2);
2144
+ switch (id.slice(0, 2)) {
2145
+ case "p:":
2146
+ removePage(shared, name);
2147
+ keys.forgetPage(name);
2148
+ writes++;
2149
+ break;
2150
+ case "s:": {
2151
+ const slash = name.indexOf("/");
2152
+ const page = name.slice(0, slash);
2153
+ if (!now.has(page.startsWith(masterScope("")) ? masterId(page.slice(2)) : pageId(page)))
2154
+ break;
2155
+ shared.dead.set(name, true);
2156
+ shared.shapes.delete(name);
2157
+ keys.forgetShape(page, name.slice(slash + 1));
2158
+ writes++;
2159
+ break;
2160
+ }
2161
+ case "f:":
2162
+ if (shared.cells.has(name)) {
2163
+ shared.cells.delete(name);
2164
+ writes++;
2165
+ }
2166
+ break;
2167
+ case "c:":
2168
+ if (shared.connects.has(name)) {
2169
+ shared.connects.delete(name);
2170
+ writes++;
2171
+ }
2172
+ break;
2173
+ case "m:":
2174
+ if (shared.masters.has(name)) {
2175
+ shared.masters.delete(name);
2176
+ removeScope(shared, masterScope(name));
2177
+ writes++;
2178
+ }
2179
+ keys.forgetMaster(name);
2180
+ break;
2181
+ case "i:":
2182
+ if (shared.media.has(name)) {
2183
+ shared.media.delete(name);
2184
+ writes++;
2185
+ }
2186
+ break;
2187
+ }
2188
+ }
2189
+ keys.known = now;
2190
+ writes += syncParts(ctx, snapshot);
2191
+ return writes;
994
2192
  }
995
2193
 
996
2194
  // visio/collab/bind.ts
997
2195
  var VISIO_EDIT_ORIGIN = /* @__PURE__ */ Symbol("ooxml-core:visio:edit");
998
2196
  var VISIO_SYNC_ORIGIN = /* @__PURE__ */ Symbol("ooxml-core:visio:sync");
2197
+ var VISIO_FIXUP_ORIGIN = /* @__PURE__ */ Symbol("ooxml-core:visio:fixup");
999
2198
  function bindDrawingSession(host, editor, options = {}) {
1000
2199
  const { doc } = host;
1001
2200
  const shared = sharedDrawing(doc);
@@ -1015,6 +2214,12 @@ function bindDrawingSession(host, editor, options = {}) {
1015
2214
  let settled = false;
1016
2215
  let localDirty = false;
1017
2216
  let remoteDirty = false;
2217
+ let incompatible = false;
2218
+ let queued = false;
2219
+ let queuedDone;
2220
+ const schedule = options.schedule ?? ((run) => {
2221
+ setTimeout(run, 0);
2222
+ });
1018
2223
  let running;
1019
2224
  const report = (error) => options.onError?.(error);
1020
2225
  const publish = async () => {
@@ -1033,7 +2238,14 @@ function bindDrawingSession(host, editor, options = {}) {
1033
2238
  const rebuild = async () => {
1034
2239
  const resolved = readSharedDrawing(shared);
1035
2240
  if (!resolved) return;
2241
+ if (resolved.fixups.length && host.canWrite())
2242
+ doc.transact(() => applyFixups(shared, resolved.fixups), VISIO_FIXUP_ORIGIN);
1036
2243
  const version = editor.version();
2244
+ if (keys.applied?.signature === resolved.signature && keys.applied.version === version) return;
2245
+ const before = keys.applied;
2246
+ const change = before && before.version === version && before.base === resolved.baseSignature ? {
2247
+ pages: resolved.pages.filter((page) => before.pages.get(page.key) !== page.contentSignature).map((page) => page.id)
2248
+ } : void 0;
1037
2249
  const composed = await composeDrawing(resolved, editor.exportBytes(), keys);
1038
2250
  if (disposed) return;
1039
2251
  if (editor.busy() || editor.version() !== version) {
@@ -1041,15 +2253,21 @@ function bindDrawingSession(host, editor, options = {}) {
1041
2253
  return;
1042
2254
  }
1043
2255
  try {
1044
- await editor.applyRemote(composed.bytes);
2256
+ await editor.applyRemote(composed.bytes, change);
1045
2257
  } catch (error) {
1046
2258
  if (editor.busy()) remoteDirty = true;
1047
2259
  else report(error);
1048
2260
  return;
1049
2261
  }
1050
2262
  composed.commit();
2263
+ keys.applied = {
2264
+ signature: composed.signature,
2265
+ version: editor.version(),
2266
+ base: resolved.baseSignature,
2267
+ pages: new Map(resolved.pages.map((page) => [page.key, page.contentSignature]))
2268
+ };
1051
2269
  };
1052
- const blocked = () => disposed || !settled || editor.busy();
2270
+ const blocked = () => disposed || incompatible || !settled || editor.busy();
1053
2271
  const pending = () => !blocked() && (localDirty && host.canWrite() || remoteDirty && !localDirty);
1054
2272
  const loop = async () => {
1055
2273
  while (pending()) {
@@ -1062,6 +2280,7 @@ function bindDrawingSession(host, editor, options = {}) {
1062
2280
  await rebuild();
1063
2281
  }
1064
2282
  } catch (error) {
2283
+ if (error instanceof SchemaVersionError) incompatible = true;
1065
2284
  report(error);
1066
2285
  }
1067
2286
  }
@@ -1088,18 +2307,30 @@ function bindDrawingSession(host, editor, options = {}) {
1088
2307
  }
1089
2308
  pump();
1090
2309
  };
2310
+ const queueRemote = () => {
2311
+ if (queued) return;
2312
+ queued = true;
2313
+ queuedDone = new Promise(
2314
+ (resolve) => schedule(() => {
2315
+ queued = false;
2316
+ remoteDirty = true;
2317
+ resolve();
2318
+ pump();
2319
+ })
2320
+ );
2321
+ };
1091
2322
  const afterTransaction = (transaction) => {
1092
2323
  if (disposed) return;
1093
2324
  const origin = transaction.origin;
1094
- if (origin === VISIO_EDIT_ORIGIN || origin === VISIO_SYNC_ORIGIN) return;
2325
+ if (origin === VISIO_EDIT_ORIGIN || origin === VISIO_SYNC_ORIGIN || origin === VISIO_FIXUP_ORIGIN)
2326
+ return;
1095
2327
  const changed = transaction.changed;
1096
2328
  if (!types.some((type) => changed.has(type))) return;
1097
2329
  if (!settled) {
1098
2330
  if (!isSharedDrawingEmpty(shared)) settle();
1099
2331
  return;
1100
2332
  }
1101
- remoteDirty = true;
1102
- pump();
2333
+ queueRemote();
1103
2334
  };
1104
2335
  doc.on("afterTransaction", afterTransaction);
1105
2336
  const stops = [
@@ -1117,7 +2348,10 @@ function bindDrawingSession(host, editor, options = {}) {
1117
2348
  ];
1118
2349
  if (host.synced || host.canWrite()) settle();
1119
2350
  const idle = async () => {
1120
- while (running) await running;
2351
+ while (running || queued) {
2352
+ if (queued) await queuedDone;
2353
+ if (running) await running;
2354
+ }
1121
2355
  };
1122
2356
  const replay = async (kind) => {
1123
2357
  await idle();
@@ -1149,4 +2383,4 @@ function bindDrawingSession(host, editor, options = {}) {
1149
2383
  };
1150
2384
  }
1151
2385
 
1152
- export { BODIES, CONNECTS, DEAD, DrawingKeys, META, PAGES, PARTS, SCHEMA_VERSION, SHAPES, VISIO_EDIT_ORIGIN, VISIO_SYNC_ORIGIN, bindDrawingSession, composeDrawing, isSharedDrawingEmpty, readSharedDrawing, sharedDrawing, snapshotDrawing, writeSharedDrawing };
2386
+ export { BLOBS, BODIES, CELLS, CONNECTS, DEAD, DrawingKeys, MASTERS, MEDIA, META, PAGES, PARTS, RENUM, SCHEMA_VERSION, SHAPES, SchemaVersionError, VISIO_EDIT_ORIGIN, VISIO_FIXUP_ORIGIN, VISIO_SYNC_ORIGIN, applyFixups, bindDrawingSession, composeDrawing, isSharedDrawingEmpty, readSharedDrawing, sharedDrawing, snapshotDrawing, writeSharedDrawing };