@superdoc/mcp 1.2.0-next.13 → 1.2.0-next.15

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 (2) hide show
  1. package/dist/index.js +283 -51
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31314,7 +31314,7 @@ var init_host = __esm(() => {
31314
31314
  });
31315
31315
 
31316
31316
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31317
- var SDK_VERSION = "2.15.0-next.13";
31317
+ var SDK_VERSION = "2.15.0-next.15";
31318
31318
 
31319
31319
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31320
31320
  import { chmodSync, existsSync } from "node:fs";
@@ -134832,26 +134832,68 @@ function canonicalName(namespace, localName2) {
134832
134832
  return `xml:${localName2}`;
134833
134833
  return namespace ? `{${namespace}}${localName2}` : localName2;
134834
134834
  }
134835
- function computeRunSignature(runXml) {
134836
- const canonicalXml = canonicalizeXmlForSignature(runXml);
134837
- const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_ATTR_RE, (attribute, offset, xml) => {
134838
- const precedingTag = xml.slice(xml.lastIndexOf("<", offset), offset);
134839
- return /^<w:proofErr(?:\s|$)/.test(precedingTag) ? attribute : "";
134840
- }).replace(REVISION_ELEMENT_RE, (markup) => markup.replace(REVISION_ID_ATTR_RE, "")).replace(COMMENT_MARKUP_RE, (markup, name, attributes) => isWordprocessingElement3(name, attributes, canonicalXml) ? "" : markup).replace(NOTE_REFERENCE_MARKUP_RE, (markup, runName, runAttributes, _rPrName, nestedName, nestedAttributes, standaloneName, standaloneAttributes) => {
134841
- const name = nestedName ?? standaloneName;
134842
- const attributes = nestedAttributes ?? standaloneAttributes;
134843
- if (!isWordprocessingElement3(name, attributes, canonicalXml))
134844
- return markup;
134845
- if (runName && !isWordprocessingElement3(runName, runAttributes, canonicalXml))
134846
- return markup;
134847
- return "";
134848
- }));
134849
- let hash2 = 2166136261;
134850
- for (let i = 0;i < normalized.length; i += 1) {
134851
- hash2 ^= normalized.charCodeAt(i);
134852
- hash2 = Math.imul(hash2, 16777619) >>> 0;
134835
+ function normalizeBalancedBookmarkIds(canonicalXmls) {
134836
+ const starts = new Map;
134837
+ const ends = new Set;
134838
+ const names = new Set;
134839
+ for (const xml of canonicalXmls) {
134840
+ const tags = [...xml.matchAll(BOOKMARK_TAG_RE)];
134841
+ if (tags.length !== [...xml.matchAll(BOOKMARK_OPENING_RE)].length)
134842
+ return canonicalXmls;
134843
+ for (const tag of tags) {
134844
+ if (tag[1] || !tag[0].endsWith("/>"))
134845
+ return canonicalXmls;
134846
+ const attributes = [...tag[3].matchAll(ATTRIBUTE_RE)];
134847
+ const ids = attributes.filter((attribute) => attribute[1] === "w:id");
134848
+ const bookmarkNames = attributes.filter((attribute) => attribute[1] === "w:name");
134849
+ const id = ids[0]?.[2] ?? ids[0]?.[3];
134850
+ if (ids.length !== 1 || !id || !/^\d+$/u.test(id))
134851
+ return canonicalXmls;
134852
+ if (tag[2] === "Start") {
134853
+ const name = bookmarkNames[0]?.[2] ?? bookmarkNames[0]?.[3];
134854
+ if (bookmarkNames.length !== 1 || !name || starts.has(id) || names.has(name))
134855
+ return canonicalXmls;
134856
+ starts.set(id, name);
134857
+ names.add(name);
134858
+ } else {
134859
+ if (bookmarkNames.length > 0 || !starts.has(id) || ends.has(id))
134860
+ return canonicalXmls;
134861
+ ends.add(id);
134862
+ }
134863
+ }
134853
134864
  }
134854
- return `v2-run-${hash2.toString(16).padStart(8, "0")}`;
134865
+ if (starts.size !== ends.size)
134866
+ return canonicalXmls;
134867
+ return canonicalXmls.map((xml) => xml.replace(BOOKMARK_TAG_RE, (tag, _closing, _kind, attributes) => {
134868
+ const id = /\bw:id="([^"]*)"/u.exec(attributes)[1];
134869
+ return tag.replace(/\bw:id="[^"]*"/u, `w:id=${JSON.stringify(`bookmark:${starts.get(id)}`)}`);
134870
+ }));
134871
+ }
134872
+ function computeRunSignatures(runXmls) {
134873
+ const canonicalXmls = normalizeBalancedBookmarkIds(runXmls.map(canonicalizeXmlForSignature));
134874
+ return canonicalXmls.map((canonicalXml) => {
134875
+ const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_ATTR_RE, (attribute, offset, xml) => {
134876
+ const precedingTag = xml.slice(xml.lastIndexOf("<", offset), offset);
134877
+ return /^<w:proofErr(?:\s|$)/.test(precedingTag) ? attribute : "";
134878
+ }).replace(REVISION_ELEMENT_RE, (markup) => markup.replace(REVISION_ID_ATTR_RE, "")).replace(COMMENT_MARKUP_RE, (markup, name, attributes) => isWordprocessingElement3(name, attributes, canonicalXml) ? "" : markup).replace(NOTE_REFERENCE_MARKUP_RE, (markup, runName, runAttributes, _rPrName, nestedName, nestedAttributes, standaloneName, standaloneAttributes) => {
134879
+ const name = nestedName ?? standaloneName;
134880
+ const attributes = nestedAttributes ?? standaloneAttributes;
134881
+ if (!isWordprocessingElement3(name, attributes, canonicalXml))
134882
+ return markup;
134883
+ if (runName && !isWordprocessingElement3(runName, runAttributes, canonicalXml))
134884
+ return markup;
134885
+ return "";
134886
+ }));
134887
+ let hash2 = 2166136261;
134888
+ for (let i = 0;i < normalized.length; i += 1) {
134889
+ hash2 ^= normalized.charCodeAt(i);
134890
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
134891
+ }
134892
+ return `v2-run-${hash2.toString(16).padStart(8, "0")}`;
134893
+ });
134894
+ }
134895
+ function computeRunSignature(runXml) {
134896
+ return computeRunSignatures([runXml])[0];
134855
134897
  }
134856
134898
  function normalizeRunXmlForSignature(runXml) {
134857
134899
  const pieces = [];
@@ -134934,7 +134976,7 @@ function isWordprocessingElement3(name, attributes, namespaceContext) {
134934
134976
  return true;
134935
134977
  return (declaration[2] ?? declaration[3] ?? "") === WORDPROCESSINGML_NAMESPACE2;
134936
134978
  }
134937
- var REVISION_ATTR_RE, REVISION_ELEMENT_RE, REVISION_ID_ATTR_RE, COMMENT_MARKUP_RE, PROOFING_BOUNDARY_MARKUP_RE, NOTE_REFERENCE_MARKUP_RE, RUN_RE, RUN_PROPERTIES_RE, EMPTY_RUN_PROPERTIES_RE, TEXTISH_RE, RUN_CONTROL_RE, ATTRIBUTE_RE, WORDPROCESSINGML_NAMESPACE2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", WORDPROCESSINGML_2010_NAMESPACE = "http://schemas.microsoft.com/office/word/2010/wordml", MARKUP_COMPATIBILITY_NAMESPACE = "http://schemas.openxmlformats.org/markup-compatibility/2006", XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace", XML_TAG_RE;
134979
+ var REVISION_ATTR_RE, REVISION_ELEMENT_RE, REVISION_ID_ATTR_RE, COMMENT_MARKUP_RE, PROOFING_BOUNDARY_MARKUP_RE, NOTE_REFERENCE_MARKUP_RE, RUN_RE, RUN_PROPERTIES_RE, EMPTY_RUN_PROPERTIES_RE, TEXTISH_RE, RUN_CONTROL_RE, ATTRIBUTE_RE, WORDPROCESSINGML_NAMESPACE2 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", WORDPROCESSINGML_2010_NAMESPACE = "http://schemas.microsoft.com/office/word/2010/wordml", MARKUP_COMPATIBILITY_NAMESPACE = "http://schemas.openxmlformats.org/markup-compatibility/2006", XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace", XML_TAG_RE, BOOKMARK_OPENING_RE, BOOKMARK_TAG_RE;
134938
134980
  var init_run_signature = __esm(() => {
134939
134981
  REVISION_ATTR_RE = /\s+(?:w:rsid\w*|w14:paraId|w14:textId)="[^"]*"/g;
134940
134982
  REVISION_ELEMENT_RE = /<w:(?:ins|del|moveFrom|moveTo)\b[^>]*>/g;
@@ -134949,6 +134991,8 @@ var init_run_signature = __esm(() => {
134949
134991
  RUN_CONTROL_RE = /<((?:[\w.-]+:)?(tab|br|cr))\b([^>]*)\/>/g;
134950
134992
  ATTRIBUTE_RE = /([\w.:-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
134951
134993
  XML_TAG_RE = /<(\/?)(([A-Za-z_][\w.-]*)(?::([A-Za-z_][\w.-]*))?)((?:(?:"[^"]*"|'[^']*'|[^>"'])*))>/g;
134994
+ BOOKMARK_OPENING_RE = /<\/?w:bookmark(?:Start|End)\b/g;
134995
+ BOOKMARK_TAG_RE = /<(\/?)w:bookmark(Start|End)\b((?:"[^"]*"|[^">])*)>/g;
134952
134996
  });
134953
134997
 
134954
134998
  // ../../../v2/document-compare/src/note-story-projection.ts
@@ -134960,7 +135004,7 @@ function projectCapturedNoteStoryEntries(value) {
134960
135004
  const ranges = collectWordParagraphRanges(value.notePartXml);
134961
135005
  if (!ranges)
134962
135006
  return null;
134963
- return ranges.flatMap((range, ordinal) => {
135007
+ const entries = ranges.flatMap((range, ordinal) => {
134964
135008
  if (range.start < noteStart || range.end > noteEnd)
134965
135009
  return [];
134966
135010
  const paragraphXml = value.notePartXml.slice(range.start, range.end);
@@ -134974,12 +135018,13 @@ function projectCapturedNoteStoryEntries(value) {
134974
135018
  text: visibleParagraphText(paragraphXml, range.namespaceBindings),
134975
135019
  headingLevel: metadata2.headingLevel,
134976
135020
  styleId: metadata2.styleId,
134977
- runSignature: computeRunSignature(runXml),
134978
135021
  runXml,
134979
135022
  paragraphPropertiesXml,
134980
135023
  paragraphPropertiesSignature: computeParagraphPropertiesSignature(paragraphPropertiesXml)
134981
135024
  }];
134982
135025
  });
135026
+ const runSignatures = computeRunSignatures(entries.map((entry) => entry.runXml ?? ""));
135027
+ return entries.map((entry, index) => ({ ...entry, runSignature: runSignatures[index] }));
134983
135028
  }
134984
135029
  function collectWordParagraphRanges(xml) {
134985
135030
  let valid = true;
@@ -142136,6 +142181,12 @@ function visitNumericXmlIds(xml, visit) {
142136
142181
  if (value !== null)
142137
142182
  visit(value);
142138
142183
  }
142184
+ CONTENT_CONTROL_ID_RE.lastIndex = 0;
142185
+ while (match = CONTENT_CONTROL_ID_RE.exec(xml)) {
142186
+ const value = parseRevisionWId(decodeXmlTextPreservingInvalidNumericEntities(match[2] ?? ""));
142187
+ if (value !== null)
142188
+ visit(value);
142189
+ }
142139
142190
  }
142140
142191
  function scanMaxNumericXmlId(xml) {
142141
142192
  let max = 0;
@@ -142164,12 +142215,13 @@ function observeRevisionWIdsFromXml(state, xml) {
142164
142215
  if (max > reservation.max)
142165
142216
  reservation.max = max;
142166
142217
  }
142167
- var TD8, NUMERIC_XML_ID_RE;
142218
+ var TD8, NUMERIC_XML_ID_RE, CONTENT_CONTROL_ID_RE;
142168
142219
  var init_revision_wid_scan = __esm(() => {
142169
142220
  init_src2();
142170
142221
  init_xml_text();
142171
142222
  TD8 = new TextDecoder;
142172
142223
  NUMERIC_XML_ID_RE = /\b(?:[\w.-]+:)?id\s*=\s*(["'])([^"']*)\1/g;
142224
+ CONTENT_CONTROL_ID_RE = /<(?:[\w.-]+:)?id(?=[\s/>])[^>]*?\b(?:[\w.-]+:)?val\s*=\s*(["'])([^"']*)\1/g;
142173
142225
  });
142174
142226
 
142175
142227
  // ../../../v2/editor-core/dist/document/internal/part-wml-bookmark-start-cache.js
@@ -209365,6 +209417,17 @@ function planCut(pieces, levelStart, input, rootPieces, chain = []) {
209365
209417
  { close: chunks.close, reopen: chunks.open }
209366
209418
  ]);
209367
209419
  }
209420
+ if (strict.kind === "container" && strict.containerKind === "sdt" && input.allowMetadataAnchorSplit) {
209421
+ if (input.space === "tracked")
209422
+ return { kind: "reject", reason: "paragraph-split-point-unsupported" };
209423
+ const chunks = anchoredMetadataChunks(strict, input.allocateWId);
209424
+ if (!chunks)
209425
+ return { kind: "reject", reason: "paragraph-split-point-unsupported" };
209426
+ return planCut(strict.children, chunks.contentStart, input, rootPieces, [
209427
+ ...chain,
209428
+ { close: chunks.close, reopen: chunks.reopen }
209429
+ ]);
209430
+ }
209368
209431
  return { kind: "reject", reason: "paragraph-split-point-unsupported" };
209369
209432
  }
209370
209433
  let boundaryChar = levelStart;
@@ -209413,6 +209476,32 @@ function wrapperChunks(piece) {
209413
209476
  contentStart: piece.charStart + openEnd
209414
209477
  };
209415
209478
  }
209479
+ function anchoredMetadataChunks(piece, allocateWId) {
209480
+ if (piece.children.length === 0)
209481
+ return null;
209482
+ const raw = piece.rawXml;
209483
+ const prefix = /^(<w:sdt\b[^>]*>)\s*(<w:sdtPr>[\s\S]*?<\/w:sdtPr>)\s*(<w:sdtContent\b[^>]*>)/.exec(raw);
209484
+ const close = /<\/w:sdtContent>\s*<\/w:sdt>$/.exec(raw)?.[0];
209485
+ if (!prefix || !close)
209486
+ return null;
209487
+ const properties = prefix[2];
209488
+ const normalizedProperties = properties.replace(/\s+\/>/g, "/>").replace(/>\s+</g, "><");
209489
+ const id = /<w:id w:val="\d+"\/>/.exec(normalizedProperties)?.[0];
209490
+ const originalId = /<w:id w:val="\d+"\s*\/>/.exec(properties)?.[0];
209491
+ const alias = '<w:alias w:val="Anchored metadata"/>';
209492
+ const tag = /<w:tag w:val="[^"]+"\/>/.exec(normalizedProperties)?.[0];
209493
+ const appearance = '<w15:appearance w15:val="hidden"/>';
209494
+ const plainText = "<w:text/>";
209495
+ if (!id || !originalId || !tag || !normalizedProperties.includes(alias) || !normalizedProperties.includes(appearance) || !normalizedProperties.includes(plainText) || normalizedProperties !== `<w:sdtPr>${id}${alias}${tag}${appearance}${plainText}</w:sdtPr>`) {
209496
+ return null;
209497
+ }
209498
+ const originalPrefix = prefix[0];
209499
+ return {
209500
+ contentStart: piece.charStart + originalPrefix.length,
209501
+ close,
209502
+ reopen: () => originalPrefix.replace(originalId, originalId.replace(/(w:val=")\d+/, (_match, attribute) => `${attribute}${allocateWId()}`))
209503
+ };
209504
+ }
209416
209505
  function openTagEnd(raw) {
209417
209506
  let quote = null;
209418
209507
  for (let i2 = 0;i2 < raw.length; i2 += 1) {
@@ -209439,7 +209528,7 @@ function refreshRevisionId(openChunk, allocateWId) {
209439
209528
  }
209440
209529
  function assembleHalves(src, regionStart, regionEnd, plan) {
209441
209530
  const closes = [...plan.chain].reverse().map((element) => element.close).join("");
209442
- const reopens = plan.chain.map((element) => element.reopen).join("");
209531
+ const reopens = plan.chain.map((element) => typeof element.reopen === "function" ? element.reopen() : element.reopen).join("");
209443
209532
  if (plan.kind === "boundary") {
209444
209533
  const boundaryChar = snapBoundaryPastRunClose(src, plan.boundaryChar, regionEnd);
209445
209534
  return {
@@ -213704,7 +213793,8 @@ function prepareParagraphSplit(input) {
213704
213793
  offset: input.offset,
213705
213794
  space: input.coordinateSpace,
213706
213795
  allocateWId: input.allocateWId,
213707
- atomicObjectsAsCaretSlots: true
213796
+ atomicObjectsAsCaretSlots: true,
213797
+ allowMetadataAnchorSplit: true
213708
213798
  });
213709
213799
  if (!slice.ok)
213710
213800
  return slice;
@@ -213727,6 +213817,14 @@ function prepareParagraphSplit(input) {
213727
213817
  const headXml = openTag + headProperties + slice.head + closeTag;
213728
213818
  return { ok: true, headXml, tailXml };
213729
213819
  }
213820
+ function previewParagraphSplit(input) {
213821
+ const result = prepareParagraphSplit({
213822
+ ...input,
213823
+ tailParaId: "00000000",
213824
+ allocateWId: () => 0
213825
+ });
213826
+ return result.ok ? { ok: true } : result;
213827
+ }
213730
213828
  function renderPreparedParagraphSplit(split, trackedStamp) {
213731
213829
  const head = trackedStamp ? injectParagraphMarkTrackedMarker(split.headXml, "ins", trackedStamp) : split.headXml;
213732
213830
  return head + split.tailXml;
@@ -267771,6 +267869,10 @@ var init_registry3 = __esm(() => {
267771
267869
  "blocks.move": {
267772
267870
  supportsDryRun: true,
267773
267871
  dryRunOverrideReason: "The V2 block adapter validates move previews without committing package or history state."
267872
+ },
267873
+ "blocks.split": {
267874
+ supportsDryRun: true,
267875
+ dryRunOverrideReason: "The V2 block adapter validates split previews without committing package or history state."
267774
267876
  }
267775
267877
  };
267776
267878
  COLLABORATIVE_TABLE_REPRESENTED_OPERATIONS = new Set([
@@ -303647,17 +303749,35 @@ class V2BlocksAdapter {
303647
303749
  const resolved = resolveParagraphTarget(this.context, input.target);
303648
303750
  if ("error" in resolved)
303649
303751
  return failure4(resolved.error, resolved.message);
303650
- const begun = this.context.beginMutation({ expectedRevision: options?.expectedRevision });
303651
- if ("failure" in begun)
303652
- return failure4(begun.failure.code, begun.failure.message);
303653
- const tx = begun.tx;
303654
303752
  const partBytes = getSDDocumentSessionInternalPartBytes(this.context.session, storyPartUriFromId5(resolved.storyId));
303655
303753
  const paraXml = partBytes ? TD90.decode(partBytes.subarray(resolved.entry.byteStart, resolved.entry.byteEnd)) : "";
303656
303754
  const objectSlack = (paraXml.match(/<[\w.-]+:(?:drawing|pict|object)\b/g) ?? []).length;
303657
303755
  const coordinateSpace = input.coordinateSpace ?? "visible";
303756
+ const offset = coordinateSpace === "tracked" ? Math.max(0, Math.trunc(Number.isFinite(input.offset) ? input.offset : 0)) : clampStructuralOffset(input.offset, resolved.entry.textLength + objectSlack);
303757
+ if (options?.dryRun) {
303758
+ const policy = this.context.validateMutation(options);
303759
+ if (policy)
303760
+ return failure4(policy.code, policy.message);
303761
+ if (!partBytes)
303762
+ return failure4("INVALID_CONTEXT", "paragraph-target-not-found");
303763
+ const planned = previewParagraphSplit({
303764
+ paragraphXml: paraXml,
303765
+ partBytes,
303766
+ paragraphByteStart: resolved.entry.byteStart,
303767
+ offset,
303768
+ coordinateSpace
303769
+ });
303770
+ if (!planned.ok)
303771
+ return failure4(mapRejectionReason(planned.reason), planned.reason);
303772
+ return { success: true, inserted: input.target, affectedStories: [] };
303773
+ }
303774
+ const begun = this.context.beginMutation({ expectedRevision: options?.expectedRevision });
303775
+ if ("failure" in begun)
303776
+ return failure4(begun.failure.code, begun.failure.message);
303777
+ const tx = begun.tx;
303658
303778
  tx.paragraphSplit({
303659
303779
  target: { storyId: resolved.storyId, paraId: input.target.nodeId },
303660
- offset: coordinateSpace === "tracked" ? Math.max(0, Math.trunc(Number.isFinite(input.offset) ? input.offset : 0)) : clampStructuralOffset(input.offset, resolved.entry.textLength + objectSlack),
303780
+ offset,
303661
303781
  coordinateSpace,
303662
303782
  changeMode: options?.changeMode ?? "direct"
303663
303783
  });
@@ -355004,8 +355124,12 @@ function projectStories(context, slots) {
355004
355124
  if (family === "footnotes" || family === "endnotes")
355005
355125
  requiredPartBytes(context, story.partUri);
355006
355126
  const paragraphs = readStoryParagraphs(context, story.storyId).filter(isWordprocessingParagraph);
355127
+ const runXmls = paragraphs.map((p) => extractParagraphRunXml(p.rawXml, p.namespaceBindings ?? {}));
355128
+ const runSignatures = computeRunSignatures(runXmls);
355129
+ const runXmlByOrdinal = new Map(paragraphs.map((p, i2) => [p.ordinal, runXmls[i2]]));
355130
+ const runSignatureByOrdinal = new Map(paragraphs.map((p, i2) => [p.ordinal, runSignatures[i2]]));
355007
355131
  const projectEntry2 = (p) => {
355008
- const runXml = extractParagraphRunXml(p.rawXml, p.namespaceBindings ?? {});
355132
+ const runXml = runXmlByOrdinal.get(p.ordinal);
355009
355133
  const paragraphPropertiesXml = extractParagraphPropertiesXml(p.rawXml, p.namespaceBindings ?? {});
355010
355134
  const blockId = family === "header-footer-parts" && isStructurallyBlankStoryParagraph({ text: p.text, runXml }) ? `${slotKey}::blank:${p.ordinal}` : p.blockId;
355011
355135
  return {
@@ -355014,7 +355138,7 @@ function projectStories(context, slots) {
355014
355138
  text: p.text,
355015
355139
  headingLevel: p.headingLevel,
355016
355140
  styleId: p.styleId,
355017
- runSignature: computeRunSignature(runXml),
355141
+ runSignature: runSignatureByOrdinal.get(p.ordinal),
355018
355142
  runXml,
355019
355143
  trackedChangeAnnotationKeys: trackedChangeAnnotationKeysForParagraph(context, p),
355020
355144
  paragraphPropertiesXml,
@@ -355087,8 +355211,10 @@ function bodyParagraphStructuralScopes(context, paragraphs) {
355087
355211
  function projectBody(context) {
355088
355212
  const paragraphs = readBodyParagraphs(context).filter(isWordprocessingParagraph);
355089
355213
  const scopes = bodyParagraphStructuralScopes(context, paragraphs);
355214
+ const runXmls = paragraphs.map((p) => extractParagraphRunXml(p.rawXml, p.namespaceBindings ?? {}));
355215
+ const runSignatures = computeRunSignatures(runXmls);
355090
355216
  return paragraphs.map((p, ordinal) => {
355091
- const runXml = extractParagraphRunXml(p.rawXml, p.namespaceBindings ?? {});
355217
+ const runXml = runXmls[ordinal];
355092
355218
  const paragraphPropertiesXml = extractParagraphPropertiesXml(p.rawXml, p.namespaceBindings ?? {});
355093
355219
  return {
355094
355220
  blockId: p.blockId,
@@ -355097,7 +355223,7 @@ function projectBody(context) {
355097
355223
  text: p.text,
355098
355224
  headingLevel: p.headingLevel,
355099
355225
  styleId: p.styleId,
355100
- runSignature: computeRunSignature(runXml),
355226
+ runSignature: runSignatures[ordinal],
355101
355227
  runXml,
355102
355228
  trackedChangeAnnotationKeys: trackedChangeAnnotationKeysForParagraph(context, p),
355103
355229
  paragraphPropertiesXml,
@@ -359096,9 +359222,14 @@ function buildTrackedTableRowReplayPlan(input) {
359096
359222
  if (baseTable.gridColumnCount !== targetTable.gridColumnCount) {
359097
359223
  return unsafe(`table ${baseTable.tableNodeId} changes its column grid`);
359098
359224
  }
359099
- const rowKey = (row, bytes) => sliceBytes(bytes, row.rowByteStart, row.rowByteEnd).replace(/\s+w14:paraId="[0-9A-Fa-f]{8}"/g, "");
359100
- const baseKeys = baseTable.rows.map((row) => rowKey(row, baseBytes));
359101
- const targetKeys = targetTable.rows.map((row) => rowKey(row, targetBytes));
359225
+ const rowKey = (row, bytes, root) => rowWithoutParagraphIdentities(sliceBytes(bytes, row.rowByteStart, row.rowByteEnd), root.namespaceBindings);
359226
+ const baseKeysWithInvalid = baseTable.rows.map((row) => rowKey(row, baseBytes, baseRoot));
359227
+ const targetKeysWithInvalid = targetTable.rows.map((row) => rowKey(row, targetBytes, targetRoot));
359228
+ if (baseKeysWithInvalid.includes(null) || targetKeysWithInvalid.includes(null)) {
359229
+ return unsafe(`table ${baseTable.tableNodeId} has invalid paragraph identity markup`);
359230
+ }
359231
+ const baseKeys = baseKeysWithInvalid.map((key2) => key2);
359232
+ const targetKeys = targetKeysWithInvalid.map((key2) => key2);
359102
359233
  const baseIsShorter = baseKeys.length <= targetKeys.length;
359103
359234
  const matched = matchRowSubsequence(baseIsShorter ? baseKeys : targetKeys, baseIsShorter ? targetKeys : baseKeys, (baseIsShorter ? baseTable.rows : targetTable.rows).map((row) => row.rowNodeId), (baseIsShorter ? targetTable.rows : baseTable.rows).map((row) => row.rowNodeId));
359104
359235
  if (!matched)
@@ -359376,6 +359507,66 @@ function tableSkeleton(bytes, table) {
359376
359507
  function sliceBytes(bytes, start, end) {
359377
359508
  return new TextDecoder().decode(bytes.subarray(start, end));
359378
359509
  }
359510
+ function rowWithoutParagraphIdentities(rowXml, namespaceBindings) {
359511
+ const parsed = collectXmlElementRanges(rowXml, {
359512
+ fragment: true,
359513
+ additionalNamespaces: xmlFragmentAdditionalNamespaces(rowXml, new Map(Object.entries(namespaceBindings)))
359514
+ });
359515
+ if (parsed.errors.length > 0)
359516
+ return null;
359517
+ const deletions = [];
359518
+ for (const paragraph4 of parsed.ranges) {
359519
+ if (paragraph4.tag.namespaceUri !== WORDPROCESSINGML_NS4 || paragraph4.tag.localName !== "p")
359520
+ continue;
359521
+ for (const attribute2 of paragraph4.tag.attributes) {
359522
+ if (attribute2.namespaceUri !== WORDPROCESSINGML_2010_NS || attribute2.localName !== "paraId" || !/^[0-9A-Fa-f]{8}$/.test(attribute2.value))
359523
+ continue;
359524
+ const opening = rowXml.slice(paragraph4.start, paragraph4.openEnd);
359525
+ const span = rawStartTagAttributeSpan(opening, attribute2.name, attribute2.value);
359526
+ if (!span)
359527
+ return null;
359528
+ deletions.push({ start: paragraph4.start + span.start, end: paragraph4.start + span.end });
359529
+ }
359530
+ }
359531
+ return deletions.sort((left, right) => right.start - left.start).reduce((xml, deletion) => xml.slice(0, deletion.start) + xml.slice(deletion.end), rowXml);
359532
+ }
359533
+ function rawStartTagAttributeSpan(opening, expectedName, expectedValue) {
359534
+ const whitespace = (character) => character !== undefined && /[\x20\t\r\n]/.test(character);
359535
+ let index2 = 1;
359536
+ while (index2 < opening.length && !whitespace(opening[index2]) && opening[index2] !== ">" && opening[index2] !== "/")
359537
+ index2 += 1;
359538
+ while (index2 < opening.length) {
359539
+ const start = index2;
359540
+ while (whitespace(opening[index2]))
359541
+ index2 += 1;
359542
+ if (opening[index2] === ">" || opening[index2] === "/")
359543
+ break;
359544
+ const nameStart = index2;
359545
+ while (index2 < opening.length && !whitespace(opening[index2]) && !["=", ">", "/"].includes(opening[index2]))
359546
+ index2 += 1;
359547
+ const name = opening.slice(nameStart, index2);
359548
+ while (whitespace(opening[index2]))
359549
+ index2 += 1;
359550
+ if (opening[index2] !== "=")
359551
+ return null;
359552
+ index2 += 1;
359553
+ while (whitespace(opening[index2]))
359554
+ index2 += 1;
359555
+ const quote = opening[index2];
359556
+ if (quote !== '"' && quote !== "'")
359557
+ return null;
359558
+ const valueStart = ++index2;
359559
+ while (index2 < opening.length && opening[index2] !== quote)
359560
+ index2 += 1;
359561
+ if (index2 === opening.length)
359562
+ return null;
359563
+ const value2 = opening.slice(valueStart, index2);
359564
+ index2 += 1;
359565
+ if (name === expectedName && value2 === expectedValue)
359566
+ return { start, end: index2 };
359567
+ }
359568
+ return null;
359569
+ }
359379
359570
  function documentRootContext(xml) {
359380
359571
  const roots = [];
359381
359572
  let valid = true;
@@ -360667,10 +360858,16 @@ function projectMainDocumentBodyEntries(source, options = {}) {
360667
360858
  });
360668
360859
  const scopes = mapBodyParagraphStructuralScopes(tableCatalog, paragraphIndex.entries);
360669
360860
  const namespaceBindingsByByteStart = scanParagraphNamespaceBindingsByByteStart(source.xml);
360670
- return paragraphIndex.entries.map((entry) => {
360671
- const rawXml = TD107.decode(bytes.subarray(entry.byteStart, entry.byteEnd));
360861
+ const rawXmls = paragraphIndex.entries.map((entry) => TD107.decode(bytes.subarray(entry.byteStart, entry.byteEnd)));
360862
+ const runXmls = paragraphIndex.entries.map((entry, index2) => {
360863
+ const namespaceBindings = namespaceBindingsByByteStart.get(entry.byteStart) ?? {};
360864
+ return extractParagraphRunXml(rawXmls[index2], namespaceBindings);
360865
+ });
360866
+ const runSignatures = computeRunSignatures(runXmls);
360867
+ return paragraphIndex.entries.map((entry, index2) => {
360868
+ const rawXml = rawXmls[index2];
360672
360869
  const namespaceBindings = namespaceBindingsByByteStart.get(entry.byteStart) ?? {};
360673
- const runXml = extractParagraphRunXml(rawXml, namespaceBindings);
360870
+ const runXml = runXmls[index2];
360674
360871
  const paragraphPropertiesXml = extractParagraphPropertiesXml(rawXml, namespaceBindings);
360675
360872
  return {
360676
360873
  blockId: entry.blockId,
@@ -360679,7 +360876,7 @@ function projectMainDocumentBodyEntries(source, options = {}) {
360679
360876
  text: readVisibleParagraphText(rawXml),
360680
360877
  headingLevel: paragraphHeadingLevel(rawXml, { paragraphStyleOutlineLevels }),
360681
360878
  styleId: paragraphStyleId(rawXml),
360682
- runSignature: computeRunSignature(runXml),
360879
+ runSignature: runSignatures[index2],
360683
360880
  runXml,
360684
360881
  paragraphPropertiesXml,
360685
360882
  paragraphPropertiesSignature: computeParagraphPropertiesSignature2(paragraphPropertiesXml)
@@ -363160,22 +363357,32 @@ function buildCompareApplyEligibility(context, payload, plans) {
363160
363357
  if (bodyDeferredToTableReplay)
363161
363358
  direct.add("body");
363162
363359
  }
363360
+ const trackedBodyOperations = payload.applyPlan.bodyOperations.length > 0 ? payload.applyPlan.bodyOperations : computeCompareBodyOperations(payload.base.body, payload.target.body, {
363361
+ base: payload.base.definitions,
363362
+ target: payload.target.definitions
363363
+ });
363163
363364
  const trackedTablePlan = buildTrackedTableRowReplayPlan({
363164
363365
  base: mainDocument.base,
363165
363366
  target: mainDocument.target,
363166
363367
  baseBody: payload.base.body,
363167
363368
  targetBody: payload.target.body,
363168
- bodyOperations: payload.applyPlan.bodyOperations.length > 0 ? payload.applyPlan.bodyOperations : computeCompareBodyOperations(payload.base.body, payload.target.body, {
363169
- base: payload.base.definitions,
363170
- target: payload.target.definitions
363171
- })
363369
+ bodyOperations: trackedBodyOperations
363172
363370
  });
363371
+ const trackedBlockPlan = trackedTablePlan.unsafeReasons.length > 0 && !trackedTablePlan.refuseBlockFallback ? buildTrackedTableBlockReplayPlan({
363372
+ base: mainDocument.base,
363373
+ target: mainDocument.target,
363374
+ bodyOperations: trackedBodyOperations,
363375
+ allowTopologyReplacement: true
363376
+ }) : null;
363173
363377
  if (trackedTablePlan.unsafeReasons.length === 0 && trackedTablePlan.steps.length > 0) {
363174
363378
  tracked.add("tables");
363175
363379
  if (bodyDeferredToTableReplay)
363176
363380
  tracked.add("body");
363177
363381
  if (rowReplayCoversContentControls(payload.base.annotations, payload.target.annotations, trackedTablePlan.steps, mainDocument.base.xml, mainDocument.target.xml))
363178
363382
  tracked.add("content-controls");
363383
+ } else if (trackedBlockPlan && trackedBlockPlan.steps.length > 0) {
363384
+ tracked.add("tables");
363385
+ tracked.add("body");
363179
363386
  }
363180
363387
  }
363181
363388
  const packageGraphCoveredByComments = direct.has("comments") && packageGraphKeysNotCoveredByComments(context, payload.base, payload.target, plans.commentPlan.steps).length === 0;
@@ -381859,10 +382066,27 @@ function groupMetadataAnchor(documentXml, id, knownIds) {
381859
382066
  const kept = contiguousAnchorPrefix(documentXml, matches);
381860
382067
  if (kept.length === 1)
381861
382068
  return { id, segments: kept, target: kept[0].target };
381862
- const textSegments = kept.map((anchor) => ({
381863
- blockId: anchor.blockId,
381864
- range: { start: anchor.startOffset, end: anchor.endOffset }
381865
- }));
382069
+ const textSegments = [];
382070
+ for (const anchor of kept) {
382071
+ const previous3 = textSegments.at(-1);
382072
+ if (previous3?.blockId === anchor.blockId && previous3.range.end === anchor.startOffset) {
382073
+ previous3.range.end = anchor.endOffset;
382074
+ } else {
382075
+ textSegments.push({ blockId: anchor.blockId, range: { start: anchor.startOffset, end: anchor.endOffset } });
382076
+ }
382077
+ }
382078
+ if (textSegments.length === 1) {
382079
+ const range = textSegments[0];
382080
+ return {
382081
+ id,
382082
+ segments: kept,
382083
+ target: {
382084
+ kind: "selection",
382085
+ start: { kind: "text", blockId: range.blockId, offset: range.range.start, story: bodyStoryLocator2() },
382086
+ end: { kind: "text", blockId: range.blockId, offset: range.range.end, story: bodyStoryLocator2() }
382087
+ }
382088
+ };
382089
+ }
381866
382090
  return {
381867
382091
  id,
381868
382092
  segments: kept,
@@ -381882,7 +382106,12 @@ function contiguousAnchorPrefix(documentXml, matches) {
381882
382106
  const context = map3.byBlockId(matches[i2].blockId);
381883
382107
  if (!context)
381884
382108
  break;
381885
- if (context.ordinal !== prev.ordinal + 1)
382109
+ const sameParagraph = context.ordinal === prev.ordinal;
382110
+ if (sameParagraph) {
382111
+ const previousAnchor = kept.at(-1);
382112
+ if (context.blockId !== previousAnchor.blockId || matches[i2].startOffset !== previousAnchor.endOffset || !isMetadataCarrierGap(documentXml.slice(previousAnchor.end, matches[i2].start)))
382113
+ break;
382114
+ } else if (context.ordinal !== prev.ordinal + 1)
381886
382115
  break;
381887
382116
  if (!sameTableCellPath(context.tableCellPath, first.tableCellPath))
381888
382117
  break;
@@ -381899,6 +382128,9 @@ function contiguousAnchorPrefix(documentXml, matches) {
381899
382128
  }
381900
382129
  return kept;
381901
382130
  }
382131
+ function isMetadataCarrierGap(xml) {
382132
+ return /^(?:\s*<w:proofErr\s+w:type="(?:spellStart|spellEnd|gramStart|gramEnd)"\s*\/>)*\s*$/.test(xml);
382133
+ }
381902
382134
  function sameOrdinalPath2(left, right) {
381903
382135
  return left.length === right.length && left.every((value2, index2) => value2 === right[index2]);
381904
382136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.2.0-next.13",
3
+ "version": "1.2.0-next.15",
4
4
  "description": "Model Context Protocol server for SuperDoc, giving an AI agent tools to read and edit .docx files.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -24,13 +24,13 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.26.0",
27
- "@superdoc/sdk": "2.15.0-next.13",
27
+ "@superdoc/sdk": "2.15.0-next.15",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.18.0-next.13",
33
- "@superdoc/docx-engine": "0.17.0-next.15",
32
+ "superdoc": "2.18.0-next.15",
33
+ "@superdoc/docx-engine": "0.17.0-next.17",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",